From 9997c23708a831f1d9ec4ac63468cdb60b8873c1 Mon Sep 17 00:00:00 2001 From: Martin Aeschlimann Date: Fri, 16 Oct 2020 14:25:40 +0200 Subject: [PATCH 0001/1815] resources#relativePath does not honour path casing strategy --- src/vs/base/common/resources.ts | 20 ++++++++++++++----- src/vs/base/test/common/resources.test.ts | 2 ++ src/vs/platform/workspace/common/workspace.ts | 10 ++++++---- 3 files changed, 23 insertions(+), 9 deletions(-) diff --git a/src/vs/base/common/resources.ts b/src/vs/base/common/resources.ts index 621c97368c75..88cbade64115 100644 --- a/src/vs/base/common/resources.ts +++ b/src/vs/base/common/resources.ts @@ -8,7 +8,7 @@ import * as paths from 'vs/base/common/path'; import { URI, uriToFsPath } from 'vs/base/common/uri'; import { equalsIgnoreCase, compare as strCompare } from 'vs/base/common/strings'; import { Schemas } from 'vs/base/common/network'; -import { isWindows, isLinux } from 'vs/base/common/platform'; +import { isLinux } from 'vs/base/common/platform'; import { CharCode } from 'vs/base/common/charCode'; import { ParsedExpression, IExpression, parse } from 'vs/base/common/glob'; import { TernarySearchTree } from 'vs/base/common/map'; @@ -229,11 +229,10 @@ export class ExtUri implements IExtUri { if (from.scheme !== to.scheme || !isEqualAuthority(from.authority, to.authority)) { return undefined; } - if (from.scheme === Schemas.file) { - const relativePath = paths.relative(originalFSPath(from), originalFSPath(to)); - return isWindows ? extpath.toSlashes(relativePath) : relativePath; - } let fromPath = from.path || '/', toPath = to.path || '/'; + if (getWindowsDriveLetter(fromPath) !== getWindowsDriveLetter(toPath)) { + return undefined; // no relative path possible if the drive letter doesn't match + } if (this._ignorePathCasing(from)) { // make casing of fromPath match toPath let i = 0; @@ -393,6 +392,17 @@ export function distinctParents(items: T[], resourceAccessor: (item: T) => UR return distinctParents; } +/** + * Given a URI path (not a fs path!), tests if the path looks like a Window path with drive letter and returns the lowercase variant of that drive letter. + * @param path returns the drive letter (lower case) or undefined if the path does not look like a windows path + */ +function getWindowsDriveLetter(path: string): string | undefined { + if (/^\/[a-zA-Z]:(\/|$)/.test(path)) { + return path.charAt(1).toLowerCase(); + } + return undefined; +} + /** * Data URI related helpers. */ diff --git a/src/vs/base/test/common/resources.test.ts b/src/vs/base/test/common/resources.test.ts index 8026e240bba4..c608e113a3d8 100644 --- a/src/vs/base/test/common/resources.test.ts +++ b/src/vs/base/test/common/resources.test.ts @@ -273,6 +273,8 @@ suite('Resources', () => { assertRelativePath(URI.parse('foo://a/foo'), URI.parse('foo://A/FOO/BAR/GOO'), 'BAR/GOO', false, true); assertRelativePath(URI.parse('foo://a/foo/xoo'), URI.parse('foo://A/FOO/BAR/GOO'), '../BAR/GOO', false, true); assertRelativePath(URI.parse('foo:///c:/a/foo'), URI.parse('foo:///C:/a/foo/xoo/'), 'xoo', false, true); + assertRelativePath(URI.parse('foo:///c:/a/foo'), URI.parse('foo:///D:/a/foo/xoo/'), undefined, false, true); + assertRelativePath(URI.parse('file:///c:/a/foo'), URI.parse('file:///D:/a/foo/xoo/'), undefined, false, true); if (isWindows) { assertRelativePath(URI.file('c:\\foo\\bar'), URI.file('c:\\foo\\bar'), ''); diff --git a/src/vs/platform/workspace/common/workspace.ts b/src/vs/platform/workspace/common/workspace.ts index c0c34d74f572..3e7183ea6d4c 100644 --- a/src/vs/platform/workspace/common/workspace.ts +++ b/src/vs/platform/workspace/common/workspace.ts @@ -236,12 +236,14 @@ export function toWorkspaceFolders(configuredFolders: IStoredWorkspaceFolder[], let result: WorkspaceFolder[] = []; let seen: Set = new Set(); - const relativeTo = resources.dirname(workspaceConfigFile); + const extUri = resources.extUriBiasedIgnorePathCase; + + const relativeTo = extUri.dirname(workspaceConfigFile); for (let configuredFolder of configuredFolders) { let uri: URI | null = null; if (isRawFileWorkspaceFolder(configuredFolder)) { if (configuredFolder.path) { - uri = resources.resolvePath(relativeTo, configuredFolder.path); + uri = extUri.resolvePath(relativeTo, configuredFolder.path); } } else if (isRawUriWorkspaceFolder(configuredFolder)) { try { @@ -257,11 +259,11 @@ export function toWorkspaceFolders(configuredFolders: IStoredWorkspaceFolder[], } if (uri) { // remove duplicates - let comparisonKey = resources.getComparisonKey(uri); + let comparisonKey = extUri.getComparisonKey(uri); if (!seen.has(comparisonKey)) { seen.add(comparisonKey); - const name = configuredFolder.name || resources.basenameOrAuthority(uri); + const name = configuredFolder.name || extUri.basenameOrAuthority(uri); result.push(new WorkspaceFolder({ uri, name, index: result.length }, configuredFolder)); } } From da712f4071300472ce9dcac45ab6da524ecfade4 Mon Sep 17 00:00:00 2001 From: Martin Aeschlimann Date: Fri, 16 Oct 2020 16:30:41 +0200 Subject: [PATCH 0002/1815] more changes --- src/vs/platform/workspace/common/workspace.ts | 2 +- src/vs/platform/workspaces/common/workspaces.ts | 14 +++++++++----- 2 files changed, 10 insertions(+), 6 deletions(-) diff --git a/src/vs/platform/workspace/common/workspace.ts b/src/vs/platform/workspace/common/workspace.ts index 3e7183ea6d4c..d79c047f066b 100644 --- a/src/vs/platform/workspace/common/workspace.ts +++ b/src/vs/platform/workspace/common/workspace.ts @@ -236,7 +236,7 @@ export function toWorkspaceFolders(configuredFolders: IStoredWorkspaceFolder[], let result: WorkspaceFolder[] = []; let seen: Set = new Set(); - const extUri = resources.extUriBiasedIgnorePathCase; + const extUri = resources.extUriBiasedIgnorePathCase; // To be replaced by the UriIdentityService as parameter: #108793 const relativeTo = extUri.dirname(workspaceConfigFile); for (let configuredFolder of configuredFolders) { diff --git a/src/vs/platform/workspaces/common/workspaces.ts b/src/vs/platform/workspaces/common/workspaces.ts index d6643801ab6c..a4837165c061 100644 --- a/src/vs/platform/workspaces/common/workspaces.ts +++ b/src/vs/platform/workspaces/common/workspaces.ts @@ -9,7 +9,7 @@ import { IWorkspaceFolder, IWorkspace } from 'vs/platform/workspace/common/works import { URI, UriComponents } from 'vs/base/common/uri'; import { isWindows, isLinux, isMacintosh } from 'vs/base/common/platform'; import { extname, isAbsolute } from 'vs/base/common/path'; -import { dirname, resolvePath, isEqualAuthority, relativePath, extname as resourceExtname, extUriBiasedIgnorePathCase } from 'vs/base/common/resources'; +import { isEqualAuthority, extname as resourceExtname, extUriBiasedIgnorePathCase } from 'vs/base/common/resources'; import * as jsonEdit from 'vs/base/common/jsonEdit'; import * as json from 'vs/base/common/json'; import { Schemas } from 'vs/base/common/network'; @@ -217,7 +217,9 @@ export function getStoredWorkspaceFolder(folderURI: URI, forceAbsolute: boolean, return { name: folderName, uri: folderURI.toString(true) }; } - let folderPath = !forceAbsolute ? relativePath(targetConfigFolderURI, folderURI) : undefined; + const extUri = extUriBiasedIgnorePathCase; // To be replaced by the UriIdentityService as parameter: #108793 + + let folderPath = !forceAbsolute ? extUri.relativePath(targetConfigFolderURI, folderURI) : undefined; if (folderPath !== undefined) { if (folderPath.length === 0) { folderPath = '.'; @@ -258,14 +260,16 @@ export function getStoredWorkspaceFolder(folderURI: URI, forceAbsolute: boolean, export function rewriteWorkspaceFileForNewLocation(rawWorkspaceContents: string, configPathURI: URI, isFromUntitledWorkspace: boolean, targetConfigPathURI: URI) { let storedWorkspace = doParseStoredWorkspace(configPathURI, rawWorkspaceContents); - const sourceConfigFolder = dirname(configPathURI); - const targetConfigFolder = dirname(targetConfigPathURI); + const extUri = extUriBiasedIgnorePathCase; // To be replaced by the UriIdentityService as parameter: #108793 + + const sourceConfigFolder = extUri.dirname(configPathURI); + const targetConfigFolder = extUri.dirname(targetConfigPathURI); const rewrittenFolders: IStoredWorkspaceFolder[] = []; const slashForPath = useSlashForPath(storedWorkspace.folders); for (const folder of storedWorkspace.folders) { - const folderURI = isRawFileWorkspaceFolder(folder) ? resolvePath(sourceConfigFolder, folder.path) : URI.parse(folder.uri); + const folderURI = isRawFileWorkspaceFolder(folder) ? extUri.resolvePath(sourceConfigFolder, folder.path) : URI.parse(folder.uri); let absolute; if (isFromUntitledWorkspace) { // if it was an untitled workspace, try to make paths relative From 8308a6dc10eb29477dc23b8b8e9d8c5074903cc0 Mon Sep 17 00:00:00 2001 From: Orta Date: Fri, 16 Apr 2021 22:27:19 +0100 Subject: [PATCH 0003/1815] Adds the ability to resolve @types and JSDoc imports in inline --- .../server/src/modes/javascriptMode.ts | 128 +++++++++++++----- .../server/src/test/completions.test.ts | 24 +++- .../src/test/jsdocImportFixtures/index.html | 1 + .../src/test/jsdocImportFixtures/index.js | 4 + .../test/jsdocImportFixtures/jsDocTypes.ts | 9 ++ 5 files changed, 125 insertions(+), 41 deletions(-) create mode 100644 extensions/html-language-features/server/src/test/jsdocImportFixtures/index.html create mode 100644 extensions/html-language-features/server/src/test/jsdocImportFixtures/index.js create mode 100644 extensions/html-language-features/server/src/test/jsdocImportFixtures/jsDocTypes.ts diff --git a/extensions/html-language-features/server/src/modes/javascriptMode.ts b/extensions/html-language-features/server/src/modes/javascriptMode.ts index 01e0b381d112..ea51fa0e4b21 100644 --- a/extensions/html-language-features/server/src/modes/javascriptMode.ts +++ b/extensions/html-language-features/server/src/modes/javascriptMode.ts @@ -12,20 +12,34 @@ import { } from './languageModes'; import { getWordAtText, isWhitespaceOnly, repeat } from '../utils/strings'; import { HTMLDocumentRegions } from './embeddedSupport'; +import { normalize, sep } from 'path'; import * as ts from 'typescript'; import { getSemanticTokens, getSemanticTokenLegend } from './javascriptSemanticTokens'; const JS_WORD_REGEX = /(-?\d*\.\d\w*)|([^\`\~\!\@\#\%\^\&\*\(\)\-\=\+\[\{\]\}\\\|\;\:\'\"\,\.\<\>\/\?\s]+)/g; +/** TypeScript does not handle schemes on file references, so normalize and remove the schemes when communicating with tsserver */ +function deschemeURI(uri: string) { + if (!uri.startsWith('file://')) { + return uri ; + } + // This is replicating the logic in TypeScriptServiceClient.normalizedPath + const newPath = normalize(uri.replace('file://', '')); + + // Both \ and / must be escaped in regular expressions + return newPath.replace(new RegExp('\\' + sep, '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, ''); + let currentWorkspace: Workspace = undefined!; const jsLanguageService = import(/* webpackChunkName: "javascriptLibs" */ './javascriptLibs').then(libs => { const host: ts.LanguageServiceHost = { getCompilationSettings: () => compilerOptions, - getScriptFileNames: () => [currentTextDocument.uri, 'jquery'], + getScriptFileNames: () => [deschemeURI(currentTextDocument.uri), 'jquery'], getScriptKind: (fileName) => { if (fileName === currentTextDocument.uri) { return scriptKind; @@ -33,15 +47,17 @@ function getLanguageServiceHost(scriptKind: ts.ScriptKind) { return fileName.substr(fileName.length - 2) === 'ts' ? ts.ScriptKind.TS : ts.ScriptKind.JS; }, getScriptVersion: (fileName: string) => { - if (fileName === currentTextDocument.uri) { + if (fileName === deschemeURI(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) { + if (fileName === deschemeURI(currentTextDocument.uri)) { text = currentTextDocument.getText(); + } else if (ts.sys.fileExists(fileName)) { + text = ts.sys.readFile(fileName, 'utf8')!; } else { text = libs.loadLibrary(fileName); } @@ -51,14 +67,24 @@ function getLanguageServiceHost(scriptKind: ts.ScriptKind) { getChangeRange: () => undefined }; }, - getCurrentDirectory: () => '', - getDefaultLibFileName: (_options: ts.CompilerOptions) => 'es6' + getCurrentDirectory: () => { + const workspace = currentWorkspace && currentWorkspace.folders.find(ws => deschemeURI(currentTextDocument.uri).startsWith(deschemeURI(ws.uri))); + return workspace ? deschemeURI(workspace.uri) : ''; + }, + getDefaultLibFileName: (_options: ts.CompilerOptions) => 'es6', + fileExists: ts.sys.fileExists, + readFile: ts.sys.readFile, + readDirectory: ts.sys.readDirectory, + directoryExists: ts.sys.directoryExists, + getDirectories: ts.sys.getDirectories, }; + return ts.createLanguageService(host); }); return { - async getLanguageService(jsDocument: TextDocument): Promise { + async getLanguageService(jsDocument: TextDocument, workspace: Workspace): Promise { currentTextDocument = jsDocument; + currentWorkspace = workspace; return jsLanguageService; }, getCompilationSettings() { @@ -84,9 +110,11 @@ export function getJavaScriptMode(documentRegions: LanguageModelCache { host.getCompilationSettings()['experimentalDecorators'] = settings && settings.javascript && settings.javascript.implicitProjectConfig.experimentalDecorators; 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); + const languageService = await host.getLanguageService(jsDocument, workspace); + const filePath = deschemeURI(jsDocument.uri); + + const syntaxDiagnostics: ts.Diagnostic[] = languageService.getSyntacticDiagnostics(filePath); + const semanticDiagnostics = languageService.getSemanticDiagnostics(filePath); return syntaxDiagnostics.concat(semanticDiagnostics).map((diag: ts.Diagnostic): Diagnostic => { return { range: convertRange(jsDocument, diag), @@ -98,9 +126,12 @@ export function getJavaScriptMode(documentRegions: LanguageModelCache { const jsDocument = jsDocuments.get(document); - const jsLanguageService = await host.getLanguageService(jsDocument); + const jsLanguageService = await host.getLanguageService(jsDocument, workspace); + const filePath = deschemeURI(jsDocument.uri); + let offset = jsDocument.offsetAt(position); - let completions = jsLanguageService.getCompletionsAtPosition(jsDocument.uri, offset, { includeExternalModuleExports: false, includeInsertTextCompletions: false }); + let completions = jsLanguageService.getCompletionsAtPosition(filePath, offset, { includeExternalModuleExports: false, includeInsertTextCompletions: false }); + if (!completions) { return { isIncomplete: false, items: [] }; } @@ -126,9 +157,11 @@ export function getJavaScriptMode(documentRegions: LanguageModelCache { const jsDocument = jsDocuments.get(document); - const jsLanguageService = await host.getLanguageService(jsDocument); + const jsLanguageService = await host.getLanguageService(jsDocument, workspace); + const filePath = deschemeURI(jsDocument.uri); + // @ts-expect-error until 4.3 protocol update - let details = jsLanguageService.getCompletionEntryDetails(jsDocument.uri, item.data.offset, item.label, undefined, undefined, undefined, undefined); + let details = jsLanguageService.getCompletionEntryDetails(filePath, item.data.offset, item.label, undefined, undefined, undefined, undefined); if (details) { item.detail = ts.displayPartsToString(details.displayParts); item.documentation = ts.displayPartsToString(details.documentation); @@ -138,8 +171,10 @@ 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)); + const jsLanguageService = await host.getLanguageService(jsDocument, workspace); + const filePath = deschemeURI(jsDocument.uri); + + let info = jsLanguageService.getQuickInfoAtPosition(filePath, jsDocument.offsetAt(position)); if (info) { const contents = ts.displayPartsToString(info.displayParts); return { @@ -151,8 +186,10 @@ export function getJavaScriptMode(documentRegions: LanguageModelCache { const jsDocument = jsDocuments.get(document); - const jsLanguageService = await host.getLanguageService(jsDocument); - let signHelp = jsLanguageService.getSignatureHelpItems(jsDocument.uri, jsDocument.offsetAt(position), undefined); + const jsLanguageService = await host.getLanguageService(jsDocument, workspace); + const filePath = deschemeURI(jsDocument.uri); + + let signHelp = jsLanguageService.getSignatureHelpItems(filePath, jsDocument.offsetAt(position), undefined); if (signHelp) { let ret: SignatureHelp = { activeSignature: signHelp.selectedItemIndex, @@ -189,13 +226,15 @@ export function getJavaScriptMode(documentRegions: LanguageModelCache { @@ -211,8 +250,10 @@ 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 jsLanguageService = await host.getLanguageService(jsDocument, workspace); + const filePath = deschemeURI(jsDocument.uri); + + const highlights = jsLanguageService.getDocumentHighlights(filePath, jsDocument.offsetAt(position), [filePath]); const out: DocumentHighlight[] = []; for (const entry of highlights || []) { for (const highlight of entry.highlightSpans) { @@ -226,8 +267,10 @@ export function getJavaScriptMode(documentRegions: LanguageModelCache { const jsDocument = jsDocuments.get(document); - const jsLanguageService = await host.getLanguageService(jsDocument); - let items = jsLanguageService.getNavigationBarItems(jsDocument.uri); + const jsLanguageService = await host.getLanguageService(jsDocument, workspace); + const filePath = deschemeURI(jsDocument.uri); + + let items = jsLanguageService.getNavigationBarItems(filePath); if (items) { let result: SymbolInformation[] = []; let existing = Object.create(null); @@ -263,8 +306,10 @@ 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)); + const jsLanguageService = await host.getLanguageService(jsDocument, workspace); + const filePath = deschemeURI(jsDocument.uri); + + let definition = jsLanguageService.getDefinitionAtPosition(filePath, jsDocument.offsetAt(position)); if (definition) { return definition.filter(d => d.fileName === jsDocument.uri).map(d => { return { @@ -277,10 +322,12 @@ export function getJavaScriptMode(documentRegions: LanguageModelCache { const jsDocument = jsDocuments.get(document); - const jsLanguageService = await host.getLanguageService(jsDocument); - let references = jsLanguageService.getReferencesAtPosition(jsDocument.uri, jsDocument.offsetAt(position)); + const jsLanguageService = await host.getLanguageService(jsDocument, workspace); + const filePath = deschemeURI(jsDocument.uri); + + let references = jsLanguageService.getReferencesAtPosition(filePath, jsDocument.offsetAt(position)); if (references) { - return references.filter(d => d.fileName === jsDocument.uri).map(d => { + return references.filter(d => d.fileName === filePath).map(d => { return { uri: document.uri, range: convertRange(jsDocument, d.textSpan) @@ -291,17 +338,20 @@ export function getJavaScriptMode(documentRegions: LanguageModelCache { const jsDocument = jsDocuments.get(document); - const jsLanguageService = await host.getLanguageService(jsDocument); + const jsLanguageService = await host.getLanguageService(jsDocument, workspace); + const filePath = deschemeURI(jsDocument.uri); + function convertSelectionRange(selectionRange: ts.SelectionRange): SelectionRange { const parent = selectionRange.parent ? convertSelectionRange(selectionRange.parent) : undefined; return SelectionRange.create(convertRange(jsDocument, selectionRange.textSpan), parent); } - const range = jsLanguageService.getSmartSelectionRange(jsDocument.uri, jsDocument.offsetAt(position)); + const range = jsLanguageService.getSmartSelectionRange(filePath, jsDocument.offsetAt(position)); return convertSelectionRange(range); }, 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); + const jsLanguageService = await host.getLanguageService(jsDocument, workspace); + const filePath = deschemeURI(jsDocument.uri); let formatterSettings = settings && settings.javascript && settings.javascript.format; @@ -314,7 +364,7 @@ export function getJavaScriptMode(documentRegions: LanguageModelCache { const jsDocument = jsDocuments.get(document); - const jsLanguageService = await host.getLanguageService(jsDocument); - let spans = jsLanguageService.getOutliningSpans(jsDocument.uri); + const jsLanguageService = await host.getLanguageService(jsDocument, workspace); + const filePath = deschemeURI(jsDocument.uri); + + let spans = jsLanguageService.getOutliningSpans(filePath); let ranges: FoldingRange[] = []; for (let span of spans) { let curr = convertRange(jsDocument, span.textSpan); @@ -360,8 +412,10 @@ export function getJavaScriptMode(documentRegions: LanguageModelCache { const jsDocument = jsDocuments.get(document); - const jsLanguageService = await host.getLanguageService(jsDocument); - return getSemanticTokens(jsLanguageService, jsDocument, jsDocument.uri); + const jsLanguageService = await host.getLanguageService(jsDocument, workspace); + const filePath = deschemeURI(jsDocument.uri); + + return getSemanticTokens(jsLanguageService, jsDocument, filePath); }, getSemanticTokenLegend(): { types: string[], modifiers: string[] } { return getSemanticTokenLegend(); 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 0371a2f74cd0..f0d18af2df70 100644 --- a/extensions/html-language-features/server/src/test/completions.test.ts +++ b/extensions/html-language-features/server/src/test/completions.test.ts @@ -94,11 +94,27 @@ suite('HTML Completion', () => { }); }); +const triggerSuggestCommand = { + title: 'Suggest', + command: 'editor.action.triggerSuggest' +}; + +suite('JSDoc Imports', () => { + const fixtureRoot = path.resolve(__dirname, '../../src/test/jsdocImportFixtures'); + const fixtureWorkspace = { name: 'fixture', uri: URI.file(fixtureRoot).toString() }; + const indexHtmlUri = URI.file(path.resolve(fixtureRoot, 'index.html')).toString(); + + test('Imports across files', async () => { + await testCompletionFor('', { + items: [ + { label: 'other', }, + { label: 'property', }, + ] + }, indexHtmlUri, [fixtureWorkspace] ); + }); +}); + suite('HTML Path Completion', () => { - const triggerSuggestCommand = { - title: 'Suggest', - command: 'editor.action.triggerSuggest' - }; const fixtureRoot = path.resolve(__dirname, '../../src/test/pathCompletionFixtures'); const fixtureWorkspace = { name: 'fixture', uri: URI.file(fixtureRoot).toString() }; diff --git a/extensions/html-language-features/server/src/test/jsdocImportFixtures/index.html b/extensions/html-language-features/server/src/test/jsdocImportFixtures/index.html new file mode 100644 index 000000000000..c5bd93d63657 --- /dev/null +++ b/extensions/html-language-features/server/src/test/jsdocImportFixtures/index.html @@ -0,0 +1 @@ +