Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Get emmet completions from css extension #41652

Merged
merged 8 commits into from
Feb 13, 2018
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 1 addition & 1 deletion extensions/css/client/src/cssMain.ts
Original file line number Diff line number Diff line change
Expand Up @@ -35,7 +35,7 @@ export function activate(context: ExtensionContext) {
let clientOptions: LanguageClientOptions = {
documentSelector,
synchronize: {
configurationSection: ['css', 'scss', 'less']
configurationSection: ['css', 'scss', 'less', 'emmet']
},
initializationOptions: {
}
Expand Down
4 changes: 2 additions & 2 deletions extensions/css/client/tsconfig.json
Original file line number Diff line number Diff line change
@@ -1,11 +1,11 @@
{
"compilerOptions": {
"target": "es5",
"target": "es6",
"module": "commonjs",
"outDir": "./out",
"noUnusedLocals": true,
"lib": [
"es5", "es2015.promise"
"es2016"
],
"strict": true
},
Expand Down
4 changes: 2 additions & 2 deletions extensions/css/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -713,10 +713,10 @@
]
},
"dependencies": {
"vscode-languageclient": "^3.5.0",
"vscode-languageclient": "^4.0.0-next.8",
"vscode-nls": "^3.2.1"
},
"devDependencies": {
"@types/node": "7.0.43"
}
}
}
1 change: 1 addition & 0 deletions extensions/css/server/.vscode/launch.json
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,7 @@
"type": "node",
"request": "attach",
"port": 6044,
"protocol": "inspector",
"sourceMaps": true,
"outFiles": ["${workspaceFolder}/out/**/*.js"]
},
Expand Down
7 changes: 5 additions & 2 deletions extensions/css/server/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -9,9 +9,11 @@
},
"dependencies": {
"vscode-css-languageservice": "^3.0.5",
"vscode-languageserver": "^3.5.0"
"vscode-languageserver": "^4.0.0-next.4",
"vscode-emmet-helper": "1.1.34"
},
"devDependencies": {
"@types/mocha": "2.2.33",
"@types/node": "7.0.43"
},
"scripts": {
Expand All @@ -20,6 +22,7 @@
"install-service-next": "yarn add vscode-css-languageservice@next",
"install-service-local": "npm install ../../../../vscode-css-languageservice -f",
"install-server-next": "yarn add vscode-languageserver@next",
"install-server-local": "npm install ../../../../vscode-languageserver-node/server -f"
"install-server-local": "npm install ../../../../vscode-languageserver-node/server -f",
"test": "../../../node_modules/.bin/mocha"
}
}
64 changes: 58 additions & 6 deletions extensions/css/server/src/cssServerMain.ts
Original file line number Diff line number Diff line change
Expand Up @@ -5,22 +5,26 @@
'use strict';

import {
createConnection, IConnection, TextDocuments, InitializeParams, InitializeResult, ServerCapabilities
createConnection, IConnection, TextDocuments, InitializeParams, InitializeResult, ServerCapabilities, CompletionTriggerKind
} from 'vscode-languageserver';

import { TextDocument } from 'vscode-languageserver-types';
import { TextDocument, CompletionList } from 'vscode-languageserver-types';

import { ConfigurationRequest } from 'vscode-languageserver-protocol/lib/protocol.configuration.proposed';
import { WorkspaceFolder } from 'vscode-languageserver-protocol/lib/protocol.workspaceFolders.proposed';
import { DocumentColorRequest, ServerCapabilities as CPServerCapabilities, ColorPresentationRequest } from 'vscode-languageserver-protocol/lib/protocol.colorProvider.proposed';

import { getCSSLanguageService, getSCSSLanguageService, getLESSLanguageService, LanguageSettings, LanguageService, Stylesheet } from 'vscode-css-languageservice';
import { getCSSLanguageService, getSCSSLanguageService, getLESSLanguageService, LanguageSettings, LanguageService, Stylesheet, ICompletionParticipant } from 'vscode-css-languageservice';
import { getLanguageModelCache } from './languageModelCache';
import { formatError, runSafe } from './utils/errors';
import { doComplete as emmetDoComplete, updateExtensionsPath as updateEmmetExtensionsPath, getEmmetCompletionParticipants } from 'vscode-emmet-helper';
import uri from 'vscode-uri';

export interface Settings {
css: LanguageSettings;
less: LanguageSettings;
scss: LanguageSettings;
emmet: { [key: string]: any };
}

// Create a connection for the server.
Expand Down Expand Up @@ -49,9 +53,22 @@ connection.onShutdown(() => {
});

let scopedSettingsSupport = false;
let workspaceFolders: WorkspaceFolder[] | undefined;
let emmetSettings = {};
let currentEmmetExtensionsPath: string;
const emmetTriggerCharacters = ['0', '1', '2', '3', '4', '5', '6', '7', '8', '9'];

// After the server has started the client sends an initilize request. The server receives
// in the passed params the rootPath of the workspace plus the client capabilities.
connection.onInitialize((params: InitializeParams): InitializeResult => {
workspaceFolders = (<any>params).workspaceFolders;
if (!Array.isArray(workspaceFolders)) {
workspaceFolders = [];
if (params.rootPath) {
workspaceFolders.push({ name: '', uri: uri.file(params.rootPath).toString() });
}
}

function hasClientCapability(name: string) {
let keys = name.split('.');
let c: any = params.capabilities;
Expand All @@ -65,7 +82,7 @@ connection.onInitialize((params: InitializeParams): InitializeResult => {
let capabilities: ServerCapabilities & CPServerCapabilities = {
// Tell the client that the server works in FULL text document sync mode
textDocumentSync: documents.syncKind,
completionProvider: snippetSupport ? { resolveProvider: false } : undefined,
completionProvider: snippetSupport ? { resolveProvider: false, triggerCharacters: emmetTriggerCharacters } : undefined,
hoverProvider: true,
documentSymbolProvider: true,
referencesProvider: true,
Expand Down Expand Up @@ -124,6 +141,13 @@ function updateConfiguration(settings: Settings) {
documentSettings = {};
// Revalidate any open text documents
documents.all().forEach(triggerValidation);

emmetSettings = settings.emmet;
if (currentEmmetExtensionsPath !== emmetSettings['extensionsPath']) {
currentEmmetExtensionsPath = emmetSettings['extensionsPath'];
const workspaceUri = (workspaceFolders && workspaceFolders.length === 1) ? uri.parse(workspaceFolders[0].uri) : null;
updateEmmetExtensionsPath(currentEmmetExtensionsPath, workspaceUri ? workspaceUri.fsPath : null);
}
}

let pendingValidationRequests: { [uri: string]: NodeJS.Timer } = {};
Expand Down Expand Up @@ -169,11 +193,39 @@ function validateTextDocument(textDocument: TextDocument): void {
});
}

let cachedCompletionList: CompletionList;
connection.onCompletion(textDocumentPosition => {
return runSafe(() => {
let document = documents.get(textDocumentPosition.textDocument.uri);
let stylesheet = stylesheets.get(document);
return getLanguageService(document).doComplete(document, textDocumentPosition.position, stylesheet)!; /* TODO: remove ! once LS has null annotations */
if (cachedCompletionList
&& !cachedCompletionList.isIncomplete
&& textDocumentPosition.context
&& textDocumentPosition.context.triggerKind === CompletionTriggerKind.TriggerForIncompleteCompletions
) {
let result: CompletionList = emmetDoComplete(document, textDocumentPosition.position, document.languageId, emmetSettings);
if (result && result.items) {
result.items.push(...cachedCompletionList.items);
} else {
result = cachedCompletionList;
cachedCompletionList = null;
}
return result;
}

cachedCompletionList = null;
let emmetCompletionList: CompletionList = {
isIncomplete: true,
items: undefined
};
const emmetCompletionParticipant: ICompletionParticipant = getEmmetCompletionParticipants(document, textDocumentPosition.position, document.languageId, emmetSettings, emmetCompletionList);
getLanguageService(document).setCompletionParticipants([emmetCompletionParticipant]);

const result = getLanguageService(document).doComplete(document, textDocumentPosition.position, stylesheets.get(document))!; /* TODO: remove ! once LS has null annotations */
if (emmetCompletionList && emmetCompletionList.items) {
cachedCompletionList = result;
return { isIncomplete: true, items: [...emmetCompletionList.items, ...result.items] };
}
return result;
}, null, `Error while computing completions for ${textDocumentPosition.textDocument.uri}`);
});

Expand Down
68 changes: 68 additions & 0 deletions extensions/css/server/src/test/emmet.test.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,68 @@
/*---------------------------------------------------------------------------------------------
* 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 'mocha';
import * as assert from 'assert';
import { getCSSLanguageService, getSCSSLanguageService } from 'vscode-css-languageservice/lib/cssLanguageService';
import { TextDocument, CompletionList } from 'vscode-languageserver-types';
import { getEmmetCompletionParticipants } from 'vscode-emmet-helper';

suite('Emmet Support', () => {

const cssLanguageService = getCSSLanguageService();
const scssLanguageService = getSCSSLanguageService();

function assertCompletions(syntax: string, value: string, expectedProposal: string, expectedProposalDoc: string): void {
const offset = value.indexOf('|');
value = value.substr(0, offset) + value.substr(offset + 1);

const document = TextDocument.create('test://test/test.' + syntax, syntax, 0, value);
const position = document.positionAt(offset);
const emmetCompletionList: CompletionList = {
isIncomplete: true,
items: undefined
}
const languageService = syntax === 'scss' ? scssLanguageService : cssLanguageService;
languageService.setCompletionParticipants([getEmmetCompletionParticipants(document, position, document.languageId, {}, emmetCompletionList)])
const stylesheet = languageService.parseStylesheet(document);
const list = languageService.doComplete!(document, position, stylesheet);

assert.ok(list);
assert.ok(emmetCompletionList)

if (expectedProposal && expectedProposalDoc) {
let actualLabels = emmetCompletionList!.items.map(c => c.label).sort();
let actualDocs = emmetCompletionList!.items.map(c => c.documentation).sort();
assert.ok(actualLabels.indexOf(expectedProposal) !== -1, 'Not found:' + expectedProposal + ' is ' + actualLabels.join(', '));
assert.ok(actualDocs.indexOf(expectedProposalDoc) !== -1, 'Not found:' + expectedProposalDoc + ' is ' + actualDocs.join(', '));
} else {
assert.ok(!emmetCompletionList || !emmetCompletionList.items);
}
}

test('Css Emmet Completions', function (): any {
assertCompletions('css', '.foo { display: none; m10| }', 'margin: 10px;', 'margin: 10px;');
assertCompletions('css', 'foo { display: none; pos:f| }', 'position: fixed;', 'position: fixed;');
assertCompletions('css', 'foo { display: none; margin: a| }', null, null);
assertCompletions('css', 'foo| { display: none; }', null, null);
assertCompletions('css', 'foo {| display: none; }', null, null);
assertCompletions('css', 'foo { display: none;| }', null, null);
assertCompletions('css', 'foo { display: none|; }', null, null);
assertCompletions('css', '.foo { display: none; -m-m10| }', 'margin: 10px;', '-moz-margin: 10px;\nmargin: 10px;');
});

test('Scss Emmet Completions', function (): any {
assertCompletions('scss', '.foo { display: none; .bar { m10| } }', 'margin: 10px;', 'margin: 10px;');
assertCompletions('scss', 'foo { display: none; .bar { pos:f| } }', 'position: fixed;', 'position: fixed;');
assertCompletions('scss', 'foo { display: none; margin: a| .bar {}}', null, null);
assertCompletions('scss', 'foo| { display: none; }', null, null);
assertCompletions('scss', 'foo {| display: none; }', null, null);
assertCompletions('scss', 'foo { display: none;| }', null, null);
assertCompletions('scss', 'foo { display: none|; }', null, null);
assertCompletions('scss', '.foo { display: none; -m-m10| }', 'margin: 10px;', '-moz-margin: 10px;\nmargin: 10px;');
});

});
3 changes: 3 additions & 0 deletions extensions/css/server/test/mocha.opts
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
--ui tdd
--useColors true
./out/test
50 changes: 37 additions & 13 deletions extensions/css/server/yarn.lock
Original file line number Diff line number Diff line change
Expand Up @@ -2,37 +2,61 @@
# yarn lockfile v1


"@emmetio/extract-abbreviation@^0.1.4":
version "0.1.4"
resolved "https://registry.yarnpkg.com/@emmetio/extract-abbreviation/-/extract-abbreviation-0.1.4.tgz#f5be070db97901ecc37e5204f2ace68242cdcefa"

"@types/mocha@2.2.33":
version "2.2.33"
resolved "https://registry.yarnpkg.com/@types/mocha/-/mocha-2.2.33.tgz#d79a0061ec270379f4d9e225f4096fb436669def"

"@types/node@7.0.43":
version "7.0.43"
resolved "https://registry.yarnpkg.com/@types/node/-/node-7.0.43.tgz#a187e08495a075f200ca946079c914e1a5fe962c"

jsonc-parser@^1.0.0:
version "1.0.0"
resolved "https://registry.yarnpkg.com/jsonc-parser/-/jsonc-parser-1.0.0.tgz#ddcc864ae708e60a7a6dd36daea00172fa8d9272"

vscode-css-languageservice@^3.0.5:
version "3.0.5"
resolved "https://registry.yarnpkg.com/vscode-css-languageservice/-/vscode-css-languageservice-3.0.5.tgz#8470989c07bbe740db4fa621423e98384d2c342f"
dependencies:
vscode-languageserver-types "3.5.0"
vscode-nls "^2.0.1"

vscode-jsonrpc@^3.5.0:
version "3.5.0"
resolved "https://registry.yarnpkg.com/vscode-jsonrpc/-/vscode-jsonrpc-3.5.0.tgz#87239d9e166b2d7352245b8a813597804c1d63aa"
vscode-emmet-helper@1.1.34:
version "1.1.34"
resolved "https://registry.yarnpkg.com/vscode-emmet-helper/-/vscode-emmet-helper-1.1.34.tgz#1e2d3e26c389efc6f73ceb4c0ed797ae07291874"
dependencies:
"@emmetio/extract-abbreviation" "^0.1.4"
jsonc-parser "^1.0.0"
vscode-languageserver-types "^3.6.0-next.1"

vscode-languageserver-protocol@^3.5.0:
version "3.5.0"
resolved "https://registry.yarnpkg.com/vscode-languageserver-protocol/-/vscode-languageserver-protocol-3.5.0.tgz#067c5cbe27709795398d119692c97ebba1452209"
vscode-jsonrpc@^3.6.0-next.1:
version "3.6.0-next.1"
resolved "https://registry.yarnpkg.com/vscode-jsonrpc/-/vscode-jsonrpc-3.6.0-next.1.tgz#3cb463dffe5842d6aec16718ca9252708cd6aabe"

vscode-languageserver-protocol@^3.6.0-next.5:
version "3.6.0-next.5"
resolved "https://registry.yarnpkg.com/vscode-languageserver-protocol/-/vscode-languageserver-protocol-3.6.0-next.5.tgz#ed2ec2db759826f753c0a13977dfb2bedc4d31b3"
dependencies:
vscode-jsonrpc "^3.5.0"
vscode-languageserver-types "^3.5.0"
vscode-jsonrpc "^3.6.0-next.1"
vscode-languageserver-types "^3.6.0-next.1"

vscode-languageserver-types@3.5.0, vscode-languageserver-types@^3.5.0:
vscode-languageserver-types@3.5.0:
version "3.5.0"
resolved "https://registry.yarnpkg.com/vscode-languageserver-types/-/vscode-languageserver-types-3.5.0.tgz#e48d79962f0b8e02de955e3f524908e2b19c0374"

vscode-languageserver@^3.5.0:
version "3.5.0"
resolved "https://registry.yarnpkg.com/vscode-languageserver/-/vscode-languageserver-3.5.0.tgz#d28099bc6ddda8c1dd16b707e454e1b1ddae0dba"
vscode-languageserver-types@^3.6.0-next.1:
version "3.6.0-next.1"
resolved "https://registry.yarnpkg.com/vscode-languageserver-types/-/vscode-languageserver-types-3.6.0-next.1.tgz#98e488d3f87b666b4ee1a3d89f0023e246d358f3"

vscode-languageserver@^4.0.0-next.4:
version "4.0.0-next.4"
resolved "https://registry.yarnpkg.com/vscode-languageserver/-/vscode-languageserver-4.0.0-next.4.tgz#162440b15bedaab07e1676f046e4d9b8578b3d92"
dependencies:
vscode-languageserver-protocol "^3.5.0"
vscode-languageserver-protocol "^3.6.0-next.5"
vscode-uri "^1.0.1"

vscode-nls@^2.0.1:
Expand Down
30 changes: 15 additions & 15 deletions extensions/css/yarn.lock
Original file line number Diff line number Diff line change
Expand Up @@ -6,26 +6,26 @@
version "7.0.43"
resolved "https://registry.yarnpkg.com/@types/node/-/node-7.0.43.tgz#a187e08495a075f200ca946079c914e1a5fe962c"

vscode-jsonrpc@^3.5.0:
version "3.5.0"
resolved "https://registry.yarnpkg.com/vscode-jsonrpc/-/vscode-jsonrpc-3.5.0.tgz#87239d9e166b2d7352245b8a813597804c1d63aa"
vscode-jsonrpc@^3.6.0-next.1:
version "3.6.0-next.1"
resolved "https://registry.yarnpkg.com/vscode-jsonrpc/-/vscode-jsonrpc-3.6.0-next.1.tgz#3cb463dffe5842d6aec16718ca9252708cd6aabe"

vscode-languageclient@^3.5.0:
version "3.5.0"
resolved "https://registry.yarnpkg.com/vscode-languageclient/-/vscode-languageclient-3.5.0.tgz#36d02cc186a8365a4467719a290fb200a9ae490a"
vscode-languageclient@^4.0.0-next.8:
version "4.0.0-next.8"
resolved "https://registry.yarnpkg.com/vscode-languageclient/-/vscode-languageclient-4.0.0-next.8.tgz#eb8eef0bf08399924f8fa520cb0b37071086e7c0"
dependencies:
vscode-languageserver-protocol "^3.5.0"
vscode-languageserver-protocol "^3.6.0-next.5"

vscode-languageserver-protocol@^3.5.0:
version "3.5.0"
resolved "https://registry.yarnpkg.com/vscode-languageserver-protocol/-/vscode-languageserver-protocol-3.5.0.tgz#067c5cbe27709795398d119692c97ebba1452209"
vscode-languageserver-protocol@^3.6.0-next.5:
version "3.6.0-next.5"
resolved "https://registry.yarnpkg.com/vscode-languageserver-protocol/-/vscode-languageserver-protocol-3.6.0-next.5.tgz#ed2ec2db759826f753c0a13977dfb2bedc4d31b3"
dependencies:
vscode-jsonrpc "^3.5.0"
vscode-languageserver-types "^3.5.0"
vscode-jsonrpc "^3.6.0-next.1"
vscode-languageserver-types "^3.6.0-next.1"

vscode-languageserver-types@^3.5.0:
version "3.5.0"
resolved "https://registry.yarnpkg.com/vscode-languageserver-types/-/vscode-languageserver-types-3.5.0.tgz#e48d79962f0b8e02de955e3f524908e2b19c0374"
vscode-languageserver-types@^3.6.0-next.1:
version "3.6.0-next.1"
resolved "https://registry.yarnpkg.com/vscode-languageserver-types/-/vscode-languageserver-types-3.6.0-next.1.tgz#98e488d3f87b666b4ee1a3d89f0023e246d358f3"

vscode-nls@^3.2.1:
version "3.2.1"
Expand Down
2 changes: 1 addition & 1 deletion extensions/emmet/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -337,7 +337,7 @@
"@emmetio/html-matcher": "^0.3.3",
"@emmetio/css-parser": "ramya-rao-a/css-parser#vscode",
"@emmetio/math-expression": "^0.1.1",
"vscode-emmet-helper": "^1.1.25",
"vscode-emmet-helper": "^1.1.32",
"vscode-languageserver-types": "^3.5.0",
"image-size": "^0.5.2",
"vscode-nls": "3.2.1"
Expand Down
Loading