Skip to content

Commit 3862e76

Browse files
perf(typescript-plugin): use named pipe servers more efficiently (#5070)
1 parent 06df71b commit 3862e76

File tree

6 files changed

+466
-341
lines changed

6 files changed

+466
-341
lines changed

packages/language-server/lib/hybridModeProject.ts

Lines changed: 10 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -2,7 +2,7 @@ import type { Language, LanguagePlugin, LanguageServer, LanguageServerProject, P
22
import { createLanguageServiceEnvironment } from '@volar/language-server/lib/project/simpleProject';
33
import { createLanguage } from '@vue/language-core';
44
import { createLanguageService, createUriMap, LanguageService } from '@vue/language-service';
5-
import { getReadyNamedPipePaths, onSomePipeReadyCallbacks, searchNamedPipeServerForFile } from '@vue/typescript-plugin/lib/utils';
5+
import { configuredServers, getBestServer, inferredServers, onServerReady } from '@vue/typescript-plugin/lib/utils';
66
import { URI } from 'vscode-uri';
77

88
export function createHybridModeProject(
@@ -24,7 +24,7 @@ export function createHybridModeProject(
2424
const project: LanguageServerProject = {
2525
setup(_server) {
2626
server = _server;
27-
onSomePipeReadyCallbacks.push(() => {
27+
onServerReady.push(() => {
2828
server.languageFeatures.requestRefresh(false);
2929
});
3030
server.fileWatcher.onDidChangeWatchedFiles(({ changes }) => {
@@ -38,16 +38,20 @@ export function createHybridModeProject(
3838
});
3939
const end = Date.now() + 60000;
4040
const pipeWatcher = setInterval(() => {
41-
getReadyNamedPipePaths();
41+
for (const server of configuredServers) {
42+
server.update();
43+
}
44+
for (const server of inferredServers) {
45+
server.update();
46+
}
4247
if (Date.now() > end) {
4348
clearInterval(pipeWatcher);
4449
}
45-
}, 1000);
50+
}, 2500);
4651
},
4752
async getLanguageService(uri) {
4853
const fileName = asFileName(uri);
49-
const namedPipeServer = (await searchNamedPipeServerForFile(fileName));
50-
namedPipeServer?.socket.end();
54+
const namedPipeServer = await getBestServer(fileName);
5155
if (namedPipeServer?.projectInfo?.kind === 1) {
5256
const tsconfig = namedPipeServer.projectInfo.name;
5357
const tsconfigUri = URI.file(tsconfig);

packages/language-service/lib/plugins/vue-template.ts

Lines changed: 5 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -242,7 +242,9 @@ export function create(
242242
? tagName
243243
: components.find(component => component === tagName || hyphenateTag(component) === tagName);
244244
if (checkTag) {
245-
componentProps[checkTag] ??= (await tsPluginClient?.getComponentProps(code.fileName, checkTag, true) ?? []).map(prop => prop.name);
245+
componentProps[checkTag] ??= (await tsPluginClient?.getComponentProps(code.fileName, checkTag) ?? [])
246+
.filter(prop => prop.required)
247+
.map(prop => prop.name);
246248
current = {
247249
unburnedRequiredProps: [...componentProps[checkTag]],
248250
labelOffset: scanner.getTokenOffset() + scanner.getTokenLength(),
@@ -469,7 +471,7 @@ export function create(
469471
const promises: Promise<void>[] = [];
470472
const tagInfos = new Map<string, {
471473
attrs: string[];
472-
propsInfo: { name: string, commentMarkdown: string; }[];
474+
propsInfo: { name: string, commentMarkdown?: string; }[];
473475
events: string[];
474476
}>();
475477

@@ -1010,7 +1012,7 @@ function parseLabel(label: string) {
10101012
return {
10111013
name,
10121014
leadingSlash
1013-
}
1015+
};
10141016
}
10151017

10161018
function generateItemKey(type: InternalItemId, tag: string, prop: string) {
Lines changed: 53 additions & 50 deletions
Original file line numberDiff line numberDiff line change
@@ -1,96 +1,99 @@
1-
import type { Request } from './server';
2-
import { searchNamedPipeServerForFile, sendRequestWorker } from './utils';
1+
import type { RequestData } from './server';
2+
import { getBestServer } from './utils';
33

44
export function collectExtractProps(
55
...args: Parameters<typeof import('./requests/collectExtractProps.js')['collectExtractProps']>
66
) {
7-
return sendRequest<ReturnType<typeof import('./requests/collectExtractProps')['collectExtractProps']>>({
8-
type: 'collectExtractProps',
9-
args,
10-
});
7+
return sendRequest<ReturnType<typeof import('./requests/collectExtractProps')['collectExtractProps']>>(
8+
'collectExtractProps',
9+
...args
10+
);
1111
}
1212

1313
export async function getImportPathForFile(
1414
...args: Parameters<typeof import('./requests/getImportPathForFile.js')['getImportPathForFile']>
1515
) {
16-
return await sendRequest<ReturnType<typeof import('./requests/getImportPathForFile')['getImportPathForFile']>>({
17-
type: 'getImportPathForFile',
18-
args,
19-
});
16+
return await sendRequest<ReturnType<typeof import('./requests/getImportPathForFile')['getImportPathForFile']>>(
17+
'getImportPathForFile',
18+
...args
19+
);
2020
}
2121

2222
export async function getPropertiesAtLocation(
2323
...args: Parameters<typeof import('./requests/getPropertiesAtLocation.js')['getPropertiesAtLocation']>
2424
) {
25-
return await sendRequest<ReturnType<typeof import('./requests/getPropertiesAtLocation')['getPropertiesAtLocation']>>({
26-
type: 'getPropertiesAtLocation',
27-
args,
28-
});
25+
return await sendRequest<ReturnType<typeof import('./requests/getPropertiesAtLocation')['getPropertiesAtLocation']>>(
26+
'getPropertiesAtLocation',
27+
...args
28+
);
2929
}
3030

3131
export function getQuickInfoAtPosition(
3232
...args: Parameters<typeof import('./requests/getQuickInfoAtPosition.js')['getQuickInfoAtPosition']>
3333
) {
34-
return sendRequest<ReturnType<typeof import('./requests/getQuickInfoAtPosition')['getQuickInfoAtPosition']>>({
35-
type: 'getQuickInfoAtPosition',
36-
args,
37-
});
34+
return sendRequest<ReturnType<typeof import('./requests/getQuickInfoAtPosition')['getQuickInfoAtPosition']>>(
35+
'getQuickInfoAtPosition',
36+
...args
37+
);
3838
}
3939

4040
// Component Infos
4141

42-
export function getComponentProps(
43-
...args: Parameters<typeof import('./requests/componentInfos.js')['getComponentProps']>
44-
) {
45-
return sendRequest<ReturnType<typeof import('./requests/componentInfos')['getComponentProps']>>({
46-
type: 'getComponentProps',
47-
args,
48-
});
42+
export async function getComponentProps(fileName: string, componentName: string) {
43+
const server = await getBestServer(fileName);
44+
if (!server) {
45+
return;
46+
}
47+
const componentAndProps = await server.componentNamesAndProps.get(fileName);
48+
if (!componentAndProps) {
49+
return;
50+
}
51+
return componentAndProps[componentName];
4952
}
5053

5154
export function getComponentEvents(
5255
...args: Parameters<typeof import('./requests/componentInfos.js')['getComponentEvents']>
5356
) {
54-
return sendRequest<ReturnType<typeof import('./requests/componentInfos')['getComponentEvents']>>({
55-
type: 'getComponentEvents',
56-
args,
57-
});
57+
return sendRequest<ReturnType<typeof import('./requests/componentInfos')['getComponentEvents']>>(
58+
'getComponentEvents',
59+
...args
60+
);
5861
}
5962

6063
export function getTemplateContextProps(
6164
...args: Parameters<typeof import('./requests/componentInfos.js')['getTemplateContextProps']>
6265
) {
63-
return sendRequest<ReturnType<typeof import('./requests/componentInfos')['getTemplateContextProps']>>({
64-
type: 'getTemplateContextProps',
65-
args,
66-
});
66+
return sendRequest<ReturnType<typeof import('./requests/componentInfos')['getTemplateContextProps']>>(
67+
'getTemplateContextProps',
68+
...args
69+
);
6770
}
6871

69-
export function getComponentNames(
70-
...args: Parameters<typeof import('./requests/componentInfos.js')['getComponentNames']>
71-
) {
72-
return sendRequest<ReturnType<typeof import('./requests/componentInfos')['getComponentNames']>>({
73-
type: 'getComponentNames',
74-
args,
75-
});
72+
export async function getComponentNames(fileName: string) {
73+
const server = await getBestServer(fileName);
74+
if (!server) {
75+
return;
76+
}
77+
const componentAndProps = server.componentNamesAndProps.get(fileName);
78+
if (!componentAndProps) {
79+
return;
80+
}
81+
return Object.keys(componentAndProps);
7682
}
7783

7884
export function getElementAttrs(
7985
...args: Parameters<typeof import('./requests/componentInfos.js')['getElementAttrs']>
8086
) {
81-
return sendRequest<ReturnType<typeof import('./requests/componentInfos')['getElementAttrs']>>({
82-
type: 'getElementAttrs',
83-
args,
84-
});
87+
return sendRequest<ReturnType<typeof import('./requests/componentInfos')['getElementAttrs']>>(
88+
'getElementAttrs',
89+
...args
90+
);
8591
}
8692

87-
async function sendRequest<T>(request: Request) {
88-
const server = (await searchNamedPipeServerForFile(request.args[0]));
93+
async function sendRequest<T>(requestType: RequestData[1], fileName: string, ...rest: any[]) {
94+
const server = await getBestServer(fileName);
8995
if (!server) {
90-
console.warn('[Vue Named Pipe Client] No server found for', request.args[0]);
9196
return;
9297
}
93-
const res = await sendRequestWorker<T>(request, server.socket);
94-
server.socket.end();
95-
return res;
98+
return server.request<T>(requestType, fileName, ...rest);
9699
}

packages/typescript-plugin/lib/requests/componentInfos.ts

Lines changed: 14 additions & 13 deletions
Original file line numberDiff line numberDiff line change
@@ -6,8 +6,7 @@ import type { RequestContext } from './types';
66
export function getComponentProps(
77
this: RequestContext,
88
fileName: string,
9-
tag: string,
10-
requiredOnly = false
9+
tag: string
1110
) {
1211
const { typescript: ts, language, languageService, getFileId } = this;
1312
const volarFile = language.scripts.get(getFileId(fileName));
@@ -47,20 +46,23 @@ export function getComponentProps(
4746
}
4847
}
4948

50-
const result = new Map<string, { name: string, commentMarkdown: string; }>();
49+
const result = new Map<string, {
50+
name: string;
51+
required?: true;
52+
commentMarkdown?: string;
53+
}>();
5154

5255
for (const sig of componentType.getCallSignatures()) {
5356
const propParam = sig.parameters[0];
5457
if (propParam) {
5558
const propsType = checker.getTypeOfSymbolAtLocation(propParam, components.node);
5659
const props = propsType.getProperties();
5760
for (const prop of props) {
58-
if (!requiredOnly || !(prop.flags & ts.SymbolFlags.Optional)) {
59-
const name = prop.name;
60-
const commentMarkdown = generateCommentMarkdown(prop.getDocumentationComment(checker), prop.getJsDocTags());
61+
const name = prop.name;
62+
const required = !(prop.flags & ts.SymbolFlags.Optional) || undefined;
63+
const commentMarkdown = generateCommentMarkdown(prop.getDocumentationComment(checker), prop.getJsDocTags()) || undefined;
6164

62-
result.set(name, { name, commentMarkdown });
63-
}
65+
result.set(name, { name, required, commentMarkdown });
6466
}
6567
}
6668
}
@@ -75,12 +77,11 @@ export function getComponentProps(
7577
if (prop.flags & ts.SymbolFlags.Method) { // #2443
7678
continue;
7779
}
78-
if (!requiredOnly || !(prop.flags & ts.SymbolFlags.Optional)) {
79-
const name = prop.name;
80-
const commentMarkdown = generateCommentMarkdown(prop.getDocumentationComment(checker), prop.getJsDocTags());
80+
const name = prop.name;
81+
const required = !(prop.flags & ts.SymbolFlags.Optional) || undefined;
82+
const commentMarkdown = generateCommentMarkdown(prop.getDocumentationComment(checker), prop.getJsDocTags()) || undefined;
8183

82-
result.set(name, { name, commentMarkdown });
83-
}
84+
result.set(name, { name, required, commentMarkdown });
8485
}
8586
}
8687
}

0 commit comments

Comments
 (0)