lineedit

package
v0.2.0 Latest Latest
Warning

This package is not in the latest version of its module.

Go to latest
Published: Aug 3, 2026 License: AGPL-3.0 Imports: 10 Imported by: 0

Documentation

Overview

Package lineedit is Tomobit's inline line editor (ADR-0022 Decision 3).

It owns one line of the terminal the user is already looking at: raw mode while a line is being typed, cooked mode the rest of the time. It is deliberately not a full-screen TUI — Tomo's lines and the provider's output must stay in the scrollback like any other log, the same reason the avatar is drawn with half-blocks instead of a canvas (ADR-0008).

Everything but the terminal's physics is hand-rolled: golang.org/x/term is used for raw mode and the window size only, and uniseg for display width (Japanese is full-width — cursor math is wrong without it).

Index

Constants

This section is empty.

Variables

View Source
var ErrInterrupt = errors.New("lineedit: interrupted")

ErrInterrupt is returned when the line was abandoned with Ctrl-C. The distinction from io.EOF (Ctrl-D) matters to the caller: one is "not this line", the other is "no more lines".

Functions

This section is empty.

Types

type Editor

type Editor struct {

	// Completer, when set, drives Tab completion. Given the whole buffer and
	// the cursor's rune index it returns the candidate replacements for the
	// token at the cursor and the rune index where that token starts
	// (start<0 or start>pos means "nothing to complete here"). The editor
	// stays ignorant of what is being completed — the caller owns the
	// vocabulary, the same boundary ADR-0022 Decision 3 drew for lineedit.
	Completer func(text string, pos int) (candidates []string, start int)

	// WrapIndent is the column a wrapped or multi-line input's continuation
	// rows indent to, so they align under a gutter instead of the terminal's
	// left edge. Zero (the default) keeps the flush-left wrap; the caller owns
	// the gutter width, the same boundary Completer draws.
	WrapIndent int

	// TextStyle, when set, is an SGR sequence (a faint background) painted
	// behind the typed text — not the prompt, not the gutter. Empty keeps the
	// line unstyled. The caller owns the colour and whether the terminal wants
	// one at all (NO_COLOR), the same boundary dim already draws elsewhere.
	TextStyle string
	// contains filtered or unexported fields
}

Editor reads lines from a terminal. Zero value is not usable — use New.

func New

func New(in, out *os.File) *Editor

func (*Editor) AddHistory

func (e *Editor) AddHistory(s string)

AddHistory records a submitted line. Blank lines and an immediate repeat are dropped — pressing Up should reach the last thing worth retyping. The same line is appended to the history file if one is set, so the next process starts where this one left off.

func (*Editor) Interactive

func (e *Editor) Interactive() bool

Interactive reports whether both ends are a terminal. A pipe gets the cooked path: same reader, one line per read, no ANSI.

func (*Editor) ReadLine

func (e *Editor) ReadLine(prompt string) (string, error)

ReadLine prints prompt and returns the submitted line. It returns io.EOF on Ctrl-D at an empty line (or a closed stdin) and ErrInterrupt on Ctrl-C.

func (*Editor) Reader

func (e *Editor) Reader() *bufio.Reader

Reader exposes the editor's buffered view of its input, so that prompts asked between lines (the adoption question, Tomo's) read from the same place.

What must not happen is a prompt reading os.Stdin directly: this reader has already pulled bytes off that file — typeahead typed while a provider was working — and a reader over the file cannot see them. It would block for input that has already arrived, and the bytes would be stranded here. (Passing this value back through bufio.NewReader is harmless — measured: the stdlib hands the same reader back rather than stacking a second one.)

func (*Editor) SetHistoryFile

func (e *Editor) SetHistoryFile(path string, warn io.Writer) error

SetHistoryFile loads path into the ring and remembers it for later appends, with warn as the sink for append failures (ADR-0024 Decision 1). The load failure is returned, not fatal: history is UI state, not the ledger, and has no right to keep a chat from opening — the caller warns once and carries on.

A file longer than maxHistory is compacted on load, so appending across many sessions cannot grow it without bound. A missing file is not an error: the first process to run simply has no past to recall.

func (*Editor) SetReader

func (e *Editor) SetReader(r io.Reader)

SetReader replaces the buffered input the cooked path reads from, so a caller can interpose on the bytes beneath the buffer. The NDJSON view's flush-on-read hook (ADR-0032 Decision 1) lives there — under os.Stdin, where bufio only pulls when a read blocks — because above the buffer the moment a read reaches the bottom is invisible. The *os.File given to New still backs raw-mode detection and width; the only caller (the view) runs with a non-TTY stdout, so Interactive() is false and ReadLine stays on the cooked path this reader feeds — raw mode never engages. Reader() returns this one afterwards, so a prompt asked between lines shares it.

type Key

type Key struct {
	Type KeyType
	Rune rune
	Text string
}

Key is a decoded keypress. Rune is set for KeyRune, Text for KeyPaste.

type KeyType

type KeyType int

KeyType is one editing intent, already decoded from the terminal's bytes.

const (
	KeyUnknown KeyType = iota
	KeyRune
	KeyEnter
	KeyNewline // insert a literal newline (Shift/Alt+Enter, or `\` before Enter)
	KeyBackspace
	KeyDelete
	KeyLeft
	KeyRight
	KeyUp
	KeyDown
	KeyWordLeft
	KeyWordRight
	KeyHome
	KeyEnd
	KeyKillToEnd
	KeyClearInput
	KeyKillWord
	KeyYank // Ctrl-Y: put the kill slot back at the cursor
	KeyClearScreen
	KeyTab       // completion
	KeySearch    // Ctrl-R: reverse incremental history search
	KeySuspend   // Ctrl-Z: hand the terminal back and stop (unix only)
	KeyInterrupt // Ctrl-C
	KeyEOT       // Ctrl-D: end of input on an empty buffer, delete otherwise
	KeyPaste     // Text holds the whole bracketed-paste block
)

Jump to

Keyboard shortcuts

? : This menu
/ : Search site
f or F : Jump to
y or Y : Canonical URL
⌂ 换网址