feat: initial commit

This commit is contained in:
l.weber 2025-12-05 12:20:05 +01:00
commit a161b86c9a
705 changed files with 288162 additions and 0 deletions

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

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

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

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

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

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

178
vendor/github.com/charmbracelet/x/ansi/background.go generated vendored Normal file
View 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
View 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
View file

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

55
vendor/github.com/charmbracelet/x/ansi/charset.go generated vendored Normal file
View 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
View file

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

784
vendor/github.com/charmbracelet/x/ansi/color.go generated vendored Normal file
View 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
View 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
View 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
View 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
View file

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

67
vendor/github.com/charmbracelet/x/ansi/finalterm.go generated vendored Normal file
View 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
View 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
View 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
View file

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

18
vendor/github.com/charmbracelet/x/ansi/iterm2.go generated vendored Normal file
View 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
View 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
View 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
View 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
View 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
View 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
View 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
View 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
View 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
View 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
View 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
}
}
}

View 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
View 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
// `@AZ[\]^_`az{|}~`.
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
}

View 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(&params[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
View 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
View 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
View 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
View 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
View 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
View 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
View 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
View 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
View 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
View 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 5156 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 5134 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
View 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
View 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
View 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
View 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
View 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
View 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
View file

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

473
vendor/github.com/charmbracelet/x/cellbuf/buffer.go generated vendored Normal file
View 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
View 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
View 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
View 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
View 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
View 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
View 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

File diff suppressed because it is too large Load diff

131
vendor/github.com/charmbracelet/x/cellbuf/sequence.go generated vendored Normal file
View 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
View 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
View 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
View 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
View 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
View 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
View file

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

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

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

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

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

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

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

View file

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

View file

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

87
vendor/github.com/charmbracelet/x/term/term_windows.go generated vendored Normal file
View 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
View 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
View file

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