mirror of
https://github.com/microsoft/vscode.git
synced 2026-04-20 08:38:56 +01:00
[html] split extension (for #45900)
This commit is contained in:
@@ -0,0 +1,134 @@
|
||||
/*---------------------------------------------------------------------------------------------
|
||||
* 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 'mocha';
|
||||
import * as assert from 'assert';
|
||||
import * as path from 'path';
|
||||
import Uri from 'vscode-uri';
|
||||
import { TextDocument, CompletionList, CompletionItemKind, } from 'vscode-languageserver-types';
|
||||
import { getLanguageModes } from '../modes/languageModes';
|
||||
import { getPathCompletionParticipant } from '../modes/pathCompletion';
|
||||
import { WorkspaceFolder } from 'vscode-languageserver';
|
||||
|
||||
export interface ItemDescription {
|
||||
label: string;
|
||||
documentation?: string;
|
||||
kind?: CompletionItemKind;
|
||||
resultText?: string;
|
||||
notAvailable?: boolean;
|
||||
}
|
||||
|
||||
|
||||
suite('Completions', () => {
|
||||
|
||||
let assertCompletion = function (completions: CompletionList, expected: ItemDescription, document: TextDocument, offset: number) {
|
||||
let matches = completions.items.filter(completion => {
|
||||
return completion.label === expected.label;
|
||||
});
|
||||
if (expected.notAvailable) {
|
||||
assert.equal(matches.length, 0, `${expected.label} should not existing is results`);
|
||||
return;
|
||||
}
|
||||
|
||||
assert.equal(matches.length, 1, `${expected.label} should only existing once: Actual: ${completions.items.map(c => c.label).join(', ')}`);
|
||||
let match = matches[0];
|
||||
if (expected.documentation) {
|
||||
assert.equal(match.documentation, expected.documentation);
|
||||
}
|
||||
if (expected.kind) {
|
||||
assert.equal(match.kind, expected.kind);
|
||||
}
|
||||
if (expected.resultText && match.textEdit) {
|
||||
assert.equal(TextDocument.applyEdits(document, [match.textEdit]), expected.resultText);
|
||||
}
|
||||
};
|
||||
|
||||
const testUri = 'test://test/test.html';
|
||||
|
||||
function assertCompletions(value: string, expected: { count?: number, items?: ItemDescription[] }, uri = testUri, workspaceFolders?: WorkspaceFolder[]): void {
|
||||
let offset = value.indexOf('|');
|
||||
value = value.substr(0, offset) + value.substr(offset + 1);
|
||||
|
||||
let document = TextDocument.create(uri, 'html', 0, value);
|
||||
let position = document.positionAt(offset);
|
||||
|
||||
var languageModes = getLanguageModes({ css: true, javascript: true });
|
||||
var mode = languageModes.getModeAtPosition(document, position)!;
|
||||
|
||||
if (!workspaceFolders) {
|
||||
workspaceFolders = [{ name: 'x', uri: path.dirname(uri) }];
|
||||
}
|
||||
|
||||
let participantResult = CompletionList.create([]);
|
||||
if (mode.setCompletionParticipants) {
|
||||
mode.setCompletionParticipants([getPathCompletionParticipant(document, workspaceFolders, participantResult)]);
|
||||
}
|
||||
|
||||
let list = mode.doComplete!(document, position)!;
|
||||
list.items = list.items.concat(participantResult.items);
|
||||
|
||||
if (expected.count) {
|
||||
assert.equal(list.items, expected.count);
|
||||
}
|
||||
if (expected.items) {
|
||||
for (let item of expected.items) {
|
||||
assertCompletion(list, item, document, offset);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
test('HTML Javascript Completions', function (): any {
|
||||
assertCompletions('<html><script>window.|</script></html>', {
|
||||
items: [
|
||||
{ label: 'location', resultText: '<html><script>window.location</script></html>' },
|
||||
]
|
||||
});
|
||||
assertCompletions('<html><script>$.|</script></html>', {
|
||||
items: [
|
||||
{ label: 'getJSON', resultText: '<html><script>$.getJSON</script></html>' },
|
||||
]
|
||||
});
|
||||
});
|
||||
|
||||
test('Path completion', function (): any {
|
||||
let testUri = Uri.file(path.resolve(__dirname, '../../test/pathCompletionFixtures/foo.html')).fsPath;
|
||||
|
||||
assertCompletions('<div><a href="about/|">', {
|
||||
items: [
|
||||
{ label: 'about.html', resultText: '<div><a href="about/about.html">' }
|
||||
]
|
||||
}, testUri);
|
||||
// Unquoted value is not supported in language service yet
|
||||
// assertCompletions(`<div><a href=about/|>`, {
|
||||
// items: [
|
||||
// { label: 'about.html', resultText: `<div><a href=about/about.html>` }
|
||||
// ]
|
||||
// }, testUri);
|
||||
assertCompletions(`<div><a href='about/|'>`, {
|
||||
items: [
|
||||
{ label: 'about.html', resultText: `<div><a href='about/about.html'>` }
|
||||
]
|
||||
}, testUri);
|
||||
// Don't think this is a common use case
|
||||
// assertCompletions('<div><a href="about/about|.xml">', {
|
||||
// items: [
|
||||
// { label: 'about.html', resultText: '<div><a href="about/about.html">' }
|
||||
// ]
|
||||
// }, testUri);
|
||||
assertCompletions('<div><a href="about/a|">', {
|
||||
items: [
|
||||
{ label: 'about.html', resultText: '<div><a href="about/about.html">' }
|
||||
]
|
||||
}, testUri);
|
||||
// We should not prompt suggestion before user enters any trigger character
|
||||
// assertCompletions('<div><a href="|">', {
|
||||
// items: [
|
||||
// { label: 'index.html', resultText: '<div><a href="index.html">' },
|
||||
// { label: 'about', resultText: '<div><a href="about/">' }
|
||||
// ]
|
||||
// }, testUri);
|
||||
});
|
||||
});
|
||||
@@ -0,0 +1,22 @@
|
||||
/*---------------------------------------------------------------------------------------------
|
||||
* 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 { getDocumentContext } from '../utils/documentContext';
|
||||
|
||||
suite('Document Context', () => {
|
||||
|
||||
test('Context', function (): any {
|
||||
const docURI = 'file:///users/test/folder/test.html';
|
||||
const rootFolders = [{ name: '', uri: 'file:///users/test/' }];
|
||||
|
||||
let context = getDocumentContext(docURI, rootFolders);
|
||||
assert.equal(context.resolveReference('/', docURI), 'file:///users/test/');
|
||||
assert.equal(context.resolveReference('/message.html', docURI), 'file:///users/test/message.html');
|
||||
assert.equal(context.resolveReference('message.html', docURI), 'file:///users/test/folder/message.html');
|
||||
assert.equal(context.resolveReference('message.html', 'file:///users/test/'), 'file:///users/test/message.html');
|
||||
});
|
||||
});
|
||||
@@ -0,0 +1,128 @@
|
||||
/*---------------------------------------------------------------------------------------------
|
||||
* 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 'mocha';
|
||||
import * as assert from 'assert';
|
||||
import * as embeddedSupport from '../modes/embeddedSupport';
|
||||
import { TextDocument } from 'vscode-languageserver-types';
|
||||
import { getLanguageService } from 'vscode-html-languageservice';
|
||||
|
||||
suite('HTML Embedded Support', () => {
|
||||
|
||||
var htmlLanguageService = getLanguageService();
|
||||
|
||||
function assertLanguageId(value: string, expectedLanguageId: string | undefined): void {
|
||||
let offset = value.indexOf('|');
|
||||
value = value.substr(0, offset) + value.substr(offset + 1);
|
||||
|
||||
let document = TextDocument.create('test://test/test.html', 'html', 0, value);
|
||||
|
||||
let position = document.positionAt(offset);
|
||||
|
||||
let docRegions = embeddedSupport.getDocumentRegions(htmlLanguageService, document);
|
||||
let languageId = docRegions.getLanguageAtPosition(position);
|
||||
|
||||
assert.equal(languageId, expectedLanguageId);
|
||||
}
|
||||
|
||||
function assertEmbeddedLanguageContent(value: string, languageId: string, expectedContent: string): void {
|
||||
let document = TextDocument.create('test://test/test.html', 'html', 0, value);
|
||||
|
||||
let docRegions = embeddedSupport.getDocumentRegions(htmlLanguageService, document);
|
||||
let content = docRegions.getEmbeddedDocument(languageId);
|
||||
assert.equal(content.getText(), expectedContent);
|
||||
}
|
||||
|
||||
test('Styles', function (): any {
|
||||
assertLanguageId('|<html><style>foo { }</style></html>', 'html');
|
||||
assertLanguageId('<html|><style>foo { }</style></html>', 'html');
|
||||
assertLanguageId('<html><st|yle>foo { }</style></html>', 'html');
|
||||
assertLanguageId('<html><style>|foo { }</style></html>', 'css');
|
||||
assertLanguageId('<html><style>foo| { }</style></html>', 'css');
|
||||
assertLanguageId('<html><style>foo { }|</style></html>', 'css');
|
||||
assertLanguageId('<html><style>foo { }</sty|le></html>', 'html');
|
||||
});
|
||||
|
||||
test('Styles - Incomplete HTML', function (): any {
|
||||
assertLanguageId('|<html><style>foo { }', 'html');
|
||||
assertLanguageId('<html><style>fo|o { }', 'css');
|
||||
assertLanguageId('<html><style>foo { }|', 'css');
|
||||
});
|
||||
|
||||
test('Style in attribute', function (): any {
|
||||
assertLanguageId('<div id="xy" |style="color: red"/>', 'html');
|
||||
assertLanguageId('<div id="xy" styl|e="color: red"/>', 'html');
|
||||
assertLanguageId('<div id="xy" style=|"color: red"/>', 'html');
|
||||
assertLanguageId('<div id="xy" style="|color: red"/>', 'css');
|
||||
assertLanguageId('<div id="xy" style="color|: red"/>', 'css');
|
||||
assertLanguageId('<div id="xy" style="color: red|"/>', 'css');
|
||||
assertLanguageId('<div id="xy" style="color: red"|/>', 'html');
|
||||
assertLanguageId('<div id="xy" style=\'color: r|ed\'/>', 'css');
|
||||
assertLanguageId('<div id="xy" style|=color:red/>', 'html');
|
||||
assertLanguageId('<div id="xy" style=|color:red/>', 'css');
|
||||
assertLanguageId('<div id="xy" style=color:r|ed/>', 'css');
|
||||
assertLanguageId('<div id="xy" style=color:red|/>', 'css');
|
||||
assertLanguageId('<div id="xy" style=color:red/|>', 'html');
|
||||
});
|
||||
|
||||
test('Style content', function (): any {
|
||||
assertEmbeddedLanguageContent('<html><style>foo { }</style></html>', 'css', ' foo { } ');
|
||||
assertEmbeddedLanguageContent('<html><script>var i = 0;</script></html>', 'css', ' ');
|
||||
assertEmbeddedLanguageContent('<html><style>foo { }</style>Hello<style>foo { }</style></html>', 'css', ' foo { } foo { } ');
|
||||
assertEmbeddedLanguageContent('<html>\n <style>\n foo { } \n </style>\n</html>\n', 'css', '\n \n foo { } \n \n\n');
|
||||
|
||||
assertEmbeddedLanguageContent('<div style="color: red"></div>', 'css', ' __{color: red} ');
|
||||
assertEmbeddedLanguageContent('<div style=color:red></div>', 'css', ' __{color:red} ');
|
||||
});
|
||||
|
||||
test('Scripts', function (): any {
|
||||
assertLanguageId('|<html><script>var i = 0;</script></html>', 'html');
|
||||
assertLanguageId('<html|><script>var i = 0;</script></html>', 'html');
|
||||
assertLanguageId('<html><scr|ipt>var i = 0;</script></html>', 'html');
|
||||
assertLanguageId('<html><script>|var i = 0;</script></html>', 'javascript');
|
||||
assertLanguageId('<html><script>var| i = 0;</script></html>', 'javascript');
|
||||
assertLanguageId('<html><script>var i = 0;|</script></html>', 'javascript');
|
||||
assertLanguageId('<html><script>var i = 0;</scr|ipt></html>', 'html');
|
||||
|
||||
assertLanguageId('<script type="text/javascript">var| i = 0;</script>', 'javascript');
|
||||
assertLanguageId('<script type="text/ecmascript">var| i = 0;</script>', 'javascript');
|
||||
assertLanguageId('<script type="application/javascript">var| i = 0;</script>', 'javascript');
|
||||
assertLanguageId('<script type="application/ecmascript">var| i = 0;</script>', 'javascript');
|
||||
assertLanguageId('<script type="application/typescript">var| i = 0;</script>', void 0);
|
||||
assertLanguageId('<script type=\'text/javascript\'>var| i = 0;</script>', 'javascript');
|
||||
});
|
||||
|
||||
test('Scripts in attribute', function (): any {
|
||||
assertLanguageId('<div |onKeyUp="foo()" onkeydown=\'bar()\'/>', 'html');
|
||||
assertLanguageId('<div onKeyUp=|"foo()" onkeydown=\'bar()\'/>', 'html');
|
||||
assertLanguageId('<div onKeyUp="|foo()" onkeydown=\'bar()\'/>', 'javascript');
|
||||
assertLanguageId('<div onKeyUp="foo(|)" onkeydown=\'bar()\'/>', 'javascript');
|
||||
assertLanguageId('<div onKeyUp="foo()|" onkeydown=\'bar()\'/>', 'javascript');
|
||||
assertLanguageId('<div onKeyUp="foo()"| onkeydown=\'bar()\'/>', 'html');
|
||||
assertLanguageId('<div onKeyUp="foo()" onkeydown=|\'bar()\'/>', 'html');
|
||||
assertLanguageId('<div onKeyUp="foo()" onkeydown=\'|bar()\'/>', 'javascript');
|
||||
assertLanguageId('<div onKeyUp="foo()" onkeydown=\'bar()|\'/>', 'javascript');
|
||||
assertLanguageId('<div onKeyUp="foo()" onkeydown=\'bar()\'|/>', 'html');
|
||||
|
||||
assertLanguageId('<DIV ONKEYUP|=foo()</DIV>', 'html');
|
||||
assertLanguageId('<DIV ONKEYUP=|foo()</DIV>', 'javascript');
|
||||
assertLanguageId('<DIV ONKEYUP=f|oo()</DIV>', 'javascript');
|
||||
assertLanguageId('<DIV ONKEYUP=foo(|)</DIV>', 'javascript');
|
||||
assertLanguageId('<DIV ONKEYUP=foo()|</DIV>', 'javascript');
|
||||
assertLanguageId('<DIV ONKEYUP=foo()<|/DIV>', 'html');
|
||||
|
||||
assertLanguageId('<label data-content="|Checkbox"/>', 'html');
|
||||
assertLanguageId('<label on="|Checkbox"/>', 'html');
|
||||
});
|
||||
|
||||
test('Script content', function (): any {
|
||||
assertEmbeddedLanguageContent('<html><script>var i = 0;</script></html>', 'javascript', ' var i = 0; ');
|
||||
assertEmbeddedLanguageContent('<script type="text/javascript">var i = 0;</script>', 'javascript', ' var i = 0; ');
|
||||
|
||||
assertEmbeddedLanguageContent('<div onKeyUp="foo()" onkeydown="bar()"/>', 'javascript', ' foo(); bar(); ');
|
||||
});
|
||||
|
||||
});
|
||||
@@ -0,0 +1,67 @@
|
||||
/*---------------------------------------------------------------------------------------------
|
||||
* 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 'mocha';
|
||||
import * as assert from 'assert';
|
||||
import { getHTMLMode } from '../modes/htmlMode';
|
||||
import { TextDocument, CompletionList } from 'vscode-languageserver-types';
|
||||
import { getLanguageModelCache } from '../languageModelCache';
|
||||
|
||||
import { getLanguageService } from 'vscode-html-languageservice';
|
||||
import * as embeddedSupport from '../modes/embeddedSupport';
|
||||
import { getEmmetCompletionParticipants } from 'vscode-emmet-helper';
|
||||
import { getCSSMode } from '../modes/cssMode';
|
||||
|
||||
suite('Emmet Support', () => {
|
||||
|
||||
const htmlLanguageService = getLanguageService();
|
||||
|
||||
function assertCompletions(syntax: string, value: string, expectedProposal: string | null, expectedProposalDoc: string | null): void {
|
||||
const offset = value.indexOf('|');
|
||||
value = value.substr(0, offset) + value.substr(offset + 1);
|
||||
|
||||
const document = TextDocument.create('test://test/test.' + syntax, syntax, 0, value);
|
||||
const position = document.positionAt(offset);
|
||||
const documentRegions = getLanguageModelCache<embeddedSupport.HTMLDocumentRegions>(10, 60, document => embeddedSupport.getDocumentRegions(htmlLanguageService, document));
|
||||
const mode = syntax === 'html' ? getHTMLMode(htmlLanguageService) : getCSSMode(documentRegions);
|
||||
|
||||
const emmetCompletionList = CompletionList.create([], false);
|
||||
mode.setCompletionParticipants!([getEmmetCompletionParticipants(document, position, document.languageId, {}, emmetCompletionList)]);
|
||||
|
||||
const list = mode.doComplete!(document, position);
|
||||
assert.ok(list);
|
||||
assert.ok(emmetCompletionList);
|
||||
|
||||
if (expectedProposal && expectedProposalDoc) {
|
||||
let actualLabels = emmetCompletionList.items.map(c => c.label).sort();
|
||||
let actualDocs = emmetCompletionList.items.map(c => c.documentation).sort();
|
||||
assert.ok(actualLabels.indexOf(expectedProposal) !== -1, 'Not found:' + expectedProposal + ' is ' + actualLabels.join(', '));
|
||||
assert.ok(actualDocs.indexOf(expectedProposalDoc) !== -1, 'Not found:' + expectedProposalDoc + ' is ' + actualDocs.join(', '));
|
||||
} else {
|
||||
assert.ok(!emmetCompletionList.items.length && !emmetCompletionList.isIncomplete);
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
test('Html Emmet Completions', function (): any {
|
||||
assertCompletions('html', 'ul|', 'ul', '<ul>|</ul>');
|
||||
assertCompletions('html', '<ul|', null, null);
|
||||
assertCompletions('html', '<html>ul|</html>', 'ul', '<ul>|</ul>');
|
||||
assertCompletions('html', '<img src=|', null, null);
|
||||
assertCompletions('html', '<div class=|/>', null, null);
|
||||
});
|
||||
|
||||
test('Css Emmet Completions', function (): any {
|
||||
assertCompletions('css', '<style>.foo { display: none; m10| }</style>', 'margin: 10px;', 'margin: 10px;');
|
||||
assertCompletions('css', '<style>foo { display: none; pos:f| }</style>', 'position: fixed;', 'position: fixed;');
|
||||
assertCompletions('css', '<style>foo { display: none; margin: a| }</style>', null, null);
|
||||
assertCompletions('css', '<style>foo| { display: none; }</style>', null, null);
|
||||
assertCompletions('css', '<style>foo {| display: none; }</style>', null, null);
|
||||
assertCompletions('css', '<style>foo { display: none;| }</style>', null, null);
|
||||
assertCompletions('css', '<style>foo { display: none|; }</style>', null, null);
|
||||
assertCompletions('css', '<style>.foo { display: none; -m-m10| }</style>', 'margin: 10px;', '-moz-margin: 10px;\nmargin: 10px;');
|
||||
});
|
||||
});
|
||||
22
extensions/html-language-features/server/src/test/fixtures/expected/19813-4spaces.html
vendored
Normal file
22
extensions/html-language-features/server/src/test/fixtures/expected/19813-4spaces.html
vendored
Normal file
@@ -0,0 +1,22 @@
|
||||
<!DOCTYPE html>
|
||||
<html>
|
||||
|
||||
<head>
|
||||
<meta charset="utf-8">
|
||||
<script>
|
||||
Polymer({
|
||||
is: "chat-messages",
|
||||
properties: {
|
||||
user: {},
|
||||
friend: {
|
||||
observer: "_friendChanged"
|
||||
}
|
||||
},
|
||||
});
|
||||
</script>
|
||||
</head>
|
||||
|
||||
<body>
|
||||
</body>
|
||||
|
||||
</html>
|
||||
22
extensions/html-language-features/server/src/test/fixtures/expected/19813-tab.html
vendored
Normal file
22
extensions/html-language-features/server/src/test/fixtures/expected/19813-tab.html
vendored
Normal file
@@ -0,0 +1,22 @@
|
||||
<!DOCTYPE html>
|
||||
<html>
|
||||
|
||||
<head>
|
||||
<meta charset="utf-8">
|
||||
<script>
|
||||
Polymer({
|
||||
is: "chat-messages",
|
||||
properties: {
|
||||
user: {},
|
||||
friend: {
|
||||
observer: "_friendChanged"
|
||||
}
|
||||
},
|
||||
});
|
||||
</script>
|
||||
</head>
|
||||
|
||||
<body>
|
||||
</body>
|
||||
|
||||
</html>
|
||||
22
extensions/html-language-features/server/src/test/fixtures/expected/19813.html
vendored
Normal file
22
extensions/html-language-features/server/src/test/fixtures/expected/19813.html
vendored
Normal file
@@ -0,0 +1,22 @@
|
||||
<!DOCTYPE html>
|
||||
<html>
|
||||
|
||||
<head>
|
||||
<meta charset="utf-8">
|
||||
<script>
|
||||
Polymer({
|
||||
is: "chat-messages",
|
||||
properties: {
|
||||
user: {},
|
||||
friend: {
|
||||
observer: "_friendChanged"
|
||||
}
|
||||
},
|
||||
});
|
||||
</script>
|
||||
</head>
|
||||
|
||||
<body>
|
||||
</body>
|
||||
|
||||
</html>
|
||||
6
extensions/html-language-features/server/src/test/fixtures/expected/21634.html
vendored
Normal file
6
extensions/html-language-features/server/src/test/fixtures/expected/21634.html
vendored
Normal file
@@ -0,0 +1,6 @@
|
||||
<app-route path="/module" element="page-module" bindRouter onUrlChange="updateModel"></app-route>
|
||||
|
||||
<script>
|
||||
Polymer({
|
||||
});
|
||||
</script>
|
||||
19
extensions/html-language-features/server/src/test/fixtures/inputs/19813.html
vendored
Normal file
19
extensions/html-language-features/server/src/test/fixtures/inputs/19813.html
vendored
Normal file
@@ -0,0 +1,19 @@
|
||||
<!DOCTYPE html>
|
||||
<html>
|
||||
<head>
|
||||
<meta charset="utf-8">
|
||||
<script>
|
||||
Polymer({
|
||||
is: "chat-messages",
|
||||
properties: {
|
||||
user: {},
|
||||
friend: {
|
||||
observer: "_friendChanged"
|
||||
}
|
||||
},
|
||||
});
|
||||
</script>
|
||||
</head>
|
||||
<body>
|
||||
</body>
|
||||
</html>
|
||||
6
extensions/html-language-features/server/src/test/fixtures/inputs/21634.html
vendored
Normal file
6
extensions/html-language-features/server/src/test/fixtures/inputs/21634.html
vendored
Normal file
@@ -0,0 +1,6 @@
|
||||
<app-route path="/module" element="page-module" bindRouter onUrlChange="updateModel"></app-route>
|
||||
|
||||
<script>
|
||||
Polymer({
|
||||
});
|
||||
</script>
|
||||
@@ -0,0 +1,259 @@
|
||||
/*---------------------------------------------------------------------------------------------
|
||||
* 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 'mocha';
|
||||
import * as assert from 'assert';
|
||||
import { TextDocument } from 'vscode-languageserver';
|
||||
import { getFoldingRegions } from '../modes/htmlFolding';
|
||||
import { getLanguageModes } from '../modes/languageModes';
|
||||
|
||||
interface ExpectedIndentRange {
|
||||
startLine: number;
|
||||
endLine: number;
|
||||
type?: string;
|
||||
}
|
||||
|
||||
function assertRanges(lines: string[], expected: ExpectedIndentRange[], message?: string, nRanges?: number): void {
|
||||
let document = TextDocument.create('test://foo/bar.json', 'json', 1, lines.join('\n'));
|
||||
let languageModes = getLanguageModes({ css: true, javascript: true });
|
||||
let actual = getFoldingRegions(languageModes, document, nRanges, null)!.ranges;
|
||||
|
||||
let actualRanges = [];
|
||||
for (let i = 0; i < actual.length; i++) {
|
||||
actualRanges[i] = r(actual[i].startLine, actual[i].endLine, actual[i].type);
|
||||
}
|
||||
actualRanges = actualRanges.sort((r1, r2) => r1.startLine - r2.startLine);
|
||||
assert.deepEqual(actualRanges, expected, message);
|
||||
}
|
||||
|
||||
function r(startLine: number, endLine: number, type?: string): ExpectedIndentRange {
|
||||
return { startLine, endLine, type };
|
||||
}
|
||||
|
||||
suite('Object Folding', () => {
|
||||
test('Fold one level', () => {
|
||||
let input = [
|
||||
/*0*/'<html>',
|
||||
/*1*/'Hello',
|
||||
/*2*/'</html>'
|
||||
];
|
||||
assertRanges(input, [r(0, 1)]);
|
||||
});
|
||||
|
||||
test('Fold two level', () => {
|
||||
let input = [
|
||||
/*0*/'<html>',
|
||||
/*1*/'<head>',
|
||||
/*2*/'Hello',
|
||||
/*3*/'</head>',
|
||||
/*4*/'</html>'
|
||||
];
|
||||
assertRanges(input, [r(0, 3), r(1, 2)]);
|
||||
});
|
||||
|
||||
test('Fold siblings', () => {
|
||||
let input = [
|
||||
/*0*/'<html>',
|
||||
/*1*/'<head>',
|
||||
/*2*/'Head',
|
||||
/*3*/'</head>',
|
||||
/*4*/'<body class="f">',
|
||||
/*5*/'Body',
|
||||
/*6*/'</body>',
|
||||
/*7*/'</html>'
|
||||
];
|
||||
assertRanges(input, [r(0, 6), r(1, 2), r(4, 5)]);
|
||||
});
|
||||
|
||||
test('Fold self-closing tags', () => {
|
||||
let input = [
|
||||
/*0*/'<div>',
|
||||
/*1*/'<a href="top"/>',
|
||||
/*2*/'<img src="s">',
|
||||
/*3*/'<br/>',
|
||||
/*4*/'<br>',
|
||||
/*5*/'<img class="c"',
|
||||
/*6*/' src="top"',
|
||||
/*7*/'>',
|
||||
/*8*/'</div>'
|
||||
];
|
||||
assertRanges(input, [r(0, 7), r(5, 6)]);
|
||||
});
|
||||
|
||||
test('Fold commment', () => {
|
||||
let input = [
|
||||
/*0*/'<!--',
|
||||
/*1*/' multi line',
|
||||
/*2*/'-->',
|
||||
/*3*/'<!-- some stuff',
|
||||
/*4*/' some more stuff -->',
|
||||
];
|
||||
assertRanges(input, [r(0, 2, 'comment'), r(3, 4, 'comment')]);
|
||||
});
|
||||
|
||||
test('Fold regions', () => {
|
||||
let input = [
|
||||
/*0*/'<!-- #region -->',
|
||||
/*1*/'<!-- #region -->',
|
||||
/*2*/'<!-- #endregion -->',
|
||||
/*3*/'<!-- #endregion -->',
|
||||
];
|
||||
assertRanges(input, [r(0, 3, 'region'), r(1, 2, 'region')]);
|
||||
});
|
||||
|
||||
test('Embedded JavaScript', () => {
|
||||
let input = [
|
||||
/*0*/'<html>',
|
||||
/*1*/'<head>',
|
||||
/*2*/'<script>',
|
||||
/*3*/'function f() {',
|
||||
/*4*/'}',
|
||||
/*5*/'</script>',
|
||||
/*6*/'</head>',
|
||||
/*7*/'</html>',
|
||||
];
|
||||
assertRanges(input, [r(0, 6), r(1, 5), r(2, 4), r(3, 4)]);
|
||||
});
|
||||
|
||||
test('Embedded JavaScript - mutiple areas', () => {
|
||||
let input = [
|
||||
/* 0*/'<html>',
|
||||
/* 1*/'<head>',
|
||||
/* 2*/'<script>',
|
||||
/* 3*/' var x = {',
|
||||
/* 4*/' foo: true,',
|
||||
/* 5*/' bar: {}',
|
||||
/* 6*/' };',
|
||||
/* 7*/'</script>',
|
||||
/* 8*/'<script>',
|
||||
/* 9*/' test(() => {',
|
||||
/*10*/' f();',
|
||||
/*11*/' });',
|
||||
/*12*/'</script>',
|
||||
/*13*/'</head>',
|
||||
/*14*/'</html>',
|
||||
];
|
||||
assertRanges(input, [r(0, 13), r(1, 12), r(2, 6), r(3, 6), r(8, 11), r(9, 11)]);
|
||||
});
|
||||
|
||||
test('Embedded JavaScript - incomplete', () => {
|
||||
let input = [
|
||||
/* 0*/'<html>',
|
||||
/* 1*/'<head>',
|
||||
/* 2*/'<script>',
|
||||
/* 3*/' var x = {',
|
||||
/* 4*/'</script>',
|
||||
/* 5*/'<script>',
|
||||
/* 6*/' });',
|
||||
/* 7*/'</script>',
|
||||
/* 8*/'</head>',
|
||||
/* 9*/'</html>',
|
||||
];
|
||||
assertRanges(input, [r(0, 8), r(1, 7), r(2, 3), r(5, 6)]);
|
||||
});
|
||||
|
||||
test('Embedded JavaScript - regions', () => {
|
||||
let input = [
|
||||
/* 0*/'<html>',
|
||||
/* 1*/'<head>',
|
||||
/* 2*/'<script>',
|
||||
/* 3*/' // #region Lalala',
|
||||
/* 4*/' // #region',
|
||||
/* 5*/' x = 9;',
|
||||
/* 6*/' // #endregion',
|
||||
/* 7*/' // #endregion Lalala',
|
||||
/* 8*/'</script>',
|
||||
/* 9*/'</head>',
|
||||
/*10*/'</html>',
|
||||
];
|
||||
assertRanges(input, [r(0, 9), r(1, 8), r(2, 7), r(3, 7, 'region'), r(4, 6, 'region')]);
|
||||
});
|
||||
|
||||
// test('Embedded JavaScript - mulit line comment', () => {
|
||||
// let input = [
|
||||
// /* 0*/'<html>',
|
||||
// /* 1*/'<head>',
|
||||
// /* 2*/'<script>',
|
||||
// /* 3*/' /*',
|
||||
// /* 4*/' * Hello',
|
||||
// /* 5*/' */',
|
||||
// /* 6*/'</script>',
|
||||
// /* 7*/'</head>',
|
||||
// /* 8*/'</html>',
|
||||
// ];
|
||||
// assertRanges(input, [r(0, 7), r(1, 6), r(2, 5), r(3, 5, 'comment')]);
|
||||
// });
|
||||
|
||||
test('Fold incomplete', () => {
|
||||
let input = [
|
||||
/*0*/'<body>',
|
||||
/*1*/'<div></div>',
|
||||
/*2*/'Hello',
|
||||
/*3*/'</div>',
|
||||
/*4*/'</body>',
|
||||
];
|
||||
assertRanges(input, [r(0, 3)]);
|
||||
});
|
||||
|
||||
test('Fold incomplete 2', () => {
|
||||
let input = [
|
||||
/*0*/'<be><div>',
|
||||
/*1*/'<!-- #endregion -->',
|
||||
/*2*/'</div>',
|
||||
];
|
||||
assertRanges(input, [r(0, 1)]);
|
||||
});
|
||||
|
||||
test('Fold intersecting region', () => {
|
||||
let input = [
|
||||
/*0*/'<body>',
|
||||
/*1*/'<!-- #region -->',
|
||||
/*2*/'Hello',
|
||||
/*3*/'<div></div>',
|
||||
/*4*/'</body>',
|
||||
/*5*/'<!-- #endregion -->',
|
||||
];
|
||||
assertRanges(input, [r(0, 3)]);
|
||||
});
|
||||
|
||||
|
||||
test('Test limit', () => {
|
||||
let input = [
|
||||
/* 0*/'<div>',
|
||||
/* 1*/' <span>',
|
||||
/* 2*/' <b>',
|
||||
/* 3*/' ',
|
||||
/* 4*/' </b>,',
|
||||
/* 5*/' <b>',
|
||||
/* 6*/' <pre>',
|
||||
/* 7*/' ',
|
||||
/* 8*/' </pre>,',
|
||||
/* 9*/' <pre>',
|
||||
/*10*/' ',
|
||||
/*11*/' </pre>,',
|
||||
/*12*/' </b>,',
|
||||
/*13*/' <b>',
|
||||
/*14*/' ',
|
||||
/*15*/' </b>,',
|
||||
/*16*/' <b>',
|
||||
/*17*/' ',
|
||||
/*18*/' </b>',
|
||||
/*19*/' </span>',
|
||||
/*20*/'</div>',
|
||||
];
|
||||
assertRanges(input, [r(0, 19), r(1, 18), r(2, 3), r(5, 11), r(6, 7), r(9, 10), r(13, 14), r(16, 17)], 'no limit', void 0);
|
||||
assertRanges(input, [r(0, 19), r(1, 18), r(2, 3), r(5, 11), r(6, 7), r(9, 10), r(13, 14), r(16, 17)], 'limit 8', 8);
|
||||
assertRanges(input, [r(0, 19), r(1, 18), r(2, 3), r(5, 11), r(13, 14), r(16, 17)], 'limit 7', 7);
|
||||
assertRanges(input, [r(0, 19), r(1, 18), r(2, 3), r(5, 11), r(13, 14), r(16, 17)], 'limit 6', 6);
|
||||
assertRanges(input, [r(0, 19), r(1, 18)], 'limit 5', 5);
|
||||
assertRanges(input, [r(0, 19), r(1, 18)], 'limit 4', 4);
|
||||
assertRanges(input, [r(0, 19), r(1, 18)], 'limit 3', 3);
|
||||
assertRanges(input, [r(0, 19), r(1, 18)], 'limit 2', 2);
|
||||
assertRanges(input, [r(0, 19)], 'limit 1', 1);
|
||||
});
|
||||
|
||||
});
|
||||
@@ -0,0 +1,114 @@
|
||||
/*---------------------------------------------------------------------------------------------
|
||||
* 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 'mocha';
|
||||
import * as path from 'path';
|
||||
import * as fs from 'fs';
|
||||
|
||||
import * as assert from 'assert';
|
||||
import { getLanguageModes } from '../modes/languageModes';
|
||||
import { TextDocument, Range, FormattingOptions } from 'vscode-languageserver-types';
|
||||
|
||||
import { format } from '../modes/formatting';
|
||||
|
||||
suite('HTML Embedded Formatting', () => {
|
||||
|
||||
function assertFormat(value: string, expected: string, options?: any, formatOptions?: FormattingOptions, message?: string): void {
|
||||
var languageModes = getLanguageModes({ css: true, javascript: true });
|
||||
if (options) {
|
||||
languageModes.getAllModes().forEach(m => m.configure!(options));
|
||||
}
|
||||
|
||||
let rangeStartOffset = value.indexOf('|');
|
||||
let rangeEndOffset;
|
||||
if (rangeStartOffset !== -1) {
|
||||
value = value.substr(0, rangeStartOffset) + value.substr(rangeStartOffset + 1);
|
||||
|
||||
rangeEndOffset = value.indexOf('|');
|
||||
value = value.substr(0, rangeEndOffset) + value.substr(rangeEndOffset + 1);
|
||||
} else {
|
||||
rangeStartOffset = 0;
|
||||
rangeEndOffset = value.length;
|
||||
}
|
||||
let document = TextDocument.create('test://test/test.html', 'html', 0, value);
|
||||
let range = Range.create(document.positionAt(rangeStartOffset), document.positionAt(rangeEndOffset));
|
||||
if (!formatOptions) {
|
||||
formatOptions = FormattingOptions.create(2, true);
|
||||
}
|
||||
|
||||
let result = format(languageModes, document, range, formatOptions, void 0, { css: true, javascript: true });
|
||||
|
||||
let actual = TextDocument.applyEdits(document, result);
|
||||
assert.equal(actual, expected, message);
|
||||
}
|
||||
|
||||
function assertFormatWithFixture(fixtureName: string, expectedPath: string, options?: any, formatOptions?: FormattingOptions): void {
|
||||
let input = fs.readFileSync(path.join(__dirname, 'fixtures', 'inputs', fixtureName)).toString().replace(/\r\n/mg, '\n');
|
||||
let expected = fs.readFileSync(path.join(__dirname, 'fixtures', 'expected', expectedPath)).toString().replace(/\r\n/mg, '\n');
|
||||
assertFormat(input, expected, options, formatOptions, expectedPath);
|
||||
}
|
||||
|
||||
test('HTML only', function (): any {
|
||||
assertFormat('<html><body><p>Hello</p></body></html>', '<html>\n\n<body>\n <p>Hello</p>\n</body>\n\n</html>');
|
||||
assertFormat('|<html><body><p>Hello</p></body></html>|', '<html>\n\n<body>\n <p>Hello</p>\n</body>\n\n</html>');
|
||||
assertFormat('<html>|<body><p>Hello</p></body>|</html>', '<html><body>\n <p>Hello</p>\n</body></html>');
|
||||
});
|
||||
|
||||
test('HTML & Scripts', function (): any {
|
||||
assertFormat('<html><head><script></script></head></html>', '<html>\n\n<head>\n <script></script>\n</head>\n\n</html>');
|
||||
assertFormat('<html><head><script>var x=1;</script></head></html>', '<html>\n\n<head>\n <script>var x = 1;</script>\n</head>\n\n</html>');
|
||||
assertFormat('<html><head><script>\nvar x=2;\n</script></head></html>', '<html>\n\n<head>\n <script>\n var x = 2;\n </script>\n</head>\n\n</html>');
|
||||
assertFormat('<html><head>\n <script>\nvar x=3;\n</script></head></html>', '<html>\n\n<head>\n <script>\n var x = 3;\n </script>\n</head>\n\n</html>');
|
||||
assertFormat('<html><head>\n <script>\nvar x=4;\nconsole.log("Hi");\n</script></head></html>', '<html>\n\n<head>\n <script>\n var x = 4;\n console.log("Hi");\n </script>\n</head>\n\n</html>');
|
||||
assertFormat('<html><head>\n |<script>\nvar x=5;\n</script>|</head></html>', '<html><head>\n <script>\n var x = 5;\n </script></head></html>');
|
||||
});
|
||||
|
||||
test('HTLM & Scripts - Fixtures', function () {
|
||||
assertFormatWithFixture('19813.html', '19813.html');
|
||||
assertFormatWithFixture('19813.html', '19813-4spaces.html', void 0, FormattingOptions.create(4, true));
|
||||
assertFormatWithFixture('19813.html', '19813-tab.html', void 0, FormattingOptions.create(1, false));
|
||||
assertFormatWithFixture('21634.html', '21634.html');
|
||||
});
|
||||
|
||||
test('Script end tag', function (): any {
|
||||
assertFormat('<html>\n<head>\n <script>\nvar x = 0;\n</script></head></html>', '<html>\n\n<head>\n <script>\n var x = 0;\n </script>\n</head>\n\n</html>');
|
||||
});
|
||||
|
||||
test('HTML & Multiple Scripts', function (): any {
|
||||
assertFormat('<html><head>\n<script>\nif(x){\nbar(); }\n</script><script>\nfunction(x){ }\n</script></head></html>', '<html>\n\n<head>\n <script>\n if (x) {\n bar();\n }\n </script>\n <script>\n function(x) {}\n </script>\n</head>\n\n</html>');
|
||||
});
|
||||
|
||||
test('HTML & Styles', function (): any {
|
||||
assertFormat('<html><head>\n<style>\n.foo{display:none;}\n</style></head></html>', '<html>\n\n<head>\n <style>\n .foo {\n display: none;\n }\n </style>\n</head>\n\n</html>');
|
||||
});
|
||||
|
||||
test('EndWithNewline', function (): any {
|
||||
let options = {
|
||||
html: {
|
||||
format: {
|
||||
endWithNewline: true
|
||||
}
|
||||
}
|
||||
};
|
||||
assertFormat('<html><body><p>Hello</p></body></html>', '<html>\n\n<body>\n <p>Hello</p>\n</body>\n\n</html>\n', options);
|
||||
assertFormat('<html>|<body><p>Hello</p></body>|</html>', '<html><body>\n <p>Hello</p>\n</body></html>', options);
|
||||
assertFormat('<html><head><script>\nvar x=1;\n</script></head></html>', '<html>\n\n<head>\n <script>\n var x = 1;\n </script>\n</head>\n\n</html>\n', options);
|
||||
});
|
||||
|
||||
test('Inside script', function (): any {
|
||||
assertFormat('<html><head>\n <script>\n|var x=6;|\n</script></head></html>', '<html><head>\n <script>\n var x = 6;\n</script></head></html>');
|
||||
assertFormat('<html><head>\n <script>\n|var x=6;\nvar y= 9;|\n</script></head></html>', '<html><head>\n <script>\n var x = 6;\n var y = 9;\n</script></head></html>');
|
||||
});
|
||||
|
||||
test('Range after new line', function (): any {
|
||||
assertFormat('<html><head>\n |<script>\nvar x=6;\n</script>\n|</head></html>', '<html><head>\n <script>\n var x = 6;\n </script>\n</head></html>');
|
||||
});
|
||||
|
||||
test('bug 36574', function (): any {
|
||||
assertFormat('<script src="/js/main.js"> </script>', '<script src="/js/main.js"> </script>');
|
||||
});
|
||||
|
||||
});
|
||||
@@ -0,0 +1,237 @@
|
||||
/*---------------------------------------------------------------------------------------------
|
||||
* 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/') },
|
||||
]);
|
||||
});
|
||||
});
|
||||
@@ -0,0 +1,46 @@
|
||||
/*---------------------------------------------------------------------------------------------
|
||||
* 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 words from '../utils/strings';
|
||||
|
||||
suite('Words', () => {
|
||||
|
||||
let wordRegex = /(-?\d*\.\d\w*)|([^\`\~\!\@\#\%\^\&\*\(\)\-\=\+\[\{\]\}\\\|\;\:\'\"\,\.\<\>\/\?\s]+)/g;
|
||||
|
||||
function assertWord(value: string, expected: string): void {
|
||||
let offset = value.indexOf('|');
|
||||
value = value.substr(0, offset) + value.substr(offset + 1);
|
||||
|
||||
let actualRange = words.getWordAtText(value, offset, wordRegex);
|
||||
assert(actualRange.start <= offset);
|
||||
assert(actualRange.start + actualRange.length >= offset);
|
||||
assert.equal(value.substr(actualRange.start, actualRange.length), expected);
|
||||
}
|
||||
|
||||
|
||||
test('Basic', function (): any {
|
||||
assertWord('|var x1 = new F<A>(a, b);', 'var');
|
||||
assertWord('v|ar x1 = new F<A>(a, b);', 'var');
|
||||
assertWord('var| x1 = new F<A>(a, b);', 'var');
|
||||
assertWord('var |x1 = new F<A>(a, b);', 'x1');
|
||||
assertWord('var x1| = new F<A>(a, b);', 'x1');
|
||||
assertWord('var x1 = new |F<A>(a, b);', 'F');
|
||||
assertWord('var x1 = new F<|A>(a, b);', 'A');
|
||||
assertWord('var x1 = new F<A>(|a, b);', 'a');
|
||||
assertWord('var x1 = new F<A>(a, b|);', 'b');
|
||||
assertWord('var x1 = new F<A>(a, b)|;', '');
|
||||
assertWord('var x1 = new F<A>(a, b)|;|', '');
|
||||
assertWord('var x1 = | new F<A>(a, b)|;|', '');
|
||||
});
|
||||
|
||||
test('Multiline', function (): any {
|
||||
assertWord('console.log("hello");\n|var x1 = new F<A>(a, b);', 'var');
|
||||
assertWord('console.log("hello");\n|\nvar x1 = new F<A>(a, b);', '');
|
||||
assertWord('console.log("hello");\n\r |var x1 = new F<A>(a, b);', 'var');
|
||||
});
|
||||
|
||||
});
|
||||
Reference in New Issue
Block a user