From 18fec492d5fc4faad0da69cc72e009bb58508a6e Mon Sep 17 00:00:00 2001 From: keks137 Date: Tue, 16 Jun 2026 14:36:32 +0200 Subject: [PATCH 1/3] c highlighting --- assets/highlighting-tests/c.c | 118 ++++++++++++++++++++++++++++++++++ crates/edit/src/buffer/mod.rs | 2 + crates/lsh-bin/src/main.rs | 2 + crates/lsh/definitions/c.lsh | 58 +++++++++++++++++ 4 files changed, 180 insertions(+) create mode 100644 assets/highlighting-tests/c.c create mode 100644 crates/lsh/definitions/c.lsh diff --git a/assets/highlighting-tests/c.c b/assets/highlighting-tests/c.c new file mode 100644 index 00000000000..eea29b838ef --- /dev/null +++ b/assets/highlighting-tests/c.c @@ -0,0 +1,118 @@ +// Comments +// Single-line comment + +/* + * Multi-line + * comment + */ + +// Preprocessor directives + +#include +#include "foo.h" +#ifndef HEADER_GUARD +#define HEADER_GUARD +#endif +#define MACRO(arg) (void)arg; + +// Numbers +42; +3.14; +3.14f; +.5; +1e10; +-12L; +15lU; +0xff; +0xFF; +0b1010; +0o77; + +// Constants +true; +false; +NULL; + +// Strings +"double quotes with escape: \" \n \t \\"; + +// Control flow keywords +if (true) { +} else if (false) { +} else { +} + +for (int i = 0; i < 10; i++) { + if (i == 5) + continue; + if (i == 8) + break; +} + +while (false) { +} +do { +} while (false); + +switch (42) { +case 1: + break; +default: + break; +} + +goto cleanup; + +cleanup : + + int a = 1; +signed long long b = 2; +unsigned short int c = 3; +float pi = 3.14f; +double tau = 6.283F; +short foo = 42; +char c = 'c'; +char newline = '\n'; +const char *text = "Hello World!"; + +int add(int a, int b) +{ + return a + b; +} + +void greet(const char *name) +{ + printf("Hello %s!\n", name); +} + +static inline do_nothing() +{ + static int a = 3; + volatile long long b = 4; +} + +typedef struct { + char r; + char g; + char b; + char a; +} Color; + +Color cornflower = { 100, 149, 237, 255 }; +Color blue = { .b = 255, .a = 255 }; + +enum EntityKind { + None = 0, + Bear, + Bee, + Dog, + Cat, +}; + +(void)0; + +int main(int argc, char *argv[]) +{ + greet("lhecker"); + return 0; +} diff --git a/crates/edit/src/buffer/mod.rs b/crates/edit/src/buffer/mod.rs index 9277192e500..eb3d1ad65ce 100644 --- a/crates/edit/src/buffer/mod.rs +++ b/crates/edit/src/buffer/mod.rs @@ -2173,6 +2173,8 @@ impl TextBuffer { HighlightKind::Method => Some(IndexedColor::BrightYellow), HighlightKind::String => Some(IndexedColor::BrightRed), HighlightKind::Variable => Some(IndexedColor::BrightCyan), + HighlightKind::Preprocessor => Some(IndexedColor::Cyan), + HighlightKind::TypeLanguage => Some(IndexedColor::Green), HighlightKind::ConstantLanguage => Some(IndexedColor::BrightBlue), HighlightKind::ConstantNumeric => Some(IndexedColor::BrightGreen), HighlightKind::KeywordControl => Some(IndexedColor::BrightMagenta), diff --git a/crates/lsh-bin/src/main.rs b/crates/lsh-bin/src/main.rs index 68547c431d5..e2f6766cea5 100644 --- a/crates/lsh-bin/src/main.rs +++ b/crates/lsh-bin/src/main.rs @@ -121,7 +121,9 @@ fn run_render(generator: lsh::compiler::Generator, path: &Path) -> anyhow::Resul "method" => "\x1b[93m", // Bright Yellow "string" => "\x1b[91m", // Bright Red "variable" => "\x1b[96m", // Bright Cyan + "preprocessor" => "\x1b[36m", // Cyan + "type.language" => "\x1b[32m", // Green "constant.language" => "\x1b[94m", // Bright Blue "constant.numeric" => "\x1b[92m", // Bright Green "keyword.control" => "\x1b[95m", // Bright Magenta diff --git a/crates/lsh/definitions/c.lsh b/crates/lsh/definitions/c.lsh new file mode 100644 index 00000000000..7b50e67b446 --- /dev/null +++ b/crates/lsh/definitions/c.lsh @@ -0,0 +1,58 @@ +#[display_name = "C"] +#[path = "**/*.c"] +#[path = "**/*.h"] +pub fn c() { + + until /$/ { + yield other; + + if /\/\/.*/ { + yield comment; + } else if /\/\*/ { + loop { + yield comment; + await input; + if /\*\// { + yield comment; + break; + } + } + } else if /#.*/ { + yield preprocessor; + } else if /"/ { + until /$/ { + yield string; + if /\\./ {} + else if /"/ { yield string; break; } + await input; + } + } else if /'/ { // technically not how '' works in C, but means we don't have to deal with stuff like '\n' manually + until /$/ { + yield string; + if /\\./ {} + else if /'/ { yield string; break; } + await input; + } + } else if /(?:break|case|continue|default|do|else|for|if|return|switch|while|goto)\>/ { + yield keyword.control; + } else if /(?:struct|enum|typedef|const|static|inline|volatile|sizeof|extern|register|restrict)\>/ { + yield keyword.other; + } else if /(?:void|int|long|char|float|double|signed|unsigned|short)\>/ { + yield type.language; + } else if /(?:true|false|NULL)\>/ { + yield constant.language; + } else if /(?i:-?(?:0x[\da-fA-F_]+|0b[01_]+|0o[0-7_]+|[\d_]+\.?[\d_]*|\.[\d_]+)(?:e[+-]?[\d_]+)?)(?:[fF]|[uU][lL]?|[lL][uU]?)?/ { + if /\w+/ { + // Invalid numeric literal + } else { + yield constant.numeric; + } + } else if /(\w+)\s*\(/ { + yield $1 as method; + } else if /\w+/ { + // Gobble word chars to align the next iteration on a word boundary. + } + + yield other; + } +} From 1b6c9d6107233d22783e6386a1d40b8152cf9c2e Mon Sep 17 00:00:00 2001 From: keks137 Date: Tue, 16 Jun 2026 14:49:41 +0200 Subject: [PATCH 2/3] add unions and some more --- assets/highlighting-tests/c.c | 2 +- crates/lsh/definitions/c.lsh | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/assets/highlighting-tests/c.c b/assets/highlighting-tests/c.c index eea29b838ef..963598b3c22 100644 --- a/assets/highlighting-tests/c.c +++ b/assets/highlighting-tests/c.c @@ -65,7 +65,7 @@ goto cleanup; cleanup : - int a = 1; +int a = 1; signed long long b = 2; unsigned short int c = 3; float pi = 3.14f; diff --git a/crates/lsh/definitions/c.lsh b/crates/lsh/definitions/c.lsh index 7b50e67b446..211ab8c272e 100644 --- a/crates/lsh/definitions/c.lsh +++ b/crates/lsh/definitions/c.lsh @@ -35,7 +35,7 @@ pub fn c() { } } else if /(?:break|case|continue|default|do|else|for|if|return|switch|while|goto)\>/ { yield keyword.control; - } else if /(?:struct|enum|typedef|const|static|inline|volatile|sizeof|extern|register|restrict)\>/ { + } else if /(?:struct|enum|union|alignas|alignof|static_assert|thread_local|_Static_assert|_Thread_local|typedef|const|static|inline|volatile|sizeof|extern|register|restrict)\>/ { yield keyword.other; } else if /(?:void|int|long|char|float|double|signed|unsigned|short)\>/ { yield type.language; From af754f408a66d7d23d033f421e331732fbc5d4fd Mon Sep 17 00:00:00 2001 From: keks137 Date: Tue, 16 Jun 2026 14:58:08 +0200 Subject: [PATCH 3/3] add markdown --- assets/highlighting-tests/markdown.md | 9 +++++++++ crates/lsh/definitions/markdown.lsh | 10 ++++++++++ 2 files changed, 19 insertions(+) diff --git a/assets/highlighting-tests/markdown.md b/assets/highlighting-tests/markdown.md index 555b182f961..faaffcc444c 100644 --- a/assets/highlighting-tests/markdown.md +++ b/assets/highlighting-tests/markdown.md @@ -84,3 +84,12 @@ export function greet(name) { def greet(name: str) -> str: return f"hello {name}" ``` + +```c +#include + +int main() { + printf("Hello, world\n"); + return 0; +} +``` diff --git a/crates/lsh/definitions/markdown.lsh b/crates/lsh/definitions/markdown.lsh index 77a005fe925..1e85b7f1bfd 100644 --- a/crates/lsh/definitions/markdown.lsh +++ b/crates/lsh/definitions/markdown.lsh @@ -50,6 +50,16 @@ pub fn markdown() { if /.*/ {} } } + } else if /(?i:c)/ { + loop { + await input; + if /\s*```/ { + return; + } else { + c(); + if /.*/ {} + } + } } else if /(?i:json)/ { loop { await input;