From 6277ba1ff9a43a19b4c68bd00c15c87530ddee62 Mon Sep 17 00:00:00 2001 From: dev693-ai Date: Fri, 26 Jun 2026 15:18:15 +0200 Subject: [PATCH] feat: add C# syntax highlighting (#1651) Wire the @replit/codemirror-lang-csharp Lezer grammar into @getpaseo/highlight following the existing per-language pattern (Java/Rust/Swift). Map the cs extension to the parser and alias the csharp and c# markdown fence names so fenced code blocks highlight. Register csharpLanguage.parser rather than the package's raw parser export: the highlight styleTags are applied only inside csharpLanguage (via parser.configure), so the raw parser would parse but render unstyled. Closes #1527 Co-authored-by: Clemens Wagner Co-authored-by: Claude Opus 4.8 (1M context) --- package-lock.json | 16 ++++++++++++++++ .../src/components/highlighted-code-block.tsx | 2 ++ packages/highlight/package.json | 1 + .../highlight/src/__tests__/highlighter.test.ts | 13 +++++++++++++ packages/highlight/src/__tests__/parsers.test.ts | 2 ++ packages/highlight/src/parsers.ts | 3 +++ 6 files changed, 37 insertions(+) diff --git a/package-lock.json b/package-lock.json index b6729d15d..1949e2c0e 100644 --- a/package-lock.json +++ b/package-lock.json @@ -11166,6 +11166,21 @@ "nanoid": "^3.3.11" } }, + "node_modules/@replit/codemirror-lang-csharp": { + "version": "6.2.0", + "resolved": "https://registry.npmjs.org/@replit/codemirror-lang-csharp/-/codemirror-lang-csharp-6.2.0.tgz", + "integrity": "sha512-6utbaWkoymhoAXj051mkRp+VIJlpwUgCX9Toevz3YatiZsz512fw3OVCedXQx+WcR0wb6zVHjChnuxqfCLtFVQ==", + "license": "MIT", + "peerDependencies": { + "@codemirror/autocomplete": "^6.0.0", + "@codemirror/language": "^6.0.0", + "@codemirror/state": "^6.0.0", + "@codemirror/view": "^6.0.0", + "@lezer/common": "^1.0.0", + "@lezer/highlight": "^1.0.0", + "@lezer/lr": "^1.0.0" + } + }, "node_modules/@rollup/pluginutils": { "version": "5.3.0", "resolved": "https://registry.npmjs.org/@rollup/pluginutils/-/pluginutils-5.3.0.tgz", @@ -36989,6 +37004,7 @@ "@lezer/rust": "^1.0.2", "@lezer/xml": "^1.0.6", "@lezer/yaml": "^1.0.4", + "@replit/codemirror-lang-csharp": "^6.2.0", "lezer-elixir": "^1.1.2" }, "devDependencies": { diff --git a/packages/app/src/components/highlighted-code-block.tsx b/packages/app/src/components/highlighted-code-block.tsx index c61e81db6..dd3200b3b 100644 --- a/packages/app/src/components/highlighted-code-block.tsx +++ b/packages/app/src/components/highlighted-code-block.tsx @@ -29,6 +29,8 @@ const LANGUAGE_ALIASES: Record = { rust: "rs", golang: "go", "c++": "cpp", + csharp: "cs", + "c#": "cs", objc: "m", "objective-c": "m", markdown: "md", diff --git a/packages/highlight/package.json b/packages/highlight/package.json index ef815e5ab..b69e5171a 100644 --- a/packages/highlight/package.json +++ b/packages/highlight/package.json @@ -44,6 +44,7 @@ "@lezer/rust": "^1.0.2", "@lezer/xml": "^1.0.6", "@lezer/yaml": "^1.0.4", + "@replit/codemirror-lang-csharp": "^6.2.0", "lezer-elixir": "^1.1.2" }, "devDependencies": { diff --git a/packages/highlight/src/__tests__/highlighter.test.ts b/packages/highlight/src/__tests__/highlighter.test.ts index 74bc49bbd..83bebd8ba 100644 --- a/packages/highlight/src/__tests__/highlighter.test.ts +++ b/packages/highlight/src/__tests__/highlighter.test.ts @@ -62,6 +62,19 @@ describe("highlightCode", () => { expect(stringToken?.style).toBe("string"); }); + it("highlights C# code", () => { + const code = 'class Greeter {\n string message = "hello";\n}'; + const result = highlightCode(code, "test.cs"); + + expect(result).toHaveLength(3); + + const classToken = result[0].find((t) => t.text === "class"); + expect(classToken?.style).toBe("keyword"); + + const stringToken = result[1].find((t) => t.text.includes("hello")); + expect(stringToken?.style).toBe("string"); + }); + it("highlights TSX code with correct dialect", () => { const code = 'const el =
hello
;'; const result = highlightCode(code, "test.tsx"); diff --git a/packages/highlight/src/__tests__/parsers.test.ts b/packages/highlight/src/__tests__/parsers.test.ts index a04ad64c6..d229df79c 100644 --- a/packages/highlight/src/__tests__/parsers.test.ts +++ b/packages/highlight/src/__tests__/parsers.test.ts @@ -15,6 +15,7 @@ describe("isLanguageSupported", () => { expect(isLanguageSupported("test.java")).toBe(true); expect(isLanguageSupported("test.swift")).toBe(true); expect(isLanguageSupported("test.dart")).toBe(true); + expect(isLanguageSupported("test.cs")).toBe(true); expect(isLanguageSupported("test.ex")).toBe(true); }); @@ -53,6 +54,7 @@ describe("getSupportedExtensions", () => { expect(extensions).toContain("rs"); expect(extensions).toContain("swift"); expect(extensions).toContain("dart"); + expect(extensions).toContain("cs"); expect(extensions).toContain("json"); }); }); diff --git a/packages/highlight/src/parsers.ts b/packages/highlight/src/parsers.ts index a9df5ebe2..907e44988 100644 --- a/packages/highlight/src/parsers.ts +++ b/packages/highlight/src/parsers.ts @@ -14,6 +14,7 @@ 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 { csharpLanguage } from "@replit/codemirror-lang-csharp"; import { parser as elixirParser } from "lezer-elixir"; import type { Parser } from "@lezer/common"; @@ -62,6 +63,8 @@ const parsersByExtension: Record = { swift: StreamLanguage.define(swift).parser, // Dart dart: StreamLanguage.define(dart).parser, + // C# + cs: csharpLanguage.parser, // Elixir ex: elixirParser, exs: elixirParser,