diff --git a/extensions/html/server/src/modes/pathCompletion.ts b/extensions/html/server/src/modes/pathCompletion.ts
index 9e1c659cad2..27c347013e7 100644
--- a/extensions/html/server/src/modes/pathCompletion.ts
+++ b/extensions/html/server/src/modes/pathCompletion.ts
@@ -4,7 +4,7 @@
*--------------------------------------------------------------------------------------------*/
'use strict';
-import { TextDocument, CompletionList, CompletionItemKind, CompletionItem } from 'vscode-languageserver-types';
+import { TextDocument, CompletionList, CompletionItemKind, CompletionItem, TextEdit, Range, Position } from 'vscode-languageserver-types';
import { WorkspaceFolder } from 'vscode-languageserver-protocol/lib/protocol.workspaceFolders.proposed';
import * as path from 'path';
import * as fs from 'fs';
@@ -32,7 +32,7 @@ export function getPathCompletionParticipant(
workspaceRoot = resolveWorkspaceRoot(document, workspaceFolders);
}
- const suggestions = providePathSuggestions(value, URI.parse(document.uri).fsPath, workspaceRoot);
+ const suggestions = providePathSuggestions(value, range, URI.parse(document.uri).fsPath, workspaceRoot);
result.items = [...suggestions, ...result.items];
}
}
@@ -55,7 +55,7 @@ function shouldDoPathCompletion(tag: string, attr: string, value: string): boole
return false;
}
-export function providePathSuggestions(value: string, activeDocFsPath: string, root?: string): CompletionItem[] {
+export function providePathSuggestions(value: string, range: Range, activeDocFsPath: string, root?: string): CompletionItem[] {
if (value.indexOf('/') === -1) {
return [];
}
@@ -65,8 +65,8 @@ export function providePathSuggestions(value: string, activeDocFsPath: string, r
}
const lastIndexOfSlash = value.lastIndexOf('/');
- const valueAfterLastSlash = value.slice(lastIndexOfSlash + 1);
const valueBeforeLastSlash = value.slice(0, lastIndexOfSlash + 1);
+ const valueAfterLastSlash = value.slice(lastIndexOfSlash + 1);
const parentDir = startsWith(value, '/')
? path.resolve(root, '.' + valueBeforeLastSlash)
: path.resolve(activeDocFsPath, '..', valueBeforeLastSlash);
@@ -75,11 +75,13 @@ export function providePathSuggestions(value: string, activeDocFsPath: string, r
return [];
}
+ const replaceRange = Range.create(Position.create(range.end.line, range.end.character - valueAfterLastSlash.length), range.end);
+
return fs.readdirSync(parentDir).map(f => {
return {
label: f,
kind: isDir(path.resolve(parentDir, f)) ? CompletionItemKind.Folder : CompletionItemKind.File,
- insertText: f.slice(valueAfterLastSlash.length)
+ textEdit: TextEdit.replace(replaceRange, f)
};
});
}
diff --git a/extensions/html/server/src/test/pathCompletion/pathCompletion.test.ts b/extensions/html/server/src/test/pathCompletion/pathCompletion.test.ts
index ef45f6e4d1b..908ad285442 100644
--- a/extensions/html/server/src/test/pathCompletion/pathCompletion.test.ts
+++ b/extensions/html/server/src/test/pathCompletion/pathCompletion.test.ts
@@ -7,16 +7,17 @@
import * as assert from 'assert';
import * as path from 'path';
import { providePathSuggestions } from '../../modes/pathCompletion';
-import { CompletionItemKind } from 'vscode-languageserver/lib/main';
+import { CompletionItemKind, Range, Position } from 'vscode-languageserver-types';
const fixtureRoot = path.resolve(__dirname, '../../../test/pathCompletionFixtures');
suite('Path Completion - Relative Path', () => {
+ const mockRange = Range.create(Position.create(0, 3), Position.create(0, 5));
test('Current Folder', () => {
const value = './';
const activeFileFsPath = path.resolve(fixtureRoot, 'index.html');
- const suggestions = providePathSuggestions(value, activeFileFsPath);
+ const suggestions = providePathSuggestions(value, mockRange, activeFileFsPath);
assert.equal(suggestions.length, 3);
assert.equal(suggestions[0].label, 'about');
@@ -31,7 +32,7 @@ suite('Path Completion - Relative Path', () => {
test('Parent Folder', () => {
const value = '../';
const activeFileFsPath = path.resolve(fixtureRoot, 'about/about.html');
- const suggestions = providePathSuggestions(value, activeFileFsPath);
+ const suggestions = providePathSuggestions(value, mockRange, activeFileFsPath);
assert.equal(suggestions.length, 3);
assert.equal(suggestions[0].label, 'about');
@@ -46,7 +47,7 @@ suite('Path Completion - Relative Path', () => {
test('Adjacent Folder', () => {
const value = '../src/';
const activeFileFsPath = path.resolve(fixtureRoot, 'about/about.html');
- const suggestions = providePathSuggestions(value, activeFileFsPath);
+ const suggestions = providePathSuggestions(value, mockRange, activeFileFsPath);
assert.equal(suggestions.length, 2);
assert.equal(suggestions[0].label, 'feature.js');
@@ -60,13 +61,15 @@ suite('Path Completion - Relative Path', () => {
});
suite('Path Completion - Absolute Path', () => {
+ const mockRange = Range.create(Position.create(0, 3), Position.create(0, 5));
+
test('Root', () => {
const value = '/';
const activeFileFsPath1 = path.resolve(fixtureRoot, 'index.html');
const activeFileFsPath2 = path.resolve(fixtureRoot, 'about/index.html');
- const suggestions1 = providePathSuggestions(value, activeFileFsPath1, fixtureRoot);
- const suggestions2 = providePathSuggestions(value, activeFileFsPath2, fixtureRoot);
+ const suggestions1 = providePathSuggestions(value, mockRange, activeFileFsPath1, fixtureRoot);
+ const suggestions2 = providePathSuggestions(value, mockRange, activeFileFsPath2, fixtureRoot);
const verify = (suggestions) => {
assert.equal(suggestions[0].label, 'about');
@@ -85,7 +88,7 @@ suite('Path Completion - Absolute Path', () => {
test('Sub Folder', () => {
const value = '/src/';
const activeFileFsPath = path.resolve(fixtureRoot, 'about/about.html');
- const suggestions = providePathSuggestions(value, activeFileFsPath, fixtureRoot);
+ const suggestions = providePathSuggestions(value, mockRange, activeFileFsPath, fixtureRoot);
assert.equal(suggestions.length, 2);
assert.equal(suggestions[0].label, 'feature.js');
@@ -97,10 +100,12 @@ suite('Path Completion - Absolute Path', () => {
});
suite('Path Completion - Incomplete Path at End', () => {
+ const mockRange = Range.create(Position.create(0, 3), Position.create(0, 5));
+
test('Incomplete Path that starts with slash', () => {
const value = '/src/f';
const activeFileFsPath = path.resolve(fixtureRoot, 'about/about.html');
- const suggestions = providePathSuggestions(value, activeFileFsPath, fixtureRoot);
+ const suggestions = providePathSuggestions(value, mockRange, activeFileFsPath, fixtureRoot);
assert.equal(suggestions.length, 2);
assert.equal(suggestions[0].label, 'feature.js');
@@ -113,7 +118,7 @@ suite('Path Completion - Incomplete Path at End', () => {
test('Incomplete Path that does not start with slash', () => {
const value = '../src/f';
const activeFileFsPath = path.resolve(fixtureRoot, 'about/about.html');
- const suggestions = providePathSuggestions(value, activeFileFsPath, fixtureRoot);
+ const suggestions = providePathSuggestions(value, mockRange, activeFileFsPath, fixtureRoot);
assert.equal(suggestions.length, 2);
assert.equal(suggestions[0].label, 'feature.js');
@@ -122,4 +127,22 @@ suite('Path Completion - Incomplete Path at End', () => {
assert.equal(suggestions[0].kind, CompletionItemKind.File);
assert.equal(suggestions[1].kind, CompletionItemKind.File);
});
-});
\ No newline at end of file
+});
+
+suite('Path Completion - TextEdit', () => {
+ test('TextEdit has correct replace text and range', () => {
+ const value = './';
+ const activeFileFsPath = path.resolve(fixtureRoot, 'index.html');
+ const range = Range.create(Position.create(0, 3), Position.create(0, 5));
+ const suggestions = providePathSuggestions(value, range, activeFileFsPath);
+
+ assert.equal(suggestions[0].textEdit.newText, 'about');
+ assert.equal(suggestions[1].textEdit.newText, 'index.html');
+ assert.equal(suggestions[2].textEdit.newText, 'src');
+
+ assert.equal(suggestions[0].textEdit.range.start.character, 5);
+ assert.equal(suggestions[1].textEdit.range.start.character, 5);
+ assert.equal(suggestions[2].textEdit.range.start.character, 5);
+ });
+});
+