Skip to content

Commit b196176

Browse files
committed
perf: incremental SFC parser
1 parent a0ab1d2 commit b196176

File tree

2 files changed

+77
-7
lines changed

2 files changed

+77
-7
lines changed

packages/vue-language-core/src/plugins/file-vue.ts

Lines changed: 43 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -11,7 +11,48 @@ const plugin: VueLanguagePlugin = () => {
1111

1212
return parse(content, { sourceMap: false, ignoreEmpty: false });
1313
}
14-
}
14+
},
15+
16+
updateSFC(sfc, change) {
17+
18+
// avoid broken @vue/compiler-sfc cache
19+
if (!(sfc as any).__volar_clone) {
20+
sfc = JSON.parse(JSON.stringify(sfc));
21+
(sfc as any).__volar_clone = true;
22+
}
23+
24+
const blocks = [
25+
sfc.descriptor.template,
26+
sfc.descriptor.script,
27+
sfc.descriptor.scriptSetup,
28+
...sfc.descriptor.styles,
29+
...sfc.descriptor.customBlocks,
30+
].filter((block): block is NonNullable<typeof block> => !!block);
31+
32+
const hitBlock = blocks.find(block => change.start >= block.loc.start.offset && change.end <= block.loc.end.offset);
33+
34+
if (!hitBlock) {
35+
return;
36+
}
37+
38+
hitBlock.content =
39+
hitBlock.content.substring(0, change.start - hitBlock.loc.start.offset)
40+
+ change.newText
41+
+ hitBlock.content.substring(change.end - hitBlock.loc.start.offset);
42+
43+
const lengthDiff = change.newText.length - (change.end - change.start);
44+
45+
for (const block of blocks) {
46+
if (block.loc.start.offset >= change.end) {
47+
block.loc.start.offset += lengthDiff;
48+
}
49+
if (block.loc.end.offset >= change.end) {
50+
block.loc.end.offset += lengthDiff;
51+
}
52+
}
53+
54+
return sfc;
55+
},
1556
};
16-
}
57+
};
1758
export = plugin;

packages/vue-language-core/src/sourceFile.ts

Lines changed: 34 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -17,6 +17,7 @@ export type VueLanguagePlugin = (ctx: {
1717
}) => {
1818
order?: number;
1919
parseSFC?(fileName: string, content: string): SFCParseResult | undefined;
20+
updateSFC?(oldResult: SFCParseResult, textChange: { start: number, end: number, newText: string; }): SFCParseResult | undefined;
2021
compileSFCTemplate?(lang: string, template: string, options?: CompilerDom.CompilerOptions): CompilerDom.CodegenResult | undefined;
2122
getEmbeddedFileNames?(fileName: string, sfc: Sfc): string[];
2223
resolveEmbeddedFile?(fileName: string, sfc: Sfc, embeddedFile: EmbeddedFile): void;
@@ -109,6 +110,13 @@ export function createSourceFile(
109110
}) as unknown as Sfc['scriptSetupAst'],
110111
}) as Sfc /* avoid Sfc unwrap in .d.ts by reactive */;
111112

113+
// cache
114+
let parsedSfcCache: {
115+
snapshot: ts.IScriptSnapshot,
116+
sfc: SFCParseResult,
117+
plugin: ReturnType<VueLanguagePlugin>,
118+
} | undefined;
119+
112120
// use
113121
const scriptAst = computed(() => {
114122
if (sfc.script) {
@@ -121,9 +129,28 @@ export function createSourceFile(
121129
}
122130
});
123131
const parsedSfc = computed(() => {
132+
133+
// incremental update
134+
if (parsedSfcCache?.plugin.updateSFC) {
135+
const change = snapshot.value.getChangeRange(parsedSfcCache.snapshot);
136+
if (change) {
137+
const newSfc = parsedSfcCache.plugin.updateSFC(parsedSfcCache.sfc, {
138+
start: change.span.start,
139+
end: change.span.start + change.span.length,
140+
newText: snapshot.value.getText(change.span.start, change.span.start + change.newLength),
141+
});
142+
if (newSfc) {
143+
parsedSfcCache.snapshot = snapshot.value;
144+
parsedSfcCache.sfc = newSfc;
145+
return newSfc;
146+
}
147+
}
148+
}
149+
124150
for (const plugin of plugins) {
125151
const sfc = plugin.parseSFC?.(fileName, fileContent.value);
126152
if (sfc) {
153+
parsedSfcCache = { snapshot: snapshot.value, sfc, plugin };
127154
return sfc;
128155
}
129156
}
@@ -404,13 +431,8 @@ export function createSourceFile(
404431
return;
405432
}
406433

407-
const change = newScriptSnapshot.getChangeRange(snapshot.value);
408434
snapshot.value = newScriptSnapshot;
409435

410-
if (change) {
411-
// TODO
412-
}
413-
414436
// TODO: wait for https://github.com/vuejs/core/pull/5912
415437
if (parsedSfc.value) {
416438
updateTemplate(parsedSfc.value.descriptor.template);
@@ -419,6 +441,13 @@ export function createSourceFile(
419441
updateStyles(parsedSfc.value.descriptor.styles);
420442
updateCustomBlocks(parsedSfc.value.descriptor.customBlocks);
421443
}
444+
else {
445+
updateTemplate(null);
446+
updateScript(null);
447+
updateScriptSetup(null);
448+
updateStyles([]);
449+
updateCustomBlocks([]);
450+
}
422451

423452
function updateTemplate(block: SFCTemplateBlock | null) {
424453

0 commit comments

Comments
 (0)