From 7609ccee4c34868a1c1ec541d0b0f486f6a6e89e Mon Sep 17 00:00:00 2001 From: Mohamed Boudra Date: Thu, 19 Mar 2026 15:44:04 +0700 Subject: [PATCH] feat: expand diff syntax highlighting languages --- package-lock.json | 91 ++++++++ packages/app/package.json | 7 + .../app/src/utils/diff-highlighter.test.ts | 217 ++++++++++++++++++ packages/app/src/utils/syntax-highlighter.ts | 30 +++ packages/server/package.json | 7 + .../src/server/utils/diff-highlighter.test.ts | 217 ++++++++++++++++++ .../src/server/utils/syntax-highlighter.ts | 30 +++ 7 files changed, 599 insertions(+) diff --git a/package-lock.json b/package-lock.json index f8f1be757..186649dac 100644 --- a/package-lock.json +++ b/package-lock.json @@ -7186,6 +7186,17 @@ "integrity": "sha512-6YRVG9vBkaY7p1IVxL4s44n5nUnaNnGM2/AckNgYOnxTG2kWh1vR8BMxPseWPjRNpb5VtXnMpeYAEAADoRV1Iw==", "license": "MIT" }, + "node_modules/@lezer/cpp": { + "version": "1.1.5", + "resolved": "https://registry.npmjs.org/@lezer/cpp/-/cpp-1.1.5.tgz", + "integrity": "sha512-DIhSXmYtJKLehrjzDFN+2cPt547ySQ41nA8yqcDf/GxMc+YM736xqltFkvADL2M0VebU5I+3+4ks2Vv+Kyq3Aw==", + "license": "MIT", + "dependencies": { + "@lezer/common": "^1.2.0", + "@lezer/highlight": "^1.0.0", + "@lezer/lr": "^1.0.0" + } + }, "node_modules/@lezer/css": { "version": "1.3.1", "resolved": "https://registry.npmjs.org/@lezer/css/-/css-1.3.1.tgz", @@ -7197,6 +7208,17 @@ "@lezer/lr": "^1.3.0" } }, + "node_modules/@lezer/go": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/@lezer/go/-/go-1.0.1.tgz", + "integrity": "sha512-xToRsYxwsgJNHTgNdStpcvmbVuKxTapV0dM0wey1geMMRc9aggoVyKgzYp41D2/vVOx+Ii4hmE206kvxIXBVXQ==", + "license": "MIT", + "dependencies": { + "@lezer/common": "^1.2.0", + "@lezer/highlight": "^1.0.0", + "@lezer/lr": "^1.3.0" + } + }, "node_modules/@lezer/highlight": { "version": "1.2.3", "resolved": "https://registry.npmjs.org/@lezer/highlight/-/highlight-1.2.3.tgz", @@ -7217,6 +7239,17 @@ "@lezer/lr": "^1.0.0" } }, + "node_modules/@lezer/java": { + "version": "1.1.3", + "resolved": "https://registry.npmjs.org/@lezer/java/-/java-1.1.3.tgz", + "integrity": "sha512-yHquUfujwg6Yu4Fd1GNHCvidIvJwi/1Xu2DaKl/pfWIA2c1oXkVvawH3NyXhCaFx4OdlYBVX5wvz2f7Aoa/4Xw==", + "license": "MIT", + "dependencies": { + "@lezer/common": "^1.2.0", + "@lezer/highlight": "^1.0.0", + "@lezer/lr": "^1.0.0" + } + }, "node_modules/@lezer/javascript": { "version": "1.5.4", "resolved": "https://registry.npmjs.org/@lezer/javascript/-/javascript-1.5.4.tgz", @@ -7258,6 +7291,17 @@ "@lezer/highlight": "^1.0.0" } }, + "node_modules/@lezer/php": { + "version": "1.0.5", + "resolved": "https://registry.npmjs.org/@lezer/php/-/php-1.0.5.tgz", + "integrity": "sha512-W7asp9DhM6q0W6DYNwIkLSKOvxlXRrif+UXBMxzsJUuqmhE7oVU+gS3THO4S/Puh7Xzgm858UNaFi6dxTP8dJA==", + "license": "MIT", + "dependencies": { + "@lezer/common": "^1.2.0", + "@lezer/highlight": "^1.0.0", + "@lezer/lr": "^1.1.0" + } + }, "node_modules/@lezer/python": { "version": "1.1.18", "resolved": "https://registry.npmjs.org/@lezer/python/-/python-1.1.18.tgz", @@ -7269,6 +7313,39 @@ "@lezer/lr": "^1.0.0" } }, + "node_modules/@lezer/rust": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/@lezer/rust/-/rust-1.0.2.tgz", + "integrity": "sha512-Lz5sIPBdF2FUXcWeCu1//ojFAZqzTQNRga0aYv6dYXqJqPfMdCAI0NzajWUd4Xijj1IKJLtjoXRPMvTKWBcqKg==", + "license": "MIT", + "dependencies": { + "@lezer/common": "^1.2.0", + "@lezer/highlight": "^1.0.0", + "@lezer/lr": "^1.0.0" + } + }, + "node_modules/@lezer/xml": { + "version": "1.0.6", + "resolved": "https://registry.npmjs.org/@lezer/xml/-/xml-1.0.6.tgz", + "integrity": "sha512-CdDwirL0OEaStFue/66ZmFSeppuL6Dwjlk8qk153mSQwiSH/Dlri4GNymrNWnUmPl2Um7QfV1FO9KFUyX3Twww==", + "license": "MIT", + "dependencies": { + "@lezer/common": "^1.2.0", + "@lezer/highlight": "^1.0.0", + "@lezer/lr": "^1.0.0" + } + }, + "node_modules/@lezer/yaml": { + "version": "1.0.4", + "resolved": "https://registry.npmjs.org/@lezer/yaml/-/yaml-1.0.4.tgz", + "integrity": "sha512-2lrrHqxalACEbxIbsjhqGpSW8kWpUKuY6RHgnSAFZa6qK62wvnPxA8hGOwOoDbwHcOFs5M4o27mjGu+P7TvBmw==", + "license": "MIT", + "dependencies": { + "@lezer/common": "^1.2.0", + "@lezer/highlight": "^1.0.0", + "@lezer/lr": "^1.4.0" + } + }, "node_modules/@napi-rs/wasm-runtime": { "version": "1.1.1", "resolved": "https://registry.npmjs.org/@napi-rs/wasm-runtime/-/wasm-runtime-1.1.1.tgz", @@ -31462,13 +31539,20 @@ "@gorhom/bottom-sheet": "^5.2.6", "@gorhom/portal": "^1.0.14", "@lezer/common": "^1.5.0", + "@lezer/cpp": "^1.1.5", "@lezer/css": "^1.3.0", + "@lezer/go": "^1.0.1", "@lezer/highlight": "^1.2.3", "@lezer/html": "^1.3.13", + "@lezer/java": "^1.1.3", "@lezer/javascript": "^1.5.4", "@lezer/json": "^1.0.3", "@lezer/markdown": "^1.6.2", + "@lezer/php": "^1.0.5", "@lezer/python": "^1.1.18", + "@lezer/rust": "^1.0.2", + "@lezer/xml": "^1.0.6", + "@lezer/yaml": "^1.0.4", "@react-native-async-storage/async-storage": "2.2.0", "@react-native-masked-view/masked-view": "^0.3.2", "@react-native/normalize-colors": "^0.81.5", @@ -31684,13 +31768,20 @@ "@deepgram/sdk": "^3.4.0", "@getpaseo/relay": "0.1.30", "@lezer/common": "^1.5.0", + "@lezer/cpp": "^1.1.5", "@lezer/css": "^1.3.0", + "@lezer/go": "^1.0.1", "@lezer/highlight": "^1.2.3", "@lezer/html": "^1.3.13", + "@lezer/java": "^1.1.3", "@lezer/javascript": "^1.5.4", "@lezer/json": "^1.0.3", "@lezer/markdown": "^1.6.2", + "@lezer/php": "^1.0.5", "@lezer/python": "^1.1.18", + "@lezer/rust": "^1.0.2", + "@lezer/xml": "^1.0.6", + "@lezer/yaml": "^1.0.4", "@modelcontextprotocol/sdk": "^1.20.1", "@opencode-ai/sdk": "1.2.6", "@sctg/sentencepiece-js": "^1.1.0", diff --git a/packages/app/package.json b/packages/app/package.json index 482d88b30..39c119780 100644 --- a/packages/app/package.json +++ b/packages/app/package.json @@ -38,13 +38,20 @@ "@gorhom/bottom-sheet": "^5.2.6", "@gorhom/portal": "^1.0.14", "@lezer/common": "^1.5.0", + "@lezer/cpp": "^1.1.5", "@lezer/css": "^1.3.0", + "@lezer/go": "^1.0.1", "@lezer/highlight": "^1.2.3", "@lezer/html": "^1.3.13", + "@lezer/java": "^1.1.3", "@lezer/javascript": "^1.5.4", "@lezer/json": "^1.0.3", "@lezer/markdown": "^1.6.2", + "@lezer/php": "^1.0.5", "@lezer/python": "^1.1.18", + "@lezer/rust": "^1.0.2", + "@lezer/xml": "^1.0.6", + "@lezer/yaml": "^1.0.4", "@react-native-async-storage/async-storage": "2.2.0", "@react-native-masked-view/masked-view": "^0.3.2", "@react-native/normalize-colors": "^0.81.5", diff --git a/packages/app/src/utils/diff-highlighter.test.ts b/packages/app/src/utils/diff-highlighter.test.ts index 30fa3a816..10e52cf74 100644 --- a/packages/app/src/utils/diff-highlighter.test.ts +++ b/packages/app/src/utils/diff-highlighter.test.ts @@ -59,6 +59,105 @@ index 1234567..0000000 -export { legacy }; `; +const RUST_DIFF = `diff --git a/lib.rs b/lib.rs +index 1234567..abcdefg 100644 +--- a/lib.rs ++++ b/lib.rs +@@ -1,4 +1,4 @@ + fn main() { +- let version = 1; ++ let version = 2; + println!("Hello, world!"); + } +`; + +const C_DIFF = `diff --git a/example.c b/example.c +index 1234567..abcdefg 100644 +--- a/example.c ++++ b/example.c +@@ -1,4 +1,4 @@ + int main(void) { +- int version = 1; ++ int version = 2; + return version; + } +`; + +const JAVA_DIFF = `diff --git a/Main.java b/Main.java +index 1234567..abcdefg 100644 +--- a/Main.java ++++ b/Main.java +@@ -1,5 +1,5 @@ + public class Main { + public static void main(String[] args) { +- int version = 1; ++ int version = 2; + } + } +`; + +const OBJECTIVE_C_DIFF = `diff --git a/AppDelegate.m b/AppDelegate.m +index 1234567..abcdefg 100644 +--- a/AppDelegate.m ++++ b/AppDelegate.m +@@ -1,4 +1,4 @@ + int main(void) { +- int version = 1; ++ int version = 2; + return version; + } +`; + +const GO_DIFF = `diff --git a/main.go b/main.go +index 1234567..abcdefg 100644 +--- a/main.go ++++ b/main.go +@@ -1,7 +1,7 @@ + package main + + import "fmt" + + func main() { +- version := 1 ++ version := 2 + fmt.Println(version) + } +`; + +const PHP_DIFF = `diff --git a/index.php b/index.php +index 1234567..abcdefg 100644 +--- a/index.php ++++ b/index.php +@@ -1,4 +1,4 @@ + +`; + +const YAML_DIFF = `diff --git a/config.yaml b/config.yaml +index 1234567..abcdefg 100644 +--- a/config.yaml ++++ b/config.yaml +@@ -1,3 +1,3 @@ + app: paseo +-count: 1 ++count: 2 + enabled: true +`; + +const XML_DIFF = `diff --git a/config.xml b/config.xml +index 1234567..abcdefg 100644 +--- a/config.xml ++++ b/config.xml +@@ -1,3 +1,3 @@ + +- 1 ++ 2 + +`; + describe("parseDiff", () => { it("parses a simple diff with one hunk", () => { const files = parseDiff(SIMPLE_DIFF); @@ -251,6 +350,124 @@ describe("highlightDiffFile", () => { expect(highlighted.hunks[0].lines[1].tokens).toBeUndefined(); expect(highlighted.hunks[0].lines[2].tokens).toBeUndefined(); }); + + it("adds syntax highlighting tokens to Rust code", () => { + const files = parseDiff(RUST_DIFF); + const highlighted = highlightDiffFile(files[0]); + const hunk = highlighted.hunks[0]; + + const fnLine = hunk.lines[1]; + expect(fnLine.tokens).toBeDefined(); + expect(fnLine.tokens!.some((t) => t.text === "fn" && t.style === "keyword")).toBe(true); + + const addedLine = hunk.lines.find( + (line) => line.type === "add" && line.content.includes("version") + ); + expect(addedLine?.tokens).toBeDefined(); + expect( + addedLine!.tokens!.some((t) => t.text === "2" && t.style === "number") + ).toBe(true); + }); + + it("adds syntax highlighting tokens to C code", () => { + const files = parseDiff(C_DIFF); + const highlighted = highlightDiffFile(files[0]); + const hunk = highlighted.hunks[0]; + + const mainLine = hunk.lines[1]; + expect(mainLine.tokens).toBeDefined(); + expect(mainLine.tokens!.length).toBeGreaterThan(0); + + const addedLine = hunk.lines.find( + (line) => line.type === "add" && line.content.includes("version") + ); + expect(addedLine?.tokens).toBeDefined(); + expect( + addedLine!.tokens!.some((t) => t.text === "2" && t.style === "number") + ).toBe(true); + }); + + it("adds syntax highlighting tokens to Java code", () => { + const files = parseDiff(JAVA_DIFF); + const highlighted = highlightDiffFile(files[0]); + const hunk = highlighted.hunks[0]; + + const classLine = hunk.lines[1]; + expect(classLine.tokens).toBeDefined(); + expect( + classLine.tokens!.some((t) => t.text === "public" && t.style === "keyword") + ).toBe(true); + + const addedLine = hunk.lines.find( + (line) => line.type === "add" && line.content.includes("version") + ); + expect(addedLine?.tokens).toBeDefined(); + expect( + addedLine!.tokens!.some((t) => t.text === "2" && t.style === "number") + ).toBe(true); + }); + + it("adds syntax highlighting tokens to Objective-C file extensions", () => { + const files = parseDiff(OBJECTIVE_C_DIFF); + const highlighted = highlightDiffFile(files[0]); + const hunk = highlighted.hunks[0]; + + const addedLine = hunk.lines.find( + (line) => line.type === "add" && line.content.includes("version") + ); + expect(addedLine?.tokens).toBeDefined(); + expect( + addedLine!.tokens!.some((t) => t.text === "2" && t.style === "number") + ).toBe(true); + }); + + it("adds syntax highlighting tokens to Go code", () => { + const files = parseDiff(GO_DIFF); + const highlighted = highlightDiffFile(files[0]); + const addedLine = highlighted.hunks[0].lines.find( + (line) => line.type === "add" && line.content.includes("version") + ); + + expect(addedLine?.tokens).toBeDefined(); + expect( + addedLine!.tokens!.some((t) => t.text === "2" && t.style === "number") + ).toBe(true); + }); + + it("adds syntax highlighting tokens to PHP code", () => { + const files = parseDiff(PHP_DIFF); + const highlighted = highlightDiffFile(files[0]); + const addedLine = highlighted.hunks[0].lines.find( + (line) => line.type === "add" && line.content.includes("$version") + ); + + expect(addedLine?.tokens).toBeDefined(); + expect( + addedLine!.tokens!.some((t) => t.text === "2" && t.style === "number") + ).toBe(true); + }); + + it("adds syntax highlighting tokens to YAML code", () => { + const files = parseDiff(YAML_DIFF); + const highlighted = highlightDiffFile(files[0]); + const addedLine = highlighted.hunks[0].lines.find( + (line) => line.type === "add" && line.content.includes("count") + ); + + expect(addedLine?.tokens).toBeDefined(); + expect(addedLine!.tokens!.length).toBeGreaterThan(0); + }); + + it("adds syntax highlighting tokens to XML code", () => { + const files = parseDiff(XML_DIFF); + const highlighted = highlightDiffFile(files[0]); + const addedLine = highlighted.hunks[0].lines.find( + (line) => line.type === "add" && line.content.includes("") + ); + + expect(addedLine?.tokens).toBeDefined(); + expect(addedLine!.tokens!.some((t) => t.style === "tag")).toBe(true); + }); }); describe("parseAndHighlightDiff", () => { diff --git a/packages/app/src/utils/syntax-highlighter.ts b/packages/app/src/utils/syntax-highlighter.ts index e0f38a455..89cae3952 100644 --- a/packages/app/src/utils/syntax-highlighter.ts +++ b/packages/app/src/utils/syntax-highlighter.ts @@ -2,9 +2,16 @@ import { highlightTree, tagHighlighter, tags } from "@lezer/highlight"; import { parser as jsParser } from "@lezer/javascript"; import { parser as jsonParser } from "@lezer/json"; import { parser as cssParser } from "@lezer/css"; +import { parser as cppParser } from "@lezer/cpp"; +import { parser as goParser } from "@lezer/go"; import { parser as htmlParser } from "@lezer/html"; +import { parser as javaParser } from "@lezer/java"; import { parser as pythonParser } from "@lezer/python"; import { parser as markdownParser } from "@lezer/markdown"; +import { parser as phpParser } from "@lezer/php"; +import { parser as rustParser } from "@lezer/rust"; +import { parser as xmlParser } from "@lezer/xml"; +import { parser as yamlParser } from "@lezer/yaml"; import { parser as elixirParser } from "lezer-elixir"; import type { Parser } from "@lezer/common"; @@ -17,6 +24,16 @@ const parsersByExtension: Record = { tsx: jsParser.configure({ dialect: "tsx jsx" }), mjs: jsParser, cjs: jsParser, + // C / C++ / Objective-C + c: cppParser, + h: cppParser, + cc: cppParser, + cpp: cppParser, + cxx: cppParser, + hpp: cppParser, + hxx: cppParser, + m: cppParser, + mm: cppParser, // JSON json: jsonParser, // CSS @@ -25,8 +42,21 @@ const parsersByExtension: Record = { // HTML html: htmlParser, htm: htmlParser, + // XML + xml: xmlParser, + // Java + java: javaParser, // Python py: pythonParser, + // Go + go: goParser, + // PHP + php: phpParser, + // YAML + yaml: yamlParser, + yml: yamlParser, + // Rust + rs: rustParser, // Elixir ex: elixirParser, exs: elixirParser, diff --git a/packages/server/package.json b/packages/server/package.json index b93f8a840..5d6c2cd4f 100644 --- a/packages/server/package.json +++ b/packages/server/package.json @@ -66,13 +66,20 @@ "@deepgram/sdk": "^3.4.0", "@getpaseo/relay": "0.1.30", "@lezer/common": "^1.5.0", + "@lezer/cpp": "^1.1.5", "@lezer/css": "^1.3.0", + "@lezer/go": "^1.0.1", "@lezer/highlight": "^1.2.3", "@lezer/html": "^1.3.13", + "@lezer/java": "^1.1.3", "@lezer/javascript": "^1.5.4", "@lezer/json": "^1.0.3", "@lezer/markdown": "^1.6.2", + "@lezer/php": "^1.0.5", "@lezer/python": "^1.1.18", + "@lezer/rust": "^1.0.2", + "@lezer/xml": "^1.0.6", + "@lezer/yaml": "^1.0.4", "@modelcontextprotocol/sdk": "^1.20.1", "@opencode-ai/sdk": "1.2.6", "@sctg/sentencepiece-js": "^1.1.0", diff --git a/packages/server/src/server/utils/diff-highlighter.test.ts b/packages/server/src/server/utils/diff-highlighter.test.ts index c0b61eeae..fe5ac629e 100644 --- a/packages/server/src/server/utils/diff-highlighter.test.ts +++ b/packages/server/src/server/utils/diff-highlighter.test.ts @@ -57,6 +57,105 @@ index 1234567..0000000 -export { legacy }; `; +const RUST_DIFF = `diff --git a/lib.rs b/lib.rs +index 1234567..abcdefg 100644 +--- a/lib.rs ++++ b/lib.rs +@@ -1,4 +1,4 @@ + fn main() { +- let version = 1; ++ let version = 2; + println!("Hello, world!"); + } +`; + +const C_DIFF = `diff --git a/example.c b/example.c +index 1234567..abcdefg 100644 +--- a/example.c ++++ b/example.c +@@ -1,4 +1,4 @@ + int main(void) { +- int version = 1; ++ int version = 2; + return version; + } +`; + +const JAVA_DIFF = `diff --git a/Main.java b/Main.java +index 1234567..abcdefg 100644 +--- a/Main.java ++++ b/Main.java +@@ -1,5 +1,5 @@ + public class Main { + public static void main(String[] args) { +- int version = 1; ++ int version = 2; + } + } +`; + +const OBJECTIVE_C_DIFF = `diff --git a/AppDelegate.m b/AppDelegate.m +index 1234567..abcdefg 100644 +--- a/AppDelegate.m ++++ b/AppDelegate.m +@@ -1,4 +1,4 @@ + int main(void) { +- int version = 1; ++ int version = 2; + return version; + } +`; + +const GO_DIFF = `diff --git a/main.go b/main.go +index 1234567..abcdefg 100644 +--- a/main.go ++++ b/main.go +@@ -1,7 +1,7 @@ + package main + + import "fmt" + + func main() { +- version := 1 ++ version := 2 + fmt.Println(version) + } +`; + +const PHP_DIFF = `diff --git a/index.php b/index.php +index 1234567..abcdefg 100644 +--- a/index.php ++++ b/index.php +@@ -1,4 +1,4 @@ + +`; + +const YAML_DIFF = `diff --git a/config.yaml b/config.yaml +index 1234567..abcdefg 100644 +--- a/config.yaml ++++ b/config.yaml +@@ -1,3 +1,3 @@ + app: paseo +-count: 1 ++count: 2 + enabled: true +`; + +const XML_DIFF = `diff --git a/config.xml b/config.xml +index 1234567..abcdefg 100644 +--- a/config.xml ++++ b/config.xml +@@ -1,3 +1,3 @@ + +- 1 ++ 2 + +`; + describe("parseDiff", () => { it("parses a simple diff with one hunk", () => { const files = parseDiff(SIMPLE_DIFF); @@ -249,4 +348,122 @@ describe("highlightDiffFromHunks", () => { expect(highlighted.hunks[0].lines[1].tokens).toBeUndefined(); expect(highlighted.hunks[0].lines[2].tokens).toBeUndefined(); }); + + it("adds syntax highlighting tokens to Rust code", () => { + const files = parseDiff(RUST_DIFF); + const highlighted = highlightDiffFromHunks(files[0]); + const hunk = highlighted.hunks[0]; + + const fnLine = hunk.lines[1]; + expect(fnLine.tokens).toBeDefined(); + expect(fnLine.tokens!.some((t) => t.text === "fn" && t.style === "keyword")).toBe(true); + + const addedLine = hunk.lines.find( + (line) => line.type === "add" && line.content.includes("version") + ); + expect(addedLine?.tokens).toBeDefined(); + expect( + addedLine!.tokens!.some((t) => t.text === "2" && t.style === "number") + ).toBe(true); + }); + + it("adds syntax highlighting tokens to C code", () => { + const files = parseDiff(C_DIFF); + const highlighted = highlightDiffFromHunks(files[0]); + const hunk = highlighted.hunks[0]; + + const mainLine = hunk.lines[1]; + expect(mainLine.tokens).toBeDefined(); + expect(mainLine.tokens!.length).toBeGreaterThan(0); + + const addedLine = hunk.lines.find( + (line) => line.type === "add" && line.content.includes("version") + ); + expect(addedLine?.tokens).toBeDefined(); + expect( + addedLine!.tokens!.some((t) => t.text === "2" && t.style === "number") + ).toBe(true); + }); + + it("adds syntax highlighting tokens to Java code", () => { + const files = parseDiff(JAVA_DIFF); + const highlighted = highlightDiffFromHunks(files[0]); + const hunk = highlighted.hunks[0]; + + const classLine = hunk.lines[1]; + expect(classLine.tokens).toBeDefined(); + expect( + classLine.tokens!.some((t) => t.text === "public" && t.style === "keyword") + ).toBe(true); + + const addedLine = hunk.lines.find( + (line) => line.type === "add" && line.content.includes("version") + ); + expect(addedLine?.tokens).toBeDefined(); + expect( + addedLine!.tokens!.some((t) => t.text === "2" && t.style === "number") + ).toBe(true); + }); + + it("adds syntax highlighting tokens to Objective-C file extensions", () => { + const files = parseDiff(OBJECTIVE_C_DIFF); + const highlighted = highlightDiffFromHunks(files[0]); + const hunk = highlighted.hunks[0]; + + const addedLine = hunk.lines.find( + (line) => line.type === "add" && line.content.includes("version") + ); + expect(addedLine?.tokens).toBeDefined(); + expect( + addedLine!.tokens!.some((t) => t.text === "2" && t.style === "number") + ).toBe(true); + }); + + it("adds syntax highlighting tokens to Go code", () => { + const files = parseDiff(GO_DIFF); + const highlighted = highlightDiffFromHunks(files[0]); + const addedLine = highlighted.hunks[0].lines.find( + (line) => line.type === "add" && line.content.includes("version") + ); + + expect(addedLine?.tokens).toBeDefined(); + expect( + addedLine!.tokens!.some((t) => t.text === "2" && t.style === "number") + ).toBe(true); + }); + + it("adds syntax highlighting tokens to PHP code", () => { + const files = parseDiff(PHP_DIFF); + const highlighted = highlightDiffFromHunks(files[0]); + const addedLine = highlighted.hunks[0].lines.find( + (line) => line.type === "add" && line.content.includes("$version") + ); + + expect(addedLine?.tokens).toBeDefined(); + expect( + addedLine!.tokens!.some((t) => t.text === "2" && t.style === "number") + ).toBe(true); + }); + + it("adds syntax highlighting tokens to YAML code", () => { + const files = parseDiff(YAML_DIFF); + const highlighted = highlightDiffFromHunks(files[0]); + const addedLine = highlighted.hunks[0].lines.find( + (line) => line.type === "add" && line.content.includes("count") + ); + + expect(addedLine?.tokens).toBeDefined(); + expect(addedLine!.tokens!.length).toBeGreaterThan(0); + }); + + it("adds syntax highlighting tokens to XML code", () => { + const files = parseDiff(XML_DIFF); + const highlighted = highlightDiffFromHunks(files[0]); + const addedLine = highlighted.hunks[0].lines.find( + (line) => line.type === "add" && line.content.includes("") + ); + + expect(addedLine?.tokens).toBeDefined(); + expect(addedLine!.tokens!.some((t) => t.style === "tag")).toBe(true); + }); }); diff --git a/packages/server/src/server/utils/syntax-highlighter.ts b/packages/server/src/server/utils/syntax-highlighter.ts index c86a5a40e..89483ebbb 100644 --- a/packages/server/src/server/utils/syntax-highlighter.ts +++ b/packages/server/src/server/utils/syntax-highlighter.ts @@ -2,9 +2,16 @@ import { highlightTree, tagHighlighter, tags } from "@lezer/highlight"; import { parser as jsParser } from "@lezer/javascript"; import { parser as jsonParser } from "@lezer/json"; import { parser as cssParser } from "@lezer/css"; +import { parser as cppParser } from "@lezer/cpp"; +import { parser as goParser } from "@lezer/go"; import { parser as htmlParser } from "@lezer/html"; +import { parser as javaParser } from "@lezer/java"; import { parser as pythonParser } from "@lezer/python"; import { parser as markdownParser } from "@lezer/markdown"; +import { parser as phpParser } from "@lezer/php"; +import { parser as rustParser } from "@lezer/rust"; +import { parser as xmlParser } from "@lezer/xml"; +import { parser as yamlParser } from "@lezer/yaml"; import { parser as elixirParser } from "lezer-elixir"; import type { Parser } from "@lezer/common"; @@ -17,6 +24,16 @@ const parsersByExtension: Record = { tsx: jsParser.configure({ dialect: "ts jsx" }), mjs: jsParser, cjs: jsParser, + // C / C++ / Objective-C + c: cppParser, + h: cppParser, + cc: cppParser, + cpp: cppParser, + cxx: cppParser, + hpp: cppParser, + hxx: cppParser, + m: cppParser, + mm: cppParser, // JSON json: jsonParser, // CSS @@ -25,8 +42,21 @@ const parsersByExtension: Record = { // HTML html: htmlParser, htm: htmlParser, + // XML + xml: xmlParser, + // Java + java: javaParser, // Python py: pythonParser, + // Go + go: goParser, + // PHP + php: phpParser, + // YAML + yaml: yamlParser, + yml: yamlParser, + // Rust + rs: rustParser, // Elixir ex: elixirParser, exs: elixirParser,