Skip to content

Commit 2435873

Browse files
authored
feat(language-service): native completion experience for slot names (#5552)
1 parent a5eebe0 commit 2435873

File tree

6 files changed

+72
-12
lines changed

6 files changed

+72
-12
lines changed

packages/language-core/lib/plugins/vue-tsx.ts

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -239,9 +239,10 @@ function createTsx(
239239
});
240240

241241
return {
242+
getLang,
242243
getScriptRanges,
243244
getScriptSetupRanges,
244-
getLang,
245+
getSetupSlotsAssignName,
245246
getGeneratedScript,
246247
getGeneratedTemplate,
247248
};

packages/language-server/index.ts

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -117,6 +117,9 @@ connection.onInitialize(params => {
117117
getComponentProps(...args) {
118118
return sendTsServerRequest('_vue:getComponentProps', args);
119119
},
120+
getComponentSlots(...args) {
121+
return sendTsServerRequest('_vue:getComponentSlots', args);
122+
},
120123
getElementAttrs(...args) {
121124
return sendTsServerRequest('_vue:getElementAttrs', args);
122125
},

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

Lines changed: 23 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -349,6 +349,7 @@ export function create(
349349

350350
let version = 0;
351351
let components: string[] | undefined;
352+
let values: string[] | undefined;
352353

353354
const tasks: Promise<void>[] = [];
354355
const tagMap = new Map<string, {
@@ -375,7 +376,14 @@ export function create(
375376
return tags;
376377
},
377378
provideAttributes(tag) {
378-
return htmlDataProvider.provideAttributes(tag);
379+
let attrs = htmlDataProvider.provideAttributes(tag);
380+
if (tag === 'slot') {
381+
const nameAttr = attrs.find(attr => attr.name === 'name');
382+
if (nameAttr) {
383+
nameAttr.valueSet = 'slot';
384+
}
385+
}
386+
return attrs;
379387
},
380388
provideValues(tag, attr) {
381389
return htmlDataProvider.provideValues(tag, attr);
@@ -591,7 +599,20 @@ export function create(
591599

592600
return attributes;
593601
},
594-
provideValues: () => [],
602+
provideValues: (tag, attr) => {
603+
if (!values) {
604+
values = [];
605+
tasks.push((async () => {
606+
if (tag === 'slot' && attr === 'name') {
607+
values = await tsPluginClient?.getComponentSlots(root.fileName) ?? [];
608+
}
609+
version++;
610+
})());
611+
}
612+
return values.map(value => ({
613+
name: value,
614+
}));
615+
},
595616
},
596617
]);
597618

packages/typescript-plugin/index.ts

Lines changed: 13 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -7,6 +7,7 @@ import { getComponentDirectives } from './lib/requests/getComponentDirectives';
77
import { getComponentEvents } from './lib/requests/getComponentEvents';
88
import { getComponentNames } from './lib/requests/getComponentNames';
99
import { getComponentProps } from './lib/requests/getComponentProps';
10+
import { getComponentSlots } from './lib/requests/getComponentSlots';
1011
import { getElementAttrs } from './lib/requests/getElementAttrs';
1112
import { getElementNames } from './lib/requests/getElementNames';
1213
import { getImportPathForFile } from './lib/requests/getImportPathForFile';
@@ -109,6 +110,16 @@ export = createLanguageServicePlugin(
109110
response: getPropertiesAtLocation.apply(getRequestContext(args[0]), args),
110111
};
111112
});
113+
session.addProtocolHandler('_vue:getComponentDirectives', ({ arguments: args }) => {
114+
return {
115+
response: getComponentDirectives.apply(getRequestContext(args[0]), args),
116+
};
117+
});
118+
session.addProtocolHandler('_vue:getComponentEvents', ({ arguments: args }) => {
119+
return {
120+
response: getComponentEvents.apply(getRequestContext(args[0]), args),
121+
};
122+
});
112123
session.addProtocolHandler('_vue:getComponentNames', ({ arguments: args }) => {
113124
return {
114125
response: getComponentNames.apply(getRequestContext(args[0]), args) ?? [],
@@ -119,14 +130,9 @@ export = createLanguageServicePlugin(
119130
response: getComponentProps.apply(getRequestContext(args[0]), args),
120131
};
121132
});
122-
session.addProtocolHandler('_vue:getComponentEvents', ({ arguments: args }) => {
123-
return {
124-
response: getComponentEvents.apply(getRequestContext(args[0]), args),
125-
};
126-
});
127-
session.addProtocolHandler('_vue:getComponentDirectives', ({ arguments: args }) => {
133+
session.addProtocolHandler('_vue:getComponentSlots', ({ arguments: args }) => {
128134
return {
129-
response: getComponentDirectives.apply(getRequestContext(args[0]), args),
135+
response: getComponentSlots.apply(getRequestContext(args[0]), args),
130136
};
131137
});
132138
session.addProtocolHandler('_vue:getElementAttrs', ({ arguments: args }) => {
Lines changed: 28 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,28 @@
1+
import { tsCodegen, VueVirtualCode } from '@vue/language-core';
2+
import type { RequestContext } from './types';
3+
import { getVariableType } from './utils';
4+
5+
export function getComponentSlots(
6+
this: RequestContext,
7+
fileName: string,
8+
) {
9+
const { typescript: ts, language, languageService, asScriptId } = this;
10+
const volarFile = language.scripts.get(asScriptId(fileName));
11+
if (!(volarFile?.generated?.root instanceof VueVirtualCode)) {
12+
return;
13+
}
14+
const vueCode = volarFile.generated.root;
15+
16+
const codegen = tsCodegen.get(vueCode.sfc);
17+
if (!codegen) {
18+
return;
19+
}
20+
21+
const assignName = codegen.getSetupSlotsAssignName() ?? `__VLS_slots`;
22+
const slots = getVariableType(ts, languageService, vueCode, assignName);
23+
if (!slots) {
24+
return [];
25+
}
26+
27+
return slots.type.getProperties().map(({ name }) => name);
28+
}

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

Lines changed: 3 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -6,10 +6,11 @@ export type Requests = {
66
collectExtractProps: ToRequest<typeof import('./collectExtractProps.js')['collectExtractProps']>;
77
getImportPathForFile: ToRequest<typeof import('./getImportPathForFile.js')['getImportPathForFile']>;
88
getPropertiesAtLocation: ToRequest<typeof import('./getPropertiesAtLocation.js')['getPropertiesAtLocation']>;
9+
getComponentDirectives: ToRequest<typeof import('./getComponentDirectives.js')['getComponentDirectives']>;
10+
getComponentEvents: ToRequest<typeof import('./getComponentEvents.js')['getComponentEvents']>;
911
getComponentNames: ToRequest<typeof import('./getComponentNames.js')['getComponentNames']>;
1012
getComponentProps: ToRequest<typeof import('./getComponentProps.js')['getComponentProps']>;
11-
getComponentEvents: ToRequest<typeof import('./getComponentEvents.js')['getComponentEvents']>;
12-
getComponentDirectives: ToRequest<typeof import('./getComponentDirectives.js')['getComponentDirectives']>;
13+
getComponentSlots: ToRequest<typeof import('./getComponentSlots.js')['getComponentSlots']>;
1314
getElementAttrs: ToRequest<typeof import('./getElementAttrs.js')['getElementAttrs']>;
1415
getElementNames: ToRequest<typeof import('./getElementNames.js')['getElementNames']>;
1516
getEncodedSemanticClassifications: ToRequest<(fileName: string, span: ts.TextSpan) => ts.Classifications>;

0 commit comments

Comments
 (0)