diff --git a/exp/term/input/ansi/driver.go b/exp/term/input/ansi/driver.go index dc86b3ec..1720d9a3 100644 --- a/exp/term/input/ansi/driver.go +++ b/exp/term/input/ansi/driver.go @@ -4,32 +4,93 @@ import ( "bufio" "fmt" "io" - "os" "unicode/utf8" "github.com/charmbracelet/x/exp/term/ansi" "github.com/charmbracelet/x/exp/term/input" ) -// ErrUnsupportedReader is returned when the reader is not a *bufio.Reader. -var ErrUnsupportedReader = fmt.Errorf("unsupported reader") - // Flags to control the behavior of the driver. const ( - Fctrlsp = 1 << iota // treat NUL as ctrl+space, otherwise ctrl+@ - Ftabsym // treat tab as a symbol - Fentersym // treat enter as a symbol - Fescsym // treat escape as a symbol - Fspacesym // treat space as a symbol - Fdelbackspace // treat DEL as a symbol - Ffindhome // treat find symbol as home - Fselectend // treat select symbol as end - - Fxterm // register xterm keys - Fterminfo // use terminfo - FFKeys // preserve function keys - - Stdflags = Ftabsym | Fentersym | Fescsym | Fspacesym | Fdelbackspace | Ffindhome | Fselectend | Fterminfo | Fxterm + // When this flag is set, the driver will treat both Ctrl+Space and Ctrl+@ + // as the same key sequence. + // + // Historically, the ANSI specs generate NUL (0x00) on both the Ctrl+Space + // and Ctrl+@ key sequences. This flag allows the driver to treat both as + // the same key sequence. + FlagCtrlAt = 1 << iota + + // When this flag is set, the driver will treat the Tab key and Ctrl+I as + // the same key sequence. + // + // Historically, the ANSI specs generate HT (0x09) on both the Tab key and + // Ctrl+I. This flag allows the driver to treat both as the same key + // sequence. + FlagCtrlI + + // When this flag is set, the driver will treat the Enter key and Ctrl+M as + // the same key sequence. + // + // Historically, the ANSI specs generate CR (0x0D) on both the Enter key + // and Ctrl+M. This flag allows the driver to treat both as the same key + FlagCtrlM + + // When this flag is set, the driver will treat Escape and Ctrl+[ as + // the same key sequence. + // + // Historically, the ANSI specs generate ESC (0x1B) on both the Escape key + // and Ctrl+[. This flag allows the driver to treat both as the same key + // sequence. + FlagCtrlOpenBracket + + // When this flag is set, the driver will treat space as a key rune instead + // of a key symbol. + FlagSpace + + // When this flag is set, the driver will send a BS (0x08 byte) character + // instead of a DEL (0x7F byte) character when the Backspace key is + // pressed. + // + // The VT100 terminal has both a Backspace and a Delete key. The VT220 + // terminal dropped the Backspace key and replaced it with the Delete key. + // Both terminals send a DEL character when the Delete key is pressed. + // Modern terminals and PCs later readded the Delete key but used a + // different key sequence, and the Backspace key was standardized to send a + // DEL character. + FlagBackspace + + // When this flag is set, the driver will recognize the Find key instead of + // treating it as a Home key. + // + // The Find key was part of the VT220 keyboard, and is no longer used in + // modern day PCs. + FlagFind + + // When this flag is set, the driver will recognize the Select key instead + // of treating it as a End key. + // + // The Symbol key was part of the VT220 keyboard, and is no longer used in + // modern day PCs. + FlagSelect + + // When this flag is set, the driver won't register XTerm key sequences. + // + // Most modern terminals are compatible with XTerm, so this flag is + // generally not needed. + FlagNoXTerm + + // When this flag is set, the driver won't use Terminfo databases to + // overwrite the default key sequences. + FlagNoTerminfo + + // When this flag is set, the driver will preserve function keys (F13-F63) + // as symbols. + // + // Since these keys are not part of today's standard 20th century keyboard, + // we treat them as F1-F12 modifier keys i.e. ctrl/shift/alt + Fn combos. + // Key definitions come from Terminfo, this flag is only useful when + // FlagTerminfo is not set. + FlagFKeys ) // driver represents a terminal ANSI input driver. @@ -47,12 +108,6 @@ var _ input.Driver = &driver{} // and XTerm. It supports reading Terminfo databases to overwrite the default // key sequences. func NewDriver(r io.Reader, term string, flags int) input.Driver { - if r == nil { - r = os.Stdin - } - if term == "" { - term = os.Getenv("TERM") - } d := &driver{ rd: bufio.NewReaderSize(r, 256), flags: flags, diff --git a/exp/term/input/ansi/examples/readinput/main.go b/exp/term/input/ansi/examples/readinput/main.go index 1b91ba4e..08a9043d 100644 --- a/exp/term/input/ansi/examples/readinput/main.go +++ b/exp/term/input/ansi/examples/readinput/main.go @@ -20,7 +20,7 @@ func main() { defer term.Restore(os.Stdin.Fd(), state) // r := bufio.NewReader(strings.NewReader("\x00\x1ba\x1b[Z\x1b\x01\x1b[A")) - rd := ansi.NewDriver(bufio.NewReaderSize(os.Stdin, 256), os.Getenv("TERM"), ansi.Stdflags) + rd := ansi.NewDriver(bufio.NewReaderSize(os.Stdin, 256), os.Getenv("TERM"), 0) // p, err := d.PeekInput(2) // if err != nil { diff --git a/exp/term/input/ansi/table.go b/exp/term/input/ansi/table.go index 65acc94d..b3cac138 100644 --- a/exp/term/input/ansi/table.go +++ b/exp/term/input/ansi/table.go @@ -6,56 +6,47 @@ import ( ) func (d *driver) registerKeys(flags int) { - nul := input.KeyEvent{Rune: '@', Mod: input.Ctrl} // ctrl+@ or ctrl+space - if flags&Fctrlsp != 0 { - if flags&Fspacesym != 0 { - nul.Rune = 0 - nul.Sym = input.KeySpace - } else { - nul.Rune = ' ' - } + nul := input.KeyEvent{Sym: input.KeySpace, Mod: input.Ctrl} // ctrl+@ or ctrl+space + if flags&FlagSpace != 0 { + nul = input.KeyEvent{Rune: ' ', Mod: input.Ctrl} + } + if flags&FlagCtrlAt != 0 { + nul = input.KeyEvent{Rune: '@', Mod: input.Ctrl} } - tab := input.KeyEvent{Rune: 'i', Mod: input.Ctrl} // ctrl+i or tab - if flags&Ftabsym != 0 { - tab.Rune = 0 - tab.Mod = 0 - tab.Sym = input.KeyTab + tab := input.KeyEvent{Sym: input.KeyTab} // ctrl+i or tab + if flags&FlagCtrlI != 0 { + tab = input.KeyEvent{Rune: 'i', Mod: input.Ctrl} } - enter := input.KeyEvent{Rune: 'm', Mod: input.Ctrl} // ctrl+m or enter - if flags&Fentersym != 0 { - enter.Rune = 0 - enter.Mod = 0 - enter.Sym = input.KeyEnter + enter := input.KeyEvent{Sym: input.KeyEnter} // ctrl+m or enter + if flags&FlagCtrlM != 0 { + enter = input.KeyEvent{Rune: 'm', Mod: input.Ctrl} } - esc := input.KeyEvent{Rune: '[', Mod: input.Ctrl} // ctrl+[ or escape - if flags&Fescsym != 0 { - esc.Rune = 0 - esc.Mod = 0 - esc.Sym = input.KeyEscape + esc := input.KeyEvent{Sym: input.KeyEscape} // ctrl+[ or escape + if flags&FlagCtrlOpenBracket != 0 { + esc = input.KeyEvent{Rune: '[', Mod: input.Ctrl} // ctrl+[ or escape } - sp := input.KeyEvent{Rune: ' '} - if flags&Fspacesym != 0 { - sp.Rune = 0 - sp.Sym = input.KeySpace + sp := input.KeyEvent{Sym: input.KeySpace} + if flags&FlagSpace != 0 { + sp = input.KeyEvent{Rune: ' '} } - del := input.KeyEvent{Sym: input.KeyDelete} - if flags&Fdelbackspace != 0 { - del.Sym = input.KeyBackspace + del := input.KeyEvent{Sym: input.KeyBackspace} + if flags&FlagBackspace != 0 { + del.Sym = input.KeyDelete } - find := input.KeyEvent{Sym: input.KeyFind} - if flags&Ffindhome != 0 { - find.Sym = input.KeyHome + find := input.KeyEvent{Sym: input.KeyHome} + if flags&FlagFind != 0 { + find.Sym = input.KeyFind } - sel := input.KeyEvent{Sym: input.KeySelect} - if flags&Fselectend != 0 { - sel.Sym = input.KeyEnd + sel := input.KeyEvent{Sym: input.KeyEnd} + if flags&FlagSelect != 0 { + sel.Sym = input.KeySelect } // The following is a table of key sequences and their corresponding key @@ -218,7 +209,7 @@ func (d *driver) registerKeys(flags int) { "33": {Sym: input.KeyF19}, "34": {Sym: input.KeyF20}, } - if flags&Fxterm != 0 { + if flags&FlagNoXTerm == 0 { // XTerm modifiers // These are offset by 1 to be compatible with our Mod type. // See https://invisible-island.net/xterm/ctlseqs/ctlseqs.html#h3-PC-Style-Function-Keys @@ -345,5 +336,7 @@ func (d *driver) registerKeys(flags int) { } // Register terminfo keys - d.registerTerminfoKeys() + if flags&FlagNoTerminfo == 0 { + d.registerTerminfoKeys() + } } diff --git a/exp/term/input/ansi/terminfo.go b/exp/term/input/ansi/terminfo.go index f060e561..4dd1c75e 100644 --- a/exp/term/input/ansi/terminfo.go +++ b/exp/term/input/ansi/terminfo.go @@ -9,13 +9,15 @@ import ( ) func (d *driver) registerTerminfoKeys() { + if d.term == "" { + return + } + ti, _ := terminfo.Load(d.term) if ti == nil { return } - log.Printf("Found terminfo database for %q: %#v\r\n", d.term, ti.Names) - tiTable := defaultTerminfoKeys(d.flags) // Default keys @@ -222,7 +224,7 @@ func defaultTerminfoKeys(flags int) map[string]input.KeyEvent { // Preserve F keys from F13 to F63 instead of using them for F-keys // modifiers. - if flags&FFKeys != 0 { + if flags&FlagFKeys != 0 { keys["kf13"] = input.KeyEvent{Sym: input.KeyF13} keys["kf14"] = input.KeyEvent{Sym: input.KeyF14} keys["kf15"] = input.KeyEvent{Sym: input.KeyF15}