diff --git a/extensions/vscode/syntaxes/vue.tmLanguage.json b/extensions/vscode/syntaxes/vue.tmLanguage.json
index 71109bc144..fbe30285e2 100644
--- a/extensions/vscode/syntaxes/vue.tmLanguage.json
+++ b/extensions/vscode/syntaxes/vue.tmLanguage.json
@@ -856,7 +856,7 @@
]
},
"template-tag-1": {
- "begin": "(<)(template)\\b(>)",
+ "begin": "(<)(template[a-zA-Z0-9:-]*)\\b(>)",
"beginCaptures": {
"1": {
"name": "punctuation.definition.tag.begin.html.vue"
@@ -878,7 +878,7 @@
"patterns": [
{
"begin": "\\G",
- "end": "(?=/>)|(()(template)\\b)",
+ "end": "(?=/>)|(()(template[a-zA-Z0-9:-]*)\\b)",
"endCaptures": {
"2": {
"name": "punctuation.definition.tag.begin.html.vue"
@@ -897,7 +897,7 @@
]
},
"template-tag-2": {
- "begin": "(<)(template)\\b",
+ "begin": "(<)(template[a-zA-Z0-9:-]*)\\b",
"beginCaptures": {
"1": {
"name": "punctuation.definition.tag.begin.html.vue"
@@ -916,7 +916,7 @@
"patterns": [
{
"begin": "\\G",
- "end": "(?=/>)|(()(template)\\b)",
+ "end": "(?=/>)|(()(template[a-zA-Z0-9:-]*)\\b)",
"endCaptures": {
"2": {
"name": "punctuation.definition.tag.begin.html.vue"
diff --git a/extensions/vscode/tests/__snapshots__/grammar.spec.ts.snap b/extensions/vscode/tests/__snapshots__/grammar.spec.ts.snap
index 00f1dbdb3e..c0398a7cfc 100644
--- a/extensions/vscode/tests/__snapshots__/grammar.spec.ts.snap
+++ b/extensions/vscode/tests/__snapshots__/grammar.spec.ts.snap
@@ -363,6 +363,45 @@ exports[`grammar > script-tag-in-script.vue 1`] = `
#^ source.vue"
`;
+exports[`grammar > tag-starts-with-template.vue 1`] = `
+">
+#^ source.vue punctuation.definition.tag.begin.html.vue
+# ^^^^^^^^ source.vue entity.name.tag.template.html.vue
+# ^ source.vue meta.tag-stuff punctuation.definition.tag.end.html.vue
+>
+#^ source.vue text.html.derivative
+# ^ source.vue text.html.derivative meta.template-tag.start punctuation.definition.tag.begin.html.vue
+# ^^^^^^^^^^^^ source.vue text.html.derivative meta.template-tag.start entity.name.tag.template-foo.html.vue
+# ^ source.vue text.html.derivative meta.template-tag.start punctuation.definition.tag.end.html.vue
+# ^^ source.vue text.html.derivative meta.template-tag.start meta.template-tag.end punctuation.definition.tag.begin.html.vue
+# ^^^^^^^^^^^^ source.vue text.html.derivative meta.template-tag.start meta.template-tag.end entity.name.tag.template-foo.html.vue
+# ^ source.vue text.html.derivative meta.template-tag.start punctuation.definition.tag.end.html.vue
+>
+#^ source.vue text.html.derivative
+# ^ source.vue text.html.derivative meta.template-tag.start punctuation.definition.tag.begin.html.vue
+# ^^^^^^^^^^^^ source.vue text.html.derivative meta.template-tag.start entity.name.tag.template:bar.html.vue
+# ^ source.vue text.html.derivative meta.template-tag.start punctuation.definition.tag.end.html.vue
+# ^^ source.vue text.html.derivative meta.template-tag.start meta.template-tag.end punctuation.definition.tag.begin.html.vue
+# ^^^^^^^^^^^^ source.vue text.html.derivative meta.template-tag.start meta.template-tag.end entity.name.tag.template:bar.html.vue
+# ^ source.vue text.html.derivative meta.template-tag.start punctuation.definition.tag.end.html.vue
+>
+#^ source.vue text.html.derivative
+# ^ source.vue text.html.derivative meta.template-tag.start punctuation.definition.tag.begin.html.vue
+# ^^^^^^^^^^^^ source.vue text.html.derivative meta.template-tag.start entity.name.tag.template-foo.html.vue
+# ^ source.vue text.html.derivative meta.template-tag.start meta.template-tag.end meta.tag-stuff
+# ^^ source.vue text.html.derivative meta.template-tag.start punctuation.definition.tag.end.html.vue
+>
+#^ source.vue text.html.derivative
+# ^ source.vue text.html.derivative meta.template-tag.start punctuation.definition.tag.begin.html.vue
+# ^^^^^^^^^^^^ source.vue text.html.derivative meta.template-tag.start entity.name.tag.template:bar.html.vue
+# ^ source.vue text.html.derivative meta.template-tag.start meta.template-tag.end meta.tag-stuff
+# ^^ source.vue text.html.derivative meta.template-tag.start punctuation.definition.tag.end.html.vue
+>
+#^^ source.vue punctuation.definition.tag.begin.html.vue
+# ^^^^^^^^ source.vue entity.name.tag.template.html.vue
+# ^ source.vue punctuation.definition.tag.end.html.vue"
+`;
+
exports[`grammar > template-expression.vue 1`] = `
">
#^ source.vue punctuation.definition.tag.begin.html.vue
diff --git a/extensions/vscode/tests/grammarFixtures/tag-starts-with-template.vue b/extensions/vscode/tests/grammarFixtures/tag-starts-with-template.vue
new file mode 100644
index 0000000000..a40ee5d877
--- /dev/null
+++ b/extensions/vscode/tests/grammarFixtures/tag-starts-with-template.vue
@@ -0,0 +1,6 @@
+
+
+
+
+
+
\ No newline at end of file
diff --git a/packages/language-core/lib/codegen/script/scriptSetup.ts b/packages/language-core/lib/codegen/script/scriptSetup.ts
index fb1dd9d151..5e4a8f684c 100644
--- a/packages/language-core/lib/codegen/script/scriptSetup.ts
+++ b/packages/language-core/lib/codegen/script/scriptSetup.ts
@@ -244,7 +244,15 @@ function* generateSetupFunction(
}
for (const { define } of scriptSetupRanges.templateRefs) {
if (define?.arg) {
- setupCodeModifies.push([[`<__VLS_Refs[${scriptSetup.content.slice(define.arg.start, define.arg.end)}], keyof __VLS_Refs>`], define.arg.start - 1, define.arg.start - 1]);
+ setupCodeModifies.push([
+ [
+ `<__VLS_Refs[`,
+ generateSfcBlockSection(scriptSetup, define.arg.start, define.arg.end, codeFeatures.navigation),
+ `], keyof __VLS_Refs>`
+ ],
+ define.arg.start - 1,
+ define.arg.start - 1
+ ]);
}
}
setupCodeModifies = setupCodeModifies.sort((a, b) => a[1] - b[1]);
diff --git a/packages/language-core/lib/codegen/template/element.ts b/packages/language-core/lib/codegen/template/element.ts
index 5e9b2a0c61..b8243293de 100644
--- a/packages/language-core/lib/codegen/template/element.ts
+++ b/packages/language-core/lib/codegen/template/element.ts
@@ -223,10 +223,10 @@ export function* generateComponent(
yield endOfLine;
}
- const refName = yield* generateVScope(options, ctx, node, props);
+ const [refName, offset] = yield* generateVScope(options, ctx, node, props);
if (refName) {
const varName = ctx.getInternalVariable();
- options.templateRefNames.set(refName, varName);
+ options.templateRefNames.set(refName, [varName, offset!]);
ctx.usedComponentCtxVars.add(var_defineComponentCtx);
yield `var ${varName} = {} as (Parameters[0] | null)`;
@@ -331,9 +331,9 @@ export function* generateElement(
yield endOfLine;
}
- const refName = yield* generateVScope(options, ctx, node, node.props);
+ const [refName, offset] = yield* generateVScope(options, ctx, node, node.props);
if (refName) {
- options.templateRefNames.set(refName, `__VLS_intrinsicElements['${node.tag}']`);
+ options.templateRefNames.set(refName, [`__VLS_intrinsicElements['${node.tag}']`, offset!]);
}
const slotDir = node.props.find(p => p.type === CompilerDOM.NodeTypes.DIRECTIVE && p.name === 'slot') as CompilerDOM.DirectiveNode;
@@ -360,7 +360,7 @@ function* generateVScope(
ctx: TemplateCodegenContext,
node: CompilerDOM.ElementNode,
props: (CompilerDOM.AttributeNode | CompilerDOM.DirectiveNode)[]
-): Generator {
+): Generator {
const vScope = props.find(prop => prop.type === CompilerDOM.NodeTypes.DIRECTIVE && (prop.name === 'scope' || prop.name === 'data'));
let inScope = false;
let originalConditionsNum = ctx.blockConditions.length;
@@ -384,14 +384,14 @@ function* generateVScope(
}
yield* generateElementDirectives(options, ctx, node);
- const refName = yield* generateReferencesForElements(options, ctx, node); //
+ const [refName, offset] = yield* generateReferencesForElements(options, ctx, node); //
yield* generateReferencesForScopedCssClasses(options, ctx, node);
if (inScope) {
yield `}${newLine}`;
ctx.blockConditions.length = originalConditionsNum;
}
- return refName;
+ return [refName, offset];
}
export function getCanonicalComponentName(tagText: string) {
@@ -540,7 +540,7 @@ function* generateReferencesForElements(
options: TemplateCodegenOptions,
ctx: TemplateCodegenContext,
node: CompilerDOM.ElementNode
-): Generator {
+): Generator {
for (const prop of node.props) {
if (
prop.type === CompilerDOM.NodeTypes.ATTRIBUTE
@@ -565,9 +565,10 @@ function* generateReferencesForElements(
ctx.accessExternalVariable(content, startOffset);
}
- return prop.value.content;
+ return [content, startOffset];
}
}
+ return [];
}
function* generateReferencesForScopedCssClasses(
diff --git a/packages/language-core/lib/codegen/template/index.ts b/packages/language-core/lib/codegen/template/index.ts
index 9e49256588..96ce84035b 100644
--- a/packages/language-core/lib/codegen/template/index.ts
+++ b/packages/language-core/lib/codegen/template/index.ts
@@ -5,6 +5,7 @@ import { endOfLine, newLine, wrapWith } from '../common';
import { TemplateCodegenContext, createTemplateCodegenContext } from './context';
import { getCanonicalComponentName, getPossibleOriginalComponentNames } from './element';
import { generateObjectProperty } from './objectProperty';
+import { generateStringLiteralKey } from './stringLiteralKey';
import { generateTemplateChild, getVForNode } from './templateChild';
import { generateStyleScopedClasses } from './styleScopedClasses';
@@ -16,7 +17,7 @@ export interface TemplateCodegenOptions {
scriptSetupBindingNames: Set;
scriptSetupImportComponentNames: Set;
edited: boolean;
- templateRefNames: Map;
+ templateRefNames: Map;
hasDefineSlots?: boolean;
slotsAssignName?: string;
propsAssignName?: string;
@@ -58,8 +59,13 @@ export function* generateTemplate(options: TemplateCodegenOptions): Generator {
yield `const __VLS_refs = {${newLine}`;
- for (const [name, varName] of options.templateRefNames) {
- yield `'${name}': ${varName},${newLine}`;
+ for (const [name, [varName, offset]] of options.templateRefNames) {
+ yield* generateStringLiteralKey(
+ name,
+ offset,
+ ctx.codeFeatures.navigationAndCompletion
+ )
+ yield `: ${varName},${newLine}`;
}
yield `}${endOfLine}`;
yield `declare var $refs: typeof __VLS_refs${endOfLine}`;
diff --git a/packages/language-server/tests/renaming.spec.ts b/packages/language-server/tests/renaming.spec.ts
index f2b59688d1..203724c902 100644
--- a/packages/language-server/tests/renaming.spec.ts
+++ b/packages/language-server/tests/renaming.spec.ts
@@ -871,6 +871,54 @@ describe('Renaming', async () => {
`);
});
+ it('Template Ref', async () => {
+ expect(
+ await requestRename('tsconfigProject/fixture.vue', 'vue', `
+
+
+
+
+
+ `, 'bar')
+ ).toMatchInlineSnapshot(`
+ {
+ "changes": {
+ "file://\${testWorkspacePath}/tsconfigProject/fixture.vue": [
+ {
+ "newText": "bar",
+ "range": {
+ "end": {
+ "character": 16,
+ "line": 2,
+ },
+ "start": {
+ "character": 13,
+ "line": 2,
+ },
+ },
+ },
+ {
+ "newText": "bar",
+ "range": {
+ "end": {
+ "character": 34,
+ "line": 7,
+ },
+ "start": {
+ "character": 31,
+ "line": 7,
+ },
+ },
+ },
+ ],
+ },
+ }
+ `);
+ });
+
const openedDocuments: TextDocument[] = [];
afterEach(async () => {