From 5ebacc725fdbdbe7b15d72a0ea0c1155c0d7760a Mon Sep 17 00:00:00 2001 From: Alex Dima Date: Mon, 7 Sep 2020 15:48:33 +0200 Subject: [PATCH] Run prettier --- .vscode/settings.json | 2 +- LICENSE.md | 42 +- README.md | 72 +- scripts/bundle.js | 118 +- scripts/dts.js | 5 +- scripts/release.js | 14 +- src/css.worker.ts | 2 +- src/cssMode.ts | 213 +-- src/cssWorker.ts | 364 +++-- src/fillers/monaco-editor-core-amd.ts | 2 +- src/fillers/vscode-nls.ts | 8 +- src/languageFeatures.ts | 1330 +++++++++++-------- src/monaco.contribution.ts | 420 +++--- src/tsconfig.esm.json | 28 +- src/tsconfig.json | 28 +- src/workerManager.ts | 178 +-- test/index.html | 1758 +++++++++++++------------ 17 files changed, 2479 insertions(+), 2105 deletions(-) diff --git a/.vscode/settings.json b/.vscode/settings.json index 1dc255b6..e25fee75 100644 --- a/.vscode/settings.json +++ b/.vscode/settings.json @@ -6,4 +6,4 @@ "**/release": true, "**/out": true } -} \ No newline at end of file +} diff --git a/LICENSE.md b/LICENSE.md index f8a94f6e..5ae193c9 100644 --- a/LICENSE.md +++ b/LICENSE.md @@ -1,21 +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. \ No newline at end of file +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. diff --git a/README.md b/README.md index a264ba76..6b4e68bc 100644 --- a/README.md +++ b/README.md @@ -1,35 +1,37 @@ -# Monaco CSS - -CSS language plugin for the Monaco Editor. It provides the following features when editing CSS, LESS and SCSS files: -* Code completion -* Hovers -* Validation: Syntax errors and linting -* Find definition, references & highlights for symbols in the same file -* Document Symbols -* Color Decorators - -Linting an be configured through the API. See [here](https://github.com/Microsoft/monaco-css/blob/master/src/monaco.d.ts) for the API that the -CSS plugin offers to configure the CSS/LESS/SCSS language support. - -Internally the CSS plugin uses the [vscode-css-languageservice](https://github.com/Microsoft/vscode-css-languageservice) -node module, providing the implementation of the functionally listed above. The same module is also used -in [Visual Studio Code](https://github.com/Microsoft/vscode) to power the CSS, LESS and SCSS editing experience. - -## Issues - -Please file issues concering `monaco-css` in the [`monaco-editor` repository](https://github.com/Microsoft/monaco-editor/issues). - -## Installing - -This npm module is bundled and distributed in the [monaco-editor](https://www.npmjs.com/package/monaco-editor) npm module. - -## Development - -* `npm install .` -* compile with `npm run compile` -* watch with `npm run watch` -* `npm run prepublishOnly` -* open `$/monaco-css/test/index.html` in your favorite browser. - -## License -[MIT](https://github.com/Microsoft/monaco-css/blob/master/LICENSE.md) +# Monaco CSS + +CSS language plugin for the Monaco Editor. It provides the following features when editing CSS, LESS and SCSS files: + +- Code completion +- Hovers +- Validation: Syntax errors and linting +- Find definition, references & highlights for symbols in the same file +- Document Symbols +- Color Decorators + +Linting an be configured through the API. See [here](https://github.com/Microsoft/monaco-css/blob/master/src/monaco.d.ts) for the API that the +CSS plugin offers to configure the CSS/LESS/SCSS language support. + +Internally the CSS plugin uses the [vscode-css-languageservice](https://github.com/Microsoft/vscode-css-languageservice) +node module, providing the implementation of the functionally listed above. The same module is also used +in [Visual Studio Code](https://github.com/Microsoft/vscode) to power the CSS, LESS and SCSS editing experience. + +## Issues + +Please file issues concering `monaco-css` in the [`monaco-editor` repository](https://github.com/Microsoft/monaco-editor/issues). + +## Installing + +This npm module is bundled and distributed in the [monaco-editor](https://www.npmjs.com/package/monaco-editor) npm module. + +## Development + +- `npm install .` +- compile with `npm run compile` +- watch with `npm run watch` +- `npm run prepublishOnly` +- open `$/monaco-css/test/index.html` in your favorite browser. + +## License + +[MIT](https://github.com/Microsoft/monaco-css/blob/master/LICENSE.md) diff --git a/scripts/bundle.js b/scripts/bundle.js index ead657a5..36ec957d 100644 --- a/scripts/bundle.js +++ b/scripts/bundle.js @@ -1,7 +1,7 @@ const requirejs = require('requirejs'); const path = require('path'); const fs = require('fs'); -const Terser = require("terser"); +const Terser = require('terser'); const helpers = require('monaco-plugin-helpers'); const REPO_ROOT = path.resolve(__dirname, '..'); @@ -25,51 +25,77 @@ bundleOne('cssMode', ['vs/language/css/monaco.contribution']); bundleOne('cssWorker'); function bundleOne(moduleId, exclude) { - requirejs.optimize({ - baseUrl: 'out/amd/', - name: 'vs/language/css/' + moduleId, - out: 'release/dev/' + moduleId + '.js', - exclude: exclude, - paths: { - 'vs/language/css': REPO_ROOT + '/out/amd', - 'vs/language/css/fillers/monaco-editor-core': + requirejs.optimize( + { + baseUrl: 'out/amd/', + name: 'vs/language/css/' + moduleId, + out: 'release/dev/' + moduleId + '.js', + exclude: exclude, + paths: { + 'vs/language/css': REPO_ROOT + '/out/amd', + 'vs/language/css/fillers/monaco-editor-core': REPO_ROOT + '/out/amd/fillers/monaco-editor-core-amd' + }, + optimize: 'none', + packages: [ + { + name: 'vscode-css-languageservice', + location: path.join( + REPO_ROOT, + 'node_modules/vscode-css-languageservice/lib/umd' + ), + main: 'cssLanguageService' + }, + { + name: 'vscode-languageserver-types', + location: path.join( + REPO_ROOT, + 'node_modules/vscode-languageserver-types/lib/umd' + ), + main: 'main' + }, + { + name: 'vscode-languageserver-textdocument', + location: path.join( + REPO_ROOT, + 'node_modules/vscode-languageserver-textdocument/lib/umd' + ), + main: 'main' + }, + { + name: 'vscode-uri', + location: path.join(REPO_ROOT, 'node_modules/vscode-uri/lib/umd'), + main: 'index' + }, + { + name: 'vscode-nls', + location: path.join(REPO_ROOT, '/out/amd/fillers'), + main: 'vscode-nls' + } + ] }, - optimize: 'none', - packages: [{ - name: 'vscode-css-languageservice', - location: path.join(REPO_ROOT, 'node_modules/vscode-css-languageservice/lib/umd'), - main: 'cssLanguageService' - }, { - name: 'vscode-languageserver-types', - location: path.join(REPO_ROOT, 'node_modules/vscode-languageserver-types/lib/umd'), - main: 'main' - }, { - name: 'vscode-languageserver-textdocument', - location: path.join(REPO_ROOT, 'node_modules/vscode-languageserver-textdocument/lib/umd'), - main: 'main' - }, { - name: 'vscode-uri', - location: path.join(REPO_ROOT, 'node_modules/vscode-uri/lib/umd'), - main: 'index' - }, { - name: 'vscode-nls', - location: path.join(REPO_ROOT, '/out/amd/fillers'), - main: 'vscode-nls' - }] - }, async function (buildResponse) { - const devFilePath = path.join(REPO_ROOT, 'release/dev/' + moduleId + '.js'); - const minFilePath = path.join(REPO_ROOT, 'release/min/' + moduleId + '.js'); - const fileContents = fs.readFileSync(devFilePath).toString(); - console.log(); - console.log(`Minifying ${devFilePath}...`); - const result = await Terser.minify(fileContents, { - output: { - comments: 'some' - } - }); - console.log(`Done minifying ${devFilePath}.`); - try { fs.mkdirSync(path.join(REPO_ROOT, 'release/min')) } catch (err) { } - fs.writeFileSync(minFilePath, BUNDLED_FILE_HEADER + result.code); - }) + async function (buildResponse) { + const devFilePath = path.join( + REPO_ROOT, + 'release/dev/' + moduleId + '.js' + ); + const minFilePath = path.join( + REPO_ROOT, + 'release/min/' + moduleId + '.js' + ); + const fileContents = fs.readFileSync(devFilePath).toString(); + console.log(); + console.log(`Minifying ${devFilePath}...`); + const result = await Terser.minify(fileContents, { + output: { + comments: 'some' + } + }); + console.log(`Done minifying ${devFilePath}.`); + try { + fs.mkdirSync(path.join(REPO_ROOT, 'release/min')); + } catch (err) {} + fs.writeFileSync(minFilePath, BUNDLED_FILE_HEADER + result.code); + } + ); } diff --git a/scripts/dts.js b/scripts/dts.js index 950479d6..319da6c5 100644 --- a/scripts/dts.js +++ b/scripts/dts.js @@ -10,7 +10,10 @@ const REPO_ROOT = path.join(__dirname, '../'); const SRC_PATH = path.join(REPO_ROOT, 'out/amd/monaco.contribution.d.ts'); const DST_PATH = path.join(REPO_ROOT, 'monaco.d.ts'); -const lines = fs.readFileSync(SRC_PATH).toString().split(/\r\n|\r|\n/); +const lines = fs + .readFileSync(SRC_PATH) + .toString() + .split(/\r\n|\r|\n/); let result = [ `/*---------------------------------------------------------------------------------------------`, ` * Copyright (c) Microsoft Corporation. All rights reserved.`, diff --git a/scripts/release.js b/scripts/release.js index e3d4a85f..67bf82f2 100644 --- a/scripts/release.js +++ b/scripts/release.js @@ -12,19 +12,13 @@ helpers.packageESM({ repoRoot: REPO_ROOT, esmSource: 'out/esm', esmDestination: 'release/esm', - entryPoints: [ - 'monaco.contribution.js', - 'cssMode.js', - 'css.worker.js', - ], + entryPoints: ['monaco.contribution.js', 'cssMode.js', 'css.worker.js'], resolveAlias: { - 'vscode-nls': path.join(REPO_ROOT, "out/esm/fillers/vscode-nls.js") + 'vscode-nls': path.join(REPO_ROOT, 'out/esm/fillers/vscode-nls.js') }, - resolveSkip: [ - 'monaco-editor-core' - ], + resolveSkip: ['monaco-editor-core'], destinationFolderSimplification: { - 'node_modules': '_deps', + node_modules: '_deps', 'vscode-languageserver-types/lib/esm': 'vscode-languageserver-types', 'vscode-uri/lib/esm': 'vscode-uri', 'vscode-css-languageservice/lib/esm': 'vscode-css-languageservice' diff --git a/src/css.worker.ts b/src/css.worker.ts index 16945611..ececcade 100644 --- a/src/css.worker.ts +++ b/src/css.worker.ts @@ -9,6 +9,6 @@ import { CSSWorker } from './cssWorker'; self.onmessage = () => { // ignore the first message worker.initialize((ctx, createData) => { - return new CSSWorker(ctx, createData) + return new CSSWorker(ctx, createData); }); }; diff --git a/src/cssMode.ts b/src/cssMode.ts index db76ecdc..5fcd6e41 100644 --- a/src/cssMode.ts +++ b/src/cssMode.ts @@ -1,81 +1,132 @@ -/*--------------------------------------------------------------------------------------------- - * Copyright (c) Microsoft Corporation. All rights reserved. - * Licensed under the MIT License. See License.txt in the project root for license information. - *--------------------------------------------------------------------------------------------*/ - -import { WorkerManager } from './workerManager'; -import type { CSSWorker } from './cssWorker'; -import { LanguageServiceDefaults } from './monaco.contribution'; -import * as languageFeatures from './languageFeatures'; -import { Uri, IDisposable, languages } from './fillers/monaco-editor-core'; - -export function setupMode(defaults: LanguageServiceDefaults): IDisposable { - - const disposables: IDisposable[] = []; - const providers: IDisposable[] = []; - - const client = new WorkerManager(defaults); - disposables.push(client); - - const worker: languageFeatures.WorkerAccessor = (...uris: Uri[]): Promise => { - return client.getLanguageServiceWorker(...uris); - }; - - - function registerProviders(): void { - const { languageId, modeConfiguration } = defaults; - - disposeAll(providers); - - if (modeConfiguration.completionItems) { - providers.push(languages.registerCompletionItemProvider(languageId, new languageFeatures.CompletionAdapter(worker))); - } - if (modeConfiguration.hovers) { - providers.push(languages.registerHoverProvider(languageId, new languageFeatures.HoverAdapter(worker))); - } - if (modeConfiguration.documentHighlights) { - providers.push(languages.registerDocumentHighlightProvider(languageId, new languageFeatures.DocumentHighlightAdapter(worker))); - } - if (modeConfiguration.definitions) { - providers.push(languages.registerDefinitionProvider(languageId, new languageFeatures.DefinitionAdapter(worker))); - } - if (modeConfiguration.references) { - providers.push(languages.registerReferenceProvider(languageId, new languageFeatures.ReferenceAdapter(worker))); - } - if (modeConfiguration.documentSymbols) { - providers.push(languages.registerDocumentSymbolProvider(languageId, new languageFeatures.DocumentSymbolAdapter(worker))); - } - if (modeConfiguration.rename) { - providers.push(languages.registerRenameProvider(languageId, new languageFeatures.RenameAdapter(worker))); - } - if (modeConfiguration.colors) { - providers.push(languages.registerColorProvider(languageId, new languageFeatures.DocumentColorAdapter(worker))); - } - if (modeConfiguration.foldingRanges) { - providers.push(languages.registerFoldingRangeProvider(languageId, new languageFeatures.FoldingRangeAdapter(worker))); - } - if (modeConfiguration.diagnostics) { - providers.push(new languageFeatures.DiagnosticsAdapter(languageId, worker, defaults)); - } - if (modeConfiguration.selectionRanges) { - providers.push(languages.registerSelectionRangeProvider(languageId, new languageFeatures.SelectionRangeAdapter(worker))); - } - } - - registerProviders(); - - - disposables.push(asDisposable(providers)); - - return asDisposable(disposables); -} - -function asDisposable(disposables: IDisposable[]): IDisposable { - return { dispose: () => disposeAll(disposables) }; -} - -function disposeAll(disposables: IDisposable[]) { - while (disposables.length) { - disposables.pop().dispose(); - } -} +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the MIT License. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ + +import { WorkerManager } from './workerManager'; +import type { CSSWorker } from './cssWorker'; +import { LanguageServiceDefaults } from './monaco.contribution'; +import * as languageFeatures from './languageFeatures'; +import { Uri, IDisposable, languages } from './fillers/monaco-editor-core'; + +export function setupMode(defaults: LanguageServiceDefaults): IDisposable { + const disposables: IDisposable[] = []; + const providers: IDisposable[] = []; + + const client = new WorkerManager(defaults); + disposables.push(client); + + const worker: languageFeatures.WorkerAccessor = ( + ...uris: Uri[] + ): Promise => { + return client.getLanguageServiceWorker(...uris); + }; + + function registerProviders(): void { + const { languageId, modeConfiguration } = defaults; + + disposeAll(providers); + + if (modeConfiguration.completionItems) { + providers.push( + languages.registerCompletionItemProvider( + languageId, + new languageFeatures.CompletionAdapter(worker) + ) + ); + } + if (modeConfiguration.hovers) { + providers.push( + languages.registerHoverProvider( + languageId, + new languageFeatures.HoverAdapter(worker) + ) + ); + } + if (modeConfiguration.documentHighlights) { + providers.push( + languages.registerDocumentHighlightProvider( + languageId, + new languageFeatures.DocumentHighlightAdapter(worker) + ) + ); + } + if (modeConfiguration.definitions) { + providers.push( + languages.registerDefinitionProvider( + languageId, + new languageFeatures.DefinitionAdapter(worker) + ) + ); + } + if (modeConfiguration.references) { + providers.push( + languages.registerReferenceProvider( + languageId, + new languageFeatures.ReferenceAdapter(worker) + ) + ); + } + if (modeConfiguration.documentSymbols) { + providers.push( + languages.registerDocumentSymbolProvider( + languageId, + new languageFeatures.DocumentSymbolAdapter(worker) + ) + ); + } + if (modeConfiguration.rename) { + providers.push( + languages.registerRenameProvider( + languageId, + new languageFeatures.RenameAdapter(worker) + ) + ); + } + if (modeConfiguration.colors) { + providers.push( + languages.registerColorProvider( + languageId, + new languageFeatures.DocumentColorAdapter(worker) + ) + ); + } + if (modeConfiguration.foldingRanges) { + providers.push( + languages.registerFoldingRangeProvider( + languageId, + new languageFeatures.FoldingRangeAdapter(worker) + ) + ); + } + if (modeConfiguration.diagnostics) { + providers.push( + new languageFeatures.DiagnosticsAdapter(languageId, worker, defaults) + ); + } + if (modeConfiguration.selectionRanges) { + providers.push( + languages.registerSelectionRangeProvider( + languageId, + new languageFeatures.SelectionRangeAdapter(worker) + ) + ); + } + } + + registerProviders(); + + disposables.push(asDisposable(providers)); + + return asDisposable(disposables); +} + +function asDisposable(disposables: IDisposable[]): IDisposable { + return { dispose: () => disposeAll(disposables) }; +} + +function disposeAll(disposables: IDisposable[]) { + while (disposables.length) { + disposables.pop().dispose(); + } +} diff --git a/src/cssWorker.ts b/src/cssWorker.ts index bd8ddb29..dc6e193e 100644 --- a/src/cssWorker.ts +++ b/src/cssWorker.ts @@ -1,138 +1,226 @@ -/*--------------------------------------------------------------------------------------------- - * Copyright (c) Microsoft Corporation. All rights reserved. - * Licensed under the MIT License. See License.txt in the project root for license information. - *--------------------------------------------------------------------------------------------*/ - -import type { worker } from './fillers/monaco-editor-core'; -import * as cssService from 'vscode-css-languageservice'; - -export class CSSWorker { - - // --- model sync ----------------------- - - private _ctx: worker.IWorkerContext; - private _languageService: cssService.LanguageService; - private _languageSettings: cssService.LanguageSettings; - private _languageId: string; - - constructor(ctx: worker.IWorkerContext, createData: ICreateData) { - this._ctx = ctx; - 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 --------------- - - async doValidation(uri: string): Promise { - let document = this._getTextDocument(uri); - if (document) { - let stylesheet = this._languageService.parseStylesheet(document); - let diagnostics = this._languageService.doValidation(document, stylesheet); - return Promise.resolve(diagnostics) - } - return Promise.resolve([]); - } - async doComplete(uri: string, position: cssService.Position): Promise { - let document = this._getTextDocument(uri); - let stylesheet = this._languageService.parseStylesheet(document); - let completions = this._languageService.doComplete(document, position, stylesheet); - return Promise.resolve(completions); - } - async doHover(uri: string, position: cssService.Position): Promise { - let document = this._getTextDocument(uri); - let stylesheet = this._languageService.parseStylesheet(document); - let hover = this._languageService.doHover(document, position, stylesheet); - return Promise.resolve(hover); - } - async findDefinition(uri: string, position: cssService.Position): Promise { - let document = this._getTextDocument(uri); - let stylesheet = this._languageService.parseStylesheet(document); - let definition = this._languageService.findDefinition(document, position, stylesheet); - return Promise.resolve(definition); - } - async findReferences(uri: string, position: cssService.Position): Promise { - let document = this._getTextDocument(uri); - let stylesheet = this._languageService.parseStylesheet(document); - let references = this._languageService.findReferences(document, position, stylesheet); - return Promise.resolve(references); - } - async findDocumentHighlights(uri: string, position: cssService.Position): Promise { - let document = this._getTextDocument(uri); - let stylesheet = this._languageService.parseStylesheet(document); - let highlights = this._languageService.findDocumentHighlights(document, position, stylesheet); - return Promise.resolve(highlights); - } - async findDocumentSymbols(uri: string): Promise { - let document = this._getTextDocument(uri); - let stylesheet = this._languageService.parseStylesheet(document); - let symbols = this._languageService.findDocumentSymbols(document, stylesheet); - return Promise.resolve(symbols); - } - async doCodeActions(uri: string, range: cssService.Range, context: cssService.CodeActionContext): Promise { - let document = this._getTextDocument(uri); - let stylesheet = this._languageService.parseStylesheet(document); - let actions = this._languageService.doCodeActions(document, range, context, stylesheet); - return Promise.resolve(actions); - } - async findDocumentColors(uri: string): Promise { - let document = this._getTextDocument(uri); - let stylesheet = this._languageService.parseStylesheet(document); - let colorSymbols = this._languageService.findDocumentColors(document, stylesheet); - return Promise.resolve(colorSymbols); - } - async getColorPresentations(uri: string, color: cssService.Color, range: cssService.Range): Promise { - let document = this._getTextDocument(uri); - let stylesheet = this._languageService.parseStylesheet(document); - let colorPresentations = this._languageService.getColorPresentations(document, stylesheet, color, range); - return Promise.resolve(colorPresentations); - } - async getFoldingRanges(uri: string, context?: { rangeLimit?: number; }): Promise { - let document = this._getTextDocument(uri); - let ranges = this._languageService.getFoldingRanges(document, context); - return Promise.resolve(ranges); - } - async getSelectionRanges(uri: string, positions: cssService.Position[]): Promise { - let document = this._getTextDocument(uri); - let stylesheet = this._languageService.parseStylesheet(document); - let ranges = this._languageService.getSelectionRanges(document, positions, stylesheet); - return Promise.resolve(ranges); - } - async doRename(uri: string, position: cssService.Position, newName: string): Promise { - let document = this._getTextDocument(uri); - let stylesheet = this._languageService.parseStylesheet(document); - let renames = this._languageService.doRename(document, position, newName, stylesheet); - return Promise.resolve(renames); - } - private _getTextDocument(uri: string): cssService.TextDocument { - let models = this._ctx.getMirrorModels(); - for (let model of models) { - if (model.uri.toString() === uri) { - return cssService.TextDocument.create(uri, this._languageId, model.version, model.getValue()); - } - } - return null; - } -} - -export interface ICreateData { - languageId: string; - languageSettings: cssService.LanguageSettings; -} - -export function create(ctx: worker.IWorkerContext, createData: ICreateData): CSSWorker { - return new CSSWorker(ctx, createData); -} +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the MIT License. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ + +import type { worker } from './fillers/monaco-editor-core'; +import * as cssService from 'vscode-css-languageservice'; + +export class CSSWorker { + // --- model sync ----------------------- + + private _ctx: worker.IWorkerContext; + private _languageService: cssService.LanguageService; + private _languageSettings: cssService.LanguageSettings; + private _languageId: string; + + constructor(ctx: worker.IWorkerContext, createData: ICreateData) { + this._ctx = ctx; + 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 --------------- + + async doValidation(uri: string): Promise { + let document = this._getTextDocument(uri); + if (document) { + let stylesheet = this._languageService.parseStylesheet(document); + let diagnostics = this._languageService.doValidation( + document, + stylesheet + ); + return Promise.resolve(diagnostics); + } + return Promise.resolve([]); + } + async doComplete( + uri: string, + position: cssService.Position + ): Promise { + let document = this._getTextDocument(uri); + let stylesheet = this._languageService.parseStylesheet(document); + let completions = this._languageService.doComplete( + document, + position, + stylesheet + ); + return Promise.resolve(completions); + } + async doHover( + uri: string, + position: cssService.Position + ): Promise { + let document = this._getTextDocument(uri); + let stylesheet = this._languageService.parseStylesheet(document); + let hover = this._languageService.doHover(document, position, stylesheet); + return Promise.resolve(hover); + } + async findDefinition( + uri: string, + position: cssService.Position + ): Promise { + let document = this._getTextDocument(uri); + let stylesheet = this._languageService.parseStylesheet(document); + let definition = this._languageService.findDefinition( + document, + position, + stylesheet + ); + return Promise.resolve(definition); + } + async findReferences( + uri: string, + position: cssService.Position + ): Promise { + let document = this._getTextDocument(uri); + let stylesheet = this._languageService.parseStylesheet(document); + let references = this._languageService.findReferences( + document, + position, + stylesheet + ); + return Promise.resolve(references); + } + async findDocumentHighlights( + uri: string, + position: cssService.Position + ): Promise { + let document = this._getTextDocument(uri); + let stylesheet = this._languageService.parseStylesheet(document); + let highlights = this._languageService.findDocumentHighlights( + document, + position, + stylesheet + ); + return Promise.resolve(highlights); + } + async findDocumentSymbols( + uri: string + ): Promise { + let document = this._getTextDocument(uri); + let stylesheet = this._languageService.parseStylesheet(document); + let symbols = this._languageService.findDocumentSymbols( + document, + stylesheet + ); + return Promise.resolve(symbols); + } + async doCodeActions( + uri: string, + range: cssService.Range, + context: cssService.CodeActionContext + ): Promise { + let document = this._getTextDocument(uri); + let stylesheet = this._languageService.parseStylesheet(document); + let actions = this._languageService.doCodeActions( + document, + range, + context, + stylesheet + ); + return Promise.resolve(actions); + } + async findDocumentColors( + uri: string + ): Promise { + let document = this._getTextDocument(uri); + let stylesheet = this._languageService.parseStylesheet(document); + let colorSymbols = this._languageService.findDocumentColors( + document, + stylesheet + ); + return Promise.resolve(colorSymbols); + } + async getColorPresentations( + uri: string, + color: cssService.Color, + range: cssService.Range + ): Promise { + let document = this._getTextDocument(uri); + let stylesheet = this._languageService.parseStylesheet(document); + let colorPresentations = this._languageService.getColorPresentations( + document, + stylesheet, + color, + range + ); + return Promise.resolve(colorPresentations); + } + async getFoldingRanges( + uri: string, + context?: { rangeLimit?: number } + ): Promise { + let document = this._getTextDocument(uri); + let ranges = this._languageService.getFoldingRanges(document, context); + return Promise.resolve(ranges); + } + async getSelectionRanges( + uri: string, + positions: cssService.Position[] + ): Promise { + let document = this._getTextDocument(uri); + let stylesheet = this._languageService.parseStylesheet(document); + let ranges = this._languageService.getSelectionRanges( + document, + positions, + stylesheet + ); + return Promise.resolve(ranges); + } + async doRename( + uri: string, + position: cssService.Position, + newName: string + ): Promise { + let document = this._getTextDocument(uri); + let stylesheet = this._languageService.parseStylesheet(document); + let renames = this._languageService.doRename( + document, + position, + newName, + stylesheet + ); + return Promise.resolve(renames); + } + private _getTextDocument(uri: string): cssService.TextDocument { + let models = this._ctx.getMirrorModels(); + for (let model of models) { + if (model.uri.toString() === uri) { + return cssService.TextDocument.create( + uri, + this._languageId, + model.version, + model.getValue() + ); + } + } + return null; + } +} + +export interface ICreateData { + languageId: string; + languageSettings: cssService.LanguageSettings; +} + +export function create( + ctx: worker.IWorkerContext, + createData: ICreateData +): CSSWorker { + return new CSSWorker(ctx, createData); +} diff --git a/src/fillers/monaco-editor-core-amd.ts b/src/fillers/monaco-editor-core-amd.ts index b55612a0..59874efb 100644 --- a/src/fillers/monaco-editor-core-amd.ts +++ b/src/fillers/monaco-editor-core-amd.ts @@ -7,6 +7,6 @@ declare var define; -define([], function() { +define([], function () { return (self).monaco; }); diff --git a/src/fillers/vscode-nls.ts b/src/fillers/vscode-nls.ts index cfb4e55b..09cb22e0 100644 --- a/src/fillers/vscode-nls.ts +++ b/src/fillers/vscode-nls.ts @@ -33,7 +33,11 @@ function format(message: string, args: any[]): string { return result; } -function localize(key: string | LocalizeInfo, message: string, ...args: any[]): string { +function localize( + key: string | LocalizeInfo, + message: string, + ...args: any[] +): string { return format(message, args); } @@ -43,4 +47,4 @@ export function loadMessageBundle(file?: string): LocalizeFunc { export function config(opt?: Options | string): LoadFunc { return loadMessageBundle; -} \ No newline at end of file +} diff --git a/src/languageFeatures.ts b/src/languageFeatures.ts index 59d17f23..d5b2e19b 100644 --- a/src/languageFeatures.ts +++ b/src/languageFeatures.ts @@ -1,576 +1,754 @@ -/*--------------------------------------------------------------------------------------------- - * Copyright (c) Microsoft Corporation. All rights reserved. - * Licensed under the MIT License. See License.txt in the project root for license information. - *--------------------------------------------------------------------------------------------*/ - -import { LanguageServiceDefaults } from './monaco.contribution'; -import type { CSSWorker } from './cssWorker'; -import * as cssService from 'vscode-css-languageservice'; -import { languages, editor, IMarkdownString, Uri, Position, IRange, Range, CancellationToken, IDisposable, MarkerSeverity } from './fillers/monaco-editor-core'; - -export interface WorkerAccessor { - (first: Uri, ...more: Uri[]): Promise -} - -// --- diagnostics --- --- - -export class DiagnosticsAdapter { - - private _disposables: IDisposable[] = []; - private _listener: { [uri: string]: IDisposable } = Object.create(null); - - constructor(private _languageId: string, private _worker: WorkerAccessor, defaults: LanguageServiceDefaults) { - const onModelAdd = (model: editor.IModel): void => { - let modeId = model.getModeId(); - if (modeId !== this._languageId) { - return; - } - - let handle: number; - this._listener[model.uri.toString()] = model.onDidChangeContent(() => { - window.clearTimeout(handle); - handle = window.setTimeout(() => this._doValidate(model.uri, modeId), 500); - }); - - this._doValidate(model.uri, modeId); - }; - - const onModelRemoved = (model: editor.IModel): void => { - editor.setModelMarkers(model, this._languageId, []); - - let uriStr = model.uri.toString(); - let listener = this._listener[uriStr]; - if (listener) { - listener.dispose(); - delete this._listener[uriStr]; - } - }; - - this._disposables.push(editor.onDidCreateModel(onModelAdd)); - this._disposables.push(editor.onWillDisposeModel(onModelRemoved)); - this._disposables.push(editor.onDidChangeModelLanguage(event => { - onModelRemoved(event.model); - onModelAdd(event.model); - })); - - defaults.onDidChange(_ => { - editor.getModels().forEach(model => { - if (model.getModeId() === this._languageId) { - onModelRemoved(model); - onModelAdd(model); - } - }); - }); - - this._disposables.push({ - dispose: () => { - for (let key in this._listener) { - this._listener[key].dispose(); - } - } - }); - - 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)); - let model = editor.getModel(resource); - if (model.getModeId() === languageId) { - editor.setModelMarkers(model, languageId, markers); - } - }).then(undefined, err => { - console.error(err); - }); - } -} - - -function toSeverity(lsSeverity: number): MarkerSeverity { - switch (lsSeverity) { - case cssService.DiagnosticSeverity.Error: return MarkerSeverity.Error; - case cssService.DiagnosticSeverity.Warning: return MarkerSeverity.Warning; - case cssService.DiagnosticSeverity.Information: return MarkerSeverity.Info; - case cssService.DiagnosticSeverity.Hint: return MarkerSeverity.Hint; - default: - return MarkerSeverity.Info; - } -} - -function toDiagnostics(resource: Uri, diag: cssService.Diagnostic): editor.IMarkerData { - let code = typeof diag.code === 'number' ? String(diag.code) : 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 - }; -} - -// --- completion ------ - -function fromPosition(position: Position): cssService.Position { - if (!position) { - return void 0; - } - return { character: position.column - 1, line: position.lineNumber - 1 }; -} - -function fromRange(range: IRange): cssService.Range { - if (!range) { - return void 0; - } - return { start: { line: range.startLineNumber - 1, character: range.startColumn - 1 }, end: { line: range.endLineNumber - 1, character: range.endColumn - 1 } }; -} - -function toRange(range: cssService.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): languages.CompletionItemKind { - let mItemKind = languages.CompletionItemKind; - - switch (kind) { - case cssService.CompletionItemKind.Text: return mItemKind.Text; - case cssService.CompletionItemKind.Method: return mItemKind.Method; - case cssService.CompletionItemKind.Function: return mItemKind.Function; - case cssService.CompletionItemKind.Constructor: return mItemKind.Constructor; - case cssService.CompletionItemKind.Field: return mItemKind.Field; - case cssService.CompletionItemKind.Variable: return mItemKind.Variable; - case cssService.CompletionItemKind.Class: return mItemKind.Class; - case cssService.CompletionItemKind.Interface: return mItemKind.Interface; - case cssService.CompletionItemKind.Module: return mItemKind.Module; - case cssService.CompletionItemKind.Property: return mItemKind.Property; - case cssService.CompletionItemKind.Unit: return mItemKind.Unit; - case cssService.CompletionItemKind.Value: return mItemKind.Value; - case cssService.CompletionItemKind.Enum: return mItemKind.Enum; - case cssService.CompletionItemKind.Keyword: return mItemKind.Keyword; - case cssService.CompletionItemKind.Snippet: return mItemKind.Snippet; - case cssService.CompletionItemKind.Color: return mItemKind.Color; - case cssService.CompletionItemKind.File: return mItemKind.File; - case cssService.CompletionItemKind.Reference: return mItemKind.Reference; - } - return mItemKind.Property; -} - -function toTextEdit(textEdit: cssService.TextEdit): editor.ISingleEditOperation { - if (!textEdit) { - return void 0; - } - return { - range: toRange(textEdit.range), - text: textEdit.newText - } -} - -export class CompletionAdapter implements languages.CompletionItemProvider { - - constructor(private _worker: WorkerAccessor) { - } - - public get triggerCharacters(): string[] { - return [' ', ':']; - } - - provideCompletionItems(model: editor.IReadOnlyModel, position: Position, context: languages.CompletionContext, token: CancellationToken): Promise { - const resource = model.uri; - - return this._worker(resource).then(worker => { - return worker.doComplete(resource.toString(), fromPosition(position)); - }).then(info => { - if (!info) { - return; - } - const wordInfo = model.getWordUntilPosition(position); - const wordRange = new Range(position.lineNumber, wordInfo.startColumn, position.lineNumber, wordInfo.endColumn); - - let items: languages.CompletionItem[] = info.items.map(entry => { - let item: languages.CompletionItem = { - label: entry.label, - insertText: entry.insertText || entry.label, - sortText: entry.sortText, - filterText: entry.filterText, - documentation: entry.documentation, - detail: entry.detail, - range: wordRange, - kind: toCompletionItemKind(entry.kind), - }; - if (entry.textEdit) { - item.range = toRange(entry.textEdit.range); - item.insertText = entry.textEdit.newText; - } - if (entry.additionalTextEdits) { - item.additionalTextEdits = entry.additionalTextEdits.map(toTextEdit) - } - if (entry.insertTextFormat === cssService.InsertTextFormat.Snippet) { - item.insertTextRules = languages.CompletionItemInsertTextRule.InsertAsSnippet; - } - return item; - }); - - return { - isIncomplete: info.isIncomplete, - suggestions: items - }; - }); - } -} - - -function isMarkupContent(thing: any): thing is cssService.MarkupContent { - return thing && typeof thing === 'object' && typeof (thing).kind === 'string'; -} - -function toMarkdownString(entry: cssService.MarkupContent | cssService.MarkedString): IMarkdownString { - if (typeof entry === 'string') { - return { - value: entry - }; - } - if (isMarkupContent(entry)) { - if (entry.kind === 'plaintext') { - return { - value: entry.value.replace(/[\\`*_{}[\]()#+\-.!]/g, '\\$&') - }; - } - return { - value: entry.value - }; - } - - return { value: '```' + entry.language + '\n' + entry.value + '\n```\n' }; -} - -function toMarkedStringArray(contents: cssService.MarkupContent | cssService.MarkedString | cssService.MarkedString[]): IMarkdownString[] { - if (!contents) { - return void 0; - } - if (Array.isArray(contents)) { - return contents.map(toMarkdownString); - } - return [toMarkdownString(contents)]; -} - - -// --- hover ------ - -export class HoverAdapter implements languages.HoverProvider { - - constructor(private _worker: WorkerAccessor) { - } - - provideHover(model: editor.IReadOnlyModel, position: Position, token: CancellationToken): Promise { - let resource = model.uri; - - return this._worker(resource).then(worker => { - return worker.doHover(resource.toString(), fromPosition(position)); - }).then(info => { - if (!info) { - return; - } - return { - range: toRange(info.range), - contents: toMarkedStringArray(info.contents) - }; - }); - } -} - -// --- document highlights ------ - -function toDocumentHighlightKind(kind: number): languages.DocumentHighlightKind { - switch (kind) { - case cssService.DocumentHighlightKind.Read: return languages.DocumentHighlightKind.Read; - case cssService.DocumentHighlightKind.Write: return languages.DocumentHighlightKind.Write; - case cssService.DocumentHighlightKind.Text: return languages.DocumentHighlightKind.Text; - } - return languages.DocumentHighlightKind.Text; -} - - -export class DocumentHighlightAdapter implements languages.DocumentHighlightProvider { - - constructor(private _worker: WorkerAccessor) { - } - - public provideDocumentHighlights(model: editor.IReadOnlyModel, position: Position, token: CancellationToken): Promise { - const resource = model.uri; - - return this._worker(resource).then(worker => { - return worker.findDocumentHighlights(resource.toString(), fromPosition(position)) - }).then(entries => { - if (!entries) { - return; - } - return entries.map(entry => { - return { - range: toRange(entry.range), - kind: toDocumentHighlightKind(entry.kind) - }; - }); - }); - } -} - -// --- definition ------ - -function toLocation(location: cssService.Location): languages.Location { - return { - uri: Uri.parse(location.uri), - range: toRange(location.range) - }; -} - -export class DefinitionAdapter { - - constructor(private _worker: WorkerAccessor) { - } - - public provideDefinition(model: editor.IReadOnlyModel, position: Position, token: CancellationToken): Promise { - const resource = model.uri; - - return 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 languages.ReferenceProvider { - - constructor(private _worker: WorkerAccessor) { - } - - provideReferences(model: editor.IReadOnlyModel, position: Position, context: languages.ReferenceContext, token: CancellationToken): Promise { - const resource = model.uri; - - return 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: cssService.WorkspaceEdit): languages.WorkspaceEdit { - if (!edit || !edit.changes) { - return void 0; - } - let resourceEdits: languages.WorkspaceTextEdit[] = []; - for (let uri in edit.changes) { - const _uri = Uri.parse(uri); - // let edits: languages.TextEdit[] = []; - for (let e of edit.changes[uri]) { - resourceEdits.push({ - resource: _uri, - edit: { - range: toRange(e.range), - text: e.newText - } - }); - } - } - return { - edits: resourceEdits - } -} - - -export class RenameAdapter implements languages.RenameProvider { - - constructor(private _worker: WorkerAccessor) { - } - - provideRenameEdits(model: editor.IReadOnlyModel, position: Position, newName: string, token: CancellationToken): Promise { - const resource = model.uri; - - return this._worker(resource).then(worker => { - return worker.doRename(resource.toString(), fromPosition(position), newName); - }).then(edit => { - return toWorkspaceEdit(edit); - }); - } -} - -// --- document symbols ------ - -function toSymbolKind(kind: cssService.SymbolKind): languages.SymbolKind { - let mKind = languages.SymbolKind; - - switch (kind) { - case cssService.SymbolKind.File: return mKind.Array; - case cssService.SymbolKind.Module: return mKind.Module; - case cssService.SymbolKind.Namespace: return mKind.Namespace; - case cssService.SymbolKind.Package: return mKind.Package; - case cssService.SymbolKind.Class: return mKind.Class; - case cssService.SymbolKind.Method: return mKind.Method; - case cssService.SymbolKind.Property: return mKind.Property; - case cssService.SymbolKind.Field: return mKind.Field; - case cssService.SymbolKind.Constructor: return mKind.Constructor; - case cssService.SymbolKind.Enum: return mKind.Enum; - case cssService.SymbolKind.Interface: return mKind.Interface; - case cssService.SymbolKind.Function: return mKind.Function; - case cssService.SymbolKind.Variable: return mKind.Variable; - case cssService.SymbolKind.Constant: return mKind.Constant; - case cssService.SymbolKind.String: return mKind.String; - case cssService.SymbolKind.Number: return mKind.Number; - case cssService.SymbolKind.Boolean: return mKind.Boolean; - case cssService.SymbolKind.Array: return mKind.Array; - } - return mKind.Function; -} - - -export class DocumentSymbolAdapter implements languages.DocumentSymbolProvider { - - constructor(private _worker: WorkerAccessor) { - } - - public provideDocumentSymbols(model: editor.IReadOnlyModel, token: CancellationToken): Promise { - const resource = model.uri; - - return this._worker(resource).then(worker => worker.findDocumentSymbols(resource.toString())).then(items => { - if (!items) { - return; - } - return items.map(item => ({ - name: item.name, - detail: '', - containerName: item.containerName, - kind: toSymbolKind(item.kind), - tags: [], - range: toRange(item.location.range), - selectionRange: toRange(item.location.range) - })); - }); - } -} - -export class DocumentColorAdapter implements languages.DocumentColorProvider { - - constructor(private _worker: WorkerAccessor) { - } - - public provideDocumentColors(model: editor.IReadOnlyModel, token: CancellationToken): Promise { - const resource = model.uri; - - return this._worker(resource).then(worker => worker.findDocumentColors(resource.toString())).then(infos => { - if (!infos) { - return; - } - return infos.map(item => ({ - color: item.color, - range: toRange(item.range) - })); - }); - } - - public provideColorPresentations(model: editor.IReadOnlyModel, info: languages.IColorInformation, token: CancellationToken): Promise { - const resource = model.uri; - - return this._worker(resource).then(worker => worker.getColorPresentations(resource.toString(), info.color, fromRange(info.range))).then(presentations => { - if (!presentations) { - return; - } - return presentations.map(presentation => { - let item: languages.IColorPresentation = { - label: presentation.label, - }; - if (presentation.textEdit) { - item.textEdit = toTextEdit(presentation.textEdit) - } - if (presentation.additionalTextEdits) { - item.additionalTextEdits = presentation.additionalTextEdits.map(toTextEdit) - } - return item; - }); - }); - } -} - -export class FoldingRangeAdapter implements languages.FoldingRangeProvider { - - constructor(private _worker: WorkerAccessor) { - } - - public provideFoldingRanges(model: editor.IReadOnlyModel, context: languages.FoldingContext, token: CancellationToken): Promise { - const resource = model.uri; - - return this._worker(resource).then(worker => worker.getFoldingRanges(resource.toString(), context)).then(ranges => { - if (!ranges) { - return; - } - return ranges.map(range => { - let result: languages.FoldingRange = { - start: range.startLine + 1, - end: range.endLine + 1 - }; - if (typeof range.kind !== 'undefined') { - result.kind = toFoldingRangeKind(range.kind); - } - return result; - }); - }); - } - -} - -function toFoldingRangeKind(kind: cssService.FoldingRangeKind): languages.FoldingRangeKind { - switch (kind) { - case cssService.FoldingRangeKind.Comment: return languages.FoldingRangeKind.Comment; - case cssService.FoldingRangeKind.Imports: return languages.FoldingRangeKind.Imports; - case cssService.FoldingRangeKind.Region: return languages.FoldingRangeKind.Region; - } -} - -export class SelectionRangeAdapter implements languages.SelectionRangeProvider { - - constructor(private _worker: WorkerAccessor) { - } - - public provideSelectionRanges(model: editor.IReadOnlyModel, positions: Position[], token: CancellationToken): Promise { - const resource = model.uri; - - return this._worker(resource).then(worker => worker.getSelectionRanges(resource.toString(), positions.map(fromPosition))).then(selectionRanges => { - if (!selectionRanges) { - return; - } - return selectionRanges.map(selectionRange => { - const result: languages.SelectionRange[] = []; - while (selectionRange) { - result.push({ range: toRange(selectionRange.range) }); - selectionRange = selectionRange.parent; - } - return result; - }); - }); - } - -} +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the MIT License. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ + +import { LanguageServiceDefaults } from './monaco.contribution'; +import type { CSSWorker } from './cssWorker'; +import * as cssService from 'vscode-css-languageservice'; +import { + languages, + editor, + IMarkdownString, + Uri, + Position, + IRange, + Range, + CancellationToken, + IDisposable, + MarkerSeverity +} from './fillers/monaco-editor-core'; + +export interface WorkerAccessor { + (first: Uri, ...more: Uri[]): Promise; +} + +// --- diagnostics --- --- + +export class DiagnosticsAdapter { + private _disposables: IDisposable[] = []; + private _listener: { [uri: string]: IDisposable } = Object.create(null); + + constructor( + private _languageId: string, + private _worker: WorkerAccessor, + defaults: LanguageServiceDefaults + ) { + const onModelAdd = (model: editor.IModel): void => { + let modeId = model.getModeId(); + if (modeId !== this._languageId) { + return; + } + + let handle: number; + this._listener[model.uri.toString()] = model.onDidChangeContent(() => { + window.clearTimeout(handle); + handle = window.setTimeout( + () => this._doValidate(model.uri, modeId), + 500 + ); + }); + + this._doValidate(model.uri, modeId); + }; + + const onModelRemoved = (model: editor.IModel): void => { + editor.setModelMarkers(model, this._languageId, []); + + let uriStr = model.uri.toString(); + let listener = this._listener[uriStr]; + if (listener) { + listener.dispose(); + delete this._listener[uriStr]; + } + }; + + this._disposables.push(editor.onDidCreateModel(onModelAdd)); + this._disposables.push(editor.onWillDisposeModel(onModelRemoved)); + this._disposables.push( + editor.onDidChangeModelLanguage((event) => { + onModelRemoved(event.model); + onModelAdd(event.model); + }) + ); + + defaults.onDidChange((_) => { + editor.getModels().forEach((model) => { + if (model.getModeId() === this._languageId) { + onModelRemoved(model); + onModelAdd(model); + } + }); + }); + + this._disposables.push({ + dispose: () => { + for (let key in this._listener) { + this._listener[key].dispose(); + } + } + }); + + 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)); + let model = editor.getModel(resource); + if (model.getModeId() === languageId) { + editor.setModelMarkers(model, languageId, markers); + } + }) + .then(undefined, (err) => { + console.error(err); + }); + } +} + +function toSeverity(lsSeverity: number): MarkerSeverity { + switch (lsSeverity) { + case cssService.DiagnosticSeverity.Error: + return MarkerSeverity.Error; + case cssService.DiagnosticSeverity.Warning: + return MarkerSeverity.Warning; + case cssService.DiagnosticSeverity.Information: + return MarkerSeverity.Info; + case cssService.DiagnosticSeverity.Hint: + return MarkerSeverity.Hint; + default: + return MarkerSeverity.Info; + } +} + +function toDiagnostics( + resource: Uri, + diag: cssService.Diagnostic +): editor.IMarkerData { + let code = + typeof diag.code === 'number' ? String(diag.code) : 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 + }; +} + +// --- completion ------ + +function fromPosition(position: Position): cssService.Position { + if (!position) { + return void 0; + } + return { character: position.column - 1, line: position.lineNumber - 1 }; +} + +function fromRange(range: IRange): cssService.Range { + if (!range) { + return void 0; + } + return { + start: { + line: range.startLineNumber - 1, + character: range.startColumn - 1 + }, + end: { line: range.endLineNumber - 1, character: range.endColumn - 1 } + }; +} + +function toRange(range: cssService.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): languages.CompletionItemKind { + let mItemKind = languages.CompletionItemKind; + + switch (kind) { + case cssService.CompletionItemKind.Text: + return mItemKind.Text; + case cssService.CompletionItemKind.Method: + return mItemKind.Method; + case cssService.CompletionItemKind.Function: + return mItemKind.Function; + case cssService.CompletionItemKind.Constructor: + return mItemKind.Constructor; + case cssService.CompletionItemKind.Field: + return mItemKind.Field; + case cssService.CompletionItemKind.Variable: + return mItemKind.Variable; + case cssService.CompletionItemKind.Class: + return mItemKind.Class; + case cssService.CompletionItemKind.Interface: + return mItemKind.Interface; + case cssService.CompletionItemKind.Module: + return mItemKind.Module; + case cssService.CompletionItemKind.Property: + return mItemKind.Property; + case cssService.CompletionItemKind.Unit: + return mItemKind.Unit; + case cssService.CompletionItemKind.Value: + return mItemKind.Value; + case cssService.CompletionItemKind.Enum: + return mItemKind.Enum; + case cssService.CompletionItemKind.Keyword: + return mItemKind.Keyword; + case cssService.CompletionItemKind.Snippet: + return mItemKind.Snippet; + case cssService.CompletionItemKind.Color: + return mItemKind.Color; + case cssService.CompletionItemKind.File: + return mItemKind.File; + case cssService.CompletionItemKind.Reference: + return mItemKind.Reference; + } + return mItemKind.Property; +} + +function toTextEdit( + textEdit: cssService.TextEdit +): editor.ISingleEditOperation { + if (!textEdit) { + return void 0; + } + return { + range: toRange(textEdit.range), + text: textEdit.newText + }; +} + +export class CompletionAdapter implements languages.CompletionItemProvider { + constructor(private _worker: WorkerAccessor) {} + + public get triggerCharacters(): string[] { + return [' ', ':']; + } + + provideCompletionItems( + model: editor.IReadOnlyModel, + position: Position, + context: languages.CompletionContext, + token: CancellationToken + ): Promise { + const resource = model.uri; + + return this._worker(resource) + .then((worker) => { + return worker.doComplete(resource.toString(), fromPosition(position)); + }) + .then((info) => { + if (!info) { + return; + } + const wordInfo = model.getWordUntilPosition(position); + const wordRange = new Range( + position.lineNumber, + wordInfo.startColumn, + position.lineNumber, + wordInfo.endColumn + ); + + let items: languages.CompletionItem[] = info.items.map((entry) => { + let item: languages.CompletionItem = { + label: entry.label, + insertText: entry.insertText || entry.label, + sortText: entry.sortText, + filterText: entry.filterText, + documentation: entry.documentation, + detail: entry.detail, + range: wordRange, + kind: toCompletionItemKind(entry.kind) + }; + if (entry.textEdit) { + item.range = toRange(entry.textEdit.range); + item.insertText = entry.textEdit.newText; + } + if (entry.additionalTextEdits) { + item.additionalTextEdits = entry.additionalTextEdits.map( + toTextEdit + ); + } + if (entry.insertTextFormat === cssService.InsertTextFormat.Snippet) { + item.insertTextRules = + languages.CompletionItemInsertTextRule.InsertAsSnippet; + } + return item; + }); + + return { + isIncomplete: info.isIncomplete, + suggestions: items + }; + }); + } +} + +function isMarkupContent(thing: any): thing is cssService.MarkupContent { + return ( + thing && + typeof thing === 'object' && + typeof (thing).kind === 'string' + ); +} + +function toMarkdownString( + entry: cssService.MarkupContent | cssService.MarkedString +): IMarkdownString { + if (typeof entry === 'string') { + return { + value: entry + }; + } + if (isMarkupContent(entry)) { + if (entry.kind === 'plaintext') { + return { + value: entry.value.replace(/[\\`*_{}[\]()#+\-.!]/g, '\\$&') + }; + } + return { + value: entry.value + }; + } + + return { value: '```' + entry.language + '\n' + entry.value + '\n```\n' }; +} + +function toMarkedStringArray( + contents: + | cssService.MarkupContent + | cssService.MarkedString + | cssService.MarkedString[] +): IMarkdownString[] { + if (!contents) { + return void 0; + } + if (Array.isArray(contents)) { + return contents.map(toMarkdownString); + } + return [toMarkdownString(contents)]; +} + +// --- hover ------ + +export class HoverAdapter implements languages.HoverProvider { + constructor(private _worker: WorkerAccessor) {} + + provideHover( + model: editor.IReadOnlyModel, + position: Position, + token: CancellationToken + ): Promise { + let resource = model.uri; + + return this._worker(resource) + .then((worker) => { + return worker.doHover(resource.toString(), fromPosition(position)); + }) + .then((info) => { + if (!info) { + return; + } + return { + range: toRange(info.range), + contents: toMarkedStringArray(info.contents) + }; + }); + } +} + +// --- document highlights ------ + +function toDocumentHighlightKind( + kind: number +): languages.DocumentHighlightKind { + switch (kind) { + case cssService.DocumentHighlightKind.Read: + return languages.DocumentHighlightKind.Read; + case cssService.DocumentHighlightKind.Write: + return languages.DocumentHighlightKind.Write; + case cssService.DocumentHighlightKind.Text: + return languages.DocumentHighlightKind.Text; + } + return languages.DocumentHighlightKind.Text; +} + +export class DocumentHighlightAdapter + implements languages.DocumentHighlightProvider { + constructor(private _worker: WorkerAccessor) {} + + public provideDocumentHighlights( + model: editor.IReadOnlyModel, + position: Position, + token: CancellationToken + ): Promise { + const resource = model.uri; + + return this._worker(resource) + .then((worker) => { + return worker.findDocumentHighlights( + resource.toString(), + fromPosition(position) + ); + }) + .then((entries) => { + if (!entries) { + return; + } + return entries.map((entry) => { + return { + range: toRange(entry.range), + kind: toDocumentHighlightKind(entry.kind) + }; + }); + }); + } +} + +// --- definition ------ + +function toLocation(location: cssService.Location): languages.Location { + return { + uri: Uri.parse(location.uri), + range: toRange(location.range) + }; +} + +export class DefinitionAdapter { + constructor(private _worker: WorkerAccessor) {} + + public provideDefinition( + model: editor.IReadOnlyModel, + position: Position, + token: CancellationToken + ): Promise { + const resource = model.uri; + + return 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 languages.ReferenceProvider { + constructor(private _worker: WorkerAccessor) {} + + provideReferences( + model: editor.IReadOnlyModel, + position: Position, + context: languages.ReferenceContext, + token: CancellationToken + ): Promise { + const resource = model.uri; + + return 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: cssService.WorkspaceEdit +): languages.WorkspaceEdit { + if (!edit || !edit.changes) { + return void 0; + } + let resourceEdits: languages.WorkspaceTextEdit[] = []; + for (let uri in edit.changes) { + const _uri = Uri.parse(uri); + // let edits: languages.TextEdit[] = []; + for (let e of edit.changes[uri]) { + resourceEdits.push({ + resource: _uri, + edit: { + range: toRange(e.range), + text: e.newText + } + }); + } + } + return { + edits: resourceEdits + }; +} + +export class RenameAdapter implements languages.RenameProvider { + constructor(private _worker: WorkerAccessor) {} + + provideRenameEdits( + model: editor.IReadOnlyModel, + position: Position, + newName: string, + token: CancellationToken + ): Promise { + const resource = model.uri; + + return this._worker(resource) + .then((worker) => { + return worker.doRename( + resource.toString(), + fromPosition(position), + newName + ); + }) + .then((edit) => { + return toWorkspaceEdit(edit); + }); + } +} + +// --- document symbols ------ + +function toSymbolKind(kind: cssService.SymbolKind): languages.SymbolKind { + let mKind = languages.SymbolKind; + + switch (kind) { + case cssService.SymbolKind.File: + return mKind.Array; + case cssService.SymbolKind.Module: + return mKind.Module; + case cssService.SymbolKind.Namespace: + return mKind.Namespace; + case cssService.SymbolKind.Package: + return mKind.Package; + case cssService.SymbolKind.Class: + return mKind.Class; + case cssService.SymbolKind.Method: + return mKind.Method; + case cssService.SymbolKind.Property: + return mKind.Property; + case cssService.SymbolKind.Field: + return mKind.Field; + case cssService.SymbolKind.Constructor: + return mKind.Constructor; + case cssService.SymbolKind.Enum: + return mKind.Enum; + case cssService.SymbolKind.Interface: + return mKind.Interface; + case cssService.SymbolKind.Function: + return mKind.Function; + case cssService.SymbolKind.Variable: + return mKind.Variable; + case cssService.SymbolKind.Constant: + return mKind.Constant; + case cssService.SymbolKind.String: + return mKind.String; + case cssService.SymbolKind.Number: + return mKind.Number; + case cssService.SymbolKind.Boolean: + return mKind.Boolean; + case cssService.SymbolKind.Array: + return mKind.Array; + } + return mKind.Function; +} + +export class DocumentSymbolAdapter implements languages.DocumentSymbolProvider { + constructor(private _worker: WorkerAccessor) {} + + public provideDocumentSymbols( + model: editor.IReadOnlyModel, + token: CancellationToken + ): Promise { + const resource = model.uri; + + return this._worker(resource) + .then((worker) => worker.findDocumentSymbols(resource.toString())) + .then((items) => { + if (!items) { + return; + } + return items.map((item) => ({ + name: item.name, + detail: '', + containerName: item.containerName, + kind: toSymbolKind(item.kind), + tags: [], + range: toRange(item.location.range), + selectionRange: toRange(item.location.range) + })); + }); + } +} + +export class DocumentColorAdapter implements languages.DocumentColorProvider { + constructor(private _worker: WorkerAccessor) {} + + public provideDocumentColors( + model: editor.IReadOnlyModel, + token: CancellationToken + ): Promise { + const resource = model.uri; + + return this._worker(resource) + .then((worker) => worker.findDocumentColors(resource.toString())) + .then((infos) => { + if (!infos) { + return; + } + return infos.map((item) => ({ + color: item.color, + range: toRange(item.range) + })); + }); + } + + public provideColorPresentations( + model: editor.IReadOnlyModel, + info: languages.IColorInformation, + token: CancellationToken + ): Promise { + const resource = model.uri; + + return this._worker(resource) + .then((worker) => + worker.getColorPresentations( + resource.toString(), + info.color, + fromRange(info.range) + ) + ) + .then((presentations) => { + if (!presentations) { + return; + } + return presentations.map((presentation) => { + let item: languages.IColorPresentation = { + label: presentation.label + }; + if (presentation.textEdit) { + item.textEdit = toTextEdit(presentation.textEdit); + } + if (presentation.additionalTextEdits) { + item.additionalTextEdits = presentation.additionalTextEdits.map( + toTextEdit + ); + } + return item; + }); + }); + } +} + +export class FoldingRangeAdapter implements languages.FoldingRangeProvider { + constructor(private _worker: WorkerAccessor) {} + + public provideFoldingRanges( + model: editor.IReadOnlyModel, + context: languages.FoldingContext, + token: CancellationToken + ): Promise { + const resource = model.uri; + + return this._worker(resource) + .then((worker) => worker.getFoldingRanges(resource.toString(), context)) + .then((ranges) => { + if (!ranges) { + return; + } + return ranges.map((range) => { + let result: languages.FoldingRange = { + start: range.startLine + 1, + end: range.endLine + 1 + }; + if (typeof range.kind !== 'undefined') { + result.kind = toFoldingRangeKind( + range.kind + ); + } + return result; + }); + }); + } +} + +function toFoldingRangeKind( + kind: cssService.FoldingRangeKind +): languages.FoldingRangeKind { + switch (kind) { + case cssService.FoldingRangeKind.Comment: + return languages.FoldingRangeKind.Comment; + case cssService.FoldingRangeKind.Imports: + return languages.FoldingRangeKind.Imports; + case cssService.FoldingRangeKind.Region: + return languages.FoldingRangeKind.Region; + } +} + +export class SelectionRangeAdapter implements languages.SelectionRangeProvider { + constructor(private _worker: WorkerAccessor) {} + + public provideSelectionRanges( + model: editor.IReadOnlyModel, + positions: Position[], + token: CancellationToken + ): Promise { + const resource = model.uri; + + return this._worker(resource) + .then((worker) => + worker.getSelectionRanges( + resource.toString(), + positions.map(fromPosition) + ) + ) + .then((selectionRanges) => { + if (!selectionRanges) { + return; + } + return selectionRanges.map((selectionRange) => { + const result: languages.SelectionRange[] = []; + while (selectionRange) { + result.push({ range: toRange(selectionRange.range) }); + selectionRange = selectionRange.parent; + } + return result; + }); + }); + } +} diff --git a/src/monaco.contribution.ts b/src/monaco.contribution.ts index 6a8c4f1e..1122c37a 100644 --- a/src/monaco.contribution.ts +++ b/src/monaco.contribution.ts @@ -1,203 +1,217 @@ -/*--------------------------------------------------------------------------------------------- - * Copyright (c) Microsoft Corporation. All rights reserved. - * Licensed under the MIT License. See License.txt in the project root for license information. - *--------------------------------------------------------------------------------------------*/ - -import * as mode from './cssMode'; -import { languages, Emitter, IEvent } from './fillers/monaco-editor-core'; - -export interface DiagnosticsOptions { - readonly validate?: boolean; - readonly lint?: { - readonly compatibleVendorPrefixes?: 'ignore' | 'warning' | 'error', - readonly vendorPrefix?: 'ignore' | 'warning' | 'error', - readonly duplicateProperties?: 'ignore' | 'warning' | 'error', - readonly emptyRules?: 'ignore' | 'warning' | 'error', - readonly importStatement?: 'ignore' | 'warning' | 'error', - readonly boxModel?: 'ignore' | 'warning' | 'error', - readonly universalSelector?: 'ignore' | 'warning' | 'error', - readonly zeroUnits?: 'ignore' | 'warning' | 'error', - readonly fontFaceProperties?: 'ignore' | 'warning' | 'error', - readonly hexColorLength?: 'ignore' | 'warning' | 'error', - readonly argumentsInColorFunction?: 'ignore' | 'warning' | 'error', - readonly unknownProperties?: 'ignore' | 'warning' | 'error', - readonly ieHack?: 'ignore' | 'warning' | 'error', - readonly unknownVendorSpecificProperties?: 'ignore' | 'warning' | 'error', - readonly propertyIgnoredDueToDisplay?: 'ignore' | 'warning' | 'error', - readonly important?: 'ignore' | 'warning' | 'error', - readonly float?: 'ignore' | 'warning' | 'error', - readonly idSelector?: 'ignore' | 'warning' | 'error' - } -} - -export interface ModeConfiguration { - /** - * Defines whether the built-in completionItemProvider is enabled. - */ - readonly completionItems?: boolean; - - /** - * Defines whether the built-in hoverProvider is enabled. - */ - readonly hovers?: boolean; - - /** - * Defines whether the built-in documentSymbolProvider is enabled. - */ - readonly documentSymbols?: boolean; - - /** - * Defines whether the built-in definitions provider is enabled. - */ - readonly definitions?: boolean; - - /** - * Defines whether the built-in references provider is enabled. - */ - readonly references?: boolean; - - /** - * Defines whether the built-in references provider is enabled. - */ - readonly documentHighlights?: boolean; - - /** - * Defines whether the built-in rename provider is enabled. - */ - readonly rename?: boolean; - - /** - * Defines whether the built-in color provider is enabled. - */ - readonly colors?: boolean; - - /** - * Defines whether the built-in foldingRange provider is enabled. - */ - readonly foldingRanges?: boolean; - - /** - * Defines whether the built-in diagnostic provider is enabled. - */ - readonly diagnostics?: boolean; - - /** - * Defines whether the built-in selection range provider is enabled. - */ - readonly selectionRanges?: boolean; - -} - -export interface LanguageServiceDefaults { - readonly languageId: string; - readonly onDidChange: IEvent; - readonly diagnosticsOptions: DiagnosticsOptions; - readonly modeConfiguration: ModeConfiguration; - setDiagnosticsOptions(options: DiagnosticsOptions): void; - setModeConfiguration(modeConfiguration: ModeConfiguration): void; -} - -// --- CSS configuration and defaults --------- - -class LanguageServiceDefaultsImpl implements LanguageServiceDefaults { - - private _onDidChange = new Emitter(); - private _diagnosticsOptions: DiagnosticsOptions; - private _modeConfiguration: ModeConfiguration; - private _languageId: string; - - constructor(languageId: string, diagnosticsOptions: DiagnosticsOptions, modeConfiguration: ModeConfiguration) { - this._languageId = languageId; - this.setDiagnosticsOptions(diagnosticsOptions); - this.setModeConfiguration(modeConfiguration); - } - - get onDidChange(): IEvent { - return this._onDidChange.event; - } - - get languageId(): string { - return this._languageId; - } - - get modeConfiguration(): ModeConfiguration { - return this._modeConfiguration; - } - - get diagnosticsOptions(): DiagnosticsOptions { - return this._diagnosticsOptions; - } - - setDiagnosticsOptions(options: DiagnosticsOptions): void { - this._diagnosticsOptions = options || Object.create(null); - this._onDidChange.fire(this); - } - - setModeConfiguration(modeConfiguration: ModeConfiguration): void { - this._modeConfiguration = modeConfiguration || Object.create(null); - this._onDidChange.fire(this); - }; -} - -const diagnosticDefault: Required = { - 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 modeConfigurationDefault: Required = { - completionItems: true, - hovers: true, - documentSymbols: true, - definitions: true, - references: true, - documentHighlights: true, - rename: true, - colors: true, - foldingRanges: true, - diagnostics: true, - selectionRanges: true -} - -export const cssDefaults: LanguageServiceDefaults = new LanguageServiceDefaultsImpl('css', diagnosticDefault, modeConfigurationDefault); -export const scssDefaults: LanguageServiceDefaults = new LanguageServiceDefaultsImpl('scss', diagnosticDefault, modeConfigurationDefault); -export const lessDefaults: LanguageServiceDefaults = new LanguageServiceDefaultsImpl('less', diagnosticDefault, modeConfigurationDefault); - -// export to the global based API -(languages).json = { cssDefaults, lessDefaults, scssDefaults }; - -// --- Registration to monaco editor --- - -function getMode(): Promise { - return import('./cssMode'); -} - -languages.onLanguage('less', () => { - getMode().then(mode => mode.setupMode(lessDefaults)); -}); - -languages.onLanguage('scss', () => { - getMode().then(mode => mode.setupMode(scssDefaults)); -}); - -languages.onLanguage('css', () => { - getMode().then(mode => mode.setupMode(cssDefaults)); -}); +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the MIT License. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ + +import * as mode from './cssMode'; +import { languages, Emitter, IEvent } from './fillers/monaco-editor-core'; + +export interface DiagnosticsOptions { + readonly validate?: boolean; + readonly lint?: { + readonly compatibleVendorPrefixes?: 'ignore' | 'warning' | 'error'; + readonly vendorPrefix?: 'ignore' | 'warning' | 'error'; + readonly duplicateProperties?: 'ignore' | 'warning' | 'error'; + readonly emptyRules?: 'ignore' | 'warning' | 'error'; + readonly importStatement?: 'ignore' | 'warning' | 'error'; + readonly boxModel?: 'ignore' | 'warning' | 'error'; + readonly universalSelector?: 'ignore' | 'warning' | 'error'; + readonly zeroUnits?: 'ignore' | 'warning' | 'error'; + readonly fontFaceProperties?: 'ignore' | 'warning' | 'error'; + readonly hexColorLength?: 'ignore' | 'warning' | 'error'; + readonly argumentsInColorFunction?: 'ignore' | 'warning' | 'error'; + readonly unknownProperties?: 'ignore' | 'warning' | 'error'; + readonly ieHack?: 'ignore' | 'warning' | 'error'; + readonly unknownVendorSpecificProperties?: 'ignore' | 'warning' | 'error'; + readonly propertyIgnoredDueToDisplay?: 'ignore' | 'warning' | 'error'; + readonly important?: 'ignore' | 'warning' | 'error'; + readonly float?: 'ignore' | 'warning' | 'error'; + readonly idSelector?: 'ignore' | 'warning' | 'error'; + }; +} + +export interface ModeConfiguration { + /** + * Defines whether the built-in completionItemProvider is enabled. + */ + readonly completionItems?: boolean; + + /** + * Defines whether the built-in hoverProvider is enabled. + */ + readonly hovers?: boolean; + + /** + * Defines whether the built-in documentSymbolProvider is enabled. + */ + readonly documentSymbols?: boolean; + + /** + * Defines whether the built-in definitions provider is enabled. + */ + readonly definitions?: boolean; + + /** + * Defines whether the built-in references provider is enabled. + */ + readonly references?: boolean; + + /** + * Defines whether the built-in references provider is enabled. + */ + readonly documentHighlights?: boolean; + + /** + * Defines whether the built-in rename provider is enabled. + */ + readonly rename?: boolean; + + /** + * Defines whether the built-in color provider is enabled. + */ + readonly colors?: boolean; + + /** + * Defines whether the built-in foldingRange provider is enabled. + */ + readonly foldingRanges?: boolean; + + /** + * Defines whether the built-in diagnostic provider is enabled. + */ + readonly diagnostics?: boolean; + + /** + * Defines whether the built-in selection range provider is enabled. + */ + readonly selectionRanges?: boolean; +} + +export interface LanguageServiceDefaults { + readonly languageId: string; + readonly onDidChange: IEvent; + readonly diagnosticsOptions: DiagnosticsOptions; + readonly modeConfiguration: ModeConfiguration; + setDiagnosticsOptions(options: DiagnosticsOptions): void; + setModeConfiguration(modeConfiguration: ModeConfiguration): void; +} + +// --- CSS configuration and defaults --------- + +class LanguageServiceDefaultsImpl implements LanguageServiceDefaults { + private _onDidChange = new Emitter(); + private _diagnosticsOptions: DiagnosticsOptions; + private _modeConfiguration: ModeConfiguration; + private _languageId: string; + + constructor( + languageId: string, + diagnosticsOptions: DiagnosticsOptions, + modeConfiguration: ModeConfiguration + ) { + this._languageId = languageId; + this.setDiagnosticsOptions(diagnosticsOptions); + this.setModeConfiguration(modeConfiguration); + } + + get onDidChange(): IEvent { + return this._onDidChange.event; + } + + get languageId(): string { + return this._languageId; + } + + get modeConfiguration(): ModeConfiguration { + return this._modeConfiguration; + } + + get diagnosticsOptions(): DiagnosticsOptions { + return this._diagnosticsOptions; + } + + setDiagnosticsOptions(options: DiagnosticsOptions): void { + this._diagnosticsOptions = options || Object.create(null); + this._onDidChange.fire(this); + } + + setModeConfiguration(modeConfiguration: ModeConfiguration): void { + this._modeConfiguration = modeConfiguration || Object.create(null); + this._onDidChange.fire(this); + } +} + +const diagnosticDefault: Required = { + 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 modeConfigurationDefault: Required = { + completionItems: true, + hovers: true, + documentSymbols: true, + definitions: true, + references: true, + documentHighlights: true, + rename: true, + colors: true, + foldingRanges: true, + diagnostics: true, + selectionRanges: true +}; + +export const cssDefaults: LanguageServiceDefaults = new LanguageServiceDefaultsImpl( + 'css', + diagnosticDefault, + modeConfigurationDefault +); +export const scssDefaults: LanguageServiceDefaults = new LanguageServiceDefaultsImpl( + 'scss', + diagnosticDefault, + modeConfigurationDefault +); +export const lessDefaults: LanguageServiceDefaults = new LanguageServiceDefaultsImpl( + 'less', + diagnosticDefault, + modeConfigurationDefault +); + +// export to the global based API +(languages).json = { cssDefaults, lessDefaults, scssDefaults }; + +// --- Registration to monaco editor --- + +function getMode(): Promise { + return import('./cssMode'); +} + +languages.onLanguage('less', () => { + getMode().then((mode) => mode.setupMode(lessDefaults)); +}); + +languages.onLanguage('scss', () => { + getMode().then((mode) => mode.setupMode(scssDefaults)); +}); + +languages.onLanguage('css', () => { + getMode().then((mode) => mode.setupMode(cssDefaults)); +}); diff --git a/src/tsconfig.esm.json b/src/tsconfig.esm.json index 5ce16c6e..2023da46 100644 --- a/src/tsconfig.esm.json +++ b/src/tsconfig.esm.json @@ -1,16 +1,16 @@ { - "compilerOptions": { - "module": "esnext", - "moduleResolution": "node", - "outDir": "../out/esm", - "declaration": true, - "target": "es5", - "lib": [ - "dom", - "es5", - "es2015.collection", - "es2015.promise", - "es2015.iterable" - ] - } + "compilerOptions": { + "module": "esnext", + "moduleResolution": "node", + "outDir": "../out/esm", + "declaration": true, + "target": "es5", + "lib": [ + "dom", + "es5", + "es2015.collection", + "es2015.promise", + "es2015.iterable" + ] + } } diff --git a/src/tsconfig.json b/src/tsconfig.json index 5daa1b31..6b43d988 100644 --- a/src/tsconfig.json +++ b/src/tsconfig.json @@ -1,16 +1,16 @@ { - "compilerOptions": { - "module": "amd", - "moduleResolution": "node", - "outDir": "../out/amd", - "declaration": true, - "target": "es5", - "lib": [ - "dom", - "es5", - "es2015.collection", - "es2015.promise", - "es2015.iterable" - ] - } + "compilerOptions": { + "module": "amd", + "moduleResolution": "node", + "outDir": "../out/amd", + "declaration": true, + "target": "es5", + "lib": [ + "dom", + "es5", + "es2015.collection", + "es2015.promise", + "es2015.iterable" + ] + } } diff --git a/src/workerManager.ts b/src/workerManager.ts index 3d3d4cc2..1cd2c290 100644 --- a/src/workerManager.ts +++ b/src/workerManager.ts @@ -1,86 +1,92 @@ -/*--------------------------------------------------------------------------------------------- - * Copyright (c) Microsoft Corporation. All rights reserved. - * Licensed under the MIT License. See License.txt in the project root for license information. - *--------------------------------------------------------------------------------------------*/ - -import { LanguageServiceDefaults } from './monaco.contribution'; -import type { CSSWorker } from './cssWorker'; -import { editor, IDisposable, Uri } from './fillers/monaco-editor-core'; - -const STOP_WHEN_IDLE_FOR = 2 * 60 * 1000; // 2min - -export class WorkerManager { - - private _defaults: LanguageServiceDefaults; - private _idleCheckInterval: number; - private _lastUsedTime: number; - private _configChangeListener: IDisposable; - - private _worker: editor.MonacoWebWorker; - private _client: Promise; - - constructor(defaults: LanguageServiceDefaults) { - this._defaults = defaults; - this._worker = null; - this._idleCheckInterval = window.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 { - this._lastUsedTime = Date.now(); - - if (!this._client) { - this._worker = editor.createWebWorker({ - - // module that exports the create() method and returns a `CSSWorker` instance - moduleId: 'vs/language/css/cssWorker', - - label: this._defaults.languageId, - - // passed in to the create() method - createData: { - languageSettings: this._defaults.diagnosticsOptions, - languageId: this._defaults.languageId - } - }); - - this._client = >this._worker.getProxy(); - } - - return this._client; - } - - getLanguageServiceWorker(...resources: Uri[]): Promise { - let _client: CSSWorker; - return this._getClient().then((client) => { - _client = client - }).then(_ => { - return this._worker.withSyncedResources(resources) - }).then(_ => _client); - } -} +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the MIT License. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ + +import { LanguageServiceDefaults } from './monaco.contribution'; +import type { CSSWorker } from './cssWorker'; +import { editor, IDisposable, Uri } from './fillers/monaco-editor-core'; + +const STOP_WHEN_IDLE_FOR = 2 * 60 * 1000; // 2min + +export class WorkerManager { + private _defaults: LanguageServiceDefaults; + private _idleCheckInterval: number; + private _lastUsedTime: number; + private _configChangeListener: IDisposable; + + private _worker: editor.MonacoWebWorker; + private _client: Promise; + + constructor(defaults: LanguageServiceDefaults) { + this._defaults = defaults; + this._worker = null; + this._idleCheckInterval = window.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 { + this._lastUsedTime = Date.now(); + + if (!this._client) { + this._worker = editor.createWebWorker({ + // module that exports the create() method and returns a `CSSWorker` instance + moduleId: 'vs/language/css/cssWorker', + + label: this._defaults.languageId, + + // passed in to the create() method + createData: { + languageSettings: this._defaults.diagnosticsOptions, + languageId: this._defaults.languageId + } + }); + + this._client = >(this._worker.getProxy()); + } + + return this._client; + } + + getLanguageServiceWorker(...resources: Uri[]): Promise { + let _client: CSSWorker; + return this._getClient() + .then((client) => { + _client = client; + }) + .then((_) => { + return this._worker.withSyncedResources(resources); + }) + .then((_) => _client); + } +} diff --git a/test/index.html b/test/index.html index 38a654b2..a792811b 100644 --- a/test/index.html +++ b/test/index.html @@ -1,875 +1,883 @@ - - - - - - - - - -

Monaco Editor CSS test page

-
- - - - - - - - - - + + + + + + + + +

Monaco Editor CSS test page

+
+ + + + + + + + +