From f7894236eea20569aca7c7e64390c0d300db8b77 Mon Sep 17 00:00:00 2001 From: Pine Wu Date: Mon, 7 Jan 2019 16:52:04 -0800 Subject: [PATCH] css.experimental.customData --- .../.vscode/settings.json | 2 + .../client/src/cssMain.ts | 19 ++++++++- extensions/css-language-features/package.json | 4 ++ .../server/src/cssServerMain.ts | 41 ++++++++++++++++--- .../server/src/languageFacts.ts | 21 ++++++++++ 5 files changed, 80 insertions(+), 7 deletions(-) create mode 100644 extensions/css-language-features/.vscode/settings.json create mode 100644 extensions/css-language-features/server/src/languageFacts.ts diff --git a/extensions/css-language-features/.vscode/settings.json b/extensions/css-language-features/.vscode/settings.json new file mode 100644 index 00000000000..7a73a41bfdf --- /dev/null +++ b/extensions/css-language-features/.vscode/settings.json @@ -0,0 +1,2 @@ +{ +} \ No newline at end of file diff --git a/extensions/css-language-features/client/src/cssMain.ts b/extensions/css-language-features/client/src/cssMain.ts index 6e0f2eb77c7..fbbec3cee86 100644 --- a/extensions/css-language-features/client/src/cssMain.ts +++ b/extensions/css-language-features/client/src/cssMain.ts @@ -9,7 +9,7 @@ import * as fs from 'fs'; import * as nls from 'vscode-nls'; const localize = nls.loadMessageBundle(); -import { languages, window, commands, ExtensionContext, Range, Position, CompletionItem, CompletionItemKind, TextEdit, SnippetString } from 'vscode'; +import { languages, window, commands, ExtensionContext, Range, Position, CompletionItem, CompletionItemKind, TextEdit, SnippetString, workspace } from 'vscode'; import { LanguageClient, LanguageClientOptions, ServerOptions, TransportKind, Disposable } from 'vscode-languageclient'; // this method is called when vs code is activated @@ -30,6 +30,22 @@ export function activate(context: ExtensionContext) { let documentSelector = ['css', 'scss', 'less']; + let dataPaths: string[] = workspace.getConfiguration('css').get('experimental.customData', []); + if (dataPaths && dataPaths.length > 0) { + if (!workspace.workspaceFolders) { + dataPaths = []; + } else { + try { + const workspaceRoot = workspace.workspaceFolders[0].uri.fsPath; + dataPaths = dataPaths.map(d => { + return path.resolve(workspaceRoot, d); + }); + } catch (err) { + dataPaths = []; + } + } + } + // Options to control the language client let clientOptions: LanguageClientOptions = { documentSelector, @@ -37,6 +53,7 @@ export function activate(context: ExtensionContext) { configurationSection: ['css', 'scss', 'less'] }, initializationOptions: { + dataPaths } }; diff --git a/extensions/css-language-features/package.json b/extensions/css-language-features/package.json index e6f71ff0260..4583d0b2f65 100644 --- a/extensions/css-language-features/package.json +++ b/extensions/css-language-features/package.json @@ -32,6 +32,10 @@ "id": "css", "title": "%css.title%", "properties": { + "css.experimental.customData": { + "type": "array", + "description": "A list of JSON file paths that define custom CSS data that loads extra properties, at directives, pseudo classes / elements." + }, "css.validate": { "type": "boolean", "scope": "resource", diff --git a/extensions/css-language-features/server/src/cssServerMain.ts b/extensions/css-language-features/server/src/cssServerMain.ts index 8c516fb8598..e376eaec816 100644 --- a/extensions/css-language-features/server/src/cssServerMain.ts +++ b/extensions/css-language-features/server/src/cssServerMain.ts @@ -7,6 +7,7 @@ import { createConnection, IConnection, TextDocuments, InitializeParams, InitializeResult, ServerCapabilities, ConfigurationRequest, WorkspaceFolder } from 'vscode-languageserver'; import URI from 'vscode-uri'; +import * as fs from 'fs'; import { TextDocument, CompletionList } from 'vscode-languageserver-types'; import { getCSSLanguageService, getSCSSLanguageService, getLESSLanguageService, LanguageSettings, LanguageService, Stylesheet } from 'vscode-css-languageservice'; @@ -14,6 +15,7 @@ import { getLanguageModelCache } from './languageModelCache'; import { getPathCompletionParticipant } from './pathCompletion'; import { formatError, runSafe } from './utils/runner'; import { getDocumentContext } from './utils/documentContext'; +import { parseCSSData } from './languageFacts'; export interface Settings { css: LanguageSettings; @@ -50,6 +52,8 @@ let scopedSettingsSupport = false; let foldingRangeLimit = Number.MAX_VALUE; let workspaceFolders: WorkspaceFolder[]; +const languageServices: { [id: string]: LanguageService } = {}; + // After the server has started the client sends an initialize request. The server receives // in the passed params the rootPath of the workspace plus the client capabilities. connection.onInitialize((params: InitializeParams): InitializeResult => { @@ -61,6 +65,33 @@ connection.onInitialize((params: InitializeParams): InitializeResult => { } } + const dataPaths: string[] = params.initializationOptions.dataPaths; + + let customData = { + customProperties: [], + customAtDirectives: [], + customPseudoElements: [], + customPseudoClasses: [] + }; + + dataPaths.forEach(p => { + if (fs.existsSync(p)) { + const { + properties, + atDirectives, + pseudoClasses, + pseudoElements + } = parseCSSData(fs.readFileSync(p, 'utf-8')); + + customData.customProperties = customData.customProperties.concat(properties); + customData.customAtDirectives = customData.customAtDirectives.concat(atDirectives); + customData.customPseudoClasses = customData.customPseudoClasses.concat(pseudoClasses); + customData.customPseudoElements = customData.customPseudoElements.concat(pseudoElements); + } else { + return; + } + }); + function getClientCapability(name: string, def: T) { const keys = name.split('.'); let c: any = params.capabilities; @@ -76,6 +107,10 @@ connection.onInitialize((params: InitializeParams): InitializeResult => { scopedSettingsSupport = !!getClientCapability('workspace.configuration', false); foldingRangeLimit = getClientCapability('textDocument.foldingRange.rangeLimit', Number.MAX_VALUE); + languageServices.css = getCSSLanguageService(customData); + languageServices.scss = getSCSSLanguageService(customData); + languageServices.less = getLESSLanguageService(customData); + const capabilities: ServerCapabilities = { // Tell the client that the server works in FULL text document sync mode textDocumentSync: documents.syncKind, @@ -96,12 +131,6 @@ connection.onInitialize((params: InitializeParams): InitializeResult => { return { capabilities }; }); -const languageServices: { [id: string]: LanguageService } = { - css: getCSSLanguageService(), - scss: getSCSSLanguageService(), - less: getLESSLanguageService() -}; - function getLanguageService(document: TextDocument) { let service = languageServices[document.languageId]; if (!service) { diff --git a/extensions/css-language-features/server/src/languageFacts.ts b/extensions/css-language-features/server/src/languageFacts.ts new file mode 100644 index 00000000000..3e2061101e3 --- /dev/null +++ b/extensions/css-language-features/server/src/languageFacts.ts @@ -0,0 +1,21 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the MIT License. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ + +export function parseCSSData(source: string) { + let rawData: any; + + try { + rawData = JSON.parse(source); + } catch (err) { + return {}; + } + + return { + properties: rawData.properties || [], + atDirectives: rawData.atdirectives || [], + pseudoClasses: rawData.pseudoclasses || [], + pseudoElements: rawData.pseudoelements || [] + }; +}