Bump tcell dependency to v3

This commit is contained in:
Stefan Haller
2026-04-30 22:14:26 +02:00
parent 64996d12d9
commit 5d3715f96b
142 changed files with 15398 additions and 8297 deletions
+4 -2
View File
@@ -13,7 +13,7 @@ require (
github.com/cli/go-gh/v2 v2.13.0
github.com/cloudfoundry/jibber_jabber v0.0.0-20151120183258-bcc4c8345a21
github.com/creack/pty v1.1.24
github.com/gdamore/tcell/v2 v2.13.8
github.com/gdamore/tcell/v3 v3.3.0
github.com/go-errors/errors v1.5.1
github.com/gookit/color v1.6.0
github.com/integrii/flaggy v1.8.0
@@ -47,6 +47,8 @@ require (
github.com/bahlo/generic-list-go v0.2.0 // indirect
github.com/buger/jsonparser v1.1.2 // indirect
github.com/cli/safeexec v1.0.1 // indirect
github.com/clipperhouse/displaywidth v0.11.0 // indirect
github.com/clipperhouse/uax29/v2 v2.7.0 // indirect
github.com/davecgh/go-spew v1.1.1 // indirect
github.com/fatih/color v1.9.0 // indirect
github.com/gdamore/encoding v1.0.1 // indirect
@@ -67,7 +69,7 @@ require (
github.com/rogpeppe/go-internal v1.14.1 // indirect
github.com/wk8/go-ordered-map/v2 v2.1.8 // indirect
golang.org/x/net v0.47.0 // indirect
golang.org/x/term v0.41.0 // indirect
golang.org/x/term v0.42.0 // indirect
golang.org/x/text v0.36.0 // indirect
gopkg.in/check.v1 v1.0.0-20201130134442-10cb98267c6c // indirect
gopkg.in/fsnotify.v1 v1.4.7 // indirect
+8 -4
View File
@@ -15,6 +15,10 @@ github.com/cli/go-gh/v2 v2.13.0 h1:jEHZu/VPVoIJkciK3pzZd3rbT8J90swsK5Ui4ewH1ys=
github.com/cli/go-gh/v2 v2.13.0/go.mod h1:Us/NbQ8VNM0fdaILgoXSz6PKkV5PWaEzkJdc9vR2geM=
github.com/cli/safeexec v1.0.1 h1:e/C79PbXF4yYTN/wauC4tviMxEV13BwljGj0N9j+N00=
github.com/cli/safeexec v1.0.1/go.mod h1:Z/D4tTN8Vs5gXYHDCbaM1S/anmEDnJb1iW0+EJ5zx3Q=
github.com/clipperhouse/displaywidth v0.11.0 h1:lBc6kY44VFw+TDx4I8opi/EtL9m20WSEFgwIwO+UVM8=
github.com/clipperhouse/displaywidth v0.11.0/go.mod h1:bkrFNkf81G8HyVqmKGxsPufD3JhNl3dSqnGhOoSD/o0=
github.com/clipperhouse/uax29/v2 v2.7.0 h1:+gs4oBZ2gPfVrKPthwbMzWZDaAFPGYK72F0NJv2v7Vk=
github.com/clipperhouse/uax29/v2 v2.7.0/go.mod h1:EFJ2TJMRUaplDxHKj1qAEhCtQPW2tJSwu5BF98AuoVM=
github.com/cloudfoundry/jibber_jabber v0.0.0-20151120183258-bcc4c8345a21 h1:tuijfIjZyjZaHq9xDUh0tNitwXshJpbLkqMOJv4H3do=
github.com/cloudfoundry/jibber_jabber v0.0.0-20151120183258-bcc4c8345a21/go.mod h1:po7NpZ/QiTKzBKyrsEAxwnTamCoh8uDk/egRpQ7siIc=
github.com/creack/pty v1.1.9/go.mod h1:oKZEueFk5CKHvIhNR5MUki03XCEU+Q6VDXinZuGJ33E=
@@ -29,8 +33,8 @@ github.com/fatih/color v1.9.0 h1:8xPHl4/q1VyqGIPif1F+1V3Y3lSmrq01EabUW3CoW5s=
github.com/fatih/color v1.9.0/go.mod h1:eQcE1qtQxscV5RaZvpXrrb8Drkc3/DdQ+uUYCNjL+zU=
github.com/gdamore/encoding v1.0.1 h1:YzKZckdBL6jVt2Gc+5p82qhrGiqMdG/eNs6Wy0u3Uhw=
github.com/gdamore/encoding v1.0.1/go.mod h1:0Z0cMFinngz9kS1QfMjCP8TY7em3bZYeeklsSDPivEo=
github.com/gdamore/tcell/v2 v2.13.8 h1:Mys/Kl5wfC/GcC5Cx4C2BIQH9dbnhnkPgS9/wF3RlfU=
github.com/gdamore/tcell/v2 v2.13.8/go.mod h1:+Wfe208WDdB7INEtCsNrAN6O2m+wsTPk1RAovjaILlo=
github.com/gdamore/tcell/v3 v3.3.0 h1:lGo3VwiV8iYMH4TdwBDwEOoDctnOLns6QfrXvZkKNe8=
github.com/gdamore/tcell/v3 v3.3.0/go.mod h1:8CJpEjUiAlFIrs7jUhzobiZsSLBAtXo8Dq9mCTrVnqo=
github.com/go-errors/errors v1.5.1 h1:ZwEMSLRCapFLflTpT7NKaAc7ukJ8ZPEjzlxt8rPN8bk=
github.com/go-errors/errors v1.5.1/go.mod h1:sIVyrIiJhuEF+Pj9Ebtd6P/rEYROXFi3BopGUQ5a5Og=
github.com/go-logfmt/logfmt v0.4.0/go.mod h1:3RMwSq7FuexP4Kalkev3ejPJsZTpXXBr9+V4qmtdjCk=
@@ -163,8 +167,8 @@ golang.org/x/sys v0.43.0/go.mod h1:4GL1E5IUh+htKOUEOaiffhrAeqysfVGipDYzABqnCmw=
golang.org/x/term v0.0.0-20201126162022-7de9c90e9dd1/go.mod h1:bj7SfCRtBDWHUb9snDiAeCFNEtKQo2Wmx5Cou7ajbmo=
golang.org/x/term v0.0.0-20210927222741-03fcf44c2211/go.mod h1:jbD1KX2456YbFQfuXm/mYQcufACuNUgVhRMnK/tPxf8=
golang.org/x/term v0.5.0/go.mod h1:jMB1sMXY+tzblOD4FWmEbocvup2/aLOaQEp7JmGp78k=
golang.org/x/term v0.41.0 h1:QCgPso/Q3RTJx2Th4bDLqML4W6iJiaXFq2/ftQF13YU=
golang.org/x/term v0.41.0/go.mod h1:3pfBgksrReYfZ5lvYM0kSO0LIkAl4Yl2bXOkKP7Ec2A=
golang.org/x/term v0.42.0 h1:UiKe+zDFmJobeJ5ggPwOshJIVt6/Ft0rcfrXZDLWAWY=
golang.org/x/term v0.42.0/go.mod h1:Dq/D+snpsbazcBG5+F9Q1n2rXV8Ma+71xEjTRufARgY=
golang.org/x/text v0.3.0/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ=
golang.org/x/text v0.3.3/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ=
golang.org/x/text v0.3.7/go.mod h1:u+2+/6zg+i71rQMx5EYifcz6MCKuco9NR6JIITiCfzQ=
-5
View File
@@ -42,8 +42,6 @@ var LabelByKey = map[gocui.KeyName]string{
gocui.KeyAltEnter: "<a-enter>",
gocui.KeyEsc: "<esc>", // <c-[>, <c-3>
gocui.KeyBackspace: "<backspace>", // <c-h>
gocui.KeyCtrlSpace: "<c-space>", // <c-~>, <c-2>
gocui.KeyCtrlSlash: "<c-/>", // <c-_>
gocui.KeySpace: "<space>",
gocui.KeyCtrlA: "<c-a>",
gocui.KeyCtrlB: "<c-b>",
@@ -68,9 +66,6 @@ var LabelByKey = map[gocui.KeyName]string{
gocui.KeyCtrlX: "<c-x>",
gocui.KeyCtrlY: "<c-y>",
gocui.KeyCtrlZ: "<c-z>",
gocui.KeyCtrl4: "<c-4>", // <c-\>
gocui.KeyCtrl5: "<c-5>", // <c-]>
gocui.KeyCtrl6: "<c-6>",
gocui.KeyCtrl8: "<c-8>",
gocui.MouseWheelUp: "mouse wheel up",
gocui.MouseWheelDown: "mouse wheel down",
+1 -1
View File
@@ -4,7 +4,7 @@
package gocui
import "github.com/gdamore/tcell/v2"
import "github.com/gdamore/tcell/v3"
// Attribute affects the presentation of characters, such as color, boldness, etc.
type Attribute uint64
+2 -2
View File
@@ -62,8 +62,8 @@ func SimpleEditor(v *View, key Key, mod Modifier) bool {
v.TextArea.BackSpaceWord()
case key.KeyName() == KeyCtrlY:
v.TextArea.Yank()
case key.Ch() != 0:
v.TextArea.TypeCharacter(string(key.Ch()))
case key.Str() != "":
v.TextArea.TypeCharacter(key.Str())
default:
return false
}
+3 -3
View File
@@ -11,7 +11,7 @@ import (
"sync"
"time"
"github.com/gdamore/tcell/v2"
"github.com/gdamore/tcell/v3"
"github.com/go-errors/errors"
"github.com/rivo/uniseg"
)
@@ -558,7 +558,7 @@ func (g *Gui) SetKeybinding(viewname string, key Key, mod Modifier, handler func
// DeleteKeybinding deletes a keybinding.
func (g *Gui) DeleteKeybinding(viewname string, key Key, mod Modifier) error {
for i, kb := range g.keybindings {
if kb.viewName == viewname && kb.key.keyName == key.KeyName() && kb.key.ch == key.ch && kb.mod == mod {
if kb.viewName == viewname && kb.key.keyName == key.KeyName() && kb.key.str == key.str && kb.mod == mod {
g.keybindings = append(g.keybindings[:i], g.keybindings[i+1:]...)
return nil
}
@@ -1595,7 +1595,7 @@ func (g *Gui) matchView(v *View, kb *keybinding) bool {
if v == nil {
return false
}
if v.Editable && kb.key.ch != 0 {
if v.Editable && kb.key.Str() != "" {
return false
}
if kb.viewName != v.name {
+9 -9
View File
@@ -4,31 +4,31 @@
package gocui
import "github.com/gdamore/tcell/v2"
import "github.com/gdamore/tcell/v3"
type Key struct {
keyName KeyName
ch rune
str string
}
func NewKey(keyName KeyName, ch rune) Key {
func NewKey(keyName KeyName, str string) Key {
return Key{
keyName: keyName,
ch: ch,
str: str,
}
}
func NewKeyName(keyName KeyName) Key {
return Key{
keyName: keyName,
ch: 0,
str: "",
}
}
func NewKeyRune(ch rune) Key {
return Key{
keyName: KeyName(tcell.KeyRune),
ch: ch,
str: string(ch),
}
}
@@ -36,8 +36,8 @@ func (k Key) KeyName() KeyName {
return k.keyName
}
func (k Key) Ch() rune {
return k.ch
func (k Key) Str() string {
return k.str
}
func (k Key) IsSet() bool {
@@ -45,5 +45,5 @@ func (k Key) IsSet() bool {
}
func (k Key) Equals(otherKey Key) bool {
return k.keyName == otherKey.keyName && k.ch == otherKey.ch
return k.keyName == otherKey.keyName && k.str == otherKey.str
}
+45 -57
View File
@@ -5,7 +5,7 @@
package gocui
import (
"github.com/gdamore/tcell/v2"
"github.com/gdamore/tcell/v3"
)
// KeyName represents special keys or keys combinations.
@@ -69,67 +69,55 @@ const (
// Keys combinations.
const (
KeyCtrlTilde = KeyName(tcell.KeyF64) // arbitrary assignment
KeyCtrlSpace = KeyName(tcell.KeyCtrlSpace)
KeyCtrlA = KeyName(tcell.KeyCtrlA)
KeyCtrlB = KeyName(tcell.KeyCtrlB)
KeyCtrlC = KeyName(tcell.KeyCtrlC)
KeyCtrlD = KeyName(tcell.KeyCtrlD)
KeyCtrlE = KeyName(tcell.KeyCtrlE)
KeyCtrlF = KeyName(tcell.KeyCtrlF)
KeyCtrlG = KeyName(tcell.KeyCtrlG)
KeyBackspace = KeyName(tcell.KeyBackspace)
KeyCtrlH = KeyName(tcell.KeyCtrlH)
KeyTab = KeyName(tcell.KeyTab)
KeyBacktab = KeyName(tcell.KeyBacktab)
KeyCtrlI = KeyName(tcell.KeyCtrlI)
KeyCtrlJ = KeyName(tcell.KeyCtrlJ)
KeyCtrlK = KeyName(tcell.KeyCtrlK)
KeyCtrlL = KeyName(tcell.KeyCtrlL)
KeyEnter = KeyName(tcell.KeyEnter)
KeyCtrlM = KeyName(tcell.KeyCtrlM)
KeyCtrlN = KeyName(tcell.KeyCtrlN)
KeyCtrlO = KeyName(tcell.KeyCtrlO)
KeyCtrlP = KeyName(tcell.KeyCtrlP)
KeyCtrlQ = KeyName(tcell.KeyCtrlQ)
KeyCtrlR = KeyName(tcell.KeyCtrlR)
KeyCtrlS = KeyName(tcell.KeyCtrlS)
KeyCtrlT = KeyName(tcell.KeyCtrlT)
KeyCtrlU = KeyName(tcell.KeyCtrlU)
KeyCtrlV = KeyName(tcell.KeyCtrlV)
KeyCtrlW = KeyName(tcell.KeyCtrlW)
KeyCtrlX = KeyName(tcell.KeyCtrlX)
KeyCtrlY = KeyName(tcell.KeyCtrlY)
KeyCtrlZ = KeyName(tcell.KeyCtrlZ)
KeyEsc = KeyName(tcell.KeyEscape)
KeyCtrlUnderscore = KeyName(tcell.KeyCtrlUnderscore)
KeySpace = KeyName(32)
KeyBackspace2 = KeyName(tcell.KeyBackspace2)
KeyCtrl8 = KeyName(tcell.KeyBackspace2) // same key as in termbox-go
KeyCtrlTilde = KeyName(tcell.KeyF64) // arbitrary assignment
KeyCtrlA = KeyName(tcell.KeyCtrlA)
KeyCtrlB = KeyName(tcell.KeyCtrlB)
KeyCtrlC = KeyName(tcell.KeyCtrlC)
KeyCtrlD = KeyName(tcell.KeyCtrlD)
KeyCtrlE = KeyName(tcell.KeyCtrlE)
KeyCtrlF = KeyName(tcell.KeyCtrlF)
KeyCtrlG = KeyName(tcell.KeyCtrlG)
KeyBackspace = KeyName(tcell.KeyBackspace)
KeyCtrlH = KeyName(tcell.KeyCtrlH)
KeyTab = KeyName(tcell.KeyTab)
KeyBacktab = KeyName(tcell.KeyBacktab)
KeyCtrlI = KeyName(tcell.KeyCtrlI)
KeyCtrlJ = KeyName(tcell.KeyCtrlJ)
KeyCtrlK = KeyName(tcell.KeyCtrlK)
KeyCtrlL = KeyName(tcell.KeyCtrlL)
KeyEnter = KeyName(tcell.KeyEnter)
KeyCtrlM = KeyName(tcell.KeyCtrlM)
KeyCtrlN = KeyName(tcell.KeyCtrlN)
KeyCtrlO = KeyName(tcell.KeyCtrlO)
KeyCtrlP = KeyName(tcell.KeyCtrlP)
KeyCtrlQ = KeyName(tcell.KeyCtrlQ)
KeyCtrlR = KeyName(tcell.KeyCtrlR)
KeyCtrlS = KeyName(tcell.KeyCtrlS)
KeyCtrlT = KeyName(tcell.KeyCtrlT)
KeyCtrlU = KeyName(tcell.KeyCtrlU)
KeyCtrlV = KeyName(tcell.KeyCtrlV)
KeyCtrlW = KeyName(tcell.KeyCtrlW)
KeyCtrlX = KeyName(tcell.KeyCtrlX)
KeyCtrlY = KeyName(tcell.KeyCtrlY)
KeyCtrlZ = KeyName(tcell.KeyCtrlZ)
KeyEsc = KeyName(tcell.KeyEscape)
KeySpace = KeyName(32)
KeyBackspace2 = KeyName(tcell.KeyBackspace2)
KeyCtrl8 = KeyName(tcell.KeyBackspace2) // same key as in termbox-go
// The following assignments were used in termbox implementation.
// In tcell, these are not keys per se. But in gocui we have them
// mapped to the keys so we have to use placeholder keys.
KeyAltEnter = KeyName(tcell.KeyF64) // arbitrary assignments
MouseLeft = KeyName(tcell.KeyF63)
MouseRight = KeyName(tcell.KeyF62)
MouseMiddle = KeyName(tcell.KeyF61)
MouseRelease = KeyName(tcell.KeyF60)
MouseWheelUp = KeyName(tcell.KeyF59)
MouseWheelDown = KeyName(tcell.KeyF58)
MouseWheelLeft = KeyName(tcell.KeyF57)
MouseWheelRight = KeyName(tcell.KeyF56)
KeyCtrl2 = KeyName(tcell.KeyNUL) // termbox defines theses
KeyCtrl3 = KeyName(tcell.KeyEscape)
KeyCtrl4 = KeyName(tcell.KeyCtrlBackslash)
KeyCtrl5 = KeyName(tcell.KeyCtrlRightSq)
KeyCtrl6 = KeyName(tcell.KeyCtrlCarat)
KeyCtrl7 = KeyName(tcell.KeyCtrlUnderscore)
KeyCtrlSlash = KeyName(tcell.KeyCtrlUnderscore)
KeyCtrlRsqBracket = KeyName(tcell.KeyCtrlRightSq)
KeyCtrlBackslash = KeyName(tcell.KeyCtrlBackslash)
KeyCtrlLsqBracket = KeyName(tcell.KeyCtrlLeftSq)
KeyAltEnter = KeyName(tcell.KeyF64) // arbitrary assignments
MouseLeft = KeyName(tcell.KeyF63)
MouseRight = KeyName(tcell.KeyF62)
MouseMiddle = KeyName(tcell.KeyF61)
MouseRelease = KeyName(tcell.KeyF60)
MouseWheelUp = KeyName(tcell.KeyF59)
MouseWheelDown = KeyName(tcell.KeyF58)
MouseWheelLeft = KeyName(tcell.KeyF57)
MouseWheelRight = KeyName(tcell.KeyF56)
)
// Modifiers.
+19 -18
View File
@@ -5,7 +5,8 @@
package gocui
import (
"github.com/gdamore/tcell/v2"
"github.com/gdamore/tcell/v3"
"github.com/gdamore/tcell/v3/vt"
)
// We probably don't want this being a global variable for YOLO for now
@@ -83,16 +84,17 @@ func registerRuneFallbacks(s tcell.Screen, additional map[rune]string) {
// tcellInitSimulation initializes tcell screen for use.
func (g *Gui) tcellInitSimulation(width int, height int) error {
s := tcell.NewSimulationScreen("")
if e := s.Init(); e != nil {
mt := vt.NewMockTerm(vt.MockOptSize{X: vt.Col(width), Y: vt.Row(height)})
s, e := tcell.NewTerminfoScreenFromTty(mt)
if e != nil {
return e
}
if e = s.Init(); e != nil {
return e
}
g.screen = s
Screen = s
// setting to a larger value than the typical terminal size
// so that during a test we're more likely to see an item to select in a view.
s.SetSize(width, height)
s.Sync()
return nil
}
@@ -206,7 +208,7 @@ type TcellKeyEventWrapper struct {
Timestamp int64
Mod tcell.ModMask
Key tcell.Key
Ch rune
Ch string
}
func NewTcellKeyEventWrapper(event *tcell.EventKey, timestamp int64) *TcellKeyEventWrapper {
@@ -214,7 +216,7 @@ func NewTcellKeyEventWrapper(event *tcell.EventKey, timestamp int64) *TcellKeyEv
Timestamp: timestamp,
Mod: event.Modifiers(),
Key: event.Key(),
Ch: event.Rune(),
Ch: event.Str(),
}
}
@@ -278,7 +280,7 @@ func (g *Gui) pollEvent() GocuiEvent {
tev = (ev).toTcellEvent()
}
} else {
tev = Screen.PollEvent()
tev = <-Screen.EventQ()
}
switch tev := tev.(type) {
@@ -289,28 +291,27 @@ func (g *Gui) pollEvent() GocuiEvent {
return GocuiEvent{Type: eventResize, Width: w, Height: h}
case *tcell.EventKey:
k := tev.Key()
ch := rune(0)
ch := ""
if k == tcell.KeyRune {
ch = tev.Rune()
if ch == ' ' {
ch = tev.Str()
if ch == " " {
// special handling for spacebar
k = tcell.Key(KeySpace)
ch = rune(0)
ch = ""
}
}
mod := tev.Modifiers()
// remove control modifier and setup special handling of ctrl+spacebar, etc.
if mod == tcell.ModCtrl && k == 32 {
mod = 0
ch = rune(0)
k = tcell.KeyCtrlSpace
ch = " "
k = tcell.KeyRune
} else if mod == tcell.ModShift && k == tcell.KeyUp {
mod = 0
ch = rune(0)
ch = ""
k = tcell.KeyF62
} else if mod == tcell.ModShift && k == tcell.KeyDown {
mod = 0
ch = rune(0)
ch = ""
k = tcell.KeyF63
} else if mod == tcell.ModCtrl || mod == tcell.ModShift {
// remove Ctrl or Shift if specified
+1 -1
View File
@@ -12,7 +12,7 @@ import (
"unicode"
"unicode/utf8"
"github.com/gdamore/tcell/v2"
"github.com/gdamore/tcell/v3"
"github.com/rivo/uniseg"
)
+2 -2
View File
@@ -6,7 +6,7 @@ import (
"strings"
"time"
"github.com/gdamore/tcell/v2"
"github.com/gdamore/tcell/v3"
"github.com/jesseduffield/lazygit/pkg/commands/models"
"github.com/jesseduffield/lazygit/pkg/config"
"github.com/jesseduffield/lazygit/pkg/gocui"
@@ -32,7 +32,7 @@ func (self *GuiDriver) PressKey(keyStr string) {
key := keybindings.GetKey(keyStr)
self.gui.g.ReplayedEvents.Keys <- gocui.NewTcellKeyEventWrapper(
tcell.NewEventKey(tcell.Key(key.KeyName()), key.Ch(), tcell.ModNone),
tcell.NewEventKey(tcell.Key(key.KeyName()), key.Str(), tcell.ModNone),
0,
)
+2 -2
View File
@@ -5,7 +5,7 @@ import (
"strings"
"unicode/utf8"
"github.com/gdamore/tcell/v2"
"github.com/gdamore/tcell/v3"
"github.com/jesseduffield/lazygit/pkg/config"
"github.com/jesseduffield/lazygit/pkg/constants"
"github.com/jesseduffield/lazygit/pkg/gocui"
@@ -17,7 +17,7 @@ func LabelFromKey(key gocui.Key) string {
}
if key.KeyName() == gocui.KeyName(tcell.KeyRune) {
return string(key.Ch())
return key.Str()
}
value, ok := config.LabelByKey[key.KeyName()]
+3
View File
@@ -0,0 +1,3 @@
.DS_Store
*.out
*.test
+51
View File
@@ -0,0 +1,51 @@
The goals and overview of this package can be found in the README.md file,
start by reading that.
The goal of this package is to determine the display (column) width of a
string, UTF-8 bytes, or runes, as would happen in a monospace font, especially
in a terminal.
When troubleshooting, write Go unit tests instead of executing debug scripts.
The tests can return whatever logs or output you need. If those tests are
only for temporary troubleshooting, clean up the tests after the debugging is
done.
(Separate executable debugging scripts are messy, tend to have conflicting
dependencies and are hard to cleanup.)
If you make changes to the trie generation in internal/gen, it can be invoked
by running `go generate` from the top package directory.
## Pull Requests and branches
For PRs (pull requests), you can use the gh CLI tool. Compare the current branch with main. Reviewing a PR and reviewing a branch are about the same, but the PR may add context.
Understand the goals of the PR. Note any API changes, especially breaking changes.
Look for thoroughness of tests, as well as GoDoc comments.
Retrieve and consider the comments on the PR, which may have come from GitHub Copilot or Cursor BugBot. Think like GitHub Copilot or Cursor BugBot.
Offer to optionally post a brief summary of the review to the PR, via the gh CLI tool.
## Tagged Go releases
If I ask you whether we are ready to release, this means a tagged Go release on the main branch. Go releases are git tagged with a version number.
Review the changes since the last release, i.e. the previous git tag. Ensure that the changes are complete and correct. Identify new features, bug fixes, and performance improvements.
Identify breaking changes, especially API changes.
Ensure good test coverage. Look for performance changes, especially performance regressions, by running benchmarks against the previous release.
Ensure that the documentation in READMEs and GoDocs are complete, correct and consistent.
## Comparisons to go-runewidth
We originally attempted to make this package compatible with go-runewidth.
However, we found that there were too many differences in the handling of
certain characters and properties.
We believe, preliminarily, that our choices are more correct and complete,
by using more complete categories such as Unicode Cf (format) for zero-width
and Mn (Nonspacing_Mark) for combining marks.
+129
View File
@@ -0,0 +1,129 @@
# Changelog
## [0.11.0]
[Compare](https://github.com/clipperhouse/displaywidth/compare/v0.10.0...v0.11.0)
### Added
- New `ControlSequences8Bit` option to treat 8-bit ECMA-48 (C1) escape sequences as zero-width. (#22)
### Changed
- Upgraded uax29 dependency to v2.7.0 for 8-bit escape sequence support in the grapheme iterator.
- Truncation now validates that preserved trailing escape sequences are zero-width, preventing edge cases where non-zero-width sequences could leak into output.
### Note
- `ControlSequences8Bit` is deliberately ignored by `TruncateString` and `TruncateBytes`, because C1 byte values (0x800x9F) overlap with UTF-8 multi-byte encoding.
## [0.10.0]
[Compare](https://github.com/clipperhouse/displaywidth/compare/v0.9.0...v0.10.0)
### Added
- New `ControlSequences` option to treat ECMA-48/ANSI escape sequences as zero-width. (#20)
- `TruncateString` and `TruncateBytes` now preserve trailing ANSI escape sequences (such as SGR resets) when `ControlSequences` is true, preventing color bleed in terminal output.
### Changed
- Removed `stringish` dependency; generic type constraints are now inline `~string | []byte`.
- Upgraded uax29 dependency to v2.6.0 for ANSI escape sequence support in the grapheme iterator.
## [0.9.0]
[Compare](https://github.com/clipperhouse/displaywidth/compare/v0.8.0...v0.9.0)
### Changed
- Unicode 17 support: East Asian Width and emoji data updated to Unicode 17.0.0. (#18)
- Upgraded uax29 dependency to v2.5.0 (Unicode 17 grapheme segmentation).
## [0.8.0]
[Compare](https://github.com/clipperhouse/displaywidth/compare/v0.7.0...v0.8.0)
### Changed
- Performance: ASCII fast path that applies to any run of printable
ASCII. 2x-10x faster for ASCII text vs v0.7.0. (#16)
- Upgraded uax29 dependency to v2.4.0 for Unicode 16 support. Text that includes
Indic_Conjunct_Break may segment differently (and more correctly). (#15)
## [0.7.0]
[Compare](https://github.com/clipperhouse/displaywidth/compare/v0.6.2...v0.7.0)
### Added
- New `TruncateString` and `TruncateBytes` methods to truncate strings to a
maximum display width, with optional tail (like an ellipsis). (#13)
## [0.6.2]
[Compare](https://github.com/clipperhouse/displaywidth/compare/v0.6.1...v0.6.2)
### Changed
- Internal: reduced property categories for simpler trie.
## [0.6.1]
[Compare](https://github.com/clipperhouse/displaywidth/compare/v0.6.0...v0.6.1)
### Changed
- Perf improvements: replaced the ASCII lookup table with a simple
function. A bit more cache-friendly. More inlining.
- Bug fix: single regional indicators are now treated as width 2, since that
is what actual terminals do.
## [0.6.0]
[Compare](https://github.com/clipperhouse/displaywidth/compare/v0.5.0...v0.6.0)
### Added
- New `StringGraphemes` and `BytesGraphemes` methods, for iterating over the
widths of grapheme clusters.
### Changed
- Fast ASCII lookups
## [0.5.0]
[Compare](https://github.com/clipperhouse/displaywidth/compare/v0.4.1...v0.5.0)
### Added
- Unicode 16 support
- Improved emoji presentation handling per Unicode TR51
### Changed
- Corrected VS15 (U+FE0E) handling: now preserves base character width (no-op) per Unicode TR51
- Performance optimizations: reduced property lookups
### Fixed
- VS15 variation selector now correctly preserves base character width instead of forcing width 1
## [0.4.1]
[Compare](https://github.com/clipperhouse/displaywidth/compare/v0.4.0...v0.4.1)
### Changed
- Updated uax29 dependency
- Improved flag handling
## [0.4.0]
[Compare](https://github.com/clipperhouse/displaywidth/compare/v0.3.1...v0.4.0)
### Added
- Support for variation selectors (VS15, VS16) and regional indicator pairs (flags)
## [0.3.1]
[Compare](https://github.com/clipperhouse/displaywidth/compare/v0.3.0...v0.3.1)
### Added
- Fuzz testing support
### Changed
- Updated stringish dependency
## [0.3.0]
[Compare](https://github.com/clipperhouse/displaywidth/compare/v0.2.0...v0.3.0)
### Changed
- Dropped compatibility with go-runewidth
- Trie implementation cleanup
+21
View File
@@ -0,0 +1,21 @@
MIT License
Copyright (c) 2025 Matt Sherman
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.
+190
View File
@@ -0,0 +1,190 @@
# displaywidth
A high-performance Go package for measuring the monospace display width of strings, UTF-8 bytes, and runes.
[![Documentation](https://pkg.go.dev/badge/github.com/clipperhouse/displaywidth.svg)](https://pkg.go.dev/github.com/clipperhouse/displaywidth)
[![Test](https://github.com/clipperhouse/displaywidth/actions/workflows/gotest.yml/badge.svg)](https://github.com/clipperhouse/displaywidth/actions/workflows/gotest.yml)
[![Fuzz](https://github.com/clipperhouse/displaywidth/actions/workflows/gofuzz.yml/badge.svg)](https://github.com/clipperhouse/displaywidth/actions/workflows/gofuzz.yml)
## Install
```bash
go get github.com/clipperhouse/displaywidth
```
## Usage
```go
package main
import (
"fmt"
"github.com/clipperhouse/displaywidth"
)
func main() {
width := displaywidth.String("Hello, 世界!")
fmt.Println(width)
width = displaywidth.Bytes([]byte("🌍"))
fmt.Println(width)
width = displaywidth.Rune('🌍')
fmt.Println(width)
}
```
For most purposes, you should use the `String` or `Bytes` methods. They sum
the widths of grapheme clusters in the string or byte slice.
> Note: in your application, iterating over runes to measure width is likely incorrect;
the smallest unit of display is a grapheme, not a rune.
### Iterating over graphemes
If you need the individual graphemes:
```go
import (
"fmt"
"github.com/clipperhouse/displaywidth"
)
func main() {
g := displaywidth.StringGraphemes("Hello, 世界!")
for g.Next() {
width := g.Width()
value := g.Value()
// do something with the width or value
}
}
```
### Options
Create the options you need, and then use methods on the options struct.
```go
var myOptions = displaywidth.Options{
EastAsianWidth: true,
ControlSequences: true,
}
width := myOptions.String("Hello, 世界!")
```
#### ControlSequences
`ControlSequences` specifies whether to ignore ECMA-48 escape sequences
when calculating the display width. When `false` (default), ANSI escape
sequences are treated as just a series of characters. When `true`, they are
treated as a single zero-width unit.
#### ControlSequences8Bit
`ControlSequences8Bit` specifies whether to ignore 8-bit ECMA-48 escape sequences
when calculating the display width. When `false` (default), these are treated
as just a series of characters. When `true`, they are treated as a single
zero-width unit.
Note: this option is ignored by the `Truncate` methods, as the concatenation
can lead to unintended UTF-8 semantics.
#### EastAsianWidth
`EastAsianWidth` defines how
[East Asian Ambiguous characters](https://www.unicode.org/reports/tr11/#Ambiguous)
are treated.
When `false` (default), East Asian Ambiguous characters are treated as width 1.
When `true`, they are treated as width 2.
You may wish to configure this based on environment variables or locale.
`go-runewidth`, for example, does so
[during package initialization](https://github.com/mattn/go-runewidth/blob/master/runewidth.go#L26C1-L45C2). `displaywidth` does not do this automatically, we prefer to leave it to you.
## Technical standards and compatibility
This package implements the Unicode East Asian Width standard
([UAX #11](https://www.unicode.org/reports/tr11/tr11-43.html)), and handles
[version selectors](https://en.wikipedia.org/wiki/Variation_Selectors_(Unicode_block)),
and [regional indicator pairs](https://en.wikipedia.org/wiki/Regional_indicator_symbol)
(flags). We implement [Unicode TR51](https://www.unicode.org/reports/tr51/tr51-27.html)
for emojis. We are keeping an eye on
[emerging standards](https://www.jeffquast.com/post/state-of-terminal-emulation-2025/).
For control sequences, we implement the [ECMA-48](https://ecma-international.org/publications-and-standards/standards/ecma-48/) standard for 7-bit and 8-bit control sequences.
`clipperhouse/displaywidth`, `mattn/go-runewidth`, and `rivo/uniseg` will
give the same outputs for most real-world text. Extensive details are in the
[compatibility analysis](comparison/COMPATIBILITY_ANALYSIS.md).
## Invalid UTF-8
This package does not validate UTF-8. If you pass invalid UTF-8, the results
are undefined. We fuzz against invalid UTF-8 to ensure we don't panic or
loop indefinitely.
The `ControlSequences8Bit` option means that we will segment valid 8-bit
control sequences, which are typically _not_ valid UTF-8. 8-bit control bytes
happen to also be UTF-8 continuation bytes. Use with caution.
## Prior Art
[mattn/go-runewidth](https://github.com/mattn/go-runewidth)
[rivo/uniseg](https://github.com/rivo/uniseg)
[x/text/width](https://pkg.go.dev/golang.org/x/text/width)
[x/text/internal/triegen](https://pkg.go.dev/golang.org/x/text/internal/triegen)
## Benchmarks
```bash
cd comparison
go test -bench=. -benchmem
```
```
goos: darwin
goarch: arm64
pkg: github.com/clipperhouse/displaywidth/comparison
cpu: Apple M2
BenchmarkString_Mixed/clipperhouse/displaywidth-8 5784 ns/op 291.69 MB/s 0 B/op 0 allocs/op
BenchmarkString_Mixed/mattn/go-runewidth-8 14751 ns/op 114.36 MB/s 0 B/op 0 allocs/op
BenchmarkString_Mixed/rivo/uniseg-8 19360 ns/op 87.14 MB/s 0 B/op 0 allocs/op
BenchmarkString_ASCII/clipperhouse/displaywidth-8 54.60 ns/op 2344.32 MB/s 0 B/op 0 allocs/op
BenchmarkString_ASCII/mattn/go-runewidth-8 1195 ns/op 107.08 MB/s 0 B/op 0 allocs/op
BenchmarkString_ASCII/rivo/uniseg-8 1578 ns/op 81.13 MB/s 0 B/op 0 allocs/op
BenchmarkString_EastAsian/clipperhouse/displaywidth-8 5837 ns/op 289.01 MB/s 0 B/op 0 allocs/op
BenchmarkString_EastAsian/mattn/go-runewidth-8 24418 ns/op 69.09 MB/s 0 B/op 0 allocs/op
BenchmarkString_EastAsian/rivo/uniseg-8 19339 ns/op 87.23 MB/s 0 B/op 0 allocs/op
BenchmarkString_Emoji/clipperhouse/displaywidth-8 3225 ns/op 224.51 MB/s 0 B/op 0 allocs/op
BenchmarkString_Emoji/mattn/go-runewidth-8 4851 ns/op 149.25 MB/s 0 B/op 0 allocs/op
BenchmarkString_Emoji/rivo/uniseg-8 6591 ns/op 109.85 MB/s 0 B/op 0 allocs/op
BenchmarkRune_Mixed/clipperhouse/displaywidth-8 3385 ns/op 498.34 MB/s 0 B/op 0 allocs/op
BenchmarkRune_Mixed/mattn/go-runewidth-8 5354 ns/op 315.07 MB/s 0 B/op 0 allocs/op
BenchmarkRune_EastAsian/clipperhouse/displaywidth-8 3397 ns/op 496.56 MB/s 0 B/op 0 allocs/op
BenchmarkRune_EastAsian/mattn/go-runewidth-8 15673 ns/op 107.64 MB/s 0 B/op 0 allocs/op
BenchmarkRune_ASCII/clipperhouse/displaywidth-8 255.7 ns/op 500.53 MB/s 0 B/op 0 allocs/op
BenchmarkRune_ASCII/mattn/go-runewidth-8 261.5 ns/op 489.55 MB/s 0 B/op 0 allocs/op
BenchmarkRune_Emoji/clipperhouse/displaywidth-8 1371 ns/op 528.22 MB/s 0 B/op 0 allocs/op
BenchmarkRune_Emoji/mattn/go-runewidth-8 2267 ns/op 319.43 MB/s 0 B/op 0 allocs/op
BenchmarkTruncateWithTail/clipperhouse/displaywidth-8 3229 ns/op 54.82 MB/s 192 B/op 14 allocs/op
BenchmarkTruncateWithTail/mattn/go-runewidth-8 8408 ns/op 21.05 MB/s 192 B/op 14 allocs/op
BenchmarkTruncateWithoutTail/clipperhouse/displaywidth-8 3554 ns/op 64.43 MB/s 0 B/op 0 allocs/op
BenchmarkTruncateWithoutTail/mattn/go-runewidth-8 11189 ns/op 20.47 MB/s 0 B/op 0 allocs/op
```
Here are some notes on [how to make Unicode things fast](https://clipperhouse.com/go-unicode/).
+3
View File
@@ -0,0 +1,3 @@
package displaywidth
//go:generate go run -C internal/gen .
+73
View File
@@ -0,0 +1,73 @@
package displaywidth
import (
"github.com/clipperhouse/uax29/v2/graphemes"
)
// Graphemes is an iterator over grapheme clusters.
//
// Iterate using the Next method, and get the width of the current grapheme
// using the Width method.
type Graphemes[T ~string | []byte] struct {
iter *graphemes.Iterator[T]
options Options
}
// Next advances the iterator to the next grapheme cluster.
func (g *Graphemes[T]) Next() bool {
return g.iter.Next()
}
// Value returns the current grapheme cluster.
func (g *Graphemes[T]) Value() T {
return g.iter.Value()
}
// Width returns the display width of the current grapheme cluster.
func (g *Graphemes[T]) Width() int {
return graphemeWidth(g.Value(), g.options)
}
// StringGraphemes returns an iterator over grapheme clusters for the given
// string.
//
// Iterate using the Next method, and get the width of the current grapheme
// using the Width method.
func StringGraphemes(s string) Graphemes[string] {
return DefaultOptions.StringGraphemes(s)
}
// StringGraphemes returns an iterator over grapheme clusters for the given
// string, with the given options.
//
// Iterate using the Next method, and get the width of the current grapheme
// using the Width method.
func (options Options) StringGraphemes(s string) Graphemes[string] {
g := graphemes.FromString(s)
g.AnsiEscapeSequences = options.ControlSequences
g.AnsiEscapeSequences8Bit = options.ControlSequences8Bit
return Graphemes[string]{iter: g, options: options}
}
// BytesGraphemes returns an iterator over grapheme clusters for the given
// []byte.
//
// Iterate using the Next method, and get the width of the current grapheme
// using the Width method.
func BytesGraphemes(s []byte) Graphemes[[]byte] {
return DefaultOptions.BytesGraphemes(s)
}
// BytesGraphemes returns an iterator over grapheme clusters for the given
// []byte, with the given options.
//
// Iterate using the Next method, and get the width of the current grapheme
// using the Width method.
func (options Options) BytesGraphemes(s []byte) Graphemes[[]byte] {
g := graphemes.FromBytes(s)
g.AnsiEscapeSequences = options.ControlSequences
g.AnsiEscapeSequences8Bit = options.ControlSequences8Bit
return Graphemes[[]byte]{iter: g, options: options}
}
+30
View File
@@ -0,0 +1,30 @@
package displaywidth
// Options allows you to specify the treatment of ambiguous East Asian
// characters and ANSI escape sequences.
type Options struct {
// EastAsianWidth specifies whether to treat ambiguous East Asian characters
// as width 1 or 2. When false (default), ambiguous East Asian characters
// are treated as width 1. When true, they are width 2.
EastAsianWidth bool
// ControlSequences specifies whether to ignore 7-bit ECMA-48 escape sequences
// when calculating the display width. When false (default), ANSI escape
// sequences are treated as just a series of characters. When true, they are
// treated as a single zero-width unit.
ControlSequences bool
// ControlSequences8Bit specifies whether to ignore 8-bit ECMA-48 escape sequences
// when calculating the display width. When false (default), these are treated
// as just a series of characters. When true, they are treated as a single
// zero-width unit.
ControlSequences8Bit bool
}
// DefaultOptions is the default options for the display width
// calculation, which is EastAsianWidth false, ControlSequences false, and
// ControlSequences8Bit false.
var DefaultOptions = Options{
EastAsianWidth: false,
ControlSequences: false,
ControlSequences8Bit: false,
}
File diff suppressed because it is too large Load Diff
+149
View File
@@ -0,0 +1,149 @@
package displaywidth
import (
"strings"
"github.com/clipperhouse/uax29/v2/graphemes"
)
// TruncateString truncates a string to the given maxWidth, and appends the
// given tail if the string is truncated.
//
// It ensures the visible width, including the width of the tail, is less than or
// equal to maxWidth.
//
// When [Options.ControlSequences] is true, 7-bit ANSI escape sequences that
// appear after the truncation point are preserved in the output. This ensures
// that escape sequences such as SGR resets are not lost, preventing color
// bleed in terminal output.
//
// [Options.ControlSequences8Bit] is ignored by truncation. 8-bit C1 byte values
// (0x80-0x9F) overlap with UTF-8 multi-byte encoding, so manipulating them
// during truncation can shift byte boundaries and form unintended visible
// characters. Use [Options.String] or [Options.Bytes] for 8-bit-aware width
// measurement.
func (options Options) TruncateString(s string, maxWidth int, tail string) string {
// We deliberately ignore ControlSequences8Bit for truncation, see above.
options.ControlSequences8Bit = false
maxWidthWithoutTail := maxWidth - options.String(tail)
var pos, total int
g := graphemes.FromString(s)
g.AnsiEscapeSequences = options.ControlSequences
for g.Next() {
gw := graphemeWidth(g.Value(), options)
if total+gw <= maxWidthWithoutTail {
pos = g.End()
}
total += gw
if total > maxWidth {
if options.ControlSequences {
// Build result with trailing 7-bit ANSI escape sequences preserved
var b strings.Builder
b.Grow(len(s) + len(tail)) // at most original + tail
b.WriteString(s[:pos])
b.WriteString(tail)
rem := graphemes.FromString(s[pos:])
rem.AnsiEscapeSequences = options.ControlSequences
for rem.Next() {
v := rem.Value()
// Only preserve 7-bit escapes (ESC = 0x1B) that measure
// as zero-width on their own; some sequences (e.g. SOS)
// are only valid in their original context.
if len(v) > 0 && v[0] == 0x1B && options.String(v) == 0 {
b.WriteString(v)
}
}
return b.String()
}
return s[:pos] + tail
}
}
// No truncation
return s
}
// TruncateString truncates a string to the given maxWidth, and appends the
// given tail if the string is truncated.
//
// It ensures the total width, including the width of the tail, is less than or
// equal to maxWidth.
func TruncateString(s string, maxWidth int, tail string) string {
return DefaultOptions.TruncateString(s, maxWidth, tail)
}
// TruncateBytes truncates a []byte to the given maxWidth, and appends the
// given tail if the []byte is truncated.
//
// It ensures the visible width, including the width of the tail, is less than or
// equal to maxWidth.
//
// When [Options.ControlSequences] is true, 7-bit ANSI escape sequences that
// appear after the truncation point are preserved in the output. This ensures
// that escape sequences such as SGR resets are not lost, preventing color
// bleed in terminal output.
//
// [Options.ControlSequences8Bit] is ignored by truncation. 8-bit C1 byte values
// (0x80-0x9F) overlap with UTF-8 multi-byte encoding, so manipulating them
// during truncation can shift byte boundaries and form unintended visible
// characters. Use [Options.String] or [Options.Bytes] for 8-bit-aware width
// measurement.
func (options Options) TruncateBytes(s []byte, maxWidth int, tail []byte) []byte {
// We deliberately ignore ControlSequences8Bit for truncation, see above.
options.ControlSequences8Bit = false
maxWidthWithoutTail := maxWidth - options.Bytes(tail)
var pos, total int
g := graphemes.FromBytes(s)
g.AnsiEscapeSequences = options.ControlSequences
for g.Next() {
gw := graphemeWidth(g.Value(), options)
if total+gw <= maxWidthWithoutTail {
pos = g.End()
}
total += gw
if total > maxWidth {
if options.ControlSequences {
// Build result with trailing 7-bit ANSI escape sequences preserved
result := make([]byte, 0, len(s)+len(tail)) // at most original + tail
result = append(result, s[:pos]...)
result = append(result, tail...)
rem := graphemes.FromBytes(s[pos:])
rem.AnsiEscapeSequences = options.ControlSequences
for rem.Next() {
v := rem.Value()
// Only preserve 7-bit escapes (ESC = 0x1B) that measure
// as zero-width on their own; some sequences (e.g. SOS)
// are only valid in their original context.
if len(v) > 0 && v[0] == 0x1B && options.Bytes(v) == 0 {
result = append(result, v...)
}
}
return result
}
result := make([]byte, 0, pos+len(tail))
result = append(result, s[:pos]...)
result = append(result, tail...)
return result
}
}
// No truncation
return s
}
// TruncateBytes truncates a []byte to the given maxWidth, and appends the
// given tail if the []byte is truncated.
//
// It ensures the total width, including the width of the tail, is less than or
// equal to maxWidth.
func TruncateBytes(s []byte, maxWidth int, tail []byte) []byte {
return DefaultOptions.TruncateBytes(s, maxWidth, tail)
}
+239
View File
@@ -0,0 +1,239 @@
package displaywidth
import (
"unicode/utf8"
"github.com/clipperhouse/uax29/v2/graphemes"
)
// String calculates the display width of a string,
// by iterating over grapheme clusters in the string
// and summing their widths.
func String(s string) int {
return DefaultOptions.String(s)
}
// String calculates the display width of a string, for the given options, by
// iterating over grapheme clusters in the string and summing their widths.
func (options Options) String(s string) int {
width := 0
pos := 0
for pos < len(s) {
// Try ASCII optimization
asciiLen := printableASCIILength(s[pos:])
if asciiLen > 0 {
width += asciiLen
pos += asciiLen
continue
}
// Not ASCII, use grapheme parsing
g := graphemes.FromString(s[pos:])
g.AnsiEscapeSequences = options.ControlSequences
g.AnsiEscapeSequences8Bit = options.ControlSequences8Bit
start := pos
for g.Next() {
v := g.Value()
width += graphemeWidth(v, options)
pos += len(v)
// Quick check: if remaining might have printable ASCII, break to outer loop
if pos < len(s) && s[pos] >= 0x20 && s[pos] <= 0x7E {
break
}
}
// Defensive, should not happen: if no progress was made,
// skip a byte to prevent infinite loop. Only applies if
// the grapheme parser misbehaves.
if pos == start {
pos++
}
}
return width
}
// Bytes calculates the display width of a []byte,
// by iterating over grapheme clusters in the byte slice
// and summing their widths.
func Bytes(s []byte) int {
return DefaultOptions.Bytes(s)
}
// Bytes calculates the display width of a []byte, for the given options, by
// iterating over grapheme clusters in the slice and summing their widths.
func (options Options) Bytes(s []byte) int {
width := 0
pos := 0
for pos < len(s) {
// Try ASCII optimization
asciiLen := printableASCIILength(s[pos:])
if asciiLen > 0 {
width += asciiLen
pos += asciiLen
continue
}
// Not ASCII, use grapheme parsing
g := graphemes.FromBytes(s[pos:])
g.AnsiEscapeSequences = options.ControlSequences
g.AnsiEscapeSequences8Bit = options.ControlSequences8Bit
start := pos
for g.Next() {
v := g.Value()
width += graphemeWidth(v, options)
pos += len(v)
// Quick check: if remaining might have printable ASCII, break to outer loop
if pos < len(s) && s[pos] >= 0x20 && s[pos] <= 0x7E {
break
}
}
// Defensive, should not happen: if no progress was made,
// skip a byte to prevent infinite loop. Only applies if
// the grapheme parser misbehaves.
if pos == start {
pos++
}
}
return width
}
// Rune calculates the display width of a rune. You
// should almost certainly use [String] or [Bytes] for
// most purposes.
//
// The smallest unit of display width is a grapheme
// cluster, not a rune. Iterating over runes to measure
// width is incorrect in many cases.
func Rune(r rune) int {
return DefaultOptions.Rune(r)
}
// Rune calculates the display width of a rune, for the given options.
//
// You should almost certainly use [String] or [Bytes] for most purposes.
//
// The smallest unit of display width is a grapheme cluster, not a rune.
// Iterating over runes to measure width is incorrect in many cases.
func (options Options) Rune(r rune) int {
if r < utf8.RuneSelf {
return asciiWidth(byte(r))
}
// Surrogates (U+D800-U+DFFF) are invalid UTF-8.
if r >= 0xD800 && r <= 0xDFFF {
return 0
}
var buf [4]byte
n := utf8.EncodeRune(buf[:], r)
// Skip the grapheme iterator
return graphemeWidth(buf[:n], options)
}
const _Default property = 0
// graphemeWidth returns the display width of a grapheme cluster.
// The passed string must be a single grapheme cluster.
func graphemeWidth[T ~string | []byte](s T, options Options) int {
if len(s) == 0 {
return 0
}
// C1 controls (0x80-0x9F) are zero-width when 8-bit control sequences
// are enabled. This must be checked before the single-byte optimization
// below, which would otherwise return width 1 for these bytes.
if options.ControlSequences8Bit && s[0] >= 0x80 && s[0] <= 0x9F {
return 0
}
// Optimization: single-byte graphemes need no property lookup
if len(s) == 1 {
return asciiWidth(s[0])
}
// Multi-byte grapheme clusters led by a C0 control (0x00-0x1F)
if s[0] <= 0x1F {
return 0
}
p, sz := lookup(s)
prop := property(p)
// Variation Selector 16 (VS16) requests emoji presentation
if prop != _Wide && sz > 0 && len(s) >= sz+3 {
vs := s[sz : sz+3]
if isVS16(vs) {
prop = _Wide
}
// VS15 (0x8E) requests text presentation but does not affect width,
// in my reading of Unicode TR51. Falls through to return the base
// character's property.
}
if options.EastAsianWidth && prop == _East_Asian_Ambiguous {
prop = _Wide
}
if prop > upperBound {
prop = _Default
}
return propertyWidths[prop]
}
func asciiWidth(b byte) int {
if b <= 0x1F || b == 0x7F {
return 0
}
return 1
}
// printableASCIILength returns the length of consecutive printable ASCII bytes
// starting at the beginning of s.
func printableASCIILength[T string | []byte](s T) int {
i := 0
for ; i < len(s); i++ {
b := s[i]
// Printable ASCII is 0x20-0x7E (space through tilde)
if b < 0x20 || b > 0x7E {
break
}
}
// If the next byte is non-ASCII (>= 0x80), back off by 1. The grapheme
// parser may group the last ASCII byte with subsequent non-ASCII bytes,
// such as combining marks.
if i > 0 && i < len(s) && s[i] >= 0x80 {
i--
}
return i
}
// isVS16 checks if the slice matches VS16 (U+FE0F) UTF-8 encoding
// (EF B8 8F). It assumes len(s) >= 3.
func isVS16[T ~string | []byte](s T) bool {
return s[0] == 0xEF && s[1] == 0xB8 && s[2] == 0x8F
}
// propertyWidths is a jump table of sorts, instead of a switch
var propertyWidths = [4]int{
_Default: 1,
_Zero_Width: 0,
_Wide: 2,
_East_Asian_Ambiguous: 1,
}
const upperBound = property(len(propertyWidths) - 1)
+21
View File
@@ -0,0 +1,21 @@
MIT License
Copyright (c) 2020 Matt Sherman
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.
+120
View File
@@ -0,0 +1,120 @@
An implementation of grapheme cluster boundaries from [Unicode text segmentation](https://unicode.org/reports/tr29/#Grapheme_Cluster_Boundaries) (UAX 29), for Unicode 17.
[![Documentation](https://pkg.go.dev/badge/github.com/clipperhouse/uax29/v2/graphemes.svg)](https://pkg.go.dev/github.com/clipperhouse/uax29/v2/graphemes)
![Tests](https://github.com/clipperhouse/uax29/actions/workflows/gotest.yml/badge.svg)
![Fuzz](https://github.com/clipperhouse/uax29/actions/workflows/gofuzz.yml/badge.svg)
## Quick start
```
go get github.com/clipperhouse/uax29/v2/graphemes
```
```go
import "github.com/clipperhouse/uax29/v2/graphemes"
text := "Hello, 世界. Nice dog! 👍🐶"
g := graphemes.FromString(text)
for g.Next() { // Next() returns true until end of data
fmt.Println(g.Value()) // Do something with the current grapheme
}
```
_A grapheme is a “single visible character”, which might be a simple as a single letter, or a complex emoji that consists of several Unicode code points._
## Conformance
We use the Unicode [test suite](https://unicode.org/reports/tr41/tr41-36.html#Tests29).
![Tests](https://github.com/clipperhouse/uax29/actions/workflows/gotest.yml/badge.svg)
![Fuzz](https://github.com/clipperhouse/uax29/actions/workflows/gofuzz.yml/badge.svg)
## APIs
### If you have a `string`
```go
text := "Hello, 世界. Nice dog! 👍🐶"
g := graphemes.FromString(text)
for g.Next() { // Next() returns true until end of data
fmt.Println(g.Value()) // Do something with the current grapheme
}
```
### If you have an `io.Reader`
`FromReader` embeds a [`bufio.Scanner`](https://pkg.go.dev/bufio#Scanner), so just use those methods.
```go
r := getYourReader() // from a file or network maybe
g := graphemes.FromReader(r)
for g.Scan() { // Scan() returns true until error or EOF
fmt.Println(g.Text()) // Do something with the current grapheme
}
if g.Err() != nil { // Check the error
log.Fatal(g.Err())
}
```
### If you have a `[]byte`
```go
b := []byte("Hello, 世界. Nice dog! 👍🐶")
g := graphemes.FromBytes(b)
for g.Next() { // Next() returns true until end of data
fmt.Println(g.Value()) // Do something with the current grapheme
}
```
### ANSI escape sequences
By the UAX 29 specification, ANSI escape sequences are not grapheme clusters. To treat 7-bit ANSI escape sequences as a single cluster, set `AnsiEscapeSequences` to true.
```go
text := "Hello, \x1b[31mworld\x1b[0m!"
g := graphemes.FromString(text)
g.AnsiEscapeSequences = true
for g.Next() {
fmt.Println(g.Value())
}
```
To also parse 8-bit C1 controls (non-UTF-8 bytes), set `AnsiEscapeSequences8Bit` to true.
```go
g.AnsiEscapeSequences = true // 7-bit forms (ESC ...)
g.AnsiEscapeSequences8Bit = true // 8-bit C1 forms (0x80-0x9F), not valid UTF-8
```
For ESC-initiated (7-bit) control strings, only 7-bit terminators are recognized.
For C1-initiated (8-bit) control strings, only C1 ST (`0x9C`) is recognized as ST.
We implement [ECMA-48](https://ecma-international.org/publications-and-standards/standards/ecma-48/) control codes in both 7-bit and 8-bit representations. 8-bit control codes are not UTF-8 encoded and are not valid UTF-8, caveat emptor.
### Benchmarks
```
goos: darwin
goarch: arm64
pkg: github.com/clipperhouse/uax29/graphemes/comparative
cpu: Apple M2
BenchmarkGraphemesMixed/clipperhouse/uax29-8 142635 ns/op 245.12 MB/s 0 B/op 0 allocs/op
BenchmarkGraphemesMixed/rivo/uniseg-8 2018284 ns/op 17.32 MB/s 0 B/op 0 allocs/op
BenchmarkGraphemesASCII/clipperhouse/uax29-8 8846 ns/op 508.73 MB/s 0 B/op 0 allocs/op
BenchmarkGraphemesASCII/rivo/uniseg-8 366760 ns/op 12.27 MB/s 0 B/op 0 allocs/op
```
### Invalid inputs
Invalid UTF-8 input is considered undefined behavior. We test to ensure that bad inputs will not cause pathological outcomes, such as a panic or infinite loop. Callers should expect “garbage-in, garbage-out”.
Your pipeline should probably include a call to [`utf8.Valid()`](https://pkg.go.dev/unicode/utf8#Valid).
+138
View File
@@ -0,0 +1,138 @@
package graphemes
// ansiEscapeLength returns the byte length of a valid 7-bit ANSI escape
// sequence at the start of data, or 0 if none.
//
// Recognized forms (ECMA-48 / ISO 6429):
// - CSI: ESC [ then parameter bytes (0x30-0x3F), intermediate (0x20-0x2F), final (0x40-0x7E)
// - OSC: ESC ] then payload until BEL (0x07), 7-bit ST (ESC \), CAN (0x18), or SUB (0x1A)
// - DCS, SOS, PM, APC: ESC P/X/^/_ then payload until 7-bit ST (ESC \), CAN, or SUB
// - Two-byte: ESC + Fe/Fs (0x40-0x7E excluding above), or Fp (0x30-0x3F), or nF (0x20-0x2F then final)
func ansiEscapeLength[T ~string | ~[]byte](data T) int {
n := len(data)
if n < 2 || data[0] != esc {
return 0
}
b1 := data[1]
switch b1 {
case '[': // CSI
body := csiBodyLength(data[2:])
if body == 0 {
return 0
}
return 2 + body
case ']': // OSC - allows BEL or 7-bit ST terminator
body := oscLength(data[2:])
if body < 0 {
return 0
}
return 2 + body
case 'P', 'X', '^', '_': // DCS, SOS, PM, APC
body := stSequenceLength(data[2:])
if body < 0 {
return 0
}
return 2 + body
}
if b1 >= 0x40 && b1 <= 0x7E {
// Fe/Fs two-byte; [ ] P X ^ _ handled above
return 2
}
if b1 >= 0x30 && b1 <= 0x3F {
// Fp (private) two-byte
return 2
}
if b1 >= 0x20 && b1 <= 0x2F {
// nF: intermediates then one final (0x30-0x7E)
i := 2
for i < n && data[i] >= 0x20 && data[i] <= 0x2F {
i++
}
if i < n && data[i] >= 0x30 && data[i] <= 0x7E {
return i + 1
}
return 0
}
return 0
}
// csiBodyLength returns the length of the CSI body (param/intermediate/final bytes).
// data is the slice after "ESC [".
// Per ECMA-48, the CSI body has the form:
//
// parameters (0x300x3F)*, intermediates (0x200x2F)*, final (0x400x7E)
//
// Once an intermediate byte is seen, subsequent parameter bytes are invalid.
func csiBodyLength[T ~string | ~[]byte](data T) int {
seenIntermediate := false
for i := 0; i < len(data); i++ {
b := data[i]
if b >= 0x30 && b <= 0x3F {
if seenIntermediate {
return 0
}
continue
}
if b >= 0x20 && b <= 0x2F {
seenIntermediate = true
continue
}
if b >= 0x40 && b <= 0x7E {
return i + 1
}
return 0
}
return 0
}
// oscLength returns the length of the OSC body.
// data is the slice after "ESC ]".
//
// Returns:
// - n >= 0: consumed body length (includes BEL/ST terminator when present)
// - -1: not terminated in the provided data
//
// OSC accepts BEL (0x07) or 7-bit ST (ESC \) as terminators by widespread convention.
// Per ECMA-48, CAN (0x18) and SUB (0x1A) cancel the control string; in that
// case they are not part of the OSC sequence length.
func oscLength[T ~string | ~[]byte](data T) int {
for i := 0; i < len(data); i++ {
b := data[i]
if b == bel {
return i + 1
}
if b == can || b == sub {
return i
}
if b == esc && i+1 < len(data) && data[i+1] == '\\' {
return i + 2
}
}
return -1
}
// stSequenceLength returns the length of a control-string body.
// data is the slice after "ESC x".
//
// Returns:
// - n >= 0: consumed body length (includes ST terminator when present)
// - -1: not terminated in the provided data
//
// Used for DCS, SOS, PM, and APC, which per ECMA-48 terminate with ST.
// ST here is the 7-bit form (ESC \).
// CAN (0x18) and SUB (0x1A) cancel the control string; in that case they are
// not part of the sequence length.
func stSequenceLength[T ~string | ~[]byte](data T) int {
for i := 0; i < len(data); i++ {
if data[i] == can || data[i] == sub {
return i
}
if data[i] == esc && i+1 < len(data) && data[i+1] == '\\' {
return i + 2
}
}
return -1
}
+79
View File
@@ -0,0 +1,79 @@
package graphemes
// ansiEscapeLength8Bit returns the byte length of a valid 8-bit C1 ANSI
// sequence at the start of data, or 0 if none.
//
// Recognized forms (ECMA-48 / ISO 6429):
// - C1 CSI (0x9B) body as parameter/intermediate/final bytes
// - C1 OSC (0x9D) body terminated by BEL, C1 ST, CAN, or SUB
// - C1 DCS/SOS/PM/APC (0x90/0x98/0x9E/0x9F) body terminated by C1 ST, CAN, or SUB
// - Standalone C1 controls (0x80..0x9F not listed above): single byte
func ansiEscapeLength8Bit[T ~string | ~[]byte](data T) int {
if len(data) == 0 {
return 0
}
switch data[0] {
case 0x9B: // C1 CSI
body := csiBodyLength(data[1:])
if body == 0 {
return 0
}
return 1 + body
case 0x9D: // C1 OSC
body := oscLengthC1(data[1:])
if body < 0 {
return 0
}
return 1 + body
case 0x90, 0x98, 0x9E, 0x9F: // C1 DCS, SOS, PM, APC
body := stSequenceLengthC1(data[1:])
if body < 0 {
return 0
}
return 1 + body
default:
if data[0] >= 0x80 && data[0] <= 0x9F {
return 1
}
}
return 0
}
// oscLengthC1 returns the length of a C1 OSC body.
// data is the slice after the C1 OSC initiator (0x9D).
//
// Returns:
// - n >= 0: consumed body length (includes BEL/ST terminator when present)
// - -1: not terminated in the provided data
//
// Terminators: BEL (0x07) or C1 ST (0x9C).
// CAN (0x18) and SUB (0x1A) cancel the control string.
func oscLengthC1[T ~string | ~[]byte](data T) int {
for i := 0; i < len(data); i++ {
b := data[i]
if b == bel || b == st {
return i + 1
}
if b == can || b == sub {
return i
}
}
return -1
}
// stSequenceLengthC1 parses DCS/SOS/PM/APC bodies that terminate with C1 ST
// (0x9C), or are canceled by CAN/SUB.
func stSequenceLengthC1[T ~string | ~[]byte](data T) int {
for i := 0; i < len(data); i++ {
b := data[i]
if b == can || b == sub {
return i
}
if b == st {
return i + 1
}
}
return -1
}
+144
View File
@@ -0,0 +1,144 @@
package graphemes
import "unicode/utf8"
// FromString returns an iterator for the grapheme clusters in the input string.
// Iterate while Next() is true, and access the grapheme via Value().
func FromString(s string) *Iterator[string] {
return &Iterator[string]{
split: splitFuncString,
data: s,
}
}
// FromBytes returns an iterator for the grapheme clusters in the input bytes.
// Iterate while Next() is true, and access the grapheme via Value().
func FromBytes(b []byte) *Iterator[[]byte] {
return &Iterator[[]byte]{
split: splitFuncBytes,
data: b,
}
}
// Iterator is a generic iterator for grapheme clusters in strings or byte slices,
// with an ASCII hot path optimization.
type Iterator[T ~string | ~[]byte] struct {
split func(T, bool) (int, T, error)
data T
pos int
start int
// AnsiEscapeSequences treats 7-bit ANSI escape sequences (ECMA-48) as
// single grapheme clusters when true. The default is false.
//
// 8-bit controls are not enabled by this option. See [AnsiEscapeSequences8Bit].
AnsiEscapeSequences bool
// AnsiEscapeSequences8Bit treats 8-bit C1 ANSI escape sequences (ECMA-48) as single
// grapheme clusters when true. The default is false.
//
// 8-bit control bytes are not UTF-8 encoded, i.e. not valid UTF-8. If you
// choose this option, you are choosing to interpret non-UTF-8 data, caveat
// emptor.
AnsiEscapeSequences8Bit bool
}
var (
splitFuncString = splitFunc[string]
splitFuncBytes = splitFunc[[]byte]
)
const (
esc = 0x1B
cr = 0x0D
bel = 0x07
can = 0x18
sub = 0x1A
st = 0x9C
)
// Next advances the iterator to the next grapheme cluster.
// Returns false when there are no more grapheme clusters.
func (iter *Iterator[T]) Next() bool {
if iter.pos >= len(iter.data) {
return false
}
iter.start = iter.pos
b := iter.data[iter.pos]
if iter.AnsiEscapeSequences && b == esc {
if a := ansiEscapeLength(iter.data[iter.pos:]); a > 0 {
iter.pos += a
return true
}
}
if iter.AnsiEscapeSequences8Bit && b >= 0x80 && b <= 0x9F {
if a := ansiEscapeLength8Bit(iter.data[iter.pos:]); a > 0 {
iter.pos += a
return true
}
}
// ASCII hot path: any ASCII is one grapheme when next byte is ASCII or end.
if b < utf8.RuneSelf && b != cr {
if iter.pos+1 >= len(iter.data) || iter.data[iter.pos+1] < utf8.RuneSelf {
iter.pos++
return true
}
}
// Fall back to UAX29 grapheme parsing
remaining := iter.data[iter.pos:]
advance, _, err := iter.split(remaining, true)
if err != nil {
panic(err)
}
if advance <= 0 {
panic("splitFunc returned a zero or negative advance")
}
iter.pos += advance
if iter.pos > len(iter.data) {
panic("splitFunc advanced beyond end of data")
}
return true
}
// Value returns the current grapheme cluster.
func (iter *Iterator[T]) Value() T {
return iter.data[iter.start:iter.pos]
}
// Start returns the byte position of the current grapheme in the original data.
func (iter *Iterator[T]) Start() int {
return iter.start
}
// End returns the byte position after the current grapheme in the original data.
func (iter *Iterator[T]) End() int {
return iter.pos
}
// Reset resets the iterator to the beginning of the data.
func (iter *Iterator[T]) Reset() {
iter.start = 0
iter.pos = 0
}
// SetText sets the data for the iterator to operate on, and resets all state.
func (iter *Iterator[T]) SetText(data T) {
iter.data = data
iter.start = 0
iter.pos = 0
}
// First returns the first grapheme cluster without advancing the iterator.
func (iter *Iterator[T]) First() T {
if len(iter.data) == 0 {
return iter.data
}
// Use a copy to leverage Next()'s ASCII optimization
cp := *iter
cp.pos = 0
cp.start = 0
cp.Next()
return cp.Value()
}
+25
View File
@@ -0,0 +1,25 @@
// Package graphemes implements Unicode grapheme cluster boundaries: https://unicode.org/reports/tr29/#Grapheme_Cluster_Boundaries
package graphemes
import (
"bufio"
"io"
)
type Scanner struct {
*bufio.Scanner
}
// FromReader returns a Scanner, to split graphemes per
// https://unicode.org/reports/tr29/#Grapheme_Cluster_Boundaries.
//
// It embeds a [bufio.Scanner], so you can use its methods.
//
// Iterate through graphemes by calling Scan() until false, then check Err().
func FromReader(r io.Reader) *Scanner {
sc := bufio.NewScanner(r)
sc.Split(SplitFunc)
return &Scanner{
Scanner: sc,
}
}
+205
View File
@@ -0,0 +1,205 @@
package graphemes
import (
"bufio"
)
// is determines if lookup intersects propert(ies)
func (lookup property) is(properties property) bool {
return (lookup & properties) != 0
}
const _Ignore = _Extend
// incbState tracks state for GB9c rule (Indic conjunct clusters)
// Pattern: Consonant (Extend|Linker)* Linker (Extend|Linker)* × Consonant
type incbState int
const (
incbNone incbState = iota // initial/reset
incbConsonant // seen Consonant, awaiting Linker
incbLinker // seen Consonant and Linker (conjunct ready)
)
// SplitFunc is a bufio.SplitFunc implementation of Unicode grapheme cluster segmentation, for use with bufio.Scanner.
//
// See https://unicode.org/reports/tr29/#Grapheme_Cluster_Boundaries.
var SplitFunc bufio.SplitFunc = splitFunc[[]byte]
func splitFunc[T ~string | ~[]byte](data T, atEOF bool) (advance int, token T, err error) {
var empty T
if len(data) == 0 {
return 0, empty, nil
}
// These vars are stateful across loop iterations
var pos int
var lastExIgnore property = 0 // "last excluding ignored categories"
var lastLastExIgnore property = 0 // "last one before that"
var regionalIndicatorCount int
// GB9c state: tracking Indic conjunct clusters
var incb incbState
// Rules are usually of the form Cat1 × Cat2; "current" refers to the first property
// to the right of the ×, from which we look back or forward
current, w := lookup(data[pos:])
if w == 0 {
if !atEOF {
// Rune extends past current data, request more
return 0, empty, nil
}
pos = len(data)
return pos, data[:pos], nil
}
// https://unicode.org/reports/tr29/#GB1
// Start of text always advances
pos += w
for {
eot := pos == len(data) // "end of text"
if eot {
if !atEOF {
// Token extends past current data, request more
return 0, empty, nil
}
// https://unicode.org/reports/tr29/#GB2
break
}
/*
We've switched the evaluation order of GB1↓ and GB2↑. It's ok:
because we've checked for len(data) at the top of this function,
sot and eot are mutually exclusive, order doesn't matter.
*/
// Rules are usually of the form Cat1 × Cat2; "current" refers to the first property
// to the right of the ×, from which we look back or forward
// Remember previous properties to avoid lookups/lookbacks
last := current
if !last.is(_Ignore) {
lastLastExIgnore = lastExIgnore
lastExIgnore = last
}
// Update GB9c state based on what we just advanced past
if last.is(_InCBConsonant | _InCBLinker | _InCBExtend) {
switch {
case last.is(_InCBConsonant):
if incb != incbLinker {
incb = incbConsonant
}
case last.is(_InCBLinker):
if incb >= incbConsonant {
incb = incbLinker
}
// case last.is(_InCBExtend): stay in current state
}
} else {
incb = incbNone
}
current, w = lookup(data[pos:])
if w == 0 {
if atEOF {
// Just return the bytes, we can't do anything with them
pos = len(data)
break
}
// Rune extends past current data, request more
return 0, empty, nil
}
// Optimization: no rule can possibly apply
if current|last == 0 { // i.e. both are zero
break
}
// https://unicode.org/reports/tr29/#GB3
if current.is(_LF) && last.is(_CR) {
pos += w
continue
}
// https://unicode.org/reports/tr29/#GB4
// https://unicode.org/reports/tr29/#GB5
if (current | last).is(_Control | _CR | _LF) {
break
}
// https://unicode.org/reports/tr29/#GB6
if current.is(_L|_V|_LV|_LVT) && last.is(_L) {
pos += w
continue
}
// https://unicode.org/reports/tr29/#GB7
if current.is(_V|_T) && last.is(_LV|_V) {
pos += w
continue
}
// https://unicode.org/reports/tr29/#GB8
if current.is(_T) && last.is(_LVT|_T) {
pos += w
continue
}
// https://unicode.org/reports/tr29/#GB9
if current.is(_Extend | _ZWJ) {
pos += w
continue
}
// https://unicode.org/reports/tr29/#GB9a
if current.is(_SpacingMark) {
pos += w
continue
}
// https://unicode.org/reports/tr29/#GB9b
if last.is(_Prepend) {
pos += w
continue
}
// https://unicode.org/reports/tr29/#GB9c
// Do not break within certain combinations with Indic_Conjunct_Break (InCB)=Linker.
if incb == incbLinker && current.is(_InCBConsonant) {
// After matching the pattern, reset state to start tracking a new pattern
// The current Consonant becomes the start of the new pattern
incb = incbConsonant
pos += w
continue
}
// https://unicode.org/reports/tr29/#GB11
if current.is(_ExtendedPictographic) && last.is(_ZWJ) && lastLastExIgnore.is(_ExtendedPictographic) {
pos += w
continue
}
// https://unicode.org/reports/tr29/#GB12
// https://unicode.org/reports/tr29/#GB13
if (current & last).is(_RegionalIndicator) {
regionalIndicatorCount++
odd := regionalIndicatorCount%2 == 1
if odd {
pos += w
continue
}
}
// If we fall through all the above rules, it's a grapheme cluster break
break
}
// Return token
return pos, data[:pos], nil
}
File diff suppressed because it is too large Load Diff
-1128
View File
File diff suppressed because it is too large Load Diff
-24
View File
@@ -1,24 +0,0 @@
//go:build !windows
// +build !windows
// Copyright 2015 The TCell Authors
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use file except in compliance with the License.
// You may obtain a copy of the license at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
package tcell
// NewConsoleScreen returns a console based screen. This platform
// doesn't have support for any, so it returns nil and a suitable error.
func NewConsoleScreen() (Screen, error) {
return nil, ErrNoScreen
}
-1275
View File
File diff suppressed because it is too large Load Diff
-47
View File
@@ -1,47 +0,0 @@
// Copyright 2018 The TCell Authors
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use file except in compliance with the License.
// You may obtain a copy of the license at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
// Package tcell provides a lower-level, portable API for building
// programs that interact with terminals or consoles. It works with
// both common (and many uncommon!) terminals or terminal emulators,
// and Windows console implementations.
//
// It provides support for up to 256 colors, text attributes, and box drawing
// elements. A database of terminals built from a real terminfo database
// is provided, along with code to generate new database entries.
//
// Tcell offers very rich support for mice, dependent upon the terminal
// of course. (Windows, XTerm, and iTerm 2 are known to work very well.)
//
// If the environment is not Unicode by default, such as an ISO8859 based
// locale or GB18030, Tcell can convert input and output, so that your
// terminal can operate in whatever locale is most convenient, while the
// application program can just assume "everything is UTF-8". Reasonable
// defaults are used for updating characters to something suitable for
// display. Unicode box drawing characters will be converted to use the
// alternate character set of your terminal, if native conversions are
// not available. If no ACS is available, then some ASCII fallbacks will
// be used.
//
// Note that support for non-UTF-8 locales (other than C) must be enabled
// by the application using RegisterEncoding() -- we don't have them all
// enabled by default to avoid bloating the application unnecessarily.
// (These days UTF-8 is good enough for almost everyone, and nobody should
// be using legacy locales anymore.) Also, actual glyphs for various code
// point will only be displayed if your terminal or emulator (or the font
// the emulator is using) supports them.
//
// A rich set of key codes is supported, with support for up to 65 function
// keys, and various other special keys.
package tcell
-535
View File
@@ -1,535 +0,0 @@
// Copyright 2024 The TCell Authors
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use file except in compliance with the License.
// You may obtain a copy of the license at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
package tcell
import (
"sync"
"unicode/utf8"
"golang.org/x/text/transform"
)
// NewSimulationScreen returns a SimulationScreen. Note that
// SimulationScreen is also a Screen.
func NewSimulationScreen(charset string) SimulationScreen {
if charset == "" {
charset = "UTF-8"
}
ss := &simscreen{charset: charset}
ss.Screen = &baseScreen{screenImpl: ss}
return ss
}
// SimulationScreen represents a screen simulation. This is intended to
// be a superset of normal Screens, but also adds some important interfaces
// for testing.
type SimulationScreen interface {
Screen
// InjectKeyBytes injects a stream of bytes corresponding to
// the native encoding (see charset). It turns true if the entire
// set of bytes were processed and delivered as KeyEvents, false
// if any bytes were not fully understood. Any bytes that are not
// fully converted are discarded.
InjectKeyBytes(buf []byte) bool
// InjectKey injects a key event. The rune is a UTF-8 rune, post
// any translation.
InjectKey(key Key, r rune, mod ModMask)
// InjectMouse injects a mouse event.
InjectMouse(x, y int, buttons ButtonMask, mod ModMask)
// GetContents returns screen contents as an array of
// cells, along with the physical width & height. Note that the
// physical contents will be used until the next time SetSize()
// is called.
GetContents() (cells []SimCell, width int, height int)
// GetCursor returns the cursor details.
GetCursor() (x int, y int, visible bool)
// GetTitle gets the previously set title.
GetTitle() string
// GetClipboardData gets the actual data for the clipboard.
GetClipboardData() []byte
}
// SimCell represents a simulated screen cell. The purpose of this
// is to track on screen content.
type SimCell struct {
// Bytes is the actual character bytes. Normally this is
// rune data, but it could be be data in another encoding system.
Bytes []byte
// Style is the style used to display the data.
Style Style
// Runes is the list of runes, unadulterated, in UTF-8.
Runes []rune
}
type simscreen struct {
physw int
physh int
fini bool
style Style
evch chan Event
quit chan struct{}
front []SimCell
back CellBuffer
clear bool
cursorx int
cursory int
cursorvis bool
mouse bool
paste bool
charset string
encoder transform.Transformer
decoder transform.Transformer
fillchar rune
fillstyle Style
fallback map[rune]string
title string
clipboard []byte
Screen
sync.Mutex
}
func (s *simscreen) Init() error {
s.evch = make(chan Event, 10)
s.quit = make(chan struct{})
s.fillchar = 'X'
s.fillstyle = StyleDefault
s.mouse = false
s.physw = 80
s.physh = 25
s.cursorx = -1
s.cursory = -1
s.style = StyleDefault
if enc := GetEncoding(s.charset); enc != nil {
s.encoder = enc.NewEncoder()
s.decoder = enc.NewDecoder()
} else {
return ErrNoCharset
}
s.front = make([]SimCell, s.physw*s.physh)
s.back.Resize(80, 25)
// default fallbacks
s.fallback = make(map[rune]string)
for k, v := range RuneFallbacks {
s.fallback[k] = v
}
return nil
}
func (s *simscreen) Fini() {
s.Lock()
s.fini = true
s.back.Resize(0, 0)
s.Unlock()
if s.quit != nil {
close(s.quit)
}
s.physw = 0
s.physh = 0
s.front = nil
}
func (s *simscreen) SetStyle(style Style) {
s.Lock()
s.style = style
s.Unlock()
}
func (s *simscreen) drawCell(x, y int) int {
mainc, combc, style, width := s.back.GetContent(x, y)
if !s.back.Dirty(x, y) {
return width
}
if x >= s.physw || y >= s.physh || x < 0 || y < 0 {
return width
}
simc := &s.front[(y*s.physw)+x]
if style == StyleDefault {
style = s.style
}
simc.Style = style
simc.Runes = append([]rune{mainc}, combc...)
// now emit runes - taking care to not overrun width with a
// wide character, and to ensure that we emit exactly one regular
// character followed up by any residual combing characters
simc.Bytes = nil
if x > s.physw-width {
simc.Runes = []rune{' '}
simc.Bytes = []byte{' '}
return width
}
lbuf := make([]byte, 12)
ubuf := make([]byte, 12)
nout := 0
for _, r := range simc.Runes {
l := utf8.EncodeRune(ubuf, r)
nout, _, _ = s.encoder.Transform(lbuf, ubuf[:l], true)
if nout == 0 || lbuf[0] == '\x1a' {
// skip combining
if subst, ok := s.fallback[r]; ok {
simc.Bytes = append(simc.Bytes,
[]byte(subst)...)
} else if r >= ' ' && r <= '~' {
simc.Bytes = append(simc.Bytes, byte(r))
} else if simc.Bytes == nil {
simc.Bytes = append(simc.Bytes, '?')
}
} else {
simc.Bytes = append(simc.Bytes, lbuf[:nout]...)
}
}
s.back.SetDirty(x, y, false)
return width
}
func (s *simscreen) ShowCursor(x, y int) {
s.Lock()
s.cursorx, s.cursory = x, y
s.showCursor()
s.Unlock()
}
func (s *simscreen) HideCursor() {
s.ShowCursor(-1, -1)
}
func (s *simscreen) showCursor() {
x, y := s.cursorx, s.cursory
if x < 0 || y < 0 || x >= s.physw || y >= s.physh {
s.cursorvis = false
} else {
s.cursorvis = true
}
}
func (s *simscreen) hideCursor() {
// does not update cursor position
s.cursorvis = false
}
func (s *simscreen) SetCursor(CursorStyle, Color) {}
func (s *simscreen) Show() {
s.Lock()
s.resize()
s.draw()
s.Unlock()
}
func (s *simscreen) clearScreen() {
// We emulate a hardware clear by filling with a specific pattern
for i := range s.front {
s.front[i].Style = s.fillstyle
s.front[i].Runes = []rune{s.fillchar}
s.front[i].Bytes = []byte{byte(s.fillchar)}
}
s.clear = false
}
func (s *simscreen) draw() {
s.hideCursor()
if s.clear {
s.clearScreen()
}
w, h := s.back.Size()
for y := 0; y < h; y++ {
for x := 0; x < w; x++ {
width := s.drawCell(x, y)
x += width - 1
}
}
s.showCursor()
}
func (s *simscreen) EnableMouse(...MouseFlags) {
s.mouse = true
}
func (s *simscreen) DisableMouse() {
s.mouse = false
}
func (s *simscreen) EnablePaste() {
s.paste = true
}
func (s *simscreen) DisablePaste() {
s.paste = false
}
func (s *simscreen) EnableFocus() {
}
func (s *simscreen) DisableFocus() {
}
func (s *simscreen) Size() (int, int) {
s.Lock()
w, h := s.back.Size()
s.Unlock()
return w, h
}
func (s *simscreen) resize() {
w, h := s.physw, s.physh
ow, oh := s.back.Size()
if w != ow || h != oh {
s.back.Resize(w, h)
ev := NewEventResize(w, h)
s.postEvent(ev)
}
}
func (s *simscreen) Colors() int {
return 256
}
func (s *simscreen) postEvent(ev Event) {
select {
case s.evch <- ev:
case <-s.quit:
}
}
func (s *simscreen) InjectMouse(x, y int, buttons ButtonMask, mod ModMask) {
ev := NewEventMouse(x, y, buttons, mod)
s.postEvent(ev)
}
func (s *simscreen) InjectKey(key Key, r rune, mod ModMask) {
ev := NewEventKey(key, r, mod)
s.postEvent(ev)
}
func (s *simscreen) InjectKeyBytes(b []byte) bool {
failed := false
outer:
for len(b) > 0 {
if b[0] >= ' ' && b[0] <= 0x7F {
// printable ASCII easy to deal with -- no encodings
ev := NewEventKey(KeyRune, rune(b[0]), ModNone)
s.postEvent(ev)
b = b[1:]
continue
}
if b[0] < 0x80 {
// No encodings start with low numbered values
if b[0] > 0 && b[0] < ' ' { // control keys
switch Key(b[0]) {
case KeyESC, KeyEnter, KeyTAB:
s.postEvent(NewEventKey(Key(b[0]), 0, 0))
continue;
default:
s.postEvent(NewEventKey(Key(b[0]), rune(b[0])+'\x60', ModCtrl))
continue
}
}
mod := ModNone
ev := NewEventKey(Key(b[0]), 0, mod)
s.postEvent(ev)
b = b[1:]
continue
}
utfb := make([]byte, len(b)*4) // worst case
for l := 1; l < len(b); l++ {
s.decoder.Reset()
nout, nin, _ := s.decoder.Transform(utfb, b[:l], true)
if nout != 0 {
r, _ := utf8.DecodeRune(utfb[:nout])
if r != utf8.RuneError {
ev := NewEventKey(KeyRune, r, ModNone)
s.postEvent(ev)
}
b = b[nin:]
continue outer
}
}
failed = true
b = b[1:]
continue
}
return !failed
}
func (s *simscreen) Sync() {
s.Lock()
s.clear = true
s.resize()
s.back.Invalidate()
s.draw()
s.Unlock()
}
func (s *simscreen) CharacterSet() string {
return s.charset
}
func (s *simscreen) SetSize(w, h int) {
s.Lock()
newc := make([]SimCell, w*h)
for row := 0; row < h && row < s.physh; row++ {
for col := 0; col < w && col < s.physw; col++ {
newc[(row*w)+col] = s.front[(row*s.physw)+col]
}
}
s.cursorx, s.cursory = -1, -1
s.physw, s.physh = w, h
s.front = newc
s.back.Resize(w, h)
s.Unlock()
}
func (s *simscreen) GetContents() ([]SimCell, int, int) {
s.Lock()
cells, w, h := s.front, s.physw, s.physh
s.Unlock()
return cells, w, h
}
func (s *simscreen) GetCursor() (int, int, bool) {
s.Lock()
x, y, vis := s.cursorx, s.cursory, s.cursorvis
s.Unlock()
return x, y, vis
}
func (s *simscreen) RegisterRuneFallback(r rune, subst string) {
s.Lock()
s.fallback[r] = subst
s.Unlock()
}
func (s *simscreen) UnregisterRuneFallback(r rune) {
s.Lock()
delete(s.fallback, r)
s.Unlock()
}
func (s *simscreen) CanDisplay(r rune, checkFallbacks bool) bool {
if enc := s.encoder; enc != nil {
nb := make([]byte, 6)
ob := make([]byte, 6)
num := utf8.EncodeRune(ob, r)
enc.Reset()
dst, _, err := enc.Transform(nb, ob[:num], true)
if dst != 0 && err == nil && nb[0] != '\x1A' {
return true
}
}
if !checkFallbacks {
return false
}
if _, ok := s.fallback[r]; ok {
return true
}
return false
}
func (s *simscreen) HasMouse() bool {
return false
}
func (s *simscreen) Resize(int, int, int, int) {}
func (s *simscreen) HasKey(Key) bool {
return true
}
func (s *simscreen) Beep() error {
return nil
}
func (s *simscreen) Suspend() error {
return nil
}
func (s *simscreen) Resume() error {
return nil
}
func (s *simscreen) Tty() (Tty, bool) {
return nil, false
}
func (s *simscreen) GetCells() *CellBuffer {
return &s.back
}
func (s *simscreen) EventQ() chan Event {
return s.evch
}
func (s *simscreen) StopQ() <-chan struct{} {
return s.quit
}
func (s *simscreen) SetTitle(title string) {
s.title = title
}
func (s *simscreen) GetTitle() string {
return s.title
}
func (s *simscreen) SetClipboard(data []byte) {
s.clipboard = data
}
func (s *simscreen) GetClipboard() {
if s.clipboard != nil {
ev := NewEventClipboard(s.clipboard)
s.postEvent(ev)
}
}
func (s *simscreen) GetClipboardData() []byte {
return s.clipboard
}
-1
View File
@@ -1 +0,0 @@
mkinfo
-25
View File
@@ -1,25 +0,0 @@
This package represents the parent for all terminals.
In older versions of tcell we had (a couple of) different
external file formats for the terminal database. Those are
now removed. All terminal definitions are supplied by
one of two methods:
1. Compiled Go code
2. For systems with terminfo and infocmp, dynamically
generated at runtime.
The Go code can be generated using the mkinfo utility in
this directory. The database entry should be generated
into a package in a directory named as the first character
of the package name. (This permits us to group them all
without having a huge directory of little packages.)
It may be desirable to add new packages to the extended
package, or -- rarely -- the base package.
Applications which want to have the large set of terminal
descriptions built into the binary can simply import the
extended package. Otherwise a smaller reasonable default
set (the base package) will be included instead.
-7
View File
@@ -1,7 +0,0 @@
TERMINALS
=========
The best way to populate terminals on Debian is to install ncurses,
ncurses-term, screen, tmux, rxvt-unicode, and dvtm. This populates the
the terminfo database so that we can have a reasonable set of starting
terminals.
-31
View File
@@ -1,31 +0,0 @@
// Generated automatically. DO NOT HAND-EDIT.
package aixterm
import "github.com/gdamore/tcell/v2/terminfo"
func init() {
// IBM Aixterm Terminal Emulator
terminfo.AddTerminfo(&terminfo.Terminfo{
Name: "aixterm",
Columns: 80,
Lines: 25,
Colors: 8,
Clear: "\x1b[H\x1b[J",
AttrOff: "\x1b[0;10m\x1b(B",
Underline: "\x1b[4m",
Bold: "\x1b[1m",
Reverse: "\x1b[7m",
SetFg: "\x1b[3%p1%dm",
SetBg: "\x1b[4%p1%dm",
SetFgBg: "\x1b[3%p1%d;4%p2%dm",
ResetFgBg: "\x1b[32m\x1b[40m",
PadChar: "\x00",
AltChars: "jjkkllmmnnqqttuuvvwwxx",
EnterAcs: "\x1b(0",
ExitAcs: "\x1b(B",
SetCursor: "\x1b[%i%p1%d;%p2%dH",
AutoMargin: true,
})
}
-41
View File
@@ -1,41 +0,0 @@
// Generated automatically. DO NOT HAND-EDIT.
package alacritty
import "github.com/gdamore/tcell/v2/terminfo"
func init() {
// alacritty with direct color indexing
terminfo.AddTerminfo(&terminfo.Terminfo{
Name: "alacritty-direct",
Columns: 80,
Lines: 24,
Colors: 16777216,
Clear: "\x1b[H\x1b[2J",
EnterCA: "\x1b[?1049h\x1b[22;0;0t",
ExitCA: "\x1b[?1049l\x1b[23;0;0t",
ShowCursor: "\x1b[?12l\x1b[?25h",
HideCursor: "\x1b[?25l",
AttrOff: "\x1b(B\x1b[m",
Underline: "\x1b[4m",
Bold: "\x1b[1m",
Dim: "\x1b[2m",
Italic: "\x1b[3m",
Reverse: "\x1b[7m",
EnterKeypad: "\x1b[?1h\x1b=",
ExitKeypad: "\x1b[?1l\x1b>",
SetFg: "\x1b[%?%p1%{8}%<%t3%p1%d%e%p1%{16}%<%t9%p1%{8}%-%d%e38;5;%p1%d%;m",
SetBg: "\x1b[%?%p1%{8}%<%t4%p1%d%e%p1%{16}%<%t10%p1%{8}%-%d%e48;5;%p1%d%;m",
SetFgBg: "\x1b[%?%p1%{8}%<%t3%p1%d%e%p1%{16}%<%t9%p1%{8}%-%d%e38;5;%p1%d%;;%?%p2%{8}%<%t4%p2%d%e%p2%{16}%<%t10%p2%{8}%-%d%e48;5;%p2%d%;m",
ResetFgBg: "\x1b[39;49m",
AltChars: "``aaffggiijjkkllmmnnooppqqrrssttuuvvwwxxyyzz{{||}}~~",
EnterAcs: "\x1b(0",
ExitAcs: "\x1b(B",
StrikeThrough: "\x1b[9m",
Mouse: "\x1b[M",
SetCursor: "\x1b[%i%p1%d;%p2%dH",
TrueColor: true,
AutoMargin: true,
})
}
-44
View File
@@ -1,44 +0,0 @@
// Generated automatically. DO NOT HAND-EDIT.
package alacritty
import "github.com/gdamore/tcell/v2/terminfo"
func init() {
// alacritty terminal emulator
terminfo.AddTerminfo(&terminfo.Terminfo{
Name: "alacritty",
Columns: 80,
Lines: 24,
Colors: 256,
Clear: "\x1b[H\x1b[2J",
EnterCA: "\x1b[?1049h\x1b[22;0;0t",
ExitCA: "\x1b[?1049l\x1b[23;0;0t",
ShowCursor: "\x1b[?12l\x1b[?25h",
HideCursor: "\x1b[?25l",
AttrOff: "\x1b(B\x1b[m",
Underline: "\x1b[4m",
Bold: "\x1b[1m",
Dim: "\x1b[2m",
Italic: "\x1b[3m",
Blink: "\x1b[5m",
Reverse: "\x1b[7m",
EnterKeypad: "\x1b[?1h\x1b=",
ExitKeypad: "\x1b[?1l\x1b>",
SetFg: "\x1b[%?%p1%{8}%<%t3%p1%d%e%p1%{16}%<%t9%p1%{8}%-%d%e38;5;%p1%d%;m",
SetBg: "\x1b[%?%p1%{8}%<%t4%p1%d%e%p1%{16}%<%t10%p1%{8}%-%d%e48;5;%p1%d%;m",
SetFgBg: "\x1b[%?%p1%{8}%<%t3%p1%d%e%p1%{16}%<%t9%p1%{8}%-%d%e38;5;%p1%d%;;%?%p2%{8}%<%t4%p2%d%e%p2%{16}%<%t10%p2%{8}%-%d%e48;5;%p2%d%;m",
ResetFgBg: "\x1b[39;49m",
AltChars: "``aaffggiijjkkllmmnnooppqqrrssttuuvvwwxxyyzz{{||}}~~",
EnterAcs: "\x1b(0",
ExitAcs: "\x1b(B",
EnableAutoMargin: "\x1b[?7h",
DisableAutoMargin: "\x1b[?7l",
StrikeThrough: "\x1b[9m",
Mouse: "\x1b[<",
SetCursor: "\x1b[%i%p1%d;%p2%dH",
AutoMargin: true,
XTermLike: true,
})
}
-32
View File
@@ -1,32 +0,0 @@
// Generated automatically. DO NOT HAND-EDIT.
package ansi
import "github.com/gdamore/tcell/v2/terminfo"
func init() {
// ansi/pc-term compatible with color
terminfo.AddTerminfo(&terminfo.Terminfo{
Name: "ansi",
Columns: 80,
Lines: 24,
Colors: 8,
Clear: "\x1b[H\x1b[J",
AttrOff: "\x1b[0;10m",
Underline: "\x1b[4m",
Bold: "\x1b[1m",
Blink: "\x1b[5m",
Reverse: "\x1b[7m",
SetFg: "\x1b[3%p1%dm",
SetBg: "\x1b[4%p1%dm",
SetFgBg: "\x1b[3%p1%d;4%p2%dm",
ResetFgBg: "\x1b[39;49m",
PadChar: "\x00",
AltChars: "+\x10,\x11-\x18.\x190\xdb`\x04a\xb1f\xf8g\xf1h\xb0j\xd9k\xbfl\xdam\xc0n\xc5o~p\xc4q\xc4r\xc4s_t\xc3u\xb4v\xc1w\xc2x\xb3y\xf3z\xf2{\xe3|\xd8}\x9c~\xfe",
EnterAcs: "\x1b[11m",
ExitAcs: "\x1b[10m",
SetCursor: "\x1b[%i%p1%d;%p2%dH",
AutoMargin: true,
})
}
-33
View File
@@ -1,33 +0,0 @@
// Copyright 2020 The TCell Authors
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use file except in compliance with the License.
// You may obtain a copy of the license at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
// This is just a "minimalist" set of the base terminal descriptions.
// It should be sufficient for most applications.
// Package base contains the base terminal descriptions that are likely
// to be needed by any stock application. It is imported by default in the
// terminfo package, so terminal types listed here will be available to any
// tcell application.
package base
import (
// The following imports just register themselves --
// these are the terminal types we aggregate in this package.
_ "github.com/gdamore/tcell/v2/terminfo/a/ansi"
_ "github.com/gdamore/tcell/v2/terminfo/t/tmux"
_ "github.com/gdamore/tcell/v2/terminfo/v/vt100"
_ "github.com/gdamore/tcell/v2/terminfo/v/vt102"
_ "github.com/gdamore/tcell/v2/terminfo/v/vt220"
_ "github.com/gdamore/tcell/v2/terminfo/x/xterm"
)
-32
View File
@@ -1,32 +0,0 @@
// Generated automatically. DO NOT HAND-EDIT.
package cygwin
import "github.com/gdamore/tcell/v2/terminfo"
func init() {
// ANSI emulation for Cygwin
terminfo.AddTerminfo(&terminfo.Terminfo{
Name: "cygwin",
Colors: 8,
Clear: "\x1b[H\x1b[J",
EnterCA: "\x1b7\x1b[?47h",
ExitCA: "\x1b[2J\x1b[?47l\x1b8",
AttrOff: "\x1b[0;10m",
Underline: "\x1b[4m",
Bold: "\x1b[1m",
Reverse: "\x1b[7m",
SetFg: "\x1b[3%p1%dm",
SetBg: "\x1b[4%p1%dm",
SetFgBg: "\x1b[3%p1%d;4%p2%dm",
ResetFgBg: "\x1b[39;49m",
PadChar: "\x00",
AltChars: "+\x10,\x11-\x18.\x190\xdb`\x04a\xb1f\xf8g\xf1h\xb0j\xd9k\xbfl\xdam\xc0n\xc5o~p\xc4q\xc4r\xc4s_t\xc3u\xb4v\xc1w\xc2x\xb3y\xf3z\xf2{\xe3|\xd8}\x9c~\xfe",
EnterAcs: "\x1b[11m",
ExitAcs: "\x1b[10m",
SetCursor: "\x1b[%i%p1%d;%p2%dH",
AutoMargin: true,
InsertChar: "\x1b[@",
})
}
-38
View File
@@ -1,38 +0,0 @@
// Generated automatically. DO NOT HAND-EDIT.
package dtterm
import "github.com/gdamore/tcell/v2/terminfo"
func init() {
// CDE desktop terminal
terminfo.AddTerminfo(&terminfo.Terminfo{
Name: "dtterm",
Columns: 80,
Lines: 24,
Colors: 8,
Clear: "\x1b[H\x1b[J",
ShowCursor: "\x1b[?25h",
HideCursor: "\x1b[?25l",
AttrOff: "\x1b[m\x0f",
Underline: "\x1b[4m",
Bold: "\x1b[1m",
Dim: "\x1b[2m",
Blink: "\x1b[5m",
Reverse: "\x1b[7m",
SetFg: "\x1b[3%p1%dm",
SetBg: "\x1b[4%p1%dm",
SetFgBg: "\x1b[3%p1%d;4%p2%dm",
ResetFgBg: "\x1b[39;49m",
PadChar: "\x00",
AltChars: "``aaffggjjkkllmmnnooppqqrrssttuuvvwwxxyyzz{{||}}~~",
EnterAcs: "\x0e",
ExitAcs: "\x0f",
EnableAcs: "\x1b(B\x1b)0",
EnableAutoMargin: "\x1b[?7h",
DisableAutoMargin: "\x1b[?7l",
SetCursor: "\x1b[%i%p1%d;%p2%dH",
AutoMargin: true,
})
}
-266
View File
@@ -1,266 +0,0 @@
// Copyright 2021 The TCell Authors
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use file except in compliance with the License.
// You may obtain a copy of the license at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
// The dynamic package is used to generate a terminal description dynamically,
// using infocmp. This is really a method of last resort, as the performance
// will be slow, and it requires a working infocmp. But, the hope is that it
// will assist folks who have to deal with a terminal description that isn't
// already built in. This requires infocmp to be in the user's path, and to
// support reasonably the -1 option.
package dynamic
import (
"bytes"
"errors"
"fmt"
"os/exec"
"regexp"
"strconv"
"strings"
"github.com/gdamore/tcell/v2/terminfo"
)
type termcap struct {
name string
desc string
aliases []string
bools map[string]bool
nums map[string]int
strs map[string]string
}
func (tc *termcap) getnum(s string) int {
return (tc.nums[s])
}
func (tc *termcap) getflag(s string) bool {
return (tc.bools[s])
}
func (tc *termcap) getstr(s string) string {
return (tc.strs[s])
}
const (
none = iota
control
escaped
)
var errNotAddressable = errors.New("terminal not cursor addressable")
func unescape(s string) string {
// Various escapes are in \x format. Control codes are
// encoded as ^M (carat followed by ASCII equivalent).
// escapes are: \e, \E - escape
// \0 NULL, \n \l \r \t \b \f \s for equivalent C escape.
buf := &bytes.Buffer{}
esc := none
for i := 0; i < len(s); i++ {
c := s[i]
switch esc {
case none:
switch c {
case '\\':
esc = escaped
case '^':
esc = control
default:
buf.WriteByte(c)
}
case control:
buf.WriteByte(c ^ 1<<6)
esc = none
case escaped:
switch c {
case 'E', 'e':
buf.WriteByte(0x1b)
case '0', '1', '2', '3', '4', '5', '6', '7':
if i+2 < len(s) && s[i+1] >= '0' && s[i+1] <= '7' && s[i+2] >= '0' && s[i+2] <= '7' {
buf.WriteByte(((c - '0') * 64) + ((s[i+1] - '0') * 8) + (s[i+2] - '0'))
i = i + 2
} else if c == '0' {
buf.WriteByte(0)
}
case 'n':
buf.WriteByte('\n')
case 'r':
buf.WriteByte('\r')
case 't':
buf.WriteByte('\t')
case 'b':
buf.WriteByte('\b')
case 'f':
buf.WriteByte('\f')
case 's':
buf.WriteByte(' ')
default:
buf.WriteByte(c)
}
esc = none
}
}
return (buf.String())
}
func (tc *termcap) setupterm(name string) error {
cmd := exec.Command("infocmp", "-1", name)
output := &bytes.Buffer{}
cmd.Stdout = output
tc.strs = make(map[string]string)
tc.bools = make(map[string]bool)
tc.nums = make(map[string]int)
if err := cmd.Run(); err != nil {
return fmt.Errorf("couldn't open terminfo ($TERM) file for %s: %w", name, err)
}
// Now parse the output.
// We get comment lines (starting with "#"), followed by
// a header line that looks like "<name>|<alias>|...|<desc>"
// then capabilities, one per line, starting with a tab and ending
// with a comma and newline.
lines := strings.Split(output.String(), "\n")
for len(lines) > 0 && strings.HasPrefix(lines[0], "#") {
lines = lines[1:]
}
// Ditch trailing empty last line
if lines[len(lines)-1] == "" {
lines = lines[:len(lines)-1]
}
header := lines[0]
header = strings.TrimSuffix(header, ",")
names := strings.Split(header, "|")
tc.name = names[0]
names = names[1:]
if len(names) > 0 {
tc.desc = names[len(names)-1]
names = names[:len(names)-1]
}
tc.aliases = names
for _, val := range lines[1:] {
if (!strings.HasPrefix(val, "\t")) ||
(!strings.HasSuffix(val, ",")) {
return (errors.New("malformed infocmp: " + val))
}
val = val[1:]
val = val[:len(val)-1]
if k := strings.SplitN(val, "=", 2); len(k) == 2 {
tc.strs[k[0]] = unescape(k[1])
} else if k := strings.SplitN(val, "#", 2); len(k) == 2 {
u, err := strconv.ParseUint(k[1], 0, 0)
if err != nil {
return (err)
}
tc.nums[k[0]] = int(u)
} else {
tc.bools[val] = true
}
}
return nil
}
// LoadTerminfo creates a Terminfo by for named terminal by attempting to parse
// the output from infocmp. This returns the terminfo entry, a description of
// the terminal, and either nil or an error.
func LoadTerminfo(name string) (*terminfo.Terminfo, string, error) {
var tc termcap
if err := tc.setupterm(name); err != nil {
return nil, "", err
}
t := &terminfo.Terminfo{}
t.Name = tc.name
t.Aliases = tc.aliases
t.Colors = tc.getnum("colors")
t.Columns = tc.getnum("cols")
t.Lines = tc.getnum("lines")
t.Clear = tc.getstr("clear")
t.EnterCA = tc.getstr("smcup")
t.ExitCA = tc.getstr("rmcup")
t.ShowCursor = tc.getstr("cnorm")
t.HideCursor = tc.getstr("civis")
t.AttrOff = tc.getstr("sgr0")
t.Underline = tc.getstr("smul")
t.Bold = tc.getstr("bold")
t.Blink = tc.getstr("blink")
t.Dim = tc.getstr("dim")
t.Italic = tc.getstr("sitm")
t.Reverse = tc.getstr("rev")
t.EnterKeypad = tc.getstr("smkx")
t.ExitKeypad = tc.getstr("rmkx")
t.SetFg = tc.getstr("setaf")
t.SetBg = tc.getstr("setab")
t.SetCursor = tc.getstr("cup")
t.AltChars = tc.getstr("acsc")
t.EnterAcs = tc.getstr("smacs")
t.ExitAcs = tc.getstr("rmacs")
t.EnableAcs = tc.getstr("enacs")
t.Mouse = tc.getstr("kmous")
// Technically the RGB flag that is provided for xterm-direct is not
// quite right. The problem is that the -direct flag that was introduced
// with ncurses 6.1 requires a parsing for the parameters that we lack.
// For this case we'll just assume it's XTerm compatible. Someday this
// may be incorrect, but right now it is correct, and nobody uses it
// anyway.
if tc.getflag("Tc") {
// This presumes XTerm 24-bit true color.
t.TrueColor = true
} else if tc.getflag("RGB") {
// This is for xterm-direct, which uses a different scheme entirely.
// (ncurses went a very different direction from everyone else, and
// so it's unlikely anything is using this definition.)
t.TrueColor = true
t.SetBg = "\x1b[%?%p1%{8}%<%t4%p1%d%e%p1%{16}%<%t10%p1%{8}%-%d%e48;5;%p1%d%;m"
t.SetFg = "\x1b[%?%p1%{8}%<%t3%p1%d%e%p1%{16}%<%t9%p1%{8}%-%d%e38;5;%p1%d%;m"
}
// We only support colors in ANSI 8 or 256 color mode.
if t.Colors < 8 || t.SetFg == "" {
t.Colors = 0
}
if t.SetCursor == "" {
return nil, "", errNotAddressable
}
// For padding, we lookup the pad char. If that isn't present,
// and npc is *not* set, then we assume a null byte.
t.PadChar = tc.getstr("pad")
if t.PadChar == "" {
if !tc.getflag("npc") {
t.PadChar = "\u0000"
}
}
// For terminals that use "standard" SGR sequences, lets combine the
// foreground and background together.
if strings.HasPrefix(t.SetFg, "\x1b[") &&
strings.HasPrefix(t.SetBg, "\x1b[") &&
strings.HasSuffix(t.SetFg, "m") &&
strings.HasSuffix(t.SetBg, "m") {
fg := t.SetFg[:len(t.SetFg)-1]
r := regexp.MustCompile("%p1")
bg := r.ReplaceAllString(t.SetBg[2:], "%p2")
t.SetFgBg = fg + ";" + bg
}
return t, tc.desc, nil
}
-48
View File
@@ -1,48 +0,0 @@
// Generated automatically. DO NOT HAND-EDIT.
package emacs
import "github.com/gdamore/tcell/v2/terminfo"
func init() {
// GNU Emacs term.el terminal emulation
terminfo.AddTerminfo(&terminfo.Terminfo{
Name: "eterm",
Columns: 80,
Lines: 24,
Clear: "\x1b[H\x1b[J",
EnterCA: "\x1b7\x1b[?47h",
ExitCA: "\x1b[2J\x1b[?47l\x1b8",
AttrOff: "\x1b[m",
Underline: "\x1b[4m",
Bold: "\x1b[1m",
Reverse: "\x1b[7m",
PadChar: "\x00",
SetCursor: "\x1b[%i%p1%d;%p2%dH",
AutoMargin: true,
})
// Emacs term.el terminal emulator term-protocol-version 0.96
terminfo.AddTerminfo(&terminfo.Terminfo{
Name: "eterm-color",
Columns: 80,
Lines: 24,
Colors: 8,
Clear: "\x1b[H\x1b[J",
EnterCA: "\x1b7\x1b[?47h",
ExitCA: "\x1b[2J\x1b[?47l\x1b8",
AttrOff: "\x1b[m",
Underline: "\x1b[4m",
Bold: "\x1b[1m",
Blink: "\x1b[5m",
Reverse: "\x1b[7m",
SetFg: "\x1b[%p1%{30}%+%dm",
SetBg: "\x1b[%p1%'('%+%dm",
SetFgBg: "\x1b[%p1%{30}%+%d;%p2%'('%+%dm",
ResetFgBg: "\x1b[39;49m",
PadChar: "\x00",
SetCursor: "\x1b[%i%p1%d;%p2%dH",
AutoMargin: true,
})
}
-51
View File
@@ -1,51 +0,0 @@
// Copyright 2024 The TCell Authors
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use file except in compliance with the License.
// You may obtain a copy of the license at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
// Package extended contains an extended set of terminal descriptions.
// Applications desiring to have a better chance of Just Working by
// default should include this package. This will significantly increase
// the size of the program.
package extended
import (
// The following imports just register themselves --
// these are the terminal types we aggregate in this package.
_ "github.com/gdamore/tcell/v2/terminfo/a/aixterm"
_ "github.com/gdamore/tcell/v2/terminfo/a/alacritty"
_ "github.com/gdamore/tcell/v2/terminfo/a/ansi"
_ "github.com/gdamore/tcell/v2/terminfo/c/cygwin"
_ "github.com/gdamore/tcell/v2/terminfo/d/dtterm"
_ "github.com/gdamore/tcell/v2/terminfo/e/emacs"
_ "github.com/gdamore/tcell/v2/terminfo/f/foot"
_ "github.com/gdamore/tcell/v2/terminfo/g/gnome"
_ "github.com/gdamore/tcell/v2/terminfo/k/konsole"
_ "github.com/gdamore/tcell/v2/terminfo/k/kterm"
_ "github.com/gdamore/tcell/v2/terminfo/l/linux"
_ "github.com/gdamore/tcell/v2/terminfo/p/pcansi"
_ "github.com/gdamore/tcell/v2/terminfo/r/rxvt"
_ "github.com/gdamore/tcell/v2/terminfo/s/screen"
_ "github.com/gdamore/tcell/v2/terminfo/s/simpleterm"
_ "github.com/gdamore/tcell/v2/terminfo/s/sun"
_ "github.com/gdamore/tcell/v2/terminfo/t/tmux"
_ "github.com/gdamore/tcell/v2/terminfo/v/vt100"
_ "github.com/gdamore/tcell/v2/terminfo/v/vt102"
_ "github.com/gdamore/tcell/v2/terminfo/v/vt220"
_ "github.com/gdamore/tcell/v2/terminfo/v/vt320"
_ "github.com/gdamore/tcell/v2/terminfo/v/vt400"
_ "github.com/gdamore/tcell/v2/terminfo/v/vt420"
_ "github.com/gdamore/tcell/v2/terminfo/x/xfce"
_ "github.com/gdamore/tcell/v2/terminfo/x/xterm"
_ "github.com/gdamore/tcell/v2/terminfo/x/xterm_ghostty"
_ "github.com/gdamore/tcell/v2/terminfo/x/xterm_kitty"
)
-42
View File
@@ -1,42 +0,0 @@
// Generated automatically. DO NOT HAND-EDIT.
package foot
import "github.com/gdamore/tcell/v2/terminfo"
func init() {
// foot terminal emulator
terminfo.AddTerminfo(&terminfo.Terminfo{
Name: "foot",
Aliases: []string{"foot-extra"},
Columns: 80,
Lines: 24,
Colors: 256,
Clear: "\x1b[H\x1b[2J",
EnterCA: "\x1b[?1049h\x1b[22;0;0t",
ExitCA: "\x1b[?1049l\x1b[23;0;0t",
ShowCursor: "\x1b[?12l\x1b[?25h",
HideCursor: "\x1b[?25l",
AttrOff: "\x1b(B\x1b[m",
Underline: "\x1b[4m",
Bold: "\x1b[1m",
Dim: "\x1b[2m",
Italic: "\x1b[3m",
Blink: "\x1b[5m",
Reverse: "\x1b[7m",
EnterKeypad: "\x1b[?1h\x1b=",
ExitKeypad: "\x1b[?1l\x1b>",
SetFg: "\x1b[%?%p1%{8}%<%t3%p1%d%e%p1%{16}%<%t9%p1%{8}%-%d%e38:5:%p1%d%;m",
SetBg: "\x1b[%?%p1%{8}%<%t4%p1%d%e%p1%{16}%<%t10%p1%{8}%-%d%e48:5:%p1%d%;m",
SetFgBg: "\x1b[%?%p1%{8}%<%t3%p1%d%e%p1%{16}%<%t9%p1%{8}%-%d%e38:5:%p1%d%;;%?%p2%{8}%<%t4%p2%d%e%p2%{16}%<%t10%p2%{8}%-%d%e48:5:%p2%d%;m",
ResetFgBg: "\x1b[39;49m",
AltChars: "``aaffggiijjkkllmmnnooppqqrrssttuuvvwwxxyyzz{{||}}~~",
EnterAcs: "\x1b(0",
ExitAcs: "\x1b(B",
StrikeThrough: "\x1b[9m",
Mouse: "\x1b[M",
SetCursor: "\x1b[%i%p1%d;%p2%dH",
AutoMargin: true,
})
}
-80
View File
@@ -1,80 +0,0 @@
// Generated automatically. DO NOT HAND-EDIT.
package gnome
import "github.com/gdamore/tcell/v2/terminfo"
func init() {
// GNOME Terminal
terminfo.AddTerminfo(&terminfo.Terminfo{
Name: "gnome",
Columns: 80,
Lines: 24,
Colors: 8,
Clear: "\x1b[H\x1b[2J",
EnterCA: "\x1b7\x1b[?47h",
ExitCA: "\x1b[2J\x1b[?47l\x1b8",
ShowCursor: "\x1b[?25h",
HideCursor: "\x1b[?25l",
AttrOff: "\x1b[0m\x0f",
Underline: "\x1b[4m",
Bold: "\x1b[1m",
Dim: "\x1b[2m",
Italic: "\x1b[3m",
Reverse: "\x1b[7m",
EnterKeypad: "\x1b[?1h\x1b=",
ExitKeypad: "\x1b[?1l\x1b>",
SetFg: "\x1b[3%p1%dm",
SetBg: "\x1b[4%p1%dm",
SetFgBg: "\x1b[3%p1%d;4%p2%dm",
ResetFgBg: "\x1b[39;49m",
PadChar: "\x00",
AltChars: "``aaffggiijjkkllmmnnooppqqrrssttuuvvwwxxyyzz{{||}}~~",
EnterAcs: "\x0e",
ExitAcs: "\x0f",
EnableAcs: "\x1b)0",
EnableAutoMargin: "\x1b[?7h",
DisableAutoMargin: "\x1b[?7l",
Mouse: "\x1b[M",
SetCursor: "\x1b[%i%p1%d;%p2%dH",
AutoMargin: true,
XTermLike: true,
})
// GNOME Terminal with xterm 256-colors
terminfo.AddTerminfo(&terminfo.Terminfo{
Name: "gnome-256color",
Columns: 80,
Lines: 24,
Colors: 256,
Clear: "\x1b[H\x1b[2J",
EnterCA: "\x1b7\x1b[?47h",
ExitCA: "\x1b[2J\x1b[?47l\x1b8",
ShowCursor: "\x1b[?25h",
HideCursor: "\x1b[?25l",
AttrOff: "\x1b[0m\x0f",
Underline: "\x1b[4m",
Bold: "\x1b[1m",
Dim: "\x1b[2m",
Italic: "\x1b[3m",
Reverse: "\x1b[7m",
EnterKeypad: "\x1b[?1h\x1b=",
ExitKeypad: "\x1b[?1l\x1b>",
SetFg: "\x1b[%?%p1%{8}%<%t3%p1%d%e%p1%{16}%<%t9%p1%{8}%-%d%e38;5;%p1%d%;m",
SetBg: "\x1b[%?%p1%{8}%<%t4%p1%d%e%p1%{16}%<%t10%p1%{8}%-%d%e48;5;%p1%d%;m",
SetFgBg: "\x1b[%?%p1%{8}%<%t3%p1%d%e%p1%{16}%<%t9%p1%{8}%-%d%e38;5;%p1%d%;;%?%p2%{8}%<%t4%p2%d%e%p2%{16}%<%t10%p2%{8}%-%d%e48;5;%p2%d%;m",
ResetFgBg: "\x1b[39;49m",
PadChar: "\x00",
AltChars: "``aaffggiijjkkllmmnnooppqqrrssttuuvvwwxxyyzz{{||}}~~",
EnterAcs: "\x0e",
ExitAcs: "\x0f",
EnableAcs: "\x1b)0",
EnableAutoMargin: "\x1b[?7h",
DisableAutoMargin: "\x1b[?7l",
Mouse: "\x1b[M",
SetCursor: "\x1b[%i%p1%d;%p2%dH",
AutoMargin: true,
XTermLike: true,
})
}
-19
View File
@@ -1,19 +0,0 @@
#!/bin/bash
while read line
do
case "$line" in
*'|'*)
alias=${line#*|}
line=${line%|*}
;;
*)
alias=${line%%,*}
;;
esac
alias=${alias//-/_}
direc=${alias:0:1}
mkdir -p ${direc}/${alias}
go run mkinfo.go -P ${alias} -go ${direc}/${alias}/term.go ${line//,/ }
done < models.txt
-82
View File
@@ -1,82 +0,0 @@
// Generated automatically. DO NOT HAND-EDIT.
package konsole
import "github.com/gdamore/tcell/v2/terminfo"
func init() {
// KDE console window
terminfo.AddTerminfo(&terminfo.Terminfo{
Name: "konsole",
Columns: 80,
Lines: 24,
Colors: 8,
Clear: "\x1b[H\x1b[2J",
EnterCA: "\x1b7\x1b[?47h",
ExitCA: "\x1b[2J\x1b[?47l\x1b8",
ShowCursor: "\x1b[?25h",
HideCursor: "\x1b[?25l",
AttrOff: "\x1b[0m\x0f",
Underline: "\x1b[4m",
Bold: "\x1b[1m",
Dim: "\x1b[2m",
Italic: "\x1b[3m",
Blink: "\x1b[5m",
Reverse: "\x1b[7m",
EnterKeypad: "\x1b[?1h\x1b=",
ExitKeypad: "\x1b[?1l\x1b>",
SetFg: "\x1b[3%p1%dm",
SetBg: "\x1b[4%p1%dm",
SetFgBg: "\x1b[3%p1%d;4%p2%dm",
ResetFgBg: "\x1b[39;49m",
AltChars: "``aaffggiijjkkllmmnnooppqqrrssttuuvvwwxxyyzz{{||}}~~",
EnterAcs: "\x0e",
ExitAcs: "\x0f",
EnableAcs: "\x1b)0",
EnableAutoMargin: "\x1b[?7h",
DisableAutoMargin: "\x1b[?7l",
StrikeThrough: "\x1b[9m",
Mouse: "\x1b[<",
SetCursor: "\x1b[%i%p1%d;%p2%dH",
AutoMargin: true,
XTermLike: true,
})
// KDE console window with xterm 256-colors
terminfo.AddTerminfo(&terminfo.Terminfo{
Name: "konsole-256color",
Columns: 80,
Lines: 24,
Colors: 256,
Clear: "\x1b[H\x1b[2J",
EnterCA: "\x1b7\x1b[?47h",
ExitCA: "\x1b[2J\x1b[?47l\x1b8",
ShowCursor: "\x1b[?25h",
HideCursor: "\x1b[?25l",
AttrOff: "\x1b[0m\x0f",
Underline: "\x1b[4m",
Bold: "\x1b[1m",
Dim: "\x1b[2m",
Italic: "\x1b[3m",
Blink: "\x1b[5m",
Reverse: "\x1b[7m",
EnterKeypad: "\x1b[?1h\x1b=",
ExitKeypad: "\x1b[?1l\x1b>",
SetFg: "\x1b[%?%p1%{8}%<%t3%p1%d%e%p1%{16}%<%t9%p1%{8}%-%d%e38;5;%p1%d%;m",
SetBg: "\x1b[%?%p1%{8}%<%t4%p1%d%e%p1%{16}%<%t10%p1%{8}%-%d%e48;5;%p1%d%;m",
SetFgBg: "\x1b[%?%p1%{8}%<%t3%p1%d%e%p1%{16}%<%t9%p1%{8}%-%d%e38;5;%p1%d%;;%?%p2%{8}%<%t4%p2%d%e%p2%{16}%<%t10%p2%{8}%-%d%e48;5;%p2%d%;m",
ResetFgBg: "\x1b[39;49m",
AltChars: "``aaffggiijjkkllmmnnooppqqrrssttuuvvwwxxyyzz{{||}}~~",
EnterAcs: "\x0e",
ExitAcs: "\x0f",
EnableAcs: "\x1b)0",
EnableAutoMargin: "\x1b[?7h",
DisableAutoMargin: "\x1b[?7l",
StrikeThrough: "\x1b[9m",
Mouse: "\x1b[<",
SetCursor: "\x1b[%i%p1%d;%p2%dH",
AutoMargin: true,
XTermLike: true,
})
}
-39
View File
@@ -1,39 +0,0 @@
// Generated automatically. DO NOT HAND-EDIT.
package kterm
import "github.com/gdamore/tcell/v2/terminfo"
func init() {
// kterm kanji terminal emulator (X window system)
terminfo.AddTerminfo(&terminfo.Terminfo{
Name: "kterm",
Columns: 80,
Lines: 24,
Colors: 8,
Clear: "\x1b[H\x1b[2J",
EnterCA: "\x1b7\x1b[?47h",
ExitCA: "\x1b[2J\x1b[?47l\x1b8",
AttrOff: "\x1b[m\x1b(B",
Underline: "\x1b[4m",
Bold: "\x1b[1m",
Reverse: "\x1b[7m",
EnterKeypad: "\x1b[?1h\x1b=",
ExitKeypad: "\x1b[?1l\x1b>",
SetFg: "\x1b[3%p1%dm",
SetBg: "\x1b[4%p1%dm",
SetFgBg: "\x1b[3%p1%d;4%p2%dm",
ResetFgBg: "\x1b[39;49m",
PadChar: "\x00",
AltChars: "``aajjkkllmmnnooppqqrrssttuuvvwwxx~~",
EnterAcs: "\x1b(0",
ExitAcs: "\x1b(B",
EnableAutoMargin: "\x1b[?7h",
DisableAutoMargin: "\x1b[?7l",
Mouse: "\x1b[M",
SetCursor: "\x1b[%i%p1%d;%p2%dH",
AutoMargin: true,
XTermLike: true,
})
}
-38
View File
@@ -1,38 +0,0 @@
// Generated automatically. DO NOT HAND-EDIT.
package linux
import "github.com/gdamore/tcell/v2/terminfo"
func init() {
// Linux console
terminfo.AddTerminfo(&terminfo.Terminfo{
Name: "linux",
Colors: 8,
Clear: "\x1b[H\x1b[J",
ShowCursor: "\x1b[?25h\x1b[?0c",
HideCursor: "\x1b[?25l\x1b[?1c",
AttrOff: "\x1b[m\x0f",
Underline: "\x1b[4m",
Bold: "\x1b[1m",
Dim: "\x1b[2m",
Blink: "\x1b[5m",
Reverse: "\x1b[7m",
SetFg: "\x1b[3%p1%dm",
SetBg: "\x1b[4%p1%dm",
SetFgBg: "\x1b[3%p1%d;4%p2%dm",
ResetFgBg: "\x1b[39;49m",
PadChar: "\x00",
AltChars: "++,,--..00``aaffgghhiijjkkllmmnnooppqqrrssttuuvvwwxxyyzz{{||}}~~",
EnterAcs: "\x0e",
ExitAcs: "\x0f",
EnableAcs: "\x1b)0",
EnableAutoMargin: "\x1b[?7h",
DisableAutoMargin: "\x1b[?7l",
Mouse: "\x1b[M",
SetCursor: "\x1b[%i%p1%d;%p2%dH",
AutoMargin: true,
InsertChar: "\x1b[@",
})
}
-29
View File
@@ -1,29 +0,0 @@
aixterm
alacritty
ansi
cygwin
dtterm
eterm,eterm-color|emacs
gnome,gnome-256color
hpterm
konsole,konsole-256color
kterm
linux
pcansi
rxvt,rxvt-256color,rxvt-88color,rxvt-unicode,rxvt-unicode-256color
screen,screen-256color
st,st-256color|simpleterm
tmux,tmux-256color
vt100
vt102
vt220
vt320
vt400
vt420
wy50
wy60
wy99-ansi,wy99a-ansi
xfce
xterm,xterm-88color,xterm-256color
xterm-ghostty
xterm-kitty
-32
View File
@@ -1,32 +0,0 @@
// Generated automatically. DO NOT HAND-EDIT.
package pcansi
import "github.com/gdamore/tcell/v2/terminfo"
func init() {
// ibm-pc terminal programs claiming to be ANSI
terminfo.AddTerminfo(&terminfo.Terminfo{
Name: "pcansi",
Columns: 80,
Lines: 24,
Colors: 8,
Clear: "\x1b[H\x1b[J",
AttrOff: "\x1b[0;10m",
Underline: "\x1b[4m",
Bold: "\x1b[1m",
Blink: "\x1b[5m",
Reverse: "\x1b[7m",
SetFg: "\x1b[3%p1%dm",
SetBg: "\x1b[4%p1%dm",
SetFgBg: "\x1b[3%p1%d;4%p2%dm",
ResetFgBg: "\x1b[37;40m",
PadChar: "\x00",
AltChars: "+\x10,\x11-\x18.\x190\xdb`\x04a\xb1f\xf8g\xf1h\xb0j\xd9k\xbfl\xdam\xc0n\xc5o~p\xc4q\xc4r\xc4s_t\xc3u\xb4v\xc1w\xc2x\xb3y\xf3z\xf2{\xe3|\xd8}\x9c~\xfe",
EnterAcs: "\x1b[12m",
ExitAcs: "\x1b[10m",
SetCursor: "\x1b[%i%p1%d;%p2%dH",
AutoMargin: true,
})
}
-176
View File
@@ -1,176 +0,0 @@
// Generated automatically. DO NOT HAND-EDIT.
package rxvt
import "github.com/gdamore/tcell/v2/terminfo"
func init() {
// rxvt terminal emulator (X Window System)
terminfo.AddTerminfo(&terminfo.Terminfo{
Name: "rxvt",
Aliases: []string{"rxvt-color"},
Columns: 80,
Lines: 24,
Colors: 8,
Clear: "\x1b[H\x1b[2J",
EnterCA: "\x1b7\x1b[?47h",
ExitCA: "\x1b[2J\x1b[?47l\x1b8",
ShowCursor: "\x1b[?25h",
HideCursor: "\x1b[?25l",
AttrOff: "\x1b[m\x0f",
Underline: "\x1b[4m",
Bold: "\x1b[1m",
Blink: "\x1b[5m",
Reverse: "\x1b[7m",
EnterKeypad: "\x1b=",
ExitKeypad: "\x1b>",
SetFg: "\x1b[3%p1%dm",
SetBg: "\x1b[4%p1%dm",
SetFgBg: "\x1b[3%p1%d;4%p2%dm",
ResetFgBg: "\x1b[39;49m",
PadChar: "\x00",
AltChars: "``aaffggjjkkllmmnnooppqqrrssttuuvvwwxxyyzz{{||}}~~",
EnterAcs: "\x0e",
ExitAcs: "\x0f",
EnableAcs: "\x1b(B\x1b)0",
Mouse: "\x1b[M",
SetCursor: "\x1b[%i%p1%d;%p2%dH",
AutoMargin: true,
XTermLike: true,
})
// rxvt 2.7.9 with xterm 256-colors
terminfo.AddTerminfo(&terminfo.Terminfo{
Name: "rxvt-256color",
Columns: 80,
Lines: 24,
Colors: 256,
Clear: "\x1b[H\x1b[2J",
EnterCA: "\x1b7\x1b[?47h",
ExitCA: "\x1b[2J\x1b[?47l\x1b8",
ShowCursor: "\x1b[?25h",
HideCursor: "\x1b[?25l",
AttrOff: "\x1b[m\x0f",
Underline: "\x1b[4m",
Bold: "\x1b[1m",
Blink: "\x1b[5m",
Reverse: "\x1b[7m",
EnterKeypad: "\x1b=",
ExitKeypad: "\x1b>",
SetFg: "\x1b[%?%p1%{8}%<%t3%p1%d%e%p1%{16}%<%t9%p1%{8}%-%d%e38;5;%p1%d%;m",
SetBg: "\x1b[%?%p1%{8}%<%t4%p1%d%e%p1%{16}%<%t10%p1%{8}%-%d%e48;5;%p1%d%;m",
SetFgBg: "\x1b[%?%p1%{8}%<%t3%p1%d%e%p1%{16}%<%t9%p1%{8}%-%d%e38;5;%p1%d%;;%?%p2%{8}%<%t4%p2%d%e%p2%{16}%<%t10%p2%{8}%-%d%e48;5;%p2%d%;m",
ResetFgBg: "\x1b[39;49m",
PadChar: "\x00",
AltChars: "``aaffggjjkkllmmnnooppqqrrssttuuvvwwxxyyzz{{||}}~~",
EnterAcs: "\x0e",
ExitAcs: "\x0f",
EnableAcs: "\x1b(B\x1b)0",
Mouse: "\x1b[M",
SetCursor: "\x1b[%i%p1%d;%p2%dH",
AutoMargin: true,
XTermLike: true,
})
// rxvt 2.7.9 with xterm 88-colors
terminfo.AddTerminfo(&terminfo.Terminfo{
Name: "rxvt-88color",
Columns: 80,
Lines: 24,
Colors: 88,
Clear: "\x1b[H\x1b[2J",
EnterCA: "\x1b7\x1b[?47h",
ExitCA: "\x1b[2J\x1b[?47l\x1b8",
ShowCursor: "\x1b[?25h",
HideCursor: "\x1b[?25l",
AttrOff: "\x1b[m\x0f",
Underline: "\x1b[4m",
Bold: "\x1b[1m",
Blink: "\x1b[5m",
Reverse: "\x1b[7m",
EnterKeypad: "\x1b=",
ExitKeypad: "\x1b>",
SetFg: "\x1b[%?%p1%{8}%<%t3%p1%d%e%p1%{16}%<%t9%p1%{8}%-%d%e38;5;%p1%d%;m",
SetBg: "\x1b[%?%p1%{8}%<%t4%p1%d%e%p1%{16}%<%t10%p1%{8}%-%d%e48;5;%p1%d%;m",
SetFgBg: "\x1b[%?%p1%{8}%<%t3%p1%d%e%p1%{16}%<%t9%p1%{8}%-%d%e38;5;%p1%d%;;%?%p2%{8}%<%t4%p2%d%e%p2%{16}%<%t10%p2%{8}%-%d%e48;5;%p2%d%;m",
ResetFgBg: "\x1b[39;49m",
PadChar: "\x00",
AltChars: "``aaffggjjkkllmmnnooppqqrrssttuuvvwwxxyyzz{{||}}~~",
EnterAcs: "\x0e",
ExitAcs: "\x0f",
EnableAcs: "\x1b(B\x1b)0",
Mouse: "\x1b[M",
SetCursor: "\x1b[%i%p1%d;%p2%dH",
AutoMargin: true,
XTermLike: true,
})
// rxvt-unicode terminal (X Window System)
terminfo.AddTerminfo(&terminfo.Terminfo{
Name: "rxvt-unicode",
Columns: 80,
Lines: 24,
Colors: 88,
Clear: "\x1b[H\x1b[2J",
EnterCA: "\x1b[?1049h",
ExitCA: "\x1b[r\x1b[?1049l",
ShowCursor: "\x1b[?12l\x1b[?25h",
HideCursor: "\x1b[?25l",
AttrOff: "\x1b[m\x1b(B",
Underline: "\x1b[4m",
Bold: "\x1b[1m",
Italic: "\x1b[3m",
Blink: "\x1b[5m",
Reverse: "\x1b[7m",
EnterKeypad: "\x1b=",
ExitKeypad: "\x1b>",
SetFg: "\x1b[38;5;%p1%dm",
SetBg: "\x1b[48;5;%p1%dm",
SetFgBg: "\x1b[38;5;%p1%d;48;5;%p2%dm",
ResetFgBg: "\x1b[39;49m",
AltChars: "+C,D-A.B0E``aaffgghFiGjjkkllmmnnooppqqrrssttuuvvwwxxyyzz{{||}}~~",
EnterAcs: "\x1b(0",
ExitAcs: "\x1b(B",
EnableAutoMargin: "\x1b[?7h",
DisableAutoMargin: "\x1b[?7l",
Mouse: "\x1b[M",
SetCursor: "\x1b[%i%p1%d;%p2%dH",
AutoMargin: true,
InsertChar: "\x1b[@",
})
// rxvt-unicode terminal with 256 colors (X Window System)
terminfo.AddTerminfo(&terminfo.Terminfo{
Name: "rxvt-unicode-256color",
Columns: 80,
Lines: 24,
Colors: 256,
Clear: "\x1b[H\x1b[2J",
EnterCA: "\x1b[?1049h",
ExitCA: "\x1b[r\x1b[?1049l",
ShowCursor: "\x1b[?12l\x1b[?25h",
HideCursor: "\x1b[?25l",
AttrOff: "\x1b[m\x1b(B",
Underline: "\x1b[4m",
Bold: "\x1b[1m",
Italic: "\x1b[3m",
Blink: "\x1b[5m",
Reverse: "\x1b[7m",
EnterKeypad: "\x1b=",
ExitKeypad: "\x1b>",
SetFg: "\x1b[38;5;%p1%dm",
SetBg: "\x1b[48;5;%p1%dm",
SetFgBg: "\x1b[38;5;%p1%d;48;5;%p2%dm",
ResetFgBg: "\x1b[39;49m",
AltChars: "+C,D-A.B0E``aaffgghFiGjjkkllmmnnooppqqrrssttuuvvwwxxyyzz{{||}}~~",
EnterAcs: "\x1b(0",
ExitAcs: "\x1b(B",
EnableAutoMargin: "\x1b[?7h",
DisableAutoMargin: "\x1b[?7l",
Mouse: "\x1b[M",
SetCursor: "\x1b[%i%p1%d;%p2%dH",
AutoMargin: true,
InsertChar: "\x1b[@",
})
}
-74
View File
@@ -1,74 +0,0 @@
// Generated automatically. DO NOT HAND-EDIT.
package screen
import "github.com/gdamore/tcell/v2/terminfo"
func init() {
// VT 100/ANSI X3.64 virtual terminal
terminfo.AddTerminfo(&terminfo.Terminfo{
Name: "screen",
Columns: 80,
Lines: 24,
Colors: 8,
Clear: "\x1b[H\x1b[J",
EnterCA: "\x1b[?1049h",
ExitCA: "\x1b[?1049l",
ShowCursor: "\x1b[34h\x1b[?25h",
HideCursor: "\x1b[?25l",
AttrOff: "\x1b[m\x0f",
Underline: "\x1b[4m",
Bold: "\x1b[1m",
Dim: "\x1b[2m",
Blink: "\x1b[5m",
Reverse: "\x1b[7m",
EnterKeypad: "\x1b[?1h\x1b=",
ExitKeypad: "\x1b[?1l\x1b>",
SetFg: "\x1b[3%p1%dm",
SetBg: "\x1b[4%p1%dm",
SetFgBg: "\x1b[3%p1%d;4%p2%dm",
ResetFgBg: "\x1b[39;49m",
PadChar: "\x00",
AltChars: "++,,--..00``aaffgghhiijjkkllmmnnooppqqrrssttuuvvwwxxyyzz{{||}}~~",
EnterAcs: "\x0e",
ExitAcs: "\x0f",
EnableAcs: "\x1b(B\x1b)0",
Mouse: "\x1b[M",
SetCursor: "\x1b[%i%p1%d;%p2%dH",
AutoMargin: true,
})
// GNU Screen with 256 colors
terminfo.AddTerminfo(&terminfo.Terminfo{
Name: "screen-256color",
Columns: 80,
Lines: 24,
Colors: 256,
Clear: "\x1b[H\x1b[J",
EnterCA: "\x1b[?1049h",
ExitCA: "\x1b[?1049l",
ShowCursor: "\x1b[34h\x1b[?25h",
HideCursor: "\x1b[?25l",
AttrOff: "\x1b[m\x0f",
Underline: "\x1b[4m",
Bold: "\x1b[1m",
Dim: "\x1b[2m",
Blink: "\x1b[5m",
Reverse: "\x1b[7m",
EnterKeypad: "\x1b[?1h\x1b=",
ExitKeypad: "\x1b[?1l\x1b>",
SetFg: "\x1b[%?%p1%{8}%<%t3%p1%d%e%p1%{16}%<%t9%p1%{8}%-%d%e38;5;%p1%d%;m",
SetBg: "\x1b[%?%p1%{8}%<%t4%p1%d%e%p1%{16}%<%t10%p1%{8}%-%d%e48;5;%p1%d%;m",
SetFgBg: "\x1b[%?%p1%{8}%<%t3%p1%d%e%p1%{16}%<%t9%p1%{8}%-%d%e38;5;%p1%d%;;%?%p2%{8}%<%t4%p2%d%e%p2%{16}%<%t10%p2%{8}%-%d%e48;5;%p2%d%;m",
ResetFgBg: "\x1b[39;49m",
PadChar: "\x00",
AltChars: "++,,--..00``aaffgghhiijjkkllmmnnooppqqrrssttuuvvwwxxyyzz{{||}}~~",
EnterAcs: "\x0e",
ExitAcs: "\x0f",
EnableAcs: "\x1b(B\x1b)0",
Mouse: "\x1b[M",
SetCursor: "\x1b[%i%p1%d;%p2%dH",
AutoMargin: true,
})
}
-80
View File
@@ -1,80 +0,0 @@
// Generated automatically. DO NOT HAND-EDIT.
package simpleterm
import "github.com/gdamore/tcell/v2/terminfo"
func init() {
// aka simpleterm
terminfo.AddTerminfo(&terminfo.Terminfo{
Name: "st",
Aliases: []string{"stterm"},
Columns: 80,
Lines: 24,
Colors: 8,
Clear: "\x1b[H\x1b[2J",
EnterCA: "\x1b[?1049h",
ExitCA: "\x1b[?1049l",
ShowCursor: "\x1b[?25h",
HideCursor: "\x1b[?25l",
AttrOff: "\x1b[0m",
Underline: "\x1b[4m",
Bold: "\x1b[1m",
Dim: "\x1b[2m",
Italic: "\x1b[3m",
Blink: "\x1b[5m",
Reverse: "\x1b[7m",
EnterKeypad: "\x1b[?1h\x1b=",
ExitKeypad: "\x1b[?1l\x1b>",
SetFg: "\x1b[3%p1%dm",
SetBg: "\x1b[4%p1%dm",
SetFgBg: "\x1b[3%p1%d;4%p2%dm",
ResetFgBg: "\x1b[39;49m",
AltChars: "+C,D-A.B0E``aaffgghFiGjjkkllmmnnooppqqrrssttuuvvwwxxyyzz{{||}}~~",
EnterAcs: "\x1b(0",
ExitAcs: "\x1b(B",
EnableAcs: "\x1b)0",
StrikeThrough: "\x1b[9m",
Mouse: "\x1b[M",
SetCursor: "\x1b[%i%p1%d;%p2%dH",
AutoMargin: true,
XTermLike: true,
})
// simpleterm with 256 colors
terminfo.AddTerminfo(&terminfo.Terminfo{
Name: "st-256color",
Aliases: []string{"stterm-256color"},
Columns: 80,
Lines: 24,
Colors: 256,
Clear: "\x1b[H\x1b[2J",
EnterCA: "\x1b[?1049h",
ExitCA: "\x1b[?1049l",
ShowCursor: "\x1b[?25h",
HideCursor: "\x1b[?25l",
AttrOff: "\x1b[0m",
Underline: "\x1b[4m",
Bold: "\x1b[1m",
Dim: "\x1b[2m",
Italic: "\x1b[3m",
Blink: "\x1b[5m",
Reverse: "\x1b[7m",
EnterKeypad: "\x1b[?1h\x1b=",
ExitKeypad: "\x1b[?1l\x1b>",
SetFg: "\x1b[%?%p1%{8}%<%t3%p1%d%e%p1%{16}%<%t9%p1%{8}%-%d%e38;5;%p1%d%;m",
SetBg: "\x1b[%?%p1%{8}%<%t4%p1%d%e%p1%{16}%<%t10%p1%{8}%-%d%e48;5;%p1%d%;m",
SetFgBg: "\x1b[%?%p1%{8}%<%t3%p1%d%e%p1%{16}%<%t9%p1%{8}%-%d%e38;5;%p1%d%;;%?%p2%{8}%<%t4%p2%d%e%p2%{16}%<%t10%p2%{8}%-%d%e48;5;%p2%d%;m",
ResetFgBg: "\x1b[39;49m",
AltChars: "+C,D-A.B0E``aaffgghFiGjjkkllmmnnooppqqrrssttuuvvwwxxyyzz{{||}}~~",
EnterAcs: "\x1b(0",
ExitAcs: "\x1b(B",
EnableAcs: "\x1b)0",
StrikeThrough: "\x1b[9m",
Mouse: "\x1b[M",
SetCursor: "\x1b[%i%p1%d;%p2%dH",
AutoMargin: true,
XTermLike: true,
})
}
-60
View File
@@ -1,60 +0,0 @@
// Copyright 2021 The TCell Authors
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use file except in compliance with the License.
// You may obtain a copy of the license at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
// This terminal definition is hand-coded, as the default terminfo for
// this terminal is busted with respect to color. Unlike pretty much every
// other ANSI compliant terminal, this terminal cannot combine foreground and
// background escapes. The default terminfo also only provides escapes for
// 16-bit color.
package sun
import "github.com/gdamore/tcell/v2/terminfo"
func init() {
// Sun Microsystems Inc. workstation console
terminfo.AddTerminfo(&terminfo.Terminfo{
Name: "sun",
Aliases: []string{"sun1", "sun2"},
Columns: 80,
Lines: 34,
Clear: "\f",
AttrOff: "\x1b[m",
Reverse: "\x1b[7m",
PadChar: "\x00",
SetCursor: "\x1b[%i%p1%d;%p2%dH",
AutoMargin: true,
InsertChar: "\x1b[@",
})
// Sun Microsystems Workstation console with color support (IA systems)
terminfo.AddTerminfo(&terminfo.Terminfo{
Name: "sun-color",
Columns: 80,
Lines: 34,
Colors: 256,
Clear: "\f",
AttrOff: "\x1b[m",
Bold: "\x1b[1m",
Reverse: "\x1b[7m",
SetFg: "\x1b[38;5;%p1%dm",
SetBg: "\x1b[48;5;%p1%dm",
ResetFgBg: "\x1b[0m",
PadChar: "\x00",
SetCursor: "\x1b[%i%p1%d;%p2%dH",
AutoMargin: true,
InsertChar: "\x1b[@",
})
}
-80
View File
@@ -1,80 +0,0 @@
// Generated automatically. DO NOT HAND-EDIT.
package tmux
import "github.com/gdamore/tcell/v2/terminfo"
func init() {
// tmux terminal multiplexer
terminfo.AddTerminfo(&terminfo.Terminfo{
Name: "tmux",
Columns: 80,
Lines: 24,
Colors: 8,
Clear: "\x1b[H\x1b[J",
EnterCA: "\x1b[?1049h",
ExitCA: "\x1b[?1049l",
ShowCursor: "\x1b[34h\x1b[?25h",
HideCursor: "\x1b[?25l",
AttrOff: "\x1b[m\x0f",
Underline: "\x1b[4m",
Bold: "\x1b[1m",
Dim: "\x1b[2m",
Italic: "\x1b[3m",
Blink: "\x1b[5m",
Reverse: "\x1b[7m",
EnterKeypad: "\x1b[?1h\x1b=",
ExitKeypad: "\x1b[?1l\x1b>",
SetFg: "\x1b[3%p1%dm",
SetBg: "\x1b[4%p1%dm",
SetFgBg: "\x1b[3%p1%d;4%p2%dm",
ResetFgBg: "\x1b[39;49m",
PadChar: "\x00",
AltChars: "++,,--..00``aaffgghhiijjkkllmmnnooppqqrrssttuuvvwwxxyyzz{{||}}~~",
EnterAcs: "\x0e",
ExitAcs: "\x0f",
EnableAcs: "\x1b(B\x1b)0",
StrikeThrough: "\x1b[9m",
Mouse: "\x1b[M",
SetCursor: "\x1b[%i%p1%d;%p2%dH",
AutoMargin: true,
XTermLike: true,
})
// tmux with 256 colors
terminfo.AddTerminfo(&terminfo.Terminfo{
Name: "tmux-256color",
Columns: 80,
Lines: 24,
Colors: 256,
Clear: "\x1b[H\x1b[J",
EnterCA: "\x1b[?1049h",
ExitCA: "\x1b[?1049l",
ShowCursor: "\x1b[34h\x1b[?25h",
HideCursor: "\x1b[?25l",
AttrOff: "\x1b[m\x0f",
Underline: "\x1b[4m",
Bold: "\x1b[1m",
Dim: "\x1b[2m",
Italic: "\x1b[3m",
Blink: "\x1b[5m",
Reverse: "\x1b[7m",
EnterKeypad: "\x1b[?1h\x1b=",
ExitKeypad: "\x1b[?1l\x1b>",
SetFg: "\x1b[%?%p1%{8}%<%t3%p1%d%e%p1%{16}%<%t9%p1%{8}%-%d%e38;5;%p1%d%;m",
SetBg: "\x1b[%?%p1%{8}%<%t4%p1%d%e%p1%{16}%<%t10%p1%{8}%-%d%e48;5;%p1%d%;m",
SetFgBg: "\x1b[%?%p1%{8}%<%t3%p1%d%e%p1%{16}%<%t9%p1%{8}%-%d%e38;5;%p1%d%;;%?%p2%{8}%<%t4%p2%d%e%p2%{16}%<%t10%p2%{8}%-%d%e48;5;%p2%d%;m",
ResetFgBg: "\x1b[39;49m",
PadChar: "\x00",
AltChars: "++,,--..00``aaffgghhiijjkkllmmnnooppqqrrssttuuvvwwxxyyzz{{||}}~~",
EnterAcs: "\x0e",
ExitAcs: "\x0f",
EnableAcs: "\x1b(B\x1b)0",
StrikeThrough: "\x1b[9m",
Mouse: "\x1b[M",
SetCursor: "\x1b[%i%p1%d;%p2%dH",
AutoMargin: true,
XTermLike: true,
})
}
-628
View File
@@ -1,628 +0,0 @@
// Copyright 2025 The TCell Authors
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use file except in compliance with the License.
// You may obtain a copy of the license at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
package terminfo
import (
"bytes"
"errors"
"fmt"
"io"
"os"
"strconv"
"strings"
"sync"
"time"
)
var (
// ErrTermNotFound indicates that a suitable terminal entry could
// not be found. This can result from either not having TERM set,
// or from the TERM failing to support certain minimal functionality,
// in particular absolute cursor addressability (the cup capability)
// is required. For example, legacy "adm3" lacks this capability,
// whereas the slightly newer "adm3a" supports it. This failure
// occurs most often with "dumb".
ErrTermNotFound = errors.New("terminal entry not found")
)
// Terminfo represents a terminfo entry. Note that we use friendly names
// in Go, but when we write out JSON, we use the same names as terminfo.
// The name, aliases and smous, rmous fields do not come from terminfo directly.
type Terminfo struct {
Name string
Aliases []string
Columns int // cols
Lines int // lines
Colors int // colors
Clear string // clear
EnterCA string // smcup
ExitCA string // rmcup
ShowCursor string // cnorm
HideCursor string // civis
AttrOff string // sgr0
Underline string // smul
Bold string // bold
Blink string // blink
Reverse string // rev
Dim string // dim
Italic string // sitm
EnterKeypad string // smkx
ExitKeypad string // rmkx
SetFg string // setaf
SetBg string // setab
ResetFgBg string // op
SetCursor string // cup
PadChar string // pad
Mouse string // kmous
AltChars string // acsc
EnterAcs string // smacs
ExitAcs string // rmacs
EnableAcs string // enacs
// These are non-standard extensions to terminfo. This includes
// true color support, and some additional keys. Its kind of bizarre
// that shifted variants of left and right exist, but not up and down.
// Terminal support for these are going to vary amongst XTerm
// emulations, so don't depend too much on them in your application.
StrikeThrough string // smxx
SetFgBg string // setfgbg
SetFgBgRGB string // setfgbgrgb
SetFgRGB string // setfrgb
SetBgRGB string // setbrgb
InsertChar string // string to insert a character (ich1)
AutoMargin bool // true if writing to last cell in line advances
TrueColor bool // true if the terminal supports direct color
DisableAutoMargin string // smam
EnableAutoMargin string // rmam
XTermLike bool // (XT) has XTerm extensions
}
type stack []any
func (st stack) Push(v any) stack {
if b, ok := v.(bool); ok {
if b {
return append(st, 1)
} else {
return append(st, 0)
}
}
return append(st, v)
}
func (st stack) PopString() (string, stack) {
if len(st) > 0 {
e := st[len(st)-1]
var s string
switch v := e.(type) {
case int:
s = strconv.Itoa(v)
case string:
s = v
}
return s, st[:len(st)-1]
}
return "", st
}
func (st stack) PopInt() (int, stack) {
if len(st) > 0 {
e := st[len(st)-1]
var i int
switch v := e.(type) {
case int:
i = v
case string:
i, _ = strconv.Atoi(v)
}
return i, st[:len(st)-1]
}
return 0, st
}
// static vars
var svars [26]string
type paramsBuffer struct {
out bytes.Buffer
buf bytes.Buffer
}
// Start initializes the params buffer with the initial string data.
// It also locks the paramsBuffer. The caller must call End() when
// finished.
func (pb *paramsBuffer) Start(s string) {
pb.out.Reset()
pb.buf.Reset()
pb.buf.WriteString(s)
}
// End returns the final output from TParam, but it also releases the lock.
func (pb *paramsBuffer) End() string {
s := pb.out.String()
return s
}
// NextCh returns the next input character to the expander.
func (pb *paramsBuffer) NextCh() (byte, error) {
return pb.buf.ReadByte()
}
// PutCh "emits" (rather schedules for output) a single byte character.
func (pb *paramsBuffer) PutCh(ch byte) {
pb.out.WriteByte(ch)
}
// PutString schedules a string for output.
func (pb *paramsBuffer) PutString(s string) {
pb.out.WriteString(s)
}
// TParm takes a terminfo parameterized string, such as setaf or cup, and
// evaluates the string, and returns the result with the parameter
// applied.
func (t *Terminfo) TParm(s string, p ...any) string {
var stk stack
var a string
var ai, bi int
var dvars [26]string
var params [9]any
var pb = &paramsBuffer{}
pb.Start(s)
// make sure we always have 9 parameters -- makes it easier
// later to skip checks
for i := 0; i < len(params) && i < len(p); i++ {
params[i] = p[i]
}
const (
emit = iota
toEnd
toElse
)
skip := emit
for {
ch, err := pb.NextCh()
if err != nil {
break
}
if ch != '%' {
if skip == emit {
pb.PutCh(ch)
}
continue
}
ch, err = pb.NextCh()
if err != nil {
// XXX Error
break
}
if skip == toEnd {
if ch == ';' {
skip = emit
}
continue
} else if skip == toElse {
if ch == 'e' || ch == ';' {
skip = emit
}
continue
}
switch ch {
case '%': // quoted %
pb.PutCh(ch)
case 'i': // increment both parameters (ANSI cup support)
if i, ok := params[0].(int); ok {
params[0] = i + 1
}
if i, ok := params[1].(int); ok {
params[1] = i + 1
}
case 's':
// NB: 's', 'c', and 'd' below are special cased for
// efficiency. They could be handled by the richer
// format support below, less efficiently.
a, stk = stk.PopString()
pb.PutString(a)
case 'c':
// Integer as special character.
ai, stk = stk.PopInt()
pb.PutCh(byte(ai))
case 'd':
ai, stk = stk.PopInt()
pb.PutString(strconv.Itoa(ai))
case '0', '1', '2', '3', '4', '5', '6', '7', '8', '9', 'x', 'X', 'o', ':':
// This is pretty suboptimal, but this is rarely used.
// None of the mainstream terminals use any of this,
// and it would surprise me if this code is ever
// executed outside test cases.
f := "%"
if ch == ':' {
ch, _ = pb.NextCh()
}
f += string(ch)
for ch == '+' || ch == '-' || ch == '#' || ch == ' ' {
ch, _ = pb.NextCh()
f += string(ch)
}
for (ch >= '0' && ch <= '9') || ch == '.' {
ch, _ = pb.NextCh()
f += string(ch)
}
switch ch {
case 'd', 'x', 'X', 'o':
ai, stk = stk.PopInt()
pb.PutString(fmt.Sprintf(f, ai))
case 's':
a, stk = stk.PopString()
pb.PutString(fmt.Sprintf(f, a))
case 'c':
ai, stk = stk.PopInt()
pb.PutString(fmt.Sprintf(f, ai))
}
case 'p': // push parameter
ch, _ = pb.NextCh()
ai = int(ch - '1')
if ai >= 0 && ai < len(params) {
stk = stk.Push(params[ai])
} else {
stk = stk.Push(0)
}
case 'P': // pop & store variable
ch, _ = pb.NextCh()
if ch >= 'A' && ch <= 'Z' {
svars[int(ch-'A')], stk = stk.PopString()
} else if ch >= 'a' && ch <= 'z' {
dvars[int(ch-'a')], stk = stk.PopString()
}
case 'g': // recall & push variable
ch, _ = pb.NextCh()
if ch >= 'A' && ch <= 'Z' {
stk = stk.Push(svars[int(ch-'A')])
} else if ch >= 'a' && ch <= 'z' {
stk = stk.Push(dvars[int(ch-'a')])
}
case '\'': // push(char) - the integer value of it
ch, _ = pb.NextCh()
_, _ = pb.NextCh() // must be ' but we don't check
stk = stk.Push(int(ch))
case '{': // push(int)
ai = 0
ch, _ = pb.NextCh()
for ch >= '0' && ch <= '9' {
ai *= 10
ai += int(ch - '0')
ch, _ = pb.NextCh()
}
// ch must be '}' but no verification
stk = stk.Push(ai)
case 'l': // push(strlen(pop))
a, stk = stk.PopString()
stk = stk.Push(len(a))
case '+':
bi, stk = stk.PopInt()
ai, stk = stk.PopInt()
stk = stk.Push(ai + bi)
case '-':
bi, stk = stk.PopInt()
ai, stk = stk.PopInt()
stk = stk.Push(ai - bi)
case '*':
bi, stk = stk.PopInt()
ai, stk = stk.PopInt()
stk = stk.Push(ai * bi)
case '/':
bi, stk = stk.PopInt()
ai, stk = stk.PopInt()
if bi != 0 {
stk = stk.Push(ai / bi)
} else {
stk = stk.Push(0)
}
case 'm': // push(pop mod pop)
bi, stk = stk.PopInt()
ai, stk = stk.PopInt()
if bi != 0 {
stk = stk.Push(ai % bi)
} else {
stk = stk.Push(0)
}
case '&': // AND
bi, stk = stk.PopInt()
ai, stk = stk.PopInt()
stk = stk.Push(ai & bi)
case '|': // OR
bi, stk = stk.PopInt()
ai, stk = stk.PopInt()
stk = stk.Push(ai | bi)
case '^': // XOR
bi, stk = stk.PopInt()
ai, stk = stk.PopInt()
stk = stk.Push(ai ^ bi)
case '~': // bit complement
ai, stk = stk.PopInt()
stk = stk.Push(ai ^ -1)
case '!': // logical NOT
ai, stk = stk.PopInt()
stk = stk.Push(ai == 0)
case '=': // numeric compare
bi, stk = stk.PopInt()
ai, stk = stk.PopInt()
stk = stk.Push(ai == bi)
case '>': // greater than, numeric
bi, stk = stk.PopInt()
ai, stk = stk.PopInt()
stk = stk.Push(ai > bi)
case '<': // less than, numeric
bi, stk = stk.PopInt()
ai, stk = stk.PopInt()
stk = stk.Push(ai < bi)
case '?': // start conditional
case ';':
skip = emit
case 't':
ai, stk = stk.PopInt()
if ai == 0 {
skip = toElse
}
case 'e':
skip = toEnd
default:
pb.PutString("%" + string(ch))
}
}
return pb.End()
}
// TPuts emits the string to the writer, but expands inline padding
// indications (of the form $<[delay]> where [delay] is msec) to
// a suitable time (unless the terminfo string indicates this isn't needed
// by specifying npc - no padding). All Terminfo based strings should be
// emitted using this function.
func (t *Terminfo) TPuts(w io.Writer, s string) {
for {
beg := strings.Index(s, "$<")
if beg < 0 {
// Most strings don't need padding, which is good news!
_, _ = io.WriteString(w, s)
return
}
_, _ = io.WriteString(w, s[:beg])
s = s[beg+2:]
end := strings.Index(s, ">")
if end < 0 {
// unterminated.. just emit bytes unadulterated
_, _ = io.WriteString(w, "$<"+s)
return
}
val := s[:end]
s = s[end+1:]
padus := 0
unit := time.Millisecond
dot := false
loop:
for i := range val {
switch val[i] {
case '0', '1', '2', '3', '4', '5', '6', '7', '8', '9':
padus *= 10
padus += int(val[i] - '0')
if dot {
unit /= 10
}
case '.':
if !dot {
dot = true
} else {
break loop
}
default:
break loop
}
}
// Curses historically uses padding to achieve "fine grained"
// delays. We have much better clocks these days, and so we
// do not rely on padding but simply sleep a bit.
if len(t.PadChar) > 0 {
time.Sleep(unit * time.Duration(padus))
}
}
}
// TGoto returns a string suitable for addressing the cursor at the given
// row and column. The origin 0, 0 is in the upper left corner of the screen.
func (t *Terminfo) TGoto(col, row int) string {
return t.TParm(t.SetCursor, row, col)
}
// TColor returns a string corresponding to the given foreground and background
// colors. Either fg or bg can be set to -1 to elide.
func (t *Terminfo) TColor(fi, bi int) string {
rv := ""
// As a special case, we map bright colors to lower versions if the
// color table only holds 8. For the remaining 240 colors, the user
// is out of luck. Someday we could create a mapping table, but its
// not worth it.
if t.Colors == 8 {
if fi > 7 && fi < 16 {
fi -= 8
}
if bi > 7 && bi < 16 {
bi -= 8
}
}
if t.Colors > fi && fi >= 0 {
rv += t.TParm(t.SetFg, fi)
}
if t.Colors > bi && bi >= 0 {
rv += t.TParm(t.SetBg, bi)
}
return rv
}
var (
dblock sync.Mutex
terminfos = make(map[string]*Terminfo)
)
// AddTerminfo can be called to register a new Terminfo entry.
func AddTerminfo(t *Terminfo) {
dblock.Lock()
terminfos[t.Name] = t
for _, x := range t.Aliases {
terminfos[x] = t
}
dblock.Unlock()
}
// LookupTerminfo attempts to find a definition for the named $TERM.
func LookupTerminfo(name string) (*Terminfo, error) {
if name == "" {
// else on windows: index out of bounds
// on the name[0] reference below
return nil, ErrTermNotFound
}
addtruecolor := false
add256color := false
switch os.Getenv("COLORTERM") {
case "truecolor", "24bit", "24-bit":
addtruecolor = true
}
dblock.Lock()
t := terminfos[name]
dblock.Unlock()
// If the name ends in -truecolor, then fabricate an entry
// from the corresponding -256color, -color, or bare terminal.
if t != nil && t.TrueColor {
addtruecolor = true
} else if t == nil && strings.HasSuffix(name, "-truecolor") {
suffixes := []string{
"-256color",
"-88color",
"-color",
"",
}
base := name[:len(name)-len("-truecolor")]
for _, s := range suffixes {
if t, _ = LookupTerminfo(base + s); t != nil {
addtruecolor = true
break
}
}
}
// If the name ends in -256color, maybe fabricate using the xterm 256 color sequences
if t == nil && strings.HasSuffix(name, "-256color") {
suffixes := []string{
"-88color",
"-color",
}
base := name[:len(name)-len("-256color")]
for _, s := range suffixes {
if t, _ = LookupTerminfo(base + s); t != nil {
add256color = true
break
}
}
}
if t == nil {
return nil, ErrTermNotFound
}
switch os.Getenv("TCELL_TRUECOLOR") {
case "":
case "disable":
addtruecolor = false
default:
addtruecolor = true
}
// If the user has requested 24-bit color with $COLORTERM, then
// amend the value (unless already present). This means we don't
// need to have a value present.
if addtruecolor &&
t.SetFgBgRGB == "" &&
t.SetFgRGB == "" &&
t.SetBgRGB == "" {
// Supply vanilla ISO 8613-6:1994 24-bit color sequences.
t.SetFgRGB = "\x1b[38;2;%p1%d;%p2%d;%p3%dm"
t.SetBgRGB = "\x1b[48;2;%p1%d;%p2%d;%p3%dm"
t.SetFgBgRGB = "\x1b[38;2;%p1%d;%p2%d;%p3%d;" +
"48;2;%p4%d;%p5%d;%p6%dm"
}
if add256color {
t.Colors = 256
t.SetFg = "\x1b[%?%p1%{8}%<%t3%p1%d%e%p1%{16}%<%t9%p1%{8}%-%d%e38;5;%p1%d%;m"
t.SetBg = "\x1b[%?%p1%{8}%<%t4%p1%d%e%p1%{16}%<%t10%p1%{8}%-%d%e48;5;%p1%d%;m"
t.SetFgBg = "\x1b[%?%p1%{8}%<%t3%p1%d%e%p1%{16}%<%t9%p1%{8}%-%d%e38;5;%p1%d%;;%?%p2%{8}%<%t4%p2%d%e%p2%{16}%<%t10%p2%{8}%-%d%e48;5;%p2%d%;m"
t.ResetFgBg = "\x1b[39;49m"
}
return t, nil
}
func TerminfoNames() []string {
res := make([]string, 0, len(terminfos))
for m := range terminfos {
res = append(res, m)
}
return res
}
-33
View File
@@ -1,33 +0,0 @@
// Generated automatically. DO NOT HAND-EDIT.
package vt100
import "github.com/gdamore/tcell/v2/terminfo"
func init() {
// DEC VT100 (w/advanced video)
terminfo.AddTerminfo(&terminfo.Terminfo{
Name: "vt100",
Aliases: []string{"vt100-am"},
Columns: 80,
Lines: 24,
Clear: "\x1b[H\x1b[J$<50>",
AttrOff: "\x1b[m\x0f$<2>",
Underline: "\x1b[4m$<2>",
Bold: "\x1b[1m$<2>",
Blink: "\x1b[5m$<2>",
Reverse: "\x1b[7m$<2>",
EnterKeypad: "\x1b[?1h\x1b=",
ExitKeypad: "\x1b[?1l\x1b>",
PadChar: "\x00",
AltChars: "``aaffggjjkkllmmnnooppqqrrssttuuvvwwxxyyzz{{||}}~~",
EnterAcs: "\x0e",
ExitAcs: "\x0f",
EnableAcs: "\x1b(B\x1b)0",
EnableAutoMargin: "\x1b[?7h",
DisableAutoMargin: "\x1b[?7l",
SetCursor: "\x1b[%i%p1%d;%p2%dH$<5>",
AutoMargin: true,
})
}
-32
View File
@@ -1,32 +0,0 @@
// Generated automatically. DO NOT HAND-EDIT.
package vt102
import "github.com/gdamore/tcell/v2/terminfo"
func init() {
// DEC VT102
terminfo.AddTerminfo(&terminfo.Terminfo{
Name: "vt102",
Columns: 80,
Lines: 24,
Clear: "\x1b[H\x1b[J$<50>",
AttrOff: "\x1b[m\x0f$<2>",
Underline: "\x1b[4m$<2>",
Bold: "\x1b[1m$<2>",
Blink: "\x1b[5m$<2>",
Reverse: "\x1b[7m$<2>",
EnterKeypad: "\x1b[?1h\x1b=",
ExitKeypad: "\x1b[?1l\x1b>",
PadChar: "\x00",
AltChars: "``aaffggjjkkllmmnnooppqqrrssttuuvvwwxxyyzz{{||}}~~",
EnterAcs: "\x0e",
ExitAcs: "\x0f",
EnableAcs: "\x1b(B\x1b)0",
EnableAutoMargin: "\x1b[?7h",
DisableAutoMargin: "\x1b[?7l",
SetCursor: "\x1b[%i%p1%d;%p2%dH$<5>",
AutoMargin: true,
})
}
-33
View File
@@ -1,33 +0,0 @@
// Generated automatically. DO NOT HAND-EDIT.
package vt220
import "github.com/gdamore/tcell/v2/terminfo"
func init() {
// DEC VT220
terminfo.AddTerminfo(&terminfo.Terminfo{
Name: "vt220",
Aliases: []string{"vt200"},
Columns: 80,
Lines: 24,
Clear: "\x1b[H\x1b[J",
ShowCursor: "\x1b[?25h",
HideCursor: "\x1b[?25l",
AttrOff: "\x1b[m\x1b(B",
Underline: "\x1b[4m",
Bold: "\x1b[1m",
Blink: "\x1b[5m",
Reverse: "\x1b[7m",
PadChar: "\x00",
AltChars: "``aaffggjjkkllmmnnooppqqrrssttuuvvwwxxyyzz{{||}}~~",
EnterAcs: "\x1b(0$<2>",
ExitAcs: "\x1b(B$<4>",
EnableAcs: "\x1b)0",
EnableAutoMargin: "\x1b[?7h",
DisableAutoMargin: "\x1b[?7l",
SetCursor: "\x1b[%i%p1%d;%p2%dH",
AutoMargin: true,
})
}
-34
View File
@@ -1,34 +0,0 @@
// Generated automatically. DO NOT HAND-EDIT.
package vt320
import "github.com/gdamore/tcell/v2/terminfo"
func init() {
// DEC VT320 7 bit terminal
terminfo.AddTerminfo(&terminfo.Terminfo{
Name: "vt320",
Aliases: []string{"vt300"},
Columns: 80,
Lines: 24,
Clear: "\x1b[H\x1b[2J",
ShowCursor: "\x1b[?25h",
HideCursor: "\x1b[?25l",
AttrOff: "\x1b[m\x1b(B",
Underline: "\x1b[4m",
Bold: "\x1b[1m",
Blink: "\x1b[5m",
Reverse: "\x1b[7m",
EnterKeypad: "\x1b[?1h\x1b=",
ExitKeypad: "\x1b[?1l\x1b>",
PadChar: "\x00",
AltChars: "``aaffggjjkkllmmnnooppqqrrssttuuvvwwxxyyzz{{||}}~~",
EnterAcs: "\x1b(0",
ExitAcs: "\x1b(B",
EnableAutoMargin: "\x1b[?7h",
DisableAutoMargin: "\x1b[?7l",
SetCursor: "\x1b[%i%p1%d;%p2%dH",
AutoMargin: true,
})
}
-35
View File
@@ -1,35 +0,0 @@
// Generated automatically. DO NOT HAND-EDIT.
package vt400
import "github.com/gdamore/tcell/v2/terminfo"
func init() {
// DEC VT400 24x80 column autowrap
terminfo.AddTerminfo(&terminfo.Terminfo{
Name: "vt400",
Aliases: []string{"vt400-24", "dec-vt400"},
Columns: 80,
Lines: 24,
Clear: "\x1b[H\x1b[J$<10/>",
ShowCursor: "\x1b[?25h",
HideCursor: "\x1b[?25l",
AttrOff: "\x1b[m\x1b(B",
Underline: "\x1b[4m",
Bold: "\x1b[1m",
Blink: "\x1b[5m",
Reverse: "\x1b[7m",
EnterKeypad: "\x1b[?1h\x1b=",
ExitKeypad: "\x1b[?1l\x1b>",
PadChar: "\x00",
AltChars: "``aaffggjjkkllmmnnooppqqrrssttuuvvwwxxyyzz{{||}}~~",
EnterAcs: "\x1b(0",
ExitAcs: "\x1b(B",
EnableAutoMargin: "\x1b[?7h",
DisableAutoMargin: "\x1b[?7l",
SetCursor: "\x1b[%i%p1%d;%p2%dH",
AutoMargin: true,
InsertChar: "\x1b[@",
})
}
-37
View File
@@ -1,37 +0,0 @@
// This file was originally generated automatically,
// but it is edited to correct for errors in the VT420
// terminfo data. Additionally we have added extended
// information for the extended F-keys.
package vt420
import "github.com/gdamore/tcell/v2/terminfo"
func init() {
// DEC VT420
terminfo.AddTerminfo(&terminfo.Terminfo{
Name: "vt420",
Columns: 80,
Lines: 24,
Clear: "\x1b[H\x1b[2J$<50>",
ShowCursor: "\x1b[?25h",
HideCursor: "\x1b[?25l",
AttrOff: "\x1b[m\x1b(B$<2>",
Underline: "\x1b[4m",
Bold: "\x1b[1m$<2>",
Blink: "\x1b[5m$<2>",
Reverse: "\x1b[7m$<2>",
EnterKeypad: "\x1b=",
ExitKeypad: "\x1b>",
PadChar: "\x00",
AltChars: "``aaffggjjkkllmmnnooppqqrrssttuuvvwwxxyyzz{{||}}~~",
EnterAcs: "\x1b(0$<2>",
ExitAcs: "\x1b(B$<4>",
EnableAcs: "\x1b)0",
EnableAutoMargin: "\x1b[?7h",
DisableAutoMargin: "\x1b[?7l",
SetCursor: "\x1b[%i%p1%d;%p2%dH$<10>",
AutoMargin: true,
})
}
-42
View File
@@ -1,42 +0,0 @@
// Generated automatically. DO NOT HAND-EDIT.
package xfce
import "github.com/gdamore/tcell/v2/terminfo"
func init() {
// Xfce Terminal
terminfo.AddTerminfo(&terminfo.Terminfo{
Name: "xfce",
Columns: 80,
Lines: 24,
Colors: 8,
Clear: "\x1b[H\x1b[2J",
EnterCA: "\x1b7\x1b[?47h",
ExitCA: "\x1b[2J\x1b[?47l\x1b8",
ShowCursor: "\x1b[?25h",
HideCursor: "\x1b[?25l",
AttrOff: "\x1b[0m\x0f",
Underline: "\x1b[4m",
Bold: "\x1b[1m",
Reverse: "\x1b[7m",
EnterKeypad: "\x1b[?1h\x1b=",
ExitKeypad: "\x1b[?1l\x1b>",
SetFg: "\x1b[3%p1%dm",
SetBg: "\x1b[4%p1%dm",
SetFgBg: "\x1b[3%p1%d;4%p2%dm",
ResetFgBg: "\x1b[39;49m",
PadChar: "\x00",
AltChars: "``aaffggiijjkkllmmnnooppqqrrssttuuvvwwxxyyzz{{||}}~~",
EnterAcs: "\x0e",
ExitAcs: "\x0f",
EnableAcs: "\x1b)0",
EnableAutoMargin: "\x1b[?7h",
DisableAutoMargin: "\x1b[?7l",
Mouse: "\x1b[M",
SetCursor: "\x1b[%i%p1%d;%p2%dH",
AutoMargin: true,
XTermLike: true,
})
}
-64
View File
@@ -1,64 +0,0 @@
// Copyright 2021 The TCell Authors
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use file except in compliance with the License.
// You may obtain a copy of the license at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
// This terminal definition is derived from the xterm-256color definition, but
// makes use of the RGB property these terminals have to support direct color.
// The terminfo entry for this uses a new format for the color handling introduced
// by ncurses 6.1 (and used by nobody else), so this override ensures we get
// good handling even in the face of this.
package xterm
import "github.com/gdamore/tcell/v2/terminfo"
func init() {
// derived from xterm-256color, but adds full RGB support
terminfo.AddTerminfo(&terminfo.Terminfo{
Name: "xterm-direct",
Aliases: []string{"xterm-truecolor"},
Columns: 80,
Lines: 24,
Colors: 256,
Clear: "\x1b[H\x1b[2J",
EnterCA: "\x1b[?1049h\x1b[22;0;0t",
ExitCA: "\x1b[?1049l\x1b[23;0;0t",
ShowCursor: "\x1b[?12l\x1b[?25h",
HideCursor: "\x1b[?25l",
AttrOff: "\x1b(B\x1b[m",
Underline: "\x1b[4m",
Bold: "\x1b[1m",
Dim: "\x1b[2m",
Italic: "\x1b[3m",
Blink: "\x1b[5m",
Reverse: "\x1b[7m",
EnterKeypad: "\x1b[?1h\x1b=",
ExitKeypad: "\x1b[?1l\x1b>",
SetFg: "\x1b[%?%p1%{8}%<%t3%p1%d%e%p1%{16}%<%t9%p1%{8}%-%d%e38;5;%p1%d%;m",
SetBg: "\x1b[%?%p1%{8}%<%t4%p1%d%e%p1%{16}%<%t10%p1%{8}%-%d%e48;5;%p1%d%;m",
SetFgBg: "\x1b[%?%p1%{8}%<%t3%p1%d%e%p1%{16}%<%t9%p1%{8}%-%d%e38;5;%p1%d%;;%?%p2%{8}%<%t4%p2%d%e%p2%{16}%<%t10%p2%{8}%-%d%e48;5;%p2%d%;m",
SetFgRGB: "\x1b[38;2;%p1%d;%p2%d;%p3%dm",
SetBgRGB: "\x1b[48;2;%p1%d;%p2%d;%p3%dm",
SetFgBgRGB: "\x1b[38;2;%p1%d;%p2%d;%p3%d;48;2;%p4%d;%p5%d;%p6%dm",
ResetFgBg: "\x1b[39;49m",
AltChars: "``aaffggiijjkkllmmnnooppqqrrssttuuvvwwxxyyzz{{||}}~~",
EnterAcs: "\x1b(0",
ExitAcs: "\x1b(B",
StrikeThrough: "\x1b[9m",
Mouse: "\x1b[M",
SetCursor: "\x1b[%i%p1%d;%p2%dH",
AutoMargin: true,
TrueColor: true,
})
}
-117
View File
@@ -1,117 +0,0 @@
// Generated automatically. DO NOT HAND-EDIT.
package xterm
import "github.com/gdamore/tcell/v2/terminfo"
func init() {
// xterm terminal emulator (X Window System)
terminfo.AddTerminfo(&terminfo.Terminfo{
Name: "xterm",
Aliases: []string{"xterm-debian"},
Columns: 80,
Lines: 24,
Colors: 8,
Clear: "\x1b[H\x1b[2J",
EnterCA: "\x1b[?1049h\x1b[22;0;0t",
ExitCA: "\x1b[?1049l\x1b[23;0;0t",
ShowCursor: "\x1b[?12l\x1b[?25h",
HideCursor: "\x1b[?25l",
AttrOff: "\x1b(B\x1b[m",
Underline: "\x1b[4m",
Bold: "\x1b[1m",
Dim: "\x1b[2m",
Italic: "\x1b[3m",
Blink: "\x1b[5m",
Reverse: "\x1b[7m",
EnterKeypad: "\x1b[?1h\x1b=",
ExitKeypad: "\x1b[?1l\x1b>",
SetFg: "\x1b[3%p1%dm",
SetBg: "\x1b[4%p1%dm",
SetFgBg: "\x1b[3%p1%d;4%p2%dm",
ResetFgBg: "\x1b[39;49m",
AltChars: "``aaffggiijjkkllmmnnooppqqrrssttuuvvwwxxyyzz{{||}}~~",
EnterAcs: "\x1b(0",
ExitAcs: "\x1b(B",
EnableAutoMargin: "\x1b[?7h",
DisableAutoMargin: "\x1b[?7l",
StrikeThrough: "\x1b[9m",
Mouse: "\x1b[<",
SetCursor: "\x1b[%i%p1%d;%p2%dH",
AutoMargin: true,
XTermLike: true,
})
// xterm with 88 colors
terminfo.AddTerminfo(&terminfo.Terminfo{
Name: "xterm-88color",
Columns: 80,
Lines: 24,
Colors: 88,
Clear: "\x1b[H\x1b[2J",
EnterCA: "\x1b[?1049h\x1b[22;0;0t",
ExitCA: "\x1b[?1049l\x1b[23;0;0t",
ShowCursor: "\x1b[?12l\x1b[?25h",
HideCursor: "\x1b[?25l",
AttrOff: "\x1b(B\x1b[m",
Underline: "\x1b[4m",
Bold: "\x1b[1m",
Dim: "\x1b[2m",
Italic: "\x1b[3m",
Blink: "\x1b[5m",
Reverse: "\x1b[7m",
EnterKeypad: "\x1b[?1h\x1b=",
ExitKeypad: "\x1b[?1l\x1b>",
SetFg: "\x1b[%?%p1%{8}%<%t3%p1%d%e%p1%{16}%<%t9%p1%{8}%-%d%e38;5;%p1%d%;m",
SetBg: "\x1b[%?%p1%{8}%<%t4%p1%d%e%p1%{16}%<%t10%p1%{8}%-%d%e48;5;%p1%d%;m",
SetFgBg: "\x1b[%?%p1%{8}%<%t3%p1%d%e%p1%{16}%<%t9%p1%{8}%-%d%e38;5;%p1%d%;;%?%p2%{8}%<%t4%p2%d%e%p2%{16}%<%t10%p2%{8}%-%d%e48;5;%p2%d%;m",
ResetFgBg: "\x1b[39;49m",
AltChars: "``aaffggiijjkkllmmnnooppqqrrssttuuvvwwxxyyzz{{||}}~~",
EnterAcs: "\x1b(0",
ExitAcs: "\x1b(B",
EnableAutoMargin: "\x1b[?7h",
DisableAutoMargin: "\x1b[?7l",
StrikeThrough: "\x1b[9m",
Mouse: "\x1b[<",
SetCursor: "\x1b[%i%p1%d;%p2%dH",
AutoMargin: true,
XTermLike: true,
})
// xterm with 256 colors
terminfo.AddTerminfo(&terminfo.Terminfo{
Name: "xterm-256color",
Columns: 80,
Lines: 24,
Colors: 256,
Clear: "\x1b[H\x1b[2J",
EnterCA: "\x1b[?1049h\x1b[22;0;0t",
ExitCA: "\x1b[?1049l\x1b[23;0;0t",
ShowCursor: "\x1b[?12l\x1b[?25h",
HideCursor: "\x1b[?25l",
AttrOff: "\x1b(B\x1b[m",
Underline: "\x1b[4m",
Bold: "\x1b[1m",
Dim: "\x1b[2m",
Italic: "\x1b[3m",
Blink: "\x1b[5m",
Reverse: "\x1b[7m",
EnterKeypad: "\x1b[?1h\x1b=",
ExitKeypad: "\x1b[?1l\x1b>",
SetFg: "\x1b[%?%p1%{8}%<%t3%p1%d%e%p1%{16}%<%t9%p1%{8}%-%d%e38;5;%p1%d%;m",
SetBg: "\x1b[%?%p1%{8}%<%t4%p1%d%e%p1%{16}%<%t10%p1%{8}%-%d%e48;5;%p1%d%;m",
SetFgBg: "\x1b[%?%p1%{8}%<%t3%p1%d%e%p1%{16}%<%t9%p1%{8}%-%d%e38;5;%p1%d%;;%?%p2%{8}%<%t4%p2%d%e%p2%{16}%<%t10%p2%{8}%-%d%e48;5;%p2%d%;m",
ResetFgBg: "\x1b[39;49m",
AltChars: "``aaffggiijjkkllmmnnooppqqrrssttuuvvwwxxyyzz{{||}}~~",
EnterAcs: "\x1b(0",
ExitAcs: "\x1b(B",
EnableAutoMargin: "\x1b[?7h",
DisableAutoMargin: "\x1b[?7l",
StrikeThrough: "\x1b[9m",
Mouse: "\x1b[<",
SetCursor: "\x1b[%i%p1%d;%p2%dH",
AutoMargin: true,
XTermLike: true,
})
}
-47
View File
@@ -1,47 +0,0 @@
// Generated automatically. DO NOT HAND-EDIT.
package xterm_ghostty
import "github.com/gdamore/tcell/v2/terminfo"
func init() {
// Ghostty
terminfo.AddTerminfo(&terminfo.Terminfo{
Name: "xterm-ghostty",
Aliases: []string{"ghostty"},
Columns: 80,
Lines: 24,
Colors: 256,
Clear: "\x1b[H\x1b[2J",
EnterCA: "\x1b[?1049h",
ExitCA: "\x1b[?1049l",
ShowCursor: "\x1b[?12l\x1b[?25h",
HideCursor: "\x1b[?25l",
AttrOff: "\x1b(B\x1b[m",
Underline: "\x1b[4m",
Bold: "\x1b[1m",
Dim: "\x1b[2m",
Italic: "\x1b[3m",
Blink: "\x1b[5m",
Reverse: "\x1b[7m",
EnterKeypad: "\x1b[?1h\x1b=",
ExitKeypad: "\x1b[?1l\x1b>",
SetFg: "\x1b[%?%p1%{8}%<%t3%p1%d%e%p1%{16}%<%t9%p1%{8}%-%d%e38;5;%p1%d%;m",
SetBg: "\x1b[%?%p1%{8}%<%t4%p1%d%e%p1%{16}%<%t10%p1%{8}%-%d%e48;5;%p1%d%;m",
SetFgBg: "\x1b[%?%p1%{8}%<%t3%p1%d%e%p1%{16}%<%t9%p1%{8}%-%d%e38;5;%p1%d%;;%?%p2%{8}%<%t4%p2%d%e%p2%{16}%<%t10%p2%{8}%-%d%e48;5;%p2%d%;m",
ResetFgBg: "\x1b[39;49m",
AltChars: "++,,--..00``aaffgghhiijjkkllmmnnooppqqrrssttuuvvwwxxyyzz{{||}}~~",
EnterAcs: "\x1b(0",
ExitAcs: "\x1b(B",
EnableAutoMargin: "\x1b[?7h",
DisableAutoMargin: "\x1b[?7l",
StrikeThrough: "\x1b[9m",
Mouse: "\x1b[<",
SetCursor: "\x1b[%i%p1%d;%p2%dH",
TrueColor: true,
AutoMargin: true,
InsertChar: "\x1b[@",
XTermLike: true,
})
}
-44
View File
@@ -1,44 +0,0 @@
// Generated automatically. DO NOT HAND-EDIT.
package xterm_kitty
import "github.com/gdamore/tcell/v2/terminfo"
func init() {
// KovIdTTY
terminfo.AddTerminfo(&terminfo.Terminfo{
Name: "xterm-kitty",
Columns: 80,
Lines: 24,
Colors: 256,
Clear: "\x1b[H\x1b[2J",
EnterCA: "\x1b[?1049h",
ExitCA: "\x1b[?1049l",
ShowCursor: "\x1b[?12h\x1b[?25h",
HideCursor: "\x1b[?25l",
AttrOff: "\x1b(B\x1b[m",
Underline: "\x1b[4m",
Bold: "\x1b[1m",
Dim: "\x1b[2m",
Italic: "\x1b[3m",
Reverse: "\x1b[7m",
EnterKeypad: "\x1b[?1h",
ExitKeypad: "\x1b[?1l",
SetFg: "\x1b[%?%p1%{8}%<%t3%p1%d%e%p1%{16}%<%t9%p1%{8}%-%d%e38;5;%p1%d%;m",
SetBg: "\x1b[%?%p1%{8}%<%t4%p1%d%e%p1%{16}%<%t10%p1%{8}%-%d%e48;5;%p1%d%;m",
SetFgBg: "\x1b[%?%p1%{8}%<%t3%p1%d%e%p1%{16}%<%t9%p1%{8}%-%d%e38;5;%p1%d%;;%?%p2%{8}%<%t4%p2%d%e%p2%{16}%<%t10%p2%{8}%-%d%e48;5;%p2%d%;m",
ResetFgBg: "\x1b[39;49m",
AltChars: "++,,--..00``aaffgghhiijjkkllmmnnooppqqrrssttuuvvwwxxyyzz{{||}}~~",
EnterAcs: "\x1b(0",
ExitAcs: "\x1b(B",
EnableAutoMargin: "\x1b[?7h",
DisableAutoMargin: "\x1b[?7l",
StrikeThrough: "\x1b[9m",
Mouse: "\x1b[M",
SetCursor: "\x1b[%i%p1%d;%p2%dH",
TrueColor: true,
AutoMargin: true,
XTermLike: true,
})
}
-43
View File
@@ -1,43 +0,0 @@
//go:build !tcell_minimal && !nacl && !js && !zos && !plan9 && !windows && !android
// +build !tcell_minimal,!nacl,!js,!zos,!plan9,!windows,!android
// Copyright 2019 The TCell Authors
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use file except in compliance with the License.
// You may obtain a copy of the license at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
package tcell
import (
// This imports a dynamic version of the terminal database, which
// is built using infocmp. This relies on a working installation
// of infocmp (typically supplied with ncurses). We only do this
// for systems likely to have that -- i.e. UNIX based hosts. We
// also don't support Android here, because you really don't want
// to run external programs there. Generally the android terminals
// will be automatically included anyway.
"github.com/gdamore/tcell/v2/terminfo"
"github.com/gdamore/tcell/v2/terminfo/dynamic"
"fmt"
)
func loadDynamicTerminfo(term string) (*terminfo.Terminfo, error) {
if term == "" {
return nil, fmt.Errorf("%w: term not set", ErrTermNotFound)
}
ti, _, e := dynamic.LoadTerminfo(term)
if e != nil {
return nil, e
}
return ti, nil
}
-28
View File
@@ -1,28 +0,0 @@
//go:build tcell_minimal || nacl || zos || plan9 || windows || android || js
// +build tcell_minimal nacl zos plan9 windows android js
// Copyright 2019 The TCell Authors
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use file except in compliance with the License.
// You may obtain a copy of the license at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
package tcell
import (
"errors"
"github.com/gdamore/tcell/v2/terminfo"
)
func loadDynamicTerminfo(_ string) (*terminfo.Terminfo, error) {
return nil, errors.New("terminal type unsupported")
}
-1317
View File
File diff suppressed because it is too large Load Diff
+2
View File
@@ -0,0 +1,2 @@
ignore:
- "vt/tests"
+1
View File
@@ -0,0 +1 @@
Use the `gopls` MCP server by default when working on Go code in this repository, unless I explicitly ask you not to.
@@ -79,4 +79,4 @@ _Tcell_ provides the long requested capability to discriminate paste event by us
bracketed-paste capability present in some terminals. This is automatically available on
terminals that support XTerm style mouse handling, but applications must opt-in to this
by using the new `EnablePaste()` function. A new `EventPaste` type of event will be
delivered when starting and finishing a paste operation.
delivered when starting and finishing a paste operation.
+106
View File
@@ -0,0 +1,106 @@
## Breaking Changes in _Tcell_ v3
There are a number of changes in _Tcell_ v3, mostly aimed at simplifying things for
applications, but some also intended to reduce the burden for support. Every application
will need at least some changes, but it is expected that those changes will be small,
possibly even mechanical in nature.
### Cell and Contents APIs
In order to improve support for multi-rune grapheme clusters, and to provide an
experience that reduces friction when using it, some APIs have been removed, and
newer APIs exist in their place.
- `SetCell` and `SetContents` are removed. Use `Put` instead.
- `GetContents` is removed. Use `Get` instead.
### Events (PostEvent, PollEvent, ChannelEvents)
The event channel is now directly exposed via `EventQ`, and events may be read from or written
directly to the channel in the standard Go fashion. This should help applications that want to
integrate into `select` statements (e.g. for timed key presses).
The `ChannelEvents`, `PollEvent`, `PostEvent`, and `PostEventWait` functions are removed, as
applications can now just access the event channel directly.
### Key Event Changes
`EventKey` now carries a string for `KeyRune` instead of a single rune.
As a result the old `Rune` method for `EventKey` is replaced by `Str`.
The main difference for most users will be that `Str` returns a string, and most
of the time that string will consist of only a single rune. However, it is possible
now to inject synthetic key strokes consisting of multi-rune grapheme clusters.
Additionally the following special keys are removed, as they are delivered
instead as `KeyRune` with the relevant rune, and the `ModCtrl` modifier:
`KeyCtrlSpace`, `KeyCtrlLeftSq`, `KeyCtrlRightSq`, `KeyCtrlBackslash`, and
`KeyCtrlUnderscore`.
Note that `KeyRune` will never have a `ModShift` applied unless it is applied
also with other modifiers.
The `KeyCtrlA` through `KeyCtrlZ` keys are delivered, but will also carry
the associated lower case rune (e.g. "a", "b", etc.) and `ModCtrl`.
The `KeyBackspace2` key is no longer delivered, but is converted to
`KeyBackspace`. (This resolves some inconsistency around e.g. CTRL-H vs DELETE.)
### Termbox Compatibility Removed
The `termbox` compatibility package is removed. Few applications were using it,
and the compatibility was imperfect. Also the package had limited support for many
newer features. Further, _Termbox_ itself is no longer being maintained.
Applications that still need this should keep using _Tcell_ v2.
### Terminfo Removed
The Terminfo subsystem has been removed entirely.
Essentially the old terminfo based design has long proved to be inferior for modern terminal
applications, and has not kept up with newer terminal features such as 24-bit color,
different mouse reporting modes, bracketed paste, advanced text styling, and so forth.
As part of this, we're removing the parsed terminfo logic entirely. It turns out that pretty much
all of the terminal logic can be consolidated to just a few classes of terminals with substantial
overlap.
A consequence of this is that support for some legacy terminals that are either functionally
extinct (such as _hpterm_) or unlikely to be found outside of a museum (such as VT52, Wyse50, or
anything produced more than 40 years ago.)
Note that VT100 and later will work in emulation, and VT220 and later physical terminals should still work.
VT100 physical terminals may not work, as the padding delays that existed for them are removed.
Those delays hurt emulations that do not need them, and existed only to accommodate limitations found on the
physical hardware from the 1970s.
Note that we still examine `$TERM` when appropriate, but if the value is not one we recognize,
then we will assume something reasonably capable and compatible at some level with _xterm_ or
at least ECMA-48.
### Color, Attributes, Etc. Bit Sizes
The `Color` type is now only 32-bits, which should save some memory on large terminal windows.
The `AttrMask` type is now only 16-bits, and the `UnderlineStyle` is now 8 bits.
All these lead to further savings in the memory per cell.
### Underline
`AttrUnderline` is gone. It was not sufficient to describe styled and colored underlines.
### Removed Capability Queries
Deprecated APIs `HasKey`, `HasMouse`, and `CanDisplay` are removed.
These functions weren't reliable and served no useful purpose.
### Windows Console API
`NewConsoleScreen` is removed as is support for Windows console mode.
Instead this uses the more modern Windows VT modes.
As a consequence, this means that _Tcell_ on Windows requires at least Winows 10 build 1703 (the Creators Update).
If you are using a version of Windows 10 older than that, you should really upgrade for _many_ reasons, not just
because _Tcell_ doesn't support it anymore.
### InputProcessor is no longer Public
This structure, and the associated `NewInputProcessor` function, were made public incorrectly.
They are not part of our public API going forward, and are now private symbols.
+16
View File
@@ -0,0 +1,16 @@
# _Tcell_ on Plan 9
> [!NOTE]
> Plan 9 is supported on a best-effort basis, as the main _Tcell_ development team does not have a Plan 9 environment.
The Plan 9 backend opens `/dev/cons` for I/O, enables raw mode by writing `rawon`/`rawoff` to `/dev/consctl`.
It watches `/dev/wctl` for resize notifications.
The default mode for `vt((1)` is VT100, which will only provide basic monochrome text, and few additional features.
In this case, it is expected that `TERM=vt100` is set.
It may be possible to emulate more modern terminals using `-2` (VT220), `-a` (ANSI), or `-x` (XTerm) flags to `vt`.
While this has not been tested, the use of `-x` to get xterm like features, combinerd with a `TERM=xterm` may yield superior results,
including possibly color and mouse support.
Note that if _Tcell_ does not find a suitable value for `TERM` in the environment, it will assume XTerm like functionality.
+52
View File
@@ -0,0 +1,52 @@
# _Tcell_ on Windows
Windows is supported starting from either Windows 10 version 1703 (the Creators Update, released in April 2017)
or Windows Server 2016.
> [NOTE!]
> Windows 8 and earlier are _not_ supported!
On Windows, _Tcell_ uses the modern VT API for console applications, including the _win32-input-mode_ to give
advanced key reporting. However, in order to receive resize notifications, the `ReadConsoleInput` API is
used even while in VT input mode.
On Windows we use 24-bit color by default.
## Terminal Emulators
Our preferred terminal emulator on Windows 11 is actually the modern _Windows Terminal_, which can also be
used as a portable application. This is the main test target we use, and the one we recommend if problems are
encountered using third party applications.
We have tested _Alacritty_ and found it to work reasonably well, including support for modern
key reporting, mouse events, and bracketed paste, although its support support for advanced Unicode features
is limited.
We have also tested _WezTerm_, _Termius_, _Putty_, and _MobaXterm_.
These appear to work reasonably for remote sessions, but do not support the newer keyboard protocols, and
may not work well for local sessions at all.
_Termius_ in particular gave a poor experience when used for local sessions, and we would not recommend it.
While historically popular, we cannot recommend _ConEmu_ or _mintty_. These applications have not kept pace
with modern APIs nor modern terminal standards, and we found their experience suboptimal.
## SSH From Windows
Unfortunately, we have found that some features (particularly the rich keyboard support) are degraded when
using SSH from a Windows Terminal (and probably other terminals as well!) to a remote host. It appears that
the full _win32-input-mode_ is captured and refactored into legacy VT style encodings, which can result in
old-school issues such as the inability to to distinguish CTRL-I from TAB. This appears to be something
done by the Windows SSH or terminal application and is nothing we can fix.
It's likely that WSL will suffer these limitations as well.
Further, you'll probably need to request 24-bit color explicitly by setting `COLORTERM=truecolor` in your
environment, as this is not typically done for Windows terminals as it is for Posix terminals.
Note that _Alacritty_ has support for the full key bindings both locally and remotely. Others may as well.
_WezTerm_, while not great locally, performed quite well as an SSH client, with complete support for all
various modern keyboard featuers, bracketed paste, and good Unicode support.
Another option might be to run an X11 server (such as _MobaXterm_) and then remotely display a Linux terminal
application such as _Ghostty_ or _Kitty_.
@@ -5,19 +5,23 @@
_Tcell_ is a _Go_ package that provides a cell based view for text terminals, like _XTerm_.
It was inspired by _termbox_, but includes many additional improvements.
[![Stand With Ukraine](https://raw.githubusercontent.com/vshymanskyy/StandWithUkraine/main/badges/StandWithUkraine.svg)](https://stand-with-ukraine.pp.ua)
[![Stand With Ukraine](logos/ukraine.svg)](https://stand-with-ukraine.pp.ua)
[![Docs](https://img.shields.io/badge/godoc-reference-blue.svg?label=&logo=go)](https://pkg.go.dev/github.com/gdamore/tcell/v3)
[![Linux](https://img.shields.io/github/actions/workflow/status/gdamore/tcell/linux.yml?branch=main&logoColor=grey&logo=linux&label=)](https://github.com/gdamore/tcell/actions/workflows/linux.yml)
[![Windows](https://img.shields.io/github/actions/workflow/status/gdamore/tcell/windows.yml?branch=main&logoColor=grey&label=Windows)](https://github.com/gdamore/tcell/actions/workflows/windows.yml)
[![macOS](https://img.shields.io/github/actions/workflow/status/gdamore/tcell/macos.yml?branch=main&logoColor=grey&logo=apple&label=)](https://github.com/gdamore/tcell/actions/workflows/macos.yml)
[![Windows](https://custom-icon-badges.demolab.com/github/actions/workflow/status/gdamore/tcell/windows.yml?branch=main&logoColor=grey&logo=windows10&label=)](https://github.com/gdamore/tcell/actions/workflows/windows.yml)
[![Web Assembly](https://img.shields.io/github/actions/workflow/status/gdamore/tcell/webasm.yml?branch=main&logoColor=grey&logo=webassembly&label=)](https://github.com/gdamore/tcell/actions/workflows/webasm.yml)
[![Apache License](https://img.shields.io/github/license/gdamore/tcell.svg?logoColor=silver&logo=opensourceinitiative&color=blue&label=)](https://github.com/gdamore/tcell/blob/master/LICENSE)
[![Docs](https://img.shields.io/badge/godoc-reference-blue.svg?label=&logo=go)](https://pkg.go.dev/github.com/gdamore/tcell/v2)
[![Discord](https://img.shields.io/discord/639503822733180969?label=&logo=discord)](https://discord.gg/urTTxDN)
[![Coverage](https://img.shields.io/codecov/c/github/gdamore/tcell?logoColor=grey&logo=codecov&label=)](https://codecov.io/gh/gdamore/tcell)
[![Go Report Card](https://goreportcard.com/badge/github.com/gdamore/tcell/v2)](https://goreportcard.com/report/github.com/gdamore/tcell/v2)
[![Go Report Card](https://img.shields.io/badge/go%20report-A+-brightgreen.svg?style=flat&label=&logo=go&logoColor=grey)](https://goreportcard.com/report/github.com/gdamore/tcell/v3)
[![Discord](https://img.shields.io/discord/639503822733180969?label=&logo=discord)](https://discord.gg/urTTxDN)
[![Latest Release](https://img.shields.io/github/v/release/gdamore/tcell.svg?logo=github&label=)](https://github.com/gdamore/tcell/releases)
NOTE: This is version 2 of _Tcell_. There are breaking changes relative to version 1.
Version 1.x remains available using the import `github.com/gdamore/tcell`.
> [!NOTE]
> This is version 3 of _Tcell_.
> There are breaking changes relative to versions 1 and 2.
> [Version 2](https://github.com/gdamore/tcell/tree/v2) remains available using the import `github.com/gdamore/tcell/v2`.
> [Version 1](https://github.com/gdamore/tcell/tree/v1) Version 1.x remains available using the import `github.com/gdamore/tcell`, but is
> unmaintained and should not be used.
## Tutorial
@@ -26,14 +30,14 @@ A brief, and still somewhat rough, [tutorial](TUTORIAL.md) is available.
## Examples
A number of example are posted up on our [Gallery](https://github.com/gdamore/tcell/wikis/Gallery/).
That's a wiki, and please do submit updates if you have something you want to showcase.
Let us know if you want to add your masterpiece to the list!
There are also demonstration programs in the `./demos` directory, as well as some in `./_demos`.
## More Portable
_Tcell_ is portable to a wide variety of systems, and is pure Go, without
any need for CGO.
_Tcell_ is believed to work with mainstream systems officially supported by golang.
_Tcell_ is portable to a wide variety of systems, and is pure Go, without any need for CGO.
_Tcell_ works with mainstream systems officially supported by golang.
Following the Go support policy, _Tcell_ officially only supports the current ("stable") version of go,
and the version immediately prior ("oldstable"). This policy is necessary to make sure that we can
@@ -43,9 +47,7 @@ update dependencies to pick up security fixes and new features, and it allows us
## Rich Unicode & non-Unicode support
_Tcell_ includes enhanced support for Unicode, including wide characters and
combining characters, provided your terminal can support them.
Note that
Windows terminals generally don't support the full Unicode repertoire.
grapheme clusters, provided your terminal can support them.
It will also convert to and from Unicode locales, so that the program
can work with UTF-8 internally, and get reasonable output in other locales.
@@ -53,21 +55,17 @@ _Tcell_ tries hard to convert to native characters on both input and output.
On output _Tcell_ even makes use of the alternate character set to facilitate
drawing certain characters.
## More Function Keys
## Better Keyboard Support
_Tcell_ also has richer support for a larger number of special keys that some
terminals can send.
terminals can send. On modern terminal emulators we can also support a rich set of
modifiers, and can discriminate between e.g. CTRL-I and TAB. (This does require
the terminal emulator to support one of the modern keyboard protocols.)
## Better Mouse Support
_Tcell_ supports enhanced mouse tracking mode, so your application can receive
regular mouse motion events, and wheel events, if your terminal supports it.
## _Termbox_ Compatibility
A compatibility layer for _termbox_ is provided in the `compat` directory.
To use it, try importing `github.com/gdamore/tcell/termbox` instead.
Most _termbox-go_ programs will probably work without further modification.
regular mouse motion events, click-drag, and wheel events, if your terminal supports it.
## Working With Unicode
@@ -82,8 +80,8 @@ If you're lazy, and want them all anyway, see the `encoding` sub-directory.
## Wide & Combining Characters
The `Put()` API takes a string, which should be legal UTF-8, and displays
the first grapheme (which may composed of multiple runes). It returns the
actual width displayed, which can be used to advance the column positiion
the first grapheme cluster (which may composed of multiple runes).
It returns the actual width displayed, which can be used to advance the column positiion
for the next display grapheme. Alternatively, `PutStr()` or `PutStrStyled()`
can be used to display a single line of text (which will be clipped at the
edge of the screen).
@@ -93,12 +91,8 @@ wide character (offset by one instead of by two), then the results are undefined
## Colors
_Tcell_ assumes the ANSI/XTerm color model, including the 256 color map that
XTerm uses when it supports 256 colors. The terminfo guidance will be
honored, with respect to the number of colors supported. Also, only
terminals which expose ANSI style `setaf` and `setab` will support color;
if you have a color terminal that only has `setf` and `setb`, please submit
a ticket.
_Tcell_ assumes the ANSI/XTerm color palette for up to 256 colors, although terminals
such as legacy ANSI terminals may only support 8 colors.
## 24-bit Color
@@ -106,18 +100,14 @@ _Tcell_ _supports 24-bit color!_ (That is, if your terminal can support it.)
There are a few ways you can enable (or disable) 24-bit color.
- For many terminals, we can detect it automatically if your terminal
includes the `RGB` or `Tc` capabilities (or rather it did when the database
was updated.)
- You can force this one by setting the `COLORTERM` environment variable to
`24-bit`, `truecolor` or `24bit`. This is the same method used
by most other terminal applications that support 24-bit color.
`truecolor`. This environment variable is frequently set by terminal emulators
that support 24-bit color.
- On Windows, 24-bit color support is assumed. (All modern Windows terminal emulators support it.)
- If you set your `TERM` environment variable to a value with the suffix `-truecolor`
then 24-bit color compatible with XTerm and ECMA-48 will be assumed.
(This feature is deprecated.
It is recommended to use one of other methods listed above.)
or `-direct`, then 24-bit color compatible with XTerm and ECMA-48 will be assumed.
- You can disable 24-bit color by setting `TCELL_TRUECOLOR=disable` in your
environment.
@@ -136,42 +126,44 @@ avoiding repeated sequences or drawing the same cell on refresh updates.
## Mouse Support
Mouse tracking, buttons, and even wheel mice works fine on most terminal
Mouse tracking, buttons, and even wheel mice are supported on most terminal
emulators, as well as Windows.
## Bracketed Paste
Terminals that appear to support the XTerm mouse model also can support
bracketed paste, for applications that opt-in. See `EnablePaste()` for details.
Terminals that support support it, can use bracketed paste.
See `EnablePaste()` for details.
## Testability
## Breaking Changes in v3
There is a `SimulationScreen`, that can be used to simulate a real screen
for automated testing. The supplied tests do this. The simulation contains
event delivery, screen resizing support, and capabilities to inject events
and examine "`physical`" screen contents.
There are a number of changes in _Tcell_ version 3, which break compatibility with
version 2 and version 1.
Your application will almost certainly need some minor updates to work with version 3.
Please see the [CHANGESv3](CHANGESv3.md) document for a list.
## Platforms
### POSIX (Linux, FreeBSD, macOS, Solaris, etc.)
Everything works using pure Go on mainstream platforms. Some more esoteric
platforms (e.g., AIX) may need to be added. Pull requests are welcome!
Everything works using pure Go on mainstream platforms.
Esoteric platforms (e.g. zOS or AIX) are supported on a best-effort
only basis. Pull requests to fix any issues found are welcome!
### Windows
Windows console mode applications are supported.
Modern console applications like ConEmu and the Windows Terminal,
support all the good features (resize, mouse tracking, etc.)
Modern Windows is supported. Please see the [README-windows](README-windows.md)
document for much more detailed information.
### WASM
WASM is supported, but needs additional setup detailed in [README-wasm](README-wasm.md).
### Plan9 and its variants
### Plan 9
Plan 9 is supported on a limited basis. The Plan 9 backend opens `/dev/cons` for I/O, enables raw mode by writing `rawon`/`rawoff` to `/dev/consctl`, watches `/dev/wctl` for resize notifications, and then constructs a **terminfo-backed** `Screen` (so `NewScreen` works as on other platforms). Typical usage is inside `vt(1)` with `TERM=vt100`. Expect **monochrome text** and **no mouse reporting** under stock `vt(1)` (it generally does not emit ANSI color or xterm mouse sequences). If a Plan 9 terminal supplies ANSI color escape sequences and xterm-style mouse reporting, color can be picked up via **terminfo** and mouse support could be added by wiring those sequences into the Plan 9 TTY path; contributions that improve terminal detection and broaden feature support are welcome.
Plan 9 is supported on a best-effort basis. Please see the [README-plan9](README-plan9.md)
document for more information.
### Commercial Support
@@ -3,7 +3,7 @@
It's somewhat unlikely that tcell is in a security sensitive path,
but we do take security seriously.
## Vulnerabilityu Response
## Vulnerability Response
If you report a vulnerability, we will respond within 2 business days.
@@ -38,8 +38,8 @@ When a non-rune key is pressed, it is available as the `Key` of the event.
```go
switch ev := ev.(type) {
case *tcell.EventKey:
mod, key, ch := ev.Mod(), ev.Key(), ev.Rune()
logMessage(fmt.Sprintf("EventKey Modifiers: %d Key: %d Rune: %d", mod, key, ch))
mod, key, ch := ev.Mod(), ev.Key(), ev.Str()
logMessage(fmt.Sprintf("EventKey Modifiers: %d Key: %d Str: %q", mod, key, ch))
}
```
@@ -100,7 +100,7 @@ if err := s.Init(); err != nil {
}
// Set default text style
defStyle := tcell.StyleDefault.Background(tcell.ColorReset).Foreground(tcell.ColorReset)
defStyle := tcell.StyleDefault.Background(color.Reset).Foreground(color.Reset)
s.SetStyle(defStyle)
// Clear screen
@@ -110,9 +110,9 @@ s.Clear()
Text may be drawn on the screen using `Put`, `PutStr`, or `PutStrStyled`.
```go
s.Put(0, 0, 'H', defStyle)
s.Put(1, 0, 'i', defStyle)
s.Put(2, 0, '!', defStyle)
s.Put(0, 0, "H", defStyle)
s.Put(1, 0, "i", defStyle)
s.Put(2, 0, "!", defStyle)
```
which is equivalent to
@@ -157,8 +157,8 @@ for {
// Update screen
s.Show()
// Poll event
ev := s.PollEvent()
// Poll event (can be used in select statement as well)
ev := <-s.EventQ()
// Process event
switch ev := ev.(type) {
@@ -183,7 +183,8 @@ import (
"fmt"
"log"
"github.com/gdamore/tcell/v2"
"github.com/gdamore/tcell/v3"
"github.com/gdamore/tcell/v3/color"
)
func drawText(s tcell.Screen, x1, y1, x2, y2 int, style tcell.Style, text string) {
@@ -244,8 +245,8 @@ func drawBox(s tcell.Screen, x1, y1, x2, y2 int, style tcell.Style, text string)
}
func main() {
defStyle := tcell.StyleDefault.Background(tcell.ColorReset).Foreground(tcell.ColorReset)
boxStyle := tcell.StyleDefault.Foreground(tcell.ColorWhite).Background(tcell.ColorPurple)
defStyle := tcell.StyleDefault.Background(color.Reset).Foreground(color.Reset)
boxStyle := tcell.StyleDefault.Foreground(color.White).Background(color.Purple)
// Initialize screen
s, err := tcell.NewScreen()
@@ -280,10 +281,11 @@ func main() {
// xmax, ymax := s.Size()
// Here's an example of how to inject a keystroke where it will
// be picked up by the next PollEvent call. Note that the
// queue is LIFO, it has a limited length, and PostEvent() can
// return an error.
// s.PostEvent(tcell.NewEventKey(tcell.KeyRune, rune('a'), 0))
// be picked up by a future read of the event queue. Note that
// care should be used to avoid blocking writes to the queue if
// this is done from the same thread that is responsible for reading
// the queue, or else a single-party deadlock might occur.
// s.EventQ() <- tcell.NewEventKey(tcell.KeyRune, rune('a'), 0)
// Event loop
ox, oy := -1, -1
@@ -291,8 +293,8 @@ func main() {
// Update screen
s.Show()
// Poll event
ev := s.PollEvent()
// Poll event (this can be in a select statement as well)
ev := <-s.EventQ()
// Process event
switch ev := ev.(type) {
@@ -303,7 +305,7 @@ func main() {
return
} else if ev.Key() == tcell.KeyCtrlL {
s.Sync()
} else if ev.Rune() == 'C' || ev.Rune() == 'c' {
} else if ev.Str() == "C" || ev.Str() == "c" {
s.Clear()
}
case *tcell.EventMouse:
@@ -1,8 +1,8 @@
// Copyright 2024 The TCell Authors
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use file except in compliance with the License.
// You may obtain a copy of the license at
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
@@ -16,7 +16,10 @@ package tcell
// AttrMask represents a mask of text attributes, apart from color.
// Note that support for attributes may vary widely across terminals.
type AttrMask uint
// Deprecated: This should not be used directly by applications.
// Instead use the accessor functions on the style. (In particular the
// details of the encoding are subject to change.)
type AttrMask uint16
// Attributes are not colors, but affect the display of text. They can
// be combined, in some cases, but not others. (E.g. you can have Dim Italic,
@@ -25,10 +28,9 @@ const (
AttrBold AttrMask = 1 << iota
AttrBlink
AttrReverse
AttrUnderline // Deprecated: Use UnderlineStyle
AttrDim
AttrItalic
AttrStrikeThrough
AttrInvalid AttrMask = 1 << 31 // Mark the style or attributes invalid
AttrInvalid AttrMask = 1 << 15 // Mark the style or attributes invalid
AttrNone AttrMask = 0 // Just normal text.
)
@@ -1,8 +1,8 @@
// Copyright 2025 The TCell Authors
// Copyright 2026 The TCell Authors
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use file except in compliance with the License.
// You may obtain a copy of the license at
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
@@ -14,10 +14,6 @@
package tcell
import (
"github.com/rivo/uniseg"
)
type cell struct {
currStr string
lastStr string
@@ -40,7 +36,7 @@ func (c *cell) setDirty(dirty bool) {
}
// CellBuffer represents a two-dimensional array of character cells.
// This is primarily intended for use by Screen implementors; it
// This is primarily intended for use by Screen implementers; it
// contains much of the common code they need. To create one, just
// declare a variable of its type; no explicit initialization is necessary.
//
@@ -51,41 +47,29 @@ type CellBuffer struct {
cells []cell
}
// SetContent sets the contents (primary rune, combining runes,
// and style) for a cell at a given location. If the background or
// foreground of the style is set to ColorNone, then the respective
// color is left un changed.
//
// Deprecated: Use Put instead, which this is implemented in terms of.
func (cb *CellBuffer) SetContent(x int, y int, mainc rune, combc []rune, style Style) {
cb.Put(x, y, string(append([]rune{mainc}, combc...)), style)
}
// Put a single styled grapheme using the given string and style
// at the same location. Note that only the first grapheme in the string
// will bre displayed, using only the 1 or 2 (depending on width) cells
// will be displayed, using only the 1 or 2 (depending on width) cells
// located at x, y. It returns the rest of the string, and the width used.
func (cb *CellBuffer) Put(x int, y int, str string, style Style) (string, int) {
var width int = 0
if x >= 0 && y >= 0 && x < cb.w && y < cb.h {
var cl string
c := &cb.cells[(y*cb.w)+x]
state := -1
for width == 0 && str != "" {
var g string
g, str, width, state = uniseg.FirstGraphemeClusterInString(str, state)
cl += g
if g == "" {
break
}
g := textWidthOptions.StringGraphemes(str)
for width == 0 && g.Next() {
cluster := g.Value()
cl += cluster
width = g.Width()
str = str[len(cluster):]
}
// Wide characters: we want to mark the "wide" cells
// dirty as well as the base cell, to make sure we consider
// both cells as dirty together. We only need to do this
// if we're changing content
if width > 0 && cl != c.currStr {
// Prevent unnecessary boundchecks for first cell, since we already
if width > 1 && cl != c.currStr {
// Prevent unnecessary bounds checks for first cell, since we already
// received that one.
c.setDirty(true)
for i := 1; i < width; i++ {
@@ -126,28 +110,6 @@ func (cb *CellBuffer) Get(x, y int) (string, Style, int) {
return str, style, width
}
// GetContent returns the contents of a character cell, including the
// primary rune, any combining character runes (which will usually be
// nil), the style, and the display width in cells. (The width can be
// either 1, normally, or 2 for East Asian full-width characters.)
//
// Deprecated: Use Get, which this implemented in terms of.
func (cb *CellBuffer) GetContent(x, y int) (rune, []rune, Style, int) {
var style Style
var width int
var mainc rune
var combc []rune
str, style, width := cb.Get(x, y)
for i, r := range str {
if i == 0 {
mainc = r
} else {
combc = append(combc, r)
}
}
return mainc, combc, style, width
}
// Size returns the (width, height) in cells of the buffer.
func (cb *CellBuffer) Size() (int, int) {
return cb.w, cb.h
@@ -4,8 +4,8 @@
// Copyright 2025 The TCell Authors
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use file except in compliance with the License.
// You may obtain a copy of the license at
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
@@ -1,11 +1,11 @@
//go:build !windows && !nacl && !plan9
// +build !windows,!nacl,!plan9
//go:build unix
// +build unix
// Copyright 2016 The TCell Authors
// Copyright 2026 The TCell Authors
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use file except in compliance with the License.
// You may obtain a copy of the license at
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
@@ -45,6 +45,5 @@ func getCharset() string {
// without a character set, which we assume implies UTF-8.
return "UTF-8"
}
// XXX: add support for aliases
return locale
}
@@ -4,8 +4,8 @@
// Copyright 2015 The TCell Authors
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use file except in compliance with the License.
// You may obtain a copy of the license at
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
+515
View File
@@ -0,0 +1,515 @@
// Copyright 2025 The TCell Authors
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
package tcell
import (
ic "image/color"
"github.com/gdamore/tcell/v3/color"
)
// Note that the entire contents of this file should be considered deprecated
// in favor of the color subpackage. (This also means others can use the color
// package without importing the entirety of tcell into their binaries.)
// Color represents a color. The low numeric values are the same as used
// by ECMA-48, and beyond that XTerm.
//
// Note that on various terminals colors may be approximated however, or
// not supported at all. If no suitable representation for a color is known,
// the library will simply not set any color, deferring to whatever default
// attributes the terminal uses.
type Color = color.Color
const (
// ColorDefault is used to leave the Color unchanged from whatever
// system or terminal default may exist. It's also the zero value.
ColorDefault = color.Default
// ColorValid is used to indicate the color value is actually
// valid (initialized).
// Deprecated: Use color.IsValid instead.
ColorValid = color.IsValid
// ColorIsRGB is used to indicate that the numeric value is not
// a known color constant, but rather an RGB value.
// Deprecated: Use color.IsRGB instead.
ColorIsRGB = color.IsRGB
// ColorSpecial is a flag used to indicate that the values have
// special meaning, and live outside of the color space(s).
// Deprecated.
ColorSpecial = color.IsSpecial
)
// Note that the order of these options is important -- it follows the
// definitions used by ECMA and XTerm. Hence any further named colors
// must begin at a value not less than 256.
//
// Deprecated: Use color.XXX symbols instead.
const (
ColorBlack = color.XTerm0
ColorMaroon = color.XTerm1
ColorGreen = color.XTerm2
ColorOlive = color.XTerm3
ColorNavy = color.XTerm4
ColorPurple = color.XTerm5
ColorTeal = color.XTerm6
ColorSilver = color.XTerm7
ColorGray = color.XTerm8
ColorRed = color.XTerm9
ColorLime = color.XTerm10
ColorYellow = color.XTerm11
ColorBlue = color.XTerm12
ColorFuchsia = color.XTerm13
ColorAqua = color.XTerm14
ColorWhite = color.XTerm15
Color16 = color.XTerm16
Color17 = color.XTerm17
Color18 = color.XTerm18
Color19 = color.XTerm19
Color20 = color.XTerm20
Color21 = color.XTerm21
Color22 = color.XTerm22
Color23 = color.XTerm23
Color24 = color.XTerm24
Color25 = color.XTerm25
Color26 = color.XTerm26
Color27 = color.XTerm27
Color28 = color.XTerm28
Color29 = color.XTerm29
Color30 = color.XTerm30
Color31 = color.XTerm31
Color32 = color.XTerm32
Color33 = color.XTerm33
Color34 = color.XTerm34
Color35 = color.XTerm35
Color36 = color.XTerm36
Color37 = color.XTerm37
Color38 = color.XTerm38
Color39 = color.XTerm39
Color40 = color.XTerm40
Color41 = color.XTerm41
Color42 = color.XTerm42
Color43 = color.XTerm43
Color44 = color.XTerm44
Color45 = color.XTerm45
Color46 = color.XTerm46
Color47 = color.XTerm47
Color48 = color.XTerm48
Color49 = color.XTerm49
Color50 = color.XTerm50
Color51 = color.XTerm51
Color52 = color.XTerm52
Color53 = color.XTerm53
Color54 = color.XTerm54
Color55 = color.XTerm55
Color56 = color.XTerm56
Color57 = color.XTerm57
Color58 = color.XTerm58
Color59 = color.XTerm59
Color60 = color.XTerm60
Color61 = color.XTerm61
Color62 = color.XTerm62
Color63 = color.XTerm63
Color64 = color.XTerm64
Color65 = color.XTerm65
Color66 = color.XTerm66
Color67 = color.XTerm67
Color68 = color.XTerm68
Color69 = color.XTerm69
Color70 = color.XTerm70
Color71 = color.XTerm71
Color72 = color.XTerm72
Color73 = color.XTerm73
Color74 = color.XTerm74
Color75 = color.XTerm75
Color76 = color.XTerm76
Color77 = color.XTerm77
Color78 = color.XTerm78
Color79 = color.XTerm79
Color80 = color.XTerm80
Color81 = color.XTerm81
Color82 = color.XTerm82
Color83 = color.XTerm83
Color84 = color.XTerm84
Color85 = color.XTerm85
Color86 = color.XTerm86
Color87 = color.XTerm87
Color88 = color.XTerm88
Color89 = color.XTerm89
Color90 = color.XTerm90
Color91 = color.XTerm91
Color92 = color.XTerm92
Color93 = color.XTerm93
Color94 = color.XTerm94
Color95 = color.XTerm95
Color96 = color.XTerm96
Color97 = color.XTerm97
Color98 = color.XTerm98
Color99 = color.XTerm99
Color100 = color.XTerm100
Color101 = color.XTerm101
Color102 = color.XTerm102
Color103 = color.XTerm103
Color104 = color.XTerm104
Color105 = color.XTerm105
Color106 = color.XTerm106
Color107 = color.XTerm107
Color108 = color.XTerm108
Color109 = color.XTerm109
Color110 = color.XTerm110
Color111 = color.XTerm111
Color112 = color.XTerm112
Color113 = color.XTerm113
Color114 = color.XTerm114
Color115 = color.XTerm115
Color116 = color.XTerm116
Color117 = color.XTerm117
Color118 = color.XTerm118
Color119 = color.XTerm119
Color120 = color.XTerm120
Color121 = color.XTerm121
Color122 = color.XTerm122
Color123 = color.XTerm123
Color124 = color.XTerm124
Color125 = color.XTerm125
Color126 = color.XTerm126
Color127 = color.XTerm127
Color128 = color.XTerm128
Color129 = color.XTerm129
Color130 = color.XTerm130
Color131 = color.XTerm131
Color132 = color.XTerm132
Color133 = color.XTerm133
Color134 = color.XTerm134
Color135 = color.XTerm135
Color136 = color.XTerm136
Color137 = color.XTerm137
Color138 = color.XTerm138
Color139 = color.XTerm139
Color140 = color.XTerm140
Color141 = color.XTerm141
Color142 = color.XTerm142
Color143 = color.XTerm143
Color144 = color.XTerm144
Color145 = color.XTerm145
Color146 = color.XTerm146
Color147 = color.XTerm147
Color148 = color.XTerm148
Color149 = color.XTerm149
Color150 = color.XTerm150
Color151 = color.XTerm151
Color152 = color.XTerm152
Color153 = color.XTerm153
Color154 = color.XTerm154
Color155 = color.XTerm155
Color156 = color.XTerm156
Color157 = color.XTerm157
Color158 = color.XTerm158
Color159 = color.XTerm159
Color160 = color.XTerm160
Color161 = color.XTerm161
Color162 = color.XTerm162
Color163 = color.XTerm163
Color164 = color.XTerm164
Color165 = color.XTerm165
Color166 = color.XTerm166
Color167 = color.XTerm167
Color168 = color.XTerm168
Color169 = color.XTerm169
Color170 = color.XTerm170
Color171 = color.XTerm171
Color172 = color.XTerm172
Color173 = color.XTerm173
Color174 = color.XTerm174
Color175 = color.XTerm175
Color176 = color.XTerm176
Color177 = color.XTerm177
Color178 = color.XTerm178
Color179 = color.XTerm179
Color180 = color.XTerm180
Color181 = color.XTerm181
Color182 = color.XTerm182
Color183 = color.XTerm183
Color184 = color.XTerm184
Color185 = color.XTerm185
Color186 = color.XTerm186
Color187 = color.XTerm187
Color188 = color.XTerm188
Color189 = color.XTerm189
Color190 = color.XTerm190
Color191 = color.XTerm191
Color192 = color.XTerm192
Color193 = color.XTerm193
Color194 = color.XTerm194
Color195 = color.XTerm195
Color196 = color.XTerm196
Color197 = color.XTerm197
Color198 = color.XTerm198
Color199 = color.XTerm199
Color200 = color.XTerm200
Color201 = color.XTerm201
Color202 = color.XTerm202
Color203 = color.XTerm203
Color204 = color.XTerm204
Color205 = color.XTerm205
Color206 = color.XTerm206
Color207 = color.XTerm207
Color208 = color.XTerm208
Color209 = color.XTerm209
Color210 = color.XTerm210
Color211 = color.XTerm211
Color212 = color.XTerm212
Color213 = color.XTerm213
Color214 = color.XTerm214
Color215 = color.XTerm215
Color216 = color.XTerm216
Color217 = color.XTerm217
Color218 = color.XTerm218
Color219 = color.XTerm219
Color220 = color.XTerm220
Color221 = color.XTerm221
Color222 = color.XTerm222
Color223 = color.XTerm223
Color224 = color.XTerm224
Color225 = color.XTerm225
Color226 = color.XTerm226
Color227 = color.XTerm227
Color228 = color.XTerm228
Color229 = color.XTerm229
Color230 = color.XTerm230
Color231 = color.XTerm231
Color232 = color.XTerm232
Color233 = color.XTerm233
Color234 = color.XTerm234
Color235 = color.XTerm235
Color236 = color.XTerm236
Color237 = color.XTerm237
Color238 = color.XTerm238
Color239 = color.XTerm239
Color240 = color.XTerm240
Color241 = color.XTerm241
Color242 = color.XTerm242
Color243 = color.XTerm243
Color244 = color.XTerm244
Color245 = color.XTerm245
Color246 = color.XTerm246
Color247 = color.XTerm247
Color248 = color.XTerm248
Color249 = color.XTerm249
Color250 = color.XTerm250
Color251 = color.XTerm251
Color252 = color.XTerm252
Color253 = color.XTerm253
Color254 = color.XTerm254
Color255 = color.XTerm255
ColorAliceBlue = color.AliceBlue
ColorAntiqueWhite = color.AntiqueWhite
ColorAquaMarine = color.AquaMarine
ColorAzure = color.Azure
ColorBeige = color.Beige
ColorBisque = color.Bisque
ColorBlanchedAlmond = color.BlanchedAlmond
ColorBlueViolet = color.BlueViolet
ColorBrown = color.Brown
ColorBurlyWood = color.BurlyWood
ColorCadetBlue = color.CadetBlue
ColorChartreuse = color.Chartreuse
ColorChocolate = color.Chocolate
ColorCoral = color.Coral
ColorCornflowerBlue = color.CornflowerBlue
ColorCornsilk = color.Cornsilk
ColorCrimson = color.Crimson
ColorDarkBlue = color.DarkBlue
ColorDarkCyan = color.DarkCyan
ColorDarkGoldenrod = color.DarkGoldenrod
ColorDarkGray = color.DarkGray
ColorDarkGreen = color.DarkGreen
ColorDarkKhaki = color.DarkKhaki
ColorDarkMagenta = color.DarkMagenta
ColorDarkOliveGreen = color.DarkOliveGreen
ColorDarkOrange = color.DarkOrange
ColorDarkOrchid = color.DarkOrchid
ColorDarkRed = color.DarkRed
ColorDarkSalmon = color.DarkSalmon
ColorDarkSeaGreen = color.DarkSeaGreen
ColorDarkSlateBlue = color.DarkSlateBlue
ColorDarkSlateGray = color.DarkSlateGray
ColorDarkTurquoise = color.DarkTurquoise
ColorDarkViolet = color.DarkViolet
ColorDeepPink = color.DeepPink
ColorDeepSkyBlue = color.DeepSkyBlue
ColorDimGray = color.DimGray
ColorDodgerBlue = color.DodgerBlue
ColorFireBrick = color.FireBrick
ColorFloralWhite = color.FloralWhite
ColorForestGreen = color.ForestGreen
ColorGainsboro = color.Gainsboro
ColorGhostWhite = color.GhostWhite
ColorGold = color.Gold
ColorGoldenrod = color.Goldenrod
ColorGreenYellow = color.GreenYellow
ColorHoneydew = color.Honeydew
ColorHotPink = color.HotPink
ColorIndianRed = color.IndianRed
ColorIndigo = color.Indigo
ColorIvory = color.Ivory
ColorKhaki = color.Khaki
ColorLavender = color.Lavender
ColorLavenderBlush = color.LavenderBlush
ColorLawnGreen = color.LawnGreen
ColorLemonChiffon = color.LemonChiffon
ColorLightBlue = color.LightBlue
ColorLightCoral = color.LightCoral
ColorLightCyan = color.LightCyan
ColorLightGoldenrodYellow = color.LightGoldenrodYellow
ColorLightGray = color.LightGray
ColorLightGreen = color.LightGreen
ColorLightPink = color.LightPink
ColorLightSalmon = color.LightSalmon
ColorLightSeaGreen = color.LightSeaGreen
ColorLightSkyBlue = color.LightSkyBlue
ColorLightSlateGray = color.LightSlateGray
ColorLightSteelBlue = color.LightSteelBlue
ColorLightYellow = color.LightYellow
ColorLimeGreen = color.LimeGreen
ColorLinen = color.Linen
ColorMediumAquamarine = color.MediumAquamarine
ColorMediumBlue = color.MediumBlue
ColorMediumOrchid = color.MediumOrchid
ColorMediumPurple = color.MediumPurple
ColorMediumSeaGreen = color.MediumSeaGreen
ColorMediumSlateBlue = color.MediumSlateBlue
ColorMediumSpringGreen = color.MediumSpringGreen
ColorMediumTurquoise = color.MediumTurquoise
ColorMediumVioletRed = color.MediumVioletRed
ColorMidnightBlue = color.MidnightBlue
ColorMintCream = color.MintCream
ColorMistyRose = color.MistyRose
ColorMoccasin = color.Moccasin
ColorNavajoWhite = color.NavajoWhite
ColorOldLace = color.OldLace
ColorOliveDrab = color.OliveDrab
ColorOrange = color.Orange
ColorOrangeRed = color.OrangeRed
ColorOrchid = color.Orchid
ColorPaleGoldenrod = color.PaleGoldenrod
ColorPaleGreen = color.PaleGreen
ColorPaleTurquoise = color.PaleTurquoise
ColorPaleVioletRed = color.PaleVioletRed
ColorPapayaWhip = color.PapayaWhip
ColorPeachPuff = color.PeachPuff
ColorPeru = color.Peru
ColorPink = color.Pink
ColorPlum = color.Plum
ColorPowderBlue = color.PowderBlue
ColorRebeccaPurple = color.RebeccaPurple
ColorRosyBrown = color.RosyBrown
ColorRoyalBlue = color.RoyalBlue
ColorSaddleBrown = color.SaddleBrown
ColorSalmon = color.Salmon
ColorSandyBrown = color.SandyBrown
ColorSeaGreen = color.SeaGreen
ColorSeashell = color.Seashell
ColorSienna = color.Sienna
ColorSkyblue = color.Skyblue
ColorSlateBlue = color.SlateBlue
ColorSlateGray = color.SlateGray
ColorSnow = color.Snow
ColorSpringGreen = color.SpringGreen
ColorSteelBlue = color.SteelBlue
ColorTan = color.Tan
ColorThistle = color.Thistle
ColorTomato = color.Tomato
ColorTurquoise = color.Turquoise
ColorViolet = color.Violet
ColorWheat = color.Wheat
ColorWhiteSmoke = color.WhiteSmoke
ColorYellowGreen = color.YellowGreen
)
// These are aliases for the color gray, because some of us spell
// it as grey. Deprecated: Use color values.
const (
ColorGrey = color.Gray
ColorDimGrey = color.DimGray
ColorDarkGrey = color.DarkGray
ColorDarkSlateGrey = color.DarkSlateGray
ColorLightGrey = color.LightGray
ColorLightSlateGrey = color.LightSlateGray
ColorSlateGrey = color.SlateGray
)
// ColorValues maps color constants to their RGB values.
var ColorValues = color.ColorValues
// Special colors.
const (
// ColorReset is used to indicate that the color should use the
// vanilla terminal colors. (Basically go back to the defaults.)
// Deprecated: Use color.Reset.
ColorReset = color.Reset
// ColorNone indicates that we should not change the color from
// whatever is already displayed. This can only be used in limited
// circumstances.
// Deprecated: Use color.None.
ColorNone = color.None
)
// ColorNames holds the written names of colors. Useful to present a list of
// recognized named colors. Deprecated: Use color.Names.
var ColorNames = color.Names
// NewRGBColor returns a new color with the given red, green, and blue values.
// Each value must be represented in the range 0-255.
// Deprecated: Use color.NewRGBColor.
func NewRGBColor(r, g, b int32) Color {
return color.NewRGBColor(r, g, b)
}
// NewHexColor returns a color using the given 24-bit RGB value.
// Deprecated: Use color.NewHexColor.
func NewHexColor(v int32) Color {
return color.NewHexColor(v)
}
// GetColor creates a Color from a color name (W3C name). A hex value may
// be supplied as a string in the format "#ffffff".
// Deprecated: Use color.GetColor.
func GetColor(name string) Color {
return color.GetColor(name)
}
// PaletteColor creates a color based on the palette index.
// Deprecated: Use color.PaletteColor.
func PaletteColor(index int) Color {
return color.PaletteColor(index)
}
// FromImageColor converts an image/color.Color into Color.
// Deprecated: Use color.FromImageColor.
func FromImageColor(imageColor ic.Color) Color {
return color.FromImageColor(imageColor)
}
// FindColor attempts to find a given color, or the best match possible for it,
// from the palette given. This is an expensive operation, so results should
// be cached by the caller.
// Deprecated: Use color.Find.
func FindColor(c Color, palette []Color) Color {
return color.Find(c, palette)
}
+1174
View File
File diff suppressed because it is too large Load Diff
@@ -1,8 +1,8 @@
// Copyright 2016 The TCell Authors
// Copyright 2025 The TCell Authors
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use file except in compliance with the License.
// You may obtain a copy of the license at
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
@@ -12,19 +12,17 @@
// See the License for the specific language governing permissions and
// limitations under the License.
package tcell
package color
import (
"math"
"github.com/lucasb-eyer/go-colorful"
)
// FindColor attempts to find a given color, or the best match possible for it,
// Find attempts to find a given color, or the best match possible for it,
// from the palette given. This is an expensive operation, so results should
// be cached by the caller.
func FindColor(c Color, palette []Color) Color {
match := ColorDefault
func Find(c Color, palette []Color) Color {
match := Default
dist := float64(0)
r, g, b := c.RGB()
c1 := colorful.Color{
@@ -41,10 +39,9 @@ func FindColor(c Color, palette []Color) Color {
}
// CIE94 is more accurate, but really really expensive.
nd := c1.DistanceCIE76(c2)
if math.IsNaN(nd) {
nd = math.Inf(1)
}
if match == ColorDefault || nd < dist {
// NB: nd < dist is false if is NaN.
// We have never seen a case where the CIE76 algorithm returns NaN.
if match == Default || nd < dist {
match = d
dist = nd
}
+23
View File
@@ -0,0 +1,23 @@
// Copyright 2025 The TCell Authors
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
// Package tcell provides a lower-level, portable API for building
// programs that interact with terminals or consoles. It works with
// both common (and many uncommon!) terminals or terminal emulators,
// and Windows console implementations.
//
// It supports rich color, and modern terminal capabilities such as
// rich key reporting, mouse tracking, bracketed paste, desktop notifications
// and 24-bit color, when the underlying terminal supports it.
package tcell
@@ -1,11 +1,8 @@
//go:build nacl
// +build nacl
// Copyright 2015 The TCell Authors
// Copyright 2026 The TCell Authors
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use file except in compliance with the License.
// You may obtain a copy of the license at
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
@@ -17,6 +14,8 @@
package tcell
func getCharset() string {
return ""
}
import (
"github.com/gdamore/tcell/v3/internal/widthutil"
)
var textWidthOptions = widthutil.Options()

Some files were not shown because too many files have changed in this diff Show More