ktsu.SyntaxHighlighting 3.33.1

Prefix Reserved
dotnet add package ktsu.SyntaxHighlighting --version 3.33.1
                    
NuGet\Install-Package ktsu.SyntaxHighlighting -Version 3.33.1
                    
This command is intended to be used within the Package Manager Console in Visual Studio, as it uses the NuGet module's version of Install-Package.
<PackageReference Include="ktsu.SyntaxHighlighting" Version="3.33.1" />
                    
For projects that support PackageReference, copy this XML node into the project file to reference the package.
<PackageVersion Include="ktsu.SyntaxHighlighting" Version="3.33.1" />
                    
Directory.Packages.props
<PackageReference Include="ktsu.SyntaxHighlighting" />
                    
Project file
For projects that support Central Package Management (CPM), copy this XML node into the solution Directory.Packages.props file to version the package.
paket add ktsu.SyntaxHighlighting --version 3.33.1
                    
#r "nuget: ktsu.SyntaxHighlighting, 3.33.1"
                    
#r directive can be used in F# Interactive and Polyglot Notebooks. Copy this into the interactive tool or source code of the script to reference the package.
#:package ktsu.SyntaxHighlighting@3.33.1
                    
#:package directive can be used in C# file-based apps starting in .NET 10 preview 4. Copy this into a .cs file before any lines of code to reference the package.
#addin nuget:?package=ktsu.SyntaxHighlighting&version=3.33.1
                    
Install as a Cake Addin
#tool nuget:?package=ktsu.SyntaxHighlighting&version=3.33.1
                    
Install as a Cake Tool

ktsu.SyntaxHighlighting

SyntaxHighlighting turns source text into classified token runs. It is UI-agnostic: nothing in the package draws, references a graphics API, or knows that Dear ImGui exists. ktsu.ImGui.SyntaxHighlighting is one renderer over it; any host that can draw colored text can be another.

Features

  • Fifteen built-in languages: C#, C, C++, JavaScript, TypeScript, Python, JSON, YAML, XML, HTML, CSS, SQL, shell, Lua, and plain text, each reachable by name or alias (cs, c#, js, py, bash, yml, …)
  • Data-driven definitions: a language is a LanguageDefinition record — comment, string, keyword, operator and embedded-language rules — so an application can register its own, or derive a variant of a built-in with a with expression
  • Embedded languages: XML in a doc comment, JSON in a fixture string and SQL in a query string are found inside the host language and highlighted in place, escapes and all
  • Two tokenizers: a general lexer driven by the definition, and a structural one for angle-bracket markup, which has no keywords
  • Colors without a renderer: SyntaxTheme holds one ktsu.Semantics.Color.Color per TokenKind, with Dark and Light built in, and leaves background, plain text and gutter unset so a host can fill them from its own theme
  • Cached tokenization: SyntaxHighlighter.HighlightCached keys a bounded cache by source, language and tab width, and HighlightedCode tokenizes once for hot render paths

Installation

dotnet add package ktsu.SyntaxHighlighting

Quick Start

using ktsu.SyntaxHighlighting;

foreach (HighlightedLine line in SyntaxHighlighter.Highlight(source, "csharp"))
{
    foreach (HighlightedToken token in line.Tokens)
    {
        Draw(token.Text, SyntaxTheme.Dark.ColorFor(token.Kind));
    }
}

Tokens tile the source: concatenating every token's text, with a newline between lines, reproduces the input exactly (with tabs expanded). Unknown language names never throw — they fall back to plain text, since the name usually comes from a markdown fence or a user-selected file.

For a render loop, tokenize once:

private static readonly HighlightedCode Snippet = new("SELECT * FROM users;", "sql");

Embedded languages

XML, JSON and SQL are written inside other languages' comments and strings constantly, and a lexer that stops at the quote leaves them a single flat color. Each LanguageDefinition carries EmbeddedLanguages: rules saying which language to look for, and where.

/// <summary>Posts an order.</summary>          // tags are XML, the prose stays a comment
string body = @"{""id"": 7, ""paid"": true}";   // keys, numbers and constants are JSON
string query = "SELECT id FROM receipts";       // keywords are SQL

Three things make this safe to leave on:

  • Recognition is strict. JSON is parsed, not pattern-matched, so a fragment or a brace-heavy sentence is rejected. Markup must open with a tag and close with >. SQL must open with a statement keyword and go on to use a second one, which is what keeps "Update the cache" from turning into a query — and SQL is only looked for in strings, never in comments.
  • Unclassified text keeps its host's color. Prose between doc comment tags still reads as a comment, so a false positive costs a few punctuation glyphs rather than a paragraph.
  • Escapes are resolved for the tokenizer, not for the output. The inner tokenizer sees {"id": 7} where the source holds {\"id\": 7}, but every token is re-sliced from the original text, so the backslashes are still drawn.

When recognition cannot work — a fragment, a heavily interpolated string — name the language outright with a hint comment, which applies to the next string literal:

// lang=sql
string tail = "ORDER BY total DESC";

/* language=sql */
string other = "ORDER BY total DESC";

Post(/* lang=json */ "{\"id\": 7}");

The hint is read from the comment's body, so either comment style works, lang and language are interchangeable, and an inline block comment hints the string beside it. The name runs to the next whitespace, so write lang=json rather than lang=json,.

Embedding is one level deep, and applies to LanguageDefinition.EmbeddedLanguages on the host language only. Turn it off, or change it, by deriving a definition:

LanguageRegistry.Register(BuiltInLanguages.CSharp with { EmbeddedLanguages = [] });

LanguageRegistry.Register(BuiltInLanguages.Python with
{
    EmbeddedLanguages = [.. BuiltInEmbeddedRules.Default, new EmbeddedLanguageRule
    {
        Language = "graphql",
        Hosts = EmbeddedHosts.StringLiteral,
        Matches = body => body.TrimStart().StartsWith("query ", StringComparison.Ordinal),
    }],
});

Registering a language

A language is plain data, so nothing needs to be subclassed:

LanguageRegistry.Register(new LanguageDefinition
{
    Name = "ini",
    Aliases = ["conf", "cfg"],
    LineComments = [new LineCommentRule { Prefix = ";" }],
    Strings = [new StringRule { Open = "\"", Close = "\"" }],
    Constants = ["true", "false", "yes", "no"],
    HighlightPropertyNames = true,
    HighlightFunctionCalls = false,
});

Limitations

  • Highlighting is lexical, not semantic: user-defined type names are not distinguished from other identifiers, and a call is recognized by the ( that follows it
  • Embedding is one level deep and is not applied by the markup tokenizer, so <script> and <style> bodies inside HTML are still treated as markup text
  • Doc comments written one /// line at a time are tokenized a line at a time, so an XML construct split across lines is classified per line
  • Lines are not wrapped and nothing is drawn; this classifies code, a renderer decides what to do with it

Contributing

Contributions are welcome! Feel free to open issues or submit pull requests.

License

This project is licensed under the MIT License. See the LICENSE.md file for details.

Product Compatible and additional computed target framework versions.
.NET net8.0 is compatible.  net8.0-android was computed.  net8.0-browser was computed.  net8.0-ios was computed.  net8.0-maccatalyst was computed.  net8.0-macos was computed.  net8.0-tvos was computed.  net8.0-windows was computed.  net9.0 is compatible.  net9.0-android was computed.  net9.0-browser was computed.  net9.0-ios was computed.  net9.0-maccatalyst was computed.  net9.0-macos was computed.  net9.0-tvos was computed.  net9.0-windows was computed.  net10.0 is compatible.  net10.0-android was computed.  net10.0-browser was computed.  net10.0-ios was computed.  net10.0-maccatalyst was computed.  net10.0-macos was computed.  net10.0-tvos was computed.  net10.0-windows was computed. 
Compatible target framework(s)
Included target framework(s) (in package)
Learn more about Target Frameworks and .NET Standard.

NuGet packages (1)

Showing the top 1 NuGet packages that depend on ktsu.SyntaxHighlighting:

Package Downloads
ktsu.ImGui.SyntaxHighlighting

Renders syntax-highlighted source code inside Dear ImGui. Ships a data-driven tokenizer with definitions for fifteen languages, theme-aware palettes that follow the host application's light or dark theme, an optional line-number gutter, and cached tokenization for immediate-mode render loops. Layered on ktsu.ImGui.Color only, with no dependency on ktsu.ImGui.App, so it drops into any Hexa.NET.ImGui application — including as the code-block renderer for ktsu.ImGui.Markdown.

GitHub repositories

This package is not used by any popular GitHub repositories.

Version Downloads Last Updated
3.33.1 0 9/13/2026
3.33.0 39 9/12/2026
3.32.3 51 9/11/2026
3.32.2 51 9/10/2026
3.32.1 63 9/9/2026
3.32.0 188 9/9/2026
3.31.0 83 9/9/2026
3.30.0 67 9/9/2026
3.29.0 95 9/9/2026
3.28.0 78 9/9/2026
3.27.0 78 9/9/2026
3.26.1 55 9/8/2026
3.26.0 73 9/8/2026
3.25.0 87 9/8/2026
3.24.0 80 9/8/2026
3.23.0 76 9/8/2026
3.22.0 73 9/8/2026
3.21.0 106 9/8/2026
3.20.0 82 9/8/2026
3.19.0 81 9/8/2026

## v3.33.1 (patch)

Changes since v3.33.0:

- ci: adopt the consolidated .NET workflow [patch] ([@Claude](https://github.com/Claude))
- ci: tolerate a SonarQube Cloud outage instead of failing the build [patch] ([@Claude](https://github.com/Claude))