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