ktsu.ImGui.SyntaxHighlighting
3.79.0
Prefix Reserved
See the version list below for details.
dotnet add package ktsu.ImGui.SyntaxHighlighting --version 3.79.0
NuGet\Install-Package ktsu.ImGui.SyntaxHighlighting -Version 3.79.0
<PackageReference Include="ktsu.ImGui.SyntaxHighlighting" Version="3.79.0" />
<PackageVersion Include="ktsu.ImGui.SyntaxHighlighting" Version="3.79.0" />
<PackageReference Include="ktsu.ImGui.SyntaxHighlighting" />
paket add ktsu.ImGui.SyntaxHighlighting --version 3.79.0
#r "nuget: ktsu.ImGui.SyntaxHighlighting, 3.79.0"
#:package ktsu.ImGui.SyntaxHighlighting@3.79.0
#addin nuget:?package=ktsu.ImGui.SyntaxHighlighting&version=3.79.0
#tool nuget:?package=ktsu.ImGui.SyntaxHighlighting&version=3.79.0
ktsu.ImGui.SyntaxHighlighting
ImGui.SyntaxHighlighting draws syntax-highlighted source code directly inside Dear ImGui. Like its sibling ktsu.ImGui.Markdown, it is a standalone package layered on ktsu.ImGui.Color only, with no dependency on ktsu.ImGui.App, so it can be dropped into any Hexa.NET.ImGui application.
Tokenizing lives one layer down, in the renderer-agnostic ktsu.SyntaxHighlighting: languages, themes, the token kinds and the embedded-language rules are all defined there and know nothing about ImGui. This package is the drawing half — a background panel, a line-number gutter, and colored token runs painted into the window draw list.
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
LanguageDefinitionrecord — comment, string, keyword and operator rules — so an application can register its own, or derive a variant of a built-in with awithexpression - 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, or named outright with a
// lang=jsonhint comment - Theme-aware colors: by default the palette is picked per frame from the luminance of the ImGui window background, and unset palette entries (background, plain text, gutter) come from the active ImGui theme, so code keeps matching the surrounding UI
- Line numbers: an optional right-aligned gutter that does not shift the code column as the digit count grows
- Cached tokenization: the static
Rendercaches by source text, andHighlightedCodetokenizes once for hot render paths - Tokens without rendering:
Highlightreturns the classified runs for callers that want to draw, export, or test them themselves - Markdown integration: pairs with
ktsu.ImGui.Markdown'sCodeBlockRendererhook to highlight fenced code blocks, with neither library depending on the other
Installation
dotnet add package ktsu.ImGui.SyntaxHighlighting
Quick Start
Static render (cached by source)
ImGuiSyntaxHighlighting.Render tokenizes the given code and caches the result keyed by the source text, language and tab width, so calling it every frame with the same code does not re-tokenize it.
using ktsu.ImGui.SyntaxHighlighting;
using Hexa.NET.ImGui;
ImGui.Begin("Code");
ImGuiSyntaxHighlighting.Render("""
/// <summary>Greets the world.</summary>
public static void Main()
{
Console.WriteLine("Hello, ImGui");
}
""", "csharp");
ImGui.End();
The doc comment's tags in that snippet are highlighted as XML while its prose stays comment-colored — see embedded languages for what is recognized and how to change it.
HighlightedCode for hot paths
When the same source is rendered every frame, tokenize it once and render that instance instead of relying on the source-keyed cache. HighlightedCode comes from ktsu.SyntaxHighlighting; Render is an extension this package adds to it.
private static readonly HighlightedCode Snippet = new("SELECT * FROM users;", "sql");
// In the render loop:
Snippet.Render();
Tokens without rendering
Highlight forwards to SyntaxHighlighter.Highlight, so a caller with no ImGui context can reference ktsu.SyntaxHighlighting alone and skip this package entirely.
foreach (HighlightedLine line in ImGuiSyntaxHighlighting.Highlight(source, "python"))
{
foreach (HighlightedToken token in line.Tokens)
{
Console.WriteLine($"{token.Kind}: {token.Text}");
}
}
Configuration
SyntaxHighlightConfig is a record; every member is optional.
SyntaxHighlightConfig config = new()
{
ShowLineNumbers = true,
FirstLineNumber = 1,
TabWidth = 4,
Theme = null, // null follows the ImGui theme; or SyntaxTheme.Dark / .Light
FontResolver = size => monoFont,
ShowBackground = true,
BackgroundRounding = 3.0f,
PaddingPixels = 6.0f,
LineSpacingPixels = 2.0f,
GutterSpacingPixels = 10.0f,
FontSizePixels = null, // null keeps the current size, so DPI scaling is followed
Width = null, // null uses the available content width; the block widens to its longest line
};
Code is never wrapped. A block is as wide as its longest line, or as Width if that is wider, so inside a child window opened with ImGuiWindowFlags.HorizontalScrollbar the end of a long line can be scrolled to; outside one, the window clips it.
Only the lines inside the window's clip rectangle are measured and drawn each frame, so a long file in a scrolling child costs what is on screen. The width of the widest line is measured once per block and font and kept.
Themes
SyntaxTheme holds one color per TokenKind as a semantic ktsu.Semantics.Color.Color. SyntaxTheme.Dark and SyntaxTheme.Light are the built-ins, and any theme can be derived with a with expression:
SyntaxTheme theme = SyntaxTheme.Dark with
{
Comment = Color.FromHex("#7f848e"),
Keyword = Color.FromHex("#c678dd"),
};
Leaving Background, Plain or LineNumber unset makes the renderer take them from the ImGui theme (FrameBg, Text and TextDisabled).
Registering a language
Definitions live in ktsu.SyntaxHighlighting, and 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,
});
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.
Highlighting markdown code blocks
ktsu.ImGui.Markdown exposes a CodeBlockRenderer hook that receives a fence's info string and its text. Pointing it at this package highlights fenced code without either library depending on the other:
MarkdownConfig markdown = new()
{
CodeBlockRenderer = (language, code) =>
ImGuiSyntaxHighlighting.Render(code, language ?? "text", highlightConfig),
};
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 - Embedded languages are found in comments and strings only, one level deep — script and style bodies inside HTML are still treated as markup text
- Lines are not wrapped, and there is no built-in scrolling, selection, or editing; this renders code, it is not a text editor
Demo
See examples/ImGuiSyntaxHighlightingDemo/ for a runnable demo covering the language samples, the line-number gutter, palette switching, and markdown code blocks.
dotnet run --project examples/ImGuiSyntaxHighlightingDemo
Acknowledgments
- ktsu.SyntaxHighlighting - The renderer-agnostic tokenizer, languages and themes this draws
- Dear ImGui - The immediate mode GUI library this draws into
- Hexa.NET.ImGui - The .NET bindings for Dear ImGui that this package is built on
- ktsu.Semantics - The
Colortype each theme entry is held as, converted at the ImGui seam byktsu.ImGui.Color
Contributing
Contributions are welcome! For feature requests, bug reports, or questions, please open an issue on the GitHub repository. If you would like to contribute code, please open a pull request with your changes.
License
ImGui.SyntaxHighlighting is licensed under the MIT License. See LICENSE.md for more information.
| Product | Versions 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. |
-
net10.0
- Hexa.NET.ImGui (>= 2.2.9)
- ktsu.ImGui.Color (>= 3.79.0)
- ktsu.Semantics.Color (>= 5.12.1)
- ktsu.SyntaxHighlighting (>= 3.79.0)
-
net8.0
- Hexa.NET.ImGui (>= 2.2.9)
- ktsu.ImGui.Color (>= 3.79.0)
- ktsu.Semantics.Color (>= 5.12.1)
- ktsu.SyntaxHighlighting (>= 3.79.0)
-
net9.0
- Hexa.NET.ImGui (>= 2.2.9)
- ktsu.ImGui.Color (>= 3.79.0)
- ktsu.Semantics.Color (>= 5.12.1)
- ktsu.SyntaxHighlighting (>= 3.79.0)
NuGet packages
This package is not used by any NuGet packages.
GitHub repositories
This package is not used by any popular GitHub repositories.
| Version | Downloads | Last Updated |
|---|---|---|
| 3.79.1-pre.3 | 51 | 10/28/2026 |
| 3.79.1-pre.2 | 58 | 10/27/2026 |
| 3.79.1-pre.1 | 63 | 10/26/2026 |
| 3.79.0 | 115 | 10/26/2026 |
| 3.78.3 | 103 | 10/25/2026 |
| 3.78.2 | 106 | 10/24/2026 |
| 3.78.1 | 107 | 10/24/2026 |
| 3.78.0 | 107 | 10/22/2026 |
| 3.77.3 | 113 | 10/21/2026 |
| 3.77.2 | 91 | 10/21/2026 |
| 3.77.1 | 122 | 10/21/2026 |
| 3.77.0 | 104 | 10/20/2026 |
| 3.76.1 | 100 | 10/20/2026 |
| 3.76.0 | 94 | 10/20/2026 |
| 3.75.4 | 290 | 10/19/2026 |
| 3.75.3 | 111 | 10/18/2026 |
| 3.75.2 | 118 | 10/17/2026 |
| 3.75.2-pre.1 | 66 | 10/16/2026 |
| 3.75.1 | 119 | 10/14/2026 |
| 3.75.0 | 120 | 10/12/2026 |
## v3.79.0 (minor)
Changes since v3.78.0:
- Size text fields from their value in managed code, with no native buffer (Claude)
- Grow every text field's buffer with its text instead of giving it a fixed capacity (Claude)
- Give every DividerContainer zone the same gap, whatever the zone count or layout (Claude)
- Let StickyHeaderList's pinned header take the pointer from the rows beneath it (Claude)
- Keep a TabPanel's tab across a hidden strip and tab removal (Claude)
- [patch] Keep a value slider's minus sign readable in every culture and font (Claude)
- [patch] Apply a window icon set after launch again once the window shows (Claude)
- [patch] Reopen a window saved maximized as maximized (#725) (matt-edmondson)
- [patch] Reopen a window saved maximized as maximized (Claude)