Skip to content

Commit

Permalink
chore(input): cleanup and document flags
Browse files Browse the repository at this point in the history
  • Loading branch information
aymanbagabas committed Feb 15, 2024
1 parent af6af36 commit 2ed1819
Show file tree
Hide file tree
Showing 4 changed files with 116 additions and 66 deletions.
103 changes: 79 additions & 24 deletions exp/term/input/ansi/driver.go
Original file line number Diff line number Diff line change
Expand Up @@ -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.
Expand All @@ -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,
Expand Down
2 changes: 1 addition & 1 deletion exp/term/input/ansi/examples/readinput/main.go
Original file line number Diff line number Diff line change
Expand Up @@ -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 {
Expand Down
69 changes: 31 additions & 38 deletions exp/term/input/ansi/table.go
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand Down Expand Up @@ -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
Expand Down Expand Up @@ -345,5 +336,7 @@ func (d *driver) registerKeys(flags int) {
}

// Register terminfo keys
d.registerTerminfoKeys()
if flags&FlagNoTerminfo == 0 {
d.registerTerminfoKeys()
}
}
8 changes: 5 additions & 3 deletions exp/term/input/ansi/terminfo.go
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand Down Expand Up @@ -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}
Expand Down

0 comments on commit 2ed1819

Please sign in to comment.