diff --git a/extensions/html-language-features/server/src/htmlServerMain.ts b/extensions/html-language-features/server/src/htmlServerMain.ts
index e22ad15c346..8afdc4d3222 100644
--- a/extensions/html-language-features/server/src/htmlServerMain.ts
+++ b/extensions/html-language-features/server/src/htmlServerMain.ts
@@ -19,7 +19,6 @@ import { getDocumentContext } from './utils/documentContext';
import uri from 'vscode-uri';
import { formatError, runSafe, runSafeAsync } from './utils/runner';
import { doComplete as emmetDoComplete, updateExtensionsPath as updateEmmetExtensionsPath, getEmmetCompletionParticipants } from 'vscode-emmet-helper';
-import { getPathCompletionParticipant } from './modes/pathCompletion';
import { FoldingRangesRequest, FoldingProviderServerCapabilities } from './protocol/foldingProvider.proposed';
import { getFoldingRegions } from './modes/htmlFolding';
@@ -95,7 +94,11 @@ connection.onInitialize((params: InitializeParams): InitializeResult => {
}
}
- languageModes = getLanguageModes(initializationOptions ? initializationOptions.embeddedLanguages : { css: true, javascript: true });
+ const workspace = {
+ get settings() { return globalSettings; },
+ get folders() { return workspaceFolders; }
+ };
+ languageModes = getLanguageModes(initializationOptions ? initializationOptions.embeddedLanguages : { css: true, javascript: true }, workspace);
documents.onDidClose(e => {
languageModes.onDocumentRemoved(e.document);
});
@@ -149,6 +152,7 @@ connection.onInitialized((p) => {
}
}
workspaceFolders = updatedFolders.concat(toAdd);
+ documents.all().forEach(triggerValidation);
});
}
});
@@ -158,13 +162,7 @@ let formatterRegistration: Thenable | null = null;
// The settings have changed. Is send on server activation as well.
connection.onDidChangeConfiguration((change) => {
globalSettings = change.settings;
-
documentSettings = {}; // reset all document settings
- languageModes.getAllModes().forEach(m => {
- if (m.configure) {
- m.configure(change.settings);
- }
- });
documents.all().forEach(triggerValidation);
// dynamically enable & disable the formatter
@@ -288,24 +286,12 @@ connection.onCompletion(async (textDocumentPosition, token) => {
cachedCompletionList = null;
const emmetCompletionList = CompletionList.create([], false);
- const pathCompletionList = CompletionList.create([], false);
const emmetCompletionParticipant = getEmmetCompletionParticipants(document, textDocumentPosition.position, mode.getId(), emmetSettings, emmetCompletionList);
const completionParticipants = [emmetCompletionParticipant];
- // Ideally, fix this in the Language Service side
- // Check participants' methods before calling them
- if (mode.getId() === 'html') {
- const pathCompletionParticipant = getPathCompletionParticipant(document, workspaceFolders, pathCompletionList);
- completionParticipants.push(pathCompletionParticipant);
- }
let settings = await getDocumentSettings(document, () => doComplete.length > 2);
let result = doComplete(document, textDocumentPosition.position, settings, completionParticipants);
- if (!result) {
- result = pathCompletionList;
- } else {
- result.items.push(...pathCompletionList.items);
- }
if (emmetCompletionList.isIncomplete) {
cachedCompletionList = result;
if (hexColorRegex.test(emmetCompletionList.items[0].label) && result.items.some(x => x.label === emmetCompletionList.items[0].label)) {
diff --git a/extensions/html-language-features/server/src/modes/cssMode.ts b/extensions/html-language-features/server/src/modes/cssMode.ts
index fef5c40b6fc..61f00efdbd5 100644
--- a/extensions/html-language-features/server/src/modes/cssMode.ts
+++ b/extensions/html-language-features/server/src/modes/cssMode.ts
@@ -5,14 +5,14 @@
'use strict';
import { LanguageModelCache, getLanguageModelCache } from '../languageModelCache';
-import { TextDocument, Position, Range } from 'vscode-languageserver-types';
+import { TextDocument, Position, Range, CompletionList } from 'vscode-languageserver-types';
import { getCSSLanguageService, Stylesheet, ICompletionParticipant } from 'vscode-css-languageservice';
-import { LanguageMode, Settings } from './languageModes';
+import { LanguageMode, Workspace } from './languageModes';
import { HTMLDocumentRegions, CSS_STYLE_RULE } from './embeddedSupport';
import { Color } from 'vscode-languageserver';
import { extractAbbreviation } from 'vscode-emmet-helper';
-export function getCSSMode(documentRegions: LanguageModelCache): LanguageMode {
+export function getCSSMode(documentRegions: LanguageModelCache, workspace: Workspace): LanguageMode {
let cssLanguageService = getCSSLanguageService();
let embeddedCSSDocuments = getLanguageModelCache(10, 60, document => documentRegions.get(document).getEmbeddedDocument('css'));
let cssStylesheets = getLanguageModelCache(10, 60, document => cssLanguageService.parseStylesheet(document));
@@ -21,19 +21,16 @@ export function getCSSMode(documentRegions: LanguageModelCache(10, 60, document => htmlLanguageService.parseHTMLDocument(document));
- let completionParticipants: ICompletionParticipant[] = [];
return {
getId() {
return 'html';
},
- configure(options: any) {
- globalSettings = options;
- },
- doComplete(document: TextDocument, position: Position, settings: Settings = globalSettings, registeredCompletionParticipants?: ICompletionParticipant[]) {
- if (registeredCompletionParticipants) {
- completionParticipants = registeredCompletionParticipants;
- }
+ doComplete(document: TextDocument, position: Position, settings = workspace.settings, completionParticipants?: ICompletionParticipant[]) {
let options = settings && settings.html && settings.html.suggest;
let doAutoComplete = settings && settings.html && settings.html.autoClosingTags;
if (doAutoComplete) {
options.hideAutoCompleteProposals = true;
}
+ let pathCompletionProposals: CompletionItem[] = [];
+ let participants = [getPathCompletionParticipant(document, workspace.folders, pathCompletionProposals)];
+ if (completionParticipants) {
+ participants.push(...completionParticipants);
+ }
+ htmlLanguageService.setCompletionParticipants(participants);
const htmlDocument = htmlDocuments.get(document);
- htmlLanguageService.setCompletionParticipants(completionParticipants);
-
- return htmlLanguageService.doComplete(document, position, htmlDocument, options);
- },
- setCompletionParticipants(registeredCompletionParticipants: any[]) {
- completionParticipants = registeredCompletionParticipants;
+ let completionList = htmlLanguageService.doComplete(document, position, htmlDocument, options);
+ completionList.items.push(...pathCompletionProposals);
+ return completionList;
},
doHover(document: TextDocument, position: Position) {
return htmlLanguageService.doHover(document, position, htmlDocuments.get(document));
@@ -53,7 +49,7 @@ export function getHTMLMode(htmlLanguageService: HTMLLanguageService): LanguageM
findDocumentSymbols(document: TextDocument) {
return htmlLanguageService.findDocumentSymbols(document, htmlDocuments.get(document));
},
- format(document: TextDocument, range: Range, formatParams: FormattingOptions, settings: Settings = globalSettings) {
+ format(document: TextDocument, range: Range, formatParams: FormattingOptions, settings = workspace.settings) {
let formatSettings: HTMLFormatConfiguration = settings && settings.html && settings.html.format;
if (formatSettings) {
formatSettings = merge(formatSettings, {});
diff --git a/extensions/html-language-features/server/src/modes/javascriptMode.ts b/extensions/html-language-features/server/src/modes/javascriptMode.ts
index c7c803e5684..23a0e5b7134 100644
--- a/extensions/html-language-features/server/src/modes/javascriptMode.ts
+++ b/extensions/html-language-features/server/src/modes/javascriptMode.ts
@@ -6,7 +6,7 @@
import { LanguageModelCache, getLanguageModelCache } from '../languageModelCache';
import { SymbolInformation, SymbolKind, CompletionItem, Location, SignatureHelp, SignatureInformation, ParameterInformation, Definition, TextEdit, TextDocument, Diagnostic, DiagnosticSeverity, Range, CompletionItemKind, Hover, MarkedString, DocumentHighlight, DocumentHighlightKind, CompletionList, Position, FormattingOptions } from 'vscode-languageserver-types';
-import { LanguageMode, Settings } from './languageModes';
+import { LanguageMode, Settings, Workspace } from './languageModes';
import { getWordAtText, startsWith, isWhitespaceOnly, repeat } from '../utils/strings';
import { HTMLDocumentRegions } from './embeddedSupport';
@@ -19,7 +19,7 @@ const JQUERY_D_TS = join(__dirname, '../../lib/jquery.d.ts');
const JS_WORD_REGEX = /(-?\d*\.\d\w*)|([^\`\~\!\@\#\%\^\&\*\(\)\-\=\+\[\{\]\}\\\|\;\:\'\"\,\.\<\>\/\?\s]+)/g;
-export function getJavascriptMode(documentRegions: LanguageModelCache): LanguageMode {
+export function getJavascriptMode(documentRegions: LanguageModelCache, workspace: Workspace): LanguageMode {
let jsDocuments = getLanguageModelCache(10, 60, document => documentRegions.get(document).getEmbeddedDocument('javascript'));
let compilerOptions: ts.CompilerOptions = { allowNonTsExtensions: true, allowJs: true, lib: ['lib.es6.d.ts'], target: ts.ScriptTarget.Latest, moduleResolution: ts.ModuleResolutionKind.Classic };
@@ -67,9 +67,6 @@ export function getJavascriptMode(documentRegions: LanguageModelCache;
+export interface Workspace {
+ readonly settings: Settings;
+ readonly folders: WorkspaceFolder[];
}
export interface LanguageMode {
getId(): string;
- configure?: (options: Settings) => void;
doValidation?: (document: TextDocument, settings?: Settings) => Diagnostic[];
- doComplete?: (document: TextDocument, position: Position, settings?: Settings, registeredCompletionParticipants?: any[]) => CompletionList | null;
- setCompletionParticipants?: (registeredCompletionParticipants: any[]) => void;
+ doComplete?: (document: TextDocument, position: Position, settings?: Settings, registeredCompletionParticipants?: any[]) => CompletionList;
doResolve?: (document: TextDocument, item: CompletionItem) => CompletionItem;
doHover?: (document: TextDocument, position: Position) => Hover | null;
doSignatureHelp?: (document: TextDocument, position: Position) => SignatureHelp | null;
@@ -69,7 +68,7 @@ export interface LanguageModeRange extends Range {
attributeValue?: boolean;
}
-export function getLanguageModes(supportedLanguages: { [languageId: string]: boolean; }): LanguageModes {
+export function getLanguageModes(supportedLanguages: { [languageId: string]: boolean; }, workspace: Workspace): LanguageModes {
var htmlLanguageService = getHTMLLanguageService();
let documentRegions = getLanguageModelCache(10, 60, document => getDocumentRegions(htmlLanguageService, document));
@@ -78,12 +77,12 @@ export function getLanguageModes(supportedLanguages: { [languageId: string]: boo
modelCaches.push(documentRegions);
let modes = Object.create(null);
- modes['html'] = getHTMLMode(htmlLanguageService);
+ modes['html'] = getHTMLMode(htmlLanguageService, workspace);
if (supportedLanguages['css']) {
- modes['css'] = getCSSMode(documentRegions);
+ modes['css'] = getCSSMode(documentRegions, workspace);
}
if (supportedLanguages['javascript']) {
- modes['javascript'] = getJavascriptMode(documentRegions);
+ modes['javascript'] = getJavascriptMode(documentRegions, workspace);
}
return {
getModeAtPosition(document: TextDocument, position: Position): LanguageMode | undefined {
diff --git a/extensions/html-language-features/server/src/modes/pathCompletion.ts b/extensions/html-language-features/server/src/modes/pathCompletion.ts
index 5e2c2077b16..afb5089fbfd 100644
--- a/extensions/html-language-features/server/src/modes/pathCompletion.ts
+++ b/extensions/html-language-features/server/src/modes/pathCompletion.ts
@@ -4,7 +4,7 @@
*--------------------------------------------------------------------------------------------*/
'use strict';
-import { TextDocument, CompletionList, CompletionItemKind, CompletionItem, TextEdit, Range, Position } from 'vscode-languageserver-types';
+import { TextDocument, CompletionItemKind, CompletionItem, TextEdit, Range, Position } from 'vscode-languageserver-types';
import { WorkspaceFolder } from 'vscode-languageserver';
import * as path from 'path';
import * as fs from 'fs';
@@ -15,22 +15,21 @@ import { contains } from '../utils/arrays';
export function getPathCompletionParticipant(
document: TextDocument,
- workspaceFolders: WorkspaceFolder[] | undefined,
- result: CompletionList
+ workspaceFolders: WorkspaceFolder[],
+ result: CompletionItem[]
): ICompletionParticipant {
return {
onHtmlAttributeValue: ({ tag, position, attribute, value: valueBeforeCursor, range }) => {
const fullValue = getFullValueWithoutQuotes(document, range);
if (shouldDoPathCompletion(tag, attribute, fullValue)) {
- if (!workspaceFolders || workspaceFolders.length === 0) {
+ if (workspaceFolders.length === 0) {
return;
}
const workspaceRoot = resolveWorkspaceRoot(document, workspaceFolders);
const paths = providePaths(valueBeforeCursor, URI.parse(document.uri).fsPath, workspaceRoot);
- const suggestions = paths.map(p => pathToSuggestion(p, valueBeforeCursor, fullValue, range));
- result.items = [...suggestions, ...result.items];
+ result.push(...paths.map(p => pathToSuggestion(p, valueBeforeCursor, fullValue, range)));
}
}
};
diff --git a/extensions/html-language-features/server/src/test/completions.test.ts b/extensions/html-language-features/server/src/test/completions.test.ts
index 80503b77362..23b119eb826 100644
--- a/extensions/html-language-features/server/src/test/completions.test.ts
+++ b/extensions/html-language-features/server/src/test/completions.test.ts
@@ -7,10 +7,9 @@
import 'mocha';
import * as assert from 'assert';
import * as path from 'path';
-// import Uri from 'vscode-uri';
+import Uri from 'vscode-uri';
import { TextDocument, CompletionList, CompletionItemKind } from 'vscode-languageserver-types';
import { getLanguageModes } from '../modes/languageModes';
-import { getPathCompletionParticipant } from '../modes/pathCompletion';
import { WorkspaceFolder } from 'vscode-languageserver';
export interface ItemDescription {
@@ -22,7 +21,7 @@ export interface ItemDescription {
notAvailable?: boolean;
}
-export function assertCompletion (completions: CompletionList, expected: ItemDescription, document: TextDocument, offset: number) {
+export function assertCompletion(completions: CompletionList, expected: ItemDescription, document: TextDocument, offset: number) {
let matches = completions.items.filter(completion => {
return completion.label === expected.label;
});
@@ -49,32 +48,22 @@ export function assertCompletion (completions: CompletionList, expected: ItemDes
const testUri = 'test://test/test.html';
-export function testCompletionFor(
- value: string,
- expected: { count?: number, items?: ItemDescription[] },
- uri = testUri,
- workspaceFolders?: WorkspaceFolder[]
-): void {
+export function testCompletionFor(value: string, expected: { count?: number, items?: ItemDescription[] }, uri = testUri, workspaceFolders?: WorkspaceFolder[]): void {
let offset = value.indexOf('|');
value = value.substr(0, offset) + value.substr(offset + 1);
+ let workspace = {
+ settings: {},
+ folders: workspaceFolders || [{ name: 'x', uri: uri.substr(0, uri.lastIndexOf('/')) }]
+ };
+
let document = TextDocument.create(uri, 'html', 0, value);
let position = document.positionAt(offset);
- var languageModes = getLanguageModes({ css: true, javascript: true });
+ var languageModes = getLanguageModes({ css: true, javascript: true }, workspace);
var mode = languageModes.getModeAtPosition(document, position)!;
- if (!workspaceFolders) {
- workspaceFolders = [{ name: 'x', uri: path.dirname(uri) }];
- }
-
- let participantResult = CompletionList.create([]);
- if (mode.setCompletionParticipants) {
- mode.setCompletionParticipants([getPathCompletionParticipant(document, workspaceFolders, participantResult)]);
- }
-
- let list = mode.doComplete!(document, position)!;
- list.items = list.items.concat(participantResult.items);
+ let list = mode.doComplete!(document, position);
if (expected.count) {
assert.equal(list.items, expected.count);
@@ -107,10 +96,10 @@ suite('HTML Path Completion', () => {
command: 'editor.action.triggerSuggest'
};
- const fixtureRoot = path.resolve(__dirname, 'pathcompletionfixtures');
- const fixtureWorkspace = { name: 'fixture', uri: fixtureRoot };
- const indexHtmlUri = path.resolve(fixtureRoot, 'index.html');
- const aboutHtmlUri = path.resolve(fixtureRoot, 'about/about.html');
+ const fixtureRoot = path.resolve(__dirname, 'pathCompletionFixtures');
+ const fixtureWorkspace = { name: 'fixture', uri: Uri.file(fixtureRoot).toString() };
+ const indexHtmlUri = Uri.file(path.resolve(fixtureRoot, 'index.html')).toString();
+ const aboutHtmlUri = Uri.file(path.resolve(fixtureRoot, 'about/about.html')).toString();
test('Basics - Correct label/kind/result/command', () => {
testCompletionFor('