Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Integrate Volar for the built-in HTML Extension #171547

Open
wants to merge 89 commits into
base: main
Choose a base branch
from
Open
Changes from 1 commit
Commits
Show all changes
89 commits
Select commit Hold shift + click to select a range
4a8997a
Embedded support by Volar
johnsoncodehk Feb 9, 2024
aca75b3
Bump Volar
johnsoncodehk Feb 9, 2024
e77f14a
Fix tsc build structure
johnsoncodehk Feb 9, 2024
eefc9cc
Embedded TS support
johnsoncodehk Feb 14, 2024
f9563f3
Reco test completions.test.ts
johnsoncodehk Feb 16, 2024
ee529da
Update index.html
johnsoncodehk Feb 16, 2024
013870a
Completion tests passed
johnsoncodehk Feb 16, 2024
8692e04
Respect sys version for web
johnsoncodehk Feb 16, 2024
88858ea
Redo test folding.test.ts
johnsoncodehk Feb 16, 2024
148839b
Update folding.test.ts
johnsoncodehk Feb 16, 2024
a6d57da
(WIP) Add selectionRanges.test.ts
johnsoncodehk Feb 16, 2024
9ac8537
Enable "structure" for inline style
johnsoncodehk Feb 16, 2024
3473fe8
Redo test semanticTokens.test.ts
johnsoncodehk Feb 17, 2024
066fc84
Pass documentContext.test.ts
johnsoncodehk Feb 17, 2024
5637b36
Pass rename.test.ts
johnsoncodehk Feb 17, 2024
69868d3
(WIP) Add formatting.test.ts
johnsoncodehk Feb 17, 2024
2e97700
One test passed
johnsoncodehk Feb 17, 2024
8ab0873
All tests passed
johnsoncodehk Feb 20, 2024
1f565bd
Create embedded.test.ts
johnsoncodehk Feb 20, 2024
e37f35a
Code split
johnsoncodehk Feb 20, 2024
0bc7045
Update embedded.test.ts
johnsoncodehk Feb 20, 2024
92f4666
Update folding.test.ts [skip ci]
johnsoncodehk Feb 20, 2024
f913dae
Restore client structure
johnsoncodehk Feb 20, 2024
6dbf647
Update htmlClientMain.ts
johnsoncodehk Feb 20, 2024
cd5df6b
Upgrade Volar
johnsoncodehk Feb 27, 2024
b0c95c2
Merge branch 'main' into pr/171547
johnsoncodehk Feb 27, 2024
d6b988d
Use `html.format.enable` as formatting option for all services
johnsoncodehk Feb 27, 2024
27e7a62
Update timeout
johnsoncodehk Feb 27, 2024
021113f
Add missing copyright
johnsoncodehk Feb 27, 2024
1d68f03
Much faster testing
johnsoncodehk Feb 27, 2024
b49b0c1
Update projectProvider.ts
johnsoncodehk Feb 27, 2024
0faa011
Update to Volar 2.3 alpha
johnsoncodehk May 30, 2024
44036d1
Merge branch 'main' into pr/171547
johnsoncodehk May 30, 2024
168a576
update yarn.lock
johnsoncodehk May 30, 2024
9a51349
Update Volar
johnsoncodehk May 30, 2024
3903726
Update Volar
johnsoncodehk May 30, 2024
d45866f
updates
johnsoncodehk May 31, 2024
f09ad99
Update Volar
johnsoncodehk Jun 22, 2024
154ec5a
Merge branch 'main' into pr/171547
johnsoncodehk Jun 22, 2024
b3829e5
Redo htmlServer.ts
johnsoncodehk Jun 22, 2024
0d45578
Add support for `html.validate.*`
johnsoncodehk Jun 22, 2024
c1c9971
Support for `handledSchemas` option
johnsoncodehk Jun 22, 2024
25d1dd1
All tests passed
johnsoncodehk Jun 22, 2024
aa90c7b
Update project.ts
johnsoncodehk Jun 22, 2024
03d3a32
Add support for cross-file semantic capabilities
johnsoncodehk Jun 23, 2024
810631d
Add support for `<script type="application/json">`
johnsoncodehk Jun 23, 2024
91e1ba7
Add support for `<style type="text/scss">`
johnsoncodehk Jun 23, 2024
f6940b9
Add support for `<style type="text/less">`
johnsoncodehk Jun 23, 2024
7d4f877
Fix script module scope [skip ci]
johnsoncodehk Jun 23, 2024
af76b43
Update Volar
johnsoncodehk Jun 23, 2024
6e99156
Create htmlClientMain.ts
johnsoncodehk Jun 23, 2024
5285f10
servicePlugins.ts -> languageServicePlugins.ts
johnsoncodehk Jun 23, 2024
6b907f4
Fix #210303
johnsoncodehk Jun 23, 2024
3fcbe6d
Implement #26338
johnsoncodehk Jun 23, 2024
c122ede
Fix #135849
johnsoncodehk Jun 23, 2024
c56c223
Merge branch 'main' into pr/171547
johnsoncodehk Jun 23, 2024
44d4b89
Merge branch 'main' into pr/171547
johnsoncodehk Jun 24, 2024
fda16f9
Handle `http://`, `https://`, `//`, `file://` protocols
johnsoncodehk Jun 24, 2024
efa660c
Update languagePlugin.ts
johnsoncodehk Jun 24, 2024
57f17ea
Update Volar
johnsoncodehk Jun 24, 2024
bb3d243
Update package.json [skip ci]
johnsoncodehk Jun 24, 2024
09a4ca8
Use single language service instance
johnsoncodehk Jun 24, 2024
fa56e1d
Update currentDirectory if needed
johnsoncodehk Jun 24, 2024
1aa0efc
Update virtual code ID
johnsoncodehk Jun 24, 2024
df712e7
Enable verification in attribute value
johnsoncodehk Jun 24, 2024
6a97223
Clear FS cache when reload project
johnsoncodehk Jun 24, 2024
768c50c
Code cleanup
johnsoncodehk Jun 24, 2024
646aa1b
Merge branch 'main' into pr/171547
johnsoncodehk Jun 24, 2024
e387157
Merge branch 'main' into pr/171547
johnsoncodehk Jun 24, 2024
81962b2
Update completions.test.ts
johnsoncodehk Jun 24, 2024
08e2023
Create documentContext.test.ts
johnsoncodehk Jun 24, 2024
8888117
Code cleanup
johnsoncodehk Jun 24, 2024
fde0542
Update yarn.lock
johnsoncodehk Jun 25, 2024
b35cdec
Update yarn.lock
johnsoncodehk Jun 25, 2024
7277b91
Merge branch 'main' into pr/171547
johnsoncodehk Jun 25, 2024
7035dc7
Update Volar
johnsoncodehk Jun 26, 2024
14b94bb
Merge branch 'main' into pr/171547
johnsoncodehk Jun 26, 2024
aefcaec
Update htmlClientMain.ts
johnsoncodehk Jun 26, 2024
d747720
Update languagePlugin.ts
johnsoncodehk Jun 27, 2024
27b5bde
Update Volar
johnsoncodehk Jun 29, 2024
d5a2ecc
Update shared.ts
johnsoncodehk Jun 29, 2024
3cfc1ec
Update Volar to 2.4.0-alpha.17
johnsoncodehk Jul 21, 2024
fa9ac6c
Merge branch 'main' into pr/171547
johnsoncodehk Jul 21, 2024
3782ec5
Volar 2.4.0
johnsoncodehk Aug 19, 2024
f708bb5
Merge branch 'main' into pr/171547
johnsoncodehk Aug 19, 2024
1c3eb9a
Install fs in onInitialize
johnsoncodehk Aug 19, 2024
9e5a937
Merge branch 'main' into pr/171547
johnsoncodehk Aug 24, 2024
6ead37d
Remove @volar/test-utils
johnsoncodehk Aug 24, 2024
b0feaa0
Update semanticTokens.test.ts
johnsoncodehk Aug 24, 2024
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Prev Previous commit
Next Next commit
Redo test folding.test.ts
johnsoncodehk committed Feb 16, 2024
commit 88858ea10fe156fe3c27484562b505f5ec2384f8
Original file line number Diff line number Diff line change
@@ -1,5 +1,4 @@
import { ServerProject, ServerProjectProviderFactory } from '@volar/language-server';
import { getInferredCompilerOptions } from '@volar/language-server/lib/project/inferredCompilerOptions';
import { ServerContext, ServerOptions } from '@volar/language-server/lib/server';
import { createServiceEnvironment, getWorkspaceFolder } from '@volar/language-server/node';
import { LanguageService, ServiceEnvironment, ServicePlugin, TypeScriptProjectHost, createLanguageService, resolveCommonLanguageId } from '@volar/language-service';
@@ -38,19 +37,17 @@ export const serverProjectProviderFactory: ServerProjectProviderFactory = (conte
async function getOrCreateProject(workspaceFolder: URI) {
if (!inferredProject) {
inferredProject = (async () => {
const inferOptions = await getInferredCompilerOptions(context.configurationHost);
const serviceEnv = createServiceEnvironment(context, workspaceFolder);
return createProject(inferOptions, context, serviceEnv, serverOptions, servicePlugins);
return createProject(context, serviceEnv, serverOptions.getLanguagePlugins, servicePlugins);
})();
}
return await inferredProject;
}

async function createProject(
tsconfig: string | ts.CompilerOptions,
context: ServerContext,
serviceEnv: ServiceEnvironment,
serverOptions: ServerOptions,
getLanguagePlugins: ServerOptions['getLanguagePlugins'],
servicePlugins: ServicePlugin[],
): Promise<ServerProject> {

@@ -89,9 +86,9 @@ export const serverProjectProviderFactory: ServerProjectProviderFactory = (conte
getLocalizedDiagnosticMessages: context.tsLocalized ? () => context.tsLocalized : undefined,
getLanguageId: uri => context.documents.get(uri)?.languageId ?? resolveCommonLanguageId(uri),
};
const languagePlugins = await serverOptions.getLanguagePlugins(serviceEnv, {
const languagePlugins = await getLanguagePlugins(serviceEnv, {
typescript: {
configFileName: typeof tsconfig === 'string' ? tsconfig : undefined,
configFileName: undefined,
host,
sys,
},
@@ -110,7 +107,7 @@ export const serverProjectProviderFactory: ServerProjectProviderFactory = (conte
ts,
sys,
languagePlugins,
typeof tsconfig === 'string' ? tsconfig : undefined,
undefined,
host,
{
fileNameToFileId: serviceEnv.typescript!.fileNameToUri,
Original file line number Diff line number Diff line change
@@ -6,8 +6,9 @@ import 'mocha';
import * as assert from 'assert';
import * as path from 'path';
import { URI } from 'vscode-uri';
import { WorkspaceFolder, TextDocument, CompletionList, CompletionItemKind, TextEdit, DiagnosticModel } from '@volar/language-server';
import { WorkspaceFolder, TextDocument, CompletionList, CompletionItemKind, TextEdit, DiagnosticModel, ClientCapabilities } from '@volar/language-server';
import { startLanguageServer } from '@volar/test-utils';

export interface ItemDescription {
label: string;
documentation?: string;
@@ -44,7 +45,8 @@ export function assertCompletion(completions: CompletionList, expected: ItemDesc
}

const testUri = 'test://test/test.html';
const testServers = new Map<string, ReturnType<typeof startLanguageServer>>();

export const testServers = new Map<string, ReturnType<typeof startLanguageServer>>();

export async function testCompletionFor(value: string, expected: { count?: number; items?: ItemDescription[] }, uri = testUri, workspaceFolder?: WorkspaceFolder): Promise<void> {
const offset = value.indexOf('|');
@@ -53,11 +55,8 @@ export async function testCompletionFor(value: string, expected: { count?: numbe
const document = TextDocument.create(uri, 'html', 0, value);
const position = document.positionAt(offset);
const server = await getTestServer(workspaceFolder?.uri || uri.substr(0, uri.lastIndexOf('/')));

await server.openInMemoryDocument(uri, 'html', value);

const list = await server.sendCompletionRequest(uri, position);

assert(!!list);

if (expected.count) {
@@ -70,16 +69,24 @@ export async function testCompletionFor(value: string, expected: { count?: numbe
}
}

async function getTestServer(rootUri: string) {
export async function getTestServer(rootUri: string, capabilities?: ClientCapabilities) {
let server = testServers.get(rootUri);
let needInit = false;
if (!server) {
server = startLanguageServer(require.resolve('../node/htmlServerMain'));
testServers.set(rootUri, server);
needInit = true;
}
else if (capabilities) {
await server.shutdown();
needInit = true;
}
if (needInit) {
await server.initialize(rootUri, {
typescript: { tsdk: path.dirname(require.resolve('typescript')) },
diagnosticModel: DiagnosticModel.Pull,
fullCompletionList: true,
});
}, capabilities);
}
return server;
}
226 changes: 226 additions & 0 deletions extensions/html-language-features/server/src/test/folding.test.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,226 @@
/*---------------------------------------------------------------------------------------------
* Copyright (c) Microsoft Corporation. All rights reserved.
* Licensed under the MIT License. See License.txt in the project root for license information.
*--------------------------------------------------------------------------------------------*/

import 'mocha';
import * as assert from 'assert';
import { getTestServer, testServers } from './completions.test';

interface ExpectedIndentRange {
startLine: number;
endLine: number;
kind?: string;
}

const testUri = 'test://foo/bar.html';

async function assertRanges(lines: string[], expected: ExpectedIndentRange[], message?: string, nRanges?: number): Promise<void> {
const rootUri = testUri.substr(0, testUri.lastIndexOf('/'));
const server = await getTestServer(rootUri, nRanges !== undefined ? {
textDocument: {
foldingRange: {
rangeLimit: nRanges,
},
},
} : undefined);
await server.openInMemoryDocument(testUri, 'html', lines.join('\n'));
const actual = await server.sendFoldingRangesRequest(testUri);
assert(!!actual);

let actualRanges = [];
for (let i = 0; i < actual.length; i++) {
actualRanges[i] = r(actual[i].startLine, actual[i].endLine, actual[i].kind);
}
actualRanges = actualRanges.sort((r1, r2) => r1.startLine - r2.startLine);
assert.deepStrictEqual(actualRanges, expected, message);
}

function r(startLine: number, endLine: number, kind?: string): ExpectedIndentRange {
return { startLine, endLine, kind };
}

suite('HTML Folding', () => {

test('Embedded JavaScript', async () => {
const input = [
/*0*/'<html>',
/*1*/'<head>',
/*2*/'<script>',
/*3*/'function f() {',
/*4*/'}',
/*5*/'</script>',
/*6*/'</head>',
/*7*/'</html>',
];
await assertRanges(input, [r(0, 6), r(1, 5), r(2, 4), r(3, 3 /* TODO */)]);
});

test('Embedded JavaScript - multiple areas', async () => {
const input = [
/* 0*/'<html>',
/* 1*/'<head>',
/* 2*/'<script>',
/* 3*/' var x = {',
/* 4*/' foo: true,',
/* 5*/' bar: {}',
/* 6*/' };',
/* 7*/'</script>',
/* 8*/'<script>',
/* 9*/' test(() => { // hello',
/*10*/' f();',
/*11*/' });',
/*12*/'</script>',
/*13*/'</head>',
/*14*/'</html>',
];
await assertRanges(input, [r(0, 13), r(1, 12), r(2, 6), r(3, 5), r(5, 5), r(8, 11), r(9, 10), r(9, 10)]);
});

test('Embedded JavaScript - incomplete', async () => {
const input = [
/* 0*/'<html>',
/* 1*/'<head>',
/* 2*/'<script>',
/* 3*/' var x = {',
/* 4*/'</script>',
/* 5*/'<script>',
/* 6*/' });',
/* 7*/'</script>',
/* 8*/'</head>',
/* 9*/'</html>',
];
await assertRanges(input, [r(0, 8), r(1, 7), r(2, 3), r(5, 6)]);
});

test('Embedded JavaScript - regions', async () => {
const 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>',
];
await assertRanges(input, [r(0, 9), r(1, 8), r(2, 7), r(3, 7, 'region'), r(4, 6, 'region')]);
});

test('Embedded CSS', async () => {
const input = [
/* 0*/'<html>',
/* 1*/'<head>',
/* 2*/'<style>',
/* 3*/' foo {',
/* 4*/' display: block;',
/* 5*/' color: black;',
/* 6*/' }',
/* 7*/'</style>',
/* 8*/'</head>',
/* 9*/'</html>',
];
await assertRanges(input, [r(0, 8), r(1, 7), r(2, 6), r(3, 5)]);
});

test('Embedded CSS - multiple areas', async () => {
const input = [
/* 0*/'<html>',
/* 1*/'<head style="color:red">',
/* 2*/'<style>',
/* 3*/' /*',
/* 4*/' foo: true,',
/* 5*/' bar: {}',
/* 6*/' */',
/* 7*/'</style>',
/* 8*/'<style>',
/* 9*/' @keyframes mymove {',
/*10*/' from {top: 0px;}',
/*11*/' }',
/*12*/'</style>',
/*13*/'</head>',
/*14*/'</html>',
];
await assertRanges(input, [r(0, 13), r(1, 12), r(2, 6), r(3, 6, 'comment'), r(8, 11), r(9, 10)]);
});

test('Embedded CSS - regions', async () => {
const input = [
/* 0*/'<html>',
/* 1*/'<head>',
/* 2*/'<style>',
/* 3*/' /* #region Lalala */',
/* 4*/' /* #region*/',
/* 5*/' x = 9;',
/* 6*/' /* #endregion*/',
/* 7*/' /* #endregion Lalala*/',
/* 8*/'</style>',
/* 9*/'</head>',
/*10*/'</html>',
];
await assertRanges(input, [r(0, 9), r(1, 8), r(2, 7), r(3, 7, 'region'), r(4, 6, 'region')]);
});

test('Embedded JavaScript - multi line comment', async () => {
const input = [
/* 0*/'<html>',
/* 1*/'<head>',
/* 2*/'<script>',
/* 3*/' /*',
/* 4*/' * Hello',
/* 5*/' */',
/* 6*/'</script>',
/* 7*/'</head>',
/* 8*/'</html>',
];
await assertRanges(input, [r(0, 7), r(1, 6), r(2, 5), r(3, 5, 'comment')]);
});

{
const 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>',
];
test('Test limit', async () => {
await 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', undefined);
await 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);
await assertRanges(input, [r(0, 19), r(1, 18), r(2, 3), r(5, 11), r(6, 7), r(13, 14), r(16, 17)], 'limit 7', 7);
await assertRanges(input, [r(0, 19), r(1, 18), r(2, 3), r(5, 11), r(13, 14), r(16, 17)], 'limit 6', 6);
await assertRanges(input, [r(0, 19), r(1, 18), r(2, 3), r(5, 11), r(13, 14)], 'limit 5', 5);
});
// Avoid timeout
test('Test limit', async () => {
await assertRanges(input, [r(0, 19), r(1, 18), r(2, 3), r(5, 11)], 'limit 4', 4);
await assertRanges(input, [r(0, 19), r(1, 18), r(2, 3)], 'limit 3', 3);
await assertRanges(input, [r(0, 19), r(1, 18)], 'limit 2', 2);
await assertRanges(input, [r(0, 19)], 'limit 1', 1);
});
}

}).afterAll(() => {
for (const server of testServers.values()) {
server.connection.dispose();
}
});;