diff --git a/extensions/html/client/src/colorDecorators.ts b/extensions/html/client/src/colorDecorators.ts deleted file mode 100644 index e6374c3ec17..00000000000 --- a/extensions/html/client/src/colorDecorators.ts +++ /dev/null @@ -1,126 +0,0 @@ -/*--------------------------------------------------------------------------------------------- - * 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 { window, workspace, DecorationOptions, DecorationRenderOptions, Disposable, Range, TextDocument } from 'vscode'; - -const MAX_DECORATORS = 500; - -let decorationType: DecorationRenderOptions = { - before: { - contentText: ' ', - border: 'solid 0.1em #000', - margin: '0.1em 0.2em 0 0.2em', - width: '0.8em', - height: '0.8em' - }, - dark: { - before: { - border: 'solid 0.1em #eee' - } - } -}; - -export function activateColorDecorations(decoratorProvider: (uri: string) => Thenable, supportedLanguages: { [id: string]: boolean }, isDecoratorEnabled: (languageId: string) => boolean): Disposable { - - let disposables: Disposable[] = []; - - let colorsDecorationType = window.createTextEditorDecorationType(decorationType); - disposables.push(colorsDecorationType); - - let decoratorEnablement = {}; - for (let languageId in supportedLanguages) { - decoratorEnablement[languageId] = isDecoratorEnabled(languageId); - } - - let pendingUpdateRequests: { [key: string]: NodeJS.Timer; } = {}; - - window.onDidChangeVisibleTextEditors(editors => { - for (let editor of editors) { - triggerUpdateDecorations(editor.document); - } - }, null, disposables); - - workspace.onDidChangeTextDocument(event => triggerUpdateDecorations(event.document), null, disposables); - - // track open and close for document languageId changes - workspace.onDidCloseTextDocument(event => triggerUpdateDecorations(event, true)); - workspace.onDidOpenTextDocument(event => triggerUpdateDecorations(event)); - - workspace.onDidChangeConfiguration(_ => { - let hasChanges = false; - for (let languageId in supportedLanguages) { - let prev = decoratorEnablement[languageId]; - let curr = isDecoratorEnabled(languageId); - if (prev !== curr) { - decoratorEnablement[languageId] = curr; - hasChanges = true; - } - } - if (hasChanges) { - updateAllVisibleEditors(true); - } - }, null, disposables); - - updateAllVisibleEditors(false); - - function updateAllVisibleEditors(settingsChanges: boolean) { - window.visibleTextEditors.forEach(editor => { - if (editor.document) { - triggerUpdateDecorations(editor.document, settingsChanges); - } - }); - } - - function triggerUpdateDecorations(document: TextDocument, settingsChanges = false) { - let triggerUpdate = supportedLanguages[document.languageId] && (decoratorEnablement[document.languageId] || settingsChanges); - if (triggerUpdate) { - let documentUriStr = document.uri.toString(); - let timeout = pendingUpdateRequests[documentUriStr]; - if (typeof timeout !== 'undefined') { - clearTimeout(timeout); - } - pendingUpdateRequests[documentUriStr] = setTimeout(() => { - // check if the document is in use by an active editor - for (let editor of window.visibleTextEditors) { - if (editor.document && documentUriStr === editor.document.uri.toString()) { - if (decoratorEnablement[editor.document.languageId]) { - updateDecorationForEditor(documentUriStr, editor.document.version); - break; - } else { - editor.setDecorations(colorsDecorationType, []); - } - } - } - delete pendingUpdateRequests[documentUriStr]; - }, 500); - } - } - - function updateDecorationForEditor(contentUri: string, documentVersion: number) { - decoratorProvider(contentUri).then(ranges => { - for (let editor of window.visibleTextEditors) { - let document = editor.document; - - if (document && document.version === documentVersion && contentUri === document.uri.toString()) { - let decorations = ranges.slice(0, MAX_DECORATORS).map(range => { - let color = document.getText(range); - return { - range: range, - renderOptions: { - before: { - backgroundColor: color - } - } - }; - }); - editor.setDecorations(colorsDecorationType, decorations); - } - } - }); - } - - return Disposable.from(...disposables); -} diff --git a/extensions/html/client/src/htmlMain.ts b/extensions/html/client/src/htmlMain.ts index b100b35a052..bb391d50d3e 100644 --- a/extensions/html/client/src/htmlMain.ts +++ b/extensions/html/client/src/htmlMain.ts @@ -6,22 +6,18 @@ import * as path from 'path'; -import { languages, workspace, ExtensionContext, IndentAction, Position, TextDocument } from 'vscode'; -import { LanguageClient, LanguageClientOptions, ServerOptions, TransportKind, Range as LSRange, RequestType, TextDocumentPositionParams } from 'vscode-languageclient'; +import { languages, ExtensionContext, IndentAction, Position, TextDocument, Color, ColorRange } from 'vscode'; +import { LanguageClient, LanguageClientOptions, ServerOptions, TransportKind, RequestType, TextDocumentPositionParams } from 'vscode-languageclient'; import { EMPTY_ELEMENTS } from './htmlEmptyTagsShared'; -import { activateColorDecorations } from './colorDecorators'; import { activateTagClosing } from './tagClosing'; import TelemetryReporter from 'vscode-extension-telemetry'; import { ConfigurationFeature } from 'vscode-languageclient/lib/proposed'; +import { DocumentColorRequest } from 'vscode-languageserver-protocol/lib/protocol.colorProvider.proposed'; import * as nls from 'vscode-nls'; let localize = nls.loadMessageBundle(); -namespace ColorSymbolRequest { - export const type: RequestType = new RequestType('html/colorSymbols'); -} - namespace TagCloseRequest { export const type: RequestType = new RequestType('html/tag'); } @@ -32,6 +28,18 @@ interface IPackageInfo { aiKey: string; } +const CSSColorFormats = { + Hex: '#{red:X}{green:X}{blue:X}', + RGB: { + opaque: 'rgb({red:d[0-255]}, {green:d[0-255]}, {blue:d[0-255]})', + transparent: 'rgba({red:d[0-255]}, {green:d[0-255]}, {blue:d[0-255]}, {alpha})' + }, + HSL: { + opaque: 'hsl({hue:d[0-360]}, {saturation:d[0-100]}%, {luminance:d[0-100]}%)', + transparent: 'hsla({hue:d[0-360]}, {saturation:d[0-100]}%, {luminance:d[0-100]}%, {alpha})' + } +}; + export function activate(context: ExtensionContext) { let toDispose = context.subscriptions; @@ -74,13 +82,18 @@ export function activate(context: ExtensionContext) { let disposable = client.start(); toDispose.push(disposable); client.onReady().then(() => { - let colorRequestor = (uri: string) => { - return client.sendRequest(ColorSymbolRequest.type, uri).then(ranges => ranges.map(client.protocol2CodeConverter.asRange)); - }; - let isDecoratorEnabled = (languageId: string) => { - return workspace.getConfiguration().get('css.colorDecorators.enable'); - }; - let disposable = activateColorDecorations(colorRequestor, { html: true, handlebars: true, razor: true }, isDecoratorEnabled); + disposable = languages.registerColorProvider(documentSelector, { + provideDocumentColors(document: TextDocument): Thenable { + let params = client.code2ProtocolConverter.asDocumentSymbolParams(document); + return client.sendRequest(DocumentColorRequest.type, params).then(symbols => { + return symbols.map(symbol => { + let range = client.protocol2CodeConverter.asRange(symbol.range); + let color = new Color(symbol.color.red * 255, symbol.color.green * 255, symbol.color.blue * 255, symbol.color.alpha); + return new ColorRange(range, color, [CSSColorFormats.Hex, CSSColorFormats.RGB, CSSColorFormats.HSL]); + }); + }); + } + }); toDispose.push(disposable); let tagRequestor = (document: TextDocument, position: Position) => { diff --git a/extensions/html/client/src/typings/ref.d.ts b/extensions/html/client/src/typings/ref.d.ts index 7c0805ba6c3..2b90c6587fe 100644 --- a/extensions/html/client/src/typings/ref.d.ts +++ b/extensions/html/client/src/typings/ref.d.ts @@ -3,4 +3,5 @@ * Licensed under the MIT License. See License.txt in the project root for license information. *--------------------------------------------------------------------------------------------*/ -/// \ No newline at end of file +/// +/// \ No newline at end of file diff --git a/extensions/html/npm-shrinkwrap.json b/extensions/html/npm-shrinkwrap.json index 909f58f38bf..f8f32b0eef7 100644 --- a/extensions/html/npm-shrinkwrap.json +++ b/extensions/html/npm-shrinkwrap.json @@ -23,9 +23,9 @@ "resolved": "https://registry.npmjs.org/vscode-languageclient/-/vscode-languageclient-3.4.0-next.17.tgz" }, "vscode-languageserver-protocol": { - "version": "3.1.0", - "from": "vscode-languageserver-protocol@>=3.1.0 <4.0.0", - "resolved": "https://registry.npmjs.org/vscode-languageserver-protocol/-/vscode-languageserver-protocol-3.1.0.tgz" + "version": "3.1.1", + "from": "vscode-languageserver-protocol@>=3.1.1 <4.0.0", + "resolved": "https://registry.npmjs.org/vscode-languageserver-protocol/-/vscode-languageserver-protocol-3.1.1.tgz" }, "vscode-languageserver-types": { "version": "3.3.0", diff --git a/extensions/html/package.json b/extensions/html/package.json index e2ca558c767..466f0a18f60 100644 --- a/extensions/html/package.json +++ b/extensions/html/package.json @@ -11,6 +11,7 @@ "onLanguage:handlebars", "onLanguage:razor" ], + "enableProposedApi": true, "main": "./client/out/htmlMain", "scripts": { "compile": "gulp compile-extension:html-client && gulp compile-extension:html-server", @@ -216,6 +217,7 @@ "dependencies": { "vscode-extension-telemetry": "0.0.8", "vscode-languageclient": "3.4.0-next.17", + "vscode-languageserver-protocol": "^3.1.1", "vscode-languageserver-types": "^3.3.0", "vscode-nls": "2.0.2" }, diff --git a/extensions/html/server/npm-shrinkwrap.json b/extensions/html/server/npm-shrinkwrap.json index 946cd3d80c1..62a5bf9b691 100644 --- a/extensions/html/server/npm-shrinkwrap.json +++ b/extensions/html/server/npm-shrinkwrap.json @@ -3,9 +3,9 @@ "version": "1.0.0", "dependencies": { "vscode-css-languageservice": { - "version": "2.1.3", + "version": "2.1.4", "from": "vscode-css-languageservice@next", - "resolved": "https://registry.npmjs.org/vscode-css-languageservice/-/vscode-css-languageservice-2.1.3.tgz" + "resolved": "https://registry.npmjs.org/vscode-css-languageservice/-/vscode-css-languageservice-2.1.4.tgz" }, "vscode-html-languageservice": { "version": "2.0.7", @@ -23,9 +23,9 @@ "resolved": "https://registry.npmjs.org/vscode-languageserver/-/vscode-languageserver-3.4.0-next.6.tgz" }, "vscode-languageserver-protocol": { - "version": "3.1.0", - "from": "vscode-languageserver-protocol@>=3.1.0 <4.0.0", - "resolved": "https://registry.npmjs.org/vscode-languageserver-protocol/-/vscode-languageserver-protocol-3.1.0.tgz" + "version": "3.1.1", + "from": "vscode-languageserver-protocol@>=3.1.1 <4.0.0", + "resolved": "https://registry.npmjs.org/vscode-languageserver-protocol/-/vscode-languageserver-protocol-3.1.1.tgz" }, "vscode-languageserver-types": { "version": "3.3.0", diff --git a/extensions/html/server/package.json b/extensions/html/server/package.json index da18cc5454c..3cac05b7da9 100644 --- a/extensions/html/server/package.json +++ b/extensions/html/server/package.json @@ -8,9 +8,10 @@ "node": "*" }, "dependencies": { - "vscode-css-languageservice": "^2.1.3", + "vscode-css-languageservice": "^2.1.4", "vscode-html-languageservice": "^2.0.7", "vscode-languageserver": "3.4.0-next.6", + "vscode-languageserver-protocol": "^3.1.1", "vscode-languageserver-types": "^3.3.0", "vscode-nls": "^2.0.2", "vscode-uri": "^1.0.1" diff --git a/extensions/html/server/src/htmlServerMain.ts b/extensions/html/server/src/htmlServerMain.ts index 0cb4d776319..891e177400d 100644 --- a/extensions/html/server/src/htmlServerMain.ts +++ b/extensions/html/server/src/htmlServerMain.ts @@ -4,12 +4,13 @@ *--------------------------------------------------------------------------------------------*/ 'use strict'; -import { createConnection, IConnection, TextDocuments, InitializeParams, InitializeResult, RequestType, DocumentRangeFormattingRequest, Disposable, DocumentSelector, GetConfigurationParams, TextDocumentPositionParams } from 'vscode-languageserver'; +import { createConnection, IConnection, TextDocuments, InitializeParams, InitializeResult, RequestType, DocumentRangeFormattingRequest, Disposable, DocumentSelector, GetConfigurationParams, TextDocumentPositionParams, ServerCapabilities } from 'vscode-languageserver'; import { DocumentContext } from 'vscode-html-languageservice'; -import { TextDocument, Diagnostic, DocumentLink, Range, SymbolInformation } from 'vscode-languageserver-types'; +import { TextDocument, Diagnostic, DocumentLink, SymbolInformation } from 'vscode-languageserver-types'; import { getLanguageModes, LanguageModes, Settings } from './modes/languageModes'; import { GetConfigurationRequest } from 'vscode-languageserver-protocol/lib/protocol.configuration.proposed'; +import { DocumentColorRequest, ServerCapabilities as CPServerCapabilities, ColorInformation } from 'vscode-languageserver-protocol/lib/protocol.colorProvider.proposed'; import { format } from './modes/formatting'; import { pushAll } from './utils/arrays'; @@ -21,15 +22,10 @@ import uri from 'vscode-uri'; import * as nls from 'vscode-nls'; nls.config(process.env['VSCODE_NLS_CONFIG']); -namespace ColorSymbolRequest { - export const type: RequestType = new RequestType('html/colorSymbols'); -} - namespace TagCloseRequest { export const type: RequestType = new RequestType('html/tag'); } - // Create a connection for the server let connection: IConnection = createConnection(); @@ -97,21 +93,22 @@ connection.onInitialize((params: InitializeParams): InitializeResult => { clientSnippetSupport = hasClientCapability('textDocument', 'completion', 'completionItem', 'snippetSupport'); clientDynamicRegisterSupport = hasClientCapability('workspace', 'symbol', 'dynamicRegistration'); scopedSettingsSupport = hasClientCapability('workspace', 'configuration'); - return { - capabilities: { - // Tell the client that the server works in FULL text document sync mode - textDocumentSync: documents.syncKind, - completionProvider: clientSnippetSupport ? { resolveProvider: true, triggerCharacters: ['.', ':', '<', '"', '=', '/', '>'] } : null, - hoverProvider: true, - documentHighlightProvider: true, - documentRangeFormattingProvider: false, - documentLinkProvider: { resolveProvider: false }, - documentSymbolProvider: true, - definitionProvider: true, - signatureHelpProvider: { triggerCharacters: ['('] }, - referencesProvider: true, - } + let capabilities: ServerCapabilities & CPServerCapabilities = { + // Tell the client that the server works in FULL text document sync mode + textDocumentSync: documents.syncKind, + completionProvider: clientSnippetSupport ? { resolveProvider: true, triggerCharacters: ['.', ':', '<', '"', '=', '/', '>'] } : null, + hoverProvider: true, + documentHighlightProvider: true, + documentRangeFormattingProvider: false, + documentLinkProvider: { resolveProvider: false }, + documentSymbolProvider: true, + definitionProvider: true, + signatureHelpProvider: { triggerCharacters: ['('] }, + referencesProvider: true, + colorProvider: true }; + + return { capabilities }; }); let formatterRegistration: Thenable = null; @@ -313,17 +310,17 @@ connection.onDocumentSymbol(documentSymbolParms => { return symbols; }); -connection.onRequest(ColorSymbolRequest.type, uri => { - let ranges: Range[] = []; - let document = documents.get(uri); +connection.onRequest(DocumentColorRequest.type, params => { + let infos: ColorInformation[] = []; + let document = documents.get(params.textDocument.uri); if (document) { languageModes.getAllModesInDocument(document).forEach(m => { - if (m.findColorSymbols) { - pushAll(ranges, m.findColorSymbols(document)); + if (m.findDocumentColors) { + pushAll(infos, m.findDocumentColors(document)); } }); } - return ranges; + return infos; }); connection.onRequest(TagCloseRequest.type, params => { diff --git a/extensions/html/server/src/modes/cssMode.ts b/extensions/html/server/src/modes/cssMode.ts index bcaa48391d9..dcb221e83d5 100644 --- a/extensions/html/server/src/modes/cssMode.ts +++ b/extensions/html/server/src/modes/cssMode.ts @@ -50,9 +50,9 @@ export function getCSSMode(documentRegions: LanguageModelCache Definition; findReferences?: (document: TextDocument, position: Position) => Location[]; format?: (document: TextDocument, range: Range, options: FormattingOptions, settings: Settings) => TextEdit[]; - findColorSymbols?: (document: TextDocument) => Range[]; + findDocumentColors?: (document: TextDocument) => ColorInformation[]; doAutoClose?: (document: TextDocument, position: Position) => string; onDocumentRemoved(document: TextDocument): void; dispose(): void;