From a5cc44ef57ee8ca5ee321e6fc441f569a22547e0 Mon Sep 17 00:00:00 2001 From: Pine Wu Date: Wed, 21 Mar 2018 16:49:59 -0700 Subject: [PATCH] Fix #44814 --- .../server/src/modes/pathCompletion.ts | 61 ++++++++++++------- .../server/src/test/completions.test.ts | 27 +++++++- 2 files changed, 65 insertions(+), 23 deletions(-) diff --git a/extensions/html-language-features/server/src/modes/pathCompletion.ts b/extensions/html-language-features/server/src/modes/pathCompletion.ts index c9e35f89d3b..5e2c2077b16 100644 --- a/extensions/html-language-features/server/src/modes/pathCompletion.ts +++ b/extensions/html-language-features/server/src/modes/pathCompletion.ts @@ -19,22 +19,32 @@ export function getPathCompletionParticipant( result: CompletionList ): ICompletionParticipant { return { - onHtmlAttributeValue: ({ tag, attribute, value, range }) => { + onHtmlAttributeValue: ({ tag, position, attribute, value: valueBeforeCursor, range }) => { + const fullValue = getFullValueWithoutQuotes(document, range); - if (shouldDoPathCompletion(tag, attribute, value)) { + if (shouldDoPathCompletion(tag, attribute, fullValue)) { if (!workspaceFolders || workspaceFolders.length === 0) { return; } const workspaceRoot = resolveWorkspaceRoot(document, workspaceFolders); - const paths = providePaths(value, URI.parse(document.uri).fsPath, workspaceRoot); - const suggestions = paths.map(p => pathToSuggestion(p, value, range)); + const paths = providePaths(valueBeforeCursor, URI.parse(document.uri).fsPath, workspaceRoot); + const suggestions = paths.map(p => pathToSuggestion(p, valueBeforeCursor, fullValue, range)); result.items = [...suggestions, ...result.items]; } } }; } +function getFullValueWithoutQuotes(document: TextDocument, range: Range) { + const fullValue = document.getText(range); + if (startsWith(fullValue, `'`) || startsWith(fullValue, `"`)) { + return fullValue.slice(1, -1); + } else { + return fullValue; + } +} + function shouldDoPathCompletion(tag: string, attr: string, value: string): boolean { if (startsWith(value, 'http') || startsWith(value, 'https') || startsWith(value, '//')) { return false; @@ -54,19 +64,19 @@ function shouldDoPathCompletion(tag: string, attr: string, value: string): boole /** * Get a list of path suggestions. Folder suggestions are suffixed with a slash. */ -function providePaths(value: string, activeDocFsPath: string, root?: string): string[] { - if (startsWith(value, '/') && !root) { +function providePaths(valueBeforeCursor: string, activeDocFsPath: string, root?: string): string[] { + if (startsWith(valueBeforeCursor, '/') && !root) { return []; } - const lastIndexOfSlash = value.lastIndexOf('/'); + const lastIndexOfSlash = valueBeforeCursor.lastIndexOf('/'); let parentDir: string; if (lastIndexOfSlash === -1) { parentDir = path.resolve(root); } else { - const valueBeforeLastSlash = value.slice(0, lastIndexOfSlash + 1); + const valueBeforeLastSlash = valueBeforeCursor.slice(0, lastIndexOfSlash + 1); - parentDir = startsWith(value, '/') + parentDir = startsWith(valueBeforeCursor, '/') ? path.resolve(root, '.' + valueBeforeLastSlash) : path.resolve(activeDocFsPath, '..', valueBeforeLastSlash); } @@ -82,16 +92,27 @@ function providePaths(value: string, activeDocFsPath: string, root?: string): st } } -function pathToSuggestion(p: string, value: string, range: Range): CompletionItem { +function pathToSuggestion(p: string, valueBeforeCursor: string, fullValue: string, range: Range): CompletionItem { const isDir = p[p.length - 1] === '/'; let replaceRange: Range; - const lastIndexOfSlash = value.lastIndexOf('/'); + const lastIndexOfSlash = valueBeforeCursor.lastIndexOf('/'); if (lastIndexOfSlash === -1) { - replaceRange = getFullReplaceRange(range); + replaceRange = shiftRange(range, 1, -1); } else { - const valueAfterLastSlash = value.slice(lastIndexOfSlash + 1); - replaceRange = getReplaceRange(range, valueAfterLastSlash); + // For cases where cursor is in the middle of attribute value, like