Skip to content

Commit 70397ef

Browse files
committed
Add RA syntax highlighter with subscript rendering
Custom syntax highlighter for the RA editor that: - Renders operators (σ, π, ρ, ×, ⋈, etc.) in purple - Renders bracket content ([...], {...}) as italic subscript-style text with faint brackets to visually approximate mathematical notation - Renders <- as ← and -> as → for visual clarity - Highlights strings (green), numbers (amber), logic keywords (blue) - Highlights -- comments in gray italic - Supports LaTeX-style _{} notation with faint underscore - Supports implicit subscripts (σ condition (R)) 14 highlighter tests + 96 parser tests, all passing. https://claude.ai/code/session_01TJyw8nESra9cc5RpVUpmt6
1 parent 516ff38 commit 70397ef

3 files changed

Lines changed: 425 additions & 3 deletions

File tree

src/App.tsx

Lines changed: 4 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -31,6 +31,7 @@ import { format } from "sql-formatter";
3131
import initSqlJs from "sql.js";
3232
import ViewsTable, { View } from "./ViewsTable";
3333
import { raToSQL, RAError } from "./relationalAlgebra";
34+
import { highlightRA } from "./raHighlight";
3435

3536
import { Info, Settings, XCircle, CheckCircle2, Eye, EyeOff } from "lucide-react";
3637
import { Button } from "@/components/ui/button";
@@ -901,10 +902,10 @@ function App() {
901902
<Editor
902903
id="placeholder-editor"
903904
itemID="placeholder-editor"
904-
value={t("selectQuestionComment")}
905+
value={editorMode === "ra" ? t("raPlaceholder") : t("selectQuestionComment")}
905906
disabled={true}
906907
onValueChange={_code => null}
907-
highlight={code => highlight(code, languages.sql)}
908+
highlight={code => editorMode === "ra" ? highlightRA(code) : highlight(code, languages.sql)}
908909
padding={10}
909910
tabSize={2}
910911
className="font-mono text-base w-full dark:bg-slate-800 bg-slate-100 min-h-32 rounded-md"
@@ -916,7 +917,7 @@ function App() {
916917
itemID="editor"
917918
value={query}
918919
onValueChange={code => setQuery(code)}
919-
highlight={code => highlight(code, languages.sql)}
920+
highlight={code => editorMode === "ra" ? highlightRA(code) : highlight(code, languages.sql)}
920921
padding={10}
921922
tabSize={2}
922923
className="font-mono text-base w-full dark:bg-slate-800 bg-slate-100 border dark:border-slate-600 border-gray-300 min-h-32 rounded-md"

src/raHighlight.test.ts

Lines changed: 82 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,82 @@
1+
import { describe, it, expect } from "vitest";
2+
import { highlightRA } from "./raHighlight";
3+
4+
describe("RA highlighter", () => {
5+
it("should highlight σ operator in purple", () => {
6+
const html = highlightRA("σ[age > 20](Person)");
7+
expect(html).toContain("color: #7c3aed"); // operator color
8+
expect(html).toContain("σ");
9+
});
10+
11+
it("should render brackets faintly", () => {
12+
const html = highlightRA("σ[age > 20](Person)");
13+
expect(html).toContain("opacity: 0.4"); // faint brackets
14+
});
15+
16+
it("should render subscript content in italic gray", () => {
17+
const html = highlightRA("π[name](Person)");
18+
expect(html).toContain("font-style: italic"); // subscript styling
19+
});
20+
21+
it("should highlight string literals in green", () => {
22+
const html = highlightRA("σ[name = 'Alice'](Person)");
23+
expect(html).toContain("color: #059669"); // string color
24+
expect(html).toContain("Alice");
25+
});
26+
27+
it("should highlight numbers in amber", () => {
28+
const html = highlightRA("σ[age > 20](Person)");
29+
expect(html).toContain("color: #d97706"); // number color
30+
});
31+
32+
it("should highlight AND/OR in blue", () => {
33+
const html = highlightRA("σ[a > 1 and b < 2](T)");
34+
expect(html).toContain("color: #2563eb"); // logic color
35+
expect(html).toContain("and");
36+
});
37+
38+
it("should highlight comments in gray italic", () => {
39+
const html = highlightRA("-- this is a comment\nPerson");
40+
expect(html).toContain("font-style: italic");
41+
expect(html).toContain("this is a comment");
42+
});
43+
44+
it("should render <- as ← visually", () => {
45+
const html = highlightRA("A <- Person");
46+
expect(html).toContain("←");
47+
});
48+
49+
it("should render -> as → visually", () => {
50+
const html = highlightRA("ρ[name->fullName](Person)");
51+
expect(html).toContain("→");
52+
});
53+
54+
it("should handle LaTeX-style _{} notation", () => {
55+
const html = highlightRA("σ_{age > 20}(Person)");
56+
// Should contain faint underscore and brackets
57+
expect(html).toContain("opacity: 0.4");
58+
expect(html).toContain("font-style: italic"); // subscript content
59+
});
60+
61+
it("should highlight keyword operators", () => {
62+
const html = highlightRA("sigma[age > 20](Person)");
63+
expect(html).toContain("color: #7c3aed"); // operator color
64+
expect(html).toContain("sigma");
65+
});
66+
67+
it("should highlight binary keyword operators", () => {
68+
const html = highlightRA("A cross B");
69+
expect(html).toContain("color: #7c3aed");
70+
expect(html).toContain("cross");
71+
});
72+
73+
it("should preserve newlines", () => {
74+
const html = highlightRA("A <- Person\nA");
75+
expect(html).toContain("\n");
76+
});
77+
78+
it("should handle implicit subscripts with whitespace", () => {
79+
const html = highlightRA("σ age > 20 (Person)");
80+
expect(html).toContain("font-style: italic"); // subscript styling for implicit content
81+
});
82+
});

0 commit comments

Comments
 (0)