mirror of
https://github.com/1f349/voidterm.git
synced 2024-11-09 22:52:55 +00:00
Before editing termutil
This commit is contained in:
parent
7080321cb9
commit
4d10bde04b
59
buffer.go
59
buffer.go
@ -1,59 +0,0 @@
|
||||
package voidterm
|
||||
|
||||
import (
|
||||
"io"
|
||||
"sync/atomic"
|
||||
)
|
||||
|
||||
type Buffer struct {
|
||||
lines []Line
|
||||
cursor Position
|
||||
|
||||
inputStream io.Reader
|
||||
|
||||
width, height atomic.Uint64
|
||||
|
||||
done chan struct{}
|
||||
updateBuffer chan struct{}
|
||||
}
|
||||
|
||||
func NewBuffer(width, height uint64, input io.Reader) *Buffer {
|
||||
v := &Buffer{
|
||||
lines: make([]Line, 0),
|
||||
cursor: Position{0, 0},
|
||||
inputStream: input,
|
||||
done: make(chan struct{}),
|
||||
updateBuffer: make(chan struct{}),
|
||||
}
|
||||
v.width.Store(width)
|
||||
v.height.Store(height)
|
||||
return v
|
||||
}
|
||||
|
||||
func (v *Buffer) renderBuffer() [][]Cell {
|
||||
width := v.width.Load()
|
||||
height := v.height.Load()
|
||||
buf := make([][]Cell, height)
|
||||
y := v.height.Load() - 1
|
||||
lineIdx := len(v.lines) - 1
|
||||
for _, i := range v.lines {
|
||||
w := i.Wrap(width)
|
||||
for _, j := range w {
|
||||
buf[y] = j
|
||||
y++
|
||||
}
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
var specialChars = map[rune]func(t *Buffer){
|
||||
0x07: handleOutputBell,
|
||||
0x08: handleOutputBackspace,
|
||||
'\n': handleOutputLineFeed,
|
||||
'\v': handleOutputLineFeed,
|
||||
'\f': handleOutputLineFeed,
|
||||
'\r': handleOutputCarriageReturn,
|
||||
'\t': handleOutputTab,
|
||||
0x0e: handleShiftOut, // handle switch to G1 character set
|
||||
0x0f: handleShiftIn, // handle switch to G0 character set
|
||||
}
|
14
cell.go
14
cell.go
@ -1,14 +0,0 @@
|
||||
package voidterm
|
||||
|
||||
type Cell struct {
|
||||
r MeasuredRune
|
||||
attr CellAttributes
|
||||
}
|
||||
|
||||
func (c *Cell) Attr() CellAttributes {
|
||||
return c.attr
|
||||
}
|
||||
|
||||
func (c *Cell) Rune() MeasuredRune {
|
||||
return c.r
|
||||
}
|
19
cmd/voidterm/main.go
Normal file
19
cmd/voidterm/main.go
Normal file
@ -0,0 +1,19 @@
|
||||
package main
|
||||
|
||||
import (
|
||||
"github.com/1f349/voidterm"
|
||||
"github.com/1f349/voidterm/termutil"
|
||||
"log"
|
||||
)
|
||||
|
||||
func main() {
|
||||
updateChan := make(chan struct{})
|
||||
|
||||
void := voidterm.New("unix:///var/run/docker.sock", "aa5e8ebe40c4")
|
||||
void.Run(updateChan, 14, 11)
|
||||
term := termutil.New(termutil.WithShell("/usr/bin/bash"))
|
||||
err := term.Run(updateChan, 14, 11)
|
||||
if err != nil {
|
||||
log.Fatal(err)
|
||||
}
|
||||
}
|
8
go.mod
8
go.mod
@ -2,10 +2,16 @@ module github.com/1f349/voidterm
|
||||
|
||||
go 1.21.6
|
||||
|
||||
require github.com/stretchr/testify v1.8.4
|
||||
require (
|
||||
github.com/creack/pty v1.1.21
|
||||
github.com/liamg/darktile v0.0.11
|
||||
github.com/stretchr/testify v1.8.4
|
||||
golang.org/x/term v0.16.0
|
||||
)
|
||||
|
||||
require (
|
||||
github.com/davecgh/go-spew v1.1.1 // indirect
|
||||
github.com/pmezard/go-difflib v1.0.0 // indirect
|
||||
golang.org/x/sys v0.16.0 // indirect
|
||||
gopkg.in/yaml.v3 v3.0.1 // indirect
|
||||
)
|
||||
|
551
go.sum
551
go.sum
@ -1,10 +1,559 @@
|
||||
cloud.google.com/go v0.26.0/go.mod h1:aQUYkXzVsufM+DwF1aE+0xfcU+56JwCaLick0ClmMTw=
|
||||
cloud.google.com/go v0.34.0/go.mod h1:aQUYkXzVsufM+DwF1aE+0xfcU+56JwCaLick0ClmMTw=
|
||||
cloud.google.com/go v0.38.0/go.mod h1:990N+gfupTy94rShfmMCWGDn0LpTmnzTp2qbd1dvSRU=
|
||||
cloud.google.com/go v0.44.1/go.mod h1:iSa0KzasP4Uvy3f1mN/7PiObzGgflwredwwASm/v6AU=
|
||||
cloud.google.com/go v0.44.2/go.mod h1:60680Gw3Yr4ikxnPRS/oxxkBccT6SA1yMk63TGekxKY=
|
||||
cloud.google.com/go v0.45.1/go.mod h1:RpBamKRgapWJb87xiFSdk4g1CME7QZg3uwTez+TSTjc=
|
||||
cloud.google.com/go v0.46.3/go.mod h1:a6bKKbmY7er1mI7TEI4lsAkts/mkhTSZK8w33B4RAg0=
|
||||
cloud.google.com/go/bigquery v1.0.1/go.mod h1:i/xbL2UlR5RvWAURpBYZTtm/cXjCha9lbfbpx4poX+o=
|
||||
cloud.google.com/go/datastore v1.0.0/go.mod h1:LXYbyblFSglQ5pkeyhO+Qmw7ukd3C+pD7TKLgZqpHYE=
|
||||
cloud.google.com/go/firestore v1.1.0/go.mod h1:ulACoGHTpvq5r8rxGJ4ddJZBZqakUQqClKRT5SZwBmk=
|
||||
cloud.google.com/go/pubsub v1.0.1/go.mod h1:R0Gpsv3s54REJCy4fxDixWD93lHJMoZTyQ2kNxGRt3I=
|
||||
cloud.google.com/go/storage v1.0.0/go.mod h1:IhtSnM/ZTZV8YYJWCY8RULGVqBDmpoyjwiyrjsg+URw=
|
||||
dmitri.shuralyov.com/gpu/mtl v0.0.0-20190408044501-666a987793e9/go.mod h1:H6x//7gZCb22OMCxBHrMx7a5I7Hp++hsVxbQ4BYO7hU=
|
||||
dmitri.shuralyov.com/gpu/mtl v0.0.0-20201218220906-28db891af037/go.mod h1:H6x//7gZCb22OMCxBHrMx7a5I7Hp++hsVxbQ4BYO7hU=
|
||||
git.wow.st/gmp/clip v0.0.0-20191001134149-1458ba6a7cf5/go.mod h1:NLdpaBoMQNFqncwP8OVRNWUDw1Kt9XWm3snfT7cXu24=
|
||||
github.com/BurntSushi/toml v0.3.1/go.mod h1:xHWCNGjB5oqiDr8zfno3MHue2Ht5sIBksp03qcyfWMU=
|
||||
github.com/BurntSushi/xgb v0.0.0-20160522181843-27f122750802/go.mod h1:IVnqGOEym/WlBOVXweHU+Q+/VP0lqqI8lqeDx9IjBqo=
|
||||
github.com/BurntSushi/xgb v0.0.0-20200324125942-20f126ea2843/go.mod h1:IVnqGOEym/WlBOVXweHU+Q+/VP0lqqI8lqeDx9IjBqo=
|
||||
github.com/Knetic/govaluate v3.0.1-0.20171022003610-9aa49832a739+incompatible/go.mod h1:r7JcOSlj0wfOMncg0iLm8Leh48TZaKVeNIfJntJ2wa0=
|
||||
github.com/OneOfOne/xxhash v1.2.2/go.mod h1:HSdplMjZKSmBqAxg5vPj2TmRDmfkzw+cTzAElWljhcU=
|
||||
github.com/Shopify/sarama v1.19.0/go.mod h1:FVkBWblsNy7DGZRfXLU0O9RCGt5g3g3yEuWXgklEdEo=
|
||||
github.com/Shopify/toxiproxy v2.1.4+incompatible/go.mod h1:OXgGpZ6Cli1/URJOF1DMxUHB2q5Ap20/P/eIdh4G0pI=
|
||||
github.com/VividCortex/gohistogram v1.0.0/go.mod h1:Pf5mBqqDxYaXu3hDrrU+w6nw50o/4+TcAqDqk/vUH7g=
|
||||
github.com/afex/hystrix-go v0.0.0-20180502004556-fa1af6a1f4f5/go.mod h1:SkGFH1ia65gfNATL8TAiHDNxPzPdmEL5uirI2Uyuz6c=
|
||||
github.com/alecthomas/template v0.0.0-20160405071501-a0175ee3bccc/go.mod h1:LOuyumcjzFXgccqObfd/Ljyb9UuFJ6TxHnclSeseNhc=
|
||||
github.com/alecthomas/template v0.0.0-20190718012654-fb15b899a751/go.mod h1:LOuyumcjzFXgccqObfd/Ljyb9UuFJ6TxHnclSeseNhc=
|
||||
github.com/alecthomas/units v0.0.0-20151022065526-2efee857e7cf/go.mod h1:ybxpYRFXyAe+OPACYpWeL0wqObRcbAqCMya13uyzqw0=
|
||||
github.com/alecthomas/units v0.0.0-20190717042225-c3de453c63f4/go.mod h1:ybxpYRFXyAe+OPACYpWeL0wqObRcbAqCMya13uyzqw0=
|
||||
github.com/apache/thrift v0.12.0/go.mod h1:cp2SuWMxlEZw2r+iP2GNCdIi4C1qmUzdZFSVb+bacwQ=
|
||||
github.com/apache/thrift v0.13.0/go.mod h1:cp2SuWMxlEZw2r+iP2GNCdIi4C1qmUzdZFSVb+bacwQ=
|
||||
github.com/armon/circbuf v0.0.0-20150827004946-bbbad097214e/go.mod h1:3U/XgcO3hCbHZ8TKRvWD2dDTCfh9M9ya+I9JpbB7O8o=
|
||||
github.com/armon/go-metrics v0.0.0-20180917152333-f0300d1749da/go.mod h1:Q73ZrmVTwzkszR9V5SSuryQ31EELlFMUz1kKyl939pY=
|
||||
github.com/armon/go-radix v0.0.0-20180808171621-7fddfc383310/go.mod h1:ufUuZ+zHj4x4TnLV4JWEpy2hxWSpsRywHrMgIH9cCH8=
|
||||
github.com/aryann/difflib v0.0.0-20170710044230-e206f873d14a/go.mod h1:DAHtR1m6lCRdSC2Tm3DSWRPvIPr6xNKyeHdqDQSQT+A=
|
||||
github.com/aws/aws-lambda-go v1.13.3/go.mod h1:4UKl9IzQMoD+QF79YdCuzCwp8VbmG4VAQwij/eHl5CU=
|
||||
github.com/aws/aws-sdk-go v1.27.0/go.mod h1:KmX6BPdI08NWTb3/sm4ZGu5ShLoqVDhKgpiN924inxo=
|
||||
github.com/aws/aws-sdk-go-v2 v0.18.0/go.mod h1:JWVYvqSMppoMJC0x5wdwiImzgXTI9FuZwxzkQq9wy+g=
|
||||
github.com/beorn7/perks v0.0.0-20180321164747-3a771d992973/go.mod h1:Dwedo/Wpr24TaqPxmxbtue+5NUziq4I4S80YR8gNf3Q=
|
||||
github.com/beorn7/perks v1.0.0/go.mod h1:KWe93zE9D1o94FZ5RNwFwVgaQK1VOXiVxmqh+CedLV8=
|
||||
github.com/beorn7/perks v1.0.1/go.mod h1:G2ZrVWU2WbWT9wwq4/hrbKbnv/1ERSJQ0ibhJ6rlkpw=
|
||||
github.com/bgentry/speakeasy v0.1.0/go.mod h1:+zsyZBPWlz7T6j88CTgSN5bM796AkVf0kBD4zp0CCIs=
|
||||
github.com/bketelsen/crypt v0.0.3-0.20200106085610-5cbc8cc4026c/go.mod h1:MKsuJmJgSg28kpZDP6UIiPt0e0Oz0kqKNGyRaWEPv84=
|
||||
github.com/casbin/casbin/v2 v2.1.2/go.mod h1:YcPU1XXisHhLzuxH9coDNf2FbKpjGlbCg3n9yuLkIJQ=
|
||||
github.com/cenkalti/backoff v2.2.1+incompatible/go.mod h1:90ReRw6GdpyfrHakVjL/QHaoyV4aDUVVkXQJJJ3NXXM=
|
||||
github.com/census-instrumentation/opencensus-proto v0.2.1/go.mod h1:f6KPmirojxKA12rnyqOA5BBL4O983OfeGPqjHWSTneU=
|
||||
github.com/cespare/xxhash v1.1.0/go.mod h1:XrSqR1VqqWfGrhpAt58auRo0WTKS1nRRg3ghfAqPWnc=
|
||||
github.com/cespare/xxhash/v2 v2.1.1/go.mod h1:VGX0DQ3Q6kWi7AoAeZDth3/j3BFtOZR5XLFGgcrjCOs=
|
||||
github.com/clbanning/x2j v0.0.0-20191024224557-825249438eec/go.mod h1:jMjuTZXRI4dUb/I5gc9Hdhagfvm9+RyrPryS/auMzxE=
|
||||
github.com/client9/misspell v0.3.4/go.mod h1:qj6jICC3Q7zFZvVWo7KLAzC3yx5G7kyvSDkc90ppPyw=
|
||||
github.com/cockroachdb/datadriven v0.0.0-20190809214429-80d97fb3cbaa/go.mod h1:zn76sxSg3SzpJ0PPJaLDCu+Bu0Lg3sKTORVIj19EIF8=
|
||||
github.com/codahale/hdrhistogram v0.0.0-20161010025455-3a0bb77429bd/go.mod h1:sE/e/2PUdi/liOCUjSTXgM1o87ZssimdTWN964YiIeI=
|
||||
github.com/coreos/bbolt v1.3.2/go.mod h1:iRUV2dpdMOn7Bo10OQBFzIJO9kkE559Wcmn+qkEiiKk=
|
||||
github.com/coreos/etcd v3.3.13+incompatible/go.mod h1:uF7uidLiAD3TWHmW31ZFd/JWoc32PjwdhPthX9715RE=
|
||||
github.com/coreos/go-semver v0.2.0/go.mod h1:nnelYz7RCh+5ahJtPPxZlU+153eP4D4r3EedlOD2RNk=
|
||||
github.com/coreos/go-semver v0.3.0/go.mod h1:nnelYz7RCh+5ahJtPPxZlU+153eP4D4r3EedlOD2RNk=
|
||||
github.com/coreos/go-systemd v0.0.0-20180511133405-39ca1b05acc7/go.mod h1:F5haX7vjVVG0kc13fIWeqUViNPyEJxv/OmvnBo0Yme4=
|
||||
github.com/coreos/go-systemd v0.0.0-20190321100706-95778dfbb74e/go.mod h1:F5haX7vjVVG0kc13fIWeqUViNPyEJxv/OmvnBo0Yme4=
|
||||
github.com/coreos/pkg v0.0.0-20160727233714-3ac0863d7acf/go.mod h1:E3G3o1h8I7cfcXa63jLwjI0eiQQMgzzUDFVpN/nH/eA=
|
||||
github.com/coreos/pkg v0.0.0-20180928190104-399ea9e2e55f/go.mod h1:E3G3o1h8I7cfcXa63jLwjI0eiQQMgzzUDFVpN/nH/eA=
|
||||
github.com/cpuguy83/go-md2man/v2 v2.0.0-20190314233015-f79a8a8ca69d/go.mod h1:maD7wRr/U5Z6m/iR4s+kqSMx2CaBsrgA7czyZG/E6dU=
|
||||
github.com/cpuguy83/go-md2man/v2 v2.0.0/go.mod h1:maD7wRr/U5Z6m/iR4s+kqSMx2CaBsrgA7czyZG/E6dU=
|
||||
github.com/creack/pty v1.1.7/go.mod h1:lj5s0c3V2DBrqTV7llrYr5NG6My20zk30Fl46Y7DoTY=
|
||||
github.com/creack/pty v1.1.12/go.mod h1:oKZEueFk5CKHvIhNR5MUki03XCEU+Q6VDXinZuGJ33E=
|
||||
github.com/creack/pty v1.1.21 h1:1/QdRyBaHHJP61QkWMXlOIBfsgdDeeKfK8SYVUWJKf0=
|
||||
github.com/creack/pty v1.1.21/go.mod h1:MOBLtS5ELjhRRrroQr9kyvTxUAFNvYEK993ew/Vr4O4=
|
||||
github.com/d-tsuji/clipboard v0.0.3/go.mod h1:hF88aLYx9LHNUFRrT6KPRkXEUm34nqP97IFgORGBRFs=
|
||||
github.com/davecgh/go-spew v1.1.0/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38=
|
||||
github.com/davecgh/go-spew v1.1.1 h1:vj9j/u1bqnvCEfJOwUhtlOARqs3+rkHYY13jYWTU97c=
|
||||
github.com/davecgh/go-spew v1.1.1/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38=
|
||||
github.com/dgrijalva/jwt-go v3.2.0+incompatible/go.mod h1:E3ru+11k8xSBh+hMPgOLZmtrrCbhqsmaPHjLKYnJCaQ=
|
||||
github.com/dgryski/go-sip13 v0.0.0-20181026042036-e10d5fee7954/go.mod h1:vAd38F8PWV+bWy6jNmig1y/TA+kYO4g3RSRF0IAv0no=
|
||||
github.com/dustin/go-humanize v0.0.0-20171111073723-bb3d318650d4/go.mod h1:HtrtbFcZ19U5GC7JDqmcUSB87Iq5E25KnS6fMYU6eOk=
|
||||
github.com/eapache/go-resiliency v1.1.0/go.mod h1:kFI+JgMyC7bLPUVY133qvEBtVayf5mFgVsvEsIPBvNs=
|
||||
github.com/eapache/go-xerial-snappy v0.0.0-20180814174437-776d5712da21/go.mod h1:+020luEh2TKB4/GOp8oxxtq0Daoen/Cii55CzbTV6DU=
|
||||
github.com/eapache/queue v1.1.0/go.mod h1:6eCeP0CKFpHLu8blIFXhExK/dRa7WDZfr6jVFPTqq+I=
|
||||
github.com/edsrzf/mmap-go v1.0.0/go.mod h1:YO35OhQPt3KJa3ryjFM5Bs14WD66h8eGKpfaBNrHW5M=
|
||||
github.com/envoyproxy/go-control-plane v0.6.9/go.mod h1:SBwIajubJHhxtWwsL9s8ss4safvEdbitLhGGK48rN6g=
|
||||
github.com/envoyproxy/go-control-plane v0.9.1-0.20191026205805-5f8ba28d4473/go.mod h1:YTl/9mNaCwkRvm6d1a2C3ymFceY/DCBVvsKhRF0iEA4=
|
||||
github.com/envoyproxy/protoc-gen-validate v0.1.0/go.mod h1:iSmxcyjqTsJpI2R4NaDN7+kN2VEUnK/pcBlmesArF7c=
|
||||
github.com/fatih/color v1.7.0/go.mod h1:Zm6kSWBoL9eyXnKyktHP6abPY2pDugNf5KwzbycvMj4=
|
||||
github.com/franela/goblin v0.0.0-20200105215937-c9ffbefa60db/go.mod h1:7dvUGVsVBjqR7JHJk0brhHOZYGmfBYOrK0ZhYMEtBr4=
|
||||
github.com/franela/goreq v0.0.0-20171204163338-bcd34c9993f8/go.mod h1:ZhphrRTfi2rbfLwlschooIH4+wKKDR4Pdxhh+TRoA20=
|
||||
github.com/fsnotify/fsnotify v1.4.7/go.mod h1:jwhsz4b93w/PPRr/qN1Yymfu8t87LnFCMoQvtojpjFo=
|
||||
github.com/ghodss/yaml v1.0.0/go.mod h1:4dBDuWmgqj2HViK6kFavaiC9ZROes6MMH2rRYeMEF04=
|
||||
github.com/go-gl/glfw v0.0.0-20190409004039-e6da0acd62b1/go.mod h1:vR7hzQXu2zJy9AVAgeJqvqgH9Q5CA+iKCZ2gyEVpxRU=
|
||||
github.com/go-gl/glfw/v3.3/glfw v0.0.0-20200222043503-6f7a984d4dc4/go.mod h1:tQ2UAYgL5IevRw8kRxooKSPJfGvJ9fJQFa0TUsXzTg8=
|
||||
github.com/go-gl/glfw/v3.3/glfw v0.0.0-20210715014612-ab6297867137/go.mod h1:tQ2UAYgL5IevRw8kRxooKSPJfGvJ9fJQFa0TUsXzTg8=
|
||||
github.com/go-gl/glfw/v3.3/glfw v0.0.0-20210727001814-0db043d8d5be/go.mod h1:tQ2UAYgL5IevRw8kRxooKSPJfGvJ9fJQFa0TUsXzTg8=
|
||||
github.com/go-kit/kit v0.8.0/go.mod h1:xBxKIO96dXMWWy0MnWVtmwkA9/13aqxPnvrjFYMA2as=
|
||||
github.com/go-kit/kit v0.9.0/go.mod h1:xBxKIO96dXMWWy0MnWVtmwkA9/13aqxPnvrjFYMA2as=
|
||||
github.com/go-kit/kit v0.10.0/go.mod h1:xUsJbQ/Fp4kEt7AFgCuvyX4a71u8h9jB8tj/ORgOZ7o=
|
||||
github.com/go-logfmt/logfmt v0.3.0/go.mod h1:Qt1PoO58o5twSAckw1HlFXLmHsOX5/0LbT9GBnD5lWE=
|
||||
github.com/go-logfmt/logfmt v0.4.0/go.mod h1:3RMwSq7FuexP4Kalkev3ejPJsZTpXXBr9+V4qmtdjCk=
|
||||
github.com/go-logfmt/logfmt v0.5.0/go.mod h1:wCYkCAKZfumFQihp8CzCvQ3paCTfi41vtzG1KdI/P7A=
|
||||
github.com/go-logr/logr v0.4.0/go.mod h1:z6/tIYblkpsD+a4lm/fGIIU9mZ+XfAiaFtq7xTgseGU=
|
||||
github.com/go-sql-driver/mysql v1.4.0/go.mod h1:zAC/RDZ24gD3HViQzih4MyKcchzm+sOG5ZlKdlhCg5w=
|
||||
github.com/go-stack/stack v1.8.0/go.mod h1:v0f6uXyyMGvRgIKkXu+yp6POWl0qKG85gN/melR3HDY=
|
||||
github.com/gofrs/flock v0.8.0/go.mod h1:F1TvTiK9OcQqauNUHlbJvyl9Qa1QvF/gOUDKA14jxHU=
|
||||
github.com/gogo/googleapis v1.1.0/go.mod h1:gf4bu3Q80BeJ6H1S1vYPm8/ELATdvryBaNFGgqEef3s=
|
||||
github.com/gogo/protobuf v1.1.1/go.mod h1:r8qH/GZQm5c6nD/R0oafs1akxWv10x8SbQlK7atdtwQ=
|
||||
github.com/gogo/protobuf v1.2.0/go.mod h1:r8qH/GZQm5c6nD/R0oafs1akxWv10x8SbQlK7atdtwQ=
|
||||
github.com/gogo/protobuf v1.2.1/go.mod h1:hp+jE20tsWTFYpLwKvXlhS1hjn+gTNwPg2I6zVXpSg4=
|
||||
github.com/golang/glog v0.0.0-20160126235308-23def4e6c14b/go.mod h1:SBH7ygxi8pfUlaOkMMuAQtPIUF8ecWP5IEl/CR7VP2Q=
|
||||
github.com/golang/groupcache v0.0.0-20160516000752-02826c3e7903/go.mod h1:cIg4eruTrX1D+g88fzRXU5OdNfaM+9IcxsU14FzY7Hc=
|
||||
github.com/golang/groupcache v0.0.0-20190129154638-5b532d6fd5ef/go.mod h1:cIg4eruTrX1D+g88fzRXU5OdNfaM+9IcxsU14FzY7Hc=
|
||||
github.com/golang/groupcache v0.0.0-20190702054246-869f871628b6/go.mod h1:cIg4eruTrX1D+g88fzRXU5OdNfaM+9IcxsU14FzY7Hc=
|
||||
github.com/golang/mock v1.1.1/go.mod h1:oTYuIxOrZwtPieC+H1uAHpcLFnEyAGVDL/k47Jfbm0A=
|
||||
github.com/golang/mock v1.2.0/go.mod h1:oTYuIxOrZwtPieC+H1uAHpcLFnEyAGVDL/k47Jfbm0A=
|
||||
github.com/golang/mock v1.3.1/go.mod h1:sBzyDLLjw3U8JLTeZvSv8jJB+tU5PVekmnlKIyFUx0Y=
|
||||
github.com/golang/protobuf v1.2.0/go.mod h1:6lQm79b+lXiMfvg/cZm0SGofjICqVBUtrP5yJMmIC1U=
|
||||
github.com/golang/protobuf v1.3.1/go.mod h1:6lQm79b+lXiMfvg/cZm0SGofjICqVBUtrP5yJMmIC1U=
|
||||
github.com/golang/protobuf v1.3.2/go.mod h1:6lQm79b+lXiMfvg/cZm0SGofjICqVBUtrP5yJMmIC1U=
|
||||
github.com/golang/snappy v0.0.0-20180518054509-2e65f85255db/go.mod h1:/XxbfmMg8lxefKM7IXC3fBNl/7bRcc72aCRzEWrmP2Q=
|
||||
github.com/google/btree v0.0.0-20180813153112-4030bb1f1f0c/go.mod h1:lNA+9X1NB3Zf8V7Ke586lFgjr2dZNuvo3lPJSGZ5JPQ=
|
||||
github.com/google/btree v1.0.0/go.mod h1:lNA+9X1NB3Zf8V7Ke586lFgjr2dZNuvo3lPJSGZ5JPQ=
|
||||
github.com/google/go-cmp v0.2.0/go.mod h1:oXzfMopK8JAjlY9xF4vHSVASa0yLyX7SntLO5aqRK0M=
|
||||
github.com/google/go-cmp v0.3.0/go.mod h1:8QqcDgzrUqlUb/G2PQTWiueGozuR1884gddMywk6iLU=
|
||||
github.com/google/go-cmp v0.3.1/go.mod h1:8QqcDgzrUqlUb/G2PQTWiueGozuR1884gddMywk6iLU=
|
||||
github.com/google/go-cmp v0.5.5/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE=
|
||||
github.com/google/gofuzz v1.0.0/go.mod h1:dBl0BpW6vV/+mYPU4Po3pmUjxk6FQPldtuIdl/M65Eg=
|
||||
github.com/google/martian v2.1.0+incompatible/go.mod h1:9I4somxYTbIHy5NJKHRl3wXiIaQGbYVAs8BPL6v8lEs=
|
||||
github.com/google/pprof v0.0.0-20181206194817-3ea8567a2e57/go.mod h1:zfwlbNMJ+OItoe0UupaVj+oy1omPYYDuagoSzA8v9mc=
|
||||
github.com/google/pprof v0.0.0-20190515194954-54271f7e092f/go.mod h1:zfwlbNMJ+OItoe0UupaVj+oy1omPYYDuagoSzA8v9mc=
|
||||
github.com/google/renameio v0.1.0/go.mod h1:KWCgfxg9yswjAJkECMjeO8J8rahYeXnNhOm40UhjYkI=
|
||||
github.com/google/uuid v1.0.0/go.mod h1:TIyPZe4MgqvfeYDBFedMoGGpEw/LqOeaOT+nhxU+yHo=
|
||||
github.com/googleapis/gax-go/v2 v2.0.4/go.mod h1:0Wqv26UfaUD9n4G6kQubkQ+KchISgw+vpHVxEJEs9eg=
|
||||
github.com/googleapis/gax-go/v2 v2.0.5/go.mod h1:DWXyrwAJ9X0FpwwEdw+IPEYBICEFu5mhpdKc/us6bOk=
|
||||
github.com/gopherjs/gopherjs v0.0.0-20181017120253-0766667cb4d1/go.mod h1:wJfORRmW1u3UXTncJ5qlYoELFm8eSnnEO6hX4iZ3EWY=
|
||||
github.com/gorilla/context v1.1.1/go.mod h1:kBGZzfjB9CEq2AlWe17Uuf7NDRt0dE0s8S51q0aT7Yg=
|
||||
github.com/gorilla/mux v1.6.2/go.mod h1:1lud6UwP+6orDFRuTfBEV8e9/aOM/c4fVVCaMa2zaAs=
|
||||
github.com/gorilla/mux v1.7.3/go.mod h1:1lud6UwP+6orDFRuTfBEV8e9/aOM/c4fVVCaMa2zaAs=
|
||||
github.com/gorilla/websocket v0.0.0-20170926233335-4201258b820c/go.mod h1:E7qHFY5m1UJ88s3WnNqhKjPHQ0heANvMoAMk2YaljkQ=
|
||||
github.com/gorilla/websocket v1.4.2/go.mod h1:YR8l580nyteQvAITg2hZ9XVh4b55+EU/adAjf1fMHhE=
|
||||
github.com/grpc-ecosystem/go-grpc-middleware v1.0.0/go.mod h1:FiyG127CGDf3tlThmgyCl78X/SZQqEOJBCDaAfeWzPs=
|
||||
github.com/grpc-ecosystem/go-grpc-middleware v1.0.1-0.20190118093823-f849b5445de4/go.mod h1:FiyG127CGDf3tlThmgyCl78X/SZQqEOJBCDaAfeWzPs=
|
||||
github.com/grpc-ecosystem/go-grpc-prometheus v1.2.0/go.mod h1:8NvIoxWQoOIhqOTXgfV/d3M/q6VIi02HzZEHgUlZvzk=
|
||||
github.com/grpc-ecosystem/grpc-gateway v1.9.0/go.mod h1:vNeuVxBJEsws4ogUvrchl83t/GYV9WGTSLVdBhOQFDY=
|
||||
github.com/grpc-ecosystem/grpc-gateway v1.9.5/go.mod h1:vNeuVxBJEsws4ogUvrchl83t/GYV9WGTSLVdBhOQFDY=
|
||||
github.com/hajimehoshi/bitmapfont/v2 v2.1.3/go.mod h1:2BnYrkTQGThpr/CY6LorYtt/zEPNzvE/ND69CRTaHMs=
|
||||
github.com/hajimehoshi/ebiten/v2 v2.2.0-alpha.11.0.20210724070913-1706d9436a78/go.mod h1:4AG16fE4/E9OfftCnkhL1KXUEAkA/my+AQ0eY/vi8jw=
|
||||
github.com/hajimehoshi/file2byteslice v0.0.0-20200812174855-0e5e8a80490e/go.mod h1:CqqAHp7Dk/AqQiwuhV1yT2334qbA/tFWQW0MD2dGqUE=
|
||||
github.com/hajimehoshi/go-mp3 v0.3.2/go.mod h1:qMJj/CSDxx6CGHiZeCgbiq2DSUkbK0UbtXShQcnfyMM=
|
||||
github.com/hajimehoshi/oto v0.6.1/go.mod h1:0QXGEkbuJRohbJaxr7ZQSxnju7hEhseiPx2hrh6raOI=
|
||||
github.com/hajimehoshi/oto v0.7.1/go.mod h1:wovJ8WWMfFKvP587mhHgot/MBr4DnNy9m6EepeVGnos=
|
||||
github.com/hashicorp/consul/api v1.1.0/go.mod h1:VmuI/Lkw1nC05EYQWNKwWGbkg+FbDBtguAZLlVdkD9Q=
|
||||
github.com/hashicorp/consul/api v1.3.0/go.mod h1:MmDNSzIMUjNpY/mQ398R4bk2FnqQLoPndWW5VkKPlCE=
|
||||
github.com/hashicorp/consul/sdk v0.1.1/go.mod h1:VKf9jXwCTEY1QZP2MOLRhb5i/I/ssyNV1vwHyQBF0x8=
|
||||
github.com/hashicorp/consul/sdk v0.3.0/go.mod h1:VKf9jXwCTEY1QZP2MOLRhb5i/I/ssyNV1vwHyQBF0x8=
|
||||
github.com/hashicorp/errwrap v1.0.0/go.mod h1:YH+1FKiLXxHSkmPseP+kNlulaMuP3n2brvKWEqk/Jc4=
|
||||
github.com/hashicorp/go-cleanhttp v0.5.1/go.mod h1:JpRdi6/HCYpAwUzNwuwqhbovhLtngrth3wmdIIUrZ80=
|
||||
github.com/hashicorp/go-immutable-radix v1.0.0/go.mod h1:0y9vanUI8NX6FsYoO3zeMjhV/C5i9g4Q3DwcSNZ4P60=
|
||||
github.com/hashicorp/go-msgpack v0.5.3/go.mod h1:ahLV/dePpqEmjfWmKiqvPkv/twdG7iPBM1vqhUKIvfM=
|
||||
github.com/hashicorp/go-multierror v1.0.0/go.mod h1:dHtQlpGsu+cZNNAkkCN/P3hoUDHhCYQXV3UM06sGGrk=
|
||||
github.com/hashicorp/go-rootcerts v1.0.0/go.mod h1:K6zTfqpRlCUIjkwsN4Z+hiSfzSTQa6eBIzfwKfwNnHU=
|
||||
github.com/hashicorp/go-sockaddr v1.0.0/go.mod h1:7Xibr9yA9JjQq1JpNB2Vw7kxv8xerXegt+ozgdvDeDU=
|
||||
github.com/hashicorp/go-syslog v1.0.0/go.mod h1:qPfqrKkXGihmCqbJM2mZgkZGvKG1dFdvsLplgctolz4=
|
||||
github.com/hashicorp/go-uuid v1.0.0/go.mod h1:6SBZvOh/SIDV7/2o3Jml5SYk/TvGqwFJ/bN7x4byOro=
|
||||
github.com/hashicorp/go-uuid v1.0.1/go.mod h1:6SBZvOh/SIDV7/2o3Jml5SYk/TvGqwFJ/bN7x4byOro=
|
||||
github.com/hashicorp/go-version v1.2.0/go.mod h1:fltr4n8CU8Ke44wwGCBoEymUuxUHl09ZGVZPK5anwXA=
|
||||
github.com/hashicorp/go.net v0.0.1/go.mod h1:hjKkEWcCURg++eb33jQU7oqQcI9XDCnUzHA0oac0k90=
|
||||
github.com/hashicorp/golang-lru v0.5.0/go.mod h1:/m3WP610KZHVQ1SGc6re/UDhFvYD7pJ4Ao+sR/qLZy8=
|
||||
github.com/hashicorp/golang-lru v0.5.1/go.mod h1:/m3WP610KZHVQ1SGc6re/UDhFvYD7pJ4Ao+sR/qLZy8=
|
||||
github.com/hashicorp/hcl v1.0.0/go.mod h1:E5yfLk+7swimpb2L/Alb/PJmXilQ/rhwaUYs4T20WEQ=
|
||||
github.com/hashicorp/logutils v1.0.0/go.mod h1:QIAnNjmIWmVIIkWDTG1z5v++HQmx9WQRO+LraFDTW64=
|
||||
github.com/hashicorp/mdns v1.0.0/go.mod h1:tL+uN++7HEJ6SQLQ2/p+z2pH24WQKWjBPkE0mNTz8vQ=
|
||||
github.com/hashicorp/memberlist v0.1.3/go.mod h1:ajVTdAv/9Im8oMAAj5G31PhhMCZJV2pPBoIllUwCN7I=
|
||||
github.com/hashicorp/serf v0.8.2/go.mod h1:6hOLApaqBFA1NXqRQAsxw9QxuDEvNxSQRwA/JwenrHc=
|
||||
github.com/hpcloud/tail v1.0.0/go.mod h1:ab1qPbhIpdTxEkNHXyeSf5vhxWSCs/tWer42PpOxQnU=
|
||||
github.com/hudl/fargo v1.3.0/go.mod h1:y3CKSmjA+wD2gak7sUSXTAoopbhU08POFhmITJgmKTg=
|
||||
github.com/inconshreveable/mousetrap v1.0.0/go.mod h1:PxqpIevigyE2G7u3NXJIT2ANytuPF1OarO4DADm73n8=
|
||||
github.com/influxdata/influxdb1-client v0.0.0-20191209144304-8bf82d3c094d/go.mod h1:qj24IKcXYK6Iy9ceXlo3Tc+vtHo9lIhSX5JddghvEPo=
|
||||
github.com/jakecoffman/cp v1.1.0/go.mod h1:JjY/Fp6d8E1CHnu74gWNnU0+b9VzEdUVPoJxg2PsTQg=
|
||||
github.com/jfreymuth/oggvorbis v1.0.3/go.mod h1:1U4pqWmghcoVsCJJ4fRBKv9peUJMBHixthRlBeD6uII=
|
||||
github.com/jfreymuth/vorbis v1.0.2/go.mod h1:DoftRo4AznKnShRl1GxiTFCseHr4zR9BN3TWXyuzrqQ=
|
||||
github.com/jmespath/go-jmespath v0.0.0-20180206201540-c2b33e8439af/go.mod h1:Nht3zPeWKUH0NzdCt2Blrr5ys8VGpn0CEB0cQHVjt7k=
|
||||
github.com/jonboulle/clockwork v0.1.0/go.mod h1:Ii8DK3G1RaLaWxj9trq07+26W01tbo22gdxWY5EU2bo=
|
||||
github.com/json-iterator/go v1.1.6/go.mod h1:+SdeFBvtyEkXs7REEP0seUULqWtbJapLOCVDaaPEHmU=
|
||||
github.com/json-iterator/go v1.1.7/go.mod h1:KdQUCv79m/52Kvf8AW2vK1V8akMuk1QjK/uOdHXbAo4=
|
||||
github.com/json-iterator/go v1.1.8/go.mod h1:KdQUCv79m/52Kvf8AW2vK1V8akMuk1QjK/uOdHXbAo4=
|
||||
github.com/jstemmer/go-junit-report v0.0.0-20190106144839-af01ea7f8024/go.mod h1:6v2b51hI/fHJwM22ozAgKL4VKDeJcHhJFhtBdhmNjmU=
|
||||
github.com/jtolds/gls v4.20.0+incompatible/go.mod h1:QJZ7F/aHp+rZTRtaJ1ow/lLfFfVYBRgL+9YlvaHOwJU=
|
||||
github.com/julienschmidt/httprouter v1.2.0/go.mod h1:SYymIcj16QtmaHHD7aYtjjsJG7VTCxuUUipMqKk8s4w=
|
||||
github.com/kisielk/errcheck v1.1.0/go.mod h1:EZBBE59ingxPouuu3KfxchcWSUPOHkagtvWXihfKN4Q=
|
||||
github.com/kisielk/gotool v1.0.0/go.mod h1:XhKaO+MFFWcvkIS/tQcRk01m1F5IRFswLeQ+oQHNcck=
|
||||
github.com/konsorten/go-windows-terminal-sequences v1.0.1/go.mod h1:T0+1ngSBFLxvqU3pZ+m/2kptfBszLMUkC4ZK/EgS/cQ=
|
||||
github.com/kr/logfmt v0.0.0-20140226030751-b84e30acd515/go.mod h1:+0opPa2QZZtGFBFZlji/RkVcI2GknAs/DXo4wKdlNEc=
|
||||
github.com/kr/pretty v0.1.0/go.mod h1:dAy3ld7l9f0ibDNOQOHHMYYIIbhfbHSm3C4ZsoJORNo=
|
||||
github.com/kr/pretty v0.2.1 h1:Fmg33tUaq4/8ym9TJN1x7sLJnHVwhP33CNkpYV/7rwI=
|
||||
github.com/kr/pretty v0.2.1/go.mod h1:ipq/a2n7PKx3OHsz4KJII5eveXtPO4qwEXGdVfWzfnI=
|
||||
github.com/kr/pty v1.1.1/go.mod h1:pFQYn66WHrOpPYNljwOMqo10TkYh1fy3cYio2l3bCsQ=
|
||||
github.com/kr/text v0.1.0 h1:45sCR5RtlFHMR4UwH9sdQ5TC8v0qDQCHnXt+kaKSTVE=
|
||||
github.com/kr/text v0.1.0/go.mod h1:4Jbv+DJW3UT/LiOwJeYQe1efqtUx/iVham/4vfdArNI=
|
||||
github.com/liamg/darktile v0.0.11 h1:RmJSTWgdqA3hM7blp3NY92xJVPlNzNsIK2O8CimPuQw=
|
||||
github.com/liamg/darktile v0.0.11/go.mod h1:vFhboN4Fqebzm9KL6U/XHKbbq+M4pSdO/8jBUMa3lu0=
|
||||
github.com/liamg/fontinfo v0.1.3/go.mod h1:6REdGXLC8yXmxpX31DDwjjzT06g1c7UcvY75AGf9sH4=
|
||||
github.com/lightstep/lightstep-tracer-common/golang/gogo v0.0.0-20190605223551-bc2310a04743/go.mod h1:qklhhLq1aX+mtWk9cPHPzaBjWImj5ULL6C7HFJtXQMM=
|
||||
github.com/lightstep/lightstep-tracer-go v0.18.1/go.mod h1:jlF1pusYV4pidLvZ+XD0UBX0ZE6WURAspgAczcDHrL4=
|
||||
github.com/lxn/walk v0.0.0-20191128110447-55ccb3a9f5c1/go.mod h1:E23UucZGqpuUANJooIbHWCufXvOcT6E7Stq81gU+CSQ=
|
||||
github.com/lxn/win v0.0.0-20191128105842-2da648fda5b4/go.mod h1:ouWl4wViUNh8tPSIwxTVMuS014WakR1hqvBc2I0bMoA=
|
||||
github.com/lyft/protoc-gen-validate v0.0.13/go.mod h1:XbGvPuh87YZc5TdIa2/I4pLk0QoUACkjt2znoq26NVQ=
|
||||
github.com/magiconair/properties v1.8.1/go.mod h1:PppfXfuXeibc/6YijjN8zIbojt8czPbwD3XqdrwzmxQ=
|
||||
github.com/mattn/go-colorable v0.0.9/go.mod h1:9vuHe8Xs5qXnSaW/c/ABM9alt+Vo+STaOChaDxuIBZU=
|
||||
github.com/mattn/go-isatty v0.0.3/go.mod h1:M+lRXTBqGeGNdLjl/ufCoiOlB5xdOkqRJdNxMWT7Zi4=
|
||||
github.com/mattn/go-isatty v0.0.4/go.mod h1:M+lRXTBqGeGNdLjl/ufCoiOlB5xdOkqRJdNxMWT7Zi4=
|
||||
github.com/mattn/go-runewidth v0.0.2/go.mod h1:LwmH8dsx7+W8Uxz3IHJYH5QSwggIsqBzpuz5H//U1FU=
|
||||
github.com/matttproud/golang_protobuf_extensions v1.0.1/go.mod h1:D8He9yQNgCq6Z5Ld7szi9bcBfOoFv/3dc6xSMkL2PC0=
|
||||
github.com/miekg/dns v1.0.14/go.mod h1:W1PPwlIAgtquWBMBEV9nkV9Cazfe8ScdGz/Lj7v3Nrg=
|
||||
github.com/mitchellh/cli v1.0.0/go.mod h1:hNIlj7HEI86fIcpObd7a0FcrxTWetlwJDGcceTlRvqc=
|
||||
github.com/mitchellh/go-homedir v1.0.0/go.mod h1:SfyaCUpYCn1Vlf4IUYiD9fPX4A5wJrkLzIz1N1q0pr0=
|
||||
github.com/mitchellh/go-homedir v1.1.0/go.mod h1:SfyaCUpYCn1Vlf4IUYiD9fPX4A5wJrkLzIz1N1q0pr0=
|
||||
github.com/mitchellh/go-testing-interface v1.0.0/go.mod h1:kRemZodwjscx+RGhAo8eIhFbs2+BFgRtFPeD/KE+zxI=
|
||||
github.com/mitchellh/gox v0.4.0/go.mod h1:Sd9lOJ0+aimLBi73mGofS1ycjY8lL3uZM3JPS42BGNg=
|
||||
github.com/mitchellh/iochan v1.0.0/go.mod h1:JwYml1nuB7xOzsp52dPpHFffvOCDupsG0QubkSMEySY=
|
||||
github.com/mitchellh/mapstructure v0.0.0-20160808181253-ca63d7c062ee/go.mod h1:FVVH3fgwuzCH5S8UJGiWEs2h04kUh9fWfEaFds41c1Y=
|
||||
github.com/mitchellh/mapstructure v1.1.2/go.mod h1:FVVH3fgwuzCH5S8UJGiWEs2h04kUh9fWfEaFds41c1Y=
|
||||
github.com/modern-go/concurrent v0.0.0-20180228061459-e0a39a4cb421/go.mod h1:6dJC0mAP4ikYIbvyc7fijjWJddQyLn8Ig3JB5CqoB9Q=
|
||||
github.com/modern-go/concurrent v0.0.0-20180306012644-bacd9c7ef1dd/go.mod h1:6dJC0mAP4ikYIbvyc7fijjWJddQyLn8Ig3JB5CqoB9Q=
|
||||
github.com/modern-go/reflect2 v0.0.0-20180701023420-4b7aa43c6742/go.mod h1:bx2lNnkwVCuqBIxFjflWJWanXIb3RllmbCylyMrvgv0=
|
||||
github.com/modern-go/reflect2 v1.0.1/go.mod h1:bx2lNnkwVCuqBIxFjflWJWanXIb3RllmbCylyMrvgv0=
|
||||
github.com/mvdan/xurls v1.1.0/go.mod h1:tQlNn3BED8bE/15hnSL2HLkDeLWpNPAwtw7wkEq44oU=
|
||||
github.com/mwitkow/go-conntrack v0.0.0-20161129095857-cc309e4a2223/go.mod h1:qRWi+5nqEBWmkhHvq77mSJWrCKwh8bxhgT7d/eI7P4U=
|
||||
github.com/nats-io/jwt v0.3.0/go.mod h1:fRYCDE99xlTsqUzISS1Bi75UBJ6ljOJQOAAu5VglpSg=
|
||||
github.com/nats-io/jwt v0.3.2/go.mod h1:/euKqTS1ZD+zzjYrY7pseZrTtWQSjujC7xjPc8wL6eU=
|
||||
github.com/nats-io/nats-server/v2 v2.1.2/go.mod h1:Afk+wRZqkMQs/p45uXdrVLuab3gwv3Z8C4HTBu8GD/k=
|
||||
github.com/nats-io/nats.go v1.9.1/go.mod h1:ZjDU1L/7fJ09jvUSRVBR2e7+RnLiiIQyqyzEE/Zbp4w=
|
||||
github.com/nats-io/nkeys v0.1.0/go.mod h1:xpnFELMwJABBLVhffcfd1MZx6VsNRFpEugbxziKVo7w=
|
||||
github.com/nats-io/nkeys v0.1.3/go.mod h1:xpnFELMwJABBLVhffcfd1MZx6VsNRFpEugbxziKVo7w=
|
||||
github.com/nats-io/nuid v1.0.1/go.mod h1:19wcPz3Ph3q0Jbyiqsd0kePYG7A95tJPxeL+1OSON2c=
|
||||
github.com/oklog/oklog v0.3.2/go.mod h1:FCV+B7mhrz4o+ueLpx+KqkyXRGMWOYEvfiXtdGtbWGs=
|
||||
github.com/oklog/run v1.0.0/go.mod h1:dlhp/R75TPv97u0XWUtDeV/lRKWPKSdTuV0TZvrmrQA=
|
||||
github.com/oklog/ulid v1.3.1/go.mod h1:CirwcVhetQ6Lv90oh/F+FBtV6XMibvdAFo93nm5qn4U=
|
||||
github.com/olekukonko/tablewriter v0.0.0-20170122224234-a0225b3f23b5/go.mod h1:vsDQFd/mU46D+Z4whnwzcISnGGzXWMclvtLoiIKAKIo=
|
||||
github.com/onsi/ginkgo v1.6.0/go.mod h1:lLunBs/Ym6LB5Z9jYTR76FiuTmxDTDusOGeTQH+WWjE=
|
||||
github.com/onsi/ginkgo v1.7.0/go.mod h1:lLunBs/Ym6LB5Z9jYTR76FiuTmxDTDusOGeTQH+WWjE=
|
||||
github.com/onsi/gomega v1.4.3/go.mod h1:ex+gbHU/CVuBBDIJjb2X0qEXbFg53c61hWP/1CpauHY=
|
||||
github.com/op/go-logging v0.0.0-20160315200505-970db520ece7/go.mod h1:HzydrMdWErDVzsI23lYNej1Htcns9BCg93Dk0bBINWk=
|
||||
github.com/opentracing-contrib/go-observer v0.0.0-20170622124052-a52f23424492/go.mod h1:Ngi6UdF0k5OKD5t5wlmGhe/EDKPoUM3BXZSSfIuJbis=
|
||||
github.com/opentracing/basictracer-go v1.0.0/go.mod h1:QfBfYuafItcjQuMwinw9GhYKwFXS9KnPs5lxoYwgW74=
|
||||
github.com/opentracing/opentracing-go v1.0.2/go.mod h1:UkNAQd3GIcIGf0SeVgPpRdFStlNbqXla1AfSYxPUl2o=
|
||||
github.com/opentracing/opentracing-go v1.1.0/go.mod h1:UkNAQd3GIcIGf0SeVgPpRdFStlNbqXla1AfSYxPUl2o=
|
||||
github.com/openzipkin-contrib/zipkin-go-opentracing v0.4.5/go.mod h1:/wsWhb9smxSfWAKL3wpBW7V8scJMt8N8gnaMCS9E/cA=
|
||||
github.com/openzipkin/zipkin-go v0.1.6/go.mod h1:QgAqvLzwWbR/WpD4A3cGpPtJrZXNIiJc5AZX7/PBEpw=
|
||||
github.com/openzipkin/zipkin-go v0.2.1/go.mod h1:NaW6tEwdmWMaCDZzg8sh+IBNOxHMPnhQw8ySjnjRyN4=
|
||||
github.com/openzipkin/zipkin-go v0.2.2/go.mod h1:NaW6tEwdmWMaCDZzg8sh+IBNOxHMPnhQw8ySjnjRyN4=
|
||||
github.com/pact-foundation/pact-go v1.0.4/go.mod h1:uExwJY4kCzNPcHRj+hCR/HBbOOIwwtUjcrb0b5/5kLM=
|
||||
github.com/pascaldekloe/goe v0.0.0-20180627143212-57f6aae5913c/go.mod h1:lzWF7FIEvWOWxwDKqyGYQf6ZUaNfKdP144TG7ZOy1lc=
|
||||
github.com/pborman/uuid v1.2.0/go.mod h1:X/NO0urCmaxf9VXbdlT7C2Yzkj2IKimNn4k+gtPdI/k=
|
||||
github.com/pelletier/go-toml v1.2.0/go.mod h1:5z9KED0ma1S8pY6P1sdut58dfprrGBbd/94hg7ilaic=
|
||||
github.com/performancecopilot/speed v3.0.0+incompatible/go.mod h1:/CLtqpZ5gBg1M9iaPbIdPPGyKcA8hKdoy6hAWba7Yac=
|
||||
github.com/pierrec/lz4 v1.0.2-0.20190131084431-473cd7ce01a1/go.mod h1:3/3N9NVKO0jef7pBehbT1qWhCMrIgbYNnFAZCqQ5LRc=
|
||||
github.com/pierrec/lz4 v2.0.5+incompatible/go.mod h1:pdkljMzZIN41W+lC3N2tnIh5sFi+IEE17M5jbnwPHcY=
|
||||
github.com/pkg/browser v0.0.0-20180916011732-0a3d74bf9ce4/go.mod h1:4OwLy04Bl9Ef3GJJCoec+30X3LQs/0/m4HFRt/2LUSA=
|
||||
github.com/pkg/errors v0.8.0/go.mod h1:bwawxfHBFNV+L2hUp1rHADufV3IMtnDRdf1r5NINEl0=
|
||||
github.com/pkg/errors v0.8.1/go.mod h1:bwawxfHBFNV+L2hUp1rHADufV3IMtnDRdf1r5NINEl0=
|
||||
github.com/pkg/errors v0.9.1/go.mod h1:bwawxfHBFNV+L2hUp1rHADufV3IMtnDRdf1r5NINEl0=
|
||||
github.com/pkg/profile v1.2.1/go.mod h1:hJw3o1OdXxsrSjjVksARp5W95eeEaEfptyVZyv6JUPA=
|
||||
github.com/pmezard/go-difflib v1.0.0 h1:4DBwDE0NGyQoBHbLQYPwSUPoCMWR5BEzIk/f1lZbAQM=
|
||||
github.com/pmezard/go-difflib v1.0.0/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4=
|
||||
github.com/posener/complete v1.1.1/go.mod h1:em0nMJCgc9GFtwrmVmEMR/ZL6WyhyjMBndrE9hABlRI=
|
||||
github.com/prometheus/client_golang v0.9.1/go.mod h1:7SWBe2y4D6OKWSNQJUaRYU/AaXPKyh/dDVn+NZz0KFw=
|
||||
github.com/prometheus/client_golang v0.9.3-0.20190127221311-3c4408c8b829/go.mod h1:p2iRAGwDERtqlqzRXnrOVns+ignqQo//hLXqYxZYVNs=
|
||||
github.com/prometheus/client_golang v0.9.3/go.mod h1:/TN21ttK/J9q6uSwhBd54HahCDft0ttaMvbicHlPoso=
|
||||
github.com/prometheus/client_golang v1.0.0/go.mod h1:db9x61etRT2tGnBNRi70OPL5FsnadC4Ky3P0J6CfImo=
|
||||
github.com/prometheus/client_golang v1.3.0/go.mod h1:hJaj2vgQTGQmVCsAACORcieXFeDPbaTKGT+JTgUa3og=
|
||||
github.com/prometheus/client_model v0.0.0-20180712105110-5c3871d89910/go.mod h1:MbSGuTsp3dbXC40dX6PRTWyKYBIrTGTE9sqQNg2J8bo=
|
||||
github.com/prometheus/client_model v0.0.0-20190115171406-56726106282f/go.mod h1:MbSGuTsp3dbXC40dX6PRTWyKYBIrTGTE9sqQNg2J8bo=
|
||||
github.com/prometheus/client_model v0.0.0-20190129233127-fd36f4220a90/go.mod h1:xMI15A0UPsDsEKsMN9yxemIoYk6Tm2C1GtYGdfGttqA=
|
||||
github.com/prometheus/client_model v0.0.0-20190812154241-14fe0d1b01d4/go.mod h1:xMI15A0UPsDsEKsMN9yxemIoYk6Tm2C1GtYGdfGttqA=
|
||||
github.com/prometheus/client_model v0.1.0/go.mod h1:xMI15A0UPsDsEKsMN9yxemIoYk6Tm2C1GtYGdfGttqA=
|
||||
github.com/prometheus/common v0.0.0-20181113130724-41aa239b4cce/go.mod h1:daVV7qP5qjZbuso7PdcryaAu0sAZbrN9i7WWcTMWvro=
|
||||
github.com/prometheus/common v0.2.0/go.mod h1:TNfzLD0ON7rHzMJeJkieUDPYmFC7Snx/y86RQel1bk4=
|
||||
github.com/prometheus/common v0.4.0/go.mod h1:TNfzLD0ON7rHzMJeJkieUDPYmFC7Snx/y86RQel1bk4=
|
||||
github.com/prometheus/common v0.4.1/go.mod h1:TNfzLD0ON7rHzMJeJkieUDPYmFC7Snx/y86RQel1bk4=
|
||||
github.com/prometheus/common v0.7.0/go.mod h1:DjGbpBbp5NYNiECxcL/VnbXCCaQpKd3tt26CguLLsqA=
|
||||
github.com/prometheus/procfs v0.0.0-20181005140218-185b4288413d/go.mod h1:c3At6R/oaqEKCNdg8wHV1ftS6bRYblBhIjjI8uT2IGk=
|
||||
github.com/prometheus/procfs v0.0.0-20190117184657-bf6a532e95b1/go.mod h1:c3At6R/oaqEKCNdg8wHV1ftS6bRYblBhIjjI8uT2IGk=
|
||||
github.com/prometheus/procfs v0.0.0-20190507164030-5867b95ac084/go.mod h1:TjEm7ze935MbeOT/UhFTIMYKhuLP4wbCsTZCD3I8kEA=
|
||||
github.com/prometheus/procfs v0.0.2/go.mod h1:TjEm7ze935MbeOT/UhFTIMYKhuLP4wbCsTZCD3I8kEA=
|
||||
github.com/prometheus/procfs v0.0.8/go.mod h1:7Qr8sr6344vo1JqZ6HhLceV9o3AJ1Ff+GxbHq6oeK9A=
|
||||
github.com/prometheus/tsdb v0.7.1/go.mod h1:qhTCs0VvXwvX/y3TZrWD7rabWM+ijKTux40TwIPHuXU=
|
||||
github.com/rcrowley/go-metrics v0.0.0-20181016184325-3113b8401b8a/go.mod h1:bCqnVzQkZxMG4s8nGwiZ5l3QUCyqpo9Y+/ZMZ9VjZe4=
|
||||
github.com/rogpeppe/fastuuid v0.0.0-20150106093220-6724a57986af/go.mod h1:XWv6SoW27p1b0cqNHllgS5HIMJraePCO15w5zCzIWYg=
|
||||
github.com/rogpeppe/go-internal v1.3.0/go.mod h1:M8bDsm7K2OlrFYOpmOWEs/qY81heoFRclV5y23lUDJ4=
|
||||
github.com/rs/xid v1.2.1/go.mod h1:+uKXf+4Djp6Md1KODXJxgGQPKngRmWyn10oCKFzNHOQ=
|
||||
github.com/rs/zerolog v1.21.0/go.mod h1:ZPhntP/xmq1nnND05hhpAh2QMhSsA4UN3MGZ6O2J3hM=
|
||||
github.com/russross/blackfriday/v2 v2.0.1/go.mod h1:+Rmxgy9KzJVeS9/2gXHxylqXiyQDYRxCVz55jmeOWTM=
|
||||
github.com/ryanuber/columnize v0.0.0-20160712163229-9b3edd62028f/go.mod h1:sm1tb6uqfes/u+d4ooFouqFdy9/2g9QGwK3SQygK0Ts=
|
||||
github.com/samuel/go-zookeeper v0.0.0-20190923202752-2cc03de413da/go.mod h1:gi+0XIa01GRL2eRQVjQkKGqKF3SF9vZR/HnPullcV2E=
|
||||
github.com/sean-/seed v0.0.0-20170313163322-e2103e2c3529/go.mod h1:DxrIzT+xaE7yg65j358z/aeFdxmN0P9QXhEzd20vsDc=
|
||||
github.com/shurcooL/sanitized_anchor_name v1.0.0/go.mod h1:1NzhyTcUVG4SuEtjjoZeVRXNmyL/1OwPU0+IJeTBvfc=
|
||||
github.com/sirupsen/logrus v1.2.0/go.mod h1:LxeOpSwHxABJmUn/MG1IvRgCAasNZTLOkJPxbbu5VWo=
|
||||
github.com/sirupsen/logrus v1.4.2/go.mod h1:tLMulIdttU9McNUspp0xgXVQah82FyeX6MwdIuYE2rE=
|
||||
github.com/sirupsen/logrus v1.8.1/go.mod h1:yWOB1SBYBC5VeMP7gHvWumXLIWorT60ONWic61uBYv0=
|
||||
github.com/skratchdot/open-golang v0.0.0-20200116055534-eef842397966/go.mod h1:sUM3LWHvSMaG192sy56D9F7CNvL7jUJVXoqM1QKLnog=
|
||||
github.com/smartystreets/assertions v0.0.0-20180927180507-b2de0cb4f26d/go.mod h1:OnSkiWE9lh6wB0YB77sQom3nweQdgAjqCqsofrRNTgc=
|
||||
github.com/smartystreets/goconvey v1.6.4/go.mod h1:syvi0/a8iFYH4r/RixwvyeAJjdLS9QV7WQ/tjFTllLA=
|
||||
github.com/soheilhy/cmux v0.1.4/go.mod h1:IM3LyeVVIOuxMH7sFAkER9+bJ4dT7Ms6E4xg4kGIyLM=
|
||||
github.com/sony/gobreaker v0.4.1/go.mod h1:ZKptC7FHNvhBz7dN2LGjPVBz2sZJmc0/PkyDJOjmxWY=
|
||||
github.com/spaolacci/murmur3 v0.0.0-20180118202830-f09979ecbc72/go.mod h1:JwIasOWyU6f++ZhiEuf87xNszmSA2myDM2Kzu9HwQUA=
|
||||
github.com/spf13/afero v1.1.2/go.mod h1:j4pytiNVoe2o6bmDsKpLACNPDBIoEAkihy7loJ1B0CQ=
|
||||
github.com/spf13/cast v1.3.0/go.mod h1:Qx5cxh0v+4UWYiBimWS+eyWzqEqokIECu5etghLkUJE=
|
||||
github.com/spf13/cobra v0.0.3/go.mod h1:1l0Ry5zgKvJasoi3XT1TypsSe7PqH0Sj9dhYf7v3XqQ=
|
||||
github.com/spf13/cobra v1.1.3/go.mod h1:pGADOWyqRD/YMrPZigI/zbliZ2wVD/23d+is3pSWzOo=
|
||||
github.com/spf13/jwalterweatherman v1.0.0/go.mod h1:cQK4TGJAtQXfYWX+Ddv3mKDzgVb68N+wFjFa4jdeBTo=
|
||||
github.com/spf13/pflag v1.0.1/go.mod h1:DYY7MBk1bdzusC3SYhjObp+wFpr4gzcvqqNjLnInEg4=
|
||||
github.com/spf13/pflag v1.0.3/go.mod h1:DYY7MBk1bdzusC3SYhjObp+wFpr4gzcvqqNjLnInEg4=
|
||||
github.com/spf13/pflag v1.0.5/go.mod h1:McXfInJRrz4CZXVZOBLb0bTZqETkiAhM9Iw0y3An2Bg=
|
||||
github.com/spf13/viper v1.7.0/go.mod h1:8WkrPz2fc9jxqZNCJI/76HCieCp4Q8HaLFoCha5qpdg=
|
||||
github.com/streadway/amqp v0.0.0-20190404075320-75d898a42a94/go.mod h1:AZpEONHx3DKn8O/DFsRAY58/XVQiIPMTMB1SddzLXVw=
|
||||
github.com/streadway/amqp v0.0.0-20190827072141-edfb9018d271/go.mod h1:AZpEONHx3DKn8O/DFsRAY58/XVQiIPMTMB1SddzLXVw=
|
||||
github.com/streadway/handy v0.0.0-20190108123426-d5acb3125c2a/go.mod h1:qNTQ5P5JnDBl6z3cMAg/SywNDC5ABu5ApDIw6lUbRmI=
|
||||
github.com/stretchr/objx v0.1.0/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME=
|
||||
github.com/stretchr/objx v0.1.1/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME=
|
||||
github.com/stretchr/testify v1.2.2/go.mod h1:a8OnRcib4nhh0OaRAV+Yts87kKdq0PP7pXfy6kDkUVs=
|
||||
github.com/stretchr/testify v1.3.0/go.mod h1:M5WIy9Dh21IEIfnGCwXGc5bZfKNJtfHm1UVUgZn+9EI=
|
||||
github.com/stretchr/testify v1.4.0/go.mod h1:j7eGeouHqKxXV5pUuKE4zz7dFj8WfuZ+81PSLYec5m4=
|
||||
github.com/stretchr/testify v1.7.0/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/h/Wwjteg=
|
||||
github.com/stretchr/testify v1.8.4 h1:CcVxjf3Q8PM0mHUKJCdn+eZZtm5yQwehR5yeSVQQcUk=
|
||||
github.com/stretchr/testify v1.8.4/go.mod h1:sz/lmYIOXD/1dqDmKjjqLyZ2RngseejIcXlSw2iwfAo=
|
||||
gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405 h1:yhCVgyC4o1eVCa2tZl7eS0r+SDo693bJlVdllGtEeKM=
|
||||
github.com/subosito/gotenv v1.2.0/go.mod h1:N0PQaV/YGNqwC0u51sEeR/aUtSLEXKX9iv69rRypqCw=
|
||||
github.com/tmc/grpc-websocket-proxy v0.0.0-20170815181823-89b8d40f7ca8/go.mod h1:ncp9v5uamzpCO7NfCPTXjqaC+bZgJeR0sMTm6dMHP7U=
|
||||
github.com/tmc/grpc-websocket-proxy v0.0.0-20190109142713-0ad062ec5ee5/go.mod h1:ncp9v5uamzpCO7NfCPTXjqaC+bZgJeR0sMTm6dMHP7U=
|
||||
github.com/urfave/cli v1.20.0/go.mod h1:70zkFmudgCuE/ngEzBv17Jvp/497gISqfk5gWijbERA=
|
||||
github.com/urfave/cli v1.22.1/go.mod h1:Gos4lmkARVdJ6EkW0WaNv/tZAAMe9V7XWyB60NtXRu0=
|
||||
github.com/xiang90/probing v0.0.0-20190116061207-43a291ad63a2/go.mod h1:UETIi67q53MR2AWcXfiuqkDkRtnGDLqkBTpCHuJHxtU=
|
||||
github.com/yuin/goldmark v1.2.1/go.mod h1:3hX8gzYuyVAZsxl0MRgGTJEmQBFcNTphYh9decYSb74=
|
||||
github.com/yuin/goldmark v1.3.5/go.mod h1:mwnBkeHKe2W/ZEtQ+71ViKU8L12m81fl3OWwC1Zlc8k=
|
||||
go.etcd.io/bbolt v1.3.2/go.mod h1:IbVyRI1SCnLcuJnV2u8VeU0CEYM7e686BmAb1XKL+uU=
|
||||
go.etcd.io/bbolt v1.3.3/go.mod h1:IbVyRI1SCnLcuJnV2u8VeU0CEYM7e686BmAb1XKL+uU=
|
||||
go.etcd.io/etcd v0.0.0-20191023171146-3cf2f69b5738/go.mod h1:dnLIgRNXwCJa5e+c6mIZCrds/GIG4ncV9HhK5PX7jPg=
|
||||
go.opencensus.io v0.20.1/go.mod h1:6WKK9ahsWS3RSO+PY9ZHZUfv2irvY6gN279GOPZjmmk=
|
||||
go.opencensus.io v0.20.2/go.mod h1:6WKK9ahsWS3RSO+PY9ZHZUfv2irvY6gN279GOPZjmmk=
|
||||
go.opencensus.io v0.21.0/go.mod h1:mSImk1erAIZhrmZN+AvHh14ztQfjbGwt4TtuofqLduU=
|
||||
go.opencensus.io v0.22.0/go.mod h1:+kGneAE2xo2IficOXnaByMWTGM9T73dGwxeWcUqIpI8=
|
||||
go.opencensus.io v0.22.2/go.mod h1:yxeiOL68Rb0Xd1ddK5vPZ/oVn4vY4Ynel7k9FzqtOIw=
|
||||
go.opentelemetry.io/otel v0.20.0/go.mod h1:Y3ugLH2oa81t5QO+Lty+zXf8zC9L26ax4Nzoxm/dooo=
|
||||
go.opentelemetry.io/otel/metric v0.20.0/go.mod h1:598I5tYlH1vzBjn+BTuhzTCSb/9debfNp6R3s7Pr1eU=
|
||||
go.opentelemetry.io/otel/oteltest v0.20.0/go.mod h1:L7bgKf9ZB7qCwT9Up7i9/pn0PWIa9FqQ2IQ8LoxiGnw=
|
||||
go.opentelemetry.io/otel/sdk v0.20.0/go.mod h1:g/IcepuwNsoiX5Byy2nNV0ySUF1em498m7hBWC279Yc=
|
||||
go.opentelemetry.io/otel/trace v0.20.0/go.mod h1:6GjCW8zgDjwGHGa6GkyeB8+/5vjT16gUEi0Nf1iBdgw=
|
||||
go.uber.org/atomic v1.3.2/go.mod h1:gD2HeocX3+yG+ygLZcrzQJaqmWj9AIm7n08wl/qW/PE=
|
||||
go.uber.org/atomic v1.4.0/go.mod h1:gD2HeocX3+yG+ygLZcrzQJaqmWj9AIm7n08wl/qW/PE=
|
||||
go.uber.org/atomic v1.5.0/go.mod h1:sABNBOSYdrvTF6hTgEIbc7YasKWGhgEQZyfxyTvoXHQ=
|
||||
go.uber.org/atomic v1.6.0/go.mod h1:sABNBOSYdrvTF6hTgEIbc7YasKWGhgEQZyfxyTvoXHQ=
|
||||
go.uber.org/atomic v1.7.0/go.mod h1:fEN4uk6kAWBTFdckzkM89CLk9XfWZrxpCo0nPH17wJc=
|
||||
go.uber.org/multierr v1.1.0/go.mod h1:wR5kodmAFQ0UK8QlbwjlSNy0Z68gJhDJUG5sjR94q/0=
|
||||
go.uber.org/multierr v1.3.0/go.mod h1:VgVr7evmIr6uPjLBxg28wmKNXyqE9akIJ5XnfpiKl+4=
|
||||
go.uber.org/multierr v1.5.0/go.mod h1:FeouvMocqHpRaaGuG9EjoKcStLC43Zu/fmqdUMPcKYU=
|
||||
go.uber.org/multierr v1.6.0/go.mod h1:cdWPpRnG4AhwMwsgIHip0KRBQjJy5kYEpYjJxpXp9iU=
|
||||
go.uber.org/tools v0.0.0-20190618225709-2cfd321de3ee/go.mod h1:vJERXedbb3MVM5f9Ejo0C68/HhF8uaILCdgjnY+goOA=
|
||||
go.uber.org/zap v1.10.0/go.mod h1:vwi/ZaCAaUcBkycHslxD9B2zi4UTXhF60s6SWpuDF0Q=
|
||||
go.uber.org/zap v1.13.0/go.mod h1:zwrFLgMcdUuIBviXEYEH1YKNaOBnKXsx2IPda5bBwHM=
|
||||
go.uber.org/zap v1.16.0/go.mod h1:MA8QOfq0BHJwdXa996Y4dYkAqRKB8/1K1QMMZVaNZjQ=
|
||||
golang.org/x/crypto v0.0.0-20180904163835-0709b304e793/go.mod h1:6SG95UA2DQfeDnfUPMdvaQW0Q7yPrPDi9nlGo2tz2b4=
|
||||
golang.org/x/crypto v0.0.0-20181029021203-45a5f77698d3/go.mod h1:6SG95UA2DQfeDnfUPMdvaQW0Q7yPrPDi9nlGo2tz2b4=
|
||||
golang.org/x/crypto v0.0.0-20190308221718-c2843e01d9a2/go.mod h1:djNgcEr1/C05ACkg1iLfiJU5Ep61QUkGW8qpdssI0+w=
|
||||
golang.org/x/crypto v0.0.0-20190510104115-cbcb75029529/go.mod h1:yigFU9vqHzYiE8UmvKecakEJjdnWj3jj499lnFckfCI=
|
||||
golang.org/x/crypto v0.0.0-20190605123033-f99c8df09eb5/go.mod h1:yigFU9vqHzYiE8UmvKecakEJjdnWj3jj499lnFckfCI=
|
||||
golang.org/x/crypto v0.0.0-20190701094942-4def268fd1a4/go.mod h1:yigFU9vqHzYiE8UmvKecakEJjdnWj3jj499lnFckfCI=
|
||||
golang.org/x/crypto v0.0.0-20191011191535-87dc89f01550/go.mod h1:yigFU9vqHzYiE8UmvKecakEJjdnWj3jj499lnFckfCI=
|
||||
golang.org/x/crypto v0.0.0-20200622213623-75b288015ac9/go.mod h1:LzIPMQfyMNhhGPhUkYOs5KpL4U8rLKemX1yGLhDgUto=
|
||||
golang.org/x/exp v0.0.0-20190121172915-509febef88a4/go.mod h1:CJ0aWSM057203Lf6IL+f9T1iT9GByDxfZKAQTCR3kQA=
|
||||
golang.org/x/exp v0.0.0-20190306152737-a1d7652674e8/go.mod h1:CJ0aWSM057203Lf6IL+f9T1iT9GByDxfZKAQTCR3kQA=
|
||||
golang.org/x/exp v0.0.0-20190510132918-efd6b22b2522/go.mod h1:ZjyILWgesfNpC6sMxTJOJm9Kp84zZh5NQWvqDGG3Qr8=
|
||||
golang.org/x/exp v0.0.0-20190731235908-ec7cb31e5a56/go.mod h1:JhuoJpWY28nO4Vef9tZUw9qufEGTyX1+7lmHxV5q5G4=
|
||||
golang.org/x/exp v0.0.0-20190829153037-c13cbed26979/go.mod h1:86+5VVa7VpoJ4kLfm080zCjGlMRFzhUhsZKEZO7MGek=
|
||||
golang.org/x/exp v0.0.0-20191030013958-a1ab85dbe136/go.mod h1:JXzH8nQsPlswgeRAPE3MuO9GYsAcnJvJ4vnMwN/5qkY=
|
||||
golang.org/x/exp v0.0.0-20210729172720-737cce5152fc/go.mod h1:DVyR6MI7P4kEQgvZJSj1fQGrWIi2RzIrfYWycwheUAc=
|
||||
golang.org/x/image v0.0.0-20190227222117-0694c2d4d067/go.mod h1:kZ7UVZpmo3dzQBMxlp+ypCbDeSB+sBbTgSJuh5dn5js=
|
||||
golang.org/x/image v0.0.0-20190703141733-d6a02ce849c9/go.mod h1:FeLwcggjj3mMvU+oOTbSwawSJRM1uh48EjtB4UJZlP0=
|
||||
golang.org/x/image v0.0.0-20190802002840-cff245a6509b/go.mod h1:FeLwcggjj3mMvU+oOTbSwawSJRM1uh48EjtB4UJZlP0=
|
||||
golang.org/x/image v0.0.0-20210220032944-ac19c3e999fb/go.mod h1:FeLwcggjj3mMvU+oOTbSwawSJRM1uh48EjtB4UJZlP0=
|
||||
golang.org/x/image v0.0.0-20210628002857-a66eb6448b8d/go.mod h1:023OzeP/+EPmXeapQh35lcL3II3LrY8Ic+EFFKVhULM=
|
||||
golang.org/x/lint v0.0.0-20181026193005-c67002cb31c3/go.mod h1:UVdnD1Gm6xHRNCYTkRU2/jEulfH38KcIWyp/GAMgvoE=
|
||||
golang.org/x/lint v0.0.0-20190227174305-5b3e6a55c961/go.mod h1:wehouNa3lNwaWXcvxsM5YxQ5yQlVC4a0KAMCusXpPoU=
|
||||
golang.org/x/lint v0.0.0-20190301231843-5614ed5bae6f/go.mod h1:UVdnD1Gm6xHRNCYTkRU2/jEulfH38KcIWyp/GAMgvoE=
|
||||
golang.org/x/lint v0.0.0-20190313153728-d0100b6bd8b3/go.mod h1:6SW0HCj/g11FgYtHlgUYUwCkIfeOF89ocIRzGO/8vkc=
|
||||
golang.org/x/lint v0.0.0-20190409202823-959b441ac422/go.mod h1:6SW0HCj/g11FgYtHlgUYUwCkIfeOF89ocIRzGO/8vkc=
|
||||
golang.org/x/lint v0.0.0-20190909230951-414d861bb4ac/go.mod h1:6SW0HCj/g11FgYtHlgUYUwCkIfeOF89ocIRzGO/8vkc=
|
||||
golang.org/x/lint v0.0.0-20190930215403-16217165b5de/go.mod h1:6SW0HCj/g11FgYtHlgUYUwCkIfeOF89ocIRzGO/8vkc=
|
||||
golang.org/x/mobile v0.0.0-20190312151609-d3739f865fa6/go.mod h1:z+o9i4GpDbdi3rU15maQ/Ox0txvL9dWGYEHz965HBQE=
|
||||
golang.org/x/mobile v0.0.0-20190415191353-3e0bab5405d6/go.mod h1:E/iHnbuqvinMTCcRqshq8CkpyQDoeVncDDYHnLhea+o=
|
||||
golang.org/x/mobile v0.0.0-20190719004257-d2bd2a29d028/go.mod h1:E/iHnbuqvinMTCcRqshq8CkpyQDoeVncDDYHnLhea+o=
|
||||
golang.org/x/mobile v0.0.0-20201217150744-e6ae53a27f4f/go.mod h1:skQtrUTUwhdJvXM/2KKJzY8pDgNr9I/FOMqDVRPBUS4=
|
||||
golang.org/x/mobile v0.0.0-20210716004757-34ab1303b554/go.mod h1:jFTmtFYCV0MFtXBU+J5V/+5AUeVS0ON/0WkE/KSrl6E=
|
||||
golang.org/x/mod v0.0.0-20190513183733-4bf6d317e70e/go.mod h1:mXi4GBBbnImb6dmsKGUJ2LatrhH/nqhxcFungHvyanc=
|
||||
golang.org/x/mod v0.1.0/go.mod h1:0QHyrYULN0/3qlju5TqG8bIK38QM8yzMo5ekMj3DlcY=
|
||||
golang.org/x/mod v0.1.1-0.20191105210325-c90efee705ee/go.mod h1:QqPTAvyqsEbceGzBzNggFXnrqF1CaUcvgkdR5Ot7KZg=
|
||||
golang.org/x/mod v0.1.1-0.20191209134235-331c550502dd/go.mod h1:s0Qsj1ACt9ePp/hMypM3fl4fZqREWJwdYDEqhRiZZUA=
|
||||
golang.org/x/mod v0.3.0/go.mod h1:s0Qsj1ACt9ePp/hMypM3fl4fZqREWJwdYDEqhRiZZUA=
|
||||
golang.org/x/mod v0.4.2/go.mod h1:s0Qsj1ACt9ePp/hMypM3fl4fZqREWJwdYDEqhRiZZUA=
|
||||
golang.org/x/net v0.0.0-20180724234803-3673e40ba225/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4=
|
||||
golang.org/x/net v0.0.0-20180826012351-8a410e7b638d/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4=
|
||||
golang.org/x/net v0.0.0-20180906233101-161cd47e91fd/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4=
|
||||
golang.org/x/net v0.0.0-20181023162649-9b4f9f5ad519/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4=
|
||||
golang.org/x/net v0.0.0-20181114220301-adae6a3d119a/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4=
|
||||
golang.org/x/net v0.0.0-20181201002055-351d144fa1fc/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4=
|
||||
golang.org/x/net v0.0.0-20181220203305-927f97764cc3/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4=
|
||||
golang.org/x/net v0.0.0-20190108225652-1e06a53dbb7e/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4=
|
||||
golang.org/x/net v0.0.0-20190125091013-d26f9f9a57f3/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4=
|
||||
golang.org/x/net v0.0.0-20190213061140-3a22650c66bd/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4=
|
||||
golang.org/x/net v0.0.0-20190311183353-d8887717615a/go.mod h1:t9HGtf8HONx5eT2rtn7q6eTqICYqUVnKs3thJo3Qplg=
|
||||
golang.org/x/net v0.0.0-20190404232315-eb5bcb51f2a3/go.mod h1:t9HGtf8HONx5eT2rtn7q6eTqICYqUVnKs3thJo3Qplg=
|
||||
golang.org/x/net v0.0.0-20190501004415-9ce7a6920f09/go.mod h1:t9HGtf8HONx5eT2rtn7q6eTqICYqUVnKs3thJo3Qplg=
|
||||
golang.org/x/net v0.0.0-20190503192946-f4e77d36d62c/go.mod h1:t9HGtf8HONx5eT2rtn7q6eTqICYqUVnKs3thJo3Qplg=
|
||||
golang.org/x/net v0.0.0-20190603091049-60506f45cf65/go.mod h1:HSz+uSET+XFnRR8LxR5pz3Of3rY3CfYBVs4xY44aLks=
|
||||
golang.org/x/net v0.0.0-20190613194153-d28f0bde5980/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s=
|
||||
golang.org/x/net v0.0.0-20190620200207-3b0461eec859/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s=
|
||||
golang.org/x/net v0.0.0-20190813141303-74dc4d7220e7/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s=
|
||||
golang.org/x/net v0.0.0-20201021035429-f5854403a974/go.mod h1:sp8m0HH+o8qH0wwXwYZr8TS3Oi6o0r6Gce1SSxlDquU=
|
||||
golang.org/x/net v0.0.0-20210405180319-a5a99cb37ef4/go.mod h1:p54w0d4576C0XHj96bSt6lcn1PtDYWL6XObtHCRCNQM=
|
||||
golang.org/x/oauth2 v0.0.0-20180821212333-d2e6202438be/go.mod h1:N/0e6XlmueqKjAGxoOufVs8QHGRruUQn6yWY3a++T0U=
|
||||
golang.org/x/oauth2 v0.0.0-20190226205417-e64efc72b421/go.mod h1:gOpvHmFTYa4IltrdGE7lF6nIHvwfUNPOp7c8zoXwtLw=
|
||||
golang.org/x/oauth2 v0.0.0-20190604053449-0f29369cfe45/go.mod h1:gOpvHmFTYa4IltrdGE7lF6nIHvwfUNPOp7c8zoXwtLw=
|
||||
golang.org/x/sync v0.0.0-20180314180146-1d60e4601c6f/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
|
||||
golang.org/x/sync v0.0.0-20181108010431-42b317875d0f/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
|
||||
golang.org/x/sync v0.0.0-20181221193216-37e7f081c4d4/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
|
||||
golang.org/x/sync v0.0.0-20190227155943-e225da77a7e6/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
|
||||
golang.org/x/sync v0.0.0-20190423024810-112230192c58/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
|
||||
golang.org/x/sync v0.0.0-20190911185100-cd5d95a43a6e/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
|
||||
golang.org/x/sync v0.0.0-20201020160332-67f06af15bc9/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
|
||||
golang.org/x/sync v0.0.0-20210220032951-036812b2e83c/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
|
||||
golang.org/x/sys v0.0.0-20180823144017-11551d06cbcc/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=
|
||||
golang.org/x/sys v0.0.0-20180830151530-49385e6e1522/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=
|
||||
golang.org/x/sys v0.0.0-20180905080454-ebe1bf3edb33/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=
|
||||
golang.org/x/sys v0.0.0-20180909124046-d0be0721c37e/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=
|
||||
golang.org/x/sys v0.0.0-20181026203630-95b1ffbd15a5/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=
|
||||
golang.org/x/sys v0.0.0-20181107165924-66b7b1311ac8/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=
|
||||
golang.org/x/sys v0.0.0-20181116152217-5ac8a444bdc5/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=
|
||||
golang.org/x/sys v0.0.0-20181122145206-62eef0e2fa9b/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=
|
||||
golang.org/x/sys v0.0.0-20190215142949-d0b11bdaac8a/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=
|
||||
golang.org/x/sys v0.0.0-20190312061237-fead79001313/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
||||
golang.org/x/sys v0.0.0-20190412213103-97732733099d/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
||||
golang.org/x/sys v0.0.0-20190422165155-953cdadca894/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
||||
golang.org/x/sys v0.0.0-20190429190828-d89cdac9e872/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
||||
golang.org/x/sys v0.0.0-20190502145724-3ef323f4f1fd/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
||||
golang.org/x/sys v0.0.0-20190507160741-ecd444e8653b/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
||||
golang.org/x/sys v0.0.0-20190606165138-5da285871e9c/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
||||
golang.org/x/sys v0.0.0-20190624142023-c5567b49c5d0/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
||||
golang.org/x/sys v0.0.0-20190726091711-fc99dfbffb4e/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
||||
golang.org/x/sys v0.0.0-20190826190057-c7b8b68b1456/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
||||
golang.org/x/sys v0.0.0-20190904154756-749cb33beabd/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
||||
golang.org/x/sys v0.0.0-20191026070338-33540a1f6037/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
||||
golang.org/x/sys v0.0.0-20191220142924-d4481acd189f/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
||||
golang.org/x/sys v0.0.0-20200509044756-6aff5f38e54f/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
||||
golang.org/x/sys v0.0.0-20200930185726-fdedc70b468f/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
||||
golang.org/x/sys v0.0.0-20201119102817-f84b799fce68/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
||||
golang.org/x/sys v0.0.0-20210119212857-b64e53b001e4/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
||||
golang.org/x/sys v0.0.0-20210330210617-4fbd30eecc44/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
||||
golang.org/x/sys v0.0.0-20210510120138-977fb7262007/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
|
||||
golang.org/x/sys v0.0.0-20210630005230-0f9fa26af87c/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
|
||||
golang.org/x/sys v0.16.0 h1:xWw16ngr6ZMtmxDyKyIgsE93KNKz5HKmMa3b8ALHidU=
|
||||
golang.org/x/sys v0.16.0/go.mod h1:/VUhepiaJMQUp4+oa/7Zr1D23ma6VTLIYjOOTFZPUcA=
|
||||
golang.org/x/term v0.0.0-20201126162022-7de9c90e9dd1/go.mod h1:bj7SfCRtBDWHUb9snDiAeCFNEtKQo2Wmx5Cou7ajbmo=
|
||||
golang.org/x/term v0.16.0 h1:m+B6fahuftsE9qjo0VWp2FW0mB3MTJvR0BaMQrq0pmE=
|
||||
golang.org/x/term v0.16.0/go.mod h1:yn7UURbUtPyrVJPGPq404EukNFxcm/foM+bV/bfcDsY=
|
||||
golang.org/x/text v0.3.0/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ=
|
||||
golang.org/x/text v0.3.1-0.20180807135948-17ff2d5776d2/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ=
|
||||
golang.org/x/text v0.3.2/go.mod h1:bEr9sfX3Q8Zfm5fL9x+3itogRgK3+ptLWKqgva+5dAk=
|
||||
golang.org/x/text v0.3.3/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ=
|
||||
golang.org/x/text v0.3.6/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ=
|
||||
golang.org/x/time v0.0.0-20180412165947-fbb02b2291d2/go.mod h1:tRJNPiyCQ0inRvYxbN9jk5I+vvW/OXSQhTDSoE431IQ=
|
||||
golang.org/x/time v0.0.0-20181108054448-85acf8d2951c/go.mod h1:tRJNPiyCQ0inRvYxbN9jk5I+vvW/OXSQhTDSoE431IQ=
|
||||
golang.org/x/time v0.0.0-20190308202827-9d24e82272b4/go.mod h1:tRJNPiyCQ0inRvYxbN9jk5I+vvW/OXSQhTDSoE431IQ=
|
||||
golang.org/x/time v0.0.0-20191024005414-555d28b269f0/go.mod h1:tRJNPiyCQ0inRvYxbN9jk5I+vvW/OXSQhTDSoE431IQ=
|
||||
golang.org/x/tools v0.0.0-20180221164845-07fd8470d635/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ=
|
||||
golang.org/x/tools v0.0.0-20180828015842-6cd1fcedba52/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ=
|
||||
golang.org/x/tools v0.0.0-20180917221912-90fa682c2a6e/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ=
|
||||
golang.org/x/tools v0.0.0-20190114222345-bf090417da8b/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ=
|
||||
golang.org/x/tools v0.0.0-20190226205152-f727befe758c/go.mod h1:9Yl7xja0Znq3iFh3HoIrodX9oNMXvdceNzlUR8zjMvY=
|
||||
golang.org/x/tools v0.0.0-20190311212946-11955173bddd/go.mod h1:LCzVGOaR6xXOjkQ3onu1FJEFr0SW1gC7cKk1uF8kGRs=
|
||||
golang.org/x/tools v0.0.0-20190312151545-0bb0c0a6e846/go.mod h1:LCzVGOaR6xXOjkQ3onu1FJEFr0SW1gC7cKk1uF8kGRs=
|
||||
golang.org/x/tools v0.0.0-20190312170243-e65039ee4138/go.mod h1:LCzVGOaR6xXOjkQ3onu1FJEFr0SW1gC7cKk1uF8kGRs=
|
||||
golang.org/x/tools v0.0.0-20190328211700-ab21143f2384/go.mod h1:LCzVGOaR6xXOjkQ3onu1FJEFr0SW1gC7cKk1uF8kGRs=
|
||||
golang.org/x/tools v0.0.0-20190425150028-36563e24a262/go.mod h1:RgjU9mgBXZiqYHBnxXauZ1Gv1EHHAz9KjViQ78xBX0Q=
|
||||
golang.org/x/tools v0.0.0-20190506145303-2d16b83fe98c/go.mod h1:RgjU9mgBXZiqYHBnxXauZ1Gv1EHHAz9KjViQ78xBX0Q=
|
||||
golang.org/x/tools v0.0.0-20190524140312-2c0ae7006135/go.mod h1:RgjU9mgBXZiqYHBnxXauZ1Gv1EHHAz9KjViQ78xBX0Q=
|
||||
golang.org/x/tools v0.0.0-20190606124116-d0a3d012864b/go.mod h1:/rFqwRUd4F7ZHNgwSSTFct+R/Kf4OFW1sUzUTQQTgfc=
|
||||
golang.org/x/tools v0.0.0-20190621195816-6e04913cbbac/go.mod h1:/rFqwRUd4F7ZHNgwSSTFct+R/Kf4OFW1sUzUTQQTgfc=
|
||||
golang.org/x/tools v0.0.0-20190628153133-6cdbf07be9d0/go.mod h1:/rFqwRUd4F7ZHNgwSSTFct+R/Kf4OFW1sUzUTQQTgfc=
|
||||
golang.org/x/tools v0.0.0-20190816200558-6889da9d5479/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo=
|
||||
golang.org/x/tools v0.0.0-20190911174233-4f2ddba30aff/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo=
|
||||
golang.org/x/tools v0.0.0-20191012152004-8de300cfc20a/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo=
|
||||
golang.org/x/tools v0.0.0-20191029041327-9cc4af7d6b2c/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo=
|
||||
golang.org/x/tools v0.0.0-20191029190741-b9c20aec41a5/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo=
|
||||
golang.org/x/tools v0.0.0-20191112195655-aa38f8e97acc/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo=
|
||||
golang.org/x/tools v0.0.0-20191119224855-298f0cb1881e/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo=
|
||||
golang.org/x/tools v0.0.0-20200103221440-774c71fcf114/go.mod h1:TB2adYChydJhpapKDTa4BR/hXlZSLoq2Wpct/0txZ28=
|
||||
golang.org/x/tools v0.0.0-20200117012304-6edc0a871e69/go.mod h1:TB2adYChydJhpapKDTa4BR/hXlZSLoq2Wpct/0txZ28=
|
||||
golang.org/x/tools v0.1.0/go.mod h1:xkSsbof2nBLbhDlRMhhhyNLN/zl3eTqcnHD5viDpcZ0=
|
||||
golang.org/x/tools v0.1.2/go.mod h1:o0xws9oXOQQZyjljx8fwUC0k7L1pTE6eaCbjGeHmOkk=
|
||||
golang.org/x/xerrors v0.0.0-20190717185122-a985d3407aa7/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0=
|
||||
golang.org/x/xerrors v0.0.0-20191011141410-1b5146add898/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0=
|
||||
golang.org/x/xerrors v0.0.0-20191204190536-9bdfabe68543/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0=
|
||||
golang.org/x/xerrors v0.0.0-20200804184101-5ec99f83aff1/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0=
|
||||
google.golang.org/api v0.3.1/go.mod h1:6wY9I6uQWHQ8EM57III9mq/AjF+i8G65rmVagqKMtkk=
|
||||
google.golang.org/api v0.4.0/go.mod h1:8k5glujaEP+g9n7WNsDg8QP6cUVNI86fCNMcbazEtwE=
|
||||
google.golang.org/api v0.7.0/go.mod h1:WtwebWUNSVBH/HAw79HIFXZNqEvBhG+Ra+ax0hx3E3M=
|
||||
google.golang.org/api v0.8.0/go.mod h1:o4eAsZoiT+ibD93RtjEohWalFOjRDx6CVaqeizhEnKg=
|
||||
google.golang.org/api v0.9.0/go.mod h1:o4eAsZoiT+ibD93RtjEohWalFOjRDx6CVaqeizhEnKg=
|
||||
google.golang.org/api v0.13.0/go.mod h1:iLdEw5Ide6rF15KTC1Kkl0iskquN2gFfn9o9XIsbkAI=
|
||||
google.golang.org/appengine v1.1.0/go.mod h1:EbEs0AVv82hx2wNQdGPgUI5lhzA/G0D9YwlJXL52JkM=
|
||||
google.golang.org/appengine v1.2.0/go.mod h1:xpcJRLb0r/rnEns0DIKYYv+WjYCduHsrkT7/EB5XEv4=
|
||||
google.golang.org/appengine v1.4.0/go.mod h1:xpcJRLb0r/rnEns0DIKYYv+WjYCduHsrkT7/EB5XEv4=
|
||||
google.golang.org/appengine v1.5.0/go.mod h1:xpcJRLb0r/rnEns0DIKYYv+WjYCduHsrkT7/EB5XEv4=
|
||||
google.golang.org/appengine v1.6.1/go.mod h1:i06prIuMbXzDqacNJfV5OdTW448YApPu5ww/cMBSeb0=
|
||||
google.golang.org/genproto v0.0.0-20180817151627-c66870c02cf8/go.mod h1:JiN7NxoALGmiZfu7CAH4rXhgtRTLTxftemlI0sWmxmc=
|
||||
google.golang.org/genproto v0.0.0-20190307195333-5fe7a883aa19/go.mod h1:VzzqZJRnGkLBvHegQrXjBqPurQTc5/KpmUdxsrq26oE=
|
||||
google.golang.org/genproto v0.0.0-20190418145605-e7d98fc518a7/go.mod h1:VzzqZJRnGkLBvHegQrXjBqPurQTc5/KpmUdxsrq26oE=
|
||||
google.golang.org/genproto v0.0.0-20190425155659-357c62f0e4bb/go.mod h1:VzzqZJRnGkLBvHegQrXjBqPurQTc5/KpmUdxsrq26oE=
|
||||
google.golang.org/genproto v0.0.0-20190502173448-54afdca5d873/go.mod h1:VzzqZJRnGkLBvHegQrXjBqPurQTc5/KpmUdxsrq26oE=
|
||||
google.golang.org/genproto v0.0.0-20190530194941-fb225487d101/go.mod h1:z3L6/3dTEVtUr6QSP8miRzeRqwQOioJ9I66odjN4I7s=
|
||||
google.golang.org/genproto v0.0.0-20190801165951-fa694d86fc64/go.mod h1:DMBHOl98Agz4BDEuKkezgsaosCRResVns1a3J2ZsMNc=
|
||||
google.golang.org/genproto v0.0.0-20190819201941-24fa4b261c55/go.mod h1:DMBHOl98Agz4BDEuKkezgsaosCRResVns1a3J2ZsMNc=
|
||||
google.golang.org/genproto v0.0.0-20190911173649-1774047e7e51/go.mod h1:IbNlFCBrqXvoKpeg0TB2l7cyZUmoaFKYIwrEpbDKLA8=
|
||||
google.golang.org/genproto v0.0.0-20191108220845-16a3f7862a1a/go.mod h1:n3cpQtvxv34hfy77yVDNjmbRyujviMdxYliBSkLhpCc=
|
||||
google.golang.org/grpc v1.17.0/go.mod h1:6QZJwpn2B+Zp71q/5VxRsJ6NXXVCE5NRUHRo+f3cWCs=
|
||||
google.golang.org/grpc v1.19.0/go.mod h1:mqu4LbDTu4XGKhr4mRzUsmM4RtVoemTSY81AxZiDr8c=
|
||||
google.golang.org/grpc v1.20.0/go.mod h1:chYK+tFQF0nDUGJgXMSgLCQk3phJEuONr2DCgLDdAQM=
|
||||
google.golang.org/grpc v1.20.1/go.mod h1:10oTOabMzJvdu6/UiuZezV6QK5dSlG84ov/aaiqXj38=
|
||||
google.golang.org/grpc v1.21.0/go.mod h1:oYelfM1adQP15Ek0mdvEgi9Df8B9CZIaU1084ijfRaM=
|
||||
google.golang.org/grpc v1.21.1/go.mod h1:oYelfM1adQP15Ek0mdvEgi9Df8B9CZIaU1084ijfRaM=
|
||||
google.golang.org/grpc v1.22.1/go.mod h1:Y5yQAOtifL1yxbo5wqy6BxZv8vAUGQwXBOALyacEbxg=
|
||||
google.golang.org/grpc v1.23.0/go.mod h1:Y5yQAOtifL1yxbo5wqy6BxZv8vAUGQwXBOALyacEbxg=
|
||||
google.golang.org/grpc v1.23.1/go.mod h1:Y5yQAOtifL1yxbo5wqy6BxZv8vAUGQwXBOALyacEbxg=
|
||||
google.golang.org/grpc v1.26.0/go.mod h1:qbnxyOmOxrQa7FizSgH+ReBfzJrCY1pSN7KXBS8abTk=
|
||||
gopkg.in/Knetic/govaluate.v3 v3.0.0/go.mod h1:csKLBORsPbafmSCGTEh3U7Ozmsuq8ZSIlKk1bcqph0E=
|
||||
gopkg.in/alecthomas/kingpin.v2 v2.2.6/go.mod h1:FMv+mEhP44yOT+4EoQTLFTRgOQ1FBLkstjWtayDeSgw=
|
||||
gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0=
|
||||
gopkg.in/check.v1 v1.0.0-20180628173108-788fd7840127/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0=
|
||||
gopkg.in/check.v1 v1.0.0-20201130134442-10cb98267c6c h1:Hei/4ADfdWqJk1ZMxUNpqntNwaWcugrBjAiHlqqRiVk=
|
||||
gopkg.in/check.v1 v1.0.0-20201130134442-10cb98267c6c/go.mod h1:JHkPIbrfpd72SG/EVd6muEfDQjcINNoR0C8j2r3qZ4Q=
|
||||
gopkg.in/cheggaaa/pb.v1 v1.0.25/go.mod h1:V/YB90LKu/1FcN3WVnfiiE5oMCibMjukxqG/qStrOgw=
|
||||
gopkg.in/errgo.v2 v2.1.0/go.mod h1:hNsd1EY+bozCKY1Ytp96fpM3vjJbqLJn88ws8XvfDNI=
|
||||
gopkg.in/fsnotify.v1 v1.4.7/go.mod h1:Tz8NjZHkW78fSQdbUxIjBTcgA1z1m8ZHf0WmKUhAMys=
|
||||
gopkg.in/gcfg.v1 v1.2.3/go.mod h1:yesOnuUOFQAhST5vPY4nbZsb/huCgGGXlipJsBn0b3o=
|
||||
gopkg.in/ini.v1 v1.51.0/go.mod h1:pNLf8WUiyNEtQjuu5G5vTm06TEv9tsIgeAvK8hOrP4k=
|
||||
gopkg.in/resty.v1 v1.12.0/go.mod h1:mDo4pnntr5jdWRML875a/NmxYqAlA73dVijT2AXvQQo=
|
||||
gopkg.in/tomb.v1 v1.0.0-20141024135613-dd632973f1e7/go.mod h1:dt/ZhP58zS4L8KSrWDmTeBkI65Dw0HsyUHuEVlX15mw=
|
||||
gopkg.in/warnings.v0 v0.1.2/go.mod h1:jksf8JmL6Qr/oQM2OXTHunEvvTAsrWBLb6OOjuVWRNI=
|
||||
gopkg.in/yaml.v2 v2.0.0-20170812160011-eb3733d160e7/go.mod h1:JAlM8MvJe8wmxCU4Bli9HhUf9+ttbYbLASfIpnQbh74=
|
||||
gopkg.in/yaml.v2 v2.2.1/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI=
|
||||
gopkg.in/yaml.v2 v2.2.2/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI=
|
||||
gopkg.in/yaml.v2 v2.2.4/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI=
|
||||
gopkg.in/yaml.v2 v2.4.0/go.mod h1:RDklbk79AGWmwhnvt/jBztapEOGDOx6ZbXqjP6csGnQ=
|
||||
gopkg.in/yaml.v3 v3.0.0-20200313102051-9f266ea9e77c/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM=
|
||||
gopkg.in/yaml.v3 v3.0.1 h1:fxVm/GzAzEWqLHuvctI91KS9hhNmmWOoWu0XTYJS7CA=
|
||||
gopkg.in/yaml.v3 v3.0.1/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM=
|
||||
honnef.co/go/tools v0.0.0-20180728063816-88497007e858/go.mod h1:rf3lG4BRIbNafJWhAfAdb/ePZxsR/4RtNHQocxwk9r4=
|
||||
honnef.co/go/tools v0.0.0-20190102054323-c2f93a96b099/go.mod h1:rf3lG4BRIbNafJWhAfAdb/ePZxsR/4RtNHQocxwk9r4=
|
||||
honnef.co/go/tools v0.0.0-20190106161140-3f1c8253044a/go.mod h1:rf3lG4BRIbNafJWhAfAdb/ePZxsR/4RtNHQocxwk9r4=
|
||||
honnef.co/go/tools v0.0.0-20190418001031-e561f6794a2a/go.mod h1:rf3lG4BRIbNafJWhAfAdb/ePZxsR/4RtNHQocxwk9r4=
|
||||
honnef.co/go/tools v0.0.0-20190523083050-ea95bdfd59fc/go.mod h1:rf3lG4BRIbNafJWhAfAdb/ePZxsR/4RtNHQocxwk9r4=
|
||||
honnef.co/go/tools v0.0.1-2019.2.3/go.mod h1:a3bituU0lyd329TUQxRnasdCoJDkEUEAqEt0JzvZhAg=
|
||||
honnef.co/go/tools v0.1.3/go.mod h1:NgwopIslSNH47DimFoV78dnkksY2EFtX0ajyb3K/las=
|
||||
mvdan.cc/xurls v1.1.0/go.mod h1:TNWuhvo+IqbUCmtUIb/3LJSQdrzel8loVpgFm0HikbI=
|
||||
rsc.io/binaryregexp v0.2.0/go.mod h1:qTv7/COck+e2FymRvadv62gMdZztPaShugOCi3I+8D8=
|
||||
sigs.k8s.io/yaml v1.1.0/go.mod h1:UJmg0vDUVViEyp3mgSv9WPwZCDxu4rQW1olrI1uml+o=
|
||||
sourcegraph.com/sourcegraph/appdash v0.0.0-20190731080439-ebfcffb1b5c0/go.mod h1:hI742Nqp5OhwiqlzhgfbWU4mW4yO10fP+LoT9WOswdU=
|
||||
|
32
line.go
32
line.go
@ -1,32 +0,0 @@
|
||||
package voidterm
|
||||
|
||||
type Line struct {
|
||||
wrapped bool
|
||||
cells []Cell
|
||||
}
|
||||
|
||||
func LineFromRunes(runes []rune, style CellAttributes) Line {
|
||||
l := make(Line, len(runes))
|
||||
for i, r := range runes {
|
||||
l[i] = Cell{
|
||||
r: r,
|
||||
s: style,
|
||||
}
|
||||
}
|
||||
return l
|
||||
}
|
||||
|
||||
func (l Line) Wrap(width uint64) WrappedLine {
|
||||
if uint64(len(l)) <= width {
|
||||
return WrappedLine{l}
|
||||
}
|
||||
a := l
|
||||
w := make(WrappedLine, 0, 1+uint64(len(l)-1)/width)
|
||||
for uint64(len(a)) > width {
|
||||
w = append(w, a[:width])
|
||||
a = a[width:]
|
||||
}
|
||||
return w
|
||||
}
|
||||
|
||||
type WrappedLine []Line
|
23
line_test.go
23
line_test.go
@ -1,23 +0,0 @@
|
||||
package voidterm
|
||||
|
||||
import (
|
||||
"github.com/stretchr/testify/assert"
|
||||
"testing"
|
||||
)
|
||||
|
||||
func TestLine_Wrap(t *testing.T) {
|
||||
t.Run("too wide", func(t *testing.T) {
|
||||
l := LineFromRunes([]rune("Hello world!"), CellAttributes{}).Wrap(16)
|
||||
assert.Equal(t, WrappedLine{
|
||||
LineFromRunes([]rune("Hello world!"), CellAttributes{}),
|
||||
}, l)
|
||||
})
|
||||
t.Run("too thin", func(t *testing.T) {
|
||||
l := LineFromRunes([]rune("Hello world!"), CellAttributes{}).Wrap(4)
|
||||
assert.Equal(t, WrappedLine{
|
||||
LineFromRunes([]rune("Hell"), CellAttributes{}),
|
||||
LineFromRunes([]rune("o wo"), CellAttributes{}),
|
||||
LineFromRunes([]rune("rld!"), CellAttributes{}),
|
||||
}, l)
|
||||
})
|
||||
}
|
@ -1,5 +0,0 @@
|
||||
package voidterm
|
||||
|
||||
type Position struct {
|
||||
X, Y int
|
||||
}
|
109
termutil/ansi.go
Normal file
109
termutil/ansi.go
Normal file
@ -0,0 +1,109 @@
|
||||
package termutil
|
||||
|
||||
func (t *Terminal) handleANSI(readChan chan MeasuredRune) (renderRequired bool) {
|
||||
// if the byte is an escape character, read the next byte to determine which one
|
||||
r := <-readChan
|
||||
|
||||
t.log("ANSI SEQ %c 0x%X", r.Rune, r.Rune)
|
||||
|
||||
t.mu.Lock()
|
||||
defer t.mu.Unlock()
|
||||
|
||||
switch r.Rune {
|
||||
case '[':
|
||||
return t.handleCSI(readChan)
|
||||
case ']':
|
||||
return t.handleOSC(readChan)
|
||||
case '(':
|
||||
return t.handleSCS0(readChan) // select character set into G0
|
||||
case ')':
|
||||
return t.handleSCS1(readChan) // select character set into G1
|
||||
case '*':
|
||||
return swallowHandler(1)(readChan) // character set bullshit
|
||||
case '+':
|
||||
return swallowHandler(1)(readChan) // character set bullshit
|
||||
case '>':
|
||||
return swallowHandler(0)(readChan) // numeric char selection
|
||||
case '=':
|
||||
return swallowHandler(0)(readChan) // alt char selection
|
||||
case '7':
|
||||
t.GetActiveBuffer().saveCursor()
|
||||
case '8':
|
||||
t.GetActiveBuffer().restoreCursor()
|
||||
case 'D':
|
||||
t.GetActiveBuffer().index()
|
||||
case 'E':
|
||||
t.GetActiveBuffer().newLineEx(true)
|
||||
case 'H':
|
||||
t.GetActiveBuffer().tabSetAtCursor()
|
||||
case 'M':
|
||||
t.GetActiveBuffer().reverseIndex()
|
||||
case 'P': // sixel
|
||||
t.handleSixel(readChan)
|
||||
case 'c':
|
||||
t.GetActiveBuffer().clear()
|
||||
case '#':
|
||||
return t.handleScreenState(readChan)
|
||||
case '^':
|
||||
return t.handlePrivacyMessage(readChan)
|
||||
default:
|
||||
t.log("UNKNOWN ESCAPE SEQUENCE: 0x%X", r.Rune)
|
||||
return false
|
||||
}
|
||||
|
||||
return true
|
||||
}
|
||||
|
||||
func swallowHandler(size int) func(pty chan MeasuredRune) bool {
|
||||
return func(pty chan MeasuredRune) bool {
|
||||
for i := 0; i < size; i++ {
|
||||
<-pty
|
||||
}
|
||||
return false
|
||||
}
|
||||
}
|
||||
|
||||
func (t *Terminal) handleScreenState(readChan chan MeasuredRune) bool {
|
||||
b := <-readChan
|
||||
switch b.Rune {
|
||||
case '8': // DECALN -- Screen Alignment Pattern
|
||||
|
||||
// hide cursor?
|
||||
buffer := t.GetActiveBuffer()
|
||||
buffer.resetVerticalMargins(uint(buffer.viewHeight))
|
||||
buffer.SetScrollOffset(0)
|
||||
|
||||
// Fill the whole screen with E's
|
||||
count := buffer.ViewHeight() * buffer.ViewWidth()
|
||||
for count > 0 {
|
||||
buffer.write(MeasuredRune{Rune: 'E', Width: 1})
|
||||
count--
|
||||
if count > 0 && !buffer.modes.AutoWrap && count%buffer.ViewWidth() == 0 {
|
||||
buffer.index()
|
||||
buffer.carriageReturn()
|
||||
}
|
||||
}
|
||||
// restore cursor
|
||||
buffer.setPosition(0, 0)
|
||||
default:
|
||||
return false
|
||||
}
|
||||
return true
|
||||
}
|
||||
|
||||
func (t *Terminal) handlePrivacyMessage(readChan chan MeasuredRune) bool {
|
||||
isEscaped := false
|
||||
for {
|
||||
b := <-readChan
|
||||
if b.Rune == 0x18 /*CAN*/ || b.Rune == 0x1a /*SUB*/ || (b.Rune == 0x5c /*backslash*/ && isEscaped) {
|
||||
break
|
||||
}
|
||||
if isEscaped {
|
||||
isEscaped = false
|
||||
} else if b.Rune == 0x1b {
|
||||
isEscaped = true
|
||||
continue
|
||||
}
|
||||
}
|
||||
return false
|
||||
}
|
891
termutil/buffer.go
Normal file
891
termutil/buffer.go
Normal file
@ -0,0 +1,891 @@
|
||||
package termutil
|
||||
|
||||
import (
|
||||
"image"
|
||||
"image/color"
|
||||
"sync"
|
||||
)
|
||||
|
||||
const TabSize = 8
|
||||
|
||||
type CursorShape uint8
|
||||
|
||||
const (
|
||||
CursorShapeBlinkingBlock CursorShape = iota
|
||||
CursorShapeDefault
|
||||
CursorShapeSteadyBlock
|
||||
CursorShapeBlinkingUnderline
|
||||
CursorShapeSteadyUnderline
|
||||
CursorShapeBlinkingBar
|
||||
CursorShapeSteadyBar
|
||||
)
|
||||
|
||||
type Buffer struct {
|
||||
lines []Line
|
||||
savedCursorPos Position
|
||||
savedCursorAttr *CellAttributes
|
||||
cursorShape CursorShape
|
||||
savedCharsets []*map[rune]rune
|
||||
savedCurrentCharset int
|
||||
topMargin uint // see DECSTBM docs - this is for scrollable regions
|
||||
bottomMargin uint // see DECSTBM docs - this is for scrollable regions
|
||||
viewWidth uint16
|
||||
viewHeight uint16
|
||||
cursorPosition Position // raw
|
||||
cursorAttr CellAttributes
|
||||
scrollLinesFromBottom uint
|
||||
maxLines uint64
|
||||
tabStops []uint16
|
||||
charsets []*map[rune]rune // array of 2 charsets, nil means ASCII (no conversion)
|
||||
currentCharset int // active charset index in charsets array, valid values are 0 or 1
|
||||
modes Modes
|
||||
selectionStart *Position
|
||||
selectionEnd *Position
|
||||
highlightStart *Position
|
||||
highlightEnd *Position
|
||||
highlightAnnotation *Annotation
|
||||
sixels []Sixel
|
||||
selectionMu sync.Mutex
|
||||
}
|
||||
|
||||
type Annotation struct {
|
||||
Image image.Image
|
||||
Text string
|
||||
Width float64 // Width in cells
|
||||
Height float64 // Height in cells
|
||||
}
|
||||
|
||||
type Selection struct {
|
||||
Start Position
|
||||
End Position
|
||||
}
|
||||
|
||||
type Position struct {
|
||||
Line uint64
|
||||
Col uint16
|
||||
}
|
||||
|
||||
// NewBuffer creates a new terminal buffer
|
||||
func NewBuffer(width, height uint16, maxLines uint64, fg color.Color, bg color.Color) *Buffer {
|
||||
b := &Buffer{
|
||||
lines: []Line{},
|
||||
viewHeight: height,
|
||||
viewWidth: width,
|
||||
maxLines: maxLines,
|
||||
topMargin: 0,
|
||||
bottomMargin: uint(height - 1),
|
||||
cursorAttr: CellAttributes{
|
||||
fgColour: fg,
|
||||
bgColour: bg,
|
||||
},
|
||||
charsets: []*map[rune]rune{nil, nil},
|
||||
modes: Modes{
|
||||
LineFeedMode: true,
|
||||
AutoWrap: true,
|
||||
ShowCursor: true,
|
||||
SixelScrolling: true,
|
||||
},
|
||||
cursorShape: CursorShapeDefault,
|
||||
}
|
||||
return b
|
||||
}
|
||||
|
||||
func (buffer *Buffer) SetCursorShape(shape CursorShape) {
|
||||
buffer.cursorShape = shape
|
||||
}
|
||||
|
||||
func (buffer *Buffer) GetCursorShape() CursorShape {
|
||||
return buffer.cursorShape
|
||||
}
|
||||
|
||||
func (buffer *Buffer) IsCursorVisible() bool {
|
||||
return buffer.modes.ShowCursor
|
||||
}
|
||||
|
||||
func (buffer *Buffer) IsApplicationCursorKeysModeEnabled() bool {
|
||||
return buffer.modes.ApplicationCursorKeys
|
||||
}
|
||||
|
||||
func (buffer *Buffer) HasScrollableRegion() bool {
|
||||
return buffer.topMargin > 0 || buffer.bottomMargin < uint(buffer.ViewHeight())-1
|
||||
}
|
||||
|
||||
func (buffer *Buffer) InScrollableRegion() bool {
|
||||
cursorVY := buffer.convertRawLineToViewLine(buffer.cursorPosition.Line)
|
||||
return buffer.HasScrollableRegion() && uint(cursorVY) >= buffer.topMargin && uint(cursorVY) <= buffer.bottomMargin
|
||||
}
|
||||
|
||||
// NOTE: bottom is exclusive
|
||||
func (buffer *Buffer) getAreaScrollRange() (top uint64, bottom uint64) {
|
||||
top = buffer.convertViewLineToRawLine(uint16(buffer.topMargin))
|
||||
bottom = buffer.convertViewLineToRawLine(uint16(buffer.bottomMargin)) + 1
|
||||
if bottom > uint64(len(buffer.lines)) {
|
||||
bottom = uint64(len(buffer.lines))
|
||||
}
|
||||
return top, bottom
|
||||
}
|
||||
|
||||
func (buffer *Buffer) areaScrollDown(lines uint16) {
|
||||
|
||||
// NOTE: bottom is exclusive
|
||||
top, bottom := buffer.getAreaScrollRange()
|
||||
|
||||
for i := bottom; i > top; {
|
||||
i--
|
||||
if i >= top+uint64(lines) {
|
||||
buffer.lines[i] = buffer.lines[i-uint64(lines)]
|
||||
} else {
|
||||
buffer.lines[i] = newLine()
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
func (buffer *Buffer) areaScrollUp(lines uint16) {
|
||||
|
||||
// NOTE: bottom is exclusive
|
||||
top, bottom := buffer.getAreaScrollRange()
|
||||
|
||||
for i := top; i < bottom; i++ {
|
||||
from := i + uint64(lines)
|
||||
if from < bottom {
|
||||
buffer.lines[i] = buffer.lines[from]
|
||||
} else {
|
||||
buffer.lines[i] = newLine()
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
func (buffer *Buffer) saveCursor() {
|
||||
copiedAttr := buffer.cursorAttr
|
||||
buffer.savedCursorAttr = &copiedAttr
|
||||
buffer.savedCursorPos = buffer.cursorPosition
|
||||
buffer.savedCharsets = make([]*map[rune]rune, len(buffer.charsets))
|
||||
copy(buffer.savedCharsets, buffer.charsets)
|
||||
buffer.savedCurrentCharset = buffer.currentCharset
|
||||
}
|
||||
|
||||
func (buffer *Buffer) restoreCursor() {
|
||||
// TODO: Do we need to restore attributes on cursor restore? conflicting sources but vim + htop work better without doing so
|
||||
//if buffer.savedCursorAttr != nil {
|
||||
// copiedAttr := *buffer.savedCursorAttr
|
||||
// copiedAttr.bgColour = buffer.defaultCell(false).attr.bgColour
|
||||
// copiedAttr.fgColour = buffer.defaultCell(false).attr.fgColour
|
||||
// buffer.cursorAttr = copiedAttr
|
||||
//}
|
||||
buffer.cursorPosition = buffer.savedCursorPos
|
||||
if buffer.savedCharsets != nil {
|
||||
buffer.charsets = make([]*map[rune]rune, len(buffer.savedCharsets))
|
||||
copy(buffer.charsets, buffer.savedCharsets)
|
||||
buffer.currentCharset = buffer.savedCurrentCharset
|
||||
}
|
||||
}
|
||||
|
||||
func (buffer *Buffer) getCursorAttr() *CellAttributes {
|
||||
return &buffer.cursorAttr
|
||||
}
|
||||
|
||||
func (buffer *Buffer) GetCell(viewCol uint16, viewRow uint16) *Cell {
|
||||
rawLine := buffer.convertViewLineToRawLine(viewRow)
|
||||
return buffer.getRawCell(viewCol, rawLine)
|
||||
}
|
||||
|
||||
func (buffer *Buffer) getRawCell(viewCol uint16, rawLine uint64) *Cell {
|
||||
if rawLine >= uint64(len(buffer.lines)) {
|
||||
return nil
|
||||
}
|
||||
line := &buffer.lines[rawLine]
|
||||
if int(viewCol) >= len(line.cells) {
|
||||
return nil
|
||||
}
|
||||
return &line.cells[viewCol]
|
||||
}
|
||||
|
||||
// Column returns cursor column
|
||||
func (buffer *Buffer) CursorColumn() uint16 {
|
||||
// @todo originMode and left margin
|
||||
return buffer.cursorPosition.Col
|
||||
}
|
||||
|
||||
// CursorLineAbsolute returns absolute cursor line coordinate (ignoring Origin Mode) - view format
|
||||
func (buffer *Buffer) CursorLineAbsolute() uint16 {
|
||||
cursorVY := buffer.convertRawLineToViewLine(buffer.cursorPosition.Line)
|
||||
return cursorVY
|
||||
}
|
||||
|
||||
// CursorLine returns cursor line (in Origin Mode it is relative to the top margin)
|
||||
func (buffer *Buffer) CursorLine() uint16 {
|
||||
if buffer.modes.OriginMode {
|
||||
return buffer.CursorLineAbsolute() - uint16(buffer.topMargin)
|
||||
}
|
||||
return buffer.CursorLineAbsolute()
|
||||
}
|
||||
|
||||
func (buffer *Buffer) TopMargin() uint {
|
||||
return buffer.topMargin
|
||||
}
|
||||
|
||||
func (buffer *Buffer) BottomMargin() uint {
|
||||
return buffer.bottomMargin
|
||||
}
|
||||
|
||||
// cursor Y (raw)
|
||||
func (buffer *Buffer) RawLine() uint64 {
|
||||
return buffer.cursorPosition.Line
|
||||
}
|
||||
|
||||
func (buffer *Buffer) convertViewLineToRawLine(viewLine uint16) uint64 {
|
||||
rawHeight := buffer.Height()
|
||||
if int(buffer.viewHeight) > rawHeight {
|
||||
return uint64(viewLine)
|
||||
}
|
||||
return uint64(int(viewLine) + (rawHeight - int(buffer.viewHeight+uint16(buffer.scrollLinesFromBottom))))
|
||||
}
|
||||
|
||||
func (buffer *Buffer) convertRawLineToViewLine(rawLine uint64) uint16 {
|
||||
rawHeight := buffer.Height()
|
||||
if int(buffer.viewHeight) > rawHeight {
|
||||
return uint16(rawLine)
|
||||
}
|
||||
return uint16(int(rawLine) - (rawHeight - int(buffer.viewHeight+uint16(buffer.scrollLinesFromBottom))))
|
||||
}
|
||||
|
||||
func (buffer *Buffer) GetVPosition() int {
|
||||
result := int(uint(buffer.Height()) - uint(buffer.ViewHeight()) - buffer.scrollLinesFromBottom)
|
||||
if result < 0 {
|
||||
result = 0
|
||||
}
|
||||
|
||||
return result
|
||||
}
|
||||
|
||||
// Width returns the width of the buffer in columns
|
||||
func (buffer *Buffer) Width() uint16 {
|
||||
return buffer.viewWidth
|
||||
}
|
||||
|
||||
func (buffer *Buffer) ViewWidth() uint16 {
|
||||
return buffer.viewWidth
|
||||
}
|
||||
|
||||
func (buffer *Buffer) Height() int {
|
||||
return len(buffer.lines)
|
||||
}
|
||||
|
||||
func (buffer *Buffer) ViewHeight() uint16 {
|
||||
return buffer.viewHeight
|
||||
}
|
||||
|
||||
func (buffer *Buffer) deleteLine() {
|
||||
index := int(buffer.RawLine())
|
||||
buffer.lines = buffer.lines[:index+copy(buffer.lines[index:], buffer.lines[index+1:])]
|
||||
}
|
||||
|
||||
func (buffer *Buffer) insertLine() {
|
||||
|
||||
if !buffer.InScrollableRegion() {
|
||||
pos := buffer.RawLine()
|
||||
maxLines := buffer.GetMaxLines()
|
||||
newLineCount := uint64(len(buffer.lines) + 1)
|
||||
if newLineCount > maxLines {
|
||||
newLineCount = maxLines
|
||||
}
|
||||
|
||||
out := make([]Line, newLineCount)
|
||||
copy(
|
||||
out[:pos-(uint64(len(buffer.lines))+1-newLineCount)],
|
||||
buffer.lines[uint64(len(buffer.lines))+1-newLineCount:pos])
|
||||
out[pos] = newLine()
|
||||
copy(out[pos+1:], buffer.lines[pos:])
|
||||
buffer.lines = out
|
||||
} else {
|
||||
topIndex := buffer.convertViewLineToRawLine(uint16(buffer.topMargin))
|
||||
bottomIndex := buffer.convertViewLineToRawLine(uint16(buffer.bottomMargin))
|
||||
before := buffer.lines[:topIndex]
|
||||
after := buffer.lines[bottomIndex+1:]
|
||||
out := make([]Line, len(buffer.lines))
|
||||
copy(out[0:], before)
|
||||
|
||||
pos := buffer.RawLine()
|
||||
for i := topIndex; i < bottomIndex; i++ {
|
||||
if i < pos {
|
||||
out[i] = buffer.lines[i]
|
||||
} else {
|
||||
out[i+1] = buffer.lines[i]
|
||||
}
|
||||
}
|
||||
|
||||
copy(out[bottomIndex+1:], after)
|
||||
|
||||
out[pos] = newLine()
|
||||
buffer.lines = out
|
||||
}
|
||||
}
|
||||
|
||||
func (buffer *Buffer) insertBlankCharacters(count int) {
|
||||
|
||||
index := int(buffer.RawLine())
|
||||
for i := 0; i < count; i++ {
|
||||
cells := buffer.lines[index].cells
|
||||
buffer.lines[index].cells = append(cells[:buffer.cursorPosition.Col], append([]Cell{buffer.defaultCell(true)}, cells[buffer.cursorPosition.Col:]...)...)
|
||||
}
|
||||
}
|
||||
|
||||
func (buffer *Buffer) insertLines(count int) {
|
||||
|
||||
if buffer.HasScrollableRegion() && !buffer.InScrollableRegion() {
|
||||
// should have no effect outside of scrollable region
|
||||
return
|
||||
}
|
||||
|
||||
buffer.cursorPosition.Col = 0
|
||||
|
||||
for i := 0; i < count; i++ {
|
||||
buffer.insertLine()
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
func (buffer *Buffer) deleteLines(count int) {
|
||||
|
||||
if buffer.HasScrollableRegion() && !buffer.InScrollableRegion() {
|
||||
// should have no effect outside of scrollable region
|
||||
return
|
||||
}
|
||||
|
||||
buffer.cursorPosition.Col = 0
|
||||
|
||||
for i := 0; i < count; i++ {
|
||||
buffer.deleteLine()
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
func (buffer *Buffer) index() {
|
||||
|
||||
// This sequence causes the active position to move downward one line without changing the column position.
|
||||
// If the active position is at the bottom margin, a scroll up is performed."
|
||||
|
||||
cursorVY := buffer.convertRawLineToViewLine(buffer.cursorPosition.Line)
|
||||
|
||||
if buffer.InScrollableRegion() {
|
||||
|
||||
if uint(cursorVY) < buffer.bottomMargin {
|
||||
buffer.cursorPosition.Line++
|
||||
} else {
|
||||
buffer.areaScrollUp(1)
|
||||
}
|
||||
|
||||
return
|
||||
}
|
||||
|
||||
if cursorVY >= buffer.ViewHeight()-1 {
|
||||
buffer.lines = append(buffer.lines, newLine())
|
||||
maxLines := buffer.GetMaxLines()
|
||||
if uint64(len(buffer.lines)) > maxLines {
|
||||
copy(buffer.lines, buffer.lines[uint64(len(buffer.lines))-maxLines:])
|
||||
buffer.lines = buffer.lines[:maxLines]
|
||||
}
|
||||
}
|
||||
buffer.cursorPosition.Line++
|
||||
}
|
||||
|
||||
func (buffer *Buffer) reverseIndex() {
|
||||
|
||||
cursorVY := buffer.convertRawLineToViewLine(buffer.cursorPosition.Line)
|
||||
|
||||
if uint(cursorVY) == buffer.topMargin {
|
||||
buffer.areaScrollDown(1)
|
||||
} else if cursorVY > 0 {
|
||||
buffer.cursorPosition.Line--
|
||||
}
|
||||
}
|
||||
|
||||
// write will write a rune to the terminal at the position of the cursor, and increment the cursor position
|
||||
func (buffer *Buffer) write(runes ...MeasuredRune) {
|
||||
|
||||
// scroll to bottom on input
|
||||
buffer.scrollLinesFromBottom = 0
|
||||
|
||||
for _, r := range runes {
|
||||
|
||||
line := buffer.getCurrentLine()
|
||||
|
||||
if buffer.modes.ReplaceMode {
|
||||
|
||||
if buffer.CursorColumn() >= buffer.Width() {
|
||||
if buffer.modes.AutoWrap {
|
||||
buffer.cursorPosition.Line++
|
||||
buffer.cursorPosition.Col = 0
|
||||
line = buffer.getCurrentLine()
|
||||
|
||||
} else {
|
||||
// no more room on line and wrapping is disabled
|
||||
return
|
||||
}
|
||||
}
|
||||
|
||||
for int(buffer.CursorColumn()) >= len(line.cells) {
|
||||
line.append(buffer.defaultCell(int(buffer.CursorColumn()) == len(line.cells)))
|
||||
}
|
||||
line.cells[buffer.cursorPosition.Col].attr = buffer.cursorAttr
|
||||
line.cells[buffer.cursorPosition.Col].setRune(r)
|
||||
buffer.incrementCursorPosition()
|
||||
continue
|
||||
}
|
||||
|
||||
if buffer.CursorColumn() >= buffer.Width() { // if we're after the line, move to next
|
||||
|
||||
if buffer.modes.AutoWrap {
|
||||
|
||||
buffer.newLineEx(true)
|
||||
|
||||
newLine := buffer.getCurrentLine()
|
||||
if len(newLine.cells) == 0 {
|
||||
newLine.append(buffer.defaultCell(true))
|
||||
}
|
||||
cell := &newLine.cells[0]
|
||||
cell.setRune(r)
|
||||
cell.attr = buffer.cursorAttr
|
||||
|
||||
} else {
|
||||
// no more room on line and wrapping is disabled
|
||||
return
|
||||
}
|
||||
|
||||
} else {
|
||||
|
||||
for int(buffer.CursorColumn()) >= len(line.cells) {
|
||||
line.append(buffer.defaultCell(int(buffer.CursorColumn()) == len(line.cells)))
|
||||
}
|
||||
|
||||
cell := &line.cells[buffer.CursorColumn()]
|
||||
cell.setRune(r)
|
||||
cell.attr = buffer.cursorAttr
|
||||
}
|
||||
|
||||
buffer.incrementCursorPosition()
|
||||
}
|
||||
}
|
||||
|
||||
func (buffer *Buffer) incrementCursorPosition() {
|
||||
// we can increment one column past the end of the line.
|
||||
// this is effectively the beginning of the next line, except when we \r etc.
|
||||
if buffer.CursorColumn() < buffer.Width() {
|
||||
buffer.cursorPosition.Col++
|
||||
}
|
||||
}
|
||||
|
||||
func (buffer *Buffer) inDoWrap() bool {
|
||||
// xterm uses 'do_wrap' flag for this special terminal state
|
||||
// we use the cursor position right after the boundary
|
||||
// let's see how it works out
|
||||
return buffer.cursorPosition.Col == buffer.viewWidth // @todo rightMargin
|
||||
}
|
||||
|
||||
func (buffer *Buffer) backspace() {
|
||||
|
||||
if buffer.cursorPosition.Col == 0 {
|
||||
line := buffer.getCurrentLine()
|
||||
if line.wrapped {
|
||||
buffer.movePosition(int16(buffer.Width()-1), -1)
|
||||
}
|
||||
} else if buffer.inDoWrap() {
|
||||
// the "do_wrap" implementation
|
||||
buffer.movePosition(-2, 0)
|
||||
} else {
|
||||
buffer.movePosition(-1, 0)
|
||||
}
|
||||
}
|
||||
|
||||
func (buffer *Buffer) carriageReturn() {
|
||||
|
||||
cursorVY := buffer.convertRawLineToViewLine(buffer.cursorPosition.Line)
|
||||
|
||||
for {
|
||||
line := buffer.getCurrentLine()
|
||||
if line == nil {
|
||||
break
|
||||
}
|
||||
if line.wrapped && cursorVY > 0 {
|
||||
buffer.cursorPosition.Line--
|
||||
} else {
|
||||
break
|
||||
}
|
||||
}
|
||||
|
||||
buffer.cursorPosition.Col = 0
|
||||
}
|
||||
|
||||
func (buffer *Buffer) tab() {
|
||||
|
||||
tabStop := buffer.getNextTabStopAfter(buffer.cursorPosition.Col)
|
||||
for buffer.cursorPosition.Col < tabStop && buffer.cursorPosition.Col < buffer.viewWidth-1 { // @todo rightMargin
|
||||
buffer.write(MeasuredRune{Rune: ' ', Width: 1})
|
||||
}
|
||||
}
|
||||
|
||||
// return next tab stop x pos
|
||||
func (buffer *Buffer) getNextTabStopAfter(col uint16) uint16 {
|
||||
|
||||
defaultStop := col + (TabSize - (col % TabSize))
|
||||
if defaultStop == col {
|
||||
defaultStop += TabSize
|
||||
}
|
||||
|
||||
var low uint16
|
||||
for _, stop := range buffer.tabStops {
|
||||
if stop > col {
|
||||
if stop < low || low == 0 {
|
||||
low = stop
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if low == 0 {
|
||||
return defaultStop
|
||||
}
|
||||
|
||||
return low
|
||||
}
|
||||
|
||||
func (buffer *Buffer) newLine() {
|
||||
buffer.newLineEx(false)
|
||||
}
|
||||
|
||||
func (buffer *Buffer) verticalTab() {
|
||||
buffer.index()
|
||||
|
||||
for {
|
||||
line := buffer.getCurrentLine()
|
||||
if !line.wrapped {
|
||||
break
|
||||
}
|
||||
buffer.index()
|
||||
}
|
||||
}
|
||||
|
||||
func (buffer *Buffer) newLineEx(forceCursorToMargin bool) {
|
||||
|
||||
if buffer.IsNewLineMode() || forceCursorToMargin {
|
||||
buffer.cursorPosition.Col = 0
|
||||
}
|
||||
buffer.index()
|
||||
|
||||
for {
|
||||
line := buffer.getCurrentLine()
|
||||
if !line.wrapped {
|
||||
break
|
||||
}
|
||||
buffer.index()
|
||||
}
|
||||
}
|
||||
|
||||
func (buffer *Buffer) movePosition(x int16, y int16) {
|
||||
|
||||
var toX uint16
|
||||
var toY uint16
|
||||
|
||||
if int16(buffer.CursorColumn())+x < 0 {
|
||||
toX = 0
|
||||
} else {
|
||||
toX = uint16(int16(buffer.CursorColumn()) + x)
|
||||
}
|
||||
|
||||
// should either use CursorLine() and setPosition() or use absolutes, mind Origin Mode (DECOM)
|
||||
if int16(buffer.CursorLine())+y < 0 {
|
||||
toY = 0
|
||||
} else {
|
||||
toY = uint16(int16(buffer.CursorLine()) + y)
|
||||
}
|
||||
|
||||
buffer.setPosition(toX, toY)
|
||||
}
|
||||
|
||||
func (buffer *Buffer) setPosition(col uint16, line uint16) {
|
||||
|
||||
useCol := col
|
||||
useLine := line
|
||||
maxLine := buffer.ViewHeight() - 1
|
||||
|
||||
if buffer.modes.OriginMode {
|
||||
useLine += uint16(buffer.topMargin)
|
||||
maxLine = uint16(buffer.bottomMargin)
|
||||
// @todo left and right margins
|
||||
}
|
||||
if useLine > maxLine {
|
||||
useLine = maxLine
|
||||
}
|
||||
|
||||
if useCol >= buffer.ViewWidth() {
|
||||
useCol = buffer.ViewWidth() - 1
|
||||
}
|
||||
|
||||
buffer.cursorPosition.Col = useCol
|
||||
buffer.cursorPosition.Line = buffer.convertViewLineToRawLine(useLine)
|
||||
}
|
||||
|
||||
func (buffer *Buffer) GetVisibleLines() []Line {
|
||||
lines := []Line{}
|
||||
|
||||
for i := buffer.Height() - int(buffer.ViewHeight()); i < buffer.Height(); i++ {
|
||||
y := i - int(buffer.scrollLinesFromBottom)
|
||||
if y >= 0 && y < len(buffer.lines) {
|
||||
lines = append(lines, buffer.lines[y])
|
||||
}
|
||||
}
|
||||
return lines
|
||||
}
|
||||
|
||||
// tested to here
|
||||
|
||||
func (buffer *Buffer) clear() {
|
||||
for i := 0; i < int(buffer.ViewHeight()); i++ {
|
||||
buffer.lines = append(buffer.lines, newLine())
|
||||
}
|
||||
buffer.setPosition(0, 0)
|
||||
}
|
||||
|
||||
// creates if necessary
|
||||
func (buffer *Buffer) getCurrentLine() *Line {
|
||||
cursorVY := buffer.convertRawLineToViewLine(buffer.cursorPosition.Line)
|
||||
return buffer.getViewLine(cursorVY)
|
||||
}
|
||||
|
||||
func (buffer *Buffer) getViewLine(index uint16) *Line {
|
||||
|
||||
if index >= buffer.ViewHeight() {
|
||||
return &buffer.lines[len(buffer.lines)-1]
|
||||
}
|
||||
|
||||
if len(buffer.lines) < int(buffer.ViewHeight()) {
|
||||
for int(index) >= len(buffer.lines) {
|
||||
buffer.lines = append(buffer.lines, newLine())
|
||||
}
|
||||
return &buffer.lines[int(index)]
|
||||
}
|
||||
|
||||
if raw := int(buffer.convertViewLineToRawLine(index)); raw < len(buffer.lines) {
|
||||
return &buffer.lines[raw]
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
func (buffer *Buffer) eraseLine() {
|
||||
|
||||
buffer.clearSixelsAtRawLine(buffer.cursorPosition.Line)
|
||||
|
||||
line := buffer.getCurrentLine()
|
||||
|
||||
for i := 0; i < int(buffer.viewWidth); i++ {
|
||||
if i >= len(line.cells) {
|
||||
line.cells = append(line.cells, buffer.defaultCell(false))
|
||||
} else {
|
||||
line.cells[i] = buffer.defaultCell(false)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
func (buffer *Buffer) eraseLineToCursor() {
|
||||
buffer.clearSixelsAtRawLine(buffer.cursorPosition.Line)
|
||||
line := buffer.getCurrentLine()
|
||||
for i := 0; i <= int(buffer.cursorPosition.Col); i++ {
|
||||
if i < len(line.cells) {
|
||||
line.cells[i].erase(buffer.cursorAttr.bgColour)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
func (buffer *Buffer) eraseLineFromCursor() {
|
||||
buffer.clearSixelsAtRawLine(buffer.cursorPosition.Line)
|
||||
line := buffer.getCurrentLine()
|
||||
|
||||
for i := buffer.cursorPosition.Col; i < buffer.viewWidth; i++ {
|
||||
if int(i) >= len(line.cells) {
|
||||
line.cells = append(line.cells, buffer.defaultCell(false))
|
||||
} else {
|
||||
line.cells[i] = buffer.defaultCell(false)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
func (buffer *Buffer) eraseDisplay() {
|
||||
for i := uint16(0); i < (buffer.ViewHeight()); i++ {
|
||||
rawLine := buffer.convertViewLineToRawLine(i)
|
||||
buffer.clearSixelsAtRawLine(rawLine)
|
||||
if int(rawLine) < len(buffer.lines) {
|
||||
buffer.lines[int(rawLine)].cells = []Cell{}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
func (buffer *Buffer) deleteChars(n int) {
|
||||
|
||||
line := buffer.getCurrentLine()
|
||||
if int(buffer.cursorPosition.Col) >= len(line.cells) {
|
||||
return
|
||||
}
|
||||
before := line.cells[:buffer.cursorPosition.Col]
|
||||
if int(buffer.cursorPosition.Col)+n >= len(line.cells) {
|
||||
n = len(line.cells) - int(buffer.cursorPosition.Col)
|
||||
}
|
||||
after := line.cells[int(buffer.cursorPosition.Col)+n:]
|
||||
line.cells = append(before, after...)
|
||||
}
|
||||
|
||||
func (buffer *Buffer) eraseCharacters(n int) {
|
||||
|
||||
line := buffer.getCurrentLine()
|
||||
|
||||
max := int(buffer.cursorPosition.Col) + n
|
||||
if max > len(line.cells) {
|
||||
max = len(line.cells)
|
||||
}
|
||||
|
||||
for i := int(buffer.cursorPosition.Col); i < max; i++ {
|
||||
line.cells[i].erase(buffer.cursorAttr.bgColour)
|
||||
}
|
||||
}
|
||||
|
||||
func (buffer *Buffer) eraseDisplayFromCursor() {
|
||||
line := buffer.getCurrentLine()
|
||||
|
||||
max := int(buffer.cursorPosition.Col)
|
||||
if max > len(line.cells) {
|
||||
max = len(line.cells)
|
||||
}
|
||||
|
||||
line.cells = line.cells[:max]
|
||||
|
||||
for rawLine := buffer.cursorPosition.Line + 1; int(rawLine) < len(buffer.lines); rawLine++ {
|
||||
buffer.clearSixelsAtRawLine(rawLine)
|
||||
buffer.lines[int(rawLine)].cells = []Cell{}
|
||||
}
|
||||
}
|
||||
|
||||
func (buffer *Buffer) eraseDisplayToCursor() {
|
||||
line := buffer.getCurrentLine()
|
||||
|
||||
for i := 0; i <= int(buffer.cursorPosition.Col); i++ {
|
||||
if i >= len(line.cells) {
|
||||
break
|
||||
}
|
||||
line.cells[i].erase(buffer.cursorAttr.bgColour)
|
||||
}
|
||||
|
||||
cursorVY := buffer.convertRawLineToViewLine(buffer.cursorPosition.Line)
|
||||
|
||||
for i := uint16(0); i < cursorVY; i++ {
|
||||
rawLine := buffer.convertViewLineToRawLine(i)
|
||||
buffer.clearSixelsAtRawLine(rawLine)
|
||||
if int(rawLine) < len(buffer.lines) {
|
||||
buffer.lines[int(rawLine)].cells = []Cell{}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
func (buffer *Buffer) GetMaxLines() uint64 {
|
||||
result := buffer.maxLines
|
||||
if result < uint64(buffer.viewHeight) {
|
||||
result = uint64(buffer.viewHeight)
|
||||
}
|
||||
|
||||
return result
|
||||
}
|
||||
|
||||
func (buffer *Buffer) setVerticalMargins(top uint, bottom uint) {
|
||||
buffer.topMargin = top
|
||||
buffer.bottomMargin = bottom
|
||||
}
|
||||
|
||||
// resetVerticalMargins resets margins to extreme positions
|
||||
func (buffer *Buffer) resetVerticalMargins(height uint) {
|
||||
buffer.setVerticalMargins(0, height-1)
|
||||
}
|
||||
|
||||
func (buffer *Buffer) defaultCell(applyEffects bool) Cell {
|
||||
attr := buffer.cursorAttr
|
||||
if !applyEffects {
|
||||
attr.blink = false
|
||||
attr.bold = false
|
||||
attr.dim = false
|
||||
attr.inverse = false
|
||||
attr.underline = false
|
||||
attr.dim = false
|
||||
}
|
||||
return Cell{attr: attr}
|
||||
}
|
||||
|
||||
func (buffer *Buffer) IsNewLineMode() bool {
|
||||
return !buffer.modes.LineFeedMode
|
||||
}
|
||||
|
||||
func (buffer *Buffer) tabReset() {
|
||||
buffer.tabStops = nil
|
||||
}
|
||||
|
||||
func (buffer *Buffer) tabSet(index uint16) {
|
||||
buffer.tabStops = append(buffer.tabStops, index)
|
||||
}
|
||||
|
||||
func (buffer *Buffer) tabClear(index uint16) {
|
||||
var filtered []uint16
|
||||
for _, stop := range buffer.tabStops {
|
||||
if stop != buffer.cursorPosition.Col {
|
||||
filtered = append(filtered, stop)
|
||||
}
|
||||
}
|
||||
buffer.tabStops = filtered
|
||||
}
|
||||
|
||||
func (buffer *Buffer) IsTabSetAtCursor() bool {
|
||||
if buffer.cursorPosition.Col%TabSize > 0 {
|
||||
return false
|
||||
}
|
||||
for _, stop := range buffer.tabStops {
|
||||
if stop == buffer.cursorPosition.Col {
|
||||
return true
|
||||
}
|
||||
}
|
||||
return false
|
||||
}
|
||||
|
||||
func (buffer *Buffer) tabClearAtCursor() {
|
||||
buffer.tabClear(buffer.cursorPosition.Col)
|
||||
}
|
||||
|
||||
func (buffer *Buffer) tabSetAtCursor() {
|
||||
buffer.tabSet(buffer.cursorPosition.Col)
|
||||
}
|
||||
|
||||
func (buffer *Buffer) GetScrollOffset() uint {
|
||||
return buffer.scrollLinesFromBottom
|
||||
}
|
||||
|
||||
func (buffer *Buffer) SetScrollOffset(offset uint) {
|
||||
buffer.scrollLinesFromBottom = offset
|
||||
}
|
||||
|
||||
func (buffer *Buffer) ScrollToEnd() {
|
||||
buffer.scrollLinesFromBottom = 0
|
||||
}
|
||||
|
||||
func (buffer *Buffer) ScrollUp(lines uint) {
|
||||
if int(buffer.scrollLinesFromBottom)+int(lines) < len(buffer.lines)-int(buffer.viewHeight) {
|
||||
buffer.scrollLinesFromBottom += lines
|
||||
} else {
|
||||
lines := len(buffer.lines) - int(buffer.viewHeight)
|
||||
if lines < 0 {
|
||||
lines = 0
|
||||
}
|
||||
buffer.scrollLinesFromBottom = uint(lines)
|
||||
}
|
||||
}
|
||||
|
||||
func (buffer *Buffer) ScrollDown(lines uint) {
|
||||
if int(buffer.scrollLinesFromBottom)-int(lines) >= 0 {
|
||||
buffer.scrollLinesFromBottom -= lines
|
||||
} else {
|
||||
buffer.scrollLinesFromBottom = 0
|
||||
}
|
||||
}
|
688
termutil/buffer_test.go
Normal file
688
termutil/buffer_test.go
Normal file
@ -0,0 +1,688 @@
|
||||
package termutil
|
||||
|
||||
import (
|
||||
"image/color"
|
||||
"strings"
|
||||
"testing"
|
||||
|
||||
"github.com/stretchr/testify/require"
|
||||
|
||||
"github.com/stretchr/testify/assert"
|
||||
)
|
||||
|
||||
func writeRaw(buf *Buffer, runes ...rune) {
|
||||
for _, r := range runes {
|
||||
buf.write(MeasuredRune{Rune: r, Width: 1})
|
||||
}
|
||||
}
|
||||
|
||||
func TestBufferCreation(t *testing.T) {
|
||||
b := makeBufferForTesting(10, 20)
|
||||
assert.Equal(t, uint16(10), b.Width())
|
||||
assert.Equal(t, uint16(20), b.ViewHeight())
|
||||
assert.Equal(t, uint16(0), b.CursorColumn())
|
||||
assert.Equal(t, uint16(0), b.CursorLine())
|
||||
assert.NotNil(t, b.lines)
|
||||
}
|
||||
|
||||
func TestNewLine(t *testing.T) {
|
||||
b := makeBufferForTesting(30, 3)
|
||||
writeRaw(b, []rune("hello")...)
|
||||
b.carriageReturn()
|
||||
b.newLine()
|
||||
writeRaw(b, []rune("goodbye")...)
|
||||
b.carriageReturn()
|
||||
b.newLine()
|
||||
expected := `
|
||||
hello
|
||||
goodbye
|
||||
`
|
||||
|
||||
lines := b.GetVisibleLines()
|
||||
strs := []string{}
|
||||
for _, l := range lines {
|
||||
strs = append(strs, l.String())
|
||||
}
|
||||
require.Equal(t, strings.TrimSpace(expected), strings.TrimSpace(strings.Join(strs, "\n")))
|
||||
}
|
||||
|
||||
func TestTabbing(t *testing.T) {
|
||||
b := makeBufferForTesting(30, 3)
|
||||
writeRaw(b, []rune("hello")...)
|
||||
b.tab()
|
||||
writeRaw(b, []rune("x")...)
|
||||
b.tab()
|
||||
writeRaw(b, []rune("goodbye")...)
|
||||
b.carriageReturn()
|
||||
b.newLine()
|
||||
writeRaw(b, []rune("hell")...)
|
||||
b.tab()
|
||||
writeRaw(b, []rune("xxx")...)
|
||||
b.tab()
|
||||
writeRaw(b, []rune("good")...)
|
||||
b.carriageReturn()
|
||||
b.newLine()
|
||||
expected := `
|
||||
hello x goodbye
|
||||
hell xxx good
|
||||
`
|
||||
|
||||
lines := b.GetVisibleLines()
|
||||
strs := []string{}
|
||||
for _, l := range lines {
|
||||
strs = append(strs, l.String())
|
||||
}
|
||||
require.Equal(t, strings.TrimSpace(expected), strings.TrimSpace(strings.Join(strs, "\n")))
|
||||
}
|
||||
|
||||
func TestOffsets(t *testing.T) {
|
||||
b := makeBufferForTesting(10, 3)
|
||||
writeRaw(b, []rune("hello")...)
|
||||
b.carriageReturn()
|
||||
b.newLine()
|
||||
writeRaw(b, []rune("hello")...)
|
||||
b.carriageReturn()
|
||||
b.newLine()
|
||||
writeRaw(b, []rune("hello")...)
|
||||
b.carriageReturn()
|
||||
b.newLine()
|
||||
writeRaw(b, []rune("hello")...)
|
||||
b.carriageReturn()
|
||||
b.newLine()
|
||||
writeRaw(b, []rune("hello")...)
|
||||
assert.Equal(t, uint16(10), b.ViewWidth())
|
||||
assert.Equal(t, uint16(10), b.Width())
|
||||
assert.Equal(t, uint16(3), b.ViewHeight())
|
||||
assert.Equal(t, 5, b.Height())
|
||||
}
|
||||
|
||||
func TestBufferWriteIncrementsCursorCorrectly(t *testing.T) {
|
||||
b := makeBufferForTesting(5, 4)
|
||||
|
||||
/*01234
|
||||
|-----
|
||||
0|xxxxx
|
||||
1|
|
||||
2|
|
||||
3|
|
||||
|-----
|
||||
*/
|
||||
|
||||
writeRaw(b, 'x')
|
||||
require.Equal(t, uint16(1), b.CursorColumn())
|
||||
require.Equal(t, uint16(0), b.CursorLine())
|
||||
|
||||
writeRaw(b, 'x')
|
||||
require.Equal(t, uint16(2), b.CursorColumn())
|
||||
require.Equal(t, uint16(0), b.CursorLine())
|
||||
|
||||
writeRaw(b, 'x')
|
||||
require.Equal(t, uint16(3), b.CursorColumn())
|
||||
require.Equal(t, uint16(0), b.CursorLine())
|
||||
|
||||
writeRaw(b, 'x')
|
||||
require.Equal(t, uint16(4), b.CursorColumn())
|
||||
require.Equal(t, uint16(0), b.CursorLine())
|
||||
|
||||
writeRaw(b, 'x')
|
||||
require.Equal(t, uint16(5), b.CursorColumn())
|
||||
require.Equal(t, uint16(0), b.CursorLine())
|
||||
|
||||
writeRaw(b, 'x')
|
||||
require.Equal(t, uint16(1), b.CursorColumn())
|
||||
require.Equal(t, uint16(1), b.CursorLine())
|
||||
|
||||
writeRaw(b, 'x')
|
||||
require.Equal(t, uint16(2), b.CursorColumn())
|
||||
require.Equal(t, uint16(1), b.CursorLine())
|
||||
|
||||
lines := b.GetVisibleLines()
|
||||
require.Equal(t, 2, len(lines))
|
||||
assert.Equal(t, "xxxxx", lines[0].String())
|
||||
assert.Equal(t, "xx", lines[1].String())
|
||||
|
||||
}
|
||||
|
||||
func TestWritingNewLineAsFirstRuneOnWrappedLine(t *testing.T) {
|
||||
b := makeBufferForTesting(3, 20)
|
||||
b.modes.LineFeedMode = false
|
||||
|
||||
writeRaw(b, 'a', 'b', 'c')
|
||||
assert.Equal(t, uint16(3), b.cursorPosition.Col)
|
||||
assert.Equal(t, uint64(0), b.cursorPosition.Line)
|
||||
b.newLine()
|
||||
assert.Equal(t, uint16(0), b.cursorPosition.Col)
|
||||
assert.Equal(t, uint64(1), b.cursorPosition.Line)
|
||||
|
||||
writeRaw(b, 'd', 'e', 'f')
|
||||
assert.Equal(t, uint16(3), b.cursorPosition.Col)
|
||||
assert.Equal(t, uint64(1), b.cursorPosition.Line)
|
||||
b.newLine()
|
||||
|
||||
assert.Equal(t, uint16(0), b.cursorPosition.Col)
|
||||
assert.Equal(t, uint64(2), b.cursorPosition.Line)
|
||||
|
||||
require.Equal(t, 3, len(b.lines))
|
||||
assert.Equal(t, "abc", b.lines[0].String())
|
||||
assert.Equal(t, "def", b.lines[1].String())
|
||||
|
||||
}
|
||||
|
||||
func TestWritingNewLineAsSecondRuneOnWrappedLine(t *testing.T) {
|
||||
b := makeBufferForTesting(3, 20)
|
||||
b.modes.LineFeedMode = false
|
||||
/*
|
||||
|abc
|
||||
|d
|
||||
|ef
|
||||
|
|
||||
|
|
||||
|z
|
||||
*/
|
||||
|
||||
writeRaw(b, 'a', 'b', 'c', 'd')
|
||||
b.newLine()
|
||||
writeRaw(b, 'e', 'f')
|
||||
b.newLine()
|
||||
b.newLine()
|
||||
b.newLine()
|
||||
writeRaw(b, 'z')
|
||||
|
||||
assert.Equal(t, "abc", b.lines[0].String())
|
||||
assert.Equal(t, "d", b.lines[1].String())
|
||||
assert.Equal(t, "ef", b.lines[2].String())
|
||||
assert.Equal(t, "", b.lines[3].String())
|
||||
assert.Equal(t, "", b.lines[4].String())
|
||||
assert.Equal(t, "z", b.lines[5].String())
|
||||
}
|
||||
|
||||
func TestSetPosition(t *testing.T) {
|
||||
b := makeBufferForTesting(120, 80)
|
||||
assert.Equal(t, 0, int(b.CursorColumn()))
|
||||
assert.Equal(t, 0, int(b.CursorLine()))
|
||||
|
||||
b.setPosition(60, 10)
|
||||
assert.Equal(t, 60, int(b.CursorColumn()))
|
||||
assert.Equal(t, 10, int(b.CursorLine()))
|
||||
|
||||
b.setPosition(0, 0)
|
||||
assert.Equal(t, 0, int(b.CursorColumn()))
|
||||
assert.Equal(t, 0, int(b.CursorLine()))
|
||||
|
||||
b.setPosition(120, 90)
|
||||
assert.Equal(t, 119, int(b.CursorColumn()))
|
||||
assert.Equal(t, 79, int(b.CursorLine()))
|
||||
|
||||
}
|
||||
|
||||
func TestMovePosition(t *testing.T) {
|
||||
b := makeBufferForTesting(120, 80)
|
||||
assert.Equal(t, 0, int(b.CursorColumn()))
|
||||
assert.Equal(t, 0, int(b.CursorLine()))
|
||||
|
||||
b.movePosition(-1, -1)
|
||||
assert.Equal(t, 0, int(b.CursorColumn()))
|
||||
assert.Equal(t, 0, int(b.CursorLine()))
|
||||
|
||||
b.movePosition(30, 20)
|
||||
assert.Equal(t, 30, int(b.CursorColumn()))
|
||||
assert.Equal(t, 20, int(b.CursorLine()))
|
||||
|
||||
b.movePosition(30, 20)
|
||||
assert.Equal(t, 60, int(b.CursorColumn()))
|
||||
assert.Equal(t, 40, int(b.CursorLine()))
|
||||
|
||||
b.movePosition(-1, -1)
|
||||
assert.Equal(t, 59, int(b.CursorColumn()))
|
||||
assert.Equal(t, 39, int(b.CursorLine()))
|
||||
|
||||
b.movePosition(100, 100)
|
||||
assert.Equal(t, 119, int(b.CursorColumn()))
|
||||
assert.Equal(t, 79, int(b.CursorLine()))
|
||||
|
||||
}
|
||||
|
||||
func TestVisibleLines(t *testing.T) {
|
||||
b := makeBufferForTesting(80, 10)
|
||||
writeRaw(b, []rune("hello 1")...)
|
||||
b.carriageReturn()
|
||||
b.newLine()
|
||||
writeRaw(b, []rune("hello 2")...)
|
||||
b.carriageReturn()
|
||||
b.newLine()
|
||||
writeRaw(b, []rune("hello 3")...)
|
||||
b.carriageReturn()
|
||||
b.newLine()
|
||||
writeRaw(b, []rune("hello 4")...)
|
||||
b.carriageReturn()
|
||||
b.newLine()
|
||||
writeRaw(b, []rune("hello 5")...)
|
||||
b.carriageReturn()
|
||||
b.newLine()
|
||||
writeRaw(b, []rune("hello 6")...)
|
||||
b.carriageReturn()
|
||||
b.newLine()
|
||||
writeRaw(b, []rune("hello 7")...)
|
||||
b.carriageReturn()
|
||||
b.newLine()
|
||||
writeRaw(b, []rune("hello 8")...)
|
||||
b.carriageReturn()
|
||||
b.newLine()
|
||||
writeRaw(b, []rune("hello 9")...)
|
||||
b.carriageReturn()
|
||||
b.newLine()
|
||||
writeRaw(b, []rune("hello 10")...)
|
||||
b.carriageReturn()
|
||||
b.newLine()
|
||||
writeRaw(b, []rune("hello 11")...)
|
||||
b.carriageReturn()
|
||||
b.newLine()
|
||||
writeRaw(b, []rune("hello 12")...)
|
||||
b.carriageReturn()
|
||||
b.newLine()
|
||||
writeRaw(b, []rune("hello 13")...)
|
||||
b.carriageReturn()
|
||||
b.newLine()
|
||||
writeRaw(b, []rune("hello 14")...)
|
||||
|
||||
lines := b.GetVisibleLines()
|
||||
require.Equal(t, 10, len(lines))
|
||||
assert.Equal(t, "hello 5", lines[0].String())
|
||||
assert.Equal(t, "hello 14", lines[9].String())
|
||||
|
||||
}
|
||||
|
||||
func TestClearWithoutFullView(t *testing.T) {
|
||||
b := makeBufferForTesting(80, 10)
|
||||
writeRaw(b, []rune("hello 1")...)
|
||||
b.carriageReturn()
|
||||
b.newLine()
|
||||
writeRaw(b, []rune("hello 1")...)
|
||||
b.carriageReturn()
|
||||
b.newLine()
|
||||
writeRaw(b, []rune("hello 1")...)
|
||||
b.clear()
|
||||
lines := b.GetVisibleLines()
|
||||
for _, line := range lines {
|
||||
assert.Equal(t, "", line.String())
|
||||
}
|
||||
}
|
||||
|
||||
func TestClearWithFullView(t *testing.T) {
|
||||
b := makeBufferForTesting(80, 5)
|
||||
writeRaw(b, []rune("hello 1")...)
|
||||
b.carriageReturn()
|
||||
b.newLine()
|
||||
writeRaw(b, []rune("hello 1")...)
|
||||
b.carriageReturn()
|
||||
b.newLine()
|
||||
writeRaw(b, []rune("hello 1")...)
|
||||
b.carriageReturn()
|
||||
b.newLine()
|
||||
writeRaw(b, []rune("hello 1")...)
|
||||
b.carriageReturn()
|
||||
b.newLine()
|
||||
writeRaw(b, []rune("hello 1")...)
|
||||
b.carriageReturn()
|
||||
b.newLine()
|
||||
writeRaw(b, []rune("hello 1")...)
|
||||
b.carriageReturn()
|
||||
b.newLine()
|
||||
writeRaw(b, []rune("hello 1")...)
|
||||
b.carriageReturn()
|
||||
b.newLine()
|
||||
writeRaw(b, []rune("hello 1")...)
|
||||
b.clear()
|
||||
lines := b.GetVisibleLines()
|
||||
for _, line := range lines {
|
||||
assert.Equal(t, "", line.String())
|
||||
}
|
||||
}
|
||||
|
||||
func TestCarriageReturn(t *testing.T) {
|
||||
b := makeBufferForTesting(80, 20)
|
||||
writeRaw(b, []rune("hello!")...)
|
||||
b.carriageReturn()
|
||||
writeRaw(b, []rune("secret")...)
|
||||
lines := b.GetVisibleLines()
|
||||
assert.Equal(t, "secret", lines[0].String())
|
||||
}
|
||||
|
||||
func TestCarriageReturnOnFullLine(t *testing.T) {
|
||||
b := makeBufferForTesting(20, 20)
|
||||
writeRaw(b, []rune("abcdeabcdeabcdeabcde")...)
|
||||
b.carriageReturn()
|
||||
writeRaw(b, []rune("xxxxxxxxxxxxxxxxxxxx")...)
|
||||
lines := b.GetVisibleLines()
|
||||
assert.Equal(t, "xxxxxxxxxxxxxxxxxxxx", lines[0].String())
|
||||
}
|
||||
|
||||
func TestCarriageReturnOnFullLastLine(t *testing.T) {
|
||||
b := makeBufferForTesting(20, 2)
|
||||
b.newLine()
|
||||
writeRaw(b, []rune("abcdeabcdeabcdeabcde")...)
|
||||
b.carriageReturn()
|
||||
writeRaw(b, []rune("xxxxxxxxxxxxxxxxxxxx")...)
|
||||
lines := b.GetVisibleLines()
|
||||
assert.Equal(t, "", lines[0].String())
|
||||
assert.Equal(t, "xxxxxxxxxxxxxxxxxxxx", lines[1].String())
|
||||
}
|
||||
|
||||
func TestCarriageReturnOnWrappedLine(t *testing.T) {
|
||||
b := makeBufferForTesting(80, 6)
|
||||
writeRaw(b, []rune("hello!")...)
|
||||
b.carriageReturn()
|
||||
writeRaw(b, []rune("secret")...)
|
||||
|
||||
lines := b.GetVisibleLines()
|
||||
assert.Equal(t, "secret", lines[0].String())
|
||||
}
|
||||
|
||||
func TestCarriageReturnOnLineThatDoesntExist(t *testing.T) {
|
||||
b := makeBufferForTesting(6, 10)
|
||||
b.cursorPosition.Line = 3
|
||||
b.carriageReturn()
|
||||
assert.Equal(t, uint16(0), b.cursorPosition.Col)
|
||||
assert.Equal(t, uint64(3), b.cursorPosition.Line)
|
||||
}
|
||||
|
||||
func TestGetCell(t *testing.T) {
|
||||
b := makeBufferForTesting(80, 20)
|
||||
writeRaw(b, []rune("Hello")...)
|
||||
b.carriageReturn()
|
||||
b.newLine()
|
||||
|
||||
writeRaw(b, []rune("there")...)
|
||||
b.carriageReturn()
|
||||
b.newLine()
|
||||
|
||||
writeRaw(b, []rune("something...")...)
|
||||
cell := b.GetCell(8, 2)
|
||||
require.NotNil(t, cell)
|
||||
assert.Equal(t, 'g', cell.Rune().Rune)
|
||||
}
|
||||
|
||||
func TestGetCellWithHistory(t *testing.T) {
|
||||
b := makeBufferForTesting(80, 2)
|
||||
|
||||
writeRaw(b, []rune("Hello")...)
|
||||
b.carriageReturn()
|
||||
b.newLine()
|
||||
|
||||
writeRaw(b, []rune("there")...)
|
||||
b.carriageReturn()
|
||||
b.newLine()
|
||||
|
||||
writeRaw(b, []rune("something...")...)
|
||||
|
||||
cell := b.GetCell(8, 1)
|
||||
require.NotNil(t, cell)
|
||||
assert.Equal(t, 'g', cell.Rune().Rune)
|
||||
}
|
||||
|
||||
func TestGetCellWithBadCursor(t *testing.T) {
|
||||
b := makeBufferForTesting(80, 2)
|
||||
writeRaw(b, []rune("Hello\r\nthere\r\nsomething...")...)
|
||||
require.Nil(t, b.GetCell(8, 3))
|
||||
require.Nil(t, b.GetCell(90, 0))
|
||||
|
||||
}
|
||||
|
||||
func TestCursorPositionQuerying(t *testing.T) {
|
||||
b := makeBufferForTesting(80, 20)
|
||||
b.cursorPosition.Col = 17
|
||||
b.cursorPosition.Line = 9
|
||||
assert.Equal(t, b.cursorPosition.Col, b.CursorColumn())
|
||||
assert.Equal(t, b.convertRawLineToViewLine(b.cursorPosition.Line), b.CursorLine())
|
||||
}
|
||||
|
||||
// CSI 2 K
|
||||
func TestEraseLine(t *testing.T) {
|
||||
b := makeBufferForTesting(80, 5)
|
||||
writeRaw(b, []rune("hello, this is a test")...)
|
||||
b.carriageReturn()
|
||||
b.newLine()
|
||||
writeRaw(b, []rune("this line should be deleted")...)
|
||||
b.eraseLine()
|
||||
assert.Equal(t, "hello, this is a test", b.lines[0].String())
|
||||
assert.Equal(t, "", b.lines[1].String())
|
||||
}
|
||||
|
||||
// CSI 1 K
|
||||
func TestEraseLineToCursor(t *testing.T) {
|
||||
b := makeBufferForTesting(80, 5)
|
||||
writeRaw(b, []rune("hello, this is a test")...)
|
||||
b.carriageReturn()
|
||||
b.newLine()
|
||||
writeRaw(b, []rune("deleted")...)
|
||||
|
||||
b.movePosition(-3, 0)
|
||||
b.eraseLineToCursor()
|
||||
assert.Equal(t, "hello, this is a test", b.lines[0].String())
|
||||
assert.Equal(t, "\x00\x00\x00\x00\x00ed", b.lines[1].String())
|
||||
}
|
||||
|
||||
// CSI 0 K
|
||||
func TestEraseLineAfterCursor(t *testing.T) {
|
||||
b := makeBufferForTesting(80, 5)
|
||||
writeRaw(b, []rune("hello, this is a test")...)
|
||||
b.carriageReturn()
|
||||
b.newLine()
|
||||
writeRaw(b, []rune("deleted")...)
|
||||
b.movePosition(-3, 0)
|
||||
b.eraseLineFromCursor()
|
||||
assert.Equal(t, "hello, this is a test", b.lines[0].String())
|
||||
assert.Equal(t, "dele", b.lines[1].String())
|
||||
}
|
||||
func TestEraseDisplay(t *testing.T) {
|
||||
b := makeBufferForTesting(80, 5)
|
||||
writeRaw(b, []rune("hello")...)
|
||||
b.carriageReturn()
|
||||
b.newLine()
|
||||
writeRaw(b, []rune("asdasd")...)
|
||||
b.carriageReturn()
|
||||
b.newLine()
|
||||
writeRaw(b, []rune("thing")...)
|
||||
b.movePosition(2, 1)
|
||||
b.eraseDisplay()
|
||||
lines := b.GetVisibleLines()
|
||||
for _, line := range lines {
|
||||
assert.Equal(t, "", line.String())
|
||||
}
|
||||
}
|
||||
func TestEraseDisplayToCursor(t *testing.T) {
|
||||
b := makeBufferForTesting(80, 5)
|
||||
writeRaw(b, []rune("hello")...)
|
||||
b.carriageReturn()
|
||||
b.newLine()
|
||||
writeRaw(b, []rune("asdasd")...)
|
||||
b.carriageReturn()
|
||||
b.newLine()
|
||||
writeRaw(b, []rune("thing")...)
|
||||
b.movePosition(-2, 0)
|
||||
b.eraseDisplayToCursor()
|
||||
lines := b.GetVisibleLines()
|
||||
assert.Equal(t, "", lines[0].String())
|
||||
assert.Equal(t, "", lines[1].String())
|
||||
assert.Equal(t, "\x00\x00\x00\x00g", lines[2].String())
|
||||
|
||||
}
|
||||
|
||||
func TestEraseDisplayFromCursor(t *testing.T) {
|
||||
b := makeBufferForTesting(80, 5)
|
||||
writeRaw(b, []rune("hello")...)
|
||||
b.carriageReturn()
|
||||
b.newLine()
|
||||
writeRaw(b, []rune("asdasd")...)
|
||||
b.carriageReturn()
|
||||
b.newLine()
|
||||
writeRaw(b, []rune("things")...)
|
||||
b.movePosition(-3, -1)
|
||||
b.eraseDisplayFromCursor()
|
||||
lines := b.GetVisibleLines()
|
||||
assert.Equal(t, "hello", lines[0].String())
|
||||
assert.Equal(t, "asd", lines[1].String())
|
||||
assert.Equal(t, "", lines[2].String())
|
||||
}
|
||||
func TestBackspace(t *testing.T) {
|
||||
b := makeBufferForTesting(80, 5)
|
||||
writeRaw(b, []rune("hello")...)
|
||||
b.backspace()
|
||||
b.backspace()
|
||||
writeRaw(b, []rune("p")...)
|
||||
lines := b.GetVisibleLines()
|
||||
assert.Equal(t, "helpo", lines[0].String())
|
||||
}
|
||||
|
||||
func TestHorizontalResizeView(t *testing.T) {
|
||||
b := makeBufferForTesting(80, 10)
|
||||
|
||||
// 60 characters
|
||||
writeRaw(b, []rune(`hellohellohellohellohellohellohellohellohellohellohellohello`)...)
|
||||
|
||||
b.carriageReturn()
|
||||
b.newLine()
|
||||
|
||||
writeRaw(b, []rune(`goodbyegoodbye`)...)
|
||||
|
||||
require.Equal(t, uint16(14), b.cursorPosition.Col)
|
||||
require.Equal(t, uint64(1), b.cursorPosition.Line)
|
||||
|
||||
b.resizeView(40, 10)
|
||||
|
||||
expected := `hellohellohellohellohellohellohellohello
|
||||
hellohellohellohello
|
||||
goodbyegoodbye`
|
||||
|
||||
require.Equal(t, uint16(14), b.cursorPosition.Col)
|
||||
require.Equal(t, uint64(2), b.cursorPosition.Line)
|
||||
|
||||
lines := b.GetVisibleLines()
|
||||
strs := []string{}
|
||||
for _, l := range lines {
|
||||
strs = append(strs, l.String())
|
||||
}
|
||||
require.Equal(t, expected, strings.Join(strs, "\n"))
|
||||
|
||||
b.resizeView(20, 10)
|
||||
|
||||
expected = `hellohellohellohello
|
||||
hellohellohellohello
|
||||
hellohellohellohello
|
||||
goodbyegoodbye`
|
||||
|
||||
lines = b.GetVisibleLines()
|
||||
strs = []string{}
|
||||
for _, l := range lines {
|
||||
strs = append(strs, l.String())
|
||||
}
|
||||
require.Equal(t, expected, strings.Join(strs, "\n"))
|
||||
|
||||
b.resizeView(10, 10)
|
||||
|
||||
expected = `hellohello
|
||||
hellohello
|
||||
hellohello
|
||||
hellohello
|
||||
hellohello
|
||||
hellohello
|
||||
goodbyegoo
|
||||
dbye`
|
||||
|
||||
lines = b.GetVisibleLines()
|
||||
strs = []string{}
|
||||
for _, l := range lines {
|
||||
strs = append(strs, l.String())
|
||||
}
|
||||
require.Equal(t, expected, strings.Join(strs, "\n"))
|
||||
|
||||
b.resizeView(80, 20)
|
||||
|
||||
expected = `hellohellohellohellohellohellohellohellohellohellohellohello
|
||||
goodbyegoodbye`
|
||||
|
||||
lines = b.GetVisibleLines()
|
||||
strs = []string{}
|
||||
for _, l := range lines {
|
||||
strs = append(strs, l.String())
|
||||
}
|
||||
require.Equal(t, expected, strings.Join(strs, "\n"))
|
||||
|
||||
require.Equal(t, uint16(4), b.cursorPosition.Col)
|
||||
require.Equal(t, uint64(1), b.cursorPosition.Line)
|
||||
|
||||
}
|
||||
|
||||
func TestBufferMaxLines(t *testing.T) {
|
||||
b := NewBuffer(80, 2, 2, color.White, color.Black)
|
||||
b.modes.LineFeedMode = false
|
||||
|
||||
writeRaw(b, []rune("hello")...)
|
||||
b.newLine()
|
||||
writeRaw(b, []rune("funny")...)
|
||||
b.newLine()
|
||||
writeRaw(b, []rune("world")...)
|
||||
|
||||
assert.Equal(t, 2, len(b.lines))
|
||||
assert.Equal(t, "funny", b.lines[0].String())
|
||||
assert.Equal(t, "world", b.lines[1].String())
|
||||
}
|
||||
|
||||
func TestShrinkingThenGrowing(t *testing.T) {
|
||||
b := makeBufferForTesting(30, 100)
|
||||
writeRaw(b, []rune("hellohellohellohellohello")...)
|
||||
b.carriageReturn()
|
||||
b.newLine()
|
||||
writeRaw(b, []rune("01234567890123456789")...)
|
||||
b.carriageReturn()
|
||||
b.newLine()
|
||||
|
||||
b.resizeView(25, 100)
|
||||
b.resizeView(24, 100)
|
||||
|
||||
b.resizeView(30, 100)
|
||||
|
||||
expected := `hellohellohellohellohello
|
||||
01234567890123456789
|
||||
`
|
||||
lines := b.GetVisibleLines()
|
||||
var strs []string
|
||||
for _, l := range lines {
|
||||
strs = append(strs, l.String())
|
||||
}
|
||||
require.Equal(t, expected, strings.Join(strs, "\n"))
|
||||
}
|
||||
|
||||
func TestShrinkingThenRestoring(t *testing.T) {
|
||||
b := makeBufferForTesting(30, 100)
|
||||
writeRaw(b, []rune("hellohellohellohellohello")...)
|
||||
b.carriageReturn()
|
||||
b.newLine()
|
||||
writeRaw(b, []rune("01234567890123456789")...)
|
||||
b.carriageReturn()
|
||||
b.newLine()
|
||||
|
||||
b.cursorPosition.Line = 2
|
||||
|
||||
for i := uint16(29); i > 5; i-- {
|
||||
b.resizeView(i, 100)
|
||||
}
|
||||
|
||||
for i := uint16(15); i < 30; i++ {
|
||||
b.resizeView(i, 100)
|
||||
}
|
||||
|
||||
expected := `hellohellohellohellohello
|
||||
01234567890123456789
|
||||
`
|
||||
lines := b.GetVisibleLines()
|
||||
var strs []string
|
||||
for _, l := range lines {
|
||||
strs = append(strs, l.String())
|
||||
}
|
||||
require.Equal(t, expected, strings.Join(strs, "\n"))
|
||||
}
|
||||
|
||||
func makeBufferForTesting(cols, rows uint16) *Buffer {
|
||||
return NewBuffer(cols, rows, 100, color.White, color.Black)
|
||||
}
|
59
termutil/cell.go
Normal file
59
termutil/cell.go
Normal file
@ -0,0 +1,59 @@
|
||||
package termutil
|
||||
|
||||
import "image/color"
|
||||
|
||||
type Cell struct {
|
||||
r MeasuredRune
|
||||
attr CellAttributes
|
||||
}
|
||||
|
||||
func (cell *Cell) Attr() CellAttributes {
|
||||
return cell.attr
|
||||
}
|
||||
|
||||
func (cell *Cell) Rune() MeasuredRune {
|
||||
return cell.r
|
||||
}
|
||||
|
||||
func (cell *Cell) Fg() color.Color {
|
||||
if cell.Attr().inverse {
|
||||
return cell.attr.bgColour
|
||||
}
|
||||
return cell.attr.fgColour
|
||||
}
|
||||
|
||||
func (cell *Cell) Bold() bool {
|
||||
return cell.attr.bold
|
||||
}
|
||||
|
||||
func (cell *Cell) Dim() bool {
|
||||
return cell.attr.dim
|
||||
}
|
||||
|
||||
func (cell *Cell) Italic() bool {
|
||||
return cell.attr.italic
|
||||
}
|
||||
|
||||
func (cell *Cell) Underline() bool {
|
||||
return cell.attr.underline
|
||||
}
|
||||
|
||||
func (cell *Cell) Strikethrough() bool {
|
||||
return cell.attr.strikethrough
|
||||
}
|
||||
|
||||
func (cell *Cell) Bg() color.Color {
|
||||
if cell.Attr().inverse {
|
||||
return cell.attr.fgColour
|
||||
}
|
||||
return cell.attr.bgColour
|
||||
}
|
||||
|
||||
func (cell *Cell) erase(bgColour color.Color) {
|
||||
cell.setRune(MeasuredRune{Rune: 0})
|
||||
cell.attr.bgColour = bgColour
|
||||
}
|
||||
|
||||
func (cell *Cell) setRune(r MeasuredRune) {
|
||||
cell.r = r
|
||||
}
|
@ -1,12 +1,12 @@
|
||||
package voidterm
|
||||
package termutil
|
||||
|
||||
import (
|
||||
"image/color"
|
||||
)
|
||||
|
||||
type CellAttributes struct {
|
||||
fg color.Color
|
||||
bg color.Color
|
||||
fgColour color.Color
|
||||
bgColour color.Color
|
||||
bold bool
|
||||
italic bool
|
||||
dim bool
|
64
termutil/charsets.go
Normal file
64
termutil/charsets.go
Normal file
@ -0,0 +1,64 @@
|
||||
package termutil
|
||||
|
||||
var charSets = map[rune]*map[rune]rune{
|
||||
'0': &decSpecGraphics,
|
||||
'B': nil, // ASCII
|
||||
// @todo 1,2,A
|
||||
}
|
||||
|
||||
var decSpecGraphics = map[rune]rune{
|
||||
0x5f: 0x00A0, // NO-BREAK SPACE
|
||||
0x60: 0x25C6, // BLACK DIAMOND
|
||||
0x61: 0x2592, // MEDIUM SHADE
|
||||
0x62: 0x2409, // SYMBOL FOR HORIZONTAL TABULATION
|
||||
0x63: 0x240C, // SYMBOL FOR FORM FEED
|
||||
0x64: 0x240D, // SYMBOL FOR CARRIAGE RETURN
|
||||
0x65: 0x240A, // SYMBOL FOR LINE FEED
|
||||
0x66: 0x00B0, // DEGREE SIGN
|
||||
0x67: 0x00B1, // PLUS-MINUS SIGN
|
||||
0x68: 0x2424, // SYMBOL FOR NEWLINE
|
||||
0x69: 0x240B, // SYMBOL FOR VERTICAL TABULATION
|
||||
0x6a: 0x2518, // BOX DRAWINGS LIGHT UP AND LEFT
|
||||
0x6b: 0x2510, // BOX DRAWINGS LIGHT DOWN AND LEFT
|
||||
0x6c: 0x250C, // BOX DRAWINGS LIGHT DOWN AND RIGHT
|
||||
0x6d: 0x2514, // BOX DRAWINGS LIGHT UP AND RIGHT
|
||||
0x6e: 0x253C, // BOX DRAWINGS LIGHT VERTICAL AND HORIZONTAL
|
||||
0x6f: 0x23BA, // HORIZONTAL SCAN LINE-1
|
||||
0x70: 0x23BB, // HORIZONTAL SCAN LINE-3
|
||||
0x71: 0x2500, // BOX DRAWINGS LIGHT HORIZONTAL
|
||||
0x72: 0x23BC, // HORIZONTAL SCAN LINE-7
|
||||
0x73: 0x23BD, // HORIZONTAL SCAN LINE-9
|
||||
0x74: 0x251C, // BOX DRAWINGS LIGHT VERTICAL AND RIGHT
|
||||
0x75: 0x2524, // BOX DRAWINGS LIGHT VERTICAL AND LEFT
|
||||
0x76: 0x2534, // BOX DRAWINGS LIGHT UP AND HORIZONTAL
|
||||
0x77: 0x252C, // BOX DRAWINGS LIGHT DOWN AND HORIZONTAL
|
||||
0x78: 0x2502, // BOX DRAWINGS LIGHT VERTICAL
|
||||
0x79: 0x2264, // LESS-THAN OR EQUAL TO
|
||||
0x7a: 0x2265, // GREATER-THAN OR EQUAL TO
|
||||
0x7b: 0x03C0, // GREEK SMALL LETTER PI
|
||||
0x7c: 0x2260, // NOT EQUAL TO
|
||||
0x7d: 0x00A3, // POUND SIGN
|
||||
0x7e: 0x00B7, // MIDDLE DOT
|
||||
}
|
||||
|
||||
func (t *Terminal) handleSCS0(pty chan MeasuredRune) bool {
|
||||
return t.scsHandler(pty, 0)
|
||||
}
|
||||
|
||||
func (t *Terminal) handleSCS1(pty chan MeasuredRune) bool {
|
||||
return t.scsHandler(pty, 1)
|
||||
}
|
||||
|
||||
func (t *Terminal) scsHandler(pty chan MeasuredRune, which int) bool {
|
||||
b := <-pty
|
||||
|
||||
cs, ok := charSets[b.Rune]
|
||||
if ok {
|
||||
//terminal.logger.Debugf("Selected charset %v into G%v", string(b), which)
|
||||
t.activeBuffer.charsets[which] = cs
|
||||
return false
|
||||
}
|
||||
|
||||
t.activeBuffer.charsets[which] = nil
|
||||
return false
|
||||
}
|
5
termutil/consts_unix.go
Normal file
5
termutil/consts_unix.go
Normal file
@ -0,0 +1,5 @@
|
||||
//+build !windows
|
||||
|
||||
package termutil
|
||||
|
||||
var oscTerminators = []rune{0x07, 0x5c}
|
5
termutil/consts_windows.go
Normal file
5
termutil/consts_windows.go
Normal file
@ -0,0 +1,5 @@
|
||||
//+build windows
|
||||
|
||||
package termutil
|
||||
|
||||
var oscTerminators = []rune{0x07, 0x00}
|
997
termutil/csi.go
Normal file
997
termutil/csi.go
Normal file
@ -0,0 +1,997 @@
|
||||
package termutil
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"strconv"
|
||||
"strings"
|
||||
)
|
||||
|
||||
func parseCSI(readChan chan MeasuredRune) (final rune, params []string, intermediate []rune, raw []rune) {
|
||||
var b MeasuredRune
|
||||
|
||||
param := ""
|
||||
intermediate = []rune{}
|
||||
CSI:
|
||||
for {
|
||||
b = <-readChan
|
||||
raw = append(raw, b.Rune)
|
||||
switch true {
|
||||
case b.Rune >= 0x30 && b.Rune <= 0x3F:
|
||||
param = param + string(b.Rune)
|
||||
case b.Rune > 0 && b.Rune <= 0x2F:
|
||||
intermediate = append(intermediate, b.Rune)
|
||||
case b.Rune >= 0x40 && b.Rune <= 0x7e:
|
||||
final = b.Rune
|
||||
break CSI
|
||||
}
|
||||
}
|
||||
|
||||
unprocessed := strings.Split(param, ";")
|
||||
for _, par := range unprocessed {
|
||||
if par != "" {
|
||||
par = strings.TrimLeft(par, "0")
|
||||
if par == "" {
|
||||
par = "0"
|
||||
}
|
||||
params = append(params, par)
|
||||
}
|
||||
}
|
||||
|
||||
return final, params, intermediate, raw
|
||||
}
|
||||
|
||||
func (t *Terminal) handleCSI(readChan chan MeasuredRune) (renderRequired bool) {
|
||||
final, params, intermediate, raw := parseCSI(readChan)
|
||||
|
||||
t.log("CSI P(%q) I(%q) %c", strings.Join(params, ";"), string(intermediate), final)
|
||||
|
||||
switch final {
|
||||
case 'c':
|
||||
return t.csiSendDeviceAttributesHandler(params)
|
||||
case 'd':
|
||||
return t.csiLinePositionAbsoluteHandler(params)
|
||||
case 'f':
|
||||
return t.csiCursorPositionHandler(params)
|
||||
case 'g':
|
||||
return t.csiTabClearHandler(params)
|
||||
case 'h':
|
||||
return t.csiSetModeHandler(params)
|
||||
case 'l':
|
||||
return t.csiResetModeHandler(params)
|
||||
case 'm':
|
||||
return t.sgrSequenceHandler(params)
|
||||
case 'n':
|
||||
return t.csiDeviceStatusReportHandler(params)
|
||||
case 'r':
|
||||
return t.csiSetMarginsHandler(params)
|
||||
case 't':
|
||||
return t.csiWindowManipulation(params)
|
||||
case 'q':
|
||||
if string(intermediate) == " " {
|
||||
return t.csiCursorSelection(params)
|
||||
}
|
||||
case 'A':
|
||||
return t.csiCursorUpHandler(params)
|
||||
case 'B':
|
||||
return t.csiCursorDownHandler(params)
|
||||
case 'C':
|
||||
return t.csiCursorForwardHandler(params)
|
||||
case 'D':
|
||||
return t.csiCursorBackwardHandler(params)
|
||||
case 'E':
|
||||
return t.csiCursorNextLineHandler(params)
|
||||
case 'F':
|
||||
return t.csiCursorPrecedingLineHandler(params)
|
||||
case 'G':
|
||||
return t.csiCursorCharacterAbsoluteHandler(params)
|
||||
case 'H':
|
||||
return t.csiCursorPositionHandler(params)
|
||||
case 'J':
|
||||
return t.csiEraseInDisplayHandler(params)
|
||||
case 'K':
|
||||
return t.csiEraseInLineHandler(params)
|
||||
case 'L':
|
||||
return t.csiInsertLinesHandler(params)
|
||||
case 'M':
|
||||
return t.csiDeleteLinesHandler(params)
|
||||
case 'P':
|
||||
return t.csiDeleteHandler(params)
|
||||
case 'S':
|
||||
return t.csiScrollUpHandler(params)
|
||||
case 'T':
|
||||
return t.csiScrollDownHandler(params)
|
||||
case 'X':
|
||||
return t.csiEraseCharactersHandler(params)
|
||||
case '@':
|
||||
return t.csiInsertBlankCharactersHandler(params)
|
||||
case 'p': // reset handler
|
||||
if string(intermediate) == "!" {
|
||||
return t.csiSoftResetHandler(params)
|
||||
}
|
||||
return false
|
||||
}
|
||||
|
||||
for _, b := range intermediate {
|
||||
t.processRunes(MeasuredRune{
|
||||
Rune: b,
|
||||
Width: 1,
|
||||
})
|
||||
}
|
||||
|
||||
// TODO review this:
|
||||
// if this is an unknown CSI sequence, write it to stdout as we can't handle it?
|
||||
//_ = t.writeToRealStdOut(append([]rune{0x1b, '['}, raw...)...)
|
||||
_ = raw
|
||||
t.log("UNKNOWN CSI P(%s) I(%s) %c", strings.Join(params, ";"), string(intermediate), final)
|
||||
return false
|
||||
|
||||
}
|
||||
|
||||
type WindowState uint8
|
||||
|
||||
const (
|
||||
StateUnknown WindowState = iota
|
||||
StateMinimised
|
||||
StateNormal
|
||||
StateMaximised
|
||||
)
|
||||
|
||||
type WindowManipulator interface {
|
||||
State() WindowState
|
||||
Minimise()
|
||||
Maximise()
|
||||
Restore()
|
||||
SetTitle(title string)
|
||||
Position() (int, int)
|
||||
SizeInPixels() (int, int)
|
||||
CellSizeInPixels() (int, int)
|
||||
SizeInChars() (int, int)
|
||||
ResizeInPixels(int, int)
|
||||
ResizeInChars(int, int)
|
||||
ScreenSizeInPixels() (int, int)
|
||||
ScreenSizeInChars() (int, int)
|
||||
Move(x, y int)
|
||||
IsFullscreen() bool
|
||||
SetFullscreen(enabled bool)
|
||||
GetTitle() string
|
||||
SaveTitleToStack()
|
||||
RestoreTitleFromStack()
|
||||
ReportError(err error)
|
||||
}
|
||||
|
||||
func (t *Terminal) csiWindowManipulation(params []string) (renderRequired bool) {
|
||||
|
||||
if t.windowManipulator == nil {
|
||||
return false
|
||||
}
|
||||
|
||||
for i := 0; i < len(params); i++ {
|
||||
switch params[i] {
|
||||
case "1":
|
||||
t.windowManipulator.Restore()
|
||||
case "2":
|
||||
t.windowManipulator.Minimise()
|
||||
case "3": //move window
|
||||
if i+2 >= len(params) {
|
||||
return false
|
||||
}
|
||||
x, _ := strconv.Atoi(params[i+1])
|
||||
y, _ := strconv.Atoi(params[i+2])
|
||||
i += 2
|
||||
t.windowManipulator.Move(x, y)
|
||||
case "4": //resize h,w
|
||||
w, h := t.windowManipulator.SizeInPixels()
|
||||
if i+1 < len(params) {
|
||||
h, _ = strconv.Atoi(params[i+1])
|
||||
i++
|
||||
}
|
||||
if i+2 < len(params) {
|
||||
w, _ = strconv.Atoi(params[i+2])
|
||||
i++
|
||||
}
|
||||
sw, sh := t.windowManipulator.ScreenSizeInPixels()
|
||||
if w == 0 {
|
||||
w = sw
|
||||
}
|
||||
if h == 0 {
|
||||
h = sh
|
||||
}
|
||||
t.windowManipulator.ResizeInPixels(w, h)
|
||||
case "8":
|
||||
// resize in rows, cols
|
||||
w, h := t.windowManipulator.SizeInChars()
|
||||
if i+1 < len(params) {
|
||||
h, _ = strconv.Atoi(params[i+1])
|
||||
i++
|
||||
}
|
||||
if i+2 < len(params) {
|
||||
w, _ = strconv.Atoi(params[i+2])
|
||||
i++
|
||||
}
|
||||
sw, sh := t.windowManipulator.ScreenSizeInChars()
|
||||
if w == 0 {
|
||||
w = sw
|
||||
}
|
||||
if h == 0 {
|
||||
h = sh
|
||||
}
|
||||
t.windowManipulator.ResizeInChars(w, h)
|
||||
case "9":
|
||||
if i+1 >= len(params) {
|
||||
return false
|
||||
}
|
||||
switch params[i+1] {
|
||||
case "0":
|
||||
t.windowManipulator.Restore()
|
||||
case "1":
|
||||
t.windowManipulator.Maximise()
|
||||
case "2":
|
||||
w, _ := t.windowManipulator.SizeInPixels()
|
||||
_, sh := t.windowManipulator.ScreenSizeInPixels()
|
||||
t.windowManipulator.ResizeInPixels(w, sh)
|
||||
case "3":
|
||||
_, h := t.windowManipulator.SizeInPixels()
|
||||
sw, _ := t.windowManipulator.ScreenSizeInPixels()
|
||||
t.windowManipulator.ResizeInPixels(sw, h)
|
||||
}
|
||||
i++
|
||||
case "10":
|
||||
if i+1 >= len(params) {
|
||||
return false
|
||||
}
|
||||
switch params[i+1] {
|
||||
case "0":
|
||||
t.windowManipulator.SetFullscreen(false)
|
||||
case "1":
|
||||
t.windowManipulator.SetFullscreen(true)
|
||||
case "2":
|
||||
// toggle
|
||||
t.windowManipulator.SetFullscreen(!t.windowManipulator.IsFullscreen())
|
||||
}
|
||||
i++
|
||||
|
||||
case "11":
|
||||
if t.windowManipulator.State() != StateMinimised {
|
||||
t.WriteToPty([]byte("\x1b[1t"))
|
||||
} else {
|
||||
t.WriteToPty([]byte("\x1b[2t"))
|
||||
}
|
||||
case "13":
|
||||
if i < len(params)-1 {
|
||||
i++
|
||||
}
|
||||
x, y := t.windowManipulator.Position()
|
||||
t.WriteToPty([]byte(fmt.Sprintf("\x1b[3;%d;%dt", x, y)))
|
||||
case "14":
|
||||
if i < len(params)-1 {
|
||||
i++
|
||||
}
|
||||
w, h := t.windowManipulator.SizeInPixels()
|
||||
t.WriteToPty([]byte(fmt.Sprintf("\x1b[4;%d;%dt", h, w)))
|
||||
case "15":
|
||||
w, h := t.windowManipulator.ScreenSizeInPixels()
|
||||
t.WriteToPty([]byte(fmt.Sprintf("\x1b[5;%d;%dt", h, w)))
|
||||
case "16":
|
||||
w, h := t.windowManipulator.CellSizeInPixels()
|
||||
t.WriteToPty([]byte(fmt.Sprintf("\x1b[6;%d;%dt", h, w)))
|
||||
case "18":
|
||||
w, h := t.windowManipulator.SizeInChars()
|
||||
t.WriteToPty([]byte(fmt.Sprintf("\x1b[8;%d;%dt", h, w)))
|
||||
case "19":
|
||||
w, h := t.windowManipulator.ScreenSizeInChars()
|
||||
t.WriteToPty([]byte(fmt.Sprintf("\x1b[9;%d;%dt", h, w)))
|
||||
case "20":
|
||||
t.WriteToPty([]byte(fmt.Sprintf("\x1b]L%s\x1b\\", t.windowManipulator.GetTitle())))
|
||||
case "21":
|
||||
t.WriteToPty([]byte(fmt.Sprintf("\x1b]l%s\x1b\\", t.windowManipulator.GetTitle())))
|
||||
case "22":
|
||||
if i < len(params)-1 {
|
||||
i++
|
||||
}
|
||||
t.windowManipulator.SaveTitleToStack()
|
||||
case "23":
|
||||
if i < len(params)-1 {
|
||||
i++
|
||||
}
|
||||
t.windowManipulator.RestoreTitleFromStack()
|
||||
}
|
||||
}
|
||||
|
||||
return true
|
||||
}
|
||||
|
||||
// CSI c
|
||||
// Send Device Attributes (Primary/Secondary/Tertiary DA)
|
||||
func (t *Terminal) csiSendDeviceAttributesHandler(params []string) (renderRequired bool) {
|
||||
|
||||
// we are VT100
|
||||
// for DA1 we'll respond ?1;2
|
||||
// for DA2 we'll respond >0;0;0
|
||||
response := "?1;2"
|
||||
if len(params) > 0 && len(params[0]) > 0 && params[0][0] == '>' {
|
||||
response = ">0;0;0"
|
||||
}
|
||||
|
||||
// write response to source pty
|
||||
t.WriteToPty([]byte("\x1b[" + response + "c"))
|
||||
return false
|
||||
}
|
||||
|
||||
// CSI n
|
||||
// Device Status Report (DSR)
|
||||
func (t *Terminal) csiDeviceStatusReportHandler(params []string) (renderRequired bool) {
|
||||
|
||||
if len(params) == 0 {
|
||||
return false
|
||||
}
|
||||
|
||||
switch params[0] {
|
||||
case "5":
|
||||
t.WriteToPty([]byte("\x1b[0n")) // everything is cool
|
||||
case "6": // report cursor position
|
||||
t.WriteToPty([]byte(fmt.Sprintf(
|
||||
"\x1b[%d;%dR",
|
||||
t.GetActiveBuffer().CursorLine()+1,
|
||||
t.GetActiveBuffer().CursorColumn()+1,
|
||||
)))
|
||||
}
|
||||
|
||||
return false
|
||||
}
|
||||
|
||||
// CSI A
|
||||
// Cursor Up Ps Times (default = 1) (CUU)
|
||||
func (t *Terminal) csiCursorUpHandler(params []string) (renderRequired bool) {
|
||||
distance := 1
|
||||
if len(params) > 0 {
|
||||
var err error
|
||||
distance, err = strconv.Atoi(params[0])
|
||||
if err != nil || distance < 1 {
|
||||
distance = 1
|
||||
}
|
||||
}
|
||||
t.GetActiveBuffer().movePosition(0, -int16(distance))
|
||||
return true
|
||||
}
|
||||
|
||||
// CSI B
|
||||
// Cursor Down Ps Times (default = 1) (CUD)
|
||||
func (t *Terminal) csiCursorDownHandler(params []string) (renderRequired bool) {
|
||||
distance := 1
|
||||
if len(params) > 0 {
|
||||
var err error
|
||||
distance, err = strconv.Atoi(params[0])
|
||||
if err != nil || distance < 1 {
|
||||
distance = 1
|
||||
}
|
||||
}
|
||||
|
||||
t.GetActiveBuffer().movePosition(0, int16(distance))
|
||||
return true
|
||||
}
|
||||
|
||||
// CSI C
|
||||
// Cursor Forward Ps Times (default = 1) (CUF)
|
||||
func (t *Terminal) csiCursorForwardHandler(params []string) (renderRequired bool) {
|
||||
distance := 1
|
||||
if len(params) > 0 {
|
||||
var err error
|
||||
distance, err = strconv.Atoi(params[0])
|
||||
if err != nil || distance < 1 {
|
||||
distance = 1
|
||||
}
|
||||
}
|
||||
|
||||
t.GetActiveBuffer().movePosition(int16(distance), 0)
|
||||
return true
|
||||
}
|
||||
|
||||
// CSI D
|
||||
// Cursor Backward Ps Times (default = 1) (CUB)
|
||||
func (t *Terminal) csiCursorBackwardHandler(params []string) (renderRequired bool) {
|
||||
distance := 1
|
||||
if len(params) > 0 {
|
||||
var err error
|
||||
distance, err = strconv.Atoi(params[0])
|
||||
if err != nil || distance < 1 {
|
||||
distance = 1
|
||||
}
|
||||
}
|
||||
|
||||
t.GetActiveBuffer().movePosition(-int16(distance), 0)
|
||||
return true
|
||||
}
|
||||
|
||||
// CSI E
|
||||
// Cursor Next Line Ps Times (default = 1) (CNL)
|
||||
func (t *Terminal) csiCursorNextLineHandler(params []string) (renderRequired bool) {
|
||||
|
||||
distance := 1
|
||||
if len(params) > 0 {
|
||||
var err error
|
||||
distance, err = strconv.Atoi(params[0])
|
||||
if err != nil || distance < 1 {
|
||||
distance = 1
|
||||
}
|
||||
}
|
||||
|
||||
t.GetActiveBuffer().movePosition(0, int16(distance))
|
||||
t.GetActiveBuffer().setPosition(0, t.GetActiveBuffer().CursorLine())
|
||||
return true
|
||||
}
|
||||
|
||||
// CSI F
|
||||
// Cursor Preceding Line Ps Times (default = 1) (CPL)
|
||||
func (t *Terminal) csiCursorPrecedingLineHandler(params []string) (renderRequired bool) {
|
||||
|
||||
distance := 1
|
||||
if len(params) > 0 {
|
||||
var err error
|
||||
distance, err = strconv.Atoi(params[0])
|
||||
if err != nil || distance < 1 {
|
||||
distance = 1
|
||||
}
|
||||
}
|
||||
t.GetActiveBuffer().movePosition(0, -int16(distance))
|
||||
t.GetActiveBuffer().setPosition(0, t.GetActiveBuffer().CursorLine())
|
||||
return true
|
||||
}
|
||||
|
||||
// CSI G
|
||||
// Cursor Horizontal Absolute [column] (default = [row,1]) (CHA)
|
||||
func (t *Terminal) csiCursorCharacterAbsoluteHandler(params []string) (renderRequired bool) {
|
||||
distance := 1
|
||||
if len(params) > 0 {
|
||||
var err error
|
||||
distance, err = strconv.Atoi(params[0])
|
||||
if err != nil || params[0] == "" {
|
||||
distance = 1
|
||||
}
|
||||
}
|
||||
|
||||
t.GetActiveBuffer().setPosition(uint16(distance-1), t.GetActiveBuffer().CursorLine())
|
||||
return true
|
||||
}
|
||||
|
||||
func parseCursorPosition(params []string) (x, y int) {
|
||||
x, y = 1, 1
|
||||
if len(params) >= 1 {
|
||||
var err error
|
||||
if params[0] != "" {
|
||||
y, err = strconv.Atoi(string(params[0]))
|
||||
if err != nil || y < 1 {
|
||||
y = 1
|
||||
}
|
||||
}
|
||||
}
|
||||
if len(params) >= 2 {
|
||||
if params[1] != "" {
|
||||
var err error
|
||||
x, err = strconv.Atoi(string(params[1]))
|
||||
if err != nil || x < 1 {
|
||||
x = 1
|
||||
}
|
||||
}
|
||||
}
|
||||
return x, y
|
||||
}
|
||||
|
||||
// CSI f
|
||||
// Horizontal and Vertical Position [row;column] (default = [1,1]) (HVP)
|
||||
// AND
|
||||
// CSI H
|
||||
// Cursor Position [row;column] (default = [1,1]) (CUP)
|
||||
func (t *Terminal) csiCursorPositionHandler(params []string) (renderRequired bool) {
|
||||
x, y := parseCursorPosition(params)
|
||||
t.GetActiveBuffer().setPosition(uint16(x-1), uint16(y-1))
|
||||
return true
|
||||
}
|
||||
|
||||
// CSI S
|
||||
// Scroll up Ps lines (default = 1) (SU), VT420, ECMA-48
|
||||
func (t *Terminal) csiScrollUpHandler(params []string) (renderRequired bool) {
|
||||
distance := 1
|
||||
if len(params) > 1 {
|
||||
return false
|
||||
}
|
||||
if len(params) == 1 {
|
||||
var err error
|
||||
distance, err = strconv.Atoi(params[0])
|
||||
if err != nil || distance < 1 {
|
||||
distance = 1
|
||||
}
|
||||
}
|
||||
t.GetActiveBuffer().areaScrollUp(uint16(distance))
|
||||
return true
|
||||
}
|
||||
|
||||
// CSI @
|
||||
// Insert Ps (Blank) Character(s) (default = 1) (ICH)
|
||||
func (t *Terminal) csiInsertBlankCharactersHandler(params []string) (renderRequired bool) {
|
||||
count := 1
|
||||
if len(params) > 1 {
|
||||
return false
|
||||
}
|
||||
if len(params) == 1 {
|
||||
var err error
|
||||
count, err = strconv.Atoi(params[0])
|
||||
if err != nil || count < 1 {
|
||||
count = 1
|
||||
}
|
||||
}
|
||||
|
||||
t.GetActiveBuffer().insertBlankCharacters(count)
|
||||
return true
|
||||
}
|
||||
|
||||
// CSI L
|
||||
// Insert Ps Line(s) (default = 1) (IL)
|
||||
func (t *Terminal) csiInsertLinesHandler(params []string) (renderRequired bool) {
|
||||
count := 1
|
||||
if len(params) > 1 {
|
||||
return false
|
||||
}
|
||||
if len(params) == 1 {
|
||||
var err error
|
||||
count, err = strconv.Atoi(params[0])
|
||||
if err != nil || count < 1 {
|
||||
count = 1
|
||||
}
|
||||
}
|
||||
|
||||
t.GetActiveBuffer().insertLines(count)
|
||||
return true
|
||||
}
|
||||
|
||||
// CSI M
|
||||
// Delete Ps Line(s) (default = 1) (DL)
|
||||
func (t *Terminal) csiDeleteLinesHandler(params []string) (renderRequired bool) {
|
||||
count := 1
|
||||
if len(params) > 1 {
|
||||
return false
|
||||
}
|
||||
if len(params) == 1 {
|
||||
var err error
|
||||
count, err = strconv.Atoi(params[0])
|
||||
if err != nil || count < 1 {
|
||||
count = 1
|
||||
}
|
||||
}
|
||||
|
||||
t.GetActiveBuffer().deleteLines(count)
|
||||
return true
|
||||
}
|
||||
|
||||
// CSI T
|
||||
// Scroll down Ps lines (default = 1) (SD), VT420
|
||||
func (t *Terminal) csiScrollDownHandler(params []string) (renderRequired bool) {
|
||||
distance := 1
|
||||
if len(params) > 1 {
|
||||
return false
|
||||
}
|
||||
if len(params) == 1 {
|
||||
var err error
|
||||
distance, err = strconv.Atoi(params[0])
|
||||
if err != nil || distance < 1 {
|
||||
distance = 1
|
||||
}
|
||||
}
|
||||
t.GetActiveBuffer().areaScrollDown(uint16(distance))
|
||||
return true
|
||||
}
|
||||
|
||||
// CSI r
|
||||
// Set Scrolling Region [top;bottom] (default = full size of window) (DECSTBM), VT100
|
||||
func (t *Terminal) csiSetMarginsHandler(params []string) (renderRequired bool) {
|
||||
top := 1
|
||||
bottom := int(t.GetActiveBuffer().ViewHeight())
|
||||
|
||||
if len(params) > 2 {
|
||||
return false
|
||||
}
|
||||
|
||||
if len(params) > 0 {
|
||||
var err error
|
||||
top, err = strconv.Atoi(params[0])
|
||||
if err != nil || top < 1 {
|
||||
top = 1
|
||||
}
|
||||
|
||||
if len(params) > 1 {
|
||||
var err error
|
||||
bottom, err = strconv.Atoi(params[1])
|
||||
if err != nil || bottom > int(t.GetActiveBuffer().ViewHeight()) || bottom < 1 {
|
||||
bottom = int(t.GetActiveBuffer().ViewHeight())
|
||||
}
|
||||
}
|
||||
}
|
||||
top--
|
||||
bottom--
|
||||
|
||||
t.activeBuffer.setVerticalMargins(uint(top), uint(bottom))
|
||||
t.GetActiveBuffer().setPosition(0, 0)
|
||||
return true
|
||||
}
|
||||
|
||||
// CSI X
|
||||
// Erase Ps Character(s) (default = 1) (ECH)
|
||||
func (t *Terminal) csiEraseCharactersHandler(params []string) (renderRequired bool) {
|
||||
count := 1
|
||||
if len(params) > 0 {
|
||||
var err error
|
||||
count, err = strconv.Atoi(params[0])
|
||||
if err != nil || count < 1 {
|
||||
count = 1
|
||||
}
|
||||
}
|
||||
|
||||
t.GetActiveBuffer().eraseCharacters(count)
|
||||
return true
|
||||
}
|
||||
|
||||
// CSI l
|
||||
// Reset Mode (RM)
|
||||
func (t *Terminal) csiResetModeHandler(params []string) (renderRequired bool) {
|
||||
return t.csiSetModes(params, false)
|
||||
}
|
||||
|
||||
// CSI h
|
||||
// Set Mode (SM)
|
||||
func (t *Terminal) csiSetModeHandler(params []string) (renderRequired bool) {
|
||||
return t.csiSetModes(params, true)
|
||||
}
|
||||
|
||||
func (t *Terminal) csiSetModes(modes []string, enabled bool) bool {
|
||||
if len(modes) == 0 {
|
||||
return false
|
||||
}
|
||||
if len(modes) == 1 {
|
||||
return t.csiSetMode(modes[0], enabled)
|
||||
}
|
||||
// should we propagate DEC prefix?
|
||||
const decPrefix = '?'
|
||||
isDec := len(modes[0]) > 0 && modes[0][0] == decPrefix
|
||||
|
||||
var render bool
|
||||
|
||||
// iterate through params, propagating DEC prefix to subsequent elements
|
||||
for i, v := range modes {
|
||||
updatedMode := v
|
||||
if i > 0 && isDec {
|
||||
updatedMode = string(decPrefix) + v
|
||||
}
|
||||
render = t.csiSetMode(updatedMode, enabled) || render
|
||||
}
|
||||
|
||||
return render
|
||||
}
|
||||
|
||||
func parseModes(mode string) []string {
|
||||
|
||||
var output []string
|
||||
|
||||
if mode == "" {
|
||||
return nil
|
||||
}
|
||||
var prefix string
|
||||
if mode[0] == '?' {
|
||||
prefix = "?"
|
||||
mode = mode[1:]
|
||||
}
|
||||
|
||||
for len(mode) > 4 {
|
||||
output = append(output, prefix+mode[:4])
|
||||
mode = mode[4:]
|
||||
}
|
||||
|
||||
output = append(output, prefix+mode)
|
||||
return output
|
||||
}
|
||||
|
||||
func (t *Terminal) csiSetMode(modes string, enabled bool) bool {
|
||||
|
||||
for _, modeStr := range parseModes(modes) {
|
||||
|
||||
switch modeStr {
|
||||
case "4":
|
||||
t.activeBuffer.modes.ReplaceMode = !enabled
|
||||
case "20":
|
||||
t.activeBuffer.modes.LineFeedMode = false
|
||||
case "?1":
|
||||
t.activeBuffer.modes.ApplicationCursorKeys = enabled
|
||||
case "?3":
|
||||
if t.windowManipulator != nil {
|
||||
if enabled {
|
||||
// DECCOLM - COLumn mode, 132 characters per line
|
||||
t.windowManipulator.ResizeInChars(132, int(t.activeBuffer.viewHeight))
|
||||
} else {
|
||||
// DECCOLM - 80 characters per line (erases screen)
|
||||
t.windowManipulator.ResizeInChars(80, int(t.activeBuffer.viewHeight))
|
||||
}
|
||||
t.activeBuffer.clear()
|
||||
}
|
||||
case "?5": // DECSCNM
|
||||
t.activeBuffer.modes.ScreenMode = enabled
|
||||
case "?6":
|
||||
// DECOM
|
||||
t.activeBuffer.modes.OriginMode = enabled
|
||||
case "?7":
|
||||
// auto-wrap mode
|
||||
//DECAWM
|
||||
t.activeBuffer.modes.AutoWrap = enabled
|
||||
case "?9":
|
||||
if enabled {
|
||||
t.mouseMode = (MouseModeX10)
|
||||
} else {
|
||||
t.mouseMode = (MouseModeNone)
|
||||
}
|
||||
case "?12", "?13":
|
||||
t.activeBuffer.modes.BlinkingCursor = enabled
|
||||
case "?25":
|
||||
t.activeBuffer.modes.ShowCursor = enabled
|
||||
case "?47", "?1047":
|
||||
if enabled {
|
||||
t.useAltBuffer()
|
||||
} else {
|
||||
t.useMainBuffer()
|
||||
}
|
||||
case "?1000": // ?10061000 seen from htop
|
||||
// enable mouse tracking
|
||||
// 1000 refers to ext mode for extended mouse click area - otherwise only x <= 255-31
|
||||
if enabled {
|
||||
t.mouseMode = (MouseModeVT200)
|
||||
} else {
|
||||
t.mouseMode = (MouseModeNone)
|
||||
}
|
||||
case "?1002":
|
||||
if enabled {
|
||||
t.mouseMode = (MouseModeButtonEvent)
|
||||
} else {
|
||||
t.mouseMode = (MouseModeNone)
|
||||
}
|
||||
case "?1003":
|
||||
if enabled {
|
||||
t.mouseMode = MouseModeAnyEvent
|
||||
} else {
|
||||
t.mouseMode = MouseModeNone
|
||||
}
|
||||
case "?1005":
|
||||
if enabled {
|
||||
t.mouseExtMode = MouseExtUTF
|
||||
} else {
|
||||
t.mouseExtMode = MouseExtNone
|
||||
}
|
||||
|
||||
case "?1006":
|
||||
if enabled {
|
||||
t.mouseExtMode = MouseExtSGR
|
||||
} else {
|
||||
t.mouseExtMode = (MouseExtNone)
|
||||
}
|
||||
case "?1015":
|
||||
if enabled {
|
||||
t.mouseExtMode = (MouseExtURXVT)
|
||||
} else {
|
||||
t.mouseExtMode = (MouseExtNone)
|
||||
}
|
||||
case "?1048":
|
||||
if enabled {
|
||||
t.GetActiveBuffer().saveCursor()
|
||||
} else {
|
||||
t.GetActiveBuffer().restoreCursor()
|
||||
}
|
||||
case "?1049":
|
||||
if enabled {
|
||||
t.useAltBuffer()
|
||||
} else {
|
||||
t.useMainBuffer()
|
||||
}
|
||||
case "?2004":
|
||||
t.activeBuffer.modes.BracketedPasteMode = enabled
|
||||
case "?80":
|
||||
t.activeBuffer.modes.SixelScrolling = enabled
|
||||
default:
|
||||
t.log("Unsupported CSI mode %s = %t", modeStr, enabled)
|
||||
}
|
||||
}
|
||||
return false
|
||||
}
|
||||
|
||||
// CSI d
|
||||
// Line Position Absolute [row] (default = [1,column]) (VPA)
|
||||
func (t *Terminal) csiLinePositionAbsoluteHandler(params []string) (renderRequired bool) {
|
||||
row := 1
|
||||
if len(params) > 0 {
|
||||
var err error
|
||||
row, err = strconv.Atoi(params[0])
|
||||
if err != nil || row < 1 {
|
||||
row = 1
|
||||
}
|
||||
}
|
||||
|
||||
t.GetActiveBuffer().setPosition(t.GetActiveBuffer().CursorColumn(), uint16(row-1))
|
||||
|
||||
return true
|
||||
}
|
||||
|
||||
// CSI P
|
||||
// Delete Ps Character(s) (default = 1) (DCH)
|
||||
func (t *Terminal) csiDeleteHandler(params []string) (renderRequired bool) {
|
||||
n := 1
|
||||
if len(params) >= 1 {
|
||||
var err error
|
||||
n, err = strconv.Atoi(params[0])
|
||||
if err != nil || n < 1 {
|
||||
n = 1
|
||||
}
|
||||
}
|
||||
|
||||
t.GetActiveBuffer().deleteChars(n)
|
||||
return true
|
||||
}
|
||||
|
||||
// CSI g
|
||||
// tab clear (TBC)
|
||||
func (t *Terminal) csiTabClearHandler(params []string) (renderRequired bool) {
|
||||
n := "0"
|
||||
if len(params) > 0 {
|
||||
n = params[0]
|
||||
}
|
||||
switch n {
|
||||
case "0", "":
|
||||
t.activeBuffer.tabClearAtCursor()
|
||||
case "3":
|
||||
t.activeBuffer.tabReset()
|
||||
default:
|
||||
return false
|
||||
}
|
||||
|
||||
return true
|
||||
}
|
||||
|
||||
// CSI J
|
||||
// Erase in Display (ED), VT100
|
||||
func (t *Terminal) csiEraseInDisplayHandler(params []string) (renderRequired bool) {
|
||||
n := "0"
|
||||
if len(params) > 0 {
|
||||
n = params[0]
|
||||
}
|
||||
|
||||
switch n {
|
||||
case "0", "":
|
||||
t.GetActiveBuffer().eraseDisplayFromCursor()
|
||||
case "1":
|
||||
t.GetActiveBuffer().eraseDisplayToCursor()
|
||||
case "2", "3":
|
||||
t.GetActiveBuffer().eraseDisplay()
|
||||
default:
|
||||
return false
|
||||
}
|
||||
|
||||
return true
|
||||
}
|
||||
|
||||
// CSI K
|
||||
// Erase in Line (EL), VT100
|
||||
func (t *Terminal) csiEraseInLineHandler(params []string) (renderRequired bool) {
|
||||
|
||||
n := "0"
|
||||
if len(params) > 0 {
|
||||
n = params[0]
|
||||
}
|
||||
|
||||
switch n {
|
||||
case "0", "": //erase adter cursor
|
||||
t.GetActiveBuffer().eraseLineFromCursor()
|
||||
case "1": // erase to cursor inclusive
|
||||
t.GetActiveBuffer().eraseLineToCursor()
|
||||
case "2": // erase entire
|
||||
t.GetActiveBuffer().eraseLine()
|
||||
default:
|
||||
return false
|
||||
}
|
||||
return true
|
||||
}
|
||||
|
||||
// CSI m
|
||||
// Character Attributes (SGR)
|
||||
func (t *Terminal) sgrSequenceHandler(params []string) bool {
|
||||
|
||||
if len(params) == 0 {
|
||||
params = []string{"0"}
|
||||
}
|
||||
|
||||
for i := range params {
|
||||
|
||||
p := strings.Replace(strings.Replace(params[i], "[", "", -1), "]", "", -1)
|
||||
|
||||
switch p {
|
||||
case "00", "0", "":
|
||||
attr := t.GetActiveBuffer().getCursorAttr()
|
||||
*attr = CellAttributes{}
|
||||
case "1", "01":
|
||||
t.GetActiveBuffer().getCursorAttr().bold = true
|
||||
t.GetActiveBuffer().getCursorAttr().dim = false
|
||||
case "2", "02":
|
||||
t.GetActiveBuffer().getCursorAttr().bold = false
|
||||
t.GetActiveBuffer().getCursorAttr().dim = true
|
||||
case "3", "03":
|
||||
t.GetActiveBuffer().getCursorAttr().italic = true
|
||||
case "4", "04":
|
||||
t.GetActiveBuffer().getCursorAttr().underline = true
|
||||
case "5", "05":
|
||||
t.GetActiveBuffer().getCursorAttr().blink = true
|
||||
case "7", "07":
|
||||
t.GetActiveBuffer().getCursorAttr().inverse = true
|
||||
case "8", "08":
|
||||
t.GetActiveBuffer().getCursorAttr().hidden = true
|
||||
case "9", "09":
|
||||
t.GetActiveBuffer().getCursorAttr().strikethrough = true
|
||||
case "21":
|
||||
t.GetActiveBuffer().getCursorAttr().bold = false
|
||||
case "22":
|
||||
t.GetActiveBuffer().getCursorAttr().dim = false
|
||||
t.GetActiveBuffer().getCursorAttr().bold = false
|
||||
case "23":
|
||||
t.GetActiveBuffer().getCursorAttr().italic = false
|
||||
case "24":
|
||||
t.GetActiveBuffer().getCursorAttr().underline = false
|
||||
case "25":
|
||||
t.GetActiveBuffer().getCursorAttr().blink = false
|
||||
case "27":
|
||||
t.GetActiveBuffer().getCursorAttr().inverse = false
|
||||
case "28":
|
||||
t.GetActiveBuffer().getCursorAttr().hidden = false
|
||||
case "29":
|
||||
t.GetActiveBuffer().getCursorAttr().strikethrough = false
|
||||
case "38": // set foreground
|
||||
t.GetActiveBuffer().getCursorAttr().fgColour, _ = t.theme.ColourFromAnsi(params[i+1:], false)
|
||||
return false
|
||||
case "48": // set background
|
||||
t.GetActiveBuffer().getCursorAttr().bgColour, _ = t.theme.ColourFromAnsi(params[i+1:], true)
|
||||
return false
|
||||
case "39":
|
||||
t.GetActiveBuffer().getCursorAttr().fgColour = t.theme.DefaultForeground()
|
||||
case "49":
|
||||
t.GetActiveBuffer().getCursorAttr().bgColour = t.theme.DefaultBackground()
|
||||
default:
|
||||
bi, err := strconv.Atoi(p)
|
||||
if err != nil {
|
||||
return false
|
||||
}
|
||||
i := byte(bi)
|
||||
switch true {
|
||||
case i >= 30 && i <= 37, i >= 90 && i <= 97:
|
||||
t.GetActiveBuffer().getCursorAttr().fgColour = t.theme.ColourFrom4Bit(i)
|
||||
case i >= 40 && i <= 47, i >= 100 && i <= 107:
|
||||
t.GetActiveBuffer().getCursorAttr().bgColour = t.theme.ColourFrom4Bit(i)
|
||||
}
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
x := t.GetActiveBuffer().CursorColumn()
|
||||
y := t.GetActiveBuffer().CursorLine()
|
||||
if cell := t.GetActiveBuffer().GetCell(x, y); cell != nil {
|
||||
cell.attr = t.GetActiveBuffer().cursorAttr
|
||||
}
|
||||
|
||||
return false
|
||||
}
|
||||
|
||||
func (t *Terminal) csiSoftResetHandler(params []string) bool {
|
||||
t.reset()
|
||||
return true
|
||||
}
|
||||
|
||||
func (t *Terminal) csiCursorSelection(params []string) (renderRequired bool) {
|
||||
if len(params) == 0 {
|
||||
return false
|
||||
}
|
||||
i, err := strconv.Atoi(params[0])
|
||||
if err != nil {
|
||||
return false
|
||||
}
|
||||
t.GetActiveBuffer().SetCursorShape(CursorShape(i))
|
||||
return true
|
||||
}
|
66
termutil/line.go
Normal file
66
termutil/line.go
Normal file
@ -0,0 +1,66 @@
|
||||
package termutil
|
||||
|
||||
import "strings"
|
||||
|
||||
type Line struct {
|
||||
wrapped bool // whether line was wrapped onto from the previous one
|
||||
cells []Cell
|
||||
}
|
||||
|
||||
func newLine() Line {
|
||||
return Line{
|
||||
wrapped: false,
|
||||
cells: []Cell{},
|
||||
}
|
||||
}
|
||||
|
||||
func (line *Line) Len() uint16 {
|
||||
return uint16(len(line.cells))
|
||||
}
|
||||
|
||||
func (line *Line) String() string {
|
||||
runes := []rune{}
|
||||
for _, cell := range line.cells {
|
||||
runes = append(runes, cell.r.Rune)
|
||||
}
|
||||
return strings.TrimRight(string(runes), "\x00")
|
||||
}
|
||||
|
||||
func (line *Line) append(cells ...Cell) {
|
||||
line.cells = append(line.cells, cells...)
|
||||
}
|
||||
|
||||
func (line *Line) shrink(width uint16) {
|
||||
if line.Len() <= width {
|
||||
return
|
||||
}
|
||||
remove := line.Len() - width
|
||||
var cells []Cell
|
||||
for _, cell := range line.cells {
|
||||
if cell.r.Rune == 0 && remove > 0 {
|
||||
remove--
|
||||
} else {
|
||||
cells = append(cells, cell)
|
||||
}
|
||||
}
|
||||
line.cells = cells
|
||||
}
|
||||
|
||||
func (line *Line) wrap(width uint16) []Line {
|
||||
|
||||
var output []Line
|
||||
var current Line
|
||||
|
||||
current.wrapped = line.wrapped
|
||||
|
||||
for _, cell := range line.cells {
|
||||
if len(current.cells) == int(width) {
|
||||
output = append(output, current)
|
||||
current = newLine()
|
||||
current.wrapped = true
|
||||
}
|
||||
current.cells = append(current.cells, cell)
|
||||
}
|
||||
|
||||
return append(output, current)
|
||||
}
|
@ -1,4 +1,4 @@
|
||||
package voidterm
|
||||
package termutil
|
||||
|
||||
type MeasuredRune struct {
|
||||
Rune rune
|
@ -1,15 +1,15 @@
|
||||
package voidterm
|
||||
package termutil
|
||||
|
||||
type Modes struct {
|
||||
ShowCursor bool
|
||||
ApplicationCursorKeys bool
|
||||
BlinkingCursor bool
|
||||
ReplaceMode bool
|
||||
OriginMode bool
|
||||
ReplaceMode bool // overwrite character at cursor or insert new
|
||||
OriginMode bool // see DECOM docs - whether cursor is positioned within the margins or not
|
||||
LineFeedMode bool
|
||||
ScreenMode bool
|
||||
ScreenMode bool // DECSCNM (black on white background)
|
||||
AutoWrap bool
|
||||
SixelScrolling bool
|
||||
SixelScrolling bool // DECSDM
|
||||
BracketedPasteMode bool
|
||||
}
|
||||
|
||||
@ -23,9 +23,6 @@ const (
|
||||
MouseModeVT200Highlight
|
||||
MouseModeButtonEvent
|
||||
MouseModeAnyEvent
|
||||
)
|
||||
|
||||
const (
|
||||
MouseExtNone MouseExtMode = iota
|
||||
MouseExtUTF
|
||||
MouseExtSGR
|
41
termutil/options.go
Normal file
41
termutil/options.go
Normal file
@ -0,0 +1,41 @@
|
||||
package termutil
|
||||
|
||||
import (
|
||||
"os"
|
||||
)
|
||||
|
||||
type Option func(t *Terminal)
|
||||
|
||||
func WithLogFile(path string) Option {
|
||||
return func(t *Terminal) {
|
||||
if path == "-" {
|
||||
t.logFile = os.Stdout
|
||||
return
|
||||
}
|
||||
t.logFile, _ = os.Create(path)
|
||||
}
|
||||
}
|
||||
|
||||
func WithTheme(theme *Theme) Option {
|
||||
return func(t *Terminal) {
|
||||
t.theme = theme
|
||||
}
|
||||
}
|
||||
|
||||
func WithShell(shell string) Option {
|
||||
return func(t *Terminal) {
|
||||
t.shell = shell
|
||||
}
|
||||
}
|
||||
|
||||
func WithInitialCommand(cmd string) Option {
|
||||
return func(t *Terminal) {
|
||||
t.initialCommand = cmd + "\n"
|
||||
}
|
||||
}
|
||||
|
||||
func WithWindowManipulator(m WindowManipulator) Option {
|
||||
return func(t *Terminal) {
|
||||
t.windowManipulator = m
|
||||
}
|
||||
}
|
69
termutil/osc.go
Normal file
69
termutil/osc.go
Normal file
@ -0,0 +1,69 @@
|
||||
package termutil
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
)
|
||||
|
||||
func (t *Terminal) handleOSC(readChan chan MeasuredRune) (renderRequired bool) {
|
||||
|
||||
params := []string{}
|
||||
param := ""
|
||||
|
||||
READ:
|
||||
for {
|
||||
select {
|
||||
case b := <-readChan:
|
||||
if t.isOSCTerminator(b.Rune) {
|
||||
params = append(params, param)
|
||||
break READ
|
||||
}
|
||||
if b.Rune == ';' {
|
||||
params = append(params, param)
|
||||
param = ""
|
||||
continue
|
||||
}
|
||||
param = fmt.Sprintf("%s%c", param, b.Rune)
|
||||
default:
|
||||
return false
|
||||
}
|
||||
}
|
||||
|
||||
if len(params) == 0 {
|
||||
return false
|
||||
}
|
||||
|
||||
pT := params[len(params)-1]
|
||||
pS := params[:len(params)-1]
|
||||
|
||||
if len(pS) == 0 {
|
||||
pS = []string{pT}
|
||||
pT = ""
|
||||
}
|
||||
|
||||
switch pS[0] {
|
||||
case "0", "2", "l":
|
||||
t.setTitle(pT)
|
||||
case "10": // get/set foreground colour
|
||||
if len(pS) > 1 {
|
||||
if pS[1] == "?" {
|
||||
t.WriteToPty([]byte("\x1b]10;15"))
|
||||
}
|
||||
}
|
||||
case "11": // get/set background colour
|
||||
if len(pS) > 1 {
|
||||
if pS[1] == "?" {
|
||||
t.WriteToPty([]byte("\x1b]10;0"))
|
||||
}
|
||||
}
|
||||
}
|
||||
return false
|
||||
}
|
||||
|
||||
func (t *Terminal) isOSCTerminator(r rune) bool {
|
||||
for _, terminator := range oscTerminators {
|
||||
if terminator == r {
|
||||
return true
|
||||
}
|
||||
}
|
||||
return false
|
||||
}
|
93
termutil/resize.go
Normal file
93
termutil/resize.go
Normal file
@ -0,0 +1,93 @@
|
||||
package termutil
|
||||
|
||||
func (buffer *Buffer) shrink(width uint16) {
|
||||
|
||||
var replace []Line
|
||||
|
||||
prevCursor := int(buffer.cursorPosition.Line)
|
||||
|
||||
for i, line := range buffer.lines {
|
||||
|
||||
line.shrink(width)
|
||||
|
||||
// this line fits within the new width restriction, keep it as is and continue
|
||||
if line.Len() <= width {
|
||||
replace = append(replace, line)
|
||||
continue
|
||||
}
|
||||
|
||||
wrappedLines := line.wrap(width)
|
||||
|
||||
if prevCursor >= i {
|
||||
buffer.cursorPosition.Line += uint64(len(wrappedLines) - 1)
|
||||
|
||||
}
|
||||
|
||||
replace = append(replace, wrappedLines...)
|
||||
}
|
||||
|
||||
buffer.cursorPosition.Col = buffer.cursorPosition.Col % width
|
||||
|
||||
buffer.lines = replace
|
||||
}
|
||||
|
||||
func (buffer *Buffer) grow(width uint16) {
|
||||
|
||||
var replace []Line
|
||||
var current Line
|
||||
|
||||
prevCursor := int(buffer.cursorPosition.Line)
|
||||
|
||||
for i, line := range buffer.lines {
|
||||
|
||||
if !line.wrapped {
|
||||
if i > 0 {
|
||||
replace = append(replace, current)
|
||||
}
|
||||
current = newLine()
|
||||
}
|
||||
|
||||
if i == prevCursor {
|
||||
buffer.cursorPosition.Line -= uint64(i - len(replace))
|
||||
}
|
||||
|
||||
for _, cell := range line.cells {
|
||||
if len(current.cells) == int(width) {
|
||||
replace = append(replace, current)
|
||||
current = newLine()
|
||||
current.wrapped = true
|
||||
}
|
||||
current.cells = append(current.cells, cell)
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
replace = append(replace, current)
|
||||
|
||||
buffer.lines = replace
|
||||
}
|
||||
|
||||
// deprecated
|
||||
func (buffer *Buffer) resizeView(width uint16, height uint16) {
|
||||
|
||||
if buffer.viewHeight == 0 {
|
||||
buffer.viewWidth = width
|
||||
buffer.viewHeight = height
|
||||
return
|
||||
}
|
||||
|
||||
// scroll to bottom
|
||||
buffer.scrollLinesFromBottom = 0
|
||||
|
||||
if width < buffer.viewWidth { // wrap lines if we're shrinking
|
||||
buffer.shrink(width)
|
||||
buffer.grow(width)
|
||||
} else if width > buffer.viewWidth { // unwrap lines if we're growing
|
||||
buffer.grow(width)
|
||||
}
|
||||
|
||||
buffer.viewWidth = width
|
||||
buffer.viewHeight = height
|
||||
|
||||
buffer.resetVerticalMargins(uint(buffer.viewHeight))
|
||||
}
|
324
termutil/selection.go
Normal file
324
termutil/selection.go
Normal file
@ -0,0 +1,324 @@
|
||||
package termutil
|
||||
|
||||
func (buffer *Buffer) ClearSelection() {
|
||||
buffer.selectionMu.Lock()
|
||||
defer buffer.selectionMu.Unlock()
|
||||
buffer.selectionStart = nil
|
||||
buffer.selectionEnd = nil
|
||||
}
|
||||
|
||||
func (buffer *Buffer) GetBoundedTextAtPosition(pos Position) (start Position, end Position, text string, textIndex int, found bool) {
|
||||
return buffer.FindWordAt(pos, func(r rune) bool {
|
||||
return r > 0 && r < 256
|
||||
})
|
||||
}
|
||||
|
||||
// if the selection is invalid - e.g. lines are selected that no longer exist in the buffer
|
||||
func (buffer *Buffer) fixSelection() bool {
|
||||
buffer.selectionMu.Lock()
|
||||
defer buffer.selectionMu.Unlock()
|
||||
|
||||
if buffer.selectionStart == nil || buffer.selectionEnd == nil {
|
||||
return false
|
||||
}
|
||||
|
||||
if buffer.selectionStart.Line >= uint64(len(buffer.lines)) {
|
||||
buffer.selectionStart.Line = uint64(len(buffer.lines)) - 1
|
||||
}
|
||||
|
||||
if buffer.selectionEnd.Line >= uint64(len(buffer.lines)) {
|
||||
buffer.selectionEnd.Line = uint64(len(buffer.lines)) - 1
|
||||
}
|
||||
|
||||
if buffer.selectionStart.Col >= uint16(len(buffer.lines[buffer.selectionStart.Line].cells)) {
|
||||
buffer.selectionStart.Col = 0
|
||||
if buffer.selectionStart.Line < uint64(len(buffer.lines))-1 {
|
||||
buffer.selectionStart.Line++
|
||||
}
|
||||
}
|
||||
|
||||
if buffer.selectionEnd.Col >= uint16(len(buffer.lines[buffer.selectionEnd.Line].cells)) {
|
||||
buffer.selectionEnd.Col = uint16(len(buffer.lines[buffer.selectionEnd.Line].cells)) - 1
|
||||
}
|
||||
|
||||
return true
|
||||
}
|
||||
|
||||
func (buffer *Buffer) ExtendSelectionToEntireLines() {
|
||||
if !buffer.fixSelection() {
|
||||
return
|
||||
}
|
||||
|
||||
buffer.selectionMu.Lock()
|
||||
defer buffer.selectionMu.Unlock()
|
||||
|
||||
buffer.selectionStart.Col = 0
|
||||
buffer.selectionEnd.Col = uint16(len(buffer.lines[buffer.selectionEnd.Line].cells)) - 1
|
||||
}
|
||||
|
||||
type RuneMatcher func(r rune) bool
|
||||
|
||||
func (buffer *Buffer) SelectWordAt(pos Position, runeMatcher RuneMatcher) {
|
||||
start, end, _, _, found := buffer.FindWordAt(pos, runeMatcher)
|
||||
if !found {
|
||||
return
|
||||
}
|
||||
buffer.setRawSelectionStart(start)
|
||||
buffer.setRawSelectionEnd(end)
|
||||
}
|
||||
|
||||
// takes raw coords
|
||||
func (buffer *Buffer) Highlight(start Position, end Position, annotation *Annotation) {
|
||||
buffer.highlightStart = &start
|
||||
buffer.highlightEnd = &end
|
||||
buffer.highlightAnnotation = annotation
|
||||
}
|
||||
|
||||
func (buffer *Buffer) ClearHighlight() {
|
||||
buffer.highlightStart = nil
|
||||
buffer.highlightEnd = nil
|
||||
}
|
||||
|
||||
// returns raw lines
|
||||
func (buffer *Buffer) FindWordAt(pos Position, runeMatcher RuneMatcher) (start Position, end Position, text string, textIndex int, found bool) {
|
||||
line := buffer.convertViewLineToRawLine(uint16(pos.Line))
|
||||
col := pos.Col
|
||||
|
||||
if line >= uint64(len(buffer.lines)) {
|
||||
return
|
||||
}
|
||||
if col >= uint16(len(buffer.lines[line].cells)) {
|
||||
return
|
||||
}
|
||||
|
||||
if !runeMatcher(buffer.lines[line].cells[col].r.Rune) {
|
||||
return
|
||||
}
|
||||
|
||||
found = true
|
||||
|
||||
start = Position{
|
||||
Line: line,
|
||||
Col: col,
|
||||
}
|
||||
end = Position{
|
||||
Line: line,
|
||||
Col: col,
|
||||
}
|
||||
|
||||
var startCol uint16
|
||||
BACK:
|
||||
for y := int(line); y >= 0; y-- {
|
||||
if y == int(line) {
|
||||
startCol = col
|
||||
} else {
|
||||
if len(buffer.lines[y].cells) < int(buffer.viewWidth) {
|
||||
break
|
||||
}
|
||||
startCol = uint16(len(buffer.lines[y].cells) - 1)
|
||||
}
|
||||
for x := int(startCol); x >= 0; x-- {
|
||||
if runeMatcher(buffer.lines[y].cells[x].r.Rune) {
|
||||
start = Position{
|
||||
Line: uint64(y),
|
||||
Col: uint16(x),
|
||||
}
|
||||
text = string(buffer.lines[y].cells[x].r.Rune) + text
|
||||
} else {
|
||||
break BACK
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
textIndex = len([]rune(text)) - 1
|
||||
FORWARD:
|
||||
for y := uint64(line); y < uint64(len(buffer.lines)); y++ {
|
||||
if y == line {
|
||||
startCol = col + 1
|
||||
} else {
|
||||
startCol = 0
|
||||
}
|
||||
for x := int(startCol); x < len(buffer.lines[y].cells); x++ {
|
||||
if runeMatcher(buffer.lines[y].cells[x].r.Rune) {
|
||||
end = Position{
|
||||
Line: y,
|
||||
Col: uint16(x),
|
||||
}
|
||||
text = text + string(buffer.lines[y].cells[x].r.Rune)
|
||||
} else {
|
||||
break FORWARD
|
||||
}
|
||||
}
|
||||
if len(buffer.lines[y].cells) < int(buffer.viewWidth) {
|
||||
break
|
||||
}
|
||||
}
|
||||
|
||||
return
|
||||
}
|
||||
|
||||
func (buffer *Buffer) SetSelectionStart(pos Position) {
|
||||
buffer.selectionMu.Lock()
|
||||
defer buffer.selectionMu.Unlock()
|
||||
buffer.selectionStart = &Position{
|
||||
Col: pos.Col,
|
||||
Line: buffer.convertViewLineToRawLine(uint16(pos.Line)),
|
||||
}
|
||||
}
|
||||
|
||||
func (buffer *Buffer) setRawSelectionStart(pos Position) {
|
||||
buffer.selectionMu.Lock()
|
||||
defer buffer.selectionMu.Unlock()
|
||||
buffer.selectionStart = &pos
|
||||
}
|
||||
|
||||
func (buffer *Buffer) SetSelectionEnd(pos Position) {
|
||||
buffer.selectionMu.Lock()
|
||||
defer buffer.selectionMu.Unlock()
|
||||
buffer.selectionEnd = &Position{
|
||||
Col: pos.Col,
|
||||
Line: buffer.convertViewLineToRawLine(uint16(pos.Line)),
|
||||
}
|
||||
}
|
||||
|
||||
func (buffer *Buffer) setRawSelectionEnd(pos Position) {
|
||||
buffer.selectionMu.Lock()
|
||||
defer buffer.selectionMu.Unlock()
|
||||
buffer.selectionEnd = &pos
|
||||
}
|
||||
|
||||
func (buffer *Buffer) GetSelection() (string, *Selection) {
|
||||
if !buffer.fixSelection() {
|
||||
return "", nil
|
||||
}
|
||||
|
||||
buffer.selectionMu.Lock()
|
||||
defer buffer.selectionMu.Unlock()
|
||||
|
||||
start := *buffer.selectionStart
|
||||
end := *buffer.selectionEnd
|
||||
|
||||
if end.Line < start.Line || (end.Line == start.Line && end.Col < start.Col) {
|
||||
swap := end
|
||||
end = start
|
||||
start = swap
|
||||
}
|
||||
|
||||
var text string
|
||||
for y := start.Line; y <= end.Line; y++ {
|
||||
if y >= uint64(len(buffer.lines)) {
|
||||
break
|
||||
}
|
||||
line := buffer.lines[y]
|
||||
startX := 0
|
||||
endX := len(line.cells) - 1
|
||||
if y == start.Line {
|
||||
startX = int(start.Col)
|
||||
}
|
||||
if y == end.Line {
|
||||
endX = int(end.Col)
|
||||
}
|
||||
if y > start.Line {
|
||||
text += "\n"
|
||||
}
|
||||
for x := startX; x <= endX; x++ {
|
||||
if x >= len(line.cells) {
|
||||
break
|
||||
}
|
||||
mr := line.cells[x].Rune()
|
||||
if mr.Width == 0 {
|
||||
continue
|
||||
}
|
||||
x += mr.Width - 1
|
||||
text += string(mr.Rune)
|
||||
}
|
||||
}
|
||||
|
||||
viewSelection := Selection{
|
||||
Start: start,
|
||||
End: end,
|
||||
}
|
||||
|
||||
viewSelection.Start.Line = uint64(buffer.convertRawLineToViewLine(viewSelection.Start.Line))
|
||||
viewSelection.End.Line = uint64(buffer.convertRawLineToViewLine(viewSelection.End.Line))
|
||||
return text, &viewSelection
|
||||
}
|
||||
|
||||
func (buffer *Buffer) InSelection(pos Position) bool {
|
||||
|
||||
if !buffer.fixSelection() {
|
||||
return false
|
||||
}
|
||||
buffer.selectionMu.Lock()
|
||||
defer buffer.selectionMu.Unlock()
|
||||
|
||||
start := *buffer.selectionStart
|
||||
end := *buffer.selectionEnd
|
||||
|
||||
if end.Line < start.Line || (end.Line == start.Line && end.Col < start.Col) {
|
||||
swap := end
|
||||
end = start
|
||||
start = swap
|
||||
}
|
||||
|
||||
rY := buffer.convertViewLineToRawLine(uint16(pos.Line))
|
||||
if rY < start.Line {
|
||||
return false
|
||||
}
|
||||
if rY > end.Line {
|
||||
return false
|
||||
}
|
||||
if rY == start.Line {
|
||||
if pos.Col < start.Col {
|
||||
return false
|
||||
}
|
||||
}
|
||||
if rY == end.Line {
|
||||
if pos.Col > end.Col {
|
||||
return false
|
||||
}
|
||||
}
|
||||
|
||||
return true
|
||||
}
|
||||
|
||||
func (buffer *Buffer) GetHighlightAnnotation() *Annotation {
|
||||
return buffer.highlightAnnotation
|
||||
}
|
||||
|
||||
func (buffer *Buffer) GetViewHighlight() (start Position, end Position, exists bool) {
|
||||
|
||||
if buffer.highlightStart == nil || buffer.highlightEnd == nil {
|
||||
return
|
||||
}
|
||||
|
||||
if buffer.highlightStart.Line >= uint64(len(buffer.lines)) {
|
||||
return
|
||||
}
|
||||
|
||||
if buffer.highlightEnd.Line >= uint64(len(buffer.lines)) {
|
||||
return
|
||||
}
|
||||
|
||||
if buffer.highlightStart.Col >= uint16(len(buffer.lines[buffer.highlightStart.Line].cells)) {
|
||||
return
|
||||
}
|
||||
|
||||
if buffer.highlightEnd.Col >= uint16(len(buffer.lines[buffer.highlightEnd.Line].cells)) {
|
||||
return
|
||||
}
|
||||
|
||||
start = *buffer.highlightStart
|
||||
end = *buffer.highlightEnd
|
||||
|
||||
if end.Line < start.Line || (end.Line == start.Line && end.Col < start.Col) {
|
||||
swap := end
|
||||
end = start
|
||||
start = swap
|
||||
}
|
||||
|
||||
start.Line = uint64(buffer.convertRawLineToViewLine(start.Line))
|
||||
end.Line = uint64(buffer.convertRawLineToViewLine(end.Line))
|
||||
|
||||
return start, end, true
|
||||
}
|
106
termutil/sixel.go
Normal file
106
termutil/sixel.go
Normal file
@ -0,0 +1,106 @@
|
||||
package termutil
|
||||
|
||||
import (
|
||||
"image"
|
||||
"math"
|
||||
"strings"
|
||||
|
||||
"github.com/liamg/darktile/internal/app/darktile/sixel"
|
||||
)
|
||||
|
||||
type Sixel struct {
|
||||
X uint16
|
||||
Y uint64 // raw line
|
||||
Width uint64
|
||||
Height uint64
|
||||
Image image.Image
|
||||
}
|
||||
|
||||
type VisibleSixel struct {
|
||||
ViewLineOffset int
|
||||
Sixel Sixel
|
||||
}
|
||||
|
||||
func (b *Buffer) addSixel(img image.Image, widthCells int, heightCells int) {
|
||||
b.sixels = append(b.sixels, Sixel{
|
||||
X: b.CursorColumn(),
|
||||
Y: b.cursorPosition.Line,
|
||||
Width: uint64(widthCells),
|
||||
Height: uint64(heightCells),
|
||||
Image: img,
|
||||
})
|
||||
if b.modes.SixelScrolling {
|
||||
b.cursorPosition.Line += uint64(heightCells)
|
||||
}
|
||||
}
|
||||
|
||||
func (b *Buffer) clearSixelsAtRawLine(rawLine uint64) {
|
||||
var filtered []Sixel
|
||||
|
||||
for _, sixelImage := range b.sixels {
|
||||
if sixelImage.Y+sixelImage.Height-1 >= rawLine && sixelImage.Y <= rawLine {
|
||||
continue
|
||||
}
|
||||
|
||||
filtered = append(filtered, sixelImage)
|
||||
}
|
||||
|
||||
b.sixels = filtered
|
||||
}
|
||||
|
||||
func (b *Buffer) GetVisibleSixels() []VisibleSixel {
|
||||
|
||||
firstLine := b.convertViewLineToRawLine(0)
|
||||
lastLine := b.convertViewLineToRawLine(b.viewHeight - 1)
|
||||
|
||||
var visible []VisibleSixel
|
||||
|
||||
for _, sixelImage := range b.sixels {
|
||||
if sixelImage.Y+sixelImage.Height-1 < firstLine {
|
||||
continue
|
||||
}
|
||||
if sixelImage.Y > lastLine {
|
||||
continue
|
||||
}
|
||||
|
||||
visible = append(visible, VisibleSixel{
|
||||
ViewLineOffset: int(sixelImage.Y) - int(firstLine),
|
||||
Sixel: sixelImage,
|
||||
})
|
||||
}
|
||||
|
||||
return visible
|
||||
}
|
||||
|
||||
func (t *Terminal) handleSixel(readChan chan MeasuredRune) (renderRequired bool) {
|
||||
|
||||
var data []rune
|
||||
|
||||
var inEscape bool
|
||||
|
||||
for {
|
||||
r := <-readChan
|
||||
|
||||
switch r.Rune {
|
||||
case 0x1b:
|
||||
inEscape = true
|
||||
continue
|
||||
case 0x5c:
|
||||
if inEscape {
|
||||
img, err := sixel.Decode(strings.NewReader(string(data)), t.theme.DefaultBackground())
|
||||
if err != nil {
|
||||
return false
|
||||
}
|
||||
w, h := t.windowManipulator.CellSizeInPixels()
|
||||
cw := int(math.Ceil(float64(img.Bounds().Dx()) / float64(w)))
|
||||
ch := int(math.Ceil(float64(img.Bounds().Dy()) / float64(h)))
|
||||
t.activeBuffer.addSixel(img, cw, ch)
|
||||
return true
|
||||
}
|
||||
}
|
||||
|
||||
inEscape = false
|
||||
|
||||
data = append(data, r.Rune)
|
||||
}
|
||||
}
|
323
termutil/terminal.go
Normal file
323
termutil/terminal.go
Normal file
@ -0,0 +1,323 @@
|
||||
package termutil
|
||||
|
||||
import (
|
||||
"bufio"
|
||||
"bytes"
|
||||
"fmt"
|
||||
"io"
|
||||
"os"
|
||||
"os/exec"
|
||||
"sync"
|
||||
|
||||
"github.com/creack/pty"
|
||||
"golang.org/x/term"
|
||||
)
|
||||
|
||||
const (
|
||||
MainBuffer uint8 = 0
|
||||
AltBuffer uint8 = 1
|
||||
InternalBuffer uint8 = 2
|
||||
)
|
||||
|
||||
// Terminal communicates with the underlying terminal
|
||||
type Terminal struct {
|
||||
mu sync.Mutex
|
||||
windowManipulator WindowManipulator
|
||||
pty *os.File
|
||||
updateChan chan struct{}
|
||||
processChan chan MeasuredRune
|
||||
closeChan chan struct{}
|
||||
buffers []*Buffer
|
||||
activeBuffer *Buffer
|
||||
mouseMode MouseMode
|
||||
mouseExtMode MouseExtMode
|
||||
logFile *os.File
|
||||
theme *Theme
|
||||
running bool
|
||||
shell string
|
||||
initialCommand string
|
||||
}
|
||||
|
||||
// NewTerminal creates a new terminal instance
|
||||
func New(options ...Option) *Terminal {
|
||||
term := &Terminal{
|
||||
processChan: make(chan MeasuredRune, 0xffff),
|
||||
closeChan: make(chan struct{}),
|
||||
theme: &Theme{},
|
||||
}
|
||||
for _, opt := range options {
|
||||
opt(term)
|
||||
}
|
||||
fg := term.theme.DefaultForeground()
|
||||
bg := term.theme.DefaultBackground()
|
||||
term.buffers = []*Buffer{
|
||||
NewBuffer(1, 1, 0xffff, fg, bg),
|
||||
NewBuffer(1, 1, 0xffff, fg, bg),
|
||||
NewBuffer(1, 1, 0xffff, fg, bg),
|
||||
}
|
||||
term.activeBuffer = term.buffers[0]
|
||||
return term
|
||||
}
|
||||
|
||||
func (t *Terminal) SetWindowManipulator(m WindowManipulator) {
|
||||
t.windowManipulator = m
|
||||
}
|
||||
|
||||
func (t *Terminal) log(line string, params ...interface{}) {
|
||||
if t.logFile != nil {
|
||||
_, _ = fmt.Fprintf(t.logFile, line+"\n", params...)
|
||||
}
|
||||
}
|
||||
|
||||
func (t *Terminal) reset() {
|
||||
fg := t.theme.DefaultForeground()
|
||||
bg := t.theme.DefaultBackground()
|
||||
t.buffers = []*Buffer{
|
||||
NewBuffer(1, 1, 0xffff, fg, bg),
|
||||
NewBuffer(1, 1, 0xffff, fg, bg),
|
||||
NewBuffer(1, 1, 0xffff, fg, bg),
|
||||
}
|
||||
t.useMainBuffer()
|
||||
}
|
||||
|
||||
// Pty exposes the underlying terminal pty, if it exists
|
||||
func (t *Terminal) Pty() *os.File {
|
||||
return t.pty
|
||||
}
|
||||
|
||||
func (t *Terminal) WriteToPty(data []byte) error {
|
||||
_, err := t.pty.Write(data)
|
||||
return err
|
||||
}
|
||||
|
||||
func (t *Terminal) GetTitle() string {
|
||||
return t.windowManipulator.GetTitle()
|
||||
}
|
||||
|
||||
func (t *Terminal) Theme() *Theme {
|
||||
return t.theme
|
||||
}
|
||||
|
||||
// write takes data from StdOut of the child shell and processes it
|
||||
func (t *Terminal) Write(data []byte) (n int, err error) {
|
||||
reader := bufio.NewReader(bytes.NewBuffer(data))
|
||||
for {
|
||||
r, size, err := reader.ReadRune()
|
||||
if err == io.EOF {
|
||||
break
|
||||
}
|
||||
t.processChan <- MeasuredRune{Rune: r, Width: size}
|
||||
}
|
||||
return len(data), nil
|
||||
}
|
||||
|
||||
func (t *Terminal) SetSize(rows, cols uint16) error {
|
||||
if t.pty == nil {
|
||||
return fmt.Errorf("terminal is not running")
|
||||
}
|
||||
|
||||
t.log("RESIZE %d, %d\n", cols, rows)
|
||||
|
||||
t.activeBuffer.resizeView(cols, rows)
|
||||
|
||||
if err := pty.Setsize(t.pty, &pty.Winsize{
|
||||
Rows: rows,
|
||||
Cols: cols,
|
||||
}); err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
// Run starts the terminal/shell proxying process
|
||||
func (t *Terminal) Run(updateChan chan struct{}, rows uint16, cols uint16) error {
|
||||
|
||||
os.Setenv("TERM", "xterm-256color")
|
||||
|
||||
t.updateChan = updateChan
|
||||
|
||||
if t.shell == "" {
|
||||
t.shell = os.Getenv("SHELL")
|
||||
if t.shell == "" {
|
||||
t.shell = "/bin/sh"
|
||||
}
|
||||
}
|
||||
|
||||
// Create arbitrary command.
|
||||
c := exec.Command(t.shell)
|
||||
|
||||
// Start the command with a pty.
|
||||
var err error
|
||||
t.pty, err = pty.Start(c)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
// Make sure to close the pty at the end.
|
||||
defer func() { _ = t.pty.Close() }() // Best effort.
|
||||
|
||||
if err := t.SetSize(rows, cols); err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
// Set stdin in raw mode.
|
||||
|
||||
if fd := int(os.Stdin.Fd()); term.IsTerminal(fd) {
|
||||
oldState, err := term.MakeRaw(fd)
|
||||
if err != nil {
|
||||
t.windowManipulator.ReportError(err)
|
||||
}
|
||||
defer func() { _ = term.Restore(fd, oldState) }() // Best effort.
|
||||
}
|
||||
|
||||
go t.process()
|
||||
|
||||
t.running = true
|
||||
|
||||
t.windowManipulator.SetTitle("darktile")
|
||||
|
||||
if t.initialCommand != "" {
|
||||
if err := t.WriteToPty([]byte(t.initialCommand)); err != nil {
|
||||
return err
|
||||
}
|
||||
}
|
||||
|
||||
_, _ = io.Copy(t, t.pty)
|
||||
close(t.closeChan)
|
||||
return nil
|
||||
}
|
||||
|
||||
func (t *Terminal) IsRunning() bool {
|
||||
return t.running
|
||||
}
|
||||
|
||||
func (t *Terminal) requestRender() {
|
||||
select {
|
||||
case t.updateChan <- struct{}{}:
|
||||
default:
|
||||
}
|
||||
}
|
||||
|
||||
func (t *Terminal) processSequence(mr MeasuredRune) (render bool) {
|
||||
if mr.Rune == 0x1b {
|
||||
return t.handleANSI(t.processChan)
|
||||
}
|
||||
return t.processRunes(mr)
|
||||
}
|
||||
|
||||
func (t *Terminal) process() {
|
||||
for {
|
||||
select {
|
||||
case <-t.closeChan:
|
||||
return
|
||||
case mr := <-t.processChan:
|
||||
if t.processSequence(mr) {
|
||||
t.requestRender()
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
func (t *Terminal) processRunes(runes ...MeasuredRune) (renderRequired bool) {
|
||||
t.mu.Lock()
|
||||
defer t.mu.Unlock()
|
||||
|
||||
for _, r := range runes {
|
||||
|
||||
t.log("%c 0x%X", r.Rune, r.Rune)
|
||||
|
||||
switch r.Rune {
|
||||
case 0x05: //enq
|
||||
continue
|
||||
case 0x07: //bell
|
||||
//DING DING DING
|
||||
continue
|
||||
case 0x8: //backspace
|
||||
t.activeBuffer.backspace()
|
||||
renderRequired = true
|
||||
case 0x9: //tab
|
||||
t.activeBuffer.tab()
|
||||
renderRequired = true
|
||||
case 0xa, 0xc: //newLine/form feed
|
||||
t.activeBuffer.newLine()
|
||||
renderRequired = true
|
||||
case 0xb: //vertical tab
|
||||
t.activeBuffer.verticalTab()
|
||||
renderRequired = true
|
||||
case 0xd: //carriageReturn
|
||||
t.activeBuffer.carriageReturn()
|
||||
renderRequired = true
|
||||
case 0xe: //shiftOut
|
||||
t.activeBuffer.currentCharset = 1
|
||||
case 0xf: //shiftIn
|
||||
t.activeBuffer.currentCharset = 0
|
||||
default:
|
||||
if r.Rune < 0x20 {
|
||||
// handle any other control chars here?
|
||||
continue
|
||||
}
|
||||
|
||||
t.activeBuffer.write(t.translateRune(r))
|
||||
renderRequired = true
|
||||
}
|
||||
}
|
||||
|
||||
return renderRequired
|
||||
}
|
||||
|
||||
func (t *Terminal) translateRune(b MeasuredRune) MeasuredRune {
|
||||
table := t.activeBuffer.charsets[t.activeBuffer.currentCharset]
|
||||
if table == nil {
|
||||
return b
|
||||
}
|
||||
chr, ok := (*table)[b.Rune]
|
||||
if ok {
|
||||
return MeasuredRune{Rune: chr, Width: 1}
|
||||
}
|
||||
return b
|
||||
}
|
||||
|
||||
func (t *Terminal) setTitle(title string) {
|
||||
t.windowManipulator.SetTitle(title)
|
||||
}
|
||||
|
||||
func (t *Terminal) switchBuffer(index uint8) {
|
||||
var carrySize bool
|
||||
var w, h uint16
|
||||
if t.activeBuffer != nil {
|
||||
w, h = t.activeBuffer.viewWidth, t.activeBuffer.viewHeight
|
||||
carrySize = true
|
||||
}
|
||||
t.activeBuffer = t.buffers[index]
|
||||
if carrySize {
|
||||
t.activeBuffer.resizeView(w, h)
|
||||
}
|
||||
}
|
||||
|
||||
func (t *Terminal) GetMouseMode() MouseMode {
|
||||
return t.mouseMode
|
||||
}
|
||||
|
||||
func (t *Terminal) GetMouseExtMode() MouseExtMode {
|
||||
return t.mouseExtMode
|
||||
}
|
||||
|
||||
func (t *Terminal) GetActiveBuffer() *Buffer {
|
||||
return t.activeBuffer
|
||||
}
|
||||
|
||||
func (t *Terminal) useMainBuffer() {
|
||||
t.switchBuffer(MainBuffer)
|
||||
}
|
||||
|
||||
func (t *Terminal) useAltBuffer() {
|
||||
t.switchBuffer(AltBuffer)
|
||||
}
|
||||
|
||||
func (t *Terminal) Lock() {
|
||||
t.mu.Lock()
|
||||
}
|
||||
|
||||
func (t *Terminal) Unlock() {
|
||||
t.mu.Unlock()
|
||||
}
|
214
termutil/theme.go
Normal file
214
termutil/theme.go
Normal file
@ -0,0 +1,214 @@
|
||||
package termutil
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"image/color"
|
||||
"strconv"
|
||||
)
|
||||
|
||||
type Colour uint8
|
||||
|
||||
// See https://en.wikipedia.org/wiki/ANSI_escape_code#3-bit_and_4-bit
|
||||
const (
|
||||
ColourBlack Colour = iota
|
||||
ColourRed
|
||||
ColourGreen
|
||||
ColourYellow
|
||||
ColourBlue
|
||||
ColourMagenta
|
||||
ColourCyan
|
||||
ColourWhite
|
||||
ColourBrightBlack
|
||||
ColourBrightRed
|
||||
ColourBrightGreen
|
||||
ColourBrightYellow
|
||||
ColourBrightBlue
|
||||
ColourBrightMagenta
|
||||
ColourBrightCyan
|
||||
ColourBrightWhite
|
||||
ColourBackground
|
||||
ColourForeground
|
||||
ColourSelectionBackground
|
||||
ColourSelectionForeground
|
||||
ColourCursorForeground
|
||||
ColourCursorBackground
|
||||
)
|
||||
|
||||
type Theme struct {
|
||||
colourMap map[Colour]color.Color
|
||||
}
|
||||
|
||||
var (
|
||||
map4Bit = map[uint8]Colour{
|
||||
30: ColourBlack,
|
||||
31: ColourRed,
|
||||
32: ColourGreen,
|
||||
33: ColourYellow,
|
||||
34: ColourBlue,
|
||||
35: ColourMagenta,
|
||||
36: ColourCyan,
|
||||
37: ColourWhite,
|
||||
90: ColourBrightBlack,
|
||||
91: ColourBrightRed,
|
||||
92: ColourBrightGreen,
|
||||
93: ColourBrightYellow,
|
||||
94: ColourBrightBlue,
|
||||
95: ColourBrightMagenta,
|
||||
96: ColourBrightCyan,
|
||||
97: ColourBrightWhite,
|
||||
40: ColourBlack,
|
||||
41: ColourRed,
|
||||
42: ColourGreen,
|
||||
43: ColourYellow,
|
||||
44: ColourBlue,
|
||||
45: ColourMagenta,
|
||||
46: ColourCyan,
|
||||
47: ColourWhite,
|
||||
100: ColourBrightBlack,
|
||||
101: ColourBrightRed,
|
||||
102: ColourBrightGreen,
|
||||
103: ColourBrightYellow,
|
||||
104: ColourBrightBlue,
|
||||
105: ColourBrightMagenta,
|
||||
106: ColourBrightCyan,
|
||||
107: ColourBrightWhite,
|
||||
}
|
||||
)
|
||||
|
||||
func (t *Theme) ColourFrom4Bit(code uint8) color.Color {
|
||||
colour, ok := map4Bit[code]
|
||||
if !ok {
|
||||
return color.Black
|
||||
}
|
||||
return t.colourMap[colour]
|
||||
}
|
||||
|
||||
func (t *Theme) DefaultBackground() color.Color {
|
||||
c, ok := t.colourMap[ColourBackground]
|
||||
if !ok {
|
||||
return color.RGBA{0, 0, 0, 0xff}
|
||||
}
|
||||
return c
|
||||
}
|
||||
|
||||
func (t *Theme) DefaultForeground() color.Color {
|
||||
c, ok := t.colourMap[ColourForeground]
|
||||
if !ok {
|
||||
return color.RGBA{255, 255, 255, 0xff}
|
||||
}
|
||||
return c
|
||||
}
|
||||
|
||||
func (t *Theme) SelectionBackground() color.Color {
|
||||
c, ok := t.colourMap[ColourSelectionBackground]
|
||||
if !ok {
|
||||
return color.RGBA{0, 0, 0, 0xff}
|
||||
}
|
||||
return c
|
||||
}
|
||||
|
||||
func (t *Theme) SelectionForeground() color.Color {
|
||||
c, ok := t.colourMap[ColourSelectionForeground]
|
||||
if !ok {
|
||||
return color.RGBA{255, 255, 255, 0xff}
|
||||
}
|
||||
return c
|
||||
}
|
||||
|
||||
func (t *Theme) CursorBackground() color.Color {
|
||||
c, ok := t.colourMap[ColourCursorBackground]
|
||||
if !ok {
|
||||
return color.RGBA{255, 255, 255, 0xff}
|
||||
}
|
||||
return c
|
||||
}
|
||||
|
||||
func (t *Theme) CursorForeground() color.Color {
|
||||
c, ok := t.colourMap[ColourCursorForeground]
|
||||
if !ok {
|
||||
return color.RGBA{0, 0, 0, 0xff}
|
||||
}
|
||||
return c
|
||||
}
|
||||
|
||||
func (t *Theme) ColourFrom8Bit(n string) (color.Color, error) {
|
||||
|
||||
index, err := strconv.Atoi(n)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
if index < 16 {
|
||||
return t.colourMap[Colour(index)], nil
|
||||
}
|
||||
|
||||
if index >= 232 {
|
||||
c := ((index - 232) * 0xff) / 0x18
|
||||
return color.RGBA{
|
||||
R: byte(c),
|
||||
G: byte(c),
|
||||
B: byte(c),
|
||||
A: 0xff,
|
||||
}, nil
|
||||
}
|
||||
|
||||
var colour color.RGBA
|
||||
colour.A = 0xff
|
||||
indexR := ((index - 16) / 36)
|
||||
if indexR > 0 {
|
||||
colour.R = uint8(55 + indexR*40)
|
||||
}
|
||||
indexG := (((index - 16) % 36) / 6)
|
||||
if indexG > 0 {
|
||||
colour.G = uint8(55 + indexG*40)
|
||||
}
|
||||
indexB := ((index - 16) % 6)
|
||||
if indexB > 0 {
|
||||
colour.B = uint8(55 + indexB*40)
|
||||
}
|
||||
|
||||
return colour, nil
|
||||
}
|
||||
|
||||
func (t *Theme) ColourFrom24Bit(r, g, b string) (color.Color, error) {
|
||||
ri, err := strconv.Atoi(r)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
gi, err := strconv.Atoi(g)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
bi, err := strconv.Atoi(b)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
return color.RGBA{
|
||||
R: byte(ri),
|
||||
G: byte(gi),
|
||||
B: byte(bi),
|
||||
A: 0xff,
|
||||
}, nil
|
||||
}
|
||||
|
||||
func (t *Theme) ColourFromAnsi(ansi []string, bg bool) (color.Color, error) {
|
||||
|
||||
if len(ansi) == 0 {
|
||||
return nil, fmt.Errorf("invalid ansi colour code")
|
||||
}
|
||||
|
||||
switch ansi[0] {
|
||||
case "2":
|
||||
if len(ansi) != 4 {
|
||||
return nil, fmt.Errorf("invalid 24-bit ansi colour code")
|
||||
}
|
||||
return t.ColourFrom24Bit(ansi[1], ansi[2], ansi[3])
|
||||
case "5":
|
||||
if len(ansi) != 2 {
|
||||
return nil, fmt.Errorf("invalid 8-bit ansi colour code")
|
||||
}
|
||||
return t.ColourFrom8Bit(ansi[1])
|
||||
default:
|
||||
return nil, fmt.Errorf("invalid ansi colour code")
|
||||
}
|
||||
}
|
35
termutil/theme_factory.go
Normal file
35
termutil/theme_factory.go
Normal file
@ -0,0 +1,35 @@
|
||||
package termutil
|
||||
|
||||
import "image/color"
|
||||
|
||||
type ThemeFactory struct {
|
||||
theme *Theme
|
||||
colourMap map[Colour]color.Color
|
||||
}
|
||||
|
||||
func NewThemeFactory() *ThemeFactory {
|
||||
return &ThemeFactory{
|
||||
theme: &Theme{
|
||||
colourMap: map[Colour]color.Color{},
|
||||
},
|
||||
colourMap: make(map[Colour]color.Color),
|
||||
}
|
||||
}
|
||||
|
||||
func (t *ThemeFactory) Build() *Theme {
|
||||
for id, col := range t.colourMap {
|
||||
r, g, b, _ := col.RGBA()
|
||||
t.theme.colourMap[id] = color.RGBA{
|
||||
R: uint8(r / 0xff),
|
||||
G: uint8(g / 0xff),
|
||||
B: uint8(b / 0xff),
|
||||
A: 0xff,
|
||||
}
|
||||
}
|
||||
return t.theme
|
||||
}
|
||||
|
||||
func (t *ThemeFactory) WithColour(key Colour, colour color.Color) *ThemeFactory {
|
||||
t.colourMap[key] = colour
|
||||
return t
|
||||
}
|
31
voidterm.go
31
voidterm.go
@ -1,27 +1,18 @@
|
||||
package voidterm
|
||||
|
||||
import (
|
||||
"os"
|
||||
"sync"
|
||||
)
|
||||
|
||||
const (
|
||||
MainBuffer uint8 = 0
|
||||
AltBuffer uint8 = 1
|
||||
InternalBuffer uint8 = 2
|
||||
"github.com/1f349/voidterm/termutil"
|
||||
)
|
||||
|
||||
type VoidTerm struct {
|
||||
mu sync.Mutex
|
||||
pty *os.File
|
||||
updateChan chan struct{}
|
||||
processChan chan MeasuredRune
|
||||
closeChan chan struct{}
|
||||
buffers []*Buffer
|
||||
activeBuffer *Buffer
|
||||
mouseMode MouseMode
|
||||
mouseExtMode MouseExtMode
|
||||
running bool
|
||||
shell string
|
||||
initialCommand string
|
||||
term *termutil.Terminal
|
||||
}
|
||||
|
||||
func New(dockerContainer, shell string) *VoidTerm {
|
||||
term := termutil.New(termutil.WithShell(shell))
|
||||
return &VoidTerm{term: term}
|
||||
}
|
||||
|
||||
func (v *VoidTerm) Run() {
|
||||
v.term.Run()
|
||||
}
|
||||
|
Loading…
Reference in New Issue
Block a user