дзеркало
https://github.com/l0ng-ai/openidea.git
синхронізовано 2026-11-08 19:02:17 +02:00
perf(angular): parse only text with Angular syntax as Angular
Every text node of a template got its own nested Angular parse, including
the whitespace between tags, which is most of them. Text without a `{`, `}`
or `@` holds no interpolation or block, so it stays HTML text. A full parse
of a 12k-line template takes about a fifth less work.
Цей коміт міститься в:
1 джерело
75718a6d21
коміт
d533750d1c
2 files changed
+25
-6
No files matched your search
@@ -556,6 +556,18 @@ describe("Angular templates", () => {
|
||||
}
|
||||
});
|
||||
|
||||
it("parses only text with Angular syntax as Angular", () => {
|
||||
const doc = "<ul>\n <li>Tom & Jerry</li>\n <li>{{ a }} & {{ b }}</li>\n</ul>\n@if (x) {\n}";
|
||||
const tree = angularLanguage.parser.parse(doc);
|
||||
const parentAt = (text: string) => tree.resolveInner(doc.indexOf(text) + 1, 1).parent?.name;
|
||||
// Plain text and the whitespace between tags stay HTML text.
|
||||
expect(parentAt("Tom")).toBe("Element");
|
||||
expect(parentAt("\n <li>")).toBe("Element");
|
||||
expect(parentAt("{{ a")).toBe("Interpolation");
|
||||
expect(tokens(doc)).toContain("@if:control");
|
||||
expect(tokens(doc)).toContain("b:var");
|
||||
});
|
||||
|
||||
it("is generated from angular.grammar", () => {
|
||||
const dir = import.meta.dirname;
|
||||
const { parser, terms } = buildParserFile(readFileSync(resolve(dir, "angular.grammar"), "utf8"), {
|
||||
|
||||
@@ -93,8 +93,15 @@ const textMixed = { parser: textParser },
|
||||
// document) holding it.
|
||||
const TEXT_NODES = new Set(["Text", "EntityReference", "CharacterReference", "InvalidEntity"]);
|
||||
|
||||
/** The runs of text directly in `node` that entities split, `null` without any. */
|
||||
function entityRuns(node: SyntaxNode): { from: number; to: number }[] | null {
|
||||
/**
|
||||
* Whether text holds Angular syntax: interpolations, blocks and their braces all
|
||||
* need a `{`, `}` or `@`. Other text (mostly the whitespace between tags) stays
|
||||
* plain HTML text instead of costing a nested parse each.
|
||||
*/
|
||||
const ANGULAR_TEXT = /[{}@]/;
|
||||
|
||||
/** The runs of Angular text directly in `node` that entities split, `null` without any. */
|
||||
function entityRuns(node: SyntaxNode, input: Input): { from: number; to: number }[] | null {
|
||||
const runs: { from: number; to: number }[] = [];
|
||||
let from = -1,
|
||||
to = -1,
|
||||
@@ -108,12 +115,12 @@ function entityRuns(node: SyntaxNode): { from: number; to: number }[] | null {
|
||||
parts++;
|
||||
continue;
|
||||
}
|
||||
if (parts > 1) runs.push({ from, to });
|
||||
if (parts > 1 && ANGULAR_TEXT.test(input.read(from, to))) runs.push({ from, to });
|
||||
({ from, to } = child);
|
||||
parts = 1;
|
||||
} while (child.nextSibling());
|
||||
}
|
||||
if (parts > 1) runs.push({ from, to });
|
||||
if (parts > 1 && ANGULAR_TEXT.test(input.read(from, to))) runs.push({ from, to });
|
||||
return runs.length ? runs : null;
|
||||
}
|
||||
|
||||
@@ -125,10 +132,10 @@ function mixAngular(node: SyntaxNodeRef, input: Input): NestedParse | null {
|
||||
return /^[*#([]|\{\{/.test(input.read(node.from, node.to)) ? attrMixed : null;
|
||||
// Text in a run is covered by its parent's overlay, so not asked about.
|
||||
case "Text":
|
||||
return textMixed;
|
||||
return ANGULAR_TEXT.test(input.read(node.from, node.to)) ? textMixed : null;
|
||||
case "Document":
|
||||
case "Element": {
|
||||
const overlay = entityRuns(node.node);
|
||||
const overlay = entityRuns(node.node, input);
|
||||
return overlay && { parser: node.name == "Document" ? documentRunsParser : runsParser, overlay };
|
||||
}
|
||||
}
|
||||
|
||||
Reference in new issue
Заблокувати користувача