html headless

This commit is contained in:
Martin Aeschlimann
2020-06-19 23:35:45 +02:00
parent c23285f8c8
commit d16e306c2e
32 changed files with 1529 additions and 967 deletions

View File

@@ -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: [

View File

@@ -8,6 +8,7 @@ import * as assert from 'assert';
import { getFoldingRanges } from '../modes/htmlFolding';
import { TextDocument, getLanguageModes } from '../modes/languageModes';
import { ClientCapabilities } from 'vscode-css-languageservice';
import { getNodeFSRequestService } from '../node/nodeFs';
interface ExpectedIndentRange {
startLine: number;
@@ -21,7 +22,7 @@ function assertRanges(lines: string[], expected: ExpectedIndentRange[], message?
settings: {},
folders: [{ name: 'foo', uri: 'test://foo' }]
};
const languageModes = getLanguageModes({ css: true, javascript: true }, workspace, ClientCapabilities.LATEST);
const languageModes = getLanguageModes({ css: true, javascript: true }, workspace, ClientCapabilities.LATEST, getNodeFSRequestService());
const actual = getFoldingRanges(languageModes, document, nRanges, null);
let actualRanges = [];

View File

@@ -10,6 +10,7 @@ import * as assert from 'assert';
import { getLanguageModes, TextDocument, Range, FormattingOptions, ClientCapabilities } from '../modes/languageModes';
import { format } from '../modes/formatting';
import { getNodeFSRequestService } from '../node/nodeFs';
suite('HTML Embedded Formatting', () => {
@@ -18,7 +19,7 @@ suite('HTML Embedded Formatting', () => {
settings: options,
folders: [{ name: 'foo', uri: 'test://foo' }]
};
let languageModes = getLanguageModes({ css: true, javascript: true }, workspace, ClientCapabilities.LATEST);
const languageModes = getLanguageModes({ css: true, javascript: true }, workspace, ClientCapabilities.LATEST, getNodeFSRequestService());
let rangeStartOffset = value.indexOf('|');
let rangeEndOffset;

View File

@@ -7,6 +7,7 @@ import 'mocha';
import * as assert from 'assert';
import { getLanguageModes, ClientCapabilities, TextDocument, SelectionRange} from '../modes/languageModes';
import { getSelectionRanges } from '../modes/selectionRanges';
import { getNodeFSRequestService } from '../node/nodeFs';
function assertRanges(content: string, expected: (number | string)[][]): void {
let message = `${content} gives selection range:\n`;
@@ -18,7 +19,7 @@ function assertRanges(content: string, expected: (number | string)[][]): void {
settings: {},
folders: [{ name: 'foo', uri: 'test://foo' }]
};
let languageModes = getLanguageModes({ css: true, javascript: true }, workspace, ClientCapabilities.LATEST);
const languageModes = getLanguageModes({ css: true, javascript: true }, workspace, ClientCapabilities.LATEST, getNodeFSRequestService());
const document = TextDocument.create('test://foo.html', 'html', 1, content);
const actualRanges = getSelectionRanges(languageModes, document, [document.positionAt(offset)]);

View File

@@ -7,6 +7,7 @@ import 'mocha';
import * as assert from 'assert';
import { TextDocument, getLanguageModes, ClientCapabilities, Range, Position } from '../modes/languageModes';
import { newSemanticTokenProvider } from '../modes/semanticTokens';
import { getNodeFSRequestService } from '../node/nodeFs';
interface ExpectedToken {
startLine: number;
@@ -21,7 +22,7 @@ function assertTokens(lines: string[], expected: ExpectedToken[], ranges?: Range
settings: {},
folders: [{ name: 'foo', uri: 'test://foo' }]
};
const languageModes = getLanguageModes({ css: true, javascript: true }, workspace, ClientCapabilities.LATEST);
const languageModes = getLanguageModes({ css: true, javascript: true }, workspace, ClientCapabilities.LATEST, getNodeFSRequestService());
const semanticTokensProvider = newSemanticTokenProvider(languageModes);
const legend = semanticTokensProvider.legend;