mirror of
https://github.com/Horizontal42/summarum.git
synced 2026-11-11 01:02:01 +03:00
perf: avoid expensive doc stringification for autocomplete
Refactored the autocomplete variable matching logic in `src/ui/editor.ts` to iterate through document lines using CodeMirror's `iterLines()` instead of allocating the entire document string in memory with `toString()`. Also pulled the regex up and introduced a quick `.includes("=")` bailout to skip the regex engine completely for most lines.
Co-authored-by: Horizontal42 <75383519+Horizontal42@users.noreply.github.com>
This commit is contained in:
parent
868a34497b
commit
45c73ec8ba
2 changed files with 61 additions and 3 deletions
51
benchmark_editor.ts
Normal file
51
benchmark_editor.ts
Normal file
|
|
@ -0,0 +1,51 @@
|
||||||
|
import { EditorState } from "@codemirror/state";
|
||||||
|
|
||||||
|
const lines = [];
|
||||||
|
for (let i = 0; i < 500000; i++) {
|
||||||
|
lines.push(`some_var_${i} = ${i * 10} + 5`);
|
||||||
|
}
|
||||||
|
const text = lines.join('\n');
|
||||||
|
const state = EditorState.create({ doc: text });
|
||||||
|
|
||||||
|
function benchmarkOld() {
|
||||||
|
const start = performance.now();
|
||||||
|
const options = [];
|
||||||
|
const docText = state.doc.toString();
|
||||||
|
for (const m of docText.matchAll(/^\s*([\p{L}_][\p{L}\d_]*)\s*=/gmu)) {
|
||||||
|
options.push({ label: m[1], type: "variable" });
|
||||||
|
}
|
||||||
|
const end = performance.now();
|
||||||
|
return end - start;
|
||||||
|
}
|
||||||
|
|
||||||
|
const pattern = /^\s*([\p{L}_][\p{L}\d_]*)\s*=/u;
|
||||||
|
function benchmarkRegexPerLine() {
|
||||||
|
const start = performance.now();
|
||||||
|
const options = [];
|
||||||
|
const iter = state.doc.iterLines();
|
||||||
|
for (let next = iter.next(); !next.done; next = iter.next()) {
|
||||||
|
const line = next.value;
|
||||||
|
// Optimization: checking '=' first is significantly faster than firing regex blindly
|
||||||
|
if (line.includes('=')) {
|
||||||
|
const m = pattern.exec(line);
|
||||||
|
if (m) {
|
||||||
|
options.push({ label: m[1], type: "variable" });
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
const end = performance.now();
|
||||||
|
return end - start;
|
||||||
|
}
|
||||||
|
|
||||||
|
benchmarkOld();
|
||||||
|
benchmarkRegexPerLine();
|
||||||
|
|
||||||
|
let oldTotal = 0;
|
||||||
|
let newTotal = 0;
|
||||||
|
for (let i = 0; i < 10; i++) {
|
||||||
|
oldTotal += benchmarkOld();
|
||||||
|
newTotal += benchmarkRegexPerLine();
|
||||||
|
}
|
||||||
|
|
||||||
|
console.log(`Old avg: ${oldTotal / 10} ms`);
|
||||||
|
console.log(`Regex per line with '=' check avg: ${newTotal / 10} ms`);
|
||||||
|
|
@ -170,6 +170,7 @@ export class SumEditor {
|
||||||
this.resultsEl = resultsEl;
|
this.resultsEl = resultsEl;
|
||||||
|
|
||||||
let engineOptions: { label: string; type: string; detail?: string }[] | null = null;
|
let engineOptions: { label: string; type: string; detail?: string }[] | null = null;
|
||||||
|
const varPattern = /^\s*([\p{L}_][\p{L}\d_]*)\s*=/u;
|
||||||
const completionSource = (ctx: CompletionContext): CompletionResult | null => {
|
const completionSource = (ctx: CompletionContext): CompletionResult | null => {
|
||||||
const word = ctx.matchBefore(/[\p{L}_]+$/u);
|
const word = ctx.matchBefore(/[\p{L}_]+$/u);
|
||||||
if (!word || (word.from === word.to && !ctx.explicit)) return null;
|
if (!word || (word.from === word.to && !ctx.explicit)) return null;
|
||||||
|
|
@ -182,9 +183,15 @@ export class SumEditor {
|
||||||
}));
|
}));
|
||||||
const options = [...engineOptions];
|
const options = [...engineOptions];
|
||||||
// document variables
|
// document variables
|
||||||
const text = ctx.state.doc.toString();
|
const iter = ctx.state.doc.iterLines();
|
||||||
for (const m of text.matchAll(/^\s*([\p{L}_][\p{L}\d_]*)\s*=/gmu)) {
|
for (let next = iter.next(); !next.done; next = iter.next()) {
|
||||||
options.push({ label: m[1], type: "variable" });
|
const line = next.value;
|
||||||
|
if (line.includes("=")) {
|
||||||
|
const m = varPattern.exec(line);
|
||||||
|
if (m) {
|
||||||
|
options.push({ label: m[1], type: "variable" });
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
return { from: word.from, options, validFor: /^[\p{L}_]*$/u };
|
return { from: word.from, options, validFor: /^[\p{L}_]*$/u };
|
||||||
};
|
};
|
||||||
|
|
|
||||||
Loading…
Add table
Reference in a new issue