Documentation
¶
Overview ¶
Package editor is the composer's line editor: the multi-line buffer the user types a prompt into, with grapheme-correct cursor movement, a kill ring, undo, and atomic paste chips.
The buffer is a sequence of atoms. An atom is either one grapheme cluster (so an emoji ZWJ sequence or a combining accent moves and deletes as one unit, never splitting into broken runes) or a paste chip: a large paste is held out of the buffer and represented by a single atom that the cursor steps over and Backspace removes whole, so a 400-line paste occupies one cell of editing surface instead of drowning the composer. Content expands chips back to their full text at submit time.
The editor is pure state: no terminal, no reader, no timers. It consumes the input package's decoded events through Handle and reports what the caller should do (redraw, submit, recall history), and it renders itself to width-bounded rows for the screen painter. Determinism holds by construction: undo grouping keys off edit boundaries, not wall-clock idle time.
Index ¶
- func ParseChord(s string) (string, error)
- type Action
- type Attachment
- type Command
- type Editor
- func (e *Editor) Attachments() []Attachment
- func (e *Editor) Backspace()
- func (e *Editor) Clear()
- func (e *Editor) CompleteToken(trigger rune, text string)
- func (e *Editor) Content() string
- func (e *Editor) Delete()
- func (e *Editor) Down() bool
- func (e *Editor) Empty() bool
- func (e *Editor) Handle(ev input.Event) Action
- func (e *Editor) Insert(s string)
- func (e *Editor) InsertImage(att Attachment)
- func (e *Editor) InsertPaste(text string)
- func (e *Editor) KillToEnd()
- func (e *Editor) KillToStart()
- func (e *Editor) KillWordBack()
- func (e *Editor) KillWordForward()
- func (e *Editor) Left()
- func (e *Editor) LineEnd()
- func (e *Editor) LineStart()
- func (e *Editor) Redo() bool
- func (e *Editor) Render(width int) (rows []string, curRow, curCol int)
- func (e *Editor) Right()
- func (e *Editor) SetKeymap(km Keymap)
- func (e *Editor) Token(trigger rune) (string, bool)
- func (e *Editor) Undo() bool
- func (e *Editor) Up() bool
- func (e *Editor) WordLeft()
- func (e *Editor) WordRight()
- func (e *Editor) Yank()
- func (e *Editor) YankPop()
- type Keymap
Constants ¶
This section is empty.
Variables ¶
This section is empty.
Functions ¶
func ParseChord ¶
ParseChord normalizes one chord spelling ("Ctrl+Shift+Left", "alt+d", "enter") into the canonical form Keymap keys use: lowercase, modifiers in ctrl-alt-shift-super order, then a key name or a single character. It refuses spellings it cannot represent, so a typo in a keymap file is an error, not a binding that never fires.
Types ¶
type Action ¶
type Action int
Action is what the caller should do after the editor consumed an event.
const ( // ActionNone reports the event was not the editor's to handle. ActionNone Action = iota // ActionRedraw reports the buffer or cursor changed; repaint the composer. ActionRedraw // ActionSubmit reports the user pressed Enter; send Content and Clear. ActionSubmit // ActionEsc reports Escape with the buffer untouched by this key; the // caller owns what Esc means (backtrack, dismiss an overlay, interrupt). ActionEsc // ActionTab hands completion to the caller, since it needs the file and // command universe the editor has no business knowing. ActionTab // ActionHistoryPrev reports the cursor tried to move above the first // line; recall the previous prompt. ActionHistoryPrev // ActionHistoryNext reports the cursor tried to move below the last // line; recall the next prompt. ActionHistoryNext )
type Attachment ¶
Attachment is one image bound to the composer: the encoded bytes and their IANA media type. The clipboard port yields PNG; the value is what a submit carries alongside the prompt text so the image reaches the model. It is deliberately a plain UI-layer type, not the model port's llm.Image, so the reusable composer stays free of a model-port dependency; the host converts it at the one point where the prompt is turned into a turn.
type Command ¶
type Command string
Command names one editor operation a key chord can bind to. Names are dotted: "compose.*" commands resolve to caller-owned actions (submit, escape, completion), "editor.*" commands act on the buffer directly.
const ( CmdUnbind Command = "none" CmdSubmit Command = "compose.submit" CmdNewline Command = "compose.newline" CmdEscape Command = "compose.escape" CmdComplete Command = "compose.complete" CmdBackspace Command = "editor.backspace" CmdDelete Command = "editor.delete" CmdDeleteOrEOF Command = "editor.delete-or-eof" CmdLeft Command = "editor.left" CmdRight Command = "editor.right" CmdWordLeft Command = "editor.word-left" CmdWordRight Command = "editor.word-right" CmdCursorUp Command = "editor.cursor-up" CmdCursorDown Command = "editor.cursor-down" CmdLineStart Command = "editor.line-start" CmdLineEnd Command = "editor.line-end" CmdKillToEnd Command = "editor.kill-to-end" CmdKillToStart Command = "editor.kill-to-start" CmdKillWordBack Command = "editor.kill-word-back" CmdKillWordForward Command = "editor.kill-word-forward" CmdYank Command = "editor.yank" CmdYankPop Command = "editor.yank-pop" CmdUndo Command = "editor.undo" CmdRedo Command = "editor.redo" )
The bindable command vocabulary. CmdUnbind is the explicit "none": binding a chord to it removes the default binding for that chord.
type Editor ¶
type Editor struct {
// contains filtered or unexported fields
}
Editor is the multi-line composer buffer. The zero value is ready to use.
func (*Editor) Attachments ¶
func (e *Editor) Attachments() []Attachment
Attachments returns the images bound to the composer in buffer order, so the order the model sees matches the order the chips appear and a chip the user backspaced away is absent. It returns nil when there are none.
func (*Editor) Backspace ¶
func (e *Editor) Backspace()
Backspace deletes the atom before the cursor: one grapheme cluster, or a whole chip.
func (*Editor) Clear ¶
func (e *Editor) Clear()
Clear resets the buffer, history, and chips for the next prompt.
func (*Editor) CompleteToken ¶
CompleteToken replaces the active trigger token with the trigger, the chosen text, and a trailing space, and puts the cursor after the space. Without an active token it does nothing, so a stale accept (the buffer changed under a queued key) cannot splice the wrong range. One undo step restores the typed query.
func (*Editor) Content ¶
Content returns the buffer's text with every paste chip expanded to its full pasted text. Image chips contribute nothing: an image travels with the turn as a separate attachment (see Attachments), not as text. This is the prompt text submit sends.
func (*Editor) Down ¶
Down mirrors Up toward the next line; false on the last line means the caller should move forward in prompt history.
func (*Editor) Handle ¶
Handle consumes one decoded input event, resolving keys through the editor's keymap (the default map unless SetKeymap changed it).
func (*Editor) Insert ¶
Insert splices text at the cursor. Line endings are normalized to \n, tabs become spaces (a tab's cell width depends on its column, which would make row widths unstable under the painter's overflow guard), and other control characters are dropped.
func (*Editor) InsertImage ¶
func (e *Editor) InsertImage(att Attachment)
InsertImage binds an image to the composer and inserts an atomic chip for it at the cursor: like a paste chip, the cursor steps over it whole and Backspace removes it whole, and it holds no bytes in the buffer. The image itself is carried out of line and surfaced by Attachments at submit.
func (*Editor) InsertPaste ¶
InsertPaste inserts one paste event: inline when small and single-line, as an atomic chip otherwise.
func (*Editor) KillToEnd ¶
func (e *Editor) KillToEnd()
KillToEnd kills from the cursor to the end of the line, or the newline itself when the cursor already sits at the end (emacs Ctrl+K).
func (*Editor) KillToStart ¶
func (e *Editor) KillToStart()
KillToStart kills from the start of the line to the cursor.
func (*Editor) KillWordBack ¶
func (e *Editor) KillWordBack()
KillWordBack kills the word before the cursor.
func (*Editor) KillWordForward ¶
func (e *Editor) KillWordForward()
KillWordForward kills the word after the cursor.
func (*Editor) Left ¶
func (e *Editor) Left()
Left moves the cursor one atom back; atom granularity is what makes chips and grapheme clusters atomic to the user.
func (*Editor) LineEnd ¶
func (e *Editor) LineEnd()
LineEnd moves the cursor to the end of the current logical line.
func (*Editor) LineStart ¶
func (e *Editor) LineStart()
LineStart moves the cursor to the start of the current logical line.
func (*Editor) Render ¶
Render lays the buffer out as width-bounded rows for the screen painter and reports the cursor's cell position within them. Logical lines soft-wrap at the width; no row ever exceeds it, matching the painter's overflow guard. Rows are plain text: styling (the chip highlight, the prompt gutter) is the caller's composition step, and keeping ANSI out of the editor keeps width arithmetic exact.
func (*Editor) SetKeymap ¶
SetKeymap replaces the editor's bindings. Nil restores the default map. The map is used as given and must not be mutated afterwards.
func (*Editor) Token ¶
Token returns the text after the trigger in the token ending at the cursor, and whether such a token is active. The query may be empty: a trigger just typed is an active token asking for the unfiltered universe.
func (*Editor) Up ¶
Up moves the cursor to the previous logical line, keeping the column when it fits. It reports false when the cursor is already on the first line, which is the caller's cue to recall prompt history instead.
func (*Editor) WordLeft ¶
func (e *Editor) WordLeft()
WordLeft moves the cursor to the start of the previous word.
type Keymap ¶
Keymap maps a normalized key chord (see ParseChord) to a command. Chords not in the map fall through: printable text inserts, everything else is unclaimed.
func Default ¶
func Default() Keymap
Default is the built-in keymap: the emacs and readline set every shell user already knows, plus the arrow, Home/End, and Delete keys in both their legacy and kitty encodings (the input decoder has already normalized those onto one vocabulary).
func LoadKeymap ¶
LoadKeymap reads a JSON keymap and returns it layered over the default map, so a user keymap starts from the complete built-in set and only overrides what it names. Binding a chord to "none" removes its default. Unknown commands and malformed chords are refused rather than ignored: a misspelled name in a keymap file would otherwise silently bind nothing, which is the kind of failure a user cannot debug.