feat: initial commit
This commit is contained in:
commit
a161b86c9a
705 changed files with 288162 additions and 0 deletions
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) //nolint:wrapcheck
|
||||
}
|
||||
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
|
||||
)
|
||||
178
vendor/github.com/charmbracelet/x/ansi/background.go
generated
vendored
Normal file
178
vendor/github.com/charmbracelet/x/ansi/background.go
generated
vendored
Normal file
|
|
@ -0,0 +1,178 @@
|
|||
package ansi
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"image/color"
|
||||
|
||||
"github.com/lucasb-eyer/go-colorful"
|
||||
)
|
||||
|
||||
// HexColor is a [color.Color] that can be formatted as a hex string.
|
||||
type HexColor string
|
||||
|
||||
// RGBA returns the RGBA values of the color.
|
||||
func (h HexColor) RGBA() (r, g, b, a uint32) {
|
||||
hex := h.color()
|
||||
if hex == nil {
|
||||
return 0, 0, 0, 0
|
||||
}
|
||||
return hex.RGBA()
|
||||
}
|
||||
|
||||
// Hex returns the hex representation of the color. If the color is invalid, it
|
||||
// returns an empty string.
|
||||
func (h HexColor) Hex() string {
|
||||
hex := h.color()
|
||||
if hex == nil {
|
||||
return ""
|
||||
}
|
||||
return hex.Hex()
|
||||
}
|
||||
|
||||
// String returns the color as a hex string. If the color is nil, an empty
|
||||
// string is returned.
|
||||
func (h HexColor) String() string {
|
||||
return h.Hex()
|
||||
}
|
||||
|
||||
// color returns the underlying color of the HexColor.
|
||||
func (h HexColor) color() *colorful.Color {
|
||||
hex, err := colorful.Hex(string(h))
|
||||
if err != nil {
|
||||
return nil
|
||||
}
|
||||
return &hex
|
||||
}
|
||||
|
||||
// XRGBColor is a [color.Color] that can be formatted as an XParseColor
|
||||
// rgb: string.
|
||||
//
|
||||
// See: https://linux.die.net/man/3/xparsecolor
|
||||
type XRGBColor struct {
|
||||
color.Color
|
||||
}
|
||||
|
||||
// RGBA returns the RGBA values of the color.
|
||||
func (x XRGBColor) RGBA() (r, g, b, a uint32) {
|
||||
if x.Color == nil {
|
||||
return 0, 0, 0, 0
|
||||
}
|
||||
return x.Color.RGBA()
|
||||
}
|
||||
|
||||
// String returns the color as an XParseColor rgb: string. If the color is nil,
|
||||
// an empty string is returned.
|
||||
func (x XRGBColor) String() string {
|
||||
if x.Color == nil {
|
||||
return ""
|
||||
}
|
||||
r, g, b, _ := x.Color.RGBA()
|
||||
// Get the lower 8 bits
|
||||
return fmt.Sprintf("rgb:%04x/%04x/%04x", r, g, b)
|
||||
}
|
||||
|
||||
// XRGBAColor is a [color.Color] that can be formatted as an XParseColor
|
||||
// rgba: string.
|
||||
//
|
||||
// See: https://linux.die.net/man/3/xparsecolor
|
||||
type XRGBAColor struct {
|
||||
color.Color
|
||||
}
|
||||
|
||||
// RGBA returns the RGBA values of the color.
|
||||
func (x XRGBAColor) RGBA() (r, g, b, a uint32) {
|
||||
if x.Color == nil {
|
||||
return 0, 0, 0, 0
|
||||
}
|
||||
return x.Color.RGBA()
|
||||
}
|
||||
|
||||
// String returns the color as an XParseColor rgba: string. If the color is nil,
|
||||
// an empty string is returned.
|
||||
func (x XRGBAColor) String() string {
|
||||
if x.Color == nil {
|
||||
return ""
|
||||
}
|
||||
r, g, b, a := x.RGBA()
|
||||
// Get the lower 8 bits
|
||||
return fmt.Sprintf("rgba:%04x/%04x/%04x/%04x", r, g, b, a)
|
||||
}
|
||||
|
||||
// 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. Most terminals support hex,
|
||||
// XParseColor rgb: and rgba: strings. You could use [HexColor], [XRGBColor],
|
||||
// or [XRGBAColor] to format the color.
|
||||
//
|
||||
// See: https://invisible-island.net/xterm/ctlseqs/ctlseqs.html#h3-Operating-System-Commands
|
||||
func SetForegroundColor(s string) string {
|
||||
return "\x1b]10;" + s + "\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"
|
||||
|
||||
// ResetForegroundColor is a sequence that resets the default terminal
|
||||
// foreground color.
|
||||
//
|
||||
// See: https://invisible-island.net/xterm/ctlseqs/ctlseqs.html#h3-Operating-System-Commands
|
||||
const ResetForegroundColor = "\x1b]110\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. Most terminals support hex,
|
||||
// XParseColor rgb: and rgba: strings. You could use [HexColor], [XRGBColor],
|
||||
// or [XRGBAColor] to format the color.
|
||||
//
|
||||
// See: https://invisible-island.net/xterm/ctlseqs/ctlseqs.html#h3-Operating-System-Commands
|
||||
func SetBackgroundColor(s string) string {
|
||||
return "\x1b]11;" + s + "\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"
|
||||
|
||||
// ResetBackgroundColor is a sequence that resets the default terminal
|
||||
// background color.
|
||||
//
|
||||
// See: https://invisible-island.net/xterm/ctlseqs/ctlseqs.html#h3-Operating-System-Commands
|
||||
const ResetBackgroundColor = "\x1b]111\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. Most terminals support hex,
|
||||
// XParseColor rgb: and rgba: strings. You could use [HexColor], [XRGBColor],
|
||||
// or [XRGBAColor] to format the color.
|
||||
//
|
||||
// See: https://invisible-island.net/xterm/ctlseqs/ctlseqs.html#h3-Operating-System-Commands
|
||||
func SetCursorColor(s string) string {
|
||||
return "\x1b]12;" + s + "\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"
|
||||
|
||||
// ResetCursorColor is a sequence that resets the terminal cursor color.
|
||||
//
|
||||
// See: https://invisible-island.net/xterm/ctlseqs/ctlseqs.html#h3-Operating-System-Commands
|
||||
const ResetCursorColor = "\x1b]112\x07"
|
||||
79
vendor/github.com/charmbracelet/x/ansi/c0.go
generated
vendored
Normal file
79
vendor/github.com/charmbracelet/x/ansi/c0.go
generated
vendored
Normal file
|
|
@ -0,0 +1,79 @@
|
|||
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
|
||||
|
||||
// LS0 is the locking shift 0 character.
|
||||
// This is an alias for [SI].
|
||||
LS0 = SI
|
||||
// LS1 is the locking shift 1 character.
|
||||
// This is an alias for [SO].
|
||||
LS1 = SO
|
||||
)
|
||||
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
|
||||
)
|
||||
55
vendor/github.com/charmbracelet/x/ansi/charset.go
generated
vendored
Normal file
55
vendor/github.com/charmbracelet/x/ansi/charset.go
generated
vendored
Normal file
|
|
@ -0,0 +1,55 @@
|
|||
package ansi
|
||||
|
||||
// SelectCharacterSet sets the G-set character designator to the specified
|
||||
// character set.
|
||||
//
|
||||
// ESC Ps Pd
|
||||
//
|
||||
// Where Ps is the G-set character designator, and Pd is the identifier.
|
||||
// For 94-character sets, the designator can be one of:
|
||||
// - ( G0
|
||||
// - ) G1
|
||||
// - * G2
|
||||
// - + G3
|
||||
//
|
||||
// For 96-character sets, the designator can be one of:
|
||||
// - - G1
|
||||
// - . G2
|
||||
// - / G3
|
||||
//
|
||||
// Some common 94-character sets are:
|
||||
// - 0 DEC Special Drawing Set
|
||||
// - A United Kingdom (UK)
|
||||
// - B United States (USASCII)
|
||||
//
|
||||
// Examples:
|
||||
//
|
||||
// ESC ( B Select character set G0 = United States (USASCII)
|
||||
// ESC ( 0 Select character set G0 = Special Character and Line Drawing Set
|
||||
// ESC ) 0 Select character set G1 = Special Character and Line Drawing Set
|
||||
// ESC * A Select character set G2 = United Kingdom (UK)
|
||||
//
|
||||
// See: https://vt100.net/docs/vt510-rm/SCS.html
|
||||
func SelectCharacterSet(gset byte, charset byte) string {
|
||||
return "\x1b" + string(gset) + string(charset)
|
||||
}
|
||||
|
||||
// SCS is an alias for SelectCharacterSet.
|
||||
func SCS(gset byte, charset byte) string {
|
||||
return SelectCharacterSet(gset, charset)
|
||||
}
|
||||
|
||||
// LS1R (Locking Shift 1 Right) shifts G1 into GR character set.
|
||||
const LS1R = "\x1b~"
|
||||
|
||||
// LS2 (Locking Shift 2) shifts G2 into GL character set.
|
||||
const LS2 = "\x1bn"
|
||||
|
||||
// LS2R (Locking Shift 2 Right) shifts G2 into GR character set.
|
||||
const LS2R = "\x1b}"
|
||||
|
||||
// LS3 (Locking Shift 3) shifts G3 into GL character set.
|
||||
const LS3 = "\x1bo"
|
||||
|
||||
// LS3R (Locking Shift 3 Right) shifts G3 into GR character set.
|
||||
const LS3R = "\x1b|"
|
||||
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"
|
||||
784
vendor/github.com/charmbracelet/x/ansi/color.go
generated
vendored
Normal file
784
vendor/github.com/charmbracelet/x/ansi/color.go
generated
vendored
Normal file
|
|
@ -0,0 +1,784 @@
|
|||
package ansi
|
||||
|
||||
import (
|
||||
"image/color"
|
||||
|
||||
"github.com/lucasb-eyer/go-colorful"
|
||||
)
|
||||
|
||||
// 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
|
||||
}
|
||||
|
||||
return ansiToRGB(byte(ansi)).RGBA()
|
||||
}
|
||||
|
||||
// IndexedColor is an ANSI 256 (8-bit) color with a value from 0 to 255.
|
||||
type IndexedColor uint8
|
||||
|
||||
var _ Color = IndexedColor(0)
|
||||
|
||||
// RGBA returns the red, green, blue and alpha components of the color. It
|
||||
// satisfies the color.Color interface.
|
||||
func (c IndexedColor) RGBA() (uint32, uint32, uint32, uint32) {
|
||||
return ansiToRGB(byte(c)).RGBA()
|
||||
}
|
||||
|
||||
// ExtendedColor is an ANSI 256 (8-bit) color with a value from 0 to 255.
|
||||
//
|
||||
// Deprecated: use [IndexedColor] instead.
|
||||
type ExtendedColor = IndexedColor
|
||||
|
||||
// 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)
|
||||
//
|
||||
// Deprecated: use [RGBColor] instead.
|
||||
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)
|
||||
}
|
||||
|
||||
// RGBColor is a 24-bit color that can be used in the terminal.
|
||||
// This can be used to represent RGB colors.
|
||||
type RGBColor struct {
|
||||
R uint8
|
||||
G uint8
|
||||
B uint8
|
||||
}
|
||||
|
||||
// RGBA returns the red, green, blue and alpha components of the color. It
|
||||
// satisfies the color.Color interface.
|
||||
func (c RGBColor) RGBA() (uint32, uint32, uint32, uint32) {
|
||||
return toRGBA(uint32(c.R), uint32(c.G), uint32(c.B))
|
||||
}
|
||||
|
||||
// ansiToRGB converts an ANSI color to a 24-bit RGB color.
|
||||
//
|
||||
// r, g, b := ansiToRGB(57)
|
||||
func ansiToRGB(ansi byte) color.Color {
|
||||
return ansiHex[ansi]
|
||||
}
|
||||
|
||||
// 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 & 0xff, 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
|
||||
}
|
||||
|
||||
//nolint:unused
|
||||
func distSq(r1, g1, b1, r2, g2, b2 int) int {
|
||||
return ((r1-r2)*(r1-r2) + (g1-g2)*(g1-g2) + (b1-b2)*(b1-b2))
|
||||
}
|
||||
|
||||
func to6Cube[T int | float64](v T) int {
|
||||
if v < 48 {
|
||||
return 0
|
||||
}
|
||||
if v < 115 {
|
||||
return 1
|
||||
}
|
||||
return int((v - 35) / 40)
|
||||
}
|
||||
|
||||
// Convert256 converts a [color.Color], usually a 24-bit color, to xterm(1) 256
|
||||
// color palette.
|
||||
//
|
||||
// xterm provides a 6x6x6 color cube (16 - 231) and 24 greys (232 - 255). We
|
||||
// map our RGB color to the closest in the cube, also work out the closest
|
||||
// grey, and use the nearest of the two based on the lightness of the color.
|
||||
//
|
||||
// Note that the xterm has much lower resolution for darker colors (they are
|
||||
// not evenly spread out), so our 6 levels are not evenly spread: 0x0, 0x5f
|
||||
// (95), 0x87 (135), 0xaf (175), 0xd7 (215) and 0xff (255). Greys are more
|
||||
// evenly spread (8, 18, 28 ... 238).
|
||||
func Convert256(c color.Color) IndexedColor {
|
||||
// If the color is already an IndexedColor, return it.
|
||||
if i, ok := c.(IndexedColor); ok {
|
||||
return i
|
||||
}
|
||||
|
||||
// Note: this is mostly ported from tmux/colour.c.
|
||||
col, ok := colorful.MakeColor(c)
|
||||
if !ok {
|
||||
return IndexedColor(0)
|
||||
}
|
||||
|
||||
r := col.R * 255
|
||||
g := col.G * 255
|
||||
b := col.B * 255
|
||||
|
||||
q2c := [6]int{0x00, 0x5f, 0x87, 0xaf, 0xd7, 0xff}
|
||||
|
||||
// Map RGB to 6x6x6 cube.
|
||||
qr := to6Cube(r)
|
||||
cr := q2c[qr]
|
||||
qg := to6Cube(g)
|
||||
cg := q2c[qg]
|
||||
qb := to6Cube(b)
|
||||
cb := q2c[qb]
|
||||
|
||||
// If we have hit the color exactly, return early.
|
||||
ci := (36 * qr) + (6 * qg) + qb
|
||||
if cr == int(r) && cg == int(g) && cb == int(b) {
|
||||
return IndexedColor(16 + ci) //nolint:gosec
|
||||
}
|
||||
|
||||
// Work out the closest grey (average of RGB).
|
||||
greyAvg := int(r+g+b) / 3
|
||||
var greyIdx int
|
||||
if greyAvg > 238 {
|
||||
greyIdx = 23
|
||||
} else {
|
||||
greyIdx = (greyAvg - 3) / 10
|
||||
}
|
||||
grey := 8 + (10 * greyIdx)
|
||||
|
||||
// Return the one which is nearer to the original input rgb value
|
||||
// XXX: This is where it differs from tmux's implementation, we prefer the
|
||||
// closer color to the original in terms of light distances rather than the
|
||||
// cube distance.
|
||||
c2 := colorful.Color{R: float64(cr) / 255.0, G: float64(cg) / 255.0, B: float64(cb) / 255.0}
|
||||
g2 := colorful.Color{R: float64(grey) / 255.0, G: float64(grey) / 255.0, B: float64(grey) / 255.0}
|
||||
colorDist := col.DistanceHSLuv(c2)
|
||||
grayDist := col.DistanceHSLuv(g2)
|
||||
|
||||
if colorDist <= grayDist {
|
||||
return IndexedColor(16 + ci) //nolint:gosec
|
||||
}
|
||||
return IndexedColor(232 + greyIdx) //nolint:gosec
|
||||
|
||||
// // Is grey or 6x6x6 color closest?
|
||||
// d := distSq(cr, cg, cb, int(r), int(g), int(b))
|
||||
// if distSq(grey, grey, grey, int(r), int(g), int(b)) < d {
|
||||
// return IndexedColor(232 + greyIdx) //nolint:gosec
|
||||
// }
|
||||
// return IndexedColor(16 + ci) //nolint:gosec
|
||||
}
|
||||
|
||||
// Convert16 converts a [color.Color] to a 16-color ANSI color. It will first
|
||||
// try to find a match in the 256 xterm(1) color palette, and then map that to
|
||||
// the 16-color ANSI palette.
|
||||
func Convert16(c color.Color) BasicColor {
|
||||
switch c := c.(type) {
|
||||
case BasicColor:
|
||||
// If the color is already a BasicColor, return it.
|
||||
return c
|
||||
case IndexedColor:
|
||||
// If the color is already an IndexedColor, return the corresponding
|
||||
// BasicColor.
|
||||
return ansi256To16[c]
|
||||
default:
|
||||
c256 := Convert256(c)
|
||||
return ansi256To16[c256]
|
||||
}
|
||||
}
|
||||
|
||||
// RGB values of ANSI colors (0-255).
|
||||
var ansiHex = [...]color.RGBA{
|
||||
0: {R: 0x00, G: 0x00, B: 0x00, A: 0xff}, // "#000000"
|
||||
1: {R: 0x80, G: 0x00, B: 0x00, A: 0xff}, // "#800000"
|
||||
2: {R: 0x00, G: 0x80, B: 0x00, A: 0xff}, // "#008000"
|
||||
3: {R: 0x80, G: 0x80, B: 0x00, A: 0xff}, // "#808000"
|
||||
4: {R: 0x00, G: 0x00, B: 0x80, A: 0xff}, // "#000080"
|
||||
5: {R: 0x80, G: 0x00, B: 0x80, A: 0xff}, // "#800080"
|
||||
6: {R: 0x00, G: 0x80, B: 0x80, A: 0xff}, // "#008080"
|
||||
7: {R: 0xc0, G: 0xc0, B: 0xc0, A: 0xff}, // "#c0c0c0"
|
||||
8: {R: 0x80, G: 0x80, B: 0x80, A: 0xff}, // "#808080"
|
||||
9: {R: 0xff, G: 0x00, B: 0x00, A: 0xff}, // "#ff0000"
|
||||
10: {R: 0x00, G: 0xff, B: 0x00, A: 0xff}, // "#00ff00"
|
||||
11: {R: 0xff, G: 0xff, B: 0x00, A: 0xff}, // "#ffff00"
|
||||
12: {R: 0x00, G: 0x00, B: 0xff, A: 0xff}, // "#0000ff"
|
||||
13: {R: 0xff, G: 0x00, B: 0xff, A: 0xff}, // "#ff00ff"
|
||||
14: {R: 0x00, G: 0xff, B: 0xff, A: 0xff}, // "#00ffff"
|
||||
15: {R: 0xff, G: 0xff, B: 0xff, A: 0xff}, // "#ffffff"
|
||||
16: {R: 0x00, G: 0x00, B: 0x00, A: 0xff}, // "#000000"
|
||||
17: {R: 0x00, G: 0x00, B: 0x5f, A: 0xff}, // "#00005f"
|
||||
18: {R: 0x00, G: 0x00, B: 0x87, A: 0xff}, // "#000087"
|
||||
19: {R: 0x00, G: 0x00, B: 0xaf, A: 0xff}, // "#0000af"
|
||||
20: {R: 0x00, G: 0x00, B: 0xd7, A: 0xff}, // "#0000d7"
|
||||
21: {R: 0x00, G: 0x00, B: 0xff, A: 0xff}, // "#0000ff"
|
||||
22: {R: 0x00, G: 0x5f, B: 0x00, A: 0xff}, // "#005f00"
|
||||
23: {R: 0x00, G: 0x5f, B: 0x5f, A: 0xff}, // "#005f5f"
|
||||
24: {R: 0x00, G: 0x5f, B: 0x87, A: 0xff}, // "#005f87"
|
||||
25: {R: 0x00, G: 0x5f, B: 0xaf, A: 0xff}, // "#005faf"
|
||||
26: {R: 0x00, G: 0x5f, B: 0xd7, A: 0xff}, // "#005fd7"
|
||||
27: {R: 0x00, G: 0x5f, B: 0xff, A: 0xff}, // "#005fff"
|
||||
28: {R: 0x00, G: 0x87, B: 0x00, A: 0xff}, // "#008700"
|
||||
29: {R: 0x00, G: 0x87, B: 0x5f, A: 0xff}, // "#00875f"
|
||||
30: {R: 0x00, G: 0x87, B: 0x87, A: 0xff}, // "#008787"
|
||||
31: {R: 0x00, G: 0x87, B: 0xaf, A: 0xff}, // "#0087af"
|
||||
32: {R: 0x00, G: 0x87, B: 0xd7, A: 0xff}, // "#0087d7"
|
||||
33: {R: 0x00, G: 0x87, B: 0xff, A: 0xff}, // "#0087ff"
|
||||
34: {R: 0x00, G: 0xaf, B: 0x00, A: 0xff}, // "#00af00"
|
||||
35: {R: 0x00, G: 0xaf, B: 0x5f, A: 0xff}, // "#00af5f"
|
||||
36: {R: 0x00, G: 0xaf, B: 0x87, A: 0xff}, // "#00af87"
|
||||
37: {R: 0x00, G: 0xaf, B: 0xaf, A: 0xff}, // "#00afaf"
|
||||
38: {R: 0x00, G: 0xaf, B: 0xd7, A: 0xff}, // "#00afd7"
|
||||
39: {R: 0x00, G: 0xaf, B: 0xff, A: 0xff}, // "#00afff"
|
||||
40: {R: 0x00, G: 0xd7, B: 0x00, A: 0xff}, // "#00d700"
|
||||
41: {R: 0x00, G: 0xd7, B: 0x5f, A: 0xff}, // "#00d75f"
|
||||
42: {R: 0x00, G: 0xd7, B: 0x87, A: 0xff}, // "#00d787"
|
||||
43: {R: 0x00, G: 0xd7, B: 0xaf, A: 0xff}, // "#00d7af"
|
||||
44: {R: 0x00, G: 0xd7, B: 0xd7, A: 0xff}, // "#00d7d7"
|
||||
45: {R: 0x00, G: 0xd7, B: 0xff, A: 0xff}, // "#00d7ff"
|
||||
46: {R: 0x00, G: 0xff, B: 0x00, A: 0xff}, // "#00ff00"
|
||||
47: {R: 0x00, G: 0xff, B: 0x5f, A: 0xff}, // "#00ff5f"
|
||||
48: {R: 0x00, G: 0xff, B: 0x87, A: 0xff}, // "#00ff87"
|
||||
49: {R: 0x00, G: 0xff, B: 0xaf, A: 0xff}, // "#00ffaf"
|
||||
50: {R: 0x00, G: 0xff, B: 0xd7, A: 0xff}, // "#00ffd7"
|
||||
51: {R: 0x00, G: 0xff, B: 0xff, A: 0xff}, // "#00ffff"
|
||||
52: {R: 0x5f, G: 0x00, B: 0x00, A: 0xff}, // "#5f0000"
|
||||
53: {R: 0x5f, G: 0x00, B: 0x5f, A: 0xff}, // "#5f005f"
|
||||
54: {R: 0x5f, G: 0x00, B: 0x87, A: 0xff}, // "#5f0087"
|
||||
55: {R: 0x5f, G: 0x00, B: 0xaf, A: 0xff}, // "#5f00af"
|
||||
56: {R: 0x5f, G: 0x00, B: 0xd7, A: 0xff}, // "#5f00d7"
|
||||
57: {R: 0x5f, G: 0x00, B: 0xff, A: 0xff}, // "#5f00ff"
|
||||
58: {R: 0x5f, G: 0x5f, B: 0x00, A: 0xff}, // "#5f5f00"
|
||||
59: {R: 0x5f, G: 0x5f, B: 0x5f, A: 0xff}, // "#5f5f5f"
|
||||
60: {R: 0x5f, G: 0x5f, B: 0x87, A: 0xff}, // "#5f5f87"
|
||||
61: {R: 0x5f, G: 0x5f, B: 0xaf, A: 0xff}, // "#5f5faf"
|
||||
62: {R: 0x5f, G: 0x5f, B: 0xd7, A: 0xff}, // "#5f5fd7"
|
||||
63: {R: 0x5f, G: 0x5f, B: 0xff, A: 0xff}, // "#5f5fff"
|
||||
64: {R: 0x5f, G: 0x87, B: 0x00, A: 0xff}, // "#5f8700"
|
||||
65: {R: 0x5f, G: 0x87, B: 0x5f, A: 0xff}, // "#5f875f"
|
||||
66: {R: 0x5f, G: 0x87, B: 0x87, A: 0xff}, // "#5f8787"
|
||||
67: {R: 0x5f, G: 0x87, B: 0xaf, A: 0xff}, // "#5f87af"
|
||||
68: {R: 0x5f, G: 0x87, B: 0xd7, A: 0xff}, // "#5f87d7"
|
||||
69: {R: 0x5f, G: 0x87, B: 0xff, A: 0xff}, // "#5f87ff"
|
||||
70: {R: 0x5f, G: 0xaf, B: 0x00, A: 0xff}, // "#5faf00"
|
||||
71: {R: 0x5f, G: 0xaf, B: 0x5f, A: 0xff}, // "#5faf5f"
|
||||
72: {R: 0x5f, G: 0xaf, B: 0x87, A: 0xff}, // "#5faf87"
|
||||
73: {R: 0x5f, G: 0xaf, B: 0xaf, A: 0xff}, // "#5fafaf"
|
||||
74: {R: 0x5f, G: 0xaf, B: 0xd7, A: 0xff}, // "#5fafd7"
|
||||
75: {R: 0x5f, G: 0xaf, B: 0xff, A: 0xff}, // "#5fafff"
|
||||
76: {R: 0x5f, G: 0xd7, B: 0x00, A: 0xff}, // "#5fd700"
|
||||
77: {R: 0x5f, G: 0xd7, B: 0x5f, A: 0xff}, // "#5fd75f"
|
||||
78: {R: 0x5f, G: 0xd7, B: 0x87, A: 0xff}, // "#5fd787"
|
||||
79: {R: 0x5f, G: 0xd7, B: 0xaf, A: 0xff}, // "#5fd7af"
|
||||
80: {R: 0x5f, G: 0xd7, B: 0xd7, A: 0xff}, // "#5fd7d7"
|
||||
81: {R: 0x5f, G: 0xd7, B: 0xff, A: 0xff}, // "#5fd7ff"
|
||||
82: {R: 0x5f, G: 0xff, B: 0x00, A: 0xff}, // "#5fff00"
|
||||
83: {R: 0x5f, G: 0xff, B: 0x5f, A: 0xff}, // "#5fff5f"
|
||||
84: {R: 0x5f, G: 0xff, B: 0x87, A: 0xff}, // "#5fff87"
|
||||
85: {R: 0x5f, G: 0xff, B: 0xaf, A: 0xff}, // "#5fffaf"
|
||||
86: {R: 0x5f, G: 0xff, B: 0xd7, A: 0xff}, // "#5fffd7"
|
||||
87: {R: 0x5f, G: 0xff, B: 0xff, A: 0xff}, // "#5fffff"
|
||||
88: {R: 0x87, G: 0x00, B: 0x00, A: 0xff}, // "#870000"
|
||||
89: {R: 0x87, G: 0x00, B: 0x5f, A: 0xff}, // "#87005f"
|
||||
90: {R: 0x87, G: 0x00, B: 0x87, A: 0xff}, // "#870087"
|
||||
91: {R: 0x87, G: 0x00, B: 0xaf, A: 0xff}, // "#8700af"
|
||||
92: {R: 0x87, G: 0x00, B: 0xd7, A: 0xff}, // "#8700d7"
|
||||
93: {R: 0x87, G: 0x00, B: 0xff, A: 0xff}, // "#8700ff"
|
||||
94: {R: 0x87, G: 0x5f, B: 0x00, A: 0xff}, // "#875f00"
|
||||
95: {R: 0x87, G: 0x5f, B: 0x5f, A: 0xff}, // "#875f5f"
|
||||
96: {R: 0x87, G: 0x5f, B: 0x87, A: 0xff}, // "#875f87"
|
||||
97: {R: 0x87, G: 0x5f, B: 0xaf, A: 0xff}, // "#875faf"
|
||||
98: {R: 0x87, G: 0x5f, B: 0xd7, A: 0xff}, // "#875fd7"
|
||||
99: {R: 0x87, G: 0x5f, B: 0xff, A: 0xff}, // "#875fff"
|
||||
100: {R: 0x87, G: 0x87, B: 0x00, A: 0xff}, // "#878700"
|
||||
101: {R: 0x87, G: 0x87, B: 0x5f, A: 0xff}, // "#87875f"
|
||||
102: {R: 0x87, G: 0x87, B: 0x87, A: 0xff}, // "#878787"
|
||||
103: {R: 0x87, G: 0x87, B: 0xaf, A: 0xff}, // "#8787af"
|
||||
104: {R: 0x87, G: 0x87, B: 0xd7, A: 0xff}, // "#8787d7"
|
||||
105: {R: 0x87, G: 0x87, B: 0xff, A: 0xff}, // "#8787ff"
|
||||
106: {R: 0x87, G: 0xaf, B: 0x00, A: 0xff}, // "#87af00"
|
||||
107: {R: 0x87, G: 0xaf, B: 0x5f, A: 0xff}, // "#87af5f"
|
||||
108: {R: 0x87, G: 0xaf, B: 0x87, A: 0xff}, // "#87af87"
|
||||
109: {R: 0x87, G: 0xaf, B: 0xaf, A: 0xff}, // "#87afaf"
|
||||
110: {R: 0x87, G: 0xaf, B: 0xd7, A: 0xff}, // "#87afd7"
|
||||
111: {R: 0x87, G: 0xaf, B: 0xff, A: 0xff}, // "#87afff"
|
||||
112: {R: 0x87, G: 0xd7, B: 0x00, A: 0xff}, // "#87d700"
|
||||
113: {R: 0x87, G: 0xd7, B: 0x5f, A: 0xff}, // "#87d75f"
|
||||
114: {R: 0x87, G: 0xd7, B: 0x87, A: 0xff}, // "#87d787"
|
||||
115: {R: 0x87, G: 0xd7, B: 0xaf, A: 0xff}, // "#87d7af"
|
||||
116: {R: 0x87, G: 0xd7, B: 0xd7, A: 0xff}, // "#87d7d7"
|
||||
117: {R: 0x87, G: 0xd7, B: 0xff, A: 0xff}, // "#87d7ff"
|
||||
118: {R: 0x87, G: 0xff, B: 0x00, A: 0xff}, // "#87ff00"
|
||||
119: {R: 0x87, G: 0xff, B: 0x5f, A: 0xff}, // "#87ff5f"
|
||||
120: {R: 0x87, G: 0xff, B: 0x87, A: 0xff}, // "#87ff87"
|
||||
121: {R: 0x87, G: 0xff, B: 0xaf, A: 0xff}, // "#87ffaf"
|
||||
122: {R: 0x87, G: 0xff, B: 0xd7, A: 0xff}, // "#87ffd7"
|
||||
123: {R: 0x87, G: 0xff, B: 0xff, A: 0xff}, // "#87ffff"
|
||||
124: {R: 0xaf, G: 0x00, B: 0x00, A: 0xff}, // "#af0000"
|
||||
125: {R: 0xaf, G: 0x00, B: 0x5f, A: 0xff}, // "#af005f"
|
||||
126: {R: 0xaf, G: 0x00, B: 0x87, A: 0xff}, // "#af0087"
|
||||
127: {R: 0xaf, G: 0x00, B: 0xaf, A: 0xff}, // "#af00af"
|
||||
128: {R: 0xaf, G: 0x00, B: 0xd7, A: 0xff}, // "#af00d7"
|
||||
129: {R: 0xaf, G: 0x00, B: 0xff, A: 0xff}, // "#af00ff"
|
||||
130: {R: 0xaf, G: 0x5f, B: 0x00, A: 0xff}, // "#af5f00"
|
||||
131: {R: 0xaf, G: 0x5f, B: 0x5f, A: 0xff}, // "#af5f5f"
|
||||
132: {R: 0xaf, G: 0x5f, B: 0x87, A: 0xff}, // "#af5f87"
|
||||
133: {R: 0xaf, G: 0x5f, B: 0xaf, A: 0xff}, // "#af5faf"
|
||||
134: {R: 0xaf, G: 0x5f, B: 0xd7, A: 0xff}, // "#af5fd7"
|
||||
135: {R: 0xaf, G: 0x5f, B: 0xff, A: 0xff}, // "#af5fff"
|
||||
136: {R: 0xaf, G: 0x87, B: 0x00, A: 0xff}, // "#af8700"
|
||||
137: {R: 0xaf, G: 0x87, B: 0x5f, A: 0xff}, // "#af875f"
|
||||
138: {R: 0xaf, G: 0x87, B: 0x87, A: 0xff}, // "#af8787"
|
||||
139: {R: 0xaf, G: 0x87, B: 0xaf, A: 0xff}, // "#af87af"
|
||||
140: {R: 0xaf, G: 0x87, B: 0xd7, A: 0xff}, // "#af87d7"
|
||||
141: {R: 0xaf, G: 0x87, B: 0xff, A: 0xff}, // "#af87ff"
|
||||
142: {R: 0xaf, G: 0xaf, B: 0x00, A: 0xff}, // "#afaf00"
|
||||
143: {R: 0xaf, G: 0xaf, B: 0x5f, A: 0xff}, // "#afaf5f"
|
||||
144: {R: 0xaf, G: 0xaf, B: 0x87, A: 0xff}, // "#afaf87"
|
||||
145: {R: 0xaf, G: 0xaf, B: 0xaf, A: 0xff}, // "#afafaf"
|
||||
146: {R: 0xaf, G: 0xaf, B: 0xd7, A: 0xff}, // "#afafd7"
|
||||
147: {R: 0xaf, G: 0xaf, B: 0xff, A: 0xff}, // "#afafff"
|
||||
148: {R: 0xaf, G: 0xd7, B: 0x00, A: 0xff}, // "#afd700"
|
||||
149: {R: 0xaf, G: 0xd7, B: 0x5f, A: 0xff}, // "#afd75f"
|
||||
150: {R: 0xaf, G: 0xd7, B: 0x87, A: 0xff}, // "#afd787"
|
||||
151: {R: 0xaf, G: 0xd7, B: 0xaf, A: 0xff}, // "#afd7af"
|
||||
152: {R: 0xaf, G: 0xd7, B: 0xd7, A: 0xff}, // "#afd7d7"
|
||||
153: {R: 0xaf, G: 0xd7, B: 0xff, A: 0xff}, // "#afd7ff"
|
||||
154: {R: 0xaf, G: 0xff, B: 0x00, A: 0xff}, // "#afff00"
|
||||
155: {R: 0xaf, G: 0xff, B: 0x5f, A: 0xff}, // "#afff5f"
|
||||
156: {R: 0xaf, G: 0xff, B: 0x87, A: 0xff}, // "#afff87"
|
||||
157: {R: 0xaf, G: 0xff, B: 0xaf, A: 0xff}, // "#afffaf"
|
||||
158: {R: 0xaf, G: 0xff, B: 0xd7, A: 0xff}, // "#afffd7"
|
||||
159: {R: 0xaf, G: 0xff, B: 0xff, A: 0xff}, // "#afffff"
|
||||
160: {R: 0xd7, G: 0x00, B: 0x00, A: 0xff}, // "#d70000"
|
||||
161: {R: 0xd7, G: 0x00, B: 0x5f, A: 0xff}, // "#d7005f"
|
||||
162: {R: 0xd7, G: 0x00, B: 0x87, A: 0xff}, // "#d70087"
|
||||
163: {R: 0xd7, G: 0x00, B: 0xaf, A: 0xff}, // "#d700af"
|
||||
164: {R: 0xd7, G: 0x00, B: 0xd7, A: 0xff}, // "#d700d7"
|
||||
165: {R: 0xd7, G: 0x00, B: 0xff, A: 0xff}, // "#d700ff"
|
||||
166: {R: 0xd7, G: 0x5f, B: 0x00, A: 0xff}, // "#d75f00"
|
||||
167: {R: 0xd7, G: 0x5f, B: 0x5f, A: 0xff}, // "#d75f5f"
|
||||
168: {R: 0xd7, G: 0x5f, B: 0x87, A: 0xff}, // "#d75f87"
|
||||
169: {R: 0xd7, G: 0x5f, B: 0xaf, A: 0xff}, // "#d75faf"
|
||||
170: {R: 0xd7, G: 0x5f, B: 0xd7, A: 0xff}, // "#d75fd7"
|
||||
171: {R: 0xd7, G: 0x5f, B: 0xff, A: 0xff}, // "#d75fff"
|
||||
172: {R: 0xd7, G: 0x87, B: 0x00, A: 0xff}, // "#d78700"
|
||||
173: {R: 0xd7, G: 0x87, B: 0x5f, A: 0xff}, // "#d7875f"
|
||||
174: {R: 0xd7, G: 0x87, B: 0x87, A: 0xff}, // "#d78787"
|
||||
175: {R: 0xd7, G: 0x87, B: 0xaf, A: 0xff}, // "#d787af"
|
||||
176: {R: 0xd7, G: 0x87, B: 0xd7, A: 0xff}, // "#d787d7"
|
||||
177: {R: 0xd7, G: 0x87, B: 0xff, A: 0xff}, // "#d787ff"
|
||||
178: {R: 0xd7, G: 0xaf, B: 0x00, A: 0xff}, // "#d7af00"
|
||||
179: {R: 0xd7, G: 0xaf, B: 0x5f, A: 0xff}, // "#d7af5f"
|
||||
180: {R: 0xd7, G: 0xaf, B: 0x87, A: 0xff}, // "#d7af87"
|
||||
181: {R: 0xd7, G: 0xaf, B: 0xaf, A: 0xff}, // "#d7afaf"
|
||||
182: {R: 0xd7, G: 0xaf, B: 0xd7, A: 0xff}, // "#d7afd7"
|
||||
183: {R: 0xd7, G: 0xaf, B: 0xff, A: 0xff}, // "#d7afff"
|
||||
184: {R: 0xd7, G: 0xd7, B: 0x00, A: 0xff}, // "#d7d700"
|
||||
185: {R: 0xd7, G: 0xd7, B: 0x5f, A: 0xff}, // "#d7d75f"
|
||||
186: {R: 0xd7, G: 0xd7, B: 0x87, A: 0xff}, // "#d7d787"
|
||||
187: {R: 0xd7, G: 0xd7, B: 0xaf, A: 0xff}, // "#d7d7af"
|
||||
188: {R: 0xd7, G: 0xd7, B: 0xd7, A: 0xff}, // "#d7d7d7"
|
||||
189: {R: 0xd7, G: 0xd7, B: 0xff, A: 0xff}, // "#d7d7ff"
|
||||
190: {R: 0xd7, G: 0xff, B: 0x00, A: 0xff}, // "#d7ff00"
|
||||
191: {R: 0xd7, G: 0xff, B: 0x5f, A: 0xff}, // "#d7ff5f"
|
||||
192: {R: 0xd7, G: 0xff, B: 0x87, A: 0xff}, // "#d7ff87"
|
||||
193: {R: 0xd7, G: 0xff, B: 0xaf, A: 0xff}, // "#d7ffaf"
|
||||
194: {R: 0xd7, G: 0xff, B: 0xd7, A: 0xff}, // "#d7ffd7"
|
||||
195: {R: 0xd7, G: 0xff, B: 0xff, A: 0xff}, // "#d7ffff"
|
||||
196: {R: 0xff, G: 0x00, B: 0x00, A: 0xff}, // "#ff0000"
|
||||
197: {R: 0xff, G: 0x00, B: 0x5f, A: 0xff}, // "#ff005f"
|
||||
198: {R: 0xff, G: 0x00, B: 0x87, A: 0xff}, // "#ff0087"
|
||||
199: {R: 0xff, G: 0x00, B: 0xaf, A: 0xff}, // "#ff00af"
|
||||
200: {R: 0xff, G: 0x00, B: 0xd7, A: 0xff}, // "#ff00d7"
|
||||
201: {R: 0xff, G: 0x00, B: 0xff, A: 0xff}, // "#ff00ff"
|
||||
202: {R: 0xff, G: 0x5f, B: 0x00, A: 0xff}, // "#ff5f00"
|
||||
203: {R: 0xff, G: 0x5f, B: 0x5f, A: 0xff}, // "#ff5f5f"
|
||||
204: {R: 0xff, G: 0x5f, B: 0x87, A: 0xff}, // "#ff5f87"
|
||||
205: {R: 0xff, G: 0x5f, B: 0xaf, A: 0xff}, // "#ff5faf"
|
||||
206: {R: 0xff, G: 0x5f, B: 0xd7, A: 0xff}, // "#ff5fd7"
|
||||
207: {R: 0xff, G: 0x5f, B: 0xff, A: 0xff}, // "#ff5fff"
|
||||
208: {R: 0xff, G: 0x87, B: 0x00, A: 0xff}, // "#ff8700"
|
||||
209: {R: 0xff, G: 0x87, B: 0x5f, A: 0xff}, // "#ff875f"
|
||||
210: {R: 0xff, G: 0x87, B: 0x87, A: 0xff}, // "#ff8787"
|
||||
211: {R: 0xff, G: 0x87, B: 0xaf, A: 0xff}, // "#ff87af"
|
||||
212: {R: 0xff, G: 0x87, B: 0xd7, A: 0xff}, // "#ff87d7"
|
||||
213: {R: 0xff, G: 0x87, B: 0xff, A: 0xff}, // "#ff87ff"
|
||||
214: {R: 0xff, G: 0xaf, B: 0x00, A: 0xff}, // "#ffaf00"
|
||||
215: {R: 0xff, G: 0xaf, B: 0x5f, A: 0xff}, // "#ffaf5f"
|
||||
216: {R: 0xff, G: 0xaf, B: 0x87, A: 0xff}, // "#ffaf87"
|
||||
217: {R: 0xff, G: 0xaf, B: 0xaf, A: 0xff}, // "#ffafaf"
|
||||
218: {R: 0xff, G: 0xaf, B: 0xd7, A: 0xff}, // "#ffafd7"
|
||||
219: {R: 0xff, G: 0xaf, B: 0xff, A: 0xff}, // "#ffafff"
|
||||
220: {R: 0xff, G: 0xd7, B: 0x00, A: 0xff}, // "#ffd700"
|
||||
221: {R: 0xff, G: 0xd7, B: 0x5f, A: 0xff}, // "#ffd75f"
|
||||
222: {R: 0xff, G: 0xd7, B: 0x87, A: 0xff}, // "#ffd787"
|
||||
223: {R: 0xff, G: 0xd7, B: 0xaf, A: 0xff}, // "#ffd7af"
|
||||
224: {R: 0xff, G: 0xd7, B: 0xd7, A: 0xff}, // "#ffd7d7"
|
||||
225: {R: 0xff, G: 0xd7, B: 0xff, A: 0xff}, // "#ffd7ff"
|
||||
226: {R: 0xff, G: 0xff, B: 0x00, A: 0xff}, // "#ffff00"
|
||||
227: {R: 0xff, G: 0xff, B: 0x5f, A: 0xff}, // "#ffff5f"
|
||||
228: {R: 0xff, G: 0xff, B: 0x87, A: 0xff}, // "#ffff87"
|
||||
229: {R: 0xff, G: 0xff, B: 0xaf, A: 0xff}, // "#ffffaf"
|
||||
230: {R: 0xff, G: 0xff, B: 0xd7, A: 0xff}, // "#ffffd7"
|
||||
231: {R: 0xff, G: 0xff, B: 0xff, A: 0xff}, // "#ffffff"
|
||||
232: {R: 0x08, G: 0x08, B: 0x08, A: 0xff}, // "#080808"
|
||||
233: {R: 0x12, G: 0x12, B: 0x12, A: 0xff}, // "#121212"
|
||||
234: {R: 0x1c, G: 0x1c, B: 0x1c, A: 0xff}, // "#1c1c1c"
|
||||
235: {R: 0x26, G: 0x26, B: 0x26, A: 0xff}, // "#262626"
|
||||
236: {R: 0x30, G: 0x30, B: 0x30, A: 0xff}, // "#303030"
|
||||
237: {R: 0x3a, G: 0x3a, B: 0x3a, A: 0xff}, // "#3a3a3a"
|
||||
238: {R: 0x44, G: 0x44, B: 0x44, A: 0xff}, // "#444444"
|
||||
239: {R: 0x4e, G: 0x4e, B: 0x4e, A: 0xff}, // "#4e4e4e"
|
||||
240: {R: 0x58, G: 0x58, B: 0x58, A: 0xff}, // "#585858"
|
||||
241: {R: 0x62, G: 0x62, B: 0x62, A: 0xff}, // "#626262"
|
||||
242: {R: 0x6c, G: 0x6c, B: 0x6c, A: 0xff}, // "#6c6c6c"
|
||||
243: {R: 0x76, G: 0x76, B: 0x76, A: 0xff}, // "#767676"
|
||||
244: {R: 0x80, G: 0x80, B: 0x80, A: 0xff}, // "#808080"
|
||||
245: {R: 0x8a, G: 0x8a, B: 0x8a, A: 0xff}, // "#8a8a8a"
|
||||
246: {R: 0x94, G: 0x94, B: 0x94, A: 0xff}, // "#949494"
|
||||
247: {R: 0x9e, G: 0x9e, B: 0x9e, A: 0xff}, // "#9e9e9e"
|
||||
248: {R: 0xa8, G: 0xa8, B: 0xa8, A: 0xff}, // "#a8a8a8"
|
||||
249: {R: 0xb2, G: 0xb2, B: 0xb2, A: 0xff}, // "#b2b2b2"
|
||||
250: {R: 0xbc, G: 0xbc, B: 0xbc, A: 0xff}, // "#bcbcbc"
|
||||
251: {R: 0xc6, G: 0xc6, B: 0xc6, A: 0xff}, // "#c6c6c6"
|
||||
252: {R: 0xd0, G: 0xd0, B: 0xd0, A: 0xff}, // "#d0d0d0"
|
||||
253: {R: 0xda, G: 0xda, B: 0xda, A: 0xff}, // "#dadada"
|
||||
254: {R: 0xe4, G: 0xe4, B: 0xe4, A: 0xff}, // "#e4e4e4"
|
||||
255: {R: 0xee, G: 0xee, B: 0xee, A: 0xff}, // "#eeeeee"
|
||||
}
|
||||
|
||||
var ansi256To16 = [...]BasicColor{
|
||||
0: 0,
|
||||
1: 1,
|
||||
2: 2,
|
||||
3: 3,
|
||||
4: 4,
|
||||
5: 5,
|
||||
6: 6,
|
||||
7: 7,
|
||||
8: 8,
|
||||
9: 9,
|
||||
10: 10,
|
||||
11: 11,
|
||||
12: 12,
|
||||
13: 13,
|
||||
14: 14,
|
||||
15: 15,
|
||||
16: 0,
|
||||
17: 4,
|
||||
18: 4,
|
||||
19: 4,
|
||||
20: 12,
|
||||
21: 12,
|
||||
22: 2,
|
||||
23: 6,
|
||||
24: 4,
|
||||
25: 4,
|
||||
26: 12,
|
||||
27: 12,
|
||||
28: 2,
|
||||
29: 2,
|
||||
30: 6,
|
||||
31: 4,
|
||||
32: 12,
|
||||
33: 12,
|
||||
34: 2,
|
||||
35: 2,
|
||||
36: 2,
|
||||
37: 6,
|
||||
38: 12,
|
||||
39: 12,
|
||||
40: 10,
|
||||
41: 10,
|
||||
42: 10,
|
||||
43: 10,
|
||||
44: 14,
|
||||
45: 12,
|
||||
46: 10,
|
||||
47: 10,
|
||||
48: 10,
|
||||
49: 10,
|
||||
50: 10,
|
||||
51: 14,
|
||||
52: 1,
|
||||
53: 5,
|
||||
54: 4,
|
||||
55: 4,
|
||||
56: 12,
|
||||
57: 12,
|
||||
58: 3,
|
||||
59: 8,
|
||||
60: 4,
|
||||
61: 4,
|
||||
62: 12,
|
||||
63: 12,
|
||||
64: 2,
|
||||
65: 2,
|
||||
66: 6,
|
||||
67: 4,
|
||||
68: 12,
|
||||
69: 12,
|
||||
70: 2,
|
||||
71: 2,
|
||||
72: 2,
|
||||
73: 6,
|
||||
74: 12,
|
||||
75: 12,
|
||||
76: 10,
|
||||
77: 10,
|
||||
78: 10,
|
||||
79: 10,
|
||||
80: 14,
|
||||
81: 12,
|
||||
82: 10,
|
||||
83: 10,
|
||||
84: 10,
|
||||
85: 10,
|
||||
86: 10,
|
||||
87: 14,
|
||||
88: 1,
|
||||
89: 1,
|
||||
90: 5,
|
||||
91: 4,
|
||||
92: 12,
|
||||
93: 12,
|
||||
94: 1,
|
||||
95: 1,
|
||||
96: 5,
|
||||
97: 4,
|
||||
98: 12,
|
||||
99: 12,
|
||||
100: 3,
|
||||
101: 3,
|
||||
102: 8,
|
||||
103: 4,
|
||||
104: 12,
|
||||
105: 12,
|
||||
106: 2,
|
||||
107: 2,
|
||||
108: 2,
|
||||
109: 6,
|
||||
110: 12,
|
||||
111: 12,
|
||||
112: 10,
|
||||
113: 10,
|
||||
114: 10,
|
||||
115: 10,
|
||||
116: 14,
|
||||
117: 12,
|
||||
118: 10,
|
||||
119: 10,
|
||||
120: 10,
|
||||
121: 10,
|
||||
122: 10,
|
||||
123: 14,
|
||||
124: 1,
|
||||
125: 1,
|
||||
126: 1,
|
||||
127: 5,
|
||||
128: 12,
|
||||
129: 12,
|
||||
130: 1,
|
||||
131: 1,
|
||||
132: 1,
|
||||
133: 5,
|
||||
134: 12,
|
||||
135: 12,
|
||||
136: 1,
|
||||
137: 1,
|
||||
138: 1,
|
||||
139: 5,
|
||||
140: 12,
|
||||
141: 12,
|
||||
142: 3,
|
||||
143: 3,
|
||||
144: 3,
|
||||
145: 7,
|
||||
146: 12,
|
||||
147: 12,
|
||||
148: 10,
|
||||
149: 10,
|
||||
150: 10,
|
||||
151: 10,
|
||||
152: 14,
|
||||
153: 12,
|
||||
154: 10,
|
||||
155: 10,
|
||||
156: 10,
|
||||
157: 10,
|
||||
158: 10,
|
||||
159: 14,
|
||||
160: 9,
|
||||
161: 9,
|
||||
162: 9,
|
||||
163: 9,
|
||||
164: 13,
|
||||
165: 12,
|
||||
166: 9,
|
||||
167: 9,
|
||||
168: 9,
|
||||
169: 9,
|
||||
170: 13,
|
||||
171: 12,
|
||||
172: 9,
|
||||
173: 9,
|
||||
174: 9,
|
||||
175: 9,
|
||||
176: 13,
|
||||
177: 12,
|
||||
178: 9,
|
||||
179: 9,
|
||||
180: 9,
|
||||
181: 9,
|
||||
182: 13,
|
||||
183: 12,
|
||||
184: 11,
|
||||
185: 11,
|
||||
186: 11,
|
||||
187: 11,
|
||||
188: 7,
|
||||
189: 12,
|
||||
190: 10,
|
||||
191: 10,
|
||||
192: 10,
|
||||
193: 10,
|
||||
194: 10,
|
||||
195: 14,
|
||||
196: 9,
|
||||
197: 9,
|
||||
198: 9,
|
||||
199: 9,
|
||||
200: 9,
|
||||
201: 13,
|
||||
202: 9,
|
||||
203: 9,
|
||||
204: 9,
|
||||
205: 9,
|
||||
206: 9,
|
||||
207: 13,
|
||||
208: 9,
|
||||
209: 9,
|
||||
210: 9,
|
||||
211: 9,
|
||||
212: 9,
|
||||
213: 13,
|
||||
214: 9,
|
||||
215: 9,
|
||||
216: 9,
|
||||
217: 9,
|
||||
218: 9,
|
||||
219: 13,
|
||||
220: 9,
|
||||
221: 9,
|
||||
222: 9,
|
||||
223: 9,
|
||||
224: 9,
|
||||
225: 13,
|
||||
226: 11,
|
||||
227: 11,
|
||||
228: 11,
|
||||
229: 11,
|
||||
230: 11,
|
||||
231: 15,
|
||||
232: 0,
|
||||
233: 0,
|
||||
234: 0,
|
||||
235: 0,
|
||||
236: 0,
|
||||
237: 0,
|
||||
238: 8,
|
||||
239: 8,
|
||||
240: 8,
|
||||
241: 8,
|
||||
242: 8,
|
||||
243: 8,
|
||||
244: 7,
|
||||
245: 7,
|
||||
246: 7,
|
||||
247: 7,
|
||||
248: 7,
|
||||
249: 7,
|
||||
250: 15,
|
||||
251: 15,
|
||||
252: 15,
|
||||
253: 15,
|
||||
254: 15,
|
||||
255: 15,
|
||||
}
|
||||
156
vendor/github.com/charmbracelet/x/ansi/ctrl.go
generated
vendored
Normal file
156
vendor/github.com/charmbracelet/x/ansi/ctrl.go
generated
vendored
Normal file
|
|
@ -0,0 +1,156 @@
|
|||
package ansi
|
||||
|
||||
import (
|
||||
"strconv"
|
||||
"strings"
|
||||
)
|
||||
|
||||
// RequestNameVersion (XTVERSION) is a control sequence that requests the
|
||||
// terminal's name and version. It responds with a DSR sequence identifying the
|
||||
// terminal.
|
||||
//
|
||||
// CSI > 0 q
|
||||
// DCS > | text ST
|
||||
//
|
||||
// See https://invisible-island.net/xterm/ctlseqs/ctlseqs.html#h3-PC-Style-Function-Keys
|
||||
const (
|
||||
RequestNameVersion = "\x1b[>q"
|
||||
XTVERSION = RequestNameVersion
|
||||
)
|
||||
|
||||
// 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
|
||||
//
|
||||
// Deprecated: use [RequestNameVersion] instead.
|
||||
const RequestXTVersion = RequestNameVersion
|
||||
|
||||
// PrimaryDeviceAttributes (DA1) is a control sequence that reports the
|
||||
// terminal's primary device attributes.
|
||||
//
|
||||
// CSI c
|
||||
// CSI 0 c
|
||||
// CSI ? Ps ; ... c
|
||||
//
|
||||
// If no attributes are given, or if the attribute is 0, this function returns
|
||||
// the request sequence. Otherwise, it returns the response sequence.
|
||||
//
|
||||
// Common attributes include:
|
||||
// - 1 132 columns
|
||||
// - 2 Printer port
|
||||
// - 4 Sixel
|
||||
// - 6 Selective erase
|
||||
// - 7 Soft character set (DRCS)
|
||||
// - 8 User-defined keys (UDKs)
|
||||
// - 9 National replacement character sets (NRCS) (International terminal only)
|
||||
// - 12 Yugoslavian (SCS)
|
||||
// - 15 Technical character set
|
||||
// - 18 Windowing capability
|
||||
// - 21 Horizontal scrolling
|
||||
// - 23 Greek
|
||||
// - 24 Turkish
|
||||
// - 42 ISO Latin-2 character set
|
||||
// - 44 PCTerm
|
||||
// - 45 Soft key map
|
||||
// - 46 ASCII emulation
|
||||
//
|
||||
// See https://vt100.net/docs/vt510-rm/DA1.html
|
||||
func PrimaryDeviceAttributes(attrs ...int) string {
|
||||
if len(attrs) == 0 {
|
||||
return RequestPrimaryDeviceAttributes
|
||||
} else if len(attrs) == 1 && attrs[0] == 0 {
|
||||
return "\x1b[0c"
|
||||
}
|
||||
|
||||
as := make([]string, len(attrs))
|
||||
for i, a := range attrs {
|
||||
as[i] = strconv.Itoa(a)
|
||||
}
|
||||
return "\x1b[?" + strings.Join(as, ";") + "c"
|
||||
}
|
||||
|
||||
// DA1 is an alias for [PrimaryDeviceAttributes].
|
||||
func DA1(attrs ...int) string {
|
||||
return PrimaryDeviceAttributes(attrs...)
|
||||
}
|
||||
|
||||
// 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"
|
||||
|
||||
// SecondaryDeviceAttributes (DA2) is a control sequence that reports the
|
||||
// terminal's secondary device attributes.
|
||||
//
|
||||
// CSI > c
|
||||
// CSI > 0 c
|
||||
// CSI > Ps ; ... c
|
||||
//
|
||||
// See https://vt100.net/docs/vt510-rm/DA2.html
|
||||
func SecondaryDeviceAttributes(attrs ...int) string {
|
||||
if len(attrs) == 0 {
|
||||
return RequestSecondaryDeviceAttributes
|
||||
}
|
||||
|
||||
as := make([]string, len(attrs))
|
||||
for i, a := range attrs {
|
||||
as[i] = strconv.Itoa(a)
|
||||
}
|
||||
return "\x1b[>" + strings.Join(as, ";") + "c"
|
||||
}
|
||||
|
||||
// DA2 is an alias for [SecondaryDeviceAttributes].
|
||||
func DA2(attrs ...int) string {
|
||||
return SecondaryDeviceAttributes(attrs...)
|
||||
}
|
||||
|
||||
// RequestSecondaryDeviceAttributes is a control sequence that requests the
|
||||
// terminal's secondary device attributes (DA2).
|
||||
//
|
||||
// CSI > c
|
||||
//
|
||||
// See https://vt100.net/docs/vt510-rm/DA2.html
|
||||
const RequestSecondaryDeviceAttributes = "\x1b[>c"
|
||||
|
||||
// TertiaryDeviceAttributes (DA3) is a control sequence that reports the
|
||||
// terminal's tertiary device attributes.
|
||||
//
|
||||
// CSI = c
|
||||
// CSI = 0 c
|
||||
// DCS ! | Text ST
|
||||
//
|
||||
// Where Text is the unit ID for the terminal.
|
||||
//
|
||||
// If no unit ID is given, or if the unit ID is 0, this function returns the
|
||||
// request sequence. Otherwise, it returns the response sequence.
|
||||
//
|
||||
// See https://vt100.net/docs/vt510-rm/DA3.html
|
||||
func TertiaryDeviceAttributes(unitID string) string {
|
||||
switch unitID {
|
||||
case "":
|
||||
return RequestTertiaryDeviceAttributes
|
||||
case "0":
|
||||
return "\x1b[=0c"
|
||||
}
|
||||
|
||||
return "\x1bP!|" + unitID + "\x1b\\"
|
||||
}
|
||||
|
||||
// DA3 is an alias for [TertiaryDeviceAttributes].
|
||||
func DA3(unitID string) string {
|
||||
return TertiaryDeviceAttributes(unitID)
|
||||
}
|
||||
|
||||
// RequestTertiaryDeviceAttributes is a control sequence that requests the
|
||||
// terminal's tertiary device attributes (DA3).
|
||||
//
|
||||
// CSI = c
|
||||
//
|
||||
// See https://vt100.net/docs/vt510-rm/DA3.html
|
||||
const RequestTertiaryDeviceAttributes = "\x1b[=c"
|
||||
635
vendor/github.com/charmbracelet/x/ansi/cursor.go
generated
vendored
Normal file
635
vendor/github.com/charmbracelet/x/ansi/cursor.go
generated
vendored
Normal file
|
|
@ -0,0 +1,635 @@
|
|||
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"
|
||||
DECSC = SaveCursor
|
||||
)
|
||||
|
||||
// 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"
|
||||
DECRC = RestoreCursor
|
||||
)
|
||||
|
||||
// RequestCursorPosition 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
|
||||
//
|
||||
// Deprecated: use [RequestCursorPositionReport] instead.
|
||||
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
|
||||
//
|
||||
// Deprecated: use [RequestExtendedCursorPositionReport] instead.
|
||||
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"
|
||||
}
|
||||
|
||||
// CUU is an alias for [CursorUp].
|
||||
func CUU(n int) string {
|
||||
return CursorUp(n)
|
||||
}
|
||||
|
||||
// CUU1 is a sequence for moving the cursor up one cell.
|
||||
const CUU1 = "\x1b[A"
|
||||
|
||||
// CursorUp1 is a sequence for moving the cursor up one cell.
|
||||
//
|
||||
// This is equivalent to CursorUp(1).
|
||||
//
|
||||
// Deprecated: use [CUU1] instead.
|
||||
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"
|
||||
}
|
||||
|
||||
// CUD is an alias for [CursorDown].
|
||||
func CUD(n int) string {
|
||||
return CursorDown(n)
|
||||
}
|
||||
|
||||
// CUD1 is a sequence for moving the cursor down one cell.
|
||||
const CUD1 = "\x1b[B"
|
||||
|
||||
// CursorDown1 is a sequence for moving the cursor down one cell.
|
||||
//
|
||||
// This is equivalent to CursorDown(1).
|
||||
//
|
||||
// Deprecated: use [CUD1] instead.
|
||||
const CursorDown1 = "\x1b[B"
|
||||
|
||||
// CursorForward (CUF) returns a sequence for moving the cursor right n cells.
|
||||
//
|
||||
// # CSI n C
|
||||
//
|
||||
// See: https://vt100.net/docs/vt510-rm/CUF.html
|
||||
func CursorForward(n int) string {
|
||||
var s string
|
||||
if n > 1 {
|
||||
s = strconv.Itoa(n)
|
||||
}
|
||||
return "\x1b[" + s + "C"
|
||||
}
|
||||
|
||||
// CUF is an alias for [CursorForward].
|
||||
func CUF(n int) string {
|
||||
return CursorForward(n)
|
||||
}
|
||||
|
||||
// CUF1 is a sequence for moving the cursor right one cell.
|
||||
const CUF1 = "\x1b[C"
|
||||
|
||||
// CursorRight (CUF) returns a sequence for moving the cursor right n cells.
|
||||
//
|
||||
// CSI n C
|
||||
//
|
||||
// See: https://vt100.net/docs/vt510-rm/CUF.html
|
||||
//
|
||||
// Deprecated: use [CursorForward] instead.
|
||||
func CursorRight(n int) string {
|
||||
return CursorForward(n)
|
||||
}
|
||||
|
||||
// CursorRight1 is a sequence for moving the cursor right one cell.
|
||||
//
|
||||
// This is equivalent to CursorRight(1).
|
||||
//
|
||||
// Deprecated: use [CUF1] instead.
|
||||
const CursorRight1 = CUF1
|
||||
|
||||
// CursorBackward (CUB) returns a sequence for moving the cursor left n cells.
|
||||
//
|
||||
// # CSI n D
|
||||
//
|
||||
// See: https://vt100.net/docs/vt510-rm/CUB.html
|
||||
func CursorBackward(n int) string {
|
||||
var s string
|
||||
if n > 1 {
|
||||
s = strconv.Itoa(n)
|
||||
}
|
||||
return "\x1b[" + s + "D"
|
||||
}
|
||||
|
||||
// CUB is an alias for [CursorBackward].
|
||||
func CUB(n int) string {
|
||||
return CursorBackward(n)
|
||||
}
|
||||
|
||||
// CUB1 is a sequence for moving the cursor left one cell.
|
||||
const CUB1 = "\x1b[D"
|
||||
|
||||
// CursorLeft (CUB) returns a sequence for moving the cursor left n cells.
|
||||
//
|
||||
// CSI n D
|
||||
//
|
||||
// See: https://vt100.net/docs/vt510-rm/CUB.html
|
||||
//
|
||||
// Deprecated: use [CursorBackward] instead.
|
||||
func CursorLeft(n int) string {
|
||||
return CursorBackward(n)
|
||||
}
|
||||
|
||||
// CursorLeft1 is a sequence for moving the cursor left one cell.
|
||||
//
|
||||
// This is equivalent to CursorLeft(1).
|
||||
//
|
||||
// Deprecated: use [CUB1] instead.
|
||||
const CursorLeft1 = CUB1
|
||||
|
||||
// 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"
|
||||
}
|
||||
|
||||
// CNL is an alias for [CursorNextLine].
|
||||
func CNL(n int) string {
|
||||
return CursorNextLine(n)
|
||||
}
|
||||
|
||||
// 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"
|
||||
}
|
||||
|
||||
// CPL is an alias for [CursorPreviousLine].
|
||||
func CPL(n int) string {
|
||||
return CursorPreviousLine(n)
|
||||
}
|
||||
|
||||
// CursorHorizontalAbsolute (CHA) returns a sequence for moving the cursor to
|
||||
// the given column.
|
||||
//
|
||||
// Default is 1.
|
||||
//
|
||||
// CSI n G
|
||||
//
|
||||
// See: https://vt100.net/docs/vt510-rm/CHA.html
|
||||
func CursorHorizontalAbsolute(col int) string {
|
||||
var s string
|
||||
if col > 0 {
|
||||
s = strconv.Itoa(col)
|
||||
}
|
||||
return "\x1b[" + s + "G"
|
||||
}
|
||||
|
||||
// CHA is an alias for [CursorHorizontalAbsolute].
|
||||
func CHA(col int) string {
|
||||
return CursorHorizontalAbsolute(col)
|
||||
}
|
||||
|
||||
// CursorPosition (CUP) returns a sequence for setting the cursor to the
|
||||
// given row and column.
|
||||
//
|
||||
// Default is 1,1.
|
||||
//
|
||||
// CSI n ; m H
|
||||
//
|
||||
// See: https://vt100.net/docs/vt510-rm/CUP.html
|
||||
func CursorPosition(col, row int) string {
|
||||
if row <= 0 && col <= 0 {
|
||||
return CursorHomePosition
|
||||
}
|
||||
|
||||
var r, c string
|
||||
if row > 0 {
|
||||
r = strconv.Itoa(row)
|
||||
}
|
||||
if col > 0 {
|
||||
c = strconv.Itoa(col)
|
||||
}
|
||||
return "\x1b[" + r + ";" + c + "H"
|
||||
}
|
||||
|
||||
// CUP is an alias for [CursorPosition].
|
||||
func CUP(col, row int) string {
|
||||
return CursorPosition(col, row)
|
||||
}
|
||||
|
||||
// CursorHomePosition is a sequence for moving the cursor to the upper left
|
||||
// corner of the scrolling region. This is equivalent to `CursorPosition(1, 1)`.
|
||||
const CursorHomePosition = "\x1b[H"
|
||||
|
||||
// SetCursorPosition (CUP) returns a sequence for setting the cursor to the
|
||||
// given row and column.
|
||||
//
|
||||
// CSI n ; m H
|
||||
//
|
||||
// See: https://vt100.net/docs/vt510-rm/CUP.html
|
||||
//
|
||||
// Deprecated: use [CursorPosition] instead.
|
||||
func SetCursorPosition(col, row int) string {
|
||||
if row <= 0 && col <= 0 {
|
||||
return HomeCursorPosition
|
||||
}
|
||||
|
||||
var r, c string
|
||||
if row > 0 {
|
||||
r = strconv.Itoa(row)
|
||||
}
|
||||
if col > 0 {
|
||||
c = strconv.Itoa(col)
|
||||
}
|
||||
return "\x1b[" + r + ";" + c + "H"
|
||||
}
|
||||
|
||||
// HomeCursorPosition is a sequence for moving the cursor to the upper left
|
||||
// corner of the scrolling region. This is equivalent to `SetCursorPosition(1, 1)`.
|
||||
//
|
||||
// Deprecated: use [CursorHomePosition] instead.
|
||||
const HomeCursorPosition = CursorHomePosition
|
||||
|
||||
// MoveCursor (CUP) returns a sequence for setting the cursor to the
|
||||
// given row and column.
|
||||
//
|
||||
// CSI n ; m H
|
||||
//
|
||||
// See: https://vt100.net/docs/vt510-rm/CUP.html
|
||||
//
|
||||
// Deprecated: use [CursorPosition] instead.
|
||||
func MoveCursor(col, row int) string {
|
||||
return SetCursorPosition(col, row)
|
||||
}
|
||||
|
||||
// CursorOrigin is a sequence for moving the cursor to the upper left corner of
|
||||
// the display. This is equivalent to `SetCursorPosition(1, 1)`.
|
||||
//
|
||||
// Deprecated: use [CursorHomePosition] instead.
|
||||
const CursorOrigin = "\x1b[1;1H"
|
||||
|
||||
// MoveCursorOrigin is a sequence for moving the cursor to the upper left
|
||||
// corner of the display. This is equivalent to `SetCursorPosition(1, 1)`.
|
||||
//
|
||||
// Deprecated: use [CursorHomePosition] instead.
|
||||
const MoveCursorOrigin = CursorOrigin
|
||||
|
||||
// CursorHorizontalForwardTab (CHT) returns a sequence for moving the cursor to
|
||||
// the next tab stop n times.
|
||||
//
|
||||
// Default is 1.
|
||||
//
|
||||
// CSI n I
|
||||
//
|
||||
// See: https://vt100.net/docs/vt510-rm/CHT.html
|
||||
func CursorHorizontalForwardTab(n int) string {
|
||||
var s string
|
||||
if n > 1 {
|
||||
s = strconv.Itoa(n)
|
||||
}
|
||||
return "\x1b[" + s + "I"
|
||||
}
|
||||
|
||||
// CHT is an alias for [CursorHorizontalForwardTab].
|
||||
func CHT(n int) string {
|
||||
return CursorHorizontalForwardTab(n)
|
||||
}
|
||||
|
||||
// EraseCharacter (ECH) returns a sequence for erasing n characters from the
|
||||
// screen. This doesn't affect other cell attributes.
|
||||
//
|
||||
// Default is 1.
|
||||
//
|
||||
// CSI n X
|
||||
//
|
||||
// See: https://vt100.net/docs/vt510-rm/ECH.html
|
||||
func EraseCharacter(n int) string {
|
||||
var s string
|
||||
if n > 1 {
|
||||
s = strconv.Itoa(n)
|
||||
}
|
||||
return "\x1b[" + s + "X"
|
||||
}
|
||||
|
||||
// ECH is an alias for [EraseCharacter].
|
||||
func ECH(n int) string {
|
||||
return EraseCharacter(n)
|
||||
}
|
||||
|
||||
// CursorBackwardTab (CBT) returns a sequence for moving the cursor to the
|
||||
// previous tab stop n times.
|
||||
//
|
||||
// Default is 1.
|
||||
//
|
||||
// CSI n Z
|
||||
//
|
||||
// See: https://vt100.net/docs/vt510-rm/CBT.html
|
||||
func CursorBackwardTab(n int) string {
|
||||
var s string
|
||||
if n > 1 {
|
||||
s = strconv.Itoa(n)
|
||||
}
|
||||
return "\x1b[" + s + "Z"
|
||||
}
|
||||
|
||||
// CBT is an alias for [CursorBackwardTab].
|
||||
func CBT(n int) string {
|
||||
return CursorBackwardTab(n)
|
||||
}
|
||||
|
||||
// VerticalPositionAbsolute (VPA) returns a sequence for moving the cursor to
|
||||
// the given row.
|
||||
//
|
||||
// Default is 1.
|
||||
//
|
||||
// CSI n d
|
||||
//
|
||||
// See: https://vt100.net/docs/vt510-rm/VPA.html
|
||||
func VerticalPositionAbsolute(row int) string {
|
||||
var s string
|
||||
if row > 0 {
|
||||
s = strconv.Itoa(row)
|
||||
}
|
||||
return "\x1b[" + s + "d"
|
||||
}
|
||||
|
||||
// VPA is an alias for [VerticalPositionAbsolute].
|
||||
func VPA(row int) string {
|
||||
return VerticalPositionAbsolute(row)
|
||||
}
|
||||
|
||||
// VerticalPositionRelative (VPR) returns a sequence for moving the cursor down
|
||||
// n rows relative to the current position.
|
||||
//
|
||||
// Default is 1.
|
||||
//
|
||||
// CSI n e
|
||||
//
|
||||
// See: https://vt100.net/docs/vt510-rm/VPR.html
|
||||
func VerticalPositionRelative(n int) string {
|
||||
var s string
|
||||
if n > 1 {
|
||||
s = strconv.Itoa(n)
|
||||
}
|
||||
return "\x1b[" + s + "e"
|
||||
}
|
||||
|
||||
// VPR is an alias for [VerticalPositionRelative].
|
||||
func VPR(n int) string {
|
||||
return VerticalPositionRelative(n)
|
||||
}
|
||||
|
||||
// HorizontalVerticalPosition (HVP) returns a sequence for moving the cursor to
|
||||
// the given row and column.
|
||||
//
|
||||
// Default is 1,1.
|
||||
//
|
||||
// CSI n ; m f
|
||||
//
|
||||
// This has the same effect as [CursorPosition].
|
||||
//
|
||||
// See: https://vt100.net/docs/vt510-rm/HVP.html
|
||||
func HorizontalVerticalPosition(col, row int) string {
|
||||
var r, c string
|
||||
if row > 0 {
|
||||
r = strconv.Itoa(row)
|
||||
}
|
||||
if col > 0 {
|
||||
c = strconv.Itoa(col)
|
||||
}
|
||||
return "\x1b[" + r + ";" + c + "f"
|
||||
}
|
||||
|
||||
// HVP is an alias for [HorizontalVerticalPosition].
|
||||
func HVP(col, row int) string {
|
||||
return HorizontalVerticalPosition(col, row)
|
||||
}
|
||||
|
||||
// HorizontalVerticalHomePosition is a sequence for moving the cursor to the
|
||||
// upper left corner of the scrolling region. This is equivalent to
|
||||
// `HorizontalVerticalPosition(1, 1)`.
|
||||
const HorizontalVerticalHomePosition = "\x1b[f"
|
||||
|
||||
// SaveCurrentCursorPosition (SCOSC) is a sequence for saving the current cursor
|
||||
// position for SCO console mode.
|
||||
//
|
||||
// CSI s
|
||||
//
|
||||
// This acts like [DECSC], except the page number where the cursor is located
|
||||
// is not saved.
|
||||
//
|
||||
// See: https://vt100.net/docs/vt510-rm/SCOSC.html
|
||||
const (
|
||||
SaveCurrentCursorPosition = "\x1b[s"
|
||||
SCOSC = SaveCurrentCursorPosition
|
||||
)
|
||||
|
||||
// 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
|
||||
//
|
||||
// Deprecated: use [SaveCurrentCursorPosition] instead.
|
||||
const SaveCursorPosition = "\x1b[s"
|
||||
|
||||
// RestoreCurrentCursorPosition (SCORC) is a sequence for restoring the current
|
||||
// cursor position for SCO console mode.
|
||||
//
|
||||
// CSI u
|
||||
//
|
||||
// This acts like [DECRC], except the page number where the cursor was saved is
|
||||
// not restored.
|
||||
//
|
||||
// See: https://vt100.net/docs/vt510-rm/SCORC.html
|
||||
const (
|
||||
RestoreCurrentCursorPosition = "\x1b[u"
|
||||
SCORC = RestoreCurrentCursorPosition
|
||||
)
|
||||
|
||||
// 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
|
||||
//
|
||||
// Deprecated: use [RestoreCurrentCursorPosition] instead.
|
||||
const RestoreCursorPosition = "\x1b[u"
|
||||
|
||||
// SetCursorStyle (DECSCUSR) returns a sequence for changing the cursor style.
|
||||
//
|
||||
// Default is 1.
|
||||
//
|
||||
// CSI Ps SP q
|
||||
//
|
||||
// Where Ps is the cursor style:
|
||||
//
|
||||
// 0: Blinking block
|
||||
// 1: Blinking block (default)
|
||||
// 2: Steady block
|
||||
// 3: Blinking underline
|
||||
// 4: Steady underline
|
||||
// 5: Blinking bar (xterm)
|
||||
// 6: Steady bar (xterm)
|
||||
//
|
||||
// See: https://vt100.net/docs/vt510-rm/DECSCUSR.html
|
||||
// See: https://invisible-island.net/xterm/ctlseqs/ctlseqs.html#h4-Functions-using-CSI-_-ordered-by-the-final-character-lparen-s-rparen:CSI-Ps-SP-q.1D81
|
||||
func SetCursorStyle(style int) string {
|
||||
if style < 0 {
|
||||
style = 0
|
||||
}
|
||||
return "\x1b[" + strconv.Itoa(style) + " q"
|
||||
}
|
||||
|
||||
// DECSCUSR is an alias for [SetCursorStyle].
|
||||
func DECSCUSR(style int) string {
|
||||
return SetCursorStyle(style)
|
||||
}
|
||||
|
||||
// SetPointerShape returns a sequence for changing the mouse pointer cursor
|
||||
// shape. Use "default" for the default pointer shape.
|
||||
//
|
||||
// OSC 22 ; Pt ST
|
||||
// OSC 22 ; Pt BEL
|
||||
//
|
||||
// Where Pt is the pointer shape name. The name can be anything that the
|
||||
// operating system can understand. Some common names are:
|
||||
//
|
||||
// - copy
|
||||
// - crosshair
|
||||
// - default
|
||||
// - ew-resize
|
||||
// - n-resize
|
||||
// - text
|
||||
// - wait
|
||||
//
|
||||
// See: https://invisible-island.net/xterm/ctlseqs/ctlseqs.html#h2-Operating-System-Commands
|
||||
func SetPointerShape(shape string) string {
|
||||
return "\x1b]22;" + shape + "\x07"
|
||||
}
|
||||
|
||||
// ReverseIndex (RI) is an escape sequence for moving the cursor up one line in
|
||||
// the same column. If the cursor is at the top margin, the screen scrolls
|
||||
// down.
|
||||
//
|
||||
// This has the same effect as [RI].
|
||||
const ReverseIndex = "\x1bM"
|
||||
|
||||
// HorizontalPositionAbsolute (HPA) returns a sequence for moving the cursor to
|
||||
// the given column. This has the same effect as [CUP].
|
||||
//
|
||||
// Default is 1.
|
||||
//
|
||||
// CSI n \`
|
||||
//
|
||||
// See: https://vt100.net/docs/vt510-rm/HPA.html
|
||||
func HorizontalPositionAbsolute(col int) string {
|
||||
var s string
|
||||
if col > 0 {
|
||||
s = strconv.Itoa(col)
|
||||
}
|
||||
return "\x1b[" + s + "`"
|
||||
}
|
||||
|
||||
// HPA is an alias for [HorizontalPositionAbsolute].
|
||||
func HPA(col int) string {
|
||||
return HorizontalPositionAbsolute(col)
|
||||
}
|
||||
|
||||
// HorizontalPositionRelative (HPR) returns a sequence for moving the cursor
|
||||
// right n columns relative to the current position. This has the same effect
|
||||
// as [CUP].
|
||||
//
|
||||
// Default is 1.
|
||||
//
|
||||
// CSI n a
|
||||
//
|
||||
// See: https://vt100.net/docs/vt510-rm/HPR.html
|
||||
func HorizontalPositionRelative(n int) string {
|
||||
var s string
|
||||
if n > 0 {
|
||||
s = strconv.Itoa(n)
|
||||
}
|
||||
return "\x1b[" + s + "a"
|
||||
}
|
||||
|
||||
// HPR is an alias for [HorizontalPositionRelative].
|
||||
func HPR(n int) string {
|
||||
return HorizontalPositionRelative(n)
|
||||
}
|
||||
|
||||
// Index (IND) is an escape sequence for moving the cursor down one line in the
|
||||
// same column. If the cursor is at the bottom margin, the screen scrolls up.
|
||||
// This has the same effect as [IND].
|
||||
const Index = "\x1bD"
|
||||
26
vendor/github.com/charmbracelet/x/ansi/cwd.go
generated
vendored
Normal file
26
vendor/github.com/charmbracelet/x/ansi/cwd.go
generated
vendored
Normal file
|
|
@ -0,0 +1,26 @@
|
|||
package ansi
|
||||
|
||||
import (
|
||||
"net/url"
|
||||
"path"
|
||||
)
|
||||
|
||||
// NotifyWorkingDirectory returns a sequence that notifies the terminal
|
||||
// of the current working directory.
|
||||
//
|
||||
// OSC 7 ; Pt BEL
|
||||
//
|
||||
// Where Pt is a URL in the format "file://[host]/[path]".
|
||||
// Set host to "localhost" if this is a path on the local computer.
|
||||
//
|
||||
// See: https://wezfurlong.org/wezterm/shell-integration.html#osc-7-escape-sequence-to-set-the-working-directory
|
||||
// See: https://iterm2.com/documentation-escape-codes.html#:~:text=RemoteHost%20and%20CurrentDir%3A-,OSC%207,-%3B%20%5BPs%5D%20ST
|
||||
func NotifyWorkingDirectory(host string, paths ...string) string {
|
||||
path := path.Join(paths...)
|
||||
u := &url.URL{
|
||||
Scheme: "file",
|
||||
Host: host,
|
||||
Path: path,
|
||||
}
|
||||
return "\x1b]7;" + u.String() + "\x07"
|
||||
}
|
||||
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
|
||||
67
vendor/github.com/charmbracelet/x/ansi/finalterm.go
generated
vendored
Normal file
67
vendor/github.com/charmbracelet/x/ansi/finalterm.go
generated
vendored
Normal file
|
|
@ -0,0 +1,67 @@
|
|||
package ansi
|
||||
|
||||
import "strings"
|
||||
|
||||
// FinalTerm returns an escape sequence that is used for shell integrations.
|
||||
// Originally, FinalTerm designed the protocol hence the name.
|
||||
//
|
||||
// OSC 133 ; Ps ; Pm ST
|
||||
// OSC 133 ; Ps ; Pm BEL
|
||||
//
|
||||
// See: https://iterm2.com/documentation-shell-integration.html
|
||||
func FinalTerm(pm ...string) string {
|
||||
return "\x1b]133;" + strings.Join(pm, ";") + "\x07"
|
||||
}
|
||||
|
||||
// FinalTermPrompt returns an escape sequence that is used for shell
|
||||
// integrations prompt marks. This is sent just before the start of the shell
|
||||
// prompt.
|
||||
//
|
||||
// This is an alias for FinalTerm("A").
|
||||
func FinalTermPrompt(pm ...string) string {
|
||||
if len(pm) == 0 {
|
||||
return FinalTerm("A")
|
||||
}
|
||||
return FinalTerm(append([]string{"A"}, pm...)...)
|
||||
}
|
||||
|
||||
// FinalTermCmdStart returns an escape sequence that is used for shell
|
||||
// integrations command start marks. This is sent just after the end of the
|
||||
// shell prompt, before the user enters a command.
|
||||
//
|
||||
// This is an alias for FinalTerm("B").
|
||||
func FinalTermCmdStart(pm ...string) string {
|
||||
if len(pm) == 0 {
|
||||
return FinalTerm("B")
|
||||
}
|
||||
return FinalTerm(append([]string{"B"}, pm...)...)
|
||||
}
|
||||
|
||||
// FinalTermCmdExecuted returns an escape sequence that is used for shell
|
||||
// integrations command executed marks. This is sent just before the start of
|
||||
// the command output.
|
||||
//
|
||||
// This is an alias for FinalTerm("C").
|
||||
func FinalTermCmdExecuted(pm ...string) string {
|
||||
if len(pm) == 0 {
|
||||
return FinalTerm("C")
|
||||
}
|
||||
return FinalTerm(append([]string{"C"}, pm...)...)
|
||||
}
|
||||
|
||||
// FinalTermCmdFinished returns an escape sequence that is used for shell
|
||||
// integrations command finished marks.
|
||||
//
|
||||
// If the command was sent after
|
||||
// [FinalTermCmdStart], it indicates that the command was aborted. If the
|
||||
// command was sent after [FinalTermCmdExecuted], it indicates the end of the
|
||||
// command output. If neither was sent, [FinalTermCmdFinished] should be
|
||||
// ignored.
|
||||
//
|
||||
// This is an alias for FinalTerm("D").
|
||||
func FinalTermCmdFinished(pm ...string) string {
|
||||
if len(pm) == 0 {
|
||||
return FinalTerm("D")
|
||||
}
|
||||
return FinalTerm(append([]string{"D"}, pm...)...)
|
||||
}
|
||||
9
vendor/github.com/charmbracelet/x/ansi/focus.go
generated
vendored
Normal file
9
vendor/github.com/charmbracelet/x/ansi/focus.go
generated
vendored
Normal file
|
|
@ -0,0 +1,9 @@
|
|||
package ansi
|
||||
|
||||
// Focus is an escape sequence to notify the terminal that it has focus.
|
||||
// This is used with [FocusEventMode].
|
||||
const Focus = "\x1b[I"
|
||||
|
||||
// Blur is an escape sequence to notify the terminal that it has lost focus.
|
||||
// This is used with [FocusEventMode].
|
||||
const Blur = "\x1b[O"
|
||||
62
vendor/github.com/charmbracelet/x/ansi/graphics.go
generated
vendored
Normal file
62
vendor/github.com/charmbracelet/x/ansi/graphics.go
generated
vendored
Normal file
|
|
@ -0,0 +1,62 @@
|
|||
package ansi
|
||||
|
||||
import (
|
||||
"bytes"
|
||||
"strconv"
|
||||
"strings"
|
||||
)
|
||||
|
||||
// SixelGraphics returns a sequence that encodes the given sixel image payload to
|
||||
// a DCS sixel sequence.
|
||||
//
|
||||
// DCS p1; p2; p3; q [sixel payload] ST
|
||||
//
|
||||
// p1 = pixel aspect ratio, deprecated and replaced by pixel metrics in the payload
|
||||
//
|
||||
// p2 = This is supposed to be 0 for transparency, but terminals don't seem to
|
||||
// to use it properly. Value 0 leaves an unsightly black bar on all terminals
|
||||
// I've tried and looks correct with value 1.
|
||||
//
|
||||
// p3 = Horizontal grid size parameter. Everyone ignores this and uses a fixed grid
|
||||
// size, as far as I can tell.
|
||||
//
|
||||
// See https://shuford.invisible-island.net/all_about_sixels.txt
|
||||
func SixelGraphics(p1, p2, p3 int, payload []byte) string {
|
||||
var buf bytes.Buffer
|
||||
|
||||
buf.WriteString("\x1bP")
|
||||
if p1 >= 0 {
|
||||
buf.WriteString(strconv.Itoa(p1))
|
||||
}
|
||||
buf.WriteByte(';')
|
||||
if p2 >= 0 {
|
||||
buf.WriteString(strconv.Itoa(p2))
|
||||
}
|
||||
if p3 > 0 {
|
||||
buf.WriteByte(';')
|
||||
buf.WriteString(strconv.Itoa(p3))
|
||||
}
|
||||
buf.WriteByte('q')
|
||||
buf.Write(payload)
|
||||
buf.WriteString("\x1b\\")
|
||||
|
||||
return buf.String()
|
||||
}
|
||||
|
||||
// KittyGraphics returns a sequence that encodes the given image in the Kitty
|
||||
// graphics protocol.
|
||||
//
|
||||
// APC G [comma separated options] ; [base64 encoded payload] ST
|
||||
//
|
||||
// See https://sw.kovidgoyal.net/kitty/graphics-protocol/
|
||||
func KittyGraphics(payload []byte, opts ...string) string {
|
||||
var buf bytes.Buffer
|
||||
buf.WriteString("\x1b_G")
|
||||
buf.WriteString(strings.Join(opts, ","))
|
||||
if len(payload) > 0 {
|
||||
buf.WriteString(";")
|
||||
buf.Write(payload)
|
||||
}
|
||||
buf.WriteString("\x1b\\")
|
||||
return buf.String()
|
||||
}
|
||||
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...)
|
||||
}
|
||||
18
vendor/github.com/charmbracelet/x/ansi/iterm2.go
generated
vendored
Normal file
18
vendor/github.com/charmbracelet/x/ansi/iterm2.go
generated
vendored
Normal file
|
|
@ -0,0 +1,18 @@
|
|||
package ansi
|
||||
|
||||
import "fmt"
|
||||
|
||||
// ITerm2 returns a sequence that uses the iTerm2 proprietary protocol. Use the
|
||||
// iterm2 package for a more convenient API.
|
||||
//
|
||||
// OSC 1337 ; key = value ST
|
||||
//
|
||||
// Example:
|
||||
//
|
||||
// ITerm2(iterm2.File{...})
|
||||
//
|
||||
// See https://iterm2.com/documentation-escape-codes.html
|
||||
// See https://iterm2.com/documentation-images.html
|
||||
func ITerm2(data any) string {
|
||||
return "\x1b]1337;" + fmt.Sprint(data) + "\x07"
|
||||
}
|
||||
28
vendor/github.com/charmbracelet/x/ansi/keypad.go
generated
vendored
Normal file
28
vendor/github.com/charmbracelet/x/ansi/keypad.go
generated
vendored
Normal file
|
|
@ -0,0 +1,28 @@
|
|||
package ansi
|
||||
|
||||
// Keypad Application Mode (DECKPAM) is a mode that determines whether the
|
||||
// keypad sends application sequences or ANSI sequences.
|
||||
//
|
||||
// This works like enabling [DECNKM].
|
||||
// Use [NumericKeypadMode] to set the numeric keypad mode.
|
||||
//
|
||||
// ESC =
|
||||
//
|
||||
// See: https://vt100.net/docs/vt510-rm/DECKPAM.html
|
||||
const (
|
||||
KeypadApplicationMode = "\x1b="
|
||||
DECKPAM = KeypadApplicationMode
|
||||
)
|
||||
|
||||
// Keypad Numeric Mode (DECKPNM) is a mode that determines whether the keypad
|
||||
// sends application sequences or ANSI sequences.
|
||||
//
|
||||
// This works the same as disabling [DECNKM].
|
||||
//
|
||||
// ESC >
|
||||
//
|
||||
// See: https://vt100.net/docs/vt510-rm/DECKPNM.html
|
||||
const (
|
||||
KeypadNumericMode = "\x1b>"
|
||||
DECKPNM = KeypadNumericMode
|
||||
)
|
||||
90
vendor/github.com/charmbracelet/x/ansi/kitty.go
generated
vendored
Normal file
90
vendor/github.com/charmbracelet/x/ansi/kitty.go
generated
vendored
Normal file
|
|
@ -0,0 +1,90 @@
|
|||
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
|
||||
KittyReportAllKeysAsEscapeCodes
|
||||
KittyReportAssociatedKeys
|
||||
|
||||
KittyAllFlags = KittyDisambiguateEscapeCodes | KittyReportEventTypes |
|
||||
KittyReportAlternateKeys | KittyReportAllKeysAsEscapeCodes | 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"
|
||||
|
||||
// KittyKeyboard returns a sequence to request keyboard enhancements from the terminal.
|
||||
// The flags argument is a bitmask of the Kitty keyboard protocol flags. While
|
||||
// mode specifies how the flags should be interpreted.
|
||||
//
|
||||
// Possible values for flags mask:
|
||||
//
|
||||
// 1: Disambiguate escape codes
|
||||
// 2: Report event types
|
||||
// 4: Report alternate keys
|
||||
// 8: Report all keys as escape codes
|
||||
// 16: Report associated text
|
||||
//
|
||||
// Possible values for mode:
|
||||
//
|
||||
// 1: Set given flags and unset all others
|
||||
// 2: Set given flags and keep existing flags unchanged
|
||||
// 3: Unset given flags and keep existing flags unchanged
|
||||
//
|
||||
// See https://sw.kovidgoyal.net/kitty/keyboard-protocol/#progressive-enhancement
|
||||
func KittyKeyboard(flags, mode int) string {
|
||||
return "\x1b[=" + strconv.Itoa(flags) + ";" + strconv.Itoa(mode) + "u"
|
||||
}
|
||||
|
||||
// PushKittyKeyboard returns a sequence to push the given flags to the terminal
|
||||
// Kitty Keyboard stack.
|
||||
//
|
||||
// Possible values for flags mask:
|
||||
//
|
||||
// 0: Disable all features
|
||||
// 1: Disambiguate escape codes
|
||||
// 2: Report event types
|
||||
// 4: Report alternate keys
|
||||
// 8: Report all keys as escape codes
|
||||
// 16: Report associated text
|
||||
//
|
||||
// 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[>u"
|
||||
|
||||
// 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"
|
||||
}
|
||||
172
vendor/github.com/charmbracelet/x/ansi/method.go
generated
vendored
Normal file
172
vendor/github.com/charmbracelet/x/ansi/method.go
generated
vendored
Normal file
|
|
@ -0,0 +1,172 @@
|
|||
package ansi
|
||||
|
||||
// Method is a type that represents the how the renderer should calculate the
|
||||
// display width of cells.
|
||||
type Method uint8
|
||||
|
||||
// Display width modes.
|
||||
const (
|
||||
WcWidth Method = iota
|
||||
GraphemeWidth
|
||||
)
|
||||
|
||||
// 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 (m Method) StringWidth(s string) int {
|
||||
return stringWidth(m, s)
|
||||
}
|
||||
|
||||
// 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-Asian characters and emojis).
|
||||
func (m Method) Truncate(s string, length int, tail string) string {
|
||||
return truncate(m, s, length, tail)
|
||||
}
|
||||
|
||||
// TruncateLeft truncates a string to a given length, adding a prefix to the
|
||||
// beginning 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-Asian characters and emojis).
|
||||
func (m Method) TruncateLeft(s string, length int, prefix string) string {
|
||||
return truncateLeft(m, s, length, prefix)
|
||||
}
|
||||
|
||||
// Cut the string, without adding any prefix or tail strings. This function is
|
||||
// aware of ANSI escape codes and will not break them, and accounts for
|
||||
// wide-characters (such as East-Asian characters and emojis). Note that the
|
||||
// [left] parameter is inclusive, while [right] isn't.
|
||||
func (m Method) Cut(s string, left, right int) string {
|
||||
return cut(m, s, left, right)
|
||||
}
|
||||
|
||||
// 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.
|
||||
// This treats the text as a sequence of graphemes.
|
||||
func (m Method) Hardwrap(s string, length int, preserveSpace bool) string {
|
||||
return hardwrap(m, s, length, preserveSpace)
|
||||
}
|
||||
|
||||
// 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 (m Method) Wordwrap(s string, length int, breakpoints string) string {
|
||||
return wordwrap(m, s, length, breakpoints)
|
||||
}
|
||||
|
||||
// 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 (m Method) Wrap(s string, length int, breakpoints string) string {
|
||||
return wrap(m, s, length, breakpoints)
|
||||
}
|
||||
|
||||
// DecodeSequence decodes the first ANSI escape sequence or a printable
|
||||
// grapheme from the given data. It returns the sequence slice, the number of
|
||||
// bytes read, the cell width for each sequence, and the new state.
|
||||
//
|
||||
// The cell width will always be 0 for control and escape sequences, 1 for
|
||||
// ASCII printable characters, and the number of cells other Unicode characters
|
||||
// occupy. It uses the uniseg package to calculate the width of Unicode
|
||||
// graphemes and characters. This means it will always do grapheme clustering
|
||||
// (mode 2027).
|
||||
//
|
||||
// Passing a non-nil [*Parser] as the last argument will allow the decoder to
|
||||
// collect sequence parameters, data, and commands. The parser cmd will have
|
||||
// the packed command value that contains intermediate and prefix characters.
|
||||
// In the case of a OSC sequence, the cmd will be the OSC command number. Use
|
||||
// [Cmd] and [Param] types to unpack command intermediates and prefixes as well
|
||||
// as parameters.
|
||||
//
|
||||
// Zero [Cmd] means the CSI, DCS, or ESC sequence is invalid. Moreover, checking the
|
||||
// validity of other data sequences, OSC, DCS, etc, will require checking for
|
||||
// the returned sequence terminator bytes such as ST (ESC \\) and BEL).
|
||||
//
|
||||
// We store the command byte in [Cmd] in the most significant byte, the
|
||||
// prefix byte in the next byte, and the intermediate byte in the least
|
||||
// significant byte. This is done to avoid using a struct to store the command
|
||||
// and its intermediates and prefixes. The command byte is always the least
|
||||
// significant byte i.e. [Cmd & 0xff]. Use the [Cmd] type to unpack the
|
||||
// command, intermediate, and prefix bytes. Note that we only collect the last
|
||||
// prefix character and intermediate byte.
|
||||
//
|
||||
// The [p.Params] slice will contain the parameters of the sequence. Any
|
||||
// sub-parameter will have the [parser.HasMoreFlag] set. Use the [Param] type
|
||||
// to unpack the parameters.
|
||||
//
|
||||
// Example:
|
||||
//
|
||||
// var state byte // the initial state is always zero [NormalState]
|
||||
// p := NewParser(32, 1024) // create a new parser with a 32 params buffer and 1024 data buffer (optional)
|
||||
// input := []byte("\x1b[31mHello, World!\x1b[0m")
|
||||
// for len(input) > 0 {
|
||||
// seq, width, n, newState := DecodeSequence(input, state, p)
|
||||
// log.Printf("seq: %q, width: %d", seq, width)
|
||||
// state = newState
|
||||
// input = input[n:]
|
||||
// }
|
||||
func (m Method) DecodeSequence(data []byte, state byte, p *Parser) (seq []byte, width, n int, newState byte) {
|
||||
return decodeSequence(m, data, state, p)
|
||||
}
|
||||
|
||||
// DecodeSequenceInString decodes the first ANSI escape sequence or a printable
|
||||
// grapheme from the given data. It returns the sequence slice, the number of
|
||||
// bytes read, the cell width for each sequence, and the new state.
|
||||
//
|
||||
// The cell width will always be 0 for control and escape sequences, 1 for
|
||||
// ASCII printable characters, and the number of cells other Unicode characters
|
||||
// occupy. It uses the uniseg package to calculate the width of Unicode
|
||||
// graphemes and characters. This means it will always do grapheme clustering
|
||||
// (mode 2027).
|
||||
//
|
||||
// Passing a non-nil [*Parser] as the last argument will allow the decoder to
|
||||
// collect sequence parameters, data, and commands. The parser cmd will have
|
||||
// the packed command value that contains intermediate and prefix characters.
|
||||
// In the case of a OSC sequence, the cmd will be the OSC command number. Use
|
||||
// [Cmd] and [Param] types to unpack command intermediates and prefixes as well
|
||||
// as parameters.
|
||||
//
|
||||
// Zero [Cmd] means the CSI, DCS, or ESC sequence is invalid. Moreover, checking the
|
||||
// validity of other data sequences, OSC, DCS, etc, will require checking for
|
||||
// the returned sequence terminator bytes such as ST (ESC \\) and BEL).
|
||||
//
|
||||
// We store the command byte in [Cmd] in the most significant byte, the
|
||||
// prefix byte in the next byte, and the intermediate byte in the least
|
||||
// significant byte. This is done to avoid using a struct to store the command
|
||||
// and its intermediates and prefixes. The command byte is always the least
|
||||
// significant byte i.e. [Cmd & 0xff]. Use the [Cmd] type to unpack the
|
||||
// command, intermediate, and prefix bytes. Note that we only collect the last
|
||||
// prefix character and intermediate byte.
|
||||
//
|
||||
// The [p.Params] slice will contain the parameters of the sequence. Any
|
||||
// sub-parameter will have the [parser.HasMoreFlag] set. Use the [Param] type
|
||||
// to unpack the parameters.
|
||||
//
|
||||
// Example:
|
||||
//
|
||||
// var state byte // the initial state is always zero [NormalState]
|
||||
// p := NewParser(32, 1024) // create a new parser with a 32 params buffer and 1024 data buffer (optional)
|
||||
// input := []byte("\x1b[31mHello, World!\x1b[0m")
|
||||
// for len(input) > 0 {
|
||||
// seq, width, n, newState := DecodeSequenceInString(input, state, p)
|
||||
// log.Printf("seq: %q, width: %d", seq, width)
|
||||
// state = newState
|
||||
// input = input[n:]
|
||||
// }
|
||||
func (m Method) DecodeSequenceInString(data string, state byte, p *Parser) (seq string, width, n int, newState byte) {
|
||||
return decodeSequence(m, data, state, p)
|
||||
}
|
||||
847
vendor/github.com/charmbracelet/x/ansi/mode.go
generated
vendored
Normal file
847
vendor/github.com/charmbracelet/x/ansi/mode.go
generated
vendored
Normal file
|
|
@ -0,0 +1,847 @@
|
|||
package ansi
|
||||
|
||||
import (
|
||||
"strconv"
|
||||
"strings"
|
||||
)
|
||||
|
||||
// ModeSetting represents a mode setting.
|
||||
type ModeSetting byte
|
||||
|
||||
// ModeSetting constants.
|
||||
const (
|
||||
ModeNotRecognized ModeSetting = iota
|
||||
ModeSet
|
||||
ModeReset
|
||||
ModePermanentlySet
|
||||
ModePermanentlyReset
|
||||
)
|
||||
|
||||
// IsNotRecognized returns true if the mode is not recognized.
|
||||
func (m ModeSetting) IsNotRecognized() bool {
|
||||
return m == ModeNotRecognized
|
||||
}
|
||||
|
||||
// IsSet returns true if the mode is set or permanently set.
|
||||
func (m ModeSetting) IsSet() bool {
|
||||
return m == ModeSet || m == ModePermanentlySet
|
||||
}
|
||||
|
||||
// IsReset returns true if the mode is reset or permanently reset.
|
||||
func (m ModeSetting) IsReset() bool {
|
||||
return m == ModeReset || m == ModePermanentlyReset
|
||||
}
|
||||
|
||||
// IsPermanentlySet returns true if the mode is permanently set.
|
||||
func (m ModeSetting) IsPermanentlySet() bool {
|
||||
return m == ModePermanentlySet
|
||||
}
|
||||
|
||||
// IsPermanentlyReset returns true if the mode is permanently reset.
|
||||
func (m ModeSetting) IsPermanentlyReset() bool {
|
||||
return m == ModePermanentlyReset
|
||||
}
|
||||
|
||||
// Mode represents an interface for terminal modes.
|
||||
// Modes can be set, reset, and requested.
|
||||
type Mode interface {
|
||||
Mode() int
|
||||
}
|
||||
|
||||
// SetMode (SM) or (DECSET) returns a sequence to set a mode.
|
||||
// The mode arguments are a list of modes to set.
|
||||
//
|
||||
// If one of the modes is a [DECMode], the function will returns two escape
|
||||
// sequences.
|
||||
//
|
||||
// ANSI format:
|
||||
//
|
||||
// CSI Pd ; ... ; Pd h
|
||||
//
|
||||
// DEC format:
|
||||
//
|
||||
// CSI ? Pd ; ... ; Pd h
|
||||
//
|
||||
// See: https://vt100.net/docs/vt510-rm/SM.html
|
||||
func SetMode(modes ...Mode) string {
|
||||
return setMode(false, modes...)
|
||||
}
|
||||
|
||||
// SM is an alias for [SetMode].
|
||||
func SM(modes ...Mode) string {
|
||||
return SetMode(modes...)
|
||||
}
|
||||
|
||||
// DECSET is an alias for [SetMode].
|
||||
func DECSET(modes ...Mode) string {
|
||||
return SetMode(modes...)
|
||||
}
|
||||
|
||||
// ResetMode (RM) or (DECRST) returns a sequence to reset a mode.
|
||||
// The mode arguments are a list of modes to reset.
|
||||
//
|
||||
// If one of the modes is a [DECMode], the function will returns two escape
|
||||
// sequences.
|
||||
//
|
||||
// ANSI format:
|
||||
//
|
||||
// CSI Pd ; ... ; Pd l
|
||||
//
|
||||
// DEC format:
|
||||
//
|
||||
// CSI ? Pd ; ... ; Pd l
|
||||
//
|
||||
// See: https://vt100.net/docs/vt510-rm/RM.html
|
||||
func ResetMode(modes ...Mode) string {
|
||||
return setMode(true, modes...)
|
||||
}
|
||||
|
||||
// RM is an alias for [ResetMode].
|
||||
func RM(modes ...Mode) string {
|
||||
return ResetMode(modes...)
|
||||
}
|
||||
|
||||
// DECRST is an alias for [ResetMode].
|
||||
func DECRST(modes ...Mode) string {
|
||||
return ResetMode(modes...)
|
||||
}
|
||||
|
||||
func setMode(reset bool, modes ...Mode) (s string) {
|
||||
if len(modes) == 0 {
|
||||
return //nolint:nakedret
|
||||
}
|
||||
|
||||
cmd := "h"
|
||||
if reset {
|
||||
cmd = "l"
|
||||
}
|
||||
|
||||
seq := "\x1b["
|
||||
if len(modes) == 1 {
|
||||
switch modes[0].(type) {
|
||||
case DECMode:
|
||||
seq += "?"
|
||||
}
|
||||
return seq + strconv.Itoa(modes[0].Mode()) + cmd
|
||||
}
|
||||
|
||||
dec := make([]string, 0, len(modes)/2)
|
||||
ansi := make([]string, 0, len(modes)/2)
|
||||
for _, m := range modes {
|
||||
switch m.(type) {
|
||||
case DECMode:
|
||||
dec = append(dec, strconv.Itoa(m.Mode()))
|
||||
case ANSIMode:
|
||||
ansi = append(ansi, strconv.Itoa(m.Mode()))
|
||||
}
|
||||
}
|
||||
|
||||
if len(ansi) > 0 {
|
||||
s += seq + strings.Join(ansi, ";") + cmd
|
||||
}
|
||||
if len(dec) > 0 {
|
||||
s += seq + "?" + strings.Join(dec, ";") + cmd
|
||||
}
|
||||
return //nolint:nakedret
|
||||
}
|
||||
|
||||
// RequestMode (DECRQM) returns a sequence to request a mode from the terminal.
|
||||
// The terminal responds with a report mode function [DECRPM].
|
||||
//
|
||||
// ANSI format:
|
||||
//
|
||||
// CSI Pa $ p
|
||||
//
|
||||
// DEC format:
|
||||
//
|
||||
// CSI ? Pa $ p
|
||||
//
|
||||
// See: https://vt100.net/docs/vt510-rm/DECRQM.html
|
||||
func RequestMode(m Mode) string {
|
||||
seq := "\x1b["
|
||||
switch m.(type) {
|
||||
case DECMode:
|
||||
seq += "?"
|
||||
}
|
||||
return seq + strconv.Itoa(m.Mode()) + "$p"
|
||||
}
|
||||
|
||||
// DECRQM is an alias for [RequestMode].
|
||||
func DECRQM(m Mode) string {
|
||||
return RequestMode(m)
|
||||
}
|
||||
|
||||
// ReportMode (DECRPM) returns a sequence that the terminal sends to the host
|
||||
// in response to a mode request [DECRQM].
|
||||
//
|
||||
// ANSI format:
|
||||
//
|
||||
// CSI Pa ; Ps ; $ y
|
||||
//
|
||||
// DEC format:
|
||||
//
|
||||
// CSI ? Pa ; Ps $ y
|
||||
//
|
||||
// Where Pa is the mode number, and Ps is the mode value.
|
||||
//
|
||||
// 0: Not recognized
|
||||
// 1: Set
|
||||
// 2: Reset
|
||||
// 3: Permanent set
|
||||
// 4: Permanent reset
|
||||
//
|
||||
// See: https://vt100.net/docs/vt510-rm/DECRPM.html
|
||||
func ReportMode(mode Mode, value ModeSetting) string {
|
||||
if value > 4 {
|
||||
value = 0
|
||||
}
|
||||
switch mode.(type) {
|
||||
case DECMode:
|
||||
return "\x1b[?" + strconv.Itoa(mode.Mode()) + ";" + strconv.Itoa(int(value)) + "$y"
|
||||
}
|
||||
return "\x1b[" + strconv.Itoa(mode.Mode()) + ";" + strconv.Itoa(int(value)) + "$y"
|
||||
}
|
||||
|
||||
// DECRPM is an alias for [ReportMode].
|
||||
func DECRPM(mode Mode, value ModeSetting) string {
|
||||
return ReportMode(mode, value)
|
||||
}
|
||||
|
||||
// ANSIMode represents an ANSI terminal mode.
|
||||
type ANSIMode int //nolint:revive
|
||||
|
||||
// Mode returns the ANSI mode as an integer.
|
||||
func (m ANSIMode) Mode() int {
|
||||
return int(m)
|
||||
}
|
||||
|
||||
// DECMode represents a private DEC terminal mode.
|
||||
type DECMode int
|
||||
|
||||
// Mode returns the DEC mode as an integer.
|
||||
func (m DECMode) Mode() int {
|
||||
return int(m)
|
||||
}
|
||||
|
||||
// Keyboard Action Mode (KAM) is a mode that controls locking of the keyboard.
|
||||
// When the keyboard is locked, it cannot send data to the terminal.
|
||||
//
|
||||
// See: https://vt100.net/docs/vt510-rm/KAM.html
|
||||
const (
|
||||
KeyboardActionMode = ANSIMode(2)
|
||||
KAM = KeyboardActionMode
|
||||
|
||||
SetKeyboardActionMode = "\x1b[2h"
|
||||
ResetKeyboardActionMode = "\x1b[2l"
|
||||
RequestKeyboardActionMode = "\x1b[2$p"
|
||||
)
|
||||
|
||||
// Insert/Replace Mode (IRM) is a mode that determines whether characters are
|
||||
// inserted or replaced when typed.
|
||||
//
|
||||
// When enabled, characters are inserted at the cursor position pushing the
|
||||
// characters to the right. When disabled, characters replace the character at
|
||||
// the cursor position.
|
||||
//
|
||||
// See: https://vt100.net/docs/vt510-rm/IRM.html
|
||||
const (
|
||||
InsertReplaceMode = ANSIMode(4)
|
||||
IRM = InsertReplaceMode
|
||||
|
||||
SetInsertReplaceMode = "\x1b[4h"
|
||||
ResetInsertReplaceMode = "\x1b[4l"
|
||||
RequestInsertReplaceMode = "\x1b[4$p"
|
||||
)
|
||||
|
||||
// BiDirectional Support Mode (BDSM) is a mode that determines whether the
|
||||
// terminal supports bidirectional text. When enabled, the terminal supports
|
||||
// bidirectional text and is set to implicit bidirectional mode. When disabled,
|
||||
// the terminal does not support bidirectional text.
|
||||
//
|
||||
// See ECMA-48 7.2.1.
|
||||
const (
|
||||
BiDirectionalSupportMode = ANSIMode(8)
|
||||
BDSM = BiDirectionalSupportMode
|
||||
|
||||
SetBiDirectionalSupportMode = "\x1b[8h"
|
||||
ResetBiDirectionalSupportMode = "\x1b[8l"
|
||||
RequestBiDirectionalSupportMode = "\x1b[8$p"
|
||||
)
|
||||
|
||||
// Send Receive Mode (SRM) or Local Echo Mode is a mode that determines whether
|
||||
// the terminal echoes characters back to the host. When enabled, the terminal
|
||||
// sends characters to the host as they are typed.
|
||||
//
|
||||
// See: https://vt100.net/docs/vt510-rm/SRM.html
|
||||
const (
|
||||
SendReceiveMode = ANSIMode(12)
|
||||
LocalEchoMode = SendReceiveMode
|
||||
SRM = SendReceiveMode
|
||||
|
||||
SetSendReceiveMode = "\x1b[12h"
|
||||
ResetSendReceiveMode = "\x1b[12l"
|
||||
RequestSendReceiveMode = "\x1b[12$p"
|
||||
|
||||
SetLocalEchoMode = "\x1b[12h"
|
||||
ResetLocalEchoMode = "\x1b[12l"
|
||||
RequestLocalEchoMode = "\x1b[12$p"
|
||||
)
|
||||
|
||||
// Line Feed/New Line Mode (LNM) is a mode that determines whether the terminal
|
||||
// interprets the line feed character as a new line.
|
||||
//
|
||||
// When enabled, the terminal interprets the line feed character as a new line.
|
||||
// When disabled, the terminal interprets the line feed character as a line feed.
|
||||
//
|
||||
// A new line moves the cursor to the first position of the next line.
|
||||
// A line feed moves the cursor down one line without changing the column
|
||||
// scrolling the screen if necessary.
|
||||
//
|
||||
// See: https://vt100.net/docs/vt510-rm/LNM.html
|
||||
const (
|
||||
LineFeedNewLineMode = ANSIMode(20)
|
||||
LNM = LineFeedNewLineMode
|
||||
|
||||
SetLineFeedNewLineMode = "\x1b[20h"
|
||||
ResetLineFeedNewLineMode = "\x1b[20l"
|
||||
RequestLineFeedNewLineMode = "\x1b[20$p"
|
||||
)
|
||||
|
||||
// Cursor Keys Mode (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 (
|
||||
CursorKeysMode = DECMode(1)
|
||||
DECCKM = CursorKeysMode
|
||||
|
||||
SetCursorKeysMode = "\x1b[?1h"
|
||||
ResetCursorKeysMode = "\x1b[?1l"
|
||||
RequestCursorKeysMode = "\x1b[?1$p"
|
||||
)
|
||||
|
||||
// Deprecated: use [SetCursorKeysMode] and [ResetCursorKeysMode] instead.
|
||||
const (
|
||||
EnableCursorKeys = "\x1b[?1h" //nolint:revive // grouped constants
|
||||
DisableCursorKeys = "\x1b[?1l"
|
||||
)
|
||||
|
||||
// Origin Mode (DECOM) is a mode that determines whether the cursor moves to the
|
||||
// home position or the margin position.
|
||||
//
|
||||
// See: https://vt100.net/docs/vt510-rm/DECOM.html
|
||||
const (
|
||||
OriginMode = DECMode(6)
|
||||
DECOM = OriginMode
|
||||
|
||||
SetOriginMode = "\x1b[?6h"
|
||||
ResetOriginMode = "\x1b[?6l"
|
||||
RequestOriginMode = "\x1b[?6$p"
|
||||
)
|
||||
|
||||
// Auto Wrap Mode (DECAWM) is a mode that determines whether the cursor wraps
|
||||
// to the next line when it reaches the right margin.
|
||||
//
|
||||
// See: https://vt100.net/docs/vt510-rm/DECAWM.html
|
||||
const (
|
||||
AutoWrapMode = DECMode(7)
|
||||
DECAWM = AutoWrapMode
|
||||
|
||||
SetAutoWrapMode = "\x1b[?7h"
|
||||
ResetAutoWrapMode = "\x1b[?7l"
|
||||
RequestAutoWrapMode = "\x1b[?7$p"
|
||||
)
|
||||
|
||||
// X10 Mouse Mode is a mode that determines whether the mouse reports on button
|
||||
// presses.
|
||||
//
|
||||
// The terminal responds with the following encoding:
|
||||
//
|
||||
// CSI M CbCxCy
|
||||
//
|
||||
// Where Cb is the button-1, where it can be 1, 2, or 3.
|
||||
// Cx and Cy are the x and y coordinates of the mouse event.
|
||||
//
|
||||
// See: https://invisible-island.net/xterm/ctlseqs/ctlseqs.html#h2-Mouse-Tracking
|
||||
const (
|
||||
X10MouseMode = DECMode(9)
|
||||
|
||||
SetX10MouseMode = "\x1b[?9h"
|
||||
ResetX10MouseMode = "\x1b[?9l"
|
||||
RequestX10MouseMode = "\x1b[?9$p"
|
||||
)
|
||||
|
||||
// Text Cursor Enable Mode (DECTCEM) is a mode that shows/hides the cursor.
|
||||
//
|
||||
// See: https://vt100.net/docs/vt510-rm/DECTCEM.html
|
||||
const (
|
||||
TextCursorEnableMode = DECMode(25)
|
||||
DECTCEM = TextCursorEnableMode
|
||||
|
||||
SetTextCursorEnableMode = "\x1b[?25h"
|
||||
ResetTextCursorEnableMode = "\x1b[?25l"
|
||||
RequestTextCursorEnableMode = "\x1b[?25$p"
|
||||
)
|
||||
|
||||
// These are aliases for [SetTextCursorEnableMode] and [ResetTextCursorEnableMode].
|
||||
const (
|
||||
ShowCursor = SetTextCursorEnableMode
|
||||
HideCursor = ResetTextCursorEnableMode
|
||||
)
|
||||
|
||||
// Text Cursor Enable Mode (DECTCEM) is a mode that shows/hides the cursor.
|
||||
//
|
||||
// See: https://vt100.net/docs/vt510-rm/DECTCEM.html
|
||||
//
|
||||
// Deprecated: use [SetTextCursorEnableMode] and [ResetTextCursorEnableMode] instead.
|
||||
const (
|
||||
CursorEnableMode = DECMode(25)
|
||||
RequestCursorVisibility = "\x1b[?25$p"
|
||||
)
|
||||
|
||||
// Numeric Keypad Mode (DECNKM) is a mode that determines whether the keypad
|
||||
// sends application sequences or numeric sequences.
|
||||
//
|
||||
// This works like [DECKPAM] and [DECKPNM], but uses different sequences.
|
||||
//
|
||||
// See: https://vt100.net/docs/vt510-rm/DECNKM.html
|
||||
const (
|
||||
NumericKeypadMode = DECMode(66)
|
||||
DECNKM = NumericKeypadMode
|
||||
|
||||
SetNumericKeypadMode = "\x1b[?66h"
|
||||
ResetNumericKeypadMode = "\x1b[?66l"
|
||||
RequestNumericKeypadMode = "\x1b[?66$p"
|
||||
)
|
||||
|
||||
// Backarrow Key Mode (DECBKM) is a mode that determines whether the backspace
|
||||
// key sends a backspace or delete character. Disabled by default.
|
||||
//
|
||||
// See: https://vt100.net/docs/vt510-rm/DECBKM.html
|
||||
const (
|
||||
BackarrowKeyMode = DECMode(67)
|
||||
DECBKM = BackarrowKeyMode
|
||||
|
||||
SetBackarrowKeyMode = "\x1b[?67h"
|
||||
ResetBackarrowKeyMode = "\x1b[?67l"
|
||||
RequestBackarrowKeyMode = "\x1b[?67$p"
|
||||
)
|
||||
|
||||
// Left Right Margin Mode (DECLRMM) is a mode that determines whether the left
|
||||
// and right margins can be set with [DECSLRM].
|
||||
//
|
||||
// See: https://vt100.net/docs/vt510-rm/DECLRMM.html
|
||||
const (
|
||||
LeftRightMarginMode = DECMode(69)
|
||||
DECLRMM = LeftRightMarginMode
|
||||
|
||||
SetLeftRightMarginMode = "\x1b[?69h"
|
||||
ResetLeftRightMarginMode = "\x1b[?69l"
|
||||
RequestLeftRightMarginMode = "\x1b[?69$p"
|
||||
)
|
||||
|
||||
// Normal Mouse Mode is a mode that determines whether the mouse reports on
|
||||
// button presses and releases. It will also report modifier keys, wheel
|
||||
// events, and extra buttons.
|
||||
//
|
||||
// It uses the same encoding as [X10MouseMode] with a few differences:
|
||||
//
|
||||
// See: https://invisible-island.net/xterm/ctlseqs/ctlseqs.html#h2-Mouse-Tracking
|
||||
const (
|
||||
NormalMouseMode = DECMode(1000)
|
||||
|
||||
SetNormalMouseMode = "\x1b[?1000h"
|
||||
ResetNormalMouseMode = "\x1b[?1000l"
|
||||
RequestNormalMouseMode = "\x1b[?1000$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
|
||||
//
|
||||
// Deprecated: use [NormalMouseMode] instead.
|
||||
const (
|
||||
MouseMode = DECMode(1000)
|
||||
|
||||
EnableMouse = "\x1b[?1000h"
|
||||
DisableMouse = "\x1b[?1000l"
|
||||
RequestMouse = "\x1b[?1000$p"
|
||||
)
|
||||
|
||||
// Highlight Mouse Tracking is a mode that determines whether the mouse reports
|
||||
// on button presses, releases, and highlighted cells.
|
||||
//
|
||||
// It uses the same encoding as [NormalMouseMode] with a few differences:
|
||||
//
|
||||
// On highlight events, the terminal responds with the following encoding:
|
||||
//
|
||||
// CSI t CxCy
|
||||
// CSI T CxCyCxCyCxCy
|
||||
//
|
||||
// Where the parameters are startx, starty, endx, endy, mousex, and mousey.
|
||||
const (
|
||||
HighlightMouseMode = DECMode(1001)
|
||||
|
||||
SetHighlightMouseMode = "\x1b[?1001h"
|
||||
ResetHighlightMouseMode = "\x1b[?1001l"
|
||||
RequestHighlightMouseMode = "\x1b[?1001$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
|
||||
//
|
||||
// Deprecated: use [HighlightMouseMode] instead.
|
||||
const (
|
||||
MouseHiliteMode = DECMode(1001)
|
||||
|
||||
EnableMouseHilite = "\x1b[?1001h"
|
||||
DisableMouseHilite = "\x1b[?1001l"
|
||||
RequestMouseHilite = "\x1b[?1001$p"
|
||||
)
|
||||
|
||||
// Button Event Mouse Tracking is essentially the same as [NormalMouseMode],
|
||||
// but it also reports button-motion events when a button is pressed.
|
||||
//
|
||||
// See: https://invisible-island.net/xterm/ctlseqs/ctlseqs.html#h2-Mouse-Tracking
|
||||
const (
|
||||
ButtonEventMouseMode = DECMode(1002)
|
||||
|
||||
SetButtonEventMouseMode = "\x1b[?1002h"
|
||||
ResetButtonEventMouseMode = "\x1b[?1002l"
|
||||
RequestButtonEventMouseMode = "\x1b[?1002$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
|
||||
//
|
||||
// Deprecated: use [ButtonEventMouseMode] instead.
|
||||
const (
|
||||
MouseCellMotionMode = DECMode(1002)
|
||||
|
||||
EnableMouseCellMotion = "\x1b[?1002h"
|
||||
DisableMouseCellMotion = "\x1b[?1002l"
|
||||
RequestMouseCellMotion = "\x1b[?1002$p"
|
||||
)
|
||||
|
||||
// Any Event Mouse Tracking is the same as [ButtonEventMouseMode], except that
|
||||
// all motion events are reported even if no mouse buttons are pressed.
|
||||
//
|
||||
// See: https://invisible-island.net/xterm/ctlseqs/ctlseqs.html#h2-Mouse-Tracking
|
||||
const (
|
||||
AnyEventMouseMode = DECMode(1003)
|
||||
|
||||
SetAnyEventMouseMode = "\x1b[?1003h"
|
||||
ResetAnyEventMouseMode = "\x1b[?1003l"
|
||||
RequestAnyEventMouseMode = "\x1b[?1003$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
|
||||
//
|
||||
// Deprecated: use [AnyEventMouseMode] instead.
|
||||
const (
|
||||
MouseAllMotionMode = DECMode(1003)
|
||||
|
||||
EnableMouseAllMotion = "\x1b[?1003h"
|
||||
DisableMouseAllMotion = "\x1b[?1003l"
|
||||
RequestMouseAllMotion = "\x1b[?1003$p"
|
||||
)
|
||||
|
||||
// Focus Event Mode is a mode that determines whether the terminal reports focus
|
||||
// and blur events.
|
||||
//
|
||||
// The terminal sends the following encoding:
|
||||
//
|
||||
// CSI I // Focus In
|
||||
// CSI O // Focus Out
|
||||
//
|
||||
// See: https://invisible-island.net/xterm/ctlseqs/ctlseqs.html#h2-Focus-Tracking
|
||||
const (
|
||||
FocusEventMode = DECMode(1004)
|
||||
|
||||
SetFocusEventMode = "\x1b[?1004h"
|
||||
ResetFocusEventMode = "\x1b[?1004l"
|
||||
RequestFocusEventMode = "\x1b[?1004$p"
|
||||
)
|
||||
|
||||
// Deprecated: use [SetFocusEventMode], [ResetFocusEventMode], and
|
||||
// [RequestFocusEventMode] instead.
|
||||
// Focus reporting mode constants.
|
||||
const (
|
||||
ReportFocusMode = DECMode(1004) //nolint:revive // grouped constants
|
||||
|
||||
EnableReportFocus = "\x1b[?1004h"
|
||||
DisableReportFocus = "\x1b[?1004l"
|
||||
RequestReportFocus = "\x1b[?1004$p"
|
||||
)
|
||||
|
||||
// SGR Extended Mouse Mode is a mode that changes the mouse tracking encoding
|
||||
// to use SGR parameters.
|
||||
//
|
||||
// The terminal responds with the following encoding:
|
||||
//
|
||||
// CSI < Cb ; Cx ; Cy M
|
||||
//
|
||||
// Where Cb is the same as [NormalMouseMode], and Cx and Cy are the x and y.
|
||||
//
|
||||
// See: https://invisible-island.net/xterm/ctlseqs/ctlseqs.html#h2-Mouse-Tracking
|
||||
const (
|
||||
SgrExtMouseMode = DECMode(1006)
|
||||
|
||||
SetSgrExtMouseMode = "\x1b[?1006h"
|
||||
ResetSgrExtMouseMode = "\x1b[?1006l"
|
||||
RequestSgrExtMouseMode = "\x1b[?1006$p"
|
||||
)
|
||||
|
||||
// Deprecated: use [SgrExtMouseMode] [SetSgrExtMouseMode],
|
||||
// [ResetSgrExtMouseMode], and [RequestSgrExtMouseMode] instead.
|
||||
const (
|
||||
MouseSgrExtMode = DECMode(1006) //nolint:revive // grouped constants
|
||||
EnableMouseSgrExt = "\x1b[?1006h"
|
||||
DisableMouseSgrExt = "\x1b[?1006l"
|
||||
RequestMouseSgrExt = "\x1b[?1006$p"
|
||||
)
|
||||
|
||||
// UTF-8 Extended Mouse Mode is a mode that changes the mouse tracking encoding
|
||||
// to use UTF-8 parameters.
|
||||
//
|
||||
// See: https://invisible-island.net/xterm/ctlseqs/ctlseqs.html#h2-Mouse-Tracking
|
||||
const (
|
||||
Utf8ExtMouseMode = DECMode(1005)
|
||||
|
||||
SetUtf8ExtMouseMode = "\x1b[?1005h"
|
||||
ResetUtf8ExtMouseMode = "\x1b[?1005l"
|
||||
RequestUtf8ExtMouseMode = "\x1b[?1005$p"
|
||||
)
|
||||
|
||||
// URXVT Extended Mouse Mode is a mode that changes the mouse tracking encoding
|
||||
// to use an alternate encoding.
|
||||
//
|
||||
// See: https://invisible-island.net/xterm/ctlseqs/ctlseqs.html#h2-Mouse-Tracking
|
||||
const (
|
||||
UrxvtExtMouseMode = DECMode(1015)
|
||||
|
||||
SetUrxvtExtMouseMode = "\x1b[?1015h"
|
||||
ResetUrxvtExtMouseMode = "\x1b[?1015l"
|
||||
RequestUrxvtExtMouseMode = "\x1b[?1015$p"
|
||||
)
|
||||
|
||||
// SGR Pixel Extended Mouse Mode is a mode that changes the mouse tracking
|
||||
// encoding to use SGR parameters with pixel coordinates.
|
||||
//
|
||||
// This is similar to [SgrExtMouseMode], but also reports pixel coordinates.
|
||||
//
|
||||
// See: https://invisible-island.net/xterm/ctlseqs/ctlseqs.html#h2-Mouse-Tracking
|
||||
const (
|
||||
SgrPixelExtMouseMode = DECMode(1016)
|
||||
|
||||
SetSgrPixelExtMouseMode = "\x1b[?1016h"
|
||||
ResetSgrPixelExtMouseMode = "\x1b[?1016l"
|
||||
RequestSgrPixelExtMouseMode = "\x1b[?1016$p"
|
||||
)
|
||||
|
||||
// Alternate Screen Mode is a mode that determines whether the alternate screen
|
||||
// buffer is active. When this mode is enabled, the alternate screen buffer is
|
||||
// cleared.
|
||||
//
|
||||
// See: https://invisible-island.net/xterm/ctlseqs/ctlseqs.html#h2-The-Alternate-Screen-Buffer
|
||||
const (
|
||||
AltScreenMode = DECMode(1047)
|
||||
|
||||
SetAltScreenMode = "\x1b[?1047h"
|
||||
ResetAltScreenMode = "\x1b[?1047l"
|
||||
RequestAltScreenMode = "\x1b[?1047$p"
|
||||
)
|
||||
|
||||
// Save Cursor Mode is a mode that saves the cursor position.
|
||||
// This is equivalent to [SaveCursor] and [RestoreCursor].
|
||||
//
|
||||
// See: https://invisible-island.net/xterm/ctlseqs/ctlseqs.html#h2-The-Alternate-Screen-Buffer
|
||||
const (
|
||||
SaveCursorMode = DECMode(1048)
|
||||
|
||||
SetSaveCursorMode = "\x1b[?1048h"
|
||||
ResetSaveCursorMode = "\x1b[?1048l"
|
||||
RequestSaveCursorMode = "\x1b[?1048$p"
|
||||
)
|
||||
|
||||
// Alternate Screen Save Cursor Mode is a mode that saves the cursor position as in
|
||||
// [SaveCursorMode], switches to the alternate screen buffer as in [AltScreenMode],
|
||||
// and clears the screen on switch.
|
||||
//
|
||||
// See: https://invisible-island.net/xterm/ctlseqs/ctlseqs.html#h2-The-Alternate-Screen-Buffer
|
||||
const (
|
||||
AltScreenSaveCursorMode = DECMode(1049)
|
||||
|
||||
SetAltScreenSaveCursorMode = "\x1b[?1049h"
|
||||
ResetAltScreenSaveCursorMode = "\x1b[?1049l"
|
||||
RequestAltScreenSaveCursorMode = "\x1b[?1049$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
|
||||
//
|
||||
// Deprecated: use [AltScreenSaveCursorMode] instead.
|
||||
const (
|
||||
AltScreenBufferMode = DECMode(1049)
|
||||
|
||||
SetAltScreenBufferMode = "\x1b[?1049h"
|
||||
ResetAltScreenBufferMode = "\x1b[?1049l"
|
||||
RequestAltScreenBufferMode = "\x1b[?1049$p"
|
||||
|
||||
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 (
|
||||
BracketedPasteMode = DECMode(2004)
|
||||
|
||||
SetBracketedPasteMode = "\x1b[?2004h"
|
||||
ResetBracketedPasteMode = "\x1b[?2004l"
|
||||
RequestBracketedPasteMode = "\x1b[?2004$p"
|
||||
)
|
||||
|
||||
// Deprecated: use [SetBracketedPasteMode], [ResetBracketedPasteMode], and
|
||||
// [RequestBracketedPasteMode] instead.
|
||||
const (
|
||||
EnableBracketedPaste = "\x1b[?2004h" //nolint:revive // grouped constants
|
||||
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 (
|
||||
SynchronizedOutputMode = DECMode(2026)
|
||||
|
||||
SetSynchronizedOutputMode = "\x1b[?2026h"
|
||||
ResetSynchronizedOutputMode = "\x1b[?2026l"
|
||||
RequestSynchronizedOutputMode = "\x1b[?2026$p"
|
||||
)
|
||||
|
||||
// Synchronized Output Mode. See [SynchronizedOutputMode].
|
||||
//
|
||||
// Deprecated: use [SynchronizedOutputMode], [SetSynchronizedOutputMode], and
|
||||
// [ResetSynchronizedOutputMode], and [RequestSynchronizedOutputMode] instead.
|
||||
const (
|
||||
SyncdOutputMode = DECMode(2026)
|
||||
|
||||
EnableSyncdOutput = "\x1b[?2026h"
|
||||
DisableSyncdOutput = "\x1b[?2026l"
|
||||
RequestSyncdOutput = "\x1b[?2026$p"
|
||||
)
|
||||
|
||||
// Unicode Core Mode is a mode that determines whether the terminal should use
|
||||
// Unicode grapheme clustering to calculate the width of glyphs for each
|
||||
// terminal cell.
|
||||
//
|
||||
// See: https://github.com/contour-terminal/terminal-unicode-core
|
||||
const (
|
||||
UnicodeCoreMode = DECMode(2027)
|
||||
|
||||
SetUnicodeCoreMode = "\x1b[?2027h"
|
||||
ResetUnicodeCoreMode = "\x1b[?2027l"
|
||||
RequestUnicodeCoreMode = "\x1b[?2027$p"
|
||||
)
|
||||
|
||||
// Grapheme Clustering Mode is a mode that determines whether the terminal
|
||||
// should look for grapheme clusters instead of single runes in the rendered
|
||||
// text. This makes the terminal properly render combining characters such as
|
||||
// emojis.
|
||||
//
|
||||
// See: https://github.com/contour-terminal/terminal-unicode-core
|
||||
//
|
||||
// Deprecated: use [GraphemeClusteringMode], [SetUnicodeCoreMode],
|
||||
// [ResetUnicodeCoreMode], and [RequestUnicodeCoreMode] instead.
|
||||
const (
|
||||
GraphemeClusteringMode = DECMode(2027)
|
||||
|
||||
SetGraphemeClusteringMode = "\x1b[?2027h"
|
||||
ResetGraphemeClusteringMode = "\x1b[?2027l"
|
||||
RequestGraphemeClusteringMode = "\x1b[?2027$p"
|
||||
)
|
||||
|
||||
// Grapheme Clustering Mode. See [GraphemeClusteringMode].
|
||||
//
|
||||
// Deprecated: use [SetUnicodeCoreMode], [ResetUnicodeCoreMode], and
|
||||
// [RequestUnicodeCoreMode] instead.
|
||||
const (
|
||||
EnableGraphemeClustering = "\x1b[?2027h"
|
||||
DisableGraphemeClustering = "\x1b[?2027l"
|
||||
RequestGraphemeClustering = "\x1b[?2027$p"
|
||||
)
|
||||
|
||||
// LightDarkMode is a mode that enables reporting the operating system's color
|
||||
// scheme (light or dark) preference. It reports the color scheme as a [DSR]
|
||||
// and [LightDarkReport] escape sequences encoded as follows:
|
||||
//
|
||||
// CSI ? 997 ; 1 n for dark mode
|
||||
// CSI ? 997 ; 2 n for light mode
|
||||
//
|
||||
// The color preference can also be requested via the following [DSR] and
|
||||
// [RequestLightDarkReport] escape sequences:
|
||||
//
|
||||
// CSI ? 996 n
|
||||
//
|
||||
// See: https://contour-terminal.org/vt-extensions/color-palette-update-notifications/
|
||||
const (
|
||||
LightDarkMode = DECMode(2031)
|
||||
|
||||
SetLightDarkMode = "\x1b[?2031h"
|
||||
ResetLightDarkMode = "\x1b[?2031l"
|
||||
RequestLightDarkMode = "\x1b[?2031$p"
|
||||
)
|
||||
|
||||
// InBandResizeMode is a mode that reports terminal resize events as escape
|
||||
// sequences. This is useful for systems that do not support [SIGWINCH] like
|
||||
// Windows.
|
||||
//
|
||||
// The terminal then sends the following encoding:
|
||||
//
|
||||
// CSI 48 ; cellsHeight ; cellsWidth ; pixelHeight ; pixelWidth t
|
||||
//
|
||||
// See: https://gist.github.com/rockorager/e695fb2924d36b2bcf1fff4a3704bd83
|
||||
const (
|
||||
InBandResizeMode = DECMode(2048)
|
||||
|
||||
SetInBandResizeMode = "\x1b[?2048h"
|
||||
ResetInBandResizeMode = "\x1b[?2048l"
|
||||
RequestInBandResizeMode = "\x1b[?2048$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 (
|
||||
Win32InputMode = DECMode(9001)
|
||||
|
||||
SetWin32InputMode = "\x1b[?9001h"
|
||||
ResetWin32InputMode = "\x1b[?9001l"
|
||||
RequestWin32InputMode = "\x1b[?9001$p"
|
||||
)
|
||||
|
||||
// Deprecated: use [SetWin32InputMode], [ResetWin32InputMode], and
|
||||
// [RequestWin32InputMode] instead.
|
||||
const (
|
||||
EnableWin32Input = "\x1b[?9001h" //nolint:revive // grouped constants
|
||||
DisableWin32Input = "\x1b[?9001l"
|
||||
RequestWin32Input = "\x1b[?9001$p"
|
||||
)
|
||||
65
vendor/github.com/charmbracelet/x/ansi/modes.go
generated
vendored
Normal file
65
vendor/github.com/charmbracelet/x/ansi/modes.go
generated
vendored
Normal file
|
|
@ -0,0 +1,65 @@
|
|||
package ansi
|
||||
|
||||
// Modes represents the terminal modes that can be set or reset. By default,
|
||||
// all modes are [ModeNotRecognized].
|
||||
type Modes map[Mode]ModeSetting
|
||||
|
||||
// Get returns the setting of a terminal mode. If the mode is not set, it
|
||||
// returns [ModeNotRecognized].
|
||||
func (m Modes) Get(mode Mode) ModeSetting {
|
||||
return m[mode]
|
||||
}
|
||||
|
||||
// Delete deletes a terminal mode. This has the same effect as setting the mode
|
||||
// to [ModeNotRecognized].
|
||||
func (m Modes) Delete(mode Mode) {
|
||||
delete(m, mode)
|
||||
}
|
||||
|
||||
// Set sets a terminal mode to [ModeSet].
|
||||
func (m Modes) Set(modes ...Mode) {
|
||||
for _, mode := range modes {
|
||||
m[mode] = ModeSet
|
||||
}
|
||||
}
|
||||
|
||||
// PermanentlySet sets a terminal mode to [ModePermanentlySet].
|
||||
func (m Modes) PermanentlySet(modes ...Mode) {
|
||||
for _, mode := range modes {
|
||||
m[mode] = ModePermanentlySet
|
||||
}
|
||||
}
|
||||
|
||||
// Reset sets a terminal mode to [ModeReset].
|
||||
func (m Modes) Reset(modes ...Mode) {
|
||||
for _, mode := range modes {
|
||||
m[mode] = ModeReset
|
||||
}
|
||||
}
|
||||
|
||||
// PermanentlyReset sets a terminal mode to [ModePermanentlyReset].
|
||||
func (m Modes) PermanentlyReset(modes ...Mode) {
|
||||
for _, mode := range modes {
|
||||
m[mode] = ModePermanentlyReset
|
||||
}
|
||||
}
|
||||
|
||||
// IsSet returns true if the mode is set to [ModeSet] or [ModePermanentlySet].
|
||||
func (m Modes) IsSet(mode Mode) bool {
|
||||
return m[mode].IsSet()
|
||||
}
|
||||
|
||||
// IsPermanentlySet returns true if the mode is set to [ModePermanentlySet].
|
||||
func (m Modes) IsPermanentlySet(mode Mode) bool {
|
||||
return m[mode].IsPermanentlySet()
|
||||
}
|
||||
|
||||
// IsReset returns true if the mode is set to [ModeReset] or [ModePermanentlyReset].
|
||||
func (m Modes) IsReset(mode Mode) bool {
|
||||
return m[mode].IsReset()
|
||||
}
|
||||
|
||||
// IsPermanentlyReset returns true if the mode is set to [ModePermanentlyReset].
|
||||
func (m Modes) IsPermanentlyReset(mode Mode) bool {
|
||||
return m[mode].IsPermanentlyReset()
|
||||
}
|
||||
172
vendor/github.com/charmbracelet/x/ansi/mouse.go
generated
vendored
Normal file
172
vendor/github.com/charmbracelet/x/ansi/mouse.go
generated
vendored
Normal file
|
|
@ -0,0 +1,172 @@
|
|||
package ansi
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
)
|
||||
|
||||
// MouseButton represents the button that was pressed during a mouse message.
|
||||
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
|
||||
MouseButton1
|
||||
MouseButton2
|
||||
MouseButton3
|
||||
MouseButton4
|
||||
MouseButton5
|
||||
MouseButton6
|
||||
MouseButton7
|
||||
MouseButton8
|
||||
MouseButton9
|
||||
MouseButton10
|
||||
MouseButton11
|
||||
|
||||
MouseLeft = MouseButton1
|
||||
MouseMiddle = MouseButton2
|
||||
MouseRight = MouseButton3
|
||||
MouseWheelUp = MouseButton4
|
||||
MouseWheelDown = MouseButton5
|
||||
MouseWheelLeft = MouseButton6
|
||||
MouseWheelRight = MouseButton7
|
||||
MouseBackward = MouseButton8
|
||||
MouseForward = MouseButton9
|
||||
MouseRelease = MouseNone
|
||||
)
|
||||
|
||||
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",
|
||||
MouseButton10: "button10",
|
||||
MouseButton11: "button11",
|
||||
}
|
||||
|
||||
// String returns a string representation of the mouse button.
|
||||
func (b MouseButton) String() string {
|
||||
return mouseButtons[b]
|
||||
}
|
||||
|
||||
// EncodeMouseButton returns a byte representing a mouse button.
|
||||
// The button is a bitmask of the following leftmost values:
|
||||
//
|
||||
// - The first two bits are the button number:
|
||||
// 0 = left button, wheel up, or button no. 8 aka (backwards)
|
||||
// 1 = middle button, wheel down, or button no. 9 aka (forwards)
|
||||
// 2 = right button, wheel left, or button no. 10
|
||||
// 3 = release event, wheel right, or button no. 11
|
||||
//
|
||||
// - The third bit indicates whether the shift key was pressed.
|
||||
//
|
||||
// - The fourth bit indicates the alt key was pressed.
|
||||
//
|
||||
// - The fifth bit indicates the control key was pressed.
|
||||
//
|
||||
// - The sixth bit indicates motion events. Combined with button number 3, i.e.
|
||||
// release event, it represents a drag event.
|
||||
//
|
||||
// - The seventh bit indicates a wheel event.
|
||||
//
|
||||
// - The eighth bit indicates additional buttons.
|
||||
//
|
||||
// If button is [MouseNone], and motion is false, this returns a release event.
|
||||
// If button is undefined, this function returns 0xff.
|
||||
func EncodeMouseButton(b MouseButton, motion, shift, alt, ctrl bool) (m byte) {
|
||||
// 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
|
||||
)
|
||||
|
||||
if b == MouseNone {
|
||||
m = bitsMask
|
||||
} else if b >= MouseLeft && b <= MouseRight {
|
||||
m = byte(b - MouseLeft)
|
||||
} else if b >= MouseWheelUp && b <= MouseWheelRight {
|
||||
m = byte(b - MouseWheelUp)
|
||||
m |= bitWheel
|
||||
} else if b >= MouseBackward && b <= MouseButton11 {
|
||||
m = byte(b - MouseBackward)
|
||||
m |= bitAdd
|
||||
} else {
|
||||
m = 0xff // invalid button
|
||||
}
|
||||
|
||||
if shift {
|
||||
m |= bitShift
|
||||
}
|
||||
if alt {
|
||||
m |= bitAlt
|
||||
}
|
||||
if ctrl {
|
||||
m |= bitCtrl
|
||||
}
|
||||
if motion {
|
||||
m |= bitMotion
|
||||
}
|
||||
|
||||
return //nolint:nakedret
|
||||
}
|
||||
|
||||
// x10Offset is the offset for X10 mouse events.
|
||||
// See https://invisible-island.net/xterm/ctlseqs/ctlseqs.html#Mouse%20Tracking
|
||||
const x10Offset = 32
|
||||
|
||||
// MouseX10 returns an escape sequence representing a mouse event in X10 mode.
|
||||
// Note that this requires the terminal support X10 mouse modes.
|
||||
//
|
||||
// CSI M Cb Cx Cy
|
||||
//
|
||||
// See: https://invisible-island.net/xterm/ctlseqs/ctlseqs.html#Mouse%20Tracking
|
||||
func MouseX10(b byte, x, y int) string {
|
||||
return "\x1b[M" + string(b+x10Offset) + string(byte(x)+x10Offset+1) + string(byte(y)+x10Offset+1)
|
||||
}
|
||||
|
||||
// MouseSgr returns an escape sequence representing a mouse event in SGR mode.
|
||||
//
|
||||
// CSI < Cb ; Cx ; Cy M
|
||||
// CSI < Cb ; Cx ; Cy m (release)
|
||||
//
|
||||
// See: https://invisible-island.net/xterm/ctlseqs/ctlseqs.html#Mouse%20Tracking
|
||||
func MouseSgr(b byte, x, y int, release bool) string {
|
||||
s := 'M'
|
||||
if release {
|
||||
s = 'm'
|
||||
}
|
||||
if x < 0 {
|
||||
x = -x
|
||||
}
|
||||
if y < 0 {
|
||||
y = -y
|
||||
}
|
||||
return fmt.Sprintf("\x1b[<%d;%d;%d%c", b, x+1, y+1, s)
|
||||
}
|
||||
13
vendor/github.com/charmbracelet/x/ansi/notification.go
generated
vendored
Normal file
13
vendor/github.com/charmbracelet/x/ansi/notification.go
generated
vendored
Normal file
|
|
@ -0,0 +1,13 @@
|
|||
package ansi
|
||||
|
||||
// Notify sends a desktop notification using iTerm's OSC 9.
|
||||
//
|
||||
// OSC 9 ; Mc ST
|
||||
// OSC 9 ; Mc BEL
|
||||
//
|
||||
// Where Mc is the notification body.
|
||||
//
|
||||
// See: https://iterm2.com/documentation-escape-codes.html
|
||||
func Notify(s string) string {
|
||||
return "\x1b]9;" + s + "\x07"
|
||||
}
|
||||
417
vendor/github.com/charmbracelet/x/ansi/parser.go
generated
vendored
Normal file
417
vendor/github.com/charmbracelet/x/ansi/parser.go
generated
vendored
Normal file
|
|
@ -0,0 +1,417 @@
|
|||
package ansi
|
||||
|
||||
import (
|
||||
"unicode/utf8"
|
||||
"unsafe"
|
||||
|
||||
"github.com/charmbracelet/x/ansi/parser"
|
||||
)
|
||||
|
||||
// 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 {
|
||||
handler Handler
|
||||
|
||||
// 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.
|
||||
//
|
||||
// This is also used when collecting UTF-8 runes to keep track of the
|
||||
// number of rune bytes collected.
|
||||
paramsLen int
|
||||
|
||||
// cmd contains the raw command along with the private prefix and
|
||||
// intermediate bytes of the sequence.
|
||||
// The first lower byte contains the command byte, the next byte contains
|
||||
// the private prefix, and the next byte contains the intermediate byte.
|
||||
//
|
||||
// This is also used when collecting UTF-8 runes treating it as a slice of
|
||||
// 4 bytes.
|
||||
cmd int
|
||||
|
||||
// state is the current state of the parser.
|
||||
state byte
|
||||
}
|
||||
|
||||
// NewParser returns a new parser with the default settings.
|
||||
// The [Parser] uses a default size of 32 for the parameters and 64KB for the
|
||||
// data buffer. Use [Parser.SetParamsSize] and [Parser.SetDataSize] to set the
|
||||
// size of the parameters and data buffer respectively.
|
||||
func NewParser() *Parser {
|
||||
p := new(Parser)
|
||||
p.SetParamsSize(parser.MaxParamsSize)
|
||||
p.SetDataSize(1024 * 64) // 64KB data buffer
|
||||
return p
|
||||
}
|
||||
|
||||
// SetParamsSize sets the size of the parameters buffer.
|
||||
// This is used when constructing CSI and DCS sequences.
|
||||
func (p *Parser) SetParamsSize(size int) {
|
||||
p.params = make([]int, size)
|
||||
}
|
||||
|
||||
// SetDataSize sets the size of the data buffer.
|
||||
// This is used when constructing OSC, DCS, SOS, PM, and APC sequences.
|
||||
// If size is less than or equal to 0, the data buffer is unlimited and will
|
||||
// grow as needed.
|
||||
func (p *Parser) SetDataSize(size int) {
|
||||
if size <= 0 {
|
||||
size = 0
|
||||
p.dataLen = -1
|
||||
}
|
||||
p.data = make([]byte, size)
|
||||
}
|
||||
|
||||
// Params returns the list of parsed packed parameters.
|
||||
func (p *Parser) Params() Params {
|
||||
return unsafe.Slice((*Param)(unsafe.Pointer(&p.params[0])), p.paramsLen)
|
||||
}
|
||||
|
||||
// Param returns the parameter at the given index and falls back to the default
|
||||
// value if the parameter is missing. If the index is out of bounds, it returns
|
||||
// the default value and false.
|
||||
func (p *Parser) Param(i, def int) (int, bool) {
|
||||
if i < 0 || i >= p.paramsLen {
|
||||
return def, false
|
||||
}
|
||||
return Param(p.params[i]).Param(def), true
|
||||
}
|
||||
|
||||
// Command returns the packed command of the last dispatched sequence. Use
|
||||
// [Cmd] to unpack the command.
|
||||
func (p *Parser) Command() int {
|
||||
return p.cmd
|
||||
}
|
||||
|
||||
// Rune returns the last dispatched sequence as a rune.
|
||||
func (p *Parser) Rune() rune {
|
||||
rw := utf8ByteLen(byte(p.cmd & 0xff))
|
||||
if rw == -1 {
|
||||
return utf8.RuneError
|
||||
}
|
||||
r, _ := utf8.DecodeRune((*[utf8.UTFMax]byte)(unsafe.Pointer(&p.cmd))[:rw])
|
||||
return r
|
||||
}
|
||||
|
||||
// Control returns the last dispatched sequence as a control code.
|
||||
func (p *Parser) Control() byte {
|
||||
return byte(p.cmd & 0xff)
|
||||
}
|
||||
|
||||
// Data returns the raw data of the last dispatched sequence.
|
||||
func (p *Parser) Data() []byte {
|
||||
return p.data[:p.dataLen]
|
||||
}
|
||||
|
||||
// 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
|
||||
}
|
||||
|
||||
// State returns the current state of the parser.
|
||||
func (p *Parser) State() parser.State {
|
||||
return p.state
|
||||
}
|
||||
|
||||
// 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.
|
||||
// Deprecated: Loop over the buffer and call [Parser.Advance] instead.
|
||||
func (p *Parser) Parse(b []byte) {
|
||||
for i := range b {
|
||||
p.Advance(b[i])
|
||||
}
|
||||
}
|
||||
|
||||
// Advance advances the parser using the given byte. It returns the action
|
||||
// performed by the parser.
|
||||
func (p *Parser) Advance(b byte) parser.Action {
|
||||
switch p.state {
|
||||
case parser.Utf8State:
|
||||
// We handle UTF-8 here.
|
||||
return p.advanceUtf8(b)
|
||||
default:
|
||||
return p.advance(b)
|
||||
}
|
||||
}
|
||||
|
||||
func (p *Parser) collectRune(b byte) {
|
||||
if p.paramsLen >= utf8.UTFMax {
|
||||
return
|
||||
}
|
||||
|
||||
shift := p.paramsLen * 8
|
||||
p.cmd &^= 0xff << shift
|
||||
p.cmd |= int(b) << shift
|
||||
p.paramsLen++
|
||||
}
|
||||
|
||||
func (p *Parser) advanceUtf8(b byte) parser.Action {
|
||||
// Collect UTF-8 rune bytes.
|
||||
p.collectRune(b)
|
||||
rw := utf8ByteLen(byte(p.cmd & 0xff))
|
||||
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.paramsLen < rw {
|
||||
return parser.CollectAction
|
||||
}
|
||||
|
||||
// We have enough bytes to decode the rune using unsafe
|
||||
if p.handler.Print != nil {
|
||||
p.handler.Print(p.Rune())
|
||||
}
|
||||
|
||||
p.state = parser.GroundState
|
||||
p.paramsLen = 0
|
||||
|
||||
return parser.PrintAction
|
||||
}
|
||||
|
||||
func (p *Parser) advance(b byte) 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 {
|
||||
if p.state == parser.EscapeState {
|
||||
p.performAction(parser.ClearAction, state, 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(parser.StartAction, state, 0)
|
||||
}
|
||||
}
|
||||
|
||||
// Handle special cases
|
||||
switch {
|
||||
case b == ESC && p.state == parser.EscapeState:
|
||||
// Two ESCs in a row
|
||||
p.performAction(parser.ExecuteAction, state, b)
|
||||
default:
|
||||
p.performAction(action, state, b)
|
||||
}
|
||||
|
||||
p.state = state
|
||||
|
||||
return action
|
||||
}
|
||||
|
||||
func (p *Parser) parseStringCmd() {
|
||||
// Try to parse the command
|
||||
datalen := len(p.data)
|
||||
if p.dataLen >= 0 {
|
||||
datalen = p.dataLen
|
||||
}
|
||||
for i := range datalen {
|
||||
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')
|
||||
}
|
||||
}
|
||||
|
||||
func (p *Parser) performAction(action parser.Action, state parser.State, b byte) {
|
||||
switch action {
|
||||
case parser.IgnoreAction:
|
||||
break
|
||||
|
||||
case parser.ClearAction:
|
||||
p.clear()
|
||||
|
||||
case parser.PrintAction:
|
||||
p.cmd = int(b)
|
||||
if p.handler.Print != nil {
|
||||
p.handler.Print(rune(b))
|
||||
}
|
||||
|
||||
case parser.ExecuteAction:
|
||||
p.cmd = int(b)
|
||||
if p.handler.Execute != nil {
|
||||
p.handler.Execute(b)
|
||||
}
|
||||
|
||||
case parser.PrefixAction:
|
||||
// Collect private prefix
|
||||
// we only store the last prefix
|
||||
p.cmd &^= 0xff << parser.PrefixShift
|
||||
p.cmd |= int(b) << parser.PrefixShift
|
||||
|
||||
case parser.CollectAction:
|
||||
if state == parser.Utf8State {
|
||||
// Reset the UTF-8 counter
|
||||
p.paramsLen = 0
|
||||
p.collectRune(b)
|
||||
} else {
|
||||
// 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 != nil {
|
||||
p.data = p.data[: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 {
|
||||
p.parseStringCmd()
|
||||
}
|
||||
}
|
||||
|
||||
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 p.state == parser.OscStringState && p.cmd == parser.MissingCommand {
|
||||
// Ensure we have a command for OSC
|
||||
p.parseStringCmd()
|
||||
}
|
||||
|
||||
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)
|
||||
if p.handler.HandleCsi != nil {
|
||||
p.handler.HandleCsi(Cmd(p.cmd), p.Params())
|
||||
}
|
||||
case parser.EscapeState, parser.EscapeIntermediateState:
|
||||
p.cmd |= int(b)
|
||||
if p.handler.HandleEsc != nil {
|
||||
p.handler.HandleEsc(Cmd(p.cmd))
|
||||
}
|
||||
case parser.DcsEntryState, parser.DcsParamState, parser.DcsIntermediateState, parser.DcsStringState:
|
||||
if p.handler.HandleDcs != nil {
|
||||
p.handler.HandleDcs(Cmd(p.cmd), p.Params(), data)
|
||||
}
|
||||
case parser.OscStringState:
|
||||
if p.handler.HandleOsc != nil {
|
||||
p.handler.HandleOsc(p.cmd, data)
|
||||
}
|
||||
case parser.SosStringState:
|
||||
if p.handler.HandleSos != nil {
|
||||
p.handler.HandleSos(data)
|
||||
}
|
||||
case parser.PmStringState:
|
||||
if p.handler.HandlePm != nil {
|
||||
p.handler.HandlePm(data)
|
||||
}
|
||||
case parser.ApcStringState:
|
||||
if p.handler.HandleApc != nil {
|
||||
p.handler.HandleApc(data)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
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
|
||||
}
|
||||
79
vendor/github.com/charmbracelet/x/ansi/parser/const.go
generated
vendored
Normal file
79
vendor/github.com/charmbracelet/x/ansi/parser/const.go
generated
vendored
Normal file
|
|
@ -0,0 +1,79 @@
|
|||
// Package parser provides ANSI escape sequence parsing functionality.
|
||||
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
|
||||
PrefixAction
|
||||
DispatchAction
|
||||
ExecuteAction
|
||||
StartAction // Start of a data string
|
||||
PutAction // Put into the data string
|
||||
ParamAction
|
||||
PrintAction
|
||||
|
||||
IgnoreAction = NoneAction
|
||||
)
|
||||
|
||||
// ActionNames provides string names for parser actions.
|
||||
var ActionNames = []string{
|
||||
"NoneAction",
|
||||
"ClearAction",
|
||||
"CollectAction",
|
||||
"PrefixAction",
|
||||
"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
|
||||
)
|
||||
|
||||
// StateNames provides string names for parser states.
|
||||
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 (
|
||||
PrefixShift = 8
|
||||
IntermedShift = 16
|
||||
FinalMask = 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
|
||||
)
|
||||
|
||||
// Prefix returns the prefix 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 prefix.
|
||||
func Prefix(cmd int) int {
|
||||
return (cmd >> PrefixShift) & FinalMask
|
||||
}
|
||||
|
||||
// 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) & FinalMask
|
||||
}
|
||||
|
||||
// Command returns the command byte of the CSI sequence.
|
||||
func Command(cmd int) int {
|
||||
return cmd & FinalMask
|
||||
}
|
||||
|
||||
// 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 = range params {
|
||||
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 := range params {
|
||||
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 := range params {
|
||||
if !fn(i, Param(params, i), HasMore(params, i)) {
|
||||
break
|
||||
}
|
||||
}
|
||||
}
|
||||
273
vendor/github.com/charmbracelet/x/ansi/parser/transition_table.go
generated
vendored
Normal file
273
vendor/github.com/charmbracelet/x/ansi/parser/transition_table.go
generated
vendored
Normal file
|
|
@ -0,0 +1,273 @@
|
|||
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 := range t {
|
||||
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.
|
||||
// - The DEL (0x7F) character is executed in the Ground state.
|
||||
// - The DEL (0x7F) character is collected in the DcsPassthrough string state.
|
||||
// - The ST C1 control character (0x9C) is executed and not ignored.
|
||||
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, ExecuteAction, 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, CollectAction, Utf8State) // UTF8 2 byte sequence
|
||||
table.AddRange(0xE0, 0xEF, state, CollectAction, Utf8State) // UTF8 3 byte sequence
|
||||
table.AddRange(0xF0, 0xF4, state, CollectAction, 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, 0x7E, GroundState, PrintAction, GroundState)
|
||||
table.AddOne(0x7F, GroundState, ExecuteAction, 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, PrefixAction, 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, PutAction, 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, PrefixAction, 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
|
||||
}
|
||||
524
vendor/github.com/charmbracelet/x/ansi/parser_decode.go
generated
vendored
Normal file
524
vendor/github.com/charmbracelet/x/ansi/parser_decode.go
generated
vendored
Normal file
|
|
@ -0,0 +1,524 @@
|
|||
package ansi
|
||||
|
||||
import (
|
||||
"unicode/utf8"
|
||||
|
||||
"github.com/charmbracelet/x/ansi/parser"
|
||||
"github.com/mattn/go-runewidth"
|
||||
"github.com/rivo/uniseg"
|
||||
)
|
||||
|
||||
// State represents the state of the ANSI escape sequence parser used by
|
||||
// [DecodeSequence].
|
||||
type State = byte
|
||||
|
||||
// ANSI escape sequence states used by [DecodeSequence].
|
||||
const (
|
||||
NormalState State = iota
|
||||
PrefixState
|
||||
ParamsState
|
||||
IntermedState
|
||||
EscapeState
|
||||
StringState
|
||||
)
|
||||
|
||||
// DecodeSequence decodes the first ANSI escape sequence or a printable
|
||||
// grapheme from the given data. It returns the sequence slice, the number of
|
||||
// bytes read, the cell width for each sequence, and the new state.
|
||||
//
|
||||
// The cell width will always be 0 for control and escape sequences, 1 for
|
||||
// ASCII printable characters, and the number of cells other Unicode characters
|
||||
// occupy. It uses the uniseg package to calculate the width of Unicode
|
||||
// graphemes and characters. This means it will always do grapheme clustering
|
||||
// (mode 2027).
|
||||
//
|
||||
// Passing a non-nil [*Parser] as the last argument will allow the decoder to
|
||||
// collect sequence parameters, data, and commands. The parser cmd will have
|
||||
// the packed command value that contains intermediate and prefix characters.
|
||||
// In the case of a OSC sequence, the cmd will be the OSC command number. Use
|
||||
// [Cmd] and [Param] types to unpack command intermediates and prefixes as well
|
||||
// as parameters.
|
||||
//
|
||||
// Zero [Cmd] means the CSI, DCS, or ESC sequence is invalid. Moreover, checking the
|
||||
// validity of other data sequences, OSC, DCS, etc, will require checking for
|
||||
// the returned sequence terminator bytes such as ST (ESC \\) and BEL).
|
||||
//
|
||||
// We store the command byte in [Cmd] in the most significant byte, the
|
||||
// prefix byte in the next byte, and the intermediate byte in the least
|
||||
// significant byte. This is done to avoid using a struct to store the command
|
||||
// and its intermediates and prefixes. The command byte is always the least
|
||||
// significant byte i.e. [Cmd & 0xff]. Use the [Cmd] type to unpack the
|
||||
// command, intermediate, and prefix bytes. Note that we only collect the last
|
||||
// prefix character and intermediate byte.
|
||||
//
|
||||
// The [p.Params] slice will contain the parameters of the sequence. Any
|
||||
// sub-parameter will have the [parser.HasMoreFlag] set. Use the [Param] type
|
||||
// to unpack the parameters.
|
||||
//
|
||||
// Example:
|
||||
//
|
||||
// var state byte // the initial state is always zero [NormalState]
|
||||
// p := NewParser(32, 1024) // create a new parser with a 32 params buffer and 1024 data buffer (optional)
|
||||
// input := []byte("\x1b[31mHello, World!\x1b[0m")
|
||||
// for len(input) > 0 {
|
||||
// seq, width, n, newState := DecodeSequence(input, state, p)
|
||||
// log.Printf("seq: %q, width: %d", seq, width)
|
||||
// state = newState
|
||||
// input = input[n:]
|
||||
// }
|
||||
//
|
||||
// This function treats the text as a sequence of grapheme clusters.
|
||||
func DecodeSequence[T string | []byte](b T, state byte, p *Parser) (seq T, width int, n int, newState byte) {
|
||||
return decodeSequence(GraphemeWidth, b, state, p)
|
||||
}
|
||||
|
||||
// DecodeSequenceWc decodes the first ANSI escape sequence or a printable
|
||||
// grapheme from the given data. It returns the sequence slice, the number of
|
||||
// bytes read, the cell width for each sequence, and the new state.
|
||||
//
|
||||
// The cell width will always be 0 for control and escape sequences, 1 for
|
||||
// ASCII printable characters, and the number of cells other Unicode characters
|
||||
// occupy. It uses the uniseg package to calculate the width of Unicode
|
||||
// graphemes and characters. This means it will always do grapheme clustering
|
||||
// (mode 2027).
|
||||
//
|
||||
// Passing a non-nil [*Parser] as the last argument will allow the decoder to
|
||||
// collect sequence parameters, data, and commands. The parser cmd will have
|
||||
// the packed command value that contains intermediate and prefix characters.
|
||||
// In the case of a OSC sequence, the cmd will be the OSC command number. Use
|
||||
// [Cmd] and [Param] types to unpack command intermediates and prefixes as well
|
||||
// as parameters.
|
||||
//
|
||||
// Zero [Cmd] means the CSI, DCS, or ESC sequence is invalid. Moreover, checking the
|
||||
// validity of other data sequences, OSC, DCS, etc, will require checking for
|
||||
// the returned sequence terminator bytes such as ST (ESC \\) and BEL).
|
||||
//
|
||||
// We store the command byte in [Cmd] in the most significant byte, the
|
||||
// prefix byte in the next byte, and the intermediate byte in the least
|
||||
// significant byte. This is done to avoid using a struct to store the command
|
||||
// and its intermediates and prefixes. The command byte is always the least
|
||||
// significant byte i.e. [Cmd & 0xff]. Use the [Cmd] type to unpack the
|
||||
// command, intermediate, and prefix bytes. Note that we only collect the last
|
||||
// prefix character and intermediate byte.
|
||||
//
|
||||
// The [p.Params] slice will contain the parameters of the sequence. Any
|
||||
// sub-parameter will have the [parser.HasMoreFlag] set. Use the [Param] type
|
||||
// to unpack the parameters.
|
||||
//
|
||||
// Example:
|
||||
//
|
||||
// var state byte // the initial state is always zero [NormalState]
|
||||
// p := NewParser(32, 1024) // create a new parser with a 32 params buffer and 1024 data buffer (optional)
|
||||
// input := []byte("\x1b[31mHello, World!\x1b[0m")
|
||||
// for len(input) > 0 {
|
||||
// seq, width, n, newState := DecodeSequenceWc(input, state, p)
|
||||
// log.Printf("seq: %q, width: %d", seq, width)
|
||||
// state = newState
|
||||
// input = input[n:]
|
||||
// }
|
||||
//
|
||||
// This function treats the text as a sequence of wide characters and runes.
|
||||
func DecodeSequenceWc[T string | []byte](b T, state byte, p *Parser) (seq T, width int, n int, newState byte) {
|
||||
return decodeSequence(WcWidth, b, state, p)
|
||||
}
|
||||
|
||||
func decodeSequence[T string | []byte](m Method, b T, state State, p *Parser) (seq T, width int, n int, newState byte) {
|
||||
for i := 0; i < len(b); i++ {
|
||||
c := b[i]
|
||||
|
||||
switch state {
|
||||
case NormalState:
|
||||
switch c {
|
||||
case ESC:
|
||||
if p != nil {
|
||||
if len(p.params) > 0 {
|
||||
p.params[0] = parser.MissingParam
|
||||
}
|
||||
p.cmd = 0
|
||||
p.paramsLen = 0
|
||||
p.dataLen = 0
|
||||
}
|
||||
state = EscapeState
|
||||
continue
|
||||
case CSI, DCS:
|
||||
if p != nil {
|
||||
if len(p.params) > 0 {
|
||||
p.params[0] = parser.MissingParam
|
||||
}
|
||||
p.cmd = 0
|
||||
p.paramsLen = 0
|
||||
p.dataLen = 0
|
||||
}
|
||||
state = PrefixState
|
||||
continue
|
||||
case OSC, APC, SOS, PM:
|
||||
if p != nil {
|
||||
p.cmd = parser.MissingCommand
|
||||
p.dataLen = 0
|
||||
}
|
||||
state = StringState
|
||||
continue
|
||||
}
|
||||
|
||||
if p != nil {
|
||||
p.dataLen = 0
|
||||
p.paramsLen = 0
|
||||
p.cmd = 0
|
||||
}
|
||||
if c > US && c < DEL {
|
||||
// ASCII printable characters
|
||||
return b[i : i+1], 1, 1, NormalState
|
||||
}
|
||||
|
||||
if c <= US || c == DEL || c < 0xC0 {
|
||||
// C0 & C1 control characters & DEL
|
||||
return b[i : i+1], 0, 1, NormalState
|
||||
}
|
||||
|
||||
if utf8.RuneStart(c) {
|
||||
seq, _, width, _ = FirstGraphemeCluster(b, -1)
|
||||
if m == WcWidth {
|
||||
width = runewidth.StringWidth(string(seq))
|
||||
}
|
||||
i += len(seq)
|
||||
return b[:i], width, i, NormalState
|
||||
}
|
||||
|
||||
// Invalid UTF-8 sequence
|
||||
return b[:i], 0, i, NormalState
|
||||
case PrefixState:
|
||||
if c >= '<' && c <= '?' {
|
||||
if p != nil {
|
||||
// We only collect the last prefix character.
|
||||
p.cmd &^= 0xff << parser.PrefixShift
|
||||
p.cmd |= int(c) << parser.PrefixShift
|
||||
}
|
||||
break
|
||||
}
|
||||
|
||||
state = ParamsState
|
||||
fallthrough
|
||||
case ParamsState:
|
||||
if c >= '0' && c <= '9' {
|
||||
if p != nil {
|
||||
if p.params[p.paramsLen] == parser.MissingParam {
|
||||
p.params[p.paramsLen] = 0
|
||||
}
|
||||
|
||||
p.params[p.paramsLen] *= 10
|
||||
p.params[p.paramsLen] += int(c - '0')
|
||||
}
|
||||
break
|
||||
}
|
||||
|
||||
if c == ':' {
|
||||
if p != nil {
|
||||
p.params[p.paramsLen] |= parser.HasMoreFlag
|
||||
}
|
||||
}
|
||||
|
||||
if c == ';' || c == ':' {
|
||||
if p != nil {
|
||||
p.paramsLen++
|
||||
if p.paramsLen < len(p.params) {
|
||||
p.params[p.paramsLen] = parser.MissingParam
|
||||
}
|
||||
}
|
||||
break
|
||||
}
|
||||
|
||||
state = IntermedState
|
||||
fallthrough
|
||||
case IntermedState:
|
||||
if c >= ' ' && c <= '/' {
|
||||
if p != nil {
|
||||
p.cmd &^= 0xff << parser.IntermedShift
|
||||
p.cmd |= int(c) << parser.IntermedShift
|
||||
}
|
||||
break
|
||||
}
|
||||
|
||||
if p != nil {
|
||||
// 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 c >= '@' && c <= '~' {
|
||||
if p != nil {
|
||||
p.cmd &^= 0xff
|
||||
p.cmd |= int(c)
|
||||
}
|
||||
|
||||
if HasDcsPrefix(b) {
|
||||
// Continue to collect DCS data
|
||||
if p != nil {
|
||||
p.dataLen = 0
|
||||
}
|
||||
state = StringState
|
||||
continue
|
||||
}
|
||||
|
||||
return b[:i+1], 0, i + 1, NormalState
|
||||
}
|
||||
|
||||
// Invalid CSI/DCS sequence
|
||||
return b[:i], 0, i, NormalState
|
||||
case EscapeState:
|
||||
switch c {
|
||||
case '[', 'P':
|
||||
if p != nil {
|
||||
if len(p.params) > 0 {
|
||||
p.params[0] = parser.MissingParam
|
||||
}
|
||||
p.paramsLen = 0
|
||||
p.cmd = 0
|
||||
}
|
||||
state = PrefixState
|
||||
continue
|
||||
case ']', 'X', '^', '_':
|
||||
if p != nil {
|
||||
p.cmd = parser.MissingCommand
|
||||
p.dataLen = 0
|
||||
}
|
||||
state = StringState
|
||||
continue
|
||||
}
|
||||
|
||||
if c >= ' ' && c <= '/' {
|
||||
if p != nil {
|
||||
p.cmd &^= 0xff << parser.IntermedShift
|
||||
p.cmd |= int(c) << parser.IntermedShift
|
||||
}
|
||||
continue
|
||||
} else if c >= '0' && c <= '~' {
|
||||
if p != nil {
|
||||
p.cmd &^= 0xff
|
||||
p.cmd |= int(c)
|
||||
}
|
||||
return b[:i+1], 0, i + 1, NormalState
|
||||
}
|
||||
|
||||
// Invalid escape sequence
|
||||
return b[:i], 0, i, NormalState
|
||||
case StringState:
|
||||
switch c {
|
||||
case BEL:
|
||||
if HasOscPrefix(b) {
|
||||
parseOscCmd(p)
|
||||
return b[:i+1], 0, i + 1, NormalState
|
||||
}
|
||||
case CAN, SUB:
|
||||
if HasOscPrefix(b) {
|
||||
// Ensure we parse the OSC command number
|
||||
parseOscCmd(p)
|
||||
}
|
||||
|
||||
// Cancel the sequence
|
||||
return b[:i], 0, i, NormalState
|
||||
case ST:
|
||||
if HasOscPrefix(b) {
|
||||
// Ensure we parse the OSC command number
|
||||
parseOscCmd(p)
|
||||
}
|
||||
|
||||
return b[:i+1], 0, i + 1, NormalState
|
||||
case ESC:
|
||||
if HasStPrefix(b[i:]) {
|
||||
if HasOscPrefix(b) {
|
||||
// Ensure we parse the OSC command number
|
||||
parseOscCmd(p)
|
||||
}
|
||||
|
||||
// End of string 7-bit (ST)
|
||||
return b[:i+2], 0, i + 2, NormalState
|
||||
}
|
||||
|
||||
// Otherwise, cancel the sequence
|
||||
return b[:i], 0, i, NormalState
|
||||
}
|
||||
|
||||
if p != nil && p.dataLen < len(p.data) {
|
||||
p.data[p.dataLen] = c
|
||||
p.dataLen++
|
||||
|
||||
// Parse the OSC command number
|
||||
if c == ';' && HasOscPrefix(b) {
|
||||
parseOscCmd(p)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return b, 0, len(b), state
|
||||
}
|
||||
|
||||
func parseOscCmd(p *Parser) {
|
||||
if p == nil || p.cmd != parser.MissingCommand {
|
||||
return
|
||||
}
|
||||
for j := range p.dataLen {
|
||||
d := p.data[j]
|
||||
if d < '0' || d > '9' {
|
||||
break
|
||||
}
|
||||
if p.cmd == parser.MissingCommand {
|
||||
p.cmd = 0
|
||||
}
|
||||
p.cmd *= 10
|
||||
p.cmd += int(d - '0')
|
||||
}
|
||||
}
|
||||
|
||||
// Equal returns true if the given byte slices are equal.
|
||||
func Equal[T string | []byte](a, b T) bool {
|
||||
return string(a) == string(b)
|
||||
}
|
||||
|
||||
// HasPrefix returns true if the given byte slice has prefix.
|
||||
func HasPrefix[T string | []byte](b, prefix T) bool {
|
||||
return len(b) >= len(prefix) && Equal(b[0:len(prefix)], prefix)
|
||||
}
|
||||
|
||||
// HasSuffix returns true if the given byte slice has suffix.
|
||||
func HasSuffix[T string | []byte](b, suffix T) bool {
|
||||
return len(b) >= len(suffix) && Equal(b[len(b)-len(suffix):], suffix)
|
||||
}
|
||||
|
||||
// HasCsiPrefix returns true if the given byte slice has a CSI prefix.
|
||||
func HasCsiPrefix[T string | []byte](b T) bool {
|
||||
return (len(b) > 0 && b[0] == CSI) ||
|
||||
(len(b) > 1 && b[0] == ESC && b[1] == '[')
|
||||
}
|
||||
|
||||
// HasOscPrefix returns true if the given byte slice has an OSC prefix.
|
||||
func HasOscPrefix[T string | []byte](b T) bool {
|
||||
return (len(b) > 0 && b[0] == OSC) ||
|
||||
(len(b) > 1 && b[0] == ESC && b[1] == ']')
|
||||
}
|
||||
|
||||
// HasApcPrefix returns true if the given byte slice has an APC prefix.
|
||||
func HasApcPrefix[T string | []byte](b T) bool {
|
||||
return (len(b) > 0 && b[0] == APC) ||
|
||||
(len(b) > 1 && b[0] == ESC && b[1] == '_')
|
||||
}
|
||||
|
||||
// HasDcsPrefix returns true if the given byte slice has a DCS prefix.
|
||||
func HasDcsPrefix[T string | []byte](b T) bool {
|
||||
return (len(b) > 0 && b[0] == DCS) ||
|
||||
(len(b) > 1 && b[0] == ESC && b[1] == 'P')
|
||||
}
|
||||
|
||||
// HasSosPrefix returns true if the given byte slice has a SOS prefix.
|
||||
func HasSosPrefix[T string | []byte](b T) bool {
|
||||
return (len(b) > 0 && b[0] == SOS) ||
|
||||
(len(b) > 1 && b[0] == ESC && b[1] == 'X')
|
||||
}
|
||||
|
||||
// HasPmPrefix returns true if the given byte slice has a PM prefix.
|
||||
func HasPmPrefix[T string | []byte](b T) bool {
|
||||
return (len(b) > 0 && b[0] == PM) ||
|
||||
(len(b) > 1 && b[0] == ESC && b[1] == '^')
|
||||
}
|
||||
|
||||
// HasStPrefix returns true if the given byte slice has a ST prefix.
|
||||
func HasStPrefix[T string | []byte](b T) bool {
|
||||
return (len(b) > 0 && b[0] == ST) ||
|
||||
(len(b) > 1 && b[0] == ESC && b[1] == '\\')
|
||||
}
|
||||
|
||||
// HasEscPrefix returns true if the given byte slice has an ESC prefix.
|
||||
func HasEscPrefix[T string | []byte](b T) bool {
|
||||
return len(b) > 0 && b[0] == ESC
|
||||
}
|
||||
|
||||
// FirstGraphemeCluster returns the first grapheme cluster in the given string or byte slice.
|
||||
// This is a syntactic sugar function that wraps
|
||||
// uniseg.FirstGraphemeClusterInString and uniseg.FirstGraphemeCluster.
|
||||
func FirstGraphemeCluster[T string | []byte](b T, state int) (T, T, int, int) {
|
||||
switch b := any(b).(type) {
|
||||
case string:
|
||||
cluster, rest, width, newState := uniseg.FirstGraphemeClusterInString(b, state)
|
||||
return T(cluster), T(rest), width, newState
|
||||
case []byte:
|
||||
cluster, rest, width, newState := uniseg.FirstGraphemeCluster(b, state)
|
||||
return T(cluster), T(rest), width, newState
|
||||
}
|
||||
panic("unreachable")
|
||||
}
|
||||
|
||||
// Cmd represents a sequence command. This is used to pack/unpack a sequence
|
||||
// command with its intermediate and prefix characters. Those are commonly
|
||||
// found in CSI and DCS sequences.
|
||||
type Cmd int
|
||||
|
||||
// Prefix returns the unpacked prefix 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 prefix.
|
||||
func (c Cmd) Prefix() byte {
|
||||
return byte(parser.Prefix(int(c)))
|
||||
}
|
||||
|
||||
// Intermediate returns the unpacked 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 (c Cmd) Intermediate() byte {
|
||||
return byte(parser.Intermediate(int(c)))
|
||||
}
|
||||
|
||||
// Final returns the unpacked command byte of the CSI sequence.
|
||||
func (c Cmd) Final() byte {
|
||||
return byte(parser.Command(int(c)))
|
||||
}
|
||||
|
||||
// Command packs a command with the given prefix, intermediate, and final. A
|
||||
// zero byte means the sequence does not have a prefix or intermediate.
|
||||
//
|
||||
// Prefixes are in the range of 0x3C-0x3F that is one of `<=>?`.
|
||||
//
|
||||
// Intermediates are in the range of 0x20-0x2F that is anything in
|
||||
// `!"#$%&'()*+,-./`.
|
||||
//
|
||||
// Final bytes are in the range of 0x40-0x7E that is anything in the range
|
||||
// `@A–Z[\]^_`a–z{|}~`.
|
||||
func Command(prefix, inter, final byte) (c int) {
|
||||
c = int(final)
|
||||
c |= int(prefix) << parser.PrefixShift
|
||||
c |= int(inter) << parser.IntermedShift
|
||||
return
|
||||
}
|
||||
|
||||
// Param represents a sequence parameter. Sequence parameters with
|
||||
// sub-parameters are packed with the HasMoreFlag set. This is used to unpack
|
||||
// the parameters from a CSI and DCS sequences.
|
||||
type Param int
|
||||
|
||||
// Param returns the unpacked parameter at the given index.
|
||||
// It returns the default value if the parameter is missing.
|
||||
func (s Param) Param(def int) int {
|
||||
p := int(s) & parser.ParamMask
|
||||
if p == parser.MissingParam {
|
||||
return def
|
||||
}
|
||||
return p
|
||||
}
|
||||
|
||||
// HasMore unpacks the HasMoreFlag from the parameter.
|
||||
func (s Param) HasMore() bool {
|
||||
return s&parser.HasMoreFlag != 0
|
||||
}
|
||||
|
||||
// Parameter packs an escape code parameter with the given parameter and
|
||||
// whether this parameter has following sub-parameters.
|
||||
func Parameter(p int, hasMore bool) (s int) {
|
||||
s = p & parser.ParamMask
|
||||
if hasMore {
|
||||
s |= parser.HasMoreFlag
|
||||
}
|
||||
return
|
||||
}
|
||||
60
vendor/github.com/charmbracelet/x/ansi/parser_handler.go
generated
vendored
Normal file
60
vendor/github.com/charmbracelet/x/ansi/parser_handler.go
generated
vendored
Normal file
|
|
@ -0,0 +1,60 @@
|
|||
package ansi
|
||||
|
||||
import "unsafe"
|
||||
|
||||
// Params represents a list of packed parameters.
|
||||
type Params []Param
|
||||
|
||||
// Param returns the parameter at the given index and if it is part of a
|
||||
// sub-parameters. It falls back to the default value if the parameter is
|
||||
// missing. If the index is out of bounds, it returns the default value and
|
||||
// false.
|
||||
func (p Params) Param(i, def int) (int, bool, bool) {
|
||||
if i < 0 || i >= len(p) {
|
||||
return def, false, false
|
||||
}
|
||||
return p[i].Param(def), p[i].HasMore(), true
|
||||
}
|
||||
|
||||
// ForEach iterates over the parameters and calls the given function for each
|
||||
// parameter. If a parameter is part of a sub-parameter, it will be called with
|
||||
// hasMore set to true.
|
||||
// Use def to set a default value for missing parameters.
|
||||
func (p Params) ForEach(def int, f func(i, param int, hasMore bool)) {
|
||||
for i := range p {
|
||||
f(i, p[i].Param(def), p[i].HasMore())
|
||||
}
|
||||
}
|
||||
|
||||
// ToParams converts a list of integers to a list of parameters.
|
||||
func ToParams(params []int) Params {
|
||||
return unsafe.Slice((*Param)(unsafe.Pointer(¶ms[0])), len(params))
|
||||
}
|
||||
|
||||
// Handler handles actions performed by the parser.
|
||||
// It is used to handle ANSI escape sequences, control characters, and runes.
|
||||
type Handler struct {
|
||||
// Print is called when a printable rune is encountered.
|
||||
Print func(r rune)
|
||||
// Execute is called when a control character is encountered.
|
||||
Execute func(b byte)
|
||||
// HandleCsi is called when a CSI sequence is encountered.
|
||||
HandleCsi func(cmd Cmd, params Params)
|
||||
// HandleEsc is called when an ESC sequence is encountered.
|
||||
HandleEsc func(cmd Cmd)
|
||||
// HandleDcs is called when a DCS sequence is encountered.
|
||||
HandleDcs func(cmd Cmd, params Params, data []byte)
|
||||
// HandleOsc is called when an OSC sequence is encountered.
|
||||
HandleOsc func(cmd int, data []byte)
|
||||
// HandlePm is called when a PM sequence is encountered.
|
||||
HandlePm func(data []byte)
|
||||
// HandleApc is called when an APC sequence is encountered.
|
||||
HandleApc func(data []byte)
|
||||
// HandleSos is called when a SOS sequence is encountered.
|
||||
HandleSos func(data []byte)
|
||||
}
|
||||
|
||||
// SetHandler sets the handler for the parser.
|
||||
func (p *Parser) SetHandler(h Handler) {
|
||||
p.handler = h
|
||||
}
|
||||
29
vendor/github.com/charmbracelet/x/ansi/parser_sync.go
generated
vendored
Normal file
29
vendor/github.com/charmbracelet/x/ansi/parser_sync.go
generated
vendored
Normal file
|
|
@ -0,0 +1,29 @@
|
|||
package ansi
|
||||
|
||||
import (
|
||||
"sync"
|
||||
|
||||
"github.com/charmbracelet/x/ansi/parser"
|
||||
)
|
||||
|
||||
var parserPool = sync.Pool{
|
||||
New: func() any {
|
||||
p := NewParser()
|
||||
p.SetParamsSize(parser.MaxParamsSize)
|
||||
p.SetDataSize(1024 * 1024 * 4) // 4MB of data buffer
|
||||
return p
|
||||
},
|
||||
}
|
||||
|
||||
// GetParser returns a parser from a sync pool.
|
||||
func GetParser() *Parser {
|
||||
return parserPool.Get().(*Parser)
|
||||
}
|
||||
|
||||
// PutParser returns a parser to a sync pool. The parser is reset
|
||||
// automatically.
|
||||
func PutParser(p *Parser) {
|
||||
p.Reset()
|
||||
p.dataLen = 0
|
||||
parserPool.Put(p)
|
||||
}
|
||||
60
vendor/github.com/charmbracelet/x/ansi/passthrough.go
generated
vendored
Normal file
60
vendor/github.com/charmbracelet/x/ansi/passthrough.go
generated
vendored
Normal file
|
|
@ -0,0 +1,60 @@
|
|||
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 := min(i+limit, 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 := range len(seq) {
|
||||
if seq[i] == ESC {
|
||||
b.WriteByte(ESC)
|
||||
}
|
||||
b.WriteByte(seq[i])
|
||||
}
|
||||
b.WriteString("\x1b\\")
|
||||
return b.String()
|
||||
}
|
||||
7
vendor/github.com/charmbracelet/x/ansi/paste.go
generated
vendored
Normal file
7
vendor/github.com/charmbracelet/x/ansi/paste.go
generated
vendored
Normal file
|
|
@ -0,0 +1,7 @@
|
|||
package ansi
|
||||
|
||||
// BracketedPasteStart is the control sequence to enable bracketed paste mode.
|
||||
const BracketedPasteStart = "\x1b[200~"
|
||||
|
||||
// BracketedPasteEnd is the control sequence to disable bracketed paste mode.
|
||||
const BracketedPasteEnd = "\x1b[201~"
|
||||
11
vendor/github.com/charmbracelet/x/ansi/reset.go
generated
vendored
Normal file
11
vendor/github.com/charmbracelet/x/ansi/reset.go
generated
vendored
Normal file
|
|
@ -0,0 +1,11 @@
|
|||
package ansi
|
||||
|
||||
// ResetInitialState (RIS) resets the terminal to its initial state.
|
||||
//
|
||||
// ESC c
|
||||
//
|
||||
// See: https://vt100.net/docs/vt510-rm/RIS.html
|
||||
const (
|
||||
ResetInitialState = "\x1bc"
|
||||
RIS = ResetInitialState
|
||||
)
|
||||
410
vendor/github.com/charmbracelet/x/ansi/screen.go
generated
vendored
Normal file
410
vendor/github.com/charmbracelet/x/ansi/screen.go
generated
vendored
Normal file
|
|
@ -0,0 +1,410 @@
|
|||
package ansi
|
||||
|
||||
import (
|
||||
"strconv"
|
||||
"strings"
|
||||
)
|
||||
|
||||
// EraseDisplay (ED) clears the display or parts of the display. A screen is
|
||||
// the shown part of the terminal display excluding the scrollback buffer.
|
||||
// Possible values:
|
||||
//
|
||||
// Default is 0.
|
||||
//
|
||||
// 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 display which delete all lines saved in the scrollback buffer (xterm).
|
||||
//
|
||||
// CSI <n> J
|
||||
//
|
||||
// See: https://vt100.net/docs/vt510-rm/ED.html
|
||||
func EraseDisplay(n int) string {
|
||||
var s string
|
||||
if n > 0 {
|
||||
s = strconv.Itoa(n)
|
||||
}
|
||||
return "\x1b[" + s + "J"
|
||||
}
|
||||
|
||||
// ED is an alias for [EraseDisplay].
|
||||
func ED(n int) string {
|
||||
return EraseDisplay(n)
|
||||
}
|
||||
|
||||
// EraseDisplay constants.
|
||||
// These are the possible values for the EraseDisplay function.
|
||||
const (
|
||||
EraseScreenBelow = "\x1b[J"
|
||||
EraseScreenAbove = "\x1b[1J"
|
||||
EraseEntireScreen = "\x1b[2J"
|
||||
EraseEntireDisplay = "\x1b[3J"
|
||||
)
|
||||
|
||||
// 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 {
|
||||
var s string
|
||||
if n > 0 {
|
||||
s = strconv.Itoa(n)
|
||||
}
|
||||
return "\x1b[" + s + "K"
|
||||
}
|
||||
|
||||
// EL is an alias for [EraseLine].
|
||||
func EL(n int) string {
|
||||
return EraseLine(n)
|
||||
}
|
||||
|
||||
// EraseLine constants.
|
||||
// These are the possible values for the EraseLine function.
|
||||
const (
|
||||
EraseLineRight = "\x1b[K"
|
||||
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 Pn 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"
|
||||
}
|
||||
|
||||
// PanDown is an alias for [ScrollUp].
|
||||
func PanDown(n int) string {
|
||||
return ScrollUp(n)
|
||||
}
|
||||
|
||||
// SU is an alias for [ScrollUp].
|
||||
func SU(n int) string {
|
||||
return ScrollUp(n)
|
||||
}
|
||||
|
||||
// ScrollDown (SD) scrolls the screen down n lines. New lines are added at the
|
||||
// top of the screen.
|
||||
//
|
||||
// CSI Pn 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"
|
||||
}
|
||||
|
||||
// PanUp is an alias for [ScrollDown].
|
||||
func PanUp(n int) string {
|
||||
return ScrollDown(n)
|
||||
}
|
||||
|
||||
// SD is an alias for [ScrollDown].
|
||||
func SD(n int) string {
|
||||
return ScrollDown(n)
|
||||
}
|
||||
|
||||
// InsertLine (IL) inserts n blank lines at the current cursor position.
|
||||
// Existing lines are moved down.
|
||||
//
|
||||
// CSI Pn 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"
|
||||
}
|
||||
|
||||
// IL is an alias for [InsertLine].
|
||||
func IL(n int) string {
|
||||
return InsertLine(n)
|
||||
}
|
||||
|
||||
// DeleteLine (DL) deletes n lines at the current cursor position. Existing
|
||||
// lines are moved up.
|
||||
//
|
||||
// CSI Pn 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"
|
||||
}
|
||||
|
||||
// DL is an alias for [DeleteLine].
|
||||
func DL(n int) string {
|
||||
return DeleteLine(n)
|
||||
}
|
||||
|
||||
// SetTopBottomMargins (DECSTBM) sets the top and bottom margins for the scrolling
|
||||
// region. The default is the entire screen.
|
||||
//
|
||||
// Default is 1 and the bottom of the screen.
|
||||
//
|
||||
// CSI Pt ; Pb r
|
||||
//
|
||||
// See: https://vt100.net/docs/vt510-rm/DECSTBM.html
|
||||
func SetTopBottomMargins(top, bot int) string {
|
||||
var t, b string
|
||||
if top > 0 {
|
||||
t = strconv.Itoa(top)
|
||||
}
|
||||
if bot > 0 {
|
||||
b = strconv.Itoa(bot)
|
||||
}
|
||||
return "\x1b[" + t + ";" + b + "r"
|
||||
}
|
||||
|
||||
// DECSTBM is an alias for [SetTopBottomMargins].
|
||||
func DECSTBM(top, bot int) string {
|
||||
return SetTopBottomMargins(top, bot)
|
||||
}
|
||||
|
||||
// SetLeftRightMargins (DECSLRM) sets the left and right margins for the scrolling
|
||||
// region.
|
||||
//
|
||||
// Default is 1 and the right of the screen.
|
||||
//
|
||||
// CSI Pl ; Pr s
|
||||
//
|
||||
// See: https://vt100.net/docs/vt510-rm/DECSLRM.html
|
||||
func SetLeftRightMargins(left, right int) string {
|
||||
var l, r string
|
||||
if left > 0 {
|
||||
l = strconv.Itoa(left)
|
||||
}
|
||||
if right > 0 {
|
||||
r = strconv.Itoa(right)
|
||||
}
|
||||
return "\x1b[" + l + ";" + r + "s"
|
||||
}
|
||||
|
||||
// DECSLRM is an alias for [SetLeftRightMargins].
|
||||
func DECSLRM(left, right int) string {
|
||||
return SetLeftRightMargins(left, right)
|
||||
}
|
||||
|
||||
// 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
|
||||
//
|
||||
// Deprecated: use [SetTopBottomMargins] instead.
|
||||
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"
|
||||
}
|
||||
|
||||
// InsertCharacter (ICH) inserts n blank characters at the current cursor
|
||||
// position. Existing characters move to the right. Characters moved past the
|
||||
// right margin are lost. ICH has no effect outside the scrolling margins.
|
||||
//
|
||||
// Default is 1.
|
||||
//
|
||||
// CSI Pn @
|
||||
//
|
||||
// See: https://vt100.net/docs/vt510-rm/ICH.html
|
||||
func InsertCharacter(n int) string {
|
||||
var s string
|
||||
if n > 1 {
|
||||
s = strconv.Itoa(n)
|
||||
}
|
||||
return "\x1b[" + s + "@"
|
||||
}
|
||||
|
||||
// ICH is an alias for [InsertCharacter].
|
||||
func ICH(n int) string {
|
||||
return InsertCharacter(n)
|
||||
}
|
||||
|
||||
// DeleteCharacter (DCH) deletes n characters at the current cursor position.
|
||||
// As the characters are deleted, the remaining characters move to the left and
|
||||
// the cursor remains at the same position.
|
||||
//
|
||||
// Default is 1.
|
||||
//
|
||||
// CSI Pn P
|
||||
//
|
||||
// See: https://vt100.net/docs/vt510-rm/DCH.html
|
||||
func DeleteCharacter(n int) string {
|
||||
var s string
|
||||
if n > 1 {
|
||||
s = strconv.Itoa(n)
|
||||
}
|
||||
return "\x1b[" + s + "P"
|
||||
}
|
||||
|
||||
// DCH is an alias for [DeleteCharacter].
|
||||
func DCH(n int) string {
|
||||
return DeleteCharacter(n)
|
||||
}
|
||||
|
||||
// SetTabEvery8Columns (DECST8C) sets the tab stops at every 8 columns.
|
||||
//
|
||||
// CSI ? 5 W
|
||||
//
|
||||
// See: https://vt100.net/docs/vt510-rm/DECST8C.html
|
||||
const (
|
||||
SetTabEvery8Columns = "\x1b[?5W"
|
||||
DECST8C = SetTabEvery8Columns
|
||||
)
|
||||
|
||||
// HorizontalTabSet (HTS) sets a horizontal tab stop at the current cursor
|
||||
// column.
|
||||
//
|
||||
// This is equivalent to [HTS].
|
||||
//
|
||||
// ESC H
|
||||
//
|
||||
// See: https://vt100.net/docs/vt510-rm/HTS.html
|
||||
const HorizontalTabSet = "\x1bH"
|
||||
|
||||
// TabClear (TBC) clears tab stops.
|
||||
//
|
||||
// Default is 0.
|
||||
//
|
||||
// Possible values:
|
||||
// 0: Clear tab stop at the current column. (default)
|
||||
// 3: Clear all tab stops.
|
||||
//
|
||||
// CSI Pn g
|
||||
//
|
||||
// See: https://vt100.net/docs/vt510-rm/TBC.html
|
||||
func TabClear(n int) string {
|
||||
var s string
|
||||
if n > 0 {
|
||||
s = strconv.Itoa(n)
|
||||
}
|
||||
return "\x1b[" + s + "g"
|
||||
}
|
||||
|
||||
// TBC is an alias for [TabClear].
|
||||
func TBC(n int) string {
|
||||
return TabClear(n)
|
||||
}
|
||||
|
||||
// RequestPresentationStateReport (DECRQPSR) requests the terminal to send a
|
||||
// report of the presentation state. This includes the cursor information [DECCIR],
|
||||
// and tab stop [DECTABSR] reports.
|
||||
//
|
||||
// Default is 0.
|
||||
//
|
||||
// Possible values:
|
||||
// 0: Error, request ignored.
|
||||
// 1: Cursor information report [DECCIR].
|
||||
// 2: Tab stop report [DECTABSR].
|
||||
//
|
||||
// CSI Ps $ w
|
||||
//
|
||||
// See: https://vt100.net/docs/vt510-rm/DECRQPSR.html
|
||||
func RequestPresentationStateReport(n int) string {
|
||||
var s string
|
||||
if n > 0 {
|
||||
s = strconv.Itoa(n)
|
||||
}
|
||||
return "\x1b[" + s + "$w"
|
||||
}
|
||||
|
||||
// DECRQPSR is an alias for [RequestPresentationStateReport].
|
||||
func DECRQPSR(n int) string {
|
||||
return RequestPresentationStateReport(n)
|
||||
}
|
||||
|
||||
// TabStopReport (DECTABSR) is the response to a tab stop report request.
|
||||
// It reports the tab stops set in the terminal.
|
||||
//
|
||||
// The response is a list of tab stops separated by a slash (/) character.
|
||||
//
|
||||
// DCS 2 $ u D ... D ST
|
||||
//
|
||||
// Where D is a decimal number representing a tab stop.
|
||||
//
|
||||
// See: https://vt100.net/docs/vt510-rm/DECTABSR.html
|
||||
func TabStopReport(stops ...int) string {
|
||||
var s []string //nolint:prealloc
|
||||
for _, v := range stops {
|
||||
s = append(s, strconv.Itoa(v))
|
||||
}
|
||||
return "\x1bP2$u" + strings.Join(s, "/") + "\x1b\\"
|
||||
}
|
||||
|
||||
// DECTABSR is an alias for [TabStopReport].
|
||||
func DECTABSR(stops ...int) string {
|
||||
return TabStopReport(stops...)
|
||||
}
|
||||
|
||||
// CursorInformationReport (DECCIR) is the response to a cursor information
|
||||
// report request. It reports the cursor position, visual attributes, and
|
||||
// character protection attributes. It also reports the status of origin mode
|
||||
// [DECOM] and the current active character set.
|
||||
//
|
||||
// The response is a list of values separated by a semicolon (;) character.
|
||||
//
|
||||
// DCS 1 $ u D ... D ST
|
||||
//
|
||||
// Where D is a decimal number representing a value.
|
||||
//
|
||||
// See: https://vt100.net/docs/vt510-rm/DECCIR.html
|
||||
func CursorInformationReport(values ...int) string {
|
||||
var s []string //nolint:prealloc
|
||||
for _, v := range values {
|
||||
s = append(s, strconv.Itoa(v))
|
||||
}
|
||||
return "\x1bP1$u" + strings.Join(s, ";") + "\x1b\\"
|
||||
}
|
||||
|
||||
// DECCIR is an alias for [CursorInformationReport].
|
||||
func DECCIR(values ...int) string {
|
||||
return CursorInformationReport(values...)
|
||||
}
|
||||
|
||||
// RepeatPreviousCharacter (REP) repeats the previous character n times.
|
||||
// This is identical to typing the same character n times.
|
||||
//
|
||||
// Default is 1.
|
||||
//
|
||||
// CSI Pn b
|
||||
//
|
||||
// See: ECMA-48 § 8.3.103.
|
||||
func RepeatPreviousCharacter(n int) string {
|
||||
var s string
|
||||
if n > 1 {
|
||||
s = strconv.Itoa(n)
|
||||
}
|
||||
return "\x1b[" + s + "b"
|
||||
}
|
||||
|
||||
// REP is an alias for [RepeatPreviousCharacter].
|
||||
func REP(n int) string {
|
||||
return RepeatPreviousCharacter(n)
|
||||
}
|
||||
79
vendor/github.com/charmbracelet/x/ansi/sgr.go
generated
vendored
Normal file
79
vendor/github.com/charmbracelet/x/ansi/sgr.go
generated
vendored
Normal file
|
|
@ -0,0 +1,79 @@
|
|||
package ansi
|
||||
|
||||
// SelectGraphicRendition (SGR) is a command that sets display attributes.
|
||||
//
|
||||
// Default is 0.
|
||||
//
|
||||
// CSI Ps ; Ps ... m
|
||||
//
|
||||
// See: https://vt100.net/docs/vt510-rm/SGR.html
|
||||
func SelectGraphicRendition(ps ...Attr) string {
|
||||
if len(ps) == 0 {
|
||||
return ResetStyle
|
||||
}
|
||||
|
||||
return NewStyle(ps...).String()
|
||||
}
|
||||
|
||||
// SGR is an alias for [SelectGraphicRendition].
|
||||
func SGR(ps ...Attr) string {
|
||||
return SelectGraphicRendition(ps...)
|
||||
}
|
||||
|
||||
var attrStrings = map[int]string{
|
||||
ResetAttr: resetAttr,
|
||||
BoldAttr: boldAttr,
|
||||
FaintAttr: faintAttr,
|
||||
ItalicAttr: italicAttr,
|
||||
UnderlineAttr: underlineAttr,
|
||||
SlowBlinkAttr: slowBlinkAttr,
|
||||
RapidBlinkAttr: rapidBlinkAttr,
|
||||
ReverseAttr: reverseAttr,
|
||||
ConcealAttr: concealAttr,
|
||||
StrikethroughAttr: strikethroughAttr,
|
||||
NormalIntensityAttr: normalIntensityAttr,
|
||||
NoItalicAttr: noItalicAttr,
|
||||
NoUnderlineAttr: noUnderlineAttr,
|
||||
NoBlinkAttr: noBlinkAttr,
|
||||
NoReverseAttr: noReverseAttr,
|
||||
NoConcealAttr: noConcealAttr,
|
||||
NoStrikethroughAttr: noStrikethroughAttr,
|
||||
BlackForegroundColorAttr: blackForegroundColorAttr,
|
||||
RedForegroundColorAttr: redForegroundColorAttr,
|
||||
GreenForegroundColorAttr: greenForegroundColorAttr,
|
||||
YellowForegroundColorAttr: yellowForegroundColorAttr,
|
||||
BlueForegroundColorAttr: blueForegroundColorAttr,
|
||||
MagentaForegroundColorAttr: magentaForegroundColorAttr,
|
||||
CyanForegroundColorAttr: cyanForegroundColorAttr,
|
||||
WhiteForegroundColorAttr: whiteForegroundColorAttr,
|
||||
ExtendedForegroundColorAttr: extendedForegroundColorAttr,
|
||||
DefaultForegroundColorAttr: defaultForegroundColorAttr,
|
||||
BlackBackgroundColorAttr: blackBackgroundColorAttr,
|
||||
RedBackgroundColorAttr: redBackgroundColorAttr,
|
||||
GreenBackgroundColorAttr: greenBackgroundColorAttr,
|
||||
YellowBackgroundColorAttr: yellowBackgroundColorAttr,
|
||||
BlueBackgroundColorAttr: blueBackgroundColorAttr,
|
||||
MagentaBackgroundColorAttr: magentaBackgroundColorAttr,
|
||||
CyanBackgroundColorAttr: cyanBackgroundColorAttr,
|
||||
WhiteBackgroundColorAttr: whiteBackgroundColorAttr,
|
||||
ExtendedBackgroundColorAttr: extendedBackgroundColorAttr,
|
||||
DefaultBackgroundColorAttr: defaultBackgroundColorAttr,
|
||||
ExtendedUnderlineColorAttr: extendedUnderlineColorAttr,
|
||||
DefaultUnderlineColorAttr: defaultUnderlineColorAttr,
|
||||
BrightBlackForegroundColorAttr: brightBlackForegroundColorAttr,
|
||||
BrightRedForegroundColorAttr: brightRedForegroundColorAttr,
|
||||
BrightGreenForegroundColorAttr: brightGreenForegroundColorAttr,
|
||||
BrightYellowForegroundColorAttr: brightYellowForegroundColorAttr,
|
||||
BrightBlueForegroundColorAttr: brightBlueForegroundColorAttr,
|
||||
BrightMagentaForegroundColorAttr: brightMagentaForegroundColorAttr,
|
||||
BrightCyanForegroundColorAttr: brightCyanForegroundColorAttr,
|
||||
BrightWhiteForegroundColorAttr: brightWhiteForegroundColorAttr,
|
||||
BrightBlackBackgroundColorAttr: brightBlackBackgroundColorAttr,
|
||||
BrightRedBackgroundColorAttr: brightRedBackgroundColorAttr,
|
||||
BrightGreenBackgroundColorAttr: brightGreenBackgroundColorAttr,
|
||||
BrightYellowBackgroundColorAttr: brightYellowBackgroundColorAttr,
|
||||
BrightBlueBackgroundColorAttr: brightBlueBackgroundColorAttr,
|
||||
BrightMagentaBackgroundColorAttr: brightMagentaBackgroundColorAttr,
|
||||
BrightCyanBackgroundColorAttr: brightCyanBackgroundColorAttr,
|
||||
BrightWhiteBackgroundColorAttr: brightWhiteBackgroundColorAttr,
|
||||
}
|
||||
168
vendor/github.com/charmbracelet/x/ansi/status.go
generated
vendored
Normal file
168
vendor/github.com/charmbracelet/x/ansi/status.go
generated
vendored
Normal file
|
|
@ -0,0 +1,168 @@
|
|||
package ansi
|
||||
|
||||
import (
|
||||
"strconv"
|
||||
"strings"
|
||||
)
|
||||
|
||||
// StatusReport represents a terminal status report.
|
||||
type StatusReport interface {
|
||||
// StatusReport returns the status report identifier.
|
||||
StatusReport() int
|
||||
}
|
||||
|
||||
// ANSIStatusReport represents an ANSI terminal status report.
|
||||
type ANSIStatusReport int //nolint:revive
|
||||
|
||||
// StatusReport returns the status report identifier.
|
||||
func (s ANSIStatusReport) StatusReport() int {
|
||||
return int(s)
|
||||
}
|
||||
|
||||
// DECStatusReport represents a DEC terminal status report.
|
||||
type DECStatusReport int
|
||||
|
||||
// StatusReport returns the status report identifier.
|
||||
func (s DECStatusReport) StatusReport() int {
|
||||
return int(s)
|
||||
}
|
||||
|
||||
// DeviceStatusReport (DSR) is a control sequence that reports the terminal's
|
||||
// status.
|
||||
// The terminal responds with a DSR sequence.
|
||||
//
|
||||
// CSI Ps n
|
||||
// CSI ? Ps n
|
||||
//
|
||||
// If one of the statuses is a [DECStatus], the sequence will use the DEC
|
||||
// format.
|
||||
//
|
||||
// See also https://vt100.net/docs/vt510-rm/DSR.html
|
||||
func DeviceStatusReport(statues ...StatusReport) string {
|
||||
var dec bool
|
||||
list := make([]string, len(statues))
|
||||
seq := "\x1b["
|
||||
for i, status := range statues {
|
||||
list[i] = strconv.Itoa(status.StatusReport())
|
||||
switch status.(type) {
|
||||
case DECStatusReport:
|
||||
dec = true
|
||||
}
|
||||
}
|
||||
if dec {
|
||||
seq += "?"
|
||||
}
|
||||
return seq + strings.Join(list, ";") + "n"
|
||||
}
|
||||
|
||||
// DSR is an alias for [DeviceStatusReport].
|
||||
func DSR(status StatusReport) string {
|
||||
return DeviceStatusReport(status)
|
||||
}
|
||||
|
||||
// RequestCursorPositionReport 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 RequestCursorPositionReport = "\x1b[6n"
|
||||
|
||||
// RequestExtendedCursorPositionReport (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 RequestExtendedCursorPositionReport = "\x1b[?6n"
|
||||
|
||||
// RequestLightDarkReport is a control sequence that requests the terminal to
|
||||
// report its operating system light/dark color preference. Supported terminals
|
||||
// should respond with a [LightDarkReport] sequence as follows:
|
||||
//
|
||||
// CSI ? 997 ; 1 n for dark mode
|
||||
// CSI ? 997 ; 2 n for light mode
|
||||
//
|
||||
// See: https://contour-terminal.org/vt-extensions/color-palette-update-notifications/
|
||||
const RequestLightDarkReport = "\x1b[?996n"
|
||||
|
||||
// CursorPositionReport (CPR) is a control sequence that reports the cursor's
|
||||
// position.
|
||||
//
|
||||
// CSI Pl ; Pc R
|
||||
//
|
||||
// Where Pl is the line number and Pc is the column number.
|
||||
//
|
||||
// See also https://vt100.net/docs/vt510-rm/CPR.html
|
||||
func CursorPositionReport(line, column int) string {
|
||||
if line < 1 {
|
||||
line = 1
|
||||
}
|
||||
if column < 1 {
|
||||
column = 1
|
||||
}
|
||||
return "\x1b[" + strconv.Itoa(line) + ";" + strconv.Itoa(column) + "R"
|
||||
}
|
||||
|
||||
// CPR is an alias for [CursorPositionReport].
|
||||
func CPR(line, column int) string {
|
||||
return CursorPositionReport(line, column)
|
||||
}
|
||||
|
||||
// ExtendedCursorPositionReport (DECXCPR) is a control sequence that reports the
|
||||
// cursor's position along with the page number (optional).
|
||||
//
|
||||
// CSI ? Pl ; Pc R
|
||||
// CSI ? Pl ; Pc ; Pv R
|
||||
//
|
||||
// Where Pl is the line number, Pc is the column number, and Pv is the page
|
||||
// number.
|
||||
//
|
||||
// If the page number is zero or negative, the returned sequence won't include
|
||||
// the page number.
|
||||
//
|
||||
// See also https://vt100.net/docs/vt510-rm/DECXCPR.html
|
||||
func ExtendedCursorPositionReport(line, column, page int) string {
|
||||
if line < 1 {
|
||||
line = 1
|
||||
}
|
||||
if column < 1 {
|
||||
column = 1
|
||||
}
|
||||
if page < 1 {
|
||||
return "\x1b[?" + strconv.Itoa(line) + ";" + strconv.Itoa(column) + "R"
|
||||
}
|
||||
return "\x1b[?" + strconv.Itoa(line) + ";" + strconv.Itoa(column) + ";" + strconv.Itoa(page) + "R"
|
||||
}
|
||||
|
||||
// DECXCPR is an alias for [ExtendedCursorPositionReport].
|
||||
func DECXCPR(line, column, page int) string {
|
||||
return ExtendedCursorPositionReport(line, column, page)
|
||||
}
|
||||
|
||||
// LightDarkReport is a control sequence that reports the terminal's operating
|
||||
// system light/dark color preference.
|
||||
//
|
||||
// CSI ? 997 ; 1 n for dark mode
|
||||
// CSI ? 997 ; 2 n for light mode
|
||||
//
|
||||
// See: https://contour-terminal.org/vt-extensions/color-palette-update-notifications/
|
||||
func LightDarkReport(dark bool) string {
|
||||
if dark {
|
||||
return "\x1b[?997;1n"
|
||||
}
|
||||
return "\x1b[?997;2n"
|
||||
}
|
||||
673
vendor/github.com/charmbracelet/x/ansi/style.go
generated
vendored
Normal file
673
vendor/github.com/charmbracelet/x/ansi/style.go
generated
vendored
Normal file
|
|
@ -0,0 +1,673 @@
|
|||
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 = int
|
||||
|
||||
// Style represents an ANSI SGR (Select Graphic Rendition) style.
|
||||
type Style []string
|
||||
|
||||
// NewStyle returns a new style with the given attributes.
|
||||
func NewStyle(attrs ...Attr) Style {
|
||||
if len(attrs) == 0 {
|
||||
return Style{}
|
||||
}
|
||||
s := make(Style, 0, len(attrs))
|
||||
for _, a := range attrs {
|
||||
attr, ok := attrStrings[a]
|
||||
if ok {
|
||||
s = append(s, attr)
|
||||
} else {
|
||||
if a < 0 {
|
||||
a = 0
|
||||
}
|
||||
s = append(s, strconv.Itoa(a))
|
||||
}
|
||||
}
|
||||
return s
|
||||
}
|
||||
|
||||
// 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)
|
||||
}
|
||||
|
||||
// UnderlineStyle appends the underline style attribute to the style.
|
||||
func (s Style) UnderlineStyle(u UnderlineStyle) Style {
|
||||
switch u {
|
||||
case NoUnderlineStyle:
|
||||
return s.NoUnderline()
|
||||
case SingleUnderlineStyle:
|
||||
return s.Underline()
|
||||
case DoubleUnderlineStyle:
|
||||
return append(s, doubleUnderlineStyle)
|
||||
case CurlyUnderlineStyle:
|
||||
return append(s, curlyUnderlineStyle)
|
||||
case DottedUnderlineStyle:
|
||||
return append(s, dottedUnderlineStyle)
|
||||
case DashedUnderlineStyle:
|
||||
return append(s, dashedUnderlineStyle)
|
||||
}
|
||||
return s
|
||||
}
|
||||
|
||||
// DoubleUnderline appends the double underline style attribute to the style.
|
||||
// This is a convenience method for UnderlineStyle(DoubleUnderlineStyle).
|
||||
func (s Style) DoubleUnderline() Style {
|
||||
return s.UnderlineStyle(DoubleUnderlineStyle)
|
||||
}
|
||||
|
||||
// CurlyUnderline appends the curly underline style attribute to the style.
|
||||
// This is a convenience method for UnderlineStyle(CurlyUnderlineStyle).
|
||||
func (s Style) CurlyUnderline() Style {
|
||||
return s.UnderlineStyle(CurlyUnderlineStyle)
|
||||
}
|
||||
|
||||
// DottedUnderline appends the dotted underline style attribute to the style.
|
||||
// This is a convenience method for UnderlineStyle(DottedUnderlineStyle).
|
||||
func (s Style) DottedUnderline() Style {
|
||||
return s.UnderlineStyle(DottedUnderlineStyle)
|
||||
}
|
||||
|
||||
// DashedUnderline appends the dashed underline style attribute to the style.
|
||||
// This is a convenience method for UnderlineStyle(DashedUnderlineStyle).
|
||||
func (s Style) DashedUnderline() Style {
|
||||
return s.UnderlineStyle(DashedUnderlineStyle)
|
||||
}
|
||||
|
||||
// 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)
|
||||
}
|
||||
|
||||
// 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, foregroundColorString(c))
|
||||
}
|
||||
|
||||
// BackgroundColor appends the background color style attribute to the style.
|
||||
func (s Style) BackgroundColor(c Color) Style {
|
||||
return append(s, backgroundColorString(c))
|
||||
}
|
||||
|
||||
// UnderlineColor appends the underline color style attribute to the style.
|
||||
func (s Style) UnderlineColor(c Color) Style {
|
||||
return append(s, underlineColorString(c))
|
||||
}
|
||||
|
||||
// UnderlineStyle represents an ANSI SGR (Select Graphic Rendition) underline
|
||||
// style.
|
||||
type UnderlineStyle = byte
|
||||
|
||||
const (
|
||||
doubleUnderlineStyle = "4:2"
|
||||
curlyUnderlineStyle = "4:3"
|
||||
dottedUnderlineStyle = "4:4"
|
||||
dashedUnderlineStyle = "4:5"
|
||||
)
|
||||
|
||||
const (
|
||||
// NoUnderlineStyle is the default underline style.
|
||||
NoUnderlineStyle UnderlineStyle = iota
|
||||
// SingleUnderlineStyle is a single underline style.
|
||||
SingleUnderlineStyle
|
||||
// DoubleUnderlineStyle is a double underline style.
|
||||
DoubleUnderlineStyle
|
||||
// CurlyUnderlineStyle is a curly underline style.
|
||||
CurlyUnderlineStyle
|
||||
// DottedUnderlineStyle is a dotted underline style.
|
||||
DottedUnderlineStyle
|
||||
// DashedUnderlineStyle is a dashed underline style.
|
||||
DashedUnderlineStyle
|
||||
)
|
||||
|
||||
// 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
|
||||
SlowBlinkAttr Attr = 5
|
||||
RapidBlinkAttr Attr = 6
|
||||
ReverseAttr Attr = 7
|
||||
ConcealAttr Attr = 8
|
||||
StrikethroughAttr Attr = 9
|
||||
NormalIntensityAttr Attr = 22
|
||||
NoItalicAttr Attr = 23
|
||||
NoUnderlineAttr Attr = 24
|
||||
NoBlinkAttr Attr = 25
|
||||
NoReverseAttr Attr = 27
|
||||
NoConcealAttr Attr = 28
|
||||
NoStrikethroughAttr Attr = 29
|
||||
BlackForegroundColorAttr Attr = 30
|
||||
RedForegroundColorAttr Attr = 31
|
||||
GreenForegroundColorAttr Attr = 32
|
||||
YellowForegroundColorAttr Attr = 33
|
||||
BlueForegroundColorAttr Attr = 34
|
||||
MagentaForegroundColorAttr Attr = 35
|
||||
CyanForegroundColorAttr Attr = 36
|
||||
WhiteForegroundColorAttr Attr = 37
|
||||
ExtendedForegroundColorAttr Attr = 38
|
||||
DefaultForegroundColorAttr Attr = 39
|
||||
BlackBackgroundColorAttr Attr = 40
|
||||
RedBackgroundColorAttr Attr = 41
|
||||
GreenBackgroundColorAttr Attr = 42
|
||||
YellowBackgroundColorAttr Attr = 43
|
||||
BlueBackgroundColorAttr Attr = 44
|
||||
MagentaBackgroundColorAttr Attr = 45
|
||||
CyanBackgroundColorAttr Attr = 46
|
||||
WhiteBackgroundColorAttr Attr = 47
|
||||
ExtendedBackgroundColorAttr Attr = 48
|
||||
DefaultBackgroundColorAttr Attr = 49
|
||||
ExtendedUnderlineColorAttr Attr = 58
|
||||
DefaultUnderlineColorAttr Attr = 59
|
||||
BrightBlackForegroundColorAttr Attr = 90
|
||||
BrightRedForegroundColorAttr Attr = 91
|
||||
BrightGreenForegroundColorAttr Attr = 92
|
||||
BrightYellowForegroundColorAttr Attr = 93
|
||||
BrightBlueForegroundColorAttr Attr = 94
|
||||
BrightMagentaForegroundColorAttr Attr = 95
|
||||
BrightCyanForegroundColorAttr Attr = 96
|
||||
BrightWhiteForegroundColorAttr Attr = 97
|
||||
BrightBlackBackgroundColorAttr Attr = 100
|
||||
BrightRedBackgroundColorAttr Attr = 101
|
||||
BrightGreenBackgroundColorAttr Attr = 102
|
||||
BrightYellowBackgroundColorAttr Attr = 103
|
||||
BrightBlueBackgroundColorAttr Attr = 104
|
||||
BrightMagentaBackgroundColorAttr Attr = 105
|
||||
BrightCyanBackgroundColorAttr Attr = 106
|
||||
BrightWhiteBackgroundColorAttr Attr = 107
|
||||
|
||||
RGBColorIntroducerAttr Attr = 2
|
||||
ExtendedColorIntroducerAttr Attr = 5
|
||||
)
|
||||
|
||||
const (
|
||||
resetAttr = "0"
|
||||
boldAttr = "1"
|
||||
faintAttr = "2"
|
||||
italicAttr = "3"
|
||||
underlineAttr = "4"
|
||||
slowBlinkAttr = "5"
|
||||
rapidBlinkAttr = "6"
|
||||
reverseAttr = "7"
|
||||
concealAttr = "8"
|
||||
strikethroughAttr = "9"
|
||||
normalIntensityAttr = "22"
|
||||
noItalicAttr = "23"
|
||||
noUnderlineAttr = "24"
|
||||
noBlinkAttr = "25"
|
||||
noReverseAttr = "27"
|
||||
noConcealAttr = "28"
|
||||
noStrikethroughAttr = "29"
|
||||
blackForegroundColorAttr = "30"
|
||||
redForegroundColorAttr = "31"
|
||||
greenForegroundColorAttr = "32"
|
||||
yellowForegroundColorAttr = "33"
|
||||
blueForegroundColorAttr = "34"
|
||||
magentaForegroundColorAttr = "35"
|
||||
cyanForegroundColorAttr = "36"
|
||||
whiteForegroundColorAttr = "37"
|
||||
extendedForegroundColorAttr = "38"
|
||||
defaultForegroundColorAttr = "39"
|
||||
blackBackgroundColorAttr = "40"
|
||||
redBackgroundColorAttr = "41"
|
||||
greenBackgroundColorAttr = "42"
|
||||
yellowBackgroundColorAttr = "43"
|
||||
blueBackgroundColorAttr = "44"
|
||||
magentaBackgroundColorAttr = "45"
|
||||
cyanBackgroundColorAttr = "46"
|
||||
whiteBackgroundColorAttr = "47"
|
||||
extendedBackgroundColorAttr = "48"
|
||||
defaultBackgroundColorAttr = "49"
|
||||
extendedUnderlineColorAttr = "58"
|
||||
defaultUnderlineColorAttr = "59"
|
||||
brightBlackForegroundColorAttr = "90"
|
||||
brightRedForegroundColorAttr = "91"
|
||||
brightGreenForegroundColorAttr = "92"
|
||||
brightYellowForegroundColorAttr = "93"
|
||||
brightBlueForegroundColorAttr = "94"
|
||||
brightMagentaForegroundColorAttr = "95"
|
||||
brightCyanForegroundColorAttr = "96"
|
||||
brightWhiteForegroundColorAttr = "97"
|
||||
brightBlackBackgroundColorAttr = "100"
|
||||
brightRedBackgroundColorAttr = "101"
|
||||
brightGreenBackgroundColorAttr = "102"
|
||||
brightYellowBackgroundColorAttr = "103"
|
||||
brightBlueBackgroundColorAttr = "104"
|
||||
brightMagentaBackgroundColorAttr = "105"
|
||||
brightCyanBackgroundColorAttr = "106"
|
||||
brightWhiteBackgroundColorAttr = "107"
|
||||
)
|
||||
|
||||
// foregroundColorString 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 foregroundColorString(c Color) string {
|
||||
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
|
||||
switch c {
|
||||
case Black:
|
||||
return blackForegroundColorAttr
|
||||
case Red:
|
||||
return redForegroundColorAttr
|
||||
case Green:
|
||||
return greenForegroundColorAttr
|
||||
case Yellow:
|
||||
return yellowForegroundColorAttr
|
||||
case Blue:
|
||||
return blueForegroundColorAttr
|
||||
case Magenta:
|
||||
return magentaForegroundColorAttr
|
||||
case Cyan:
|
||||
return cyanForegroundColorAttr
|
||||
case White:
|
||||
return whiteForegroundColorAttr
|
||||
case BrightBlack:
|
||||
return brightBlackForegroundColorAttr
|
||||
case BrightRed:
|
||||
return brightRedForegroundColorAttr
|
||||
case BrightGreen:
|
||||
return brightGreenForegroundColorAttr
|
||||
case BrightYellow:
|
||||
return brightYellowForegroundColorAttr
|
||||
case BrightBlue:
|
||||
return brightBlueForegroundColorAttr
|
||||
case BrightMagenta:
|
||||
return brightMagentaForegroundColorAttr
|
||||
case BrightCyan:
|
||||
return brightCyanForegroundColorAttr
|
||||
case BrightWhite:
|
||||
return brightWhiteForegroundColorAttr
|
||||
}
|
||||
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
|
||||
}
|
||||
|
||||
// backgroundColorString 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 backgroundColorString(c Color) string {
|
||||
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
|
||||
switch c {
|
||||
case Black:
|
||||
return blackBackgroundColorAttr
|
||||
case Red:
|
||||
return redBackgroundColorAttr
|
||||
case Green:
|
||||
return greenBackgroundColorAttr
|
||||
case Yellow:
|
||||
return yellowBackgroundColorAttr
|
||||
case Blue:
|
||||
return blueBackgroundColorAttr
|
||||
case Magenta:
|
||||
return magentaBackgroundColorAttr
|
||||
case Cyan:
|
||||
return cyanBackgroundColorAttr
|
||||
case White:
|
||||
return whiteBackgroundColorAttr
|
||||
case BrightBlack:
|
||||
return brightBlackBackgroundColorAttr
|
||||
case BrightRed:
|
||||
return brightRedBackgroundColorAttr
|
||||
case BrightGreen:
|
||||
return brightGreenBackgroundColorAttr
|
||||
case BrightYellow:
|
||||
return brightYellowBackgroundColorAttr
|
||||
case BrightBlue:
|
||||
return brightBlueBackgroundColorAttr
|
||||
case BrightMagenta:
|
||||
return brightMagentaBackgroundColorAttr
|
||||
case BrightCyan:
|
||||
return brightCyanBackgroundColorAttr
|
||||
case BrightWhite:
|
||||
return brightWhiteBackgroundColorAttr
|
||||
}
|
||||
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
|
||||
}
|
||||
|
||||
// underlineColorString 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 underlineColorString(c Color) string {
|
||||
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
|
||||
}
|
||||
|
||||
// ReadStyleColor decodes a color from a slice of parameters. It returns the
|
||||
// number of parameters read and the color. This function is used to read SGR
|
||||
// color parameters following the ITU T.416 standard.
|
||||
//
|
||||
// It supports reading the following color types:
|
||||
// - 0: implementation defined
|
||||
// - 1: transparent
|
||||
// - 2: RGB direct color
|
||||
// - 3: CMY direct color
|
||||
// - 4: CMYK direct color
|
||||
// - 5: indexed color
|
||||
// - 6: RGBA direct color (WezTerm extension)
|
||||
//
|
||||
// The parameters can be separated by semicolons (;) or colons (:). Mixing
|
||||
// separators is not allowed.
|
||||
//
|
||||
// The specs supports defining a color space id, a color tolerance value, and a
|
||||
// tolerance color space id. However, these values have no effect on the
|
||||
// returned color and will be ignored.
|
||||
//
|
||||
// This implementation includes a few modifications to the specs:
|
||||
// 1. Support for legacy color values separated by semicolons (;) with respect to RGB, and indexed colors
|
||||
// 2. Support ignoring and omitting the color space id (second parameter) with respect to RGB colors
|
||||
// 3. Support ignoring and omitting the 6th parameter with respect to RGB and CMY colors
|
||||
// 4. Support reading RGBA colors
|
||||
func ReadStyleColor(params Params, co *color.Color) (n int) {
|
||||
if len(params) < 2 { // Need at least SGR type and color type
|
||||
return 0
|
||||
}
|
||||
|
||||
// First parameter indicates one of 38, 48, or 58 (foreground, background, or underline)
|
||||
s := params[0]
|
||||
p := params[1]
|
||||
colorType := p.Param(0)
|
||||
n = 2
|
||||
|
||||
paramsfn := func() (p1, p2, p3, p4 int) {
|
||||
// Where should we start reading the color?
|
||||
switch {
|
||||
case s.HasMore() && p.HasMore() && len(params) > 8 && params[2].HasMore() && params[3].HasMore() && params[4].HasMore() && params[5].HasMore() && params[6].HasMore() && params[7].HasMore():
|
||||
// We have color space id, a 6th parameter, a tolerance value, and a tolerance color space
|
||||
n += 7
|
||||
return params[3].Param(0), params[4].Param(0), params[5].Param(0), params[6].Param(0)
|
||||
case s.HasMore() && p.HasMore() && len(params) > 7 && params[2].HasMore() && params[3].HasMore() && params[4].HasMore() && params[5].HasMore() && params[6].HasMore():
|
||||
// We have color space id, a 6th parameter, and a tolerance value
|
||||
n += 6
|
||||
return params[3].Param(0), params[4].Param(0), params[5].Param(0), params[6].Param(0)
|
||||
case s.HasMore() && p.HasMore() && len(params) > 6 && params[2].HasMore() && params[3].HasMore() && params[4].HasMore() && params[5].HasMore():
|
||||
// We have color space id and a 6th parameter
|
||||
// 48 : 4 : : 1 : 2 : 3 :4
|
||||
n += 5
|
||||
return params[3].Param(0), params[4].Param(0), params[5].Param(0), params[6].Param(0)
|
||||
case s.HasMore() && p.HasMore() && len(params) > 5 && params[2].HasMore() && params[3].HasMore() && params[4].HasMore() && !params[5].HasMore():
|
||||
// We have color space
|
||||
// 48 : 3 : : 1 : 2 : 3
|
||||
n += 4
|
||||
return params[3].Param(0), params[4].Param(0), params[5].Param(0), -1
|
||||
case s.HasMore() && p.HasMore() && p.Param(0) == 2 && params[2].HasMore() && params[3].HasMore() && !params[4].HasMore():
|
||||
// We have color values separated by colons (:)
|
||||
// 48 : 2 : 1 : 2 : 3
|
||||
fallthrough
|
||||
case !s.HasMore() && !p.HasMore() && p.Param(0) == 2 && !params[2].HasMore() && !params[3].HasMore() && !params[4].HasMore():
|
||||
// Support legacy color values separated by semicolons (;)
|
||||
// 48 ; 2 ; 1 ; 2 ; 3
|
||||
n += 3
|
||||
return params[2].Param(0), params[3].Param(0), params[4].Param(0), -1
|
||||
}
|
||||
// Ambiguous SGR color
|
||||
return -1, -1, -1, -1
|
||||
}
|
||||
|
||||
switch colorType {
|
||||
case 0: // implementation defined
|
||||
return 2
|
||||
case 1: // transparent
|
||||
*co = color.Transparent
|
||||
return 2
|
||||
case 2: // RGB direct color
|
||||
if len(params) < 5 {
|
||||
return 0
|
||||
}
|
||||
|
||||
r, g, b, _ := paramsfn()
|
||||
if r == -1 || g == -1 || b == -1 {
|
||||
return 0
|
||||
}
|
||||
|
||||
*co = color.RGBA{
|
||||
R: uint8(r), //nolint:gosec
|
||||
G: uint8(g), //nolint:gosec
|
||||
B: uint8(b), //nolint:gosec
|
||||
A: 0xff,
|
||||
}
|
||||
return //nolint:nakedret
|
||||
|
||||
case 3: // CMY direct color
|
||||
if len(params) < 5 {
|
||||
return 0
|
||||
}
|
||||
|
||||
c, m, y, _ := paramsfn()
|
||||
if c == -1 || m == -1 || y == -1 {
|
||||
return 0
|
||||
}
|
||||
|
||||
*co = color.CMYK{
|
||||
C: uint8(c), //nolint:gosec
|
||||
M: uint8(m), //nolint:gosec
|
||||
Y: uint8(y), //nolint:gosec
|
||||
K: 0,
|
||||
}
|
||||
return //nolint:nakedret
|
||||
|
||||
case 4: // CMYK direct color
|
||||
if len(params) < 6 {
|
||||
return 0
|
||||
}
|
||||
|
||||
c, m, y, k := paramsfn()
|
||||
if c == -1 || m == -1 || y == -1 || k == -1 {
|
||||
return 0
|
||||
}
|
||||
|
||||
*co = color.CMYK{
|
||||
C: uint8(c), //nolint:gosec
|
||||
M: uint8(m), //nolint:gosec
|
||||
Y: uint8(y), //nolint:gosec
|
||||
K: uint8(k), //nolint:gosec
|
||||
}
|
||||
return //nolint:nakedret
|
||||
|
||||
case 5: // indexed color
|
||||
if len(params) < 3 {
|
||||
return 0
|
||||
}
|
||||
switch {
|
||||
case s.HasMore() && p.HasMore() && !params[2].HasMore():
|
||||
// Colon separated indexed color
|
||||
// 38 : 5 : 234
|
||||
case !s.HasMore() && !p.HasMore() && !params[2].HasMore():
|
||||
// Legacy semicolon indexed color
|
||||
// 38 ; 5 ; 234
|
||||
default:
|
||||
return 0
|
||||
}
|
||||
*co = ExtendedColor(params[2].Param(0)) //nolint:gosec
|
||||
return 3
|
||||
|
||||
case 6: // RGBA direct color
|
||||
if len(params) < 6 {
|
||||
return 0
|
||||
}
|
||||
|
||||
r, g, b, a := paramsfn()
|
||||
if r == -1 || g == -1 || b == -1 || a == -1 {
|
||||
return 0
|
||||
}
|
||||
|
||||
*co = color.RGBA{
|
||||
R: uint8(r), //nolint:gosec
|
||||
G: uint8(g), //nolint:gosec
|
||||
B: uint8(b), //nolint:gosec
|
||||
A: uint8(a), //nolint:gosec
|
||||
}
|
||||
return //nolint:nakedret
|
||||
|
||||
default:
|
||||
return 0
|
||||
}
|
||||
}
|
||||
41
vendor/github.com/charmbracelet/x/ansi/termcap.go
generated
vendored
Normal file
41
vendor/github.com/charmbracelet/x/ansi/termcap.go
generated
vendored
Normal file
|
|
@ -0,0 +1,41 @@
|
|||
package ansi
|
||||
|
||||
import (
|
||||
"encoding/hex"
|
||||
"strings"
|
||||
)
|
||||
|
||||
// XTGETTCAP (RequestTermcap) 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 XTGETTCAP(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\\"
|
||||
}
|
||||
|
||||
// RequestTermcap is an alias for [XTGETTCAP].
|
||||
func RequestTermcap(caps ...string) string {
|
||||
return XTGETTCAP(caps...)
|
||||
}
|
||||
|
||||
// RequestTerminfo is an alias for [XTGETTCAP].
|
||||
func RequestTerminfo(caps ...string) string {
|
||||
return XTGETTCAP(caps...)
|
||||
}
|
||||
48
vendor/github.com/charmbracelet/x/ansi/title.go
generated
vendored
Normal file
48
vendor/github.com/charmbracelet/x/ansi/title.go
generated
vendored
Normal file
|
|
@ -0,0 +1,48 @@
|
|||
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"
|
||||
}
|
||||
|
||||
// DECSWT is a sequence for setting the window title.
|
||||
//
|
||||
// This is an alias for [SetWindowTitle]("1;<name>").
|
||||
// See: EK-VT520-RM 5–156 https://vt100.net/dec/ek-vt520-rm.pdf
|
||||
func DECSWT(name string) string {
|
||||
return SetWindowTitle("1;" + name)
|
||||
}
|
||||
|
||||
// DECSIN is a sequence for setting the icon name.
|
||||
//
|
||||
// This is an alias for [SetWindowTitle]("L;<name>").
|
||||
// See: EK-VT520-RM 5–134 https://vt100.net/dec/ek-vt520-rm.pdf
|
||||
func DECSIN(name string) string {
|
||||
return SetWindowTitle("L;" + name)
|
||||
}
|
||||
299
vendor/github.com/charmbracelet/x/ansi/truncate.go
generated
vendored
Normal file
299
vendor/github.com/charmbracelet/x/ansi/truncate.go
generated
vendored
Normal file
|
|
@ -0,0 +1,299 @@
|
|||
package ansi
|
||||
|
||||
import (
|
||||
"bytes"
|
||||
|
||||
"github.com/charmbracelet/x/ansi/parser"
|
||||
"github.com/mattn/go-runewidth"
|
||||
"github.com/rivo/uniseg"
|
||||
)
|
||||
|
||||
// Cut the string, without adding any prefix or tail strings. This function is
|
||||
// aware of ANSI escape codes and will not break them, and accounts for
|
||||
// wide-characters (such as East-Asian characters and emojis).
|
||||
// This treats the text as a sequence of graphemes.
|
||||
func Cut(s string, left, right int) string {
|
||||
return cut(GraphemeWidth, s, left, right)
|
||||
}
|
||||
|
||||
// CutWc the string, without adding any prefix or tail strings. This function is
|
||||
// aware of ANSI escape codes and will not break them, and accounts for
|
||||
// wide-characters (such as East-Asian characters and emojis).
|
||||
// Note that the [left] parameter is inclusive, while [right] isn't,
|
||||
// which is to say it'll return `[left, right)`.
|
||||
//
|
||||
// This treats the text as a sequence of wide characters and runes.
|
||||
func CutWc(s string, left, right int) string {
|
||||
return cut(WcWidth, s, left, right)
|
||||
}
|
||||
|
||||
func cut(m Method, s string, left, right int) string {
|
||||
if right <= left {
|
||||
return ""
|
||||
}
|
||||
|
||||
truncate := Truncate
|
||||
truncateLeft := TruncateLeft
|
||||
if m == WcWidth {
|
||||
truncate = TruncateWc
|
||||
truncateLeft = TruncateWc
|
||||
}
|
||||
|
||||
if left == 0 {
|
||||
return truncate(s, right, "")
|
||||
}
|
||||
return truncateLeft(truncate(s, right, ""), left, "")
|
||||
}
|
||||
|
||||
// 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-Asian characters and emojis).
|
||||
// This treats the text as a sequence of graphemes.
|
||||
func Truncate(s string, length int, tail string) string {
|
||||
return truncate(GraphemeWidth, s, length, tail)
|
||||
}
|
||||
|
||||
// TruncateWc 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-Asian characters and emojis).
|
||||
// This treats the text as a sequence of wide characters and runes.
|
||||
func TruncateWc(s string, length int, tail string) string {
|
||||
return truncate(WcWidth, s, length, tail)
|
||||
}
|
||||
|
||||
func truncate(m Method, 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
|
||||
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])
|
||||
if state == parser.Utf8State {
|
||||
// This action happens when we transition to the Utf8State.
|
||||
var width int
|
||||
cluster, _, width, _ = uniseg.FirstGraphemeCluster(b[i:], -1)
|
||||
if m == WcWidth {
|
||||
width = runewidth.StringWidth(string(cluster))
|
||||
}
|
||||
|
||||
// increment the index by the length of the cluster
|
||||
i += len(cluster)
|
||||
curWidth += width
|
||||
|
||||
// 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 > length && !ignoring {
|
||||
ignoring = true
|
||||
buf.WriteString(tail)
|
||||
}
|
||||
|
||||
if curWidth > length {
|
||||
continue
|
||||
}
|
||||
|
||||
buf.Write(cluster)
|
||||
|
||||
// Done collecting, now we're back in the ground state.
|
||||
pstate = parser.GroundState
|
||||
continue
|
||||
}
|
||||
|
||||
switch action {
|
||||
case parser.PrintAction:
|
||||
// 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
|
||||
case parser.ExecuteAction:
|
||||
// execute action will be things like \n, which, if outside the cut,
|
||||
// should be ignored.
|
||||
if ignoring {
|
||||
i++
|
||||
continue
|
||||
}
|
||||
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()
|
||||
}
|
||||
|
||||
// TruncateLeft truncates a string from the left side by removing n characters,
|
||||
// adding a prefix to the beginning if the string is longer than n.
|
||||
// This function is aware of ANSI escape codes and will not break them, and
|
||||
// accounts for wide-characters (such as East-Asian characters and emojis).
|
||||
// This treats the text as a sequence of graphemes.
|
||||
func TruncateLeft(s string, n int, prefix string) string {
|
||||
return truncateLeft(GraphemeWidth, s, n, prefix)
|
||||
}
|
||||
|
||||
// TruncateLeftWc truncates a string from the left side by removing n characters,
|
||||
// adding a prefix to the beginning if the string is longer than n.
|
||||
// This function is aware of ANSI escape codes and will not break them, and
|
||||
// accounts for wide-characters (such as East-Asian characters and emojis).
|
||||
// This treats the text as a sequence of wide characters and runes.
|
||||
func TruncateLeftWc(s string, n int, prefix string) string {
|
||||
return truncateLeft(WcWidth, s, n, prefix)
|
||||
}
|
||||
|
||||
func truncateLeft(m Method, s string, n int, prefix string) string {
|
||||
if n <= 0 {
|
||||
return s
|
||||
}
|
||||
|
||||
var cluster []byte
|
||||
var buf bytes.Buffer
|
||||
curWidth := 0
|
||||
ignoring := true
|
||||
pstate := parser.GroundState
|
||||
b := []byte(s)
|
||||
i := 0
|
||||
|
||||
for i < len(b) {
|
||||
if !ignoring {
|
||||
buf.Write(b[i:])
|
||||
break
|
||||
}
|
||||
|
||||
state, action := parser.Table.Transition(pstate, b[i])
|
||||
if state == parser.Utf8State {
|
||||
var width int
|
||||
cluster, _, width, _ = uniseg.FirstGraphemeCluster(b[i:], -1)
|
||||
if m == WcWidth {
|
||||
width = runewidth.StringWidth(string(cluster))
|
||||
}
|
||||
|
||||
i += len(cluster)
|
||||
curWidth += width
|
||||
|
||||
if curWidth > n && ignoring {
|
||||
ignoring = false
|
||||
buf.WriteString(prefix)
|
||||
}
|
||||
|
||||
if curWidth > n {
|
||||
buf.Write(cluster)
|
||||
}
|
||||
|
||||
if ignoring {
|
||||
continue
|
||||
}
|
||||
|
||||
pstate = parser.GroundState
|
||||
continue
|
||||
}
|
||||
|
||||
switch action {
|
||||
case parser.PrintAction:
|
||||
curWidth++
|
||||
|
||||
if curWidth > n && ignoring {
|
||||
ignoring = false
|
||||
buf.WriteString(prefix)
|
||||
}
|
||||
|
||||
if ignoring {
|
||||
i++
|
||||
continue
|
||||
}
|
||||
|
||||
fallthrough
|
||||
case parser.ExecuteAction:
|
||||
// execute action will be things like \n, which, if outside the cut,
|
||||
// should be ignored.
|
||||
if ignoring {
|
||||
i++
|
||||
continue
|
||||
}
|
||||
fallthrough
|
||||
default:
|
||||
buf.WriteByte(b[i])
|
||||
i++
|
||||
}
|
||||
|
||||
pstate = state
|
||||
if curWidth > n && ignoring {
|
||||
ignoring = false
|
||||
buf.WriteString(prefix)
|
||||
}
|
||||
}
|
||||
|
||||
return buf.String()
|
||||
}
|
||||
|
||||
// ByteToGraphemeRange takes start and stop byte positions and converts them to
|
||||
// grapheme-aware char positions.
|
||||
// You can use this with [Truncate], [TruncateLeft], and [Cut].
|
||||
func ByteToGraphemeRange(str string, byteStart, byteStop int) (charStart, charStop int) {
|
||||
bytePos, charPos := 0, 0
|
||||
gr := uniseg.NewGraphemes(str)
|
||||
for byteStart > bytePos {
|
||||
if !gr.Next() {
|
||||
break
|
||||
}
|
||||
bytePos += len(gr.Str())
|
||||
charPos += max(1, gr.Width())
|
||||
}
|
||||
charStart = charPos
|
||||
for byteStop > bytePos {
|
||||
if !gr.Next() {
|
||||
break
|
||||
}
|
||||
bytePos += len(gr.Str())
|
||||
charPos += max(1, gr.Width())
|
||||
}
|
||||
charStop = charPos
|
||||
return
|
||||
}
|
||||
92
vendor/github.com/charmbracelet/x/ansi/util.go
generated
vendored
Normal file
92
vendor/github.com/charmbracelet/x/ansi/util.go
generated
vendored
Normal file
|
|
@ -0,0 +1,92 @@
|
|||
package ansi
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"image/color"
|
||||
"strconv"
|
||||
"strings"
|
||||
|
||||
"github.com/lucasb-eyer/go-colorful"
|
||||
)
|
||||
|
||||
// colorToHexString returns a hex string representation of a color.
|
||||
func colorToHexString(c color.Color) string { //nolint:unused
|
||||
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 { //nolint:unused
|
||||
return r<<16 + g<<8 + b
|
||||
}
|
||||
|
||||
type shiftable interface {
|
||||
~uint | ~uint16 | ~uint32 | ~uint64
|
||||
}
|
||||
|
||||
func shift[T shiftable](x T) T {
|
||||
if x > 0xff {
|
||||
x >>= 8
|
||||
}
|
||||
return x
|
||||
}
|
||||
|
||||
// XParseColor is a helper function that parses a string into a color.Color. It
|
||||
// provides a similar interface to the XParseColor function in Xlib. It
|
||||
// supports the following formats:
|
||||
//
|
||||
// - #RGB
|
||||
// - #RRGGBB
|
||||
// - rgb:RRRR/GGGG/BBBB
|
||||
// - rgba:RRRR/GGGG/BBBB/AAAA
|
||||
//
|
||||
// If the string is not a valid color, nil is returned.
|
||||
//
|
||||
// See: https://linux.die.net/man/3/xparsecolor
|
||||
func XParseColor(s string) color.Color {
|
||||
switch {
|
||||
case strings.HasPrefix(s, "#"):
|
||||
c, err := colorful.Hex(s)
|
||||
if err != nil {
|
||||
return nil
|
||||
}
|
||||
|
||||
return c
|
||||
case strings.HasPrefix(s, "rgb:"):
|
||||
parts := strings.Split(s[4:], "/")
|
||||
if len(parts) != 3 {
|
||||
return nil
|
||||
}
|
||||
|
||||
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} //nolint:gosec
|
||||
case strings.HasPrefix(s, "rgba:"):
|
||||
parts := strings.Split(s[5:], "/")
|
||||
if len(parts) != 4 {
|
||||
return nil
|
||||
}
|
||||
|
||||
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))} //nolint:gosec
|
||||
}
|
||||
return nil
|
||||
}
|
||||
113
vendor/github.com/charmbracelet/x/ansi/width.go
generated
vendored
Normal file
113
vendor/github.com/charmbracelet/x/ansi/width.go
generated
vendored
Normal file
|
|
@ -0,0 +1,113 @@
|
|||
package ansi
|
||||
|
||||
import (
|
||||
"bytes"
|
||||
|
||||
"github.com/charmbracelet/x/ansi/parser"
|
||||
"github.com/mattn/go-runewidth"
|
||||
"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 := range len(s) {
|
||||
if 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
|
||||
continue
|
||||
}
|
||||
|
||||
state, action := parser.Table.Transition(pstate, s[i])
|
||||
switch action {
|
||||
case parser.CollectAction:
|
||||
if state == parser.Utf8State {
|
||||
// This action happens when we transition to the Utf8State.
|
||||
rw = utf8ByteLen(s[i])
|
||||
buf.WriteByte(s[i])
|
||||
ri++
|
||||
}
|
||||
case parser.PrintAction, 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.
|
||||
// This treats the text as a sequence of grapheme clusters.
|
||||
func StringWidth(s string) int {
|
||||
return stringWidth(GraphemeWidth, s)
|
||||
}
|
||||
|
||||
// StringWidthWc 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.
|
||||
// This treats the text as a sequence of wide characters and runes.
|
||||
func StringWidthWc(s string) int {
|
||||
return stringWidth(WcWidth, s)
|
||||
}
|
||||
|
||||
func stringWidth(m Method, s string) int {
|
||||
if s == "" {
|
||||
return 0
|
||||
}
|
||||
|
||||
var (
|
||||
pstate = parser.GroundState // initial state
|
||||
cluster string
|
||||
width int
|
||||
)
|
||||
|
||||
for i := 0; i < len(s); i++ {
|
||||
state, action := parser.Table.Transition(pstate, s[i])
|
||||
if state == parser.Utf8State {
|
||||
var w int
|
||||
cluster, _, w, _ = uniseg.FirstGraphemeClusterInString(s[i:], -1)
|
||||
if m == WcWidth {
|
||||
w = runewidth.StringWidth(cluster)
|
||||
}
|
||||
width += w
|
||||
i += len(cluster) - 1
|
||||
pstate = parser.GroundState
|
||||
continue
|
||||
}
|
||||
|
||||
if action == parser.PrintAction {
|
||||
width++
|
||||
}
|
||||
|
||||
pstate = state
|
||||
}
|
||||
|
||||
return width
|
||||
}
|
||||
53
vendor/github.com/charmbracelet/x/ansi/winop.go
generated
vendored
Normal file
53
vendor/github.com/charmbracelet/x/ansi/winop.go
generated
vendored
Normal file
|
|
@ -0,0 +1,53 @@
|
|||
package ansi
|
||||
|
||||
import (
|
||||
"strconv"
|
||||
"strings"
|
||||
)
|
||||
|
||||
const (
|
||||
// ResizeWindowWinOp is a window operation that resizes the terminal
|
||||
// window.
|
||||
ResizeWindowWinOp = 4
|
||||
|
||||
// RequestWindowSizeWinOp is a window operation that requests a report of
|
||||
// the size of the terminal window in pixels. The response is in the form:
|
||||
// CSI 4 ; height ; width t
|
||||
RequestWindowSizeWinOp = 14
|
||||
|
||||
// RequestCellSizeWinOp is a window operation that requests a report of
|
||||
// the size of the terminal cell size in pixels. The response is in the form:
|
||||
// CSI 6 ; height ; width t
|
||||
RequestCellSizeWinOp = 16
|
||||
)
|
||||
|
||||
// WindowOp (XTWINOPS) is a sequence that manipulates the terminal window.
|
||||
//
|
||||
// CSI Ps ; Ps ; Ps t
|
||||
//
|
||||
// Ps is a semicolon-separated list of parameters.
|
||||
// See https://invisible-island.net/xterm/ctlseqs/ctlseqs.html#h4-Functions-using-CSI-_-ordered-by-the-final-character-lparen-s-rparen:CSI-Ps;Ps;Ps-t.1EB0
|
||||
func WindowOp(p int, ps ...int) string {
|
||||
if p <= 0 {
|
||||
return ""
|
||||
}
|
||||
|
||||
if len(ps) == 0 {
|
||||
return "\x1b[" + strconv.Itoa(p) + "t"
|
||||
}
|
||||
|
||||
params := make([]string, 0, len(ps)+1)
|
||||
params = append(params, strconv.Itoa(p))
|
||||
for _, p := range ps {
|
||||
if p >= 0 {
|
||||
params = append(params, strconv.Itoa(p))
|
||||
}
|
||||
}
|
||||
|
||||
return "\x1b[" + strings.Join(params, ";") + "t"
|
||||
}
|
||||
|
||||
// XTWINOPS is an alias for [WindowOp].
|
||||
func XTWINOPS(p int, ps ...int) string {
|
||||
return WindowOp(p, ps...)
|
||||
}
|
||||
474
vendor/github.com/charmbracelet/x/ansi/wrap.go
generated
vendored
Normal file
474
vendor/github.com/charmbracelet/x/ansi/wrap.go
generated
vendored
Normal file
|
|
@ -0,0 +1,474 @@
|
|||
package ansi
|
||||
|
||||
import (
|
||||
"bytes"
|
||||
"unicode"
|
||||
"unicode/utf8"
|
||||
|
||||
"github.com/charmbracelet/x/ansi/parser"
|
||||
"github.com/mattn/go-runewidth"
|
||||
"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.
|
||||
// This treats the text as a sequence of graphemes.
|
||||
func Hardwrap(s string, limit int, preserveSpace bool) string {
|
||||
return hardwrap(GraphemeWidth, s, limit, preserveSpace)
|
||||
}
|
||||
|
||||
// HardwrapWc 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.
|
||||
// This treats the text as a sequence of wide characters and runes.
|
||||
func HardwrapWc(s string, limit int, preserveSpace bool) string {
|
||||
return hardwrap(WcWidth, s, limit, preserveSpace)
|
||||
}
|
||||
|
||||
func hardwrap(m Method, s string, limit int, preserveSpace bool) string {
|
||||
if limit < 1 {
|
||||
return s
|
||||
}
|
||||
|
||||
var (
|
||||
cluster []byte
|
||||
buf bytes.Buffer
|
||||
curWidth int
|
||||
forceNewline bool
|
||||
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])
|
||||
if state == parser.Utf8State { //nolint:nestif
|
||||
var width int
|
||||
cluster, _, width, _ = uniseg.FirstGraphemeCluster(b[i:], -1)
|
||||
if m == WcWidth {
|
||||
width = runewidth.StringWidth(string(cluster))
|
||||
}
|
||||
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
|
||||
pstate = parser.GroundState
|
||||
continue
|
||||
}
|
||||
|
||||
switch action {
|
||||
case parser.PrintAction, 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])
|
||||
if action == parser.PrintAction {
|
||||
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.
|
||||
//
|
||||
// This treats the text as a sequence of graphemes.
|
||||
func Wordwrap(s string, limit int, breakpoints string) string {
|
||||
return wordwrap(GraphemeWidth, s, limit, breakpoints)
|
||||
}
|
||||
|
||||
// WordwrapWc 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.
|
||||
//
|
||||
// This treats the text as a sequence of wide characters and runes.
|
||||
func WordwrapWc(s string, limit int, breakpoints string) string {
|
||||
return wordwrap(WcWidth, s, limit, breakpoints)
|
||||
}
|
||||
|
||||
func wordwrap(m Method, 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
|
||||
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])
|
||||
if state == parser.Utf8State { //nolint:nestif
|
||||
var width int
|
||||
cluster, _, width, _ = uniseg.FirstGraphemeCluster(b[i:], -1)
|
||||
if m == WcWidth {
|
||||
width = runewidth.StringWidth(string(cluster))
|
||||
}
|
||||
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
|
||||
}
|
||||
|
||||
switch action {
|
||||
case parser.PrintAction, 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.
|
||||
//
|
||||
// This treats the text as a sequence of graphemes.
|
||||
func Wrap(s string, limit int, breakpoints string) string {
|
||||
return wrap(GraphemeWidth, s, limit, breakpoints)
|
||||
}
|
||||
|
||||
// WrapWc 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.
|
||||
//
|
||||
// This treats the text as a sequence of wide characters and runes.
|
||||
func WrapWc(s string, limit int, breakpoints string) string {
|
||||
return wrap(WcWidth, s, limit, breakpoints)
|
||||
}
|
||||
|
||||
func wrap(m Method, s string, limit int, breakpoints string) string {
|
||||
if limit < 1 {
|
||||
return s
|
||||
}
|
||||
|
||||
var (
|
||||
cluster []byte
|
||||
buf bytes.Buffer
|
||||
word bytes.Buffer
|
||||
space bytes.Buffer
|
||||
spaceWidth int // width of the space buffer
|
||||
curWidth int // written width of the line
|
||||
wordLen int // word buffer len without ANSI escape codes
|
||||
pstate = parser.GroundState // initial state
|
||||
b = []byte(s)
|
||||
)
|
||||
|
||||
addSpace := func() {
|
||||
curWidth += spaceWidth
|
||||
buf.Write(space.Bytes())
|
||||
space.Reset()
|
||||
spaceWidth = 0
|
||||
}
|
||||
|
||||
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()
|
||||
spaceWidth = 0
|
||||
}
|
||||
|
||||
i := 0
|
||||
for i < len(b) {
|
||||
state, action := parser.Table.Transition(pstate, b[i])
|
||||
if state == parser.Utf8State { //nolint:nestif
|
||||
var width int
|
||||
cluster, _, width, _ = uniseg.FirstGraphemeCluster(b[i:], -1)
|
||||
if m == WcWidth {
|
||||
width = runewidth.StringWidth(string(cluster))
|
||||
}
|
||||
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)
|
||||
spaceWidth += width
|
||||
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+spaceWidth > limit {
|
||||
addNewline()
|
||||
}
|
||||
}
|
||||
|
||||
pstate = parser.GroundState
|
||||
continue
|
||||
}
|
||||
|
||||
switch action {
|
||||
case parser.PrintAction, parser.ExecuteAction:
|
||||
switch r := rune(b[i]); {
|
||||
case r == '\n':
|
||||
if wordLen == 0 {
|
||||
if curWidth+spaceWidth > limit {
|
||||
curWidth = 0
|
||||
} else {
|
||||
// preserve whitespaces
|
||||
buf.Write(space.Bytes())
|
||||
}
|
||||
space.Reset()
|
||||
spaceWidth = 0
|
||||
}
|
||||
|
||||
addWord()
|
||||
addNewline()
|
||||
case unicode.IsSpace(r):
|
||||
addWord()
|
||||
space.WriteRune(r)
|
||||
spaceWidth++
|
||||
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:
|
||||
if curWidth == limit {
|
||||
addNewline()
|
||||
}
|
||||
word.WriteRune(r)
|
||||
wordLen++
|
||||
|
||||
if wordLen == limit {
|
||||
// Hardwrap the word if it's too long
|
||||
addWord()
|
||||
}
|
||||
|
||||
if curWidth+wordLen+spaceWidth > limit {
|
||||
addNewline()
|
||||
}
|
||||
}
|
||||
|
||||
default:
|
||||
word.WriteByte(b[i])
|
||||
}
|
||||
|
||||
// We manage the UTF8 state separately manually above.
|
||||
if pstate != parser.Utf8State {
|
||||
pstate = state
|
||||
}
|
||||
i++
|
||||
}
|
||||
|
||||
if wordLen == 0 {
|
||||
if curWidth+spaceWidth > limit {
|
||||
curWidth = 0
|
||||
} else {
|
||||
// preserve whitespaces
|
||||
buf.Write(space.Bytes())
|
||||
}
|
||||
space.Reset()
|
||||
spaceWidth = 0
|
||||
}
|
||||
|
||||
addWord()
|
||||
|
||||
return buf.String()
|
||||
}
|
||||
|
||||
func runeContainsAny(r rune, s string) bool {
|
||||
for _, c := range s {
|
||||
if c == r {
|
||||
return true
|
||||
}
|
||||
}
|
||||
return false
|
||||
}
|
||||
138
vendor/github.com/charmbracelet/x/ansi/xterm.go
generated
vendored
Normal file
138
vendor/github.com/charmbracelet/x/ansi/xterm.go
generated
vendored
Normal file
|
|
@ -0,0 +1,138 @@
|
|||
package ansi
|
||||
|
||||
import "strconv"
|
||||
|
||||
// KeyModifierOptions (XTMODKEYS) sets/resets xterm key modifier options.
|
||||
//
|
||||
// Default is 0.
|
||||
//
|
||||
// CSI > Pp m
|
||||
// CSI > Pp ; Pv m
|
||||
//
|
||||
// If Pv is omitted, the resource is reset to its initial value.
|
||||
//
|
||||
// See: https://invisible-island.net/xterm/ctlseqs/ctlseqs.html#h3-Functions-using-CSI-_-ordered-by-the-final-character_s_
|
||||
func KeyModifierOptions(p int, vs ...int) string {
|
||||
var pp, pv string
|
||||
if p > 0 {
|
||||
pp = strconv.Itoa(p)
|
||||
}
|
||||
|
||||
if len(vs) == 0 {
|
||||
return "\x1b[>" + strconv.Itoa(p) + "m"
|
||||
}
|
||||
|
||||
v := vs[0]
|
||||
if v > 0 {
|
||||
pv = strconv.Itoa(v)
|
||||
return "\x1b[>" + pp + ";" + pv + "m"
|
||||
}
|
||||
|
||||
return "\x1b[>" + pp + "m"
|
||||
}
|
||||
|
||||
// XTMODKEYS is an alias for [KeyModifierOptions].
|
||||
func XTMODKEYS(p int, vs ...int) string {
|
||||
return KeyModifierOptions(p, vs...)
|
||||
}
|
||||
|
||||
// SetKeyModifierOptions sets xterm key modifier options.
|
||||
// This is an alias for [KeyModifierOptions].
|
||||
func SetKeyModifierOptions(pp int, pv int) string {
|
||||
return KeyModifierOptions(pp, pv)
|
||||
}
|
||||
|
||||
// ResetKeyModifierOptions resets xterm key modifier options.
|
||||
// This is an alias for [KeyModifierOptions].
|
||||
func ResetKeyModifierOptions(pp int) string {
|
||||
return KeyModifierOptions(pp)
|
||||
}
|
||||
|
||||
// QueryKeyModifierOptions (XTQMODKEYS) requests xterm key modifier options.
|
||||
//
|
||||
// Default is 0.
|
||||
//
|
||||
// CSI ? Pp m
|
||||
//
|
||||
// See: https://invisible-island.net/xterm/ctlseqs/ctlseqs.html#h3-Functions-using-CSI-_-ordered-by-the-final-character_s_
|
||||
func QueryKeyModifierOptions(pp int) string {
|
||||
var p string
|
||||
if pp > 0 {
|
||||
p = strconv.Itoa(pp)
|
||||
}
|
||||
return "\x1b[?" + p + "m"
|
||||
}
|
||||
|
||||
// XTQMODKEYS is an alias for [QueryKeyModifierOptions].
|
||||
func XTQMODKEYS(pp int) string {
|
||||
return QueryKeyModifierOptions(pp)
|
||||
}
|
||||
|
||||
// Modify Other Keys (modifyOtherKeys) is an xterm feature that allows the
|
||||
// terminal to modify the behavior of certain keys to send different escape
|
||||
// sequences when pressed.
|
||||
//
|
||||
// See: https://invisible-island.net/xterm/manpage/xterm.html#VT100-Widget-Resources:modifyOtherKeys
|
||||
const (
|
||||
SetModifyOtherKeys1 = "\x1b[>4;1m"
|
||||
SetModifyOtherKeys2 = "\x1b[>4;2m"
|
||||
ResetModifyOtherKeys = "\x1b[>4m"
|
||||
QueryModifyOtherKeys = "\x1b[?4m"
|
||||
)
|
||||
|
||||
// ModifyOtherKeys returns a sequence that sets XTerm modifyOtherKeys mode.
|
||||
// The mode argument specifies the mode to set.
|
||||
//
|
||||
// 0: Disable modifyOtherKeys mode.
|
||||
// 1: Enable modifyOtherKeys mode 1.
|
||||
// 2: Enable modifyOtherKeys mode 2.
|
||||
//
|
||||
// CSI > 4 ; mode 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
|
||||
//
|
||||
// Deprecated: use [SetModifyOtherKeys1] or [SetModifyOtherKeys2] instead.
|
||||
func ModifyOtherKeys(mode int) string {
|
||||
return "\x1b[>4;" + strconv.Itoa(mode) + "m"
|
||||
}
|
||||
|
||||
// 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
|
||||
//
|
||||
// Deprecated: use [ResetModifyOtherKeys] instead.
|
||||
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
|
||||
//
|
||||
// Deprecated: use [SetModifyOtherKeys1] instead.
|
||||
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
|
||||
//
|
||||
// Deprecated: use [SetModifyOtherKeys2] instead.
|
||||
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
|
||||
//
|
||||
// Deprecated: use [QueryModifyOtherKeys] instead.
|
||||
const RequestModifyOtherKeys = "\x1b[?4m"
|
||||
21
vendor/github.com/charmbracelet/x/cellbuf/LICENSE
generated
vendored
Normal file
21
vendor/github.com/charmbracelet/x/cellbuf/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.
|
||||
473
vendor/github.com/charmbracelet/x/cellbuf/buffer.go
generated
vendored
Normal file
473
vendor/github.com/charmbracelet/x/cellbuf/buffer.go
generated
vendored
Normal file
|
|
@ -0,0 +1,473 @@
|
|||
package cellbuf
|
||||
|
||||
import (
|
||||
"strings"
|
||||
|
||||
"github.com/mattn/go-runewidth"
|
||||
"github.com/rivo/uniseg"
|
||||
)
|
||||
|
||||
// NewCell returns a new cell. This is a convenience function that initializes a
|
||||
// new cell with the given content. The cell's width is determined by the
|
||||
// content using [runewidth.RuneWidth].
|
||||
// This will only account for the first combined rune in the content. If the
|
||||
// content is empty, it will return an empty cell with a width of 0.
|
||||
func NewCell(r rune, comb ...rune) (c *Cell) {
|
||||
c = new(Cell)
|
||||
c.Rune = r
|
||||
c.Width = runewidth.RuneWidth(r)
|
||||
for _, r := range comb {
|
||||
if runewidth.RuneWidth(r) > 0 {
|
||||
break
|
||||
}
|
||||
c.Comb = append(c.Comb, r)
|
||||
}
|
||||
c.Comb = comb
|
||||
c.Width = runewidth.StringWidth(string(append([]rune{r}, comb...)))
|
||||
return
|
||||
}
|
||||
|
||||
// NewCellString returns a new cell with the given string content. This is a
|
||||
// convenience function that initializes a new cell with the given content. The
|
||||
// cell's width is determined by the content using [runewidth.StringWidth].
|
||||
// This will only use the first combined rune in the string. If the string is
|
||||
// empty, it will return an empty cell with a width of 0.
|
||||
func NewCellString(s string) (c *Cell) {
|
||||
c = new(Cell)
|
||||
for i, r := range s {
|
||||
if i == 0 {
|
||||
c.Rune = r
|
||||
// We only care about the first rune's width
|
||||
c.Width = runewidth.RuneWidth(r)
|
||||
} else {
|
||||
if runewidth.RuneWidth(r) > 0 {
|
||||
break
|
||||
}
|
||||
c.Comb = append(c.Comb, r)
|
||||
}
|
||||
}
|
||||
return
|
||||
}
|
||||
|
||||
// NewGraphemeCell returns a new cell. This is a convenience function that
|
||||
// initializes a new cell with the given content. The cell's width is determined
|
||||
// by the content using [uniseg.FirstGraphemeClusterInString].
|
||||
// This is used when the content is a grapheme cluster i.e. a sequence of runes
|
||||
// that form a single visual unit.
|
||||
// This will only return the first grapheme cluster in the string. If the
|
||||
// string is empty, it will return an empty cell with a width of 0.
|
||||
func NewGraphemeCell(s string) (c *Cell) {
|
||||
g, _, w, _ := uniseg.FirstGraphemeClusterInString(s, -1)
|
||||
return newGraphemeCell(g, w)
|
||||
}
|
||||
|
||||
func newGraphemeCell(s string, w int) (c *Cell) {
|
||||
c = new(Cell)
|
||||
c.Width = w
|
||||
for i, r := range s {
|
||||
if i == 0 {
|
||||
c.Rune = r
|
||||
} else {
|
||||
c.Comb = append(c.Comb, r)
|
||||
}
|
||||
}
|
||||
return
|
||||
}
|
||||
|
||||
// Line represents a line in the terminal.
|
||||
// A nil cell represents an blank cell, a cell with a space character and a
|
||||
// width of 1.
|
||||
// If a cell has no content and a width of 0, it is a placeholder for a wide
|
||||
// cell.
|
||||
type Line []*Cell
|
||||
|
||||
// Width returns the width of the line.
|
||||
func (l Line) Width() int {
|
||||
return len(l)
|
||||
}
|
||||
|
||||
// Len returns the length of the line.
|
||||
func (l Line) Len() int {
|
||||
return len(l)
|
||||
}
|
||||
|
||||
// String returns the string representation of the line. Any trailing spaces
|
||||
// are removed.
|
||||
func (l Line) String() (s string) {
|
||||
for _, c := range l {
|
||||
if c == nil {
|
||||
s += " "
|
||||
} else if c.Empty() {
|
||||
continue
|
||||
} else {
|
||||
s += c.String()
|
||||
}
|
||||
}
|
||||
s = strings.TrimRight(s, " ")
|
||||
return
|
||||
}
|
||||
|
||||
// At returns the cell at the given x position.
|
||||
// If the cell does not exist, it returns nil.
|
||||
func (l Line) At(x int) *Cell {
|
||||
if x < 0 || x >= len(l) {
|
||||
return nil
|
||||
}
|
||||
|
||||
c := l[x]
|
||||
if c == nil {
|
||||
newCell := BlankCell
|
||||
return &newCell
|
||||
}
|
||||
|
||||
return c
|
||||
}
|
||||
|
||||
// Set sets the cell at the given x position. If a wide cell is given, it will
|
||||
// set the cell and the following cells to [EmptyCell]. It returns true if the
|
||||
// cell was set.
|
||||
func (l Line) Set(x int, c *Cell) bool {
|
||||
return l.set(x, c, true)
|
||||
}
|
||||
|
||||
func (l Line) set(x int, c *Cell, clone bool) bool {
|
||||
width := l.Width()
|
||||
if x < 0 || x >= width {
|
||||
return false
|
||||
}
|
||||
|
||||
// When a wide cell is partially overwritten, we need
|
||||
// to fill the rest of the cell with space cells to
|
||||
// avoid rendering issues.
|
||||
prev := l.At(x)
|
||||
if prev != nil && prev.Width > 1 {
|
||||
// Writing to the first wide cell
|
||||
for j := 0; j < prev.Width && x+j < l.Width(); j++ {
|
||||
l[x+j] = prev.Clone().Blank()
|
||||
}
|
||||
} else if prev != nil && prev.Width == 0 {
|
||||
// Writing to wide cell placeholders
|
||||
for j := 1; j < maxCellWidth && x-j >= 0; j++ {
|
||||
wide := l.At(x - j)
|
||||
if wide != nil && wide.Width > 1 && j < wide.Width {
|
||||
for k := 0; k < wide.Width; k++ {
|
||||
l[x-j+k] = wide.Clone().Blank()
|
||||
}
|
||||
break
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if clone && c != nil {
|
||||
// Clone the cell if not nil.
|
||||
c = c.Clone()
|
||||
}
|
||||
|
||||
if c != nil && x+c.Width > width {
|
||||
// If the cell is too wide, we write blanks with the same style.
|
||||
for i := 0; i < c.Width && x+i < width; i++ {
|
||||
l[x+i] = c.Clone().Blank()
|
||||
}
|
||||
} else {
|
||||
l[x] = c
|
||||
|
||||
// Mark wide cells with an empty cell zero width
|
||||
// We set the wide cell down below
|
||||
if c != nil && c.Width > 1 {
|
||||
for j := 1; j < c.Width && x+j < l.Width(); j++ {
|
||||
var wide Cell
|
||||
l[x+j] = &wide
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return true
|
||||
}
|
||||
|
||||
// Buffer is a 2D grid of cells representing a screen or terminal.
|
||||
type Buffer struct {
|
||||
// Lines holds the lines of the buffer.
|
||||
Lines []Line
|
||||
}
|
||||
|
||||
// NewBuffer creates a new buffer with the given width and height.
|
||||
// This is a convenience function that initializes a new buffer and resizes it.
|
||||
func NewBuffer(width int, height int) *Buffer {
|
||||
b := new(Buffer)
|
||||
b.Resize(width, height)
|
||||
return b
|
||||
}
|
||||
|
||||
// String returns the string representation of the buffer.
|
||||
func (b *Buffer) String() (s string) {
|
||||
for i, l := range b.Lines {
|
||||
s += l.String()
|
||||
if i < len(b.Lines)-1 {
|
||||
s += "\r\n"
|
||||
}
|
||||
}
|
||||
return
|
||||
}
|
||||
|
||||
// Line returns a pointer to the line at the given y position.
|
||||
// If the line does not exist, it returns nil.
|
||||
func (b *Buffer) Line(y int) Line {
|
||||
if y < 0 || y >= len(b.Lines) {
|
||||
return nil
|
||||
}
|
||||
return b.Lines[y]
|
||||
}
|
||||
|
||||
// Cell implements Screen.
|
||||
func (b *Buffer) Cell(x int, y int) *Cell {
|
||||
if y < 0 || y >= len(b.Lines) {
|
||||
return nil
|
||||
}
|
||||
return b.Lines[y].At(x)
|
||||
}
|
||||
|
||||
// maxCellWidth is the maximum width a terminal cell can get.
|
||||
const maxCellWidth = 4
|
||||
|
||||
// SetCell sets the cell at the given x, y position.
|
||||
func (b *Buffer) SetCell(x, y int, c *Cell) bool {
|
||||
return b.setCell(x, y, c, true)
|
||||
}
|
||||
|
||||
// setCell sets the cell at the given x, y position. This will always clone and
|
||||
// allocates a new cell if c is not nil.
|
||||
func (b *Buffer) setCell(x, y int, c *Cell, clone bool) bool {
|
||||
if y < 0 || y >= len(b.Lines) {
|
||||
return false
|
||||
}
|
||||
return b.Lines[y].set(x, c, clone)
|
||||
}
|
||||
|
||||
// Height implements Screen.
|
||||
func (b *Buffer) Height() int {
|
||||
return len(b.Lines)
|
||||
}
|
||||
|
||||
// Width implements Screen.
|
||||
func (b *Buffer) Width() int {
|
||||
if len(b.Lines) == 0 {
|
||||
return 0
|
||||
}
|
||||
return b.Lines[0].Width()
|
||||
}
|
||||
|
||||
// Bounds returns the bounds of the buffer.
|
||||
func (b *Buffer) Bounds() Rectangle {
|
||||
return Rect(0, 0, b.Width(), b.Height())
|
||||
}
|
||||
|
||||
// Resize resizes the buffer to the given width and height.
|
||||
func (b *Buffer) Resize(width int, height int) {
|
||||
if width == 0 || height == 0 {
|
||||
b.Lines = nil
|
||||
return
|
||||
}
|
||||
|
||||
if width > b.Width() {
|
||||
line := make(Line, width-b.Width())
|
||||
for i := range b.Lines {
|
||||
b.Lines[i] = append(b.Lines[i], line...)
|
||||
}
|
||||
} else if width < b.Width() {
|
||||
for i := range b.Lines {
|
||||
b.Lines[i] = b.Lines[i][:width]
|
||||
}
|
||||
}
|
||||
|
||||
if height > len(b.Lines) {
|
||||
for i := len(b.Lines); i < height; i++ {
|
||||
b.Lines = append(b.Lines, make(Line, width))
|
||||
}
|
||||
} else if height < len(b.Lines) {
|
||||
b.Lines = b.Lines[:height]
|
||||
}
|
||||
}
|
||||
|
||||
// FillRect fills the buffer with the given cell and rectangle.
|
||||
func (b *Buffer) FillRect(c *Cell, rect Rectangle) {
|
||||
cellWidth := 1
|
||||
if c != nil && c.Width > 1 {
|
||||
cellWidth = c.Width
|
||||
}
|
||||
for y := rect.Min.Y; y < rect.Max.Y; y++ {
|
||||
for x := rect.Min.X; x < rect.Max.X; x += cellWidth {
|
||||
b.setCell(x, y, c, false) //nolint:errcheck
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Fill fills the buffer with the given cell and rectangle.
|
||||
func (b *Buffer) Fill(c *Cell) {
|
||||
b.FillRect(c, b.Bounds())
|
||||
}
|
||||
|
||||
// Clear clears the buffer with space cells and rectangle.
|
||||
func (b *Buffer) Clear() {
|
||||
b.ClearRect(b.Bounds())
|
||||
}
|
||||
|
||||
// ClearRect clears the buffer with space cells within the specified
|
||||
// rectangles. Only cells within the rectangle's bounds are affected.
|
||||
func (b *Buffer) ClearRect(rect Rectangle) {
|
||||
b.FillRect(nil, rect)
|
||||
}
|
||||
|
||||
// InsertLine inserts n lines at the given line position, with the given
|
||||
// optional cell, within the specified rectangles. If no rectangles are
|
||||
// specified, it inserts lines in the entire buffer. Only cells within the
|
||||
// rectangle's horizontal bounds are affected. Lines are pushed out of the
|
||||
// rectangle bounds and lost. This follows terminal [ansi.IL] behavior.
|
||||
// It returns the pushed out lines.
|
||||
func (b *Buffer) InsertLine(y, n int, c *Cell) {
|
||||
b.InsertLineRect(y, n, c, b.Bounds())
|
||||
}
|
||||
|
||||
// InsertLineRect inserts new lines at the given line position, with the
|
||||
// given optional cell, within the rectangle bounds. Only cells within the
|
||||
// rectangle's horizontal bounds are affected. Lines are pushed out of the
|
||||
// rectangle bounds and lost. This follows terminal [ansi.IL] behavior.
|
||||
func (b *Buffer) InsertLineRect(y, n int, c *Cell, rect Rectangle) {
|
||||
if n <= 0 || y < rect.Min.Y || y >= rect.Max.Y || y >= b.Height() {
|
||||
return
|
||||
}
|
||||
|
||||
// Limit number of lines to insert to available space
|
||||
if y+n > rect.Max.Y {
|
||||
n = rect.Max.Y - y
|
||||
}
|
||||
|
||||
// Move existing lines down within the bounds
|
||||
for i := rect.Max.Y - 1; i >= y+n; i-- {
|
||||
for x := rect.Min.X; x < rect.Max.X; x++ {
|
||||
// We don't need to clone c here because we're just moving lines down.
|
||||
b.setCell(x, i, b.Lines[i-n][x], false)
|
||||
}
|
||||
}
|
||||
|
||||
// Clear the newly inserted lines within bounds
|
||||
for i := y; i < y+n; i++ {
|
||||
for x := rect.Min.X; x < rect.Max.X; x++ {
|
||||
b.setCell(x, i, c, true)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// DeleteLineRect deletes lines at the given line position, with the given
|
||||
// optional cell, within the rectangle bounds. Only cells within the
|
||||
// rectangle's bounds are affected. Lines are shifted up within the bounds and
|
||||
// new blank lines are created at the bottom. This follows terminal [ansi.DL]
|
||||
// behavior.
|
||||
func (b *Buffer) DeleteLineRect(y, n int, c *Cell, rect Rectangle) {
|
||||
if n <= 0 || y < rect.Min.Y || y >= rect.Max.Y || y >= b.Height() {
|
||||
return
|
||||
}
|
||||
|
||||
// Limit deletion count to available space in scroll region
|
||||
if n > rect.Max.Y-y {
|
||||
n = rect.Max.Y - y
|
||||
}
|
||||
|
||||
// Shift cells up within the bounds
|
||||
for dst := y; dst < rect.Max.Y-n; dst++ {
|
||||
src := dst + n
|
||||
for x := rect.Min.X; x < rect.Max.X; x++ {
|
||||
// We don't need to clone c here because we're just moving cells up.
|
||||
// b.lines[dst][x] = b.lines[src][x]
|
||||
b.setCell(x, dst, b.Lines[src][x], false)
|
||||
}
|
||||
}
|
||||
|
||||
// Fill the bottom n lines with blank cells
|
||||
for i := rect.Max.Y - n; i < rect.Max.Y; i++ {
|
||||
for x := rect.Min.X; x < rect.Max.X; x++ {
|
||||
b.setCell(x, i, c, true)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// DeleteLine deletes n lines at the given line position, with the given
|
||||
// optional cell, within the specified rectangles. If no rectangles are
|
||||
// specified, it deletes lines in the entire buffer.
|
||||
func (b *Buffer) DeleteLine(y, n int, c *Cell) {
|
||||
b.DeleteLineRect(y, n, c, b.Bounds())
|
||||
}
|
||||
|
||||
// InsertCell inserts new cells at the given position, with the given optional
|
||||
// cell, within the specified rectangles. If no rectangles are specified, it
|
||||
// inserts cells in the entire buffer. This follows terminal [ansi.ICH]
|
||||
// behavior.
|
||||
func (b *Buffer) InsertCell(x, y, n int, c *Cell) {
|
||||
b.InsertCellRect(x, y, n, c, b.Bounds())
|
||||
}
|
||||
|
||||
// InsertCellRect inserts new cells at the given position, with the given
|
||||
// optional cell, within the rectangle bounds. Only cells within the
|
||||
// rectangle's bounds are affected, following terminal [ansi.ICH] behavior.
|
||||
func (b *Buffer) InsertCellRect(x, y, n int, c *Cell, rect Rectangle) {
|
||||
if n <= 0 || y < rect.Min.Y || y >= rect.Max.Y || y >= b.Height() ||
|
||||
x < rect.Min.X || x >= rect.Max.X || x >= b.Width() {
|
||||
return
|
||||
}
|
||||
|
||||
// Limit number of cells to insert to available space
|
||||
if x+n > rect.Max.X {
|
||||
n = rect.Max.X - x
|
||||
}
|
||||
|
||||
// Move existing cells within rectangle bounds to the right
|
||||
for i := rect.Max.X - 1; i >= x+n && i-n >= rect.Min.X; i-- {
|
||||
// We don't need to clone c here because we're just moving cells to the
|
||||
// right.
|
||||
// b.lines[y][i] = b.lines[y][i-n]
|
||||
b.setCell(i, y, b.Lines[y][i-n], false)
|
||||
}
|
||||
|
||||
// Clear the newly inserted cells within rectangle bounds
|
||||
for i := x; i < x+n && i < rect.Max.X; i++ {
|
||||
b.setCell(i, y, c, true)
|
||||
}
|
||||
}
|
||||
|
||||
// DeleteCell deletes cells at the given position, with the given optional
|
||||
// cell, within the specified rectangles. If no rectangles are specified, it
|
||||
// deletes cells in the entire buffer. This follows terminal [ansi.DCH]
|
||||
// behavior.
|
||||
func (b *Buffer) DeleteCell(x, y, n int, c *Cell) {
|
||||
b.DeleteCellRect(x, y, n, c, b.Bounds())
|
||||
}
|
||||
|
||||
// DeleteCellRect deletes cells at the given position, with the given
|
||||
// optional cell, within the rectangle bounds. Only cells within the
|
||||
// rectangle's bounds are affected, following terminal [ansi.DCH] behavior.
|
||||
func (b *Buffer) DeleteCellRect(x, y, n int, c *Cell, rect Rectangle) {
|
||||
if n <= 0 || y < rect.Min.Y || y >= rect.Max.Y || y >= b.Height() ||
|
||||
x < rect.Min.X || x >= rect.Max.X || x >= b.Width() {
|
||||
return
|
||||
}
|
||||
|
||||
// Calculate how many positions we can actually delete
|
||||
remainingCells := rect.Max.X - x
|
||||
if n > remainingCells {
|
||||
n = remainingCells
|
||||
}
|
||||
|
||||
// Shift the remaining cells to the left
|
||||
for i := x; i < rect.Max.X-n; i++ {
|
||||
if i+n < rect.Max.X {
|
||||
// We don't need to clone c here because we're just moving cells to
|
||||
// the left.
|
||||
// b.lines[y][i] = b.lines[y][i+n]
|
||||
b.setCell(i, y, b.Lines[y][i+n], false)
|
||||
}
|
||||
}
|
||||
|
||||
// Fill the vacated positions with the given cell
|
||||
for i := rect.Max.X - n; i < rect.Max.X; i++ {
|
||||
b.setCell(i, y, c, true)
|
||||
}
|
||||
}
|
||||
503
vendor/github.com/charmbracelet/x/cellbuf/cell.go
generated
vendored
Normal file
503
vendor/github.com/charmbracelet/x/cellbuf/cell.go
generated
vendored
Normal file
|
|
@ -0,0 +1,503 @@
|
|||
package cellbuf
|
||||
|
||||
import (
|
||||
"github.com/charmbracelet/x/ansi"
|
||||
)
|
||||
|
||||
var (
|
||||
// BlankCell is a cell with a single space, width of 1, and no style or link.
|
||||
BlankCell = Cell{Rune: ' ', Width: 1}
|
||||
|
||||
// EmptyCell is just an empty cell used for comparisons and as a placeholder
|
||||
// for wide cells.
|
||||
EmptyCell = Cell{}
|
||||
)
|
||||
|
||||
// Cell represents a single cell in the terminal screen.
|
||||
type Cell struct {
|
||||
// The style of the cell. Nil style means no style. Zero value prints a
|
||||
// reset sequence.
|
||||
Style Style
|
||||
|
||||
// Link is the hyperlink of the cell.
|
||||
Link Link
|
||||
|
||||
// Comb is the combining runes of the cell. This is nil if the cell is a
|
||||
// single rune or if it's a zero width cell that is part of a wider cell.
|
||||
Comb []rune
|
||||
|
||||
// Width is the mono-space width of the grapheme cluster.
|
||||
Width int
|
||||
|
||||
// Rune is the main rune of the cell. This is zero if the cell is part of a
|
||||
// wider cell.
|
||||
Rune rune
|
||||
}
|
||||
|
||||
// Append appends runes to the cell without changing the width. This is useful
|
||||
// when we want to use the cell to store escape sequences or other runes that
|
||||
// don't affect the width of the cell.
|
||||
func (c *Cell) Append(r ...rune) {
|
||||
for i, r := range r {
|
||||
if i == 0 && c.Rune == 0 {
|
||||
c.Rune = r
|
||||
continue
|
||||
}
|
||||
c.Comb = append(c.Comb, r)
|
||||
}
|
||||
}
|
||||
|
||||
// String returns the string content of the cell excluding any styles, links,
|
||||
// and escape sequences.
|
||||
func (c Cell) String() string {
|
||||
if c.Rune == 0 {
|
||||
return ""
|
||||
}
|
||||
if len(c.Comb) == 0 {
|
||||
return string(c.Rune)
|
||||
}
|
||||
return string(append([]rune{c.Rune}, c.Comb...))
|
||||
}
|
||||
|
||||
// Equal returns whether the cell is equal to the other cell.
|
||||
func (c *Cell) Equal(o *Cell) bool {
|
||||
return o != nil &&
|
||||
c.Width == o.Width &&
|
||||
c.Rune == o.Rune &&
|
||||
runesEqual(c.Comb, o.Comb) &&
|
||||
c.Style.Equal(&o.Style) &&
|
||||
c.Link.Equal(&o.Link)
|
||||
}
|
||||
|
||||
// Empty returns whether the cell is an empty cell. An empty cell is a cell
|
||||
// with a width of 0, a rune of 0, and no combining runes.
|
||||
func (c Cell) Empty() bool {
|
||||
return c.Width == 0 &&
|
||||
c.Rune == 0 &&
|
||||
len(c.Comb) == 0
|
||||
}
|
||||
|
||||
// Reset resets the cell to the default state zero value.
|
||||
func (c *Cell) Reset() {
|
||||
c.Rune = 0
|
||||
c.Comb = nil
|
||||
c.Width = 0
|
||||
c.Style.Reset()
|
||||
c.Link.Reset()
|
||||
}
|
||||
|
||||
// Clear returns whether the cell consists of only attributes that don't
|
||||
// affect appearance of a space character.
|
||||
func (c *Cell) Clear() bool {
|
||||
return c.Rune == ' ' && len(c.Comb) == 0 && c.Width == 1 && c.Style.Clear() && c.Link.Empty()
|
||||
}
|
||||
|
||||
// Clone returns a copy of the cell.
|
||||
func (c *Cell) Clone() (n *Cell) {
|
||||
n = new(Cell)
|
||||
*n = *c
|
||||
return
|
||||
}
|
||||
|
||||
// Blank makes the cell a blank cell by setting the rune to a space, comb to
|
||||
// nil, and the width to 1.
|
||||
func (c *Cell) Blank() *Cell {
|
||||
c.Rune = ' '
|
||||
c.Comb = nil
|
||||
c.Width = 1
|
||||
return c
|
||||
}
|
||||
|
||||
// Link represents a hyperlink in the terminal screen.
|
||||
type Link struct {
|
||||
URL string
|
||||
Params string
|
||||
}
|
||||
|
||||
// String returns a string representation of the hyperlink.
|
||||
func (h Link) String() string {
|
||||
return h.URL
|
||||
}
|
||||
|
||||
// Reset resets the hyperlink to the default state zero value.
|
||||
func (h *Link) Reset() {
|
||||
h.URL = ""
|
||||
h.Params = ""
|
||||
}
|
||||
|
||||
// Equal returns whether the hyperlink is equal to the other hyperlink.
|
||||
func (h *Link) Equal(o *Link) bool {
|
||||
return o != nil && h.URL == o.URL && h.Params == o.Params
|
||||
}
|
||||
|
||||
// Empty returns whether the hyperlink is empty.
|
||||
func (h Link) Empty() bool {
|
||||
return h.URL == "" && h.Params == ""
|
||||
}
|
||||
|
||||
// AttrMask is a bitmask for text attributes that can change the look of text.
|
||||
// These attributes can be combined to create different styles.
|
||||
type AttrMask uint8
|
||||
|
||||
// These are the available text attributes that can be combined to create
|
||||
// different styles.
|
||||
const (
|
||||
BoldAttr AttrMask = 1 << iota
|
||||
FaintAttr
|
||||
ItalicAttr
|
||||
SlowBlinkAttr
|
||||
RapidBlinkAttr
|
||||
ReverseAttr
|
||||
ConcealAttr
|
||||
StrikethroughAttr
|
||||
|
||||
ResetAttr AttrMask = 0
|
||||
)
|
||||
|
||||
// UnderlineStyle is the style of underline to use for text.
|
||||
type UnderlineStyle = ansi.UnderlineStyle
|
||||
|
||||
// These are the available underline styles.
|
||||
const (
|
||||
NoUnderline = ansi.NoUnderlineStyle
|
||||
SingleUnderline = ansi.SingleUnderlineStyle
|
||||
DoubleUnderline = ansi.DoubleUnderlineStyle
|
||||
CurlyUnderline = ansi.CurlyUnderlineStyle
|
||||
DottedUnderline = ansi.DottedUnderlineStyle
|
||||
DashedUnderline = ansi.DashedUnderlineStyle
|
||||
)
|
||||
|
||||
// Style represents the Style of a cell.
|
||||
type Style struct {
|
||||
Fg ansi.Color
|
||||
Bg ansi.Color
|
||||
Ul ansi.Color
|
||||
Attrs AttrMask
|
||||
UlStyle UnderlineStyle
|
||||
}
|
||||
|
||||
// Sequence returns the ANSI sequence that sets the style.
|
||||
func (s Style) Sequence() string {
|
||||
if s.Empty() {
|
||||
return ansi.ResetStyle
|
||||
}
|
||||
|
||||
var b ansi.Style
|
||||
|
||||
if s.Attrs != 0 {
|
||||
if s.Attrs&BoldAttr != 0 {
|
||||
b = b.Bold()
|
||||
}
|
||||
if s.Attrs&FaintAttr != 0 {
|
||||
b = b.Faint()
|
||||
}
|
||||
if s.Attrs&ItalicAttr != 0 {
|
||||
b = b.Italic()
|
||||
}
|
||||
if s.Attrs&SlowBlinkAttr != 0 {
|
||||
b = b.SlowBlink()
|
||||
}
|
||||
if s.Attrs&RapidBlinkAttr != 0 {
|
||||
b = b.RapidBlink()
|
||||
}
|
||||
if s.Attrs&ReverseAttr != 0 {
|
||||
b = b.Reverse()
|
||||
}
|
||||
if s.Attrs&ConcealAttr != 0 {
|
||||
b = b.Conceal()
|
||||
}
|
||||
if s.Attrs&StrikethroughAttr != 0 {
|
||||
b = b.Strikethrough()
|
||||
}
|
||||
}
|
||||
if s.UlStyle != NoUnderline {
|
||||
switch s.UlStyle {
|
||||
case SingleUnderline:
|
||||
b = b.Underline()
|
||||
case DoubleUnderline:
|
||||
b = b.DoubleUnderline()
|
||||
case CurlyUnderline:
|
||||
b = b.CurlyUnderline()
|
||||
case DottedUnderline:
|
||||
b = b.DottedUnderline()
|
||||
case DashedUnderline:
|
||||
b = b.DashedUnderline()
|
||||
}
|
||||
}
|
||||
if s.Fg != nil {
|
||||
b = b.ForegroundColor(s.Fg)
|
||||
}
|
||||
if s.Bg != nil {
|
||||
b = b.BackgroundColor(s.Bg)
|
||||
}
|
||||
if s.Ul != nil {
|
||||
b = b.UnderlineColor(s.Ul)
|
||||
}
|
||||
|
||||
return b.String()
|
||||
}
|
||||
|
||||
// DiffSequence returns the ANSI sequence that sets the style as a diff from
|
||||
// another style.
|
||||
func (s Style) DiffSequence(o Style) string {
|
||||
if o.Empty() {
|
||||
return s.Sequence()
|
||||
}
|
||||
|
||||
var b ansi.Style
|
||||
|
||||
if !colorEqual(s.Fg, o.Fg) {
|
||||
b = b.ForegroundColor(s.Fg)
|
||||
}
|
||||
|
||||
if !colorEqual(s.Bg, o.Bg) {
|
||||
b = b.BackgroundColor(s.Bg)
|
||||
}
|
||||
|
||||
if !colorEqual(s.Ul, o.Ul) {
|
||||
b = b.UnderlineColor(s.Ul)
|
||||
}
|
||||
|
||||
var (
|
||||
noBlink bool
|
||||
isNormal bool
|
||||
)
|
||||
|
||||
if s.Attrs != o.Attrs {
|
||||
if s.Attrs&BoldAttr != o.Attrs&BoldAttr {
|
||||
if s.Attrs&BoldAttr != 0 {
|
||||
b = b.Bold()
|
||||
} else if !isNormal {
|
||||
isNormal = true
|
||||
b = b.NormalIntensity()
|
||||
}
|
||||
}
|
||||
if s.Attrs&FaintAttr != o.Attrs&FaintAttr {
|
||||
if s.Attrs&FaintAttr != 0 {
|
||||
b = b.Faint()
|
||||
} else if !isNormal {
|
||||
b = b.NormalIntensity()
|
||||
}
|
||||
}
|
||||
if s.Attrs&ItalicAttr != o.Attrs&ItalicAttr {
|
||||
if s.Attrs&ItalicAttr != 0 {
|
||||
b = b.Italic()
|
||||
} else {
|
||||
b = b.NoItalic()
|
||||
}
|
||||
}
|
||||
if s.Attrs&SlowBlinkAttr != o.Attrs&SlowBlinkAttr {
|
||||
if s.Attrs&SlowBlinkAttr != 0 {
|
||||
b = b.SlowBlink()
|
||||
} else if !noBlink {
|
||||
noBlink = true
|
||||
b = b.NoBlink()
|
||||
}
|
||||
}
|
||||
if s.Attrs&RapidBlinkAttr != o.Attrs&RapidBlinkAttr {
|
||||
if s.Attrs&RapidBlinkAttr != 0 {
|
||||
b = b.RapidBlink()
|
||||
} else if !noBlink {
|
||||
b = b.NoBlink()
|
||||
}
|
||||
}
|
||||
if s.Attrs&ReverseAttr != o.Attrs&ReverseAttr {
|
||||
if s.Attrs&ReverseAttr != 0 {
|
||||
b = b.Reverse()
|
||||
} else {
|
||||
b = b.NoReverse()
|
||||
}
|
||||
}
|
||||
if s.Attrs&ConcealAttr != o.Attrs&ConcealAttr {
|
||||
if s.Attrs&ConcealAttr != 0 {
|
||||
b = b.Conceal()
|
||||
} else {
|
||||
b = b.NoConceal()
|
||||
}
|
||||
}
|
||||
if s.Attrs&StrikethroughAttr != o.Attrs&StrikethroughAttr {
|
||||
if s.Attrs&StrikethroughAttr != 0 {
|
||||
b = b.Strikethrough()
|
||||
} else {
|
||||
b = b.NoStrikethrough()
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if s.UlStyle != o.UlStyle {
|
||||
b = b.UnderlineStyle(s.UlStyle)
|
||||
}
|
||||
|
||||
return b.String()
|
||||
}
|
||||
|
||||
// Equal returns true if the style is equal to the other style.
|
||||
func (s *Style) Equal(o *Style) bool {
|
||||
return s.Attrs == o.Attrs &&
|
||||
s.UlStyle == o.UlStyle &&
|
||||
colorEqual(s.Fg, o.Fg) &&
|
||||
colorEqual(s.Bg, o.Bg) &&
|
||||
colorEqual(s.Ul, o.Ul)
|
||||
}
|
||||
|
||||
func colorEqual(c, o ansi.Color) bool {
|
||||
if c == nil && o == nil {
|
||||
return true
|
||||
}
|
||||
if c == nil || o == nil {
|
||||
return false
|
||||
}
|
||||
cr, cg, cb, ca := c.RGBA()
|
||||
or, og, ob, oa := o.RGBA()
|
||||
return cr == or && cg == og && cb == ob && ca == oa
|
||||
}
|
||||
|
||||
// Bold sets the bold attribute.
|
||||
func (s *Style) Bold(v bool) *Style {
|
||||
if v {
|
||||
s.Attrs |= BoldAttr
|
||||
} else {
|
||||
s.Attrs &^= BoldAttr
|
||||
}
|
||||
return s
|
||||
}
|
||||
|
||||
// Faint sets the faint attribute.
|
||||
func (s *Style) Faint(v bool) *Style {
|
||||
if v {
|
||||
s.Attrs |= FaintAttr
|
||||
} else {
|
||||
s.Attrs &^= FaintAttr
|
||||
}
|
||||
return s
|
||||
}
|
||||
|
||||
// Italic sets the italic attribute.
|
||||
func (s *Style) Italic(v bool) *Style {
|
||||
if v {
|
||||
s.Attrs |= ItalicAttr
|
||||
} else {
|
||||
s.Attrs &^= ItalicAttr
|
||||
}
|
||||
return s
|
||||
}
|
||||
|
||||
// SlowBlink sets the slow blink attribute.
|
||||
func (s *Style) SlowBlink(v bool) *Style {
|
||||
if v {
|
||||
s.Attrs |= SlowBlinkAttr
|
||||
} else {
|
||||
s.Attrs &^= SlowBlinkAttr
|
||||
}
|
||||
return s
|
||||
}
|
||||
|
||||
// RapidBlink sets the rapid blink attribute.
|
||||
func (s *Style) RapidBlink(v bool) *Style {
|
||||
if v {
|
||||
s.Attrs |= RapidBlinkAttr
|
||||
} else {
|
||||
s.Attrs &^= RapidBlinkAttr
|
||||
}
|
||||
return s
|
||||
}
|
||||
|
||||
// Reverse sets the reverse attribute.
|
||||
func (s *Style) Reverse(v bool) *Style {
|
||||
if v {
|
||||
s.Attrs |= ReverseAttr
|
||||
} else {
|
||||
s.Attrs &^= ReverseAttr
|
||||
}
|
||||
return s
|
||||
}
|
||||
|
||||
// Conceal sets the conceal attribute.
|
||||
func (s *Style) Conceal(v bool) *Style {
|
||||
if v {
|
||||
s.Attrs |= ConcealAttr
|
||||
} else {
|
||||
s.Attrs &^= ConcealAttr
|
||||
}
|
||||
return s
|
||||
}
|
||||
|
||||
// Strikethrough sets the strikethrough attribute.
|
||||
func (s *Style) Strikethrough(v bool) *Style {
|
||||
if v {
|
||||
s.Attrs |= StrikethroughAttr
|
||||
} else {
|
||||
s.Attrs &^= StrikethroughAttr
|
||||
}
|
||||
return s
|
||||
}
|
||||
|
||||
// UnderlineStyle sets the underline style.
|
||||
func (s *Style) UnderlineStyle(style UnderlineStyle) *Style {
|
||||
s.UlStyle = style
|
||||
return s
|
||||
}
|
||||
|
||||
// Underline sets the underline attribute.
|
||||
// This is a syntactic sugar for [UnderlineStyle].
|
||||
func (s *Style) Underline(v bool) *Style {
|
||||
if v {
|
||||
return s.UnderlineStyle(SingleUnderline)
|
||||
}
|
||||
return s.UnderlineStyle(NoUnderline)
|
||||
}
|
||||
|
||||
// Foreground sets the foreground color.
|
||||
func (s *Style) Foreground(c ansi.Color) *Style {
|
||||
s.Fg = c
|
||||
return s
|
||||
}
|
||||
|
||||
// Background sets the background color.
|
||||
func (s *Style) Background(c ansi.Color) *Style {
|
||||
s.Bg = c
|
||||
return s
|
||||
}
|
||||
|
||||
// UnderlineColor sets the underline color.
|
||||
func (s *Style) UnderlineColor(c ansi.Color) *Style {
|
||||
s.Ul = c
|
||||
return s
|
||||
}
|
||||
|
||||
// Reset resets the style to default.
|
||||
func (s *Style) Reset() *Style {
|
||||
s.Fg = nil
|
||||
s.Bg = nil
|
||||
s.Ul = nil
|
||||
s.Attrs = ResetAttr
|
||||
s.UlStyle = NoUnderline
|
||||
return s
|
||||
}
|
||||
|
||||
// Empty returns true if the style is empty.
|
||||
func (s *Style) Empty() bool {
|
||||
return s.Fg == nil && s.Bg == nil && s.Ul == nil && s.Attrs == ResetAttr && s.UlStyle == NoUnderline
|
||||
}
|
||||
|
||||
// Clear returns whether the style consists of only attributes that don't
|
||||
// affect appearance of a space character.
|
||||
func (s *Style) Clear() bool {
|
||||
return s.UlStyle == NoUnderline &&
|
||||
s.Attrs&^(BoldAttr|FaintAttr|ItalicAttr|SlowBlinkAttr|RapidBlinkAttr) == 0 &&
|
||||
s.Fg == nil &&
|
||||
s.Bg == nil &&
|
||||
s.Ul == nil
|
||||
}
|
||||
|
||||
func runesEqual(a, b []rune) bool {
|
||||
if len(a) != len(b) {
|
||||
return false
|
||||
}
|
||||
for i, r := range a {
|
||||
if r != b[i] {
|
||||
return false
|
||||
}
|
||||
}
|
||||
return true
|
||||
}
|
||||
6
vendor/github.com/charmbracelet/x/cellbuf/errors.go
generated
vendored
Normal file
6
vendor/github.com/charmbracelet/x/cellbuf/errors.go
generated
vendored
Normal file
|
|
@ -0,0 +1,6 @@
|
|||
package cellbuf
|
||||
|
||||
import "errors"
|
||||
|
||||
// ErrOutOfBounds is returned when the given x, y position is out of bounds.
|
||||
var ErrOutOfBounds = errors.New("out of bounds")
|
||||
21
vendor/github.com/charmbracelet/x/cellbuf/geom.go
generated
vendored
Normal file
21
vendor/github.com/charmbracelet/x/cellbuf/geom.go
generated
vendored
Normal file
|
|
@ -0,0 +1,21 @@
|
|||
package cellbuf
|
||||
|
||||
import (
|
||||
"image"
|
||||
)
|
||||
|
||||
// Position represents an x, y position.
|
||||
type Position = image.Point
|
||||
|
||||
// Pos is a shorthand for Position{X: x, Y: y}.
|
||||
func Pos(x, y int) Position {
|
||||
return image.Pt(x, y)
|
||||
}
|
||||
|
||||
// Rectange represents a rectangle.
|
||||
type Rectangle = image.Rectangle
|
||||
|
||||
// Rect is a shorthand for Rectangle.
|
||||
func Rect(x, y, w, h int) Rectangle {
|
||||
return image.Rect(x, y, x+w, y+h)
|
||||
}
|
||||
272
vendor/github.com/charmbracelet/x/cellbuf/hardscroll.go
generated
vendored
Normal file
272
vendor/github.com/charmbracelet/x/cellbuf/hardscroll.go
generated
vendored
Normal file
|
|
@ -0,0 +1,272 @@
|
|||
package cellbuf
|
||||
|
||||
import (
|
||||
"strings"
|
||||
|
||||
"github.com/charmbracelet/x/ansi"
|
||||
)
|
||||
|
||||
// scrollOptimize optimizes the screen to transform the old buffer into the new
|
||||
// buffer.
|
||||
func (s *Screen) scrollOptimize() {
|
||||
height := s.newbuf.Height()
|
||||
if s.oldnum == nil || len(s.oldnum) < height {
|
||||
s.oldnum = make([]int, height)
|
||||
}
|
||||
|
||||
// Calculate the indices
|
||||
s.updateHashmap()
|
||||
if len(s.hashtab) < height {
|
||||
return
|
||||
}
|
||||
|
||||
// Pass 1 - from top to bottom scrolling up
|
||||
for i := 0; i < height; {
|
||||
for i < height && (s.oldnum[i] == newIndex || s.oldnum[i] <= i) {
|
||||
i++
|
||||
}
|
||||
if i >= height {
|
||||
break
|
||||
}
|
||||
|
||||
shift := s.oldnum[i] - i // shift > 0
|
||||
start := i
|
||||
|
||||
i++
|
||||
for i < height && s.oldnum[i] != newIndex && s.oldnum[i]-i == shift {
|
||||
i++
|
||||
}
|
||||
end := i - 1 + shift
|
||||
|
||||
if !s.scrolln(shift, start, end, height-1) {
|
||||
continue
|
||||
}
|
||||
}
|
||||
|
||||
// Pass 2 - from bottom to top scrolling down
|
||||
for i := height - 1; i >= 0; {
|
||||
for i >= 0 && (s.oldnum[i] == newIndex || s.oldnum[i] >= i) {
|
||||
i--
|
||||
}
|
||||
if i < 0 {
|
||||
break
|
||||
}
|
||||
|
||||
shift := s.oldnum[i] - i // shift < 0
|
||||
end := i
|
||||
|
||||
i--
|
||||
for i >= 0 && s.oldnum[i] != newIndex && s.oldnum[i]-i == shift {
|
||||
i--
|
||||
}
|
||||
|
||||
start := i + 1 - (-shift)
|
||||
if !s.scrolln(shift, start, end, height-1) {
|
||||
continue
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// scrolln scrolls the screen up by n lines.
|
||||
func (s *Screen) scrolln(n, top, bot, maxY int) (v bool) { //nolint:unparam
|
||||
const (
|
||||
nonDestScrollRegion = false
|
||||
memoryBelow = false
|
||||
)
|
||||
|
||||
blank := s.clearBlank()
|
||||
if n > 0 {
|
||||
// Scroll up (forward)
|
||||
v = s.scrollUp(n, top, bot, 0, maxY, blank)
|
||||
if !v {
|
||||
s.buf.WriteString(ansi.SetTopBottomMargins(top+1, bot+1))
|
||||
|
||||
// XXX: How should we handle this in inline mode when not using alternate screen?
|
||||
s.cur.X, s.cur.Y = -1, -1
|
||||
v = s.scrollUp(n, top, bot, top, bot, blank)
|
||||
s.buf.WriteString(ansi.SetTopBottomMargins(1, maxY+1))
|
||||
s.cur.X, s.cur.Y = -1, -1
|
||||
}
|
||||
|
||||
if !v {
|
||||
v = s.scrollIdl(n, top, bot-n+1, blank)
|
||||
}
|
||||
|
||||
// Clear newly shifted-in lines.
|
||||
if v &&
|
||||
(nonDestScrollRegion || (memoryBelow && bot == maxY)) {
|
||||
if bot == maxY {
|
||||
s.move(0, bot-n+1)
|
||||
s.clearToBottom(nil)
|
||||
} else {
|
||||
for i := 0; i < n; i++ {
|
||||
s.move(0, bot-i)
|
||||
s.clearToEnd(nil, false)
|
||||
}
|
||||
}
|
||||
}
|
||||
} else if n < 0 {
|
||||
// Scroll down (backward)
|
||||
v = s.scrollDown(-n, top, bot, 0, maxY, blank)
|
||||
if !v {
|
||||
s.buf.WriteString(ansi.SetTopBottomMargins(top+1, bot+1))
|
||||
|
||||
// XXX: How should we handle this in inline mode when not using alternate screen?
|
||||
s.cur.X, s.cur.Y = -1, -1
|
||||
v = s.scrollDown(-n, top, bot, top, bot, blank)
|
||||
s.buf.WriteString(ansi.SetTopBottomMargins(1, maxY+1))
|
||||
s.cur.X, s.cur.Y = -1, -1
|
||||
|
||||
if !v {
|
||||
v = s.scrollIdl(-n, bot+n+1, top, blank)
|
||||
}
|
||||
|
||||
// Clear newly shifted-in lines.
|
||||
if v &&
|
||||
(nonDestScrollRegion || (memoryBelow && top == 0)) {
|
||||
for i := 0; i < -n; i++ {
|
||||
s.move(0, top+i)
|
||||
s.clearToEnd(nil, false)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if !v {
|
||||
return
|
||||
}
|
||||
|
||||
s.scrollBuffer(s.curbuf, n, top, bot, blank)
|
||||
|
||||
// shift hash values too, they can be reused
|
||||
s.scrollOldhash(n, top, bot)
|
||||
|
||||
return true
|
||||
}
|
||||
|
||||
// scrollBuffer scrolls the buffer by n lines.
|
||||
func (s *Screen) scrollBuffer(b *Buffer, n, top, bot int, blank *Cell) {
|
||||
if top < 0 || bot < top || bot >= b.Height() {
|
||||
// Nothing to scroll
|
||||
return
|
||||
}
|
||||
|
||||
if n < 0 {
|
||||
// shift n lines downwards
|
||||
limit := top - n
|
||||
for line := bot; line >= limit && line >= 0 && line >= top; line-- {
|
||||
copy(b.Lines[line], b.Lines[line+n])
|
||||
}
|
||||
for line := top; line < limit && line <= b.Height()-1 && line <= bot; line++ {
|
||||
b.FillRect(blank, Rect(0, line, b.Width(), 1))
|
||||
}
|
||||
}
|
||||
|
||||
if n > 0 {
|
||||
// shift n lines upwards
|
||||
limit := bot - n
|
||||
for line := top; line <= limit && line <= b.Height()-1 && line <= bot; line++ {
|
||||
copy(b.Lines[line], b.Lines[line+n])
|
||||
}
|
||||
for line := bot; line > limit && line >= 0 && line >= top; line-- {
|
||||
b.FillRect(blank, Rect(0, line, b.Width(), 1))
|
||||
}
|
||||
}
|
||||
|
||||
s.touchLine(b.Width(), b.Height(), top, bot-top+1, true)
|
||||
}
|
||||
|
||||
// touchLine marks the line as touched.
|
||||
func (s *Screen) touchLine(width, height, y, n int, changed bool) {
|
||||
if n < 0 || y < 0 || y >= height {
|
||||
return // Nothing to touch
|
||||
}
|
||||
|
||||
for i := y; i < y+n && i < height; i++ {
|
||||
if changed {
|
||||
s.touch[i] = lineData{firstCell: 0, lastCell: width - 1}
|
||||
} else {
|
||||
delete(s.touch, i)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// scrollUp scrolls the screen up by n lines.
|
||||
func (s *Screen) scrollUp(n, top, bot, minY, maxY int, blank *Cell) bool {
|
||||
if n == 1 && top == minY && bot == maxY {
|
||||
s.move(0, bot)
|
||||
s.updatePen(blank)
|
||||
s.buf.WriteByte('\n')
|
||||
} else if n == 1 && bot == maxY {
|
||||
s.move(0, top)
|
||||
s.updatePen(blank)
|
||||
s.buf.WriteString(ansi.DeleteLine(1))
|
||||
} else if top == minY && bot == maxY {
|
||||
if s.xtermLike {
|
||||
s.move(0, bot)
|
||||
} else {
|
||||
s.move(0, top)
|
||||
}
|
||||
s.updatePen(blank)
|
||||
if s.xtermLike {
|
||||
s.buf.WriteString(ansi.ScrollUp(n))
|
||||
} else {
|
||||
s.buf.WriteString(strings.Repeat("\n", n))
|
||||
}
|
||||
} else if bot == maxY {
|
||||
s.move(0, top)
|
||||
s.updatePen(blank)
|
||||
s.buf.WriteString(ansi.DeleteLine(n))
|
||||
} else {
|
||||
return false
|
||||
}
|
||||
return true
|
||||
}
|
||||
|
||||
// scrollDown scrolls the screen down by n lines.
|
||||
func (s *Screen) scrollDown(n, top, bot, minY, maxY int, blank *Cell) bool {
|
||||
if n == 1 && top == minY && bot == maxY {
|
||||
s.move(0, top)
|
||||
s.updatePen(blank)
|
||||
s.buf.WriteString(ansi.ReverseIndex)
|
||||
} else if n == 1 && bot == maxY {
|
||||
s.move(0, top)
|
||||
s.updatePen(blank)
|
||||
s.buf.WriteString(ansi.InsertLine(1))
|
||||
} else if top == minY && bot == maxY {
|
||||
s.move(0, top)
|
||||
s.updatePen(blank)
|
||||
if s.xtermLike {
|
||||
s.buf.WriteString(ansi.ScrollDown(n))
|
||||
} else {
|
||||
s.buf.WriteString(strings.Repeat(ansi.ReverseIndex, n))
|
||||
}
|
||||
} else if bot == maxY {
|
||||
s.move(0, top)
|
||||
s.updatePen(blank)
|
||||
s.buf.WriteString(ansi.InsertLine(n))
|
||||
} else {
|
||||
return false
|
||||
}
|
||||
return true
|
||||
}
|
||||
|
||||
// scrollIdl scrolls the screen n lines by using [ansi.DL] at del and using
|
||||
// [ansi.IL] at ins.
|
||||
func (s *Screen) scrollIdl(n, del, ins int, blank *Cell) bool {
|
||||
if n < 0 {
|
||||
return false
|
||||
}
|
||||
|
||||
// Delete lines
|
||||
s.move(0, del)
|
||||
s.updatePen(blank)
|
||||
s.buf.WriteString(ansi.DeleteLine(n))
|
||||
|
||||
// Insert lines
|
||||
s.move(0, ins)
|
||||
s.updatePen(blank)
|
||||
s.buf.WriteString(ansi.InsertLine(n))
|
||||
|
||||
return true
|
||||
}
|
||||
301
vendor/github.com/charmbracelet/x/cellbuf/hashmap.go
generated
vendored
Normal file
301
vendor/github.com/charmbracelet/x/cellbuf/hashmap.go
generated
vendored
Normal file
|
|
@ -0,0 +1,301 @@
|
|||
package cellbuf
|
||||
|
||||
import (
|
||||
"github.com/charmbracelet/x/ansi"
|
||||
)
|
||||
|
||||
// hash returns the hash value of a [Line].
|
||||
func hash(l Line) (h uint64) {
|
||||
for _, c := range l {
|
||||
var r rune
|
||||
if c == nil {
|
||||
r = ansi.SP
|
||||
} else {
|
||||
r = c.Rune
|
||||
}
|
||||
h += (h << 5) + uint64(r)
|
||||
}
|
||||
return
|
||||
}
|
||||
|
||||
// hashmap represents a single [Line] hash.
|
||||
type hashmap struct {
|
||||
value uint64
|
||||
oldcount, newcount int
|
||||
oldindex, newindex int
|
||||
}
|
||||
|
||||
// The value used to indicate lines created by insertions and scrolls.
|
||||
const newIndex = -1
|
||||
|
||||
// updateHashmap updates the hashmap with the new hash value.
|
||||
func (s *Screen) updateHashmap() {
|
||||
height := s.newbuf.Height()
|
||||
if len(s.oldhash) >= height && len(s.newhash) >= height {
|
||||
// rehash changed lines
|
||||
for i := 0; i < height; i++ {
|
||||
_, ok := s.touch[i]
|
||||
if ok {
|
||||
s.oldhash[i] = hash(s.curbuf.Line(i))
|
||||
s.newhash[i] = hash(s.newbuf.Line(i))
|
||||
}
|
||||
}
|
||||
} else {
|
||||
// rehash all
|
||||
if len(s.oldhash) != height {
|
||||
s.oldhash = make([]uint64, height)
|
||||
}
|
||||
if len(s.newhash) != height {
|
||||
s.newhash = make([]uint64, height)
|
||||
}
|
||||
for i := 0; i < height; i++ {
|
||||
s.oldhash[i] = hash(s.curbuf.Line(i))
|
||||
s.newhash[i] = hash(s.newbuf.Line(i))
|
||||
}
|
||||
}
|
||||
|
||||
s.hashtab = make([]hashmap, height*2)
|
||||
for i := 0; i < height; i++ {
|
||||
hashval := s.oldhash[i]
|
||||
|
||||
// Find matching hash or empty slot
|
||||
idx := 0
|
||||
for idx < len(s.hashtab) && s.hashtab[idx].value != 0 {
|
||||
if s.hashtab[idx].value == hashval {
|
||||
break
|
||||
}
|
||||
idx++
|
||||
}
|
||||
|
||||
s.hashtab[idx].value = hashval // in case this is a new hash
|
||||
s.hashtab[idx].oldcount++
|
||||
s.hashtab[idx].oldindex = i
|
||||
}
|
||||
for i := 0; i < height; i++ {
|
||||
hashval := s.newhash[i]
|
||||
|
||||
// Find matching hash or empty slot
|
||||
idx := 0
|
||||
for idx < len(s.hashtab) && s.hashtab[idx].value != 0 {
|
||||
if s.hashtab[idx].value == hashval {
|
||||
break
|
||||
}
|
||||
idx++
|
||||
}
|
||||
|
||||
s.hashtab[idx].value = hashval // in case this is a new hash
|
||||
s.hashtab[idx].newcount++
|
||||
s.hashtab[idx].newindex = i
|
||||
|
||||
s.oldnum[i] = newIndex // init old indices slice
|
||||
}
|
||||
|
||||
// Mark line pair corresponding to unique hash pairs.
|
||||
for i := 0; i < len(s.hashtab) && s.hashtab[i].value != 0; i++ {
|
||||
hsp := &s.hashtab[i]
|
||||
if hsp.oldcount == 1 && hsp.newcount == 1 && hsp.oldindex != hsp.newindex {
|
||||
s.oldnum[hsp.newindex] = hsp.oldindex
|
||||
}
|
||||
}
|
||||
|
||||
s.growHunks()
|
||||
|
||||
// Eliminate bad or impossible shifts. This includes removing those hunks
|
||||
// which could not grow because of conflicts, as well those which are to be
|
||||
// moved too far, they are likely to destroy more than carry.
|
||||
for i := 0; i < height; {
|
||||
var start, shift, size int
|
||||
for i < height && s.oldnum[i] == newIndex {
|
||||
i++
|
||||
}
|
||||
if i >= height {
|
||||
break
|
||||
}
|
||||
start = i
|
||||
shift = s.oldnum[i] - i
|
||||
i++
|
||||
for i < height && s.oldnum[i] != newIndex && s.oldnum[i]-i == shift {
|
||||
i++
|
||||
}
|
||||
size = i - start
|
||||
if size < 3 || size+min(size/8, 2) < abs(shift) {
|
||||
for start < i {
|
||||
s.oldnum[start] = newIndex
|
||||
start++
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// After clearing invalid hunks, try grow the rest.
|
||||
s.growHunks()
|
||||
}
|
||||
|
||||
// scrollOldhash
|
||||
func (s *Screen) scrollOldhash(n, top, bot int) {
|
||||
if len(s.oldhash) == 0 {
|
||||
return
|
||||
}
|
||||
|
||||
size := bot - top + 1 - abs(n)
|
||||
if n > 0 {
|
||||
// Move existing hashes up
|
||||
copy(s.oldhash[top:], s.oldhash[top+n:top+n+size])
|
||||
// Recalculate hashes for newly shifted-in lines
|
||||
for i := bot; i > bot-n; i-- {
|
||||
s.oldhash[i] = hash(s.curbuf.Line(i))
|
||||
}
|
||||
} else {
|
||||
// Move existing hashes down
|
||||
copy(s.oldhash[top-n:], s.oldhash[top:top+size])
|
||||
// Recalculate hashes for newly shifted-in lines
|
||||
for i := top; i < top-n; i++ {
|
||||
s.oldhash[i] = hash(s.curbuf.Line(i))
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
func (s *Screen) growHunks() {
|
||||
var (
|
||||
backLimit int // limits for cells to fill
|
||||
backRefLimit int // limit for references
|
||||
i int
|
||||
nextHunk int
|
||||
)
|
||||
|
||||
height := s.newbuf.Height()
|
||||
for i < height && s.oldnum[i] == newIndex {
|
||||
i++
|
||||
}
|
||||
for ; i < height; i = nextHunk {
|
||||
var (
|
||||
forwardLimit int
|
||||
forwardRefLimit int
|
||||
end int
|
||||
start = i
|
||||
shift = s.oldnum[i] - i
|
||||
)
|
||||
|
||||
// get forward limit
|
||||
i = start + 1
|
||||
for i < height &&
|
||||
s.oldnum[i] != newIndex &&
|
||||
s.oldnum[i]-i == shift {
|
||||
i++
|
||||
}
|
||||
|
||||
end = i
|
||||
for i < height && s.oldnum[i] == newIndex {
|
||||
i++
|
||||
}
|
||||
|
||||
nextHunk = i
|
||||
forwardLimit = i
|
||||
if i >= height || s.oldnum[i] >= i {
|
||||
forwardRefLimit = i
|
||||
} else {
|
||||
forwardRefLimit = s.oldnum[i]
|
||||
}
|
||||
|
||||
i = start - 1
|
||||
|
||||
// grow back
|
||||
if shift < 0 {
|
||||
backLimit = backRefLimit + (-shift)
|
||||
}
|
||||
for i >= backLimit {
|
||||
if s.newhash[i] == s.oldhash[i+shift] ||
|
||||
s.costEffective(i+shift, i, shift < 0) {
|
||||
s.oldnum[i] = i + shift
|
||||
} else {
|
||||
break
|
||||
}
|
||||
i--
|
||||
}
|
||||
|
||||
i = end
|
||||
// grow forward
|
||||
if shift > 0 {
|
||||
forwardLimit = forwardRefLimit - shift
|
||||
}
|
||||
for i < forwardLimit {
|
||||
if s.newhash[i] == s.oldhash[i+shift] ||
|
||||
s.costEffective(i+shift, i, shift > 0) {
|
||||
s.oldnum[i] = i + shift
|
||||
} else {
|
||||
break
|
||||
}
|
||||
i++
|
||||
}
|
||||
|
||||
backLimit = i
|
||||
backRefLimit = backLimit
|
||||
if shift > 0 {
|
||||
backRefLimit += shift
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// costEffective returns true if the cost of moving line 'from' to line 'to' seems to be
|
||||
// cost effective. 'blank' indicates whether the line 'to' would become blank.
|
||||
func (s *Screen) costEffective(from, to int, blank bool) bool {
|
||||
if from == to {
|
||||
return false
|
||||
}
|
||||
|
||||
newFrom := s.oldnum[from]
|
||||
if newFrom == newIndex {
|
||||
newFrom = from
|
||||
}
|
||||
|
||||
// On the left side of >= is the cost before moving. On the right side --
|
||||
// cost after moving.
|
||||
|
||||
// Calculate costs before moving.
|
||||
var costBeforeMove int
|
||||
if blank {
|
||||
// Cost of updating blank line at destination.
|
||||
costBeforeMove = s.updateCostBlank(s.newbuf.Line(to))
|
||||
} else {
|
||||
// Cost of updating exiting line at destination.
|
||||
costBeforeMove = s.updateCost(s.curbuf.Line(to), s.newbuf.Line(to))
|
||||
}
|
||||
|
||||
// Add cost of updating source line
|
||||
costBeforeMove += s.updateCost(s.curbuf.Line(newFrom), s.newbuf.Line(from))
|
||||
|
||||
// Calculate costs after moving.
|
||||
var costAfterMove int
|
||||
if newFrom == from {
|
||||
// Source becomes blank after move
|
||||
costAfterMove = s.updateCostBlank(s.newbuf.Line(from))
|
||||
} else {
|
||||
// Source gets updated from another line
|
||||
costAfterMove = s.updateCost(s.curbuf.Line(newFrom), s.newbuf.Line(from))
|
||||
}
|
||||
|
||||
// Add cost of moving source line to destination
|
||||
costAfterMove += s.updateCost(s.curbuf.Line(from), s.newbuf.Line(to))
|
||||
|
||||
// Return true if moving is cost effective (costs less or equal)
|
||||
return costBeforeMove >= costAfterMove
|
||||
}
|
||||
|
||||
func (s *Screen) updateCost(from, to Line) (cost int) {
|
||||
var fidx, tidx int
|
||||
for i := s.newbuf.Width() - 1; i > 0; i, fidx, tidx = i-1, fidx+1, tidx+1 {
|
||||
if !cellEqual(from.At(fidx), to.At(tidx)) {
|
||||
cost++
|
||||
}
|
||||
}
|
||||
return
|
||||
}
|
||||
|
||||
func (s *Screen) updateCostBlank(to Line) (cost int) {
|
||||
var tidx int
|
||||
for i := s.newbuf.Width() - 1; i > 0; i, tidx = i-1, tidx+1 {
|
||||
if !cellEqual(nil, to.At(tidx)) {
|
||||
cost++
|
||||
}
|
||||
}
|
||||
return
|
||||
}
|
||||
14
vendor/github.com/charmbracelet/x/cellbuf/link.go
generated
vendored
Normal file
14
vendor/github.com/charmbracelet/x/cellbuf/link.go
generated
vendored
Normal file
|
|
@ -0,0 +1,14 @@
|
|||
package cellbuf
|
||||
|
||||
import (
|
||||
"github.com/charmbracelet/colorprofile"
|
||||
)
|
||||
|
||||
// Convert converts a hyperlink to respect the given color profile.
|
||||
func ConvertLink(h Link, p colorprofile.Profile) Link {
|
||||
if p == colorprofile.NoTTY {
|
||||
return Link{}
|
||||
}
|
||||
|
||||
return h
|
||||
}
|
||||
1457
vendor/github.com/charmbracelet/x/cellbuf/screen.go
generated
vendored
Normal file
1457
vendor/github.com/charmbracelet/x/cellbuf/screen.go
generated
vendored
Normal file
File diff suppressed because it is too large
Load diff
131
vendor/github.com/charmbracelet/x/cellbuf/sequence.go
generated
vendored
Normal file
131
vendor/github.com/charmbracelet/x/cellbuf/sequence.go
generated
vendored
Normal file
|
|
@ -0,0 +1,131 @@
|
|||
package cellbuf
|
||||
|
||||
import (
|
||||
"bytes"
|
||||
"image/color"
|
||||
|
||||
"github.com/charmbracelet/x/ansi"
|
||||
)
|
||||
|
||||
// ReadStyle reads a Select Graphic Rendition (SGR) escape sequences from a
|
||||
// list of parameters.
|
||||
func ReadStyle(params ansi.Params, pen *Style) {
|
||||
if len(params) == 0 {
|
||||
pen.Reset()
|
||||
return
|
||||
}
|
||||
|
||||
for i := 0; i < len(params); i++ {
|
||||
param, hasMore, _ := params.Param(i, 0)
|
||||
switch param {
|
||||
case 0: // Reset
|
||||
pen.Reset()
|
||||
case 1: // Bold
|
||||
pen.Bold(true)
|
||||
case 2: // Dim/Faint
|
||||
pen.Faint(true)
|
||||
case 3: // Italic
|
||||
pen.Italic(true)
|
||||
case 4: // Underline
|
||||
nextParam, _, ok := params.Param(i+1, 0)
|
||||
if hasMore && ok { // Only accept subparameters i.e. separated by ":"
|
||||
switch nextParam {
|
||||
case 0, 1, 2, 3, 4, 5:
|
||||
i++
|
||||
switch nextParam {
|
||||
case 0: // No Underline
|
||||
pen.UnderlineStyle(NoUnderline)
|
||||
case 1: // Single Underline
|
||||
pen.UnderlineStyle(SingleUnderline)
|
||||
case 2: // Double Underline
|
||||
pen.UnderlineStyle(DoubleUnderline)
|
||||
case 3: // Curly Underline
|
||||
pen.UnderlineStyle(CurlyUnderline)
|
||||
case 4: // Dotted Underline
|
||||
pen.UnderlineStyle(DottedUnderline)
|
||||
case 5: // Dashed Underline
|
||||
pen.UnderlineStyle(DashedUnderline)
|
||||
}
|
||||
}
|
||||
} else {
|
||||
// Single Underline
|
||||
pen.Underline(true)
|
||||
}
|
||||
case 5: // Slow Blink
|
||||
pen.SlowBlink(true)
|
||||
case 6: // Rapid Blink
|
||||
pen.RapidBlink(true)
|
||||
case 7: // Reverse
|
||||
pen.Reverse(true)
|
||||
case 8: // Conceal
|
||||
pen.Conceal(true)
|
||||
case 9: // Crossed-out/Strikethrough
|
||||
pen.Strikethrough(true)
|
||||
case 22: // Normal Intensity (not bold or faint)
|
||||
pen.Bold(false).Faint(false)
|
||||
case 23: // Not italic, not Fraktur
|
||||
pen.Italic(false)
|
||||
case 24: // Not underlined
|
||||
pen.Underline(false)
|
||||
case 25: // Blink off
|
||||
pen.SlowBlink(false).RapidBlink(false)
|
||||
case 27: // Positive (not reverse)
|
||||
pen.Reverse(false)
|
||||
case 28: // Reveal
|
||||
pen.Conceal(false)
|
||||
case 29: // Not crossed out
|
||||
pen.Strikethrough(false)
|
||||
case 30, 31, 32, 33, 34, 35, 36, 37: // Set foreground
|
||||
pen.Foreground(ansi.Black + ansi.BasicColor(param-30)) //nolint:gosec
|
||||
case 38: // Set foreground 256 or truecolor
|
||||
var c color.Color
|
||||
n := ReadStyleColor(params[i:], &c)
|
||||
if n > 0 {
|
||||
pen.Foreground(c)
|
||||
i += n - 1
|
||||
}
|
||||
case 39: // Default foreground
|
||||
pen.Foreground(nil)
|
||||
case 40, 41, 42, 43, 44, 45, 46, 47: // Set background
|
||||
pen.Background(ansi.Black + ansi.BasicColor(param-40)) //nolint:gosec
|
||||
case 48: // Set background 256 or truecolor
|
||||
var c color.Color
|
||||
n := ReadStyleColor(params[i:], &c)
|
||||
if n > 0 {
|
||||
pen.Background(c)
|
||||
i += n - 1
|
||||
}
|
||||
case 49: // Default Background
|
||||
pen.Background(nil)
|
||||
case 58: // Set underline color
|
||||
var c color.Color
|
||||
n := ReadStyleColor(params[i:], &c)
|
||||
if n > 0 {
|
||||
pen.UnderlineColor(c)
|
||||
i += n - 1
|
||||
}
|
||||
case 59: // Default underline color
|
||||
pen.UnderlineColor(nil)
|
||||
case 90, 91, 92, 93, 94, 95, 96, 97: // Set bright foreground
|
||||
pen.Foreground(ansi.BrightBlack + ansi.BasicColor(param-90)) //nolint:gosec
|
||||
case 100, 101, 102, 103, 104, 105, 106, 107: // Set bright background
|
||||
pen.Background(ansi.BrightBlack + ansi.BasicColor(param-100)) //nolint:gosec
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// ReadLink reads a hyperlink escape sequence from a data buffer.
|
||||
func ReadLink(p []byte, link *Link) {
|
||||
params := bytes.Split(p, []byte{';'})
|
||||
if len(params) != 3 {
|
||||
return
|
||||
}
|
||||
link.Params = string(params[1])
|
||||
link.URL = string(params[2])
|
||||
}
|
||||
|
||||
// ReadStyleColor reads a color from a list of parameters.
|
||||
// See [ansi.ReadStyleColor] for more information.
|
||||
func ReadStyleColor(params ansi.Params, c *color.Color) int {
|
||||
return ansi.ReadStyleColor(params, c)
|
||||
}
|
||||
31
vendor/github.com/charmbracelet/x/cellbuf/style.go
generated
vendored
Normal file
31
vendor/github.com/charmbracelet/x/cellbuf/style.go
generated
vendored
Normal file
|
|
@ -0,0 +1,31 @@
|
|||
package cellbuf
|
||||
|
||||
import (
|
||||
"github.com/charmbracelet/colorprofile"
|
||||
)
|
||||
|
||||
// Convert converts a style to respect the given color profile.
|
||||
func ConvertStyle(s Style, p colorprofile.Profile) Style {
|
||||
switch p {
|
||||
case colorprofile.TrueColor:
|
||||
return s
|
||||
case colorprofile.Ascii:
|
||||
s.Fg = nil
|
||||
s.Bg = nil
|
||||
s.Ul = nil
|
||||
case colorprofile.NoTTY:
|
||||
return Style{}
|
||||
}
|
||||
|
||||
if s.Fg != nil {
|
||||
s.Fg = p.Convert(s.Fg)
|
||||
}
|
||||
if s.Bg != nil {
|
||||
s.Bg = p.Convert(s.Bg)
|
||||
}
|
||||
if s.Ul != nil {
|
||||
s.Ul = p.Convert(s.Ul)
|
||||
}
|
||||
|
||||
return s
|
||||
}
|
||||
137
vendor/github.com/charmbracelet/x/cellbuf/tabstop.go
generated
vendored
Normal file
137
vendor/github.com/charmbracelet/x/cellbuf/tabstop.go
generated
vendored
Normal file
|
|
@ -0,0 +1,137 @@
|
|||
package cellbuf
|
||||
|
||||
// DefaultTabInterval is the default tab interval.
|
||||
const DefaultTabInterval = 8
|
||||
|
||||
// TabStops represents horizontal line tab stops.
|
||||
type TabStops struct {
|
||||
stops []int
|
||||
interval int
|
||||
width int
|
||||
}
|
||||
|
||||
// NewTabStops creates a new set of tab stops from a number of columns and an
|
||||
// interval.
|
||||
func NewTabStops(width, interval int) *TabStops {
|
||||
ts := new(TabStops)
|
||||
ts.interval = interval
|
||||
ts.width = width
|
||||
ts.stops = make([]int, (width+(interval-1))/interval)
|
||||
ts.init(0, width)
|
||||
return ts
|
||||
}
|
||||
|
||||
// DefaultTabStops creates a new set of tab stops with the default interval.
|
||||
func DefaultTabStops(cols int) *TabStops {
|
||||
return NewTabStops(cols, DefaultTabInterval)
|
||||
}
|
||||
|
||||
// Resize resizes the tab stops to the given width.
|
||||
func (ts *TabStops) Resize(width int) {
|
||||
if width == ts.width {
|
||||
return
|
||||
}
|
||||
|
||||
if width < ts.width {
|
||||
size := (width + (ts.interval - 1)) / ts.interval
|
||||
ts.stops = ts.stops[:size]
|
||||
} else {
|
||||
size := (width - ts.width + (ts.interval - 1)) / ts.interval
|
||||
ts.stops = append(ts.stops, make([]int, size)...)
|
||||
}
|
||||
|
||||
ts.init(ts.width, width)
|
||||
ts.width = width
|
||||
}
|
||||
|
||||
// IsStop returns true if the given column is a tab stop.
|
||||
func (ts TabStops) IsStop(col int) bool {
|
||||
mask := ts.mask(col)
|
||||
i := col >> 3
|
||||
if i < 0 || i >= len(ts.stops) {
|
||||
return false
|
||||
}
|
||||
return ts.stops[i]&mask != 0
|
||||
}
|
||||
|
||||
// Next returns the next tab stop after the given column.
|
||||
func (ts TabStops) Next(col int) int {
|
||||
return ts.Find(col, 1)
|
||||
}
|
||||
|
||||
// Prev returns the previous tab stop before the given column.
|
||||
func (ts TabStops) Prev(col int) int {
|
||||
return ts.Find(col, -1)
|
||||
}
|
||||
|
||||
// Find returns the prev/next tab stop before/after the given column and delta.
|
||||
// If delta is positive, it returns the next tab stop after the given column.
|
||||
// If delta is negative, it returns the previous tab stop before the given column.
|
||||
// If delta is zero, it returns the given column.
|
||||
func (ts TabStops) Find(col, delta int) int {
|
||||
if delta == 0 {
|
||||
return col
|
||||
}
|
||||
|
||||
var prev bool
|
||||
count := delta
|
||||
if count < 0 {
|
||||
count = -count
|
||||
prev = true
|
||||
}
|
||||
|
||||
for count > 0 {
|
||||
if !prev {
|
||||
if col >= ts.width-1 {
|
||||
return col
|
||||
}
|
||||
|
||||
col++
|
||||
} else {
|
||||
if col < 1 {
|
||||
return col
|
||||
}
|
||||
|
||||
col--
|
||||
}
|
||||
|
||||
if ts.IsStop(col) {
|
||||
count--
|
||||
}
|
||||
}
|
||||
|
||||
return col
|
||||
}
|
||||
|
||||
// Set adds a tab stop at the given column.
|
||||
func (ts *TabStops) Set(col int) {
|
||||
mask := ts.mask(col)
|
||||
ts.stops[col>>3] |= mask
|
||||
}
|
||||
|
||||
// Reset removes the tab stop at the given column.
|
||||
func (ts *TabStops) Reset(col int) {
|
||||
mask := ts.mask(col)
|
||||
ts.stops[col>>3] &= ^mask
|
||||
}
|
||||
|
||||
// Clear removes all tab stops.
|
||||
func (ts *TabStops) Clear() {
|
||||
ts.stops = make([]int, len(ts.stops))
|
||||
}
|
||||
|
||||
// mask returns the mask for the given column.
|
||||
func (ts *TabStops) mask(col int) int {
|
||||
return 1 << (col & (ts.interval - 1))
|
||||
}
|
||||
|
||||
// init initializes the tab stops starting from col until width.
|
||||
func (ts *TabStops) init(col, width int) {
|
||||
for x := col; x < width; x++ {
|
||||
if x%ts.interval == 0 {
|
||||
ts.Set(x)
|
||||
} else {
|
||||
ts.Reset(x)
|
||||
}
|
||||
}
|
||||
}
|
||||
38
vendor/github.com/charmbracelet/x/cellbuf/utils.go
generated
vendored
Normal file
38
vendor/github.com/charmbracelet/x/cellbuf/utils.go
generated
vendored
Normal file
|
|
@ -0,0 +1,38 @@
|
|||
package cellbuf
|
||||
|
||||
import (
|
||||
"strings"
|
||||
)
|
||||
|
||||
// Height returns the height of a string.
|
||||
func Height(s string) int {
|
||||
return strings.Count(s, "\n") + 1
|
||||
}
|
||||
|
||||
func min(a, b int) int { //nolint:predeclared
|
||||
if a > b {
|
||||
return b
|
||||
}
|
||||
return a
|
||||
}
|
||||
|
||||
func max(a, b int) int { //nolint:predeclared
|
||||
if a > b {
|
||||
return a
|
||||
}
|
||||
return b
|
||||
}
|
||||
|
||||
func clamp(v, low, high int) int {
|
||||
if high < low {
|
||||
low, high = high, low
|
||||
}
|
||||
return min(high, max(low, v))
|
||||
}
|
||||
|
||||
func abs(a int) int {
|
||||
if a < 0 {
|
||||
return -a
|
||||
}
|
||||
return a
|
||||
}
|
||||
178
vendor/github.com/charmbracelet/x/cellbuf/wrap.go
generated
vendored
Normal file
178
vendor/github.com/charmbracelet/x/cellbuf/wrap.go
generated
vendored
Normal file
|
|
@ -0,0 +1,178 @@
|
|||
package cellbuf
|
||||
|
||||
import (
|
||||
"bytes"
|
||||
"unicode"
|
||||
"unicode/utf8"
|
||||
|
||||
"github.com/charmbracelet/x/ansi"
|
||||
)
|
||||
|
||||
// Wrap returns a string that is wrapped to the specified limit applying any
|
||||
// ANSI escape sequences in the string. It tries to wrap the string at word
|
||||
// boundaries, but will break words if necessary.
|
||||
//
|
||||
// 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 len(s) == 0 {
|
||||
return ""
|
||||
}
|
||||
|
||||
if limit < 1 {
|
||||
return s
|
||||
}
|
||||
|
||||
p := ansi.GetParser()
|
||||
defer ansi.PutParser(p)
|
||||
|
||||
var (
|
||||
buf bytes.Buffer
|
||||
word bytes.Buffer
|
||||
space bytes.Buffer
|
||||
style, curStyle Style
|
||||
link, curLink Link
|
||||
curWidth int
|
||||
wordLen int
|
||||
)
|
||||
|
||||
addSpace := func() {
|
||||
curWidth += space.Len()
|
||||
buf.Write(space.Bytes())
|
||||
space.Reset()
|
||||
}
|
||||
|
||||
addWord := func() {
|
||||
if word.Len() == 0 {
|
||||
return
|
||||
}
|
||||
|
||||
curLink = link
|
||||
curStyle = style
|
||||
|
||||
addSpace()
|
||||
curWidth += wordLen
|
||||
buf.Write(word.Bytes())
|
||||
word.Reset()
|
||||
wordLen = 0
|
||||
}
|
||||
|
||||
addNewline := func() {
|
||||
if !curStyle.Empty() {
|
||||
buf.WriteString(ansi.ResetStyle)
|
||||
}
|
||||
if !curLink.Empty() {
|
||||
buf.WriteString(ansi.ResetHyperlink())
|
||||
}
|
||||
buf.WriteByte('\n')
|
||||
if !curLink.Empty() {
|
||||
buf.WriteString(ansi.SetHyperlink(curLink.URL, curLink.Params))
|
||||
}
|
||||
if !curStyle.Empty() {
|
||||
buf.WriteString(curStyle.Sequence())
|
||||
}
|
||||
curWidth = 0
|
||||
space.Reset()
|
||||
}
|
||||
|
||||
var state byte
|
||||
for len(s) > 0 {
|
||||
seq, width, n, newState := ansi.DecodeSequence(s, state, p)
|
||||
switch width {
|
||||
case 0:
|
||||
if ansi.Equal(seq, "\t") {
|
||||
addWord()
|
||||
space.WriteString(seq)
|
||||
break
|
||||
} else if ansi.Equal(seq, "\n") {
|
||||
if wordLen == 0 {
|
||||
if curWidth+space.Len() > limit {
|
||||
curWidth = 0
|
||||
} else {
|
||||
// preserve whitespaces
|
||||
buf.Write(space.Bytes())
|
||||
}
|
||||
space.Reset()
|
||||
}
|
||||
|
||||
addWord()
|
||||
addNewline()
|
||||
break
|
||||
} else if ansi.HasCsiPrefix(seq) && p.Command() == 'm' {
|
||||
// SGR style sequence [ansi.SGR]
|
||||
ReadStyle(p.Params(), &style)
|
||||
} else if ansi.HasOscPrefix(seq) && p.Command() == 8 {
|
||||
// Hyperlink sequence [ansi.SetHyperlink]
|
||||
ReadLink(p.Data(), &link)
|
||||
}
|
||||
|
||||
word.WriteString(seq)
|
||||
default:
|
||||
if len(seq) == 1 {
|
||||
// ASCII
|
||||
r, _ := utf8.DecodeRuneInString(seq)
|
||||
if unicode.IsSpace(r) {
|
||||
addWord()
|
||||
space.WriteRune(r)
|
||||
break
|
||||
} else if r == '-' || runeContainsAny(r, breakpoints) {
|
||||
addSpace()
|
||||
if curWidth+wordLen+width <= limit {
|
||||
addWord()
|
||||
buf.WriteString(seq)
|
||||
curWidth += width
|
||||
break
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if wordLen+width > limit {
|
||||
// Hardwrap the word if it's too long
|
||||
addWord()
|
||||
}
|
||||
|
||||
word.WriteString(seq)
|
||||
wordLen += width
|
||||
|
||||
if curWidth+wordLen+space.Len() > limit {
|
||||
addNewline()
|
||||
}
|
||||
}
|
||||
|
||||
s = s[n:]
|
||||
state = newState
|
||||
}
|
||||
|
||||
if wordLen == 0 {
|
||||
if curWidth+space.Len() > limit {
|
||||
curWidth = 0
|
||||
} else {
|
||||
// preserve whitespaces
|
||||
buf.Write(space.Bytes())
|
||||
}
|
||||
space.Reset()
|
||||
}
|
||||
|
||||
addWord()
|
||||
|
||||
if !curLink.Empty() {
|
||||
buf.WriteString(ansi.ResetHyperlink())
|
||||
}
|
||||
if !curStyle.Empty() {
|
||||
buf.WriteString(ansi.ResetStyle)
|
||||
}
|
||||
|
||||
return buf.String()
|
||||
}
|
||||
|
||||
func runeContainsAny[T string | []rune](r rune, s T) bool {
|
||||
for _, c := range []rune(s) {
|
||||
if c == r {
|
||||
return true
|
||||
}
|
||||
}
|
||||
return false
|
||||
}
|
||||
339
vendor/github.com/charmbracelet/x/cellbuf/writer.go
generated
vendored
Normal file
339
vendor/github.com/charmbracelet/x/cellbuf/writer.go
generated
vendored
Normal file
|
|
@ -0,0 +1,339 @@
|
|||
package cellbuf
|
||||
|
||||
import (
|
||||
"bytes"
|
||||
"fmt"
|
||||
"strings"
|
||||
|
||||
"github.com/charmbracelet/x/ansi"
|
||||
)
|
||||
|
||||
// CellBuffer is a cell buffer that represents a set of cells in a screen or a
|
||||
// grid.
|
||||
type CellBuffer interface {
|
||||
// Cell returns the cell at the given position.
|
||||
Cell(x, y int) *Cell
|
||||
// SetCell sets the cell at the given position to the given cell. It
|
||||
// returns whether the cell was set successfully.
|
||||
SetCell(x, y int, c *Cell) bool
|
||||
// Bounds returns the bounds of the cell buffer.
|
||||
Bounds() Rectangle
|
||||
}
|
||||
|
||||
// FillRect fills the rectangle within the cell buffer with the given cell.
|
||||
// This will not fill cells outside the bounds of the cell buffer.
|
||||
func FillRect(s CellBuffer, c *Cell, rect Rectangle) {
|
||||
for y := rect.Min.Y; y < rect.Max.Y; y++ {
|
||||
for x := rect.Min.X; x < rect.Max.X; x++ {
|
||||
s.SetCell(x, y, c) //nolint:errcheck
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Fill fills the cell buffer with the given cell.
|
||||
func Fill(s CellBuffer, c *Cell) {
|
||||
FillRect(s, c, s.Bounds())
|
||||
}
|
||||
|
||||
// ClearRect clears the rectangle within the cell buffer with blank cells.
|
||||
func ClearRect(s CellBuffer, rect Rectangle) {
|
||||
FillRect(s, nil, rect)
|
||||
}
|
||||
|
||||
// Clear clears the cell buffer with blank cells.
|
||||
func Clear(s CellBuffer) {
|
||||
Fill(s, nil)
|
||||
}
|
||||
|
||||
// SetContentRect clears the rectangle within the cell buffer with blank cells,
|
||||
// and sets the given string as its content. If the height or width of the
|
||||
// string exceeds the height or width of the cell buffer, it will be truncated.
|
||||
func SetContentRect(s CellBuffer, str string, rect Rectangle) {
|
||||
// Replace all "\n" with "\r\n" to ensure the cursor is reset to the start
|
||||
// of the line. Make sure we don't replace "\r\n" with "\r\r\n".
|
||||
str = strings.ReplaceAll(str, "\r\n", "\n")
|
||||
str = strings.ReplaceAll(str, "\n", "\r\n")
|
||||
ClearRect(s, rect)
|
||||
printString(s, ansi.GraphemeWidth, rect.Min.X, rect.Min.Y, rect, str, true, "")
|
||||
}
|
||||
|
||||
// SetContent clears the cell buffer with blank cells, and sets the given string
|
||||
// as its content. If the height or width of the string exceeds the height or
|
||||
// width of the cell buffer, it will be truncated.
|
||||
func SetContent(s CellBuffer, str string) {
|
||||
SetContentRect(s, str, s.Bounds())
|
||||
}
|
||||
|
||||
// Render returns a string representation of the grid with ANSI escape sequences.
|
||||
func Render(d CellBuffer) string {
|
||||
var buf bytes.Buffer
|
||||
height := d.Bounds().Dy()
|
||||
for y := 0; y < height; y++ {
|
||||
_, line := RenderLine(d, y)
|
||||
buf.WriteString(line)
|
||||
if y < height-1 {
|
||||
buf.WriteString("\r\n")
|
||||
}
|
||||
}
|
||||
return buf.String()
|
||||
}
|
||||
|
||||
// RenderLine returns a string representation of the yth line of the grid along
|
||||
// with the width of the line.
|
||||
func RenderLine(d CellBuffer, n int) (w int, line string) {
|
||||
var pen Style
|
||||
var link Link
|
||||
var buf bytes.Buffer
|
||||
var pendingLine string
|
||||
var pendingWidth int // this ignores space cells until we hit a non-space cell
|
||||
|
||||
writePending := func() {
|
||||
// If there's no pending line, we don't need to do anything.
|
||||
if len(pendingLine) == 0 {
|
||||
return
|
||||
}
|
||||
buf.WriteString(pendingLine)
|
||||
w += pendingWidth
|
||||
pendingWidth = 0
|
||||
pendingLine = ""
|
||||
}
|
||||
|
||||
for x := 0; x < d.Bounds().Dx(); x++ {
|
||||
if cell := d.Cell(x, n); cell != nil && cell.Width > 0 {
|
||||
// Convert the cell's style and link to the given color profile.
|
||||
cellStyle := cell.Style
|
||||
cellLink := cell.Link
|
||||
if cellStyle.Empty() && !pen.Empty() {
|
||||
writePending()
|
||||
buf.WriteString(ansi.ResetStyle) //nolint:errcheck
|
||||
pen.Reset()
|
||||
}
|
||||
if !cellStyle.Equal(&pen) {
|
||||
writePending()
|
||||
seq := cellStyle.DiffSequence(pen)
|
||||
buf.WriteString(seq) // nolint:errcheck
|
||||
pen = cellStyle
|
||||
}
|
||||
|
||||
// Write the URL escape sequence
|
||||
if cellLink != link && link.URL != "" {
|
||||
writePending()
|
||||
buf.WriteString(ansi.ResetHyperlink()) //nolint:errcheck
|
||||
link.Reset()
|
||||
}
|
||||
if cellLink != link {
|
||||
writePending()
|
||||
buf.WriteString(ansi.SetHyperlink(cellLink.URL, cellLink.Params)) //nolint:errcheck
|
||||
link = cellLink
|
||||
}
|
||||
|
||||
// We only write the cell content if it's not empty. If it is, we
|
||||
// append it to the pending line and width to be evaluated later.
|
||||
if cell.Equal(&BlankCell) {
|
||||
pendingLine += cell.String()
|
||||
pendingWidth += cell.Width
|
||||
} else {
|
||||
writePending()
|
||||
buf.WriteString(cell.String())
|
||||
w += cell.Width
|
||||
}
|
||||
}
|
||||
}
|
||||
if link.URL != "" {
|
||||
buf.WriteString(ansi.ResetHyperlink()) //nolint:errcheck
|
||||
}
|
||||
if !pen.Empty() {
|
||||
buf.WriteString(ansi.ResetStyle) //nolint:errcheck
|
||||
}
|
||||
return w, strings.TrimRight(buf.String(), " ") // Trim trailing spaces
|
||||
}
|
||||
|
||||
// ScreenWriter represents a writer that writes to a [Screen] parsing ANSI
|
||||
// escape sequences and Unicode characters and converting them into cells that
|
||||
// can be written to a cell [Buffer].
|
||||
type ScreenWriter struct {
|
||||
*Screen
|
||||
}
|
||||
|
||||
// NewScreenWriter creates a new ScreenWriter that writes to the given Screen.
|
||||
// This is a convenience function for creating a ScreenWriter.
|
||||
func NewScreenWriter(s *Screen) *ScreenWriter {
|
||||
return &ScreenWriter{s}
|
||||
}
|
||||
|
||||
// Write writes the given bytes to the screen.
|
||||
// This will recognize ANSI [ansi.SGR] style and [ansi.SetHyperlink] escape
|
||||
// sequences.
|
||||
func (s *ScreenWriter) Write(p []byte) (n int, err error) {
|
||||
printString(s.Screen, s.method,
|
||||
s.cur.X, s.cur.Y, s.Bounds(),
|
||||
p, false, "")
|
||||
return len(p), nil
|
||||
}
|
||||
|
||||
// SetContent clears the screen with blank cells, and sets the given string as
|
||||
// its content. If the height or width of the string exceeds the height or
|
||||
// width of the screen, it will be truncated.
|
||||
//
|
||||
// This will recognize ANSI [ansi.SGR] style and [ansi.SetHyperlink] escape sequences.
|
||||
func (s *ScreenWriter) SetContent(str string) {
|
||||
s.SetContentRect(str, s.Bounds())
|
||||
}
|
||||
|
||||
// SetContentRect clears the rectangle within the screen with blank cells, and
|
||||
// sets the given string as its content. If the height or width of the string
|
||||
// exceeds the height or width of the screen, it will be truncated.
|
||||
//
|
||||
// This will recognize ANSI [ansi.SGR] style and [ansi.SetHyperlink] escape
|
||||
// sequences.
|
||||
func (s *ScreenWriter) SetContentRect(str string, rect Rectangle) {
|
||||
// Replace all "\n" with "\r\n" to ensure the cursor is reset to the start
|
||||
// of the line. Make sure we don't replace "\r\n" with "\r\r\n".
|
||||
str = strings.ReplaceAll(str, "\r\n", "\n")
|
||||
str = strings.ReplaceAll(str, "\n", "\r\n")
|
||||
s.ClearRect(rect)
|
||||
printString(s.Screen, s.method,
|
||||
rect.Min.X, rect.Min.Y, rect,
|
||||
str, true, "")
|
||||
}
|
||||
|
||||
// Print prints the string at the current cursor position. It will wrap the
|
||||
// string to the width of the screen if it exceeds the width of the screen.
|
||||
// This will recognize ANSI [ansi.SGR] style and [ansi.SetHyperlink] escape
|
||||
// sequences.
|
||||
func (s *ScreenWriter) Print(str string, v ...interface{}) {
|
||||
if len(v) > 0 {
|
||||
str = fmt.Sprintf(str, v...)
|
||||
}
|
||||
printString(s.Screen, s.method,
|
||||
s.cur.X, s.cur.Y, s.Bounds(),
|
||||
str, false, "")
|
||||
}
|
||||
|
||||
// PrintAt prints the string at the given position. It will wrap the string to
|
||||
// the width of the screen if it exceeds the width of the screen.
|
||||
// This will recognize ANSI [ansi.SGR] style and [ansi.SetHyperlink] escape
|
||||
// sequences.
|
||||
func (s *ScreenWriter) PrintAt(x, y int, str string, v ...interface{}) {
|
||||
if len(v) > 0 {
|
||||
str = fmt.Sprintf(str, v...)
|
||||
}
|
||||
printString(s.Screen, s.method,
|
||||
x, y, s.Bounds(),
|
||||
str, false, "")
|
||||
}
|
||||
|
||||
// PrintCrop prints the string at the current cursor position and truncates the
|
||||
// text if it exceeds the width of the screen. Use tail to specify a string to
|
||||
// append if the string is truncated.
|
||||
// This will recognize ANSI [ansi.SGR] style and [ansi.SetHyperlink] escape
|
||||
// sequences.
|
||||
func (s *ScreenWriter) PrintCrop(str string, tail string) {
|
||||
printString(s.Screen, s.method,
|
||||
s.cur.X, s.cur.Y, s.Bounds(),
|
||||
str, true, tail)
|
||||
}
|
||||
|
||||
// PrintCropAt prints the string at the given position and truncates the text
|
||||
// if it exceeds the width of the screen. Use tail to specify a string to append
|
||||
// if the string is truncated.
|
||||
// This will recognize ANSI [ansi.SGR] style and [ansi.SetHyperlink] escape
|
||||
// sequences.
|
||||
func (s *ScreenWriter) PrintCropAt(x, y int, str string, tail string) {
|
||||
printString(s.Screen, s.method,
|
||||
x, y, s.Bounds(),
|
||||
str, true, tail)
|
||||
}
|
||||
|
||||
// printString draws a string starting at the given position.
|
||||
func printString[T []byte | string](
|
||||
s CellBuffer,
|
||||
m ansi.Method,
|
||||
x, y int,
|
||||
bounds Rectangle, str T,
|
||||
truncate bool, tail string,
|
||||
) {
|
||||
p := ansi.GetParser()
|
||||
defer ansi.PutParser(p)
|
||||
|
||||
var tailc Cell
|
||||
if truncate && len(tail) > 0 {
|
||||
if m == ansi.WcWidth {
|
||||
tailc = *NewCellString(tail)
|
||||
} else {
|
||||
tailc = *NewGraphemeCell(tail)
|
||||
}
|
||||
}
|
||||
|
||||
decoder := ansi.DecodeSequenceWc[T]
|
||||
if m == ansi.GraphemeWidth {
|
||||
decoder = ansi.DecodeSequence[T]
|
||||
}
|
||||
|
||||
var cell Cell
|
||||
var style Style
|
||||
var link Link
|
||||
var state byte
|
||||
for len(str) > 0 {
|
||||
seq, width, n, newState := decoder(str, state, p)
|
||||
|
||||
switch width {
|
||||
case 1, 2, 3, 4: // wide cells can go up to 4 cells wide
|
||||
cell.Width += width
|
||||
cell.Append([]rune(string(seq))...)
|
||||
|
||||
if !truncate && x+cell.Width > bounds.Max.X && y+1 < bounds.Max.Y {
|
||||
// Wrap the string to the width of the window
|
||||
x = bounds.Min.X
|
||||
y++
|
||||
}
|
||||
if Pos(x, y).In(bounds) {
|
||||
if truncate && tailc.Width > 0 && x+cell.Width > bounds.Max.X-tailc.Width {
|
||||
// Truncate the string and append the tail if any.
|
||||
cell := tailc
|
||||
cell.Style = style
|
||||
cell.Link = link
|
||||
s.SetCell(x, y, &cell)
|
||||
x += tailc.Width
|
||||
} else {
|
||||
// Print the cell to the screen
|
||||
cell.Style = style
|
||||
cell.Link = link
|
||||
s.SetCell(x, y, &cell) //nolint:errcheck
|
||||
x += width
|
||||
}
|
||||
}
|
||||
|
||||
// String is too long for the line, truncate it.
|
||||
// Make sure we reset the cell for the next iteration.
|
||||
cell.Reset()
|
||||
default:
|
||||
// Valid sequences always have a non-zero Cmd.
|
||||
// TODO: Handle cursor movement and other sequences
|
||||
switch {
|
||||
case ansi.HasCsiPrefix(seq) && p.Command() == 'm':
|
||||
// SGR - Select Graphic Rendition
|
||||
ReadStyle(p.Params(), &style)
|
||||
case ansi.HasOscPrefix(seq) && p.Command() == 8:
|
||||
// Hyperlinks
|
||||
ReadLink(p.Data(), &link)
|
||||
case ansi.Equal(seq, T("\n")):
|
||||
y++
|
||||
case ansi.Equal(seq, T("\r")):
|
||||
x = bounds.Min.X
|
||||
default:
|
||||
cell.Append([]rune(string(seq))...)
|
||||
}
|
||||
}
|
||||
|
||||
// Advance the state and data
|
||||
state = newState
|
||||
str = str[n:]
|
||||
}
|
||||
|
||||
// Make sure to set the last cell if it's not empty.
|
||||
if !cell.Empty() {
|
||||
s.SetCell(x, y, &cell) //nolint:errcheck
|
||||
cell.Reset()
|
||||
}
|
||||
}
|
||||
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
|
||||
)
|
||||
87
vendor/github.com/charmbracelet/x/term/term_windows.go
generated
vendored
Normal file
87
vendor/github.com/charmbracelet/x/term/term_windows.go
generated
vendored
Normal file
|
|
@ -0,0 +1,87 @@
|
|||
//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)
|
||||
raw |= windows.ENABLE_VIRTUAL_TERMINAL_INPUT
|
||||
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)
|
||||
}
|
||||
12
vendor/github.com/charmbracelet/x/term/terminal.go
generated
vendored
Normal file
12
vendor/github.com/charmbracelet/x/term/terminal.go
generated
vendored
Normal file
|
|
@ -0,0 +1,12 @@
|
|||
package term
|
||||
|
||||
import (
|
||||
"io"
|
||||
)
|
||||
|
||||
// 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
|
||||
}
|
||||
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
|
||||
}
|
||||
}
|
||||
}
|
||||
Loading…
Add table
Add a link
Reference in a new issue