Skip to content

Commit

Permalink
feat(language-core): support @vue-generic (#4971)
Browse files Browse the repository at this point in the history
  • Loading branch information
KazariEX authored Nov 2, 2024
1 parent 6651dff commit 4c3c8c4
Show file tree
Hide file tree
Showing 9 changed files with 90 additions and 12 deletions.
5 changes: 5 additions & 0 deletions packages/language-core/lib/codegen/template/context.ts
Original file line number Diff line number Diff line change
Expand Up @@ -63,6 +63,10 @@ export function createTemplateCodegenContext(options: Pick<TemplateCodegenOption
errors: number;
node: CompilerDOM.CommentNode;
} | undefined;
let lastGenericComment: {
content: string;
offset: number;
} | undefined;
let variableId = 0;

const codeFeatures = new Proxy(_codeFeatures, {
Expand Down Expand Up @@ -123,6 +127,7 @@ export function createTemplateCodegenContext(options: Pick<TemplateCodegenOption
dynamicSlots,
codeFeatures,
accessExternalVariables,
lastGenericComment,
hasSlotElements,
blockConditions,
usedComponentCtxVars,
Expand Down
26 changes: 25 additions & 1 deletion packages/language-core/lib/codegen/template/element.ts
Original file line number Diff line number Diff line change
Expand Up @@ -205,7 +205,9 @@ export function* generateComponent(
yield* generateElementProps(options, ctx, node, props, false);
yield `}))${endOfLine}`;

yield `const ${var_componentInstance} = ${var_functionalComponent}(`;
yield `const ${var_componentInstance} = ${var_functionalComponent}`;
yield* generateComponentGeneric(ctx);
yield `(`;
yield* wrapWith(
startTagOffset,
startTagOffset + node.tag.length,
Expand Down Expand Up @@ -452,6 +454,28 @@ function* generateCanonicalComponentName(tagText: string, offset: number, featur
}
}

function* generateComponentGeneric(
ctx: TemplateCodegenContext
): Generator<Code> {
if (ctx.lastGenericComment) {
const { content, offset } = ctx.lastGenericComment;
yield* wrapWith(
offset,
offset + content.length,
ctx.codeFeatures.verification,
`<`,
[
content,
'template',
offset,
ctx.codeFeatures.all
],
`>`
);
}
ctx.lastGenericComment = undefined;
}

function* generateComponentSlot(
options: TemplateCodegenOptions,
ctx: TemplateCodegenContext,
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -24,6 +24,7 @@ export function* generateElementDirectives(
|| prop.name === 'bind'
|| prop.name === 'scope'
|| prop.name === 'data'
|| prop.name === 'generic'
) {
continue;
}
Expand Down
16 changes: 13 additions & 3 deletions packages/language-core/lib/codegen/template/templateChild.ts
Original file line number Diff line number Diff line change
Expand Up @@ -36,16 +36,26 @@ export function* generateTemplateChild(
): Generator<Code> {
if (prevNode?.type === CompilerDOM.NodeTypes.COMMENT) {
const commentText = prevNode.content.trim().split(' ')[0];
if (commentText.match(/^@vue-skip\b[\s\S]*/)) {
if (/^@vue-skip\b[\s\S]*/.test(commentText)) {
yield `// @vue-skip${newLine}`;
return;
}
else if (commentText.match(/^@vue-ignore\b[\s\S]*/)) {
else if (/^@vue-ignore\b[\s\S]*/.test(commentText)) {
yield* ctx.ignoreError();
}
else if (commentText.match(/^@vue-expect-error\b[\s\S]*/)) {
else if (/^@vue-expect-error\b[\s\S]*/.test(commentText)) {
yield* ctx.expectError(prevNode);
}
else {
const match = prevNode.loc.source.match(/^<!--\s*@vue-generic\b\s*\{(?<content>[^}]*)\}/);
if (match) {
const { content } = match.groups ?? {};
ctx.lastGenericComment = {
content,
offset: prevNode.loc.start.offset + match[0].indexOf(content)
};
}
}
}

const shouldInheritRootNodeAttrs = options.inheritAttrs;
Expand Down
14 changes: 13 additions & 1 deletion packages/language-core/lib/plugins/vue-template-inline-ts.ts
Original file line number Diff line number Diff line change
Expand Up @@ -19,6 +19,7 @@ const formatBrackets = {
// fix https://github.com/vuejs/language-tools/issues/2305
curly: ['0 +', '+ 0;'] as [string, string],
event: ['() => ', ';'] as [string, string],
generic: ['<', '>() => {};'] as [string, string],
};

const plugin: VueLanguagePlugin = ctx => {
Expand Down Expand Up @@ -71,7 +72,18 @@ const plugin: VueLanguagePlugin = ctx => {
return data;

function visit(node: CompilerDOM.TemplateChildNode | CompilerDOM.SimpleExpressionNode) {
if (node.type === CompilerDOM.NodeTypes.ELEMENT) {
if (node.type === CompilerDOM.NodeTypes.COMMENT) {
const match = node.loc.source.match(/^<!--\s*@vue-generic\b\s*\{(?<content>[^}]*)\}/);
if (match) {
const { content } = match.groups ?? {};
addFormatCodes(
content,
node.loc.start.offset + match[0].indexOf(content),
formatBrackets.generic
);
}
}
else if (node.type === CompilerDOM.NodeTypes.ELEMENT) {
for (const prop of node.props) {
if (prop.type !== CompilerDOM.NodeTypes.DIRECTIVE) {
continue;
Expand Down
17 changes: 10 additions & 7 deletions packages/language-service/lib/plugins/vue-directive-comments.ts
Original file line number Diff line number Diff line change
@@ -1,9 +1,11 @@
import type { CompletionItem, LanguageServicePlugin, LanguageServicePluginInstance } from '@volar/language-service';
import type * as vscode from 'vscode-languageserver-protocol';

const cmds = [
'vue-ignore',
'vue-skip',
'vue-expect-error',
['vue-ignore'],
['vue-skip'],
['vue-expect-error'],
['vue-generic', 'vue-generic {$1}'],
];

const directiveCommentReg = /<!--\s*@/;
Expand Down Expand Up @@ -34,17 +36,17 @@ export function create(): LanguageServicePlugin {
const remainText = line.substring(startIndex);
const result: CompletionItem[] = [];

for (const cmd of cmds) {
for (const [label, text = label] of cmds) {
let match = true;
for (let i = 0; i < remainText.length; i++) {
if (remainText[i] !== cmd[i]) {
if (remainText[i] !== label[i]) {
match = false;
break;
}
}
if (match) {
result.push({
label: '@' + cmd,
label: '@' + label,
textEdit: {
range: {
start: {
Expand All @@ -53,8 +55,9 @@ export function create(): LanguageServicePlugin {
},
end: position,
},
newText: '@' + cmd,
newText: '@' + text,
},
insertTextFormat: 2 satisfies typeof vscode.InsertTextFormat.Snippet
});
}
}
Expand Down
1 change: 1 addition & 0 deletions test-workspace/tsc/passedFixtures/vue2/tsconfig.json
Original file line number Diff line number Diff line change
Expand Up @@ -41,5 +41,6 @@
"../vue3/slots",
"../vue3/templateRef",
"../vue3/templateRef_native",
"../vue3/v-generic"
]
}
5 changes: 5 additions & 0 deletions test-workspace/tsc/passedFixtures/vue3/v-generic/comp.vue
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
<script setup lang="ts" generic="T extends string | number, K = any">
defineEmits<{
foo: [bar: T, baz: K]
}>();
</script>
17 changes: 17 additions & 0 deletions test-workspace/tsc/passedFixtures/vue3/v-generic/main.vue
Original file line number Diff line number Diff line change
@@ -0,0 +1,17 @@
<script setup lang="ts">
import { exactType } from '../../shared';
import Comp from './comp.vue';
</script>

<template>
<!-- @vue-generic {string} -->
<Comp @foo="(bar) => exactType(bar, {} as string)" />
<!-- @vue-generic {number, boolean} -->
<Comp @foo="(bar, baz) => (
exactType(bar, {} as number),
exactType(baz, {} as boolean)
)" />
<!-- @vue-expect-error -->
<!-- @vue-generic {boolean} -->
<Comp />
</template>

0 comments on commit 4c3c8c4

Please sign in to comment.