Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
158 changes: 158 additions & 0 deletions assets/highlighting-tests/csharp.cs
Original file line number Diff line number Diff line change
@@ -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<int> { 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;
}
}
1 change: 1 addition & 0 deletions crates/edit/src/buffer/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -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),
Expand Down
31 changes: 17 additions & 14 deletions crates/lsh-bin/src/main.rs
Original file line number Diff line number Diff line change
Expand Up @@ -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());
Expand Down
75 changes: 75 additions & 0 deletions crates/lsh/definitions/csharp.lsh
Original file line number Diff line number Diff line change
@@ -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;
}
}
11 changes: 11 additions & 0 deletions crates/lsh/src/compiler/charset.rs
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand Down
5 changes: 5 additions & 0 deletions crates/lsh/src/compiler/regex.rs
Original file line number Diff line number Diff line change
Expand Up @@ -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 |
Expand Down Expand Up @@ -441,6 +442,10 @@ impl<'a> RegexParser<'a> {
}
}

if self.case_insensitive {
charset.fold_case();
}

if negated {
charset.invert();
}
Expand Down
Loading