From ac436160b5fbb20a6764cd66b890dbf0eec3cb9d Mon Sep 17 00:00:00 2001 From: l0ng-ai <24760907+l0ng-ai@users.noreply.github.com> Date: Thu, 5 Nov 2026 15:04:02 +0800 Subject: [PATCH] fix(editor): keep YAML highlighting after comment lines in longer files (#1489) In some YAML files over about a kilobyte, a mapping that follows comment lines lost its highlighting from the second comment to the end of the mapping. The YAML parser (@lezer/yaml with @lezer/lr) can give that mapping node an inverted range when it falls on a tree buffer boundary (1024 entries by default), and highlighting skips the whole node. The YAML mode now parses into buffers of 65536 entries, so ordinary YAML files stay in one buffer and never hit the boundary. Refs #809 --- src/editor/core/cm/languages.test.ts | 24 ++++++++++++++++++++++++ src/editor/core/cm/languages.ts | 15 +++++++++++++++ 2 files changed, 39 insertions(+) diff --git a/src/editor/core/cm/languages.test.ts b/src/editor/core/cm/languages.test.ts index c6e71b62..5ca460d6 100644 --- a/src/editor/core/cm/languages.test.ts +++ b/src/editor/core/cm/languages.test.ts @@ -24,6 +24,30 @@ describe("language modes", () => { ]); }); + it("parses YAML into large tree buffers and highlights mappings after comment lines", async () => { + for (const path of ["/p/application.yml", "/p/compose.yaml"]) { + const support = await loadSupport(findLanguageDescription("YAML", path)!); + // @lezer/yaml can invert a node's range on a tree buffer boundary; one buffer avoids it. + expect((support!.language.parser as unknown as { bufferLength: number }).bufferLength).toBe(1 << 16); + } + const support = await loadSupport(findLanguageDescription("YAML", "/p/application.yml")!); + const source = "spring:\n datasource:\n # driver: a\n # url: b\n driver: c # d\n url: e\n"; + const found: Array<[string, string]> = []; + highlightTree( + support!.language.parser.parse(source), + tagHighlighter([ + { tag: t.lineComment, class: "comment" }, + { tag: t.definition(t.propertyName), class: "key" }, + ]), + (from, to, style) => found.push([source.slice(from, to), style]), + ); + expect(found.filter(([, style]) => style === "comment").map(([text]) => text)).toEqual([ + "# driver: a", + "# url: b", + "# d", + ]); + }); + it("recognizes AsciiDoc files and loads their lightweight syntax mode", async () => { for (const path of ["/docs/guide.adoc", "/docs/guide.asciidoc"]) { const description = findLanguageDescription("AsciiDoc", path); diff --git a/src/editor/core/cm/languages.ts b/src/editor/core/cm/languages.ts index 2c61f901..5bf64848 100644 --- a/src/editor/core/cm/languages.ts +++ b/src/editor/core/cm/languages.ts @@ -115,6 +115,21 @@ const OWN_MODES = [ load: () => import("./erb").then((m) => m.erb()), }), LanguageDescription.of({ name: "JSP", load: () => import("./jsp").then((m) => m.jsp()) }), + LanguageDescription.of({ + name: "YAML", + alias: ["yml"], + extensions: ["yaml", "yml"], + load: async () => { + const support = await languageData.find((l) => l.name === "YAML")!.load(); + // @lezer/yaml can give a mapping that follows comment lines an inverted range when it + // lands on a tree buffer boundary (1024 entries by default); highlighting then skips + // the whole mapping. Larger buffers keep ordinary YAML files in one. + return new LanguageSupport( + (support.language as LRLanguage).configure({ bufferLength: 1 << 16 }), + support.support, + ); + }, + }), ]; const BATCH_KEYWORDS = new Set([