From 25ab68554b71b43dcfb3a3ffaf8d8889c98c665c Mon Sep 17 00:00:00 2001 From: Lu'ay Date: Thu, 16 Apr 2026 11:18:57 +0100 Subject: [PATCH 1/7] Added comment highlighting for .cs --- assets/highlighting-tests/cs.cs | 7 +++++++ crates/lsh/definitions/cs.lsh | 22 ++++++++++++++++++++++ 2 files changed, 29 insertions(+) create mode 100644 assets/highlighting-tests/cs.cs create mode 100644 crates/lsh/definitions/cs.lsh diff --git a/assets/highlighting-tests/cs.cs b/assets/highlighting-tests/cs.cs new file mode 100644 index 00000000000..5579d066521 --- /dev/null +++ b/assets/highlighting-tests/cs.cs @@ -0,0 +1,7 @@ +// Comments +// Single-line comment + +/* +Multi-line +comment +*/ diff --git a/crates/lsh/definitions/cs.lsh b/crates/lsh/definitions/cs.lsh new file mode 100644 index 00000000000..126c278ea7f --- /dev/null +++ b/crates/lsh/definitions/cs.lsh @@ -0,0 +1,22 @@ +#[display_name = "C#"] +#[path = "**/*.cs"] +pub fn CSharp() { + until /$/ { + yield other; + + if /\/\/.*/ { + yield comment; + } else if /\/\*/ { + loop { + yield comment; + await input; + if /\*\// { + yield comment; + break; + } + } + } + + yield other; + } +} From 7f1c836c436c2d07e130ed2181bc61a23a161fbe Mon Sep 17 00:00:00 2001 From: Lu'ay Date: Thu, 16 Apr 2026 12:23:01 +0100 Subject: [PATCH 2/7] Added basic string and char highlighting --- assets/highlighting-tests/cs.cs | 117 ++++++++++++++++++++++++++++++++ crates/lsh/definitions/cs.lsh | 20 ++++++ 2 files changed, 137 insertions(+) diff --git a/assets/highlighting-tests/cs.cs b/assets/highlighting-tests/cs.cs index 5579d066521..0f732fca3f0 100644 --- a/assets/highlighting-tests/cs.cs +++ b/assets/highlighting-tests/cs.cs @@ -5,3 +5,120 @@ Multi-line comment */ + +global using static System.Console; +using System.Diagnostics; +using System; + +// Numbers +42; +3.14; +.5; +10_000_000; +1e10; +1.5e-3; +0xff; +0xFF; +0Xff; +0XFF; +0b1010; +0B1010; +42u; +42U; +42l; +42L; +42UL; +42Ul; +42uL; +42ul; +42LU; +42Lu; +42lU; +42lu; +3.14f; +3.14d; + +// Constants +true; +false; +null; + +// Strings and Characters +'a'; +'\n'; +"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 (true); + +switch (42) +{ + case 1: break; + default: break; +} + +try +{ + throw new Exception("oops"); +} +catch (System.Exception) +{ + +} +finally +{ + +} + +Debug; + +// Other keywords (some are contextually reserved) +var a = 1; +dynamic b = 2; +T c = 3; + +void Greet(string name) +{ + return "Hello, " + name; +} + +static void Greet(string name) +{ + return "Hello, " + name; +} + +async void Greet(string name) +{ + return "Hello, " + name; +} + +class Animal +{ + private int age; + protected bool isAlive; + public Animal() +} diff --git a/crates/lsh/definitions/cs.lsh b/crates/lsh/definitions/cs.lsh index 126c278ea7f..d2cae4f6fdd 100644 --- a/crates/lsh/definitions/cs.lsh +++ b/crates/lsh/definitions/cs.lsh @@ -15,6 +15,26 @@ pub fn CSharp() { break; } } + } else if /"/ { + until /$/ { + yield string; + if /\\./ {} + else if /"/ { + yield string; + break; + } + await input; + } + } else if /'/ { + until /$/ { + yield string; + if /\\./ {} + else if /'/ { + yield string; + break; + } + await input; + } } yield other; From a30040569b69da9e32e570af8cd754dd522126aa Mon Sep 17 00:00:00 2001 From: Lu'ay Date: Thu, 16 Apr 2026 19:35:45 +0100 Subject: [PATCH 3/7] Added keyword, method and numeric highlighting, numeric is not perfect --- assets/highlighting-tests/cs.cs | 6 ++++++ crates/lsh/definitions/cs.lsh | 18 ++++++++++++++++++ 2 files changed, 24 insertions(+) diff --git a/assets/highlighting-tests/cs.cs b/assets/highlighting-tests/cs.cs index 0f732fca3f0..f4d672fe962 100644 --- a/assets/highlighting-tests/cs.cs +++ b/assets/highlighting-tests/cs.cs @@ -18,9 +18,15 @@ 1e10; 1.5e-3; 0xff; +0xffu; +0xffl; +0xfful; 0xFF; 0Xff; 0XFF; +0XFFU; +0XFFL; +0XFFLU; 0b1010; 0B1010; 42u; diff --git a/crates/lsh/definitions/cs.lsh b/crates/lsh/definitions/cs.lsh index d2cae4f6fdd..4bb3670daa2 100644 --- a/crates/lsh/definitions/cs.lsh +++ b/crates/lsh/definitions/cs.lsh @@ -35,6 +35,24 @@ pub fn CSharp() { } await input; } + } else if /(?:global|using|static)/ { + yield keyword.control; + } else if /(?:as|break|case|catch|checked|continue|default|do|else|for|foreach|finaly|fixed|goto|if|is$|lock|return|switch|throw|try|while|unchecked|by|descending|equals|extention|from|group|into|join|nameof|on|or|orderby|select|when|where|with|yield)/ { + yield keyword.control; + } else if /(?:abstract|bool|byte|char|class|const|decimal|delegate|double|enum|event|explicit|extern|float|implicit|int|interface|internal|long|namespace|new|object|operator|out|override|params|private|protected|public|readonly|ref|sealed|short|static|string|struct|this|typeof|uint|ulong|unsafe|ushort|virtual|void|volatile|add|args|async|await|dynamic|field|file|get|init|let|managed|nint|not|notnull|nuint|partial|record|required|scoped|set|unmanaged)/ { + yield keyword.other; + } else if /(?:true|false|null)/ { + yield constant.language; + } else if /(?i:-?(?:0x[\da-fA-F_]+|0b[01_]+|[\d']+\.?[\d_]*|\.[\d]+(?i:_\d+)*)(?:p[+-]?[\d']+|e[+-]?[\d']+)?[ulfz]*)/ { + 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 d0e591071188ec70f837bcd6c43fa5c93c3036e8 Mon Sep 17 00:00:00 2001 From: Lu'ay Date: Thu, 16 Apr 2026 20:05:58 +0100 Subject: [PATCH 4/7] numeric highlighting is a tad better --- crates/lsh/definitions/cs.lsh | 12 ++++++------ 1 file changed, 6 insertions(+), 6 deletions(-) diff --git a/crates/lsh/definitions/cs.lsh b/crates/lsh/definitions/cs.lsh index 4bb3670daa2..f3bf2d8a64b 100644 --- a/crates/lsh/definitions/cs.lsh +++ b/crates/lsh/definitions/cs.lsh @@ -25,11 +25,11 @@ pub fn CSharp() { } await input; } - } else if /'/ { + } else if /_/ { until /$/ { yield string; if /\\./ {} - else if /'/ { + else if /_/ { yield string; break; } @@ -39,16 +39,16 @@ pub fn CSharp() { yield keyword.control; } else if /(?:as|break|case|catch|checked|continue|default|do|else|for|foreach|finaly|fixed|goto|if|is$|lock|return|switch|throw|try|while|unchecked|by|descending|equals|extention|from|group|into|join|nameof|on|or|orderby|select|when|where|with|yield)/ { yield keyword.control; - } else if /(?:abstract|bool|byte|char|class|const|decimal|delegate|double|enum|event|explicit|extern|float|implicit|int|interface|internal|long|namespace|new|object|operator|out|override|params|private|protected|public|readonly|ref|sealed|short|static|string|struct|this|typeof|uint|ulong|unsafe|ushort|virtual|void|volatile|add|args|async|await|dynamic|field|file|get|init|let|managed|nint|not|notnull|nuint|partial|record|required|scoped|set|unmanaged)/ { + } else if /(?:abstract|bool|byte|char|class|const|decimal|delegate|double|enum|event|explicit|extern|float|implicit|int|interface|internal|long|namespace|new|object|operator|out|override|params|private|protected|public|readonly|ref|sealed|short|static|string|struct|this|typeof|uint|ulong|unsafe|ushort|virtual|void|volatile|add|args|async|await|dynamic|field|file|get|init|let|managed|nint|not|notnull|nuint|partial|record|required|sbyte|scoped|set|T|unmanaged|var)/ { yield keyword.other; - } else if /(?:true|false|null)/ { - yield constant.language; - } else if /(?i:-?(?:0x[\da-fA-F_]+|0b[01_]+|[\d']+\.?[\d_]*|\.[\d]+(?i:_\d+)*)(?:p[+-]?[\d']+|e[+-]?[\d']+)?[ulfz]*)/ { + } else if /(?i:-?(?:0x[\da-fA-F_]+|0b[01_]+|[\d_]+\.?[\d_]*|\.[\d_]+)(?:p[+-]?[\d_]+|e[+-]?[\d_]+)?[ulfd]*)/ { if /\w+/ { // Invalid numeric literal } else { yield constant.numeric; } + } else if /(?:true|false|null)/ { + yield constant.language; }else if /(\w+)\s*\(/ { yield $1 as method; } else if /\w+/ { From 42f609727b973373b8593fe31a1e716d9ee4718f Mon Sep 17 00:00:00 2001 From: Lu'ay Date: Thu, 16 Apr 2026 21:01:49 +0100 Subject: [PATCH 5/7] numeric highlighting polished --- crates/lsh/definitions/cs.lsh | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/crates/lsh/definitions/cs.lsh b/crates/lsh/definitions/cs.lsh index f3bf2d8a64b..86ce193d207 100644 --- a/crates/lsh/definitions/cs.lsh +++ b/crates/lsh/definitions/cs.lsh @@ -41,7 +41,7 @@ pub fn CSharp() { yield keyword.control; } else if /(?:abstract|bool|byte|char|class|const|decimal|delegate|double|enum|event|explicit|extern|float|implicit|int|interface|internal|long|namespace|new|object|operator|out|override|params|private|protected|public|readonly|ref|sealed|short|static|string|struct|this|typeof|uint|ulong|unsafe|ushort|virtual|void|volatile|add|args|async|await|dynamic|field|file|get|init|let|managed|nint|not|notnull|nuint|partial|record|required|sbyte|scoped|set|T|unmanaged|var)/ { yield keyword.other; - } else if /(?i:-?(?:0x[\da-fA-F_]+|0b[01_]+|[\d_]+\.?[\d_]*|\.[\d_]+)(?:p[+-]?[\d_]+|e[+-]?[\d_]+)?[ulfd]*)/ { + } else if /(?i:-?(?i:0x[\da-fA-F_]+|0b[01_]+|[\d_]+\.?[\d_]*|\.[\d_]+)(?i:p[+-]?[\d_]+|e[+-]?[\d_]+)?[uUlLfFdD]*)/ { if /\w+/ { // Invalid numeric literal } else { From 854cebb02e7448352e33e8719e420d670453d096 Mon Sep 17 00:00:00 2001 From: Lu'ay Date: Fri, 17 Apr 2026 09:07:28 +0100 Subject: [PATCH 6/7] Updated markdown and c# example --- assets/highlighting-tests/cs.cs | 49 +++++++++++++++++++-------- assets/highlighting-tests/markdown.md | 7 ++++ crates/lsh/definitions/cs.lsh | 10 +++--- crates/lsh/definitions/markdown.lsh | 10 ++++++ 4 files changed, 57 insertions(+), 19 deletions(-) diff --git a/assets/highlighting-tests/cs.cs b/assets/highlighting-tests/cs.cs index f4d672fe962..cee84344a91 100644 --- a/assets/highlighting-tests/cs.cs +++ b/assets/highlighting-tests/cs.cs @@ -7,7 +7,6 @@ */ global using static System.Console; -using System.Diagnostics; using System; // Numbers @@ -43,6 +42,7 @@ 42lu; 3.14f; 3.14d; +-.5_0_1e3_0d; // Constants true; @@ -53,8 +53,8 @@ 'a'; '\n'; "double quotes with escape: \" \n \t \\"; -$""; -@""; +$"double quotes with a value {1 + 1}"; +@"double quotes with "; $@""; @$""; @@ -100,31 +100,52 @@ } -Debug; - // Other keywords (some are contextually reserved) var a = 1; dynamic b = 2; T c = 3; -void Greet(string name) +string Greet(string name) { return "Hello, " + name; } -static void Greet(string name) +static int Factorial(int n) { - return "Hello, " + name; + if (n == 0) + { + return 1; + } + + return n * Factorial(n - 1); } -async void Greet(string name) +class Animal { - return "Hello, " + name; + private int _Age; + public int Age { get => _Age; } + protected bool _IsAlive; + public bool IsAlive { get => _IsAlive; } + public Animal(int age, bool isAlive) + { + _Age = age; + _IsAlive = isAlive; + } + + public virtual void Speak() { } } -class Animal +class Dog : Animal { - private int age; - protected bool isAlive; - public Animal() + private string _Bark; + + public Dog(string bark, int age, bool isAlive) : base(age, isAlive) + { + + } + + public override void Speak() + { + Console.WriteLine(_Bark); + } } diff --git a/assets/highlighting-tests/markdown.md b/assets/highlighting-tests/markdown.md index 555b182f961..b0ecfbf1d1f 100644 --- a/assets/highlighting-tests/markdown.md +++ b/assets/highlighting-tests/markdown.md @@ -84,3 +84,10 @@ export function greet(name) { def greet(name: str) -> str: return f"hello {name}" ``` + +```CSharp +public string Greet(string name) +{ + return $"hello {name}"; +} +``` diff --git a/crates/lsh/definitions/cs.lsh b/crates/lsh/definitions/cs.lsh index 86ce193d207..18cd02f67be 100644 --- a/crates/lsh/definitions/cs.lsh +++ b/crates/lsh/definitions/cs.lsh @@ -15,7 +15,7 @@ pub fn CSharp() { break; } } - } else if /"/ { + } else if /[@$]{0,2}"/ { until /$/ { yield string; if /\\./ {} @@ -25,11 +25,11 @@ pub fn CSharp() { } await input; } - } else if /_/ { + } else if /'/ { until /$/ { yield string; if /\\./ {} - else if /_/ { + else if /'/ { yield string; break; } @@ -37,11 +37,11 @@ pub fn CSharp() { } } else if /(?:global|using|static)/ { yield keyword.control; - } else if /(?:as|break|case|catch|checked|continue|default|do|else|for|foreach|finaly|fixed|goto|if|is$|lock|return|switch|throw|try|while|unchecked|by|descending|equals|extention|from|group|into|join|nameof|on|or|orderby|select|when|where|with|yield)/ { + } else if /(?:as$|break|case|catch|checked|continue|default|do|else|for|foreach|finaly|fixed|goto|if|is$|lock|return|switch|throw|try|while|unchecked|by|descending|equals|extention|from|group|into|join|nameof|on$|or$|orderby|select|when|where|with|yield)/ { yield keyword.control; } else if /(?:abstract|bool|byte|char|class|const|decimal|delegate|double|enum|event|explicit|extern|float|implicit|int|interface|internal|long|namespace|new|object|operator|out|override|params|private|protected|public|readonly|ref|sealed|short|static|string|struct|this|typeof|uint|ulong|unsafe|ushort|virtual|void|volatile|add|args|async|await|dynamic|field|file|get|init|let|managed|nint|not|notnull|nuint|partial|record|required|sbyte|scoped|set|T|unmanaged|var)/ { yield keyword.other; - } else if /(?i:-?(?i:0x[\da-fA-F_]+|0b[01_]+|[\d_]+\.?[\d_]*|\.[\d_]+)(?i:p[+-]?[\d_]+|e[+-]?[\d_]+)?[uUlLfFdD]*)/ { + } else if /(?i:-?(?i:0x[\da-fA-F_]+|0b[01_]+|[\d_]+\.?[\d_]*|\.[\d_]+)(?i:[+-]?[\d_]+|e[+-]?[\d_]+)?[uUlLfFdD]*)/ { if /\w+/ { // Invalid numeric literal } else { diff --git a/crates/lsh/definitions/markdown.lsh b/crates/lsh/definitions/markdown.lsh index 77a005fe925..784c46e29cc 100644 --- a/crates/lsh/definitions/markdown.lsh +++ b/crates/lsh/definitions/markdown.lsh @@ -90,6 +90,16 @@ pub fn markdown() { if /.*/ {} } } + } else if /(?i:C\#|CSharp|cs)/ { + loop { + await input; + if /\s*```/ { + return; + } else { + CSharp(); + if /.*/ { } + } + } } else { loop { await input; From bb2c897b2c7a487911186aee9ee89ecd75e03160 Mon Sep 17 00:00:00 2001 From: Leonard Hecker Date: Wed, 29 Jul 2026 16:16:15 +0200 Subject: [PATCH 7/7] Merge with #856 and simplify LSH --- assets/highlighting-tests/cs.cs | 151 ------------------------ assets/highlighting-tests/csharp.cs | 158 ++++++++++++++++++++++++++ assets/highlighting-tests/markdown.md | 7 -- crates/edit/src/buffer/mod.rs | 1 + crates/lsh-bin/src/main.rs | 31 ++--- crates/lsh/definitions/cs.lsh | 60 ---------- crates/lsh/definitions/csharp.lsh | 75 ++++++++++++ crates/lsh/definitions/markdown.lsh | 10 -- crates/lsh/src/compiler/charset.rs | 11 ++ crates/lsh/src/compiler/regex.rs | 5 + 10 files changed, 267 insertions(+), 242 deletions(-) delete mode 100644 assets/highlighting-tests/cs.cs create mode 100644 assets/highlighting-tests/csharp.cs delete mode 100644 crates/lsh/definitions/cs.lsh create mode 100644 crates/lsh/definitions/csharp.lsh diff --git a/assets/highlighting-tests/cs.cs b/assets/highlighting-tests/cs.cs deleted file mode 100644 index cee84344a91..00000000000 --- a/assets/highlighting-tests/cs.cs +++ /dev/null @@ -1,151 +0,0 @@ -// Comments -// Single-line comment - -/* -Multi-line -comment -*/ - -global using static System.Console; -using System; - -// Numbers -42; -3.14; -.5; -10_000_000; -1e10; -1.5e-3; -0xff; -0xffu; -0xffl; -0xfful; -0xFF; -0Xff; -0XFF; -0XFFU; -0XFFL; -0XFFLU; -0b1010; -0B1010; -42u; -42U; -42l; -42L; -42UL; -42Ul; -42uL; -42ul; -42LU; -42Lu; -42lU; -42lu; -3.14f; -3.14d; --.5_0_1e3_0d; - -// Constants -true; -false; -null; - -// Strings and Characters -'a'; -'\n'; -"double quotes with escape: \" \n \t \\"; -$"double quotes with a value {1 + 1}"; -@"double quotes with "; -$@""; -@$""; - -// 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 (true); - -switch (42) -{ - case 1: break; - default: break; -} - -try -{ - throw new Exception("oops"); -} -catch (System.Exception) -{ - -} -finally -{ - -} - -// Other keywords (some are contextually reserved) -var a = 1; -dynamic b = 2; -T c = 3; - -string Greet(string name) -{ - return "Hello, " + name; -} - -static int Factorial(int n) -{ - if (n == 0) - { - return 1; - } - - return n * Factorial(n - 1); -} - -class Animal -{ - private int _Age; - public int Age { get => _Age; } - protected bool _IsAlive; - public bool IsAlive { get => _IsAlive; } - public Animal(int age, bool isAlive) - { - _Age = age; - _IsAlive = isAlive; - } - - public virtual void Speak() { } -} - -class Dog : Animal -{ - private string _Bark; - - public Dog(string bark, int age, bool isAlive) : base(age, isAlive) - { - - } - - public override void Speak() - { - Console.WriteLine(_Bark); - } -} diff --git a/assets/highlighting-tests/csharp.cs b/assets/highlighting-tests/csharp.cs new file mode 100644 index 00000000000..07c920e4611 --- /dev/null +++ b/assets/highlighting-tests/csharp.cs @@ -0,0 +1,158 @@ +// Single-line comment +/* Block comment + spanning multiple lines */ + +#nullable enable +#region Usings +global using static System.Console; +using System; +using System.Collections.Generic; +#endregion + +#if DEBUG +#pragma warning disable CS1591 +#endif + +namespace Demo.App; + +// Numbers +42; +3.14; +.5; +10_000_000; +1e10; +1.5e-3; +0xff; +0xffu; +0xffl; +0xfful; +0xFF; +0XFF; +0b1010; +0B1010; +42u; +42U; +42L; +42UL; +42ul; +42Ul; +42lu; +3.14f; +3.14d; +3.14m; +3.14M; +-.5_0_1e3_0d; +-.5_0_1E3_0D; + +// Constants +true; +false; +null; + +// Strings and characters +'a'; +'\n'; +"double quotes with escape: \" \n \t \\"; +$"double quotes with a value {1 + 1}"; +@"C:\Temp\file.txt"; +@"verbatim with ""escaped"" quotes"; +$@"interpolated verbatim {1}"; +@$"interpolated verbatim {1}"; +""" +raw string literal +"""; + +[Obsolete("Use NewThing instead")] +public sealed class Greeter +{ + private readonly string _name; + + public Greeter(string name) + { + _name = name; + } + + public string SayHello(int count = 3) + { + var list = new List { 1, 2, 3 }; + var pi = 3.14159; + var hex = 0xDEAD_BEEF; + return $"Hello {_name}, count={count}"; + } +} + +public record Person(string Name, int Age); + +class Animal +{ + private int _age; + public int Age { get => _age; init => _age = value; } + protected bool _isAlive; + public bool IsAlive => _isAlive; + + public virtual void Speak() { } +} + +class Dog : Animal +{ + private string _bark = "woof"; + + public override void Speak() + { + Console.WriteLine(_bark); + } +} + +public static class Program +{ + public static int Main(string[] args) + { + // Control flow keywords + if (args.Length == 0) + { + return 1; + } + else if (args.Length > 10) + { + return 2; + } + else + { + for (int i = 0; i < 10; i++) + { + if (i == 5) continue; + if (i == 8) break; + } + + foreach (var arg in args) + { + Console.WriteLine(arg); + } + + while (false) { } + do { } while (true); + + switch (args.Length) + { + case 1: break; + default: break; + } + } + + try + { + throw new Exception("oops"); + } + catch (Exception e) when (e is InvalidOperationException) + { + } + finally + { + } + + var query = from a in args where a is not null orderby a select a; + var g = new Greeter(args[0]); + Console.WriteLine(g.SayHello()); + return 0; + } +} diff --git a/assets/highlighting-tests/markdown.md b/assets/highlighting-tests/markdown.md index 8d719251f13..8044e5a9895 100644 --- a/assets/highlighting-tests/markdown.md +++ b/assets/highlighting-tests/markdown.md @@ -94,10 +94,3 @@ export type Greeting = (name: string) -> string def greet(name: str) -> str: return f"hello {name}" ``` - -```CSharp -public string Greet(string name) -{ - return $"hello {name}"; -} -``` diff --git a/crates/edit/src/buffer/mod.rs b/crates/edit/src/buffer/mod.rs index 468eb6898f1..f5dde4adb03 100644 --- a/crates/edit/src/buffer/mod.rs +++ b/crates/edit/src/buffer/mod.rs @@ -2177,6 +2177,7 @@ impl TextBuffer { HighlightKind::ConstantNumeric => Some(IndexedColor::BrightGreen), HighlightKind::KeywordControl => Some(IndexedColor::BrightMagenta), HighlightKind::KeywordOther => Some(IndexedColor::BrightBlue), + HighlightKind::KeywordPreprocessor => Some(IndexedColor::BrightBlue), HighlightKind::MarkupBold => None, HighlightKind::MarkupChanged => Some(IndexedColor::BrightBlue), HighlightKind::MarkupDeleted => Some(IndexedColor::BrightRed), diff --git a/crates/lsh-bin/src/main.rs b/crates/lsh-bin/src/main.rs index 68547c431d5..748483d9012 100644 --- a/crates/lsh-bin/src/main.rs +++ b/crates/lsh-bin/src/main.rs @@ -122,20 +122,23 @@ fn run_render(generator: lsh::compiler::Generator, path: &Path) -> anyhow::Resul "string" => "\x1b[91m", // Bright Red "variable" => "\x1b[96m", // Bright Cyan - "constant.language" => "\x1b[94m", // Bright Blue - "constant.numeric" => "\x1b[92m", // Bright Green - "keyword.control" => "\x1b[95m", // Bright Magenta - "keyword.other" => "\x1b[94m", // Bright Blue - "markup.bold" => "\x1b[1m", // Bold - "markup.changed" => "\x1b[94m", // Bright Blue - "markup.deleted" => "\x1b[91m", // Bright Red - "markup.heading" => "\x1b[94m", // Bright Blue - "markup.inserted" => "\x1b[92m", // Bright Green - "markup.italic" => "\x1b[3m", // Italic - "markup.link" => "\x1b[4m", // Underlined - "markup.list" => "\x1b[94m", // Bright Blue - "markup.strikethrough" => "\x1b[9m", // Strikethrough - "meta.header" => "\x1b[94m", // Bright Blue + "constant.language" => "\x1b[94m", // Bright Blue + "constant.numeric" => "\x1b[92m", // Bright Green + "keyword.control" => "\x1b[95m", // Bright Magenta + "keyword.other" => "\x1b[94m", // Bright Blue + "keyword.preprocessor" => "\x1b[94m", // Bright Blue + "markup.bold" => "\x1b[1m", // Bold + "markup.changed" => "\x1b[94m", // Bright Blue + "markup.deleted" => "\x1b[91m", // Bright Red + "markup.heading" => "\x1b[94m", // Bright Blue + "markup.inserted" => "\x1b[92m", // Bright Green + "markup.italic" => "\x1b[3m", // Italic + "markup.link" => "\x1b[4m", // Underlined + "markup.list" => "\x1b[94m", // Bright Blue + "markup.strikethrough" => "\x1b[9m", // Strikethrough + "meta.header" => "\x1b[94m", // Bright Blue + "storage.annotation" => "\x1b[36m", // Cyan + "storage.type" => "\x1b[36m", // Cyan _ => { unknown_kinds.push(hk.identifier.to_string()); diff --git a/crates/lsh/definitions/cs.lsh b/crates/lsh/definitions/cs.lsh deleted file mode 100644 index 18cd02f67be..00000000000 --- a/crates/lsh/definitions/cs.lsh +++ /dev/null @@ -1,60 +0,0 @@ -#[display_name = "C#"] -#[path = "**/*.cs"] -pub fn CSharp() { - until /$/ { - yield other; - - if /\/\/.*/ { - yield comment; - } else if /\/\*/ { - loop { - yield comment; - await input; - if /\*\// { - yield comment; - break; - } - } - } else if /[@$]{0,2}"/ { - until /$/ { - yield string; - if /\\./ {} - else if /"/ { - yield string; - break; - } - await input; - } - } else if /'/ { - until /$/ { - yield string; - if /\\./ {} - else if /'/ { - yield string; - break; - } - await input; - } - } else if /(?:global|using|static)/ { - yield keyword.control; - } else if /(?:as$|break|case|catch|checked|continue|default|do|else|for|foreach|finaly|fixed|goto|if|is$|lock|return|switch|throw|try|while|unchecked|by|descending|equals|extention|from|group|into|join|nameof|on$|or$|orderby|select|when|where|with|yield)/ { - yield keyword.control; - } else if /(?:abstract|bool|byte|char|class|const|decimal|delegate|double|enum|event|explicit|extern|float|implicit|int|interface|internal|long|namespace|new|object|operator|out|override|params|private|protected|public|readonly|ref|sealed|short|static|string|struct|this|typeof|uint|ulong|unsafe|ushort|virtual|void|volatile|add|args|async|await|dynamic|field|file|get|init|let|managed|nint|not|notnull|nuint|partial|record|required|sbyte|scoped|set|T|unmanaged|var)/ { - yield keyword.other; - } else if /(?i:-?(?i:0x[\da-fA-F_]+|0b[01_]+|[\d_]+\.?[\d_]*|\.[\d_]+)(?i:[+-]?[\d_]+|e[+-]?[\d_]+)?[uUlLfFdD]*)/ { - if /\w+/ { - // Invalid numeric literal - } else { - yield constant.numeric; - } - } else if /(?:true|false|null)/ { - yield constant.language; - }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; - } -} diff --git a/crates/lsh/definitions/csharp.lsh b/crates/lsh/definitions/csharp.lsh new file mode 100644 index 00000000000..16e1d9b9bc1 --- /dev/null +++ b/crates/lsh/definitions/csharp.lsh @@ -0,0 +1,75 @@ +#[display_name = "C#"] +#[path = "**/*.cs"] +#[path = "**/*.csx"] +pub fn csharp() { + // Preprocessor directives always occupy the entire line. + if /\s*#\s*(?:define|elif|else|endif|endregion|error|if|line|nullable|pragma|region|undef|warning)\>.*/ { + yield keyword.preprocessor; + return; + } + + until /$/ { + yield other; + + if /\/\/.*/ { + yield comment; + } else if /\/\*/ { + loop { + yield comment; + await input; + if /\*\// { + yield comment; + break; + } + } + } else if /\$*"""/ { + // Raw string literal. It has no escape sequences and may span lines. + loop { + yield string; + if /"""/ { + yield string; + break; + } + await input; + } + } else if /\$?@\$?"/ { + // Verbatim string. `""` is the only escape sequence and it may span lines. + loop { + yield string; + if /""/ { + // Escaped quote + } else if /"/ { + yield string; + break; + } + await input; + } + } else if /\$?"/ { + double_quote_string(); + } else if /'/ { + single_quote_string(); + } else if /\[([A-Z]\w*)/ { + yield $1 as storage.annotation; + } else if /(?:break|case|catch|continue|default|do|else|finally|foreach|for|goto|if|return|switch|throw|try|while|yield)\>/ { + yield keyword.control; + } else if /(?:abstract|and|async|as|await|base|checked|class|const|delegate|dynamic|enum|event|explicit|extern|file|fixed|from|get|global|group|implicit|init|interface|internal|into|in|is|join|let|lock|nameof|namespace|new|notnull|not|operator|orderby|or|out|override|params|partial|private|protected|public|readonly|record|ref|required|scoped|sealed|select|set|sizeof|stackalloc|static|struct|this|typeof|unchecked|unmanaged|unsafe|using|var|virtual|volatile|when|where|with)\>/ { + yield keyword.other; + } else if /(?:true|false|null)\>/ { + yield constant.language; + } else if /(?:bool|byte|char|decimal|double|float|int|long|nint|nuint|object|sbyte|short|string|uint|ulong|ushort|void)\>/ { + yield storage.type; + } else if /(?i:-?(?:0x[\da-f_]+|0b[01_]+|[\d_]+\.?[\d_]*|\.[\d_]+)(?:e[+-]?[\d_]+)?[dflmu]{0,2})/ { + 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; + } +} diff --git a/crates/lsh/definitions/markdown.lsh b/crates/lsh/definitions/markdown.lsh index 6da428980f8..75d5c1a6cbd 100644 --- a/crates/lsh/definitions/markdown.lsh +++ b/crates/lsh/definitions/markdown.lsh @@ -100,16 +100,6 @@ pub fn markdown() { if /.*/ {} } } - } else if /(?i:C\#|CSharp|cs)/ { - loop { - await input; - if /\s*```/ { - return; - } else { - CSharp(); - if /.*/ { } - } - } } else { loop { await input; diff --git a/crates/lsh/src/compiler/charset.rs b/crates/lsh/src/compiler/charset.rs index e7e301e3253..50529672301 100644 --- a/crates/lsh/src/compiler/charset.rs +++ b/crates/lsh/src/compiler/charset.rs @@ -100,6 +100,17 @@ impl Charset { } } + /// Adds the opposite case of every ASCII letter in the set. + pub fn fold_case(&mut self) { + for lower in b'a'..=b'z' { + let upper = lower.to_ascii_uppercase(); + if self.get(lower) || self.get(upper) { + self.set(lower, true); + self.set(upper, true); + } + } + } + pub fn is_superset_of(&self, other: &Charset) -> bool { for (&s, &o) in self.bits.iter().zip(other.bits.iter()) { // For self to be a superset, every bit in other must be in self diff --git a/crates/lsh/src/compiler/regex.rs b/crates/lsh/src/compiler/regex.rs index 9cc3370e25a..fba4206df0a 100644 --- a/crates/lsh/src/compiler/regex.rs +++ b/crates/lsh/src/compiler/regex.rs @@ -27,6 +27,7 @@ //! | `foo` | `Prefix("foo")` - single prefix check | //! | `\+\+\+` | `Prefix("+++")` - escapes fused into literals | //! | `(?i:foo)` | `PrefixInsensitive("foo")` | +//! | `(?i:[a-f])` | `Charset` with both cases folded in | //! | `[a-z]+` | `Charset{cs, min=1, max=∞}` - greedy char class | //! | `[a-z]?` | `Charset{cs, min=0, max=1}` - optional char | //! | `$` | `EndOfLine` condition | @@ -441,6 +442,10 @@ impl<'a> RegexParser<'a> { } } + if self.case_insensitive { + charset.fold_case(); + } + if negated { charset.invert(); }