diff --git a/packages/highlight/src/__tests__/highlighter.test.ts b/packages/highlight/src/__tests__/highlighter.test.ts index c245d42da1..a943e6dd4a 100644 --- a/packages/highlight/src/__tests__/highlighter.test.ts +++ b/packages/highlight/src/__tests__/highlighter.test.ts @@ -144,6 +144,34 @@ const title = "Hello"; ); }); + it("highlights Astro markup containing only an empty expression", () => { + const code = "
{}
"; + + const tokens = highlightCode(code, "Empty.astro").flat(); + + expect(tokens.map((token) => token.text).join("")).toBe(code); + expect(tokens).toContainEqual({ text: "p", style: "tag" }); + }); + + it.each([ + "{title} {} {title}
", + '{title}
', + "{title}
", + ])("highlights Astro containing an empty expression: %s", (markup) => { + const code = `---\nconst title = "Hello";\n---\n${markup}\n`; + + const lines = highlightCode(code, "Empty.astro"); + + expect(lines).toHaveLength(code.split("\n").length); + for (const [index, tokens] of lines.entries()) { + expect(tokens.map((token) => token.text).join("")).toBe(code.split("\n")[index]); + } + expect(lines.flat()).toContainEqual({ text: "const", style: "keyword" }); + expect(lines.flat()).toContainEqual({ text: "p", style: "tag" }); + expect(lines.flat()).toContainEqual({ text: "color", style: "property" }); + expect(lines.flat()).toContainEqual({ text: "title", style: "variable" }); + }); + it("keeps an Astro expression open through nested template strings", () => { const code = "{`a ${`}`} b`}
"; diff --git a/packages/highlight/src/astro/parser.ts b/packages/highlight/src/astro/parser.ts index 202c921252..cef0748b5a 100644 --- a/packages/highlight/src/astro/parser.ts +++ b/packages/highlight/src/astro/parser.ts @@ -45,10 +45,12 @@ function maskExpressions(text: string): string { } function expressionOverlays(node: SyntaxNodeRef, input: Input): Range[] | null { - const overlays = findExpressions(input.read(node.from, node.to)).map(({ from, to }) => ({ - from: node.from + from + 1, - to: node.from + to, - })); + const overlays = findExpressions(input.read(node.from, node.to)) + .filter(({ from, to }) => to > from + 1) + .map(({ from, to }) => ({ + from: node.from + from + 1, + to: node.from + to, + })); return overlays.length > 0 ? overlays : null; } diff --git a/packages/server/src/server/utils/diff-highlighter.test.ts b/packages/server/src/server/utils/diff-highlighter.test.ts index 56f77fc706..5ca1177799 100644 --- a/packages/server/src/server/utils/diff-highlighter.test.ts +++ b/packages/server/src/server/utils/diff-highlighter.test.ts @@ -625,6 +625,28 @@ describe("highlightDiffFromHunks", () => { }); describe("highlightDiffWithFileContent", () => { + it("renders an Astro diff with an empty expression and keeps surrounding highlighting", async () => { + const diff = ASTRO_DIFF.replace("{title}", "{title} {}"); + const file = parseDiff(diff)[0]; + const highlighted = await highlightDiffWithFileContent(file, ".", { + oldFileContent: + '---\nconst title = "Hello";\n---\n