diff --git a/packages/language-core/lib/codegen/codeFeatures.ts b/packages/language-core/lib/codegen/codeFeatures.ts index b8bad1b82b..0baa2d992c 100644 --- a/packages/language-core/lib/codegen/codeFeatures.ts +++ b/packages/language-core/lib/codegen/codeFeatures.ts @@ -7,7 +7,6 @@ const raw = { semantic: true, navigation: true, }, - none: {}, verification: { verification: true, }, @@ -45,6 +44,9 @@ const raw = { completion: true, semantic: true, }, + semanticWithoutHighlight: { + semantic: { shouldHighlight: () => false }, + }, withoutHighlight: { semantic: { shouldHighlight: () => false }, verification: true, diff --git a/packages/language-core/lib/codegen/script/template.ts b/packages/language-core/lib/codegen/script/template.ts index 3d48d28d11..db6658a98c 100644 --- a/packages/language-core/lib/codegen/script/template.ts +++ b/packages/language-core/lib/codegen/script/template.ts @@ -124,7 +124,6 @@ function* generateTemplateBody( yield* options.templateCodegen.codes; } else { - yield `// no template${newLine}`; if (!options.scriptSetupRanges?.defineSlots) { yield `type __VLS_Slots = {}${endOfLine}`; } diff --git a/packages/language-core/lib/codegen/template/context.ts b/packages/language-core/lib/codegen/template/context.ts index d2a089ee10..2fb1cf9a73 100644 --- a/packages/language-core/lib/codegen/template/context.ts +++ b/packages/language-core/lib/codegen/template/context.ts @@ -281,7 +281,7 @@ export function createTemplateCodegenContext(options: Pick p !== prop); break; @@ -81,7 +79,6 @@ export function* generateComponent( dynamicTagInfo = { tag: node.tag, offsets: tagOffsets, - astHolder: node.loc, }; } @@ -120,7 +117,6 @@ export function* generateComponent( ctx.codeFeatures.all, dynamicTagInfo.tag, dynamicTagInfo.offsets[0], - dynamicTagInfo.astHolder, `(`, `)`, ); @@ -133,7 +129,6 @@ export function* generateComponent( ctx.codeFeatures.withoutCompletion, dynamicTagInfo.tag, dynamicTagInfo.offsets[1], - dynamicTagInfo.astHolder, `(`, `)`, ); @@ -376,7 +371,6 @@ function* generateFailedPropExps( ctx.codeFeatures.all, failedExp.node.loc.source, failedExp.node.loc.start.offset, - failedExp.node.loc, failedExp.prefix, failedExp.suffix, ); @@ -460,7 +454,6 @@ function* generateElementReference( content, startOffset, ctx.codeFeatures.navigation, - prop.value.loc, ); yield `} */${endOfLine}`; diff --git a/packages/language-core/lib/codegen/template/elementDirectives.ts b/packages/language-core/lib/codegen/template/elementDirectives.ts index e6a001131a..02bf57f3c7 100644 --- a/packages/language-core/lib/codegen/template/elementDirectives.ts +++ b/packages/language-core/lib/codegen/template/elementDirectives.ts @@ -111,7 +111,6 @@ function* generateArg( ctx.codeFeatures.all, arg.content, startOffset, - arg.loc, `(`, `)`, ); diff --git a/packages/language-core/lib/codegen/template/elementEvents.ts b/packages/language-core/lib/codegen/template/elementEvents.ts index 4fe6d09834..bda5673ac6 100644 --- a/packages/language-core/lib/codegen/template/elementEvents.ts +++ b/packages/language-core/lib/codegen/template/elementEvents.ts @@ -120,7 +120,7 @@ export function* generateEventExpression( let suffix = `)`; let isFirstMapping = true; - const ast = createTsAst(options.ts, prop.exp, prop.exp.content); + const ast = createTsAst(options.ts, options.template.ast, prop.exp.content); const isCompound = isCompoundExpression(options.ts, ast); if (isCompound) { @@ -156,7 +156,6 @@ export function* generateEventExpression( }, prop.exp.content, prop.exp.loc.start.offset, - prop.exp.loc, prefix, suffix, ); @@ -189,7 +188,6 @@ export function* generateModelEventExpression( ctx.codeFeatures.verification, prop.exp.content, prop.exp.loc.start.offset, - prop.exp.loc, ); yield ` = $event${endOfLine}`; yield `}`; diff --git a/packages/language-core/lib/codegen/template/elementProps.ts b/packages/language-core/lib/codegen/template/elementProps.ts index f448791f20..8507e3b71c 100644 --- a/packages/language-core/lib/codegen/template/elementProps.ts +++ b/packages/language-core/lib/codegen/template/elementProps.ts @@ -130,7 +130,6 @@ export function* generateElementProps( propName, prop.arg.loc.start.offset, codeInfo, - (prop.loc as any).name_2 ??= {}, shouldCamelize, ) : wrapWith( @@ -208,7 +207,6 @@ export function* generateElementProps( prop.name, prop.loc.start.offset, codeInfo, - (prop.loc as any).name_1 ??= {}, shouldCamelize, ), `: `, @@ -287,7 +285,6 @@ export function* generatePropExp( features, exp.loc.source, exp.loc.start.offset, - exp.loc, `(`, `)`, ); diff --git a/packages/language-core/lib/codegen/template/index.ts b/packages/language-core/lib/codegen/template/index.ts index 49c4f8b832..923bb241c6 100644 --- a/packages/language-core/lib/codegen/template/index.ts +++ b/packages/language-core/lib/codegen/template/index.ts @@ -88,7 +88,6 @@ function* generateSlots( slot.name, slot.offset, ctx.codeFeatures.withoutHighlightAndCompletion, - slot.nodeLoc, ); } else { diff --git a/packages/language-core/lib/codegen/template/interpolation.ts b/packages/language-core/lib/codegen/template/interpolation.ts index d21ac6f5d1..55590095fb 100644 --- a/packages/language-core/lib/codegen/template/interpolation.ts +++ b/packages/language-core/lib/codegen/template/interpolation.ts @@ -1,36 +1,40 @@ import { isGloballyAllowed, makeMap } from '@vue/shared'; import type * as ts from 'typescript'; -import type { Code, VueCodeInformation } from '../../types'; +import type { Code, Sfc, VueCodeInformation } from '../../types'; import { getNodeText, getStartEnd } from '../../utils/shared'; +import type { ScriptCodegenOptions } from '../script'; import { collectVars, createTsAst, identifierRegex } from '../utils'; import type { TemplateCodegenContext } from './context'; +import type { TemplateCodegenOptions } from './index'; // https://github.com/vuejs/core/blob/fb0c3ca519f1fccf52049cd6b8db3a67a669afe9/packages/compiler-core/src/transforms/transformExpression.ts#L47 const isLiteralWhitelisted = /*@__PURE__*/ makeMap('true,false,null,this'); export function* generateInterpolation( - options: { - ts: typeof ts, - destructuredPropNames: Set | undefined, - templateRefNames: Set | undefined; - }, + options: TemplateCodegenOptions | ScriptCodegenOptions, ctx: TemplateCodegenContext, source: string, data: VueCodeInformation | ((offset: number) => VueCodeInformation) | undefined, code: string, start: number | undefined, - astHolder: any = {}, prefix: string = '', suffix: string = '', ): Generator { + const { + ts, + destructuredPropNames, + templateRefNames, + } = options; + const template = 'template' in options ? options.template : options.sfc.template; + for (let [section, offset, type] of forEachInterpolationSegment( - options.ts, - options.destructuredPropNames, - options.templateRefNames, + ts, + template, + destructuredPropNames, + templateRefNames, ctx, code, start, - astHolder, prefix, suffix, )) { @@ -88,12 +92,12 @@ type Segment = [ function* forEachInterpolationSegment( ts: typeof import('typescript'), + template: Sfc['template'], destructuredPropNames: Set | undefined, templateRefNames: Set | undefined, ctx: TemplateCodegenContext, originalCode: string, start: number | undefined, - astHolder: any, prefix: string, suffix: string, ): Generator { @@ -108,7 +112,7 @@ function* forEachInterpolationSegment( }); } else { - const ast = createTsAst(ts, astHolder, code); + const ast = createTsAst(ts, template?.ast, code); const varCb = (id: ts.Identifier, isShorthand: boolean) => { const text = getNodeText(ts, id, ast); if (!shouldIdentifierSkipped(ctx, text, destructuredPropNames)) { diff --git a/packages/language-core/lib/codegen/template/objectProperty.ts b/packages/language-core/lib/codegen/template/objectProperty.ts index 5c9edfec52..81ff213475 100644 --- a/packages/language-core/lib/codegen/template/objectProperty.ts +++ b/packages/language-core/lib/codegen/template/objectProperty.ts @@ -14,11 +14,10 @@ export function* generateObjectProperty( code: string, offset: number, features: VueCodeInformation, - astHolder?: any, shouldCamelize = false, shouldBeConstant = false, ): Generator { - if (code.startsWith('[') && code.endsWith(']') && astHolder) { + if (code.startsWith('[') && code.endsWith(']')) { if (shouldBeConstant) { yield* generateInterpolation( options, @@ -27,7 +26,6 @@ export function* generateObjectProperty( features, code.slice(1, -1), offset + 1, - astHolder, `[__VLS_tryAsConstant(`, `)]`, ); @@ -40,7 +38,6 @@ export function* generateObjectProperty( features, code, offset, - astHolder, ); } } diff --git a/packages/language-core/lib/codegen/template/propertyAccess.ts b/packages/language-core/lib/codegen/template/propertyAccess.ts index ff120fb7cc..08b9adfa41 100644 --- a/packages/language-core/lib/codegen/template/propertyAccess.ts +++ b/packages/language-core/lib/codegen/template/propertyAccess.ts @@ -11,7 +11,6 @@ export function* generatePropertyAccess( code: string, offset?: number, features?: VueCodeInformation, - astHolder?: any, ): Generator { if (!options.compilerOptions.noPropertyAccessFromIndexSignature && identifierRegex.test(code)) { yield `.`; @@ -27,7 +26,6 @@ export function* generatePropertyAccess( features, code, offset, - astHolder, ); } else { diff --git a/packages/language-core/lib/codegen/template/slotOutlet.ts b/packages/language-core/lib/codegen/template/slotOutlet.ts index 63e45b6539..0c5bdd378e 100644 --- a/packages/language-core/lib/codegen/template/slotOutlet.ts +++ b/packages/language-core/lib/codegen/template/slotOutlet.ts @@ -150,7 +150,6 @@ export function* generateSlotOutlet( ctx.codeFeatures.all, nameProp.exp.content, nameProp.exp.loc.start.offset, - nameProp.exp, ); yield `)${endOfLine}`; ctx.dynamicSlots.push({ diff --git a/packages/language-core/lib/codegen/template/templateChild.ts b/packages/language-core/lib/codegen/template/templateChild.ts index bb7922d90d..b319909874 100644 --- a/packages/language-core/lib/codegen/template/templateChild.ts +++ b/packages/language-core/lib/codegen/template/templateChild.ts @@ -101,7 +101,6 @@ export function* generateTemplateChild( ctx.codeFeatures.all, content, start, - node.content.loc, `(`, `)${endOfLine}`, ); diff --git a/packages/language-core/lib/codegen/template/vFor.ts b/packages/language-core/lib/codegen/template/vFor.ts index 396200cb16..921c4f2419 100644 --- a/packages/language-core/lib/codegen/template/vFor.ts +++ b/packages/language-core/lib/codegen/template/vFor.ts @@ -17,7 +17,7 @@ export function* generateVFor( yield `for (const [`; if (leftExpressionRange && leftExpressionText) { - const collectAst = createTsAst(options.ts, node.parseResult, `const [${leftExpressionText}]`); + const collectAst = createTsAst(options.ts, options.template.ast, `const [${leftExpressionText}]`); collectVars(options.ts, collectAst, collectAst, forBlockVars); yield [ leftExpressionText, @@ -36,7 +36,6 @@ export function* generateVFor( ctx.codeFeatures.all, source.content, source.loc.start.offset, - source.loc, `(`, `)`, ); @@ -74,7 +73,6 @@ export function* generateVFor( ctx.codeFeatures.all, prop.value.content, prop.value.loc.start.offset, - prop.value.loc, `(`, `)`, ); diff --git a/packages/language-core/lib/codegen/template/vIf.ts b/packages/language-core/lib/codegen/template/vIf.ts index daf8fc06a5..fcd2096373 100644 --- a/packages/language-core/lib/codegen/template/vIf.ts +++ b/packages/language-core/lib/codegen/template/vIf.ts @@ -39,7 +39,6 @@ export function* generateVIf( ctx.codeFeatures.all, branch.condition.content, branch.condition.loc.start.offset, - branch.condition.loc, `(`, `)`, )]; diff --git a/packages/language-core/lib/codegen/template/vSlot.ts b/packages/language-core/lib/codegen/template/vSlot.ts index 1c96b85850..2ed3ff3600 100644 --- a/packages/language-core/lib/codegen/template/vSlot.ts +++ b/packages/language-core/lib/codegen/template/vSlot.ts @@ -37,7 +37,6 @@ export function* generateVSlot( slotDir.arg.loc.source, slotDir.arg.loc.start.offset, slotDir.arg.isStatic ? ctx.codeFeatures.withoutHighlight : ctx.codeFeatures.all, - slotDir.arg.loc, false, true, ); @@ -65,7 +64,7 @@ export function* generateVSlot( } if (slotDir?.exp?.type === CompilerDOM.NodeTypes.SIMPLE_EXPRESSION) { - const slotAst = createTsAst(options.ts, slotDir, `(${slotDir.exp.content}) => {}`); + const slotAst = createTsAst(options.ts, options.template.ast, `(${slotDir.exp.content}) => {}`); collectVars(options.ts, slotAst, slotAst, slotBlockVars); yield* generateSlotParameters(options, ctx, slotAst, slotDir.exp, slotVar); } diff --git a/packages/language-core/lib/codegen/utils/index.ts b/packages/language-core/lib/codegen/utils/index.ts index f07c96bc7a..78c6415d6a 100644 --- a/packages/language-core/lib/codegen/utils/index.ts +++ b/packages/language-core/lib/codegen/utils/index.ts @@ -66,12 +66,19 @@ export function normalizeAttributeValue(node: CompilerDOM.TextNode): [string, nu return [content, offset]; } -export function createTsAst(ts: typeof import('typescript'), astHolder: any, text: string) { - if (astHolder.__volar_ast_text !== text) { - astHolder.__volar_ast_text = text; - astHolder.__volar_ast = ts.createSourceFile('/a.ts', text, 99 satisfies ts.ScriptTarget.ESNext); +export function createTsAst( + ts: typeof import('typescript'), + templateAst: CompilerDOM.RootNode | undefined, + text: string, +) { + const inlineTsAsts = (templateAst as any)?.__volar_inlineTsAsts; + let ast = inlineTsAsts?.get(text); + if (!ast) { + ast = ts.createSourceFile('/a.ts', text, 99 satisfies ts.ScriptTarget.ESNext); + inlineTsAsts?.set(text, ast); } - return astHolder.__volar_ast as ts.SourceFile; + ast.__volar_used = true; + return ast as ts.SourceFile; } export function generateSfcBlockSection(block: SfcBlock, start: number, end: number, features: VueCodeInformation): Code { diff --git a/packages/language-core/lib/plugins/vue-template-inline-ts.ts b/packages/language-core/lib/plugins/vue-template-inline-ts.ts index a06479c08c..43e27c3ad5 100644 --- a/packages/language-core/lib/plugins/vue-template-inline-ts.ts +++ b/packages/language-core/lib/plugins/vue-template-inline-ts.ts @@ -107,7 +107,7 @@ const plugin: VueLanguagePlugin = ctx => { && prop.exp.constType !== CompilerDOM.ConstantTypes.CAN_STRINGIFY // style='z-index: 2' will compile to {'z-index':'2'} ) { if (prop.name === 'on' && prop.arg?.type === CompilerDOM.NodeTypes.SIMPLE_EXPRESSION) { - const ast = createTsAst(ctx.modules.typescript, prop.exp, prop.exp.content); + const ast = createTsAst(ctx.modules.typescript, sfc.template!.ast, prop.exp.content); if (isCompoundExpression(ctx.modules.typescript, ast)) { addFormatCodes( prop.exp.loc.source, diff --git a/packages/language-core/lib/virtualFile/computedSfc.ts b/packages/language-core/lib/virtualFile/computedSfc.ts index 87e18d80f3..28e1505945 100644 --- a/packages/language-core/lib/virtualFile/computedSfc.ts +++ b/packages/language-core/lib/virtualFile/computedSfc.ts @@ -170,13 +170,33 @@ export function computedSfc( plugin: VueLanguagePluginReturn, } | undefined; + let inlineTsAsts: Map | undefined; + + function updateInlineTsAsts(newAst: CompilerDOM.RootNode, oldAst?: CompilerDOM.RootNode) { + const newTsAsts: Map = (newAst as any).__volar_inlineTsAsts ??= new Map(); + const oldTsAsts: Map = (oldAst as any)?.__volar_inlineTsAsts ?? inlineTsAsts; + + if (oldTsAsts) { + for (const [text, ast] of oldTsAsts) { + if (!ast.__volar_used) { + oldTsAsts.delete(text); + } + else { + newTsAsts.set(text, ast); + ast.__volar_used = false; + } + } + } + inlineTsAsts = new Map(newTsAsts); + } + return computed(() => { if (cache?.template === base.content) { return { errors: [], warnings: [], - ast: cache?.result.ast, + ast: cache.result.ast, }; } @@ -197,6 +217,7 @@ export function computedSfc( newText, }); if (newResult) { + updateInlineTsAsts(newResult.ast, cache.result.ast); cache.template = base.content; cache.snapshot = getUntrackedSnapshot(); cache.result = newResult; @@ -229,6 +250,9 @@ export function computedSfc( try { result = plugin.compileSFCTemplate?.(base.lang, base.content, options); + if (result) { + updateInlineTsAsts(result.ast, cache?.result.ast); + } } catch (e) { const err = e as CompilerDOM.CompilerError;