Add vendor

Specifically because I want to modify a dependency
This commit is contained in:
2024-08-26 01:00:50 +02:00
parent 64061af4c8
commit 3c5e991ec5
704 changed files with 291662 additions and 0 deletions

21
vendor/github.com/charmbracelet/x/ansi/LICENSE generated vendored Normal file
View File

@@ -0,0 +1,21 @@
MIT License
Copyright (c) 2023 Charmbracelet, Inc.
Permission is hereby granted, free of charge, to any person obtaining a copy
of this software and associated documentation files (the "Software"), to deal
in the Software without restriction, including without limitation the rights
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
copies of the Software, and to permit persons to whom the Software is
furnished to do so, subject to the following conditions:
The above copyright notice and this permission notice shall be included in all
copies or substantial portions of the Software.
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
SOFTWARE.

11
vendor/github.com/charmbracelet/x/ansi/ansi.go generated vendored Normal file
View File

@@ -0,0 +1,11 @@
package ansi
import "io"
// Execute is a function that "execute" the given escape sequence by writing it
// to the provided output writter.
//
// This is a syntactic sugar over [io.WriteString].
func Execute(w io.Writer, s string) (int, error) {
return io.WriteString(w, s)
}

8
vendor/github.com/charmbracelet/x/ansi/ascii.go generated vendored Normal file
View File

@@ -0,0 +1,8 @@
package ansi
const (
// SP is the space character (Char: \x20).
SP = 0x20
// DEL is the delete character (Caret: ^?, Char: \x7f).
DEL = 0x7F
)

61
vendor/github.com/charmbracelet/x/ansi/background.go generated vendored Normal file
View File

@@ -0,0 +1,61 @@
package ansi
import (
"image/color"
)
// SetForegroundColor returns a sequence that sets the default terminal
// foreground color.
//
// OSC 10 ; color ST
// OSC 10 ; color BEL
//
// Where color is the encoded color number.
//
// See: https://invisible-island.net/xterm/ctlseqs/ctlseqs.html#h3-Operating-System-Commands
func SetForegroundColor(c color.Color) string {
return "\x1b]10;" + colorToHexString(c) + "\x07"
}
// RequestForegroundColor is a sequence that requests the current default
// terminal foreground color.
//
// See: https://invisible-island.net/xterm/ctlseqs/ctlseqs.html#h3-Operating-System-Commands
const RequestForegroundColor = "\x1b]10;?\x07"
// SetBackgroundColor returns a sequence that sets the default terminal
// background color.
//
// OSC 11 ; color ST
// OSC 11 ; color BEL
//
// Where color is the encoded color number.
//
// See: https://invisible-island.net/xterm/ctlseqs/ctlseqs.html#h3-Operating-System-Commands
func SetBackgroundColor(c color.Color) string {
return "\x1b]11;" + colorToHexString(c) + "\x07"
}
// RequestBackgroundColor is a sequence that requests the current default
// terminal background color.
//
// See: https://invisible-island.net/xterm/ctlseqs/ctlseqs.html#h3-Operating-System-Commands
const RequestBackgroundColor = "\x1b]11;?\x07"
// SetCursorColor returns a sequence that sets the terminal cursor color.
//
// OSC 12 ; color ST
// OSC 12 ; color BEL
//
// Where color is the encoded color number.
//
// See: https://invisible-island.net/xterm/ctlseqs/ctlseqs.html#h3-Operating-System-Commands
func SetCursorColor(c color.Color) string {
return "\x1b]12;" + colorToHexString(c) + "\x07"
}
// RequestCursorColor is a sequence that requests the current terminal cursor
// color.
//
// See: https://invisible-island.net/xterm/ctlseqs/ctlseqs.html#h3-Operating-System-Commands
const RequestCursorColor = "\x1b]12;?\x07"

72
vendor/github.com/charmbracelet/x/ansi/c0.go generated vendored Normal file
View File

@@ -0,0 +1,72 @@
package ansi
// C0 control characters.
//
// These range from (0x00-0x1F) as defined in ISO 646 (ASCII).
// See: https://en.wikipedia.org/wiki/C0_and_C1_control_codes
const (
// NUL is the null character (Caret: ^@, Char: \0).
NUL = 0x00
// SOH is the start of heading character (Caret: ^A).
SOH = 0x01
// STX is the start of text character (Caret: ^B).
STX = 0x02
// ETX is the end of text character (Caret: ^C).
ETX = 0x03
// EOT is the end of transmission character (Caret: ^D).
EOT = 0x04
// ENQ is the enquiry character (Caret: ^E).
ENQ = 0x05
// ACK is the acknowledge character (Caret: ^F).
ACK = 0x06
// BEL is the bell character (Caret: ^G, Char: \a).
BEL = 0x07
// BS is the backspace character (Caret: ^H, Char: \b).
BS = 0x08
// HT is the horizontal tab character (Caret: ^I, Char: \t).
HT = 0x09
// LF is the line feed character (Caret: ^J, Char: \n).
LF = 0x0A
// VT is the vertical tab character (Caret: ^K, Char: \v).
VT = 0x0B
// FF is the form feed character (Caret: ^L, Char: \f).
FF = 0x0C
// CR is the carriage return character (Caret: ^M, Char: \r).
CR = 0x0D
// SO is the shift out character (Caret: ^N).
SO = 0x0E
// SI is the shift in character (Caret: ^O).
SI = 0x0F
// DLE is the data link escape character (Caret: ^P).
DLE = 0x10
// DC1 is the device control 1 character (Caret: ^Q).
DC1 = 0x11
// DC2 is the device control 2 character (Caret: ^R).
DC2 = 0x12
// DC3 is the device control 3 character (Caret: ^S).
DC3 = 0x13
// DC4 is the device control 4 character (Caret: ^T).
DC4 = 0x14
// NAK is the negative acknowledge character (Caret: ^U).
NAK = 0x15
// SYN is the synchronous idle character (Caret: ^V).
SYN = 0x16
// ETB is the end of transmission block character (Caret: ^W).
ETB = 0x17
// CAN is the cancel character (Caret: ^X).
CAN = 0x18
// EM is the end of medium character (Caret: ^Y).
EM = 0x19
// SUB is the substitute character (Caret: ^Z).
SUB = 0x1A
// ESC is the escape character (Caret: ^[, Char: \e).
ESC = 0x1B
// FS is the file separator character (Caret: ^\).
FS = 0x1C
// GS is the group separator character (Caret: ^]).
GS = 0x1D
// RS is the record separator character (Caret: ^^).
RS = 0x1E
// US is the unit separator character (Caret: ^_).
US = 0x1F
)

72
vendor/github.com/charmbracelet/x/ansi/c1.go generated vendored Normal file
View File

@@ -0,0 +1,72 @@
package ansi
// C1 control characters.
//
// These range from (0x80-0x9F) as defined in ISO 6429 (ECMA-48).
// See: https://en.wikipedia.org/wiki/C0_and_C1_control_codes
const (
// PAD is the padding character.
PAD = 0x80
// HOP is the high octet preset character.
HOP = 0x81
// BPH is the break permitted here character.
BPH = 0x82
// NBH is the no break here character.
NBH = 0x83
// IND is the index character.
IND = 0x84
// NEL is the next line character.
NEL = 0x85
// SSA is the start of selected area character.
SSA = 0x86
// ESA is the end of selected area character.
ESA = 0x87
// HTS is the horizontal tab set character.
HTS = 0x88
// HTJ is the horizontal tab with justification character.
HTJ = 0x89
// VTS is the vertical tab set character.
VTS = 0x8A
// PLD is the partial line forward character.
PLD = 0x8B
// PLU is the partial line backward character.
PLU = 0x8C
// RI is the reverse index character.
RI = 0x8D
// SS2 is the single shift 2 character.
SS2 = 0x8E
// SS3 is the single shift 3 character.
SS3 = 0x8F
// DCS is the device control string character.
DCS = 0x90
// PU1 is the private use 1 character.
PU1 = 0x91
// PU2 is the private use 2 character.
PU2 = 0x92
// STS is the set transmit state character.
STS = 0x93
// CCH is the cancel character.
CCH = 0x94
// MW is the message waiting character.
MW = 0x95
// SPA is the start of guarded area character.
SPA = 0x96
// EPA is the end of guarded area character.
EPA = 0x97
// SOS is the start of string character.
SOS = 0x98
// SGCI is the single graphic character introducer character.
SGCI = 0x99
// SCI is the single character introducer character.
SCI = 0x9A
// CSI is the control sequence introducer character.
CSI = 0x9B
// ST is the string terminator character.
ST = 0x9C
// OSC is the operating system command character.
OSC = 0x9D
// PM is the privacy message character.
PM = 0x9E
// APC is the application program command character.
APC = 0x9F
)

75
vendor/github.com/charmbracelet/x/ansi/clipboard.go generated vendored Normal file
View File

@@ -0,0 +1,75 @@
package ansi
import "encoding/base64"
// Clipboard names.
const (
SystemClipboard = 'c'
PrimaryClipboard = 'p'
)
// SetClipboard returns a sequence for manipulating the clipboard.
//
// OSC 52 ; Pc ; Pd ST
// OSC 52 ; Pc ; Pd BEL
//
// Where Pc is the clipboard name and Pd is the base64 encoded data.
// Empty data or invalid base64 data will reset the clipboard.
//
// See: https://invisible-island.net/xterm/ctlseqs/ctlseqs.html#h3-Operating-System-Commands
func SetClipboard(c byte, d string) string {
if d != "" {
d = base64.StdEncoding.EncodeToString([]byte(d))
}
return "\x1b]52;" + string(c) + ";" + d + "\x07"
}
// SetSystemClipboard returns a sequence for setting the system clipboard.
//
// This is equivalent to SetClipboard(SystemClipboard, d).
func SetSystemClipboard(d string) string {
return SetClipboard(SystemClipboard, d)
}
// SetPrimaryClipboard returns a sequence for setting the primary clipboard.
//
// This is equivalent to SetClipboard(PrimaryClipboard, d).
func SetPrimaryClipboard(d string) string {
return SetClipboard(PrimaryClipboard, d)
}
// ResetClipboard returns a sequence for resetting the clipboard.
//
// This is equivalent to SetClipboard(c, "").
//
// See: https://invisible-island.net/xterm/ctlseqs/ctlseqs.html#h3-Operating-System-Commands
func ResetClipboard(c byte) string {
return SetClipboard(c, "")
}
// ResetSystemClipboard is a sequence for resetting the system clipboard.
//
// This is equivalent to ResetClipboard(SystemClipboard).
const ResetSystemClipboard = "\x1b]52;c;\x07"
// ResetPrimaryClipboard is a sequence for resetting the primary clipboard.
//
// This is equivalent to ResetClipboard(PrimaryClipboard).
const ResetPrimaryClipboard = "\x1b]52;p;\x07"
// RequestClipboard returns a sequence for requesting the clipboard.
//
// See: https://invisible-island.net/xterm/ctlseqs/ctlseqs.html#h3-Operating-System-Commands
func RequestClipboard(c byte) string {
return "\x1b]52;" + string(c) + ";?\x07"
}
// RequestSystemClipboard is a sequence for requesting the system clipboard.
//
// This is equivalent to RequestClipboard(SystemClipboard).
const RequestSystemClipboard = "\x1b]52;c;?\x07"
// RequestPrimaryClipboard is a sequence for requesting the primary clipboard.
//
// This is equivalent to RequestClipboard(PrimaryClipboard).
const RequestPrimaryClipboard = "\x1b]52;p;?\x07"

196
vendor/github.com/charmbracelet/x/ansi/color.go generated vendored Normal file
View File

@@ -0,0 +1,196 @@
package ansi
import (
"image/color"
)
// Technically speaking, the 16 basic ANSI colors are arbitrary and can be
// customized at the terminal level. Given that, we're returning what we feel
// are good defaults.
//
// This could also be a slice, but we use a map to make the mappings very
// explicit.
//
// See: https://www.ditig.com/publications/256-colors-cheat-sheet
var lowANSI = map[uint32]uint32{
0: 0x000000, // black
1: 0x800000, // red
2: 0x008000, // green
3: 0x808000, // yellow
4: 0x000080, // blue
5: 0x800080, // magenta
6: 0x008080, // cyan
7: 0xc0c0c0, // white
8: 0x808080, // bright black
9: 0xff0000, // bright red
10: 0x00ff00, // bright green
11: 0xffff00, // bright yellow
12: 0x0000ff, // bright blue
13: 0xff00ff, // bright magenta
14: 0x00ffff, // bright cyan
15: 0xffffff, // bright white
}
// Color is a color that can be used in a terminal. ANSI (including
// ANSI256) and 24-bit "true colors" fall under this category.
type Color interface {
color.Color
}
// BasicColor is an ANSI 3-bit or 4-bit color with a value from 0 to 15.
type BasicColor uint8
var _ Color = BasicColor(0)
const (
// Black is the ANSI black color.
Black BasicColor = iota
// Red is the ANSI red color.
Red
// Green is the ANSI green color.
Green
// Yellow is the ANSI yellow color.
Yellow
// Blue is the ANSI blue color.
Blue
// Magenta is the ANSI magenta color.
Magenta
// Cyan is the ANSI cyan color.
Cyan
// White is the ANSI white color.
White
// BrightBlack is the ANSI bright black color.
BrightBlack
// BrightRed is the ANSI bright red color.
BrightRed
// BrightGreen is the ANSI bright green color.
BrightGreen
// BrightYellow is the ANSI bright yellow color.
BrightYellow
// BrightBlue is the ANSI bright blue color.
BrightBlue
// BrightMagenta is the ANSI bright magenta color.
BrightMagenta
// BrightCyan is the ANSI bright cyan color.
BrightCyan
// BrightWhite is the ANSI bright white color.
BrightWhite
)
// RGBA returns the red, green, blue and alpha components of the color. It
// satisfies the color.Color interface.
func (c BasicColor) RGBA() (uint32, uint32, uint32, uint32) {
ansi := uint32(c)
if ansi > 15 {
return 0, 0, 0, 0xffff
}
r, g, b := ansiToRGB(ansi)
return toRGBA(r, g, b)
}
// ExtendedColor is an ANSI 256 (8-bit) color with a value from 0 to 255.
type ExtendedColor uint8
var _ Color = ExtendedColor(0)
// RGBA returns the red, green, blue and alpha components of the color. It
// satisfies the color.Color interface.
func (c ExtendedColor) RGBA() (uint32, uint32, uint32, uint32) {
r, g, b := ansiToRGB(uint32(c))
return toRGBA(r, g, b)
}
// TrueColor is a 24-bit color that can be used in the terminal.
// This can be used to represent RGB colors.
//
// For example, the color red can be represented as:
//
// TrueColor(0xff0000)
type TrueColor uint32
var _ Color = TrueColor(0)
// RGBA returns the red, green, blue and alpha components of the color. It
// satisfies the color.Color interface.
func (c TrueColor) RGBA() (uint32, uint32, uint32, uint32) {
r, g, b := hexToRGB(uint32(c))
return toRGBA(r, g, b)
}
// ansiToRGB converts an ANSI color to a 24-bit RGB color.
//
// r, g, b := ansiToRGB(57)
func ansiToRGB(ansi uint32) (uint32, uint32, uint32) {
// For out-of-range values return black.
if ansi > 255 {
return 0, 0, 0
}
// Low ANSI.
if ansi < 16 {
h, ok := lowANSI[ansi]
if !ok {
return 0, 0, 0
}
r, g, b := hexToRGB(h)
return r, g, b
}
// Grays.
if ansi > 231 {
s := (ansi-232)*10 + 8
return s, s, s
}
// ANSI256.
n := ansi - 16
b := n % 6
g := (n - b) / 6 % 6
r := (n - b - g*6) / 36 % 6
for _, v := range []*uint32{&r, &g, &b} {
if *v > 0 {
c := *v*40 + 55
*v = c
}
}
return r, g, b
}
// hexToRGB converts a number in hexadecimal format to red, green, and blue
// values.
//
// r, g, b := hexToRGB(0x0000FF)
func hexToRGB(hex uint32) (uint32, uint32, uint32) {
return hex >> 16, hex >> 8 & 0xff, hex & 0xff
}
// toRGBA converts an RGB 8-bit color values to 32-bit color values suitable
// for color.Color.
//
// color.Color requires 16-bit color values, so we duplicate the 8-bit values
// to fill the 16-bit values.
//
// This always returns 0xffff (opaque) for the alpha channel.
func toRGBA(r, g, b uint32) (uint32, uint32, uint32, uint32) {
r |= r << 8
g |= g << 8
b |= b << 8
return r, g, b, 0xffff
}

141
vendor/github.com/charmbracelet/x/ansi/csi.go generated vendored Normal file
View File

@@ -0,0 +1,141 @@
package ansi
import (
"bytes"
"strconv"
"github.com/charmbracelet/x/ansi/parser"
)
// CsiSequence represents a control sequence introducer (CSI) sequence.
//
// The sequence starts with a CSI sequence, CSI (0x9B) in a 8-bit environment
// or ESC [ (0x1B 0x5B) in a 7-bit environment, followed by any number of
// parameters in the range of 0x30-0x3F, then by any number of intermediate
// byte in the range of 0x20-0x2F, then finally with a single final byte in the
// range of 0x20-0x7E.
//
// CSI P..P I..I F
//
// See ECMA-48 § 5.4.
type CsiSequence struct {
// Params contains the raw parameters of the sequence.
// This is a slice of integers, where each integer is a 32-bit integer
// containing the parameter value in the lower 31 bits and a flag in the
// most significant bit indicating whether there are more sub-parameters.
Params []int
// Cmd contains the raw command of the sequence.
// The command is a 32-bit integer containing the CSI command byte in the
// lower 8 bits, the private marker in the next 8 bits, and the intermediate
// byte in the next 8 bits.
//
// CSI ? u
//
// Is represented as:
//
// 'u' | '?' << 8
Cmd int
}
var _ Sequence = CsiSequence{}
// Marker returns the marker byte of the CSI sequence.
// This is always gonna be one of the following '<' '=' '>' '?' and in the
// range of 0x3C-0x3F.
// Zero is returned if the sequence does not have a marker.
func (s CsiSequence) Marker() int {
return parser.Marker(s.Cmd)
}
// Intermediate returns the intermediate byte of the CSI sequence.
// An intermediate byte is in the range of 0x20-0x2F. This includes these
// characters from ' ', '!', '"', '#', '$', '%', '&', ”', '(', ')', '*', '+',
// ',', '-', '.', '/'.
// Zero is returned if the sequence does not have an intermediate byte.
func (s CsiSequence) Intermediate() int {
return parser.Intermediate(s.Cmd)
}
// Command returns the command byte of the CSI sequence.
func (s CsiSequence) Command() int {
return parser.Command(s.Cmd)
}
// Param returns the parameter at the given index.
// It returns -1 if the parameter does not exist.
func (s CsiSequence) Param(i int) int {
return parser.Param(s.Params, i)
}
// HasMore returns true if the parameter has more sub-parameters.
func (s CsiSequence) HasMore(i int) bool {
return parser.HasMore(s.Params, i)
}
// Subparams returns the sub-parameters of the given parameter.
// It returns nil if the parameter does not exist.
func (s CsiSequence) Subparams(i int) []int {
return parser.Subparams(s.Params, i)
}
// Len returns the number of parameters in the sequence.
// This will return the number of parameters in the sequence, excluding any
// sub-parameters.
func (s CsiSequence) Len() int {
return parser.Len(s.Params)
}
// Range iterates over the parameters of the sequence and calls the given
// function for each parameter.
// The function should return false to stop the iteration.
func (s CsiSequence) Range(fn func(i int, param int, hasMore bool) bool) {
parser.Range(s.Params, fn)
}
// Clone returns a copy of the CSI sequence.
func (s CsiSequence) Clone() Sequence {
return CsiSequence{
Params: append([]int(nil), s.Params...),
Cmd: s.Cmd,
}
}
// String returns a string representation of the sequence.
// The string will always be in the 7-bit format i.e (ESC [ P..P I..I F).
func (s CsiSequence) String() string {
return s.buffer().String()
}
// buffer returns a buffer containing the sequence.
func (s CsiSequence) buffer() *bytes.Buffer {
var b bytes.Buffer
b.WriteString("\x1b[")
if m := s.Marker(); m != 0 {
b.WriteByte(byte(m))
}
s.Range(func(i, param int, hasMore bool) bool {
if param >= 0 {
b.WriteString(strconv.Itoa(param))
}
if i < len(s.Params)-1 {
if hasMore {
b.WriteByte(':')
} else {
b.WriteByte(';')
}
}
return true
})
if i := s.Intermediate(); i != 0 {
b.WriteByte(byte(i))
}
b.WriteByte(byte(s.Command()))
return &b
}
// Bytes returns the byte representation of the sequence.
// The bytes will always be in the 7-bit format i.e (ESC [ P..P I..I F).
func (s CsiSequence) Bytes() []byte {
return s.buffer().Bytes()
}

17
vendor/github.com/charmbracelet/x/ansi/ctrl.go generated vendored Normal file
View File

@@ -0,0 +1,17 @@
package ansi
// RequestXTVersion is a control sequence that requests the terminal's XTVERSION. It responds with a DSR sequence identifying the version.
//
// CSI > Ps q
// DCS > | text ST
//
// See https://invisible-island.net/xterm/ctlseqs/ctlseqs.html#h3-PC-Style-Function-Keys
const RequestXTVersion = "\x1b[>0q"
// RequestPrimaryDeviceAttributes is a control sequence that requests the
// terminal's primary device attributes (DA1).
//
// CSI c
//
// See https://vt100.net/docs/vt510-rm/DA1.html
const RequestPrimaryDeviceAttributes = "\x1b[c"

190
vendor/github.com/charmbracelet/x/ansi/cursor.go generated vendored Normal file
View File

@@ -0,0 +1,190 @@
package ansi
import "strconv"
// SaveCursor (DECSC) is an escape sequence that saves the current cursor
// position.
//
// ESC 7
//
// See: https://vt100.net/docs/vt510-rm/DECSC.html
const SaveCursor = "\x1b7"
// RestoreCursor (DECRC) is an escape sequence that restores the cursor
// position.
//
// ESC 8
//
// See: https://vt100.net/docs/vt510-rm/DECRC.html
const RestoreCursor = "\x1b8"
// RequestCursorPosition (CPR) is an escape sequence that requests the current
// cursor position.
//
// CSI 6 n
//
// The terminal will report the cursor position as a CSI sequence in the
// following format:
//
// CSI Pl ; Pc R
//
// Where Pl is the line number and Pc is the column number.
// See: https://vt100.net/docs/vt510-rm/CPR.html
const RequestCursorPosition = "\x1b[6n"
// RequestExtendedCursorPosition (DECXCPR) is a sequence for requesting the
// cursor position report including the current page number.
//
// CSI ? 6 n
//
// The terminal will report the cursor position as a CSI sequence in the
// following format:
//
// CSI ? Pl ; Pc ; Pp R
//
// Where Pl is the line number, Pc is the column number, and Pp is the page
// number.
// See: https://vt100.net/docs/vt510-rm/DECXCPR.html
const RequestExtendedCursorPosition = "\x1b[?6n"
// CursorUp (CUU) returns a sequence for moving the cursor up n cells.
//
// CSI n A
//
// See: https://vt100.net/docs/vt510-rm/CUU.html
func CursorUp(n int) string {
var s string
if n > 1 {
s = strconv.Itoa(n)
}
return "\x1b[" + s + "A"
}
// CursorUp1 is a sequence for moving the cursor up one cell.
//
// This is equivalent to CursorUp(1).
const CursorUp1 = "\x1b[A"
// CursorDown (CUD) returns a sequence for moving the cursor down n cells.
//
// CSI n B
//
// See: https://vt100.net/docs/vt510-rm/CUD.html
func CursorDown(n int) string {
var s string
if n > 1 {
s = strconv.Itoa(n)
}
return "\x1b[" + s + "B"
}
// CursorDown1 is a sequence for moving the cursor down one cell.
//
// This is equivalent to CursorDown(1).
const CursorDown1 = "\x1b[B"
// CursorRight (CUF) returns a sequence for moving the cursor right n cells.
//
// CSI n C
//
// See: https://vt100.net/docs/vt510-rm/CUF.html
func CursorRight(n int) string {
var s string
if n > 1 {
s = strconv.Itoa(n)
}
return "\x1b[" + s + "C"
}
// CursorRight1 is a sequence for moving the cursor right one cell.
//
// This is equivalent to CursorRight(1).
const CursorRight1 = "\x1b[C"
// CursorLeft (CUB) returns a sequence for moving the cursor left n cells.
//
// CSI n D
//
// See: https://vt100.net/docs/vt510-rm/CUB.html
func CursorLeft(n int) string {
var s string
if n > 1 {
s = strconv.Itoa(n)
}
return "\x1b[" + s + "D"
}
// CursorLeft1 is a sequence for moving the cursor left one cell.
//
// This is equivalent to CursorLeft(1).
const CursorLeft1 = "\x1b[D"
// CursorNextLine (CNL) returns a sequence for moving the cursor to the
// beginning of the next line n times.
//
// CSI n E
//
// See: https://vt100.net/docs/vt510-rm/CNL.html
func CursorNextLine(n int) string {
var s string
if n > 1 {
s = strconv.Itoa(n)
}
return "\x1b[" + s + "E"
}
// CursorPreviousLine (CPL) returns a sequence for moving the cursor to the
// beginning of the previous line n times.
//
// CSI n F
//
// See: https://vt100.net/docs/vt510-rm/CPL.html
func CursorPreviousLine(n int) string {
var s string
if n > 1 {
s = strconv.Itoa(n)
}
return "\x1b[" + s + "F"
}
// MoveCursor (CUP) returns a sequence for moving the cursor to the given row
// and column.
//
// CSI n ; m H
//
// See: https://vt100.net/docs/vt510-rm/CUP.html
func MoveCursor(row, col int) string {
if row < 0 {
row = 0
}
if col < 0 {
col = 0
}
return "\x1b[" + strconv.Itoa(row) + ";" + strconv.Itoa(col) + "H"
}
// MoveCursorOrigin is a sequence for moving the cursor to the upper left
// corner of the screen. This is equivalent to MoveCursor(1, 1).
const MoveCursorOrigin = "\x1b[1;1H"
// SaveCursorPosition (SCP or SCOSC) is a sequence for saving the cursor
// position.
//
// CSI s
//
// This acts like Save, except the page number where the cursor is located is
// not saved.
//
// See: https://vt100.net/docs/vt510-rm/SCOSC.html
const SaveCursorPosition = "\x1b[s"
// RestoreCursorPosition (RCP or SCORC) is a sequence for restoring the cursor
// position.
//
// CSI u
//
// This acts like Restore, except the cursor stays on the same page where the
// cursor was saved.
//
// See: https://vt100.net/docs/vt510-rm/SCORC.html
const RestoreCursorPosition = "\x1b[u"

148
vendor/github.com/charmbracelet/x/ansi/dcs.go generated vendored Normal file
View File

@@ -0,0 +1,148 @@
package ansi
import (
"bytes"
"strconv"
"github.com/charmbracelet/x/ansi/parser"
)
// DcsSequence represents a Device Control String (DCS) escape sequence.
//
// The DCS sequence is used to send device control strings to the terminal. The
// sequence starts with the C1 control code character DCS (0x9B) or ESC P in
// 7-bit environments, followed by parameter bytes, intermediate bytes, a
// command byte, followed by data bytes, and ends with the C1 control code
// character ST (0x9C) or ESC \ in 7-bit environments.
//
// This follows the parameter string format.
// See ECMA-48 § 5.4.1
type DcsSequence struct {
// Params contains the raw parameters of the sequence.
// This is a slice of integers, where each integer is a 32-bit integer
// containing the parameter value in the lower 31 bits and a flag in the
// most significant bit indicating whether there are more sub-parameters.
Params []int
// Data contains the string raw data of the sequence.
// This is the data between the final byte and the escape sequence terminator.
Data []byte
// Cmd contains the raw command of the sequence.
// The command is a 32-bit integer containing the DCS command byte in the
// lower 8 bits, the private marker in the next 8 bits, and the intermediate
// byte in the next 8 bits.
//
// DCS > 0 ; 1 $ r <data> ST
//
// Is represented as:
//
// 'r' | '>' << 8 | '$' << 16
Cmd int
}
var _ Sequence = DcsSequence{}
// Marker returns the marker byte of the DCS sequence.
// This is always gonna be one of the following '<' '=' '>' '?' and in the
// range of 0x3C-0x3F.
// Zero is returned if the sequence does not have a marker.
func (s DcsSequence) Marker() int {
return parser.Marker(s.Cmd)
}
// Intermediate returns the intermediate byte of the DCS sequence.
// An intermediate byte is in the range of 0x20-0x2F. This includes these
// characters from ' ', '!', '"', '#', '$', '%', '&', ”', '(', ')', '*', '+',
// ',', '-', '.', '/'.
// Zero is returned if the sequence does not have an intermediate byte.
func (s DcsSequence) Intermediate() int {
return parser.Intermediate(s.Cmd)
}
// Command returns the command byte of the CSI sequence.
func (s DcsSequence) Command() int {
return parser.Command(s.Cmd)
}
// Param returns the parameter at the given index.
// It returns -1 if the parameter does not exist.
func (s DcsSequence) Param(i int) int {
return parser.Param(s.Params, i)
}
// HasMore returns true if the parameter has more sub-parameters.
func (s DcsSequence) HasMore(i int) bool {
return parser.HasMore(s.Params, i)
}
// Subparams returns the sub-parameters of the given parameter.
// It returns nil if the parameter does not exist.
func (s DcsSequence) Subparams(i int) []int {
return parser.Subparams(s.Params, i)
}
// Len returns the number of parameters in the sequence.
// This will return the number of parameters in the sequence, excluding any
// sub-parameters.
func (s DcsSequence) Len() int {
return parser.Len(s.Params)
}
// Range iterates over the parameters of the sequence and calls the given
// function for each parameter.
// The function should return false to stop the iteration.
func (s DcsSequence) Range(fn func(i int, param int, hasMore bool) bool) {
parser.Range(s.Params, fn)
}
// Clone returns a copy of the DCS sequence.
func (s DcsSequence) Clone() Sequence {
return DcsSequence{
Params: append([]int(nil), s.Params...),
Data: append([]byte(nil), s.Data...),
Cmd: s.Cmd,
}
}
// String returns a string representation of the sequence.
// The string will always be in the 7-bit format i.e (ESC P p..p i..i f <data> ESC \).
func (s DcsSequence) String() string {
return s.buffer().String()
}
// buffer returns a buffer containing the sequence.
func (s DcsSequence) buffer() *bytes.Buffer {
var b bytes.Buffer
b.WriteString("\x1bP")
if m := s.Marker(); m != 0 {
b.WriteByte(byte(m))
}
s.Range(func(i, param int, hasMore bool) bool {
if param >= -1 {
b.WriteString(strconv.Itoa(param))
}
if i < len(s.Params)-1 {
if hasMore {
b.WriteByte(':')
} else {
b.WriteByte(';')
}
}
return true
})
if i := s.Intermediate(); i != 0 {
b.WriteByte(byte(i))
}
b.WriteByte(byte(s.Command()))
b.Write(s.Data)
b.WriteByte(ESC)
b.WriteByte('\\')
return &b
}
// Bytes returns the byte representation of the sequence.
// The bytes will always be in the 7-bit format i.e (ESC P p..p i..i F <data> ESC \).
func (s DcsSequence) Bytes() []byte {
return s.buffer().Bytes()
}

7
vendor/github.com/charmbracelet/x/ansi/doc.go generated vendored Normal file
View File

@@ -0,0 +1,7 @@
// Package ansi defines common ANSI escape sequences based on the ECMA-48
// specs.
//
// All sequences use 7-bit C1 control codes, which are supported by most
// terminal emulators. OSC sequences are terminated by a BEL for wider
// compatibility with terminals.
package ansi

28
vendor/github.com/charmbracelet/x/ansi/hyperlink.go generated vendored Normal file
View File

@@ -0,0 +1,28 @@
package ansi
import "strings"
// SetHyperlink returns a sequence for starting a hyperlink.
//
// OSC 8 ; Params ; Uri ST
// OSC 8 ; Params ; Uri BEL
//
// To reset the hyperlink, omit the URI.
//
// See: https://gist.github.com/egmontkob/eb114294efbcd5adb1944c9f3cb5feda
func SetHyperlink(uri string, params ...string) string {
var p string
if len(params) > 0 {
p = strings.Join(params, ":")
}
return "\x1b]8;" + p + ";" + uri + "\x07"
}
// ResetHyperlink returns a sequence for resetting the hyperlink.
//
// This is equivalent to SetHyperlink("", params...).
//
// See: https://gist.github.com/egmontkob/eb114294efbcd5adb1944c9f3cb5feda
func ResetHyperlink(params ...string) string {
return SetHyperlink("", params...)
}

58
vendor/github.com/charmbracelet/x/ansi/kitty.go generated vendored Normal file
View File

@@ -0,0 +1,58 @@
package ansi
import "strconv"
// Kitty keyboard protocol progressive enhancement flags.
// See: https://sw.kovidgoyal.net/kitty/keyboard-protocol/#progressive-enhancement
const (
KittyDisambiguateEscapeCodes = 1 << iota
KittyReportEventTypes
KittyReportAlternateKeys
KittyReportAllKeys
KittyReportAssociatedKeys
KittyAllFlags = KittyDisambiguateEscapeCodes | KittyReportEventTypes |
KittyReportAlternateKeys | KittyReportAllKeys | KittyReportAssociatedKeys
)
// RequestKittyKeyboard is a sequence to request the terminal Kitty keyboard
// protocol enabled flags.
//
// See: https://sw.kovidgoyal.net/kitty/keyboard-protocol/
const RequestKittyKeyboard = "\x1b[?u"
// PushKittyKeyboard returns a sequence to push the given flags to the terminal
// Kitty Keyboard stack.
//
// CSI > flags u
//
// See https://sw.kovidgoyal.net/kitty/keyboard-protocol/#progressive-enhancement
func PushKittyKeyboard(flags int) string {
var f string
if flags > 0 {
f = strconv.Itoa(flags)
}
return "\x1b[>" + f + "u"
}
// DisableKittyKeyboard is a sequence to push zero into the terminal Kitty
// Keyboard stack to disable the protocol.
//
// This is equivalent to PushKittyKeyboard(0).
const DisableKittyKeyboard = "\x1b[>0u"
// PopKittyKeyboard returns a sequence to pop n number of flags from the
// terminal Kitty Keyboard stack.
//
// CSI < flags u
//
// See https://sw.kovidgoyal.net/kitty/keyboard-protocol/#progressive-enhancement
func PopKittyKeyboard(n int) string {
var num string
if n > 0 {
num = strconv.Itoa(n)
}
return "\x1b[<" + num + "u"
}

132
vendor/github.com/charmbracelet/x/ansi/mode.go generated vendored Normal file
View File

@@ -0,0 +1,132 @@
package ansi
// This file define uses multiple sequences to set (SM), reset (RM), and request
// (DECRQM) different ANSI and DEC modes.
//
// See: https://vt100.net/docs/vt510-rm/SM.html
// See: https://vt100.net/docs/vt510-rm/RM.html
// See: https://vt100.net/docs/vt510-rm/DECRQM.html
//
// The terminal then responds to the request with a Report Mode function
// (DECRPM) in the format:
//
// ANSI format:
//
// CSI Pa ; Ps ; $ y
//
// DEC format:
//
// CSI ? Pa ; Ps $ y
//
// Where Pa is the mode number, and Ps is the mode value.
// See: https://vt100.net/docs/vt510-rm/DECRPM.html
// Application Cursor Keys (DECCKM) is a mode that determines whether the
// cursor keys send ANSI cursor sequences or application sequences.
//
// See: https://vt100.net/docs/vt510-rm/DECCKM.html
const (
EnableCursorKeys = "\x1b[?1h"
DisableCursorKeys = "\x1b[?1l"
RequestCursorKeys = "\x1b[?1$p"
)
// Text Cursor Enable Mode (DECTCEM) is a mode that shows/hides the cursor.
//
// See: https://vt100.net/docs/vt510-rm/DECTCEM.html
const (
ShowCursor = "\x1b[?25h"
HideCursor = "\x1b[?25l"
RequestCursorVisibility = "\x1b[?25$p"
)
// VT Mouse Tracking is a mode that determines whether the mouse reports on
// button press and release.
//
// See: https://invisible-island.net/xterm/ctlseqs/ctlseqs.html#h2-Mouse-Tracking
const (
EnableMouse = "\x1b[?1000h"
DisableMouse = "\x1b[?1000l"
RequestMouse = "\x1b[?1000$p"
)
// VT Hilite Mouse Tracking is a mode that determines whether the mouse reports on
// button presses, releases, and highlighted cells.
//
// See: https://invisible-island.net/xterm/ctlseqs/ctlseqs.html#h2-Mouse-Tracking
const (
EnableMouseHilite = "\x1b[?1001h"
DisableMouseHilite = "\x1b[?1001l"
RequestMouseHilite = "\x1b[?1001$p"
)
// Cell Motion Mouse Tracking is a mode that determines whether the mouse
// reports on button press, release, and motion events.
//
// See: https://invisible-island.net/xterm/ctlseqs/ctlseqs.html#h2-Mouse-Tracking
const (
EnableMouseCellMotion = "\x1b[?1002h"
DisableMouseCellMotion = "\x1b[?1002l"
RequestMouseCellMotion = "\x1b[?1002$p"
)
// All Mouse Tracking is a mode that determines whether the mouse reports on
// button press, release, motion, and highlight events.
//
// See: https://invisible-island.net/xterm/ctlseqs/ctlseqs.html#h2-Mouse-Tracking
const (
EnableMouseAllMotion = "\x1b[?1003h"
DisableMouseAllMotion = "\x1b[?1003l"
RequestMouseAllMotion = "\x1b[?1003$p"
)
// SGR Mouse Extension is a mode that determines whether the mouse reports events
// formatted with SGR parameters.
//
// See: https://invisible-island.net/xterm/ctlseqs/ctlseqs.html#h2-Mouse-Tracking
const (
EnableMouseSgrExt = "\x1b[?1006h"
DisableMouseSgrExt = "\x1b[?1006l"
RequestMouseSgrExt = "\x1b[?1006$p"
)
// Alternate Screen Buffer is a mode that determines whether the alternate screen
// buffer is active.
//
// See: https://invisible-island.net/xterm/ctlseqs/ctlseqs.html#h2-The-Alternate-Screen-Buffer
const (
EnableAltScreenBuffer = "\x1b[?1049h"
DisableAltScreenBuffer = "\x1b[?1049l"
RequestAltScreenBuffer = "\x1b[?1049$p"
)
// Bracketed Paste Mode is a mode that determines whether pasted text is
// bracketed with escape sequences.
//
// See: https://cirw.in/blog/bracketed-paste
// See: https://invisible-island.net/xterm/ctlseqs/ctlseqs.html#h2-Bracketed-Paste-Mode
const (
EnableBracketedPaste = "\x1b[?2004h"
DisableBracketedPaste = "\x1b[?2004l"
RequestBracketedPaste = "\x1b[?2004$p"
)
// Synchronized Output Mode is a mode that determines whether output is
// synchronized with the terminal.
//
// See: https://gist.github.com/christianparpart/d8a62cc1ab659194337d73e399004036
const (
EnableSyncdOutput = "\x1b[?2026h"
DisableSyncdOutput = "\x1b[?2026l"
RequestSyncdOutput = "\x1b[?2026$p"
)
// Win32Input is a mode that determines whether input is processed by the
// Win32 console and Conpty.
//
// See: https://github.com/microsoft/terminal/blob/main/doc/specs/%234999%20-%20Improved%20keyboard%20handling%20in%20Conpty.md
const (
EnableWin32Input = "\x1b[?9001h"
DisableWin32Input = "\x1b[?9001l"
RequestWin32Input = "\x1b[?9001$p"
)

69
vendor/github.com/charmbracelet/x/ansi/osc.go generated vendored Normal file
View File

@@ -0,0 +1,69 @@
package ansi
import (
"bytes"
"strings"
)
// OscSequence represents an OSC sequence.
//
// The sequence starts with a OSC sequence, OSC (0x9D) in a 8-bit environment
// or ESC ] (0x1B 0x5D) in a 7-bit environment, followed by positive integer identifier,
// then by arbitrary data terminated by a ST (0x9C) in a 8-bit environment,
// ESC \ (0x1B 0x5C) in a 7-bit environment, or BEL (0x07) for backwards compatibility.
//
// OSC Ps ; Pt ST
// OSC Ps ; Pt BEL
//
// See ECMA-48 § 5.7.
type OscSequence struct {
// Data contains the raw data of the sequence including the identifier
// command.
Data []byte
// Cmd contains the raw command of the sequence.
Cmd int
}
var _ Sequence = OscSequence{}
// Command returns the command of the OSC sequence.
func (s OscSequence) Command() int {
return s.Cmd
}
// Params returns the parameters of the OSC sequence split by ';'.
// The first element is the identifier command.
func (s OscSequence) Params() []string {
return strings.Split(string(s.Data), ";")
}
// Clone returns a copy of the OSC sequence.
func (s OscSequence) Clone() Sequence {
return OscSequence{
Data: append([]byte(nil), s.Data...),
Cmd: s.Cmd,
}
}
// String returns the string representation of the OSC sequence.
// To be more compatible with different terminal, this will always return a
// 7-bit formatted sequence, terminated by BEL.
func (s OscSequence) String() string {
return s.buffer().String()
}
// Bytes returns the byte representation of the OSC sequence.
// To be more compatible with different terminal, this will always return a
// 7-bit formatted sequence, terminated by BEL.
func (s OscSequence) Bytes() []byte {
return s.buffer().Bytes()
}
func (s OscSequence) buffer() *bytes.Buffer {
var b bytes.Buffer
b.WriteString("\x1b]")
b.Write(s.Data)
b.WriteByte(BEL)
return &b
}

45
vendor/github.com/charmbracelet/x/ansi/params.go generated vendored Normal file
View File

@@ -0,0 +1,45 @@
package ansi
import (
"bytes"
)
// Params parses and returns a list of control sequence parameters.
//
// Parameters are positive integers separated by semicolons. Empty parameters
// default to zero. Parameters can have sub-parameters separated by colons.
//
// Any non-parameter bytes are ignored. This includes bytes that are not in the
// range of 0x30-0x3B.
//
// See ECMA-48 § 5.4.1.
func Params(p []byte) [][]uint {
if len(p) == 0 {
return [][]uint{}
}
// Filter out non-parameter bytes i.e. non 0x30-0x3B.
p = bytes.TrimFunc(p, func(r rune) bool {
return r < 0x30 || r > 0x3B
})
parts := bytes.Split(p, []byte{';'})
params := make([][]uint, len(parts))
for i, part := range parts {
sparts := bytes.Split(part, []byte{':'})
params[i] = make([]uint, len(sparts))
for j, spart := range sparts {
params[i][j] = bytesToUint16(spart)
}
}
return params
}
func bytesToUint16(b []byte) uint {
var n uint
for _, c := range b {
n = n*10 + uint(c-'0')
}
return n
}

357
vendor/github.com/charmbracelet/x/ansi/parser.go generated vendored Normal file
View File

@@ -0,0 +1,357 @@
package ansi
import (
"unicode/utf8"
"github.com/charmbracelet/x/ansi/parser"
)
// ParserDispatcher is a function that dispatches a sequence.
type ParserDispatcher func(Sequence)
// Parser represents a DEC ANSI compatible sequence parser.
//
// It uses a state machine to parse ANSI escape sequences and control
// characters. The parser is designed to be used with a terminal emulator or
// similar application that needs to parse ANSI escape sequences and control
// characters.
// See package [parser] for more information.
//
//go:generate go run ./gen.go
type Parser struct {
// Params contains the raw parameters of the sequence.
// These parameters used when constructing CSI and DCS sequences.
Params []int
// Data contains the raw data of the sequence.
// These data used when constructing OSC, DCS, SOS, PM, and APC sequences.
Data []byte
// DataLen keeps track of the length of the data buffer.
// If DataLen is -1, the data buffer is unlimited and will grow as needed.
// Otherwise, DataLen is limited by the size of the Data buffer.
DataLen int
// ParamsLen keeps track of the number of parameters.
// This is limited by the size of the Params buffer.
ParamsLen int
// Cmd contains the raw command along with the private marker and
// intermediate bytes of the sequence.
// The first lower byte contains the command byte, the next byte contains
// the private marker, and the next byte contains the intermediate byte.
Cmd int
// RuneLen keeps track of the number of bytes collected for a UTF-8 rune.
RuneLen int
// RuneBuf contains the bytes collected for a UTF-8 rune.
RuneBuf [utf8.MaxRune]byte
// State is the current state of the parser.
State byte
}
// NewParser returns a new parser with the given sizes allocated.
// If dataSize is zero, the underlying data buffer will be unlimited and will
// grow as needed.
func NewParser(paramsSize, dataSize int) *Parser {
s := &Parser{
Params: make([]int, paramsSize),
Data: make([]byte, dataSize),
}
if dataSize <= 0 {
s.DataLen = -1
}
return s
}
// Reset resets the parser to its initial state.
func (p *Parser) Reset() {
p.clear()
p.State = parser.GroundState
}
// clear clears the parser parameters and command.
func (p *Parser) clear() {
if len(p.Params) > 0 {
p.Params[0] = parser.MissingParam
}
p.ParamsLen = 0
p.Cmd = 0
p.RuneLen = 0
}
// StateName returns the name of the current state.
func (p *Parser) StateName() string {
return parser.StateNames[p.State]
}
// Parse parses the given dispatcher and byte buffer.
func (p *Parser) Parse(dispatcher ParserDispatcher, b []byte) {
for i := 0; i < len(b); i++ {
p.Advance(dispatcher, b[i], i < len(b)-1)
}
}
// Advance advances the parser with the given dispatcher and byte.
func (p *Parser) Advance(dispatcher ParserDispatcher, b byte, more bool) parser.Action {
switch p.State {
case parser.Utf8State:
// We handle UTF-8 here.
return p.advanceUtf8(dispatcher, b)
default:
return p.advance(dispatcher, b, more)
}
}
func (p *Parser) collectRune(b byte) {
if p.RuneLen < utf8.UTFMax {
p.RuneBuf[p.RuneLen] = b
p.RuneLen++
}
}
func (p *Parser) advanceUtf8(dispatcher ParserDispatcher, b byte) parser.Action {
// Collect UTF-8 rune bytes.
p.collectRune(b)
rw := utf8ByteLen(p.RuneBuf[0])
if rw == -1 {
// We panic here because the first byte comes from the state machine,
// if this panics, it means there is a bug in the state machine!
panic("invalid rune") // unreachable
}
if p.RuneLen < rw {
return parser.NoneAction
}
// We have enough bytes to decode the rune
bts := p.RuneBuf[:rw]
r, _ := utf8.DecodeRune(bts)
if dispatcher != nil {
dispatcher(Rune(r))
}
p.State = parser.GroundState
p.RuneLen = 0
return parser.NoneAction
}
func (p *Parser) advance(d ParserDispatcher, b byte, more bool) parser.Action {
state, action := parser.Table.Transition(p.State, b)
// We need to clear the parser state if the state changes from EscapeState.
// This is because when we enter the EscapeState, we don't get a chance to
// clear the parser state. For example, when a sequence terminates with a
// ST (\x1b\\ or \x9c), we dispatch the current sequence and transition to
// EscapeState. However, the parser state is not cleared in this case and
// we need to clear it here before dispatching the esc sequence.
if p.State != state {
switch p.State {
case parser.EscapeState:
p.performAction(d, parser.ClearAction, b)
}
if action == parser.PutAction &&
p.State == parser.DcsEntryState && state == parser.DcsStringState {
// XXX: This is a special case where we need to start collecting
// non-string parameterized data i.e. doesn't follow the ECMA-48 §
// 5.4.1 string parameters format.
p.performAction(d, parser.StartAction, 0)
}
}
// Handle special cases
switch {
case b == ESC && p.State == parser.EscapeState:
// Two ESCs in a row
p.performAction(d, parser.ExecuteAction, b)
if !more {
// Two ESCs at the end of the buffer
p.performAction(d, parser.ExecuteAction, b)
}
case b == ESC && !more:
// Last byte is an ESC
p.performAction(d, parser.ExecuteAction, b)
case p.State == parser.EscapeState && b == 'P' && !more:
// ESC P (DCS) at the end of the buffer
p.performAction(d, parser.DispatchAction, b)
case p.State == parser.EscapeState && b == 'X' && !more:
// ESC X (SOS) at the end of the buffer
p.performAction(d, parser.DispatchAction, b)
case p.State == parser.EscapeState && b == '[' && !more:
// ESC [ (CSI) at the end of the buffer
p.performAction(d, parser.DispatchAction, b)
case p.State == parser.EscapeState && b == ']' && !more:
// ESC ] (OSC) at the end of the buffer
p.performAction(d, parser.DispatchAction, b)
case p.State == parser.EscapeState && b == '^' && !more:
// ESC ^ (PM) at the end of the buffer
p.performAction(d, parser.DispatchAction, b)
case p.State == parser.EscapeState && b == '_' && !more:
// ESC _ (APC) at the end of the buffer
p.performAction(d, parser.DispatchAction, b)
default:
p.performAction(d, action, b)
}
p.State = state
return action
}
func (p *Parser) performAction(dispatcher ParserDispatcher, action parser.Action, b byte) {
switch action {
case parser.IgnoreAction:
break
case parser.ClearAction:
p.clear()
case parser.PrintAction:
if utf8ByteLen(b) > 1 {
p.collectRune(b)
} else if dispatcher != nil {
dispatcher(Rune(b))
}
case parser.ExecuteAction:
if dispatcher != nil {
dispatcher(ControlCode(b))
}
case parser.MarkerAction:
// Collect private marker
// we only store the last marker
p.Cmd &^= 0xff << parser.MarkerShift
p.Cmd |= int(b) << parser.MarkerShift
case parser.CollectAction:
// Collect intermediate bytes
// we only store the last intermediate byte
p.Cmd &^= 0xff << parser.IntermedShift
p.Cmd |= int(b) << parser.IntermedShift
case parser.ParamAction:
// Collect parameters
if p.ParamsLen >= len(p.Params) {
break
}
if b >= '0' && b <= '9' {
if p.Params[p.ParamsLen] == parser.MissingParam {
p.Params[p.ParamsLen] = 0
}
p.Params[p.ParamsLen] *= 10
p.Params[p.ParamsLen] += int(b - '0')
}
if b == ':' {
p.Params[p.ParamsLen] |= parser.HasMoreFlag
}
if b == ';' || b == ':' {
p.ParamsLen++
if p.ParamsLen < len(p.Params) {
p.Params[p.ParamsLen] = parser.MissingParam
}
}
case parser.StartAction:
if p.DataLen < 0 {
p.Data = make([]byte, 0)
} else {
p.DataLen = 0
}
if p.State >= parser.DcsEntryState && p.State <= parser.DcsStringState {
// Collect the command byte for DCS
p.Cmd |= int(b)
} else {
p.Cmd = parser.MissingCommand
}
case parser.PutAction:
switch p.State {
case parser.OscStringState:
if b == ';' && p.Cmd == parser.MissingCommand {
// Try to parse the command
datalen := len(p.Data)
if p.DataLen >= 0 {
datalen = p.DataLen
}
for i := 0; i < datalen; i++ {
d := p.Data[i]
if d < '0' || d > '9' {
break
}
if p.Cmd == parser.MissingCommand {
p.Cmd = 0
}
p.Cmd *= 10
p.Cmd += int(d - '0')
}
}
}
if p.DataLen < 0 {
p.Data = append(p.Data, b)
} else {
if p.DataLen < len(p.Data) {
p.Data[p.DataLen] = b
p.DataLen++
}
}
case parser.DispatchAction:
// Increment the last parameter
if p.ParamsLen > 0 && p.ParamsLen < len(p.Params)-1 ||
p.ParamsLen == 0 && len(p.Params) > 0 && p.Params[0] != parser.MissingParam {
p.ParamsLen++
}
if dispatcher == nil {
break
}
var seq Sequence
data := p.Data
if p.DataLen >= 0 {
data = data[:p.DataLen]
}
switch p.State {
case parser.CsiEntryState, parser.CsiParamState, parser.CsiIntermediateState:
p.Cmd |= int(b)
seq = CsiSequence{Cmd: p.Cmd, Params: p.Params[:p.ParamsLen]}
case parser.EscapeState, parser.EscapeIntermediateState:
p.Cmd |= int(b)
seq = EscSequence(p.Cmd)
case parser.DcsEntryState, parser.DcsParamState, parser.DcsIntermediateState, parser.DcsStringState:
seq = DcsSequence{Cmd: p.Cmd, Params: p.Params[:p.ParamsLen], Data: data}
case parser.OscStringState:
seq = OscSequence{Cmd: p.Cmd, Data: data}
case parser.SosStringState:
seq = SosSequence{Data: data}
case parser.PmStringState:
seq = PmSequence{Data: data}
case parser.ApcStringState:
seq = ApcSequence{Data: data}
}
dispatcher(seq)
}
}
func utf8ByteLen(b byte) int {
if b <= 0b0111_1111 { // 0x00-0x7F
return 1
} else if b >= 0b1100_0000 && b <= 0b1101_1111 { // 0xC0-0xDF
return 2
} else if b >= 0b1110_0000 && b <= 0b1110_1111 { // 0xE0-0xEF
return 3
} else if b >= 0b1111_0000 && b <= 0b1111_0111 { // 0xF0-0xF7
return 4
}
return -1
}

78
vendor/github.com/charmbracelet/x/ansi/parser/const.go generated vendored Normal file
View File

@@ -0,0 +1,78 @@
package parser
// Action is a DEC ANSI parser action.
type Action = byte
// These are the actions that the parser can take.
const (
NoneAction Action = iota
ClearAction
CollectAction
MarkerAction
DispatchAction
ExecuteAction
StartAction // Start of a data string
PutAction // Put into the data string
ParamAction
PrintAction
IgnoreAction = NoneAction
)
// nolint: unused
var ActionNames = []string{
"NoneAction",
"ClearAction",
"CollectAction",
"MarkerAction",
"DispatchAction",
"ExecuteAction",
"StartAction",
"PutAction",
"ParamAction",
"PrintAction",
}
// State is a DEC ANSI parser state.
type State = byte
// These are the states that the parser can be in.
const (
GroundState State = iota
CsiEntryState
CsiIntermediateState
CsiParamState
DcsEntryState
DcsIntermediateState
DcsParamState
DcsStringState
EscapeState
EscapeIntermediateState
OscStringState
SosStringState
PmStringState
ApcStringState
// Utf8State is not part of the DEC ANSI standard. It is used to handle
// UTF-8 sequences.
Utf8State
)
// nolint: unused
var StateNames = []string{
"GroundState",
"CsiEntryState",
"CsiIntermediateState",
"CsiParamState",
"DcsEntryState",
"DcsIntermediateState",
"DcsParamState",
"DcsStringState",
"EscapeState",
"EscapeIntermediateState",
"OscStringState",
"SosStringState",
"PmStringState",
"ApcStringState",
"Utf8State",
}

136
vendor/github.com/charmbracelet/x/ansi/parser/seq.go generated vendored Normal file
View File

@@ -0,0 +1,136 @@
package parser
import "math"
// Shift and masks for sequence parameters and intermediates.
const (
MarkerShift = 8
IntermedShift = 16
CommandMask = 0xff
HasMoreFlag = math.MinInt32
ParamMask = ^HasMoreFlag
MissingParam = ParamMask
MissingCommand = MissingParam
MaxParam = math.MaxUint16 // the maximum value a parameter can have
)
const (
// MaxParamsSize is the maximum number of parameters a sequence can have.
MaxParamsSize = 32
// DefaultParamValue is the default value used for missing parameters.
DefaultParamValue = 0
)
// Marker returns the marker byte of the sequence.
// This is always gonna be one of the following '<' '=' '>' '?' and in the
// range of 0x3C-0x3F.
// Zero is returned if the sequence does not have a marker.
func Marker(cmd int) int {
return (cmd >> MarkerShift) & CommandMask
}
// Intermediate returns the intermediate byte of the sequence.
// An intermediate byte is in the range of 0x20-0x2F. This includes these
// characters from ' ', '!', '"', '#', '$', '%', '&', ”', '(', ')', '*', '+',
// ',', '-', '.', '/'.
// Zero is returned if the sequence does not have an intermediate byte.
func Intermediate(cmd int) int {
return (cmd >> IntermedShift) & CommandMask
}
// Command returns the command byte of the CSI sequence.
func Command(cmd int) int {
return cmd & CommandMask
}
// Param returns the parameter at the given index.
// It returns -1 if the parameter does not exist.
func Param(params []int, i int) int {
if len(params) == 0 || i < 0 || i >= len(params) {
return -1
}
p := params[i] & ParamMask
if p == MissingParam {
return -1
}
return p
}
// HasMore returns true if the parameter has more sub-parameters.
func HasMore(params []int, i int) bool {
if len(params) == 0 || i >= len(params) {
return false
}
return params[i]&HasMoreFlag != 0
}
// Subparams returns the sub-parameters of the given parameter.
// It returns nil if the parameter does not exist.
func Subparams(params []int, i int) []int {
if len(params) == 0 || i < 0 || i >= len(params) {
return nil
}
// Count the number of parameters before the given parameter index.
var count int
var j int
for j = 0; j < len(params); j++ {
if count == i {
break
}
if !HasMore(params, j) {
count++
}
}
if count > i || j >= len(params) {
return nil
}
var subs []int
for ; j < len(params); j++ {
if !HasMore(params, j) {
break
}
p := Param(params, j)
if p == -1 {
p = DefaultParamValue
}
subs = append(subs, p)
}
p := Param(params, j)
if p == -1 {
p = DefaultParamValue
}
return append(subs, p)
}
// Len returns the number of parameters in the sequence.
// This will return the number of parameters in the sequence, excluding any
// sub-parameters.
func Len(params []int) int {
var n int
for i := 0; i < len(params); i++ {
if !HasMore(params, i) {
n++
}
}
return n
}
// Range iterates over the parameters of the sequence and calls the given
// function for each parameter.
// The function should return false to stop the iteration.
func Range(params []int, fn func(i int, param int, hasMore bool) bool) {
for i := 0; i < len(params); i++ {
if !fn(i, Param(params, i), HasMore(params, i)) {
break
}
}
}

View File

@@ -0,0 +1,269 @@
package parser
// Table values are generated like this:
//
// index: currentState << IndexStateShift | charCode
// value: action << TransitionActionShift | nextState
const (
TransitionActionShift = 4
TransitionStateMask = 15
IndexStateShift = 8
// DefaultTableSize is the default size of the transition table.
DefaultTableSize = 4096
)
// Table is a DEC ANSI transition table.
var Table = GenerateTransitionTable()
// TransitionTable is a DEC ANSI transition table.
// https://vt100.net/emu/dec_ansi_parser
type TransitionTable []byte
// NewTransitionTable returns a new DEC ANSI transition table.
func NewTransitionTable(size int) TransitionTable {
if size <= 0 {
size = DefaultTableSize
}
return TransitionTable(make([]byte, size))
}
// SetDefault sets default transition.
func (t TransitionTable) SetDefault(action Action, state State) {
for i := 0; i < len(t); i++ {
t[i] = action<<TransitionActionShift | state
}
}
// AddOne adds a transition.
func (t TransitionTable) AddOne(code byte, state State, action Action, next State) {
idx := int(state)<<IndexStateShift | int(code)
value := action<<TransitionActionShift | next
t[idx] = value
}
// AddMany adds many transitions.
func (t TransitionTable) AddMany(codes []byte, state State, action Action, next State) {
for _, code := range codes {
t.AddOne(code, state, action, next)
}
}
// AddRange adds a range of transitions.
func (t TransitionTable) AddRange(start, end byte, state State, action Action, next State) {
for i := int(start); i <= int(end); i++ {
t.AddOne(byte(i), state, action, next)
}
}
// Transition returns the next state and action for the given state and byte.
func (t TransitionTable) Transition(state State, code byte) (State, Action) {
index := int(state)<<IndexStateShift | int(code)
value := t[index]
return value & TransitionStateMask, value >> TransitionActionShift
}
// byte range macro
func r(start, end byte) []byte {
var a []byte
for i := int(start); i <= int(end); i++ {
a = append(a, byte(i))
}
return a
}
// GenerateTransitionTable generates a DEC ANSI transition table compatible
// with the VT500-series of terminals. This implementation includes a few
// modifications that include:
// - A new Utf8State is introduced to handle UTF8 sequences.
// - Osc and Dcs data accept UTF8 sequences by extending the printable range
// to 0xFF and 0xFE respectively.
// - We don't ignore 0x3A (':') when building Csi and Dcs parameters and
// instead use it to denote sub-parameters.
// - Support dispatching SosPmApc sequences.
func GenerateTransitionTable() TransitionTable {
table := NewTransitionTable(DefaultTableSize)
table.SetDefault(NoneAction, GroundState)
// Anywhere
for _, state := range r(GroundState, Utf8State) {
// Anywhere -> Ground
table.AddMany([]byte{0x18, 0x1a, 0x99, 0x9a}, state, ExecuteAction, GroundState)
table.AddRange(0x80, 0x8F, state, ExecuteAction, GroundState)
table.AddRange(0x90, 0x97, state, ExecuteAction, GroundState)
table.AddOne(0x9C, state, IgnoreAction, GroundState)
// Anywhere -> Escape
table.AddOne(0x1B, state, ClearAction, EscapeState)
// Anywhere -> SosStringState
table.AddOne(0x98, state, StartAction, SosStringState)
// Anywhere -> PmStringState
table.AddOne(0x9E, state, StartAction, PmStringState)
// Anywhere -> ApcStringState
table.AddOne(0x9F, state, StartAction, ApcStringState)
// Anywhere -> CsiEntry
table.AddOne(0x9B, state, ClearAction, CsiEntryState)
// Anywhere -> DcsEntry
table.AddOne(0x90, state, ClearAction, DcsEntryState)
// Anywhere -> OscString
table.AddOne(0x9D, state, StartAction, OscStringState)
// Anywhere -> Utf8
table.AddRange(0xC2, 0xDF, state, PrintAction, Utf8State) // UTF8 2 byte sequence
table.AddRange(0xE0, 0xEF, state, PrintAction, Utf8State) // UTF8 3 byte sequence
table.AddRange(0xF0, 0xF4, state, PrintAction, Utf8State) // UTF8 4 byte sequence
}
// Ground
table.AddRange(0x00, 0x17, GroundState, ExecuteAction, GroundState)
table.AddOne(0x19, GroundState, ExecuteAction, GroundState)
table.AddRange(0x1C, 0x1F, GroundState, ExecuteAction, GroundState)
table.AddRange(0x20, 0x7F, GroundState, PrintAction, GroundState)
// EscapeIntermediate
table.AddRange(0x00, 0x17, EscapeIntermediateState, ExecuteAction, EscapeIntermediateState)
table.AddOne(0x19, EscapeIntermediateState, ExecuteAction, EscapeIntermediateState)
table.AddRange(0x1C, 0x1F, EscapeIntermediateState, ExecuteAction, EscapeIntermediateState)
table.AddRange(0x20, 0x2F, EscapeIntermediateState, CollectAction, EscapeIntermediateState)
table.AddOne(0x7F, EscapeIntermediateState, IgnoreAction, EscapeIntermediateState)
// EscapeIntermediate -> Ground
table.AddRange(0x30, 0x7E, EscapeIntermediateState, DispatchAction, GroundState)
// Escape
table.AddRange(0x00, 0x17, EscapeState, ExecuteAction, EscapeState)
table.AddOne(0x19, EscapeState, ExecuteAction, EscapeState)
table.AddRange(0x1C, 0x1F, EscapeState, ExecuteAction, EscapeState)
table.AddOne(0x7F, EscapeState, IgnoreAction, EscapeState)
// Escape -> Ground
table.AddRange(0x30, 0x4F, EscapeState, DispatchAction, GroundState)
table.AddRange(0x51, 0x57, EscapeState, DispatchAction, GroundState)
table.AddOne(0x59, EscapeState, DispatchAction, GroundState)
table.AddOne(0x5A, EscapeState, DispatchAction, GroundState)
table.AddOne(0x5C, EscapeState, DispatchAction, GroundState)
table.AddRange(0x60, 0x7E, EscapeState, DispatchAction, GroundState)
// Escape -> Escape_intermediate
table.AddRange(0x20, 0x2F, EscapeState, CollectAction, EscapeIntermediateState)
// Escape -> Sos_pm_apc_string
table.AddOne('X', EscapeState, StartAction, SosStringState) // SOS
table.AddOne('^', EscapeState, StartAction, PmStringState) // PM
table.AddOne('_', EscapeState, StartAction, ApcStringState) // APC
// Escape -> Dcs_entry
table.AddOne('P', EscapeState, ClearAction, DcsEntryState)
// Escape -> Csi_entry
table.AddOne('[', EscapeState, ClearAction, CsiEntryState)
// Escape -> Osc_string
table.AddOne(']', EscapeState, StartAction, OscStringState)
// Sos_pm_apc_string
for _, state := range r(SosStringState, ApcStringState) {
table.AddRange(0x00, 0x17, state, PutAction, state)
table.AddOne(0x19, state, PutAction, state)
table.AddRange(0x1C, 0x1F, state, PutAction, state)
table.AddRange(0x20, 0x7F, state, PutAction, state)
// ESC, ST, CAN, and SUB terminate the sequence
table.AddOne(0x1B, state, DispatchAction, EscapeState)
table.AddOne(0x9C, state, DispatchAction, GroundState)
table.AddMany([]byte{0x18, 0x1A}, state, IgnoreAction, GroundState)
}
// Dcs_entry
table.AddRange(0x00, 0x07, DcsEntryState, IgnoreAction, DcsEntryState)
table.AddRange(0x0E, 0x17, DcsEntryState, IgnoreAction, DcsEntryState)
table.AddOne(0x19, DcsEntryState, IgnoreAction, DcsEntryState)
table.AddRange(0x1C, 0x1F, DcsEntryState, IgnoreAction, DcsEntryState)
table.AddOne(0x7F, DcsEntryState, IgnoreAction, DcsEntryState)
// Dcs_entry -> Dcs_intermediate
table.AddRange(0x20, 0x2F, DcsEntryState, CollectAction, DcsIntermediateState)
// Dcs_entry -> Dcs_param
table.AddRange(0x30, 0x3B, DcsEntryState, ParamAction, DcsParamState)
table.AddRange(0x3C, 0x3F, DcsEntryState, MarkerAction, DcsParamState)
// Dcs_entry -> Dcs_passthrough
table.AddRange(0x08, 0x0D, DcsEntryState, PutAction, DcsStringState) // Follows ECMA-48 § 8.3.27
// XXX: allows passing ESC (not a ECMA-48 standard) this to allow for
// passthrough of ANSI sequences like in Screen or Tmux passthrough mode.
table.AddOne(0x1B, DcsEntryState, PutAction, DcsStringState)
table.AddRange(0x40, 0x7E, DcsEntryState, StartAction, DcsStringState)
// Dcs_intermediate
table.AddRange(0x00, 0x17, DcsIntermediateState, IgnoreAction, DcsIntermediateState)
table.AddOne(0x19, DcsIntermediateState, IgnoreAction, DcsIntermediateState)
table.AddRange(0x1C, 0x1F, DcsIntermediateState, IgnoreAction, DcsIntermediateState)
table.AddRange(0x20, 0x2F, DcsIntermediateState, CollectAction, DcsIntermediateState)
table.AddOne(0x7F, DcsIntermediateState, IgnoreAction, DcsIntermediateState)
// Dcs_intermediate -> Dcs_passthrough
table.AddRange(0x30, 0x3F, DcsIntermediateState, StartAction, DcsStringState)
table.AddRange(0x40, 0x7E, DcsIntermediateState, StartAction, DcsStringState)
// Dcs_param
table.AddRange(0x00, 0x17, DcsParamState, IgnoreAction, DcsParamState)
table.AddOne(0x19, DcsParamState, IgnoreAction, DcsParamState)
table.AddRange(0x1C, 0x1F, DcsParamState, IgnoreAction, DcsParamState)
table.AddRange(0x30, 0x3B, DcsParamState, ParamAction, DcsParamState)
table.AddOne(0x7F, DcsParamState, IgnoreAction, DcsParamState)
table.AddRange(0x3C, 0x3F, DcsParamState, IgnoreAction, DcsParamState)
// Dcs_param -> Dcs_intermediate
table.AddRange(0x20, 0x2F, DcsParamState, CollectAction, DcsIntermediateState)
// Dcs_param -> Dcs_passthrough
table.AddRange(0x40, 0x7E, DcsParamState, StartAction, DcsStringState)
// Dcs_passthrough
table.AddRange(0x00, 0x17, DcsStringState, PutAction, DcsStringState)
table.AddOne(0x19, DcsStringState, PutAction, DcsStringState)
table.AddRange(0x1C, 0x1F, DcsStringState, PutAction, DcsStringState)
table.AddRange(0x20, 0x7E, DcsStringState, PutAction, DcsStringState)
table.AddOne(0x7F, DcsStringState, IgnoreAction, DcsStringState)
table.AddRange(0x80, 0xFF, DcsStringState, PutAction, DcsStringState) // Allow Utf8 characters by extending the printable range from 0x7F to 0xFF
// ST, CAN, SUB, and ESC terminate the sequence
table.AddOne(0x1B, DcsStringState, DispatchAction, EscapeState)
table.AddOne(0x9C, DcsStringState, DispatchAction, GroundState)
table.AddMany([]byte{0x18, 0x1A}, DcsStringState, IgnoreAction, GroundState)
// Csi_param
table.AddRange(0x00, 0x17, CsiParamState, ExecuteAction, CsiParamState)
table.AddOne(0x19, CsiParamState, ExecuteAction, CsiParamState)
table.AddRange(0x1C, 0x1F, CsiParamState, ExecuteAction, CsiParamState)
table.AddRange(0x30, 0x3B, CsiParamState, ParamAction, CsiParamState)
table.AddOne(0x7F, CsiParamState, IgnoreAction, CsiParamState)
table.AddRange(0x3C, 0x3F, CsiParamState, IgnoreAction, CsiParamState)
// Csi_param -> Ground
table.AddRange(0x40, 0x7E, CsiParamState, DispatchAction, GroundState)
// Csi_param -> Csi_intermediate
table.AddRange(0x20, 0x2F, CsiParamState, CollectAction, CsiIntermediateState)
// Csi_intermediate
table.AddRange(0x00, 0x17, CsiIntermediateState, ExecuteAction, CsiIntermediateState)
table.AddOne(0x19, CsiIntermediateState, ExecuteAction, CsiIntermediateState)
table.AddRange(0x1C, 0x1F, CsiIntermediateState, ExecuteAction, CsiIntermediateState)
table.AddRange(0x20, 0x2F, CsiIntermediateState, CollectAction, CsiIntermediateState)
table.AddOne(0x7F, CsiIntermediateState, IgnoreAction, CsiIntermediateState)
// Csi_intermediate -> Ground
table.AddRange(0x40, 0x7E, CsiIntermediateState, DispatchAction, GroundState)
// Csi_intermediate -> Csi_ignore
table.AddRange(0x30, 0x3F, CsiIntermediateState, IgnoreAction, GroundState)
// Csi_entry
table.AddRange(0x00, 0x17, CsiEntryState, ExecuteAction, CsiEntryState)
table.AddOne(0x19, CsiEntryState, ExecuteAction, CsiEntryState)
table.AddRange(0x1C, 0x1F, CsiEntryState, ExecuteAction, CsiEntryState)
table.AddOne(0x7F, CsiEntryState, IgnoreAction, CsiEntryState)
// Csi_entry -> Ground
table.AddRange(0x40, 0x7E, CsiEntryState, DispatchAction, GroundState)
// Csi_entry -> Csi_intermediate
table.AddRange(0x20, 0x2F, CsiEntryState, CollectAction, CsiIntermediateState)
// Csi_entry -> Csi_param
table.AddRange(0x30, 0x3B, CsiEntryState, ParamAction, CsiParamState)
table.AddRange(0x3C, 0x3F, CsiEntryState, MarkerAction, CsiParamState)
// Osc_string
table.AddRange(0x00, 0x06, OscStringState, IgnoreAction, OscStringState)
table.AddRange(0x08, 0x17, OscStringState, IgnoreAction, OscStringState)
table.AddOne(0x19, OscStringState, IgnoreAction, OscStringState)
table.AddRange(0x1C, 0x1F, OscStringState, IgnoreAction, OscStringState)
table.AddRange(0x20, 0xFF, OscStringState, PutAction, OscStringState) // Allow Utf8 characters by extending the printable range from 0x7F to 0xFF
// ST, CAN, SUB, ESC, and BEL terminate the sequence
table.AddOne(0x1B, OscStringState, DispatchAction, EscapeState)
table.AddOne(0x07, OscStringState, DispatchAction, GroundState)
table.AddOne(0x9C, OscStringState, DispatchAction, GroundState)
table.AddMany([]byte{0x18, 0x1A}, OscStringState, IgnoreAction, GroundState)
return table
}

63
vendor/github.com/charmbracelet/x/ansi/passthrough.go generated vendored Normal file
View File

@@ -0,0 +1,63 @@
package ansi
import (
"bytes"
)
// ScreenPassthrough wraps the given ANSI sequence in a DCS passthrough
// sequence to be sent to the outer terminal. This is used to send raw escape
// sequences to the outer terminal when running inside GNU Screen.
//
// DCS <data> ST
//
// Note: Screen limits the length of string sequences to 768 bytes (since 2014).
// Use zero to indicate no limit, otherwise, this will chunk the returned
// string into limit sized chunks.
//
// See: https://www.gnu.org/software/screen/manual/screen.html#String-Escapes
// See: https://git.savannah.gnu.org/cgit/screen.git/tree/src/screen.h?id=c184c6ec27683ff1a860c45be5cf520d896fd2ef#n44
func ScreenPassthrough(seq string, limit int) string {
var b bytes.Buffer
b.WriteString("\x1bP")
if limit > 0 {
for i := 0; i < len(seq); i += limit {
end := i + limit
if end > len(seq) {
end = len(seq)
}
b.WriteString(seq[i:end])
if end < len(seq) {
b.WriteString("\x1b\\\x1bP")
}
}
} else {
b.WriteString(seq)
}
b.WriteString("\x1b\\")
return b.String()
}
// TmuxPassthrough wraps the given ANSI sequence in a special DCS passthrough
// sequence to be sent to the outer terminal. This is used to send raw escape
// sequences to the outer terminal when running inside Tmux.
//
// DCS tmux ; <escaped-data> ST
//
// Where <escaped-data> is the given sequence in which all occurrences of ESC
// (0x1b) are doubled i.e. replaced with ESC ESC (0x1b 0x1b).
//
// Note: this needs the `allow-passthrough` option to be set to `on`.
//
// See: https://github.com/tmux/tmux/wiki/FAQ#what-is-the-passthrough-escape-sequence-and-how-do-i-use-it
func TmuxPassthrough(seq string) string {
var b bytes.Buffer
b.WriteString("\x1bPtmux;")
for i := 0; i < len(seq); i++ {
if seq[i] == ESC {
b.WriteByte(ESC)
}
b.WriteByte(seq[i])
}
b.WriteString("\x1b\\")
return b.String()
}

126
vendor/github.com/charmbracelet/x/ansi/screen.go generated vendored Normal file
View File

@@ -0,0 +1,126 @@
package ansi
import "strconv"
// EraseDisplay (ED) clears the screen or parts of the screen. Possible values:
//
// 0: Clear from cursor to end of screen.
// 1: Clear from cursor to beginning of the screen.
// 2: Clear entire screen (and moves cursor to upper left on DOS).
// 3: Clear entire screen and delete all lines saved in the scrollback buffer.
//
// CSI <n> J
//
// See: https://vt100.net/docs/vt510-rm/ED.html
func EraseDisplay(n int) string {
if n < 0 {
n = 0
}
return "\x1b[" + strconv.Itoa(n) + "J"
}
// EraseDisplay constants.
// These are the possible values for the EraseDisplay function.
const (
EraseDisplayRight = "\x1b[0J"
EraseDisplayLeft = "\x1b[1J"
EraseEntireDisplay = "\x1b[2J"
)
// EraseLine (EL) clears the current line or parts of the line. Possible values:
//
// 0: Clear from cursor to end of line.
// 1: Clear from cursor to beginning of the line.
// 2: Clear entire line.
//
// The cursor position is not affected.
//
// CSI <n> K
//
// See: https://vt100.net/docs/vt510-rm/EL.html
func EraseLine(n int) string {
if n < 0 {
n = 0
}
return "\x1b[" + strconv.Itoa(n) + "K"
}
// EraseLine constants.
// These are the possible values for the EraseLine function.
const (
EraseLineRight = "\x1b[0K"
EraseLineLeft = "\x1b[1K"
EraseEntireLine = "\x1b[2K"
)
// ScrollUp (SU) scrolls the screen up n lines. New lines are added at the
// bottom of the screen.
//
// CSI <n> S
//
// See: https://vt100.net/docs/vt510-rm/SU.html
func ScrollUp(n int) string {
var s string
if n > 1 {
s = strconv.Itoa(n)
}
return "\x1b[" + s + "S"
}
// ScrollDown (SD) scrolls the screen down n lines. New lines are added at the
// top of the screen.
//
// CSI <n> T
//
// See: https://vt100.net/docs/vt510-rm/SD.html
func ScrollDown(n int) string {
var s string
if n > 1 {
s = strconv.Itoa(n)
}
return "\x1b[" + s + "T"
}
// InsertLine (IL) inserts n blank lines at the current cursor position.
// Existing lines are moved down.
//
// CSI <n> L
//
// See: https://vt100.net/docs/vt510-rm/IL.html
func InsertLine(n int) string {
var s string
if n > 1 {
s = strconv.Itoa(n)
}
return "\x1b[" + s + "L"
}
// DeleteLine (DL) deletes n lines at the current cursor position. Existing
// lines are moved up.
//
// CSI <n> M
//
// See: https://vt100.net/docs/vt510-rm/DL.html
func DeleteLine(n int) string {
var s string
if n > 1 {
s = strconv.Itoa(n)
}
return "\x1b[" + s + "M"
}
// SetScrollingRegion (DECSTBM) sets the top and bottom margins for the scrolling
// region. The default is the entire screen.
//
// CSI <top> ; <bottom> r
//
// See: https://vt100.net/docs/vt510-rm/DECSTBM.html
func SetScrollingRegion(t, b int) string {
if t < 0 {
t = 0
}
if b < 0 {
b = 0
}
return "\x1b[" + strconv.Itoa(t) + ";" + strconv.Itoa(b) + "r"
}

199
vendor/github.com/charmbracelet/x/ansi/sequence.go generated vendored Normal file
View File

@@ -0,0 +1,199 @@
package ansi
import (
"bytes"
"github.com/charmbracelet/x/ansi/parser"
)
// Sequence represents an ANSI sequence. This can be a control sequence, escape
// sequence, a printable character, etc.
type Sequence interface {
// String returns the string representation of the sequence.
String() string
// Bytes returns the byte representation of the sequence.
Bytes() []byte
// Clone returns a copy of the sequence.
Clone() Sequence
}
// Rune represents a printable character.
type Rune rune
var _ Sequence = Rune(0)
// Bytes implements Sequence.
func (r Rune) Bytes() []byte {
return []byte(string(r))
}
// String implements Sequence.
func (r Rune) String() string {
return string(r)
}
// Clone implements Sequence.
func (r Rune) Clone() Sequence {
return r
}
// ControlCode represents a control code character. This is a character that
// is not printable and is used to control the terminal. This would be a
// character in the C0 or C1 set in the range of 0x00-0x1F and 0x80-0x9F.
type ControlCode byte
var _ Sequence = ControlCode(0)
// Bytes implements Sequence.
func (c ControlCode) Bytes() []byte {
return []byte{byte(c)}
}
// String implements Sequence.
func (c ControlCode) String() string {
return string(c)
}
// Clone implements Sequence.
func (c ControlCode) Clone() Sequence {
return c
}
// EscSequence represents an escape sequence.
type EscSequence int
var _ Sequence = EscSequence(0)
// buffer returns the buffer of the escape sequence.
func (e EscSequence) buffer() *bytes.Buffer {
var b bytes.Buffer
b.WriteByte('\x1b')
if i := parser.Intermediate(int(e)); i != 0 {
b.WriteByte(byte(i))
}
b.WriteByte(byte(e.Command()))
return &b
}
// Bytes implements Sequence.
func (e EscSequence) Bytes() []byte {
return e.buffer().Bytes()
}
// String implements Sequence.
func (e EscSequence) String() string {
return e.buffer().String()
}
// Clone implements Sequence.
func (e EscSequence) Clone() Sequence {
return e
}
// Command returns the command byte of the escape sequence.
func (e EscSequence) Command() int {
return parser.Command(int(e))
}
// Intermediate returns the intermediate byte of the escape sequence.
func (e EscSequence) Intermediate() int {
return parser.Intermediate(int(e))
}
// SosSequence represents a SOS sequence.
type SosSequence struct {
// Data contains the raw data of the sequence.
Data []byte
}
var _ Sequence = &SosSequence{}
// Clone implements Sequence.
func (s SosSequence) Clone() Sequence {
return SosSequence{Data: append([]byte(nil), s.Data...)}
}
// Bytes implements Sequence.
func (s SosSequence) Bytes() []byte {
return s.buffer().Bytes()
}
// String implements Sequence.
func (s SosSequence) String() string {
return s.buffer().String()
}
func (s SosSequence) buffer() *bytes.Buffer {
var b bytes.Buffer
b.WriteByte('\x1b')
b.WriteByte('X')
b.Write(s.Data)
b.WriteString("\x1b\\")
return &b
}
// PmSequence represents a PM sequence.
type PmSequence struct {
// Data contains the raw data of the sequence.
Data []byte
}
var _ Sequence = &PmSequence{}
// Clone implements Sequence.
func (s PmSequence) Clone() Sequence {
return PmSequence{Data: append([]byte(nil), s.Data...)}
}
// Bytes implements Sequence.
func (s PmSequence) Bytes() []byte {
return s.buffer().Bytes()
}
// String implements Sequence.
func (s PmSequence) String() string {
return s.buffer().String()
}
// buffer returns the buffer of the PM sequence.
func (s PmSequence) buffer() *bytes.Buffer {
var b bytes.Buffer
b.WriteByte('\x1b')
b.WriteByte('^')
b.Write(s.Data)
b.WriteString("\x1b\\")
return &b
}
// ApcSequence represents an APC sequence.
type ApcSequence struct {
// Data contains the raw data of the sequence.
Data []byte
}
var _ Sequence = &ApcSequence{}
// Clone implements Sequence.
func (s ApcSequence) Clone() Sequence {
return ApcSequence{Data: append([]byte(nil), s.Data...)}
}
// Bytes implements Sequence.
func (s ApcSequence) Bytes() []byte {
return s.buffer().Bytes()
}
// String implements Sequence.
func (s ApcSequence) String() string {
return s.buffer().String()
}
// buffer returns the buffer of the APC sequence.
func (s ApcSequence) buffer() *bytes.Buffer {
var b bytes.Buffer
b.WriteByte('\x1b')
b.WriteByte('_')
b.Write(s.Data)
b.WriteString("\x1b\\")
return &b
}

296
vendor/github.com/charmbracelet/x/ansi/style.go generated vendored Normal file
View File

@@ -0,0 +1,296 @@
package ansi
import (
"image/color"
"strconv"
"strings"
)
// ResetStyle is a SGR (Select Graphic Rendition) style sequence that resets
// all attributes.
// See: https://en.wikipedia.org/wiki/ANSI_escape_code#SGR_(Select_Graphic_Rendition)_parameters
const ResetStyle = "\x1b[m"
// Attr is a SGR (Select Graphic Rendition) style attribute.
type Attr = string
// Style represents an ANSI SGR (Select Graphic Rendition) style.
type Style []Attr
// String returns the ANSI SGR (Select Graphic Rendition) style sequence for
// the given style.
func (s Style) String() string {
if len(s) == 0 {
return ResetStyle
}
return "\x1b[" + strings.Join(s, ";") + "m"
}
// Styled returns a styled string with the given style applied.
func (s Style) Styled(str string) string {
if len(s) == 0 {
return str
}
return s.String() + str + ResetStyle
}
// Reset appends the reset style attribute to the style.
func (s Style) Reset() Style {
return append(s, ResetAttr)
}
// Bold appends the bold style attribute to the style.
func (s Style) Bold() Style {
return append(s, BoldAttr)
}
// Faint appends the faint style attribute to the style.
func (s Style) Faint() Style {
return append(s, FaintAttr)
}
// Italic appends the italic style attribute to the style.
func (s Style) Italic() Style {
return append(s, ItalicAttr)
}
// Underline appends the underline style attribute to the style.
func (s Style) Underline() Style {
return append(s, UnderlineAttr)
}
// DoubleUnderline appends the double underline style attribute to the style.
func (s Style) DoubleUnderline() Style {
return append(s, DoubleUnderlineAttr)
}
// CurlyUnderline appends the curly underline style attribute to the style.
func (s Style) CurlyUnderline() Style {
return append(s, CurlyUnderlineAttr)
}
// DottedUnderline appends the dotted underline style attribute to the style.
func (s Style) DottedUnderline() Style {
return append(s, DottedUnderlineAttr)
}
// DashedUnderline appends the dashed underline style attribute to the style.
func (s Style) DashedUnderline() Style {
return append(s, DashedUnderlineAttr)
}
// SlowBlink appends the slow blink style attribute to the style.
func (s Style) SlowBlink() Style {
return append(s, SlowBlinkAttr)
}
// RapidBlink appends the rapid blink style attribute to the style.
func (s Style) RapidBlink() Style {
return append(s, RapidBlinkAttr)
}
// Reverse appends the reverse style attribute to the style.
func (s Style) Reverse() Style {
return append(s, ReverseAttr)
}
// Conceal appends the conceal style attribute to the style.
func (s Style) Conceal() Style {
return append(s, ConcealAttr)
}
// Strikethrough appends the strikethrough style attribute to the style.
func (s Style) Strikethrough() Style {
return append(s, StrikethroughAttr)
}
// NoBold appends the no bold style attribute to the style.
func (s Style) NoBold() Style {
return append(s, NoBoldAttr)
}
// NormalIntensity appends the normal intensity style attribute to the style.
func (s Style) NormalIntensity() Style {
return append(s, NormalIntensityAttr)
}
// NoItalic appends the no italic style attribute to the style.
func (s Style) NoItalic() Style {
return append(s, NoItalicAttr)
}
// NoUnderline appends the no underline style attribute to the style.
func (s Style) NoUnderline() Style {
return append(s, NoUnderlineAttr)
}
// NoBlink appends the no blink style attribute to the style.
func (s Style) NoBlink() Style {
return append(s, NoBlinkAttr)
}
// NoReverse appends the no reverse style attribute to the style.
func (s Style) NoReverse() Style {
return append(s, NoReverseAttr)
}
// NoConceal appends the no conceal style attribute to the style.
func (s Style) NoConceal() Style {
return append(s, NoConcealAttr)
}
// NoStrikethrough appends the no strikethrough style attribute to the style.
func (s Style) NoStrikethrough() Style {
return append(s, NoStrikethroughAttr)
}
// DefaultForegroundColor appends the default foreground color style attribute to the style.
func (s Style) DefaultForegroundColor() Style {
return append(s, DefaultForegroundColorAttr)
}
// DefaultBackgroundColor appends the default background color style attribute to the style.
func (s Style) DefaultBackgroundColor() Style {
return append(s, DefaultBackgroundColorAttr)
}
// DefaultUnderlineColor appends the default underline color style attribute to the style.
func (s Style) DefaultUnderlineColor() Style {
return append(s, DefaultUnderlineColorAttr)
}
// ForegroundColor appends the foreground color style attribute to the style.
func (s Style) ForegroundColor(c Color) Style {
return append(s, ForegroundColorAttr(c))
}
// BackgroundColor appends the background color style attribute to the style.
func (s Style) BackgroundColor(c Color) Style {
return append(s, BackgroundColorAttr(c))
}
// UnderlineColor appends the underline color style attribute to the style.
func (s Style) UnderlineColor(c Color) Style {
return append(s, UnderlineColorAttr(c))
}
// SGR (Select Graphic Rendition) style attributes.
// See: https://en.wikipedia.org/wiki/ANSI_escape_code#SGR_(Select_Graphic_Rendition)_parameters
const (
ResetAttr Attr = "0"
BoldAttr Attr = "1"
FaintAttr Attr = "2"
ItalicAttr Attr = "3"
UnderlineAttr Attr = "4"
DoubleUnderlineAttr Attr = "4:2"
CurlyUnderlineAttr Attr = "4:3"
DottedUnderlineAttr Attr = "4:4"
DashedUnderlineAttr Attr = "4:5"
SlowBlinkAttr Attr = "5"
RapidBlinkAttr Attr = "6"
ReverseAttr Attr = "7"
ConcealAttr Attr = "8"
StrikethroughAttr Attr = "9"
NoBoldAttr Attr = "21" // Some terminals treat this as double underline.
NormalIntensityAttr Attr = "22"
NoItalicAttr Attr = "23"
NoUnderlineAttr Attr = "24"
NoBlinkAttr Attr = "25"
NoReverseAttr Attr = "27"
NoConcealAttr Attr = "28"
NoStrikethroughAttr Attr = "29"
DefaultForegroundColorAttr Attr = "39"
DefaultBackgroundColorAttr Attr = "49"
DefaultUnderlineColorAttr Attr = "59"
)
// ForegroundColorAttr returns the style SGR attribute for the given foreground
// color.
// See: https://en.wikipedia.org/wiki/ANSI_escape_code#SGR_(Select_Graphic_Rendition)_parameters
func ForegroundColorAttr(c Color) Attr {
switch c := c.(type) {
case BasicColor:
// 3-bit or 4-bit ANSI foreground
// "3<n>" or "9<n>" where n is the color number from 0 to 7
if c < 8 {
return "3" + string('0'+c)
} else if c < 16 {
return "9" + string('0'+c-8)
}
case ExtendedColor:
// 256-color ANSI foreground
// "38;5;<n>"
return "38;5;" + strconv.FormatUint(uint64(c), 10)
case TrueColor, color.Color:
// 24-bit "true color" foreground
// "38;2;<r>;<g>;<b>"
r, g, b, _ := c.RGBA()
return "38;2;" +
strconv.FormatUint(uint64(shift(r)), 10) + ";" +
strconv.FormatUint(uint64(shift(g)), 10) + ";" +
strconv.FormatUint(uint64(shift(b)), 10)
}
return DefaultForegroundColorAttr
}
// BackgroundColorAttr returns the style SGR attribute for the given background
// color.
// See: https://en.wikipedia.org/wiki/ANSI_escape_code#SGR_(Select_Graphic_Rendition)_parameters
func BackgroundColorAttr(c Color) Attr {
switch c := c.(type) {
case BasicColor:
// 3-bit or 4-bit ANSI foreground
// "4<n>" or "10<n>" where n is the color number from 0 to 7
if c < 8 {
return "4" + string('0'+c)
} else {
return "10" + string('0'+c-8)
}
case ExtendedColor:
// 256-color ANSI foreground
// "48;5;<n>"
return "48;5;" + strconv.FormatUint(uint64(c), 10)
case TrueColor, color.Color:
// 24-bit "true color" foreground
// "38;2;<r>;<g>;<b>"
r, g, b, _ := c.RGBA()
return "48;2;" +
strconv.FormatUint(uint64(shift(r)), 10) + ";" +
strconv.FormatUint(uint64(shift(g)), 10) + ";" +
strconv.FormatUint(uint64(shift(b)), 10)
}
return DefaultBackgroundColorAttr
}
// UnderlineColorAttr returns the style SGR attribute for the given underline
// color.
// See: https://en.wikipedia.org/wiki/ANSI_escape_code#SGR_(Select_Graphic_Rendition)_parameters
func UnderlineColorAttr(c Color) Attr {
switch c := c.(type) {
// NOTE: we can't use 3-bit and 4-bit ANSI color codes with underline
// color, use 256-color instead.
//
// 256-color ANSI underline color
// "58;5;<n>"
case BasicColor:
return "58;5;" + strconv.FormatUint(uint64(c), 10)
case ExtendedColor:
return "58;5;" + strconv.FormatUint(uint64(c), 10)
case TrueColor, color.Color:
// 24-bit "true color" foreground
// "38;2;<r>;<g>;<b>"
r, g, b, _ := c.RGBA()
return "58;2;" +
strconv.FormatUint(uint64(shift(r)), 10) + ";" +
strconv.FormatUint(uint64(shift(g)), 10) + ";" +
strconv.FormatUint(uint64(shift(b)), 10)
}
return DefaultUnderlineColorAttr
}
func shift(v uint32) uint32 {
if v > 0xff {
return v >> 8
}
return v
}

31
vendor/github.com/charmbracelet/x/ansi/termcap.go generated vendored Normal file
View File

@@ -0,0 +1,31 @@
package ansi
import (
"encoding/hex"
"strings"
)
// RequestTermcap (XTGETTCAP) requests Termcap/Terminfo strings.
//
// DCS + q <Pt> ST
//
// Where <Pt> is a list of Termcap/Terminfo capabilities, encoded in 2-digit
// hexadecimals, separated by semicolons.
//
// See: https://man7.org/linux/man-pages/man5/terminfo.5.html
// See: https://invisible-island.net/xterm/ctlseqs/ctlseqs.html#h3-Operating-System-Commands
func RequestTermcap(caps ...string) string {
if len(caps) == 0 {
return ""
}
s := "\x1bP+q"
for i, c := range caps {
if i > 0 {
s += ";"
}
s += strings.ToUpper(hex.EncodeToString([]byte(c)))
}
return s + "\x1b\\"
}

32
vendor/github.com/charmbracelet/x/ansi/title.go generated vendored Normal file
View File

@@ -0,0 +1,32 @@
package ansi
// SetIconNameWindowTitle returns a sequence for setting the icon name and
// window title.
//
// OSC 0 ; title ST
// OSC 0 ; title BEL
//
// See: https://invisible-island.net/xterm/ctlseqs/ctlseqs.html#h2-Operating-System-Commands
func SetIconNameWindowTitle(s string) string {
return "\x1b]0;" + s + "\x07"
}
// SetIconName returns a sequence for setting the icon name.
//
// OSC 1 ; title ST
// OSC 1 ; title BEL
//
// See: https://invisible-island.net/xterm/ctlseqs/ctlseqs.html#h2-Operating-System-Commands
func SetIconName(s string) string {
return "\x1b]1;" + s + "\x07"
}
// SetWindowTitle returns a sequence for setting the window title.
//
// OSC 2 ; title ST
// OSC 2 ; title BEL
//
// See: https://invisible-island.net/xterm/ctlseqs/ctlseqs.html#h2-Operating-System-Commands
func SetWindowTitle(s string) string {
return "\x1b]2;" + s + "\x07"
}

112
vendor/github.com/charmbracelet/x/ansi/truncate.go generated vendored Normal file
View File

@@ -0,0 +1,112 @@
package ansi
import (
"bytes"
"github.com/charmbracelet/x/ansi/parser"
"github.com/rivo/uniseg"
)
// Truncate truncates a string to a given length, adding a tail to the
// end if the string is longer than the given length.
// This function is aware of ANSI escape codes and will not break them, and
// accounts for wide-characters (such as East Asians and emojis).
func Truncate(s string, length int, tail string) string {
if sw := StringWidth(s); sw <= length {
return s
}
tw := StringWidth(tail)
length -= tw
if length < 0 {
return ""
}
var cluster []byte
var buf bytes.Buffer
curWidth := 0
ignoring := false
gstate := -1
pstate := parser.GroundState // initial state
b := []byte(s)
i := 0
// Here we iterate over the bytes of the string and collect printable
// characters and runes. We also keep track of the width of the string
// in cells.
// Once we reach the given length, we start ignoring characters and only
// collect ANSI escape codes until we reach the end of string.
for i < len(b) {
state, action := parser.Table.Transition(pstate, b[i])
switch action {
case parser.PrintAction:
if utf8ByteLen(b[i]) > 1 {
// This action happens when we transition to the Utf8State.
var width int
cluster, _, width, gstate = uniseg.FirstGraphemeCluster(b[i:], gstate)
// increment the index by the length of the cluster
i += len(cluster)
// Are we ignoring? Skip to the next byte
if ignoring {
continue
}
// Is this gonna be too wide?
// If so write the tail and stop collecting.
if curWidth+width > length && !ignoring {
ignoring = true
buf.WriteString(tail)
}
if curWidth+width > length {
continue
}
curWidth += width
for _, r := range cluster {
buf.WriteByte(r)
}
gstate = -1 // reset grapheme state otherwise, width calculation might be off
// Done collecting, now we're back in the ground state.
pstate = parser.GroundState
continue
}
// Is this gonna be too wide?
// If so write the tail and stop collecting.
if curWidth >= length && !ignoring {
ignoring = true
buf.WriteString(tail)
}
// Skip to the next byte if we're ignoring
if ignoring {
i++
continue
}
// collects printable ASCII
curWidth++
fallthrough
default:
buf.WriteByte(b[i])
i++
}
// Transition to the next state.
pstate = state
// Once we reach the given length, we start ignoring runes and write
// the tail to the buffer.
if curWidth > length && !ignoring {
ignoring = true
buf.WriteString(tail)
}
}
return buf.String()
}

29
vendor/github.com/charmbracelet/x/ansi/util.go generated vendored Normal file
View File

@@ -0,0 +1,29 @@
package ansi
import (
"fmt"
"image/color"
)
// colorToHexString returns a hex string representation of a color.
func colorToHexString(c color.Color) string {
if c == nil {
return ""
}
shift := func(v uint32) uint32 {
if v > 0xff {
return v >> 8
}
return v
}
r, g, b, _ := c.RGBA()
r, g, b = shift(r), shift(g), shift(b)
return fmt.Sprintf("#%02x%02x%02x", r, g, b)
}
// rgbToHex converts red, green, and blue values to a hexadecimal value.
//
// hex := rgbToHex(0, 0, 255) // 0x0000FF
func rgbToHex(r, g, b uint32) uint32 {
return r<<16 + g<<8 + b
}

103
vendor/github.com/charmbracelet/x/ansi/width.go generated vendored Normal file
View File

@@ -0,0 +1,103 @@
package ansi
import (
"bytes"
"github.com/charmbracelet/x/ansi/parser"
"github.com/rivo/uniseg"
)
// Strip removes ANSI escape codes from a string.
func Strip(s string) string {
var (
buf bytes.Buffer // buffer for collecting printable characters
ri int // rune index
rw int // rune width
pstate = parser.GroundState // initial state
)
// This implements a subset of the Parser to only collect runes and
// printable characters.
for i := 0; i < len(s); i++ {
var state, action byte
if pstate != parser.Utf8State {
state, action = parser.Table.Transition(pstate, s[i])
}
switch {
case pstate == parser.Utf8State:
// During this state, collect rw bytes to form a valid rune in the
// buffer. After getting all the rune bytes into the buffer,
// transition to GroundState and reset the counters.
buf.WriteByte(s[i])
ri++
if ri < rw {
continue
}
pstate = parser.GroundState
ri = 0
rw = 0
case action == parser.PrintAction:
// This action happens when we transition to the Utf8State.
if w := utf8ByteLen(s[i]); w > 1 {
rw = w
buf.WriteByte(s[i])
ri++
break
}
fallthrough
case action == parser.ExecuteAction:
// collects printable ASCII and non-printable characters
buf.WriteByte(s[i])
}
// Transition to the next state.
// The Utf8State is managed separately above.
if pstate != parser.Utf8State {
pstate = state
}
}
return buf.String()
}
// StringWidth returns the width of a string in cells. This is the number of
// cells that the string will occupy when printed in a terminal. ANSI escape
// codes are ignored and wide characters (such as East Asians and emojis) are
// accounted for.
func StringWidth(s string) int {
if s == "" {
return 0
}
var (
gstate = -1
pstate = parser.GroundState // initial state
cluster string
width int
)
for i := 0; i < len(s); i++ {
state, action := parser.Table.Transition(pstate, s[i])
switch action {
case parser.PrintAction:
if utf8ByteLen(s[i]) > 1 {
var w int
cluster, _, w, gstate = uniseg.FirstGraphemeClusterInString(s[i:], gstate)
width += w
i += len(cluster) - 1
pstate = parser.GroundState
continue
}
width++
fallthrough
default:
// Reset uniseg state when we're not in a printable state.
gstate = -1
}
pstate = state
}
return width
}

406
vendor/github.com/charmbracelet/x/ansi/wrap.go generated vendored Normal file
View File

@@ -0,0 +1,406 @@
package ansi
import (
"bytes"
"unicode"
"unicode/utf8"
"github.com/charmbracelet/x/ansi/parser"
"github.com/rivo/uniseg"
)
// nbsp is a non-breaking space
const nbsp = 0xA0
// Hardwrap wraps a string or a block of text to a given line length, breaking
// word boundaries. This will preserve ANSI escape codes and will account for
// wide-characters in the string.
// When preserveSpace is true, spaces at the beginning of a line will be
// preserved.
func Hardwrap(s string, limit int, preserveSpace bool) string {
if limit < 1 {
return s
}
var (
cluster []byte
buf bytes.Buffer
curWidth int
forceNewline bool
gstate = -1
pstate = parser.GroundState // initial state
b = []byte(s)
)
addNewline := func() {
buf.WriteByte('\n')
curWidth = 0
}
i := 0
for i < len(b) {
state, action := parser.Table.Transition(pstate, b[i])
switch action {
case parser.PrintAction:
if utf8ByteLen(b[i]) > 1 {
var width int
cluster, _, width, gstate = uniseg.FirstGraphemeCluster(b[i:], gstate)
i += len(cluster)
if curWidth+width > limit {
addNewline()
}
if !preserveSpace && curWidth == 0 && len(cluster) <= 4 {
// Skip spaces at the beginning of a line
if r, _ := utf8.DecodeRune(cluster); r != utf8.RuneError && unicode.IsSpace(r) {
pstate = parser.GroundState
continue
}
}
buf.Write(cluster)
curWidth += width
gstate = -1 // reset grapheme state otherwise, width calculation might be off
pstate = parser.GroundState
continue
}
fallthrough
case parser.ExecuteAction:
if b[i] == '\n' {
addNewline()
forceNewline = false
break
}
if curWidth+1 > limit {
addNewline()
forceNewline = true
}
// Skip spaces at the beginning of a line
if curWidth == 0 {
if !preserveSpace && forceNewline && unicode.IsSpace(rune(b[i])) {
break
}
forceNewline = false
}
buf.WriteByte(b[i])
curWidth++
default:
buf.WriteByte(b[i])
}
// We manage the UTF8 state separately manually above.
if pstate != parser.Utf8State {
pstate = state
}
i++
}
return buf.String()
}
// Wordwrap wraps a string or a block of text to a given line length, not
// breaking word boundaries. This will preserve ANSI escape codes and will
// account for wide-characters in the string.
// The breakpoints string is a list of characters that are considered
// breakpoints for word wrapping. A hyphen (-) is always considered a
// breakpoint.
//
// Note: breakpoints must be a string of 1-cell wide rune characters.
func Wordwrap(s string, limit int, breakpoints string) string {
if limit < 1 {
return s
}
var (
cluster []byte
buf bytes.Buffer
word bytes.Buffer
space bytes.Buffer
curWidth int
wordLen int
gstate = -1
pstate = parser.GroundState // initial state
b = []byte(s)
)
addSpace := func() {
curWidth += space.Len()
buf.Write(space.Bytes())
space.Reset()
}
addWord := func() {
if word.Len() == 0 {
return
}
addSpace()
curWidth += wordLen
buf.Write(word.Bytes())
word.Reset()
wordLen = 0
}
addNewline := func() {
buf.WriteByte('\n')
curWidth = 0
space.Reset()
}
i := 0
for i < len(b) {
state, action := parser.Table.Transition(pstate, b[i])
switch action {
case parser.PrintAction:
if utf8ByteLen(b[i]) > 1 {
var width int
cluster, _, width, gstate = uniseg.FirstGraphemeCluster(b[i:], gstate)
i += len(cluster)
r, _ := utf8.DecodeRune(cluster)
if r != utf8.RuneError && unicode.IsSpace(r) && r != nbsp {
addWord()
space.WriteRune(r)
} else if bytes.ContainsAny(cluster, breakpoints) {
addSpace()
addWord()
buf.Write(cluster)
curWidth++
} else {
word.Write(cluster)
wordLen += width
if curWidth+space.Len()+wordLen > limit &&
wordLen < limit {
addNewline()
}
}
pstate = parser.GroundState
continue
}
fallthrough
case parser.ExecuteAction:
r := rune(b[i])
switch {
case r == '\n':
if wordLen == 0 {
if curWidth+space.Len() > limit {
curWidth = 0
} else {
buf.Write(space.Bytes())
}
space.Reset()
}
addWord()
addNewline()
case unicode.IsSpace(r):
addWord()
space.WriteByte(b[i])
case r == '-':
fallthrough
case runeContainsAny(r, breakpoints):
addSpace()
addWord()
buf.WriteByte(b[i])
curWidth++
default:
word.WriteByte(b[i])
wordLen++
if curWidth+space.Len()+wordLen > limit &&
wordLen < limit {
addNewline()
}
}
default:
word.WriteByte(b[i])
}
// We manage the UTF8 state separately manually above.
if pstate != parser.Utf8State {
pstate = state
}
i++
}
addWord()
return buf.String()
}
// Wrap wraps a string or a block of text to a given line length, breaking word
// boundaries if necessary. This will preserve ANSI escape codes and will
// account for wide-characters in the string. The breakpoints string is a list
// of characters that are considered breakpoints for word wrapping. A hyphen
// (-) is always considered a breakpoint.
//
// Note: breakpoints must be a string of 1-cell wide rune characters.
func Wrap(s string, limit int, breakpoints string) string {
if limit < 1 {
return s
}
var (
cluster []byte
buf bytes.Buffer
word bytes.Buffer
space bytes.Buffer
curWidth int // written width of the line
wordLen int // word buffer len without ANSI escape codes
gstate = -1
pstate = parser.GroundState // initial state
b = []byte(s)
)
addSpace := func() {
curWidth += space.Len()
buf.Write(space.Bytes())
space.Reset()
}
addWord := func() {
if word.Len() == 0 {
return
}
addSpace()
curWidth += wordLen
buf.Write(word.Bytes())
word.Reset()
wordLen = 0
}
addNewline := func() {
buf.WriteByte('\n')
curWidth = 0
space.Reset()
}
i := 0
for i < len(b) {
state, action := parser.Table.Transition(pstate, b[i])
switch action {
case parser.PrintAction:
if utf8ByteLen(b[i]) > 1 {
var width int
cluster, _, width, gstate = uniseg.FirstGraphemeCluster(b[i:], gstate)
i += len(cluster)
r, _ := utf8.DecodeRune(cluster)
switch {
case r != utf8.RuneError && unicode.IsSpace(r) && r != nbsp: // nbsp is a non-breaking space
addWord()
space.WriteRune(r)
case bytes.ContainsAny(cluster, breakpoints):
addSpace()
if curWidth+wordLen+width > limit {
word.Write(cluster)
wordLen += width
} else {
addWord()
buf.Write(cluster)
curWidth += width
}
default:
if wordLen+width > limit {
// Hardwrap the word if it's too long
addWord()
}
word.Write(cluster)
wordLen += width
if curWidth+wordLen+space.Len() > limit {
addNewline()
}
}
pstate = parser.GroundState
continue
}
fallthrough
case parser.ExecuteAction:
switch r := rune(b[i]); {
case r == '\n':
if wordLen == 0 {
if curWidth+space.Len() > limit {
curWidth = 0
} else {
// preserve whitespaces
buf.Write(space.Bytes())
}
space.Reset()
}
addWord()
addNewline()
case unicode.IsSpace(r):
addWord()
space.WriteRune(r)
case r == '-':
fallthrough
case runeContainsAny(r, breakpoints):
addSpace()
if curWidth+wordLen >= limit {
// We can't fit the breakpoint in the current line, treat
// it as part of the word.
word.WriteRune(r)
wordLen++
} else {
addWord()
buf.WriteRune(r)
curWidth++
}
default:
word.WriteRune(r)
wordLen++
if wordLen == limit {
// Hardwrap the word if it's too long
addWord()
}
if curWidth+wordLen+space.Len() > limit {
addNewline()
}
}
default:
word.WriteByte(b[i])
}
// We manage the UTF8 state separately manually above.
if pstate != parser.Utf8State {
pstate = state
}
i++
}
if word.Len() != 0 {
// Preserve ANSI wrapped spaces at the end of string
if curWidth+space.Len() > limit {
buf.WriteByte('\n')
}
addSpace()
}
buf.Write(word.Bytes())
return buf.String()
}
func runeContainsAny(r rune, s string) bool {
for _, c := range s {
if c == r {
return true
}
}
return false
}

33
vendor/github.com/charmbracelet/x/ansi/xterm.go generated vendored Normal file
View File

@@ -0,0 +1,33 @@
package ansi
// DisableModifyOtherKeys disables the modifyOtherKeys mode.
//
// CSI > 4 ; 0 m
//
// See: https://invisible-island.net/xterm/ctlseqs/ctlseqs.html#h3-Functions-using-CSI-_-ordered-by-the-final-character_s_
// See: https://invisible-island.net/xterm/manpage/xterm.html#VT100-Widget-Resources:modifyOtherKeys
const DisableModifyOtherKeys = "\x1b[>4;0m"
// EnableModifyOtherKeys1 enables the modifyOtherKeys mode 1.
//
// CSI > 4 ; 1 m
//
// See: https://invisible-island.net/xterm/ctlseqs/ctlseqs.html#h3-Functions-using-CSI-_-ordered-by-the-final-character_s_
// See: https://invisible-island.net/xterm/manpage/xterm.html#VT100-Widget-Resources:modifyOtherKeys
const EnableModifyOtherKeys1 = "\x1b[>4;1m"
// EnableModifyOtherKeys2 enables the modifyOtherKeys mode 2.
//
// CSI > 4 ; 2 m
//
// See: https://invisible-island.net/xterm/ctlseqs/ctlseqs.html#h3-Functions-using-CSI-_-ordered-by-the-final-character_s_
// See: https://invisible-island.net/xterm/manpage/xterm.html#VT100-Widget-Resources:modifyOtherKeys
const EnableModifyOtherKeys2 = "\x1b[>4;2m"
// RequestModifyOtherKeys requests the modifyOtherKeys mode.
//
// CSI ? 4 m
//
// See: https://invisible-island.net/xterm/ctlseqs/ctlseqs.html#h3-Functions-using-CSI-_-ordered-by-the-final-character_s_
// See: https://invisible-island.net/xterm/manpage/xterm.html#VT100-Widget-Resources:modifyOtherKeys
const RequestModifyOtherKeys = "\x1b[?4m"

21
vendor/github.com/charmbracelet/x/input/LICENSE generated vendored Normal file
View File

@@ -0,0 +1,21 @@
MIT License
Copyright (c) 2023 Charmbracelet, Inc.
Permission is hereby granted, free of charge, to any person obtaining a copy
of this software and associated documentation files (the "Software"), to deal
in the Software without restriction, including without limitation the rights
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
copies of the Software, and to permit persons to whom the Software is
furnished to do so, subject to the following conditions:
The above copyright notice and this permission notice shall be included in all
copies or substantial portions of the Software.
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
SOFTWARE.

View File

@@ -0,0 +1,14 @@
//go:build !windows
// +build !windows
package input
import (
"io"
"github.com/muesli/cancelreader"
)
func newCancelreader(r io.Reader) (cancelreader.CancelReader, error) {
return cancelreader.NewReader(r)
}

View File

@@ -0,0 +1,217 @@
//go:build windows
// +build windows
package input
import (
"fmt"
"io"
"os"
"sync"
"time"
"github.com/erikgeiser/coninput"
"github.com/muesli/cancelreader"
"golang.org/x/sys/windows"
)
type conInputReader struct {
cancelMixin
conin windows.Handle
cancelEvent windows.Handle
originalMode uint32
// blockingReadSignal is used to signal that a blocking read is in progress.
blockingReadSignal chan struct{}
}
var _ cancelreader.CancelReader = &conInputReader{}
func newCancelreader(r io.Reader) (cancelreader.CancelReader, error) {
fallback := func(io.Reader) (cancelreader.CancelReader, error) {
return cancelreader.NewReader(r)
}
var dummy uint32
if f, ok := r.(cancelreader.File); !ok || f.Fd() != os.Stdin.Fd() ||
// If data was piped to the standard input, it does not emit events
// anymore. We can detect this if the console mode cannot be set anymore,
// in this case, we fallback to the default cancelreader implementation.
windows.GetConsoleMode(windows.Handle(f.Fd()), &dummy) != nil {
return fallback(r)
}
conin, err := coninput.NewStdinHandle()
if err != nil {
return fallback(r)
}
originalMode, err := prepareConsole(conin,
windows.ENABLE_MOUSE_INPUT,
windows.ENABLE_WINDOW_INPUT,
windows.ENABLE_EXTENDED_FLAGS,
)
if err != nil {
return nil, fmt.Errorf("failed to prepare console input: %w", err)
}
cancelEvent, err := windows.CreateEvent(nil, 0, 0, nil)
if err != nil {
return nil, fmt.Errorf("create stop event: %w", err)
}
return &conInputReader{
conin: conin,
cancelEvent: cancelEvent,
originalMode: originalMode,
blockingReadSignal: make(chan struct{}, 1),
}, nil
}
// Cancel implements cancelreader.CancelReader.
func (r *conInputReader) Cancel() bool {
r.setCanceled()
select {
case r.blockingReadSignal <- struct{}{}:
err := windows.SetEvent(r.cancelEvent)
if err != nil {
return false
}
<-r.blockingReadSignal
case <-time.After(100 * time.Millisecond):
// Read() hangs in a GetOverlappedResult which is likely due to
// WaitForMultipleObjects returning without input being available
// so we cannot cancel this ongoing read.
return false
}
return true
}
// Close implements cancelreader.CancelReader.
func (r *conInputReader) Close() error {
err := windows.CloseHandle(r.cancelEvent)
if err != nil {
return fmt.Errorf("closing cancel event handle: %w", err)
}
if r.originalMode != 0 {
err := windows.SetConsoleMode(r.conin, r.originalMode)
if err != nil {
return fmt.Errorf("reset console mode: %w", err)
}
}
return nil
}
// Read implements cancelreader.CancelReader.
func (r *conInputReader) Read(data []byte) (n int, err error) {
if r.isCanceled() {
return 0, cancelreader.ErrCanceled
}
err = waitForInput(r.conin, r.cancelEvent)
if err != nil {
return 0, err
}
if r.isCanceled() {
return 0, cancelreader.ErrCanceled
}
r.blockingReadSignal <- struct{}{}
n, err = overlappedReader(r.conin).Read(data)
<-r.blockingReadSignal
return
}
func prepareConsole(input windows.Handle, modes ...uint32) (originalMode uint32, err error) {
err = windows.GetConsoleMode(input, &originalMode)
if err != nil {
return 0, fmt.Errorf("get console mode: %w", err)
}
newMode := coninput.AddInputModes(0, modes...)
err = windows.SetConsoleMode(input, newMode)
if err != nil {
return 0, fmt.Errorf("set console mode: %w", err)
}
return originalMode, nil
}
func waitForInput(conin, cancel windows.Handle) error {
event, err := windows.WaitForMultipleObjects([]windows.Handle{conin, cancel}, false, windows.INFINITE)
switch {
case windows.WAIT_OBJECT_0 <= event && event < windows.WAIT_OBJECT_0+2:
if event == windows.WAIT_OBJECT_0+1 {
return cancelreader.ErrCanceled
}
if event == windows.WAIT_OBJECT_0 {
return nil
}
return fmt.Errorf("unexpected wait object is ready: %d", event-windows.WAIT_OBJECT_0)
case windows.WAIT_ABANDONED <= event && event < windows.WAIT_ABANDONED+2:
return fmt.Errorf("abandoned")
case event == uint32(windows.WAIT_TIMEOUT):
return fmt.Errorf("timeout")
case event == windows.WAIT_FAILED:
return fmt.Errorf("failed")
default:
return fmt.Errorf("unexpected error: %w", err)
}
}
// cancelMixin represents a goroutine-safe cancelation status.
type cancelMixin struct {
unsafeCanceled bool
lock sync.Mutex
}
func (c *cancelMixin) setCanceled() {
c.lock.Lock()
defer c.lock.Unlock()
c.unsafeCanceled = true
}
func (c *cancelMixin) isCanceled() bool {
c.lock.Lock()
defer c.lock.Unlock()
return c.unsafeCanceled
}
type overlappedReader windows.Handle
// Read performs an overlapping read fom a windows.Handle.
func (r overlappedReader) Read(data []byte) (int, error) {
hevent, err := windows.CreateEvent(nil, 0, 0, nil)
if err != nil {
return 0, fmt.Errorf("create event: %w", err)
}
overlapped := windows.Overlapped{HEvent: hevent}
var n uint32
err = windows.ReadFile(windows.Handle(r), data, &n, &overlapped)
if err != nil && err != windows.ERROR_IO_PENDING {
return int(n), err
}
err = windows.GetOverlappedResult(windows.Handle(r), &overlapped, &n, true)
if err != nil {
return int(n), nil
}
return int(n), nil
}

9
vendor/github.com/charmbracelet/x/input/clipboard.go generated vendored Normal file
View File

@@ -0,0 +1,9 @@
package input
// ClipboardEvent is a clipboard read event.
type ClipboardEvent string
// String returns the string representation of the clipboard event.
func (e ClipboardEvent) String() string {
return string(e)
}

77
vendor/github.com/charmbracelet/x/input/color.go generated vendored Normal file
View File

@@ -0,0 +1,77 @@
package input
import (
"fmt"
"image/color"
"strconv"
"strings"
)
// ForegroundColorEvent represents a foreground color change event.
type ForegroundColorEvent struct{ color.Color }
// String implements fmt.Stringer.
func (e ForegroundColorEvent) String() string {
return colorToHex(e)
}
// BackgroundColorEvent represents a background color change event.
type BackgroundColorEvent struct{ color.Color }
// String implements fmt.Stringer.
func (e BackgroundColorEvent) String() string {
return colorToHex(e)
}
// CursorColorEvent represents a cursor color change event.
type CursorColorEvent struct{ color.Color }
// String implements fmt.Stringer.
func (e CursorColorEvent) String() string {
return colorToHex(e)
}
type shiftable interface {
~uint | ~uint16 | ~uint32 | ~uint64
}
func shift[T shiftable](x T) T {
if x > 0xff {
x >>= 8
}
return x
}
func colorToHex(c color.Color) string {
r, g, b, _ := c.RGBA()
return fmt.Sprintf("#%02x%02x%02x", shift(r), shift(g), shift(b))
}
func xParseColor(s string) color.Color {
switch {
case strings.HasPrefix(s, "rgb:"):
parts := strings.Split(s[4:], "/")
if len(parts) != 3 {
return color.Black
}
r, _ := strconv.ParseUint(parts[0], 16, 32)
g, _ := strconv.ParseUint(parts[1], 16, 32)
b, _ := strconv.ParseUint(parts[2], 16, 32)
return color.RGBA{uint8(shift(r)), uint8(shift(g)), uint8(shift(b)), 255}
case strings.HasPrefix(s, "rgba:"):
parts := strings.Split(s[5:], "/")
if len(parts) != 4 {
return color.Black
}
r, _ := strconv.ParseUint(parts[0], 16, 32)
g, _ := strconv.ParseUint(parts[1], 16, 32)
b, _ := strconv.ParseUint(parts[2], 16, 32)
a, _ := strconv.ParseUint(parts[3], 16, 32)
return color.RGBA{uint8(shift(r)), uint8(shift(g)), uint8(shift(b)), uint8(shift(a))}
}
return color.Black
}

10
vendor/github.com/charmbracelet/x/input/cursor.go generated vendored Normal file
View File

@@ -0,0 +1,10 @@
package input
// CursorPositionEvent represents a cursor position event.
type CursorPositionEvent struct {
// Row is the row number.
Row int
// Column is the column number.
Column int
}

18
vendor/github.com/charmbracelet/x/input/da1.go generated vendored Normal file
View File

@@ -0,0 +1,18 @@
package input
import "github.com/charmbracelet/x/ansi"
// PrimaryDeviceAttributesEvent represents a primary device attributes event.
type PrimaryDeviceAttributesEvent []uint
func parsePrimaryDevAttrs(csi *ansi.CsiSequence) Event {
// Primary Device Attributes
da1 := make(PrimaryDeviceAttributesEvent, len(csi.Params))
csi.Range(func(i int, p int, hasMore bool) bool {
if !hasMore {
da1[i] = uint(p)
}
return true
})
return da1
}

125
vendor/github.com/charmbracelet/x/input/driver.go generated vendored Normal file
View File

@@ -0,0 +1,125 @@
package input
import (
"bytes"
"io"
"unicode/utf8"
"github.com/erikgeiser/coninput"
"github.com/muesli/cancelreader"
)
// Driver represents an ANSI terminal input Driver.
// It reads input events and parses ANSI sequences from the terminal input
// buffer.
type Driver struct {
rd cancelreader.CancelReader
table map[string]Key // table is a lookup table for key sequences.
term string // term is the terminal name $TERM.
// paste is the bracketed paste mode buffer.
// When nil, bracketed paste mode is disabled.
paste []byte
buf [256]byte // do we need a larger buffer?
// prevMouseState keeps track of the previous mouse state to determine mouse
// up button events.
prevMouseState coninput.ButtonState // nolint: unused
flags int // control the behavior of the driver.
}
// NewDriver returns a new ANSI input driver.
// This driver uses ANSI control codes compatible with VT100/VT200 terminals,
// and XTerm. It supports reading Terminfo databases to overwrite the default
// key sequences.
func NewDriver(r io.Reader, term string, flags int) (*Driver, error) {
d := new(Driver)
cr, err := newCancelreader(r)
if err != nil {
return nil, err
}
d.rd = cr
d.table = buildKeysTable(flags, term)
d.term = term
d.flags = flags
return d, nil
}
// Cancel cancels the underlying reader.
func (d *Driver) Cancel() bool {
return d.rd.Cancel()
}
// Close closes the underlying reader.
func (d *Driver) Close() error {
return d.rd.Close()
}
func (d *Driver) readEvents() (e []Event, err error) {
nb, err := d.rd.Read(d.buf[:])
if err != nil {
return nil, err
}
buf := d.buf[:nb]
// Lookup table first
if bytes.HasPrefix(buf, []byte{'\x1b'}) {
if k, ok := d.table[string(buf)]; ok {
e = append(e, KeyDownEvent(k))
return
}
}
var i int
for i < len(buf) {
nb, ev := ParseSequence(buf[i:])
// Handle bracketed-paste
if d.paste != nil {
if _, ok := ev.(PasteEndEvent); !ok {
d.paste = append(d.paste, buf[i])
i++
continue
}
}
switch ev.(type) {
case UnknownCsiEvent, UnknownSs3Event, UnknownEvent:
// If the sequence is not recognized by the parser, try looking it up.
if k, ok := d.table[string(buf[i:i+nb])]; ok {
ev = KeyDownEvent(k)
}
case PasteStartEvent:
d.paste = []byte{}
case PasteEndEvent:
// Decode the captured data into runes.
var paste []rune
for len(d.paste) > 0 {
r, w := utf8.DecodeRune(d.paste)
if r != utf8.RuneError {
paste = append(paste, r)
}
d.paste = d.paste[w:]
}
d.paste = nil // reset the buffer
e = append(e, PasteEvent(paste))
case nil:
i++
continue
}
if mevs, ok := ev.(MultiEvent); ok {
e = append(e, []Event(mevs)...)
} else {
e = append(e, ev)
}
i += nb
}
return
}

View File

@@ -0,0 +1,11 @@
//go:build !windows
// +build !windows
package input
// ReadEvents reads input events from the terminal.
//
// It reads the events available in the input buffer and returns them.
func (d *Driver) ReadEvents() ([]Event, error) {
return d.readEvents()
}

View File

@@ -0,0 +1,276 @@
//go:build windows
// +build windows
package input
import (
"errors"
"fmt"
"unicode/utf16"
"github.com/charmbracelet/x/ansi"
termwindows "github.com/charmbracelet/x/windows"
"github.com/erikgeiser/coninput"
"golang.org/x/sys/windows"
)
// ReadEvents reads input events from the terminal.
//
// It reads the events available in the input buffer and returns them.
func (d *Driver) ReadEvents() ([]Event, error) {
events, err := d.handleConInput(coninput.ReadConsoleInput)
if errors.Is(err, errNotConInputReader) {
return d.readEvents()
}
return events, err
}
var errNotConInputReader = fmt.Errorf("handleConInput: not a conInputReader")
func (d *Driver) handleConInput(
finput func(windows.Handle, []coninput.InputRecord) (uint32, error),
) ([]Event, error) {
cc, ok := d.rd.(*conInputReader)
if !ok {
return nil, errNotConInputReader
}
// read up to 256 events, this is to allow for sequences events reported as
// key events.
var events [256]coninput.InputRecord
_, err := finput(cc.conin, events[:])
if err != nil {
return nil, fmt.Errorf("read coninput events: %w", err)
}
var evs []Event
for _, event := range events {
if e := parseConInputEvent(event, &d.prevMouseState); e != nil {
evs = append(evs, e)
}
}
return d.detectConInputQuerySequences(evs), nil
}
// Using ConInput API, Windows Terminal responds to sequence query events with
// KEY_EVENT_RECORDs so we need to collect them and parse them as a single
// sequence.
// Is this a hack?
func (d *Driver) detectConInputQuerySequences(events []Event) []Event {
var newEvents []Event
start, end := -1, -1
loop:
for i, e := range events {
switch e := e.(type) {
case KeyDownEvent:
switch e.Rune {
case ansi.ESC, ansi.CSI, ansi.OSC, ansi.DCS, ansi.APC:
// start of a sequence
if start == -1 {
start = i
}
}
default:
break loop
}
end = i
}
if start == -1 || end <= start {
return events
}
var seq []byte
for i := start; i <= end; i++ {
switch e := events[i].(type) {
case KeyDownEvent:
seq = append(seq, byte(e.Rune))
}
}
n, seqevent := ParseSequence(seq)
switch seqevent.(type) {
case UnknownEvent:
// We're not interested in unknown events
default:
if start+n > len(events) {
return events
}
newEvents = events[:start]
newEvents = append(newEvents, seqevent)
newEvents = append(newEvents, events[start+n:]...)
return d.detectConInputQuerySequences(newEvents)
}
return events
}
func parseConInputEvent(event coninput.InputRecord, ps *coninput.ButtonState) Event {
switch e := event.Unwrap().(type) {
case coninput.KeyEventRecord:
event := parseWin32InputKeyEvent(e.VirtualKeyCode, e.VirtualScanCode,
e.Char, e.KeyDown, e.ControlKeyState, e.RepeatCount)
var key Key
switch event := event.(type) {
case KeyDownEvent:
key = Key(event)
case KeyUpEvent:
key = Key(event)
default:
return nil
}
// If the key is not printable, return the event as is
// (e.g. function keys, arrows, etc.)
// Otherwise, try to translate it to a rune based on the active keyboard
// layout.
if key.Rune == 0 {
return event
}
// Get active keyboard layout
fgWin := windows.GetForegroundWindow()
fgThread, err := windows.GetWindowThreadProcessId(fgWin, nil)
if err != nil {
return event
}
layout, err := termwindows.GetKeyboardLayout(fgThread)
if layout == windows.InvalidHandle || err != nil {
return event
}
// Translate key to rune
var keyState [256]byte
var utf16Buf [16]uint16
const dontChangeKernelKeyboardLayout = 0x4
ret, err := termwindows.ToUnicodeEx(
uint32(e.VirtualKeyCode),
uint32(e.VirtualScanCode),
&keyState[0],
&utf16Buf[0],
int32(len(utf16Buf)),
dontChangeKernelKeyboardLayout,
layout,
)
// -1 indicates a dead key
// 0 indicates no translation for this key
if ret < 1 || err != nil {
return event
}
runes := utf16.Decode(utf16Buf[:ret])
if len(runes) != 1 {
// Key doesn't translate to a single rune
return event
}
key.BaseRune = runes[0]
if e.KeyDown {
return KeyDownEvent(key)
}
return KeyUpEvent(key)
case coninput.WindowBufferSizeEventRecord:
return WindowSizeEvent{
Width: int(e.Size.X),
Height: int(e.Size.Y),
}
case coninput.MouseEventRecord:
mevent := mouseEvent(*ps, e)
*ps = e.ButtonState
return mevent
case coninput.FocusEventRecord, coninput.MenuEventRecord:
// ignore
}
return nil
}
func mouseEventButton(p, s coninput.ButtonState) (button MouseButton, isRelease bool) {
btn := p ^ s
if btn&s == 0 {
isRelease = true
}
if btn == 0 {
switch {
case s&coninput.FROM_LEFT_1ST_BUTTON_PRESSED > 0:
button = MouseLeft
case s&coninput.FROM_LEFT_2ND_BUTTON_PRESSED > 0:
button = MouseMiddle
case s&coninput.RIGHTMOST_BUTTON_PRESSED > 0:
button = MouseRight
case s&coninput.FROM_LEFT_3RD_BUTTON_PRESSED > 0:
button = MouseBackward
case s&coninput.FROM_LEFT_4TH_BUTTON_PRESSED > 0:
button = MouseForward
}
return
}
switch {
case btn == coninput.FROM_LEFT_1ST_BUTTON_PRESSED: // left button
button = MouseLeft
case btn == coninput.RIGHTMOST_BUTTON_PRESSED: // right button
button = MouseRight
case btn == coninput.FROM_LEFT_2ND_BUTTON_PRESSED: // middle button
button = MouseMiddle
case btn == coninput.FROM_LEFT_3RD_BUTTON_PRESSED: // unknown (possibly mouse backward)
button = MouseBackward
case btn == coninput.FROM_LEFT_4TH_BUTTON_PRESSED: // unknown (possibly mouse forward)
button = MouseForward
}
return
}
func mouseEvent(p coninput.ButtonState, e coninput.MouseEventRecord) (ev Event) {
var mod KeyMod
var isRelease bool
if e.ControlKeyState.Contains(coninput.LEFT_ALT_PRESSED | coninput.RIGHT_ALT_PRESSED) {
mod |= Alt
}
if e.ControlKeyState.Contains(coninput.LEFT_CTRL_PRESSED | coninput.RIGHT_CTRL_PRESSED) {
mod |= Ctrl
}
if e.ControlKeyState.Contains(coninput.SHIFT_PRESSED) {
mod |= Shift
}
m := Mouse{
X: int(e.MousePositon.X),
Y: int(e.MousePositon.Y),
Mod: mod,
}
switch e.EventFlags {
case coninput.CLICK, coninput.DOUBLE_CLICK:
m.Button, isRelease = mouseEventButton(p, e.ButtonState)
case coninput.MOUSE_WHEELED:
if e.WheelDirection > 0 {
m.Button = MouseWheelUp
} else {
m.Button = MouseWheelDown
}
case coninput.MOUSE_HWHEELED:
if e.WheelDirection > 0 {
m.Button = MouseWheelRight
} else {
m.Button = MouseWheelLeft
}
case coninput.MOUSE_MOVED:
m.Button, _ = mouseEventButton(p, e.ButtonState)
return MouseMotionEvent(m)
}
if isWheel(m.Button) {
return MouseWheelEvent(m)
} else if isRelease {
return MouseUpEvent(m)
}
return MouseDownEvent(m)
}

47
vendor/github.com/charmbracelet/x/input/input.go generated vendored Normal file
View File

@@ -0,0 +1,47 @@
package input
import (
"fmt"
"strings"
)
var (
// ErrUnknownEvent is returned when an unknown event is encountered.
ErrUnknownEvent = fmt.Errorf("unknown event")
// ErrEmpty is returned when the event buffer is empty.
ErrEmpty = fmt.Errorf("empty event buffer")
)
// Event represents a terminal input event.
type Event interface{}
// UnknownEvent represents an unknown event.
type UnknownEvent string
// String implements fmt.Stringer.
func (e UnknownEvent) String() string {
return fmt.Sprintf("%q", string(e))
}
// WindowSizeEvent represents a window resize event.
type WindowSizeEvent struct {
Width, Height int
}
// String implements fmt.Stringer.
func (e WindowSizeEvent) String() string {
return fmt.Sprintf("resize: %dx%d", e.Width, e.Height)
}
// MultiEvent represents multiple events.
type MultiEvent []Event
// String implements fmt.Stringer.
func (e MultiEvent) String() string {
var sb strings.Builder
for _, ev := range e {
sb.WriteString(fmt.Sprintf("%v\n", ev))
}
return sb.String()
}

410
vendor/github.com/charmbracelet/x/input/key.go generated vendored Normal file
View File

@@ -0,0 +1,410 @@
package input
// KeySym is a keyboard symbol.
type KeySym int
// Symbol constants.
const (
KeyNone KeySym = iota
// Special names in C0
KeyBackspace
KeyTab
KeyEnter
KeyEscape
// Special names in G0
KeySpace
KeyDelete
// Special keys
KeyUp
KeyDown
KeyRight
KeyLeft
KeyBegin
KeyFind
KeyInsert
KeySelect
KeyPgUp
KeyPgDown
KeyHome
KeyEnd
// Keypad keys
KeyKpEnter
KeyKpEqual
KeyKpMultiply
KeyKpPlus
KeyKpComma
KeyKpMinus
KeyKpDecimal
KeyKpDivide
KeyKp0
KeyKp1
KeyKp2
KeyKp3
KeyKp4
KeyKp5
KeyKp6
KeyKp7
KeyKp8
KeyKp9
// The following are keys defined in the Kitty keyboard protocol.
// TODO: Investigate the names of these keys
KeyKpSep
KeyKpUp
KeyKpDown
KeyKpLeft
KeyKpRight
KeyKpPgUp
KeyKpPgDown
KeyKpHome
KeyKpEnd
KeyKpInsert
KeyKpDelete
KeyKpBegin
// Function keys
KeyF1
KeyF2
KeyF3
KeyF4
KeyF5
KeyF6
KeyF7
KeyF8
KeyF9
KeyF10
KeyF11
KeyF12
KeyF13
KeyF14
KeyF15
KeyF16
KeyF17
KeyF18
KeyF19
KeyF20
KeyF21
KeyF22
KeyF23
KeyF24
KeyF25
KeyF26
KeyF27
KeyF28
KeyF29
KeyF30
KeyF31
KeyF32
KeyF33
KeyF34
KeyF35
KeyF36
KeyF37
KeyF38
KeyF39
KeyF40
KeyF41
KeyF42
KeyF43
KeyF44
KeyF45
KeyF46
KeyF47
KeyF48
KeyF49
KeyF50
KeyF51
KeyF52
KeyF53
KeyF54
KeyF55
KeyF56
KeyF57
KeyF58
KeyF59
KeyF60
KeyF61
KeyF62
KeyF63
// The following are keys defined in the Kitty keyboard protocol.
// TODO: Investigate the names of these keys
KeyCapsLock
KeyScrollLock
KeyNumLock
KeyPrintScreen
KeyPause
KeyMenu
KeyMediaPlay
KeyMediaPause
KeyMediaPlayPause
KeyMediaReverse
KeyMediaStop
KeyMediaFastForward
KeyMediaRewind
KeyMediaNext
KeyMediaPrev
KeyMediaRecord
KeyLowerVol
KeyRaiseVol
KeyMute
KeyLeftShift
KeyLeftAlt
KeyLeftCtrl
KeyLeftSuper
KeyLeftHyper
KeyLeftMeta
KeyRightShift
KeyRightAlt
KeyRightCtrl
KeyRightSuper
KeyRightHyper
KeyRightMeta
KeyIsoLevel3Shift
KeyIsoLevel5Shift
)
// Key represents a key event.
type Key struct {
Sym KeySym
Rune rune
AltRune rune
BaseRune rune
IsRepeat bool
Mod KeyMod
}
// KeyDownEvent represents a key down event.
type KeyDownEvent Key
// String implements fmt.Stringer.
func (k KeyDownEvent) String() string {
return Key(k).String()
}
// KeyUpEvent represents a key up event.
type KeyUpEvent Key
// String implements fmt.Stringer.
func (k KeyUpEvent) String() string {
return Key(k).String()
}
// String implements fmt.Stringer.
func (k Key) String() string {
var s string
if k.Mod.IsCtrl() && k.Sym != KeyLeftCtrl && k.Sym != KeyRightCtrl {
s += "ctrl+"
}
if k.Mod.IsAlt() && k.Sym != KeyLeftAlt && k.Sym != KeyRightAlt {
s += "alt+"
}
if k.Mod.IsShift() && k.Sym != KeyLeftShift && k.Sym != KeyRightShift {
s += "shift+"
}
if k.Mod.IsMeta() && k.Sym != KeyLeftMeta && k.Sym != KeyRightMeta {
s += "meta+"
}
if k.Mod.IsHyper() && k.Sym != KeyLeftHyper && k.Sym != KeyRightHyper {
s += "hyper+"
}
if k.Mod.IsSuper() && k.Sym != KeyLeftSuper && k.Sym != KeyRightSuper {
s += "super+"
}
runeStr := func(r rune) string {
// Space is the only invisible printable character.
if r == ' ' {
return "space"
}
return string(r)
}
if k.BaseRune != 0 {
// If a BaseRune is present, use it to represent a key using the standard
// PC-101 key layout.
s += runeStr(k.BaseRune)
} else if k.AltRune != 0 {
// Otherwise, use the AltRune aka the non-shifted one if present.
s += runeStr(k.AltRune)
} else if k.Rune != 0 {
// Else, just print the rune.
s += runeStr(k.Rune)
} else {
s += k.Sym.String()
}
return s
}
// String implements fmt.Stringer.
func (k KeySym) String() string {
s, ok := keySymString[k]
if !ok {
return "unknown"
}
return s
}
var keySymString = map[KeySym]string{
KeyEnter: "enter",
KeyTab: "tab",
KeyBackspace: "backspace",
KeyEscape: "esc",
KeySpace: "space",
KeyUp: "up",
KeyDown: "down",
KeyLeft: "left",
KeyRight: "right",
KeyBegin: "begin",
KeyFind: "find",
KeyInsert: "insert",
KeyDelete: "delete",
KeySelect: "select",
KeyPgUp: "pgup",
KeyPgDown: "pgdown",
KeyHome: "home",
KeyEnd: "end",
KeyKpEnter: "kpenter",
KeyKpEqual: "kpequal",
KeyKpMultiply: "kpmul",
KeyKpPlus: "kpplus",
KeyKpComma: "kpcomma",
KeyKpMinus: "kpminus",
KeyKpDecimal: "kpperiod",
KeyKpDivide: "kpdiv",
KeyKp0: "kp0",
KeyKp1: "kp1",
KeyKp2: "kp2",
KeyKp3: "kp3",
KeyKp4: "kp4",
KeyKp5: "kp5",
KeyKp6: "kp6",
KeyKp7: "kp7",
KeyKp8: "kp8",
KeyKp9: "kp9",
// Kitty keyboard extension
KeyKpSep: "kpsep",
KeyKpUp: "kpup",
KeyKpDown: "kpdown",
KeyKpLeft: "kpleft",
KeyKpRight: "kpright",
KeyKpPgUp: "kppgup",
KeyKpPgDown: "kppgdown",
KeyKpHome: "kphome",
KeyKpEnd: "kpend",
KeyKpInsert: "kpinsert",
KeyKpDelete: "kpdelete",
KeyKpBegin: "kpbegin",
KeyF1: "f1",
KeyF2: "f2",
KeyF3: "f3",
KeyF4: "f4",
KeyF5: "f5",
KeyF6: "f6",
KeyF7: "f7",
KeyF8: "f8",
KeyF9: "f9",
KeyF10: "f10",
KeyF11: "f11",
KeyF12: "f12",
KeyF13: "f13",
KeyF14: "f14",
KeyF15: "f15",
KeyF16: "f16",
KeyF17: "f17",
KeyF18: "f18",
KeyF19: "f19",
KeyF20: "f20",
KeyF21: "f21",
KeyF22: "f22",
KeyF23: "f23",
KeyF24: "f24",
KeyF25: "f25",
KeyF26: "f26",
KeyF27: "f27",
KeyF28: "f28",
KeyF29: "f29",
KeyF30: "f30",
KeyF31: "f31",
KeyF32: "f32",
KeyF33: "f33",
KeyF34: "f34",
KeyF35: "f35",
KeyF36: "f36",
KeyF37: "f37",
KeyF38: "f38",
KeyF39: "f39",
KeyF40: "f40",
KeyF41: "f41",
KeyF42: "f42",
KeyF43: "f43",
KeyF44: "f44",
KeyF45: "f45",
KeyF46: "f46",
KeyF47: "f47",
KeyF48: "f48",
KeyF49: "f49",
KeyF50: "f50",
KeyF51: "f51",
KeyF52: "f52",
KeyF53: "f53",
KeyF54: "f54",
KeyF55: "f55",
KeyF56: "f56",
KeyF57: "f57",
KeyF58: "f58",
KeyF59: "f59",
KeyF60: "f60",
KeyF61: "f61",
KeyF62: "f62",
KeyF63: "f63",
// Kitty keyboard extension
KeyCapsLock: "capslock",
KeyScrollLock: "scrolllock",
KeyNumLock: "numlock",
KeyPrintScreen: "printscreen",
KeyPause: "pause",
KeyMenu: "menu",
KeyMediaPlay: "mediaplay",
KeyMediaPause: "mediapause",
KeyMediaPlayPause: "mediaplaypause",
KeyMediaReverse: "mediareverse",
KeyMediaStop: "mediastop",
KeyMediaFastForward: "mediafastforward",
KeyMediaRewind: "mediarewind",
KeyMediaNext: "medianext",
KeyMediaPrev: "mediaprev",
KeyMediaRecord: "mediarecord",
KeyLowerVol: "lowervol",
KeyRaiseVol: "raisevol",
KeyMute: "mute",
KeyLeftShift: "leftshift",
KeyLeftAlt: "leftalt",
KeyLeftCtrl: "leftctrl",
KeyLeftSuper: "leftsuper",
KeyLeftHyper: "lefthyper",
KeyLeftMeta: "leftmeta",
KeyRightShift: "rightshift",
KeyRightAlt: "rightalt",
KeyRightCtrl: "rightctrl",
KeyRightSuper: "rightsuper",
KeyRightHyper: "righthyper",
KeyRightMeta: "rightmeta",
KeyIsoLevel3Shift: "isolevel3shift",
KeyIsoLevel5Shift: "isolevel5shift",
}

281
vendor/github.com/charmbracelet/x/input/kitty.go generated vendored Normal file
View File

@@ -0,0 +1,281 @@
package input
import (
"unicode"
"unicode/utf8"
"github.com/charmbracelet/x/ansi"
)
// KittyKeyboardEvent represents Kitty keyboard progressive enhancement flags.
type KittyKeyboardEvent int
// IsDisambiguateEscapeCodes returns true if the DisambiguateEscapeCodes flag is set.
func (e KittyKeyboardEvent) IsDisambiguateEscapeCodes() bool {
return e&ansi.KittyDisambiguateEscapeCodes != 0
}
// IsReportEventTypes returns true if the ReportEventTypes flag is set.
func (e KittyKeyboardEvent) IsReportEventTypes() bool {
return e&ansi.KittyReportEventTypes != 0
}
// IsReportAlternateKeys returns true if the ReportAlternateKeys flag is set.
func (e KittyKeyboardEvent) IsReportAlternateKeys() bool {
return e&ansi.KittyReportAlternateKeys != 0
}
// IsReportAllKeys returns true if the ReportAllKeys flag is set.
func (e KittyKeyboardEvent) IsReportAllKeys() bool {
return e&ansi.KittyReportAllKeys != 0
}
// IsReportAssociatedKeys returns true if the ReportAssociatedKeys flag is set.
func (e KittyKeyboardEvent) IsReportAssociatedKeys() bool {
return e&ansi.KittyReportAssociatedKeys != 0
}
// Kitty Clipboard Control Sequences
var kittyKeyMap = map[int]KeySym{
ansi.BS: KeyBackspace,
ansi.HT: KeyTab,
ansi.CR: KeyEnter,
ansi.ESC: KeyEscape,
ansi.DEL: KeyBackspace,
57344: KeyEscape,
57345: KeyEnter,
57346: KeyTab,
57347: KeyBackspace,
57348: KeyInsert,
57349: KeyDelete,
57350: KeyLeft,
57351: KeyRight,
57352: KeyUp,
57353: KeyDown,
57354: KeyPgUp,
57355: KeyPgDown,
57356: KeyHome,
57357: KeyEnd,
57358: KeyCapsLock,
57359: KeyScrollLock,
57360: KeyNumLock,
57361: KeyPrintScreen,
57362: KeyPause,
57363: KeyMenu,
57364: KeyF1,
57365: KeyF2,
57366: KeyF3,
57367: KeyF4,
57368: KeyF5,
57369: KeyF6,
57370: KeyF7,
57371: KeyF8,
57372: KeyF9,
57373: KeyF10,
57374: KeyF11,
57375: KeyF12,
57376: KeyF13,
57377: KeyF14,
57378: KeyF15,
57379: KeyF16,
57380: KeyF17,
57381: KeyF18,
57382: KeyF19,
57383: KeyF20,
57384: KeyF21,
57385: KeyF22,
57386: KeyF23,
57387: KeyF24,
57388: KeyF25,
57389: KeyF26,
57390: KeyF27,
57391: KeyF28,
57392: KeyF29,
57393: KeyF30,
57394: KeyF31,
57395: KeyF32,
57396: KeyF33,
57397: KeyF34,
57398: KeyF35,
57399: KeyKp0,
57400: KeyKp1,
57401: KeyKp2,
57402: KeyKp3,
57403: KeyKp4,
57404: KeyKp5,
57405: KeyKp6,
57406: KeyKp7,
57407: KeyKp8,
57408: KeyKp9,
57409: KeyKpDecimal,
57410: KeyKpDivide,
57411: KeyKpMultiply,
57412: KeyKpMinus,
57413: KeyKpPlus,
57414: KeyKpEnter,
57415: KeyKpEqual,
57416: KeyKpSep,
57417: KeyKpLeft,
57418: KeyKpRight,
57419: KeyKpUp,
57420: KeyKpDown,
57421: KeyKpPgUp,
57422: KeyKpPgDown,
57423: KeyKpHome,
57424: KeyKpEnd,
57425: KeyKpInsert,
57426: KeyKpDelete,
57427: KeyKpBegin,
57428: KeyMediaPlay,
57429: KeyMediaPause,
57430: KeyMediaPlayPause,
57431: KeyMediaReverse,
57432: KeyMediaStop,
57433: KeyMediaFastForward,
57434: KeyMediaRewind,
57435: KeyMediaNext,
57436: KeyMediaPrev,
57437: KeyMediaRecord,
57438: KeyLowerVol,
57439: KeyRaiseVol,
57440: KeyMute,
57441: KeyLeftShift,
57442: KeyLeftCtrl,
57443: KeyLeftAlt,
57444: KeyLeftSuper,
57445: KeyLeftHyper,
57446: KeyLeftMeta,
57447: KeyRightShift,
57448: KeyRightCtrl,
57449: KeyRightAlt,
57450: KeyRightSuper,
57451: KeyRightHyper,
57452: KeyRightMeta,
57453: KeyIsoLevel3Shift,
57454: KeyIsoLevel5Shift,
}
const (
kittyShift = 1 << iota
kittyAlt
kittyCtrl
kittySuper
kittyHyper
kittyMeta
kittyCapsLock
kittyNumLock
)
func fromKittyMod(mod int) KeyMod {
var m KeyMod
if mod&kittyShift != 0 {
m |= Shift
}
if mod&kittyAlt != 0 {
m |= Alt
}
if mod&kittyCtrl != 0 {
m |= Ctrl
}
if mod&kittySuper != 0 {
m |= Super
}
if mod&kittyHyper != 0 {
m |= Hyper
}
if mod&kittyMeta != 0 {
m |= Meta
}
if mod&kittyCapsLock != 0 {
m |= CapsLock
}
if mod&kittyNumLock != 0 {
m |= NumLock
}
return m
}
// parseKittyKeyboard parses a Kitty Keyboard Protocol sequence.
//
// In `CSI u`, this is parsed as:
//
// CSI codepoint ; modifiers u
// codepoint: ASCII Dec value
//
// The Kitty Keyboard Protocol extends this with optional components that can be
// enabled progressively. The full sequence is parsed as:
//
// CSI unicode-key-code:alternate-key-codes ; modifiers:event-type ; text-as-codepoints u
//
// See https://sw.kovidgoyal.net/kitty/keyboard-protocol/
func parseKittyKeyboard(csi *ansi.CsiSequence) Event {
var isRelease bool
key := Key{}
if params := csi.Subparams(0); len(params) > 0 {
code := params[0]
if sym, ok := kittyKeyMap[code]; ok {
key.Sym = sym
} else {
r := rune(code)
if !utf8.ValidRune(r) {
r = utf8.RuneError
}
key.Rune = r
// alternate key reporting
switch len(params) {
case 3:
// shifted key + base key
if b := rune(params[2]); unicode.IsPrint(b) {
// XXX: When alternate key reporting is enabled, the protocol
// can return 3 things, the unicode codepoint of the key,
// the shifted codepoint of the key, and the standard
// PC-101 key layout codepoint.
// This is useful to create an unambiguous mapping of keys
// when using a different language layout.
key.BaseRune = b
}
fallthrough
case 2:
// shifted key
if s := rune(params[1]); unicode.IsPrint(s) {
// XXX: We swap keys here because we want the shifted key
// to be the Rune that is returned by the event.
// For example, shift+a should produce "A" not "a".
// In such a case, we set AltRune to the original key "a"
// and Rune to "A".
key.AltRune = key.Rune
key.Rune = s
}
}
}
}
if params := csi.Subparams(1); len(params) > 0 {
mod := params[0]
if mod > 1 {
key.Mod = fromKittyMod(mod - 1)
}
if len(params) > 1 {
switch params[1] {
case 2:
key.IsRepeat = true
case 3:
isRelease = true
}
}
}
// TODO: Associated keys are not support yet.
// if params := csi.Subparams(2); len(params) > 0 {
// r := rune(params[0])
// if unicode.IsPrint(r) {
// key.AltRune = r
// }
// }
if isRelease {
return KeyUpEvent(key)
}
return KeyDownEvent(key)
}

70
vendor/github.com/charmbracelet/x/input/mod.go generated vendored Normal file
View File

@@ -0,0 +1,70 @@
package input
// KeyMod represents modifier keys.
type KeyMod uint16
// Modifier keys.
const (
Shift KeyMod = 1 << iota
Alt
Ctrl
Meta
// These modifiers are used with the Kitty protocol.
// XXX: Meta and Super are swapped in the Kitty protocol,
// this is to preserve compatibility with XTerm modifiers.
Hyper
Super // Windows/Command keys
// These are key lock states.
CapsLock
NumLock
ScrollLock // Defined in Windows API only
)
// IsShift reports whether the Shift modifier is set.
func (m KeyMod) IsShift() bool {
return m&Shift != 0
}
// IsAlt reports whether the Alt modifier is set.
func (m KeyMod) IsAlt() bool {
return m&Alt != 0
}
// IsCtrl reports whether the Ctrl modifier is set.
func (m KeyMod) IsCtrl() bool {
return m&Ctrl != 0
}
// IsMeta reports whether the Meta modifier is set.
func (m KeyMod) IsMeta() bool {
return m&Meta != 0
}
// IsHyper reports whether the Hyper modifier is set.
func (m KeyMod) IsHyper() bool {
return m&Hyper != 0
}
// IsSuper reports whether the Super modifier is set.
func (m KeyMod) IsSuper() bool {
return m&Super != 0
}
// HasCapsLock reports whether the CapsLock key is enabled.
func (m KeyMod) HasCapsLock() bool {
return m&CapsLock != 0
}
// HasNumLock reports whether the NumLock key is enabled.
func (m KeyMod) HasNumLock() bool {
return m&NumLock != 0
}
// HasScrollLock reports whether the ScrollLock key is enabled.
func (m KeyMod) HasScrollLock() bool {
return m&ScrollLock != 0
}

12
vendor/github.com/charmbracelet/x/input/mode.go generated vendored Normal file
View File

@@ -0,0 +1,12 @@
package input
// ReportModeEvent represents a report mode event for sequence DECRPM.
//
// See: https://vt100.net/docs/vt510-rm/DECRPM.html
type ReportModeEvent struct {
// Mode is the mode number.
Mode int
// Value is the mode value.
Value int
}

248
vendor/github.com/charmbracelet/x/input/mouse.go generated vendored Normal file
View File

@@ -0,0 +1,248 @@
package input
import (
"regexp"
"github.com/charmbracelet/x/ansi"
)
// MouseButton represents the button that was pressed during a mouse event.
type MouseButton byte
// Mouse event buttons
//
// This is based on X11 mouse button codes.
//
// 1 = left button
// 2 = middle button (pressing the scroll wheel)
// 3 = right button
// 4 = turn scroll wheel up
// 5 = turn scroll wheel down
// 6 = push scroll wheel left
// 7 = push scroll wheel right
// 8 = 4th button (aka browser backward button)
// 9 = 5th button (aka browser forward button)
// 10
// 11
//
// Other buttons are not supported.
const (
MouseNone MouseButton = iota
MouseLeft
MouseMiddle
MouseRight
MouseWheelUp
MouseWheelDown
MouseWheelLeft
MouseWheelRight
MouseBackward
MouseForward
MouseExtra1
MouseExtra2
)
var mouseButtons = map[MouseButton]string{
MouseNone: "none",
MouseLeft: "left",
MouseMiddle: "middle",
MouseRight: "right",
MouseWheelUp: "wheelup",
MouseWheelDown: "wheeldown",
MouseWheelLeft: "wheelleft",
MouseWheelRight: "wheelright",
MouseBackward: "backward",
MouseForward: "forward",
MouseExtra1: "button10",
MouseExtra2: "button11",
}
// Mouse represents a Mouse event.
type Mouse struct {
X, Y int
Button MouseButton
Mod KeyMod
}
// String implements fmt.Stringer.
func (m Mouse) String() (s string) {
if m.Mod.IsCtrl() {
s += "ctrl+"
}
if m.Mod.IsAlt() {
s += "alt+"
}
if m.Mod.IsShift() {
s += "shift+"
}
str, ok := mouseButtons[m.Button]
if !ok {
s += "unknown"
} else if str != "none" { // motion events don't have a button
s += str
}
return s
}
// MouseDownEvent represents a mouse button down event.
type MouseDownEvent Mouse
// String implements fmt.Stringer.
func (e MouseDownEvent) String() string {
return Mouse(e).String()
}
// MouseUpEvent represents a mouse button up event.
type MouseUpEvent Mouse
// String implements fmt.Stringer.
func (e MouseUpEvent) String() string {
return Mouse(e).String()
}
// MouseWheelEvent represents a mouse wheel event.
type MouseWheelEvent Mouse
// String implements fmt.Stringer.
func (e MouseWheelEvent) String() string {
return Mouse(e).String()
}
// MouseMotionEvent represents a mouse motion event.
type MouseMotionEvent Mouse
// String implements fmt.Stringer.
func (e MouseMotionEvent) String() string {
m := Mouse(e)
if m.Button != 0 {
return m.String() + "+motion"
}
return m.String() + "motion"
}
var mouseSGRRegex = regexp.MustCompile(`(\d+);(\d+);(\d+)([Mm])`)
// Parse SGR-encoded mouse events; SGR extended mouse events. SGR mouse events
// look like:
//
// ESC [ < Cb ; Cx ; Cy (M or m)
//
// where:
//
// Cb is the encoded button code
// Cx is the x-coordinate of the mouse
// Cy is the y-coordinate of the mouse
// M is for button press, m is for button release
//
// https://invisible-island.net/xterm/ctlseqs/ctlseqs.html#h3-Extended-coordinates
func parseSGRMouseEvent(csi *ansi.CsiSequence) Event {
x := csi.Param(1)
y := csi.Param(2)
release := csi.Command() == 'm'
mod, btn, _, isMotion := parseMouseButton(csi.Param(0))
// (1,1) is the upper left. We subtract 1 to normalize it to (0,0).
x--
y--
m := Mouse{X: x, Y: y, Button: btn, Mod: mod}
// Wheel buttons don't have release events
// Motion can be reported as a release event in some terminals (Windows Terminal)
if isWheel(m.Button) {
return MouseWheelEvent(m)
} else if !isMotion && release {
return MouseUpEvent(m)
} else if isMotion {
return MouseMotionEvent(m)
}
return MouseDownEvent(m)
}
const x10MouseByteOffset = 32
// Parse X10-encoded mouse events; the simplest kind. The last release of X10
// was December 1986, by the way. The original X10 mouse protocol limits the Cx
// and Cy coordinates to 223 (=255-032).
//
// X10 mouse events look like:
//
// ESC [M Cb Cx Cy
//
// See: http://www.xfree86.org/current/ctlseqs.html#Mouse%20Tracking
func parseX10MouseEvent(buf []byte) Event {
v := buf[3:6]
b := int(v[0])
if b >= x10MouseByteOffset {
// XXX: b < 32 should be impossible, but we're being defensive.
b -= x10MouseByteOffset
}
mod, btn, isRelease, isMotion := parseMouseButton(b)
// (1,1) is the upper left. We subtract 1 to normalize it to (0,0).
x := int(v[1]) - x10MouseByteOffset - 1
y := int(v[2]) - x10MouseByteOffset - 1
m := Mouse{X: x, Y: y, Button: btn, Mod: mod}
if isWheel(m.Button) {
return MouseWheelEvent(m)
} else if isMotion {
return MouseMotionEvent(m)
} else if isRelease {
return MouseUpEvent(m)
}
return MouseDownEvent(m)
}
// See: https://invisible-island.net/xterm/ctlseqs/ctlseqs.html#h3-Extended-coordinates
func parseMouseButton(b int) (mod KeyMod, btn MouseButton, isRelease bool, isMotion bool) {
// mouse bit shifts
const (
bitShift = 0b0000_0100
bitAlt = 0b0000_1000
bitCtrl = 0b0001_0000
bitMotion = 0b0010_0000
bitWheel = 0b0100_0000
bitAdd = 0b1000_0000 // additional buttons 8-11
bitsMask = 0b0000_0011
)
// Modifiers
if b&bitAlt != 0 {
mod |= Alt
}
if b&bitCtrl != 0 {
mod |= Ctrl
}
if b&bitShift != 0 {
mod |= Shift
}
if b&bitAdd != 0 {
btn = MouseBackward + MouseButton(b&bitsMask)
} else if b&bitWheel != 0 {
btn = MouseWheelUp + MouseButton(b&bitsMask)
} else {
btn = MouseLeft + MouseButton(b&bitsMask)
// X10 reports a button release as 0b0000_0011 (3)
if b&bitsMask == bitsMask {
btn = MouseNone
isRelease = true
}
}
// Motion bit doesn't get reported for wheel events.
if b&bitMotion != 0 && !isWheel(btn) {
isMotion = true
}
return
}
// isWheel returns true if the mouse event is a wheel event.
func isWheel(btn MouseButton) bool {
return btn >= MouseWheelUp && btn <= MouseWheelRight
}

836
vendor/github.com/charmbracelet/x/input/parse.go generated vendored Normal file
View File

@@ -0,0 +1,836 @@
package input
import (
"encoding/base64"
"strings"
"unicode/utf8"
"github.com/charmbracelet/x/ansi"
"github.com/charmbracelet/x/ansi/parser"
"github.com/erikgeiser/coninput"
)
// Flags to control the behavior of the parser.
const (
// 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 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 will use Terminfo databases to
// overwrite the default key sequences.
FlagTerminfo
// 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
)
var flags int
// SetFlags sets the flags for the parser.
// This will control the behavior of ParseSequence.
func SetFlags(f int) {
flags = f
}
// ParseSequence finds the first recognized event sequence and returns it along
// with its length.
//
// It will return zero and nil no sequence is recognized or when the buffer is
// empty. If a sequence is not supported, an UnknownEvent is returned.
func ParseSequence(buf []byte) (n int, e Event) {
if len(buf) == 0 {
return 0, nil
}
switch b := buf[0]; b {
case ansi.ESC:
if len(buf) == 1 {
// Escape key
return 1, KeyDownEvent{Sym: KeyEscape}
}
switch b := buf[1]; b {
case 'O': // Esc-prefixed SS3
return parseSs3(buf)
case 'P': // Esc-prefixed DCS
return parseDcs(buf)
case '[': // Esc-prefixed CSI
return parseCsi(buf)
case ']': // Esc-prefixed OSC
return parseOsc(buf)
case '_': // Esc-prefixed APC
return parseApc(buf)
default:
n, e := ParseSequence(buf[1:])
if k, ok := e.(KeyDownEvent); ok && !k.Mod.IsAlt() {
k.Mod |= Alt
return n + 1, k
}
// Not a key sequence, nor an alt modified key sequence. In that
// case, just report a single escape key.
return 1, KeyDownEvent{Sym: KeyEscape}
}
case ansi.SS3:
return parseSs3(buf)
case ansi.DCS:
return parseDcs(buf)
case ansi.CSI:
return parseCsi(buf)
case ansi.OSC:
return parseOsc(buf)
case ansi.APC:
return parseApc(buf)
default:
if b <= ansi.US || b == ansi.DEL || b == ansi.SP {
return 1, parseControl(b)
} else if b >= ansi.PAD && b <= ansi.APC {
// C1 control code
// UTF-8 never starts with a C1 control code
// Encode these as Ctrl+Alt+<code - 0x40>
return 1, KeyDownEvent{Rune: rune(b) - 0x40, Mod: Ctrl | Alt}
}
return parseUtf8(buf)
}
}
func parseCsi(b []byte) (int, Event) {
if len(b) == 2 && b[0] == ansi.ESC {
// short cut if this is an alt+[ key
return 2, KeyDownEvent{Rune: rune(b[1]), Mod: Alt}
}
var csi ansi.CsiSequence
var params [parser.MaxParamsSize]int
var paramsLen int
var i int
if b[i] == ansi.CSI || b[i] == ansi.ESC {
i++
}
if i < len(b) && b[i-1] == ansi.ESC && b[i] == '[' {
i++
}
// Initial CSI byte
if i < len(b) && b[i] >= '<' && b[i] <= '?' {
csi.Cmd |= int(b[i]) << parser.MarkerShift
}
// Scan parameter bytes in the range 0x30-0x3F
var j int
for j = 0; i < len(b) && paramsLen < len(params) && b[i] >= 0x30 && b[i] <= 0x3F; i, j = i+1, j+1 {
if b[i] >= '0' && b[i] <= '9' {
if params[paramsLen] == parser.MissingParam {
params[paramsLen] = 0
}
params[paramsLen] *= 10
params[paramsLen] += int(b[i]) - '0'
}
if b[i] == ':' {
params[paramsLen] |= parser.HasMoreFlag
}
if b[i] == ';' || b[i] == ':' {
paramsLen++
if paramsLen < len(params) {
// Don't overflow the params slice
params[paramsLen] = parser.MissingParam
}
}
}
if j > 0 && paramsLen < len(params) {
// has parameters
paramsLen++
}
// Scan intermediate bytes in the range 0x20-0x2F
var intermed byte
for ; i < len(b) && b[i] >= 0x20 && b[i] <= 0x2F; i++ {
intermed = b[i]
}
// Set the intermediate byte
csi.Cmd |= int(intermed) << parser.IntermedShift
// Scan final byte in the range 0x40-0x7E
if i >= len(b) || b[i] < 0x40 || b[i] > 0x7E {
// Special case for URxvt keys
// CSI <number> $ is an invalid sequence, but URxvt uses it for
// shift modified keys.
if b[i-1] == '$' {
n, ev := parseCsi(append(b[:i-1], '~'))
if k, ok := ev.(KeyDownEvent); ok {
k.Mod |= Shift
return n, k
}
}
return i, UnknownEvent(b[:i-1])
}
// Add the final byte
csi.Cmd |= int(b[i])
i++
csi.Params = params[:paramsLen]
marker, cmd := csi.Marker(), csi.Command()
switch marker {
case '?':
switch cmd {
case 'y':
switch intermed {
case '$':
// Report Mode (DECRPM)
if paramsLen != 2 {
return i, UnknownCsiEvent(b[:i])
}
return i, ReportModeEvent{Mode: csi.Param(0), Value: csi.Param(1)}
}
case 'c':
// Primary Device Attributes
return i, parsePrimaryDevAttrs(&csi)
case 'u':
// Kitty keyboard flags
if param := csi.Param(0); param != -1 {
return i, KittyKeyboardEvent(param)
}
case 'R':
// This report may return a third parameter representing the page
// number, but we don't really need it.
if paramsLen >= 2 {
return i, CursorPositionEvent{Row: csi.Param(0), Column: csi.Param(1)}
}
}
return i, UnknownCsiEvent(b[:i])
case '<':
switch cmd {
case 'm', 'M':
// Handle SGR mouse
if paramsLen != 3 {
return i, UnknownCsiEvent(b[:i])
}
return i, parseSGRMouseEvent(&csi)
default:
return i, UnknownCsiEvent(b[:i])
}
case '>':
switch cmd {
case 'm':
// XTerm modifyOtherKeys
if paramsLen != 2 || csi.Param(0) != 4 {
return i, UnknownCsiEvent(b[:i])
}
return i, ModifyOtherKeysEvent(csi.Param(1))
default:
return i, UnknownCsiEvent(b[:i])
}
case '=':
// We don't support any of these yet
return i, UnknownCsiEvent(b[:i])
}
switch cmd := csi.Command(); cmd {
case 'R':
// Cursor position report OR modified F3
if paramsLen == 0 {
return i, KeyDownEvent{Sym: KeyF3}
} else if paramsLen != 2 {
break
}
// XXX: We cannot differentiate between cursor position report and
// CSI 1 ; <mod> R (which is modified F3) when the cursor is at the
// row 1. In this case, we report a modified F3 event since it's more
// likely to be the case than the cursor being at the first row.
//
// For a non ambiguous cursor position report, use
// [ansi.RequestExtendedCursorPosition] (DECXCPR) instead.
if csi.Param(0) != 1 {
return i, CursorPositionEvent{Row: csi.Param(0), Column: csi.Param(1)}
}
fallthrough
case 'a', 'b', 'c', 'd', 'A', 'B', 'C', 'D', 'E', 'F', 'H', 'P', 'Q', 'S', 'Z':
var k KeyDownEvent
switch cmd {
case 'a', 'b', 'c', 'd':
k = KeyDownEvent{Sym: KeyUp + KeySym(cmd-'a'), Mod: Shift}
case 'A', 'B', 'C', 'D':
k = KeyDownEvent{Sym: KeyUp + KeySym(cmd-'A')}
case 'E':
k = KeyDownEvent{Sym: KeyBegin}
case 'F':
k = KeyDownEvent{Sym: KeyEnd}
case 'H':
k = KeyDownEvent{Sym: KeyHome}
case 'P', 'Q', 'R', 'S':
k = KeyDownEvent{Sym: KeyF1 + KeySym(cmd-'P')}
case 'Z':
k = KeyDownEvent{Sym: KeyTab, Mod: Shift}
}
if paramsLen > 1 && csi.Param(0) == 1 {
// CSI 1 ; <modifiers> A
if paramsLen > 1 {
k.Mod |= KeyMod(csi.Param(1) - 1)
}
}
return i, k
case 'M':
// Handle X10 mouse
if i+3 > len(b) {
return i, UnknownCsiEvent(b[:i])
}
return i + 3, parseX10MouseEvent(append(b[:i], b[i:i+3]...))
case 'y':
// Report Mode (DECRPM)
if paramsLen != 2 {
return i, UnknownCsiEvent(b[:i])
}
return i, ReportModeEvent{Mode: csi.Param(0), Value: csi.Param(1)}
case 'u':
// Kitty keyboard protocol & CSI u (fixterms)
if paramsLen == 0 {
return i, UnknownCsiEvent(b[:i])
}
return i, parseKittyKeyboard(&csi)
case '_':
// Win32 Input Mode
if paramsLen != 6 {
return i, UnknownCsiEvent(b[:i])
}
rc := uint16(csi.Param(5))
if rc == 0 {
rc = 1
}
event := parseWin32InputKeyEvent(
coninput.VirtualKeyCode(csi.Param(0)), // Vk wVirtualKeyCode
coninput.VirtualKeyCode(csi.Param(1)), // Sc wVirtualScanCode
rune(csi.Param(2)), // Uc UnicodeChar
csi.Param(3) == 1, // Kd bKeyDown
coninput.ControlKeyState(csi.Param(4)), // Cs dwControlKeyState
rc, // Rc wRepeatCount
)
if event == nil {
return i, UnknownCsiEvent(b[:])
}
return i, event
case '@', '^', '~':
if paramsLen == 0 {
return i, UnknownCsiEvent(b[:i])
}
param := csi.Param(0)
switch cmd {
case '~':
switch param {
case 27:
// XTerm modifyOtherKeys 2
if paramsLen != 3 {
return i, UnknownCsiEvent(b[:i])
}
return i, parseXTermModifyOtherKeys(&csi)
case 200:
// bracketed-paste start
return i, PasteStartEvent{}
case 201:
// bracketed-paste end
return i, PasteEndEvent{}
}
}
switch param {
case 1, 2, 3, 4, 5, 6, 7, 8:
fallthrough
case 11, 12, 13, 14, 15:
fallthrough
case 17, 18, 19, 20, 21, 23, 24, 25, 26:
fallthrough
case 28, 29, 31, 32, 33, 34:
var k KeyDownEvent
switch param {
case 1:
if flags&FlagFind != 0 {
k = KeyDownEvent{Sym: KeyFind}
} else {
k = KeyDownEvent{Sym: KeyHome}
}
case 2:
k = KeyDownEvent{Sym: KeyInsert}
case 3:
k = KeyDownEvent{Sym: KeyDelete}
case 4:
if flags&FlagSelect != 0 {
k = KeyDownEvent{Sym: KeySelect}
} else {
k = KeyDownEvent{Sym: KeyEnd}
}
case 5:
k = KeyDownEvent{Sym: KeyPgUp}
case 6:
k = KeyDownEvent{Sym: KeyPgDown}
case 7:
k = KeyDownEvent{Sym: KeyHome}
case 8:
k = KeyDownEvent{Sym: KeyEnd}
case 11, 12, 13, 14, 15:
k = KeyDownEvent{Sym: KeyF1 + KeySym(param-11)}
case 17, 18, 19, 20, 21:
k = KeyDownEvent{Sym: KeyF6 + KeySym(param-17)}
case 23, 24, 25, 26:
k = KeyDownEvent{Sym: KeyF11 + KeySym(param-23)}
case 28, 29:
k = KeyDownEvent{Sym: KeyF15 + KeySym(param-28)}
case 31, 32, 33, 34:
k = KeyDownEvent{Sym: KeyF17 + KeySym(param-31)}
}
// modifiers
if paramsLen > 1 {
k.Mod |= KeyMod(csi.Param(1) - 1)
}
// Handle URxvt weird keys
switch cmd {
case '^':
k.Mod |= Ctrl
case '@':
k.Mod |= Ctrl | Shift
}
return i, k
}
}
return i, UnknownCsiEvent(b[:i])
}
// parseSs3 parses a SS3 sequence.
// See https://vt100.net/docs/vt220-rm/chapter4.html#S4.4.4.2
func parseSs3(b []byte) (int, Event) {
if len(b) == 2 && b[0] == ansi.ESC {
// short cut if this is an alt+O key
return 2, KeyDownEvent{Rune: rune(b[1]), Mod: Alt}
}
var i int
if b[i] == ansi.SS3 || b[i] == ansi.ESC {
i++
}
if i < len(b) && b[i-1] == ansi.ESC && b[i] == 'O' {
i++
}
// Scan numbers from 0-9
var mod int
for ; i < len(b) && b[i] >= '0' && b[i] <= '9'; i++ {
mod *= 10
mod += int(b[i]) - '0'
}
// Scan a GL character
// A GL character is a single byte in the range 0x21-0x7E
// See https://vt100.net/docs/vt220-rm/chapter2.html#S2.3.2
if i >= len(b) || b[i] < 0x21 || b[i] > 0x7E {
return i, UnknownEvent(b[:i])
}
// GL character(s)
gl := b[i]
i++
var k KeyDownEvent
switch gl {
case 'a', 'b', 'c', 'd':
k = KeyDownEvent{Sym: KeyUp + KeySym(gl-'a'), Mod: Ctrl}
case 'A', 'B', 'C', 'D':
k = KeyDownEvent{Sym: KeyUp + KeySym(gl-'A')}
case 'E':
k = KeyDownEvent{Sym: KeyBegin}
case 'F':
k = KeyDownEvent{Sym: KeyEnd}
case 'H':
k = KeyDownEvent{Sym: KeyHome}
case 'P', 'Q', 'R', 'S':
k = KeyDownEvent{Sym: KeyF1 + KeySym(gl-'P')}
case 'M':
k = KeyDownEvent{Sym: KeyKpEnter}
case 'X':
k = KeyDownEvent{Sym: KeyKpEqual}
case 'j', 'k', 'l', 'm', 'n', 'o', 'p', 'q', 'r', 's', 't', 'u', 'v', 'w', 'x', 'y':
k = KeyDownEvent{Sym: KeyKpMultiply + KeySym(gl-'j')}
default:
return i, UnknownSs3Event(b[:i])
}
// Handle weird SS3 <modifier> Func
if mod > 0 {
k.Mod |= KeyMod(mod - 1)
}
return i, k
}
func parseOsc(b []byte) (int, Event) {
if len(b) == 2 && b[0] == ansi.ESC {
// short cut if this is an alt+] key
return 2, KeyDownEvent{Rune: rune(b[1]), Mod: Alt}
}
var i int
if b[i] == ansi.OSC || b[i] == ansi.ESC {
i++
}
if i < len(b) && b[i-1] == ansi.ESC && b[i] == ']' {
i++
}
// Parse OSC command
// An OSC sequence is terminated by a BEL, ESC, or ST character
var start, end int
cmd := -1
for ; i < len(b) && b[i] >= '0' && b[i] <= '9'; i++ {
if cmd == -1 {
cmd = 0
} else {
cmd *= 10
}
cmd += int(b[i]) - '0'
}
if i < len(b) && b[i] == ';' {
// mark the start of the sequence data
i++
start = i
}
for ; i < len(b); i++ {
// advance to the end of the sequence
if b[i] == ansi.BEL || b[i] == ansi.ESC || b[i] == ansi.ST {
break
}
}
if i >= len(b) {
return i, UnknownEvent(b[:i])
}
end = i // end of the sequence data
i++
// Check 7-bit ST (string terminator) character
if i < len(b) && b[i-1] == ansi.ESC && b[i] == '\\' {
i++
}
if end <= start {
return i, UnknownOscEvent(b[:i])
}
data := string(b[start:end])
switch cmd {
case 10:
return i, ForegroundColorEvent{xParseColor(data)}
case 11:
return i, BackgroundColorEvent{xParseColor(data)}
case 12:
return i, CursorColorEvent{xParseColor(data)}
case 52:
parts := strings.Split(data, ";")
if len(parts) == 0 {
return i, ClipboardEvent("")
}
b64 := parts[len(parts)-1]
bts, err := base64.StdEncoding.DecodeString(b64)
if err != nil {
return i, ClipboardEvent("")
}
return i, ClipboardEvent(bts)
default:
return i, UnknownOscEvent(b[:i])
}
}
// parseStTerminated parses a control sequence that gets terminated by a ST character.
func parseStTerminated(intro8, intro7 byte) func([]byte) (int, Event) {
return func(b []byte) (int, Event) {
var i int
if b[i] == intro8 || b[i] == ansi.ESC {
i++
}
if i < len(b) && b[i-1] == ansi.ESC && b[i] == intro7 {
i++
}
// Scan control sequence
// Most common control sequence is terminated by a ST character
// ST is a 7-bit string terminator character is (ESC \)
// nolint: revive
for ; i < len(b) && b[i] != ansi.ST && b[i] != ansi.ESC; i++ {
}
if i >= len(b) {
switch intro8 {
case ansi.DCS:
return i, UnknownDcsEvent(b[:i])
case ansi.APC:
return i, UnknownApcEvent(b[:i])
default:
return i, UnknownEvent(b[:i])
}
}
i++
// Check 7-bit ST (string terminator) character
if i < len(b) && b[i-1] == ansi.ESC && b[i] == '\\' {
i++
}
switch intro8 {
case ansi.DCS:
return i, UnknownDcsEvent(b[:i])
case ansi.APC:
return i, UnknownApcEvent(b[:i])
default:
return i, UnknownEvent(b[:i])
}
}
}
func parseDcs(b []byte) (int, Event) {
if len(b) == 2 && b[0] == ansi.ESC {
// short cut if this is an alt+P key
return 2, KeyDownEvent{Rune: rune(b[1]), Mod: Alt}
}
var params [16]int
var paramsLen int
var dcs ansi.DcsSequence
// DCS sequences are introduced by DCS (0x90) or ESC P (0x1b 0x50)
var i int
if b[i] == ansi.DCS || b[i] == ansi.ESC {
i++
}
if i < len(b) && b[i-1] == ansi.ESC && b[i] == 'P' {
i++
}
// initial DCS byte
if i < len(b) && b[i] >= '<' && b[i] <= '?' {
dcs.Cmd |= int(b[i]) << parser.MarkerShift
}
// Scan parameter bytes in the range 0x30-0x3F
var j int
for j = 0; i < len(b) && paramsLen < len(params) && b[i] >= 0x30 && b[i] <= 0x3F; i, j = i+1, j+1 {
if b[i] >= '0' && b[i] <= '9' {
if params[paramsLen] == parser.MissingParam {
params[paramsLen] = 0
}
params[paramsLen] *= 10
params[paramsLen] += int(b[i]) - '0'
}
if b[i] == ':' {
params[paramsLen] |= parser.HasMoreFlag
}
if b[i] == ';' || b[i] == ':' {
paramsLen++
if paramsLen < len(params) {
// Don't overflow the params slice
params[paramsLen] = parser.MissingParam
}
}
}
if j > 0 && paramsLen < len(params) {
// has parameters
paramsLen++
}
// Scan intermediate bytes in the range 0x20-0x2F
var intermed byte
for j := 0; i < len(b) && b[i] >= 0x20 && b[i] <= 0x2F; i, j = i+1, j+1 {
intermed = b[i]
}
// set intermediate byte
dcs.Cmd |= int(intermed) << parser.IntermedShift
// Scan final byte in the range 0x40-0x7E
if i >= len(b) || b[i] < 0x40 || b[i] > 0x7E {
return i, UnknownEvent(b[:i])
}
// Add the final byte
dcs.Cmd |= int(b[i])
i++
start := i // start of the sequence data
for ; i < len(b); i++ {
if b[i] == ansi.ST || b[i] == ansi.ESC {
break
}
}
if i >= len(b) {
return i, UnknownEvent(b[:i])
}
end := i // end of the sequence data
i++
// Check 7-bit ST (string terminator) character
if i < len(b) && b[i-1] == ansi.ESC && b[i] == '\\' {
i++
}
dcs.Params = params[:paramsLen]
switch cmd := dcs.Command(); cmd {
case 'r':
switch dcs.Intermediate() {
case '+':
// XTGETTCAP responses
switch param := dcs.Param(0); param {
case 0, 1:
tc := parseTermcap(b[start:end])
// XXX: some terminals like KiTTY report invalid responses with
// their queries i.e. sending a query for "Tc" using "\x1bP+q5463\x1b\\"
// returns "\x1bP0+r5463\x1b\\".
// The specs says that invalid responses should be in the form of
// DCS 0 + r ST "\x1bP0+r\x1b\\"
//
// See: https://invisible-island.net/xterm/ctlseqs/ctlseqs.html#h3-Operating-System-Commands
tc.IsValid = param == 1
return i, tc
}
}
}
return i, UnknownDcsEvent(b[:i])
}
func parseApc(b []byte) (int, Event) {
if len(b) == 2 && b[0] == ansi.ESC {
// short cut if this is an alt+_ key
return 2, KeyDownEvent{Rune: rune(b[1]), Mod: Alt}
}
// APC sequences are introduced by APC (0x9f) or ESC _ (0x1b 0x5f)
return parseStTerminated(ansi.APC, '_')(b)
}
func parseUtf8(b []byte) (int, Event) {
r, rw := utf8.DecodeRune(b)
if r <= ansi.US || r == ansi.DEL || r == ansi.SP {
// Control codes get handled by parseControl
return 1, parseControl(byte(r))
} else if r == utf8.RuneError {
return 1, UnknownEvent(b[0])
}
return rw, KeyDownEvent{Rune: r}
}
func parseControl(b byte) Event {
switch b {
case ansi.NUL:
if flags&FlagCtrlAt != 0 {
return KeyDownEvent{Rune: '@', Mod: Ctrl}
}
return KeyDownEvent{Rune: ' ', Sym: KeySpace, Mod: Ctrl}
case ansi.BS:
return KeyDownEvent{Rune: 'h', Mod: Ctrl}
case ansi.HT:
if flags&FlagCtrlI != 0 {
return KeyDownEvent{Rune: 'i', Mod: Ctrl}
}
return KeyDownEvent{Sym: KeyTab}
case ansi.CR:
if flags&FlagCtrlM != 0 {
return KeyDownEvent{Rune: 'm', Mod: Ctrl}
}
return KeyDownEvent{Sym: KeyEnter}
case ansi.ESC:
if flags&FlagCtrlOpenBracket != 0 {
return KeyDownEvent{Rune: '[', Mod: Ctrl}
}
return KeyDownEvent{Sym: KeyEscape}
case ansi.DEL:
if flags&FlagBackspace != 0 {
return KeyDownEvent{Sym: KeyDelete}
}
return KeyDownEvent{Sym: KeyBackspace}
case ansi.SP:
return KeyDownEvent{Sym: KeySpace, Rune: ' '}
default:
if b >= ansi.SOH && b <= ansi.SUB {
// Use lower case letters for control codes
return KeyDownEvent{Rune: rune(b + 0x60), Mod: Ctrl}
} else if b >= ansi.FS && b <= ansi.US {
return KeyDownEvent{Rune: rune(b + 0x40), Mod: Ctrl}
}
return UnknownEvent(b)
}
}

12
vendor/github.com/charmbracelet/x/input/paste.go generated vendored Normal file
View File

@@ -0,0 +1,12 @@
package input
// PasteEvent is an event that is emitted when a terminal receives pasted text
// using bracketed-paste.
type PasteEvent string
// PasteStartEvent is an event that is emitted when a terminal enters
// bracketed-paste mode.
type PasteStartEvent struct{}
// PasteEvent is an event that is emitted when a terminal receives pasted text.
type PasteEndEvent struct{}

45
vendor/github.com/charmbracelet/x/input/seq.go generated vendored Normal file
View File

@@ -0,0 +1,45 @@
package input
import (
"fmt"
)
// UnknownCsiEvent represents an unknown CSI sequence event.
type UnknownCsiEvent string
// String implements fmt.Stringer.
func (e UnknownCsiEvent) String() string {
return fmt.Sprintf("%q", string(e))
}
// UnknownOscEvent represents an unknown OSC sequence event.
type UnknownOscEvent string
// String implements fmt.Stringer.
func (e UnknownOscEvent) String() string {
return fmt.Sprintf("%q", string(e))
}
// UnknownDcsEvent represents an unknown DCS sequence event.
type UnknownDcsEvent string
// String implements fmt.Stringer.
func (e UnknownDcsEvent) String() string {
return fmt.Sprintf("%q", string(e))
}
// UnknownApcEvent represents an unknown APC sequence event.
type UnknownApcEvent string
// String implements fmt.Stringer.
func (e UnknownApcEvent) String() string {
return fmt.Sprintf("%q", string(e))
}
// UnknownSs3Event represents an unknown SS3 sequence event.
type UnknownSs3Event string
// String implements fmt.Stringer.
func (e UnknownSs3Event) String() string {
return fmt.Sprintf("%q", string(e))
}

388
vendor/github.com/charmbracelet/x/input/table.go generated vendored Normal file
View File

@@ -0,0 +1,388 @@
package input
import (
"strconv"
"github.com/charmbracelet/x/ansi"
)
func buildKeysTable(flags int, term string) map[string]Key {
nul := Key{Rune: ' ', Sym: KeySpace, Mod: Ctrl} // ctrl+@ or ctrl+space
if flags&FlagCtrlAt != 0 {
nul = Key{Rune: '@', Mod: Ctrl}
}
tab := Key{Sym: KeyTab} // ctrl+i or tab
if flags&FlagCtrlI != 0 {
tab = Key{Rune: 'i', Mod: Ctrl}
}
enter := Key{Sym: KeyEnter} // ctrl+m or enter
if flags&FlagCtrlM != 0 {
enter = Key{Rune: 'm', Mod: Ctrl}
}
esc := Key{Sym: KeyEscape} // ctrl+[ or escape
if flags&FlagCtrlOpenBracket != 0 {
esc = Key{Rune: '[', Mod: Ctrl} // ctrl+[ or escape
}
del := Key{Sym: KeyBackspace}
if flags&FlagBackspace != 0 {
del.Sym = KeyDelete
}
find := Key{Sym: KeyHome}
if flags&FlagFind != 0 {
find.Sym = KeyFind
}
sel := Key{Sym: KeyEnd}
if flags&FlagSelect != 0 {
sel.Sym = KeySelect
}
// The following is a table of key sequences and their corresponding key
// events based on the VT100/VT200 terminal specs.
//
// See: https://vt100.net/docs/vt100-ug/chapter3.html#S3.2
// See: https://vt100.net/docs/vt220-rm/chapter3.html
//
// XXX: These keys may be overwritten by other options like XTerm or
// Terminfo.
table := map[string]Key{
// C0 control characters
string(byte(ansi.NUL)): nul,
string(byte(ansi.SOH)): {Rune: 'a', Mod: Ctrl},
string(byte(ansi.STX)): {Rune: 'b', Mod: Ctrl},
string(byte(ansi.ETX)): {Rune: 'c', Mod: Ctrl},
string(byte(ansi.EOT)): {Rune: 'd', Mod: Ctrl},
string(byte(ansi.ENQ)): {Rune: 'e', Mod: Ctrl},
string(byte(ansi.ACK)): {Rune: 'f', Mod: Ctrl},
string(byte(ansi.BEL)): {Rune: 'g', Mod: Ctrl},
string(byte(ansi.BS)): {Rune: 'h', Mod: Ctrl},
string(byte(ansi.HT)): tab,
string(byte(ansi.LF)): {Rune: 'j', Mod: Ctrl},
string(byte(ansi.VT)): {Rune: 'k', Mod: Ctrl},
string(byte(ansi.FF)): {Rune: 'l', Mod: Ctrl},
string(byte(ansi.CR)): enter,
string(byte(ansi.SO)): {Rune: 'n', Mod: Ctrl},
string(byte(ansi.SI)): {Rune: 'o', Mod: Ctrl},
string(byte(ansi.DLE)): {Rune: 'p', Mod: Ctrl},
string(byte(ansi.DC1)): {Rune: 'q', Mod: Ctrl},
string(byte(ansi.DC2)): {Rune: 'r', Mod: Ctrl},
string(byte(ansi.DC3)): {Rune: 's', Mod: Ctrl},
string(byte(ansi.DC4)): {Rune: 't', Mod: Ctrl},
string(byte(ansi.NAK)): {Rune: 'u', Mod: Ctrl},
string(byte(ansi.SYN)): {Rune: 'v', Mod: Ctrl},
string(byte(ansi.ETB)): {Rune: 'w', Mod: Ctrl},
string(byte(ansi.CAN)): {Rune: 'x', Mod: Ctrl},
string(byte(ansi.EM)): {Rune: 'y', Mod: Ctrl},
string(byte(ansi.SUB)): {Rune: 'z', Mod: Ctrl},
string(byte(ansi.ESC)): esc,
string(byte(ansi.FS)): {Rune: '\\', Mod: Ctrl},
string(byte(ansi.GS)): {Rune: ']', Mod: Ctrl},
string(byte(ansi.RS)): {Rune: '^', Mod: Ctrl},
string(byte(ansi.US)): {Rune: '_', Mod: Ctrl},
// Special keys in G0
string(byte(ansi.SP)): {Sym: KeySpace, Rune: ' '},
string(byte(ansi.DEL)): del,
// Special keys
"\x1b[Z": {Sym: KeyTab, Mod: Shift},
"\x1b[1~": find,
"\x1b[2~": {Sym: KeyInsert},
"\x1b[3~": {Sym: KeyDelete},
"\x1b[4~": sel,
"\x1b[5~": {Sym: KeyPgUp},
"\x1b[6~": {Sym: KeyPgDown},
"\x1b[7~": {Sym: KeyHome},
"\x1b[8~": {Sym: KeyEnd},
// Normal mode
"\x1b[A": {Sym: KeyUp},
"\x1b[B": {Sym: KeyDown},
"\x1b[C": {Sym: KeyRight},
"\x1b[D": {Sym: KeyLeft},
"\x1b[E": {Sym: KeyBegin},
"\x1b[F": {Sym: KeyEnd},
"\x1b[H": {Sym: KeyHome},
"\x1b[P": {Sym: KeyF1},
"\x1b[Q": {Sym: KeyF2},
"\x1b[R": {Sym: KeyF3},
"\x1b[S": {Sym: KeyF4},
// Application Cursor Key Mode (DECCKM)
"\x1bOA": {Sym: KeyUp},
"\x1bOB": {Sym: KeyDown},
"\x1bOC": {Sym: KeyRight},
"\x1bOD": {Sym: KeyLeft},
"\x1bOE": {Sym: KeyBegin},
"\x1bOF": {Sym: KeyEnd},
"\x1bOH": {Sym: KeyHome},
"\x1bOP": {Sym: KeyF1},
"\x1bOQ": {Sym: KeyF2},
"\x1bOR": {Sym: KeyF3},
"\x1bOS": {Sym: KeyF4},
// Keypad Application Mode (DECKPAM)
"\x1bOM": {Sym: KeyKpEnter},
"\x1bOX": {Sym: KeyKpEqual},
"\x1bOj": {Sym: KeyKpMultiply},
"\x1bOk": {Sym: KeyKpPlus},
"\x1bOl": {Sym: KeyKpComma},
"\x1bOm": {Sym: KeyKpMinus},
"\x1bOn": {Sym: KeyKpDecimal},
"\x1bOo": {Sym: KeyKpDivide},
"\x1bOp": {Sym: KeyKp0},
"\x1bOq": {Sym: KeyKp1},
"\x1bOr": {Sym: KeyKp2},
"\x1bOs": {Sym: KeyKp3},
"\x1bOt": {Sym: KeyKp4},
"\x1bOu": {Sym: KeyKp5},
"\x1bOv": {Sym: KeyKp6},
"\x1bOw": {Sym: KeyKp7},
"\x1bOx": {Sym: KeyKp8},
"\x1bOy": {Sym: KeyKp9},
// Function keys
"\x1b[11~": {Sym: KeyF1},
"\x1b[12~": {Sym: KeyF2},
"\x1b[13~": {Sym: KeyF3},
"\x1b[14~": {Sym: KeyF4},
"\x1b[15~": {Sym: KeyF5},
"\x1b[17~": {Sym: KeyF6},
"\x1b[18~": {Sym: KeyF7},
"\x1b[19~": {Sym: KeyF8},
"\x1b[20~": {Sym: KeyF9},
"\x1b[21~": {Sym: KeyF10},
"\x1b[23~": {Sym: KeyF11},
"\x1b[24~": {Sym: KeyF12},
"\x1b[25~": {Sym: KeyF13},
"\x1b[26~": {Sym: KeyF14},
"\x1b[28~": {Sym: KeyF15},
"\x1b[29~": {Sym: KeyF16},
"\x1b[31~": {Sym: KeyF17},
"\x1b[32~": {Sym: KeyF18},
"\x1b[33~": {Sym: KeyF19},
"\x1b[34~": {Sym: KeyF20},
}
// CSI ~ sequence keys
csiTildeKeys := map[string]Key{
"1": find, "2": {Sym: KeyInsert},
"3": {Sym: KeyDelete}, "4": sel,
"5": {Sym: KeyPgUp}, "6": {Sym: KeyPgDown},
"7": {Sym: KeyHome}, "8": {Sym: KeyEnd},
// There are no 9 and 10 keys
"11": {Sym: KeyF1}, "12": {Sym: KeyF2},
"13": {Sym: KeyF3}, "14": {Sym: KeyF4},
"15": {Sym: KeyF5}, "17": {Sym: KeyF6},
"18": {Sym: KeyF7}, "19": {Sym: KeyF8},
"20": {Sym: KeyF9}, "21": {Sym: KeyF10},
"23": {Sym: KeyF11}, "24": {Sym: KeyF12},
"25": {Sym: KeyF13}, "26": {Sym: KeyF14},
"28": {Sym: KeyF15}, "29": {Sym: KeyF16},
"31": {Sym: KeyF17}, "32": {Sym: KeyF18},
"33": {Sym: KeyF19}, "34": {Sym: KeyF20},
}
// URxvt keys
// See https://manpages.ubuntu.com/manpages/trusty/man7/urxvt.7.html#key%20codes
table["\x1b[a"] = Key{Sym: KeyUp, Mod: Shift}
table["\x1b[b"] = Key{Sym: KeyDown, Mod: Shift}
table["\x1b[c"] = Key{Sym: KeyRight, Mod: Shift}
table["\x1b[d"] = Key{Sym: KeyLeft, Mod: Shift}
table["\x1bOa"] = Key{Sym: KeyUp, Mod: Ctrl}
table["\x1bOb"] = Key{Sym: KeyDown, Mod: Ctrl}
table["\x1bOc"] = Key{Sym: KeyRight, Mod: Ctrl}
table["\x1bOd"] = Key{Sym: KeyLeft, Mod: Ctrl}
// TODO: invistigate if shift-ctrl arrow keys collide with DECCKM keys i.e.
// "\x1bOA", "\x1bOB", "\x1bOC", "\x1bOD"
// URxvt modifier CSI ~ keys
for k, v := range csiTildeKeys {
key := v
// Normal (no modifier) already defined part of VT100/VT200
// Shift modifier
key.Mod = Shift
table["\x1b["+k+"$"] = key
// Ctrl modifier
key.Mod = Ctrl
table["\x1b["+k+"^"] = key
// Shift-Ctrl modifier
key.Mod = Shift | Ctrl
table["\x1b["+k+"@"] = key
}
// URxvt F keys
// Note: Shift + F1-F10 generates F11-F20.
// This means Shift + F1 and Shift + F2 will generate F11 and F12, the same
// applies to Ctrl + Shift F1 & F2.
//
// P.S. Don't like this? Blame URxvt, configure your terminal to use
// different escapes like XTerm, or switch to a better terminal ¯\_(ツ)_/¯
//
// See https://manpages.ubuntu.com/manpages/trusty/man7/urxvt.7.html#key%20codes
table["\x1b[23$"] = Key{Sym: KeyF11, Mod: Shift}
table["\x1b[24$"] = Key{Sym: KeyF12, Mod: Shift}
table["\x1b[25$"] = Key{Sym: KeyF13, Mod: Shift}
table["\x1b[26$"] = Key{Sym: KeyF14, Mod: Shift}
table["\x1b[28$"] = Key{Sym: KeyF15, Mod: Shift}
table["\x1b[29$"] = Key{Sym: KeyF16, Mod: Shift}
table["\x1b[31$"] = Key{Sym: KeyF17, Mod: Shift}
table["\x1b[32$"] = Key{Sym: KeyF18, Mod: Shift}
table["\x1b[33$"] = Key{Sym: KeyF19, Mod: Shift}
table["\x1b[34$"] = Key{Sym: KeyF20, Mod: Shift}
table["\x1b[11^"] = Key{Sym: KeyF1, Mod: Ctrl}
table["\x1b[12^"] = Key{Sym: KeyF2, Mod: Ctrl}
table["\x1b[13^"] = Key{Sym: KeyF3, Mod: Ctrl}
table["\x1b[14^"] = Key{Sym: KeyF4, Mod: Ctrl}
table["\x1b[15^"] = Key{Sym: KeyF5, Mod: Ctrl}
table["\x1b[17^"] = Key{Sym: KeyF6, Mod: Ctrl}
table["\x1b[18^"] = Key{Sym: KeyF7, Mod: Ctrl}
table["\x1b[19^"] = Key{Sym: KeyF8, Mod: Ctrl}
table["\x1b[20^"] = Key{Sym: KeyF9, Mod: Ctrl}
table["\x1b[21^"] = Key{Sym: KeyF10, Mod: Ctrl}
table["\x1b[23^"] = Key{Sym: KeyF11, Mod: Ctrl}
table["\x1b[24^"] = Key{Sym: KeyF12, Mod: Ctrl}
table["\x1b[25^"] = Key{Sym: KeyF13, Mod: Ctrl}
table["\x1b[26^"] = Key{Sym: KeyF14, Mod: Ctrl}
table["\x1b[28^"] = Key{Sym: KeyF15, Mod: Ctrl}
table["\x1b[29^"] = Key{Sym: KeyF16, Mod: Ctrl}
table["\x1b[31^"] = Key{Sym: KeyF17, Mod: Ctrl}
table["\x1b[32^"] = Key{Sym: KeyF18, Mod: Ctrl}
table["\x1b[33^"] = Key{Sym: KeyF19, Mod: Ctrl}
table["\x1b[34^"] = Key{Sym: KeyF20, Mod: Ctrl}
table["\x1b[23@"] = Key{Sym: KeyF11, Mod: Shift | Ctrl}
table["\x1b[24@"] = Key{Sym: KeyF12, Mod: Shift | Ctrl}
table["\x1b[25@"] = Key{Sym: KeyF13, Mod: Shift | Ctrl}
table["\x1b[26@"] = Key{Sym: KeyF14, Mod: Shift | Ctrl}
table["\x1b[28@"] = Key{Sym: KeyF15, Mod: Shift | Ctrl}
table["\x1b[29@"] = Key{Sym: KeyF16, Mod: Shift | Ctrl}
table["\x1b[31@"] = Key{Sym: KeyF17, Mod: Shift | Ctrl}
table["\x1b[32@"] = Key{Sym: KeyF18, Mod: Shift | Ctrl}
table["\x1b[33@"] = Key{Sym: KeyF19, Mod: Shift | Ctrl}
table["\x1b[34@"] = Key{Sym: KeyF20, Mod: Shift | Ctrl}
// Register Alt + <key> combinations
// XXX: this must come after URxvt but before XTerm keys to register URxvt
// keys with alt modifier
tmap := map[string]Key{}
for seq, key := range table {
key := key
key.Mod |= Alt
tmap["\x1b"+seq] = key
}
for seq, key := range tmap {
table[seq] = key
}
// 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
modifiers := []KeyMod{
Shift, // 1
Alt, // 2
Shift | Alt, // 3
Ctrl, // 4
Shift | Ctrl, // 5
Alt | Ctrl, // 6
Shift | Alt | Ctrl, // 7
Meta, // 8
Meta | Shift, // 9
Meta | Alt, // 10
Meta | Shift | Alt, // 11
Meta | Ctrl, // 12
Meta | Shift | Ctrl, // 13
Meta | Alt | Ctrl, // 14
Meta | Shift | Alt | Ctrl, // 15
}
// SS3 keypad function keys
ss3FuncKeys := map[string]Key{
// These are defined in XTerm
// Taken from Foot keymap.h and XTerm modifyOtherKeys
// https://codeberg.org/dnkl/foot/src/branch/master/keymap.h
"M": {Sym: KeyKpEnter}, "X": {Sym: KeyKpEqual},
"j": {Sym: KeyKpMultiply}, "k": {Sym: KeyKpPlus},
"l": {Sym: KeyKpComma}, "m": {Sym: KeyKpMinus},
"n": {Sym: KeyKpDecimal}, "o": {Sym: KeyKpDivide},
"p": {Sym: KeyKp0}, "q": {Sym: KeyKp1},
"r": {Sym: KeyKp2}, "s": {Sym: KeyKp3},
"t": {Sym: KeyKp4}, "u": {Sym: KeyKp5},
"v": {Sym: KeyKp6}, "w": {Sym: KeyKp7},
"x": {Sym: KeyKp8}, "y": {Sym: KeyKp9},
}
// XTerm keys
csiFuncKeys := map[string]Key{
"A": {Sym: KeyUp}, "B": {Sym: KeyDown},
"C": {Sym: KeyRight}, "D": {Sym: KeyLeft},
"E": {Sym: KeyBegin}, "F": {Sym: KeyEnd},
"H": {Sym: KeyHome}, "P": {Sym: KeyF1},
"Q": {Sym: KeyF2}, "R": {Sym: KeyF3},
"S": {Sym: KeyF4},
}
// CSI 27 ; <modifier> ; <code> ~ keys defined in XTerm modifyOtherKeys
modifyOtherKeys := map[int]Key{
ansi.BS: {Sym: KeyBackspace},
ansi.HT: {Sym: KeyTab},
ansi.CR: {Sym: KeyEnter},
ansi.ESC: {Sym: KeyEscape},
ansi.DEL: {Sym: KeyBackspace},
}
for _, m := range modifiers {
// XTerm modifier offset +1
xtermMod := strconv.Itoa(int(m) + 1)
// CSI 1 ; <modifier> <func>
for k, v := range csiFuncKeys {
// Functions always have a leading 1 param
seq := "\x1b[1;" + xtermMod + k
key := v
key.Mod = m
table[seq] = key
}
// SS3 <modifier> <func>
for k, v := range ss3FuncKeys {
seq := "\x1bO" + xtermMod + k
key := v
key.Mod = m
table[seq] = key
}
// CSI <number> ; <modifier> ~
for k, v := range csiTildeKeys {
seq := "\x1b[" + k + ";" + xtermMod + "~"
key := v
key.Mod = m
table[seq] = key
}
// CSI 27 ; <modifier> ; <code> ~
for k, v := range modifyOtherKeys {
code := strconv.Itoa(k)
seq := "\x1b[27;" + xtermMod + ";" + code + "~"
key := v
key.Mod = m
table[seq] = key
}
}
// Register terminfo keys
// XXX: this might override keys already registered in table
if flags&FlagTerminfo != 0 {
titable := buildTerminfoKeys(flags, term)
for seq, key := range titable {
table[seq] = key
}
}
return table
}

49
vendor/github.com/charmbracelet/x/input/termcap.go generated vendored Normal file
View File

@@ -0,0 +1,49 @@
package input
import (
"bytes"
"encoding/hex"
)
// TermcapEvent represents a Termcap response event. Termcap responses are
// generated by the terminal in response to RequestTermcap (XTGETTCAP)
// requests.
//
// See: https://invisible-island.net/xterm/ctlseqs/ctlseqs.html#h3-Operating-System-Commands
type TermcapEvent struct {
Values map[string]string
IsValid bool
}
func parseTermcap(data []byte) TermcapEvent {
// XTGETTCAP
if len(data) == 0 {
return TermcapEvent{}
}
tc := TermcapEvent{Values: make(map[string]string)}
split := bytes.Split(data, []byte{';'})
for _, s := range split {
parts := bytes.SplitN(s, []byte{'='}, 2)
if len(parts) == 0 {
return TermcapEvent{}
}
name, err := hex.DecodeString(string(parts[0]))
if err != nil || len(name) == 0 {
continue
}
var value []byte
if len(parts) > 1 {
value, err = hex.DecodeString(string(parts[1]))
if err != nil {
continue
}
}
tc.Values[string(name)] = string(value)
}
return tc
}

277
vendor/github.com/charmbracelet/x/input/terminfo.go generated vendored Normal file
View File

@@ -0,0 +1,277 @@
package input
import (
"strings"
"github.com/xo/terminfo"
)
func buildTerminfoKeys(flags int, term string) map[string]Key {
table := make(map[string]Key)
ti, _ := terminfo.Load(term)
if ti == nil {
return table
}
tiTable := defaultTerminfoKeys(flags)
// Default keys
for name, seq := range ti.StringCapsShort() {
if !strings.HasPrefix(name, "k") || len(seq) == 0 {
continue
}
if k, ok := tiTable[name]; ok {
table[string(seq)] = k
}
}
// Extended keys
for name, seq := range ti.ExtStringCapsShort() {
if !strings.HasPrefix(name, "k") || len(seq) == 0 {
continue
}
if k, ok := tiTable[name]; ok {
table[string(seq)] = k
}
}
return table
}
// This returns a map of terminfo keys to key events. It's a mix of ncurses
// terminfo default and user-defined key capabilities.
// Upper-case caps that are defined in the default terminfo database are
// - kNXT
// - kPRV
// - kHOM
// - kEND
// - kDC
// - kIC
// - kLFT
// - kRIT
//
// See https://man7.org/linux/man-pages/man5/terminfo.5.html
// See https://github.com/mirror/ncurses/blob/master/include/Caps-ncurses
func defaultTerminfoKeys(flags int) map[string]Key {
keys := map[string]Key{
"kcuu1": {Sym: KeyUp},
"kUP": {Sym: KeyUp, Mod: Shift},
"kUP3": {Sym: KeyUp, Mod: Alt},
"kUP4": {Sym: KeyUp, Mod: Shift | Alt},
"kUP5": {Sym: KeyUp, Mod: Ctrl},
"kUP6": {Sym: KeyUp, Mod: Shift | Ctrl},
"kUP7": {Sym: KeyUp, Mod: Alt | Ctrl},
"kUP8": {Sym: KeyUp, Mod: Shift | Alt | Ctrl},
"kcud1": {Sym: KeyDown},
"kDN": {Sym: KeyDown, Mod: Shift},
"kDN3": {Sym: KeyDown, Mod: Alt},
"kDN4": {Sym: KeyDown, Mod: Shift | Alt},
"kDN5": {Sym: KeyDown, Mod: Ctrl},
"kDN7": {Sym: KeyDown, Mod: Alt | Ctrl},
"kDN6": {Sym: KeyDown, Mod: Shift | Ctrl},
"kDN8": {Sym: KeyDown, Mod: Shift | Alt | Ctrl},
"kcub1": {Sym: KeyLeft},
"kLFT": {Sym: KeyLeft, Mod: Shift},
"kLFT3": {Sym: KeyLeft, Mod: Alt},
"kLFT4": {Sym: KeyLeft, Mod: Shift | Alt},
"kLFT5": {Sym: KeyLeft, Mod: Ctrl},
"kLFT6": {Sym: KeyLeft, Mod: Shift | Ctrl},
"kLFT7": {Sym: KeyLeft, Mod: Alt | Ctrl},
"kLFT8": {Sym: KeyLeft, Mod: Shift | Alt | Ctrl},
"kcuf1": {Sym: KeyRight},
"kRIT": {Sym: KeyRight, Mod: Shift},
"kRIT3": {Sym: KeyRight, Mod: Alt},
"kRIT4": {Sym: KeyRight, Mod: Shift | Alt},
"kRIT5": {Sym: KeyRight, Mod: Ctrl},
"kRIT6": {Sym: KeyRight, Mod: Shift | Ctrl},
"kRIT7": {Sym: KeyRight, Mod: Alt | Ctrl},
"kRIT8": {Sym: KeyRight, Mod: Shift | Alt | Ctrl},
"kich1": {Sym: KeyInsert},
"kIC": {Sym: KeyInsert, Mod: Shift},
"kIC3": {Sym: KeyInsert, Mod: Alt},
"kIC4": {Sym: KeyInsert, Mod: Shift | Alt},
"kIC5": {Sym: KeyInsert, Mod: Ctrl},
"kIC6": {Sym: KeyInsert, Mod: Shift | Ctrl},
"kIC7": {Sym: KeyInsert, Mod: Alt | Ctrl},
"kIC8": {Sym: KeyInsert, Mod: Shift | Alt | Ctrl},
"kdch1": {Sym: KeyDelete},
"kDC": {Sym: KeyDelete, Mod: Shift},
"kDC3": {Sym: KeyDelete, Mod: Alt},
"kDC4": {Sym: KeyDelete, Mod: Shift | Alt},
"kDC5": {Sym: KeyDelete, Mod: Ctrl},
"kDC6": {Sym: KeyDelete, Mod: Shift | Ctrl},
"kDC7": {Sym: KeyDelete, Mod: Alt | Ctrl},
"kDC8": {Sym: KeyDelete, Mod: Shift | Alt | Ctrl},
"khome": {Sym: KeyHome},
"kHOM": {Sym: KeyHome, Mod: Shift},
"kHOM3": {Sym: KeyHome, Mod: Alt},
"kHOM4": {Sym: KeyHome, Mod: Shift | Alt},
"kHOM5": {Sym: KeyHome, Mod: Ctrl},
"kHOM6": {Sym: KeyHome, Mod: Shift | Ctrl},
"kHOM7": {Sym: KeyHome, Mod: Alt | Ctrl},
"kHOM8": {Sym: KeyHome, Mod: Shift | Alt | Ctrl},
"kend": {Sym: KeyEnd},
"kEND": {Sym: KeyEnd, Mod: Shift},
"kEND3": {Sym: KeyEnd, Mod: Alt},
"kEND4": {Sym: KeyEnd, Mod: Shift | Alt},
"kEND5": {Sym: KeyEnd, Mod: Ctrl},
"kEND6": {Sym: KeyEnd, Mod: Shift | Ctrl},
"kEND7": {Sym: KeyEnd, Mod: Alt | Ctrl},
"kEND8": {Sym: KeyEnd, Mod: Shift | Alt | Ctrl},
"kpp": {Sym: KeyPgUp},
"kprv": {Sym: KeyPgUp},
"kPRV": {Sym: KeyPgUp, Mod: Shift},
"kPRV3": {Sym: KeyPgUp, Mod: Alt},
"kPRV4": {Sym: KeyPgUp, Mod: Shift | Alt},
"kPRV5": {Sym: KeyPgUp, Mod: Ctrl},
"kPRV6": {Sym: KeyPgUp, Mod: Shift | Ctrl},
"kPRV7": {Sym: KeyPgUp, Mod: Alt | Ctrl},
"kPRV8": {Sym: KeyPgUp, Mod: Shift | Alt | Ctrl},
"knp": {Sym: KeyPgDown},
"knxt": {Sym: KeyPgDown},
"kNXT": {Sym: KeyPgDown, Mod: Shift},
"kNXT3": {Sym: KeyPgDown, Mod: Alt},
"kNXT4": {Sym: KeyPgDown, Mod: Shift | Alt},
"kNXT5": {Sym: KeyPgDown, Mod: Ctrl},
"kNXT6": {Sym: KeyPgDown, Mod: Shift | Ctrl},
"kNXT7": {Sym: KeyPgDown, Mod: Alt | Ctrl},
"kNXT8": {Sym: KeyPgDown, Mod: Shift | Alt | Ctrl},
"kbs": {Sym: KeyBackspace},
"kcbt": {Sym: KeyTab, Mod: Shift},
// Function keys
// This only includes the first 12 function keys. The rest are treated
// as modifiers of the first 12.
// Take a look at XTerm modifyFunctionKeys
//
// XXX: To use unambiguous function keys, use fixterms or kitty clipboard.
//
// See https://invisible-island.net/xterm/manpage/xterm.html#VT100-Widget-Resources:modifyFunctionKeys
// See https://invisible-island.net/xterm/terminfo.html
"kf1": {Sym: KeyF1},
"kf2": {Sym: KeyF2},
"kf3": {Sym: KeyF3},
"kf4": {Sym: KeyF4},
"kf5": {Sym: KeyF5},
"kf6": {Sym: KeyF6},
"kf7": {Sym: KeyF7},
"kf8": {Sym: KeyF8},
"kf9": {Sym: KeyF9},
"kf10": {Sym: KeyF10},
"kf11": {Sym: KeyF11},
"kf12": {Sym: KeyF12},
"kf13": {Sym: KeyF1, Mod: Shift},
"kf14": {Sym: KeyF2, Mod: Shift},
"kf15": {Sym: KeyF3, Mod: Shift},
"kf16": {Sym: KeyF4, Mod: Shift},
"kf17": {Sym: KeyF5, Mod: Shift},
"kf18": {Sym: KeyF6, Mod: Shift},
"kf19": {Sym: KeyF7, Mod: Shift},
"kf20": {Sym: KeyF8, Mod: Shift},
"kf21": {Sym: KeyF9, Mod: Shift},
"kf22": {Sym: KeyF10, Mod: Shift},
"kf23": {Sym: KeyF11, Mod: Shift},
"kf24": {Sym: KeyF12, Mod: Shift},
"kf25": {Sym: KeyF1, Mod: Ctrl},
"kf26": {Sym: KeyF2, Mod: Ctrl},
"kf27": {Sym: KeyF3, Mod: Ctrl},
"kf28": {Sym: KeyF4, Mod: Ctrl},
"kf29": {Sym: KeyF5, Mod: Ctrl},
"kf30": {Sym: KeyF6, Mod: Ctrl},
"kf31": {Sym: KeyF7, Mod: Ctrl},
"kf32": {Sym: KeyF8, Mod: Ctrl},
"kf33": {Sym: KeyF9, Mod: Ctrl},
"kf34": {Sym: KeyF10, Mod: Ctrl},
"kf35": {Sym: KeyF11, Mod: Ctrl},
"kf36": {Sym: KeyF12, Mod: Ctrl},
"kf37": {Sym: KeyF1, Mod: Shift | Ctrl},
"kf38": {Sym: KeyF2, Mod: Shift | Ctrl},
"kf39": {Sym: KeyF3, Mod: Shift | Ctrl},
"kf40": {Sym: KeyF4, Mod: Shift | Ctrl},
"kf41": {Sym: KeyF5, Mod: Shift | Ctrl},
"kf42": {Sym: KeyF6, Mod: Shift | Ctrl},
"kf43": {Sym: KeyF7, Mod: Shift | Ctrl},
"kf44": {Sym: KeyF8, Mod: Shift | Ctrl},
"kf45": {Sym: KeyF9, Mod: Shift | Ctrl},
"kf46": {Sym: KeyF10, Mod: Shift | Ctrl},
"kf47": {Sym: KeyF11, Mod: Shift | Ctrl},
"kf48": {Sym: KeyF12, Mod: Shift | Ctrl},
"kf49": {Sym: KeyF1, Mod: Alt},
"kf50": {Sym: KeyF2, Mod: Alt},
"kf51": {Sym: KeyF3, Mod: Alt},
"kf52": {Sym: KeyF4, Mod: Alt},
"kf53": {Sym: KeyF5, Mod: Alt},
"kf54": {Sym: KeyF6, Mod: Alt},
"kf55": {Sym: KeyF7, Mod: Alt},
"kf56": {Sym: KeyF8, Mod: Alt},
"kf57": {Sym: KeyF9, Mod: Alt},
"kf58": {Sym: KeyF10, Mod: Alt},
"kf59": {Sym: KeyF11, Mod: Alt},
"kf60": {Sym: KeyF12, Mod: Alt},
"kf61": {Sym: KeyF1, Mod: Shift | Alt},
"kf62": {Sym: KeyF2, Mod: Shift | Alt},
"kf63": {Sym: KeyF3, Mod: Shift | Alt},
}
// Preserve F keys from F13 to F63 instead of using them for F-keys
// modifiers.
if flags&FlagFKeys != 0 {
keys["kf13"] = Key{Sym: KeyF13}
keys["kf14"] = Key{Sym: KeyF14}
keys["kf15"] = Key{Sym: KeyF15}
keys["kf16"] = Key{Sym: KeyF16}
keys["kf17"] = Key{Sym: KeyF17}
keys["kf18"] = Key{Sym: KeyF18}
keys["kf19"] = Key{Sym: KeyF19}
keys["kf20"] = Key{Sym: KeyF20}
keys["kf21"] = Key{Sym: KeyF21}
keys["kf22"] = Key{Sym: KeyF22}
keys["kf23"] = Key{Sym: KeyF23}
keys["kf24"] = Key{Sym: KeyF24}
keys["kf25"] = Key{Sym: KeyF25}
keys["kf26"] = Key{Sym: KeyF26}
keys["kf27"] = Key{Sym: KeyF27}
keys["kf28"] = Key{Sym: KeyF28}
keys["kf29"] = Key{Sym: KeyF29}
keys["kf30"] = Key{Sym: KeyF30}
keys["kf31"] = Key{Sym: KeyF31}
keys["kf32"] = Key{Sym: KeyF32}
keys["kf33"] = Key{Sym: KeyF33}
keys["kf34"] = Key{Sym: KeyF34}
keys["kf35"] = Key{Sym: KeyF35}
keys["kf36"] = Key{Sym: KeyF36}
keys["kf37"] = Key{Sym: KeyF37}
keys["kf38"] = Key{Sym: KeyF38}
keys["kf39"] = Key{Sym: KeyF39}
keys["kf40"] = Key{Sym: KeyF40}
keys["kf41"] = Key{Sym: KeyF41}
keys["kf42"] = Key{Sym: KeyF42}
keys["kf43"] = Key{Sym: KeyF43}
keys["kf44"] = Key{Sym: KeyF44}
keys["kf45"] = Key{Sym: KeyF45}
keys["kf46"] = Key{Sym: KeyF46}
keys["kf47"] = Key{Sym: KeyF47}
keys["kf48"] = Key{Sym: KeyF48}
keys["kf49"] = Key{Sym: KeyF49}
keys["kf50"] = Key{Sym: KeyF50}
keys["kf51"] = Key{Sym: KeyF51}
keys["kf52"] = Key{Sym: KeyF52}
keys["kf53"] = Key{Sym: KeyF53}
keys["kf54"] = Key{Sym: KeyF54}
keys["kf55"] = Key{Sym: KeyF55}
keys["kf56"] = Key{Sym: KeyF56}
keys["kf57"] = Key{Sym: KeyF57}
keys["kf58"] = Key{Sym: KeyF58}
keys["kf59"] = Key{Sym: KeyF59}
keys["kf60"] = Key{Sym: KeyF60}
keys["kf61"] = Key{Sym: KeyF61}
keys["kf62"] = Key{Sym: KeyF62}
keys["kf63"] = Key{Sym: KeyF63}
}
return keys
}

240
vendor/github.com/charmbracelet/x/input/win32input.go generated vendored Normal file
View File

@@ -0,0 +1,240 @@
package input
import (
"unicode"
"github.com/erikgeiser/coninput"
)
func parseWin32InputKeyEvent(vkc coninput.VirtualKeyCode, _ coninput.VirtualKeyCode, r rune, keyDown bool, cks coninput.ControlKeyState, repeatCount uint16) Event {
var key Key
isCtrl := cks.Contains(coninput.LEFT_CTRL_PRESSED | coninput.RIGHT_CTRL_PRESSED)
switch vkc {
case coninput.VK_SHIFT:
// We currently ignore these keys when they are pressed alone.
return nil
case coninput.VK_MENU:
if cks.Contains(coninput.LEFT_ALT_PRESSED) {
key = Key{Sym: KeyLeftAlt}
} else if cks.Contains(coninput.RIGHT_ALT_PRESSED) {
key = Key{Sym: KeyRightAlt}
} else if !keyDown {
return nil
}
case coninput.VK_CONTROL:
if cks.Contains(coninput.LEFT_CTRL_PRESSED) {
key = Key{Sym: KeyLeftCtrl}
} else if cks.Contains(coninput.RIGHT_CTRL_PRESSED) {
key = Key{Sym: KeyRightCtrl}
} else if !keyDown {
return nil
}
case coninput.VK_CAPITAL:
key = Key{Sym: KeyCapsLock}
default:
var ok bool
key, ok = vkKeyEvent[vkc]
if !ok {
if isCtrl {
key = vkCtrlRune(key, r, vkc)
} else {
key = Key{Rune: r}
}
}
}
if isCtrl {
key.Mod |= Ctrl
}
if cks.Contains(coninput.LEFT_ALT_PRESSED | coninput.RIGHT_ALT_PRESSED) {
key.Mod |= Alt
}
if cks.Contains(coninput.SHIFT_PRESSED) {
key.Mod |= Shift
}
if cks.Contains(coninput.CAPSLOCK_ON) {
key.Mod |= CapsLock
}
if cks.Contains(coninput.NUMLOCK_ON) {
key.Mod |= NumLock
}
if cks.Contains(coninput.SCROLLLOCK_ON) {
key.Mod |= ScrollLock
}
// Use the unshifted key
if cks.Contains(coninput.SHIFT_PRESSED ^ coninput.CAPSLOCK_ON) {
key.AltRune = unicode.ToUpper(key.Rune)
} else {
key.AltRune = unicode.ToLower(key.Rune)
}
var e Event = KeyDownEvent(key)
key.IsRepeat = repeatCount > 1
if !keyDown {
e = KeyUpEvent(key)
}
if repeatCount <= 1 {
return e
}
var kevents []Event
for i := 0; i < int(repeatCount); i++ {
kevents = append(kevents, e)
}
return MultiEvent(kevents)
}
var vkKeyEvent = map[coninput.VirtualKeyCode]Key{
coninput.VK_RETURN: {Sym: KeyEnter},
coninput.VK_BACK: {Sym: KeyBackspace},
coninput.VK_TAB: {Sym: KeyTab},
coninput.VK_ESCAPE: {Sym: KeyEscape},
coninput.VK_SPACE: {Sym: KeySpace, Rune: ' '},
coninput.VK_UP: {Sym: KeyUp},
coninput.VK_DOWN: {Sym: KeyDown},
coninput.VK_RIGHT: {Sym: KeyRight},
coninput.VK_LEFT: {Sym: KeyLeft},
coninput.VK_HOME: {Sym: KeyHome},
coninput.VK_END: {Sym: KeyEnd},
coninput.VK_PRIOR: {Sym: KeyPgUp},
coninput.VK_NEXT: {Sym: KeyPgDown},
coninput.VK_DELETE: {Sym: KeyDelete},
coninput.VK_SELECT: {Sym: KeySelect},
coninput.VK_SNAPSHOT: {Sym: KeyPrintScreen},
coninput.VK_INSERT: {Sym: KeyInsert},
coninput.VK_LWIN: {Sym: KeyLeftSuper},
coninput.VK_RWIN: {Sym: KeyRightSuper},
coninput.VK_APPS: {Sym: KeyMenu},
coninput.VK_NUMPAD0: {Sym: KeyKp0},
coninput.VK_NUMPAD1: {Sym: KeyKp1},
coninput.VK_NUMPAD2: {Sym: KeyKp2},
coninput.VK_NUMPAD3: {Sym: KeyKp3},
coninput.VK_NUMPAD4: {Sym: KeyKp4},
coninput.VK_NUMPAD5: {Sym: KeyKp5},
coninput.VK_NUMPAD6: {Sym: KeyKp6},
coninput.VK_NUMPAD7: {Sym: KeyKp7},
coninput.VK_NUMPAD8: {Sym: KeyKp8},
coninput.VK_NUMPAD9: {Sym: KeyKp9},
coninput.VK_MULTIPLY: {Sym: KeyKpMultiply},
coninput.VK_ADD: {Sym: KeyKpPlus},
coninput.VK_SEPARATOR: {Sym: KeyKpComma},
coninput.VK_SUBTRACT: {Sym: KeyKpMinus},
coninput.VK_DECIMAL: {Sym: KeyKpDecimal},
coninput.VK_DIVIDE: {Sym: KeyKpDivide},
coninput.VK_F1: {Sym: KeyF1},
coninput.VK_F2: {Sym: KeyF2},
coninput.VK_F3: {Sym: KeyF3},
coninput.VK_F4: {Sym: KeyF4},
coninput.VK_F5: {Sym: KeyF5},
coninput.VK_F6: {Sym: KeyF6},
coninput.VK_F7: {Sym: KeyF7},
coninput.VK_F8: {Sym: KeyF8},
coninput.VK_F9: {Sym: KeyF9},
coninput.VK_F10: {Sym: KeyF10},
coninput.VK_F11: {Sym: KeyF11},
coninput.VK_F12: {Sym: KeyF12},
coninput.VK_F13: {Sym: KeyF13},
coninput.VK_F14: {Sym: KeyF14},
coninput.VK_F15: {Sym: KeyF15},
coninput.VK_F16: {Sym: KeyF16},
coninput.VK_F17: {Sym: KeyF17},
coninput.VK_F18: {Sym: KeyF18},
coninput.VK_F19: {Sym: KeyF19},
coninput.VK_F20: {Sym: KeyF20},
coninput.VK_F21: {Sym: KeyF21},
coninput.VK_F22: {Sym: KeyF22},
coninput.VK_F23: {Sym: KeyF23},
coninput.VK_F24: {Sym: KeyF24},
coninput.VK_NUMLOCK: {Sym: KeyNumLock},
coninput.VK_SCROLL: {Sym: KeyScrollLock},
coninput.VK_LSHIFT: {Sym: KeyLeftShift},
coninput.VK_RSHIFT: {Sym: KeyRightShift},
coninput.VK_LCONTROL: {Sym: KeyLeftCtrl},
coninput.VK_RCONTROL: {Sym: KeyRightCtrl},
coninput.VK_LMENU: {Sym: KeyLeftAlt},
coninput.VK_RMENU: {Sym: KeyRightAlt},
coninput.VK_OEM_4: {Rune: '['},
// TODO: add more keys
}
func vkCtrlRune(k Key, r rune, kc coninput.VirtualKeyCode) Key {
switch r {
case '@':
k.Rune = '@'
case '\x01':
k.Rune = 'a'
case '\x02':
k.Rune = 'b'
case '\x03':
k.Rune = 'c'
case '\x04':
k.Rune = 'd'
case '\x05':
k.Rune = 'e'
case '\x06':
k.Rune = 'f'
case '\a':
k.Rune = 'g'
case '\b':
k.Rune = 'h'
case '\t':
k.Rune = 'i'
case '\n':
k.Rune = 'j'
case '\v':
k.Rune = 'k'
case '\f':
k.Rune = 'l'
case '\r':
k.Rune = 'm'
case '\x0e':
k.Rune = 'n'
case '\x0f':
k.Rune = 'o'
case '\x10':
k.Rune = 'p'
case '\x11':
k.Rune = 'q'
case '\x12':
k.Rune = 'r'
case '\x13':
k.Rune = 's'
case '\x14':
k.Rune = 't'
case '\x15':
k.Rune = 'u'
case '\x16':
k.Rune = 'v'
case '\x17':
k.Rune = 'w'
case '\x18':
k.Rune = 'x'
case '\x19':
k.Rune = 'y'
case '\x1a':
k.Rune = 'z'
case '\x1b':
k.Rune = ']'
case '\x1c':
k.Rune = '\\'
case '\x1f':
k.Rune = '_'
}
switch kc {
case coninput.VK_OEM_4:
k.Rune = '['
}
// https://learn.microsoft.com/en-us/windows/win32/inputdev/virtual-key-codes
if k.Rune == 0 &&
(kc >= 0x30 && kc <= 0x39) ||
(kc >= 0x41 && kc <= 0x5a) {
k.Rune = rune(kc)
}
return k
}

40
vendor/github.com/charmbracelet/x/input/xterm.go generated vendored Normal file
View File

@@ -0,0 +1,40 @@
package input
import (
"github.com/charmbracelet/x/ansi"
)
func parseXTermModifyOtherKeys(csi *ansi.CsiSequence) Event {
// XTerm modify other keys starts with ESC [ 27 ; <modifier> ; <code> ~
mod := KeyMod(csi.Param(1) - 1)
r := rune(csi.Param(2))
switch r {
case ansi.BS:
return KeyDownEvent{Mod: mod, Sym: KeyBackspace}
case ansi.HT:
return KeyDownEvent{Mod: mod, Sym: KeyTab}
case ansi.CR:
return KeyDownEvent{Mod: mod, Sym: KeyEnter}
case ansi.ESC:
return KeyDownEvent{Mod: mod, Sym: KeyEscape}
case ansi.DEL:
return KeyDownEvent{Mod: mod, Sym: KeyBackspace}
}
// CSI 27 ; <modifier> ; <code> ~ keys defined in XTerm modifyOtherKeys
return KeyDownEvent{
Mod: mod,
Rune: r,
}
}
// ModifyOtherKeysEvent represents a modifyOtherKeys event.
//
// 0: disable
// 1: enable mode 1
// 2: enable mode 2
//
// See: https://invisible-island.net/xterm/ctlseqs/ctlseqs.html#h3-Functions-using-CSI-_-ordered-by-the-final-character_s_
// See: https://invisible-island.net/xterm/manpage/xterm.html#VT100-Widget-Resources:modifyOtherKeys
type ModifyOtherKeysEvent uint8

21
vendor/github.com/charmbracelet/x/term/LICENSE generated vendored Normal file
View File

@@ -0,0 +1,21 @@
MIT License
Copyright (c) 2023 Charmbracelet, Inc.
Permission is hereby granted, free of charge, to any person obtaining a copy
of this software and associated documentation files (the "Software"), to deal
in the Software without restriction, including without limitation the rights
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
copies of the Software, and to permit persons to whom the Software is
furnished to do so, subject to the following conditions:
The above copyright notice and this permission notice shall be included in all
copies or substantial portions of the Software.
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
SOFTWARE.

49
vendor/github.com/charmbracelet/x/term/term.go generated vendored Normal file
View File

@@ -0,0 +1,49 @@
package term
// State contains platform-specific state of a terminal.
type State struct {
state
}
// IsTerminal returns whether the given file descriptor is a terminal.
func IsTerminal(fd uintptr) bool {
return isTerminal(fd)
}
// MakeRaw puts the terminal connected to the given file descriptor into raw
// mode and returns the previous state of the terminal so that it can be
// restored.
func MakeRaw(fd uintptr) (*State, error) {
return makeRaw(fd)
}
// GetState returns the current state of a terminal which may be useful to
// restore the terminal after a signal.
func GetState(fd uintptr) (*State, error) {
return getState(fd)
}
// SetState sets the given state of the terminal.
func SetState(fd uintptr, state *State) error {
return setState(fd, state)
}
// Restore restores the terminal connected to the given file descriptor to a
// previous state.
func Restore(fd uintptr, oldState *State) error {
return restore(fd, oldState)
}
// GetSize returns the visible dimensions of the given terminal.
//
// These dimensions don't include any scrollback buffer height.
func GetSize(fd uintptr) (width, height int, err error) {
return getSize(fd)
}
// ReadPassword reads a line of input from a terminal without local echo. This
// is commonly used for inputting passwords and other sensitive data. The slice
// returned does not include the \n.
func ReadPassword(fd uintptr) ([]byte, error) {
return readPassword(fd)
}

39
vendor/github.com/charmbracelet/x/term/term_other.go generated vendored Normal file
View File

@@ -0,0 +1,39 @@
//go:build !aix && !darwin && !dragonfly && !freebsd && !linux && !netbsd && !openbsd && !zos && !windows && !solaris && !plan9
// +build !aix,!darwin,!dragonfly,!freebsd,!linux,!netbsd,!openbsd,!zos,!windows,!solaris,!plan9
package term
import (
"fmt"
"runtime"
)
type state struct{}
func isTerminal(fd uintptr) bool {
return false
}
func makeRaw(fd uintptr) (*State, error) {
return nil, fmt.Errorf("terminal: MakeRaw not implemented on %s/%s", runtime.GOOS, runtime.GOARCH)
}
func getState(fd uintptr) (*State, error) {
return nil, fmt.Errorf("terminal: GetState not implemented on %s/%s", runtime.GOOS, runtime.GOARCH)
}
func restore(fd uintptr, state *State) error {
return fmt.Errorf("terminal: Restore not implemented on %s/%s", runtime.GOOS, runtime.GOARCH)
}
func getSize(fd uintptr) (width, height int, err error) {
return 0, 0, fmt.Errorf("terminal: GetSize not implemented on %s/%s", runtime.GOOS, runtime.GOARCH)
}
func setState(fd uintptr, state *State) error {
return fmt.Errorf("terminal: SetState not implemented on %s/%s", runtime.GOOS, runtime.GOARCH)
}
func readPassword(fd uintptr) ([]byte, error) {
return nil, fmt.Errorf("terminal: ReadPassword not implemented on %s/%s", runtime.GOOS, runtime.GOARCH)
}

96
vendor/github.com/charmbracelet/x/term/term_unix.go generated vendored Normal file
View File

@@ -0,0 +1,96 @@
//go:build aix || darwin || dragonfly || freebsd || linux || netbsd || openbsd || solaris || zos
// +build aix darwin dragonfly freebsd linux netbsd openbsd solaris zos
package term
import (
"golang.org/x/sys/unix"
)
type state struct {
unix.Termios
}
func isTerminal(fd uintptr) bool {
_, err := unix.IoctlGetTermios(int(fd), ioctlReadTermios)
return err == nil
}
func makeRaw(fd uintptr) (*State, error) {
termios, err := unix.IoctlGetTermios(int(fd), ioctlReadTermios)
if err != nil {
return nil, err
}
oldState := State{state{Termios: *termios}}
// This attempts to replicate the behaviour documented for cfmakeraw in
// the termios(3) manpage.
termios.Iflag &^= unix.IGNBRK | unix.BRKINT | unix.PARMRK | unix.ISTRIP | unix.INLCR | unix.IGNCR | unix.ICRNL | unix.IXON
termios.Oflag &^= unix.OPOST
termios.Lflag &^= unix.ECHO | unix.ECHONL | unix.ICANON | unix.ISIG | unix.IEXTEN
termios.Cflag &^= unix.CSIZE | unix.PARENB
termios.Cflag |= unix.CS8
termios.Cc[unix.VMIN] = 1
termios.Cc[unix.VTIME] = 0
if err := unix.IoctlSetTermios(int(fd), ioctlWriteTermios, termios); err != nil {
return nil, err
}
return &oldState, nil
}
func setState(fd uintptr, state *State) error {
var termios *unix.Termios
if state != nil {
termios = &state.Termios
}
return unix.IoctlSetTermios(int(fd), ioctlWriteTermios, termios)
}
func getState(fd uintptr) (*State, error) {
termios, err := unix.IoctlGetTermios(int(fd), ioctlReadTermios)
if err != nil {
return nil, err
}
return &State{state{Termios: *termios}}, nil
}
func restore(fd uintptr, state *State) error {
return unix.IoctlSetTermios(int(fd), ioctlWriteTermios, &state.Termios)
}
func getSize(fd uintptr) (width, height int, err error) {
ws, err := unix.IoctlGetWinsize(int(fd), unix.TIOCGWINSZ)
if err != nil {
return 0, 0, err
}
return int(ws.Col), int(ws.Row), nil
}
// passwordReader is an io.Reader that reads from a specific file descriptor.
type passwordReader int
func (r passwordReader) Read(buf []byte) (int, error) {
return unix.Read(int(r), buf)
}
func readPassword(fd uintptr) ([]byte, error) {
termios, err := unix.IoctlGetTermios(int(fd), ioctlReadTermios)
if err != nil {
return nil, err
}
newState := *termios
newState.Lflag &^= unix.ECHO
newState.Lflag |= unix.ICANON | unix.ISIG
newState.Iflag |= unix.ICRNL
if err := unix.IoctlSetTermios(int(fd), ioctlWriteTermios, &newState); err != nil {
return nil, err
}
defer unix.IoctlSetTermios(int(fd), ioctlWriteTermios, termios)
return readPasswordLine(passwordReader(fd))
}

View File

@@ -0,0 +1,11 @@
//go:build darwin || dragonfly || freebsd || netbsd || openbsd
// +build darwin dragonfly freebsd netbsd openbsd
package term
import "golang.org/x/sys/unix"
const (
ioctlReadTermios = unix.TIOCGETA
ioctlWriteTermios = unix.TIOCSETA
)

View File

@@ -0,0 +1,11 @@
//go:build aix || linux || solaris || zos
// +build aix linux solaris zos
package term
import "golang.org/x/sys/unix"
const (
ioctlReadTermios = unix.TCGETS
ioctlWriteTermios = unix.TCSETS
)

86
vendor/github.com/charmbracelet/x/term/term_windows.go generated vendored Normal file
View File

@@ -0,0 +1,86 @@
//go:build windows
// +build windows
package term
import (
"os"
"golang.org/x/sys/windows"
)
type state struct {
Mode uint32
}
func isTerminal(fd uintptr) bool {
var st uint32
err := windows.GetConsoleMode(windows.Handle(fd), &st)
return err == nil
}
func makeRaw(fd uintptr) (*State, error) {
var st uint32
if err := windows.GetConsoleMode(windows.Handle(fd), &st); err != nil {
return nil, err
}
raw := st &^ (windows.ENABLE_ECHO_INPUT | windows.ENABLE_PROCESSED_INPUT | windows.ENABLE_LINE_INPUT | windows.ENABLE_PROCESSED_OUTPUT)
if err := windows.SetConsoleMode(windows.Handle(fd), raw); err != nil {
return nil, err
}
return &State{state{st}}, nil
}
func setState(fd uintptr, state *State) error {
var mode uint32
if state != nil {
mode = state.Mode
}
return windows.SetConsoleMode(windows.Handle(fd), mode)
}
func getState(fd uintptr) (*State, error) {
var st uint32
if err := windows.GetConsoleMode(windows.Handle(fd), &st); err != nil {
return nil, err
}
return &State{state{st}}, nil
}
func restore(fd uintptr, state *State) error {
return windows.SetConsoleMode(windows.Handle(fd), state.Mode)
}
func getSize(fd uintptr) (width, height int, err error) {
var info windows.ConsoleScreenBufferInfo
if err := windows.GetConsoleScreenBufferInfo(windows.Handle(fd), &info); err != nil {
return 0, 0, err
}
return int(info.Window.Right - info.Window.Left + 1), int(info.Window.Bottom - info.Window.Top + 1), nil
}
func readPassword(fd uintptr) ([]byte, error) {
var st uint32
if err := windows.GetConsoleMode(windows.Handle(fd), &st); err != nil {
return nil, err
}
old := st
st &^= (windows.ENABLE_ECHO_INPUT | windows.ENABLE_LINE_INPUT)
st |= (windows.ENABLE_PROCESSED_OUTPUT | windows.ENABLE_PROCESSED_INPUT)
if err := windows.SetConsoleMode(windows.Handle(fd), st); err != nil {
return nil, err
}
defer windows.SetConsoleMode(windows.Handle(fd), old)
var h windows.Handle
p, _ := windows.GetCurrentProcess()
if err := windows.DuplicateHandle(p, windows.Handle(fd), p, &h, 0, false, windows.DUPLICATE_SAME_ACCESS); err != nil {
return nil, err
}
f := os.NewFile(uintptr(h), "stdin")
defer f.Close()
return readPasswordLine(f)
}

126
vendor/github.com/charmbracelet/x/term/terminal.go generated vendored Normal file
View File

@@ -0,0 +1,126 @@
package term
import (
"image/color"
"io"
"time"
"github.com/charmbracelet/x/ansi"
"github.com/charmbracelet/x/input"
)
// File represents a file that has a file descriptor and can be read from,
// written to, and closed.
type File interface {
io.ReadWriteCloser
Fd() uintptr
}
// QueryBackgroundColor queries the terminal for the background color.
// If the terminal does not support querying the background color, nil is
// returned.
//
// Note: you will need to set the input to raw mode before calling this
// function.
//
// state, _ := term.MakeRaw(in.Fd())
// defer term.Restore(in.Fd(), state)
func QueryBackgroundColor(in io.Reader, out io.Writer) (c color.Color, err error) {
// nolint: errcheck
err = QueryTerminal(in, out, defaultQueryTimeout,
func(events []input.Event) bool {
for _, e := range events {
switch e := e.(type) {
case input.BackgroundColorEvent:
c = e.Color
continue // we need to consume the next DA1 event
case input.PrimaryDeviceAttributesEvent:
return false
}
}
return true
}, ansi.RequestBackgroundColor+ansi.RequestPrimaryDeviceAttributes)
return
}
// QueryKittyKeyboard returns the enabled Kitty keyboard protocol options.
// -1 means the terminal does not support the feature.
//
// Note: you will need to set the input to raw mode before calling this
// function.
//
// state, _ := term.MakeRaw(in.Fd())
// defer term.Restore(in.Fd(), state)
func QueryKittyKeyboard(in io.Reader, out io.Writer) (flags int, err error) {
flags = -1
// nolint: errcheck
err = QueryTerminal(in, out, defaultQueryTimeout,
func(events []input.Event) bool {
for _, e := range events {
switch event := e.(type) {
case input.KittyKeyboardEvent:
flags = int(event)
continue // we need to consume the next DA1 event
case input.PrimaryDeviceAttributesEvent:
return false
}
}
return true
}, ansi.RequestKittyKeyboard+ansi.RequestPrimaryDeviceAttributes)
return
}
const defaultQueryTimeout = time.Second * 2
// QueryTerminalFilter is a function that filters input events using a type
// switch. If false is returned, the QueryTerminal function will stop reading
// input.
type QueryTerminalFilter func(events []input.Event) bool
// QueryTerminal queries the terminal for support of various features and
// returns a list of response events.
// Most of the time, you will need to set stdin to raw mode before calling this
// function.
// Note: This function will block until the terminal responds or the timeout
// is reached.
func QueryTerminal(
in io.Reader,
out io.Writer,
timeout time.Duration,
filter QueryTerminalFilter,
query string,
) error {
rd, err := input.NewDriver(in, "", 0)
if err != nil {
return err
}
defer rd.Close() // nolint: errcheck
done := make(chan struct{}, 1)
defer close(done)
go func() {
select {
case <-done:
case <-time.After(timeout):
rd.Cancel()
}
}()
if _, err := io.WriteString(out, query); err != nil {
return err
}
for {
events, err := rd.ReadEvents()
if err != nil {
return err
}
if !filter(events) {
break
}
}
return nil
}

47
vendor/github.com/charmbracelet/x/term/util.go generated vendored Normal file
View File

@@ -0,0 +1,47 @@
package term
import (
"io"
"runtime"
)
// readPasswordLine reads from reader until it finds \n or io.EOF.
// The slice returned does not include the \n.
// readPasswordLine also ignores any \r it finds.
// Windows uses \r as end of line. So, on Windows, readPasswordLine
// reads until it finds \r and ignores any \n it finds during processing.
func readPasswordLine(reader io.Reader) ([]byte, error) {
var buf [1]byte
var ret []byte
for {
n, err := reader.Read(buf[:])
if n > 0 {
switch buf[0] {
case '\b':
if len(ret) > 0 {
ret = ret[:len(ret)-1]
}
case '\n':
if runtime.GOOS != "windows" {
return ret, nil
}
// otherwise ignore \n
case '\r':
if runtime.GOOS == "windows" {
return ret, nil
}
// otherwise ignore \r
default:
ret = append(ret, buf[0])
}
continue
}
if err != nil {
if err == io.EOF && len(ret) > 0 {
return ret, nil
}
return ret, err
}
}
}

21
vendor/github.com/charmbracelet/x/windows/LICENSE generated vendored Normal file
View File

@@ -0,0 +1,21 @@
MIT License
Copyright (c) 2023 Charmbracelet, Inc.
Permission is hereby granted, free of charge, to any person obtaining a copy
of this software and associated documentation files (the "Software"), to deal
in the Software without restriction, including without limitation the rights
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
copies of the Software, and to permit persons to whom the Software is
furnished to do so, subject to the following conditions:
The above copyright notice and this permission notice shall be included in all
copies or substantial portions of the Software.
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
SOFTWARE.

3
vendor/github.com/charmbracelet/x/windows/doc.go generated vendored Normal file
View File

@@ -0,0 +1,3 @@
package windows
//go generate go run golang.org/x/sys/windows/mkwinsyscall -output zsyscall_windows.go syscall_windows.go

View File

@@ -0,0 +1,10 @@
package windows
import "golang.org/x/sys/windows"
var NewLazySystemDLL = windows.NewLazySystemDLL
type Handle = windows.Handle
//sys GetKeyboardLayout(threadId uint32) (hkl Handle, err error) = user32.GetKeyboardLayout
//sys ToUnicodeEx(vkey uint32, scancode uint32, keystate *byte, pwszBuff *uint16, cchBuff int32, flags uint32, hkl Handle) (ret int32, err error) = user32.ToUnicodeEx

View File

@@ -0,0 +1,61 @@
// Code generated by 'go generate'; DO NOT EDIT.
package windows
import (
"syscall"
"unsafe"
)
var _ unsafe.Pointer
// Do the interface allocations only once for common
// Errno values.
const (
errnoERROR_IO_PENDING = 997
)
var (
errERROR_IO_PENDING error = syscall.Errno(errnoERROR_IO_PENDING)
errERROR_EINVAL error = syscall.EINVAL
)
// errnoErr returns common boxed Errno values, to prevent
// allocations at runtime.
func errnoErr(e syscall.Errno) error {
switch e {
case 0:
return errERROR_EINVAL
case errnoERROR_IO_PENDING:
return errERROR_IO_PENDING
}
// TODO: add more here, after collecting data on the common
// error values see on Windows. (perhaps when running
// all.bat?)
return e
}
var (
moduser32 = NewLazySystemDLL("user32.dll")
procGetKeyboardLayout = moduser32.NewProc("GetKeyboardLayout")
procToUnicodeEx = moduser32.NewProc("ToUnicodeEx")
)
func GetKeyboardLayout(threadId uint32) (hkl Handle, err error) {
r0, _, e1 := syscall.Syscall(procGetKeyboardLayout.Addr(), 1, uintptr(threadId), 0, 0)
hkl = Handle(r0)
if hkl == 0 {
err = errnoErr(e1)
}
return
}
func ToUnicodeEx(vkey uint32, scancode uint32, keystate *byte, pwszBuff *uint16, cchBuff int32, flags uint32, hkl Handle) (ret int32, err error) {
r0, _, e1 := syscall.Syscall9(procToUnicodeEx.Addr(), 7, uintptr(vkey), uintptr(scancode), uintptr(unsafe.Pointer(keystate)), uintptr(unsafe.Pointer(pwszBuff)), uintptr(cchBuff), uintptr(flags), uintptr(hkl), 0, 0)
ret = int32(r0)
if ret == 0 {
err = errnoErr(e1)
}
return
}