diff --git a/extensions/vscode/package.json b/extensions/vscode/package.json index 4e9ad2e412..6526cbbd80 100644 --- a/extensions/vscode/package.json +++ b/extensions/vscode/package.json @@ -427,6 +427,11 @@ "default": false, "description": "Show inlay hints for component options wrapper for type support." }, + "vue.inlayHints.vbindShorthand": { + "type": "boolean", + "default": false, + "description": "Show inlay hints for v-bind shorthand." + }, "vue.format.template.initialIndent": { "type": "boolean", "default": true diff --git a/packages/language-core/lib/generators/script.ts b/packages/language-core/lib/generators/script.ts index 896764629a..1be6a109ca 100644 --- a/packages/language-core/lib/generators/script.ts +++ b/packages/language-core/lib/generators/script.ts @@ -1098,7 +1098,7 @@ type __VLS_PrettifyGlobal = { [K in keyof T]: T[K]; } & {}; classNameWithDot.substring(1), 'style_' + styleIndex, offset + 1, - disableAllFeatures({ __combineLastMappping: true }), + disableAllFeatures({ __combineLastMapping: true }), ]); yield _(`'`); yield _([ diff --git a/packages/language-core/lib/generators/template.ts b/packages/language-core/lib/generators/template.ts index 5b20689ea1..0e10c3045c 100644 --- a/packages/language-core/lib/generators/template.ts +++ b/packages/language-core/lib/generators/template.ts @@ -237,7 +237,7 @@ export function* generate( '', 'template', expectedErrorNode.loc.end.offset, - disableAllFeatures({ __combineLastMappping: true }), + disableAllFeatures({ __combineLastMapping: true }), ]); yield _ts('\n;\n'); } @@ -279,7 +279,7 @@ export function* generate( else { yield _ts(['', 'template', slot.tagRange[0], mergeFeatureSettings(presetInfos.slotNameExport, disableAllFeatures({ __referencesCodeLens: true }))]); yield _ts('default'); - yield _ts(['', 'template', slot.tagRange[1], disableAllFeatures({ __combineLastMappping: true })]); + yield _ts(['', 'template', slot.tagRange[1], disableAllFeatures({ __combineLastMapping: true })]); } yield _ts(`?(_: typeof ${slot.varName}): any,\n`); } @@ -909,7 +909,7 @@ export function* generate( yield _ts('.'); yield _ts(['', 'template', slotDir.loc.start.offset, { ...presetInfos.slotName, completion: false }] satisfies Code); yield _ts('default'); - yield _ts(['', 'template', slotDir.loc.start.offset + (slotDir.loc.source.startsWith('#') ? '#'.length : slotDir.loc.source.startsWith('v-slot:') ? 'v-slot:'.length : 0), disableAllFeatures({ __combineLastMappping: true })] satisfies Code); + yield _ts(['', 'template', slotDir.loc.start.offset + (slotDir.loc.source.startsWith('#') ? '#'.length : slotDir.loc.source.startsWith('v-slot:') ? 'v-slot:'.length : 0), disableAllFeatures({ __combineLastMapping: true })] satisfies Code); } yield _ts(['', 'template', (slotDir.arg ?? slotDir).loc.end.offset, presetInfos.diagnosticOnly]); if (hasProps) { @@ -966,7 +966,7 @@ export function* generate( yield _ts(`(${componentCtxVar}.slots!).`); yield _ts(['', 'template', node.children[0].loc.start.offset, disableAllFeatures({ navigation: true })]); yield _ts('default'); - yield _ts(['', 'template', node.children[node.children.length - 1].loc.end.offset, disableAllFeatures({ __combineLastMappping: true })]); + yield _ts(['', 'template', node.children[node.children.length - 1].loc.end.offset, disableAllFeatures({ __combineLastMapping: true })]); yield _ts(';\n'); } } @@ -1026,22 +1026,22 @@ export function* generate( yield* generateCamelized( capitalize(prop.arg.loc.source), prop.arg.loc.start.offset, - disableAllFeatures({ __combineLastMappping: true }), + disableAllFeatures({ __combineLastMapping: true }), ); } else { yield _ts(`[`); yield _ts(startCode); yield _ts(`'`); - yield _ts(['', 'template', prop.arg.loc.start.offset, disableAllFeatures({ __combineLastMappping: true })]); + yield _ts(['', 'template', prop.arg.loc.start.offset, disableAllFeatures({ __combineLastMapping: true })]); yield _ts('on'); yield* generateCamelized( capitalize(prop.arg.loc.source), prop.arg.loc.start.offset, - disableAllFeatures({ __combineLastMappping: true }), + disableAllFeatures({ __combineLastMapping: true }), ); yield _ts(`'`); - yield _ts(['', 'template', prop.arg.loc.end.offset, disableAllFeatures({ __combineLastMappping: true })]); + yield _ts(['', 'template', prop.arg.loc.end.offset, disableAllFeatures({ __combineLastMapping: true })]); yield _ts(`]`); } yield _ts(`) };\n`); @@ -1267,23 +1267,55 @@ export function* generate( shouldCamelize, ); yield _ts(': ('); - if (prop.exp && !(prop.exp.constType === CompilerDOM.ConstantTypes.CAN_STRINGIFY)) { // style='z-index: 2' will compile to {'z-index':'2'} + if (prop.exp && prop.exp.constType !== CompilerDOM.ConstantTypes.CAN_STRINGIFY) { // style='z-index: 2' will compile to {'z-index':'2'} const isShorthand = prop.arg?.loc.start.offset === prop.exp?.loc.start.offset; // vue 3.4+ - yield* generateInterpolation( - prop.exp.loc.source, - prop.exp.loc, - prop.exp.loc.start.offset, - // disable completion for shorthand expression - isShorthand ? caps_attr : caps_all, - '(', - ')', - ); - if (!isShorthand && mode === 'normal') { - yield* generateTsFormat( + if (!isShorthand) { + yield* generateInterpolation( prop.exp.loc.source, + prop.exp.loc, prop.exp.loc.start.offset, - formatBrackets.normal, + caps_all, + '(', + ')', ); + + if (mode === 'normal') { + yield* generateTsFormat( + prop.exp.loc.source, + prop.exp.loc.start.offset, + formatBrackets.normal, + ); + } + } else { + const propVariableName = camelize(prop.exp.loc.source); + + if (validTsVarReg.test(propVariableName)) { + yield _ts('__VLS_ctx.'); + yield* generateCamelized( + prop.exp.loc.source, + prop.exp.loc.start.offset, + caps_all, + ); + if (mode === 'normal') { + yield _ts([ + '', + 'template', + prop.exp.loc.end.offset, + disableAllFeatures({ + __hint: { + setting: 'vue.inlayHints.vbindShorthand', + label: `="${propVariableName}"`, + tooltip: [ + `This is a shorthand for \`${prop.exp.loc.source}="${propVariableName}"\`.`, + 'To hide this hint, set `vue.inlayHints.vbindShorthand` to `false` in IDE settings.', + '[More info](https://github.com/vuejs/core/pull/9451)', + ].join('\n\n'), + }, + }) + ]); + } + accessedGlobalVariables.add(propVariableName); + } } } else { @@ -1559,13 +1591,13 @@ export function* generate( yield _ts('__VLS_normalizeSlot('); yield _ts(['', 'template', node.loc.start.offset, presetInfos.diagnosticOnly]); yield _ts(`${slotsAssignName ?? '__VLS_slots'}[`); - yield _ts(['', 'template', node.loc.start.offset, disableAllFeatures({ __combineLastMappping: true })]); + yield _ts(['', 'template', node.loc.start.offset, disableAllFeatures({ __combineLastMapping: true })]); yield _ts(slotNameExpNode?.content ?? `('${getSlotName()?.[0] ?? 'default'}' as const)`); - yield _ts(['', 'template', node.loc.end.offset, disableAllFeatures({ __combineLastMappping: true })]); + yield _ts(['', 'template', node.loc.end.offset, disableAllFeatures({ __combineLastMapping: true })]); yield _ts(']'); - yield _ts(['', 'template', node.loc.end.offset, disableAllFeatures({ __combineLastMappping: true })]); + yield _ts(['', 'template', node.loc.end.offset, disableAllFeatures({ __combineLastMapping: true })]); yield _ts(')?.('); - yield _ts(['', 'template', startTagOffset, disableAllFeatures({ __combineLastMappping: true })]); + yield _ts(['', 'template', startTagOffset, disableAllFeatures({ __combineLastMapping: true })]); yield _ts('{\n'); } else { @@ -1750,7 +1782,7 @@ export function* generate( if (needToUnicode(content)) { yield _ts(['', 'template', start, info]); yield _ts(toUnicode(content)); - yield _ts(['', 'template', end, disableAllFeatures({ __combineLastMappping: true })]); + yield _ts(['', 'template', end, disableAllFeatures({ __combineLastMapping: true })]); } else { yield _ts([content, 'template', start, info]); @@ -1771,7 +1803,7 @@ export function* generate( offset, i === 0 ? info - : disableAllFeatures({ __combineLastMappping: true }), + : disableAllFeatures({ __combineLastMapping: true }), ]); } offset += part.length + 1; @@ -1807,9 +1839,9 @@ export function* generate( else { yield _ts(['', 'template', offset, info]); yield _ts('"'); - yield* generateCamelized(code, offset, disableAllFeatures({ __combineLastMappping: true })); + yield* generateCamelized(code, offset, disableAllFeatures({ __combineLastMapping: true })); yield _ts('"'); - yield _ts(['', 'template', offset + code.length, disableAllFeatures({ __combineLastMappping: true })]); + yield _ts(['', 'template', offset + code.length, disableAllFeatures({ __combineLastMapping: true })]); } } else { @@ -1912,9 +1944,9 @@ export function* generate( else { yield _ts(['', 'template', offset, info]); yield _ts('"'); - yield _ts([code, 'template', offset, disableAllFeatures({ __combineLastMappping: true })]); + yield _ts([code, 'template', offset, disableAllFeatures({ __combineLastMapping: true })]); yield _ts('"'); - yield _ts(['', 'template', offset + code.length, disableAllFeatures({ __combineLastMappping: true })]); + yield _ts(['', 'template', offset + code.length, disableAllFeatures({ __combineLastMapping: true })]); } } diff --git a/packages/language-core/lib/types.ts b/packages/language-core/lib/types.ts index 502662ad57..d42a57fabd 100644 --- a/packages/language-core/lib/types.ts +++ b/packages/language-core/lib/types.ts @@ -21,7 +21,7 @@ export interface VueCodeInformation extends CodeInformation { paddingRight?: boolean; paddingLeft?: boolean; }; - __combineLastMappping?: boolean; + __combineLastMapping?: boolean; } export type CodeAndStack = [code: Code, stack: string]; diff --git a/packages/language-core/lib/virtualFile/computedFiles.ts b/packages/language-core/lib/virtualFile/computedFiles.ts index 0f8ba640fb..1a85937da1 100644 --- a/packages/language-core/lib/virtualFile/computedFiles.ts +++ b/packages/language-core/lib/virtualFile/computedFiles.ts @@ -195,7 +195,7 @@ function computedPluginFiles( } mapping.source = undefined; } - if (mapping.data.__combineLastMappping) { + if (mapping.data.__combineLastMapping) { lastValidMapping!.sourceOffsets.push(...mapping.sourceOffsets); lastValidMapping!.generatedOffsets.push(...mapping.generatedOffsets); lastValidMapping!.lengths.push(...mapping.lengths); @@ -209,7 +209,7 @@ function computedPluginFiles( return { file, snapshot, - mappings: mappings.filter(mapping => !mapping.data.__combineLastMappping), + mappings: mappings.filter(mapping => !mapping.data.__combineLastMapping), codegenStacks: buildStacks(file.content, file.contentStacks), }; }); diff --git a/test-workspace/language-service/reference/v-bind-shorthand/child.vue b/test-workspace/language-service/reference/v-bind-shorthand/child.vue new file mode 100644 index 0000000000..51b389e379 --- /dev/null +++ b/test-workspace/language-service/reference/v-bind-shorthand/child.vue @@ -0,0 +1,6 @@ + diff --git a/test-workspace/language-service/reference/v-bind-shorthand/entry.vue b/test-workspace/language-service/reference/v-bind-shorthand/entry.vue new file mode 100644 index 0000000000..5f9a43593c --- /dev/null +++ b/test-workspace/language-service/reference/v-bind-shorthand/entry.vue @@ -0,0 +1,13 @@ + + + diff --git a/test-workspace/tsc/vue3/v-bind-shorthand/child.vue b/test-workspace/tsc/vue3/v-bind-shorthand/child.vue new file mode 100644 index 0000000000..51b389e379 --- /dev/null +++ b/test-workspace/tsc/vue3/v-bind-shorthand/child.vue @@ -0,0 +1,6 @@ + diff --git a/test-workspace/tsc/vue3/v-bind-shorthand/entry.vue b/test-workspace/tsc/vue3/v-bind-shorthand/entry.vue new file mode 100644 index 0000000000..be2809e426 --- /dev/null +++ b/test-workspace/tsc/vue3/v-bind-shorthand/entry.vue @@ -0,0 +1,16 @@ + + +