Skip to content

Commit a66f233

Browse files
refactor(typescript-plugin): explicitly request parameters (#5623)
1 parent 08a5af5 commit a66f233

15 files changed

+204
-221
lines changed

packages/typescript-plugin/index.ts

Lines changed: 82 additions & 39 deletions
Original file line numberDiff line numberDiff line change
@@ -2,6 +2,7 @@ import { createLanguageServicePlugin } from '@volar/typescript/lib/quickstart/cr
22
import * as vue from '@vue/language-core';
33
import type * as ts from 'typescript';
44
import { createVueLanguageServiceProxy } from './lib/common';
5+
import type { Requests } from './lib/requests';
56
import { collectExtractProps } from './lib/requests/collectExtractProps';
67
import { getComponentDirectives } from './lib/requests/getComponentDirectives';
78
import { getComponentEvents } from './lib/requests/getComponentEvents';
@@ -12,7 +13,6 @@ import { getElementAttrs } from './lib/requests/getElementAttrs';
1213
import { getElementNames } from './lib/requests/getElementNames';
1314
import { getImportPathForFile } from './lib/requests/getImportPathForFile';
1415
import { getPropertiesAtLocation } from './lib/requests/getPropertiesAtLocation';
15-
import type { RequestContext } from './lib/requests/types';
1616

1717
const windowsPathReg = /\\/g;
1818
const project2Service = new WeakMap<
@@ -43,6 +43,7 @@ export = createLanguageServicePlugin(
4343
language,
4444
info.languageService,
4545
vueOptions,
46+
fileName => fileName,
4647
);
4748

4849
// #3963
@@ -85,89 +86,131 @@ export = createLanguageServicePlugin(
8586
return;
8687
}
8788

88-
session.addProtocolHandler('_vue:projectInfo', ({ arguments: args }) => {
89-
return (session as any).handlers.get('projectInfo')?.({ arguments: args });
89+
session.addProtocolHandler('_vue:projectInfo', request => {
90+
return (session as any).handlers.get('projectInfo')?.(request);
9091
});
91-
session.addProtocolHandler('_vue:documentHighlights-full', ({ arguments: args }) => {
92-
return (session as any).handlers.get('documentHighlights-full')?.({ arguments: args });
92+
session.addProtocolHandler('_vue:documentHighlights-full', request => {
93+
return (session as any).handlers.get('documentHighlights-full')?.(request);
9394
});
94-
session.addProtocolHandler('_vue:encodedSemanticClassifications-full', ({ arguments: args }) => {
95-
return (session as any).handlers.get('encodedSemanticClassifications-full')?.({ arguments: args });
95+
session.addProtocolHandler('_vue:encodedSemanticClassifications-full', request => {
96+
return (session as any).handlers.get('encodedSemanticClassifications-full')?.(request);
9697
});
97-
session.addProtocolHandler('_vue:quickinfo', ({ arguments: args }) => {
98-
return (session as any).handlers.get('quickinfo')?.({ arguments: args });
98+
session.addProtocolHandler('_vue:quickinfo', request => {
99+
return (session as any).handlers.get('quickinfo')?.(request);
99100
});
100-
session.addProtocolHandler('_vue:collectExtractProps', ({ arguments: args }) => {
101+
session.addProtocolHandler(
102+
'_vue:collectExtractProps',
103+
request => {
104+
const [fileName, templateCodeRange]: Parameters<Requests['collectExtractProps']> = request.arguments;
105+
const { language, program, sourceScript, virtualCode } = getLanguageServiceAndVirtualCode(fileName);
106+
return {
107+
response: collectExtractProps(ts, language, program, sourceScript, virtualCode, templateCodeRange),
108+
};
109+
},
110+
);
111+
session.addProtocolHandler('_vue:getImportPathForFile', request => {
112+
const [fileName, incomingFileName, preferences]: Parameters<Requests['getImportPathForFile']> =
113+
request.arguments;
114+
const { languageServiceHost, program } = getLanguageService(fileName);
101115
return {
102-
response: collectExtractProps.apply(getRequestContext(args[0]), args),
116+
response: getImportPathForFile(ts, languageServiceHost, program, fileName, incomingFileName, preferences),
103117
};
104118
});
105-
session.addProtocolHandler('_vue:getImportPathForFile', ({ arguments: args }) => {
119+
session.addProtocolHandler('_vue:getPropertiesAtLocation', request => {
120+
const [fileName, position]: Parameters<Requests['getPropertiesAtLocation']> = request.arguments;
121+
const { language, program, sourceScript, virtualCode } = getLanguageServiceAndVirtualCode(fileName);
106122
return {
107-
response: getImportPathForFile.apply(getRequestContext(args[0]), args),
123+
response: getPropertiesAtLocation(ts, language, program, sourceScript, virtualCode, position),
108124
};
109125
});
110-
session.addProtocolHandler('_vue:getPropertiesAtLocation', ({ arguments: args }) => {
126+
session.addProtocolHandler('_vue:getComponentDirectives', request => {
127+
const [fileName]: Parameters<Requests['getComponentDirectives']> = request.arguments;
128+
const { program } = getLanguageService(fileName);
111129
return {
112-
response: getPropertiesAtLocation.apply(getRequestContext(args[0]), args),
130+
response: getComponentDirectives(ts, program, fileName),
113131
};
114132
});
115-
session.addProtocolHandler('_vue:getComponentDirectives', ({ arguments: args }) => {
133+
session.addProtocolHandler('_vue:getComponentEvents', request => {
134+
const [fileName, tag]: Parameters<Requests['getComponentEvents']> = request.arguments;
135+
const { program } = getLanguageService(fileName);
116136
return {
117-
response: getComponentDirectives.apply(getRequestContext(args[0]), args),
137+
response: getComponentEvents(ts, program, fileName, tag),
118138
};
119139
});
120-
session.addProtocolHandler('_vue:getComponentEvents', ({ arguments: args }) => {
140+
session.addProtocolHandler('_vue:getComponentNames', request => {
141+
const [fileName]: Parameters<Requests['getComponentNames']> = request.arguments;
142+
const { program } = getLanguageService(fileName);
121143
return {
122-
response: getComponentEvents.apply(getRequestContext(args[0]), args),
144+
response: getComponentNames(ts, program, fileName),
123145
};
124146
});
125-
session.addProtocolHandler('_vue:getComponentNames', ({ arguments: args }) => {
147+
session.addProtocolHandler('_vue:getComponentProps', request => {
148+
const [fileName, tag]: Parameters<Requests['getComponentProps']> = request.arguments;
149+
const { program } = getLanguageService(fileName);
126150
return {
127-
response: getComponentNames.apply(getRequestContext(args[0]), args) ?? [],
151+
response: getComponentProps(ts, program, fileName, tag),
128152
};
129153
});
130-
session.addProtocolHandler('_vue:getComponentProps', ({ arguments: args }) => {
154+
session.addProtocolHandler('_vue:getComponentSlots', request => {
155+
const [fileName]: Parameters<Requests['getComponentSlots']> = request.arguments;
156+
const { program, virtualCode } = getLanguageServiceAndVirtualCode(fileName);
131157
return {
132-
response: getComponentProps.apply(getRequestContext(args[0]), args),
158+
response: getComponentSlots(ts, program, virtualCode),
133159
};
134160
});
135-
session.addProtocolHandler('_vue:getComponentSlots', ({ arguments: args }) => {
161+
session.addProtocolHandler('_vue:getElementAttrs', request => {
162+
const [fileName, tag]: Parameters<Requests['getElementAttrs']> = request.arguments;
163+
const { program } = getLanguageService(fileName);
136164
return {
137-
response: getComponentSlots.apply(getRequestContext(args[0]), args),
165+
response: getElementAttrs(ts, program, fileName, tag),
138166
};
139167
});
140-
session.addProtocolHandler('_vue:getElementAttrs', ({ arguments: args }) => {
168+
session.addProtocolHandler('_vue:getElementNames', request => {
169+
const [fileName]: Parameters<Requests['getElementNames']> = request.arguments;
170+
const { program } = getLanguageService(fileName);
141171
return {
142-
response: getElementAttrs.apply(getRequestContext(args[0]), args),
143-
};
144-
});
145-
session.addProtocolHandler('_vue:getElementNames', ({ arguments: args }) => {
146-
return {
147-
response: getElementNames.apply(getRequestContext(args[0]), args),
172+
response: getElementNames(ts, program, fileName),
148173
};
149174
});
150175

151176
projectService.logger.info('Vue specific commands are successfully added.');
152177
}
153178

154-
function getRequestContext(fileName: string): RequestContext {
155-
const fileAndProject = (info.session as any).getFileAndProject({
179+
function getLanguageServiceAndVirtualCode(fileName: string) {
180+
const service = getLanguageService(fileName);
181+
const sourceScript = service?.language.scripts.get(fileName);
182+
if (!sourceScript) {
183+
throw new Error('No source script found for file: ' + fileName);
184+
}
185+
const virtualCode = sourceScript.generated?.root;
186+
if (!(virtualCode instanceof vue.VueVirtualCode)) {
187+
throw new Error('No virtual code found for file: ' + fileName);
188+
}
189+
return {
190+
...service,
191+
sourceScript,
192+
virtualCode,
193+
};
194+
}
195+
196+
function getLanguageService(fileName: string) {
197+
const { project } = (info.session as any).getFileAndProject({
156198
file: fileName,
157199
projectFileName: undefined,
158200
}) as {
159201
file: ts.server.NormalizedPath;
160202
project: ts.server.Project;
161203
};
162-
const service = project2Service.get(fileAndProject.project);
204+
const service = project2Service.get(project);
163205
if (!service) {
164-
throw 'No RequestContext';
206+
throw new Error('No vue service for project: ' + project.getProjectName());
165207
}
208+
const [language, languageServiceHost, languageService] = service;
166209
return {
167210
typescript: ts,
168-
languageService: service[2],
169-
languageServiceHost: service[1],
170-
language: service[0],
211+
program: languageService.getProgram()!,
212+
languageServiceHost,
213+
language,
171214
};
172215
}
173216
},

packages/typescript-plugin/lib/common.ts

Lines changed: 15 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -4,23 +4,24 @@ import type * as ts from 'typescript';
44

55
const windowsPathReg = /\\/g;
66

7-
export function createVueLanguageServiceProxy(
7+
export function createVueLanguageServiceProxy<T>(
88
ts: typeof import('typescript'),
9-
language: Language<string>,
9+
language: Language<T>,
1010
languageService: ts.LanguageService,
1111
vueOptions: VueCompilerOptions,
12+
asScriptId: (fileName: string) => T,
1213
) {
1314
const proxyCache = new Map<string | symbol, Function | undefined>();
1415
const getProxyMethod = (target: ts.LanguageService, p: string | symbol): Function | undefined => {
1516
switch (p) {
1617
case 'getCompletionsAtPosition':
17-
return getCompletionsAtPosition(ts, language, vueOptions, target[p]);
18+
return getCompletionsAtPosition(ts, language, asScriptId, vueOptions, target[p]);
1819
case 'getCompletionEntryDetails':
1920
return getCompletionEntryDetails(language, target[p]);
2021
case 'getCodeFixesAtPosition':
2122
return getCodeFixesAtPosition(target[p]);
2223
case 'getDefinitionAndBoundSpan':
23-
return getDefinitionAndBoundSpan(ts, language, languageService, vueOptions, target[p]);
24+
return getDefinitionAndBoundSpan(ts, language, asScriptId, languageService, vueOptions, target[p]);
2425
}
2526
};
2627

@@ -43,9 +44,10 @@ export function createVueLanguageServiceProxy(
4344
});
4445
}
4546

46-
function getCompletionsAtPosition(
47+
function getCompletionsAtPosition<T>(
4748
ts: typeof import('typescript'),
48-
language: Language<string>,
49+
language: Language<T>,
50+
asScriptId: (fileName: string) => T,
4951
vueOptions: VueCompilerOptions,
5052
getCompletionsAtPosition: ts.LanguageService['getCompletionsAtPosition'],
5153
): ts.LanguageService['getCompletionsAtPosition'] {
@@ -61,7 +63,7 @@ function getCompletionsAtPosition(
6163
);
6264

6365
// filter global variables in template and styles
64-
const sourceScript = language.scripts.get(fileName);
66+
const sourceScript = language.scripts.get(asScriptId(fileName));
6567
const root = sourceScript?.generated?.root;
6668
if (root instanceof VueVirtualCode) {
6769
const blocks = [
@@ -129,8 +131,8 @@ function getCompletionsAtPosition(
129131
};
130132
}
131133

132-
function getCompletionEntryDetails(
133-
language: Language<string>,
134+
function getCompletionEntryDetails<T>(
135+
language: Language<T>,
134136
getCompletionEntryDetails: ts.LanguageService['getCompletionEntryDetails'],
135137
): ts.LanguageService['getCompletionEntryDetails'] {
136138
return (...args) => {
@@ -187,9 +189,10 @@ function getCodeFixesAtPosition(
187189
};
188190
}
189191

190-
function getDefinitionAndBoundSpan(
192+
function getDefinitionAndBoundSpan<T>(
191193
ts: typeof import('typescript'),
192-
language: Language<string>,
194+
language: Language<T>,
195+
asScriptId: (fileName: string) => T,
193196
languageService: ts.LanguageService,
194197
vueOptions: VueCompilerOptions,
195198
getDefinitionAndBoundSpan: ts.LanguageService['getDefinitionAndBoundSpan'],
@@ -201,7 +204,7 @@ function getDefinitionAndBoundSpan(
201204
}
202205

203206
const program = languageService.getProgram()!;
204-
const sourceScript = language.scripts.get(fileName);
207+
const sourceScript = language.scripts.get(asScriptId(fileName));
205208
if (!sourceScript?.generated) {
206209
return result;
207210
}

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

Lines changed: 11 additions & 17 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,5 @@
1-
import { isSemanticTokensEnabled, VueVirtualCode } from '@vue/language-core';
2-
import type { RequestContext } from './types';
1+
import { isSemanticTokensEnabled, type Language, type SourceScript, type VueVirtualCode } from '@vue/language-core';
2+
import type * as ts from 'typescript';
33

44
interface ExtractPropsInfo {
55
name: string;
@@ -8,25 +8,19 @@ interface ExtractPropsInfo {
88
}
99

1010
export function collectExtractProps(
11-
this: RequestContext,
12-
fileName: string,
11+
ts: typeof import('typescript'),
12+
language: Language,
13+
program: ts.Program,
14+
sourceScript: SourceScript,
15+
virtualCode: VueVirtualCode,
1316
templateCodeRange: [number, number],
1417
): ExtractPropsInfo[] {
15-
const { typescript: ts, languageService, language } = this;
16-
17-
const sourceScript = language.scripts.get(fileName);
18-
const root = sourceScript?.generated?.root;
19-
if (!sourceScript?.generated || !(root instanceof VueVirtualCode)) {
20-
return [];
21-
}
22-
2318
const result = new Map<string, ExtractPropsInfo>();
24-
const program = languageService.getProgram()!;
25-
const sourceFile = program.getSourceFile(fileName)!;
19+
const sourceFile = program.getSourceFile(virtualCode.fileName)!;
2620
const checker = program.getTypeChecker();
27-
const script = sourceScript.generated.languagePlugin.typescript?.getServiceScript(root);
28-
const maps = script ? [...language.maps.forEach(script.code)].map(([, map]) => map) : [];
29-
const { sfc } = root;
21+
const serviceScript = sourceScript.generated!.languagePlugin.typescript?.getServiceScript(virtualCode);
22+
const maps = serviceScript ? [...language.maps.forEach(serviceScript.code)].map(([, map]) => map) : [];
23+
const { sfc } = virtualCode;
3024

3125
sourceFile.forEachChild(function visit(node) {
3226
if (

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

Lines changed: 4 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,4 @@
1-
import { VueVirtualCode } from '@vue/language-core';
2-
import type { RequestContext } from './types';
1+
import type * as ts from 'typescript';
32
import { getVariableType } from './utils';
43

54
const builtInDirectives = new Set([
@@ -12,18 +11,11 @@ const builtInDirectives = new Set([
1211
]);
1312

1413
export function getComponentDirectives(
15-
this: RequestContext,
14+
ts: typeof import('typescript'),
15+
program: ts.Program,
1616
fileName: string,
1717
): string[] {
18-
const { typescript: ts, language, languageService } = this;
19-
20-
const sourceScript = language.scripts.get(fileName);
21-
const root = sourceScript?.generated?.root;
22-
if (!sourceScript?.generated || !(root instanceof VueVirtualCode)) {
23-
return [];
24-
}
25-
26-
const directives = getVariableType(ts, languageService, root, '__VLS_directives');
18+
const directives = getVariableType(ts, program, fileName, '__VLS_directives');
2719
if (!directives) {
2820
return [];
2921
}

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

Lines changed: 5 additions & 14 deletions
Original file line numberDiff line numberDiff line change
@@ -1,28 +1,19 @@
1-
import { VueVirtualCode } from '@vue/language-core';
2-
import type { RequestContext } from './types';
1+
import type * as ts from 'typescript';
32
import { getComponentType, getVariableType } from './utils';
43

54
export function getComponentEvents(
6-
this: RequestContext,
5+
ts: typeof import('typescript'),
6+
program: ts.Program,
77
fileName: string,
88
tag: string,
99
): string[] {
10-
const { typescript: ts, language, languageService } = this;
11-
12-
const sourceScript = language.scripts.get(fileName);
13-
const root = sourceScript?.generated?.root;
14-
if (!sourceScript?.generated || !(root instanceof VueVirtualCode)) {
15-
return [];
16-
}
17-
18-
const program = languageService.getProgram()!;
1910
const checker = program.getTypeChecker();
20-
const components = getVariableType(ts, languageService, root, '__VLS_components');
11+
const components = getVariableType(ts, program, fileName, '__VLS_components');
2112
if (!components) {
2213
return [];
2314
}
2415

25-
const componentType = getComponentType(ts, languageService, root, components, fileName, tag);
16+
const componentType = getComponentType(ts, program, fileName, components, tag);
2617
if (!componentType) {
2718
return [];
2819
}

0 commit comments

Comments
 (0)