mirror of
https://github.com/microsoft/vscode.git
synced 2026-04-25 11:08:51 +01:00
[html] resolve JS completion proposals
This commit is contained in:
@@ -54,7 +54,7 @@ connection.onInitialize((params: InitializeParams): InitializeResult => {
|
||||
capabilities: {
|
||||
// Tell the client that the server works in FULL text document sync mode
|
||||
textDocumentSync: documents.syncKind,
|
||||
completionProvider: { resolveProvider: false, triggerCharacters: ['.', ':', '<', '"', '=', '/'] },
|
||||
completionProvider: { resolveProvider: true, triggerCharacters: ['.', ':', '<', '"', '=', '/'] },
|
||||
hoverProvider: true,
|
||||
documentHighlightProvider: true,
|
||||
documentRangeFormattingProvider: initializationOptions && initializationOptions['format.enable'],
|
||||
@@ -127,6 +127,18 @@ connection.onCompletion(textDocumentPosition => {
|
||||
return { isIncomplete: true, items: [] };
|
||||
});
|
||||
|
||||
connection.onCompletionResolve(item => {
|
||||
let data = item.data;
|
||||
if (data && data.languageId && data.uri) {
|
||||
let mode = languageModes.getMode(data.languageId);
|
||||
let document = documents.get(data.uri);
|
||||
if (mode && mode.doResolve && document) {
|
||||
return mode.doResolve(document, item);
|
||||
}
|
||||
}
|
||||
return item;
|
||||
});
|
||||
|
||||
connection.onHover(textDocumentPosition => {
|
||||
let document = documents.get(textDocumentPosition.textDocument.uri);
|
||||
let mode = languageModes.getModeAtPosition(document, textDocumentPosition.position);
|
||||
|
||||
@@ -7,8 +7,9 @@
|
||||
import { LanguageModelCache, getLanguageModelCache } from '../languageModelCache';
|
||||
import { LanguageService as HTMLLanguageService, HTMLDocument } from 'vscode-html-languageservice';
|
||||
import { getEmbeddedDocument } from './embeddedSupport';
|
||||
import { Location, SignatureHelp, SignatureInformation, ParameterInformation, Definition, TextEdit, TextDocument, Diagnostic, DiagnosticSeverity, Range, CompletionItemKind, Hover, MarkedString, DocumentHighlight, DocumentHighlightKind, CompletionList, Position, FormattingOptions } from 'vscode-languageserver-types';
|
||||
import { 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 } from './languageModes';
|
||||
import { getWordAtText } from '../utils/words';
|
||||
|
||||
import ts = require('./typescript/typescriptServices');
|
||||
import { contents as libdts } from './typescript/lib-ts';
|
||||
@@ -19,6 +20,8 @@ const DEFAULT_LIB = {
|
||||
};
|
||||
const FILE_NAME = 'typescript://singlefile/1'; // the same 'file' is used for all contents
|
||||
|
||||
const JS_WORD_REGEX = /(-?\d*\.\d\w*)|([^\`\~\!\@\#\%\^\&\*\(\)\-\=\+\[\{\]\}\\\|\;\:\'\"\,\.\<\>\/\?\s]+)/g;
|
||||
|
||||
export function getJavascriptMode(htmlLanguageService: HTMLLanguageService, htmlDocuments: LanguageModelCache<HTMLDocument>): LanguageMode {
|
||||
let compilerOptions = { allowNonTsExtensions: true, allowJs: true, target: ts.ScriptTarget.Latest };
|
||||
let currentTextDocument: TextDocument;
|
||||
@@ -66,20 +69,41 @@ export function getJavascriptMode(htmlLanguageService: HTMLLanguageService, html
|
||||
},
|
||||
doComplete(document: TextDocument, position: Position): CompletionList {
|
||||
currentTextDocument = jsDocuments.get(document);
|
||||
let completions = jsLanguageService.getCompletionsAtPosition(FILE_NAME, currentTextDocument.offsetAt(position));
|
||||
let offset = currentTextDocument.offsetAt(position);
|
||||
let completions = jsLanguageService.getCompletionsAtPosition(FILE_NAME, offset);
|
||||
if (!completions) {
|
||||
return { isIncomplete: false, items: [] };
|
||||
}
|
||||
let replaceRange = convertRange(currentTextDocument, getWordAtText(currentTextDocument.getText(), offset, JS_WORD_REGEX));
|
||||
return {
|
||||
isIncomplete: false,
|
||||
items: !completions ? [] : completions.entries.map(entry => {
|
||||
items: completions.entries.map(entry => {
|
||||
return {
|
||||
uri: document.uri,
|
||||
position: position,
|
||||
label: entry.name,
|
||||
sortText: entry.sortText,
|
||||
kind: convertKind(entry.kind)
|
||||
kind: convertKind(entry.kind),
|
||||
textEdit: TextEdit.replace(replaceRange, entry.name),
|
||||
data: { // data used for resolving item details (see 'doResolve')
|
||||
languageId: 'javascript',
|
||||
uri: document.uri,
|
||||
offset: offset
|
||||
}
|
||||
};
|
||||
})
|
||||
};
|
||||
},
|
||||
doResolve(document: TextDocument, item: CompletionItem): CompletionItem {
|
||||
currentTextDocument = jsDocuments.get(document);
|
||||
let details = jsLanguageService.getCompletionEntryDetails(FILE_NAME, item.data.offset, item.label);
|
||||
if (details) {
|
||||
item.detail = ts.displayPartsToString(details.displayParts);
|
||||
item.documentation = ts.displayPartsToString(details.documentation);
|
||||
delete item.data;
|
||||
}
|
||||
return item;
|
||||
},
|
||||
doHover(document: TextDocument, position: Position): Hover {
|
||||
currentTextDocument = jsDocuments.get(document);
|
||||
let info = jsLanguageService.getQuickInfoAtPosition(FILE_NAME, currentTextDocument.offsetAt(position));
|
||||
@@ -238,7 +262,7 @@ function convertOptions(options: FormattingOptions, formatSettings?: any): ts.Fo
|
||||
IndentSize: options.tabSize,
|
||||
IndentStyle: ts.IndentStyle.Smart,
|
||||
NewLineCharacter: '\n',
|
||||
BaseIndentSize: 1, //
|
||||
BaseIndentSize: 1, //
|
||||
InsertSpaceAfterCommaDelimiter: !formatSettings || formatSettings.insertSpaceAfterCommaDelimiter,
|
||||
InsertSpaceAfterSemicolonInForStatements: !formatSettings || formatSettings.insertSpaceAfterSemicolonInForStatements,
|
||||
InsertSpaceBeforeAndAfterBinaryOperators: !formatSettings || formatSettings.insertSpaceBeforeAndAfterBinaryOperators,
|
||||
|
||||
@@ -5,7 +5,10 @@
|
||||
'use strict';
|
||||
|
||||
import { HTMLDocument, getLanguageService as getHTMLLanguageService, DocumentContext } from 'vscode-html-languageservice';
|
||||
import { Location, SignatureHelp, Definition, TextEdit, TextDocument, Diagnostic, DocumentLink, Range, Hover, DocumentHighlight, CompletionList, Position, FormattingOptions } from 'vscode-languageserver-types';
|
||||
import {
|
||||
CompletionItem, Location, SignatureHelp, Definition, TextEdit, TextDocument, Diagnostic, DocumentLink, Range,
|
||||
Hover, DocumentHighlight, CompletionList, Position, FormattingOptions
|
||||
} from 'vscode-languageserver-types';
|
||||
|
||||
import { getLanguageModelCache } from '../languageModelCache';
|
||||
import { getLanguageAtPosition, getLanguagesInContent } from './embeddedSupport';
|
||||
@@ -17,6 +20,7 @@ export interface LanguageMode {
|
||||
configure?: (options: any) => void;
|
||||
doValidation?: (document: TextDocument) => Diagnostic[];
|
||||
doComplete?: (document: TextDocument, position: Position) => CompletionList;
|
||||
doResolve?: (document: TextDocument, item: CompletionItem) => CompletionItem;
|
||||
doHover?: (document: TextDocument, position: Position) => Hover;
|
||||
doSignatureHelp?: (document: TextDocument, position: Position) => SignatureHelp;
|
||||
findDocumentHighlight?: (document: TextDocument, position: Position) => DocumentHighlight[];
|
||||
|
||||
46
extensions/html/server/src/test/words.test.ts
Normal file
46
extensions/html/server/src/test/words.test.ts
Normal file
@@ -0,0 +1,46 @@
|
||||
/*---------------------------------------------------------------------------------------------
|
||||
* 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 * as assert from 'assert';
|
||||
import * as words from '../utils/words';
|
||||
|
||||
suite('Words', () => {
|
||||
|
||||
let wordRegex = /(-?\d*\.\d\w*)|([^\`\~\!\@\#\%\^\&\*\(\)\-\=\+\[\{\]\}\\\|\;\:\'\"\,\.\<\>\/\?\s]+)/g;
|
||||
|
||||
function assertWord(value: string, expected: string): void {
|
||||
let offset = value.indexOf('|');
|
||||
value = value.substr(0, offset) + value.substr(offset + 1);
|
||||
|
||||
let actualRange = words.getWordAtText(value, offset, wordRegex);
|
||||
assert(actualRange.start <= offset);
|
||||
assert(actualRange.start + actualRange.length >= offset);
|
||||
assert.equal(value.substr(actualRange.start, actualRange.length), expected);
|
||||
}
|
||||
|
||||
|
||||
test('Basic', function (): any {
|
||||
assertWord('|var x1 = new F<A>(a, b);', 'var');
|
||||
assertWord('v|ar x1 = new F<A>(a, b);', 'var');
|
||||
assertWord('var| x1 = new F<A>(a, b);', 'var');
|
||||
assertWord('var |x1 = new F<A>(a, b);', 'x1');
|
||||
assertWord('var x1| = new F<A>(a, b);', 'x1');
|
||||
assertWord('var x1 = new |F<A>(a, b);', 'F');
|
||||
assertWord('var x1 = new F<|A>(a, b);', 'A');
|
||||
assertWord('var x1 = new F<A>(|a, b);', 'a');
|
||||
assertWord('var x1 = new F<A>(a, b|);', 'b');
|
||||
assertWord('var x1 = new F<A>(a, b)|;', '');
|
||||
assertWord('var x1 = new F<A>(a, b)|;|', '');
|
||||
assertWord('var x1 = | new F<A>(a, b)|;|', '');
|
||||
});
|
||||
|
||||
test('Multiline', function (): any {
|
||||
assertWord('console.log("hello");\n|var x1 = new F<A>(a, b);', 'var');
|
||||
assertWord('console.log("hello");\n|\nvar x1 = new F<A>(a, b);', '');
|
||||
assertWord('console.log("hello");\n\r |var x1 = new F<A>(a, b);', 'var');
|
||||
});
|
||||
|
||||
});
|
||||
35
extensions/html/server/src/utils/words.ts
Normal file
35
extensions/html/server/src/utils/words.ts
Normal file
@@ -0,0 +1,35 @@
|
||||
/*---------------------------------------------------------------------------------------------
|
||||
* Copyright (c) Microsoft Corporation. All rights reserved.
|
||||
* Licensed under the MIT License. See License.txt in the project root for license information.
|
||||
*--------------------------------------------------------------------------------------------*/
|
||||
'use strict';
|
||||
|
||||
export function getWordAtText(text: string, offset: number, wordDefinition: RegExp): { start: number, length: number } {
|
||||
let lineStart = offset;
|
||||
while (lineStart > 0 && !isNewlineCharacter(text.charCodeAt(lineStart - 1))) {
|
||||
lineStart--;
|
||||
}
|
||||
let offsetInLine = offset - lineStart;
|
||||
let lineText = text.substr(lineStart);
|
||||
|
||||
// make a copy of the regex as to not keep the state
|
||||
let flags = wordDefinition.ignoreCase ? 'gi' : 'g';
|
||||
wordDefinition = new RegExp(wordDefinition.source, flags);
|
||||
|
||||
let match = wordDefinition.exec(lineText);
|
||||
while (match && match.index + match[0].length < offsetInLine) {
|
||||
match = wordDefinition.exec(lineText);
|
||||
}
|
||||
if (match && match.index <= offsetInLine) {
|
||||
return { start: match.index + lineStart, length: match[0].length };
|
||||
}
|
||||
|
||||
return { start: offset, length: 0 };
|
||||
}
|
||||
|
||||
|
||||
const CR = '\r'.charCodeAt(0);
|
||||
const NL = '\n'.charCodeAt(0);
|
||||
function isNewlineCharacter(charCode: number) {
|
||||
return charCode === CR || charCode === NL;
|
||||
}
|
||||
Reference in New Issue
Block a user