Add vendor
Specifically because I want to modify a dependency
This commit is contained in:
21
vendor/github.com/charmbracelet/x/ansi/LICENSE
generated
vendored
Normal file
21
vendor/github.com/charmbracelet/x/ansi/LICENSE
generated
vendored
Normal 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
11
vendor/github.com/charmbracelet/x/ansi/ansi.go
generated
vendored
Normal 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
8
vendor/github.com/charmbracelet/x/ansi/ascii.go
generated
vendored
Normal 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
61
vendor/github.com/charmbracelet/x/ansi/background.go
generated
vendored
Normal 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
72
vendor/github.com/charmbracelet/x/ansi/c0.go
generated
vendored
Normal 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
72
vendor/github.com/charmbracelet/x/ansi/c1.go
generated
vendored
Normal 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
75
vendor/github.com/charmbracelet/x/ansi/clipboard.go
generated
vendored
Normal 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
196
vendor/github.com/charmbracelet/x/ansi/color.go
generated
vendored
Normal 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
141
vendor/github.com/charmbracelet/x/ansi/csi.go
generated
vendored
Normal 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
17
vendor/github.com/charmbracelet/x/ansi/ctrl.go
generated
vendored
Normal 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
190
vendor/github.com/charmbracelet/x/ansi/cursor.go
generated
vendored
Normal 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
148
vendor/github.com/charmbracelet/x/ansi/dcs.go
generated
vendored
Normal 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
7
vendor/github.com/charmbracelet/x/ansi/doc.go
generated
vendored
Normal 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
28
vendor/github.com/charmbracelet/x/ansi/hyperlink.go
generated
vendored
Normal 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
58
vendor/github.com/charmbracelet/x/ansi/kitty.go
generated
vendored
Normal 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
132
vendor/github.com/charmbracelet/x/ansi/mode.go
generated
vendored
Normal 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
69
vendor/github.com/charmbracelet/x/ansi/osc.go
generated
vendored
Normal 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
45
vendor/github.com/charmbracelet/x/ansi/params.go
generated
vendored
Normal 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
357
vendor/github.com/charmbracelet/x/ansi/parser.go
generated
vendored
Normal 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
78
vendor/github.com/charmbracelet/x/ansi/parser/const.go
generated
vendored
Normal 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
136
vendor/github.com/charmbracelet/x/ansi/parser/seq.go
generated
vendored
Normal 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
|
||||
}
|
||||
}
|
||||
}
|
||||
269
vendor/github.com/charmbracelet/x/ansi/parser/transition_table.go
generated
vendored
Normal file
269
vendor/github.com/charmbracelet/x/ansi/parser/transition_table.go
generated
vendored
Normal 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
63
vendor/github.com/charmbracelet/x/ansi/passthrough.go
generated
vendored
Normal 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
126
vendor/github.com/charmbracelet/x/ansi/screen.go
generated
vendored
Normal 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
199
vendor/github.com/charmbracelet/x/ansi/sequence.go
generated
vendored
Normal 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
296
vendor/github.com/charmbracelet/x/ansi/style.go
generated
vendored
Normal 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
31
vendor/github.com/charmbracelet/x/ansi/termcap.go
generated
vendored
Normal 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
32
vendor/github.com/charmbracelet/x/ansi/title.go
generated
vendored
Normal 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
112
vendor/github.com/charmbracelet/x/ansi/truncate.go
generated
vendored
Normal 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
29
vendor/github.com/charmbracelet/x/ansi/util.go
generated
vendored
Normal 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
103
vendor/github.com/charmbracelet/x/ansi/width.go
generated
vendored
Normal 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
406
vendor/github.com/charmbracelet/x/ansi/wrap.go
generated
vendored
Normal 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
33
vendor/github.com/charmbracelet/x/ansi/xterm.go
generated
vendored
Normal 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
21
vendor/github.com/charmbracelet/x/input/LICENSE
generated
vendored
Normal 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.
|
||||
14
vendor/github.com/charmbracelet/x/input/cancelreader_other.go
generated
vendored
Normal file
14
vendor/github.com/charmbracelet/x/input/cancelreader_other.go
generated
vendored
Normal 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)
|
||||
}
|
||||
217
vendor/github.com/charmbracelet/x/input/cancelreader_windows.go
generated
vendored
Normal file
217
vendor/github.com/charmbracelet/x/input/cancelreader_windows.go
generated
vendored
Normal 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
9
vendor/github.com/charmbracelet/x/input/clipboard.go
generated
vendored
Normal 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
77
vendor/github.com/charmbracelet/x/input/color.go
generated
vendored
Normal 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
10
vendor/github.com/charmbracelet/x/input/cursor.go
generated
vendored
Normal 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
18
vendor/github.com/charmbracelet/x/input/da1.go
generated
vendored
Normal 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
125
vendor/github.com/charmbracelet/x/input/driver.go
generated
vendored
Normal 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
|
||||
}
|
||||
11
vendor/github.com/charmbracelet/x/input/driver_other.go
generated
vendored
Normal file
11
vendor/github.com/charmbracelet/x/input/driver_other.go
generated
vendored
Normal 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()
|
||||
}
|
||||
276
vendor/github.com/charmbracelet/x/input/driver_windows.go
generated
vendored
Normal file
276
vendor/github.com/charmbracelet/x/input/driver_windows.go
generated
vendored
Normal 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
47
vendor/github.com/charmbracelet/x/input/input.go
generated
vendored
Normal 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
410
vendor/github.com/charmbracelet/x/input/key.go
generated
vendored
Normal 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
281
vendor/github.com/charmbracelet/x/input/kitty.go
generated
vendored
Normal 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
70
vendor/github.com/charmbracelet/x/input/mod.go
generated
vendored
Normal 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
12
vendor/github.com/charmbracelet/x/input/mode.go
generated
vendored
Normal 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
248
vendor/github.com/charmbracelet/x/input/mouse.go
generated
vendored
Normal 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
836
vendor/github.com/charmbracelet/x/input/parse.go
generated
vendored
Normal 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
12
vendor/github.com/charmbracelet/x/input/paste.go
generated
vendored
Normal 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
45
vendor/github.com/charmbracelet/x/input/seq.go
generated
vendored
Normal 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
388
vendor/github.com/charmbracelet/x/input/table.go
generated
vendored
Normal 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
49
vendor/github.com/charmbracelet/x/input/termcap.go
generated
vendored
Normal 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
277
vendor/github.com/charmbracelet/x/input/terminfo.go
generated
vendored
Normal 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
240
vendor/github.com/charmbracelet/x/input/win32input.go
generated
vendored
Normal 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
40
vendor/github.com/charmbracelet/x/input/xterm.go
generated
vendored
Normal 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
21
vendor/github.com/charmbracelet/x/term/LICENSE
generated
vendored
Normal 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
49
vendor/github.com/charmbracelet/x/term/term.go
generated
vendored
Normal 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
39
vendor/github.com/charmbracelet/x/term/term_other.go
generated
vendored
Normal 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
96
vendor/github.com/charmbracelet/x/term/term_unix.go
generated
vendored
Normal 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))
|
||||
}
|
||||
11
vendor/github.com/charmbracelet/x/term/term_unix_bsd.go
generated
vendored
Normal file
11
vendor/github.com/charmbracelet/x/term/term_unix_bsd.go
generated
vendored
Normal 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
|
||||
)
|
||||
11
vendor/github.com/charmbracelet/x/term/term_unix_other.go
generated
vendored
Normal file
11
vendor/github.com/charmbracelet/x/term/term_unix_other.go
generated
vendored
Normal 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
86
vendor/github.com/charmbracelet/x/term/term_windows.go
generated
vendored
Normal 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
126
vendor/github.com/charmbracelet/x/term/terminal.go
generated
vendored
Normal 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
47
vendor/github.com/charmbracelet/x/term/util.go
generated
vendored
Normal 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
21
vendor/github.com/charmbracelet/x/windows/LICENSE
generated
vendored
Normal 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
3
vendor/github.com/charmbracelet/x/windows/doc.go
generated
vendored
Normal file
@@ -0,0 +1,3 @@
|
||||
package windows
|
||||
|
||||
//go generate go run golang.org/x/sys/windows/mkwinsyscall -output zsyscall_windows.go syscall_windows.go
|
||||
10
vendor/github.com/charmbracelet/x/windows/syscall_windows.go
generated
vendored
Normal file
10
vendor/github.com/charmbracelet/x/windows/syscall_windows.go
generated
vendored
Normal 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
|
||||
61
vendor/github.com/charmbracelet/x/windows/zsyscall_windows.go
generated
vendored
Normal file
61
vendor/github.com/charmbracelet/x/windows/zsyscall_windows.go
generated
vendored
Normal 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
|
||||
}
|
||||
Reference in New Issue
Block a user