mirror of
https://github.com/microsoft/vscode.git
synced 2026-04-21 17:19:01 +01:00
[html] make mode services async
This commit is contained in:
@@ -5,7 +5,7 @@
|
||||
|
||||
import { LanguageModelCache, getLanguageModelCache } from '../languageModelCache';
|
||||
import { Stylesheet, LanguageService as CSSLanguageService } from 'vscode-css-languageservice';
|
||||
import { FoldingRange, LanguageMode, Workspace, Color, TextDocument, Position, Range, CompletionList, DocumentContext } from './languageModes';
|
||||
import { LanguageMode, Workspace, Color, TextDocument, Position, Range, CompletionList, DocumentContext } from './languageModes';
|
||||
import { HTMLDocumentRegions, CSS_STYLE_RULE } from './embeddedSupport';
|
||||
|
||||
export function getCSSMode(cssLanguageService: CSSLanguageService, documentRegions: LanguageModelCache<HTMLDocumentRegions>, workspace: Workspace): LanguageMode {
|
||||
@@ -16,48 +16,48 @@ export function getCSSMode(cssLanguageService: CSSLanguageService, documentRegio
|
||||
getId() {
|
||||
return 'css';
|
||||
},
|
||||
doValidation(document: TextDocument, settings = workspace.settings) {
|
||||
async doValidation(document: TextDocument, settings = workspace.settings) {
|
||||
let embedded = embeddedCSSDocuments.get(document);
|
||||
return cssLanguageService.doValidation(embedded, cssStylesheets.get(embedded), settings && settings.css);
|
||||
},
|
||||
doComplete(document: TextDocument, position: Position, documentContext: DocumentContext, _settings = workspace.settings) {
|
||||
async doComplete(document: TextDocument, position: Position, documentContext: DocumentContext, _settings = workspace.settings) {
|
||||
let embedded = embeddedCSSDocuments.get(document);
|
||||
const stylesheet = cssStylesheets.get(embedded);
|
||||
return cssLanguageService.doComplete2(embedded, position, stylesheet, documentContext) || CompletionList.create();
|
||||
},
|
||||
doHover(document: TextDocument, position: Position) {
|
||||
async doHover(document: TextDocument, position: Position) {
|
||||
let embedded = embeddedCSSDocuments.get(document);
|
||||
return cssLanguageService.doHover(embedded, position, cssStylesheets.get(embedded));
|
||||
},
|
||||
findDocumentHighlight(document: TextDocument, position: Position) {
|
||||
async findDocumentHighlight(document: TextDocument, position: Position) {
|
||||
let embedded = embeddedCSSDocuments.get(document);
|
||||
return cssLanguageService.findDocumentHighlights(embedded, position, cssStylesheets.get(embedded));
|
||||
},
|
||||
findDocumentSymbols(document: TextDocument) {
|
||||
async findDocumentSymbols(document: TextDocument) {
|
||||
let embedded = embeddedCSSDocuments.get(document);
|
||||
return cssLanguageService.findDocumentSymbols(embedded, cssStylesheets.get(embedded)).filter(s => s.name !== CSS_STYLE_RULE);
|
||||
},
|
||||
findDefinition(document: TextDocument, position: Position) {
|
||||
async findDefinition(document: TextDocument, position: Position) {
|
||||
let embedded = embeddedCSSDocuments.get(document);
|
||||
return cssLanguageService.findDefinition(embedded, position, cssStylesheets.get(embedded));
|
||||
},
|
||||
findReferences(document: TextDocument, position: Position) {
|
||||
async findReferences(document: TextDocument, position: Position) {
|
||||
let embedded = embeddedCSSDocuments.get(document);
|
||||
return cssLanguageService.findReferences(embedded, position, cssStylesheets.get(embedded));
|
||||
},
|
||||
findDocumentColors(document: TextDocument) {
|
||||
async findDocumentColors(document: TextDocument) {
|
||||
let embedded = embeddedCSSDocuments.get(document);
|
||||
return cssLanguageService.findDocumentColors(embedded, cssStylesheets.get(embedded));
|
||||
},
|
||||
getColorPresentations(document: TextDocument, color: Color, range: Range) {
|
||||
async getColorPresentations(document: TextDocument, color: Color, range: Range) {
|
||||
let embedded = embeddedCSSDocuments.get(document);
|
||||
return cssLanguageService.getColorPresentations(embedded, cssStylesheets.get(embedded), color, range);
|
||||
},
|
||||
getFoldingRanges(document: TextDocument): FoldingRange[] {
|
||||
async getFoldingRanges(document: TextDocument) {
|
||||
let embedded = embeddedCSSDocuments.get(document);
|
||||
return cssLanguageService.getFoldingRanges(embedded, {});
|
||||
},
|
||||
getSelectionRange(document: TextDocument, position: Position) {
|
||||
async getSelectionRange(document: TextDocument, position: Position) {
|
||||
let embedded = embeddedCSSDocuments.get(document);
|
||||
return cssLanguageService.getSelectionRanges(embedded, [position], cssStylesheets.get(embedded))[0];
|
||||
},
|
||||
|
||||
@@ -7,7 +7,7 @@ import { LanguageModes, Settings, LanguageModeRange, TextDocument, Range, TextEd
|
||||
import { pushAll } from '../utils/arrays';
|
||||
import { isEOL } from '../utils/strings';
|
||||
|
||||
export function format(languageModes: LanguageModes, document: TextDocument, formatRange: Range, formattingOptions: FormattingOptions, settings: Settings | undefined, enabledModes: { [mode: string]: boolean }) {
|
||||
export async function format(languageModes: LanguageModes, document: TextDocument, formatRange: Range, formattingOptions: FormattingOptions, settings: Settings | undefined, enabledModes: { [mode: string]: boolean }) {
|
||||
let result: TextEdit[] = [];
|
||||
|
||||
let endPos = formatRange.end;
|
||||
@@ -39,7 +39,7 @@ export function format(languageModes: LanguageModes, document: TextDocument, for
|
||||
while (i < allRanges.length && !isHTML(allRanges[i])) {
|
||||
let range = allRanges[i];
|
||||
if (!range.attributeValue && range.mode && range.mode.format) {
|
||||
let edits = range.mode.format(document, Range.create(startPos, range.end), formattingOptions, settings);
|
||||
let edits = await range.mode.format(document, Range.create(startPos, range.end), formattingOptions, settings);
|
||||
pushAll(result, edits);
|
||||
}
|
||||
startPos = range.end;
|
||||
@@ -53,7 +53,7 @@ export function format(languageModes: LanguageModes, document: TextDocument, for
|
||||
|
||||
// perform a html format and apply changes to a new document
|
||||
let htmlMode = languageModes.getMode('html')!;
|
||||
let htmlEdits = htmlMode.format!(document, formatRange, formattingOptions, settings);
|
||||
let htmlEdits = await htmlMode.format!(document, formatRange, formattingOptions, settings);
|
||||
let htmlFormattedContent = TextDocument.applyEdits(document, htmlEdits);
|
||||
let newDocument = TextDocument.create(document.uri + '.tmp', document.languageId, document.version, htmlFormattedContent);
|
||||
try {
|
||||
@@ -67,7 +67,7 @@ export function format(languageModes: LanguageModes, document: TextDocument, for
|
||||
for (let r of embeddedRanges) {
|
||||
let mode = r.mode;
|
||||
if (mode && mode.format && enabledModes[mode.getId()] && !r.attributeValue) {
|
||||
let edits = mode.format(newDocument, r, formattingOptions, settings);
|
||||
let edits = await mode.format(newDocument, r, formattingOptions, settings);
|
||||
for (let edit of edits) {
|
||||
embeddedEdits.push(edit);
|
||||
}
|
||||
|
||||
@@ -6,21 +6,21 @@
|
||||
import { TextDocument, FoldingRange, Position, Range, LanguageModes, LanguageMode } from './languageModes';
|
||||
import { CancellationToken } from 'vscode-languageserver';
|
||||
|
||||
export function getFoldingRanges(languageModes: LanguageModes, document: TextDocument, maxRanges: number | undefined, _cancellationToken: CancellationToken | null): FoldingRange[] {
|
||||
export async function getFoldingRanges(languageModes: LanguageModes, document: TextDocument, maxRanges: number | undefined, _cancellationToken: CancellationToken | null): Promise<FoldingRange[]> {
|
||||
let htmlMode = languageModes.getMode('html');
|
||||
let range = Range.create(Position.create(0, 0), Position.create(document.lineCount, 0));
|
||||
let result: FoldingRange[] = [];
|
||||
if (htmlMode && htmlMode.getFoldingRanges) {
|
||||
result.push(...htmlMode.getFoldingRanges(document));
|
||||
result.push(... await htmlMode.getFoldingRanges(document));
|
||||
}
|
||||
|
||||
// cache folding ranges per mode
|
||||
let rangesPerMode: { [mode: string]: FoldingRange[] } = Object.create(null);
|
||||
let getRangesForMode = (mode: LanguageMode) => {
|
||||
let getRangesForMode = async (mode: LanguageMode) => {
|
||||
if (mode.getFoldingRanges) {
|
||||
let ranges = rangesPerMode[mode.getId()];
|
||||
if (!Array.isArray(ranges)) {
|
||||
ranges = mode.getFoldingRanges(document) || [];
|
||||
ranges = await mode.getFoldingRanges(document) || [];
|
||||
rangesPerMode[mode.getId()] = ranges;
|
||||
}
|
||||
return ranges;
|
||||
@@ -32,7 +32,7 @@ export function getFoldingRanges(languageModes: LanguageModes, document: TextDoc
|
||||
for (let modeRange of modeRanges) {
|
||||
let mode = modeRange.mode;
|
||||
if (mode && mode !== htmlMode && !modeRange.attributeValue) {
|
||||
const ranges = getRangesForMode(mode);
|
||||
const ranges = await getRangesForMode(mode);
|
||||
result.push(...ranges.filter(r => r.startLine >= modeRange.start.line && r.endLine < modeRange.end.line));
|
||||
}
|
||||
}
|
||||
|
||||
@@ -17,7 +17,7 @@ export function getHTMLMode(htmlLanguageService: HTMLLanguageService, workspace:
|
||||
getId() {
|
||||
return 'html';
|
||||
},
|
||||
getSelectionRange(document: TextDocument, position: Position): SelectionRange {
|
||||
async getSelectionRange(document: TextDocument, position: Position): Promise<SelectionRange> {
|
||||
return htmlLanguageService.getSelectionRanges(document, [position])[0];
|
||||
},
|
||||
doComplete(document: TextDocument, position: Position, documentContext: DocumentContext, settings = workspace.settings) {
|
||||
@@ -31,19 +31,19 @@ export function getHTMLMode(htmlLanguageService: HTMLLanguageService, workspace:
|
||||
let completionList = htmlLanguageService.doComplete2(document, position, htmlDocument, documentContext, options);
|
||||
return completionList;
|
||||
},
|
||||
doHover(document: TextDocument, position: Position) {
|
||||
async doHover(document: TextDocument, position: Position) {
|
||||
return htmlLanguageService.doHover(document, position, htmlDocuments.get(document));
|
||||
},
|
||||
findDocumentHighlight(document: TextDocument, position: Position) {
|
||||
async findDocumentHighlight(document: TextDocument, position: Position) {
|
||||
return htmlLanguageService.findDocumentHighlights(document, position, htmlDocuments.get(document));
|
||||
},
|
||||
findDocumentLinks(document: TextDocument, documentContext: DocumentContext) {
|
||||
async findDocumentLinks(document: TextDocument, documentContext: DocumentContext) {
|
||||
return htmlLanguageService.findDocumentLinks(document, documentContext);
|
||||
},
|
||||
findDocumentSymbols(document: TextDocument) {
|
||||
async findDocumentSymbols(document: TextDocument) {
|
||||
return htmlLanguageService.findDocumentSymbols(document, htmlDocuments.get(document));
|
||||
},
|
||||
format(document: TextDocument, range: Range, formatParams: FormattingOptions, settings = workspace.settings) {
|
||||
async format(document: TextDocument, range: Range, formatParams: FormattingOptions, settings = workspace.settings) {
|
||||
let formatSettings: HTMLFormatConfiguration = settings && settings.html && settings.html.format;
|
||||
if (formatSettings) {
|
||||
formatSettings = merge(formatSettings, {});
|
||||
@@ -58,10 +58,10 @@ export function getHTMLMode(htmlLanguageService: HTMLLanguageService, workspace:
|
||||
formatSettings = merge(formatParams, formatSettings);
|
||||
return htmlLanguageService.format(document, range, formatSettings);
|
||||
},
|
||||
getFoldingRanges(document: TextDocument): FoldingRange[] {
|
||||
async getFoldingRanges(document: TextDocument): Promise<FoldingRange[]> {
|
||||
return htmlLanguageService.getFoldingRanges(document);
|
||||
},
|
||||
doAutoClose(document: TextDocument, position: Position) {
|
||||
async doAutoClose(document: TextDocument, position: Position) {
|
||||
let offset = document.offsetAt(position);
|
||||
let text = document.getText();
|
||||
if (offset > 0 && text.charAt(offset - 1).match(/[>\/]/g)) {
|
||||
@@ -69,18 +69,18 @@ export function getHTMLMode(htmlLanguageService: HTMLLanguageService, workspace:
|
||||
}
|
||||
return null;
|
||||
},
|
||||
doRename(document: TextDocument, position: Position, newName: string) {
|
||||
async doRename(document: TextDocument, position: Position, newName: string) {
|
||||
const htmlDocument = htmlDocuments.get(document);
|
||||
return htmlLanguageService.doRename(document, position, newName, htmlDocument);
|
||||
},
|
||||
onDocumentRemoved(document: TextDocument) {
|
||||
async onDocumentRemoved(document: TextDocument) {
|
||||
htmlDocuments.onDocumentRemoved(document);
|
||||
},
|
||||
findMatchingTagPosition(document: TextDocument, position: Position) {
|
||||
async findMatchingTagPosition(document: TextDocument, position: Position) {
|
||||
const htmlDocument = htmlDocuments.get(document);
|
||||
return htmlLanguageService.findMatchingTagPosition(document, position, htmlDocument);
|
||||
},
|
||||
doOnTypeRename(document: TextDocument, position: Position) {
|
||||
async doOnTypeRename(document: TextDocument, position: Position) {
|
||||
const htmlDocument = htmlDocuments.get(document);
|
||||
return htmlLanguageService.findOnTypeRenameRanges(document, position, htmlDocument);
|
||||
},
|
||||
|
||||
@@ -15,67 +15,83 @@ import { HTMLDocumentRegions } from './embeddedSupport';
|
||||
|
||||
import * as ts from 'typescript';
|
||||
import { getSemanticTokens, getSemanticTokenLegend } from './javascriptSemanticTokens';
|
||||
import { loadLibrary } from './javascriptLibs';
|
||||
|
||||
const JS_WORD_REGEX = /(-?\d*\.\d\w*)|([^\`\~\!\@\#\%\^\&\*\(\)\-\=\+\[\{\]\}\\\|\;\:\'\"\,\.\<\>\/\?\s]+)/g;
|
||||
|
||||
function getLanguageServiceHost(scriptKind: ts.ScriptKind) {
|
||||
const compilerOptions: ts.CompilerOptions = { allowNonTsExtensions: true, allowJs: true, lib: ['lib.es6.d.ts'], target: ts.ScriptTarget.Latest, moduleResolution: ts.ModuleResolutionKind.Classic, experimentalDecorators: false };
|
||||
|
||||
let currentTextDocument = TextDocument.create('init', 'javascript', 1, '');
|
||||
const jsLanguageService = import(/* webpackChunkName: "javascriptLibs" */ './javascriptLibs').then(libs => {
|
||||
const host: ts.LanguageServiceHost = {
|
||||
getCompilationSettings: () => compilerOptions,
|
||||
getScriptFileNames: () => [currentTextDocument.uri, 'jquery'],
|
||||
getScriptKind: (fileName) => {
|
||||
if (fileName === currentTextDocument.uri) {
|
||||
return scriptKind;
|
||||
}
|
||||
return fileName.substr(fileName.length - 2) === 'ts' ? ts.ScriptKind.TS : ts.ScriptKind.JS;
|
||||
},
|
||||
getScriptVersion: (fileName: string) => {
|
||||
if (fileName === currentTextDocument.uri) {
|
||||
return String(currentTextDocument.version);
|
||||
}
|
||||
return '1'; // default lib an jquery.d.ts are static
|
||||
},
|
||||
getScriptSnapshot: (fileName: string) => {
|
||||
let text = '';
|
||||
if (fileName === currentTextDocument.uri) {
|
||||
text = currentTextDocument.getText();
|
||||
} else {
|
||||
text = libs.loadLibrary(fileName);
|
||||
}
|
||||
return {
|
||||
getText: (start, end) => text.substring(start, end),
|
||||
getLength: () => text.length,
|
||||
getChangeRange: () => undefined
|
||||
};
|
||||
},
|
||||
getCurrentDirectory: () => '',
|
||||
getDefaultLibFileName: (_options: ts.CompilerOptions) => 'es6'
|
||||
};
|
||||
return ts.createLanguageService(host);
|
||||
});
|
||||
return {
|
||||
async getLanguageService(jsDocument: TextDocument): Promise<ts.LanguageService> {
|
||||
currentTextDocument = jsDocument;
|
||||
return jsLanguageService;
|
||||
},
|
||||
getCompilationSettings() {
|
||||
return compilerOptions;
|
||||
},
|
||||
dispose() {
|
||||
if (jsLanguageService) {
|
||||
jsLanguageService.then(s => s.dispose());
|
||||
}
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
|
||||
export function getJavaScriptMode(documentRegions: LanguageModelCache<HTMLDocumentRegions>, languageId: 'javascript' | 'typescript', workspace: Workspace): LanguageMode {
|
||||
let jsDocuments = getLanguageModelCache<TextDocument>(10, 60, document => documentRegions.get(document).getEmbeddedDocument(languageId));
|
||||
|
||||
const workingFile = languageId === 'javascript' ? 'vscode://javascript/1.js' : 'vscode://javascript/2.ts'; // the same 'file' is used for all contents
|
||||
const jQueryFile = 'jquery';
|
||||
|
||||
let compilerOptions: ts.CompilerOptions = { allowNonTsExtensions: true, allowJs: true, lib: ['lib.es6.d.ts'], target: ts.ScriptTarget.Latest, moduleResolution: ts.ModuleResolutionKind.Classic, experimentalDecorators: false };
|
||||
let currentTextDocument: TextDocument;
|
||||
let scriptFileVersion: number = 0;
|
||||
function updateCurrentTextDocument(doc: TextDocument) {
|
||||
if (!currentTextDocument || doc.uri !== currentTextDocument.uri || doc.version !== currentTextDocument.version) {
|
||||
currentTextDocument = jsDocuments.get(doc);
|
||||
scriptFileVersion++;
|
||||
}
|
||||
}
|
||||
const host: ts.LanguageServiceHost = {
|
||||
getCompilationSettings: () => compilerOptions,
|
||||
getScriptFileNames: () => [workingFile, jQueryFile],
|
||||
getScriptKind: (fileName) => fileName.substr(fileName.length - 2) === 'ts' ? ts.ScriptKind.TS : ts.ScriptKind.JS,
|
||||
getScriptVersion: (fileName: string) => {
|
||||
if (fileName === workingFile) {
|
||||
return String(scriptFileVersion);
|
||||
}
|
||||
return '1'; // default lib an jquery.d.ts are static
|
||||
},
|
||||
getScriptSnapshot: (fileName: string) => {
|
||||
let text = '';
|
||||
if (fileName === workingFile) {
|
||||
text = currentTextDocument.getText();
|
||||
} else {
|
||||
text = loadLibrary(fileName);
|
||||
}
|
||||
return {
|
||||
getText: (start, end) => text.substring(start, end),
|
||||
getLength: () => text.length,
|
||||
getChangeRange: () => undefined
|
||||
};
|
||||
},
|
||||
getCurrentDirectory: () => '',
|
||||
getDefaultLibFileName: (_options: ts.CompilerOptions) => 'es6'
|
||||
};
|
||||
let jsLanguageService = ts.createLanguageService(host);
|
||||
|
||||
const host = getLanguageServiceHost(languageId === 'javascript' ? ts.ScriptKind.JS : ts.ScriptKind.TS);
|
||||
let globalSettings: Settings = {};
|
||||
|
||||
return {
|
||||
getId() {
|
||||
return languageId;
|
||||
},
|
||||
doValidation(document: TextDocument, settings = workspace.settings): Diagnostic[] {
|
||||
updateCurrentTextDocument(document);
|
||||
async doValidation(document: TextDocument, settings = workspace.settings): Promise<Diagnostic[]> {
|
||||
host.getCompilationSettings()['experimentalDecorators'] = settings && settings.javascript && settings.javascript.implicitProjectConfig.experimentalDecorators;
|
||||
const syntaxDiagnostics: ts.Diagnostic[] = jsLanguageService.getSyntacticDiagnostics(workingFile);
|
||||
const semanticDiagnostics = jsLanguageService.getSemanticDiagnostics(workingFile);
|
||||
const jsDocument = jsDocuments.get(document);
|
||||
const languageService = await host.getLanguageService(jsDocument);
|
||||
const syntaxDiagnostics: ts.Diagnostic[] = languageService.getSyntacticDiagnostics(jsDocument.uri);
|
||||
const semanticDiagnostics = languageService.getSemanticDiagnostics(jsDocument.uri);
|
||||
return syntaxDiagnostics.concat(semanticDiagnostics).map((diag: ts.Diagnostic): Diagnostic => {
|
||||
return {
|
||||
range: convertRange(currentTextDocument, diag),
|
||||
range: convertRange(jsDocument, diag),
|
||||
severity: DiagnosticSeverity.Error,
|
||||
source: languageId,
|
||||
message: ts.flattenDiagnosticMessageText(diag.messageText, '\n')
|
||||
@@ -83,13 +99,14 @@ export function getJavaScriptMode(documentRegions: LanguageModelCache<HTMLDocume
|
||||
});
|
||||
},
|
||||
async doComplete(document: TextDocument, position: Position, _documentContext: DocumentContext): Promise<CompletionList> {
|
||||
updateCurrentTextDocument(document);
|
||||
let offset = currentTextDocument.offsetAt(position);
|
||||
let completions = jsLanguageService.getCompletionsAtPosition(workingFile, offset, { includeExternalModuleExports: false, includeInsertTextCompletions: false });
|
||||
const jsDocument = jsDocuments.get(document);
|
||||
const jsLanguageService = await host.getLanguageService(jsDocument);
|
||||
let offset = jsDocument.offsetAt(position);
|
||||
let completions = jsLanguageService.getCompletionsAtPosition(jsDocument.uri, offset, { includeExternalModuleExports: false, includeInsertTextCompletions: false });
|
||||
if (!completions) {
|
||||
return { isIncomplete: false, items: [] };
|
||||
}
|
||||
let replaceRange = convertRange(currentTextDocument, getWordAtText(currentTextDocument.getText(), offset, JS_WORD_REGEX));
|
||||
let replaceRange = convertRange(jsDocument, getWordAtText(jsDocument.getText(), offset, JS_WORD_REGEX));
|
||||
return {
|
||||
isIncomplete: false,
|
||||
items: completions.entries.map(entry => {
|
||||
@@ -109,9 +126,10 @@ export function getJavaScriptMode(documentRegions: LanguageModelCache<HTMLDocume
|
||||
})
|
||||
};
|
||||
},
|
||||
doResolve(document: TextDocument, item: CompletionItem): CompletionItem {
|
||||
updateCurrentTextDocument(document);
|
||||
let details = jsLanguageService.getCompletionEntryDetails(workingFile, item.data.offset, item.label, undefined, undefined, undefined);
|
||||
async doResolve(document: TextDocument, item: CompletionItem): Promise<CompletionItem> {
|
||||
const jsDocument = jsDocuments.get(document);
|
||||
const jsLanguageService = await host.getLanguageService(jsDocument);
|
||||
let details = jsLanguageService.getCompletionEntryDetails(jsDocument.uri, item.data.offset, item.label, undefined, undefined, undefined);
|
||||
if (details) {
|
||||
item.detail = ts.displayPartsToString(details.displayParts);
|
||||
item.documentation = ts.displayPartsToString(details.documentation);
|
||||
@@ -119,21 +137,23 @@ export function getJavaScriptMode(documentRegions: LanguageModelCache<HTMLDocume
|
||||
}
|
||||
return item;
|
||||
},
|
||||
doHover(document: TextDocument, position: Position): Hover | null {
|
||||
updateCurrentTextDocument(document);
|
||||
let info = jsLanguageService.getQuickInfoAtPosition(workingFile, currentTextDocument.offsetAt(position));
|
||||
async doHover(document: TextDocument, position: Position): Promise<Hover | null> {
|
||||
const jsDocument = jsDocuments.get(document);
|
||||
const jsLanguageService = await host.getLanguageService(jsDocument);
|
||||
let info = jsLanguageService.getQuickInfoAtPosition(jsDocument.uri, jsDocument.offsetAt(position));
|
||||
if (info) {
|
||||
let contents = ts.displayPartsToString(info.displayParts);
|
||||
return {
|
||||
range: convertRange(currentTextDocument, info.textSpan),
|
||||
range: convertRange(jsDocument, info.textSpan),
|
||||
contents: MarkedString.fromPlainText(contents)
|
||||
};
|
||||
}
|
||||
return null;
|
||||
},
|
||||
doSignatureHelp(document: TextDocument, position: Position): SignatureHelp | null {
|
||||
updateCurrentTextDocument(document);
|
||||
let signHelp = jsLanguageService.getSignatureHelpItems(workingFile, currentTextDocument.offsetAt(position), undefined);
|
||||
async doSignatureHelp(document: TextDocument, position: Position): Promise<SignatureHelp | null> {
|
||||
const jsDocument = jsDocuments.get(document);
|
||||
const jsLanguageService = await host.getLanguageService(jsDocument);
|
||||
let signHelp = jsLanguageService.getSignatureHelpItems(jsDocument.uri, jsDocument.offsetAt(position), undefined);
|
||||
if (signHelp) {
|
||||
let ret: SignatureHelp = {
|
||||
activeSignature: signHelp.selectedItemIndex,
|
||||
@@ -168,23 +188,25 @@ export function getJavaScriptMode(documentRegions: LanguageModelCache<HTMLDocume
|
||||
}
|
||||
return null;
|
||||
},
|
||||
findDocumentHighlight(document: TextDocument, position: Position): DocumentHighlight[] {
|
||||
updateCurrentTextDocument(document);
|
||||
const highlights = jsLanguageService.getDocumentHighlights(workingFile, currentTextDocument.offsetAt(position), [workingFile]);
|
||||
async findDocumentHighlight(document: TextDocument, position: Position): Promise<DocumentHighlight[]> {
|
||||
const jsDocument = jsDocuments.get(document);
|
||||
const jsLanguageService = await host.getLanguageService(jsDocument);
|
||||
const highlights = jsLanguageService.getDocumentHighlights(jsDocument.uri, jsDocument.offsetAt(position), [jsDocument.uri]);
|
||||
const out: DocumentHighlight[] = [];
|
||||
for (const entry of highlights || []) {
|
||||
for (const highlight of entry.highlightSpans) {
|
||||
out.push({
|
||||
range: convertRange(currentTextDocument, highlight.textSpan),
|
||||
range: convertRange(jsDocument, highlight.textSpan),
|
||||
kind: highlight.kind === 'writtenReference' ? DocumentHighlightKind.Write : DocumentHighlightKind.Text
|
||||
});
|
||||
}
|
||||
}
|
||||
return out;
|
||||
},
|
||||
findDocumentSymbols(document: TextDocument): SymbolInformation[] {
|
||||
updateCurrentTextDocument(document);
|
||||
let items = jsLanguageService.getNavigationBarItems(workingFile);
|
||||
async findDocumentSymbols(document: TextDocument): Promise<SymbolInformation[]> {
|
||||
const jsDocument = jsDocuments.get(document);
|
||||
const jsLanguageService = await host.getLanguageService(jsDocument);
|
||||
let items = jsLanguageService.getNavigationBarItems(jsDocument.uri);
|
||||
if (items) {
|
||||
let result: SymbolInformation[] = [];
|
||||
let existing = Object.create(null);
|
||||
@@ -196,7 +218,7 @@ export function getJavaScriptMode(documentRegions: LanguageModelCache<HTMLDocume
|
||||
kind: convertSymbolKind(item.kind),
|
||||
location: {
|
||||
uri: document.uri,
|
||||
range: convertRange(currentTextDocument, item.spans[0])
|
||||
range: convertRange(jsDocument, item.spans[0])
|
||||
},
|
||||
containerName: containerLabel
|
||||
};
|
||||
@@ -218,63 +240,66 @@ export function getJavaScriptMode(documentRegions: LanguageModelCache<HTMLDocume
|
||||
}
|
||||
return [];
|
||||
},
|
||||
findDefinition(document: TextDocument, position: Position): Definition | null {
|
||||
updateCurrentTextDocument(document);
|
||||
let definition = jsLanguageService.getDefinitionAtPosition(workingFile, currentTextDocument.offsetAt(position));
|
||||
async findDefinition(document: TextDocument, position: Position): Promise<Definition | null> {
|
||||
const jsDocument = jsDocuments.get(document);
|
||||
const jsLanguageService = await host.getLanguageService(jsDocument);
|
||||
let definition = jsLanguageService.getDefinitionAtPosition(jsDocument.uri, jsDocument.offsetAt(position));
|
||||
if (definition) {
|
||||
return definition.filter(d => d.fileName === workingFile).map(d => {
|
||||
return definition.filter(d => d.fileName === jsDocument.uri).map(d => {
|
||||
return {
|
||||
uri: document.uri,
|
||||
range: convertRange(currentTextDocument, d.textSpan)
|
||||
range: convertRange(jsDocument, d.textSpan)
|
||||
};
|
||||
});
|
||||
}
|
||||
return null;
|
||||
},
|
||||
findReferences(document: TextDocument, position: Position): Location[] {
|
||||
updateCurrentTextDocument(document);
|
||||
let references = jsLanguageService.getReferencesAtPosition(workingFile, currentTextDocument.offsetAt(position));
|
||||
async findReferences(document: TextDocument, position: Position): Promise<Location[]> {
|
||||
const jsDocument = jsDocuments.get(document);
|
||||
const jsLanguageService = await host.getLanguageService(jsDocument);
|
||||
let references = jsLanguageService.getReferencesAtPosition(jsDocument.uri, jsDocument.offsetAt(position));
|
||||
if (references) {
|
||||
return references.filter(d => d.fileName === workingFile).map(d => {
|
||||
return references.filter(d => d.fileName === jsDocument.uri).map(d => {
|
||||
return {
|
||||
uri: document.uri,
|
||||
range: convertRange(currentTextDocument, d.textSpan)
|
||||
range: convertRange(jsDocument, d.textSpan)
|
||||
};
|
||||
});
|
||||
}
|
||||
return [];
|
||||
},
|
||||
getSelectionRange(document: TextDocument, position: Position): SelectionRange {
|
||||
updateCurrentTextDocument(document);
|
||||
async getSelectionRange(document: TextDocument, position: Position): Promise<SelectionRange> {
|
||||
const jsDocument = jsDocuments.get(document);
|
||||
const jsLanguageService = await host.getLanguageService(jsDocument);
|
||||
function convertSelectionRange(selectionRange: ts.SelectionRange): SelectionRange {
|
||||
const parent = selectionRange.parent ? convertSelectionRange(selectionRange.parent) : undefined;
|
||||
return SelectionRange.create(convertRange(currentTextDocument, selectionRange.textSpan), parent);
|
||||
return SelectionRange.create(convertRange(jsDocument, selectionRange.textSpan), parent);
|
||||
}
|
||||
const range = jsLanguageService.getSmartSelectionRange(workingFile, currentTextDocument.offsetAt(position));
|
||||
const range = jsLanguageService.getSmartSelectionRange(jsDocument.uri, jsDocument.offsetAt(position));
|
||||
return convertSelectionRange(range);
|
||||
},
|
||||
format(document: TextDocument, range: Range, formatParams: FormattingOptions, settings: Settings = globalSettings): TextEdit[] {
|
||||
currentTextDocument = documentRegions.get(document).getEmbeddedDocument('javascript', true);
|
||||
scriptFileVersion++;
|
||||
async format(document: TextDocument, range: Range, formatParams: FormattingOptions, settings: Settings = globalSettings): Promise<TextEdit[]> {
|
||||
const jsDocument = documentRegions.get(document).getEmbeddedDocument('javascript', true);
|
||||
const jsLanguageService = await host.getLanguageService(jsDocument);
|
||||
|
||||
let formatterSettings = settings && settings.javascript && settings.javascript.format;
|
||||
|
||||
let initialIndentLevel = computeInitialIndent(document, range, formatParams);
|
||||
let formatSettings = convertOptions(formatParams, formatterSettings, initialIndentLevel + 1);
|
||||
let start = currentTextDocument.offsetAt(range.start);
|
||||
let end = currentTextDocument.offsetAt(range.end);
|
||||
let start = jsDocument.offsetAt(range.start);
|
||||
let end = jsDocument.offsetAt(range.end);
|
||||
let lastLineRange = null;
|
||||
if (range.end.line > range.start.line && (range.end.character === 0 || isWhitespaceOnly(currentTextDocument.getText().substr(end - range.end.character, range.end.character)))) {
|
||||
if (range.end.line > range.start.line && (range.end.character === 0 || isWhitespaceOnly(jsDocument.getText().substr(end - range.end.character, range.end.character)))) {
|
||||
end -= range.end.character;
|
||||
lastLineRange = Range.create(Position.create(range.end.line, 0), range.end);
|
||||
}
|
||||
let edits = jsLanguageService.getFormattingEditsForRange(workingFile, start, end, formatSettings);
|
||||
let edits = jsLanguageService.getFormattingEditsForRange(jsDocument.uri, start, end, formatSettings);
|
||||
if (edits) {
|
||||
let result = [];
|
||||
for (let edit of edits) {
|
||||
if (edit.span.start >= start && edit.span.start + edit.span.length <= end) {
|
||||
result.push({
|
||||
range: convertRange(currentTextDocument, edit.span),
|
||||
range: convertRange(jsDocument, edit.span),
|
||||
newText: edit.newText
|
||||
});
|
||||
}
|
||||
@@ -289,12 +314,13 @@ export function getJavaScriptMode(documentRegions: LanguageModelCache<HTMLDocume
|
||||
}
|
||||
return [];
|
||||
},
|
||||
getFoldingRanges(document: TextDocument): FoldingRange[] {
|
||||
updateCurrentTextDocument(document);
|
||||
let spans = jsLanguageService.getOutliningSpans(workingFile);
|
||||
async getFoldingRanges(document: TextDocument): Promise<FoldingRange[]> {
|
||||
const jsDocument = jsDocuments.get(document);
|
||||
const jsLanguageService = await host.getLanguageService(jsDocument);
|
||||
let spans = jsLanguageService.getOutliningSpans(jsDocument.uri);
|
||||
let ranges: FoldingRange[] = [];
|
||||
for (let span of spans) {
|
||||
let curr = convertRange(currentTextDocument, span.textSpan);
|
||||
let curr = convertRange(jsDocument, span.textSpan);
|
||||
let startLine = curr.start.line;
|
||||
let endLine = curr.end.line;
|
||||
if (startLine < endLine) {
|
||||
@@ -311,15 +337,16 @@ export function getJavaScriptMode(documentRegions: LanguageModelCache<HTMLDocume
|
||||
onDocumentRemoved(document: TextDocument) {
|
||||
jsDocuments.onDocumentRemoved(document);
|
||||
},
|
||||
getSemanticTokens(document: TextDocument): SemanticTokenData[] {
|
||||
updateCurrentTextDocument(document);
|
||||
return getSemanticTokens(jsLanguageService, currentTextDocument, workingFile);
|
||||
async getSemanticTokens(document: TextDocument): Promise<SemanticTokenData[]> {
|
||||
const jsDocument = jsDocuments.get(document);
|
||||
const jsLanguageService = await host.getLanguageService(jsDocument);
|
||||
return getSemanticTokens(jsLanguageService, jsDocument, jsDocument.uri);
|
||||
},
|
||||
getSemanticTokenLegend(): { types: string[], modifiers: string[] } {
|
||||
return getSemanticTokenLegend();
|
||||
},
|
||||
dispose() {
|
||||
jsLanguageService.dispose();
|
||||
host.dispose();
|
||||
jsDocuments.dispose();
|
||||
}
|
||||
};
|
||||
|
||||
@@ -41,27 +41,27 @@ export interface SemanticTokenData {
|
||||
|
||||
export interface LanguageMode {
|
||||
getId(): string;
|
||||
getSelectionRange?: (document: TextDocument, position: Position) => SelectionRange;
|
||||
doValidation?: (document: TextDocument, settings?: Settings) => Diagnostic[];
|
||||
getSelectionRange?: (document: TextDocument, position: Position) => Promise<SelectionRange>;
|
||||
doValidation?: (document: TextDocument, settings?: Settings) => Promise<Diagnostic[]>;
|
||||
doComplete?: (document: TextDocument, position: Position, documentContext: DocumentContext, settings?: Settings) => Promise<CompletionList>;
|
||||
doResolve?: (document: TextDocument, item: CompletionItem) => CompletionItem;
|
||||
doHover?: (document: TextDocument, position: Position) => Hover | null;
|
||||
doSignatureHelp?: (document: TextDocument, position: Position) => SignatureHelp | null;
|
||||
doRename?: (document: TextDocument, position: Position, newName: string) => WorkspaceEdit | null;
|
||||
doOnTypeRename?: (document: TextDocument, position: Position) => Range[] | null;
|
||||
findDocumentHighlight?: (document: TextDocument, position: Position) => DocumentHighlight[];
|
||||
findDocumentSymbols?: (document: TextDocument) => SymbolInformation[];
|
||||
findDocumentLinks?: (document: TextDocument, documentContext: DocumentContext) => DocumentLink[];
|
||||
findDefinition?: (document: TextDocument, position: Position) => Definition | null;
|
||||
findReferences?: (document: TextDocument, position: Position) => Location[];
|
||||
format?: (document: TextDocument, range: Range, options: FormattingOptions, settings?: Settings) => TextEdit[];
|
||||
findDocumentColors?: (document: TextDocument) => ColorInformation[];
|
||||
getColorPresentations?: (document: TextDocument, color: Color, range: Range) => ColorPresentation[];
|
||||
doAutoClose?: (document: TextDocument, position: Position) => string | null;
|
||||
findMatchingTagPosition?: (document: TextDocument, position: Position) => Position | null;
|
||||
getFoldingRanges?: (document: TextDocument) => FoldingRange[];
|
||||
doResolve?: (document: TextDocument, item: CompletionItem) => Promise<CompletionItem>;
|
||||
doHover?: (document: TextDocument, position: Position) => Promise<Hover | null>;
|
||||
doSignatureHelp?: (document: TextDocument, position: Position) => Promise<SignatureHelp | null>;
|
||||
doRename?: (document: TextDocument, position: Position, newName: string) => Promise<WorkspaceEdit | null>;
|
||||
doOnTypeRename?: (document: TextDocument, position: Position) => Promise<Range[] | null>;
|
||||
findDocumentHighlight?: (document: TextDocument, position: Position) => Promise<DocumentHighlight[]>;
|
||||
findDocumentSymbols?: (document: TextDocument) => Promise<SymbolInformation[]>;
|
||||
findDocumentLinks?: (document: TextDocument, documentContext: DocumentContext) => Promise<DocumentLink[]>;
|
||||
findDefinition?: (document: TextDocument, position: Position) => Promise<Definition | null>;
|
||||
findReferences?: (document: TextDocument, position: Position) => Promise<Location[]>;
|
||||
format?: (document: TextDocument, range: Range, options: FormattingOptions, settings?: Settings) => Promise<TextEdit[]>;
|
||||
findDocumentColors?: (document: TextDocument) => Promise<ColorInformation[]>;
|
||||
getColorPresentations?: (document: TextDocument, color: Color, range: Range) => Promise<ColorPresentation[]>;
|
||||
doAutoClose?: (document: TextDocument, position: Position) => Promise<string | null>;
|
||||
findMatchingTagPosition?: (document: TextDocument, position: Position) => Promise<Position | null>;
|
||||
getFoldingRanges?: (document: TextDocument) => Promise<FoldingRange[]>;
|
||||
onDocumentRemoved(document: TextDocument): void;
|
||||
getSemanticTokens?(document: TextDocument): SemanticTokenData[];
|
||||
getSemanticTokens?(document: TextDocument): Promise<SemanticTokenData[]>;
|
||||
getSemanticTokenLegend?(): { types: string[], modifiers: string[] };
|
||||
dispose(): void;
|
||||
}
|
||||
|
||||
@@ -6,13 +6,13 @@
|
||||
import { LanguageModes, TextDocument, Position, Range, SelectionRange } from './languageModes';
|
||||
import { insideRangeButNotSame } from '../utils/positions';
|
||||
|
||||
export function getSelectionRanges(languageModes: LanguageModes, document: TextDocument, positions: Position[]) {
|
||||
export async function getSelectionRanges(languageModes: LanguageModes, document: TextDocument, positions: Position[]) {
|
||||
const htmlMode = languageModes.getMode('html');
|
||||
return positions.map(position => {
|
||||
const htmlRange = htmlMode!.getSelectionRange!(document, position);
|
||||
return Promise.all(positions.map(async position => {
|
||||
const htmlRange = await htmlMode!.getSelectionRange!(document, position);
|
||||
const mode = languageModes.getModeAtPosition(document, position);
|
||||
if (mode && mode.getSelectionRange) {
|
||||
let range = mode.getSelectionRange(document, position);
|
||||
let range = await mode.getSelectionRange(document, position);
|
||||
let top = range;
|
||||
while (top.parent && insideRangeButNotSame(htmlRange.range, top.parent.range)) {
|
||||
top = top.parent;
|
||||
@@ -21,6 +21,6 @@ export function getSelectionRanges(languageModes: LanguageModes, document: TextD
|
||||
return range;
|
||||
}
|
||||
return htmlRange || SelectionRange.create(Range.create(position, position));
|
||||
});
|
||||
}));
|
||||
}
|
||||
|
||||
|
||||
@@ -13,14 +13,14 @@ interface LegendMapping {
|
||||
|
||||
export interface SemanticTokenProvider {
|
||||
readonly legend: { types: string[]; modifiers: string[] };
|
||||
getSemanticTokens(document: TextDocument, ranges?: Range[]): number[];
|
||||
getSemanticTokens(document: TextDocument, ranges?: Range[]): Promise<number[]>;
|
||||
}
|
||||
|
||||
|
||||
export function newSemanticTokenProvider(languageModes: LanguageModes): SemanticTokenProvider {
|
||||
|
||||
// combined legend across modes
|
||||
const legend = { types: [], modifiers: [] };
|
||||
const legend: { types: string[], modifiers: string[] } = { types: [], modifiers: [] };
|
||||
const legendMappings: { [modeId: string]: LegendMapping } = {};
|
||||
|
||||
for (let mode of languageModes.getAllModes()) {
|
||||
@@ -32,12 +32,12 @@ export function newSemanticTokenProvider(languageModes: LanguageModes): Semantic
|
||||
|
||||
return {
|
||||
legend,
|
||||
getSemanticTokens(document: TextDocument, ranges?: Range[]): number[] {
|
||||
async getSemanticTokens(document: TextDocument, ranges?: Range[]): Promise<number[]> {
|
||||
const allTokens: SemanticTokenData[] = [];
|
||||
for (let mode of languageModes.getAllModesInDocument(document)) {
|
||||
if (mode.getSemanticTokens) {
|
||||
const mapping = legendMappings[mode.getId()];
|
||||
const tokens = mode.getSemanticTokens(document);
|
||||
const tokens = await mode.getSemanticTokens(document);
|
||||
applyTypesMapping(tokens, mapping.types);
|
||||
applyModifiersMapping(tokens, mapping.modifiers);
|
||||
for (let token of tokens) {
|
||||
|
||||
Reference in New Issue
Block a user