|
|
|
|
@@ -6,7 +6,9 @@ import 'mocha';
|
|
|
|
|
import * as assert from 'assert';
|
|
|
|
|
import * as path from 'path';
|
|
|
|
|
import { URI } from 'vscode-uri';
|
|
|
|
|
import { getLanguageModes, WorkspaceFolder, TextDocument, CompletionList, CompletionItemKind, ClientCapabilities} from '../modes/languageModes';
|
|
|
|
|
import { getLanguageModes, WorkspaceFolder, TextDocument, CompletionList, CompletionItemKind, ClientCapabilities, TextEdit } from '../modes/languageModes';
|
|
|
|
|
import { getNodeFSRequestService } from '../node/nodeFs';
|
|
|
|
|
import { getDocumentContext } from '../utils/documentContext';
|
|
|
|
|
export interface ItemDescription {
|
|
|
|
|
label: string;
|
|
|
|
|
documentation?: string;
|
|
|
|
|
@@ -34,7 +36,8 @@ export function assertCompletion(completions: CompletionList, expected: ItemDesc
|
|
|
|
|
assert.equal(match.kind, expected.kind);
|
|
|
|
|
}
|
|
|
|
|
if (expected.resultText && match.textEdit) {
|
|
|
|
|
assert.equal(TextDocument.applyEdits(document, [match.textEdit]), expected.resultText);
|
|
|
|
|
const edit = TextEdit.is(match.textEdit) ? match.textEdit : TextEdit.replace(match.textEdit.replace, match.textEdit.newText);
|
|
|
|
|
assert.equal(TextDocument.applyEdits(document, [edit]), expected.resultText);
|
|
|
|
|
}
|
|
|
|
|
if (expected.command) {
|
|
|
|
|
assert.deepEqual(match.command, expected.command);
|
|
|
|
|
@@ -43,7 +46,7 @@ export function assertCompletion(completions: CompletionList, expected: ItemDesc
|
|
|
|
|
|
|
|
|
|
const testUri = 'test://test/test.html';
|
|
|
|
|
|
|
|
|
|
export function testCompletionFor(value: string, expected: { count?: number, items?: ItemDescription[] }, uri = testUri, workspaceFolders?: WorkspaceFolder[]): void {
|
|
|
|
|
export async function testCompletionFor(value: string, expected: { count?: number, items?: ItemDescription[] }, uri = testUri, workspaceFolders?: WorkspaceFolder[]): Promise<void> {
|
|
|
|
|
let offset = value.indexOf('|');
|
|
|
|
|
value = value.substr(0, offset) + value.substr(offset + 1);
|
|
|
|
|
|
|
|
|
|
@@ -54,11 +57,12 @@ export function testCompletionFor(value: string, expected: { count?: number, ite
|
|
|
|
|
|
|
|
|
|
let document = TextDocument.create(uri, 'html', 0, value);
|
|
|
|
|
let position = document.positionAt(offset);
|
|
|
|
|
const context = getDocumentContext(uri, workspace.folders)
|
|
|
|
|
|
|
|
|
|
const languageModes = getLanguageModes({ css: true, javascript: true }, workspace, ClientCapabilities.LATEST);
|
|
|
|
|
const languageModes = getLanguageModes({ css: true, javascript: true }, workspace, ClientCapabilities.LATEST, getNodeFSRequestService());
|
|
|
|
|
const mode = languageModes.getModeAtPosition(document, position)!;
|
|
|
|
|
|
|
|
|
|
let list = mode.doComplete!(document, position);
|
|
|
|
|
let list = await mode.doComplete!(document, position, context);
|
|
|
|
|
|
|
|
|
|
if (expected.count) {
|
|
|
|
|
assert.equal(list.items.length, expected.count);
|
|
|
|
|
@@ -71,13 +75,13 @@ export function testCompletionFor(value: string, expected: { count?: number, ite
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
suite('HTML Completion', () => {
|
|
|
|
|
test('HTML JavaScript Completions', function (): any {
|
|
|
|
|
testCompletionFor('<html><script>window.|</script></html>', {
|
|
|
|
|
test('HTML JavaScript Completions', async () => {
|
|
|
|
|
await testCompletionFor('<html><script>window.|</script></html>', {
|
|
|
|
|
items: [
|
|
|
|
|
{ label: 'location', resultText: '<html><script>window.location</script></html>' },
|
|
|
|
|
]
|
|
|
|
|
});
|
|
|
|
|
testCompletionFor('<html><script>$.|</script></html>', {
|
|
|
|
|
await testCompletionFor('<html><script>$.|</script></html>', {
|
|
|
|
|
items: [
|
|
|
|
|
{ label: 'getJSON', resultText: '<html><script>$.getJSON</script></html>' },
|
|
|
|
|
]
|
|
|
|
|
@@ -96,8 +100,8 @@ suite('HTML Path Completion', () => {
|
|
|
|
|
const indexHtmlUri = URI.file(path.resolve(fixtureRoot, 'index.html')).toString();
|
|
|
|
|
const aboutHtmlUri = URI.file(path.resolve(fixtureRoot, 'about/about.html')).toString();
|
|
|
|
|
|
|
|
|
|
test('Basics - Correct label/kind/result/command', () => {
|
|
|
|
|
testCompletionFor('<script src="./|">', {
|
|
|
|
|
test('Basics - Correct label/kind/result/command', async () => {
|
|
|
|
|
await testCompletionFor('<script src="./|">', {
|
|
|
|
|
items: [
|
|
|
|
|
{ label: 'about/', kind: CompletionItemKind.Folder, resultText: '<script src="./about/">', command: triggerSuggestCommand },
|
|
|
|
|
{ label: 'index.html', kind: CompletionItemKind.File, resultText: '<script src="./index.html">' },
|
|
|
|
|
@@ -106,8 +110,8 @@ suite('HTML Path Completion', () => {
|
|
|
|
|
}, indexHtmlUri);
|
|
|
|
|
});
|
|
|
|
|
|
|
|
|
|
test('Basics - Single Quote', () => {
|
|
|
|
|
testCompletionFor(`<script src='./|'>`, {
|
|
|
|
|
test('Basics - Single Quote', async () => {
|
|
|
|
|
await testCompletionFor(`<script src='./|'>`, {
|
|
|
|
|
items: [
|
|
|
|
|
{ label: 'about/', kind: CompletionItemKind.Folder, resultText: `<script src='./about/'>`, command: triggerSuggestCommand },
|
|
|
|
|
{ label: 'index.html', kind: CompletionItemKind.File, resultText: `<script src='./index.html'>` },
|
|
|
|
|
@@ -116,18 +120,18 @@ suite('HTML Path Completion', () => {
|
|
|
|
|
}, indexHtmlUri);
|
|
|
|
|
});
|
|
|
|
|
|
|
|
|
|
test('No completion for remote paths', () => {
|
|
|
|
|
testCompletionFor('<script src="http:">', { items: [] });
|
|
|
|
|
testCompletionFor('<script src="http:/|">', { items: [] });
|
|
|
|
|
testCompletionFor('<script src="http://|">', { items: [] });
|
|
|
|
|
testCompletionFor('<script src="https:|">', { items: [] });
|
|
|
|
|
testCompletionFor('<script src="https:/|">', { items: [] });
|
|
|
|
|
testCompletionFor('<script src="https://|">', { items: [] });
|
|
|
|
|
testCompletionFor('<script src="//|">', { items: [] });
|
|
|
|
|
test('No completion for remote paths', async () => {
|
|
|
|
|
await testCompletionFor('<script src="http:">', { items: [] });
|
|
|
|
|
await testCompletionFor('<script src="http:/|">', { items: [] });
|
|
|
|
|
await testCompletionFor('<script src="http://|">', { items: [] });
|
|
|
|
|
await testCompletionFor('<script src="https:|">', { items: [] });
|
|
|
|
|
await testCompletionFor('<script src="https:/|">', { items: [] });
|
|
|
|
|
await testCompletionFor('<script src="https://|">', { items: [] });
|
|
|
|
|
await testCompletionFor('<script src="//|">', { items: [] });
|
|
|
|
|
});
|
|
|
|
|
|
|
|
|
|
test('Relative Path', () => {
|
|
|
|
|
testCompletionFor('<script src="../|">', {
|
|
|
|
|
test('Relative Path', async () => {
|
|
|
|
|
await testCompletionFor('<script src="../|">', {
|
|
|
|
|
items: [
|
|
|
|
|
{ label: 'about/', resultText: '<script src="../about/">' },
|
|
|
|
|
{ label: 'index.html', resultText: '<script src="../index.html">' },
|
|
|
|
|
@@ -135,7 +139,7 @@ suite('HTML Path Completion', () => {
|
|
|
|
|
]
|
|
|
|
|
}, aboutHtmlUri);
|
|
|
|
|
|
|
|
|
|
testCompletionFor('<script src="../src/|">', {
|
|
|
|
|
await testCompletionFor('<script src="../src/|">', {
|
|
|
|
|
items: [
|
|
|
|
|
{ label: 'feature.js', resultText: '<script src="../src/feature.js">' },
|
|
|
|
|
{ label: 'test.js', resultText: '<script src="../src/test.js">' },
|
|
|
|
|
@@ -143,8 +147,8 @@ suite('HTML Path Completion', () => {
|
|
|
|
|
}, aboutHtmlUri);
|
|
|
|
|
});
|
|
|
|
|
|
|
|
|
|
test('Absolute Path', () => {
|
|
|
|
|
testCompletionFor('<script src="/|">', {
|
|
|
|
|
test('Absolute Path', async () => {
|
|
|
|
|
await testCompletionFor('<script src="/|">', {
|
|
|
|
|
items: [
|
|
|
|
|
{ label: 'about/', resultText: '<script src="/about/">' },
|
|
|
|
|
{ label: 'index.html', resultText: '<script src="/index.html">' },
|
|
|
|
|
@@ -152,7 +156,7 @@ suite('HTML Path Completion', () => {
|
|
|
|
|
]
|
|
|
|
|
}, indexHtmlUri);
|
|
|
|
|
|
|
|
|
|
testCompletionFor('<script src="/src/|">', {
|
|
|
|
|
await testCompletionFor('<script src="/src/|">', {
|
|
|
|
|
items: [
|
|
|
|
|
{ label: 'feature.js', resultText: '<script src="/src/feature.js">' },
|
|
|
|
|
{ label: 'test.js', resultText: '<script src="/src/test.js">' },
|
|
|
|
|
@@ -160,9 +164,9 @@ suite('HTML Path Completion', () => {
|
|
|
|
|
}, aboutHtmlUri, [fixtureWorkspace]);
|
|
|
|
|
});
|
|
|
|
|
|
|
|
|
|
test('Empty Path Value', () => {
|
|
|
|
|
test('Empty Path Value', async () => {
|
|
|
|
|
// document: index.html
|
|
|
|
|
testCompletionFor('<script src="|">', {
|
|
|
|
|
await testCompletionFor('<script src="|">', {
|
|
|
|
|
items: [
|
|
|
|
|
{ label: 'about/', resultText: '<script src="about/">' },
|
|
|
|
|
{ label: 'index.html', resultText: '<script src="index.html">' },
|
|
|
|
|
@@ -170,7 +174,7 @@ suite('HTML Path Completion', () => {
|
|
|
|
|
]
|
|
|
|
|
}, indexHtmlUri);
|
|
|
|
|
// document: about.html
|
|
|
|
|
testCompletionFor('<script src="|">', {
|
|
|
|
|
await testCompletionFor('<script src="|">', {
|
|
|
|
|
items: [
|
|
|
|
|
{ label: 'about.css', resultText: '<script src="about.css">' },
|
|
|
|
|
{ label: 'about.html', resultText: '<script src="about.html">' },
|
|
|
|
|
@@ -178,15 +182,15 @@ suite('HTML Path Completion', () => {
|
|
|
|
|
]
|
|
|
|
|
}, aboutHtmlUri);
|
|
|
|
|
});
|
|
|
|
|
test('Incomplete Path', () => {
|
|
|
|
|
testCompletionFor('<script src="/src/f|">', {
|
|
|
|
|
test('Incomplete Path', async () => {
|
|
|
|
|
await testCompletionFor('<script src="/src/f|">', {
|
|
|
|
|
items: [
|
|
|
|
|
{ label: 'feature.js', resultText: '<script src="/src/feature.js">' },
|
|
|
|
|
{ label: 'test.js', resultText: '<script src="/src/test.js">' },
|
|
|
|
|
]
|
|
|
|
|
}, aboutHtmlUri, [fixtureWorkspace]);
|
|
|
|
|
|
|
|
|
|
testCompletionFor('<script src="../src/f|">', {
|
|
|
|
|
await testCompletionFor('<script src="../src/f|">', {
|
|
|
|
|
items: [
|
|
|
|
|
{ label: 'feature.js', resultText: '<script src="../src/feature.js">' },
|
|
|
|
|
{ label: 'test.js', resultText: '<script src="../src/test.js">' },
|
|
|
|
|
@@ -194,9 +198,9 @@ suite('HTML Path Completion', () => {
|
|
|
|
|
}, aboutHtmlUri, [fixtureWorkspace]);
|
|
|
|
|
});
|
|
|
|
|
|
|
|
|
|
test('No leading dot or slash', () => {
|
|
|
|
|
test('No leading dot or slash', async () => {
|
|
|
|
|
// document: index.html
|
|
|
|
|
testCompletionFor('<script src="s|">', {
|
|
|
|
|
await testCompletionFor('<script src="s|">', {
|
|
|
|
|
items: [
|
|
|
|
|
{ label: 'about/', resultText: '<script src="about/">' },
|
|
|
|
|
{ label: 'index.html', resultText: '<script src="index.html">' },
|
|
|
|
|
@@ -204,14 +208,14 @@ suite('HTML Path Completion', () => {
|
|
|
|
|
]
|
|
|
|
|
}, indexHtmlUri, [fixtureWorkspace]);
|
|
|
|
|
|
|
|
|
|
testCompletionFor('<script src="src/|">', {
|
|
|
|
|
await testCompletionFor('<script src="src/|">', {
|
|
|
|
|
items: [
|
|
|
|
|
{ label: 'feature.js', resultText: '<script src="src/feature.js">' },
|
|
|
|
|
{ label: 'test.js', resultText: '<script src="src/test.js">' },
|
|
|
|
|
]
|
|
|
|
|
}, indexHtmlUri, [fixtureWorkspace]);
|
|
|
|
|
|
|
|
|
|
testCompletionFor('<script src="src/f|">', {
|
|
|
|
|
await testCompletionFor('<script src="src/f|">', {
|
|
|
|
|
items: [
|
|
|
|
|
{ label: 'feature.js', resultText: '<script src="src/feature.js">' },
|
|
|
|
|
{ label: 'test.js', resultText: '<script src="src/test.js">' },
|
|
|
|
|
@@ -219,7 +223,7 @@ suite('HTML Path Completion', () => {
|
|
|
|
|
}, indexHtmlUri, [fixtureWorkspace]);
|
|
|
|
|
|
|
|
|
|
// document: about.html
|
|
|
|
|
testCompletionFor('<script src="s|">', {
|
|
|
|
|
await testCompletionFor('<script src="s|">', {
|
|
|
|
|
items: [
|
|
|
|
|
{ label: 'about.css', resultText: '<script src="about.css">' },
|
|
|
|
|
{ label: 'about.html', resultText: '<script src="about.html">' },
|
|
|
|
|
@@ -227,29 +231,29 @@ suite('HTML Path Completion', () => {
|
|
|
|
|
]
|
|
|
|
|
}, aboutHtmlUri, [fixtureWorkspace]);
|
|
|
|
|
|
|
|
|
|
testCompletionFor('<script src="media/|">', {
|
|
|
|
|
await testCompletionFor('<script src="media/|">', {
|
|
|
|
|
items: [
|
|
|
|
|
{ label: 'icon.pic', resultText: '<script src="media/icon.pic">' }
|
|
|
|
|
]
|
|
|
|
|
}, aboutHtmlUri, [fixtureWorkspace]);
|
|
|
|
|
|
|
|
|
|
testCompletionFor('<script src="media/f|">', {
|
|
|
|
|
await testCompletionFor('<script src="media/f|">', {
|
|
|
|
|
items: [
|
|
|
|
|
{ label: 'icon.pic', resultText: '<script src="media/icon.pic">' }
|
|
|
|
|
]
|
|
|
|
|
}, aboutHtmlUri, [fixtureWorkspace]);
|
|
|
|
|
});
|
|
|
|
|
|
|
|
|
|
test('Trigger completion in middle of path', () => {
|
|
|
|
|
test('Trigger completion in middle of path', async () => {
|
|
|
|
|
// document: index.html
|
|
|
|
|
testCompletionFor('<script src="src/f|eature.js">', {
|
|
|
|
|
await testCompletionFor('<script src="src/f|eature.js">', {
|
|
|
|
|
items: [
|
|
|
|
|
{ label: 'feature.js', resultText: '<script src="src/feature.js">' },
|
|
|
|
|
{ label: 'test.js', resultText: '<script src="src/test.js">' },
|
|
|
|
|
]
|
|
|
|
|
}, indexHtmlUri, [fixtureWorkspace]);
|
|
|
|
|
|
|
|
|
|
testCompletionFor('<script src="s|rc/feature.js">', {
|
|
|
|
|
await testCompletionFor('<script src="s|rc/feature.js">', {
|
|
|
|
|
items: [
|
|
|
|
|
{ label: 'about/', resultText: '<script src="about/">' },
|
|
|
|
|
{ label: 'index.html', resultText: '<script src="index.html">' },
|
|
|
|
|
@@ -258,13 +262,13 @@ suite('HTML Path Completion', () => {
|
|
|
|
|
}, indexHtmlUri, [fixtureWorkspace]);
|
|
|
|
|
|
|
|
|
|
// document: about.html
|
|
|
|
|
testCompletionFor('<script src="media/f|eature.js">', {
|
|
|
|
|
await testCompletionFor('<script src="media/f|eature.js">', {
|
|
|
|
|
items: [
|
|
|
|
|
{ label: 'icon.pic', resultText: '<script src="media/icon.pic">' }
|
|
|
|
|
]
|
|
|
|
|
}, aboutHtmlUri, [fixtureWorkspace]);
|
|
|
|
|
|
|
|
|
|
testCompletionFor('<script src="m|edia/feature.js">', {
|
|
|
|
|
await testCompletionFor('<script src="m|edia/feature.js">', {
|
|
|
|
|
items: [
|
|
|
|
|
{ label: 'about.css', resultText: '<script src="about.css">' },
|
|
|
|
|
{ label: 'about.html', resultText: '<script src="about.html">' },
|
|
|
|
|
@@ -274,8 +278,8 @@ suite('HTML Path Completion', () => {
|
|
|
|
|
});
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
test('Trigger completion in middle of path and with whitespaces', () => {
|
|
|
|
|
testCompletionFor('<script src="./| about/about.html>', {
|
|
|
|
|
test('Trigger completion in middle of path and with whitespaces', async () => {
|
|
|
|
|
await testCompletionFor('<script src="./| about/about.html>', {
|
|
|
|
|
items: [
|
|
|
|
|
{ label: 'about/', resultText: '<script src="./about/ about/about.html>' },
|
|
|
|
|
{ label: 'index.html', resultText: '<script src="./index.html about/about.html>' },
|
|
|
|
|
@@ -283,7 +287,7 @@ suite('HTML Path Completion', () => {
|
|
|
|
|
]
|
|
|
|
|
}, indexHtmlUri, [fixtureWorkspace]);
|
|
|
|
|
|
|
|
|
|
testCompletionFor('<script src="./a|bout /about.html>', {
|
|
|
|
|
await testCompletionFor('<script src="./a|bout /about.html>', {
|
|
|
|
|
items: [
|
|
|
|
|
{ label: 'about/', resultText: '<script src="./about/ /about.html>' },
|
|
|
|
|
{ label: 'index.html', resultText: '<script src="./index.html /about.html>' },
|
|
|
|
|
@@ -292,13 +296,13 @@ suite('HTML Path Completion', () => {
|
|
|
|
|
}, indexHtmlUri, [fixtureWorkspace]);
|
|
|
|
|
});
|
|
|
|
|
|
|
|
|
|
test('Completion should ignore files/folders starting with dot', () => {
|
|
|
|
|
testCompletionFor('<script src="./|"', {
|
|
|
|
|
test('Completion should ignore files/folders starting with dot', async () => {
|
|
|
|
|
await testCompletionFor('<script src="./|"', {
|
|
|
|
|
count: 3
|
|
|
|
|
}, indexHtmlUri, [fixtureWorkspace]);
|
|
|
|
|
});
|
|
|
|
|
|
|
|
|
|
test('Unquoted Path', () => {
|
|
|
|
|
test('Unquoted Path', async () => {
|
|
|
|
|
/* Unquoted value is not supported in html language service yet
|
|
|
|
|
testCompletionFor(`<div><a href=about/|>`, {
|
|
|
|
|
items: [
|
|
|
|
|
|