Skip to content

Commit 635f4fe

Browse files
feat(typescript-plugin): add emit JSDoc support (#4365)
1 parent 62c65ae commit 635f4fe

File tree

3 files changed

+51
-4
lines changed

3 files changed

+51
-4
lines changed

packages/language-core/lib/codegen/template/element.ts

Lines changed: 4 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -34,6 +34,7 @@ export function* generateComponent(
3434
const var_originalComponent = matchImportName ?? ctx.getInternalVariable();
3535
const var_functionalComponent = ctx.getInternalVariable();
3636
const var_componentInstance = ctx.getInternalVariable();
37+
const var_componentEmit = ctx.getInternalVariable();
3738
const var_componentEvents = ctx.getInternalVariable();
3839
const var_defineComponentCtx = ctx.getInternalVariable();
3940
const isComponentTag = node.tag.toLowerCase() === 'component';
@@ -238,7 +239,7 @@ export function* generateComponent(
238239
yield* generateVScope(options, ctx, node, props);
239240

240241
ctx.usedComponentCtxVars.add(componentCtxVar);
241-
yield* generateElementEvents(options, ctx, node, var_functionalComponent, var_componentInstance, var_componentEvents, () => usedComponentEventsVar = true);
242+
yield* generateElementEvents(options, ctx, node, var_functionalComponent, var_componentInstance, var_componentEmit, var_componentEvents, () => usedComponentEventsVar = true);
242243

243244
const slotDir = node.props.find(p => p.type === CompilerDOM.NodeTypes.DIRECTIVE && p.name === 'slot') as CompilerDOM.DirectiveNode;
244245
if (slotDir) {
@@ -252,7 +253,8 @@ export function* generateComponent(
252253
yield `const ${componentCtxVar} = __VLS_pickFunctionalComponentCtx(${var_originalComponent}, ${var_componentInstance})!${endOfLine}`;
253254
}
254255
if (usedComponentEventsVar) {
255-
yield `let ${var_componentEvents}!: __VLS_NormalizeEmits<typeof ${componentCtxVar}.emit>${endOfLine}`;
256+
yield `let ${var_componentEmit}!: typeof ${componentCtxVar}.emit${endOfLine}`;
257+
yield `let ${var_componentEvents}!: __VLS_NormalizeEmits<typeof ${var_componentEmit}>${endOfLine}`;
256258
}
257259
}
258260

packages/language-core/lib/codegen/template/elementEvents.ts

Lines changed: 6 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -16,6 +16,7 @@ export function* generateElementEvents(
1616
node: CompilerDOM.ElementNode,
1717
componentVar: string,
1818
componentInstanceVar: string,
19+
emitVar: string,
1920
eventsVar: string,
2021
used: () => void,
2122
): Generator<Code> {
@@ -27,7 +28,9 @@ export function* generateElementEvents(
2728
) {
2829
used();
2930
const eventVar = ctx.getInternalVariable();
30-
yield `let ${eventVar} = { '${prop.arg.loc.source}': __VLS_pickEvent(`;
31+
yield `let ${eventVar} = {${newLine}`;
32+
yield `/**__VLS_emit,${emitVar},${prop.arg.loc.source}*/${newLine}`;
33+
yield `'${prop.arg.loc.source}': __VLS_pickEvent(`;
3134
yield `${eventsVar}['${prop.arg.loc.source}'], `;
3235
yield `({} as __VLS_FunctionalComponentProps<typeof ${componentVar}, typeof ${componentInstanceVar}>)`;
3336
yield* generateEventArg(options, ctx, prop.arg, true, false);
@@ -59,7 +62,8 @@ export function* generateElementEvents(
5962
}
6063
yield `: `;
6164
yield* generateEventExpression(options, ctx, prop);
62-
yield ` }${endOfLine}`;
65+
yield newLine;
66+
yield `}${endOfLine}`;
6367
}
6468
else if (
6569
prop.type === CompilerDOM.NodeTypes.DIRECTIVE

packages/typescript-plugin/lib/common.ts

Lines changed: 41 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -16,6 +16,7 @@ export function decorateLanguageServiceForVue(
1616
getCompletionEntryDetails,
1717
getCodeFixesAtPosition,
1818
getEncodedSemanticClassifications,
19+
getQuickInfoAtPosition,
1920
} = languageService;
2021

2122
languageService.getCompletionsAtPosition = (fileName, position, options, formattingSettings) => {
@@ -105,6 +106,46 @@ export function decorateLanguageServiceForVue(
105106
result = result.filter(entry => entry.description.indexOf('__VLS_') === -1);
106107
return result;
107108
};
109+
languageService.getQuickInfoAtPosition = (...args) => {
110+
const result = getQuickInfoAtPosition(...args);
111+
if (result && result.documentation?.length === 1 && result.documentation[0].text.startsWith('__VLS_emit,')) {
112+
const [_, emitVarName, eventName] = result.documentation[0].text.split(',');
113+
const program = languageService.getProgram()!;
114+
const typeChecker = program.getTypeChecker();
115+
const sourceFile = program.getSourceFile(args[0]);
116+
117+
result.documentation = undefined;
118+
119+
let symbolNode: ts.Identifier | undefined;
120+
121+
sourceFile?.forEachChild(function visit(node) {
122+
if (ts.isIdentifier(node) && node.text === emitVarName) {
123+
symbolNode = node;
124+
}
125+
if (symbolNode) {
126+
return;
127+
}
128+
ts.forEachChild(node, visit);
129+
});
130+
131+
if (symbolNode) {
132+
const emitSymbol = typeChecker.getSymbolAtLocation(symbolNode);
133+
if (emitSymbol) {
134+
const type = typeChecker.getTypeOfSymbolAtLocation(emitSymbol, symbolNode);
135+
const calls = type.getCallSignatures();
136+
for (const call of calls) {
137+
const callEventName = (typeChecker.getTypeOfSymbolAtLocation(call.parameters[0], symbolNode) as ts.StringLiteralType).value;
138+
call.getJsDocTags();
139+
if (callEventName === eventName) {
140+
result.documentation = call.getDocumentationComment(typeChecker);
141+
result.tags = call.getJsDocTags();
142+
}
143+
}
144+
}
145+
}
146+
}
147+
return result;
148+
};
108149
if (isTsPlugin) {
109150
languageService.getEncodedSemanticClassifications = (fileName, span, format) => {
110151
const result = getEncodedSemanticClassifications(fileName, span, format);

0 commit comments

Comments
 (0)