pull/2748/head
Martin Aeschlimann 9 years ago
parent dc598757e7
commit 2380ce142c

3
.gitignore vendored

@ -0,0 +1,3 @@
/node_modules/
/out/
/release/

@ -0,0 +1,8 @@
/.vscode/
/lib/
/out/
/src/
/test/
/gulpfile.js
/tsconfig.json
/.npmignore

@ -0,0 +1,9 @@
// Place your settings in this file to overwrite default and user settings.
{
"files.trimTrailingWhitespace": true,
"search.exclude": {
"**/node_modules": true,
"**/release": true,
"**/out": true
}
}

@ -0,0 +1,21 @@
The MIT License (MIT)
Copyright (c) Microsoft Corporation
Permission is hereby granted, free of charge, to any person obtaining a copy
of this software and associated documentation files (the "Software"), to deal
in the Software without restriction, including without limitation the rights
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
copies of the Software, and to permit persons to whom the Software is
furnished to do so, subject to the following conditions:
The above copyright notice and this permission notice shall be included in all
copies or substantial portions of the Software.
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
SOFTWARE.

@ -1,2 +1,17 @@
<<<<<<< dc598757e71a1ee33423bd24c3cb1b4b849722e6
# monaco-css
CSS language support for the Monaco editor
=======
# Monaco TypeScript
TypeScript and JavaScript language support for the Monaco Editor.
![typescript](https://cloud.githubusercontent.com/assets/5047891/15926623/5262fe08-2e3d-11e6-9b90-1d43fda07178.gif)
## Installing
This npm module is bundled and distributed in the [monaco-editor](https://www.npmjs.com/package/monaco-editor) npm module.
## License
[MIT](https://github.com/Microsoft/monaco-typescript/blob/master/LICENSE.md)
>>>>>>> Init

@ -0,0 +1,210 @@
/*---------------------------------------------------------------------------------------------
* Copyright (c) Microsoft Corporation. All rights reserved.
* Licensed under the MIT License. See License.txt in the project root for license information.
*--------------------------------------------------------------------------------------------*/
var gulp = require('gulp');
var tsb = require('gulp-tsb');
var assign = require('object-assign');
var fs = require('fs');
var path = require('path');
var merge = require('merge-stream');
var rjs = require('gulp-requirejs');
var uglify = require('gulp-uglify');
var rimraf = require('rimraf');
var es = require('event-stream');
gulp.task('clean-release', function(cb) { rimraf('release', { maxBusyTries: 1 }, cb); });
gulp.task('release', ['clean-release','compile'], function() {
var sha1 = getGitVersion(__dirname);
var semver = require('./package.json').version;
var headerVersion = semver + '(' + sha1 + ')';
var BUNDLED_FILE_HEADER = [
'/*!-----------------------------------------------------------------------------',
' * Copyright (c) Microsoft Corporation. All rights reserved.',
' * monaco-typescript version: ' + headerVersion,
' * Released under the MIT license',
' * https://github.com/Microsoft/monaco-typescript/blob/master/LICENSE.md',
' *-----------------------------------------------------------------------------*/',
''
].join('\n');
function bundleOne(moduleId, exclude) {
return rjs({
baseUrl: '/out/',
name: 'vs/language/css/' + moduleId,
out: moduleId + '.js',
exclude: exclude,
paths: {
'vs/language/css': __dirname + '/out',
'vscode-css-languageservice/lib': __dirname + '/node_modules/vscode-css-languageservice/lib',
'vscode-languageserver-types/lib': __dirname + '/node_modules/vscode-languageserver-types/lib'
}
})
}
return merge(
merge(
bundleOne('monaco.contribution'),
bundleOne('mode'),
bundleOne('worker')
)
.pipe(uglify({
preserveComments: 'some'
}))
.pipe(es.through(function(data) {
data.contents = new Buffer(
BUNDLED_FILE_HEADER
+ data.contents.toString()
);
this.emit('data', data);
}))
.pipe(gulp.dest('./release/')),
gulp.src('src/monaco.d.ts').pipe(gulp.dest('./release/'))
);
});
var compilation = tsb.create(assign({ verbose: true }, require('./src/tsconfig.json').compilerOptions));
var tsSources = 'src/**/*.ts';
function compileTask() {
return merge(
gulp.src(tsSources).pipe(compilation())
)
.pipe(gulp.dest('out'));
}
gulp.task('clean-out', function(cb) { rimraf('out', { maxBusyTries: 1 }, cb); });
gulp.task('compile', ['clean-out'], compileTask);
gulp.task('compile-without-clean', compileTask);
gulp.task('watch', ['compile'], function() {
gulp.watch(tsSources, ['compile-without-clean']);
});
/**
* Escape text such that it can be used in a javascript string enclosed by double quotes (")
*/
function escapeText(text) {
// http://www.javascriptkit.com/jsref/escapesequence.shtml
// \b Backspace.
// \f Form feed.
// \n Newline.
// \O Nul character.
// \r Carriage return.
// \t Horizontal tab.
// \v Vertical tab.
// \' Single quote or apostrophe.
// \" Double quote.
// \\ Backslash.
// \ddd The Latin-1 character specified by the three octal digits between 0 and 377. ie, copyright symbol is \251.
// \xdd The Latin-1 character specified by the two hexadecimal digits dd between 00 and FF. ie, copyright symbol is \xA9.
// \udddd The Unicode character specified by the four hexadecimal digits dddd. ie, copyright symbol is \u00A9.
var _backspace = '\b'.charCodeAt(0);
var _formFeed = '\f'.charCodeAt(0);
var _newLine = '\n'.charCodeAt(0);
var _nullChar = 0;
var _carriageReturn = '\r'.charCodeAt(0);
var _tab = '\t'.charCodeAt(0);
var _verticalTab = '\v'.charCodeAt(0);
var _backslash = '\\'.charCodeAt(0);
var _doubleQuote = '"'.charCodeAt(0);
var startPos = 0, chrCode, replaceWith = null, resultPieces = [];
for (var i = 0, len = text.length; i < len; i++) {
chrCode = text.charCodeAt(i);
switch (chrCode) {
case _backspace:
replaceWith = '\\b';
break;
case _formFeed:
replaceWith = '\\f';
break;
case _newLine:
replaceWith = '\\n';
break;
case _nullChar:
replaceWith = '\\0';
break;
case _carriageReturn:
replaceWith = '\\r';
break;
case _tab:
replaceWith = '\\t';
break;
case _verticalTab:
replaceWith = '\\v';
break;
case _backslash:
replaceWith = '\\\\';
break;
case _doubleQuote:
replaceWith = '\\"';
break;
}
if (replaceWith !== null) {
resultPieces.push(text.substring(startPos, i));
resultPieces.push(replaceWith);
startPos = i + 1;
replaceWith = null;
}
}
resultPieces.push(text.substring(startPos, len));
return resultPieces.join('');
}
function getGitVersion(repo) {
var git = path.join(repo, '.git');
var headPath = path.join(git, 'HEAD');
var head;
try {
head = fs.readFileSync(headPath, 'utf8').trim();
} catch (e) {
return void 0;
}
if (/^[0-9a-f]{40}$/i.test(head)) {
return head;
}
var refMatch = /^ref: (.*)$/.exec(head);
if (!refMatch) {
return void 0;
}
var ref = refMatch[1];
var refPath = path.join(git, ref);
try {
return fs.readFileSync(refPath, 'utf8').trim();
} catch (e) {
// noop
}
var packedRefsPath = path.join(git, 'packed-refs');
var refsRaw;
try {
refsRaw = fs.readFileSync(packedRefsPath, 'utf8').trim();
} catch (e) {
return void 0;
}
var refsRegex = /^([0-9a-f]{40})\s+(.+)$/gm;
var refsMatch;
var refs = {};
while (refsMatch = refsRegex.exec(refsRaw)) {
refs[refsMatch[2]] = refsMatch[1];
}
return refs[ref];
}

@ -0,0 +1,36 @@
{
"name": "monaco-css",
"version": "0.0.1",
"description": "CSS, LESS and SCSS support for Monaco Editor",
"scripts": {
"test": "node_modules/.bin/mocha",
"watch": "node_modules/.bin/gulp watch",
"prepublish": "node_modules/.bin/gulp release"
},
"author": "Microsoft Corporation",
"license": "MIT",
"repository": {
"type": "git",
"url": "https://github.com/Microsoft/monaco-css"
},
"bugs": {
"url": "https://github.com/Microsoft/monaco-css/issues"
},
"dependencies": {
"vscode-css-languageservice": "file:../vscode-css-languageservice",
"vscode-languageserver-types": "1.0.0"
},
"devDependencies": {
"event-stream": "^3.3.2",
"gulp": "^3.9.1",
"gulp-requirejs": "^0.1.3",
"gulp-tsb": "^1.10.4",
"gulp-uglify": "^1.5.3",
"merge-stream": "^1.0.0",
"mocha": "^2.5.3",
"monaco-editor-core": "^0.4.0",
"object-assign": "^4.1.0",
"rimraf": "^2.5.2",
"typescript": "1.8.10"
}
}

@ -0,0 +1,440 @@
/*---------------------------------------------------------------------------------------------
* Copyright (c) Microsoft Corporation. All rights reserved.
* Licensed under the MIT License. See License.txt in the project root for license information.
*--------------------------------------------------------------------------------------------*/
'use strict';
import {LanguageServiceDefaultsImpl} from './monaco.contribution';
import {CSSWorker} from './worker';
import * as cssService from 'vscode-css-languageservice/lib/cssLanguageService';
import * as ls from 'vscode-languageserver-types/lib/main';
import Uri = monaco.Uri;
import Position = monaco.Position;
import Range = monaco.Range;
import Thenable = monaco.Thenable;
import Promise = monaco.Promise;
import CancellationToken = monaco.CancellationToken;
import IDisposable = monaco.IDisposable;
// --- diagnostics --- ---
export class DiagnostcsAdapter {
private _disposables: IDisposable[] = [];
private _listener: { [uri: string]: IDisposable } = Object.create(null);
constructor(private _defaults: LanguageServiceDefaultsImpl, private _languageId: string,
private _worker: (first: Uri, ...more: Uri[]) => Promise<CSSWorker>
) {
const onModelAdd = (model: monaco.editor.IModel): void => {
let modeId = model.getModeId();
if (modeId !== this._languageId) {
return;
}
let handle: number;
this._listener[model.uri.toString()] = model.onDidChangeContent(() => {
clearTimeout(handle);
handle = setTimeout(() => this._doValidate(model.uri, modeId), 500);
});
this._doValidate(model.uri, modeId);
};
const onModelRemoved = (model: monaco.editor.IModel): void => {
delete this._listener[model.uri.toString()];
};
this._disposables.push(monaco.editor.onDidCreateModel(onModelAdd));
this._disposables.push(monaco.editor.onWillDisposeModel(onModelRemoved));
this._disposables.push(monaco.editor.onDidChangeModelLanguage(event => {
onModelRemoved(event.model);
onModelAdd(event.model);
}));
this._disposables.push({
dispose: () => {
for (let key in this._listener) {
this._listener[key].dispose();
}
}
});
monaco.editor.getModels().forEach(onModelAdd);
}
public dispose(): void {
this._disposables.forEach(d => d && d.dispose());
this._disposables = [];
}
private _doValidate(resource: Uri, languageId: string): void {
this._worker(resource).then(worker => {
return worker.doValidation(resource.toString());
}).then(diagnostics => {
const markers = diagnostics.map(d => toDiagnostics(resource, d));
monaco.editor.setModelMarkers(monaco.editor.getModel(resource), languageId, markers);
}).done(undefined, err => {
console.error(err);
});
}
}
function toSeverity(lsSeverity: number): monaco.Severity {
switch (lsSeverity) {
case ls.DiagnosticSeverity.Error: return monaco.Severity.Error;
case ls.DiagnosticSeverity.Warning: return monaco.Severity.Warning;
case ls.DiagnosticSeverity.Information:
case ls.DiagnosticSeverity.Hint:
default:
return monaco.Severity.Info;
}
}
function toDiagnostics(resource: Uri, diag: ls.Diagnostic): monaco.editor.IMarkerData {
let code = typeof diag.code === 'number' ? String(diag.code) : <string>diag.code;
return {
severity: toSeverity(diag.severity),
startLineNumber: diag.range.start.line + 1,
startColumn: diag.range.start.character + 1,
endLineNumber: diag.range.end.line + 1,
endColumn: diag.range.end.character + 1,
message: diag.message,
code: code,
source: diag.source
};
}
// --- suggest ------
function fromPosition(position: Position): ls.Position {
if (!position) {
return void 0;
}
return { character: position.column - 1, line: position.lineNumber - 1 };
}
function fromRange(range: Range): ls.Range {
if (!range) {
return void 0;
}
return { start: fromPosition(range.getStartPosition()), end: fromPosition(range.getEndPosition()) };
}
function toRange(range: ls.Range): Range {
if (!range) {
return void 0;
}
return new Range(range.start.line + 1, range.start.character + 1, range.end.line + 1, range.end.character + 1);
}
function toCompletionItemKind(kind: number): monaco.languages.CompletionItemKind {
let lsItemKind = ls.CompletionItemKind;
let mItemKind = monaco.languages.CompletionItemKind;
switch (kind) {
case lsItemKind.Text: return mItemKind.Text;
case lsItemKind.Method: return mItemKind.Method;
case lsItemKind.Function: return mItemKind.Function;
case lsItemKind.Constructor: return mItemKind.Constructor;
case lsItemKind.Field: return mItemKind.Field;
case lsItemKind.Variable: return mItemKind.Variable;
case lsItemKind.Class: return mItemKind.Class;
case lsItemKind.Interface: return mItemKind.Interface;
case lsItemKind.Module: return mItemKind.Module;
case lsItemKind.Property: return mItemKind.Property;
case lsItemKind.Unit: return mItemKind.Unit;
case lsItemKind.Value: return mItemKind.Value;
case lsItemKind.Enum: return mItemKind.Enum;
case lsItemKind.Keyword: return mItemKind.Keyword;
case lsItemKind.Snippet: return mItemKind.Snippet;
case lsItemKind.Color: return mItemKind.Color;
case lsItemKind.File: return mItemKind.File;
case lsItemKind.Reference: return mItemKind.Reference;
}
return mItemKind.Property;
}
function toTextEdit(textEdit: ls.TextEdit): monaco.editor.ISingleEditOperation {
if (!textEdit) {
return void 0;
}
return {
range: toRange(textEdit.range),
text: textEdit.newText
}
}
export class CompletionAdapter implements monaco.languages.CompletionItemProvider {
constructor(private _worker: (first: Uri, ...more: Uri[]) => Promise<CSSWorker>) {
}
public get triggerCharacters(): string[] {
return [' ', ':'];
}
provideCompletionItems(model: monaco.editor.IReadOnlyModel, position: Position, token: CancellationToken): Thenable<monaco.languages.CompletionList> {
const wordInfo = model.getWordUntilPosition(position);
const resource = model.uri;
return wireCancellationToken(token, this._worker(resource).then(worker => {
return worker.doComplete(resource.toString(), fromPosition(position));
}).then(info => {
if (!info) {
return;
}
let items: monaco.languages.CompletionItem[] = info.items.map(entry => {
return {
label: entry.insertText,
sortText: entry.sortText,
filterText: entry.filterText,
documentation: entry.documentation,
detail: entry.detail,
kind: toCompletionItemKind(entry.kind),
textEdit: toTextEdit(entry.textEdit)
};
});
return {
isIncomplete: info.isIncomplete,
items: items
};
}));
}
}
function toHTMLContentElements(contents: ls.MarkedString | ls.MarkedString[]): monaco.IHTMLContentElement[] {
if (!contents) {
return void 0;
}
let toHTMLContentElement = (ms: ls.MarkedString): monaco.IHTMLContentElement => {
if (typeof ms === 'string') {
return { text: ms };
}
return {
code: {
value: ms['value'],
language: ms['language']
}
};
};
if (Array.isArray(contents)) {
return (<ls.MarkedString[]>contents).map(toHTMLContentElement);
}
return [toHTMLContentElement(<ls.MarkedString>contents)];
}
// --- hover ------
export class HoverAdapter implements monaco.languages.HoverProvider {
constructor(private _worker: (first: Uri, ...more: Uri[]) => Promise<CSSWorker>) {
}
provideHover(model: monaco.editor.IReadOnlyModel, position: Position, token: CancellationToken): Thenable<monaco.languages.Hover> {
let resource = model.uri;
return wireCancellationToken(token, this._worker(resource).then(worker => {
return worker.doHover(resource.toString(), fromPosition(position));
}).then(info => {
if (!info) {
return;
}
return <monaco.languages.Hover>{
range: toRange(info.range),
htmlContent: toHTMLContentElements(info.contents)
};
}));
}
}
// --- occurrences ------
function toDocumentHighlightKind(kind: number): monaco.languages.DocumentHighlightKind {
switch (kind) {
case ls.DocumentHighlightKind.Read: return monaco.languages.DocumentHighlightKind.Read;
case ls.DocumentHighlightKind.Write: return monaco.languages.DocumentHighlightKind.Write;
case ls.DocumentHighlightKind.Text: return monaco.languages.DocumentHighlightKind.Text;
}
return monaco.languages.DocumentHighlightKind.Text;
}
export class DocumentHighlightAdapter implements monaco.languages.DocumentHighlightProvider {
constructor(private _worker: (first: Uri, ...more: Uri[]) => Promise<CSSWorker>) {
}
public provideDocumentHighlights(model: monaco.editor.IReadOnlyModel, position: Position, token: CancellationToken): Thenable<monaco.languages.DocumentHighlight[]> {
const resource = model.uri;
return wireCancellationToken(token, this._worker(resource).then(worker => {
return worker.findDocumentHighlights(resource.toString(), fromPosition(position))
}).then(entries => {
if (!entries) {
return;
}
return entries.map(entry => {
return <monaco.languages.DocumentHighlight>{
range: toRange(entry.range),
kind: toDocumentHighlightKind(entry.kind)
};
});
}));
}
}
// --- definition ------
function toLocation(location: ls.Location): monaco.languages.Location {
return {
uri: Uri.parse(location.uri),
range: toRange(location.range)
};
}
export class DefinitionAdapter {
constructor(private _worker: (first: Uri, ...more: Uri[]) => Promise<CSSWorker>) {
}
public provideDefinition(model: monaco.editor.IReadOnlyModel, position: Position, token: CancellationToken): Thenable<monaco.languages.Definition> {
const resource = model.uri;
return wireCancellationToken(token, this._worker(resource).then(worker => {
return worker.findDefinition(resource.toString(), fromPosition(position));
}).then(definition => {
if (!definition) {
return;
}
return [toLocation(definition)];
}));
}
}
// --- references ------
export class ReferenceAdapter implements monaco.languages.ReferenceProvider {
constructor(private _worker: (first: Uri, ...more: Uri[]) => Promise<CSSWorker>) {
}
provideReferences(model: monaco.editor.IReadOnlyModel, position: Position, context: monaco.languages.ReferenceContext, token: CancellationToken): Thenable<monaco.languages.Location[]> {
const resource = model.uri;
return wireCancellationToken(token, this._worker(resource).then(worker => {
return worker.findReferences(resource.toString(), fromPosition(position));
}).then(entries => {
if (!entries) {
return;
}
return entries.map(toLocation);
}));
}
}
// --- rename ------
function toWorkspaceEdit(edit: ls.WorkspaceEdit): monaco.languages.WorkspaceEdit {
if (!edit || !edit.changes) {
return void 0;
}
let resourceEdits : monaco.languages.IResourceEdit[] = [];
for (let uri in edit.changes) {
let edits = edit.changes[uri];
for (let e of edits) {
resourceEdits.push({resource: Uri.parse(uri), range: toRange(e.range), newText: e.newText });
}
}
return {
edits: resourceEdits
}
}
export class RenameAdapter implements monaco.languages.RenameProvider {
constructor(private _worker: (first: Uri, ...more: Uri[]) => Promise<CSSWorker>) {
}
provideRenameEdits(model: monaco.editor.IReadOnlyModel, position: Position, newName: string, token: CancellationToken): Thenable<monaco.languages.WorkspaceEdit> {
const resource = model.uri;
return wireCancellationToken(token, this._worker(resource).then(worker => {
return worker.doRename(resource.toString(), fromPosition(position), newName);
}).then(edit => {
return toWorkspaceEdit(edit);
}));
}
}
// --- outline ------
function toSymbolKind(kind: ls.SymbolKind): monaco.languages.SymbolKind {
let lsKind = ls.SymbolKind;
let mKind = monaco.languages.SymbolKind;
switch (kind) {
case lsKind.File: return mKind.Array;
case lsKind.Module: return mKind.Module;
case lsKind.Namespace: return mKind.Namespace;
case lsKind.Package: return mKind.Package;
case lsKind.Class: return mKind.Class;
case lsKind.Method: return mKind.Method;
case lsKind.Property: return mKind.Property;
case lsKind.Field: return mKind.Field;
case lsKind.Constructor: return mKind.Constructor;
case lsKind.Enum: return mKind.Enum;
case lsKind.Interface: return mKind.Interface;
case lsKind.Function: return mKind.Function;
case lsKind.Variable: return mKind.Variable;
case lsKind.Constant: return mKind.Constant;
case lsKind.String: return mKind.String;
case lsKind.Number: return mKind.Number;
case lsKind.Boolean: return mKind.Boolean;
case lsKind.Array: return mKind.Array;
}
return mKind.Function;
}
export class DocumentSymbolAdapter implements monaco.languages.DocumentSymbolProvider {
constructor(private _worker: (first: Uri, ...more: Uri[]) => Promise<CSSWorker>) {
}
public provideDocumentSymbols(model: monaco.editor.IReadOnlyModel, token: CancellationToken): Thenable<monaco.languages.SymbolInformation[]> {
const resource = model.uri;
return wireCancellationToken(token, this._worker(resource).then(worker => worker.findDocumentSymbols(resource.toString())).then(items => {
if (!items) {
return;
}
return items.map(item => ({
name: item.name,
containerName: item.containerName,
kind: toSymbolKind(item.kind),
location: toLocation(item.location)
}));
}));
}
}
/**
* Hook a cancellation token to a WinJS Promise
*/
function wireCancellationToken<T>(token: CancellationToken, promise: Promise<T>): Thenable<T> {
token.onCancellationRequested(() => promise.cancel());
return promise;
}

@ -0,0 +1,120 @@
/*---------------------------------------------------------------------------------------------
* Copyright (c) Microsoft Corporation. All rights reserved.
* Licensed under the MIT License. See License.txt in the project root for license information.
*--------------------------------------------------------------------------------------------*/
'use strict';
import {WorkerManager} from './workerManager';
import {CSSWorker} from './worker';
import {LanguageServiceDefaultsImpl} from './monaco.contribution';
import * as languageFeatures from './languageFeatures';
import Promise = monaco.Promise;
import Uri = monaco.Uri;
import IDisposable = monaco.IDisposable;
export function setupCSS(defaults:LanguageServiceDefaultsImpl): void {
const cssLanguageConfiguration: monaco.languages.LanguageConfiguration = {
wordPattern: /(#?-?\d*\.\d\w*%?)|((::|[@#.!:])?[\w-?]+%?)|::|[@#.!:]/g,
comments: {
blockComment: ['/*', '*/']
},
brackets: [
['{', '}'],
['[', ']'],
['(', ')']
],
autoClosingPairs: [
{ open: '{', close: '}' },
{ open: '[', close: ']' },
{ open: '(', close: ')' },
{ open: '"', close: '"', notIn: ['string'] },
{ open: '\'', close: '\'', notIn: ['string'] }
]
};
setupMode(
defaults,
'css',
cssLanguageConfiguration
);
}
export function setupLESS(defaults:LanguageServiceDefaultsImpl): void {
const lessLanguageConfiguration: monaco.languages.LanguageConfiguration = {
wordPattern: /(#?-?\d*\.\d\w*%?)|([@#!.:]?[\w-?]+%?)|[@#!.]/g,
comments: {
blockComment: ['/*', '*/'],
lineComment: '//'
},
brackets: [['{','}'], ['[',']'], ['(',')'], ['<','>']],
autoClosingPairs: [
{ open: '"', close: '"', notIn: ['string', 'comment'] },
{ open: '\'', close: '\'', notIn: ['string', 'comment'] },
{ open: '{', close: '}', notIn: ['string', 'comment'] },
{ open: '[', close: ']', notIn: ['string', 'comment'] },
{ open: '(', close: ')', notIn: ['string', 'comment'] },
{ open: '<', close: '>', notIn: ['string', 'comment'] },
]
};
setupMode(
defaults,
'less',
lessLanguageConfiguration
);
}
export function setupSCSS(defaults:LanguageServiceDefaultsImpl): void {
const scssLanguageConfiguration: monaco.languages.LanguageConfiguration = {
wordPattern: /(#?-?\d*\.\d\w*%?)|([@#!.:]?[\w-?]+%?)|[@#!.]/g,
comments: {
blockComment: ['/*', '*/'],
lineComment: '//'
},
brackets: [['{','}'], ['[',']'], ['(',')'], ['<','>']],
autoClosingPairs: [
{ open: '"', close: '"', notIn: ['string', 'comment'] },
{ open: '\'', close: '\'', notIn: ['string', 'comment'] },
{ open: '{', close: '}', notIn: ['string', 'comment'] },
{ open: '[', close: ']', notIn: ['string', 'comment'] },
{ open: '(', close: ')', notIn: ['string', 'comment'] },
{ open: '<', close: '>', notIn: ['string', 'comment'] },
]
};
setupMode(
defaults,
'scss',
scssLanguageConfiguration
);
}
function setupMode(defaults:LanguageServiceDefaultsImpl, modeId:string, languageConfiguration: monaco.languages.LanguageConfiguration): void {
let disposables: IDisposable[] = [];
const client = new WorkerManager(defaults);
disposables.push(client);
const worker = (first: Uri, ...more: Uri[]): Promise<CSSWorker> => {
return client.getLanguageServiceWorker(...[first].concat(more));
};
disposables.push(monaco.languages.registerCompletionItemProvider(modeId, new languageFeatures.CompletionAdapter(worker)));
disposables.push(monaco.languages.registerHoverProvider(modeId, new languageFeatures.HoverAdapter(worker)));
disposables.push(monaco.languages.registerDocumentHighlightProvider(modeId, new languageFeatures.DocumentHighlightAdapter(worker)));
disposables.push(monaco.languages.registerDefinitionProvider(modeId, new languageFeatures.DefinitionAdapter(worker)));
disposables.push(monaco.languages.registerReferenceProvider(modeId, new languageFeatures.ReferenceAdapter(worker)));
disposables.push(monaco.languages.registerDocumentSymbolProvider(modeId, new languageFeatures.DocumentSymbolAdapter(worker)));
disposables.push(monaco.languages.registerRenameProvider(modeId, new languageFeatures.RenameAdapter(worker)));
disposables.push(new languageFeatures.DiagnostcsAdapter(defaults, modeId, worker));
disposables.push(monaco.languages.setLanguageConfiguration(modeId, languageConfiguration));
}

@ -0,0 +1,113 @@
/*---------------------------------------------------------------------------------------------
* Copyright (c) Microsoft Corporation. All rights reserved.
* Licensed under the MIT License. See License.txt in the project root for license information.
*--------------------------------------------------------------------------------------------*/
'use strict';
import * as mode from './mode';
import Emitter = monaco.Emitter;
import IEvent = monaco.IEvent;
import IDisposable = monaco.IDisposable;
declare var require:<T>(moduleId:[string], callback:(module:T)=>void)=>void;
// --- CSS configuration and defaults ---------
export class LanguageServiceDefaultsImpl implements monaco.languages.css.LanguageServiceDefaults {
private _onDidChange = new Emitter<monaco.languages.css.LanguageServiceDefaults>();
private _diagnosticsOptions: monaco.languages.css.DiagnosticsOptions;
constructor(diagnosticsOptions: monaco.languages.css.DiagnosticsOptions) {
this.setDiagnosticsOptions(diagnosticsOptions);
}
get onDidChange(): IEvent<monaco.languages.css.LanguageServiceDefaults>{
return this._onDidChange.event;
}
get diagnosticsOptions(): monaco.languages.css.DiagnosticsOptions {
return this._diagnosticsOptions;
}
setDiagnosticsOptions(options: monaco.languages.css.DiagnosticsOptions): void {
this._diagnosticsOptions = options || Object.create(null);
this._onDidChange.fire(this);
}
}
const diagnosticDefault : monaco.languages.css.DiagnosticsOptions = {
validate: true,
lint: {
compatibleVendorPrefixes: 'ignore',
vendorPrefix: 'warning',
duplicateProperties: 'warning',
emptyRules: 'warning',
importStatement: 'ignore',
boxModel: 'ignore',
universalSelector: 'ignore',
zeroUnits: 'ignore',
fontFaceProperties: 'warning',
hexColorLength: 'error',
argumentsInColorFunction: 'error',
unknownProperties: 'warning',
ieHack: 'ignore',
unknownVendorSpecificProperties: 'ignore',
propertyIgnoredDueToDisplay: 'warning',
important: 'ignore',
float: 'ignore',
idSelector: 'ignore'
}
}
const cssDefaults = new LanguageServiceDefaultsImpl(diagnosticDefault);
const scssDefaults = new LanguageServiceDefaultsImpl(diagnosticDefault);
const lessDefaults = new LanguageServiceDefaultsImpl(diagnosticDefault);
// Export API
function createAPI(): typeof monaco.languages.css {
return {
cssDefaults: cssDefaults,
lessDefaults: lessDefaults,
scssDefaults: scssDefaults
}
}
monaco.languages.css = createAPI();
// --- Registration to monaco editor ---
function withMode(callback:(module:typeof mode)=>void): void {
require<typeof mode>(['vs/language/css/mode'], callback);
}
monaco.languages.register({
id: 'less',
extensions: ['.less'],
aliases: ['Less', 'less'],
mimetypes: ['text/x-less', 'text/less']
});
monaco.languages.onLanguage('less', () => {
withMode((mode) => mode.setupLESS(lessDefaults));
});
monaco.languages.register({
id: 'scss',
extensions: ['.scss'],
aliases: ['Sass', 'sass', 'scss'],
mimetypes: ['text/x-scss', 'text/scss']
});
monaco.languages.onLanguage('scss', () => {
withMode((mode) => mode.setupSCSS(scssDefaults));
});
monaco.languages.register({
id: 'css',
extensions: ['.css'],
aliases: ['CSS', 'css'],
mimetypes: ['text/css']
});
monaco.languages.onLanguage('css', () => {
withMode((mode) => mode.setupCSS(cssDefaults));
});

39
src/monaco.d.ts vendored

@ -0,0 +1,39 @@
/*---------------------------------------------------------------------------------------------
* Copyright (c) Microsoft Corporation. All rights reserved.
* Licensed under the MIT License. See License.txt in the project root for license information.
*--------------------------------------------------------------------------------------------*/
declare module monaco.languages.css {
export interface DiagnosticsOptions {
validate?: boolean;
lint?: {
compatibleVendorPrefixes?: 'ignore' | 'warning' | 'error',
vendorPrefix?: 'ignore' | 'warning' | 'error',
duplicateProperties?: 'ignore' | 'warning' | 'error',
emptyRules?: 'ignore' | 'warning' | 'error',
importStatement?: 'ignore' | 'warning' | 'error',
boxModel?: 'ignore' | 'warning' | 'error',
universalSelector?: 'ignore' | 'warning' | 'error',
zeroUnits?: 'ignore' | 'warning' | 'error',
fontFaceProperties?: 'ignore' | 'warning' | 'error',
hexColorLength?: 'ignore' | 'warning' | 'error',
argumentsInColorFunction?: 'ignore' | 'warning' | 'error',
unknownProperties?: 'ignore' | 'warning' | 'error',
ieHack?: 'ignore' | 'warning' | 'error',
unknownVendorSpecificProperties?: 'ignore' | 'warning' | 'error',
propertyIgnoredDueToDisplay?: 'ignore' | 'warning' | 'error',
important?: 'ignore' | 'warning' | 'error',
float?: 'ignore' | 'warning' | 'error',
idSelector?: 'ignore' | 'warning' | 'error'
}
}
export interface LanguageServiceDefaults {
onDidChange: IEvent<LanguageServiceDefaults>;
diagnosticsOptions: DiagnosticsOptions;
setDiagnosticsOptions(options: DiagnosticsOptions): void;
}
export var cssDefaults: LanguageServiceDefaults;
export var lessDefaults: LanguageServiceDefaults;
export var scssDefaults: LanguageServiceDefaults;
}

@ -0,0 +1,8 @@
{
"compilerOptions": {
"module": "umd",
"moduleResolution": "node",
"outDir": "../out",
"target": "es5"
}
}

@ -0,0 +1,5 @@
/*---------------------------------------------------------------------------------------------
* Copyright (c) Microsoft Corporation. All rights reserved.
* Licensed under the MIT License. See License.txt in the project root for license information.
*--------------------------------------------------------------------------------------------*/
/// <reference path='../../node_modules/monaco-editor-core/monaco.d.ts'/>

@ -0,0 +1,119 @@
/*---------------------------------------------------------------------------------------------
* Copyright (c) Microsoft Corporation. All rights reserved.
* Licensed under the MIT License. See License.txt in the project root for license information.
*--------------------------------------------------------------------------------------------*/
'use strict';
import Promise = monaco.Promise;
import * as cssService from 'vscode-css-languageservice/lib/cssLanguageService';
import * as ls from 'vscode-languageserver-types/lib/main';
export class CSSWorker {
// --- model sync -----------------------
private _languageService : cssService.LanguageService;
private _languageSettings: cssService.LanguageSettings;
private _languageId: string;
constructor(createData:ICreateData) {
this._languageSettings = createData.languageSettings;
this._languageId = createData.languageId;
switch (this._languageId) {
case 'css':
this._languageService = cssService.getCSSLanguageService();
break;
case 'less':
this._languageService = cssService.getLESSLanguageService();
break;
case 'scss':
this._languageService = cssService.getSCSSLanguageService();
break;
default:
throw new Error('Invalid language id: ' + this._languageId);
}
this._languageService.configure(this._languageSettings);
}
// --- language service host ---------------
doValidation(uri: string): Promise<ls.Diagnostic[]> {
let document = this._getTextDocument(uri);
let stylesheet = this._languageService.parseStylesheet(document);
let diagnostics = this._languageService.doValidation(document, stylesheet);
return Promise.as(diagnostics)
}
doComplete(uri: string, position: ls.Position): Promise<ls.CompletionList> {
let document = this._getTextDocument(uri);
let stylesheet = this._languageService.parseStylesheet(document);
let completions = this._languageService.doComplete(document, position, stylesheet);
return Promise.as(completions);
}
doHover(uri: string, position: ls.Position): Promise<ls.Hover> {
let document = this._getTextDocument(uri);
let stylesheet = this._languageService.parseStylesheet(document);
let hover = this._languageService.doHover(document, position, stylesheet);
return Promise.as(hover);
}
findDefinition(uri: string, position: ls.Position): Promise<ls.Location> {
let document = this._getTextDocument(uri);
let stylesheet = this._languageService.parseStylesheet(document);
let definition = this._languageService.findDefinition(document, position, stylesheet);
return Promise.as(definition);
}
findReferences(uri: string, position: ls.Position): Promise<ls.Location[]> {
let document = this._getTextDocument(uri);
let stylesheet = this._languageService.parseStylesheet(document);
let references = this._languageService.findReferences(document, position, stylesheet);
return Promise.as(references);
}
findDocumentHighlights(uri: string, position: ls.Position): Promise<ls.DocumentHighlight[]> {
let document = this._getTextDocument(uri);
let stylesheet = this._languageService.parseStylesheet(document);
let highlights = this._languageService.findDocumentHighlights(document, position, stylesheet);
return Promise.as(highlights);
}
findDocumentSymbols(uri: string): Promise<ls.SymbolInformation[]> {
let document = this._getTextDocument(uri);
let stylesheet = this._languageService.parseStylesheet(document);
let symbols = this._languageService.findDocumentSymbols(document, stylesheet);
return Promise.as(symbols);
}
doCodeActions(uri: string, range: ls.Range, context: ls.CodeActionContext): Promise<ls.Command[]> {
let document = this._getTextDocument(uri);
let stylesheet = this._languageService.parseStylesheet(document);
let actions = this._languageService.doCodeActions(document, range, context, stylesheet);
return Promise.as(actions);
}
findColorSymbols(uri: string): Promise<ls.Range[]> {
let document = this._getTextDocument(uri);
let stylesheet = this._languageService.parseStylesheet(document);
let colorSymbols = this._languageService.findColorSymbols(document, stylesheet);
return Promise.as(colorSymbols);
}
doRename(uri: string, position: ls.Position, newName: string): Promise<ls.WorkspaceEdit> {
let document = this._getTextDocument(uri);
let stylesheet = this._languageService.parseStylesheet(document);
let renames = this._languageService.doRename(document, position, newName, stylesheet);
return Promise.as(renames);
}
private _getTextDocument(uri:string): ls.TextDocument {
let models = monaco.worker.getMirrorModels();
for (let model of models) {
if (model.uri.toString() === uri) {
return ls.TextDocument.create(uri, this._languageId, model.version, model.getText());
}
}
return null;
}
}
export interface ICreateData {
languageId: string;
languageSettings: cssService.LanguageSettings;
}
export function create(createData:ICreateData): CSSWorker {
return new CSSWorker(createData);
}

@ -0,0 +1,103 @@
/*---------------------------------------------------------------------------------------------
* Copyright (c) Microsoft Corporation. All rights reserved.
* Licensed under the MIT License. See License.txt in the project root for license information.
*--------------------------------------------------------------------------------------------*/
'use strict';
import {LanguageServiceDefaultsImpl} from './monaco.contribution';
import {CSSWorker} from './worker';
import Promise = monaco.Promise;
import IDisposable = monaco.IDisposable;
import Uri = monaco.Uri;
const STOP_WHEN_IDLE_FOR = 2 * 60 * 1000; // 2min
export class WorkerManager {
private _defaults: LanguageServiceDefaultsImpl;
private _idleCheckInterval: number;
private _lastUsedTime: number;
private _configChangeListener: IDisposable;
private _worker: monaco.editor.MonacoWebWorker<CSSWorker>;
private _client: Promise<CSSWorker>;
constructor(defaults: LanguageServiceDefaultsImpl) {
this._defaults = defaults;
this._worker = null;
this._idleCheckInterval = setInterval(() => this._checkIfIdle(), 30 * 1000);
this._lastUsedTime = 0;
this._configChangeListener = this._defaults.onDidChange(() => this._stopWorker());
}
private _stopWorker(): void {
if (this._worker) {
this._worker.dispose();
this._worker = null;
}
this._client = null;
}
dispose(): void {
clearInterval(this._idleCheckInterval);
this._configChangeListener.dispose();
this._stopWorker();
}
private _checkIfIdle(): void {
if (!this._worker) {
return;
}
let timePassedSinceLastUsed = Date.now() - this._lastUsedTime;
if (timePassedSinceLastUsed > STOP_WHEN_IDLE_FOR) {
this._stopWorker();
}
}
private _getClient(): Promise<CSSWorker> {
this._lastUsedTime = Date.now();
if (!this._client) {
this._worker = monaco.editor.createWebWorker<CSSWorker>({
// module that exports the create() method and returns a `CSSWorker` instance
moduleId: 'vs/language/css/src/worker',
// passed in to the create() method
createData: {
languageSettings: this._defaults.diagnosticsOptions
}
});
this._client = this._worker.getProxy();
}
return this._client;
}
getLanguageServiceWorker(...resources: Uri[]): Promise<CSSWorker> {
let _client:CSSWorker;
return toShallowCancelPromise(
this._getClient().then((client) => {
_client = client
}).then(_ => {
return this._worker.withSyncedResources(resources)
}).then(_ => _client)
);
}
}
function toShallowCancelPromise<T>(p:Promise<T>): Promise<T> {
let completeCallback: (value:T)=>void;
let errorCallback: (err:any)=>void;
let r = new Promise<T>((c, e) => {
completeCallback = c;
errorCallback = e;
}, () => { });
p.then(completeCallback, errorCallback);
return r;
}
Loading…
Cancel
Save