Skip to content

Commit 69be985

Browse files
authored
feat(language-core): navigation support for template-ref (#4726)
1 parent 63fca0b commit 69be985

File tree

4 files changed

+76
-13
lines changed

4 files changed

+76
-13
lines changed

packages/language-core/lib/codegen/script/scriptSetup.ts

+9-1
Original file line numberDiff line numberDiff line change
@@ -244,7 +244,15 @@ function* generateSetupFunction(
244244
}
245245
for (const { define } of scriptSetupRanges.templateRefs) {
246246
if (define?.arg) {
247-
setupCodeModifies.push([[`<__VLS_Refs[${scriptSetup.content.slice(define.arg.start, define.arg.end)}], keyof __VLS_Refs>`], define.arg.start - 1, define.arg.start - 1]);
247+
setupCodeModifies.push([
248+
[
249+
`<__VLS_Refs[`,
250+
generateSfcBlockSection(scriptSetup, define.arg.start, define.arg.end, codeFeatures.navigation),
251+
`], keyof __VLS_Refs>`
252+
],
253+
define.arg.start - 1,
254+
define.arg.start - 1
255+
]);
248256
}
249257
}
250258
setupCodeModifies = setupCodeModifies.sort((a, b) => a[1] - b[1]);

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

+10-9
Original file line numberDiff line numberDiff line change
@@ -223,10 +223,10 @@ export function* generateComponent(
223223
yield endOfLine;
224224
}
225225

226-
const refName = yield* generateVScope(options, ctx, node, props);
226+
const [refName, offset] = yield* generateVScope(options, ctx, node, props);
227227
if (refName) {
228228
const varName = ctx.getInternalVariable();
229-
options.templateRefNames.set(refName, varName);
229+
options.templateRefNames.set(refName, [varName, offset!]);
230230
ctx.usedComponentCtxVars.add(var_defineComponentCtx);
231231

232232
yield `// @ts-ignore${newLine}`;
@@ -332,9 +332,9 @@ export function* generateElement(
332332
yield endOfLine;
333333
}
334334

335-
const refName = yield* generateVScope(options, ctx, node, node.props);
335+
const [refName, offset] = yield* generateVScope(options, ctx, node, node.props);
336336
if (refName) {
337-
options.templateRefNames.set(refName, `__VLS_intrinsicElements['${node.tag}']`);
337+
options.templateRefNames.set(refName, [`__VLS_intrinsicElements['${node.tag}']`, offset!]);
338338
}
339339

340340
const slotDir = node.props.find(p => p.type === CompilerDOM.NodeTypes.DIRECTIVE && p.name === 'slot') as CompilerDOM.DirectiveNode;
@@ -361,7 +361,7 @@ function* generateVScope(
361361
ctx: TemplateCodegenContext,
362362
node: CompilerDOM.ElementNode,
363363
props: (CompilerDOM.AttributeNode | CompilerDOM.DirectiveNode)[]
364-
): Generator<Code> {
364+
): Generator<Code, [refName?: string, offset?: number]> {
365365
const vScope = props.find(prop => prop.type === CompilerDOM.NodeTypes.DIRECTIVE && (prop.name === 'scope' || prop.name === 'data'));
366366
let inScope = false;
367367
let originalConditionsNum = ctx.blockConditions.length;
@@ -385,14 +385,14 @@ function* generateVScope(
385385
}
386386

387387
yield* generateElementDirectives(options, ctx, node);
388-
const refName = yield* generateReferencesForElements(options, ctx, node); // <el ref="foo" />
388+
const [refName, offset] = yield* generateReferencesForElements(options, ctx, node); // <el ref="foo" />
389389
yield* generateReferencesForScopedCssClasses(options, ctx, node);
390390

391391
if (inScope) {
392392
yield `}${newLine}`;
393393
ctx.blockConditions.length = originalConditionsNum;
394394
}
395-
return refName;
395+
return [refName, offset];
396396
}
397397

398398
export function getCanonicalComponentName(tagText: string) {
@@ -541,7 +541,7 @@ function* generateReferencesForElements(
541541
options: TemplateCodegenOptions,
542542
ctx: TemplateCodegenContext,
543543
node: CompilerDOM.ElementNode
544-
): Generator<Code> {
544+
): Generator<Code, [refName: string, offset: number] | []> {
545545
for (const prop of node.props) {
546546
if (
547547
prop.type === CompilerDOM.NodeTypes.ATTRIBUTE
@@ -566,9 +566,10 @@ function* generateReferencesForElements(
566566
ctx.accessExternalVariable(content, startOffset);
567567
}
568568

569-
return prop.value.content;
569+
return [content, startOffset];
570570
}
571571
}
572+
return [];
572573
}
573574

574575
function* generateReferencesForScopedCssClasses(

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

+9-3
Original file line numberDiff line numberDiff line change
@@ -5,6 +5,7 @@ import { endOfLine, newLine, wrapWith } from '../common';
55
import { TemplateCodegenContext, createTemplateCodegenContext } from './context';
66
import { getCanonicalComponentName, getPossibleOriginalComponentNames } from './element';
77
import { generateObjectProperty } from './objectProperty';
8+
import { generateStringLiteralKey } from './stringLiteralKey';
89
import { generateTemplateChild, getVForNode } from './templateChild';
910
import { generateStyleScopedClasses } from './styleScopedClasses';
1011

@@ -16,7 +17,7 @@ export interface TemplateCodegenOptions {
1617
scriptSetupBindingNames: Set<string>;
1718
scriptSetupImportComponentNames: Set<string>;
1819
edited: boolean;
19-
templateRefNames: Map<string, string>;
20+
templateRefNames: Map<string, [varName: string, offset: number]>;
2021
hasDefineSlots?: boolean;
2122
slotsAssignName?: string;
2223
propsAssignName?: string;
@@ -58,8 +59,13 @@ export function* generateTemplate(options: TemplateCodegenOptions): Generator<Co
5859

5960
function* generateRefs(): Generator<Code> {
6061
yield `const __VLS_refs = {${newLine}`;
61-
for (const [name, varName] of options.templateRefNames) {
62-
yield `'${name}': ${varName}!,${newLine}`;
62+
for (const [name, [varName, offset]] of options.templateRefNames) {
63+
yield* generateStringLiteralKey(
64+
name,
65+
offset,
66+
ctx.codeFeatures.navigationAndCompletion
67+
)
68+
yield `: ${varName}!,${newLine}`;
6369
}
6470
yield `}${endOfLine}`;
6571
yield `declare var $refs: typeof __VLS_refs${endOfLine}`;

packages/language-server/tests/renaming.spec.ts

+48
Original file line numberDiff line numberDiff line change
@@ -871,6 +871,54 @@ describe('Renaming', async () => {
871871
`);
872872
});
873873

874+
it('Template Ref', async () => {
875+
expect(
876+
await requestRename('tsconfigProject/fixture.vue', 'vue', `
877+
<template>
878+
<a ref="foo"></a>
879+
</template>
880+
881+
<script lang="ts" setup>
882+
import { useTemplateRef } from 'vue';
883+
const el = useTemplateRef('foo|');
884+
</script>
885+
`, 'bar')
886+
).toMatchInlineSnapshot(`
887+
{
888+
"changes": {
889+
"file://\${testWorkspacePath}/tsconfigProject/fixture.vue": [
890+
{
891+
"newText": "bar",
892+
"range": {
893+
"end": {
894+
"character": 16,
895+
"line": 2,
896+
},
897+
"start": {
898+
"character": 13,
899+
"line": 2,
900+
},
901+
},
902+
},
903+
{
904+
"newText": "bar",
905+
"range": {
906+
"end": {
907+
"character": 34,
908+
"line": 7,
909+
},
910+
"start": {
911+
"character": 31,
912+
"line": 7,
913+
},
914+
},
915+
},
916+
],
917+
},
918+
}
919+
`);
920+
});
921+
874922
const openedDocuments: TextDocument[] = [];
875923

876924
afterEach(async () => {

0 commit comments

Comments
 (0)