TUI::TextEdit
Inherits TUI::Scrollable < Reference < Object
A multi-line, editable text area — the Scrollable-hosted counterpart to
TextField (src/tui/form/text_field.cr), for standalone editing (e.g. a
full-screen editor via Window.full_screen(screen, TextEdit.new(...)))
rather than one bounded field inside a form. Long lines soft-wrap at an
exact character column (not word boundaries — keeps cursor math a
simple division, matching vim's default wrap), with a trailing
marker glyph on every wrapped (non-final) segment of a line, mirroring
DetailView's \ continuation marker for its own soft-wrapped value
column. content_size counts visual rows (post-wrap), so Window's
existing Scroller/scrollbar machinery shows a scrollbar automatically
the instant wrapped content overflows the viewport — no separate
scrollbar logic needed here.
Owns its own cursor (@text_row, @text_col, into @text_lines)
because Runtime hides the native terminal cursor for the app's whole
lifetime — see #render_content for how the cursor is instead drawn
as a reverse-video cell, exactly like TextField's overlay_cursor.
Syntax-agnostic by design: #highlighter is a plain String -> Array(Cell) hook, so TextEdit itself has no notion of any particular
language or markup. A syntax-specific editor (e.g. MarkdownEdit, see
src/tui/markdown/markdown_edit.cr) subclasses TextEdit and wires
#highlighter in its own #initialize instead of TextEdit depending on
any one syntax.
Constants
Appended after every wrapped (non-final) segment of a soft-wrapped line, in the last column — the visual cue that the line continues on the next row rather than having actually ended.
Constructors
Instance methods
Total scrollable content rows. Does NOT include a header row — header is content-internal (TableView-specific), not Window's.
A positional (mouse) event, pre-translated by Window into content-local row/col (0,0 = content's own first cell).
ameba:disable Metrics/CyclomaticComplexity
Optional per-line syntax highlighter: given one logical line's raw text, returns the styled spans (Cell — plain (text, style) pairs, already used the same way by TableView/DetailView) to render it with, in left-to-right order covering the whole line. nil (the default) renders every line in one flat style, exactly as before this hook existed. Called once per logical line per render — not once per wrapped visual segment, since #render_content itself slices the returned spans to fit each segment — so a highlighter never needs to know about wrapping.
Contract: the returned Cells' text, concatenated in order, MUST equal the input line exactly (same characters, same length) — a highlighter may only re-style characters, never add, remove, or reorder them. TextEdit's cursor/click column math indexes directly into the raw line, so a highlighter that drops or rewrites characters (e.g. stripping Markdown delimiters for a read-only renderer, the way Markdown::Inline.parse does) would silently desync the visible cursor position from where edits actually land — see MarkdownEdit for a highlighter that stays contract-safe by coloring syntax markers in place instead of consuming them. #render_segment falls back to plain rendering for any line where a highlighter violates this, rather than rendering corrupted output.
Optional per-line syntax highlighter: given one logical line's raw text, returns the styled spans (Cell — plain (text, style) pairs, already used the same way by TableView/DetailView) to render it with, in left-to-right order covering the whole line. nil (the default) renders every line in one flat style, exactly as before this hook existed. Called once per logical line per render — not once per wrapped visual segment, since #render_content itself slices the returned spans to fit each segment — so a highlighter never needs to know about wrapping.
Contract: the returned Cells' text, concatenated in order, MUST equal the input line exactly (same characters, same length) — a highlighter may only re-style characters, never add, remove, or reorder them. TextEdit's cursor/click column math indexes directly into the raw line, so a highlighter that drops or rewrites characters (e.g. stripping Markdown delimiters for a read-only renderer, the way Markdown::Inline.parse does) would silently desync the visible cursor position from where edits actually land — see MarkdownEdit for a highlighter that stays contract-safe by coloring syntax markers in place instead of consuming them. #render_segment falls back to plain rendering for any line where a highlighter violates this, rather than rendering corrupted output.
Render into buffer, a region already sized to the content area
(border/scrollbar column already excluded by Window). Row 0 is
content's own first row.