Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

feat: support v-bind shorthand #3831

Merged
merged 14 commits into from
Mar 5, 2024
5 changes: 5 additions & 0 deletions extensions/vscode/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand Down
2 changes: 1 addition & 1 deletion packages/language-core/lib/generators/script.ts
Original file line number Diff line number Diff line change
Expand Up @@ -1098,7 +1098,7 @@ type __VLS_PrettifyGlobal<T> = { [K in keyof T]: T[K]; } & {};
classNameWithDot.substring(1),
'style_' + styleIndex,
offset + 1,
disableAllFeatures({ __combineLastMappping: true }),
disableAllFeatures({ __combineLastMapping: true }),
]);
yield _(`'`);
yield _([
Expand Down
94 changes: 63 additions & 31 deletions packages/language-core/lib/generators/template.ts
Original file line number Diff line number Diff line change
Expand Up @@ -237,7 +237,7 @@ export function* generate(
'',
'template',
expectedErrorNode.loc.end.offset,
disableAllFeatures({ __combineLastMappping: true }),
disableAllFeatures({ __combineLastMapping: true }),
]);
yield _ts('\n;\n');
}
Expand Down Expand Up @@ -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`);
}
Expand Down Expand Up @@ -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) {
Expand Down Expand Up @@ -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');
}
}
Expand Down Expand Up @@ -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`);
Expand Down Expand Up @@ -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 {
Expand Down Expand Up @@ -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 {
Expand Down Expand Up @@ -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]);
Expand All @@ -1771,7 +1803,7 @@ export function* generate(
offset,
i === 0
? info
: disableAllFeatures({ __combineLastMappping: true }),
: disableAllFeatures({ __combineLastMapping: true }),
]);
}
offset += part.length + 1;
Expand Down Expand Up @@ -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 {
Expand Down Expand Up @@ -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 })]);
}
}

Expand Down
2 changes: 1 addition & 1 deletion packages/language-core/lib/types.ts
Original file line number Diff line number Diff line change
Expand Up @@ -21,7 +21,7 @@ export interface VueCodeInformation extends CodeInformation {
paddingRight?: boolean;
paddingLeft?: boolean;
};
__combineLastMappping?: boolean;
__combineLastMapping?: boolean;
}

export type CodeAndStack = [code: Code, stack: string];
Expand Down
4 changes: 2 additions & 2 deletions packages/language-core/lib/virtualFile/computedFiles.ts
Original file line number Diff line number Diff line change
Expand Up @@ -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);
Expand All @@ -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),
};
});
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,6 @@
<script setup lang="ts">
defineProps<{
foo?: number;
bar?: number;
}>();
</script>
Original file line number Diff line number Diff line change
@@ -0,0 +1,13 @@
<script setup lang="ts">
import child from './child.vue';
const foo = 1;
// ^reference: 1
const bar = '';
// ^reference: 1
</script>

<template>
<child :foo></child>
<!-- @vue-expect-error should typecheck -->
<child :bar></child>
</template>
6 changes: 6 additions & 0 deletions test-workspace/tsc/vue3/v-bind-shorthand/child.vue
Original file line number Diff line number Diff line change
@@ -0,0 +1,6 @@
<script setup lang="ts">
defineProps<{
foo?: number;
bar?: number;
}>();
</script>
16 changes: 16 additions & 0 deletions test-workspace/tsc/vue3/v-bind-shorthand/entry.vue
Original file line number Diff line number Diff line change
@@ -0,0 +1,16 @@
<script setup lang="ts">
import child from './child.vue';
const foo = 1;
// ^reference: 1
const bar = '';
// ^reference: 1
const fooBar = 1;
// ^reference: 1
</script>

<template>
<child :foo></child>
<!-- @vue-expect-error should typecheck -->
<child :bar></child>
<child :foo-bar></child>
</template>
Loading