mirror of
https://github.com/microsoft/vscode.git
synced 2026-04-18 15:55:59 +01:00
238 lines
8.6 KiB
TypeScript
238 lines
8.6 KiB
TypeScript
/*---------------------------------------------------------------------------------------------
|
|
* Copyright (c) Microsoft Corporation. All rights reserved.
|
|
* Licensed under the MIT License. See License.txt in the project root for license information.
|
|
*--------------------------------------------------------------------------------------------*/
|
|
'use strict';
|
|
|
|
import * as assert from 'assert';
|
|
import * as path from 'path';
|
|
import { providePathSuggestions } from '../../modes/pathCompletion';
|
|
import { CompletionItemKind, Range, Position, CompletionItem, TextEdit, Command } from 'vscode-languageserver-types';
|
|
|
|
const fixtureRoot = path.resolve(__dirname, '../../../test/pathCompletionFixtures');
|
|
|
|
function toRange(line: number, startChar: number, endChar: number) {
|
|
return Range.create(Position.create(line, startChar), Position.create(line, endChar));
|
|
}
|
|
function toTextEdit(line: number, startChar: number, endChar: number, newText: string) {
|
|
const range = Range.create(Position.create(line, startChar), Position.create(line, endChar));
|
|
return TextEdit.replace(range, newText);
|
|
}
|
|
|
|
interface PathSuggestion {
|
|
label?: string;
|
|
kind?: CompletionItemKind;
|
|
textEdit?: TextEdit;
|
|
command?: Command;
|
|
}
|
|
|
|
function assertSuggestions(actual: CompletionItem[], expected: PathSuggestion[]) {
|
|
assert.equal(actual.length, expected.length, `Suggestions have length ${actual.length} but should have length ${expected.length}`);
|
|
|
|
for (let i = 0; i < expected.length; i++) {
|
|
if (expected[i].label) {
|
|
assert.equal(
|
|
actual[i].label,
|
|
expected[i].label,
|
|
`Suggestion ${actual[i].label} should have label ${expected[i].label}`
|
|
);
|
|
}
|
|
if (expected[i].kind) {
|
|
assert.equal(actual[i].kind,
|
|
expected[i].kind,
|
|
`Suggestion ${actual[i].label} has kind ${actual[i].kind} but should have ${expected[i].kind}`
|
|
);
|
|
}
|
|
if (expected[i].textEdit) {
|
|
assert.equal(actual[i].textEdit!.newText, expected[i].textEdit!.newText);
|
|
assert.deepEqual(actual[i].textEdit!.range, expected[i].textEdit!.range);
|
|
}
|
|
if (expected[i].command) {
|
|
assert.equal(
|
|
actual[i].command!.title,
|
|
expected[i].command!.title,
|
|
`Suggestion ${actual[i].label} has command title ${actual[i].command!.title} but should have command title ${expected[i].command!.title}`
|
|
);
|
|
assert.equal(
|
|
actual[i].command!.command,
|
|
expected[i].command!.command,
|
|
`Suggestion ${actual[i].label} has command ${actual[i].command!.command} but should have command ${expected[i].command!.command}`
|
|
);
|
|
}
|
|
}
|
|
}
|
|
|
|
suite('Path Completion - Relative Path:', () => {
|
|
const mockRange = toRange(0, 3, 5);
|
|
|
|
test('Current Folder', () => {
|
|
const value = './';
|
|
const activeFileFsPath = path.resolve(fixtureRoot, 'index.html');
|
|
const suggestions = providePathSuggestions(value, mockRange, activeFileFsPath);
|
|
|
|
assertSuggestions(suggestions, [
|
|
{ label: 'about/', kind: CompletionItemKind.Folder },
|
|
{ label: 'index.html', kind: CompletionItemKind.File },
|
|
{ label: 'src/', kind: CompletionItemKind.Folder }
|
|
]);
|
|
});
|
|
|
|
test('Parent Folder:', () => {
|
|
const value = '../';
|
|
const activeFileFsPath = path.resolve(fixtureRoot, 'about/about.html');
|
|
const suggestions = providePathSuggestions(value, mockRange, activeFileFsPath);
|
|
|
|
assertSuggestions(suggestions, [
|
|
{ label: 'about/', kind: CompletionItemKind.Folder },
|
|
{ label: 'index.html', kind: CompletionItemKind.File },
|
|
{ label: 'src/', kind: CompletionItemKind.Folder }
|
|
]);
|
|
});
|
|
|
|
test('Adjacent Folder:', () => {
|
|
const value = '../src/';
|
|
const activeFileFsPath = path.resolve(fixtureRoot, 'about/about.html');
|
|
const suggestions = providePathSuggestions(value, mockRange, activeFileFsPath);
|
|
|
|
assertSuggestions(suggestions, [
|
|
{ label: 'feature.js', kind: CompletionItemKind.File },
|
|
{ label: 'test.js', kind: CompletionItemKind.File }
|
|
]);
|
|
});
|
|
});
|
|
|
|
suite('Path Completion - Absolute Path:', () => {
|
|
const mockRange = toRange(0, 3, 5);
|
|
|
|
test('Root', () => {
|
|
const value = '/';
|
|
const activeFileFsPath1 = path.resolve(fixtureRoot, 'index.html');
|
|
const activeFileFsPath2 = path.resolve(fixtureRoot, 'about/index.html');
|
|
|
|
const suggestions1 = providePathSuggestions(value, mockRange, activeFileFsPath1, fixtureRoot);
|
|
const suggestions2 = providePathSuggestions(value, mockRange, activeFileFsPath2, fixtureRoot);
|
|
|
|
const verify = (suggestions: CompletionItem[]) => {
|
|
assertSuggestions(suggestions, [
|
|
{ label: 'about/', kind: CompletionItemKind.Folder },
|
|
{ label: 'index.html', kind: CompletionItemKind.File },
|
|
{ label: 'src/', kind: CompletionItemKind.Folder }
|
|
]);
|
|
};
|
|
|
|
verify(suggestions1);
|
|
verify(suggestions2);
|
|
});
|
|
|
|
test('Sub Folder', () => {
|
|
const value = '/src/';
|
|
const activeFileFsPath = path.resolve(fixtureRoot, 'about/about.html');
|
|
const suggestions = providePathSuggestions(value, mockRange, activeFileFsPath, fixtureRoot);
|
|
|
|
assertSuggestions(suggestions, [
|
|
{ label: 'feature.js', kind: CompletionItemKind.File },
|
|
{ label: 'test.js', kind: CompletionItemKind.File }
|
|
]);
|
|
});
|
|
});
|
|
|
|
suite('Path Completion - Folder Commands:', () => {
|
|
const mockRange = toRange(0, 3, 5);
|
|
|
|
test('Folder should have command `editor.action.triggerSuggest', () => {
|
|
const value = './';
|
|
const activeFileFsPath = path.resolve(fixtureRoot, 'index.html');
|
|
const suggestions = providePathSuggestions(value, mockRange, activeFileFsPath);
|
|
|
|
assertSuggestions(suggestions, [
|
|
{ label: 'about/', command: { title: 'Suggest', command: 'editor.action.triggerSuggest' } },
|
|
{ label: 'index.html' },
|
|
{ label: 'src/', command: { title: 'Suggest', command: 'editor.action.triggerSuggest' } },
|
|
]);
|
|
});
|
|
});
|
|
|
|
suite('Path Completion - Incomplete Path at End:', () => {
|
|
const mockRange = toRange(0, 3, 5);
|
|
|
|
test('Incomplete Path that starts with slash', () => {
|
|
const value = '/src/f';
|
|
const activeFileFsPath = path.resolve(fixtureRoot, 'about/about.html');
|
|
const suggestions = providePathSuggestions(value, mockRange, activeFileFsPath, fixtureRoot);
|
|
|
|
assertSuggestions(suggestions, [
|
|
{ label: 'feature.js', kind: CompletionItemKind.File },
|
|
{ label: 'test.js', kind: CompletionItemKind.File }
|
|
]);
|
|
});
|
|
|
|
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, mockRange, activeFileFsPath, fixtureRoot);
|
|
|
|
assertSuggestions(suggestions, [
|
|
{ label: 'feature.js', kind: CompletionItemKind.File },
|
|
{ label: 'test.js', kind: CompletionItemKind.File }
|
|
]);
|
|
});
|
|
});
|
|
|
|
suite('Path Completion - No leading dot or slash:', () => {
|
|
|
|
test('Top level completion', () => {
|
|
const value = 's';
|
|
const activeFileFsPath = path.resolve(fixtureRoot, 'index.html');
|
|
const range = toRange(0, 3, 5);
|
|
const suggestions = providePathSuggestions(value, range, activeFileFsPath, fixtureRoot);
|
|
|
|
assertSuggestions(suggestions, [
|
|
{ label: 'about/', kind: CompletionItemKind.Folder, textEdit: toTextEdit(0, 4, 4, 'about/') },
|
|
{ label: 'index.html', kind: CompletionItemKind.File, textEdit: toTextEdit(0, 4, 4, 'index.html') },
|
|
{ label: 'src/', kind: CompletionItemKind.Folder, textEdit: toTextEdit(0, 4, 4, 'src/') }
|
|
]);
|
|
});
|
|
|
|
test('src/', () => {
|
|
const value = 'src/';
|
|
const activeFileFsPath = path.resolve(fixtureRoot, 'index.html');
|
|
const range = toRange(0, 3, 8);
|
|
const suggestions = providePathSuggestions(value, range, activeFileFsPath, fixtureRoot);
|
|
|
|
assertSuggestions(suggestions, [
|
|
{ label: 'feature.js', kind: CompletionItemKind.File, textEdit: toTextEdit(0, 7, 7, 'feature.js') },
|
|
{ label: 'test.js', kind: CompletionItemKind.File, textEdit: toTextEdit(0, 7, 7, 'test.js') }
|
|
]);
|
|
});
|
|
|
|
test('src/f', () => {
|
|
const value = 'src/f';
|
|
const activeFileFsPath = path.resolve(fixtureRoot, 'index.html');
|
|
const range = toRange(0, 3, 9);
|
|
const suggestions = providePathSuggestions(value, range, activeFileFsPath, fixtureRoot);
|
|
|
|
assertSuggestions(suggestions, [
|
|
{ label: 'feature.js', kind: CompletionItemKind.File, textEdit: toTextEdit(0, 7, 8, 'feature.js') },
|
|
{ label: 'test.js', kind: CompletionItemKind.File, textEdit: toTextEdit(0, 7, 8, 'test.js') }
|
|
]);
|
|
});
|
|
});
|
|
|
|
suite('Path Completion - TextEdit:', () => {
|
|
|
|
test('TextEdit has correct replace text and range', () => {
|
|
const value = './';
|
|
const activeFileFsPath = path.resolve(fixtureRoot, 'index.html');
|
|
const range = toRange(0, 3, 5);
|
|
const expectedReplaceRange = toRange(0, 4, 4);
|
|
|
|
const suggestions = providePathSuggestions(value, range, activeFileFsPath);
|
|
|
|
assertSuggestions(suggestions, [
|
|
{ textEdit: TextEdit.replace(expectedReplaceRange, 'about/') },
|
|
{ textEdit: TextEdit.replace(expectedReplaceRange, 'index.html') },
|
|
{ textEdit: TextEdit.replace(expectedReplaceRange, 'src/') },
|
|
]);
|
|
});
|
|
});
|