Skip to content

gui-cs/Editor

Repository files navigation

Terminal.Gui.Editor

Terminal.Gui.Editor (ted demo app)

A reusable text-editing View for Terminal.Gui. Drop it into your TUI app and you get the editing experience users already expect (caret movement, selection, clipboard, undo/redo, search & replace, folding, syntax highlighting, word wrap) without writing any of it yourself.

Ships as a single NuGet package: Terminal.Gui.Editor.

What this is, and what it isn't

This is a library. It exists so that anyone building a Terminal.Gui application (developers, agents, tool authors) can embed a competent multi-line text editor into their UI with a couple of lines of code. The audience is people who need an edit field that can hold code or prose, not people shopping for a standalone editor.

Think of Editor the way you'd think of a rich-text control in a desktop framework: a building block for the editing surface inside your app. A script box in an IDE-like tool, a config pane in a TUI dashboard, a chat composer, a notes view, the body of a code-review widget, the input area of an LLM agent's terminal front-end. Whatever it is you're building, Editor is the part that handles "user types and edits text."

This is not a standalone editor and not trying to be one. It is not a competitor to vim, Emacs, Helix, micro, nano, or any other terminal editor; those are products with their own ecosystems, configs, and communities, and the world doesn't need another one. The ted app in this repo is a demo of the View, not a product. A real end-user editor built on top of this library, clet, lives in its own repo.

What's in the box

  • Document layer (Terminal.Gui.Document and sub-namespaces). UI-framework-independent. Rope-backed TextDocument, TextAnchor, UndoStack, FoldingManager, search strategies, indentation strategies, and the xshd-driven highlighting engine. Adapted from AvaloniaEdit's pure-data layers.
  • Editor view (Terminal.Gui.Editor namespace). An Editor : View subclass consuming the document layer through a cell-grid rendering pipeline (VisualLineBuilderCellVisualLine, with pluggable IVisualLineTransformers and IBackgroundRenderers for consumers that want to layer their own visual behaviour on top).

ted, the reference app

examples/ted is a reference TUI ap showing how to wire Editor into a real window: menubar + editor + status bar, File / Edit / Options menus, find & replace dialog, language + theme selectors, line numbers, fold indicators, word-wrap togle, tab controls, mouse, context menu, save-changes prompt. It's a demo of the View, not a product; its job is to make every feature reachable so you can see what's available before puling the package into your own app. Run it against any file: sh dotnet run --project examples/ted - path/to/file.cs dotnet run -project examples/ted -- -read-only path/to/file.cs

For a user-facing editor built on this library, se clet.

Status

Alpha, shipped 2026-05-12 of the develop rolling pre-release stream. See specs/plan.md for the beta roadmap and remaining work ([Obsolete] on TG TextView lands with the beta).

Inherited from Terminal.Gui

Editor is a View. The standard TG machinery applies:

  • Commands + keybindings. All editor actions are Command bindings, remappable via KeyBindings or ConfigurationManager.
  • Themes. Colors come from the active Scheme; switch themes at runtime and the editor reflows.
  • Layout. Pos / Dim constraints; Padding / Border / Margin adornments (the gutter is a View inside Padding).
  • Scrollbars. Set ViewportSettings = ViewportSettingsFlags.HasScrollBars.
  • Mouse, clipboard, popovers, dialogs, localization. TG primitives, no editor-side bridging.

Features

Editing

  • Typing, Backspace / Delete, Enter, with Unicode + grapheme-aware width.
  • Caret backed by a TextAnchor (AnchorMovementType.AfterInsertion); sticky virtual column across vertical moves through short lines.
  • Selection: any TG-bound "extend" movement command (Command.LeftExtend, RightEndExtend, PageDownExtend, …) extends the selection; Command.SelectAll selects all; typing or paste replaces selection.
  • Clipboard: Command.Cut, Command.Copy, Command.Paste; selection-aware, single-step undo, aborts cut if the clipboard write fails. Uses TG's IClipboard, so cut/copy/paste interoperates with whatever the OS clipboard contains.
  • Undo / redo with sane granularity (Command.Undo, Command.Redo). Compound operations (Enter + auto-indent, replace-all, paste over selection) collapse into one undo step via Document.RunUpdate ().
  • Read-only mode: Editor.ReadOnly = true blocks edits, undo/redo, and clipboard mutations while keeping navigation and selection live.
  • Multi-caret editing: Ctrl+Click to place additional carets; type, Backspace, Delete, or Enter at all of them simultaneously. Escape collapses back to one caret. Every multi-caret operation is a single undo step. See examples/ted/docs/multi-caret.md for details.

Indentation & tabs

  • IndentationSize, ConvertTabsToSpaces, ShowTabs (renders a glyph in the first cell of each tab); all bindable, all observable from the consumer's UI.
  • Tab / Shift+Tab indent / unindent. With a selection, indents the selected line range.
  • Indentation-aware Backspace.
  • Pluggable IIndentationStrategy; the default copies the previous line's leading whitespace on Enter (wrapped in a single undo group).

Rendering pipeline

  • Cell-grid pipeline: VisualLineBuilderCellVisualLineCellVisualLineElement (TextRunElement, TabElement).
  • Pluggable IVisualLineTransformers (syntax highlight, folding markers, …) and IBackgroundRenderers (selection, current line, search hits). Consumers can layer their own transformers on top.
  • LRU visual-line caches with ranged invalidation from Document.Changed; incremental max-width tracking avoids the O(N) all-lines walk on every edit.
  • Word wrap: Editor.WordWrap soft-wraps at whitespace boundaries (hard-breaks when none), continuation rows flush at column 0.

Folding

  • FoldingManager + FoldingTransformer collapse / expand regions.
  • Foldings auto-expand if the caret moves inside one.
  • BraceFoldingStrategy and XmlFoldingStrategy included; consumers can add their own.
  • Command.Collapse toggles the fold under the caret.
  • FoldingGutter paints +/− indicators; click to toggle.

Gutter

  • Editor.GutterOptions is a [Flags] enum: LineNumbers | Folding. Combine to show both, or hide both.
  • Gutter is a real View subview of Padding (not painted by hand), so it composes with TG popovers, menus, and focus traversal.

Find & Replace

  • Pluggable ISearchStrategy: Normal (string), RegEx, WholeWords, with MatchCase. Construct via SearchStrategyFactory.Create or assign your own.
  • FindRequested / ReplaceRequested events fire so consumers can open whatever Find UI they want (ted opens a Dialog-based form; an agent-driven app could open something else, or skip the UI entirely and drive the API directly).
  • Forward / backward navigation through hits with wrap-around.
  • SearchHitRenderer highlights every match in the viewport; invalidated automatically on document edits.
  • ReplaceAll is one undo step.

Syntax highlighting

  • xshd-driven DocumentHighlighter + HighlightingColorizer, installed automatically when you set HighlightingDefinition.
  • Built-in definitions (looked up via HighlightingManager.Instance): C#, C++, Java, JavaScript, Python, PowerShell, TSQL, VB, JSON, HTML, XML, CSS, Markdown.
  • Highlight colors compose with the active TG Scheme; pick a theme via the Configuration Manager and the editor follows.
  • TextMate grammars are a post-beta follow-up (see specs/textmate-grammars/spec.md).

Default keybindings

These are the defaults. They are Command-bound and remappable via TG's KeyBindings API or ConfigurationManager.

Command Default key Notes
Command.Cut / Copy / Paste Ctrl+X / Ctrl+C / Ctrl+V TG IClipboard
Command.Undo / Redo Ctrl+Z / Ctrl+Y (also Ctrl+Shift+Z)
Command.SelectAll Ctrl+A TG base layer
Command.NewLine Enter Auto-indents if a strategy is installed
Command.DeleteCharLeft / DeleteCharRight Backspace / Delete
Command.Start / End Ctrl+Home / Ctrl+End
Command.LeftStart / RightEnd Home / End TG base layer
Command.Up / Down / Left / Right / PageUp / PageDown arrows + PgUp/PgDn TG base layer
Command.*Extend variants Shift+ above Selection-extending
Command.Collapse Ctrl+M Toggle fold under caret
Find / FindNext / FindPrev / Replace Ctrl+F / F3 / Shift+F3 / Ctrl+H Ctrl+F / Ctrl+H raise events for the consumer's UI
Indent / Unindent Tab / Shift+Tab Range-aware with selection
Scroll mouse wheel (incl. horizontal) Bubbles up from gutter subviews

Override at the consumer level with the standard TG pattern:

editor.KeyBindings.Remove (Key.X.WithCtrl);
editor.KeyBindings.Add (Key.W.WithCtrl, Command.Cut);

…or ship a Configuration Manager JSON profile that does the same.

Quickstart

Install the package (requires the .NET 10 SDK and Terminal.Gui):

dotnet add package Terminal.Gui.Editor

Drop the editor into a Terminal.Gui app. Editor is just a View, so it gets TG's layout, scheme, scrollbars, and Configuration Manager for free:

using Terminal.Gui.App;
using Terminal.Gui.Configuration;
using Terminal.Gui.Document;
using Terminal.Gui.Editor;
using Terminal.Gui.Highlighting;
using Terminal.Gui.ViewBase;

// Pick up themes / keymaps / preferences from the user's TG config.
ConfigurationManager.Enable (ConfigLocations.All);

using IApplication app = Application.Create ();
app.Init ();

Editor editor = new ()
{
    // TG constraint-based layout: anchor below a menubar, fill above a status bar.
    X = 0,
    Y = Pos.Bottom (menu),
    Width = Dim.Fill (),
    Height = Dim.Fill (statusBar),

    // TG scrollbars come free with this flag.
    ViewportSettings = ViewportSettingsFlags.HasScrollBars,

    GutterOptions = GutterOptions.LineNumbers | GutterOptions.Folding,
    ConvertTabsToSpaces = true,
    HighlightingDefinition = HighlightingManager.Instance.GetDefinitionByExtension (".cs")
};

editor.Document = new TextDocument (File.ReadAllText ("Program.cs"));

Window win = new () { Title = "My Editor" };
win.Add (menu, editor, statusBar);

app.Run (win);

See examples/ted/TedApp.cs for the full version: menus, status bar, find/replace dialog, theme dropdown, all wired through standard TG primitives.

Repository layout

specs/        Planning and design docs (spec-kit)
src/          Terminal.Gui.Editor library (document layer + Editor view)
tests/        xUnit.v3 test projects (correctness + perf smoke)
benchmarks/   BenchmarkDotNet suite + CI baseline
examples/     ted (standalone demo app)
third_party/  AvaloniaEdit fork policy + per-file modification log

Build

Requires the .NET 10 SDK (preview). Solution file is Terminal.Gui.Editor.slnx (XML solution format).

dotnet restore Terminal.Gui.Editor.slnx
dotnet build   Terminal.Gui.Editor.slnx

# Correctness suites; run on every push/PR across ubuntu/macos/windows.
dotnet run --project tests/Terminal.Gui.Editor.Tests
dotnet run --project tests/Terminal.Gui.Editor.IntegrationTests

# Perf smoke + BenchmarkDotNet baseline gate; ubuntu-latest only in CI
# (.github/workflows/perf.yml). Run locally in Release config.
dotnet run --project tests/Terminal.Gui.Editor.PerformanceTests -c Release

FAQ

Why do //, ///, ==, !=, =>, and similar character sequences look wrong when I change styling on just one character?

This is a font ligature issue, not an Editor bug. Many popular programming fonts (Cascadia Code, Fira Code, JetBrains Mono, Iosevka, Hasklig, Victor Mono, etc.) replace common multi-character sequences with single-glyph ligatures. When the Editor applies a different text style (underline, blink, reverse, different color) to just one character in a ligature sequence, the terminal must break the ligature and re-render the characters individually. Some terminals handle this gracefully; others re-render the entire ligature glyph with the new style, making it look like all characters in the sequence are affected.

This impacts any feature that changes the attribute of a single character within a ligature-eligible sequence: multi-caret overlays, selection start/end boundaries, syntax highlighting boundaries that land mid-ligature, search-hit highlighting, etc.

Workarounds:

  • Use a non-ligature font. Consolas, Courier New, DejaVu Sans Mono, Ubuntu Mono, and any font without an active ligature table will render correctly.
  • Disable ligatures in your terminal. In Windows Terminal, add "font": { "features": { "liga": 0, "calt": 0 } } to your profile in settings.json. Other terminals have similar settings.
  • Use a ligature font that handles partial-style changes well. Terminal rendering of partially-styled ligatures varies by terminal emulator and font; some combinations work better than others.

This is a general limitation of cell-based TUI rendering in ligature-aware terminals and is not specific to this library. Terminal.Gui, Neovim, micro, and other TUI editors that render per-cell attributes all exhibit the same behavior.

License

MIT; see LICENSE.

Portions of the document layer are adapted from AvaloniaEdit (MIT). See third_party/AvaloniaEdit/UPSTREAM.md for the pinned upstream commit and per-file modification log.

About

Terminal.Gui Text Editor based on AvaloniaEdit

Resources

License

Stars

Watchers

Forks

Packages

 
 
 

Contributors