diff --git a/extensions/html-language-features/server/extension-browser.webpack.config.js b/extensions/html-language-features/server/extension-browser.webpack.config.js index 6737deb4906..ebb2c2eb5f3 100644 --- a/extensions/html-language-features/server/extension-browser.webpack.config.js +++ b/extensions/html-language-features/server/extension-browser.webpack.config.js @@ -25,6 +25,11 @@ const serverConfig = withDefaults({ performance: { hints: false }, + optimization: { + splitChunks: { + chunks: "async" + } + }, resolve: { alias: { 'vscode-nls': path.resolve(__dirname, '../../../build/polyfills/vscode-nls.js') diff --git a/extensions/html-language-features/server/src/htmlServer.ts b/extensions/html-language-features/server/src/htmlServer.ts index 680fc4f5352..ade37f22533 100644 --- a/extensions/html-language-features/server/src/htmlServer.ts +++ b/extensions/html-language-features/server/src/htmlServer.ts @@ -18,7 +18,7 @@ import { format } from './modes/formatting'; import { pushAll } from './utils/arrays'; import { getDocumentContext } from './utils/documentContext'; import { URI } from 'vscode-uri'; -import { formatError, runSafe, runSafeAsync } from './utils/runner'; +import { formatError, runSafe } from './utils/runner'; import { getFoldingRanges } from './modes/htmlFolding'; import { fetchHTMLDataProviders } from './customData'; @@ -261,11 +261,11 @@ export function startServer(connection: Connection, runtime: RuntimeEnvironment) const settings = await getDocumentSettings(textDocument, () => modes.some(m => !!m.doValidation)); const latestTextDocument = documents.get(textDocument.uri); if (latestTextDocument && latestTextDocument.version === version) { // check no new version has come in after in after the async op - modes.forEach(mode => { + for (const mode of modes) { if (mode.doValidation && isValidationEnabled(mode.getId(), settings)) { - pushAll(diagnostics, mode.doValidation(latestTextDocument, settings)); + pushAll(diagnostics, await mode.doValidation(latestTextDocument, settings)); } - }); + } connection.sendDiagnostics({ uri: latestTextDocument.uri, diagnostics }); } } @@ -275,7 +275,7 @@ export function startServer(connection: Connection, runtime: RuntimeEnvironment) } connection.onCompletion(async (textDocumentPosition, token) => { - return runSafeAsync(async () => { + return runSafe(async () => { const document = documents.get(textDocumentPosition.textDocument.uri); if (!document) { return null; @@ -303,7 +303,7 @@ export function startServer(connection: Connection, runtime: RuntimeEnvironment) }); connection.onCompletionResolve((item, token) => { - return runSafe(() => { + return runSafe(async () => { const data = item.data; if (data && data.languageId && data.uri) { const mode = languageModes.getMode(data.languageId); @@ -317,7 +317,7 @@ export function startServer(connection: Connection, runtime: RuntimeEnvironment) }); connection.onHover((textDocumentPosition, token) => { - return runSafe(() => { + return runSafe(async () => { const document = documents.get(textDocumentPosition.textDocument.uri); if (document) { const mode = languageModes.getModeAtPosition(document, textDocumentPosition.position); @@ -330,7 +330,7 @@ export function startServer(connection: Connection, runtime: RuntimeEnvironment) }); connection.onDocumentHighlight((documentHighlightParams, token) => { - return runSafe(() => { + return runSafe(async () => { const document = documents.get(documentHighlightParams.textDocument.uri); if (document) { const mode = languageModes.getModeAtPosition(document, documentHighlightParams.position); @@ -343,7 +343,7 @@ export function startServer(connection: Connection, runtime: RuntimeEnvironment) }); connection.onDefinition((definitionParams, token) => { - return runSafe(() => { + return runSafe(async () => { const document = documents.get(definitionParams.textDocument.uri); if (document) { const mode = languageModes.getModeAtPosition(document, definitionParams.position); @@ -356,7 +356,7 @@ export function startServer(connection: Connection, runtime: RuntimeEnvironment) }); connection.onReferences((referenceParams, token) => { - return runSafe(() => { + return runSafe(async () => { const document = documents.get(referenceParams.textDocument.uri); if (document) { const mode = languageModes.getModeAtPosition(document, referenceParams.position); @@ -369,7 +369,7 @@ export function startServer(connection: Connection, runtime: RuntimeEnvironment) }); connection.onSignatureHelp((signatureHelpParms, token) => { - return runSafe(() => { + return runSafe(async () => { const document = documents.get(signatureHelpParms.textDocument.uri); if (document) { const mode = languageModes.getModeAtPosition(document, signatureHelpParms.position); @@ -382,7 +382,7 @@ export function startServer(connection: Connection, runtime: RuntimeEnvironment) }); connection.onDocumentRangeFormatting(async (formatParams, token) => { - return runSafeAsync(async () => { + return runSafe(async () => { const document = documents.get(formatParams.textDocument.uri); if (document) { let settings = await getDocumentSettings(document, () => true); @@ -399,53 +399,53 @@ export function startServer(connection: Connection, runtime: RuntimeEnvironment) }); connection.onDocumentLinks((documentLinkParam, token) => { - return runSafe(() => { + return runSafe(async () => { const document = documents.get(documentLinkParam.textDocument.uri); const links: DocumentLink[] = []; if (document) { const documentContext = getDocumentContext(document.uri, workspaceFolders); - languageModes.getAllModesInDocument(document).forEach(m => { + for (const m of languageModes.getAllModesInDocument(document)) { if (m.findDocumentLinks) { - pushAll(links, m.findDocumentLinks(document, documentContext)); + pushAll(links, await m.findDocumentLinks(document, documentContext)); } - }); + } } return links; }, [], `Error while document links for ${documentLinkParam.textDocument.uri}`, token); }); connection.onDocumentSymbol((documentSymbolParms, token) => { - return runSafe(() => { + return runSafe(async () => { const document = documents.get(documentSymbolParms.textDocument.uri); const symbols: SymbolInformation[] = []; if (document) { - languageModes.getAllModesInDocument(document).forEach(m => { + for (const m of languageModes.getAllModesInDocument(document)) { if (m.findDocumentSymbols) { - pushAll(symbols, m.findDocumentSymbols(document)); + pushAll(symbols, await m.findDocumentSymbols(document)); } - }); + } } return symbols; }, [], `Error while computing document symbols for ${documentSymbolParms.textDocument.uri}`, token); }); connection.onRequest(DocumentColorRequest.type, (params, token) => { - return runSafe(() => { + return runSafe(async () => { const infos: ColorInformation[] = []; const document = documents.get(params.textDocument.uri); if (document) { - languageModes.getAllModesInDocument(document).forEach(m => { + for (const m of languageModes.getAllModesInDocument(document)) { if (m.findDocumentColors) { - pushAll(infos, m.findDocumentColors(document)); + pushAll(infos, await m.findDocumentColors(document)); } - }); + } } return infos; }, [], `Error while computing document colors for ${params.textDocument.uri}`, token); }); connection.onRequest(ColorPresentationRequest.type, (params, token) => { - return runSafe(() => { + return runSafe(async () => { const document = documents.get(params.textDocument.uri); if (document) { const mode = languageModes.getModeAtPosition(document, params.range.start); @@ -458,7 +458,7 @@ export function startServer(connection: Connection, runtime: RuntimeEnvironment) }); connection.onRequest(TagCloseRequest.type, (params, token) => { - return runSafe(() => { + return runSafe(async () => { const document = documents.get(params.textDocument.uri); if (document) { const pos = params.position; @@ -474,7 +474,7 @@ export function startServer(connection: Connection, runtime: RuntimeEnvironment) }); connection.onFoldingRanges((params, token) => { - return runSafe(() => { + return runSafe(async () => { const document = documents.get(params.textDocument.uri); if (document) { return getFoldingRanges(languageModes, document, foldingRangeLimit, token); @@ -484,7 +484,7 @@ export function startServer(connection: Connection, runtime: RuntimeEnvironment) }); connection.onSelectionRanges((params, token) => { - return runSafe(() => { + return runSafe(async () => { const document = documents.get(params.textDocument.uri); if (document) { return getSelectionRanges(languageModes, document, params.positions); @@ -494,7 +494,7 @@ export function startServer(connection: Connection, runtime: RuntimeEnvironment) }); connection.onRenameRequest((params, token) => { - return runSafe(() => { + return runSafe(async () => { const document = documents.get(params.textDocument.uri); const position: Position = params.position; @@ -509,7 +509,7 @@ export function startServer(connection: Connection, runtime: RuntimeEnvironment) }); connection.onRequest(OnTypeRenameRequest.type, (params, token) => { - return runSafe(() => { + return runSafe(async () => { const document = documents.get(params.textDocument.uri); if (document) { const pos = params.position; @@ -533,7 +533,7 @@ export function startServer(connection: Connection, runtime: RuntimeEnvironment) } connection.onRequest(SemanticTokenRequest.type, (params, token) => { - return runSafe(() => { + return runSafe(async () => { const document = documents.get(params.textDocument.uri); if (document) { return getSemanticTokenProvider().getSemanticTokens(document, params.ranges); @@ -543,7 +543,7 @@ export function startServer(connection: Connection, runtime: RuntimeEnvironment) }); connection.onRequest(SemanticTokenLegendRequest.type, (_params, token) => { - return runSafe(() => { + return runSafe(async () => { return getSemanticTokenProvider().legend; }, null, `Error while computing semantic tokens legend`, token); }); diff --git a/extensions/html-language-features/server/src/modes/cssMode.ts b/extensions/html-language-features/server/src/modes/cssMode.ts index dafc79f0feb..9b3ddd9b764 100644 --- a/extensions/html-language-features/server/src/modes/cssMode.ts +++ b/extensions/html-language-features/server/src/modes/cssMode.ts @@ -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, 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]; }, diff --git a/extensions/html-language-features/server/src/modes/formatting.ts b/extensions/html-language-features/server/src/modes/formatting.ts index 4acb60a9b9a..49c3a0dd68e 100644 --- a/extensions/html-language-features/server/src/modes/formatting.ts +++ b/extensions/html-language-features/server/src/modes/formatting.ts @@ -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); } diff --git a/extensions/html-language-features/server/src/modes/htmlFolding.ts b/extensions/html-language-features/server/src/modes/htmlFolding.ts index 5eccc0ef876..78674fb0bc4 100644 --- a/extensions/html-language-features/server/src/modes/htmlFolding.ts +++ b/extensions/html-language-features/server/src/modes/htmlFolding.ts @@ -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 { 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)); } } diff --git a/extensions/html-language-features/server/src/modes/htmlMode.ts b/extensions/html-language-features/server/src/modes/htmlMode.ts index 8faf51e15fb..96462f8e011 100644 --- a/extensions/html-language-features/server/src/modes/htmlMode.ts +++ b/extensions/html-language-features/server/src/modes/htmlMode.ts @@ -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 { 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 { 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); }, diff --git a/extensions/html-language-features/server/src/modes/javascriptMode.ts b/extensions/html-language-features/server/src/modes/javascriptMode.ts index 727fcfce225..086b8a906b8 100644 --- a/extensions/html-language-features/server/src/modes/javascriptMode.ts +++ b/extensions/html-language-features/server/src/modes/javascriptMode.ts @@ -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 { + currentTextDocument = jsDocument; + return jsLanguageService; + }, + getCompilationSettings() { + return compilerOptions; + }, + dispose() { + if (jsLanguageService) { + jsLanguageService.then(s => s.dispose()); + } + } + }; +} + + export function getJavaScriptMode(documentRegions: LanguageModelCache, languageId: 'javascript' | 'typescript', workspace: Workspace): LanguageMode { let jsDocuments = getLanguageModelCache(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 { 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 { - 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 { + 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 { + 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 { + 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 { + 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 { + 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 { + 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 { + 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 { + 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 { + 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 { + 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 { + 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(); } }; diff --git a/extensions/html-language-features/server/src/modes/languageModes.ts b/extensions/html-language-features/server/src/modes/languageModes.ts index 69ec4ce7a2a..e47df5a797b 100644 --- a/extensions/html-language-features/server/src/modes/languageModes.ts +++ b/extensions/html-language-features/server/src/modes/languageModes.ts @@ -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; + doValidation?: (document: TextDocument, settings?: Settings) => Promise; doComplete?: (document: TextDocument, position: Position, documentContext: DocumentContext, settings?: Settings) => Promise; - 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; + doHover?: (document: TextDocument, position: Position) => Promise; + doSignatureHelp?: (document: TextDocument, position: Position) => Promise; + doRename?: (document: TextDocument, position: Position, newName: string) => Promise; + doOnTypeRename?: (document: TextDocument, position: Position) => Promise; + findDocumentHighlight?: (document: TextDocument, position: Position) => Promise; + findDocumentSymbols?: (document: TextDocument) => Promise; + findDocumentLinks?: (document: TextDocument, documentContext: DocumentContext) => Promise; + findDefinition?: (document: TextDocument, position: Position) => Promise; + findReferences?: (document: TextDocument, position: Position) => Promise; + format?: (document: TextDocument, range: Range, options: FormattingOptions, settings?: Settings) => Promise; + findDocumentColors?: (document: TextDocument) => Promise; + getColorPresentations?: (document: TextDocument, color: Color, range: Range) => Promise; + doAutoClose?: (document: TextDocument, position: Position) => Promise; + findMatchingTagPosition?: (document: TextDocument, position: Position) => Promise; + getFoldingRanges?: (document: TextDocument) => Promise; onDocumentRemoved(document: TextDocument): void; - getSemanticTokens?(document: TextDocument): SemanticTokenData[]; + getSemanticTokens?(document: TextDocument): Promise; getSemanticTokenLegend?(): { types: string[], modifiers: string[] }; dispose(): void; } diff --git a/extensions/html-language-features/server/src/modes/selectionRanges.ts b/extensions/html-language-features/server/src/modes/selectionRanges.ts index a5d0d0eea8d..1db5cb4d15c 100644 --- a/extensions/html-language-features/server/src/modes/selectionRanges.ts +++ b/extensions/html-language-features/server/src/modes/selectionRanges.ts @@ -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)); - }); + })); } diff --git a/extensions/html-language-features/server/src/modes/semanticTokens.ts b/extensions/html-language-features/server/src/modes/semanticTokens.ts index 027c17e5dd9..33f17a70b54 100644 --- a/extensions/html-language-features/server/src/modes/semanticTokens.ts +++ b/extensions/html-language-features/server/src/modes/semanticTokens.ts @@ -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; } 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 { 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) { 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 cbaaba31f2a..b491ad28836 100644 --- a/extensions/html-language-features/server/src/test/completions.test.ts +++ b/extensions/html-language-features/server/src/test/completions.test.ts @@ -86,6 +86,11 @@ suite('HTML Completion', () => { { label: 'getJSON', resultText: '' }, ] }); + await testCompletionFor('', { + items: [ + { label: 'a', resultText: '' }, + ] + }, 'test://test/test2.html'); }); }); diff --git a/extensions/html-language-features/server/src/test/folding.test.ts b/extensions/html-language-features/server/src/test/folding.test.ts index 5ecf363224b..272a81463fd 100644 --- a/extensions/html-language-features/server/src/test/folding.test.ts +++ b/extensions/html-language-features/server/src/test/folding.test.ts @@ -16,14 +16,14 @@ interface ExpectedIndentRange { kind?: string; } -function assertRanges(lines: string[], expected: ExpectedIndentRange[], message?: string, nRanges?: number): void { +async function assertRanges(lines: string[], expected: ExpectedIndentRange[], message?: string, nRanges?: number): Promise { const document = TextDocument.create('test://foo/bar.html', 'html', 1, lines.join('\n')); const workspace = { settings: {}, folders: [{ name: 'foo', uri: 'test://foo' }] }; const languageModes = getLanguageModes({ css: true, javascript: true }, workspace, ClientCapabilities.LATEST, getNodeFSRequestService()); - const actual = getFoldingRanges(languageModes, document, nRanges, null); + const actual = await getFoldingRanges(languageModes, document, nRanges, null); let actualRanges = []; for (let i = 0; i < actual.length; i++) { @@ -37,9 +37,9 @@ function r(startLine: number, endLine: number, kind?: string): ExpectedIndentRan return { startLine, endLine, kind }; } -suite('HTML Folding', () => { +suite('HTML Folding', async () => { - test('Embedded JavaScript', () => { + test('Embedded JavaScript', async () => { const input = [ /*0*/'', /*1*/'', @@ -50,10 +50,10 @@ suite('HTML Folding', () => { /*6*/'', /*7*/'', ]; - assertRanges(input, [r(0, 6), r(1, 5), r(2, 4), r(3, 4)]); + await await assertRanges(input, [r(0, 6), r(1, 5), r(2, 4), r(3, 4)]); }); - test('Embedded JavaScript - multiple areas', () => { + test('Embedded JavaScript - multiple areas', async () => { const input = [ /* 0*/'', /* 1*/'', @@ -71,10 +71,10 @@ suite('HTML Folding', () => { /*13*/'', /*14*/'', ]; - assertRanges(input, [r(0, 13), r(1, 12), r(2, 6), r(3, 6), r(8, 11), r(9, 11)]); + await assertRanges(input, [r(0, 13), r(1, 12), r(2, 6), r(3, 6), r(8, 11), r(9, 11)]); }); - test('Embedded JavaScript - incomplete', () => { + test('Embedded JavaScript - incomplete', async () => { const input = [ /* 0*/'', /* 1*/'', @@ -87,10 +87,10 @@ suite('HTML Folding', () => { /* 8*/'', /* 9*/'', ]; - assertRanges(input, [r(0, 8), r(1, 7), r(2, 3), r(5, 6)]); + await assertRanges(input, [r(0, 8), r(1, 7), r(2, 3), r(5, 6)]); }); - test('Embedded JavaScript - regions', () => { + test('Embedded JavaScript - regions', async () => { const input = [ /* 0*/'', /* 1*/'', @@ -104,10 +104,10 @@ suite('HTML Folding', () => { /* 9*/'', /*10*/'', ]; - assertRanges(input, [r(0, 9), r(1, 8), r(2, 7), r(3, 7, 'region'), r(4, 6, 'region')]); + await assertRanges(input, [r(0, 9), r(1, 8), r(2, 7), r(3, 7, 'region'), r(4, 6, 'region')]); }); - test('Embedded CSS', () => { + test('Embedded CSS', async () => { const input = [ /* 0*/'', /* 1*/'', @@ -120,10 +120,10 @@ suite('HTML Folding', () => { /* 8*/'', /* 9*/'', ]; - assertRanges(input, [r(0, 8), r(1, 7), r(2, 6), r(3, 5)]); + await assertRanges(input, [r(0, 8), r(1, 7), r(2, 6), r(3, 5)]); }); - test('Embedded CSS - multiple areas', () => { + test('Embedded CSS - multiple areas', async () => { const input = [ /* 0*/'', /* 1*/'', @@ -141,10 +141,10 @@ suite('HTML Folding', () => { /*13*/'', /*14*/'', ]; - assertRanges(input, [r(0, 13), r(1, 12), r(2, 6), r(3, 6, 'comment'), r(8, 11), r(9, 10)]); + await assertRanges(input, [r(0, 13), r(1, 12), r(2, 6), r(3, 6, 'comment'), r(8, 11), r(9, 10)]); }); - test('Embedded CSS - regions', () => { + test('Embedded CSS - regions', async () => { const input = [ /* 0*/'', /* 1*/'', @@ -158,11 +158,11 @@ suite('HTML Folding', () => { /* 9*/'', /*10*/'', ]; - assertRanges(input, [r(0, 9), r(1, 8), r(2, 7), r(3, 7, 'region'), r(4, 6, 'region')]); + await assertRanges(input, [r(0, 9), r(1, 8), r(2, 7), r(3, 7, 'region'), r(4, 6, 'region')]); }); - // test('Embedded JavaScript - multi line comment', () => { + // test('Embedded JavaScript - multi line comment', async () => { // const input = [ // /* 0*/'', // /* 1*/'', @@ -174,10 +174,10 @@ suite('HTML Folding', () => { // /* 7*/'', // /* 8*/'', // ]; - // assertRanges(input, [r(0, 7), r(1, 6), r(2, 5), r(3, 5, 'comment')]); + // await assertRanges(input, [r(0, 7), r(1, 6), r(2, 5), r(3, 5, 'comment')]); // }); - test('Test limit', () => { + test('Test limit', async () => { const input = [ /* 0*/'
', /* 1*/' ', @@ -201,15 +201,15 @@ suite('HTML Folding', () => { /*19*/' ', /*20*/'
', ]; - assertRanges(input, [r(0, 19), r(1, 18), r(2, 3), r(5, 11), r(6, 7), r(9, 10), r(13, 14), r(16, 17)], 'no limit', undefined); - assertRanges(input, [r(0, 19), r(1, 18), r(2, 3), r(5, 11), r(6, 7), r(9, 10), r(13, 14), r(16, 17)], 'limit 8', 8); - assertRanges(input, [r(0, 19), r(1, 18), r(2, 3), r(5, 11), r(6, 7), r(13, 14), r(16, 17)], 'limit 7', 7); - assertRanges(input, [r(0, 19), r(1, 18), r(2, 3), r(5, 11), r(13, 14), r(16, 17)], 'limit 6', 6); - assertRanges(input, [r(0, 19), r(1, 18), r(2, 3), r(5, 11), r(13, 14)], 'limit 5', 5); - assertRanges(input, [r(0, 19), r(1, 18), r(2, 3), r(5, 11)], 'limit 4', 4); - assertRanges(input, [r(0, 19), r(1, 18), r(2, 3)], 'limit 3', 3); - assertRanges(input, [r(0, 19), r(1, 18)], 'limit 2', 2); - assertRanges(input, [r(0, 19)], 'limit 1', 1); + await assertRanges(input, [r(0, 19), r(1, 18), r(2, 3), r(5, 11), r(6, 7), r(9, 10), r(13, 14), r(16, 17)], 'no limit', undefined); + await assertRanges(input, [r(0, 19), r(1, 18), r(2, 3), r(5, 11), r(6, 7), r(9, 10), r(13, 14), r(16, 17)], 'limit 8', 8); + await assertRanges(input, [r(0, 19), r(1, 18), r(2, 3), r(5, 11), r(6, 7), r(13, 14), r(16, 17)], 'limit 7', 7); + await assertRanges(input, [r(0, 19), r(1, 18), r(2, 3), r(5, 11), r(13, 14), r(16, 17)], 'limit 6', 6); + await assertRanges(input, [r(0, 19), r(1, 18), r(2, 3), r(5, 11), r(13, 14)], 'limit 5', 5); + await assertRanges(input, [r(0, 19), r(1, 18), r(2, 3), r(5, 11)], 'limit 4', 4); + await assertRanges(input, [r(0, 19), r(1, 18), r(2, 3)], 'limit 3', 3); + await assertRanges(input, [r(0, 19), r(1, 18)], 'limit 2', 2); + await assertRanges(input, [r(0, 19)], 'limit 1', 1); }); }); diff --git a/extensions/html-language-features/server/src/test/formatting.test.ts b/extensions/html-language-features/server/src/test/formatting.test.ts index ac8779d4514..d7c922c736a 100644 --- a/extensions/html-language-features/server/src/test/formatting.test.ts +++ b/extensions/html-language-features/server/src/test/formatting.test.ts @@ -14,7 +14,7 @@ import { getNodeFSRequestService } from '../node/nodeFs'; suite('HTML Embedded Formatting', () => { - function assertFormat(value: string, expected: string, options?: any, formatOptions?: FormattingOptions, message?: string): void { + async function assertFormat(value: string, expected: string, options?: any, formatOptions?: FormattingOptions, message?: string): Promise { let workspace = { settings: options, folders: [{ name: 'foo', uri: 'test://foo' }] @@ -38,53 +38,53 @@ suite('HTML Embedded Formatting', () => { formatOptions = FormattingOptions.create(2, true); } - let result = format(languageModes, document, range, formatOptions, undefined, { css: true, javascript: true }); + let result = await format(languageModes, document, range, formatOptions, undefined, { css: true, javascript: true }); let actual = TextDocument.applyEdits(document, result); assert.equal(actual, expected, message); } - function assertFormatWithFixture(fixtureName: string, expectedPath: string, options?: any, formatOptions?: FormattingOptions): void { + async function assertFormatWithFixture(fixtureName: string, expectedPath: string, options?: any, formatOptions?: FormattingOptions): Promise { let input = fs.readFileSync(path.join(__dirname, '..', '..', 'src', 'test', 'fixtures', 'inputs', fixtureName)).toString().replace(/\r\n/mg, '\n'); let expected = fs.readFileSync(path.join(__dirname, '..', '..', 'src', 'test', 'fixtures', 'expected', expectedPath)).toString().replace(/\r\n/mg, '\n'); - assertFormat(input, expected, options, formatOptions, expectedPath); + await assertFormat(input, expected, options, formatOptions, expectedPath); } - test('HTML only', function (): any { - assertFormat('

Hello

', '\n\n\n

Hello

\n\n\n'); - assertFormat('|

Hello

|', '\n\n\n

Hello

\n\n\n'); - assertFormat('|

Hello

|', '\n

Hello

\n'); + test('HTML only', async () => { + await assertFormat('

Hello

', '\n\n\n

Hello

\n\n\n'); + await assertFormat('|

Hello

|', '\n\n\n

Hello

\n\n\n'); + await assertFormat('|

Hello

|', '\n

Hello

\n'); }); - test('HTML & Scripts', function (): any { - assertFormat('', '\n\n\n \n\n\n'); - assertFormat('', '\n\n\n \n\n\n'); - assertFormat('', '\n\n\n \n\n\n'); - assertFormat('\n ', '\n\n\n \n\n\n'); - assertFormat('\n ', '\n\n\n \n\n\n'); - assertFormat('\n ||', '\n '); + test('HTML & Scripts', async () => { + await assertFormat('', '\n\n\n \n\n\n'); + await assertFormat('', '\n\n\n \n\n\n'); + await assertFormat('', '\n\n\n \n\n\n'); + await assertFormat('\n ', '\n\n\n \n\n\n'); + await assertFormat('\n ', '\n\n\n \n\n\n'); + await assertFormat('\n ||', '\n '); }); - test('HTLM & Scripts - Fixtures', function () { + test('HTLM & Scripts - Fixtures', async () => { assertFormatWithFixture('19813.html', '19813.html'); assertFormatWithFixture('19813.html', '19813-4spaces.html', undefined, FormattingOptions.create(4, true)); assertFormatWithFixture('19813.html', '19813-tab.html', undefined, FormattingOptions.create(1, false)); assertFormatWithFixture('21634.html', '21634.html'); }); - test('Script end tag', function (): any { - assertFormat('\n\n ', '\n\n\n \n\n\n'); + test('Script end tag', async () => { + await assertFormat('\n\n ', '\n\n\n \n\n\n'); }); - test('HTML & Multiple Scripts', function (): any { - assertFormat('\n', '\n\n\n \n \n\n\n'); + test('HTML & Multiple Scripts', async () => { + await assertFormat('\n', '\n\n\n \n \n\n\n'); }); - test('HTML & Styles', function (): any { - assertFormat('\n', '\n\n\n \n\n\n'); + test('HTML & Styles', async () => { + await assertFormat('\n', '\n\n\n \n\n\n'); }); - test('EndWithNewline', function (): any { + test('EndWithNewline', async () => { let options = { html: { format: { @@ -92,26 +92,26 @@ suite('HTML Embedded Formatting', () => { } } }; - assertFormat('

Hello

', '\n\n\n

Hello

\n\n\n\n', options); - assertFormat('|

Hello

|', '\n

Hello

\n', options); - assertFormat('', '\n\n\n \n\n\n\n', options); + await assertFormat('

Hello

', '\n\n\n

Hello

\n\n\n\n', options); + await assertFormat('|

Hello

|', '\n

Hello

\n', options); + await assertFormat('', '\n\n\n \n\n\n\n', options); }); - test('Inside script', function (): any { - assertFormat('\n ', '\n '); - assertFormat('\n ', '\n '); + test('Inside script', async () => { + await assertFormat('\n ', '\n '); + await assertFormat('\n ', '\n '); }); - test('Range after new line', function (): any { - assertFormat('\n |\n|', '\n \n'); + test('Range after new line', async () => { + await assertFormat('\n |\n|', '\n \n'); }); - test('bug 36574', function (): any { - assertFormat('', ''); + test('bug 36574', async () => { + await assertFormat('', ''); }); - test('bug 48049', function (): any { - assertFormat( + test('bug 48049', async () => { + await assertFormat( [ '', '', @@ -159,7 +159,7 @@ suite('HTML Embedded Formatting', () => { ].join('\n') ); }); - test('#58435', () => { + test('#58435', async () => { let options = { html: { format: { @@ -190,7 +190,7 @@ suite('HTML Embedded Formatting', () => { '', ].join('\n'); - assertFormat(content, expected, options); + await assertFormat(content, expected, options); }); }); /* diff --git a/extensions/html-language-features/server/src/test/selectionRanges.test.ts b/extensions/html-language-features/server/src/test/selectionRanges.test.ts index b08e4604170..9f9937bcaf7 100644 --- a/extensions/html-language-features/server/src/test/selectionRanges.test.ts +++ b/extensions/html-language-features/server/src/test/selectionRanges.test.ts @@ -9,7 +9,7 @@ import { getLanguageModes, ClientCapabilities, TextDocument, SelectionRange} fro import { getSelectionRanges } from '../modes/selectionRanges'; import { getNodeFSRequestService } from '../node/nodeFs'; -function assertRanges(content: string, expected: (number | string)[][]): void { +async function assertRanges(content: string, expected: (number | string)[][]): Promise { let message = `${content} gives selection range:\n`; const offset = content.indexOf('|'); @@ -22,7 +22,7 @@ function assertRanges(content: string, expected: (number | string)[][]): void { const languageModes = getLanguageModes({ css: true, javascript: true }, workspace, ClientCapabilities.LATEST, getNodeFSRequestService()); const document = TextDocument.create('test://foo.html', 'html', 1, content); - const actualRanges = getSelectionRanges(languageModes, document, [document.positionAt(offset)]); + const actualRanges = await getSelectionRanges(languageModes, document, [document.positionAt(offset)]); assert.equal(actualRanges.length, 1); const offsetPairs: [number, string][] = []; let curr: SelectionRange | undefined = actualRanges[0]; @@ -36,8 +36,8 @@ function assertRanges(content: string, expected: (number | string)[][]): void { } suite('HTML SelectionRange', () => { - test('Embedded JavaScript', () => { - assertRanges('', [ + test('Embedded JavaScript', async () => { + await assertRanges('', [ [48, '1'], [48, '1+2'], [47, '(1+2)'], @@ -52,8 +52,8 @@ suite('HTML SelectionRange', () => { ]); }); - test('Embedded CSS', () => { - assertRanges('', [ + test('Embedded CSS', async () => { + await assertRanges('', [ [34, 'none'], [25, 'display: none'], [24, ' display: none; '], @@ -66,8 +66,8 @@ suite('HTML SelectionRange', () => { ]); }); - test('Embedded style', () => { - assertRanges('
', [ + test('Embedded style', async () => { + await assertRanges('
', [ [19, 'red'], [12, 'color: red'], [11, '"color: red"'], diff --git a/extensions/html-language-features/server/src/test/semanticTokens.test.ts b/extensions/html-language-features/server/src/test/semanticTokens.test.ts index d5c5b7203ea..6d815748e5e 100644 --- a/extensions/html-language-features/server/src/test/semanticTokens.test.ts +++ b/extensions/html-language-features/server/src/test/semanticTokens.test.ts @@ -16,7 +16,7 @@ interface ExpectedToken { tokenClassifiction: string; } -function assertTokens(lines: string[], expected: ExpectedToken[], ranges?: Range[], message?: string): void { +async function assertTokens(lines: string[], expected: ExpectedToken[], ranges?: Range[], message?: string): Promise { const document = TextDocument.create('test://foo/bar.html', 'html', 1, lines.join('\n')); const workspace = { settings: {}, @@ -26,7 +26,7 @@ function assertTokens(lines: string[], expected: ExpectedToken[], ranges?: Range const semanticTokensProvider = newSemanticTokenProvider(languageModes); const legend = semanticTokensProvider.legend; - const actual = semanticTokensProvider.getSemanticTokens(document, ranges); + const actual = await semanticTokensProvider.getSemanticTokens(document, ranges); let actualRanges = []; let lastLine = 0; @@ -49,7 +49,7 @@ function t(startLine: number, character: number, length: number, tokenClassifict suite('HTML Semantic Tokens', () => { - test('Variables', () => { + test('Variables', async () => { const input = [ /*0*/'', /*1*/'', @@ -64,7 +64,7 @@ suite('HTML Semantic Tokens', () => { /*10*/'', /*11*/'', ]; - assertTokens(input, [ + await assertTokens(input, [ t(3, 6, 1, 'variable.declaration'), t(3, 13, 2, 'variable.declaration'), t(3, 19, 1, 'variable'), t(5, 15, 1, 'variable.declaration.readonly'), t(5, 20, 2, 'variable'), t(5, 26, 1, 'variable'), t(5, 30, 1, 'variable.readonly'), t(6, 11, 1, 'variable.declaration'), @@ -72,7 +72,7 @@ suite('HTML Semantic Tokens', () => { ]); }); - test('Functions', () => { + test('Functions', async () => { const input = [ /*0*/'', /*1*/'', @@ -85,14 +85,14 @@ suite('HTML Semantic Tokens', () => { /*8*/'', /*9*/'', ]; - assertTokens(input, [ + await assertTokens(input, [ t(3, 11, 3, 'function.declaration'), t(3, 15, 2, 'parameter.declaration'), t(4, 11, 3, 'function'), t(4, 15, 4, 'interface'), t(4, 20, 3, 'member'), t(4, 24, 2, 'parameter'), t(6, 6, 6, 'variable'), t(6, 13, 8, 'property'), t(6, 24, 5, 'member'), t(6, 35, 7, 'member'), t(6, 43, 1, 'parameter.declaration'), t(6, 48, 3, 'function'), t(6, 52, 1, 'parameter') ]); }); - test('Members', () => { + test('Members', async () => { const input = [ /*0*/'', /*1*/'', @@ -111,7 +111,7 @@ suite('HTML Semantic Tokens', () => { ]; - assertTokens(input, [ + await assertTokens(input, [ t(3, 8, 1, 'class.declaration'), t(4, 11, 1, 'property.declaration.static'), t(5, 4, 1, 'property.declaration'), @@ -121,7 +121,7 @@ suite('HTML Semantic Tokens', () => { ]); }); - test('Interfaces', () => { + test('Interfaces', async () => { const input = [ /*0*/'', /*1*/'', @@ -133,14 +133,14 @@ suite('HTML Semantic Tokens', () => { /*7*/'', /*8*/'', ]; - assertTokens(input, [ + await assertTokens(input, [ t(3, 12, 8, 'interface.declaration'), t(3, 23, 1, 'property.declaration'), t(3, 34, 1, 'property.declaration'), t(4, 8, 1, 'variable.declaration.readonly'), t(4, 30, 8, 'interface'), t(5, 8, 3, 'variable.declaration.readonly'), t(5, 15, 1, 'parameter.declaration'), t(5, 18, 8, 'interface'), t(5, 31, 1, 'parameter'), t(5, 33, 1, 'property'), t(5, 37, 1, 'parameter'), t(5, 39, 1, 'property') ]); }); - test('Readonly', () => { + test('Readonly', async () => { const input = [ /*0*/'', /*1*/'', @@ -153,7 +153,7 @@ suite('HTML Semantic Tokens', () => { /*8*/'', /*9*/'', ]; - assertTokens(input, [ + await assertTokens(input, [ t(3, 8, 1, 'variable.declaration.readonly'), t(4, 8, 1, 'class.declaration'), t(4, 28, 1, 'property.declaration.static.readonly'), t(4, 42, 3, 'property.declaration.static'), t(4, 47, 3, 'interface'), t(5, 13, 1, 'enum.declaration'), t(5, 17, 1, 'property.declaration.readonly'), t(5, 24, 1, 'property.declaration.readonly'), t(5, 28, 1, 'property.readonly'), @@ -162,7 +162,7 @@ suite('HTML Semantic Tokens', () => { }); - test('Type aliases and type parameters', () => { + test('Type aliases and type parameters', async () => { const input = [ /*0*/'', /*1*/'', @@ -175,14 +175,14 @@ suite('HTML Semantic Tokens', () => { /*8*/'', /*9*/'', ]; - assertTokens(input, [ + await assertTokens(input, [ t(3, 7, 5, 'type.declaration'), t(3, 15, 3, 'interface') /* to investiagte */, t(4, 11, 1, 'function.declaration'), t(4, 13, 1, 'typeParameter.declaration'), t(4, 23, 5, 'type'), t(4, 30, 1, 'parameter.declaration'), t(4, 33, 1, 'typeParameter'), t(4, 47, 1, 'typeParameter'), t(5, 12, 1, 'typeParameter'), t(5, 29, 3, 'interface'), t(5, 41, 5, 'type'), ]); }); - test('TS and JS', () => { + test('TS and JS', async () => { const input = [ /*0*/'', /*1*/'', @@ -195,13 +195,13 @@ suite('HTML Semantic Tokens', () => { /*8*/'', /*9*/'', ]; - assertTokens(input, [ + await assertTokens(input, [ t(3, 11, 1, 'function.declaration'), t(3, 13, 1, 'typeParameter.declaration'), t(3, 16, 2, 'parameter.declaration'), t(3, 20, 1, 'typeParameter'), t(3, 24, 1, 'typeParameter'), t(3, 39, 2, 'parameter'), t(6, 2, 6, 'variable'), t(6, 9, 5, 'member') ]); }); - test('Ranges', () => { + test('Ranges', async () => { const input = [ /*0*/'', /*1*/'', @@ -214,11 +214,11 @@ suite('HTML Semantic Tokens', () => { /*8*/'', /*9*/'', ]; - assertTokens(input, [ + await assertTokens(input, [ t(3, 2, 6, 'variable'), t(3, 9, 5, 'member') ], [Range.create(Position.create(2, 0), Position.create(4, 0))]); - assertTokens(input, [ + await assertTokens(input, [ t(6, 2, 6, 'variable'), ], [Range.create(Position.create(6, 2), Position.create(6, 8))]); }); diff --git a/extensions/html-language-features/server/src/utils/runner.ts b/extensions/html-language-features/server/src/utils/runner.ts index 98a7a96f9aa..6bf8a88d0c6 100644 --- a/extensions/html-language-features/server/src/utils/runner.ts +++ b/extensions/html-language-features/server/src/utils/runner.ts @@ -17,7 +17,7 @@ export function formatError(message: string, err: any): string { return message; } -export function runSafeAsync(func: () => Thenable, errorVal: T, errorMessage: string, token: CancellationToken): Thenable> { +export function runSafe(func: () => Thenable, errorVal: T, errorMessage: string, token: CancellationToken): Thenable> { return new Promise>((resolve) => { setImmediate(() => { if (token.isCancellationRequested) { @@ -38,29 +38,7 @@ export function runSafeAsync(func: () => Thenable, errorVal: T, errorMessa }); } -export function runSafe(func: () => T, errorVal: T, errorMessage: string, token: CancellationToken): Thenable> { - return new Promise>((resolve) => { - setImmediate(() => { - if (token.isCancellationRequested) { - resolve(cancelValue()); - } else { - try { - let result = func(); - if (token.isCancellationRequested) { - resolve(cancelValue()); - return; - } else { - resolve(result); - } - } catch (e) { - console.error(formatError(errorMessage, e)); - resolve(errorVal); - } - } - }); - }); -} function cancelValue() { return new ResponseError(ErrorCodes.RequestCancelled, 'Request cancelled');