diff --git a/go.mod b/go.mod index 58aefd19..d9ca34fe 100644 --- a/go.mod +++ b/go.mod @@ -5,7 +5,7 @@ go 1.22 require ( github.com/adrg/xdg v0.4.0 github.com/charmbracelet/bubbles v0.18.0 - github.com/charmbracelet/bubbletea v0.25.0 + github.com/charmbracelet/bubbletea v0.26.6 github.com/charmbracelet/glamour v0.6.0 github.com/charmbracelet/lipgloss v0.10.0 github.com/getkin/kin-openapi v0.124.0 @@ -40,9 +40,13 @@ require ( github.com/atotto/clipboard v0.1.4 // indirect github.com/aymanbagabas/go-osc52/v2 v2.0.1 // indirect github.com/aymerick/douceur v0.2.0 // indirect - github.com/containerd/console v1.0.4-0.20230313162750-1ae8d489ac81 // indirect + github.com/charmbracelet/x/ansi v0.1.2 // indirect + github.com/charmbracelet/x/input v0.1.0 // indirect + github.com/charmbracelet/x/term v0.1.1 // indirect + github.com/charmbracelet/x/windows v0.1.0 // indirect github.com/davecgh/go-spew v1.1.2-0.20180830191138-d8f796af33cc // indirect github.com/dlclark/regexp2 v1.4.0 // indirect + github.com/erikgeiser/coninput v0.0.0-20211004153227-1c3628e74d0f // indirect github.com/felixge/httpsnoop v1.0.3 // indirect github.com/fsnotify/fsnotify v1.7.0 // indirect github.com/go-openapi/jsonpointer v0.20.2 // indirect @@ -69,7 +73,7 @@ require ( github.com/microcosm-cc/bluemonday v1.0.25 // indirect github.com/mitchellh/mapstructure v1.5.0 // indirect github.com/mohae/deepcopy v0.0.0-20170929034955-c48cc78d4826 // indirect - github.com/muesli/ansi v0.0.0-20211018074035-2e021307bc4b // indirect + github.com/muesli/ansi v0.0.0-20230316100256-276c6243b2f6 // indirect github.com/muesli/cancelreader v0.2.2 // indirect github.com/muesli/termenv v0.15.2 // indirect github.com/olekukonko/tablewriter v0.0.5 // indirect @@ -86,6 +90,7 @@ require ( github.com/spf13/cast v1.6.0 // indirect github.com/stretchr/objx v0.5.2 // indirect github.com/subosito/gotenv v1.6.0 // indirect + github.com/xo/terminfo v0.0.0-20220910002029-abceb7e1c41e // indirect github.com/yuin/goldmark v1.5.2 // indirect github.com/yuin/goldmark-emoji v1.0.1 // indirect go.uber.org/atomic v1.9.0 // indirect diff --git a/go.sum b/go.sum index cada5711..8e0e95ec 100644 --- a/go.sum +++ b/go.sum @@ -51,19 +51,25 @@ github.com/bmatcuk/doublestar v1.1.1/go.mod h1:UD6OnuiIn0yFxxA2le/rnRU1G4RaI4UvF github.com/census-instrumentation/opencensus-proto v0.2.1/go.mod h1:f6KPmirojxKA12rnyqOA5BBL4O983OfeGPqjHWSTneU= github.com/charmbracelet/bubbles v0.18.0 h1:PYv1A036luoBGroX6VWjQIE9Syf2Wby2oOl/39KLfy0= github.com/charmbracelet/bubbles v0.18.0/go.mod h1:08qhZhtIwzgrtBjAcJnij1t1H0ZRjwHyGsy6AL11PSw= -github.com/charmbracelet/bubbletea v0.25.0 h1:bAfwk7jRz7FKFl9RzlIULPkStffg5k6pNt5dywy4TcM= -github.com/charmbracelet/bubbletea v0.25.0/go.mod h1:EN3QDR1T5ZdWmdfDzYcqOCAps45+QIJbLOBxmVNWNNg= +github.com/charmbracelet/bubbletea v0.26.6 h1:zTCWSuST+3yZYZnVSvbXwKOPRSNZceVeqpzOLN2zq1s= +github.com/charmbracelet/bubbletea v0.26.6/go.mod h1:dz8CWPlfCCGLFbBlTY4N7bjLiyOGDJEnd2Muu7pOWhk= github.com/charmbracelet/glamour v0.6.0 h1:wi8fse3Y7nfcabbbDuwolqTqMQPMnVPeZhDM273bISc= github.com/charmbracelet/glamour v0.6.0/go.mod h1:taqWV4swIMMbWALc0m7AfE9JkPSU8om2538k9ITBxOc= github.com/charmbracelet/lipgloss v0.10.0 h1:KWeXFSexGcfahHX+54URiZGkBFazf70JNMtwg/AFW3s= github.com/charmbracelet/lipgloss v0.10.0/go.mod h1:Wig9DSfvANsxqkRsqj6x87irdy123SR4dOXlKa91ciE= +github.com/charmbracelet/x/ansi v0.1.2 h1:6+LR39uG8DE6zAmbu023YlqjJHkYXDF1z36ZwzO4xZY= +github.com/charmbracelet/x/ansi v0.1.2/go.mod h1:dk73KoMTT5AX5BsX0KrqhsTqAnhZZoCBjs7dGWp4Ktw= +github.com/charmbracelet/x/input v0.1.0 h1:TEsGSfZYQyOtp+STIjyBq6tpRaorH0qpwZUj8DavAhQ= +github.com/charmbracelet/x/input v0.1.0/go.mod h1:ZZwaBxPF7IG8gWWzPUVqHEtWhc1+HXJPNuerJGRGZ28= +github.com/charmbracelet/x/term v0.1.1 h1:3cosVAiPOig+EV4X9U+3LDgtwwAoEzJjNdwbXDjF6yI= +github.com/charmbracelet/x/term v0.1.1/go.mod h1:wB1fHt5ECsu3mXYusyzcngVWWlu1KKUmmLhfgr/Flxw= +github.com/charmbracelet/x/windows v0.1.0 h1:gTaxdvzDM5oMa/I2ZNF7wN78X/atWemG9Wph7Ika2k4= +github.com/charmbracelet/x/windows v0.1.0/go.mod h1:GLEO/l+lizvFDBPLIOk+49gdX49L9YWMB5t+DZd0jkQ= github.com/chzyer/logex v1.1.10/go.mod h1:+Ywpsq7O8HXn0nuIou7OrIPyXbp3wmkHB+jjWRnGsAI= github.com/chzyer/readline v0.0.0-20180603132655-2972be24d48e/go.mod h1:nSuG5e5PlCu98SY8svDHJxuZscDgtXS6KTTbou5AhLI= github.com/chzyer/test v0.0.0-20180213035817-a1ea475d72b1/go.mod h1:Q3SI9o4m/ZMnBNeIyt5eFwwo7qiLfzFZmjNmxjkiQlU= github.com/client9/misspell v0.3.4/go.mod h1:qj6jICC3Q7zFZvVWo7KLAzC3yx5G7kyvSDkc90ppPyw= github.com/cncf/udpa/go v0.0.0-20191209042840-269d4d468f6f/go.mod h1:M8M6+tZqaGXZJjfX53e64911xZQV5JYwmTeXPW+k8Sc= -github.com/containerd/console v1.0.4-0.20230313162750-1ae8d489ac81 h1:q2hJAaP1k2wIvVRd/hEHD7lacgqrCPS+k8g1MndzfWY= -github.com/containerd/console v1.0.4-0.20230313162750-1ae8d489ac81/go.mod h1:YynlIjWYF8myEu6sdkwKIvGQq+cOckRm6So2avqoYAk= github.com/cpuguy83/go-md2man/v2 v2.0.3/go.mod h1:tgQtvFlXSQOSOSIRvRPT7W67SCa46tRHOmNcaadrF8o= github.com/davecgh/go-spew v1.1.0/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38= github.com/davecgh/go-spew v1.1.1/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38= @@ -75,6 +81,8 @@ github.com/envoyproxy/go-control-plane v0.9.0/go.mod h1:YTl/9mNaCwkRvm6d1a2C3ymF github.com/envoyproxy/go-control-plane v0.9.1-0.20191026205805-5f8ba28d4473/go.mod h1:YTl/9mNaCwkRvm6d1a2C3ymFceY/DCBVvsKhRF0iEA4= github.com/envoyproxy/go-control-plane v0.9.4/go.mod h1:6rpuAdCZL397s3pYoYcLgu1mIlRU8Am5FuJP05cCM98= github.com/envoyproxy/protoc-gen-validate v0.1.0/go.mod h1:iSmxcyjqTsJpI2R4NaDN7+kN2VEUnK/pcBlmesArF7c= +github.com/erikgeiser/coninput v0.0.0-20211004153227-1c3628e74d0f h1:Y/CXytFA4m6baUTXGLOoWe4PQhGxaX0KpnayAqC48p4= +github.com/erikgeiser/coninput v0.0.0-20211004153227-1c3628e74d0f/go.mod h1:vw97MGsxSvLiUE2X8qFplwetxpGLQrlU1Q9AUEIzCaM= github.com/felixge/httpsnoop v1.0.3 h1:s/nj+GCswXYzN5v2DpNMuMQYe+0DDwt5WVCU6CWBdXk= github.com/felixge/httpsnoop v1.0.3/go.mod h1:m8KPJKqk1gH5J9DgRY2ASl2lWCfGKXixSwevea8zH2U= github.com/frankban/quicktest v1.14.6 h1:7Xjx+VpznH+oBnejlPUj8oUpdxnVs4f8XU8WnHkI4W8= @@ -232,8 +240,8 @@ github.com/mitchellh/mapstructure v1.5.0 h1:jeMsZIYE/09sWLaz43PL7Gy6RuMjD2eJVyua github.com/mitchellh/mapstructure v1.5.0/go.mod h1:bFUtVrKA4DC2yAKiSyO/QUcy7e+RRV2QTWOzhPopBRo= github.com/mohae/deepcopy v0.0.0-20170929034955-c48cc78d4826 h1:RWengNIwukTxcDr9M+97sNutRR1RKhG96O6jWumTTnw= github.com/mohae/deepcopy v0.0.0-20170929034955-c48cc78d4826/go.mod h1:TaXosZuwdSHYgviHp1DAtfrULt5eUgsSMsZf+YrPgl8= -github.com/muesli/ansi v0.0.0-20211018074035-2e021307bc4b h1:1XF24mVaiu7u+CFywTdcDo2ie1pzzhwjt6RHqzpMU34= -github.com/muesli/ansi v0.0.0-20211018074035-2e021307bc4b/go.mod h1:fQuZ0gauxyBcmsdE3ZT4NasjaRdxmbCS0jRHsrWu3Ho= +github.com/muesli/ansi v0.0.0-20230316100256-276c6243b2f6 h1:ZK8zHtRHOkbHy6Mmr5D264iyp3TiX5OmNcI5cIARiQI= +github.com/muesli/ansi v0.0.0-20230316100256-276c6243b2f6/go.mod h1:CJlz5H+gyd6CUWT45Oy4q24RdLyn7Md9Vj2/ldJBSIo= github.com/muesli/cancelreader v0.2.2 h1:3I4Kt4BQjOR54NavqnDogx/MIoWBFa0StPA8ELUXHmA= github.com/muesli/cancelreader v0.2.2/go.mod h1:3XuTXfFS2VjM+HTLZY9Ak0l6eUKfijIfMUZ4EgX0QYo= github.com/muesli/reflow v0.3.0 h1:IFsN6K9NfGtjeggFP+68I4chLZV2yIKsXJFNZ+eWh6s= @@ -310,6 +318,8 @@ github.com/ugorji/go/codec v1.2.11 h1:BMaWp1Bb6fHwEtbplGBGJ498wD+LKlNSl25MjdZY4d github.com/ugorji/go/codec v1.2.11/go.mod h1:UNopzCgEMSXjBc6AOMqYvWC1ktqTAfzJZUZgYf6w6lg= github.com/wsxiaoys/terminal v0.0.0-20160513160801-0940f3fc43a0 h1:3UeQBvD0TFrlVjOeLOBz+CPAI8dnbqNSVwUwRrkp7vQ= github.com/wsxiaoys/terminal v0.0.0-20160513160801-0940f3fc43a0/go.mod h1:IXCdmsXIht47RaVFLEdVnh1t+pgYtTAhQGj73kz+2DM= +github.com/xo/terminfo v0.0.0-20220910002029-abceb7e1c41e h1:JVG44RsyaB9T2KIHavMF/ppJZNG9ZpyihvCd0w101no= +github.com/xo/terminfo v0.0.0-20220910002029-abceb7e1c41e/go.mod h1:RbqR21r5mrJuqunuUZ/Dhy/avygyECGrLceyNeo4LiM= github.com/yuin/goldmark v1.1.25/go.mod h1:3hX8gzYuyVAZsxl0MRgGTJEmQBFcNTphYh9decYSb74= github.com/yuin/goldmark v1.1.27/go.mod h1:3hX8gzYuyVAZsxl0MRgGTJEmQBFcNTphYh9decYSb74= github.com/yuin/goldmark v1.1.32/go.mod h1:3hX8gzYuyVAZsxl0MRgGTJEmQBFcNTphYh9decYSb74= @@ -441,6 +451,7 @@ golang.org/x/sys v0.0.0-20200515095857-1151b9dac4a9/go.mod h1:h1NjWce9XRLGQEsW7w golang.org/x/sys v0.0.0-20200523222454-059865788121/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20200803210538-64077c9b5642/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20210615035016-665e8c7367d1/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= +golang.org/x/sys v0.0.0-20210809222454-d867a43fc93e/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/sys v0.0.0-20211025201205-69cdffdb9359/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/sys v0.0.0-20220728004956-3c1f35247d10/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/sys v0.0.0-20220811171246-fbc7d0a398ab/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= diff --git a/vendor/github.com/charmbracelet/bubbletea/.golangci-soft.yml b/vendor/github.com/charmbracelet/bubbletea/.golangci-soft.yml index ef456e06..1b6824bb 100644 --- a/vendor/github.com/charmbracelet/bubbletea/.golangci-soft.yml +++ b/vendor/github.com/charmbracelet/bubbletea/.golangci-soft.yml @@ -23,7 +23,6 @@ linters: - gomnd - gomoddirectives - goprintffuncname - - ifshort # - lll - misspell - nakedret diff --git a/vendor/github.com/charmbracelet/bubbletea/.golangci.yml b/vendor/github.com/charmbracelet/bubbletea/.golangci.yml index a5a91d0d..3affce91 100644 --- a/vendor/github.com/charmbracelet/bubbletea/.golangci.yml +++ b/vendor/github.com/charmbracelet/bubbletea/.golangci.yml @@ -16,6 +16,7 @@ linters: enable: - bodyclose - exportloopref + - gofumpt - goimports - gosec - nilerr diff --git a/vendor/github.com/charmbracelet/bubbletea/.goreleaser.yml b/vendor/github.com/charmbracelet/bubbletea/.goreleaser.yml new file mode 100644 index 00000000..40d9f298 --- /dev/null +++ b/vendor/github.com/charmbracelet/bubbletea/.goreleaser.yml @@ -0,0 +1,6 @@ +includes: + - from_url: + url: charmbracelet/meta/main/goreleaser-lib.yaml + +# yaml-language-server: $schema=https://goreleaser.com/static/schema-pro.json + diff --git a/vendor/github.com/charmbracelet/bubbletea/README.md b/vendor/github.com/charmbracelet/bubbletea/README.md index 0834246c..9a8d93e2 100644 --- a/vendor/github.com/charmbracelet/bubbletea/README.md +++ b/vendor/github.com/charmbracelet/bubbletea/README.md @@ -1,10 +1,11 @@ # Bubble Tea

- Bubble Tea Title Treatment
+ Bubble Tea Title Treatment
Latest Release GoDoc Build Status + phorm.ai

The fun, functional and stateful way to build terminal apps. A Go framework @@ -266,15 +267,21 @@ delve in headless mode and then connect to it: ```bash # Start the debugger -$ dlv debug --headless . -API server listening at: 127.0.0.1:34241 +$ dlv debug --headless --api-version=2 --listen=127.0.0.1:43000 . +API server listening at: 127.0.0.1:43000 # Connect to it from another terminal -$ dlv connect 127.0.0.1:34241 +$ dlv connect 127.0.0.1:43000 ``` -Note that the default port used will vary on your system and per run, so -actually watch out what address the first `dlv` run tells you to connect to. +If you do not explicitly supply the `--listen` flag, the port used will vary +per run, so passing this in makes the debugger easier to use from a script +or your IDE of choice. + +Additionally, we pass in `--api-version=2` because delve defaults to version 1 +for backwards compatibility reasons. However, delve recommends using version 2 +for all new development and some clients may no longer work with version 1. +For more information, see the [Delve documentation](https://github.com/go-delve/delve/tree/master/Documentation/api). ### Logging Stuff @@ -302,6 +309,7 @@ your program in another window. * [Lip Gloss][lipgloss]: Style, format and layout tools for terminal applications * [Harmonica][harmonica]: A spring animation library for smooth, natural motion * [BubbleZone][bubblezone]: Easy mouse event tracking for Bubble Tea components +* [ntcharts][ntcharts]: A terminal charting library built for Bubble Tea and [Lip Gloss][lipgloss] * [Termenv][termenv]: Advanced ANSI styling for terminal applications * [Reflow][reflow]: Advanced ANSI-aware methods for working with text @@ -309,6 +317,7 @@ your program in another window. [lipgloss]: https://github.com/charmbracelet/lipgloss [harmonica]: https://github.com/charmbracelet/harmonica [bubblezone]: https://github.com/lrstanley/bubblezone +[ntcharts]: https://github.com/NimbleMarkets/ntcharts [termenv]: https://github.com/muesli/termenv [reflow]: https://github.com/muesli/reflow @@ -316,25 +325,36 @@ your program in another window. For some Bubble Tea programs in production, see: +* [ASCII Movie](https://github.com/gabe565/ascii-movie): a Star Wars ASCII art movie player * [AT CLI](https://github.com/daskycodes/at_cli): execute AT Commands via serial port connections * [Aztify](https://github.com/Azure/aztfy): bring Microsoft Azure resources under Terraform * [brows](https://github.com/rubysolo/brows): a GitHub release browser * [Canard](https://github.com/mrusme/canard): an RSS client * [charm](https://github.com/charmbracelet/charm): the official Charm user account manager +* [chatgpt-cli](https://github.com/j178/chatgpt): a CLI for ChatGPT +* [chatgpt-tui](https://github.com/tearingItUp786/chatgpt-tui): a TUI for ChatGPT with SQLite sessions +* [ChatGPTUI](https://github.com/dwisiswant0/chatgptui): a TUI for ChatGPT * [chezmoi](https://github.com/twpayne/chezmoi): securely manage your dotfiles across multiple machines -* [chtop](https://github.com/chhetripradeep/chtop): monitor your ClickHouse node without leaving terminal +* [chip-8](https://github.com/braheezy/chip-8): a CHIP-8 interpreter +* [chtop](https://github.com/chhetripradeep/chtop): monitor your ClickHouse node without leaving the terminal * [circumflex](https://github.com/bensadeh/circumflex): read Hacker News in the terminal * [clidle](https://github.com/ajeetdsouza/clidle): a Wordle clone * [cLive](https://github.com/koki-develop/clive): automate terminal operations and view them live in a browser * [container-canary](https://github.com/NVIDIA/container-canary): a container validator * [countdown](https://github.com/aldernero/countdown): a multi-event countdown timer -* [dns53](https://github.com/purpleclay/dns53): dynamic DNS with Amazon Route53. Expose your EC2 quickly, securely and privately -* [eks-node-viewer](https://github.com/awslabs/eks-node-viewer): a tool for visualizing dynamic node usage within an eks cluster -* [enola](https://github.com/sherlock-project/enola): hunt down social media accounts by username across social networks +* [CRT](https://github.com/BigJk/crt): a simple terminal emulator for running Bubble Tea in a dedicated window, with optional shaders +* [cueitup](https://github.com/dhth/cueitup): inspect messages in an AWS SQS queue in a simple and deliberate manner +* [Daytona](https://github.com/daytonaio/daytona): an development environment manager +* [dns53](https://github.com/purpleclay/dns53): dynamic DNS with Amazon Route53; expose your EC2 quickly, securely and privately +* [eks-node-viewer](https://github.com/awslabs/eks-node-viewer): a tool for visualizing dynamic node usage within an EKS cluster +* [End Of Eden](https://github.com/BigJk/end_of_eden): a "Slay the Spire"-like, roguelike deck-builder game +* [enola](https://github.com/sherlock-project/enola): find social media accounts by username across social networks * [flapioca](https://github.com/kbrgl/flapioca): Flappy Bird on the CLI! * [fm](https://github.com/knipferrc/fm): a terminal-based file manager * [fork-cleaner](https://github.com/caarlos0/fork-cleaner): clean up old and inactive forks in your GitHub account +* [fractals-cli](https://github.com/MicheleFiladelfia/fractals-cli): a multiplatform terminal fractal explorer * [fztea](https://github.com/jon4hz/fztea): a Flipper Zero TUI +* [gama](https://github.com/termkit/gama): manage GitHub Actions from the terminal * [gambit](https://github.com/maaslalani/gambit): chess in the terminal * [gembro](https://git.sr.ht/~rafael/gembro): a mouse-driven Gemini browser * [gh-b](https://github.com/joaom00/gh-b): a GitHub CLI extension for managing branches @@ -343,37 +363,56 @@ For some Bubble Tea programs in production, see: * [Glow](https://github.com/charmbracelet/glow): a markdown reader, browser, and online markdown stash * [go-sweep](https://github.com/maxpaulus43/go-sweep): Minesweeper in the terminal * [gocovsh](https://github.com/orlangure/gocovsh): explore Go coverage reports from the CLI -* [got](https://github.com/fedeztk/got): a simple translator and text-to-speech app build on top of simplytranslate's APIs -* [hiSHtory](https://github.com/ddworken/hishtory): your shell history in context, synced, and queryable +* [got](https://github.com/fedeztk/got): a simple translator and text-to-speech app built on simplytranslate's APIs +* [gum](https://github.com/charmbracelet/gum): interactivity and styling for shells and shell scripts +* [hiSHtory](https://github.com/ddworken/hishtory): your shell history in context: synced, and queryable * [httpit](https://github.com/gonetx/httpit): a rapid http(s) benchmark tool +* [Huh?](https://github.com/charmbracelet/huh): an interactive prompt and form toolkit * [IDNT](https://github.com/r-darwish/idnt): a batch software uninstaller +* [json-log-viewer](https://github.com/hedhyw/json-log-viewer): an interactive JSON log viewer * [kboard](https://github.com/CamiloGarciaLaRotta/kboard): a typing game -* [mandelbrot-cli](https://github.com/MicheleFiladelfia/mandelbrot-cli): a multiplatform terminal mandelbrot set explorer +* [kplay](https://github.com/dhth/kplay): inspect messages in a Kafka topic +* [laboon](https://github.com/arisnacg/laboon): a Docker-desktop-style container manager * [mc](https://github.com/minio/mc): the official [MinIO](https://min.io) client * [mergestat](https://github.com/mergestat/mergestat): run SQL queries on git repositories +* [meteor](https://github.com/stefanlogue/meteor): a highly customizable conventional commit message tool +* [mods](https://github.com/charmbracelet/mods): AI on the CLI, built for pipelines +* [nachrichten](https://github.com/zMoooooritz/nachrichten): access up-to-date news in German provided by the [Tagesschau](https://www.tagesschau.de/) * [Neon Modem Overdrive](https://github.com/mrusme/neonmodem): a BBS-style TUI client for Discourse, Lemmy, Lobste.rs and Hacker News +* [nom](https://github.com/guyfedwards/nom): an RSS reader and manager * [Noted](https://github.com/torbratsberg/noted): a note viewer and manager -* [nom](https://github.com/guyfedwards/nom): RSS reader and manager -* [pathos](https://github.com/chip/pathos): a PATH env variable editor +* [outtasync](https://github.com/dhth/outtasync): identify CloudFormation stacks that are out of sync with their template files +* [pathos](https://github.com/chip/pathos): a PATH environment variable editor +* [Plandex](https://github.com/plandex-ai/plandex): a terminal-based AI coding engine for complex tasks * [portal](https://github.com/ZinoKader/portal): secure transfers between computers -* [redis-viewer](https://github.com/SaltFishPr/redis-viewer): a Redis databases browser -* [scrabbler](https://github.com/wI2L/scrabbler): Automatic draw TUI for your duplicate Scrabble games +* [prs](https://github.com/dhth/prs): stay up to date with your PRs +* [puffin](https://github.com/siddhantac/puffin): a TUI for hledger to manage your finances +* [pug](https://github.com/leg100/pug): terraform task manager +* [punchout](https://github.com/dhth/punchout): takes the suck out of logging time on JIRA +* [redis-viewer](https://github.com/SaltFishPr/redis-viewer): a Redis database browser +* [redis_tui](https://github.com/mat2cc/redis_tui): a Redis database browser +* [schemas](https://github.com/dhth/schemas): lets you inspect postgres schemas in the terminal +* [scrabbler](https://github.com/wI2L/scrabbler): an automatic draw tool for your duplicate Scrabble games * [sku](https://github.com/fedeztk/sku): Sudoku on the CLI * [Slides](https://github.com/maaslalani/slides): a markdown-based presentation tool -* [SlurmCommander](https://github.com/CLIP-HPC/SlurmCommander): a Slurm workload manager TUI +* [SlurmCommander](https://github.com/CLIP-HPC/SlurmCommander): a Slurm workload manager * [Soft Serve](https://github.com/charmbracelet/soft-serve): a command-line-first Git server that runs a TUI over SSH * [solitaire-tui](https://github.com/brianstrauch/solitaire-tui): Klondike Solitaire for the terminal * [StormForge Optimize Controller](https://github.com/thestormforge/optimize-controller): a tool for experimenting with application configurations in Kubernetes -* [Storydb](https://github.com/grrlopes/storydb): a bash/zsh ctrl+r improved command history finder. +* [Storydb](https://github.com/grrlopes/storydb): an improved bash/zsh-style ctrl+r command history finder * [STTG](https://github.com/wille1101/sttg): a teletext client for SVT, Sweden’s national public television station * [sttr](https://github.com/abhimanyu003/sttr): a general-purpose text transformer +* [superfile](https://github.com/MHNightCat/superfile) a fancy, modern terminal-based file manager * [tasktimer](https://github.com/caarlos0/tasktimer): a dead-simple task timer * [termdbms](https://github.com/mathaou/termdbms): a keyboard and mouse driven database browser +* [tgpt](https://github.com/aandrew-me/tgpt): conversational AI for the CLI; no API keys necessary * [ticker](https://github.com/achannarasappa/ticker): a terminal stock viewer and stock position tracker +* [trainer](https://github.com/rusinikita/trainer): a Go concurrency coding interview simulator with learning materials * [tran](https://github.com/abdfnx/tran): securely transfer stuff between computers (based on [portal](https://github.com/ZinoKader/portal)) +* [Trufflehog](https://github.com/trufflesecurity/trufflehog): find leaked credentials * [Typer](https://github.com/maaslalani/typer): a typing test -* [typioca](https://github.com/bloznelis/typioca): Cozy typing speed tester in terminal -* [tz](https://github.com/oz/tz): an aid for scheduling across multiple time zones +* [typioca](https://github.com/bloznelis/typioca): a typing test +* [tz](https://github.com/oz/tz): a scheduling aid for people in multiple time zones * [ugm](https://github.com/ariasmn/ugm): a unix user and group browser * [walk](https://github.com/antonmedv/walk): a terminal navigator * [wander](https://github.com/robinovitch61/wander): a HashiCorp Nomad terminal client diff --git a/vendor/github.com/charmbracelet/bubbletea/commands.go b/vendor/github.com/charmbracelet/bubbletea/commands.go index 7b139b88..2541bea0 100644 --- a/vendor/github.com/charmbracelet/bubbletea/commands.go +++ b/vendor/github.com/charmbracelet/bubbletea/commands.go @@ -20,11 +20,15 @@ func Batch(cmds ...Cmd) Cmd { } validCmds = append(validCmds, c) } - if len(validCmds) == 0 { + switch len(validCmds) { + case 0: return nil - } - return func() Msg { - return BatchMsg(validCmds) + case 1: + return validCmds[0] + default: + return func() Msg { + return BatchMsg(validCmds) + } } } @@ -90,11 +94,16 @@ type sequenceMsg []Cmd // // Every is analogous to Tick in the Elm Architecture. func Every(duration time.Duration, fn func(time.Time) Msg) Cmd { + n := time.Now() + d := n.Truncate(duration).Add(duration).Sub(n) + t := time.NewTimer(d) return func() Msg { - n := time.Now() - d := n.Truncate(duration).Add(duration).Sub(n) - t := time.NewTimer(d) - return fn(<-t.C) + ts := <-t.C + t.Stop() + for len(t.C) > 0 { + <-t.C + } + return fn(ts) } } @@ -137,9 +146,14 @@ func Every(duration time.Duration, fn func(time.Time) Msg) Cmd { // return m, nil // } func Tick(d time.Duration, fn func(time.Time) Msg) Cmd { + t := time.NewTimer(d) return func() Msg { - t := time.NewTimer(d) - return fn(<-t.C) + ts := <-t.C + t.Stop() + for len(t.C) > 0 { + <-t.C + } + return fn(ts) } } diff --git a/vendor/github.com/charmbracelet/bubbletea/exec.go b/vendor/github.com/charmbracelet/bubbletea/exec.go index fb6d91ed..7a14d2a7 100644 --- a/vendor/github.com/charmbracelet/bubbletea/exec.go +++ b/vendor/github.com/charmbracelet/bubbletea/exec.go @@ -109,7 +109,7 @@ func (p *Program) exec(c ExecCommand, fn ExecCallback) { } c.SetStdin(p.input) - c.SetStdout(p.output.TTY()) + c.SetStdout(p.output) c.SetStderr(os.Stderr) // Execute system command. diff --git a/vendor/github.com/charmbracelet/bubbletea/inputreader_other.go b/vendor/github.com/charmbracelet/bubbletea/inputreader_other.go new file mode 100644 index 00000000..8e63a87d --- /dev/null +++ b/vendor/github.com/charmbracelet/bubbletea/inputreader_other.go @@ -0,0 +1,14 @@ +//go:build !windows +// +build !windows + +package tea + +import ( + "io" + + "github.com/muesli/cancelreader" +) + +func newInputReader(r io.Reader) (cancelreader.CancelReader, error) { + return cancelreader.NewReader(r) +} diff --git a/vendor/github.com/charmbracelet/bubbletea/inputreader_windows.go b/vendor/github.com/charmbracelet/bubbletea/inputreader_windows.go new file mode 100644 index 00000000..449df479 --- /dev/null +++ b/vendor/github.com/charmbracelet/bubbletea/inputreader_windows.go @@ -0,0 +1,107 @@ +//go:build windows +// +build windows + +package tea + +import ( + "fmt" + "io" + "os" + "sync" + + "github.com/charmbracelet/x/term" + "github.com/erikgeiser/coninput" + "github.com/muesli/cancelreader" + "golang.org/x/sys/windows" +) + +type conInputReader struct { + cancelMixin + + conin windows.Handle + + originalMode uint32 +} + +var _ cancelreader.CancelReader = &conInputReader{} + +func newInputReader(r io.Reader) (cancelreader.CancelReader, error) { + fallback := func(io.Reader) (cancelreader.CancelReader, error) { + return cancelreader.NewReader(r) + } + if f, ok := r.(term.File); !ok || f.Fd() != os.Stdin.Fd() { + return fallback(r) + } + + conin, err := coninput.NewStdinHandle() + if err != nil { + return fallback(r) + } + + originalMode, err := prepareConsole(conin, + windows.ENABLE_MOUSE_INPUT, + windows.ENABLE_WINDOW_INPUT, + windows.ENABLE_EXTENDED_FLAGS, + ) + if err != nil { + return nil, fmt.Errorf("failed to prepare console input: %w", err) + } + + return &conInputReader{ + conin: conin, + originalMode: originalMode, + }, nil +} + +// Cancel implements cancelreader.CancelReader. +func (r *conInputReader) Cancel() bool { + r.setCanceled() + + return windows.CancelIo(r.conin) == nil +} + +// Close implements cancelreader.CancelReader. +func (r *conInputReader) Close() error { + if r.originalMode != 0 { + err := windows.SetConsoleMode(r.conin, r.originalMode) + if err != nil { + return fmt.Errorf("reset console mode: %w", err) + } + } + + return nil +} + +// Read implements cancelreader.CancelReader. +func (*conInputReader) Read(_ []byte) (n int, err error) { + return 0, nil +} + +func prepareConsole(input windows.Handle, modes ...uint32) (originalMode uint32, err error) { + err = windows.GetConsoleMode(input, &originalMode) + if err != nil { + return 0, fmt.Errorf("get console mode: %w", err) + } + + newMode := coninput.AddInputModes(0, modes...) + + err = windows.SetConsoleMode(input, newMode) + if err != nil { + return 0, fmt.Errorf("set console mode: %w", err) + } + + return originalMode, nil +} + +// cancelMixin represents a goroutine-safe cancelation status. +type cancelMixin struct { + unsafeCanceled bool + lock sync.Mutex +} + +func (c *cancelMixin) setCanceled() { + c.lock.Lock() + defer c.lock.Unlock() + + c.unsafeCanceled = true +} diff --git a/vendor/github.com/charmbracelet/bubbletea/key.go b/vendor/github.com/charmbracelet/bubbletea/key.go index f851490a..89a588ae 100644 --- a/vendor/github.com/charmbracelet/bubbletea/key.go +++ b/vendor/github.com/charmbracelet/bubbletea/key.go @@ -5,6 +5,7 @@ import ( "fmt" "io" "regexp" + "strings" "unicode/utf8" ) @@ -54,6 +55,7 @@ type Key struct { Type KeyType Runes []rune Alt bool + Paste bool } // String returns a friendly string representation for a key. It's safe (and @@ -63,15 +65,28 @@ type Key struct { // fmt.Println(k) // // Output: enter func (k Key) String() (str string) { + var buf strings.Builder if k.Alt { - str += "alt+" + buf.WriteString("alt+") } if k.Type == KeyRunes { - str += string(k.Runes) - return str + if k.Paste { + // Note: bubbles/keys bindings currently do string compares to + // recognize shortcuts. Since pasted text should never activate + // shortcuts, we need to ensure that the binding code doesn't + // match Key events that result from pastes. We achieve this + // here by enclosing pastes in '[...]' so that the string + // comparison in Matches() fails in that case. + buf.WriteByte('[') + } + buf.WriteString(string(k.Runes)) + if k.Paste { + buf.WriteByte(']') + } + return buf.String() } else if s, ok := keyNames[k.Type]; ok { - str += s - return str + buf.WriteString(s) + return buf.String() } return "" } @@ -538,9 +553,9 @@ func (u unknownCSISequenceMsg) String() string { var spaceRunes = []rune{' '} -// readInputs reads keypress and mouse inputs from a TTY and produces messages +// readAnsiInputs reads keypress and mouse inputs from a TTY and produces messages // containing information about the key or mouse events accordingly. -func readInputs(ctx context.Context, msgs chan<- Msg, input io.Reader) error { +func readAnsiInputs(ctx context.Context, msgs chan<- Msg, input io.Reader) error { var buf [256]byte var leftOverFromPrevIteration []byte @@ -566,7 +581,7 @@ loop: canHaveMoreData := numBytes == len(buf) var i, w int - for i, w = 0, 07; i < len(b); i += w { + for i, w = 0, 0; i < len(b); i += w { var msg Msg w, msg = detectOneMsg(b[i:], canHaveMoreData) if w == 0 { @@ -607,19 +622,26 @@ func detectOneMsg(b []byte, canHaveMoreData bool) (w int, msg Msg) { case '<': if matchIndices := mouseSGRRegex.FindSubmatchIndex(b[3:]); matchIndices != nil { // SGR mouse events length is the length of the match plus the length of the escape sequence - mouseEventSGRLen := matchIndices[1] + 3 + mouseEventSGRLen := matchIndices[1] + 3 //nolint:gomnd return mouseEventSGRLen, MouseMsg(parseSGRMouseEvent(b)) } } } + // Detect bracketed paste. + var foundbp bool + foundbp, w, msg = detectBracketedPaste(b) + if foundbp { + return w, msg + } + // Detect escape sequence and control characters other than NUL, // possibly with an escape character in front to mark the Alt // modifier. var foundSeq bool foundSeq, w, msg = detectSequence(b) if foundSeq { - return + return w, msg } // No non-NUL control character or escape sequence. diff --git a/vendor/github.com/charmbracelet/bubbletea/key_other.go b/vendor/github.com/charmbracelet/bubbletea/key_other.go new file mode 100644 index 00000000..b8c46082 --- /dev/null +++ b/vendor/github.com/charmbracelet/bubbletea/key_other.go @@ -0,0 +1,13 @@ +//go:build !windows +// +build !windows + +package tea + +import ( + "context" + "io" +) + +func readInputs(ctx context.Context, msgs chan<- Msg, input io.Reader) error { + return readAnsiInputs(ctx, msgs, input) +} diff --git a/vendor/github.com/charmbracelet/bubbletea/key_sequences.go b/vendor/github.com/charmbracelet/bubbletea/key_sequences.go index cc200f8d..4ba0f79e 100644 --- a/vendor/github.com/charmbracelet/bubbletea/key_sequences.go +++ b/vendor/github.com/charmbracelet/bubbletea/key_sequences.go @@ -1,6 +1,10 @@ package tea -import "sort" +import ( + "bytes" + "sort" + "unicode/utf8" +) // extSequences is used by the map-based algorithm below. It contains // the sequences plus their alternatives with an escape character @@ -69,3 +73,47 @@ func detectSequence(input []byte) (hasSeq bool, width int, msg Msg) { return false, 0, nil } + +// detectBracketedPaste detects an input pasted while bracketed +// paste mode was enabled. +// +// Note: this function is a no-op if bracketed paste was not enabled +// on the terminal, since in that case we'd never see this +// particular escape sequence. +func detectBracketedPaste(input []byte) (hasBp bool, width int, msg Msg) { + // Detect the start sequence. + const bpStart = "\x1b[200~" + if len(input) < len(bpStart) || string(input[:len(bpStart)]) != bpStart { + return false, 0, nil + } + + // Skip over the start sequence. + input = input[len(bpStart):] + + // If we saw the start sequence, then we must have an end sequence + // as well. Find it. + const bpEnd = "\x1b[201~" + idx := bytes.Index(input, []byte(bpEnd)) + inputLen := len(bpStart) + idx + len(bpEnd) + if idx == -1 { + // We have encountered the end of the input buffer without seeing + // the marker for the end of the bracketed paste. + // Tell the outer loop we have done a short read and we want more. + return true, 0, nil + } + + // The paste is everything in-between. + paste := input[:idx] + + // All there is in-between is runes, not to be interpreted further. + k := Key{Type: KeyRunes, Paste: true} + for len(paste) > 0 { + r, w := utf8.DecodeRune(paste) + if r != utf8.RuneError { + k.Runes = append(k.Runes, r) + } + paste = paste[w:] + } + + return true, inputLen, KeyMsg(k) +} diff --git a/vendor/github.com/charmbracelet/bubbletea/key_windows.go b/vendor/github.com/charmbracelet/bubbletea/key_windows.go new file mode 100644 index 00000000..b693efd6 --- /dev/null +++ b/vendor/github.com/charmbracelet/bubbletea/key_windows.go @@ -0,0 +1,351 @@ +//go:build windows +// +build windows + +package tea + +import ( + "context" + "fmt" + "io" + + "github.com/erikgeiser/coninput" + localereader "github.com/mattn/go-localereader" + "golang.org/x/sys/windows" +) + +func readInputs(ctx context.Context, msgs chan<- Msg, input io.Reader) error { + if coninReader, ok := input.(*conInputReader); ok { + return readConInputs(ctx, msgs, coninReader.conin) + } + + return readAnsiInputs(ctx, msgs, localereader.NewReader(input)) +} + +func readConInputs(ctx context.Context, msgsch chan<- Msg, con windows.Handle) error { + var ps coninput.ButtonState // keep track of previous mouse state + var ws coninput.WindowBufferSizeEventRecord // keep track of the last window size event + for { + events, err := coninput.ReadNConsoleInputs(con, 16) + if err != nil { + return fmt.Errorf("read coninput events: %w", err) + } + + for _, event := range events { + var msgs []Msg + switch e := event.Unwrap().(type) { + case coninput.KeyEventRecord: + if !e.KeyDown || e.VirtualKeyCode == coninput.VK_SHIFT { + continue + } + + for i := 0; i < int(e.RepeatCount); i++ { + eventKeyType := keyType(e) + var runes []rune + + // Add the character only if the key type is an actual character and not a control sequence. + // This mimics the behavior in readAnsiInputs where the character is also removed. + // We don't need to handle KeySpace here. See the comment in keyType(). + if eventKeyType == KeyRunes { + runes = []rune{e.Char} + } + + msgs = append(msgs, KeyMsg{ + Type: eventKeyType, + Runes: runes, + Alt: e.ControlKeyState.Contains(coninput.LEFT_ALT_PRESSED | coninput.RIGHT_ALT_PRESSED), + }) + } + case coninput.WindowBufferSizeEventRecord: + if e != ws { + ws = e + msgs = append(msgs, WindowSizeMsg{ + Width: int(e.Size.X), + Height: int(e.Size.Y), + }) + } + case coninput.MouseEventRecord: + event := mouseEvent(ps, e) + if event.Type != MouseUnknown { + msgs = append(msgs, event) + } + ps = e.ButtonState + case coninput.FocusEventRecord, coninput.MenuEventRecord: + // ignore + default: // unknown event + continue + } + + // Send all messages to the channel + for _, msg := range msgs { + select { + case msgsch <- msg: + case <-ctx.Done(): + err := ctx.Err() + if err != nil { + return fmt.Errorf("coninput context error: %w", err) + } + return err + } + } + } + } +} + +func mouseEventButton(p, s coninput.ButtonState) (button MouseButton, action MouseAction) { + btn := p ^ s + action = MouseActionPress + if btn&s == 0 { + action = MouseActionRelease + } + + if btn == 0 { + switch { + case s&coninput.FROM_LEFT_1ST_BUTTON_PRESSED > 0: + button = MouseButtonLeft + case s&coninput.FROM_LEFT_2ND_BUTTON_PRESSED > 0: + button = MouseButtonMiddle + case s&coninput.RIGHTMOST_BUTTON_PRESSED > 0: + button = MouseButtonRight + case s&coninput.FROM_LEFT_3RD_BUTTON_PRESSED > 0: + button = MouseButtonBackward + case s&coninput.FROM_LEFT_4TH_BUTTON_PRESSED > 0: + button = MouseButtonForward + } + return + } + + switch { + case btn == coninput.FROM_LEFT_1ST_BUTTON_PRESSED: // left button + button = MouseButtonLeft + case btn == coninput.RIGHTMOST_BUTTON_PRESSED: // right button + button = MouseButtonRight + case btn == coninput.FROM_LEFT_2ND_BUTTON_PRESSED: // middle button + button = MouseButtonMiddle + case btn == coninput.FROM_LEFT_3RD_BUTTON_PRESSED: // unknown (possibly mouse backward) + button = MouseButtonBackward + case btn == coninput.FROM_LEFT_4TH_BUTTON_PRESSED: // unknown (possibly mouse forward) + button = MouseButtonForward + } + + return button, action +} + +func mouseEvent(p coninput.ButtonState, e coninput.MouseEventRecord) MouseMsg { + ev := MouseMsg{ + X: int(e.MousePositon.X), + Y: int(e.MousePositon.Y), + Alt: e.ControlKeyState.Contains(coninput.LEFT_ALT_PRESSED | coninput.RIGHT_ALT_PRESSED), + Ctrl: e.ControlKeyState.Contains(coninput.LEFT_CTRL_PRESSED | coninput.RIGHT_CTRL_PRESSED), + Shift: e.ControlKeyState.Contains(coninput.SHIFT_PRESSED), + } + switch e.EventFlags { + case coninput.CLICK, coninput.DOUBLE_CLICK: + ev.Button, ev.Action = mouseEventButton(p, e.ButtonState) + if ev.Action == MouseActionRelease { + ev.Type = MouseRelease + } + switch ev.Button { + case MouseButtonLeft: + ev.Type = MouseLeft + case MouseButtonMiddle: + ev.Type = MouseMiddle + case MouseButtonRight: + ev.Type = MouseRight + case MouseButtonBackward: + ev.Type = MouseBackward + case MouseButtonForward: + ev.Type = MouseForward + } + case coninput.MOUSE_WHEELED: + if e.WheelDirection > 0 { + ev.Button = MouseButtonWheelUp + ev.Type = MouseWheelUp + } else { + ev.Button = MouseButtonWheelDown + ev.Type = MouseWheelDown + } + case coninput.MOUSE_HWHEELED: + if e.WheelDirection > 0 { + ev.Button = MouseButtonWheelRight + ev.Type = MouseWheelRight + } else { + ev.Button = MouseButtonWheelLeft + ev.Type = MouseWheelLeft + } + case coninput.MOUSE_MOVED: + ev.Button, _ = mouseEventButton(p, e.ButtonState) + ev.Action = MouseActionMotion + ev.Type = MouseMotion + } + + return ev +} + +func keyType(e coninput.KeyEventRecord) KeyType { + code := e.VirtualKeyCode + + shiftPressed := e.ControlKeyState.Contains(coninput.SHIFT_PRESSED) + ctrlPressed := e.ControlKeyState.Contains(coninput.LEFT_CTRL_PRESSED | coninput.RIGHT_CTRL_PRESSED) + + switch code { + case coninput.VK_RETURN: + return KeyEnter + case coninput.VK_BACK: + return KeyBackspace + case coninput.VK_TAB: + if shiftPressed { + return KeyShiftTab + } + return KeyTab + case coninput.VK_SPACE: + return KeyRunes // this could be KeySpace but on unix space also produces KeyRunes + case coninput.VK_ESCAPE: + return KeyEscape + case coninput.VK_UP: + switch { + case shiftPressed && ctrlPressed: + return KeyCtrlShiftUp + case shiftPressed: + return KeyShiftUp + case ctrlPressed: + return KeyCtrlUp + default: + return KeyUp + } + case coninput.VK_DOWN: + switch { + case shiftPressed && ctrlPressed: + return KeyCtrlShiftDown + case shiftPressed: + return KeyShiftDown + case ctrlPressed: + return KeyCtrlDown + default: + return KeyDown + } + case coninput.VK_RIGHT: + switch { + case shiftPressed && ctrlPressed: + return KeyCtrlShiftRight + case shiftPressed: + return KeyShiftRight + case ctrlPressed: + return KeyCtrlRight + default: + return KeyRight + } + case coninput.VK_LEFT: + switch { + case shiftPressed && ctrlPressed: + return KeyCtrlShiftLeft + case shiftPressed: + return KeyShiftLeft + case ctrlPressed: + return KeyCtrlLeft + default: + return KeyLeft + } + case coninput.VK_HOME: + switch { + case shiftPressed && ctrlPressed: + return KeyCtrlShiftHome + case shiftPressed: + return KeyShiftHome + case ctrlPressed: + return KeyCtrlHome + default: + return KeyHome + } + case coninput.VK_END: + switch { + case shiftPressed && ctrlPressed: + return KeyCtrlShiftEnd + case shiftPressed: + return KeyShiftEnd + case ctrlPressed: + return KeyCtrlEnd + default: + return KeyEnd + } + case coninput.VK_PRIOR: + return KeyPgUp + case coninput.VK_NEXT: + return KeyPgDown + case coninput.VK_DELETE: + return KeyDelete + default: + if e.ControlKeyState&(coninput.LEFT_CTRL_PRESSED|coninput.RIGHT_CTRL_PRESSED) == 0 { + return KeyRunes + } + + switch e.Char { + case '@': + return KeyCtrlAt + case '\x01': + return KeyCtrlA + case '\x02': + return KeyCtrlB + case '\x03': + return KeyCtrlC + case '\x04': + return KeyCtrlD + case '\x05': + return KeyCtrlE + case '\x06': + return KeyCtrlF + case '\a': + return KeyCtrlG + case '\b': + return KeyCtrlH + case '\t': + return KeyCtrlI + case '\n': + return KeyCtrlJ + case '\v': + return KeyCtrlK + case '\f': + return KeyCtrlL + case '\r': + return KeyCtrlM + case '\x0e': + return KeyCtrlN + case '\x0f': + return KeyCtrlO + case '\x10': + return KeyCtrlP + case '\x11': + return KeyCtrlQ + case '\x12': + return KeyCtrlR + case '\x13': + return KeyCtrlS + case '\x14': + return KeyCtrlT + case '\x15': + return KeyCtrlU + case '\x16': + return KeyCtrlV + case '\x17': + return KeyCtrlW + case '\x18': + return KeyCtrlX + case '\x19': + return KeyCtrlY + case '\x1a': + return KeyCtrlZ + case '\x1b': + return KeyCtrlCloseBracket + case '\x1c': + return KeyCtrlBackslash + case '\x1f': + return KeyCtrlUnderscore + } + + switch code { + case coninput.VK_OEM_4: + return KeyCtrlOpenBracket + } + + return KeyRunes + } +} diff --git a/vendor/github.com/charmbracelet/bubbletea/mouse.go b/vendor/github.com/charmbracelet/bubbletea/mouse.go index add8d029..6ec51cc0 100644 --- a/vendor/github.com/charmbracelet/bubbletea/mouse.go +++ b/vendor/github.com/charmbracelet/bubbletea/mouse.go @@ -45,7 +45,7 @@ func (m MouseEvent) String() (s string) { s += "shift+" } - if m.Button == MouseButtonNone { + if m.Button == MouseButtonNone { //nolint:nestif if m.Action == MouseActionMotion || m.Action == MouseActionRelease { s += mouseActions[m.Action] } else { @@ -172,7 +172,7 @@ const ( func parseSGRMouseEvent(buf []byte) MouseEvent { str := string(buf[3:]) matches := mouseSGRRegex.FindStringSubmatch(str) - if len(matches) != 5 { + if len(matches) != 5 { //nolint:gomnd // Unreachable, we already checked the regex in `detectOneMsg`. panic("invalid mouse event") } @@ -288,7 +288,7 @@ func parseMouseButton(b int, isSGR bool) MouseEvent { m.Type = MouseForward case m.Action == MouseActionMotion: m.Type = MouseMotion - switch m.Button { + switch m.Button { //nolint:exhaustive case MouseButtonLeft: m.Type = MouseLeft case MouseButtonMiddle: diff --git a/vendor/github.com/charmbracelet/bubbletea/nil_renderer.go b/vendor/github.com/charmbracelet/bubbletea/nil_renderer.go index 1b1d4409..f4a83b6b 100644 --- a/vendor/github.com/charmbracelet/bubbletea/nil_renderer.go +++ b/vendor/github.com/charmbracelet/bubbletea/nil_renderer.go @@ -2,20 +2,24 @@ package tea type nilRenderer struct{} -func (n nilRenderer) start() {} -func (n nilRenderer) stop() {} -func (n nilRenderer) kill() {} -func (n nilRenderer) write(_ string) {} -func (n nilRenderer) repaint() {} -func (n nilRenderer) clearScreen() {} -func (n nilRenderer) altScreen() bool { return false } -func (n nilRenderer) enterAltScreen() {} -func (n nilRenderer) exitAltScreen() {} -func (n nilRenderer) showCursor() {} -func (n nilRenderer) hideCursor() {} -func (n nilRenderer) enableMouseCellMotion() {} -func (n nilRenderer) disableMouseCellMotion() {} -func (n nilRenderer) enableMouseAllMotion() {} -func (n nilRenderer) disableMouseAllMotion() {} -func (n nilRenderer) enableMouseSGRMode() {} -func (n nilRenderer) disableMouseSGRMode() {} +func (n nilRenderer) start() {} +func (n nilRenderer) stop() {} +func (n nilRenderer) kill() {} +func (n nilRenderer) write(_ string) {} +func (n nilRenderer) repaint() {} +func (n nilRenderer) clearScreen() {} +func (n nilRenderer) altScreen() bool { return false } +func (n nilRenderer) enterAltScreen() {} +func (n nilRenderer) exitAltScreen() {} +func (n nilRenderer) showCursor() {} +func (n nilRenderer) hideCursor() {} +func (n nilRenderer) enableMouseCellMotion() {} +func (n nilRenderer) disableMouseCellMotion() {} +func (n nilRenderer) enableMouseAllMotion() {} +func (n nilRenderer) disableMouseAllMotion() {} +func (n nilRenderer) enableBracketedPaste() {} +func (n nilRenderer) disableBracketedPaste() {} +func (n nilRenderer) enableMouseSGRMode() {} +func (n nilRenderer) disableMouseSGRMode() {} +func (n nilRenderer) bracketedPasteActive() bool { return false } +func (n nilRenderer) setWindowTitle(_ string) {} diff --git a/vendor/github.com/charmbracelet/bubbletea/options.go b/vendor/github.com/charmbracelet/bubbletea/options.go index 71e94493..8aee6da6 100644 --- a/vendor/github.com/charmbracelet/bubbletea/options.go +++ b/vendor/github.com/charmbracelet/bubbletea/options.go @@ -4,8 +4,6 @@ import ( "context" "io" "sync/atomic" - - "github.com/muesli/termenv" ) // ProgramOption is used to set options when initializing a Program. Program can @@ -29,11 +27,7 @@ func WithContext(ctx context.Context) ProgramOption { // won't need to use this. func WithOutput(output io.Writer) ProgramOption { return func(p *Program) { - if o, ok := output.(*termenv.Output); ok { - p.output = o - } else { - p.output = termenv.NewOutput(output, termenv.WithColorCache(true)) - } + p.output = output } } @@ -101,6 +95,13 @@ func WithAltScreen() ProgramOption { } } +// WithoutBracketedPaste starts the program with bracketed paste disabled. +func WithoutBracketedPaste() ProgramOption { + return func(p *Program) { + p.startupOptions |= withoutBracketedPaste + } +} + // WithMouseCellMotion starts the program with the mouse enabled in "cell // motion" mode. // diff --git a/vendor/github.com/charmbracelet/bubbletea/renderer.go b/vendor/github.com/charmbracelet/bubbletea/renderer.go index 5a3ee3c4..de3936e7 100644 --- a/vendor/github.com/charmbracelet/bubbletea/renderer.go +++ b/vendor/github.com/charmbracelet/bubbletea/renderer.go @@ -56,6 +56,20 @@ type renderer interface { // disableMouseSGRMode disables mouse extended mode (SGR). disableMouseSGRMode() + + // enableBracketedPaste enables bracketed paste, where characters + // inside the input are not interpreted when pasted as a whole. + enableBracketedPaste() + + // disableBracketedPaste disables bracketed paste. + disableBracketedPaste() + + // bracketedPasteActive reports whether bracketed paste mode is + // currently enabled. + bracketedPasteActive() bool + + // setWindowTitle sets the terminal window title. + setWindowTitle(string) } // repaintMsg forces a full repaint. diff --git a/vendor/github.com/charmbracelet/bubbletea/screen.go b/vendor/github.com/charmbracelet/bubbletea/screen.go index d064222f..6256c159 100644 --- a/vendor/github.com/charmbracelet/bubbletea/screen.go +++ b/vendor/github.com/charmbracelet/bubbletea/screen.go @@ -116,6 +116,34 @@ func ShowCursor() Msg { // this message with ShowCursor. type showCursorMsg struct{} +// EnableBracketedPaste is a special command that tells the Bubble Tea program +// to accept bracketed paste input. +// +// Note that bracketed paste will be automatically disabled when the +// program quits. +func EnableBracketedPaste() Msg { + return enableBracketedPasteMsg{} +} + +// enableBracketedPasteMsg in an internal message signals that +// bracketed paste should be enabled. You can send an +// enableBracketedPasteMsg with EnableBracketedPaste. +type enableBracketedPasteMsg struct{} + +// DisableBracketedPaste is a special command that tells the Bubble Tea program +// to accept bracketed paste input. +// +// Note that bracketed paste will be automatically disabled when the +// program quits. +func DisableBracketedPaste() Msg { + return disableBracketedPasteMsg{} +} + +// disableBracketedPasteMsg in an internal message signals that +// bracketed paste should be disabled. You can send an +// disableBracketedPasteMsg with DisableBracketedPaste. +type disableBracketedPasteMsg struct{} + // EnterAltScreen enters the alternate screen buffer, which consumes the entire // terminal window. ExitAltScreen will return the terminal to its former state. // @@ -123,6 +151,8 @@ type showCursorMsg struct{} func (p *Program) EnterAltScreen() { if p.renderer != nil { p.renderer.enterAltScreen() + } else { + p.startupOptions |= withAltScreen } } @@ -132,6 +162,8 @@ func (p *Program) EnterAltScreen() { func (p *Program) ExitAltScreen() { if p.renderer != nil { p.renderer.exitAltScreen() + } else { + p.startupOptions &^= withAltScreen } } @@ -140,7 +172,11 @@ func (p *Program) ExitAltScreen() { // // Deprecated: Use the WithMouseCellMotion ProgramOption instead. func (p *Program) EnableMouseCellMotion() { - p.renderer.enableMouseCellMotion() + if p.renderer != nil { + p.renderer.enableMouseCellMotion() + } else { + p.startupOptions |= withMouseCellMotion + } } // DisableMouseCellMotion disables Mouse Cell Motion tracking. This will be @@ -148,7 +184,11 @@ func (p *Program) EnableMouseCellMotion() { // // Deprecated: The mouse will automatically be disabled when the program exits. func (p *Program) DisableMouseCellMotion() { - p.renderer.disableMouseCellMotion() + if p.renderer != nil { + p.renderer.disableMouseCellMotion() + } else { + p.startupOptions &^= withMouseCellMotion + } } // EnableMouseAllMotion enables mouse click, release, wheel and motion events, @@ -157,7 +197,11 @@ func (p *Program) DisableMouseCellMotion() { // // Deprecated: Use the WithMouseAllMotion ProgramOption instead. func (p *Program) EnableMouseAllMotion() { - p.renderer.enableMouseAllMotion() + if p.renderer != nil { + p.renderer.enableMouseAllMotion() + } else { + p.startupOptions |= withMouseAllMotion + } } // DisableMouseAllMotion disables All Motion mouse tracking. This will be @@ -165,10 +209,20 @@ func (p *Program) EnableMouseAllMotion() { // // Deprecated: The mouse will automatically be disabled when the program exits. func (p *Program) DisableMouseAllMotion() { - p.renderer.disableMouseAllMotion() + if p.renderer != nil { + p.renderer.disableMouseAllMotion() + } else { + p.startupOptions &^= withMouseAllMotion + } } // SetWindowTitle sets the terminal window title. +// +// Deprecated: Use the SetWindowTitle command instead. func (p *Program) SetWindowTitle(title string) { - p.output.SetWindowTitle(title) + if p.renderer != nil { + p.renderer.setWindowTitle(title) + } else { + p.startupTitle = title + } } diff --git a/vendor/github.com/charmbracelet/bubbletea/signals_unix.go b/vendor/github.com/charmbracelet/bubbletea/signals_unix.go index 826f58b9..40954038 100644 --- a/vendor/github.com/charmbracelet/bubbletea/signals_unix.go +++ b/vendor/github.com/charmbracelet/bubbletea/signals_unix.go @@ -1,5 +1,5 @@ -//go:build darwin || dragonfly || freebsd || linux || netbsd || openbsd || solaris || aix -// +build darwin dragonfly freebsd linux netbsd openbsd solaris aix +//go:build darwin || dragonfly || freebsd || linux || netbsd || openbsd || solaris || aix || zos +// +build darwin dragonfly freebsd linux netbsd openbsd solaris aix zos package tea diff --git a/vendor/github.com/charmbracelet/bubbletea/standard_renderer.go b/vendor/github.com/charmbracelet/bubbletea/standard_renderer.go index 1573a1c2..1b7f4b9b 100644 --- a/vendor/github.com/charmbracelet/bubbletea/standard_renderer.go +++ b/vendor/github.com/charmbracelet/bubbletea/standard_renderer.go @@ -8,9 +8,8 @@ import ( "sync" "time" + "github.com/charmbracelet/x/ansi" "github.com/muesli/ansi/compressor" - "github.com/muesli/reflow/truncate" - "github.com/muesli/termenv" ) const ( @@ -27,7 +26,7 @@ const ( // to exclude ranges of lines, allowing them to be written to directly. type standardRenderer struct { mtx *sync.Mutex - out *termenv.Output + out io.Writer buf bytes.Buffer queuedMessageLines []string @@ -45,6 +44,9 @@ type standardRenderer struct { // essentially whether or not we're using the full size of the terminal altScreenActive bool + // whether or not we're currently using bracketed paste + bpActive bool + // renderer dimensions; usually the size of the window width int height int @@ -55,7 +57,7 @@ type standardRenderer struct { // newRenderer creates a new renderer. Normally you'll want to initialize it // with os.Stdout as the first argument. -func newRenderer(out *termenv.Output, useANSICompressor bool, fps int) renderer { +func newRenderer(out io.Writer, useANSICompressor bool, fps int) renderer { if fps < 1 { fps = defaultFPS } else if fps > maxFPS { @@ -70,7 +72,7 @@ func newRenderer(out *termenv.Output, useANSICompressor bool, fps int) renderer queuedMessageLines: []string{}, } if r.useANSICompressor { - r.out = termenv.NewOutput(&compressor.Writer{Forward: out}) + r.out = &compressor.Writer{Forward: out} } return r } @@ -105,15 +107,20 @@ func (r *standardRenderer) stop() { r.mtx.Lock() defer r.mtx.Unlock() - r.out.ClearLine() + r.execute(ansi.EraseEntireLine) if r.useANSICompressor { - if w, ok := r.out.TTY().(io.WriteCloser); ok { + if w, ok := r.out.(io.WriteCloser); ok { _ = w.Close() } } } +// execute writes a sequence to the terminal. +func (r *standardRenderer) execute(seq string) { + _, _ = io.WriteString(r.out, seq) +} + // kill halts the renderer. The final frame will not be rendered. func (r *standardRenderer) kill() { // Stop the renderer before acquiring the mutex to avoid a deadlock. @@ -124,7 +131,7 @@ func (r *standardRenderer) kill() { r.mtx.Lock() defer r.mtx.Unlock() - r.out.ClearLine() + r.execute(ansi.EraseEntireLine) } // listen waits for ticks on the ticker, or a signal to stop the renderer. @@ -153,7 +160,6 @@ func (r *standardRenderer) flush() { // Output buffer buf := &bytes.Buffer{} - out := termenv.NewOutput(buf) newLines := strings.Split(r.buf.String(), "\n") @@ -170,25 +176,24 @@ func (r *standardRenderer) flush() { skipLines := make(map[int]struct{}) flushQueuedMessages := len(r.queuedMessageLines) > 0 && !r.altScreenActive - // Add any queued messages to this render - if flushQueuedMessages { - newLines = append(r.queuedMessageLines, newLines...) - r.queuedMessageLines = []string{} - } - // Clear any lines we painted in the last render. if r.linesRendered > 0 { for i := r.linesRendered - 1; i > 0; i-- { - // If the number of lines we want to render hasn't increased and - // new line is the same as the old line we can skip rendering for - // this line as a performance optimization. - if (len(newLines) <= len(oldLines)) && (len(newLines) > i && len(oldLines) > i) && (newLines[i] == oldLines[i]) { + // if we are clearing queued messages, we want to clear all lines, since + // printing messages allows for native terminal word-wrap, we + // don't have control over the queued lines + if flushQueuedMessages { + buf.WriteString(ansi.EraseEntireLine) + } else if (len(newLines) <= len(oldLines)) && (len(newLines) > i && len(oldLines) > i) && (newLines[i] == oldLines[i]) { + // If the number of lines we want to render hasn't increased and + // new line is the same as the old line we can skip rendering for + // this line as a performance optimization. skipLines[i] = struct{}{} } else if _, exists := r.ignoreLines[i]; !exists { - out.ClearLine() + buf.WriteString(ansi.EraseEntireLine) } - out.CursorUp(1) + buf.WriteString(ansi.CursorUp1) } if _, exists := r.ignoreLines[0]; !exists { @@ -201,8 +206,8 @@ func (r *standardRenderer) flush() { // standard (whereas others are proprietary to, say, VT100/VT52). // If cursor previous line (ESC[ + + F) were better supported // we could use that above to eliminate this step. - out.CursorBack(r.width) - out.ClearLine() + buf.WriteString(ansi.CursorLeft(r.width)) + buf.WriteString(ansi.EraseEntireLine) } } @@ -212,14 +217,30 @@ func (r *standardRenderer) flush() { skipLines[k] = v } + if flushQueuedMessages { + // Dump the lines we've queued up for printing + for _, line := range r.queuedMessageLines { + _, _ = buf.WriteString(line) + _, _ = buf.WriteString("\r\n") + } + // clear the queued message lines + r.queuedMessageLines = []string{} + } + // Paint new lines for i := 0; i < len(newLines); i++ { if _, skip := skipLines[i]; skip { // Unless this is the last line, move the cursor down. if i < len(newLines)-1 { - out.CursorDown(1) + buf.WriteString(ansi.CursorDown1) } } else { + if i == 0 && r.lastRender == "" { + // On first render, reset the cursor to the start of the line + // before writing anything. + buf.WriteByte('\r') + } + line := newLines[i] // Truncate lines wider than the width of the window to avoid @@ -230,13 +251,13 @@ func (r *standardRenderer) flush() { // program initialization, so after a resize this won't perform // correctly (signal SIGWINCH is not supported on Windows). if r.width > 0 { - line = truncate.String(line, uint(r.width)) + line = ansi.Truncate(line, r.width, "") } - _, _ = out.WriteString(line) + _, _ = buf.WriteString(line) if i < len(newLines)-1 { - _, _ = out.WriteString("\r\n") + _, _ = buf.WriteString("\r\n") } } } @@ -248,9 +269,9 @@ func (r *standardRenderer) flush() { // This case fixes a bug in macOS terminal. In other terminals the // other case seems to do the job regardless of whether or not we're // using the full terminal window. - out.MoveCursor(r.linesRendered, 0) + buf.WriteString(ansi.MoveCursor(r.linesRendered, 0)) } else { - out.CursorBack(r.width) + buf.WriteString(ansi.CursorLeft(r.width)) } _, _ = r.out.Write(buf.Bytes()) @@ -284,8 +305,8 @@ func (r *standardRenderer) clearScreen() { r.mtx.Lock() defer r.mtx.Unlock() - r.out.ClearScreen() - r.out.MoveCursor(1, 1) + r.execute(ansi.EraseEntireDisplay) + r.execute(ansi.MoveCursorOrigin) r.repaint() } @@ -306,7 +327,7 @@ func (r *standardRenderer) enterAltScreen() { } r.altScreenActive = true - r.out.AltScreen() + r.execute(ansi.EnableAltScreenBuffer) // Ensure that the terminal is cleared, even when it doesn't support // alt screen (or alt screen support is disabled, like GNU screen by @@ -314,16 +335,16 @@ func (r *standardRenderer) enterAltScreen() { // // Note: we can't use r.clearScreen() here because the mutex is already // locked. - r.out.ClearScreen() - r.out.MoveCursor(1, 1) + r.execute(ansi.EraseEntireDisplay) + r.execute(ansi.MoveCursorOrigin) // cmd.exe and other terminals keep separate cursor states for the AltScreen // and the main buffer. We have to explicitly reset the cursor visibility // whenever we enter AltScreen. if r.cursorHidden { - r.out.HideCursor() + r.execute(ansi.HideCursor) } else { - r.out.ShowCursor() + r.execute(ansi.ShowCursor) } r.repaint() @@ -338,15 +359,15 @@ func (r *standardRenderer) exitAltScreen() { } r.altScreenActive = false - r.out.ExitAltScreen() + r.execute(ansi.DisableAltScreenBuffer) // cmd.exe and other terminals keep separate cursor states for the AltScreen // and the main buffer. We have to explicitly reset the cursor visibility // whenever we exit AltScreen. if r.cursorHidden { - r.out.HideCursor() + r.execute(ansi.HideCursor) } else { - r.out.ShowCursor() + r.execute(ansi.ShowCursor) } r.repaint() @@ -357,7 +378,7 @@ func (r *standardRenderer) showCursor() { defer r.mtx.Unlock() r.cursorHidden = false - r.out.ShowCursor() + r.execute(ansi.ShowCursor) } func (r *standardRenderer) hideCursor() { @@ -365,49 +386,77 @@ func (r *standardRenderer) hideCursor() { defer r.mtx.Unlock() r.cursorHidden = true - r.out.HideCursor() + r.execute(ansi.HideCursor) } func (r *standardRenderer) enableMouseCellMotion() { r.mtx.Lock() defer r.mtx.Unlock() - r.out.EnableMouseCellMotion() + r.execute(ansi.EnableMouseCellMotion) } func (r *standardRenderer) disableMouseCellMotion() { r.mtx.Lock() defer r.mtx.Unlock() - r.out.DisableMouseCellMotion() + r.execute(ansi.DisableMouseCellMotion) } func (r *standardRenderer) enableMouseAllMotion() { r.mtx.Lock() defer r.mtx.Unlock() - r.out.EnableMouseAllMotion() + r.execute(ansi.EnableMouseAllMotion) } func (r *standardRenderer) disableMouseAllMotion() { r.mtx.Lock() defer r.mtx.Unlock() - r.out.DisableMouseAllMotion() + r.execute(ansi.DisableMouseAllMotion) } func (r *standardRenderer) enableMouseSGRMode() { r.mtx.Lock() defer r.mtx.Unlock() - r.out.EnableMouseExtendedMode() + r.execute(ansi.EnableMouseSgrExt) } func (r *standardRenderer) disableMouseSGRMode() { r.mtx.Lock() defer r.mtx.Unlock() - r.out.DisableMouseExtendedMode() + r.execute(ansi.DisableMouseSgrExt) +} + +func (r *standardRenderer) enableBracketedPaste() { + r.mtx.Lock() + defer r.mtx.Unlock() + + r.execute(ansi.EnableBracketedPaste) + r.bpActive = true +} + +func (r *standardRenderer) disableBracketedPaste() { + r.mtx.Lock() + defer r.mtx.Unlock() + + r.execute(ansi.DisableBracketedPaste) + r.bpActive = false +} + +func (r *standardRenderer) bracketedPasteActive() bool { + r.mtx.Lock() + defer r.mtx.Unlock() + + return r.bpActive +} + +// setWindowTitle sets the terminal window title. +func (r *standardRenderer) setWindowTitle(title string) { + r.execute(ansi.SetWindowTitle(title)) } // setIgnoredLines specifies lines not to be touched by the standard Bubble Tea @@ -430,15 +479,14 @@ func (r *standardRenderer) setIgnoredLines(from int, to int) { // Erase ignored lines if r.linesRendered > 0 { buf := &bytes.Buffer{} - out := termenv.NewOutput(buf) for i := r.linesRendered - 1; i >= 0; i-- { if _, exists := r.ignoreLines[i]; exists { - out.ClearLine() + buf.WriteString(ansi.EraseEntireLine) } - out.CursorUp(1) + buf.WriteString(ansi.CursorUp1) } - out.MoveCursor(r.linesRendered, 0) // put cursor back + buf.WriteString(ansi.MoveCursor(r.linesRendered, 0)) // put cursor back _, _ = r.out.Write(buf.Bytes()) } } @@ -473,16 +521,15 @@ func (r *standardRenderer) insertTop(lines []string, topBoundary, bottomBoundary defer r.mtx.Unlock() buf := &bytes.Buffer{} - out := termenv.NewOutput(buf) - out.ChangeScrollingRegion(topBoundary, bottomBoundary) - out.MoveCursor(topBoundary, 0) - out.InsertLines(len(lines)) - _, _ = out.WriteString(strings.Join(lines, "\r\n")) - out.ChangeScrollingRegion(0, r.height) + buf.WriteString(ansi.SetScrollingRegion(topBoundary, bottomBoundary)) + buf.WriteString(ansi.MoveCursor(topBoundary, 0)) + buf.WriteString(ansi.InsertLine(len(lines))) + _, _ = buf.WriteString(strings.Join(lines, "\r\n")) + buf.WriteString(ansi.SetScrollingRegion(0, r.height)) // Move cursor back to where the main rendering routine expects it to be - out.MoveCursor(r.linesRendered, 0) + buf.WriteString(ansi.MoveCursor(r.linesRendered, 0)) _, _ = r.out.Write(buf.Bytes()) } @@ -501,15 +548,14 @@ func (r *standardRenderer) insertBottom(lines []string, topBoundary, bottomBound defer r.mtx.Unlock() buf := &bytes.Buffer{} - out := termenv.NewOutput(buf) - out.ChangeScrollingRegion(topBoundary, bottomBoundary) - out.MoveCursor(bottomBoundary, 0) - _, _ = out.WriteString("\r\n" + strings.Join(lines, "\r\n")) - out.ChangeScrollingRegion(0, r.height) + buf.WriteString(ansi.SetScrollingRegion(topBoundary, bottomBoundary)) + buf.WriteString(ansi.MoveCursor(bottomBoundary, 0)) + _, _ = buf.WriteString("\r\n" + strings.Join(lines, "\r\n")) + buf.WriteString(ansi.SetScrollingRegion(0, r.height)) // Move cursor back to where the main rendering routine expects it to be - out.MoveCursor(r.linesRendered, 0) + buf.WriteString(ansi.MoveCursor(r.linesRendered, 0)) _, _ = r.out.Write(buf.Bytes()) } diff --git a/vendor/github.com/charmbracelet/bubbletea/tea.go b/vendor/github.com/charmbracelet/bubbletea/tea.go index f18cb87c..7ded48b6 100644 --- a/vendor/github.com/charmbracelet/bubbletea/tea.go +++ b/vendor/github.com/charmbracelet/bubbletea/tea.go @@ -21,10 +21,8 @@ import ( "sync/atomic" "syscall" - "github.com/containerd/console" - isatty "github.com/mattn/go-isatty" + "github.com/charmbracelet/x/term" "github.com/muesli/cancelreader" - "github.com/muesli/termenv" "golang.org/x/sync/errgroup" ) @@ -81,7 +79,7 @@ func (i inputType) String() string { // generally set with ProgramOptions. // // The options here are treated as bits. -type startupOptions byte +type startupOptions int16 func (s startupOptions) has(option startupOptions) bool { return s&option != 0 @@ -93,26 +91,27 @@ const ( withMouseAllMotion withANSICompressor withoutSignalHandler - // Catching panics is incredibly useful for restoring the terminal to a // usable state after a panic occurs. When this is set, Bubble Tea will // recover from panics, print the stack trace, and disable raw mode. This // feature is on by default. withoutCatchPanics + withoutBracketedPaste ) -// handlers manages series of channels returned by various processes. It allows -// us to wait for those processes to terminate before exiting the program. -type handlers []chan struct{} +// channelHandlers manages the series of channels returned by various processes. +// It allows us to wait for those processes to terminate before exiting the +// program. +type channelHandlers []chan struct{} // Adds a channel to the list of handlers. We wait for all handlers to terminate // gracefully on shutdown. -func (h *handlers) add(ch chan struct{}) { +func (h *channelHandlers) add(ch chan struct{}) { *h = append(*h, ch) } // shutdown waits for all handlers to terminate. -func (h handlers) shutdown() { +func (h channelHandlers) shutdown() { var wg sync.WaitGroup for _, ch := range h { wg.Add(1) @@ -132,6 +131,10 @@ type Program struct { // treated as bits. These options can be set via various ProgramOptions. startupOptions startupOptions + // startupTitle is the title that will be set on the terminal when the + // program starts. + startupTitle string + inputType inputType ctx context.Context @@ -142,28 +145,25 @@ type Program struct { finished chan struct{} // where to send output, this will usually be os.Stdout. - output *termenv.Output - restoreOutput func() error - renderer renderer + output io.Writer + // ttyOutput is null if output is not a TTY. + ttyOutput term.File + previousOutputState *term.State + renderer renderer // where to read inputs from, this will usually be os.Stdin. - input io.Reader - cancelReader cancelreader.CancelReader - readLoopDone chan struct{} - console console.Console + input io.Reader + // ttyInput is null if input is not a TTY. + ttyInput term.File + previousTtyInputState *term.State + cancelReader cancelreader.CancelReader + readLoopDone chan struct{} // was the altscreen active before releasing the terminal? altScreenWasActive bool ignoreSignals uint32 - // Stores the original reference to stdin for cases where input is not a - // TTY on windows and we've automatically opened CONIN$ to receive input. - // When the program exits this will be restored. - // - // Lint ignore note: the linter will find false positive on unix systems - // as this value only comes into play on Windows, hence the ignore comment - // below. - windowsStdin *os.File //nolint:golint,structcheck,unused + bpWasActive bool // was the bracketed paste mode active before releasing the terminal? filter func(Model, Msg) Msg @@ -203,14 +203,9 @@ func NewProgram(model Model, opts ...ProgramOption) *Program { // if no output was set, set it to stdout if p.output == nil { - p.output = termenv.DefaultOutput() - - // cache detected color values - termenv.WithColorCache(true)(p.output) + p.output = os.Stdout } - p.restoreOutput, _ = termenv.EnableVirtualTerminalProcessing(p.output) - return p } @@ -254,7 +249,7 @@ func (p *Program) handleSignals() chan struct{} { func (p *Program) handleResize() chan struct{} { ch := make(chan struct{}) - if f, ok := p.output.TTY().(*os.File); ok && isatty.IsTerminal(f.Fd()) { + if p.ttyOutput != nil { // Get the initial terminal size and send it to the program. go p.checkResize() @@ -360,6 +355,12 @@ func (p *Program) eventLoop(model Model, cmds chan Cmd) (Model, error) { case hideCursorMsg: p.renderer.hideCursor() + case enableBracketedPasteMsg: + p.renderer.enableBracketedPaste() + + case disableBracketedPasteMsg: + p.renderer.disableBracketedPaste() + case execMsg: // NB: this blocks. p.exec(msg.cmd, msg.fn) @@ -419,7 +420,7 @@ func (p *Program) eventLoop(model Model, cmds chan Cmd) (Model, error) { // terminated by either [Program.Quit], [Program.Kill], or its signal handler. // Returns the final model. func (p *Program) Run() (Model, error) { - handlers := handlers{} + handlers := channelHandlers{} cmds := make(chan Cmd) p.errs = make(chan error) p.finished = make(chan struct{}, 1) @@ -436,11 +437,11 @@ func (p *Program) Run() (Model, error) { // piped in or redirected to the application. // // To disable input entirely pass nil to the [WithInput] program option. - f, isFile := p.input.(*os.File) + f, isFile := p.input.(term.File) if !isFile { break } - if isatty.IsTerminal(f.Fd()) { + if term.IsTerminal(f.Fd()) { break } @@ -493,9 +494,15 @@ func (p *Program) Run() (Model, error) { } // Honor program startup options. + if p.startupTitle != "" { + p.renderer.setWindowTitle(p.startupTitle) + } if p.startupOptions&withAltScreen != 0 { p.renderer.enterAltScreen() } + if p.startupOptions&withoutBracketedPaste == 0 { + p.renderer.enableBracketedPaste() + } if p.startupOptions&withMouseCellMotion != 0 { p.renderer.enableMouseCellMotion() p.renderer.enableMouseSGRMode() @@ -504,6 +511,9 @@ func (p *Program) Run() (Model, error) { p.renderer.enableMouseSGRMode() } + // Start the renderer. + p.renderer.start() + // Initialize the program. model := p.initialModel if initCmd := model.Init(); initCmd != nil { @@ -520,9 +530,6 @@ func (p *Program) Run() (Model, error) { }() } - // Start the renderer. - p.renderer.start() - // Render the initial view. p.renderer.write(model.View()) @@ -638,9 +645,6 @@ func (p *Program) shutdown(kill bool) { } _ = p.restoreTerminalState() - if p.restoreOutput != nil { - _ = p.restoreOutput() - } p.finished <- struct{}{} } @@ -648,14 +652,18 @@ func (p *Program) shutdown(kill bool) { // reader. You can return control to the Program with RestoreTerminal. func (p *Program) ReleaseTerminal() error { atomic.StoreUint32(&p.ignoreSignals, 1) - p.cancelReader.Cancel() + if p.cancelReader != nil { + p.cancelReader.Cancel() + } + p.waitForReadLoop() if p.renderer != nil { p.renderer.stop() + p.altScreenWasActive = p.renderer.altScreen() + p.bpWasActive = p.renderer.bracketedPasteActive() } - p.altScreenWasActive = p.renderer.altScreen() return p.restoreTerminalState() } @@ -671,7 +679,6 @@ func (p *Program) RestoreTerminal() error { if err := p.initCancelReader(); err != nil { return err } - if p.altScreenWasActive { p.renderer.enterAltScreen() } else { @@ -681,6 +688,9 @@ func (p *Program) RestoreTerminal() error { if p.renderer != nil { p.renderer.start() } + if p.bpWasActive { + p.renderer.enableBracketedPaste() + } // If the output is a terminal, it may have been resized while another // process was at the foreground, in which case we may not have received diff --git a/vendor/github.com/charmbracelet/bubbletea/tty.go b/vendor/github.com/charmbracelet/bubbletea/tty.go index 01f084d4..2fdd36b4 100644 --- a/vendor/github.com/charmbracelet/bubbletea/tty.go +++ b/vendor/github.com/charmbracelet/bubbletea/tty.go @@ -4,28 +4,17 @@ import ( "errors" "fmt" "io" - "os" "time" - isatty "github.com/mattn/go-isatty" - localereader "github.com/mattn/go-localereader" + "github.com/charmbracelet/x/term" "github.com/muesli/cancelreader" - "golang.org/x/term" ) func (p *Program) initTerminal() error { - err := p.initInput() - if err != nil { + if err := p.initInput(); err != nil { return err } - if p.console != nil { - err = p.console.SetRaw() - if err != nil { - return fmt.Errorf("error entering raw mode: %w", err) - } - } - p.renderer.hideCursor() return nil } @@ -34,6 +23,7 @@ func (p *Program) initTerminal() error { // Bubble Tea program. func (p *Program) restoreTerminalState() error { if p.renderer != nil { + p.renderer.disableBracketedPaste() p.renderer.showCursor() p.disableMouse() @@ -45,20 +35,28 @@ func (p *Program) restoreTerminalState() error { } } - if p.console != nil { - err := p.console.Reset() - if err != nil { - return fmt.Errorf("error restoring terminal state: %w", err) + return p.restoreInput() +} + +// restoreInput restores the tty input to its original state. +func (p *Program) restoreInput() error { + if p.ttyInput != nil && p.previousTtyInputState != nil { + if err := term.Restore(p.ttyInput.Fd(), p.previousTtyInputState); err != nil { + return fmt.Errorf("error restoring console: %w", err) } } - - return p.restoreInput() + if p.ttyOutput != nil && p.previousOutputState != nil { + if err := term.Restore(p.ttyOutput.Fd(), p.previousOutputState); err != nil { + return fmt.Errorf("error restoring console: %w", err) + } + } + return nil } // initCancelReader (re)commences reading inputs. func (p *Program) initCancelReader() error { var err error - p.cancelReader, err = cancelreader.NewReader(p.input) + p.cancelReader, err = newInputReader(p.input) if err != nil { return fmt.Errorf("error creating cancelreader: %w", err) } @@ -72,8 +70,7 @@ func (p *Program) initCancelReader() error { func (p *Program) readLoop() { defer close(p.readLoopDone) - input := localereader.NewReader(p.cancelReader) - err := readInputs(p.ctx, p.msgs, input) + err := readInputs(p.ctx, p.msgs, p.cancelReader) if !errors.Is(err, io.EOF) && !errors.Is(err, cancelreader.ErrCanceled) { select { case <-p.ctx.Done(): @@ -96,13 +93,12 @@ func (p *Program) waitForReadLoop() { // checkResize detects the current size of the output and informs the program // via a WindowSizeMsg. func (p *Program) checkResize() { - f, ok := p.output.TTY().(*os.File) - if !ok || !isatty.IsTerminal(f.Fd()) { + if p.ttyOutput == nil { // can't query window size return } - w, h, err := term.GetSize(int(f.Fd())) + w, h, err := term.GetSize(p.ttyOutput.Fd()) if err != nil { select { case <-p.ctx.Done(): diff --git a/vendor/github.com/charmbracelet/bubbletea/tty_unix.go b/vendor/github.com/charmbracelet/bubbletea/tty_unix.go index a3a25b8f..362e5850 100644 --- a/vendor/github.com/charmbracelet/bubbletea/tty_unix.go +++ b/vendor/github.com/charmbracelet/bubbletea/tty_unix.go @@ -1,5 +1,5 @@ -//go:build darwin || dragonfly || freebsd || linux || netbsd || openbsd || solaris || aix -// +build darwin dragonfly freebsd linux netbsd openbsd solaris aix +//go:build darwin || dragonfly || freebsd || linux || netbsd || openbsd || solaris || aix || zos +// +build darwin dragonfly freebsd linux netbsd openbsd solaris aix zos package tea @@ -7,32 +7,23 @@ import ( "fmt" "os" - "github.com/containerd/console" + "github.com/charmbracelet/x/term" ) -func (p *Program) initInput() error { - // If input's a file, use console to manage it - if f, ok := p.input.(*os.File); ok { - c, err := console.ConsoleFromFile(f) +func (p *Program) initInput() (err error) { + // Check if input is a terminal + if f, ok := p.input.(term.File); ok && term.IsTerminal(f.Fd()) { + p.ttyInput = f + p.previousTtyInputState, err = term.MakeRaw(p.ttyInput.Fd()) if err != nil { - return nil //nolint:nilerr // ignore error, this was just a test + return fmt.Errorf("error entering raw mode: %w", err) } - p.console = c } - return nil -} - -// On unix systems, RestoreInput closes any TTYs we opened for input. Note that -// we don't do this on Windows as it causes the prompt to not be drawn until -// the terminal receives a keypress rather than appearing promptly after the -// program exits. -func (p *Program) restoreInput() error { - if p.console != nil { - if err := p.console.Reset(); err != nil { - return fmt.Errorf("error restoring console: %w", err) - } + if f, ok := p.output.(term.File); ok && term.IsTerminal(f.Fd()) { + p.ttyOutput = f } + return nil } diff --git a/vendor/github.com/charmbracelet/bubbletea/tty_windows.go b/vendor/github.com/charmbracelet/bubbletea/tty_windows.go index be415aef..de6a82f9 100644 --- a/vendor/github.com/charmbracelet/bubbletea/tty_windows.go +++ b/vendor/github.com/charmbracelet/bubbletea/tty_windows.go @@ -4,42 +4,59 @@ package tea import ( + "fmt" "os" - "github.com/containerd/console" + "github.com/charmbracelet/x/term" + "golang.org/x/sys/windows" ) -func (p *Program) initInput() error { - // If input's a file, use console to manage it - if f, ok := p.input.(*os.File); ok { - // Save a reference to the current stdin then replace stdin with our - // input. We do this so we can hand input off to containerd/console to - // set raw mode, and do it in this fashion because the method - // console.ConsoleFromFile isn't supported on Windows. - p.windowsStdin = os.Stdin - os.Stdin = f - - // Note: this will panic if it fails. - c := console.Current() - p.console = c +func (p *Program) initInput() (err error) { + // Save stdin state and enable VT input + // We also need to enable VT + // input here. + if f, ok := p.input.(term.File); ok && term.IsTerminal(f.Fd()) { + p.ttyInput = f + p.previousTtyInputState, err = term.MakeRaw(p.ttyInput.Fd()) + if err != nil { + return err + } + + // Enable VT input + var mode uint32 + if err := windows.GetConsoleMode(windows.Handle(p.ttyInput.Fd()), &mode); err != nil { + return fmt.Errorf("error getting console mode: %w", err) + } + + if err := windows.SetConsoleMode(windows.Handle(p.ttyInput.Fd()), mode|windows.ENABLE_VIRTUAL_TERMINAL_INPUT); err != nil { + return fmt.Errorf("error setting console mode: %w", err) + } } - return nil -} + // Save output screen buffer state and enable VT processing. + if f, ok := p.output.(term.File); ok && term.IsTerminal(f.Fd()) { + p.ttyOutput = f + p.previousOutputState, err = term.GetState(f.Fd()) + if err != nil { + return err + } + + var mode uint32 + if err := windows.GetConsoleMode(windows.Handle(p.ttyOutput.Fd()), &mode); err != nil { + return fmt.Errorf("error getting console mode: %w", err) + } -// restoreInput restores stdout in the event that we placed it aside to handle -// input with CONIN$, above. -func (p *Program) restoreInput() error { - if p.windowsStdin != nil { - os.Stdin = p.windowsStdin + if err := windows.SetConsoleMode(windows.Handle(p.ttyOutput.Fd()), mode|windows.ENABLE_VIRTUAL_TERMINAL_PROCESSING); err != nil { + return fmt.Errorf("error setting console mode: %w", err) + } } - return nil + return } // Open the Windows equivalent of a TTY. func openInputTTY() (*os.File, error) { - f, err := os.OpenFile("CONIN$", os.O_RDWR, 0644) + f, err := os.OpenFile("CONIN$", os.O_RDWR, 0o644) if err != nil { return nil, err } diff --git a/vendor/github.com/charmbracelet/x/ansi/LICENSE b/vendor/github.com/charmbracelet/x/ansi/LICENSE new file mode 100644 index 00000000..65a5654e --- /dev/null +++ b/vendor/github.com/charmbracelet/x/ansi/LICENSE @@ -0,0 +1,21 @@ +MIT License + +Copyright (c) 2023 Charmbracelet, Inc. + +Permission is hereby granted, free of charge, to any person obtaining a copy +of this software and associated documentation files (the "Software"), to deal +in the Software without restriction, including without limitation the rights +to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +copies of the Software, and to permit persons to whom the Software is +furnished to do so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in all +copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE +SOFTWARE. diff --git a/vendor/github.com/charmbracelet/x/ansi/ansi.go b/vendor/github.com/charmbracelet/x/ansi/ansi.go new file mode 100644 index 00000000..48d873c3 --- /dev/null +++ b/vendor/github.com/charmbracelet/x/ansi/ansi.go @@ -0,0 +1,11 @@ +package ansi + +import "io" + +// Execute is a function that "execute" the given escape sequence by writing it +// to the provided output writter. +// +// This is a syntactic sugar over [io.WriteString]. +func Execute(w io.Writer, s string) (int, error) { + return io.WriteString(w, s) +} diff --git a/vendor/github.com/charmbracelet/x/ansi/ascii.go b/vendor/github.com/charmbracelet/x/ansi/ascii.go new file mode 100644 index 00000000..188582f7 --- /dev/null +++ b/vendor/github.com/charmbracelet/x/ansi/ascii.go @@ -0,0 +1,8 @@ +package ansi + +const ( + // SP is the space character (Char: \x20). + SP = 0x20 + // DEL is the delete character (Caret: ^?, Char: \x7f). + DEL = 0x7F +) diff --git a/vendor/github.com/charmbracelet/x/ansi/background.go b/vendor/github.com/charmbracelet/x/ansi/background.go new file mode 100644 index 00000000..f519af08 --- /dev/null +++ b/vendor/github.com/charmbracelet/x/ansi/background.go @@ -0,0 +1,61 @@ +package ansi + +import ( + "image/color" +) + +// SetForegroundColor returns a sequence that sets the default terminal +// foreground color. +// +// OSC 10 ; color ST +// OSC 10 ; color BEL +// +// Where color is the encoded color number. +// +// See: https://invisible-island.net/xterm/ctlseqs/ctlseqs.html#h3-Operating-System-Commands +func SetForegroundColor(c color.Color) string { + return "\x1b]10;" + colorToHexString(c) + "\x07" +} + +// RequestForegroundColor is a sequence that requests the current default +// terminal foreground color. +// +// See: https://invisible-island.net/xterm/ctlseqs/ctlseqs.html#h3-Operating-System-Commands +const RequestForegroundColor = "\x1b]10;?\x07" + +// SetBackgroundColor returns a sequence that sets the default terminal +// background color. +// +// OSC 11 ; color ST +// OSC 11 ; color BEL +// +// Where color is the encoded color number. +// +// See: https://invisible-island.net/xterm/ctlseqs/ctlseqs.html#h3-Operating-System-Commands +func SetBackgroundColor(c color.Color) string { + return "\x1b]11;" + colorToHexString(c) + "\x07" +} + +// RequestBackgroundColor is a sequence that requests the current default +// terminal background color. +// +// See: https://invisible-island.net/xterm/ctlseqs/ctlseqs.html#h3-Operating-System-Commands +const RequestBackgroundColor = "\x1b]11;?\x07" + +// SetCursorColor returns a sequence that sets the terminal cursor color. +// +// OSC 12 ; color ST +// OSC 12 ; color BEL +// +// Where color is the encoded color number. +// +// See: https://invisible-island.net/xterm/ctlseqs/ctlseqs.html#h3-Operating-System-Commands +func SetCursorColor(c color.Color) string { + return "\x1b]12;" + colorToHexString(c) + "\x07" +} + +// RequestCursorColor is a sequence that requests the current terminal cursor +// color. +// +// See: https://invisible-island.net/xterm/ctlseqs/ctlseqs.html#h3-Operating-System-Commands +const RequestCursorColor = "\x1b]12;?\x07" diff --git a/vendor/github.com/charmbracelet/x/ansi/c0.go b/vendor/github.com/charmbracelet/x/ansi/c0.go new file mode 100644 index 00000000..13e3c6c3 --- /dev/null +++ b/vendor/github.com/charmbracelet/x/ansi/c0.go @@ -0,0 +1,72 @@ +package ansi + +// C0 control characters. +// +// These range from (0x00-0x1F) as defined in ISO 646 (ASCII). +// See: https://en.wikipedia.org/wiki/C0_and_C1_control_codes +const ( + // NUL is the null character (Caret: ^@, Char: \0). + NUL = 0x00 + // SOH is the start of heading character (Caret: ^A). + SOH = 0x01 + // STX is the start of text character (Caret: ^B). + STX = 0x02 + // ETX is the end of text character (Caret: ^C). + ETX = 0x03 + // EOT is the end of transmission character (Caret: ^D). + EOT = 0x04 + // ENQ is the enquiry character (Caret: ^E). + ENQ = 0x05 + // ACK is the acknowledge character (Caret: ^F). + ACK = 0x06 + // BEL is the bell character (Caret: ^G, Char: \a). + BEL = 0x07 + // BS is the backspace character (Caret: ^H, Char: \b). + BS = 0x08 + // HT is the horizontal tab character (Caret: ^I, Char: \t). + HT = 0x09 + // LF is the line feed character (Caret: ^J, Char: \n). + LF = 0x0A + // VT is the vertical tab character (Caret: ^K, Char: \v). + VT = 0x0B + // FF is the form feed character (Caret: ^L, Char: \f). + FF = 0x0C + // CR is the carriage return character (Caret: ^M, Char: \r). + CR = 0x0D + // SO is the shift out character (Caret: ^N). + SO = 0x0E + // SI is the shift in character (Caret: ^O). + SI = 0x0F + // DLE is the data link escape character (Caret: ^P). + DLE = 0x10 + // DC1 is the device control 1 character (Caret: ^Q). + DC1 = 0x11 + // DC2 is the device control 2 character (Caret: ^R). + DC2 = 0x12 + // DC3 is the device control 3 character (Caret: ^S). + DC3 = 0x13 + // DC4 is the device control 4 character (Caret: ^T). + DC4 = 0x14 + // NAK is the negative acknowledge character (Caret: ^U). + NAK = 0x15 + // SYN is the synchronous idle character (Caret: ^V). + SYN = 0x16 + // ETB is the end of transmission block character (Caret: ^W). + ETB = 0x17 + // CAN is the cancel character (Caret: ^X). + CAN = 0x18 + // EM is the end of medium character (Caret: ^Y). + EM = 0x19 + // SUB is the substitute character (Caret: ^Z). + SUB = 0x1A + // ESC is the escape character (Caret: ^[, Char: \e). + ESC = 0x1B + // FS is the file separator character (Caret: ^\). + FS = 0x1C + // GS is the group separator character (Caret: ^]). + GS = 0x1D + // RS is the record separator character (Caret: ^^). + RS = 0x1E + // US is the unit separator character (Caret: ^_). + US = 0x1F +) diff --git a/vendor/github.com/charmbracelet/x/ansi/c1.go b/vendor/github.com/charmbracelet/x/ansi/c1.go new file mode 100644 index 00000000..71058f53 --- /dev/null +++ b/vendor/github.com/charmbracelet/x/ansi/c1.go @@ -0,0 +1,72 @@ +package ansi + +// C1 control characters. +// +// These range from (0x80-0x9F) as defined in ISO 6429 (ECMA-48). +// See: https://en.wikipedia.org/wiki/C0_and_C1_control_codes +const ( + // PAD is the padding character. + PAD = 0x80 + // HOP is the high octet preset character. + HOP = 0x81 + // BPH is the break permitted here character. + BPH = 0x82 + // NBH is the no break here character. + NBH = 0x83 + // IND is the index character. + IND = 0x84 + // NEL is the next line character. + NEL = 0x85 + // SSA is the start of selected area character. + SSA = 0x86 + // ESA is the end of selected area character. + ESA = 0x87 + // HTS is the horizontal tab set character. + HTS = 0x88 + // HTJ is the horizontal tab with justification character. + HTJ = 0x89 + // VTS is the vertical tab set character. + VTS = 0x8A + // PLD is the partial line forward character. + PLD = 0x8B + // PLU is the partial line backward character. + PLU = 0x8C + // RI is the reverse index character. + RI = 0x8D + // SS2 is the single shift 2 character. + SS2 = 0x8E + // SS3 is the single shift 3 character. + SS3 = 0x8F + // DCS is the device control string character. + DCS = 0x90 + // PU1 is the private use 1 character. + PU1 = 0x91 + // PU2 is the private use 2 character. + PU2 = 0x92 + // STS is the set transmit state character. + STS = 0x93 + // CCH is the cancel character. + CCH = 0x94 + // MW is the message waiting character. + MW = 0x95 + // SPA is the start of guarded area character. + SPA = 0x96 + // EPA is the end of guarded area character. + EPA = 0x97 + // SOS is the start of string character. + SOS = 0x98 + // SGCI is the single graphic character introducer character. + SGCI = 0x99 + // SCI is the single character introducer character. + SCI = 0x9A + // CSI is the control sequence introducer character. + CSI = 0x9B + // ST is the string terminator character. + ST = 0x9C + // OSC is the operating system command character. + OSC = 0x9D + // PM is the privacy message character. + PM = 0x9E + // APC is the application program command character. + APC = 0x9F +) diff --git a/vendor/github.com/charmbracelet/x/ansi/clipboard.go b/vendor/github.com/charmbracelet/x/ansi/clipboard.go new file mode 100644 index 00000000..94d26c36 --- /dev/null +++ b/vendor/github.com/charmbracelet/x/ansi/clipboard.go @@ -0,0 +1,75 @@ +package ansi + +import "encoding/base64" + +// Clipboard names. +const ( + SystemClipboard = 'c' + PrimaryClipboard = 'p' +) + +// SetClipboard returns a sequence for manipulating the clipboard. +// +// OSC 52 ; Pc ; Pd ST +// OSC 52 ; Pc ; Pd BEL +// +// Where Pc is the clipboard name and Pd is the base64 encoded data. +// Empty data or invalid base64 data will reset the clipboard. +// +// See: https://invisible-island.net/xterm/ctlseqs/ctlseqs.html#h3-Operating-System-Commands +func SetClipboard(c byte, d string) string { + if d != "" { + d = base64.StdEncoding.EncodeToString([]byte(d)) + } + return "\x1b]52;" + string(c) + ";" + d + "\x07" +} + +// SetSystemClipboard returns a sequence for setting the system clipboard. +// +// This is equivalent to SetClipboard(SystemClipboard, d). +func SetSystemClipboard(d string) string { + return SetClipboard(SystemClipboard, d) +} + +// SetPrimaryClipboard returns a sequence for setting the primary clipboard. +// +// This is equivalent to SetClipboard(PrimaryClipboard, d). +func SetPrimaryClipboard(d string) string { + return SetClipboard(PrimaryClipboard, d) +} + +// ResetClipboard returns a sequence for resetting the clipboard. +// +// This is equivalent to SetClipboard(c, ""). +// +// See: https://invisible-island.net/xterm/ctlseqs/ctlseqs.html#h3-Operating-System-Commands +func ResetClipboard(c byte) string { + return SetClipboard(c, "") +} + +// ResetSystemClipboard is a sequence for resetting the system clipboard. +// +// This is equivalent to ResetClipboard(SystemClipboard). +const ResetSystemClipboard = "\x1b]52;c;\x07" + +// ResetPrimaryClipboard is a sequence for resetting the primary clipboard. +// +// This is equivalent to ResetClipboard(PrimaryClipboard). +const ResetPrimaryClipboard = "\x1b]52;p;\x07" + +// RequestClipboard returns a sequence for requesting the clipboard. +// +// See: https://invisible-island.net/xterm/ctlseqs/ctlseqs.html#h3-Operating-System-Commands +func RequestClipboard(c byte) string { + return "\x1b]52;" + string(c) + ";?\x07" +} + +// RequestSystemClipboard is a sequence for requesting the system clipboard. +// +// This is equivalent to RequestClipboard(SystemClipboard). +const RequestSystemClipboard = "\x1b]52;c;?\x07" + +// RequestPrimaryClipboard is a sequence for requesting the primary clipboard. +// +// This is equivalent to RequestClipboard(PrimaryClipboard). +const RequestPrimaryClipboard = "\x1b]52;p;?\x07" diff --git a/vendor/github.com/charmbracelet/x/ansi/color.go b/vendor/github.com/charmbracelet/x/ansi/color.go new file mode 100644 index 00000000..2ff78bd7 --- /dev/null +++ b/vendor/github.com/charmbracelet/x/ansi/color.go @@ -0,0 +1,196 @@ +package ansi + +import ( + "image/color" +) + +// Technically speaking, the 16 basic ANSI colors are arbitrary and can be +// customized at the terminal level. Given that, we're returning what we feel +// are good defaults. +// +// This could also be a slice, but we use a map to make the mappings very +// explicit. +// +// See: https://www.ditig.com/publications/256-colors-cheat-sheet +var lowANSI = map[uint32]uint32{ + 0: 0x000000, // black + 1: 0x800000, // red + 2: 0x008000, // green + 3: 0x808000, // yellow + 4: 0x000080, // blue + 5: 0x800080, // magenta + 6: 0x008080, // cyan + 7: 0xc0c0c0, // white + 8: 0x808080, // bright black + 9: 0xff0000, // bright red + 10: 0x00ff00, // bright green + 11: 0xffff00, // bright yellow + 12: 0x0000ff, // bright blue + 13: 0xff00ff, // bright magenta + 14: 0x00ffff, // bright cyan + 15: 0xffffff, // bright white +} + +// Color is a color that can be used in a terminal. ANSI (including +// ANSI256) and 24-bit "true colors" fall under this category. +type Color interface { + color.Color +} + +// BasicColor is an ANSI 3-bit or 4-bit color with a value from 0 to 15. +type BasicColor uint8 + +var _ Color = BasicColor(0) + +const ( + // Black is the ANSI black color. + Black BasicColor = iota + + // Red is the ANSI red color. + Red + + // Green is the ANSI green color. + Green + + // Yellow is the ANSI yellow color. + Yellow + + // Blue is the ANSI blue color. + Blue + + // Magenta is the ANSI magenta color. + Magenta + + // Cyan is the ANSI cyan color. + Cyan + + // White is the ANSI white color. + White + + // BrightBlack is the ANSI bright black color. + BrightBlack + + // BrightRed is the ANSI bright red color. + BrightRed + + // BrightGreen is the ANSI bright green color. + BrightGreen + + // BrightYellow is the ANSI bright yellow color. + BrightYellow + + // BrightBlue is the ANSI bright blue color. + BrightBlue + + // BrightMagenta is the ANSI bright magenta color. + BrightMagenta + + // BrightCyan is the ANSI bright cyan color. + BrightCyan + + // BrightWhite is the ANSI bright white color. + BrightWhite +) + +// RGBA returns the red, green, blue and alpha components of the color. It +// satisfies the color.Color interface. +func (c BasicColor) RGBA() (uint32, uint32, uint32, uint32) { + ansi := uint32(c) + if ansi > 15 { + return 0, 0, 0, 0xffff + } + + r, g, b := ansiToRGB(ansi) + return toRGBA(r, g, b) +} + +// ExtendedColor is an ANSI 256 (8-bit) color with a value from 0 to 255. +type ExtendedColor uint8 + +var _ Color = ExtendedColor(0) + +// RGBA returns the red, green, blue and alpha components of the color. It +// satisfies the color.Color interface. +func (c ExtendedColor) RGBA() (uint32, uint32, uint32, uint32) { + r, g, b := ansiToRGB(uint32(c)) + return toRGBA(r, g, b) +} + +// TrueColor is a 24-bit color that can be used in the terminal. +// This can be used to represent RGB colors. +// +// For example, the color red can be represented as: +// +// TrueColor(0xff0000) +type TrueColor uint32 + +var _ Color = TrueColor(0) + +// RGBA returns the red, green, blue and alpha components of the color. It +// satisfies the color.Color interface. +func (c TrueColor) RGBA() (uint32, uint32, uint32, uint32) { + r, g, b := hexToRGB(uint32(c)) + return toRGBA(r, g, b) +} + +// ansiToRGB converts an ANSI color to a 24-bit RGB color. +// +// r, g, b := ansiToRGB(57) +func ansiToRGB(ansi uint32) (uint32, uint32, uint32) { + // For out-of-range values return black. + if ansi > 255 { + return 0, 0, 0 + } + + // Low ANSI. + if ansi < 16 { + h, ok := lowANSI[ansi] + if !ok { + return 0, 0, 0 + } + r, g, b := hexToRGB(h) + return r, g, b + } + + // Grays. + if ansi > 231 { + s := (ansi-232)*10 + 8 + return s, s, s + } + + // ANSI256. + n := ansi - 16 + b := n % 6 + g := (n - b) / 6 % 6 + r := (n - b - g*6) / 36 % 6 + for _, v := range []*uint32{&r, &g, &b} { + if *v > 0 { + c := *v*40 + 55 + *v = c + } + } + + return r, g, b +} + +// hexToRGB converts a number in hexadecimal format to red, green, and blue +// values. +// +// r, g, b := hexToRGB(0x0000FF) +func hexToRGB(hex uint32) (uint32, uint32, uint32) { + return hex >> 16, hex >> 8 & 0xff, hex & 0xff +} + +// toRGBA converts an RGB 8-bit color values to 32-bit color values suitable +// for color.Color. +// +// color.Color requires 16-bit color values, so we duplicate the 8-bit values +// to fill the 16-bit values. +// +// This always returns 0xffff (opaque) for the alpha channel. +func toRGBA(r, g, b uint32) (uint32, uint32, uint32, uint32) { + r |= r << 8 + g |= g << 8 + b |= b << 8 + return r, g, b, 0xffff +} diff --git a/vendor/github.com/charmbracelet/x/ansi/csi.go b/vendor/github.com/charmbracelet/x/ansi/csi.go new file mode 100644 index 00000000..b7e5bd2d --- /dev/null +++ b/vendor/github.com/charmbracelet/x/ansi/csi.go @@ -0,0 +1,141 @@ +package ansi + +import ( + "bytes" + "strconv" + + "github.com/charmbracelet/x/ansi/parser" +) + +// CsiSequence represents a control sequence introducer (CSI) sequence. +// +// The sequence starts with a CSI sequence, CSI (0x9B) in a 8-bit environment +// or ESC [ (0x1B 0x5B) in a 7-bit environment, followed by any number of +// parameters in the range of 0x30-0x3F, then by any number of intermediate +// byte in the range of 0x20-0x2F, then finally with a single final byte in the +// range of 0x20-0x7E. +// +// CSI P..P I..I F +// +// See ECMA-48 § 5.4. +type CsiSequence struct { + // Params contains the raw parameters of the sequence. + // This is a slice of integers, where each integer is a 32-bit integer + // containing the parameter value in the lower 31 bits and a flag in the + // most significant bit indicating whether there are more sub-parameters. + Params []int + + // Cmd contains the raw command of the sequence. + // The command is a 32-bit integer containing the CSI command byte in the + // lower 8 bits, the private marker in the next 8 bits, and the intermediate + // byte in the next 8 bits. + // + // CSI ? u + // + // Is represented as: + // + // 'u' | '?' << 8 + Cmd int +} + +var _ Sequence = CsiSequence{} + +// Marker returns the marker byte of the CSI sequence. +// This is always gonna be one of the following '<' '=' '>' '?' and in the +// range of 0x3C-0x3F. +// Zero is returned if the sequence does not have a marker. +func (s CsiSequence) Marker() int { + return parser.Marker(s.Cmd) +} + +// Intermediate returns the intermediate byte of the CSI sequence. +// An intermediate byte is in the range of 0x20-0x2F. This includes these +// characters from ' ', '!', '"', '#', '$', '%', '&', ”', '(', ')', '*', '+', +// ',', '-', '.', '/'. +// Zero is returned if the sequence does not have an intermediate byte. +func (s CsiSequence) Intermediate() int { + return parser.Intermediate(s.Cmd) +} + +// Command returns the command byte of the CSI sequence. +func (s CsiSequence) Command() int { + return parser.Command(s.Cmd) +} + +// Param returns the parameter at the given index. +// It returns -1 if the parameter does not exist. +func (s CsiSequence) Param(i int) int { + return parser.Param(s.Params, i) +} + +// HasMore returns true if the parameter has more sub-parameters. +func (s CsiSequence) HasMore(i int) bool { + return parser.HasMore(s.Params, i) +} + +// Subparams returns the sub-parameters of the given parameter. +// It returns nil if the parameter does not exist. +func (s CsiSequence) Subparams(i int) []int { + return parser.Subparams(s.Params, i) +} + +// Len returns the number of parameters in the sequence. +// This will return the number of parameters in the sequence, excluding any +// sub-parameters. +func (s CsiSequence) Len() int { + return parser.Len(s.Params) +} + +// Range iterates over the parameters of the sequence and calls the given +// function for each parameter. +// The function should return false to stop the iteration. +func (s CsiSequence) Range(fn func(i int, param int, hasMore bool) bool) { + parser.Range(s.Params, fn) +} + +// Clone returns a copy of the CSI sequence. +func (s CsiSequence) Clone() Sequence { + return CsiSequence{ + Params: append([]int(nil), s.Params...), + Cmd: s.Cmd, + } +} + +// String returns a string representation of the sequence. +// The string will always be in the 7-bit format i.e (ESC [ P..P I..I F). +func (s CsiSequence) String() string { + return s.buffer().String() +} + +// buffer returns a buffer containing the sequence. +func (s CsiSequence) buffer() *bytes.Buffer { + var b bytes.Buffer + b.WriteString("\x1b[") + if m := s.Marker(); m != 0 { + b.WriteByte(byte(m)) + } + s.Range(func(i, param int, hasMore bool) bool { + if param >= 0 { + b.WriteString(strconv.Itoa(param)) + } + if i < len(s.Params)-1 { + if hasMore { + b.WriteByte(':') + } else { + b.WriteByte(';') + } + } + return true + }) + if i := s.Intermediate(); i != 0 { + b.WriteByte(byte(i)) + } + b.WriteByte(byte(s.Command())) + return &b +} + +// Bytes returns the byte representation of the sequence. +// The bytes will always be in the 7-bit format i.e (ESC [ P..P I..I F). +func (s CsiSequence) Bytes() []byte { + return s.buffer().Bytes() +} diff --git a/vendor/github.com/charmbracelet/x/ansi/ctrl.go b/vendor/github.com/charmbracelet/x/ansi/ctrl.go new file mode 100644 index 00000000..21beb9cd --- /dev/null +++ b/vendor/github.com/charmbracelet/x/ansi/ctrl.go @@ -0,0 +1,17 @@ +package ansi + +// RequestXTVersion is a control sequence that requests the terminal's XTVERSION. It responds with a DSR sequence identifying the version. +// +// CSI > Ps q +// DCS > | text ST +// +// See https://invisible-island.net/xterm/ctlseqs/ctlseqs.html#h3-PC-Style-Function-Keys +const RequestXTVersion = "\x1b[>0q" + +// RequestPrimaryDeviceAttributes is a control sequence that requests the +// terminal's primary device attributes (DA1). +// +// CSI c +// +// See https://vt100.net/docs/vt510-rm/DA1.html +const RequestPrimaryDeviceAttributes = "\x1b[c" diff --git a/vendor/github.com/charmbracelet/x/ansi/cursor.go b/vendor/github.com/charmbracelet/x/ansi/cursor.go new file mode 100644 index 00000000..5f010fd4 --- /dev/null +++ b/vendor/github.com/charmbracelet/x/ansi/cursor.go @@ -0,0 +1,190 @@ +package ansi + +import "strconv" + +// SaveCursor (DECSC) is an escape sequence that saves the current cursor +// position. +// +// ESC 7 +// +// See: https://vt100.net/docs/vt510-rm/DECSC.html +const SaveCursor = "\x1b7" + +// RestoreCursor (DECRC) is an escape sequence that restores the cursor +// position. +// +// ESC 8 +// +// See: https://vt100.net/docs/vt510-rm/DECRC.html +const RestoreCursor = "\x1b8" + +// RequestCursorPosition (CPR) is an escape sequence that requests the current +// cursor position. +// +// CSI 6 n +// +// The terminal will report the cursor position as a CSI sequence in the +// following format: +// +// CSI Pl ; Pc R +// +// Where Pl is the line number and Pc is the column number. +// See: https://vt100.net/docs/vt510-rm/CPR.html +const RequestCursorPosition = "\x1b[6n" + +// RequestExtendedCursorPosition (DECXCPR) is a sequence for requesting the +// cursor position report including the current page number. +// +// CSI ? 6 n +// +// The terminal will report the cursor position as a CSI sequence in the +// following format: +// +// CSI ? Pl ; Pc ; Pp R +// +// Where Pl is the line number, Pc is the column number, and Pp is the page +// number. +// See: https://vt100.net/docs/vt510-rm/DECXCPR.html +const RequestExtendedCursorPosition = "\x1b[?6n" + +// CursorUp (CUU) returns a sequence for moving the cursor up n cells. +// +// CSI n A +// +// See: https://vt100.net/docs/vt510-rm/CUU.html +func CursorUp(n int) string { + var s string + if n > 1 { + s = strconv.Itoa(n) + } + return "\x1b[" + s + "A" +} + +// CursorUp1 is a sequence for moving the cursor up one cell. +// +// This is equivalent to CursorUp(1). +const CursorUp1 = "\x1b[A" + +// CursorDown (CUD) returns a sequence for moving the cursor down n cells. +// +// CSI n B +// +// See: https://vt100.net/docs/vt510-rm/CUD.html +func CursorDown(n int) string { + var s string + if n > 1 { + s = strconv.Itoa(n) + } + return "\x1b[" + s + "B" +} + +// CursorDown1 is a sequence for moving the cursor down one cell. +// +// This is equivalent to CursorDown(1). +const CursorDown1 = "\x1b[B" + +// CursorRight (CUF) returns a sequence for moving the cursor right n cells. +// +// CSI n C +// +// See: https://vt100.net/docs/vt510-rm/CUF.html +func CursorRight(n int) string { + var s string + if n > 1 { + s = strconv.Itoa(n) + } + return "\x1b[" + s + "C" +} + +// CursorRight1 is a sequence for moving the cursor right one cell. +// +// This is equivalent to CursorRight(1). +const CursorRight1 = "\x1b[C" + +// CursorLeft (CUB) returns a sequence for moving the cursor left n cells. +// +// CSI n D +// +// See: https://vt100.net/docs/vt510-rm/CUB.html +func CursorLeft(n int) string { + var s string + if n > 1 { + s = strconv.Itoa(n) + } + return "\x1b[" + s + "D" +} + +// CursorLeft1 is a sequence for moving the cursor left one cell. +// +// This is equivalent to CursorLeft(1). +const CursorLeft1 = "\x1b[D" + +// CursorNextLine (CNL) returns a sequence for moving the cursor to the +// beginning of the next line n times. +// +// CSI n E +// +// See: https://vt100.net/docs/vt510-rm/CNL.html +func CursorNextLine(n int) string { + var s string + if n > 1 { + s = strconv.Itoa(n) + } + return "\x1b[" + s + "E" +} + +// CursorPreviousLine (CPL) returns a sequence for moving the cursor to the +// beginning of the previous line n times. +// +// CSI n F +// +// See: https://vt100.net/docs/vt510-rm/CPL.html +func CursorPreviousLine(n int) string { + var s string + if n > 1 { + s = strconv.Itoa(n) + } + return "\x1b[" + s + "F" +} + +// MoveCursor (CUP) returns a sequence for moving the cursor to the given row +// and column. +// +// CSI n ; m H +// +// See: https://vt100.net/docs/vt510-rm/CUP.html +func MoveCursor(row, col int) string { + if row < 0 { + row = 0 + } + if col < 0 { + col = 0 + } + return "\x1b[" + strconv.Itoa(row) + ";" + strconv.Itoa(col) + "H" +} + +// MoveCursorOrigin is a sequence for moving the cursor to the upper left +// corner of the screen. This is equivalent to MoveCursor(1, 1). +const MoveCursorOrigin = "\x1b[1;1H" + +// SaveCursorPosition (SCP or SCOSC) is a sequence for saving the cursor +// position. +// +// CSI s +// +// This acts like Save, except the page number where the cursor is located is +// not saved. +// +// See: https://vt100.net/docs/vt510-rm/SCOSC.html +const SaveCursorPosition = "\x1b[s" + +// RestoreCursorPosition (RCP or SCORC) is a sequence for restoring the cursor +// position. +// +// CSI u +// +// This acts like Restore, except the cursor stays on the same page where the +// cursor was saved. +// +// See: https://vt100.net/docs/vt510-rm/SCORC.html +const RestoreCursorPosition = "\x1b[u" diff --git a/vendor/github.com/charmbracelet/x/ansi/dcs.go b/vendor/github.com/charmbracelet/x/ansi/dcs.go new file mode 100644 index 00000000..185f0b52 --- /dev/null +++ b/vendor/github.com/charmbracelet/x/ansi/dcs.go @@ -0,0 +1,148 @@ +package ansi + +import ( + "bytes" + "strconv" + + "github.com/charmbracelet/x/ansi/parser" +) + +// DcsSequence represents a Device Control String (DCS) escape sequence. +// +// The DCS sequence is used to send device control strings to the terminal. The +// sequence starts with the C1 control code character DCS (0x9B) or ESC P in +// 7-bit environments, followed by parameter bytes, intermediate bytes, a +// command byte, followed by data bytes, and ends with the C1 control code +// character ST (0x9C) or ESC \ in 7-bit environments. +// +// This follows the parameter string format. +// See ECMA-48 § 5.4.1 +type DcsSequence struct { + // Params contains the raw parameters of the sequence. + // This is a slice of integers, where each integer is a 32-bit integer + // containing the parameter value in the lower 31 bits and a flag in the + // most significant bit indicating whether there are more sub-parameters. + Params []int + + // Data contains the string raw data of the sequence. + // This is the data between the final byte and the escape sequence terminator. + Data []byte + + // Cmd contains the raw command of the sequence. + // The command is a 32-bit integer containing the DCS command byte in the + // lower 8 bits, the private marker in the next 8 bits, and the intermediate + // byte in the next 8 bits. + // + // DCS > 0 ; 1 $ r ST + // + // Is represented as: + // + // 'r' | '>' << 8 | '$' << 16 + Cmd int +} + +var _ Sequence = DcsSequence{} + +// Marker returns the marker byte of the DCS sequence. +// This is always gonna be one of the following '<' '=' '>' '?' and in the +// range of 0x3C-0x3F. +// Zero is returned if the sequence does not have a marker. +func (s DcsSequence) Marker() int { + return parser.Marker(s.Cmd) +} + +// Intermediate returns the intermediate byte of the DCS sequence. +// An intermediate byte is in the range of 0x20-0x2F. This includes these +// characters from ' ', '!', '"', '#', '$', '%', '&', ”', '(', ')', '*', '+', +// ',', '-', '.', '/'. +// Zero is returned if the sequence does not have an intermediate byte. +func (s DcsSequence) Intermediate() int { + return parser.Intermediate(s.Cmd) +} + +// Command returns the command byte of the CSI sequence. +func (s DcsSequence) Command() int { + return parser.Command(s.Cmd) +} + +// Param returns the parameter at the given index. +// It returns -1 if the parameter does not exist. +func (s DcsSequence) Param(i int) int { + return parser.Param(s.Params, i) +} + +// HasMore returns true if the parameter has more sub-parameters. +func (s DcsSequence) HasMore(i int) bool { + return parser.HasMore(s.Params, i) +} + +// Subparams returns the sub-parameters of the given parameter. +// It returns nil if the parameter does not exist. +func (s DcsSequence) Subparams(i int) []int { + return parser.Subparams(s.Params, i) +} + +// Len returns the number of parameters in the sequence. +// This will return the number of parameters in the sequence, excluding any +// sub-parameters. +func (s DcsSequence) Len() int { + return parser.Len(s.Params) +} + +// Range iterates over the parameters of the sequence and calls the given +// function for each parameter. +// The function should return false to stop the iteration. +func (s DcsSequence) Range(fn func(i int, param int, hasMore bool) bool) { + parser.Range(s.Params, fn) +} + +// Clone returns a copy of the DCS sequence. +func (s DcsSequence) Clone() Sequence { + return DcsSequence{ + Params: append([]int(nil), s.Params...), + Data: append([]byte(nil), s.Data...), + Cmd: s.Cmd, + } +} + +// String returns a string representation of the sequence. +// The string will always be in the 7-bit format i.e (ESC P p..p i..i f ESC \). +func (s DcsSequence) String() string { + return s.buffer().String() +} + +// buffer returns a buffer containing the sequence. +func (s DcsSequence) buffer() *bytes.Buffer { + var b bytes.Buffer + b.WriteString("\x1bP") + if m := s.Marker(); m != 0 { + b.WriteByte(byte(m)) + } + s.Range(func(i, param int, hasMore bool) bool { + if param >= -1 { + b.WriteString(strconv.Itoa(param)) + } + if i < len(s.Params)-1 { + if hasMore { + b.WriteByte(':') + } else { + b.WriteByte(';') + } + } + return true + }) + if i := s.Intermediate(); i != 0 { + b.WriteByte(byte(i)) + } + b.WriteByte(byte(s.Command())) + b.Write(s.Data) + b.WriteByte(ESC) + b.WriteByte('\\') + return &b +} + +// Bytes returns the byte representation of the sequence. +// The bytes will always be in the 7-bit format i.e (ESC P p..p i..i F ESC \). +func (s DcsSequence) Bytes() []byte { + return s.buffer().Bytes() +} diff --git a/vendor/github.com/charmbracelet/x/ansi/doc.go b/vendor/github.com/charmbracelet/x/ansi/doc.go new file mode 100644 index 00000000..e955e9f1 --- /dev/null +++ b/vendor/github.com/charmbracelet/x/ansi/doc.go @@ -0,0 +1,7 @@ +// Package ansi defines common ANSI escape sequences based on the ECMA-48 +// specs. +// +// All sequences use 7-bit C1 control codes, which are supported by most +// terminal emulators. OSC sequences are terminated by a BEL for wider +// compatibility with terminals. +package ansi diff --git a/vendor/github.com/charmbracelet/x/ansi/hyperlink.go b/vendor/github.com/charmbracelet/x/ansi/hyperlink.go new file mode 100644 index 00000000..323bfe93 --- /dev/null +++ b/vendor/github.com/charmbracelet/x/ansi/hyperlink.go @@ -0,0 +1,28 @@ +package ansi + +import "strings" + +// SetHyperlink returns a sequence for starting a hyperlink. +// +// OSC 8 ; Params ; Uri ST +// OSC 8 ; Params ; Uri BEL +// +// To reset the hyperlink, omit the URI. +// +// See: https://gist.github.com/egmontkob/eb114294efbcd5adb1944c9f3cb5feda +func SetHyperlink(uri string, params ...string) string { + var p string + if len(params) > 0 { + p = strings.Join(params, ":") + } + return "\x1b]8;" + p + ";" + uri + "\x07" +} + +// ResetHyperlink returns a sequence for resetting the hyperlink. +// +// This is equivalent to SetHyperlink("", params...). +// +// See: https://gist.github.com/egmontkob/eb114294efbcd5adb1944c9f3cb5feda +func ResetHyperlink(params ...string) string { + return SetHyperlink("", params...) +} diff --git a/vendor/github.com/charmbracelet/x/ansi/kitty.go b/vendor/github.com/charmbracelet/x/ansi/kitty.go new file mode 100644 index 00000000..5bf89814 --- /dev/null +++ b/vendor/github.com/charmbracelet/x/ansi/kitty.go @@ -0,0 +1,58 @@ +package ansi + +import "strconv" + +// Kitty keyboard protocol progressive enhancement flags. +// See: https://sw.kovidgoyal.net/kitty/keyboard-protocol/#progressive-enhancement +const ( + KittyDisambiguateEscapeCodes = 1 << iota + KittyReportEventTypes + KittyReportAlternateKeys + KittyReportAllKeys + KittyReportAssociatedKeys + + KittyAllFlags = KittyDisambiguateEscapeCodes | KittyReportEventTypes | + KittyReportAlternateKeys | KittyReportAllKeys | KittyReportAssociatedKeys +) + +// RequestKittyKeyboard is a sequence to request the terminal Kitty keyboard +// protocol enabled flags. +// +// See: https://sw.kovidgoyal.net/kitty/keyboard-protocol/ +const RequestKittyKeyboard = "\x1b[?u" + +// PushKittyKeyboard returns a sequence to push the given flags to the terminal +// Kitty Keyboard stack. +// +// CSI > flags u +// +// See https://sw.kovidgoyal.net/kitty/keyboard-protocol/#progressive-enhancement +func PushKittyKeyboard(flags int) string { + var f string + if flags > 0 { + f = strconv.Itoa(flags) + } + + return "\x1b[>" + f + "u" +} + +// DisableKittyKeyboard is a sequence to push zero into the terminal Kitty +// Keyboard stack to disable the protocol. +// +// This is equivalent to PushKittyKeyboard(0). +const DisableKittyKeyboard = "\x1b[>0u" + +// PopKittyKeyboard returns a sequence to pop n number of flags from the +// terminal Kitty Keyboard stack. +// +// CSI < flags u +// +// See https://sw.kovidgoyal.net/kitty/keyboard-protocol/#progressive-enhancement +func PopKittyKeyboard(n int) string { + var num string + if n > 0 { + num = strconv.Itoa(n) + } + + return "\x1b[<" + num + "u" +} diff --git a/vendor/github.com/charmbracelet/x/ansi/mode.go b/vendor/github.com/charmbracelet/x/ansi/mode.go new file mode 100644 index 00000000..4c39e78d --- /dev/null +++ b/vendor/github.com/charmbracelet/x/ansi/mode.go @@ -0,0 +1,132 @@ +package ansi + +// This file define uses multiple sequences to set (SM), reset (RM), and request +// (DECRQM) different ANSI and DEC modes. +// +// See: https://vt100.net/docs/vt510-rm/SM.html +// See: https://vt100.net/docs/vt510-rm/RM.html +// See: https://vt100.net/docs/vt510-rm/DECRQM.html +// +// The terminal then responds to the request with a Report Mode function +// (DECRPM) in the format: +// +// ANSI format: +// +// CSI Pa ; Ps ; $ y +// +// DEC format: +// +// CSI ? Pa ; Ps $ y +// +// Where Pa is the mode number, and Ps is the mode value. +// See: https://vt100.net/docs/vt510-rm/DECRPM.html + +// Application Cursor Keys (DECCKM) is a mode that determines whether the +// cursor keys send ANSI cursor sequences or application sequences. +// +// See: https://vt100.net/docs/vt510-rm/DECCKM.html +const ( + EnableCursorKeys = "\x1b[?1h" + DisableCursorKeys = "\x1b[?1l" + RequestCursorKeys = "\x1b[?1$p" +) + +// Text Cursor Enable Mode (DECTCEM) is a mode that shows/hides the cursor. +// +// See: https://vt100.net/docs/vt510-rm/DECTCEM.html +const ( + ShowCursor = "\x1b[?25h" + HideCursor = "\x1b[?25l" + RequestCursorVisibility = "\x1b[?25$p" +) + +// VT Mouse Tracking is a mode that determines whether the mouse reports on +// button press and release. +// +// See: https://invisible-island.net/xterm/ctlseqs/ctlseqs.html#h2-Mouse-Tracking +const ( + EnableMouse = "\x1b[?1000h" + DisableMouse = "\x1b[?1000l" + RequestMouse = "\x1b[?1000$p" +) + +// VT Hilite Mouse Tracking is a mode that determines whether the mouse reports on +// button presses, releases, and highlighted cells. +// +// See: https://invisible-island.net/xterm/ctlseqs/ctlseqs.html#h2-Mouse-Tracking +const ( + EnableMouseHilite = "\x1b[?1001h" + DisableMouseHilite = "\x1b[?1001l" + RequestMouseHilite = "\x1b[?1001$p" +) + +// Cell Motion Mouse Tracking is a mode that determines whether the mouse +// reports on button press, release, and motion events. +// +// See: https://invisible-island.net/xterm/ctlseqs/ctlseqs.html#h2-Mouse-Tracking +const ( + EnableMouseCellMotion = "\x1b[?1002h" + DisableMouseCellMotion = "\x1b[?1002l" + RequestMouseCellMotion = "\x1b[?1002$p" +) + +// All Mouse Tracking is a mode that determines whether the mouse reports on +// button press, release, motion, and highlight events. +// +// See: https://invisible-island.net/xterm/ctlseqs/ctlseqs.html#h2-Mouse-Tracking +const ( + EnableMouseAllMotion = "\x1b[?1003h" + DisableMouseAllMotion = "\x1b[?1003l" + RequestMouseAllMotion = "\x1b[?1003$p" +) + +// SGR Mouse Extension is a mode that determines whether the mouse reports events +// formatted with SGR parameters. +// +// See: https://invisible-island.net/xterm/ctlseqs/ctlseqs.html#h2-Mouse-Tracking +const ( + EnableMouseSgrExt = "\x1b[?1006h" + DisableMouseSgrExt = "\x1b[?1006l" + RequestMouseSgrExt = "\x1b[?1006$p" +) + +// Alternate Screen Buffer is a mode that determines whether the alternate screen +// buffer is active. +// +// See: https://invisible-island.net/xterm/ctlseqs/ctlseqs.html#h2-The-Alternate-Screen-Buffer +const ( + EnableAltScreenBuffer = "\x1b[?1049h" + DisableAltScreenBuffer = "\x1b[?1049l" + RequestAltScreenBuffer = "\x1b[?1049$p" +) + +// Bracketed Paste Mode is a mode that determines whether pasted text is +// bracketed with escape sequences. +// +// See: https://cirw.in/blog/bracketed-paste +// See: https://invisible-island.net/xterm/ctlseqs/ctlseqs.html#h2-Bracketed-Paste-Mode +const ( + EnableBracketedPaste = "\x1b[?2004h" + DisableBracketedPaste = "\x1b[?2004l" + RequestBracketedPaste = "\x1b[?2004$p" +) + +// Synchronized Output Mode is a mode that determines whether output is +// synchronized with the terminal. +// +// See: https://gist.github.com/christianparpart/d8a62cc1ab659194337d73e399004036 +const ( + EnableSyncdOutput = "\x1b[?2026h" + DisableSyncdOutput = "\x1b[?2026l" + RequestSyncdOutput = "\x1b[?2026$p" +) + +// Win32Input is a mode that determines whether input is processed by the +// Win32 console and Conpty. +// +// See: https://github.com/microsoft/terminal/blob/main/doc/specs/%234999%20-%20Improved%20keyboard%20handling%20in%20Conpty.md +const ( + EnableWin32Input = "\x1b[?9001h" + DisableWin32Input = "\x1b[?9001l" + RequestWin32Input = "\x1b[?9001$p" +) diff --git a/vendor/github.com/charmbracelet/x/ansi/osc.go b/vendor/github.com/charmbracelet/x/ansi/osc.go new file mode 100644 index 00000000..40b543c2 --- /dev/null +++ b/vendor/github.com/charmbracelet/x/ansi/osc.go @@ -0,0 +1,69 @@ +package ansi + +import ( + "bytes" + "strings" +) + +// OscSequence represents an OSC sequence. +// +// The sequence starts with a OSC sequence, OSC (0x9D) in a 8-bit environment +// or ESC ] (0x1B 0x5D) in a 7-bit environment, followed by positive integer identifier, +// then by arbitrary data terminated by a ST (0x9C) in a 8-bit environment, +// ESC \ (0x1B 0x5C) in a 7-bit environment, or BEL (0x07) for backwards compatibility. +// +// OSC Ps ; Pt ST +// OSC Ps ; Pt BEL +// +// See ECMA-48 § 5.7. +type OscSequence struct { + // Data contains the raw data of the sequence including the identifier + // command. + Data []byte + + // Cmd contains the raw command of the sequence. + Cmd int +} + +var _ Sequence = OscSequence{} + +// Command returns the command of the OSC sequence. +func (s OscSequence) Command() int { + return s.Cmd +} + +// Params returns the parameters of the OSC sequence split by ';'. +// The first element is the identifier command. +func (s OscSequence) Params() []string { + return strings.Split(string(s.Data), ";") +} + +// Clone returns a copy of the OSC sequence. +func (s OscSequence) Clone() Sequence { + return OscSequence{ + Data: append([]byte(nil), s.Data...), + Cmd: s.Cmd, + } +} + +// String returns the string representation of the OSC sequence. +// To be more compatible with different terminal, this will always return a +// 7-bit formatted sequence, terminated by BEL. +func (s OscSequence) String() string { + return s.buffer().String() +} + +// Bytes returns the byte representation of the OSC sequence. +// To be more compatible with different terminal, this will always return a +// 7-bit formatted sequence, terminated by BEL. +func (s OscSequence) Bytes() []byte { + return s.buffer().Bytes() +} + +func (s OscSequence) buffer() *bytes.Buffer { + var b bytes.Buffer + b.WriteString("\x1b]") + b.Write(s.Data) + b.WriteByte(BEL) + return &b +} diff --git a/vendor/github.com/charmbracelet/x/ansi/params.go b/vendor/github.com/charmbracelet/x/ansi/params.go new file mode 100644 index 00000000..a1bb4249 --- /dev/null +++ b/vendor/github.com/charmbracelet/x/ansi/params.go @@ -0,0 +1,45 @@ +package ansi + +import ( + "bytes" +) + +// Params parses and returns a list of control sequence parameters. +// +// Parameters are positive integers separated by semicolons. Empty parameters +// default to zero. Parameters can have sub-parameters separated by colons. +// +// Any non-parameter bytes are ignored. This includes bytes that are not in the +// range of 0x30-0x3B. +// +// See ECMA-48 § 5.4.1. +func Params(p []byte) [][]uint { + if len(p) == 0 { + return [][]uint{} + } + + // Filter out non-parameter bytes i.e. non 0x30-0x3B. + p = bytes.TrimFunc(p, func(r rune) bool { + return r < 0x30 || r > 0x3B + }) + + parts := bytes.Split(p, []byte{';'}) + params := make([][]uint, len(parts)) + for i, part := range parts { + sparts := bytes.Split(part, []byte{':'}) + params[i] = make([]uint, len(sparts)) + for j, spart := range sparts { + params[i][j] = bytesToUint16(spart) + } + } + + return params +} + +func bytesToUint16(b []byte) uint { + var n uint + for _, c := range b { + n = n*10 + uint(c-'0') + } + return n +} diff --git a/vendor/github.com/charmbracelet/x/ansi/parser.go b/vendor/github.com/charmbracelet/x/ansi/parser.go new file mode 100644 index 00000000..4595b514 --- /dev/null +++ b/vendor/github.com/charmbracelet/x/ansi/parser.go @@ -0,0 +1,357 @@ +package ansi + +import ( + "unicode/utf8" + + "github.com/charmbracelet/x/ansi/parser" +) + +// ParserDispatcher is a function that dispatches a sequence. +type ParserDispatcher func(Sequence) + +// Parser represents a DEC ANSI compatible sequence parser. +// +// It uses a state machine to parse ANSI escape sequences and control +// characters. The parser is designed to be used with a terminal emulator or +// similar application that needs to parse ANSI escape sequences and control +// characters. +// See package [parser] for more information. +// +//go:generate go run ./gen.go +type Parser struct { + // Params contains the raw parameters of the sequence. + // These parameters used when constructing CSI and DCS sequences. + Params []int + + // Data contains the raw data of the sequence. + // These data used when constructing OSC, DCS, SOS, PM, and APC sequences. + Data []byte + + // DataLen keeps track of the length of the data buffer. + // If DataLen is -1, the data buffer is unlimited and will grow as needed. + // Otherwise, DataLen is limited by the size of the Data buffer. + DataLen int + + // ParamsLen keeps track of the number of parameters. + // This is limited by the size of the Params buffer. + ParamsLen int + + // Cmd contains the raw command along with the private marker and + // intermediate bytes of the sequence. + // The first lower byte contains the command byte, the next byte contains + // the private marker, and the next byte contains the intermediate byte. + Cmd int + + // RuneLen keeps track of the number of bytes collected for a UTF-8 rune. + RuneLen int + + // RuneBuf contains the bytes collected for a UTF-8 rune. + RuneBuf [utf8.MaxRune]byte + + // State is the current state of the parser. + State byte +} + +// NewParser returns a new parser with the given sizes allocated. +// If dataSize is zero, the underlying data buffer will be unlimited and will +// grow as needed. +func NewParser(paramsSize, dataSize int) *Parser { + s := &Parser{ + Params: make([]int, paramsSize), + Data: make([]byte, dataSize), + } + if dataSize <= 0 { + s.DataLen = -1 + } + return s +} + +// Reset resets the parser to its initial state. +func (p *Parser) Reset() { + p.clear() + p.State = parser.GroundState +} + +// clear clears the parser parameters and command. +func (p *Parser) clear() { + if len(p.Params) > 0 { + p.Params[0] = parser.MissingParam + } + p.ParamsLen = 0 + p.Cmd = 0 + p.RuneLen = 0 +} + +// StateName returns the name of the current state. +func (p *Parser) StateName() string { + return parser.StateNames[p.State] +} + +// Parse parses the given dispatcher and byte buffer. +func (p *Parser) Parse(dispatcher ParserDispatcher, b []byte) { + for i := 0; i < len(b); i++ { + p.Advance(dispatcher, b[i], i < len(b)-1) + } +} + +// Advance advances the parser with the given dispatcher and byte. +func (p *Parser) Advance(dispatcher ParserDispatcher, b byte, more bool) parser.Action { + switch p.State { + case parser.Utf8State: + // We handle UTF-8 here. + return p.advanceUtf8(dispatcher, b) + default: + return p.advance(dispatcher, b, more) + } +} + +func (p *Parser) collectRune(b byte) { + if p.RuneLen < utf8.UTFMax { + p.RuneBuf[p.RuneLen] = b + p.RuneLen++ + } +} + +func (p *Parser) advanceUtf8(dispatcher ParserDispatcher, b byte) parser.Action { + // Collect UTF-8 rune bytes. + p.collectRune(b) + rw := utf8ByteLen(p.RuneBuf[0]) + if rw == -1 { + // We panic here because the first byte comes from the state machine, + // if this panics, it means there is a bug in the state machine! + panic("invalid rune") // unreachable + } + + if p.RuneLen < rw { + return parser.NoneAction + } + + // We have enough bytes to decode the rune + bts := p.RuneBuf[:rw] + r, _ := utf8.DecodeRune(bts) + if dispatcher != nil { + dispatcher(Rune(r)) + } + + p.State = parser.GroundState + p.RuneLen = 0 + + return parser.NoneAction +} + +func (p *Parser) advance(d ParserDispatcher, b byte, more bool) parser.Action { + state, action := parser.Table.Transition(p.State, b) + + // We need to clear the parser state if the state changes from EscapeState. + // This is because when we enter the EscapeState, we don't get a chance to + // clear the parser state. For example, when a sequence terminates with a + // ST (\x1b\\ or \x9c), we dispatch the current sequence and transition to + // EscapeState. However, the parser state is not cleared in this case and + // we need to clear it here before dispatching the esc sequence. + if p.State != state { + switch p.State { + case parser.EscapeState: + p.performAction(d, parser.ClearAction, b) + } + if action == parser.PutAction && + p.State == parser.DcsEntryState && state == parser.DcsStringState { + // XXX: This is a special case where we need to start collecting + // non-string parameterized data i.e. doesn't follow the ECMA-48 § + // 5.4.1 string parameters format. + p.performAction(d, parser.StartAction, 0) + } + } + + // Handle special cases + switch { + case b == ESC && p.State == parser.EscapeState: + // Two ESCs in a row + p.performAction(d, parser.ExecuteAction, b) + if !more { + // Two ESCs at the end of the buffer + p.performAction(d, parser.ExecuteAction, b) + } + case b == ESC && !more: + // Last byte is an ESC + p.performAction(d, parser.ExecuteAction, b) + case p.State == parser.EscapeState && b == 'P' && !more: + // ESC P (DCS) at the end of the buffer + p.performAction(d, parser.DispatchAction, b) + case p.State == parser.EscapeState && b == 'X' && !more: + // ESC X (SOS) at the end of the buffer + p.performAction(d, parser.DispatchAction, b) + case p.State == parser.EscapeState && b == '[' && !more: + // ESC [ (CSI) at the end of the buffer + p.performAction(d, parser.DispatchAction, b) + case p.State == parser.EscapeState && b == ']' && !more: + // ESC ] (OSC) at the end of the buffer + p.performAction(d, parser.DispatchAction, b) + case p.State == parser.EscapeState && b == '^' && !more: + // ESC ^ (PM) at the end of the buffer + p.performAction(d, parser.DispatchAction, b) + case p.State == parser.EscapeState && b == '_' && !more: + // ESC _ (APC) at the end of the buffer + p.performAction(d, parser.DispatchAction, b) + default: + p.performAction(d, action, b) + } + + p.State = state + + return action +} + +func (p *Parser) performAction(dispatcher ParserDispatcher, action parser.Action, b byte) { + switch action { + case parser.IgnoreAction: + break + + case parser.ClearAction: + p.clear() + + case parser.PrintAction: + if utf8ByteLen(b) > 1 { + p.collectRune(b) + } else if dispatcher != nil { + dispatcher(Rune(b)) + } + + case parser.ExecuteAction: + if dispatcher != nil { + dispatcher(ControlCode(b)) + } + + case parser.MarkerAction: + // Collect private marker + // we only store the last marker + p.Cmd &^= 0xff << parser.MarkerShift + p.Cmd |= int(b) << parser.MarkerShift + + case parser.CollectAction: + // Collect intermediate bytes + // we only store the last intermediate byte + p.Cmd &^= 0xff << parser.IntermedShift + p.Cmd |= int(b) << parser.IntermedShift + + case parser.ParamAction: + // Collect parameters + if p.ParamsLen >= len(p.Params) { + break + } + + if b >= '0' && b <= '9' { + if p.Params[p.ParamsLen] == parser.MissingParam { + p.Params[p.ParamsLen] = 0 + } + + p.Params[p.ParamsLen] *= 10 + p.Params[p.ParamsLen] += int(b - '0') + } + + if b == ':' { + p.Params[p.ParamsLen] |= parser.HasMoreFlag + } + + if b == ';' || b == ':' { + p.ParamsLen++ + if p.ParamsLen < len(p.Params) { + p.Params[p.ParamsLen] = parser.MissingParam + } + } + + case parser.StartAction: + if p.DataLen < 0 { + p.Data = make([]byte, 0) + } else { + p.DataLen = 0 + } + if p.State >= parser.DcsEntryState && p.State <= parser.DcsStringState { + // Collect the command byte for DCS + p.Cmd |= int(b) + } else { + p.Cmd = parser.MissingCommand + } + + case parser.PutAction: + switch p.State { + case parser.OscStringState: + if b == ';' && p.Cmd == parser.MissingCommand { + // Try to parse the command + datalen := len(p.Data) + if p.DataLen >= 0 { + datalen = p.DataLen + } + for i := 0; i < datalen; i++ { + d := p.Data[i] + if d < '0' || d > '9' { + break + } + if p.Cmd == parser.MissingCommand { + p.Cmd = 0 + } + p.Cmd *= 10 + p.Cmd += int(d - '0') + } + } + } + + if p.DataLen < 0 { + p.Data = append(p.Data, b) + } else { + if p.DataLen < len(p.Data) { + p.Data[p.DataLen] = b + p.DataLen++ + } + } + + case parser.DispatchAction: + // Increment the last parameter + if p.ParamsLen > 0 && p.ParamsLen < len(p.Params)-1 || + p.ParamsLen == 0 && len(p.Params) > 0 && p.Params[0] != parser.MissingParam { + p.ParamsLen++ + } + + if dispatcher == nil { + break + } + + var seq Sequence + data := p.Data + if p.DataLen >= 0 { + data = data[:p.DataLen] + } + switch p.State { + case parser.CsiEntryState, parser.CsiParamState, parser.CsiIntermediateState: + p.Cmd |= int(b) + seq = CsiSequence{Cmd: p.Cmd, Params: p.Params[:p.ParamsLen]} + case parser.EscapeState, parser.EscapeIntermediateState: + p.Cmd |= int(b) + seq = EscSequence(p.Cmd) + case parser.DcsEntryState, parser.DcsParamState, parser.DcsIntermediateState, parser.DcsStringState: + seq = DcsSequence{Cmd: p.Cmd, Params: p.Params[:p.ParamsLen], Data: data} + case parser.OscStringState: + seq = OscSequence{Cmd: p.Cmd, Data: data} + case parser.SosStringState: + seq = SosSequence{Data: data} + case parser.PmStringState: + seq = PmSequence{Data: data} + case parser.ApcStringState: + seq = ApcSequence{Data: data} + } + + dispatcher(seq) + } +} + +func utf8ByteLen(b byte) int { + if b <= 0b0111_1111 { // 0x00-0x7F + return 1 + } else if b >= 0b1100_0000 && b <= 0b1101_1111 { // 0xC0-0xDF + return 2 + } else if b >= 0b1110_0000 && b <= 0b1110_1111 { // 0xE0-0xEF + return 3 + } else if b >= 0b1111_0000 && b <= 0b1111_0111 { // 0xF0-0xF7 + return 4 + } + return -1 +} diff --git a/vendor/github.com/charmbracelet/x/ansi/parser/const.go b/vendor/github.com/charmbracelet/x/ansi/parser/const.go new file mode 100644 index 00000000..54b7383b --- /dev/null +++ b/vendor/github.com/charmbracelet/x/ansi/parser/const.go @@ -0,0 +1,78 @@ +package parser + +// Action is a DEC ANSI parser action. +type Action = byte + +// These are the actions that the parser can take. +const ( + NoneAction Action = iota + ClearAction + CollectAction + MarkerAction + DispatchAction + ExecuteAction + StartAction // Start of a data string + PutAction // Put into the data string + ParamAction + PrintAction + + IgnoreAction = NoneAction +) + +// nolint: unused +var ActionNames = []string{ + "NoneAction", + "ClearAction", + "CollectAction", + "MarkerAction", + "DispatchAction", + "ExecuteAction", + "StartAction", + "PutAction", + "ParamAction", + "PrintAction", +} + +// State is a DEC ANSI parser state. +type State = byte + +// These are the states that the parser can be in. +const ( + GroundState State = iota + CsiEntryState + CsiIntermediateState + CsiParamState + DcsEntryState + DcsIntermediateState + DcsParamState + DcsStringState + EscapeState + EscapeIntermediateState + OscStringState + SosStringState + PmStringState + ApcStringState + + // Utf8State is not part of the DEC ANSI standard. It is used to handle + // UTF-8 sequences. + Utf8State +) + +// nolint: unused +var StateNames = []string{ + "GroundState", + "CsiEntryState", + "CsiIntermediateState", + "CsiParamState", + "DcsEntryState", + "DcsIntermediateState", + "DcsParamState", + "DcsStringState", + "EscapeState", + "EscapeIntermediateState", + "OscStringState", + "SosStringState", + "PmStringState", + "ApcStringState", + "Utf8State", +} diff --git a/vendor/github.com/charmbracelet/x/ansi/parser/seq.go b/vendor/github.com/charmbracelet/x/ansi/parser/seq.go new file mode 100644 index 00000000..c99f1632 --- /dev/null +++ b/vendor/github.com/charmbracelet/x/ansi/parser/seq.go @@ -0,0 +1,136 @@ +package parser + +import "math" + +// Shift and masks for sequence parameters and intermediates. +const ( + MarkerShift = 8 + IntermedShift = 16 + CommandMask = 0xff + HasMoreFlag = math.MinInt32 + ParamMask = ^HasMoreFlag + MissingParam = ParamMask + MissingCommand = MissingParam + MaxParam = math.MaxUint16 // the maximum value a parameter can have +) + +const ( + // MaxParamsSize is the maximum number of parameters a sequence can have. + MaxParamsSize = 32 + + // DefaultParamValue is the default value used for missing parameters. + DefaultParamValue = 0 +) + +// Marker returns the marker byte of the sequence. +// This is always gonna be one of the following '<' '=' '>' '?' and in the +// range of 0x3C-0x3F. +// Zero is returned if the sequence does not have a marker. +func Marker(cmd int) int { + return (cmd >> MarkerShift) & CommandMask +} + +// Intermediate returns the intermediate byte of the sequence. +// An intermediate byte is in the range of 0x20-0x2F. This includes these +// characters from ' ', '!', '"', '#', '$', '%', '&', ”', '(', ')', '*', '+', +// ',', '-', '.', '/'. +// Zero is returned if the sequence does not have an intermediate byte. +func Intermediate(cmd int) int { + return (cmd >> IntermedShift) & CommandMask +} + +// Command returns the command byte of the CSI sequence. +func Command(cmd int) int { + return cmd & CommandMask +} + +// Param returns the parameter at the given index. +// It returns -1 if the parameter does not exist. +func Param(params []int, i int) int { + if len(params) == 0 || i < 0 || i >= len(params) { + return -1 + } + + p := params[i] & ParamMask + if p == MissingParam { + return -1 + } + + return p +} + +// HasMore returns true if the parameter has more sub-parameters. +func HasMore(params []int, i int) bool { + if len(params) == 0 || i >= len(params) { + return false + } + + return params[i]&HasMoreFlag != 0 +} + +// Subparams returns the sub-parameters of the given parameter. +// It returns nil if the parameter does not exist. +func Subparams(params []int, i int) []int { + if len(params) == 0 || i < 0 || i >= len(params) { + return nil + } + + // Count the number of parameters before the given parameter index. + var count int + var j int + for j = 0; j < len(params); j++ { + if count == i { + break + } + if !HasMore(params, j) { + count++ + } + } + + if count > i || j >= len(params) { + return nil + } + + var subs []int + for ; j < len(params); j++ { + if !HasMore(params, j) { + break + } + p := Param(params, j) + if p == -1 { + p = DefaultParamValue + } + subs = append(subs, p) + } + + p := Param(params, j) + if p == -1 { + p = DefaultParamValue + } + + return append(subs, p) +} + +// Len returns the number of parameters in the sequence. +// This will return the number of parameters in the sequence, excluding any +// sub-parameters. +func Len(params []int) int { + var n int + for i := 0; i < len(params); i++ { + if !HasMore(params, i) { + n++ + } + } + return n +} + +// Range iterates over the parameters of the sequence and calls the given +// function for each parameter. +// The function should return false to stop the iteration. +func Range(params []int, fn func(i int, param int, hasMore bool) bool) { + for i := 0; i < len(params); i++ { + if !fn(i, Param(params, i), HasMore(params, i)) { + break + } + } +} diff --git a/vendor/github.com/charmbracelet/x/ansi/parser/transition_table.go b/vendor/github.com/charmbracelet/x/ansi/parser/transition_table.go new file mode 100644 index 00000000..febde15d --- /dev/null +++ b/vendor/github.com/charmbracelet/x/ansi/parser/transition_table.go @@ -0,0 +1,269 @@ +package parser + +// Table values are generated like this: +// +// index: currentState << IndexStateShift | charCode +// value: action << TransitionActionShift | nextState +const ( + TransitionActionShift = 4 + TransitionStateMask = 15 + IndexStateShift = 8 + + // DefaultTableSize is the default size of the transition table. + DefaultTableSize = 4096 +) + +// Table is a DEC ANSI transition table. +var Table = GenerateTransitionTable() + +// TransitionTable is a DEC ANSI transition table. +// https://vt100.net/emu/dec_ansi_parser +type TransitionTable []byte + +// NewTransitionTable returns a new DEC ANSI transition table. +func NewTransitionTable(size int) TransitionTable { + if size <= 0 { + size = DefaultTableSize + } + return TransitionTable(make([]byte, size)) +} + +// SetDefault sets default transition. +func (t TransitionTable) SetDefault(action Action, state State) { + for i := 0; i < len(t); i++ { + t[i] = action<> TransitionActionShift +} + +// byte range macro +func r(start, end byte) []byte { + var a []byte + for i := int(start); i <= int(end); i++ { + a = append(a, byte(i)) + } + return a +} + +// GenerateTransitionTable generates a DEC ANSI transition table compatible +// with the VT500-series of terminals. This implementation includes a few +// modifications that include: +// - A new Utf8State is introduced to handle UTF8 sequences. +// - Osc and Dcs data accept UTF8 sequences by extending the printable range +// to 0xFF and 0xFE respectively. +// - We don't ignore 0x3A (':') when building Csi and Dcs parameters and +// instead use it to denote sub-parameters. +// - Support dispatching SosPmApc sequences. +func GenerateTransitionTable() TransitionTable { + table := NewTransitionTable(DefaultTableSize) + table.SetDefault(NoneAction, GroundState) + + // Anywhere + for _, state := range r(GroundState, Utf8State) { + // Anywhere -> Ground + table.AddMany([]byte{0x18, 0x1a, 0x99, 0x9a}, state, ExecuteAction, GroundState) + table.AddRange(0x80, 0x8F, state, ExecuteAction, GroundState) + table.AddRange(0x90, 0x97, state, ExecuteAction, GroundState) + table.AddOne(0x9C, state, IgnoreAction, GroundState) + // Anywhere -> Escape + table.AddOne(0x1B, state, ClearAction, EscapeState) + // Anywhere -> SosStringState + table.AddOne(0x98, state, StartAction, SosStringState) + // Anywhere -> PmStringState + table.AddOne(0x9E, state, StartAction, PmStringState) + // Anywhere -> ApcStringState + table.AddOne(0x9F, state, StartAction, ApcStringState) + // Anywhere -> CsiEntry + table.AddOne(0x9B, state, ClearAction, CsiEntryState) + // Anywhere -> DcsEntry + table.AddOne(0x90, state, ClearAction, DcsEntryState) + // Anywhere -> OscString + table.AddOne(0x9D, state, StartAction, OscStringState) + // Anywhere -> Utf8 + table.AddRange(0xC2, 0xDF, state, PrintAction, Utf8State) // UTF8 2 byte sequence + table.AddRange(0xE0, 0xEF, state, PrintAction, Utf8State) // UTF8 3 byte sequence + table.AddRange(0xF0, 0xF4, state, PrintAction, Utf8State) // UTF8 4 byte sequence + } + + // Ground + table.AddRange(0x00, 0x17, GroundState, ExecuteAction, GroundState) + table.AddOne(0x19, GroundState, ExecuteAction, GroundState) + table.AddRange(0x1C, 0x1F, GroundState, ExecuteAction, GroundState) + table.AddRange(0x20, 0x7F, GroundState, PrintAction, GroundState) + + // EscapeIntermediate + table.AddRange(0x00, 0x17, EscapeIntermediateState, ExecuteAction, EscapeIntermediateState) + table.AddOne(0x19, EscapeIntermediateState, ExecuteAction, EscapeIntermediateState) + table.AddRange(0x1C, 0x1F, EscapeIntermediateState, ExecuteAction, EscapeIntermediateState) + table.AddRange(0x20, 0x2F, EscapeIntermediateState, CollectAction, EscapeIntermediateState) + table.AddOne(0x7F, EscapeIntermediateState, IgnoreAction, EscapeIntermediateState) + // EscapeIntermediate -> Ground + table.AddRange(0x30, 0x7E, EscapeIntermediateState, DispatchAction, GroundState) + + // Escape + table.AddRange(0x00, 0x17, EscapeState, ExecuteAction, EscapeState) + table.AddOne(0x19, EscapeState, ExecuteAction, EscapeState) + table.AddRange(0x1C, 0x1F, EscapeState, ExecuteAction, EscapeState) + table.AddOne(0x7F, EscapeState, IgnoreAction, EscapeState) + // Escape -> Ground + table.AddRange(0x30, 0x4F, EscapeState, DispatchAction, GroundState) + table.AddRange(0x51, 0x57, EscapeState, DispatchAction, GroundState) + table.AddOne(0x59, EscapeState, DispatchAction, GroundState) + table.AddOne(0x5A, EscapeState, DispatchAction, GroundState) + table.AddOne(0x5C, EscapeState, DispatchAction, GroundState) + table.AddRange(0x60, 0x7E, EscapeState, DispatchAction, GroundState) + // Escape -> Escape_intermediate + table.AddRange(0x20, 0x2F, EscapeState, CollectAction, EscapeIntermediateState) + // Escape -> Sos_pm_apc_string + table.AddOne('X', EscapeState, StartAction, SosStringState) // SOS + table.AddOne('^', EscapeState, StartAction, PmStringState) // PM + table.AddOne('_', EscapeState, StartAction, ApcStringState) // APC + // Escape -> Dcs_entry + table.AddOne('P', EscapeState, ClearAction, DcsEntryState) + // Escape -> Csi_entry + table.AddOne('[', EscapeState, ClearAction, CsiEntryState) + // Escape -> Osc_string + table.AddOne(']', EscapeState, StartAction, OscStringState) + + // Sos_pm_apc_string + for _, state := range r(SosStringState, ApcStringState) { + table.AddRange(0x00, 0x17, state, PutAction, state) + table.AddOne(0x19, state, PutAction, state) + table.AddRange(0x1C, 0x1F, state, PutAction, state) + table.AddRange(0x20, 0x7F, state, PutAction, state) + // ESC, ST, CAN, and SUB terminate the sequence + table.AddOne(0x1B, state, DispatchAction, EscapeState) + table.AddOne(0x9C, state, DispatchAction, GroundState) + table.AddMany([]byte{0x18, 0x1A}, state, IgnoreAction, GroundState) + } + + // Dcs_entry + table.AddRange(0x00, 0x07, DcsEntryState, IgnoreAction, DcsEntryState) + table.AddRange(0x0E, 0x17, DcsEntryState, IgnoreAction, DcsEntryState) + table.AddOne(0x19, DcsEntryState, IgnoreAction, DcsEntryState) + table.AddRange(0x1C, 0x1F, DcsEntryState, IgnoreAction, DcsEntryState) + table.AddOne(0x7F, DcsEntryState, IgnoreAction, DcsEntryState) + // Dcs_entry -> Dcs_intermediate + table.AddRange(0x20, 0x2F, DcsEntryState, CollectAction, DcsIntermediateState) + // Dcs_entry -> Dcs_param + table.AddRange(0x30, 0x3B, DcsEntryState, ParamAction, DcsParamState) + table.AddRange(0x3C, 0x3F, DcsEntryState, MarkerAction, DcsParamState) + // Dcs_entry -> Dcs_passthrough + table.AddRange(0x08, 0x0D, DcsEntryState, PutAction, DcsStringState) // Follows ECMA-48 § 8.3.27 + // XXX: allows passing ESC (not a ECMA-48 standard) this to allow for + // passthrough of ANSI sequences like in Screen or Tmux passthrough mode. + table.AddOne(0x1B, DcsEntryState, PutAction, DcsStringState) + table.AddRange(0x40, 0x7E, DcsEntryState, StartAction, DcsStringState) + + // Dcs_intermediate + table.AddRange(0x00, 0x17, DcsIntermediateState, IgnoreAction, DcsIntermediateState) + table.AddOne(0x19, DcsIntermediateState, IgnoreAction, DcsIntermediateState) + table.AddRange(0x1C, 0x1F, DcsIntermediateState, IgnoreAction, DcsIntermediateState) + table.AddRange(0x20, 0x2F, DcsIntermediateState, CollectAction, DcsIntermediateState) + table.AddOne(0x7F, DcsIntermediateState, IgnoreAction, DcsIntermediateState) + // Dcs_intermediate -> Dcs_passthrough + table.AddRange(0x30, 0x3F, DcsIntermediateState, StartAction, DcsStringState) + table.AddRange(0x40, 0x7E, DcsIntermediateState, StartAction, DcsStringState) + + // Dcs_param + table.AddRange(0x00, 0x17, DcsParamState, IgnoreAction, DcsParamState) + table.AddOne(0x19, DcsParamState, IgnoreAction, DcsParamState) + table.AddRange(0x1C, 0x1F, DcsParamState, IgnoreAction, DcsParamState) + table.AddRange(0x30, 0x3B, DcsParamState, ParamAction, DcsParamState) + table.AddOne(0x7F, DcsParamState, IgnoreAction, DcsParamState) + table.AddRange(0x3C, 0x3F, DcsParamState, IgnoreAction, DcsParamState) + // Dcs_param -> Dcs_intermediate + table.AddRange(0x20, 0x2F, DcsParamState, CollectAction, DcsIntermediateState) + // Dcs_param -> Dcs_passthrough + table.AddRange(0x40, 0x7E, DcsParamState, StartAction, DcsStringState) + + // Dcs_passthrough + table.AddRange(0x00, 0x17, DcsStringState, PutAction, DcsStringState) + table.AddOne(0x19, DcsStringState, PutAction, DcsStringState) + table.AddRange(0x1C, 0x1F, DcsStringState, PutAction, DcsStringState) + table.AddRange(0x20, 0x7E, DcsStringState, PutAction, DcsStringState) + table.AddOne(0x7F, DcsStringState, IgnoreAction, DcsStringState) + table.AddRange(0x80, 0xFF, DcsStringState, PutAction, DcsStringState) // Allow Utf8 characters by extending the printable range from 0x7F to 0xFF + // ST, CAN, SUB, and ESC terminate the sequence + table.AddOne(0x1B, DcsStringState, DispatchAction, EscapeState) + table.AddOne(0x9C, DcsStringState, DispatchAction, GroundState) + table.AddMany([]byte{0x18, 0x1A}, DcsStringState, IgnoreAction, GroundState) + + // Csi_param + table.AddRange(0x00, 0x17, CsiParamState, ExecuteAction, CsiParamState) + table.AddOne(0x19, CsiParamState, ExecuteAction, CsiParamState) + table.AddRange(0x1C, 0x1F, CsiParamState, ExecuteAction, CsiParamState) + table.AddRange(0x30, 0x3B, CsiParamState, ParamAction, CsiParamState) + table.AddOne(0x7F, CsiParamState, IgnoreAction, CsiParamState) + table.AddRange(0x3C, 0x3F, CsiParamState, IgnoreAction, CsiParamState) + // Csi_param -> Ground + table.AddRange(0x40, 0x7E, CsiParamState, DispatchAction, GroundState) + // Csi_param -> Csi_intermediate + table.AddRange(0x20, 0x2F, CsiParamState, CollectAction, CsiIntermediateState) + + // Csi_intermediate + table.AddRange(0x00, 0x17, CsiIntermediateState, ExecuteAction, CsiIntermediateState) + table.AddOne(0x19, CsiIntermediateState, ExecuteAction, CsiIntermediateState) + table.AddRange(0x1C, 0x1F, CsiIntermediateState, ExecuteAction, CsiIntermediateState) + table.AddRange(0x20, 0x2F, CsiIntermediateState, CollectAction, CsiIntermediateState) + table.AddOne(0x7F, CsiIntermediateState, IgnoreAction, CsiIntermediateState) + // Csi_intermediate -> Ground + table.AddRange(0x40, 0x7E, CsiIntermediateState, DispatchAction, GroundState) + // Csi_intermediate -> Csi_ignore + table.AddRange(0x30, 0x3F, CsiIntermediateState, IgnoreAction, GroundState) + + // Csi_entry + table.AddRange(0x00, 0x17, CsiEntryState, ExecuteAction, CsiEntryState) + table.AddOne(0x19, CsiEntryState, ExecuteAction, CsiEntryState) + table.AddRange(0x1C, 0x1F, CsiEntryState, ExecuteAction, CsiEntryState) + table.AddOne(0x7F, CsiEntryState, IgnoreAction, CsiEntryState) + // Csi_entry -> Ground + table.AddRange(0x40, 0x7E, CsiEntryState, DispatchAction, GroundState) + // Csi_entry -> Csi_intermediate + table.AddRange(0x20, 0x2F, CsiEntryState, CollectAction, CsiIntermediateState) + // Csi_entry -> Csi_param + table.AddRange(0x30, 0x3B, CsiEntryState, ParamAction, CsiParamState) + table.AddRange(0x3C, 0x3F, CsiEntryState, MarkerAction, CsiParamState) + + // Osc_string + table.AddRange(0x00, 0x06, OscStringState, IgnoreAction, OscStringState) + table.AddRange(0x08, 0x17, OscStringState, IgnoreAction, OscStringState) + table.AddOne(0x19, OscStringState, IgnoreAction, OscStringState) + table.AddRange(0x1C, 0x1F, OscStringState, IgnoreAction, OscStringState) + table.AddRange(0x20, 0xFF, OscStringState, PutAction, OscStringState) // Allow Utf8 characters by extending the printable range from 0x7F to 0xFF + + // ST, CAN, SUB, ESC, and BEL terminate the sequence + table.AddOne(0x1B, OscStringState, DispatchAction, EscapeState) + table.AddOne(0x07, OscStringState, DispatchAction, GroundState) + table.AddOne(0x9C, OscStringState, DispatchAction, GroundState) + table.AddMany([]byte{0x18, 0x1A}, OscStringState, IgnoreAction, GroundState) + + return table +} diff --git a/vendor/github.com/charmbracelet/x/ansi/passthrough.go b/vendor/github.com/charmbracelet/x/ansi/passthrough.go new file mode 100644 index 00000000..14a74522 --- /dev/null +++ b/vendor/github.com/charmbracelet/x/ansi/passthrough.go @@ -0,0 +1,63 @@ +package ansi + +import ( + "bytes" +) + +// ScreenPassthrough wraps the given ANSI sequence in a DCS passthrough +// sequence to be sent to the outer terminal. This is used to send raw escape +// sequences to the outer terminal when running inside GNU Screen. +// +// DCS ST +// +// Note: Screen limits the length of string sequences to 768 bytes (since 2014). +// Use zero to indicate no limit, otherwise, this will chunk the returned +// string into limit sized chunks. +// +// See: https://www.gnu.org/software/screen/manual/screen.html#String-Escapes +// See: https://git.savannah.gnu.org/cgit/screen.git/tree/src/screen.h?id=c184c6ec27683ff1a860c45be5cf520d896fd2ef#n44 +func ScreenPassthrough(seq string, limit int) string { + var b bytes.Buffer + b.WriteString("\x1bP") + if limit > 0 { + for i := 0; i < len(seq); i += limit { + end := i + limit + if end > len(seq) { + end = len(seq) + } + b.WriteString(seq[i:end]) + if end < len(seq) { + b.WriteString("\x1b\\\x1bP") + } + } + } else { + b.WriteString(seq) + } + b.WriteString("\x1b\\") + return b.String() +} + +// TmuxPassthrough wraps the given ANSI sequence in a special DCS passthrough +// sequence to be sent to the outer terminal. This is used to send raw escape +// sequences to the outer terminal when running inside Tmux. +// +// DCS tmux ; ST +// +// Where is the given sequence in which all occurrences of ESC +// (0x1b) are doubled i.e. replaced with ESC ESC (0x1b 0x1b). +// +// Note: this needs the `allow-passthrough` option to be set to `on`. +// +// See: https://github.com/tmux/tmux/wiki/FAQ#what-is-the-passthrough-escape-sequence-and-how-do-i-use-it +func TmuxPassthrough(seq string) string { + var b bytes.Buffer + b.WriteString("\x1bPtmux;") + for i := 0; i < len(seq); i++ { + if seq[i] == ESC { + b.WriteByte(ESC) + } + b.WriteByte(seq[i]) + } + b.WriteString("\x1b\\") + return b.String() +} diff --git a/vendor/github.com/charmbracelet/x/ansi/screen.go b/vendor/github.com/charmbracelet/x/ansi/screen.go new file mode 100644 index 00000000..4909cf07 --- /dev/null +++ b/vendor/github.com/charmbracelet/x/ansi/screen.go @@ -0,0 +1,126 @@ +package ansi + +import "strconv" + +// EraseDisplay (ED) clears the screen or parts of the screen. Possible values: +// +// 0: Clear from cursor to end of screen. +// 1: Clear from cursor to beginning of the screen. +// 2: Clear entire screen (and moves cursor to upper left on DOS). +// 3: Clear entire screen and delete all lines saved in the scrollback buffer. +// +// CSI J +// +// See: https://vt100.net/docs/vt510-rm/ED.html +func EraseDisplay(n int) string { + if n < 0 { + n = 0 + } + return "\x1b[" + strconv.Itoa(n) + "J" +} + +// EraseDisplay constants. +// These are the possible values for the EraseDisplay function. +const ( + EraseDisplayRight = "\x1b[0J" + EraseDisplayLeft = "\x1b[1J" + EraseEntireDisplay = "\x1b[2J" +) + +// EraseLine (EL) clears the current line or parts of the line. Possible values: +// +// 0: Clear from cursor to end of line. +// 1: Clear from cursor to beginning of the line. +// 2: Clear entire line. +// +// The cursor position is not affected. +// +// CSI K +// +// See: https://vt100.net/docs/vt510-rm/EL.html +func EraseLine(n int) string { + if n < 0 { + n = 0 + } + return "\x1b[" + strconv.Itoa(n) + "K" +} + +// EraseLine constants. +// These are the possible values for the EraseLine function. +const ( + EraseLineRight = "\x1b[0K" + EraseLineLeft = "\x1b[1K" + EraseEntireLine = "\x1b[2K" +) + +// ScrollUp (SU) scrolls the screen up n lines. New lines are added at the +// bottom of the screen. +// +// CSI S +// +// See: https://vt100.net/docs/vt510-rm/SU.html +func ScrollUp(n int) string { + var s string + if n > 1 { + s = strconv.Itoa(n) + } + return "\x1b[" + s + "S" +} + +// ScrollDown (SD) scrolls the screen down n lines. New lines are added at the +// top of the screen. +// +// CSI T +// +// See: https://vt100.net/docs/vt510-rm/SD.html +func ScrollDown(n int) string { + var s string + if n > 1 { + s = strconv.Itoa(n) + } + return "\x1b[" + s + "T" +} + +// InsertLine (IL) inserts n blank lines at the current cursor position. +// Existing lines are moved down. +// +// CSI L +// +// See: https://vt100.net/docs/vt510-rm/IL.html +func InsertLine(n int) string { + var s string + if n > 1 { + s = strconv.Itoa(n) + } + return "\x1b[" + s + "L" +} + +// DeleteLine (DL) deletes n lines at the current cursor position. Existing +// lines are moved up. +// +// CSI M +// +// See: https://vt100.net/docs/vt510-rm/DL.html +func DeleteLine(n int) string { + var s string + if n > 1 { + s = strconv.Itoa(n) + } + return "\x1b[" + s + "M" +} + +// SetScrollingRegion (DECSTBM) sets the top and bottom margins for the scrolling +// region. The default is the entire screen. +// +// CSI ; r +// +// See: https://vt100.net/docs/vt510-rm/DECSTBM.html +func SetScrollingRegion(t, b int) string { + if t < 0 { + t = 0 + } + if b < 0 { + b = 0 + } + return "\x1b[" + strconv.Itoa(t) + ";" + strconv.Itoa(b) + "r" +} diff --git a/vendor/github.com/charmbracelet/x/ansi/sequence.go b/vendor/github.com/charmbracelet/x/ansi/sequence.go new file mode 100644 index 00000000..f294a229 --- /dev/null +++ b/vendor/github.com/charmbracelet/x/ansi/sequence.go @@ -0,0 +1,199 @@ +package ansi + +import ( + "bytes" + + "github.com/charmbracelet/x/ansi/parser" +) + +// Sequence represents an ANSI sequence. This can be a control sequence, escape +// sequence, a printable character, etc. +type Sequence interface { + // String returns the string representation of the sequence. + String() string + // Bytes returns the byte representation of the sequence. + Bytes() []byte + // Clone returns a copy of the sequence. + Clone() Sequence +} + +// Rune represents a printable character. +type Rune rune + +var _ Sequence = Rune(0) + +// Bytes implements Sequence. +func (r Rune) Bytes() []byte { + return []byte(string(r)) +} + +// String implements Sequence. +func (r Rune) String() string { + return string(r) +} + +// Clone implements Sequence. +func (r Rune) Clone() Sequence { + return r +} + +// ControlCode represents a control code character. This is a character that +// is not printable and is used to control the terminal. This would be a +// character in the C0 or C1 set in the range of 0x00-0x1F and 0x80-0x9F. +type ControlCode byte + +var _ Sequence = ControlCode(0) + +// Bytes implements Sequence. +func (c ControlCode) Bytes() []byte { + return []byte{byte(c)} +} + +// String implements Sequence. +func (c ControlCode) String() string { + return string(c) +} + +// Clone implements Sequence. +func (c ControlCode) Clone() Sequence { + return c +} + +// EscSequence represents an escape sequence. +type EscSequence int + +var _ Sequence = EscSequence(0) + +// buffer returns the buffer of the escape sequence. +func (e EscSequence) buffer() *bytes.Buffer { + var b bytes.Buffer + b.WriteByte('\x1b') + if i := parser.Intermediate(int(e)); i != 0 { + b.WriteByte(byte(i)) + } + b.WriteByte(byte(e.Command())) + return &b +} + +// Bytes implements Sequence. +func (e EscSequence) Bytes() []byte { + return e.buffer().Bytes() +} + +// String implements Sequence. +func (e EscSequence) String() string { + return e.buffer().String() +} + +// Clone implements Sequence. +func (e EscSequence) Clone() Sequence { + return e +} + +// Command returns the command byte of the escape sequence. +func (e EscSequence) Command() int { + return parser.Command(int(e)) +} + +// Intermediate returns the intermediate byte of the escape sequence. +func (e EscSequence) Intermediate() int { + return parser.Intermediate(int(e)) +} + +// SosSequence represents a SOS sequence. +type SosSequence struct { + // Data contains the raw data of the sequence. + Data []byte +} + +var _ Sequence = &SosSequence{} + +// Clone implements Sequence. +func (s SosSequence) Clone() Sequence { + return SosSequence{Data: append([]byte(nil), s.Data...)} +} + +// Bytes implements Sequence. +func (s SosSequence) Bytes() []byte { + return s.buffer().Bytes() +} + +// String implements Sequence. +func (s SosSequence) String() string { + return s.buffer().String() +} + +func (s SosSequence) buffer() *bytes.Buffer { + var b bytes.Buffer + b.WriteByte('\x1b') + b.WriteByte('X') + b.Write(s.Data) + b.WriteString("\x1b\\") + return &b +} + +// PmSequence represents a PM sequence. +type PmSequence struct { + // Data contains the raw data of the sequence. + Data []byte +} + +var _ Sequence = &PmSequence{} + +// Clone implements Sequence. +func (s PmSequence) Clone() Sequence { + return PmSequence{Data: append([]byte(nil), s.Data...)} +} + +// Bytes implements Sequence. +func (s PmSequence) Bytes() []byte { + return s.buffer().Bytes() +} + +// String implements Sequence. +func (s PmSequence) String() string { + return s.buffer().String() +} + +// buffer returns the buffer of the PM sequence. +func (s PmSequence) buffer() *bytes.Buffer { + var b bytes.Buffer + b.WriteByte('\x1b') + b.WriteByte('^') + b.Write(s.Data) + b.WriteString("\x1b\\") + return &b +} + +// ApcSequence represents an APC sequence. +type ApcSequence struct { + // Data contains the raw data of the sequence. + Data []byte +} + +var _ Sequence = &ApcSequence{} + +// Clone implements Sequence. +func (s ApcSequence) Clone() Sequence { + return ApcSequence{Data: append([]byte(nil), s.Data...)} +} + +// Bytes implements Sequence. +func (s ApcSequence) Bytes() []byte { + return s.buffer().Bytes() +} + +// String implements Sequence. +func (s ApcSequence) String() string { + return s.buffer().String() +} + +// buffer returns the buffer of the APC sequence. +func (s ApcSequence) buffer() *bytes.Buffer { + var b bytes.Buffer + b.WriteByte('\x1b') + b.WriteByte('_') + b.Write(s.Data) + b.WriteString("\x1b\\") + return &b +} diff --git a/vendor/github.com/charmbracelet/x/ansi/style.go b/vendor/github.com/charmbracelet/x/ansi/style.go new file mode 100644 index 00000000..96e3c532 --- /dev/null +++ b/vendor/github.com/charmbracelet/x/ansi/style.go @@ -0,0 +1,296 @@ +package ansi + +import ( + "image/color" + "strconv" + "strings" +) + +// ResetStyle is a SGR (Select Graphic Rendition) style sequence that resets +// all attributes. +// See: https://en.wikipedia.org/wiki/ANSI_escape_code#SGR_(Select_Graphic_Rendition)_parameters +const ResetStyle = "\x1b[m" + +// Attr is a SGR (Select Graphic Rendition) style attribute. +type Attr = string + +// Style represents an ANSI SGR (Select Graphic Rendition) style. +type Style []Attr + +// String returns the ANSI SGR (Select Graphic Rendition) style sequence for +// the given style. +func (s Style) String() string { + if len(s) == 0 { + return ResetStyle + } + return "\x1b[" + strings.Join(s, ";") + "m" +} + +// Styled returns a styled string with the given style applied. +func (s Style) Styled(str string) string { + if len(s) == 0 { + return str + } + return s.String() + str + ResetStyle +} + +// Reset appends the reset style attribute to the style. +func (s Style) Reset() Style { + return append(s, ResetAttr) +} + +// Bold appends the bold style attribute to the style. +func (s Style) Bold() Style { + return append(s, BoldAttr) +} + +// Faint appends the faint style attribute to the style. +func (s Style) Faint() Style { + return append(s, FaintAttr) +} + +// Italic appends the italic style attribute to the style. +func (s Style) Italic() Style { + return append(s, ItalicAttr) +} + +// Underline appends the underline style attribute to the style. +func (s Style) Underline() Style { + return append(s, UnderlineAttr) +} + +// DoubleUnderline appends the double underline style attribute to the style. +func (s Style) DoubleUnderline() Style { + return append(s, DoubleUnderlineAttr) +} + +// CurlyUnderline appends the curly underline style attribute to the style. +func (s Style) CurlyUnderline() Style { + return append(s, CurlyUnderlineAttr) +} + +// DottedUnderline appends the dotted underline style attribute to the style. +func (s Style) DottedUnderline() Style { + return append(s, DottedUnderlineAttr) +} + +// DashedUnderline appends the dashed underline style attribute to the style. +func (s Style) DashedUnderline() Style { + return append(s, DashedUnderlineAttr) +} + +// SlowBlink appends the slow blink style attribute to the style. +func (s Style) SlowBlink() Style { + return append(s, SlowBlinkAttr) +} + +// RapidBlink appends the rapid blink style attribute to the style. +func (s Style) RapidBlink() Style { + return append(s, RapidBlinkAttr) +} + +// Reverse appends the reverse style attribute to the style. +func (s Style) Reverse() Style { + return append(s, ReverseAttr) +} + +// Conceal appends the conceal style attribute to the style. +func (s Style) Conceal() Style { + return append(s, ConcealAttr) +} + +// Strikethrough appends the strikethrough style attribute to the style. +func (s Style) Strikethrough() Style { + return append(s, StrikethroughAttr) +} + +// NoBold appends the no bold style attribute to the style. +func (s Style) NoBold() Style { + return append(s, NoBoldAttr) +} + +// NormalIntensity appends the normal intensity style attribute to the style. +func (s Style) NormalIntensity() Style { + return append(s, NormalIntensityAttr) +} + +// NoItalic appends the no italic style attribute to the style. +func (s Style) NoItalic() Style { + return append(s, NoItalicAttr) +} + +// NoUnderline appends the no underline style attribute to the style. +func (s Style) NoUnderline() Style { + return append(s, NoUnderlineAttr) +} + +// NoBlink appends the no blink style attribute to the style. +func (s Style) NoBlink() Style { + return append(s, NoBlinkAttr) +} + +// NoReverse appends the no reverse style attribute to the style. +func (s Style) NoReverse() Style { + return append(s, NoReverseAttr) +} + +// NoConceal appends the no conceal style attribute to the style. +func (s Style) NoConceal() Style { + return append(s, NoConcealAttr) +} + +// NoStrikethrough appends the no strikethrough style attribute to the style. +func (s Style) NoStrikethrough() Style { + return append(s, NoStrikethroughAttr) +} + +// DefaultForegroundColor appends the default foreground color style attribute to the style. +func (s Style) DefaultForegroundColor() Style { + return append(s, DefaultForegroundColorAttr) +} + +// DefaultBackgroundColor appends the default background color style attribute to the style. +func (s Style) DefaultBackgroundColor() Style { + return append(s, DefaultBackgroundColorAttr) +} + +// DefaultUnderlineColor appends the default underline color style attribute to the style. +func (s Style) DefaultUnderlineColor() Style { + return append(s, DefaultUnderlineColorAttr) +} + +// ForegroundColor appends the foreground color style attribute to the style. +func (s Style) ForegroundColor(c Color) Style { + return append(s, ForegroundColorAttr(c)) +} + +// BackgroundColor appends the background color style attribute to the style. +func (s Style) BackgroundColor(c Color) Style { + return append(s, BackgroundColorAttr(c)) +} + +// UnderlineColor appends the underline color style attribute to the style. +func (s Style) UnderlineColor(c Color) Style { + return append(s, UnderlineColorAttr(c)) +} + +// SGR (Select Graphic Rendition) style attributes. +// See: https://en.wikipedia.org/wiki/ANSI_escape_code#SGR_(Select_Graphic_Rendition)_parameters +const ( + ResetAttr Attr = "0" + BoldAttr Attr = "1" + FaintAttr Attr = "2" + ItalicAttr Attr = "3" + UnderlineAttr Attr = "4" + DoubleUnderlineAttr Attr = "4:2" + CurlyUnderlineAttr Attr = "4:3" + DottedUnderlineAttr Attr = "4:4" + DashedUnderlineAttr Attr = "4:5" + SlowBlinkAttr Attr = "5" + RapidBlinkAttr Attr = "6" + ReverseAttr Attr = "7" + ConcealAttr Attr = "8" + StrikethroughAttr Attr = "9" + NoBoldAttr Attr = "21" // Some terminals treat this as double underline. + NormalIntensityAttr Attr = "22" + NoItalicAttr Attr = "23" + NoUnderlineAttr Attr = "24" + NoBlinkAttr Attr = "25" + NoReverseAttr Attr = "27" + NoConcealAttr Attr = "28" + NoStrikethroughAttr Attr = "29" + DefaultForegroundColorAttr Attr = "39" + DefaultBackgroundColorAttr Attr = "49" + DefaultUnderlineColorAttr Attr = "59" +) + +// ForegroundColorAttr returns the style SGR attribute for the given foreground +// color. +// See: https://en.wikipedia.org/wiki/ANSI_escape_code#SGR_(Select_Graphic_Rendition)_parameters +func ForegroundColorAttr(c Color) Attr { + switch c := c.(type) { + case BasicColor: + // 3-bit or 4-bit ANSI foreground + // "3" or "9" where n is the color number from 0 to 7 + if c < 8 { + return "3" + string('0'+c) + } else if c < 16 { + return "9" + string('0'+c-8) + } + case ExtendedColor: + // 256-color ANSI foreground + // "38;5;" + return "38;5;" + strconv.FormatUint(uint64(c), 10) + case TrueColor, color.Color: + // 24-bit "true color" foreground + // "38;2;;;" + r, g, b, _ := c.RGBA() + return "38;2;" + + strconv.FormatUint(uint64(shift(r)), 10) + ";" + + strconv.FormatUint(uint64(shift(g)), 10) + ";" + + strconv.FormatUint(uint64(shift(b)), 10) + } + return DefaultForegroundColorAttr +} + +// BackgroundColorAttr returns the style SGR attribute for the given background +// color. +// See: https://en.wikipedia.org/wiki/ANSI_escape_code#SGR_(Select_Graphic_Rendition)_parameters +func BackgroundColorAttr(c Color) Attr { + switch c := c.(type) { + case BasicColor: + // 3-bit or 4-bit ANSI foreground + // "4" or "10" where n is the color number from 0 to 7 + if c < 8 { + return "4" + string('0'+c) + } else { + return "10" + string('0'+c-8) + } + case ExtendedColor: + // 256-color ANSI foreground + // "48;5;" + return "48;5;" + strconv.FormatUint(uint64(c), 10) + case TrueColor, color.Color: + // 24-bit "true color" foreground + // "38;2;;;" + r, g, b, _ := c.RGBA() + return "48;2;" + + strconv.FormatUint(uint64(shift(r)), 10) + ";" + + strconv.FormatUint(uint64(shift(g)), 10) + ";" + + strconv.FormatUint(uint64(shift(b)), 10) + } + return DefaultBackgroundColorAttr +} + +// UnderlineColorAttr returns the style SGR attribute for the given underline +// color. +// See: https://en.wikipedia.org/wiki/ANSI_escape_code#SGR_(Select_Graphic_Rendition)_parameters +func UnderlineColorAttr(c Color) Attr { + switch c := c.(type) { + // NOTE: we can't use 3-bit and 4-bit ANSI color codes with underline + // color, use 256-color instead. + // + // 256-color ANSI underline color + // "58;5;" + case BasicColor: + return "58;5;" + strconv.FormatUint(uint64(c), 10) + case ExtendedColor: + return "58;5;" + strconv.FormatUint(uint64(c), 10) + case TrueColor, color.Color: + // 24-bit "true color" foreground + // "38;2;;;" + r, g, b, _ := c.RGBA() + return "58;2;" + + strconv.FormatUint(uint64(shift(r)), 10) + ";" + + strconv.FormatUint(uint64(shift(g)), 10) + ";" + + strconv.FormatUint(uint64(shift(b)), 10) + } + return DefaultUnderlineColorAttr +} + +func shift(v uint32) uint32 { + if v > 0xff { + return v >> 8 + } + return v +} diff --git a/vendor/github.com/charmbracelet/x/ansi/termcap.go b/vendor/github.com/charmbracelet/x/ansi/termcap.go new file mode 100644 index 00000000..1dfc52a6 --- /dev/null +++ b/vendor/github.com/charmbracelet/x/ansi/termcap.go @@ -0,0 +1,31 @@ +package ansi + +import ( + "encoding/hex" + "strings" +) + +// RequestTermcap (XTGETTCAP) requests Termcap/Terminfo strings. +// +// DCS + q ST +// +// Where is a list of Termcap/Terminfo capabilities, encoded in 2-digit +// hexadecimals, separated by semicolons. +// +// See: https://man7.org/linux/man-pages/man5/terminfo.5.html +// See: https://invisible-island.net/xterm/ctlseqs/ctlseqs.html#h3-Operating-System-Commands +func RequestTermcap(caps ...string) string { + if len(caps) == 0 { + return "" + } + + s := "\x1bP+q" + for i, c := range caps { + if i > 0 { + s += ";" + } + s += strings.ToUpper(hex.EncodeToString([]byte(c))) + } + + return s + "\x1b\\" +} diff --git a/vendor/github.com/charmbracelet/x/ansi/title.go b/vendor/github.com/charmbracelet/x/ansi/title.go new file mode 100644 index 00000000..8fd8bf98 --- /dev/null +++ b/vendor/github.com/charmbracelet/x/ansi/title.go @@ -0,0 +1,32 @@ +package ansi + +// SetIconNameWindowTitle returns a sequence for setting the icon name and +// window title. +// +// OSC 0 ; title ST +// OSC 0 ; title BEL +// +// See: https://invisible-island.net/xterm/ctlseqs/ctlseqs.html#h2-Operating-System-Commands +func SetIconNameWindowTitle(s string) string { + return "\x1b]0;" + s + "\x07" +} + +// SetIconName returns a sequence for setting the icon name. +// +// OSC 1 ; title ST +// OSC 1 ; title BEL +// +// See: https://invisible-island.net/xterm/ctlseqs/ctlseqs.html#h2-Operating-System-Commands +func SetIconName(s string) string { + return "\x1b]1;" + s + "\x07" +} + +// SetWindowTitle returns a sequence for setting the window title. +// +// OSC 2 ; title ST +// OSC 2 ; title BEL +// +// See: https://invisible-island.net/xterm/ctlseqs/ctlseqs.html#h2-Operating-System-Commands +func SetWindowTitle(s string) string { + return "\x1b]2;" + s + "\x07" +} diff --git a/vendor/github.com/charmbracelet/x/ansi/truncate.go b/vendor/github.com/charmbracelet/x/ansi/truncate.go new file mode 100644 index 00000000..03d0a122 --- /dev/null +++ b/vendor/github.com/charmbracelet/x/ansi/truncate.go @@ -0,0 +1,108 @@ +package ansi + +import ( + "bytes" + + "github.com/charmbracelet/x/ansi/parser" + "github.com/rivo/uniseg" +) + +// Truncate truncates a string to a given length, adding a tail to the +// end if the string is longer than the given length. +// This function is aware of ANSI escape codes and will not break them, and +// accounts for wide-characters (such as East Asians and emojis). +func Truncate(s string, length int, tail string) string { + tw := StringWidth(tail) + length -= tw + if length < 0 { + return "" + } + + var cluster []byte + var buf bytes.Buffer + curWidth := 0 + ignoring := false + gstate := -1 + pstate := parser.GroundState // initial state + b := []byte(s) + i := 0 + + // Here we iterate over the bytes of the string and collect printable + // characters and runes. We also keep track of the width of the string + // in cells. + // Once we reach the given length, we start ignoring characters and only + // collect ANSI escape codes until we reach the end of string. + for i < len(b) { + state, action := parser.Table.Transition(pstate, b[i]) + + switch action { + case parser.PrintAction: + if utf8ByteLen(b[i]) > 1 { + // This action happens when we transition to the Utf8State. + var width int + cluster, _, width, gstate = uniseg.FirstGraphemeCluster(b[i:], gstate) + + // increment the index by the length of the cluster + i += len(cluster) + + // Are we ignoring? Skip to the next byte + if ignoring { + continue + } + + // Is this gonna be too wide? + // If so write the tail and stop collecting. + if curWidth+width > length && !ignoring { + ignoring = true + buf.WriteString(tail) + } + + if curWidth+width > length { + continue + } + + curWidth += width + for _, r := range cluster { + buf.WriteByte(r) + } + + gstate = -1 // reset grapheme state otherwise, width calculation might be off + // Done collecting, now we're back in the ground state. + pstate = parser.GroundState + continue + } + + // Is this gonna be too wide? + // If so write the tail and stop collecting. + if curWidth >= length && !ignoring { + ignoring = true + buf.WriteString(tail) + } + + // Skip to the next byte if we're ignoring + if ignoring { + i++ + continue + } + + // collects printable ASCII + curWidth++ + fallthrough + default: + buf.WriteByte(b[i]) + i++ + } + + // Transition to the next state. + pstate = state + + // Once we reach the given length, we start ignoring runes and write + // the tail to the buffer. + if curWidth > length && !ignoring { + ignoring = true + buf.WriteString(tail) + } + } + + return buf.String() +} diff --git a/vendor/github.com/charmbracelet/x/ansi/util.go b/vendor/github.com/charmbracelet/x/ansi/util.go new file mode 100644 index 00000000..767093f9 --- /dev/null +++ b/vendor/github.com/charmbracelet/x/ansi/util.go @@ -0,0 +1,29 @@ +package ansi + +import ( + "fmt" + "image/color" +) + +// colorToHexString returns a hex string representation of a color. +func colorToHexString(c color.Color) string { + if c == nil { + return "" + } + shift := func(v uint32) uint32 { + if v > 0xff { + return v >> 8 + } + return v + } + r, g, b, _ := c.RGBA() + r, g, b = shift(r), shift(g), shift(b) + return fmt.Sprintf("#%02x%02x%02x", r, g, b) +} + +// rgbToHex converts red, green, and blue values to a hexadecimal value. +// +// hex := rgbToHex(0, 0, 255) // 0x0000FF +func rgbToHex(r, g, b uint32) uint32 { + return r<<16 + g<<8 + b +} diff --git a/vendor/github.com/charmbracelet/x/ansi/width.go b/vendor/github.com/charmbracelet/x/ansi/width.go new file mode 100644 index 00000000..4da3959c --- /dev/null +++ b/vendor/github.com/charmbracelet/x/ansi/width.go @@ -0,0 +1,73 @@ +package ansi + +import ( + "bytes" + + "github.com/charmbracelet/x/ansi/parser" + "github.com/rivo/uniseg" +) + +// Strip removes ANSI escape codes from a string. +func Strip(s string) string { + var ( + buf bytes.Buffer // buffer for collecting printable characters + ri int // rune index + rw int // rune width + pstate = parser.GroundState // initial state + ) + + // This implements a subset of the Parser to only collect runes and + // printable characters. + for i := 0; i < len(s); i++ { + var state, action byte + if pstate != parser.Utf8State { + state, action = parser.Table.Transition(pstate, s[i]) + } + + switch { + case pstate == parser.Utf8State: + // During this state, collect rw bytes to form a valid rune in the + // buffer. After getting all the rune bytes into the buffer, + // transition to GroundState and reset the counters. + buf.WriteByte(s[i]) + ri++ + if ri < rw { + continue + } + pstate = parser.GroundState + ri = 0 + rw = 0 + case action == parser.PrintAction: + // This action happens when we transition to the Utf8State. + if w := utf8ByteLen(s[i]); w > 1 { + rw = w + buf.WriteByte(s[i]) + ri++ + break + } + fallthrough + case action == parser.ExecuteAction: + // collects printable ASCII and non-printable characters + buf.WriteByte(s[i]) + } + + // Transition to the next state. + // The Utf8State is managed separately above. + if pstate != parser.Utf8State { + pstate = state + } + } + + return buf.String() +} + +// StringWidth returns the width of a string in cells. This is the number of +// cells that the string will occupy when printed in a terminal. ANSI escape +// codes are ignored and wide characters (such as East Asians and emojis) are +// accounted for. +func StringWidth(s string) int { + if s == "" { + return 0 + } + return uniseg.StringWidth(Strip(s)) +} diff --git a/vendor/github.com/charmbracelet/x/ansi/wrap.go b/vendor/github.com/charmbracelet/x/ansi/wrap.go new file mode 100644 index 00000000..9aa9b640 --- /dev/null +++ b/vendor/github.com/charmbracelet/x/ansi/wrap.go @@ -0,0 +1,406 @@ +package ansi + +import ( + "bytes" + "unicode" + "unicode/utf8" + + "github.com/charmbracelet/x/ansi/parser" + "github.com/rivo/uniseg" +) + +// nbsp is a non-breaking space +const nbsp = 0xA0 + +// Hardwrap wraps a string or a block of text to a given line length, breaking +// word boundaries. This will preserve ANSI escape codes and will account for +// wide-characters in the string. +// When preserveSpace is true, spaces at the beginning of a line will be +// preserved. +func Hardwrap(s string, limit int, preserveSpace bool) string { + if limit < 1 { + return s + } + + var ( + cluster []byte + buf bytes.Buffer + curWidth int + forceNewline bool + gstate = -1 + pstate = parser.GroundState // initial state + b = []byte(s) + ) + + addNewline := func() { + buf.WriteByte('\n') + curWidth = 0 + } + + i := 0 + for i < len(b) { + state, action := parser.Table.Transition(pstate, b[i]) + + switch action { + case parser.PrintAction: + if utf8ByteLen(b[i]) > 1 { + var width int + cluster, _, width, gstate = uniseg.FirstGraphemeCluster(b[i:], gstate) + i += len(cluster) + + if curWidth+width > limit { + addNewline() + } + if !preserveSpace && curWidth == 0 && len(cluster) <= 4 { + // Skip spaces at the beginning of a line + if r, _ := utf8.DecodeRune(cluster); r != utf8.RuneError && unicode.IsSpace(r) { + pstate = parser.GroundState + continue + } + } + + buf.Write(cluster) + curWidth += width + gstate = -1 // reset grapheme state otherwise, width calculation might be off + pstate = parser.GroundState + continue + } + fallthrough + case parser.ExecuteAction: + if b[i] == '\n' { + addNewline() + forceNewline = false + break + } + + if curWidth+1 > limit { + addNewline() + forceNewline = true + } + + // Skip spaces at the beginning of a line + if curWidth == 0 { + if !preserveSpace && forceNewline && unicode.IsSpace(rune(b[i])) { + break + } + forceNewline = false + } + + buf.WriteByte(b[i]) + curWidth++ + default: + buf.WriteByte(b[i]) + } + + // We manage the UTF8 state separately manually above. + if pstate != parser.Utf8State { + pstate = state + } + i++ + } + + return buf.String() +} + +// Wordwrap wraps a string or a block of text to a given line length, not +// breaking word boundaries. This will preserve ANSI escape codes and will +// account for wide-characters in the string. +// The breakpoints string is a list of characters that are considered +// breakpoints for word wrapping. A hyphen (-) is always considered a +// breakpoint. +// +// Note: breakpoints must be a string of 1-cell wide rune characters. +func Wordwrap(s string, limit int, breakpoints string) string { + if limit < 1 { + return s + } + + var ( + cluster []byte + buf bytes.Buffer + word bytes.Buffer + space bytes.Buffer + curWidth int + wordLen int + gstate = -1 + pstate = parser.GroundState // initial state + b = []byte(s) + ) + + addSpace := func() { + curWidth += space.Len() + buf.Write(space.Bytes()) + space.Reset() + } + + addWord := func() { + if word.Len() == 0 { + return + } + + addSpace() + curWidth += wordLen + buf.Write(word.Bytes()) + word.Reset() + wordLen = 0 + } + + addNewline := func() { + buf.WriteByte('\n') + curWidth = 0 + space.Reset() + } + + i := 0 + for i < len(b) { + state, action := parser.Table.Transition(pstate, b[i]) + + switch action { + case parser.PrintAction: + if utf8ByteLen(b[i]) > 1 { + var width int + cluster, _, width, gstate = uniseg.FirstGraphemeCluster(b[i:], gstate) + i += len(cluster) + + r, _ := utf8.DecodeRune(cluster) + if r != utf8.RuneError && unicode.IsSpace(r) && r != nbsp { + addWord() + space.WriteRune(r) + } else if bytes.ContainsAny(cluster, breakpoints) { + addSpace() + addWord() + buf.Write(cluster) + curWidth++ + } else { + word.Write(cluster) + wordLen += width + if curWidth+space.Len()+wordLen > limit && + wordLen < limit { + addNewline() + } + } + + pstate = parser.GroundState + continue + } + fallthrough + case parser.ExecuteAction: + r := rune(b[i]) + switch { + case r == '\n': + if wordLen == 0 { + if curWidth+space.Len() > limit { + curWidth = 0 + } else { + buf.Write(space.Bytes()) + } + space.Reset() + } + + addWord() + addNewline() + case unicode.IsSpace(r): + addWord() + space.WriteByte(b[i]) + case r == '-': + fallthrough + case runeContainsAny(r, breakpoints): + addSpace() + addWord() + buf.WriteByte(b[i]) + curWidth++ + default: + word.WriteByte(b[i]) + wordLen++ + if curWidth+space.Len()+wordLen > limit && + wordLen < limit { + addNewline() + } + } + + default: + word.WriteByte(b[i]) + } + + // We manage the UTF8 state separately manually above. + if pstate != parser.Utf8State { + pstate = state + } + i++ + } + + addWord() + + return buf.String() +} + +// Wrap wraps a string or a block of text to a given line length, breaking word +// boundaries if necessary. This will preserve ANSI escape codes and will +// account for wide-characters in the string. The breakpoints string is a list +// of characters that are considered breakpoints for word wrapping. A hyphen +// (-) is always considered a breakpoint. +// +// Note: breakpoints must be a string of 1-cell wide rune characters. +func Wrap(s string, limit int, breakpoints string) string { + if limit < 1 { + return s + } + + var ( + cluster []byte + buf bytes.Buffer + word bytes.Buffer + space bytes.Buffer + curWidth int // written width of the line + wordLen int // word buffer len without ANSI escape codes + gstate = -1 + pstate = parser.GroundState // initial state + b = []byte(s) + ) + + addSpace := func() { + curWidth += space.Len() + buf.Write(space.Bytes()) + space.Reset() + } + + addWord := func() { + if word.Len() == 0 { + return + } + + addSpace() + curWidth += wordLen + buf.Write(word.Bytes()) + word.Reset() + wordLen = 0 + } + + addNewline := func() { + buf.WriteByte('\n') + curWidth = 0 + space.Reset() + } + + i := 0 + for i < len(b) { + state, action := parser.Table.Transition(pstate, b[i]) + + switch action { + case parser.PrintAction: + if utf8ByteLen(b[i]) > 1 { + var width int + cluster, _, width, gstate = uniseg.FirstGraphemeCluster(b[i:], gstate) + i += len(cluster) + + r, _ := utf8.DecodeRune(cluster) + switch { + case r != utf8.RuneError && unicode.IsSpace(r) && r != nbsp: // nbsp is a non-breaking space + addWord() + space.WriteRune(r) + case bytes.ContainsAny(cluster, breakpoints): + addSpace() + if curWidth+wordLen+width > limit { + word.Write(cluster) + wordLen += width + } else { + addWord() + buf.Write(cluster) + curWidth += width + } + default: + if wordLen+width > limit { + // Hardwrap the word if it's too long + addWord() + } + + word.Write(cluster) + wordLen += width + + if curWidth+wordLen+space.Len() > limit { + addNewline() + } + } + + pstate = parser.GroundState + continue + } + + fallthrough + case parser.ExecuteAction: + switch r := rune(b[i]); { + case r == '\n': + if wordLen == 0 { + if curWidth+space.Len() > limit { + curWidth = 0 + } else { + // preserve whitespaces + buf.Write(space.Bytes()) + } + space.Reset() + } + + addWord() + addNewline() + case unicode.IsSpace(r): + addWord() + space.WriteRune(r) + case r == '-': + fallthrough + case runeContainsAny(r, breakpoints): + addSpace() + if curWidth+wordLen >= limit { + // We can't fit the breakpoint in the current line, treat + // it as part of the word. + word.WriteRune(r) + wordLen++ + } else { + addWord() + buf.WriteRune(r) + curWidth++ + } + default: + word.WriteRune(r) + wordLen++ + + if wordLen == limit { + // Hardwrap the word if it's too long + addWord() + } + + if curWidth+wordLen+space.Len() > limit { + addNewline() + } + } + + default: + word.WriteByte(b[i]) + } + + // We manage the UTF8 state separately manually above. + if pstate != parser.Utf8State { + pstate = state + } + i++ + } + + if word.Len() != 0 { + // Preserve ANSI wrapped spaces at the end of string + if curWidth+space.Len() > limit { + buf.WriteByte('\n') + } + addSpace() + } + buf.Write(word.Bytes()) + + return buf.String() +} + +func runeContainsAny(r rune, s string) bool { + for _, c := range s { + if c == r { + return true + } + } + return false +} diff --git a/vendor/github.com/charmbracelet/x/ansi/xterm.go b/vendor/github.com/charmbracelet/x/ansi/xterm.go new file mode 100644 index 00000000..e2eb10a1 --- /dev/null +++ b/vendor/github.com/charmbracelet/x/ansi/xterm.go @@ -0,0 +1,33 @@ +package ansi + +// DisableModifyOtherKeys disables the modifyOtherKeys mode. +// +// CSI > 4 ; 0 m +// +// See: https://invisible-island.net/xterm/ctlseqs/ctlseqs.html#h3-Functions-using-CSI-_-ordered-by-the-final-character_s_ +// See: https://invisible-island.net/xterm/manpage/xterm.html#VT100-Widget-Resources:modifyOtherKeys +const DisableModifyOtherKeys = "\x1b[>4;0m" + +// EnableModifyOtherKeys1 enables the modifyOtherKeys mode 1. +// +// CSI > 4 ; 1 m +// +// See: https://invisible-island.net/xterm/ctlseqs/ctlseqs.html#h3-Functions-using-CSI-_-ordered-by-the-final-character_s_ +// See: https://invisible-island.net/xterm/manpage/xterm.html#VT100-Widget-Resources:modifyOtherKeys +const EnableModifyOtherKeys1 = "\x1b[>4;1m" + +// EnableModifyOtherKeys2 enables the modifyOtherKeys mode 2. +// +// CSI > 4 ; 2 m +// +// See: https://invisible-island.net/xterm/ctlseqs/ctlseqs.html#h3-Functions-using-CSI-_-ordered-by-the-final-character_s_ +// See: https://invisible-island.net/xterm/manpage/xterm.html#VT100-Widget-Resources:modifyOtherKeys +const EnableModifyOtherKeys2 = "\x1b[>4;2m" + +// RequestModifyOtherKeys requests the modifyOtherKeys mode. +// +// CSI ? 4 m +// +// See: https://invisible-island.net/xterm/ctlseqs/ctlseqs.html#h3-Functions-using-CSI-_-ordered-by-the-final-character_s_ +// See: https://invisible-island.net/xterm/manpage/xterm.html#VT100-Widget-Resources:modifyOtherKeys +const RequestModifyOtherKeys = "\x1b[?4m" diff --git a/vendor/github.com/charmbracelet/x/input/LICENSE b/vendor/github.com/charmbracelet/x/input/LICENSE new file mode 100644 index 00000000..65a5654e --- /dev/null +++ b/vendor/github.com/charmbracelet/x/input/LICENSE @@ -0,0 +1,21 @@ +MIT License + +Copyright (c) 2023 Charmbracelet, Inc. + +Permission is hereby granted, free of charge, to any person obtaining a copy +of this software and associated documentation files (the "Software"), to deal +in the Software without restriction, including without limitation the rights +to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +copies of the Software, and to permit persons to whom the Software is +furnished to do so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in all +copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE +SOFTWARE. diff --git a/vendor/github.com/charmbracelet/x/input/cancelreader_other.go b/vendor/github.com/charmbracelet/x/input/cancelreader_other.go new file mode 100644 index 00000000..ef0a2a03 --- /dev/null +++ b/vendor/github.com/charmbracelet/x/input/cancelreader_other.go @@ -0,0 +1,14 @@ +//go:build !windows +// +build !windows + +package input + +import ( + "io" + + "github.com/muesli/cancelreader" +) + +func newCancelreader(r io.Reader) (cancelreader.CancelReader, error) { + return cancelreader.NewReader(r) +} diff --git a/vendor/github.com/charmbracelet/x/input/cancelreader_windows.go b/vendor/github.com/charmbracelet/x/input/cancelreader_windows.go new file mode 100644 index 00000000..8e502119 --- /dev/null +++ b/vendor/github.com/charmbracelet/x/input/cancelreader_windows.go @@ -0,0 +1,217 @@ +//go:build windows +// +build windows + +package input + +import ( + "fmt" + "io" + "os" + "sync" + "time" + + "github.com/erikgeiser/coninput" + "github.com/muesli/cancelreader" + "golang.org/x/sys/windows" +) + +type conInputReader struct { + cancelMixin + + conin windows.Handle + cancelEvent windows.Handle + + originalMode uint32 + + // blockingReadSignal is used to signal that a blocking read is in progress. + blockingReadSignal chan struct{} +} + +var _ cancelreader.CancelReader = &conInputReader{} + +func newCancelreader(r io.Reader) (cancelreader.CancelReader, error) { + fallback := func(io.Reader) (cancelreader.CancelReader, error) { + return cancelreader.NewReader(r) + } + + var dummy uint32 + if f, ok := r.(cancelreader.File); !ok || f.Fd() != os.Stdin.Fd() || + // If data was piped to the standard input, it does not emit events + // anymore. We can detect this if the console mode cannot be set anymore, + // in this case, we fallback to the default cancelreader implementation. + windows.GetConsoleMode(windows.Handle(f.Fd()), &dummy) != nil { + return fallback(r) + } + + conin, err := coninput.NewStdinHandle() + if err != nil { + return fallback(r) + } + + originalMode, err := prepareConsole(conin, + windows.ENABLE_MOUSE_INPUT, + windows.ENABLE_WINDOW_INPUT, + windows.ENABLE_EXTENDED_FLAGS, + ) + if err != nil { + return nil, fmt.Errorf("failed to prepare console input: %w", err) + } + + cancelEvent, err := windows.CreateEvent(nil, 0, 0, nil) + if err != nil { + return nil, fmt.Errorf("create stop event: %w", err) + } + + return &conInputReader{ + conin: conin, + cancelEvent: cancelEvent, + originalMode: originalMode, + blockingReadSignal: make(chan struct{}, 1), + }, nil +} + +// Cancel implements cancelreader.CancelReader. +func (r *conInputReader) Cancel() bool { + r.setCanceled() + + select { + case r.blockingReadSignal <- struct{}{}: + err := windows.SetEvent(r.cancelEvent) + if err != nil { + return false + } + <-r.blockingReadSignal + case <-time.After(100 * time.Millisecond): + // Read() hangs in a GetOverlappedResult which is likely due to + // WaitForMultipleObjects returning without input being available + // so we cannot cancel this ongoing read. + return false + } + + return true +} + +// Close implements cancelreader.CancelReader. +func (r *conInputReader) Close() error { + err := windows.CloseHandle(r.cancelEvent) + if err != nil { + return fmt.Errorf("closing cancel event handle: %w", err) + } + + if r.originalMode != 0 { + err := windows.SetConsoleMode(r.conin, r.originalMode) + if err != nil { + return fmt.Errorf("reset console mode: %w", err) + } + } + + return nil +} + +// Read implements cancelreader.CancelReader. +func (r *conInputReader) Read(data []byte) (n int, err error) { + if r.isCanceled() { + return 0, cancelreader.ErrCanceled + } + + err = waitForInput(r.conin, r.cancelEvent) + if err != nil { + return 0, err + } + + if r.isCanceled() { + return 0, cancelreader.ErrCanceled + } + + r.blockingReadSignal <- struct{}{} + n, err = overlappedReader(r.conin).Read(data) + <-r.blockingReadSignal + + return +} + +func prepareConsole(input windows.Handle, modes ...uint32) (originalMode uint32, err error) { + err = windows.GetConsoleMode(input, &originalMode) + if err != nil { + return 0, fmt.Errorf("get console mode: %w", err) + } + + newMode := coninput.AddInputModes(0, modes...) + + err = windows.SetConsoleMode(input, newMode) + if err != nil { + return 0, fmt.Errorf("set console mode: %w", err) + } + + return originalMode, nil +} + +func waitForInput(conin, cancel windows.Handle) error { + event, err := windows.WaitForMultipleObjects([]windows.Handle{conin, cancel}, false, windows.INFINITE) + switch { + case windows.WAIT_OBJECT_0 <= event && event < windows.WAIT_OBJECT_0+2: + if event == windows.WAIT_OBJECT_0+1 { + return cancelreader.ErrCanceled + } + + if event == windows.WAIT_OBJECT_0 { + return nil + } + + return fmt.Errorf("unexpected wait object is ready: %d", event-windows.WAIT_OBJECT_0) + case windows.WAIT_ABANDONED <= event && event < windows.WAIT_ABANDONED+2: + return fmt.Errorf("abandoned") + case event == uint32(windows.WAIT_TIMEOUT): + return fmt.Errorf("timeout") + case event == windows.WAIT_FAILED: + return fmt.Errorf("failed") + default: + return fmt.Errorf("unexpected error: %w", err) + } +} + +// cancelMixin represents a goroutine-safe cancelation status. +type cancelMixin struct { + unsafeCanceled bool + lock sync.Mutex +} + +func (c *cancelMixin) setCanceled() { + c.lock.Lock() + defer c.lock.Unlock() + + c.unsafeCanceled = true +} + +func (c *cancelMixin) isCanceled() bool { + c.lock.Lock() + defer c.lock.Unlock() + + return c.unsafeCanceled +} + +type overlappedReader windows.Handle + +// Read performs an overlapping read fom a windows.Handle. +func (r overlappedReader) Read(data []byte) (int, error) { + hevent, err := windows.CreateEvent(nil, 0, 0, nil) + if err != nil { + return 0, fmt.Errorf("create event: %w", err) + } + + overlapped := windows.Overlapped{HEvent: hevent} + + var n uint32 + + err = windows.ReadFile(windows.Handle(r), data, &n, &overlapped) + if err != nil && err != windows.ERROR_IO_PENDING { + return int(n), err + } + + err = windows.GetOverlappedResult(windows.Handle(r), &overlapped, &n, true) + if err != nil { + return int(n), nil + } + + return int(n), nil +} diff --git a/vendor/github.com/charmbracelet/x/input/clipboard.go b/vendor/github.com/charmbracelet/x/input/clipboard.go new file mode 100644 index 00000000..c1c9fbeb --- /dev/null +++ b/vendor/github.com/charmbracelet/x/input/clipboard.go @@ -0,0 +1,9 @@ +package input + +// ClipboardEvent is a clipboard read event. +type ClipboardEvent string + +// String returns the string representation of the clipboard event. +func (e ClipboardEvent) String() string { + return string(e) +} diff --git a/vendor/github.com/charmbracelet/x/input/color.go b/vendor/github.com/charmbracelet/x/input/color.go new file mode 100644 index 00000000..309e8c9d --- /dev/null +++ b/vendor/github.com/charmbracelet/x/input/color.go @@ -0,0 +1,77 @@ +package input + +import ( + "fmt" + "image/color" + "strconv" + "strings" +) + +// ForegroundColorEvent represents a foreground color change event. +type ForegroundColorEvent struct{ color.Color } + +// String implements fmt.Stringer. +func (e ForegroundColorEvent) String() string { + return colorToHex(e) +} + +// BackgroundColorEvent represents a background color change event. +type BackgroundColorEvent struct{ color.Color } + +// String implements fmt.Stringer. +func (e BackgroundColorEvent) String() string { + return colorToHex(e) +} + +// CursorColorEvent represents a cursor color change event. +type CursorColorEvent struct{ color.Color } + +// String implements fmt.Stringer. +func (e CursorColorEvent) String() string { + return colorToHex(e) +} + +type shiftable interface { + ~uint | ~uint16 | ~uint32 | ~uint64 +} + +func shift[T shiftable](x T) T { + if x > 0xff { + x >>= 8 + } + return x +} + +func colorToHex(c color.Color) string { + r, g, b, _ := c.RGBA() + return fmt.Sprintf("#%02x%02x%02x", shift(r), shift(g), shift(b)) +} + +func xParseColor(s string) color.Color { + switch { + case strings.HasPrefix(s, "rgb:"): + parts := strings.Split(s[4:], "/") + if len(parts) != 3 { + return color.Black + } + + r, _ := strconv.ParseUint(parts[0], 16, 32) + g, _ := strconv.ParseUint(parts[1], 16, 32) + b, _ := strconv.ParseUint(parts[2], 16, 32) + + return color.RGBA{uint8(shift(r)), uint8(shift(g)), uint8(shift(b)), 255} + case strings.HasPrefix(s, "rgba:"): + parts := strings.Split(s[5:], "/") + if len(parts) != 4 { + return color.Black + } + + r, _ := strconv.ParseUint(parts[0], 16, 32) + g, _ := strconv.ParseUint(parts[1], 16, 32) + b, _ := strconv.ParseUint(parts[2], 16, 32) + a, _ := strconv.ParseUint(parts[3], 16, 32) + + return color.RGBA{uint8(shift(r)), uint8(shift(g)), uint8(shift(b)), uint8(shift(a))} + } + return color.Black +} diff --git a/vendor/github.com/charmbracelet/x/input/cursor.go b/vendor/github.com/charmbracelet/x/input/cursor.go new file mode 100644 index 00000000..c5d0fbb1 --- /dev/null +++ b/vendor/github.com/charmbracelet/x/input/cursor.go @@ -0,0 +1,10 @@ +package input + +// CursorPositionEvent represents a cursor position event. +type CursorPositionEvent struct { + // Row is the row number. + Row int + + // Column is the column number. + Column int +} diff --git a/vendor/github.com/charmbracelet/x/input/da1.go b/vendor/github.com/charmbracelet/x/input/da1.go new file mode 100644 index 00000000..d7529d54 --- /dev/null +++ b/vendor/github.com/charmbracelet/x/input/da1.go @@ -0,0 +1,18 @@ +package input + +import "github.com/charmbracelet/x/ansi" + +// PrimaryDeviceAttributesEvent represents a primary device attributes event. +type PrimaryDeviceAttributesEvent []uint + +func parsePrimaryDevAttrs(csi *ansi.CsiSequence) Event { + // Primary Device Attributes + da1 := make(PrimaryDeviceAttributesEvent, len(csi.Params)) + csi.Range(func(i int, p int, hasMore bool) bool { + if !hasMore { + da1[i] = uint(p) + } + return true + }) + return da1 +} diff --git a/vendor/github.com/charmbracelet/x/input/driver.go b/vendor/github.com/charmbracelet/x/input/driver.go new file mode 100644 index 00000000..8339f1fb --- /dev/null +++ b/vendor/github.com/charmbracelet/x/input/driver.go @@ -0,0 +1,125 @@ +package input + +import ( + "bytes" + "io" + "unicode/utf8" + + "github.com/erikgeiser/coninput" + "github.com/muesli/cancelreader" +) + +// Driver represents an ANSI terminal input Driver. +// It reads input events and parses ANSI sequences from the terminal input +// buffer. +type Driver struct { + rd cancelreader.CancelReader + table map[string]Key // table is a lookup table for key sequences. + + term string // term is the terminal name $TERM. + + // paste is the bracketed paste mode buffer. + // When nil, bracketed paste mode is disabled. + paste []byte + + buf [256]byte // do we need a larger buffer? + + // prevMouseState keeps track of the previous mouse state to determine mouse + // up button events. + prevMouseState coninput.ButtonState // nolint: unused + + flags int // control the behavior of the driver. +} + +// NewDriver returns a new ANSI input driver. +// This driver uses ANSI control codes compatible with VT100/VT200 terminals, +// and XTerm. It supports reading Terminfo databases to overwrite the default +// key sequences. +func NewDriver(r io.Reader, term string, flags int) (*Driver, error) { + d := new(Driver) + cr, err := newCancelreader(r) + if err != nil { + return nil, err + } + + d.rd = cr + d.table = buildKeysTable(flags, term) + d.term = term + d.flags = flags + return d, nil +} + +// Cancel cancels the underlying reader. +func (d *Driver) Cancel() bool { + return d.rd.Cancel() +} + +// Close closes the underlying reader. +func (d *Driver) Close() error { + return d.rd.Close() +} + +func (d *Driver) readEvents() (e []Event, err error) { + nb, err := d.rd.Read(d.buf[:]) + if err != nil { + return nil, err + } + + buf := d.buf[:nb] + + // Lookup table first + if bytes.HasPrefix(buf, []byte{'\x1b'}) { + if k, ok := d.table[string(buf)]; ok { + e = append(e, KeyDownEvent(k)) + return + } + } + + var i int + for i < len(buf) { + nb, ev := ParseSequence(buf[i:]) + + // Handle bracketed-paste + if d.paste != nil { + if _, ok := ev.(PasteEndEvent); !ok { + d.paste = append(d.paste, buf[i]) + i++ + continue + } + } + + switch ev.(type) { + case UnknownCsiEvent, UnknownSs3Event, UnknownEvent: + // If the sequence is not recognized by the parser, try looking it up. + if k, ok := d.table[string(buf[i:i+nb])]; ok { + ev = KeyDownEvent(k) + } + case PasteStartEvent: + d.paste = []byte{} + case PasteEndEvent: + // Decode the captured data into runes. + var paste []rune + for len(d.paste) > 0 { + r, w := utf8.DecodeRune(d.paste) + if r != utf8.RuneError { + paste = append(paste, r) + } + d.paste = d.paste[w:] + } + d.paste = nil // reset the buffer + e = append(e, PasteEvent(paste)) + case nil: + i++ + continue + } + + if mevs, ok := ev.(MultiEvent); ok { + e = append(e, []Event(mevs)...) + } else { + e = append(e, ev) + } + i += nb + } + + return +} diff --git a/vendor/github.com/charmbracelet/x/input/driver_other.go b/vendor/github.com/charmbracelet/x/input/driver_other.go new file mode 100644 index 00000000..7092aa31 --- /dev/null +++ b/vendor/github.com/charmbracelet/x/input/driver_other.go @@ -0,0 +1,11 @@ +//go:build !windows +// +build !windows + +package input + +// ReadEvents reads input events from the terminal. +// +// It reads the events available in the input buffer and returns them. +func (d *Driver) ReadEvents() ([]Event, error) { + return d.readEvents() +} diff --git a/vendor/github.com/charmbracelet/x/input/driver_windows.go b/vendor/github.com/charmbracelet/x/input/driver_windows.go new file mode 100644 index 00000000..63eaf9bd --- /dev/null +++ b/vendor/github.com/charmbracelet/x/input/driver_windows.go @@ -0,0 +1,276 @@ +//go:build windows +// +build windows + +package input + +import ( + "errors" + "fmt" + "unicode/utf16" + + "github.com/charmbracelet/x/ansi" + termwindows "github.com/charmbracelet/x/windows" + "github.com/erikgeiser/coninput" + "golang.org/x/sys/windows" +) + +// ReadEvents reads input events from the terminal. +// +// It reads the events available in the input buffer and returns them. +func (d *Driver) ReadEvents() ([]Event, error) { + events, err := d.handleConInput(coninput.ReadConsoleInput) + if errors.Is(err, errNotConInputReader) { + return d.readEvents() + } + return events, err +} + +var errNotConInputReader = fmt.Errorf("handleConInput: not a conInputReader") + +func (d *Driver) handleConInput( + finput func(windows.Handle, []coninput.InputRecord) (uint32, error), +) ([]Event, error) { + cc, ok := d.rd.(*conInputReader) + if !ok { + return nil, errNotConInputReader + } + + // read up to 256 events, this is to allow for sequences events reported as + // key events. + var events [256]coninput.InputRecord + _, err := finput(cc.conin, events[:]) + if err != nil { + return nil, fmt.Errorf("read coninput events: %w", err) + } + + var evs []Event + for _, event := range events { + if e := parseConInputEvent(event, &d.prevMouseState); e != nil { + evs = append(evs, e) + } + } + + return d.detectConInputQuerySequences(evs), nil +} + +// Using ConInput API, Windows Terminal responds to sequence query events with +// KEY_EVENT_RECORDs so we need to collect them and parse them as a single +// sequence. +// Is this a hack? +func (d *Driver) detectConInputQuerySequences(events []Event) []Event { + var newEvents []Event + start, end := -1, -1 + +loop: + for i, e := range events { + switch e := e.(type) { + case KeyDownEvent: + switch e.Rune { + case ansi.ESC, ansi.CSI, ansi.OSC, ansi.DCS, ansi.APC: + // start of a sequence + if start == -1 { + start = i + } + } + default: + break loop + } + end = i + } + + if start == -1 || end <= start { + return events + } + + var seq []byte + for i := start; i <= end; i++ { + switch e := events[i].(type) { + case KeyDownEvent: + seq = append(seq, byte(e.Rune)) + } + } + + n, seqevent := ParseSequence(seq) + switch seqevent.(type) { + case UnknownEvent: + // We're not interested in unknown events + default: + if start+n > len(events) { + return events + } + newEvents = events[:start] + newEvents = append(newEvents, seqevent) + newEvents = append(newEvents, events[start+n:]...) + return d.detectConInputQuerySequences(newEvents) + } + + return events +} + +func parseConInputEvent(event coninput.InputRecord, ps *coninput.ButtonState) Event { + switch e := event.Unwrap().(type) { + case coninput.KeyEventRecord: + event := parseWin32InputKeyEvent(e.VirtualKeyCode, e.VirtualScanCode, + e.Char, e.KeyDown, e.ControlKeyState, e.RepeatCount) + + var key Key + switch event := event.(type) { + case KeyDownEvent: + key = Key(event) + case KeyUpEvent: + key = Key(event) + default: + return nil + } + + // If the key is not printable, return the event as is + // (e.g. function keys, arrows, etc.) + // Otherwise, try to translate it to a rune based on the active keyboard + // layout. + if key.Rune == 0 { + return event + } + + // Get active keyboard layout + fgWin := windows.GetForegroundWindow() + fgThread, err := windows.GetWindowThreadProcessId(fgWin, nil) + if err != nil { + return event + } + + layout, err := termwindows.GetKeyboardLayout(fgThread) + if layout == windows.InvalidHandle || err != nil { + return event + } + + // Translate key to rune + var keyState [256]byte + var utf16Buf [16]uint16 + const dontChangeKernelKeyboardLayout = 0x4 + ret, err := termwindows.ToUnicodeEx( + uint32(e.VirtualKeyCode), + uint32(e.VirtualScanCode), + &keyState[0], + &utf16Buf[0], + int32(len(utf16Buf)), + dontChangeKernelKeyboardLayout, + layout, + ) + + // -1 indicates a dead key + // 0 indicates no translation for this key + if ret < 1 || err != nil { + return event + } + + runes := utf16.Decode(utf16Buf[:ret]) + if len(runes) != 1 { + // Key doesn't translate to a single rune + return event + } + + key.BaseRune = runes[0] + if e.KeyDown { + return KeyDownEvent(key) + } + + return KeyUpEvent(key) + + case coninput.WindowBufferSizeEventRecord: + return WindowSizeEvent{ + Width: int(e.Size.X), + Height: int(e.Size.Y), + } + case coninput.MouseEventRecord: + mevent := mouseEvent(*ps, e) + *ps = e.ButtonState + return mevent + case coninput.FocusEventRecord, coninput.MenuEventRecord: + // ignore + } + return nil +} + +func mouseEventButton(p, s coninput.ButtonState) (button MouseButton, isRelease bool) { + btn := p ^ s + if btn&s == 0 { + isRelease = true + } + + if btn == 0 { + switch { + case s&coninput.FROM_LEFT_1ST_BUTTON_PRESSED > 0: + button = MouseLeft + case s&coninput.FROM_LEFT_2ND_BUTTON_PRESSED > 0: + button = MouseMiddle + case s&coninput.RIGHTMOST_BUTTON_PRESSED > 0: + button = MouseRight + case s&coninput.FROM_LEFT_3RD_BUTTON_PRESSED > 0: + button = MouseBackward + case s&coninput.FROM_LEFT_4TH_BUTTON_PRESSED > 0: + button = MouseForward + } + return + } + + switch { + case btn == coninput.FROM_LEFT_1ST_BUTTON_PRESSED: // left button + button = MouseLeft + case btn == coninput.RIGHTMOST_BUTTON_PRESSED: // right button + button = MouseRight + case btn == coninput.FROM_LEFT_2ND_BUTTON_PRESSED: // middle button + button = MouseMiddle + case btn == coninput.FROM_LEFT_3RD_BUTTON_PRESSED: // unknown (possibly mouse backward) + button = MouseBackward + case btn == coninput.FROM_LEFT_4TH_BUTTON_PRESSED: // unknown (possibly mouse forward) + button = MouseForward + } + + return +} + +func mouseEvent(p coninput.ButtonState, e coninput.MouseEventRecord) (ev Event) { + var mod KeyMod + var isRelease bool + if e.ControlKeyState.Contains(coninput.LEFT_ALT_PRESSED | coninput.RIGHT_ALT_PRESSED) { + mod |= Alt + } + if e.ControlKeyState.Contains(coninput.LEFT_CTRL_PRESSED | coninput.RIGHT_CTRL_PRESSED) { + mod |= Ctrl + } + if e.ControlKeyState.Contains(coninput.SHIFT_PRESSED) { + mod |= Shift + } + m := Mouse{ + X: int(e.MousePositon.X), + Y: int(e.MousePositon.Y), + Mod: mod, + } + switch e.EventFlags { + case coninput.CLICK, coninput.DOUBLE_CLICK: + m.Button, isRelease = mouseEventButton(p, e.ButtonState) + case coninput.MOUSE_WHEELED: + if e.WheelDirection > 0 { + m.Button = MouseWheelUp + } else { + m.Button = MouseWheelDown + } + case coninput.MOUSE_HWHEELED: + if e.WheelDirection > 0 { + m.Button = MouseWheelRight + } else { + m.Button = MouseWheelLeft + } + case coninput.MOUSE_MOVED: + m.Button, _ = mouseEventButton(p, e.ButtonState) + return MouseMotionEvent(m) + } + + if isWheel(m.Button) { + return MouseWheelEvent(m) + } else if isRelease { + return MouseUpEvent(m) + } + + return MouseDownEvent(m) +} diff --git a/vendor/github.com/charmbracelet/x/input/input.go b/vendor/github.com/charmbracelet/x/input/input.go new file mode 100644 index 00000000..b29df6b5 --- /dev/null +++ b/vendor/github.com/charmbracelet/x/input/input.go @@ -0,0 +1,47 @@ +package input + +import ( + "fmt" + "strings" +) + +var ( + // ErrUnknownEvent is returned when an unknown event is encountered. + ErrUnknownEvent = fmt.Errorf("unknown event") + + // ErrEmpty is returned when the event buffer is empty. + ErrEmpty = fmt.Errorf("empty event buffer") +) + +// Event represents a terminal input event. +type Event interface{} + +// UnknownEvent represents an unknown event. +type UnknownEvent string + +// String implements fmt.Stringer. +func (e UnknownEvent) String() string { + return fmt.Sprintf("%q", string(e)) +} + +// WindowSizeEvent represents a window resize event. +type WindowSizeEvent struct { + Width, Height int +} + +// String implements fmt.Stringer. +func (e WindowSizeEvent) String() string { + return fmt.Sprintf("resize: %dx%d", e.Width, e.Height) +} + +// MultiEvent represents multiple events. +type MultiEvent []Event + +// String implements fmt.Stringer. +func (e MultiEvent) String() string { + var sb strings.Builder + for _, ev := range e { + sb.WriteString(fmt.Sprintf("%v\n", ev)) + } + return sb.String() +} diff --git a/vendor/github.com/charmbracelet/x/input/key.go b/vendor/github.com/charmbracelet/x/input/key.go new file mode 100644 index 00000000..cbf43952 --- /dev/null +++ b/vendor/github.com/charmbracelet/x/input/key.go @@ -0,0 +1,410 @@ +package input + +// KeySym is a keyboard symbol. +type KeySym int + +// Symbol constants. +const ( + KeyNone KeySym = iota + + // Special names in C0 + + KeyBackspace + KeyTab + KeyEnter + KeyEscape + + // Special names in G0 + + KeySpace + KeyDelete + + // Special keys + + KeyUp + KeyDown + KeyRight + KeyLeft + KeyBegin + KeyFind + KeyInsert + KeySelect + KeyPgUp + KeyPgDown + KeyHome + KeyEnd + + // Keypad keys + + KeyKpEnter + KeyKpEqual + KeyKpMultiply + KeyKpPlus + KeyKpComma + KeyKpMinus + KeyKpDecimal + KeyKpDivide + KeyKp0 + KeyKp1 + KeyKp2 + KeyKp3 + KeyKp4 + KeyKp5 + KeyKp6 + KeyKp7 + KeyKp8 + KeyKp9 + + // The following are keys defined in the Kitty keyboard protocol. + // TODO: Investigate the names of these keys + KeyKpSep + KeyKpUp + KeyKpDown + KeyKpLeft + KeyKpRight + KeyKpPgUp + KeyKpPgDown + KeyKpHome + KeyKpEnd + KeyKpInsert + KeyKpDelete + KeyKpBegin + + // Function keys + + KeyF1 + KeyF2 + KeyF3 + KeyF4 + KeyF5 + KeyF6 + KeyF7 + KeyF8 + KeyF9 + KeyF10 + KeyF11 + KeyF12 + KeyF13 + KeyF14 + KeyF15 + KeyF16 + KeyF17 + KeyF18 + KeyF19 + KeyF20 + KeyF21 + KeyF22 + KeyF23 + KeyF24 + KeyF25 + KeyF26 + KeyF27 + KeyF28 + KeyF29 + KeyF30 + KeyF31 + KeyF32 + KeyF33 + KeyF34 + KeyF35 + KeyF36 + KeyF37 + KeyF38 + KeyF39 + KeyF40 + KeyF41 + KeyF42 + KeyF43 + KeyF44 + KeyF45 + KeyF46 + KeyF47 + KeyF48 + KeyF49 + KeyF50 + KeyF51 + KeyF52 + KeyF53 + KeyF54 + KeyF55 + KeyF56 + KeyF57 + KeyF58 + KeyF59 + KeyF60 + KeyF61 + KeyF62 + KeyF63 + + // The following are keys defined in the Kitty keyboard protocol. + // TODO: Investigate the names of these keys + + KeyCapsLock + KeyScrollLock + KeyNumLock + KeyPrintScreen + KeyPause + KeyMenu + + KeyMediaPlay + KeyMediaPause + KeyMediaPlayPause + KeyMediaReverse + KeyMediaStop + KeyMediaFastForward + KeyMediaRewind + KeyMediaNext + KeyMediaPrev + KeyMediaRecord + + KeyLowerVol + KeyRaiseVol + KeyMute + + KeyLeftShift + KeyLeftAlt + KeyLeftCtrl + KeyLeftSuper + KeyLeftHyper + KeyLeftMeta + KeyRightShift + KeyRightAlt + KeyRightCtrl + KeyRightSuper + KeyRightHyper + KeyRightMeta + KeyIsoLevel3Shift + KeyIsoLevel5Shift +) + +// Key represents a key event. +type Key struct { + Sym KeySym + Rune rune + AltRune rune + BaseRune rune + IsRepeat bool + Mod KeyMod +} + +// KeyDownEvent represents a key down event. +type KeyDownEvent Key + +// String implements fmt.Stringer. +func (k KeyDownEvent) String() string { + return Key(k).String() +} + +// KeyUpEvent represents a key up event. +type KeyUpEvent Key + +// String implements fmt.Stringer. +func (k KeyUpEvent) String() string { + return Key(k).String() +} + +// String implements fmt.Stringer. +func (k Key) String() string { + var s string + if k.Mod.IsCtrl() && k.Sym != KeyLeftCtrl && k.Sym != KeyRightCtrl { + s += "ctrl+" + } + if k.Mod.IsAlt() && k.Sym != KeyLeftAlt && k.Sym != KeyRightAlt { + s += "alt+" + } + if k.Mod.IsShift() && k.Sym != KeyLeftShift && k.Sym != KeyRightShift { + s += "shift+" + } + if k.Mod.IsMeta() && k.Sym != KeyLeftMeta && k.Sym != KeyRightMeta { + s += "meta+" + } + if k.Mod.IsHyper() && k.Sym != KeyLeftHyper && k.Sym != KeyRightHyper { + s += "hyper+" + } + if k.Mod.IsSuper() && k.Sym != KeyLeftSuper && k.Sym != KeyRightSuper { + s += "super+" + } + + runeStr := func(r rune) string { + // Space is the only invisible printable character. + if r == ' ' { + return "space" + } + return string(r) + } + if k.BaseRune != 0 { + // If a BaseRune is present, use it to represent a key using the standard + // PC-101 key layout. + s += runeStr(k.BaseRune) + } else if k.AltRune != 0 { + // Otherwise, use the AltRune aka the non-shifted one if present. + s += runeStr(k.AltRune) + } else if k.Rune != 0 { + // Else, just print the rune. + s += runeStr(k.Rune) + } else { + s += k.Sym.String() + } + return s +} + +// String implements fmt.Stringer. +func (k KeySym) String() string { + s, ok := keySymString[k] + if !ok { + return "unknown" + } + return s +} + +var keySymString = map[KeySym]string{ + KeyEnter: "enter", + KeyTab: "tab", + KeyBackspace: "backspace", + KeyEscape: "esc", + KeySpace: "space", + KeyUp: "up", + KeyDown: "down", + KeyLeft: "left", + KeyRight: "right", + KeyBegin: "begin", + KeyFind: "find", + KeyInsert: "insert", + KeyDelete: "delete", + KeySelect: "select", + KeyPgUp: "pgup", + KeyPgDown: "pgdown", + KeyHome: "home", + KeyEnd: "end", + KeyKpEnter: "kpenter", + KeyKpEqual: "kpequal", + KeyKpMultiply: "kpmul", + KeyKpPlus: "kpplus", + KeyKpComma: "kpcomma", + KeyKpMinus: "kpminus", + KeyKpDecimal: "kpperiod", + KeyKpDivide: "kpdiv", + KeyKp0: "kp0", + KeyKp1: "kp1", + KeyKp2: "kp2", + KeyKp3: "kp3", + KeyKp4: "kp4", + KeyKp5: "kp5", + KeyKp6: "kp6", + KeyKp7: "kp7", + KeyKp8: "kp8", + KeyKp9: "kp9", + + // Kitty keyboard extension + KeyKpSep: "kpsep", + KeyKpUp: "kpup", + KeyKpDown: "kpdown", + KeyKpLeft: "kpleft", + KeyKpRight: "kpright", + KeyKpPgUp: "kppgup", + KeyKpPgDown: "kppgdown", + KeyKpHome: "kphome", + KeyKpEnd: "kpend", + KeyKpInsert: "kpinsert", + KeyKpDelete: "kpdelete", + KeyKpBegin: "kpbegin", + + KeyF1: "f1", + KeyF2: "f2", + KeyF3: "f3", + KeyF4: "f4", + KeyF5: "f5", + KeyF6: "f6", + KeyF7: "f7", + KeyF8: "f8", + KeyF9: "f9", + KeyF10: "f10", + KeyF11: "f11", + KeyF12: "f12", + KeyF13: "f13", + KeyF14: "f14", + KeyF15: "f15", + KeyF16: "f16", + KeyF17: "f17", + KeyF18: "f18", + KeyF19: "f19", + KeyF20: "f20", + KeyF21: "f21", + KeyF22: "f22", + KeyF23: "f23", + KeyF24: "f24", + KeyF25: "f25", + KeyF26: "f26", + KeyF27: "f27", + KeyF28: "f28", + KeyF29: "f29", + KeyF30: "f30", + KeyF31: "f31", + KeyF32: "f32", + KeyF33: "f33", + KeyF34: "f34", + KeyF35: "f35", + KeyF36: "f36", + KeyF37: "f37", + KeyF38: "f38", + KeyF39: "f39", + KeyF40: "f40", + KeyF41: "f41", + KeyF42: "f42", + KeyF43: "f43", + KeyF44: "f44", + KeyF45: "f45", + KeyF46: "f46", + KeyF47: "f47", + KeyF48: "f48", + KeyF49: "f49", + KeyF50: "f50", + KeyF51: "f51", + KeyF52: "f52", + KeyF53: "f53", + KeyF54: "f54", + KeyF55: "f55", + KeyF56: "f56", + KeyF57: "f57", + KeyF58: "f58", + KeyF59: "f59", + KeyF60: "f60", + KeyF61: "f61", + KeyF62: "f62", + KeyF63: "f63", + + // Kitty keyboard extension + KeyCapsLock: "capslock", + KeyScrollLock: "scrolllock", + KeyNumLock: "numlock", + KeyPrintScreen: "printscreen", + KeyPause: "pause", + KeyMenu: "menu", + KeyMediaPlay: "mediaplay", + KeyMediaPause: "mediapause", + KeyMediaPlayPause: "mediaplaypause", + KeyMediaReverse: "mediareverse", + KeyMediaStop: "mediastop", + KeyMediaFastForward: "mediafastforward", + KeyMediaRewind: "mediarewind", + KeyMediaNext: "medianext", + KeyMediaPrev: "mediaprev", + KeyMediaRecord: "mediarecord", + KeyLowerVol: "lowervol", + KeyRaiseVol: "raisevol", + KeyMute: "mute", + KeyLeftShift: "leftshift", + KeyLeftAlt: "leftalt", + KeyLeftCtrl: "leftctrl", + KeyLeftSuper: "leftsuper", + KeyLeftHyper: "lefthyper", + KeyLeftMeta: "leftmeta", + KeyRightShift: "rightshift", + KeyRightAlt: "rightalt", + KeyRightCtrl: "rightctrl", + KeyRightSuper: "rightsuper", + KeyRightHyper: "righthyper", + KeyRightMeta: "rightmeta", + KeyIsoLevel3Shift: "isolevel3shift", + KeyIsoLevel5Shift: "isolevel5shift", +} diff --git a/vendor/github.com/charmbracelet/x/input/kitty.go b/vendor/github.com/charmbracelet/x/input/kitty.go new file mode 100644 index 00000000..e9edbfb8 --- /dev/null +++ b/vendor/github.com/charmbracelet/x/input/kitty.go @@ -0,0 +1,281 @@ +package input + +import ( + "unicode" + "unicode/utf8" + + "github.com/charmbracelet/x/ansi" +) + +// KittyKeyboardEvent represents Kitty keyboard progressive enhancement flags. +type KittyKeyboardEvent int + +// IsDisambiguateEscapeCodes returns true if the DisambiguateEscapeCodes flag is set. +func (e KittyKeyboardEvent) IsDisambiguateEscapeCodes() bool { + return e&ansi.KittyDisambiguateEscapeCodes != 0 +} + +// IsReportEventTypes returns true if the ReportEventTypes flag is set. +func (e KittyKeyboardEvent) IsReportEventTypes() bool { + return e&ansi.KittyReportEventTypes != 0 +} + +// IsReportAlternateKeys returns true if the ReportAlternateKeys flag is set. +func (e KittyKeyboardEvent) IsReportAlternateKeys() bool { + return e&ansi.KittyReportAlternateKeys != 0 +} + +// IsReportAllKeys returns true if the ReportAllKeys flag is set. +func (e KittyKeyboardEvent) IsReportAllKeys() bool { + return e&ansi.KittyReportAllKeys != 0 +} + +// IsReportAssociatedKeys returns true if the ReportAssociatedKeys flag is set. +func (e KittyKeyboardEvent) IsReportAssociatedKeys() bool { + return e&ansi.KittyReportAssociatedKeys != 0 +} + +// Kitty Clipboard Control Sequences +var kittyKeyMap = map[int]KeySym{ + ansi.BS: KeyBackspace, + ansi.HT: KeyTab, + ansi.CR: KeyEnter, + ansi.ESC: KeyEscape, + ansi.DEL: KeyBackspace, + + 57344: KeyEscape, + 57345: KeyEnter, + 57346: KeyTab, + 57347: KeyBackspace, + 57348: KeyInsert, + 57349: KeyDelete, + 57350: KeyLeft, + 57351: KeyRight, + 57352: KeyUp, + 57353: KeyDown, + 57354: KeyPgUp, + 57355: KeyPgDown, + 57356: KeyHome, + 57357: KeyEnd, + 57358: KeyCapsLock, + 57359: KeyScrollLock, + 57360: KeyNumLock, + 57361: KeyPrintScreen, + 57362: KeyPause, + 57363: KeyMenu, + 57364: KeyF1, + 57365: KeyF2, + 57366: KeyF3, + 57367: KeyF4, + 57368: KeyF5, + 57369: KeyF6, + 57370: KeyF7, + 57371: KeyF8, + 57372: KeyF9, + 57373: KeyF10, + 57374: KeyF11, + 57375: KeyF12, + 57376: KeyF13, + 57377: KeyF14, + 57378: KeyF15, + 57379: KeyF16, + 57380: KeyF17, + 57381: KeyF18, + 57382: KeyF19, + 57383: KeyF20, + 57384: KeyF21, + 57385: KeyF22, + 57386: KeyF23, + 57387: KeyF24, + 57388: KeyF25, + 57389: KeyF26, + 57390: KeyF27, + 57391: KeyF28, + 57392: KeyF29, + 57393: KeyF30, + 57394: KeyF31, + 57395: KeyF32, + 57396: KeyF33, + 57397: KeyF34, + 57398: KeyF35, + 57399: KeyKp0, + 57400: KeyKp1, + 57401: KeyKp2, + 57402: KeyKp3, + 57403: KeyKp4, + 57404: KeyKp5, + 57405: KeyKp6, + 57406: KeyKp7, + 57407: KeyKp8, + 57408: KeyKp9, + 57409: KeyKpDecimal, + 57410: KeyKpDivide, + 57411: KeyKpMultiply, + 57412: KeyKpMinus, + 57413: KeyKpPlus, + 57414: KeyKpEnter, + 57415: KeyKpEqual, + 57416: KeyKpSep, + 57417: KeyKpLeft, + 57418: KeyKpRight, + 57419: KeyKpUp, + 57420: KeyKpDown, + 57421: KeyKpPgUp, + 57422: KeyKpPgDown, + 57423: KeyKpHome, + 57424: KeyKpEnd, + 57425: KeyKpInsert, + 57426: KeyKpDelete, + 57427: KeyKpBegin, + 57428: KeyMediaPlay, + 57429: KeyMediaPause, + 57430: KeyMediaPlayPause, + 57431: KeyMediaReverse, + 57432: KeyMediaStop, + 57433: KeyMediaFastForward, + 57434: KeyMediaRewind, + 57435: KeyMediaNext, + 57436: KeyMediaPrev, + 57437: KeyMediaRecord, + 57438: KeyLowerVol, + 57439: KeyRaiseVol, + 57440: KeyMute, + 57441: KeyLeftShift, + 57442: KeyLeftCtrl, + 57443: KeyLeftAlt, + 57444: KeyLeftSuper, + 57445: KeyLeftHyper, + 57446: KeyLeftMeta, + 57447: KeyRightShift, + 57448: KeyRightCtrl, + 57449: KeyRightAlt, + 57450: KeyRightSuper, + 57451: KeyRightHyper, + 57452: KeyRightMeta, + 57453: KeyIsoLevel3Shift, + 57454: KeyIsoLevel5Shift, +} + +const ( + kittyShift = 1 << iota + kittyAlt + kittyCtrl + kittySuper + kittyHyper + kittyMeta + kittyCapsLock + kittyNumLock +) + +func fromKittyMod(mod int) KeyMod { + var m KeyMod + if mod&kittyShift != 0 { + m |= Shift + } + if mod&kittyAlt != 0 { + m |= Alt + } + if mod&kittyCtrl != 0 { + m |= Ctrl + } + if mod&kittySuper != 0 { + m |= Super + } + if mod&kittyHyper != 0 { + m |= Hyper + } + if mod&kittyMeta != 0 { + m |= Meta + } + if mod&kittyCapsLock != 0 { + m |= CapsLock + } + if mod&kittyNumLock != 0 { + m |= NumLock + } + return m +} + +// parseKittyKeyboard parses a Kitty Keyboard Protocol sequence. +// +// In `CSI u`, this is parsed as: +// +// CSI codepoint ; modifiers u +// codepoint: ASCII Dec value +// +// The Kitty Keyboard Protocol extends this with optional components that can be +// enabled progressively. The full sequence is parsed as: +// +// CSI unicode-key-code:alternate-key-codes ; modifiers:event-type ; text-as-codepoints u +// +// See https://sw.kovidgoyal.net/kitty/keyboard-protocol/ +func parseKittyKeyboard(csi *ansi.CsiSequence) Event { + var isRelease bool + key := Key{} + + if params := csi.Subparams(0); len(params) > 0 { + code := params[0] + if sym, ok := kittyKeyMap[code]; ok { + key.Sym = sym + } else { + r := rune(code) + if !utf8.ValidRune(r) { + r = utf8.RuneError + } + + key.Rune = r + + // alternate key reporting + switch len(params) { + case 3: + // shifted key + base key + if b := rune(params[2]); unicode.IsPrint(b) { + // XXX: When alternate key reporting is enabled, the protocol + // can return 3 things, the unicode codepoint of the key, + // the shifted codepoint of the key, and the standard + // PC-101 key layout codepoint. + // This is useful to create an unambiguous mapping of keys + // when using a different language layout. + key.BaseRune = b + } + fallthrough + case 2: + // shifted key + if s := rune(params[1]); unicode.IsPrint(s) { + // XXX: We swap keys here because we want the shifted key + // to be the Rune that is returned by the event. + // For example, shift+a should produce "A" not "a". + // In such a case, we set AltRune to the original key "a" + // and Rune to "A". + key.AltRune = key.Rune + key.Rune = s + } + } + } + } + if params := csi.Subparams(1); len(params) > 0 { + mod := params[0] + if mod > 1 { + key.Mod = fromKittyMod(mod - 1) + } + if len(params) > 1 { + switch params[1] { + case 2: + key.IsRepeat = true + case 3: + isRelease = true + } + } + } + // TODO: Associated keys are not support yet. + // if params := csi.Subparams(2); len(params) > 0 { + // r := rune(params[0]) + // if unicode.IsPrint(r) { + // key.AltRune = r + // } + // } + if isRelease { + return KeyUpEvent(key) + } + return KeyDownEvent(key) +} diff --git a/vendor/github.com/charmbracelet/x/input/mod.go b/vendor/github.com/charmbracelet/x/input/mod.go new file mode 100644 index 00000000..1040d2fa --- /dev/null +++ b/vendor/github.com/charmbracelet/x/input/mod.go @@ -0,0 +1,70 @@ +package input + +// KeyMod represents modifier keys. +type KeyMod uint16 + +// Modifier keys. +const ( + Shift KeyMod = 1 << iota + Alt + Ctrl + Meta + + // These modifiers are used with the Kitty protocol. + // XXX: Meta and Super are swapped in the Kitty protocol, + // this is to preserve compatibility with XTerm modifiers. + + Hyper + Super // Windows/Command keys + + // These are key lock states. + + CapsLock + NumLock + ScrollLock // Defined in Windows API only +) + +// IsShift reports whether the Shift modifier is set. +func (m KeyMod) IsShift() bool { + return m&Shift != 0 +} + +// IsAlt reports whether the Alt modifier is set. +func (m KeyMod) IsAlt() bool { + return m&Alt != 0 +} + +// IsCtrl reports whether the Ctrl modifier is set. +func (m KeyMod) IsCtrl() bool { + return m&Ctrl != 0 +} + +// IsMeta reports whether the Meta modifier is set. +func (m KeyMod) IsMeta() bool { + return m&Meta != 0 +} + +// IsHyper reports whether the Hyper modifier is set. +func (m KeyMod) IsHyper() bool { + return m&Hyper != 0 +} + +// IsSuper reports whether the Super modifier is set. +func (m KeyMod) IsSuper() bool { + return m&Super != 0 +} + +// HasCapsLock reports whether the CapsLock key is enabled. +func (m KeyMod) HasCapsLock() bool { + return m&CapsLock != 0 +} + +// HasNumLock reports whether the NumLock key is enabled. +func (m KeyMod) HasNumLock() bool { + return m&NumLock != 0 +} + +// HasScrollLock reports whether the ScrollLock key is enabled. +func (m KeyMod) HasScrollLock() bool { + return m&ScrollLock != 0 +} diff --git a/vendor/github.com/charmbracelet/x/input/mode.go b/vendor/github.com/charmbracelet/x/input/mode.go new file mode 100644 index 00000000..e838e681 --- /dev/null +++ b/vendor/github.com/charmbracelet/x/input/mode.go @@ -0,0 +1,12 @@ +package input + +// ReportModeEvent represents a report mode event for sequence DECRPM. +// +// See: https://vt100.net/docs/vt510-rm/DECRPM.html +type ReportModeEvent struct { + // Mode is the mode number. + Mode int + + // Value is the mode value. + Value int +} diff --git a/vendor/github.com/charmbracelet/x/input/mouse.go b/vendor/github.com/charmbracelet/x/input/mouse.go new file mode 100644 index 00000000..0e6eea93 --- /dev/null +++ b/vendor/github.com/charmbracelet/x/input/mouse.go @@ -0,0 +1,248 @@ +package input + +import ( + "regexp" + + "github.com/charmbracelet/x/ansi" +) + +// MouseButton represents the button that was pressed during a mouse event. +type MouseButton byte + +// Mouse event buttons +// +// This is based on X11 mouse button codes. +// +// 1 = left button +// 2 = middle button (pressing the scroll wheel) +// 3 = right button +// 4 = turn scroll wheel up +// 5 = turn scroll wheel down +// 6 = push scroll wheel left +// 7 = push scroll wheel right +// 8 = 4th button (aka browser backward button) +// 9 = 5th button (aka browser forward button) +// 10 +// 11 +// +// Other buttons are not supported. +const ( + MouseNone MouseButton = iota + MouseLeft + MouseMiddle + MouseRight + MouseWheelUp + MouseWheelDown + MouseWheelLeft + MouseWheelRight + MouseBackward + MouseForward + MouseExtra1 + MouseExtra2 +) + +var mouseButtons = map[MouseButton]string{ + MouseNone: "none", + MouseLeft: "left", + MouseMiddle: "middle", + MouseRight: "right", + MouseWheelUp: "wheelup", + MouseWheelDown: "wheeldown", + MouseWheelLeft: "wheelleft", + MouseWheelRight: "wheelright", + MouseBackward: "backward", + MouseForward: "forward", + MouseExtra1: "button10", + MouseExtra2: "button11", +} + +// Mouse represents a Mouse event. +type Mouse struct { + X, Y int + Button MouseButton + Mod KeyMod +} + +// String implements fmt.Stringer. +func (m Mouse) String() (s string) { + if m.Mod.IsCtrl() { + s += "ctrl+" + } + if m.Mod.IsAlt() { + s += "alt+" + } + if m.Mod.IsShift() { + s += "shift+" + } + + str, ok := mouseButtons[m.Button] + if !ok { + s += "unknown" + } else if str != "none" { // motion events don't have a button + s += str + } + + return s +} + +// MouseDownEvent represents a mouse button down event. +type MouseDownEvent Mouse + +// String implements fmt.Stringer. +func (e MouseDownEvent) String() string { + return Mouse(e).String() +} + +// MouseUpEvent represents a mouse button up event. +type MouseUpEvent Mouse + +// String implements fmt.Stringer. +func (e MouseUpEvent) String() string { + return Mouse(e).String() +} + +// MouseWheelEvent represents a mouse wheel event. +type MouseWheelEvent Mouse + +// String implements fmt.Stringer. +func (e MouseWheelEvent) String() string { + return Mouse(e).String() +} + +// MouseMotionEvent represents a mouse motion event. +type MouseMotionEvent Mouse + +// String implements fmt.Stringer. +func (e MouseMotionEvent) String() string { + m := Mouse(e) + if m.Button != 0 { + return m.String() + "+motion" + } + return m.String() + "motion" +} + +var mouseSGRRegex = regexp.MustCompile(`(\d+);(\d+);(\d+)([Mm])`) + +// Parse SGR-encoded mouse events; SGR extended mouse events. SGR mouse events +// look like: +// +// ESC [ < Cb ; Cx ; Cy (M or m) +// +// where: +// +// Cb is the encoded button code +// Cx is the x-coordinate of the mouse +// Cy is the y-coordinate of the mouse +// M is for button press, m is for button release +// +// https://invisible-island.net/xterm/ctlseqs/ctlseqs.html#h3-Extended-coordinates +func parseSGRMouseEvent(csi *ansi.CsiSequence) Event { + x := csi.Param(1) + y := csi.Param(2) + release := csi.Command() == 'm' + mod, btn, _, isMotion := parseMouseButton(csi.Param(0)) + + // (1,1) is the upper left. We subtract 1 to normalize it to (0,0). + x-- + y-- + + m := Mouse{X: x, Y: y, Button: btn, Mod: mod} + + // Wheel buttons don't have release events + // Motion can be reported as a release event in some terminals (Windows Terminal) + if isWheel(m.Button) { + return MouseWheelEvent(m) + } else if !isMotion && release { + return MouseUpEvent(m) + } else if isMotion { + return MouseMotionEvent(m) + } + return MouseDownEvent(m) +} + +const x10MouseByteOffset = 32 + +// Parse X10-encoded mouse events; the simplest kind. The last release of X10 +// was December 1986, by the way. The original X10 mouse protocol limits the Cx +// and Cy coordinates to 223 (=255-032). +// +// X10 mouse events look like: +// +// ESC [M Cb Cx Cy +// +// See: http://www.xfree86.org/current/ctlseqs.html#Mouse%20Tracking +func parseX10MouseEvent(buf []byte) Event { + v := buf[3:6] + b := int(v[0]) + if b >= x10MouseByteOffset { + // XXX: b < 32 should be impossible, but we're being defensive. + b -= x10MouseByteOffset + } + + mod, btn, isRelease, isMotion := parseMouseButton(b) + + // (1,1) is the upper left. We subtract 1 to normalize it to (0,0). + x := int(v[1]) - x10MouseByteOffset - 1 + y := int(v[2]) - x10MouseByteOffset - 1 + + m := Mouse{X: x, Y: y, Button: btn, Mod: mod} + if isWheel(m.Button) { + return MouseWheelEvent(m) + } else if isMotion { + return MouseMotionEvent(m) + } else if isRelease { + return MouseUpEvent(m) + } + return MouseDownEvent(m) +} + +// See: https://invisible-island.net/xterm/ctlseqs/ctlseqs.html#h3-Extended-coordinates +func parseMouseButton(b int) (mod KeyMod, btn MouseButton, isRelease bool, isMotion bool) { + // mouse bit shifts + const ( + bitShift = 0b0000_0100 + bitAlt = 0b0000_1000 + bitCtrl = 0b0001_0000 + bitMotion = 0b0010_0000 + bitWheel = 0b0100_0000 + bitAdd = 0b1000_0000 // additional buttons 8-11 + + bitsMask = 0b0000_0011 + ) + + // Modifiers + if b&bitAlt != 0 { + mod |= Alt + } + if b&bitCtrl != 0 { + mod |= Ctrl + } + if b&bitShift != 0 { + mod |= Shift + } + + if b&bitAdd != 0 { + btn = MouseBackward + MouseButton(b&bitsMask) + } else if b&bitWheel != 0 { + btn = MouseWheelUp + MouseButton(b&bitsMask) + } else { + btn = MouseLeft + MouseButton(b&bitsMask) + // X10 reports a button release as 0b0000_0011 (3) + if b&bitsMask == bitsMask { + btn = MouseNone + isRelease = true + } + } + + // Motion bit doesn't get reported for wheel events. + if b&bitMotion != 0 && !isWheel(btn) { + isMotion = true + } + + return +} + +// isWheel returns true if the mouse event is a wheel event. +func isWheel(btn MouseButton) bool { + return btn >= MouseWheelUp && btn <= MouseWheelRight +} diff --git a/vendor/github.com/charmbracelet/x/input/parse.go b/vendor/github.com/charmbracelet/x/input/parse.go new file mode 100644 index 00000000..219ff711 --- /dev/null +++ b/vendor/github.com/charmbracelet/x/input/parse.go @@ -0,0 +1,836 @@ +package input + +import ( + "encoding/base64" + "strings" + "unicode/utf8" + + "github.com/charmbracelet/x/ansi" + "github.com/charmbracelet/x/ansi/parser" + "github.com/erikgeiser/coninput" +) + +// Flags to control the behavior of the parser. +const ( + // When this flag is set, the driver will treat both Ctrl+Space and Ctrl+@ + // as the same key sequence. + // + // Historically, the ANSI specs generate NUL (0x00) on both the Ctrl+Space + // and Ctrl+@ key sequences. This flag allows the driver to treat both as + // the same key sequence. + FlagCtrlAt = 1 << iota + + // When this flag is set, the driver will treat the Tab key and Ctrl+I as + // the same key sequence. + // + // Historically, the ANSI specs generate HT (0x09) on both the Tab key and + // Ctrl+I. This flag allows the driver to treat both as the same key + // sequence. + FlagCtrlI + + // When this flag is set, the driver will treat the Enter key and Ctrl+M as + // the same key sequence. + // + // Historically, the ANSI specs generate CR (0x0D) on both the Enter key + // and Ctrl+M. This flag allows the driver to treat both as the same key + FlagCtrlM + + // When this flag is set, the driver will treat Escape and Ctrl+[ as + // the same key sequence. + // + // Historically, the ANSI specs generate ESC (0x1B) on both the Escape key + // and Ctrl+[. This flag allows the driver to treat both as the same key + // sequence. + FlagCtrlOpenBracket + + // When this flag is set, the driver will send a BS (0x08 byte) character + // instead of a DEL (0x7F byte) character when the Backspace key is + // pressed. + // + // The VT100 terminal has both a Backspace and a Delete key. The VT220 + // terminal dropped the Backspace key and replaced it with the Delete key. + // Both terminals send a DEL character when the Delete key is pressed. + // Modern terminals and PCs later readded the Delete key but used a + // different key sequence, and the Backspace key was standardized to send a + // DEL character. + FlagBackspace + + // When this flag is set, the driver will recognize the Find key instead of + // treating it as a Home key. + // + // The Find key was part of the VT220 keyboard, and is no longer used in + // modern day PCs. + FlagFind + + // When this flag is set, the driver will recognize the Select key instead + // of treating it as a End key. + // + // The Symbol key was part of the VT220 keyboard, and is no longer used in + // modern day PCs. + FlagSelect + + // When this flag is set, the driver will use Terminfo databases to + // overwrite the default key sequences. + FlagTerminfo + + // When this flag is set, the driver will preserve function keys (F13-F63) + // as symbols. + // + // Since these keys are not part of today's standard 20th century keyboard, + // we treat them as F1-F12 modifier keys i.e. ctrl/shift/alt + Fn combos. + // Key definitions come from Terminfo, this flag is only useful when + // FlagTerminfo is not set. + FlagFKeys +) + +var flags int + +// SetFlags sets the flags for the parser. +// This will control the behavior of ParseSequence. +func SetFlags(f int) { + flags = f +} + +// ParseSequence finds the first recognized event sequence and returns it along +// with its length. +// +// It will return zero and nil no sequence is recognized or when the buffer is +// empty. If a sequence is not supported, an UnknownEvent is returned. +func ParseSequence(buf []byte) (n int, e Event) { + if len(buf) == 0 { + return 0, nil + } + + switch b := buf[0]; b { + case ansi.ESC: + if len(buf) == 1 { + // Escape key + return 1, KeyDownEvent{Sym: KeyEscape} + } + + switch b := buf[1]; b { + case 'O': // Esc-prefixed SS3 + return parseSs3(buf) + case 'P': // Esc-prefixed DCS + return parseDcs(buf) + case '[': // Esc-prefixed CSI + return parseCsi(buf) + case ']': // Esc-prefixed OSC + return parseOsc(buf) + case '_': // Esc-prefixed APC + return parseApc(buf) + default: + n, e := ParseSequence(buf[1:]) + if k, ok := e.(KeyDownEvent); ok && !k.Mod.IsAlt() { + k.Mod |= Alt + return n + 1, k + } + + // Not a key sequence, nor an alt modified key sequence. In that + // case, just report a single escape key. + return 1, KeyDownEvent{Sym: KeyEscape} + } + case ansi.SS3: + return parseSs3(buf) + case ansi.DCS: + return parseDcs(buf) + case ansi.CSI: + return parseCsi(buf) + case ansi.OSC: + return parseOsc(buf) + case ansi.APC: + return parseApc(buf) + default: + if b <= ansi.US || b == ansi.DEL || b == ansi.SP { + return 1, parseControl(b) + } else if b >= ansi.PAD && b <= ansi.APC { + // C1 control code + // UTF-8 never starts with a C1 control code + // Encode these as Ctrl+Alt+ + return 1, KeyDownEvent{Rune: rune(b) - 0x40, Mod: Ctrl | Alt} + } + return parseUtf8(buf) + } +} + +func parseCsi(b []byte) (int, Event) { + if len(b) == 2 && b[0] == ansi.ESC { + // short cut if this is an alt+[ key + return 2, KeyDownEvent{Rune: rune(b[1]), Mod: Alt} + } + + var csi ansi.CsiSequence + var params [parser.MaxParamsSize]int + var paramsLen int + + var i int + if b[i] == ansi.CSI || b[i] == ansi.ESC { + i++ + } + if i < len(b) && b[i-1] == ansi.ESC && b[i] == '[' { + i++ + } + + // Initial CSI byte + if i < len(b) && b[i] >= '<' && b[i] <= '?' { + csi.Cmd |= int(b[i]) << parser.MarkerShift + } + + // Scan parameter bytes in the range 0x30-0x3F + var j int + for j = 0; i < len(b) && paramsLen < len(params) && b[i] >= 0x30 && b[i] <= 0x3F; i, j = i+1, j+1 { + if b[i] >= '0' && b[i] <= '9' { + if params[paramsLen] == parser.MissingParam { + params[paramsLen] = 0 + } + params[paramsLen] *= 10 + params[paramsLen] += int(b[i]) - '0' + } + if b[i] == ':' { + params[paramsLen] |= parser.HasMoreFlag + } + if b[i] == ';' || b[i] == ':' { + paramsLen++ + if paramsLen < len(params) { + // Don't overflow the params slice + params[paramsLen] = parser.MissingParam + } + } + } + + if j > 0 && paramsLen < len(params) { + // has parameters + paramsLen++ + } + + // Scan intermediate bytes in the range 0x20-0x2F + var intermed byte + for ; i < len(b) && b[i] >= 0x20 && b[i] <= 0x2F; i++ { + intermed = b[i] + } + + // Set the intermediate byte + csi.Cmd |= int(intermed) << parser.IntermedShift + + // Scan final byte in the range 0x40-0x7E + if i >= len(b) || b[i] < 0x40 || b[i] > 0x7E { + // Special case for URxvt keys + // CSI $ is an invalid sequence, but URxvt uses it for + // shift modified keys. + if b[i-1] == '$' { + n, ev := parseCsi(append(b[:i-1], '~')) + if k, ok := ev.(KeyDownEvent); ok { + k.Mod |= Shift + return n, k + } + } + return i, UnknownEvent(b[:i-1]) + } + + // Add the final byte + csi.Cmd |= int(b[i]) + i++ + + csi.Params = params[:paramsLen] + marker, cmd := csi.Marker(), csi.Command() + switch marker { + case '?': + switch cmd { + case 'y': + switch intermed { + case '$': + // Report Mode (DECRPM) + if paramsLen != 2 { + return i, UnknownCsiEvent(b[:i]) + } + return i, ReportModeEvent{Mode: csi.Param(0), Value: csi.Param(1)} + } + case 'c': + // Primary Device Attributes + return i, parsePrimaryDevAttrs(&csi) + case 'u': + // Kitty keyboard flags + if param := csi.Param(0); param != -1 { + return i, KittyKeyboardEvent(param) + } + case 'R': + // This report may return a third parameter representing the page + // number, but we don't really need it. + if paramsLen >= 2 { + return i, CursorPositionEvent{Row: csi.Param(0), Column: csi.Param(1)} + } + } + return i, UnknownCsiEvent(b[:i]) + case '<': + switch cmd { + case 'm', 'M': + // Handle SGR mouse + if paramsLen != 3 { + return i, UnknownCsiEvent(b[:i]) + } + return i, parseSGRMouseEvent(&csi) + default: + return i, UnknownCsiEvent(b[:i]) + } + case '>': + switch cmd { + case 'm': + // XTerm modifyOtherKeys + if paramsLen != 2 || csi.Param(0) != 4 { + return i, UnknownCsiEvent(b[:i]) + } + + return i, ModifyOtherKeysEvent(csi.Param(1)) + default: + return i, UnknownCsiEvent(b[:i]) + } + case '=': + // We don't support any of these yet + return i, UnknownCsiEvent(b[:i]) + } + + switch cmd := csi.Command(); cmd { + case 'R': + // Cursor position report OR modified F3 + if paramsLen == 0 { + return i, KeyDownEvent{Sym: KeyF3} + } else if paramsLen != 2 { + break + } + + // XXX: We cannot differentiate between cursor position report and + // CSI 1 ; R (which is modified F3) when the cursor is at the + // row 1. In this case, we report a modified F3 event since it's more + // likely to be the case than the cursor being at the first row. + // + // For a non ambiguous cursor position report, use + // [ansi.RequestExtendedCursorPosition] (DECXCPR) instead. + if csi.Param(0) != 1 { + return i, CursorPositionEvent{Row: csi.Param(0), Column: csi.Param(1)} + } + + fallthrough + case 'a', 'b', 'c', 'd', 'A', 'B', 'C', 'D', 'E', 'F', 'H', 'P', 'Q', 'S', 'Z': + var k KeyDownEvent + switch cmd { + case 'a', 'b', 'c', 'd': + k = KeyDownEvent{Sym: KeyUp + KeySym(cmd-'a'), Mod: Shift} + case 'A', 'B', 'C', 'D': + k = KeyDownEvent{Sym: KeyUp + KeySym(cmd-'A')} + case 'E': + k = KeyDownEvent{Sym: KeyBegin} + case 'F': + k = KeyDownEvent{Sym: KeyEnd} + case 'H': + k = KeyDownEvent{Sym: KeyHome} + case 'P', 'Q', 'R', 'S': + k = KeyDownEvent{Sym: KeyF1 + KeySym(cmd-'P')} + case 'Z': + k = KeyDownEvent{Sym: KeyTab, Mod: Shift} + } + if paramsLen > 1 && csi.Param(0) == 1 { + // CSI 1 ; A + if paramsLen > 1 { + k.Mod |= KeyMod(csi.Param(1) - 1) + } + } + return i, k + case 'M': + // Handle X10 mouse + if i+3 > len(b) { + return i, UnknownCsiEvent(b[:i]) + } + return i + 3, parseX10MouseEvent(append(b[:i], b[i:i+3]...)) + case 'y': + // Report Mode (DECRPM) + if paramsLen != 2 { + return i, UnknownCsiEvent(b[:i]) + } + return i, ReportModeEvent{Mode: csi.Param(0), Value: csi.Param(1)} + case 'u': + // Kitty keyboard protocol & CSI u (fixterms) + if paramsLen == 0 { + return i, UnknownCsiEvent(b[:i]) + } + return i, parseKittyKeyboard(&csi) + case '_': + // Win32 Input Mode + if paramsLen != 6 { + return i, UnknownCsiEvent(b[:i]) + } + + rc := uint16(csi.Param(5)) + if rc == 0 { + rc = 1 + } + + event := parseWin32InputKeyEvent( + coninput.VirtualKeyCode(csi.Param(0)), // Vk wVirtualKeyCode + coninput.VirtualKeyCode(csi.Param(1)), // Sc wVirtualScanCode + rune(csi.Param(2)), // Uc UnicodeChar + csi.Param(3) == 1, // Kd bKeyDown + coninput.ControlKeyState(csi.Param(4)), // Cs dwControlKeyState + rc, // Rc wRepeatCount + ) + + if event == nil { + return i, UnknownCsiEvent(b[:]) + } + + return i, event + case '@', '^', '~': + if paramsLen == 0 { + return i, UnknownCsiEvent(b[:i]) + } + + param := csi.Param(0) + switch cmd { + case '~': + switch param { + case 27: + // XTerm modifyOtherKeys 2 + if paramsLen != 3 { + return i, UnknownCsiEvent(b[:i]) + } + return i, parseXTermModifyOtherKeys(&csi) + case 200: + // bracketed-paste start + return i, PasteStartEvent{} + case 201: + // bracketed-paste end + return i, PasteEndEvent{} + } + } + + switch param { + case 1, 2, 3, 4, 5, 6, 7, 8: + fallthrough + case 11, 12, 13, 14, 15: + fallthrough + case 17, 18, 19, 20, 21, 23, 24, 25, 26: + fallthrough + case 28, 29, 31, 32, 33, 34: + var k KeyDownEvent + switch param { + case 1: + if flags&FlagFind != 0 { + k = KeyDownEvent{Sym: KeyFind} + } else { + k = KeyDownEvent{Sym: KeyHome} + } + case 2: + k = KeyDownEvent{Sym: KeyInsert} + case 3: + k = KeyDownEvent{Sym: KeyDelete} + case 4: + if flags&FlagSelect != 0 { + k = KeyDownEvent{Sym: KeySelect} + } else { + k = KeyDownEvent{Sym: KeyEnd} + } + case 5: + k = KeyDownEvent{Sym: KeyPgUp} + case 6: + k = KeyDownEvent{Sym: KeyPgDown} + case 7: + k = KeyDownEvent{Sym: KeyHome} + case 8: + k = KeyDownEvent{Sym: KeyEnd} + case 11, 12, 13, 14, 15: + k = KeyDownEvent{Sym: KeyF1 + KeySym(param-11)} + case 17, 18, 19, 20, 21: + k = KeyDownEvent{Sym: KeyF6 + KeySym(param-17)} + case 23, 24, 25, 26: + k = KeyDownEvent{Sym: KeyF11 + KeySym(param-23)} + case 28, 29: + k = KeyDownEvent{Sym: KeyF15 + KeySym(param-28)} + case 31, 32, 33, 34: + k = KeyDownEvent{Sym: KeyF17 + KeySym(param-31)} + } + + // modifiers + if paramsLen > 1 { + k.Mod |= KeyMod(csi.Param(1) - 1) + } + + // Handle URxvt weird keys + switch cmd { + case '^': + k.Mod |= Ctrl + case '@': + k.Mod |= Ctrl | Shift + } + + return i, k + } + } + return i, UnknownCsiEvent(b[:i]) +} + +// parseSs3 parses a SS3 sequence. +// See https://vt100.net/docs/vt220-rm/chapter4.html#S4.4.4.2 +func parseSs3(b []byte) (int, Event) { + if len(b) == 2 && b[0] == ansi.ESC { + // short cut if this is an alt+O key + return 2, KeyDownEvent{Rune: rune(b[1]), Mod: Alt} + } + + var i int + if b[i] == ansi.SS3 || b[i] == ansi.ESC { + i++ + } + if i < len(b) && b[i-1] == ansi.ESC && b[i] == 'O' { + i++ + } + + // Scan numbers from 0-9 + var mod int + for ; i < len(b) && b[i] >= '0' && b[i] <= '9'; i++ { + mod *= 10 + mod += int(b[i]) - '0' + } + + // Scan a GL character + // A GL character is a single byte in the range 0x21-0x7E + // See https://vt100.net/docs/vt220-rm/chapter2.html#S2.3.2 + if i >= len(b) || b[i] < 0x21 || b[i] > 0x7E { + return i, UnknownEvent(b[:i]) + } + + // GL character(s) + gl := b[i] + i++ + + var k KeyDownEvent + switch gl { + case 'a', 'b', 'c', 'd': + k = KeyDownEvent{Sym: KeyUp + KeySym(gl-'a'), Mod: Ctrl} + case 'A', 'B', 'C', 'D': + k = KeyDownEvent{Sym: KeyUp + KeySym(gl-'A')} + case 'E': + k = KeyDownEvent{Sym: KeyBegin} + case 'F': + k = KeyDownEvent{Sym: KeyEnd} + case 'H': + k = KeyDownEvent{Sym: KeyHome} + case 'P', 'Q', 'R', 'S': + k = KeyDownEvent{Sym: KeyF1 + KeySym(gl-'P')} + case 'M': + k = KeyDownEvent{Sym: KeyKpEnter} + case 'X': + k = KeyDownEvent{Sym: KeyKpEqual} + case 'j', 'k', 'l', 'm', 'n', 'o', 'p', 'q', 'r', 's', 't', 'u', 'v', 'w', 'x', 'y': + k = KeyDownEvent{Sym: KeyKpMultiply + KeySym(gl-'j')} + default: + return i, UnknownSs3Event(b[:i]) + } + + // Handle weird SS3 Func + if mod > 0 { + k.Mod |= KeyMod(mod - 1) + } + + return i, k +} + +func parseOsc(b []byte) (int, Event) { + if len(b) == 2 && b[0] == ansi.ESC { + // short cut if this is an alt+] key + return 2, KeyDownEvent{Rune: rune(b[1]), Mod: Alt} + } + + var i int + if b[i] == ansi.OSC || b[i] == ansi.ESC { + i++ + } + if i < len(b) && b[i-1] == ansi.ESC && b[i] == ']' { + i++ + } + + // Parse OSC command + // An OSC sequence is terminated by a BEL, ESC, or ST character + var start, end int + cmd := -1 + for ; i < len(b) && b[i] >= '0' && b[i] <= '9'; i++ { + if cmd == -1 { + cmd = 0 + } else { + cmd *= 10 + } + cmd += int(b[i]) - '0' + } + + if i < len(b) && b[i] == ';' { + // mark the start of the sequence data + i++ + start = i + } + + for ; i < len(b); i++ { + // advance to the end of the sequence + if b[i] == ansi.BEL || b[i] == ansi.ESC || b[i] == ansi.ST { + break + } + } + + if i >= len(b) { + return i, UnknownEvent(b[:i]) + } + + end = i // end of the sequence data + i++ + + // Check 7-bit ST (string terminator) character + if i < len(b) && b[i-1] == ansi.ESC && b[i] == '\\' { + i++ + } + + if end <= start { + return i, UnknownOscEvent(b[:i]) + } + + data := string(b[start:end]) + switch cmd { + case 10: + return i, ForegroundColorEvent{xParseColor(data)} + case 11: + return i, BackgroundColorEvent{xParseColor(data)} + case 12: + return i, CursorColorEvent{xParseColor(data)} + case 52: + parts := strings.Split(data, ";") + if len(parts) == 0 { + return i, ClipboardEvent("") + } + b64 := parts[len(parts)-1] + bts, err := base64.StdEncoding.DecodeString(b64) + if err != nil { + return i, ClipboardEvent("") + } + return i, ClipboardEvent(bts) + default: + return i, UnknownOscEvent(b[:i]) + } +} + +// parseStTerminated parses a control sequence that gets terminated by a ST character. +func parseStTerminated(intro8, intro7 byte) func([]byte) (int, Event) { + return func(b []byte) (int, Event) { + var i int + if b[i] == intro8 || b[i] == ansi.ESC { + i++ + } + if i < len(b) && b[i-1] == ansi.ESC && b[i] == intro7 { + i++ + } + + // Scan control sequence + // Most common control sequence is terminated by a ST character + // ST is a 7-bit string terminator character is (ESC \) + // nolint: revive + for ; i < len(b) && b[i] != ansi.ST && b[i] != ansi.ESC; i++ { + } + + if i >= len(b) { + switch intro8 { + case ansi.DCS: + return i, UnknownDcsEvent(b[:i]) + case ansi.APC: + return i, UnknownApcEvent(b[:i]) + default: + return i, UnknownEvent(b[:i]) + } + } + i++ + + // Check 7-bit ST (string terminator) character + if i < len(b) && b[i-1] == ansi.ESC && b[i] == '\\' { + i++ + } + + switch intro8 { + case ansi.DCS: + return i, UnknownDcsEvent(b[:i]) + case ansi.APC: + return i, UnknownApcEvent(b[:i]) + default: + return i, UnknownEvent(b[:i]) + } + } +} + +func parseDcs(b []byte) (int, Event) { + if len(b) == 2 && b[0] == ansi.ESC { + // short cut if this is an alt+P key + return 2, KeyDownEvent{Rune: rune(b[1]), Mod: Alt} + } + + var params [16]int + var paramsLen int + var dcs ansi.DcsSequence + + // DCS sequences are introduced by DCS (0x90) or ESC P (0x1b 0x50) + var i int + if b[i] == ansi.DCS || b[i] == ansi.ESC { + i++ + } + if i < len(b) && b[i-1] == ansi.ESC && b[i] == 'P' { + i++ + } + + // initial DCS byte + if i < len(b) && b[i] >= '<' && b[i] <= '?' { + dcs.Cmd |= int(b[i]) << parser.MarkerShift + } + + // Scan parameter bytes in the range 0x30-0x3F + var j int + for j = 0; i < len(b) && paramsLen < len(params) && b[i] >= 0x30 && b[i] <= 0x3F; i, j = i+1, j+1 { + if b[i] >= '0' && b[i] <= '9' { + if params[paramsLen] == parser.MissingParam { + params[paramsLen] = 0 + } + params[paramsLen] *= 10 + params[paramsLen] += int(b[i]) - '0' + } + if b[i] == ':' { + params[paramsLen] |= parser.HasMoreFlag + } + if b[i] == ';' || b[i] == ':' { + paramsLen++ + if paramsLen < len(params) { + // Don't overflow the params slice + params[paramsLen] = parser.MissingParam + } + } + } + + if j > 0 && paramsLen < len(params) { + // has parameters + paramsLen++ + } + + // Scan intermediate bytes in the range 0x20-0x2F + var intermed byte + for j := 0; i < len(b) && b[i] >= 0x20 && b[i] <= 0x2F; i, j = i+1, j+1 { + intermed = b[i] + } + + // set intermediate byte + dcs.Cmd |= int(intermed) << parser.IntermedShift + + // Scan final byte in the range 0x40-0x7E + if i >= len(b) || b[i] < 0x40 || b[i] > 0x7E { + return i, UnknownEvent(b[:i]) + } + + // Add the final byte + dcs.Cmd |= int(b[i]) + i++ + + start := i // start of the sequence data + for ; i < len(b); i++ { + if b[i] == ansi.ST || b[i] == ansi.ESC { + break + } + } + + if i >= len(b) { + return i, UnknownEvent(b[:i]) + } + + end := i // end of the sequence data + i++ + + // Check 7-bit ST (string terminator) character + if i < len(b) && b[i-1] == ansi.ESC && b[i] == '\\' { + i++ + } + + dcs.Params = params[:paramsLen] + switch cmd := dcs.Command(); cmd { + case 'r': + switch dcs.Intermediate() { + case '+': + // XTGETTCAP responses + switch param := dcs.Param(0); param { + case 0, 1: + tc := parseTermcap(b[start:end]) + // XXX: some terminals like KiTTY report invalid responses with + // their queries i.e. sending a query for "Tc" using "\x1bP+q5463\x1b\\" + // returns "\x1bP0+r5463\x1b\\". + // The specs says that invalid responses should be in the form of + // DCS 0 + r ST "\x1bP0+r\x1b\\" + // + // See: https://invisible-island.net/xterm/ctlseqs/ctlseqs.html#h3-Operating-System-Commands + tc.IsValid = param == 1 + return i, tc + } + } + } + + return i, UnknownDcsEvent(b[:i]) +} + +func parseApc(b []byte) (int, Event) { + if len(b) == 2 && b[0] == ansi.ESC { + // short cut if this is an alt+_ key + return 2, KeyDownEvent{Rune: rune(b[1]), Mod: Alt} + } + + // APC sequences are introduced by APC (0x9f) or ESC _ (0x1b 0x5f) + return parseStTerminated(ansi.APC, '_')(b) +} + +func parseUtf8(b []byte) (int, Event) { + r, rw := utf8.DecodeRune(b) + if r <= ansi.US || r == ansi.DEL || r == ansi.SP { + // Control codes get handled by parseControl + return 1, parseControl(byte(r)) + } else if r == utf8.RuneError { + return 1, UnknownEvent(b[0]) + } + return rw, KeyDownEvent{Rune: r} +} + +func parseControl(b byte) Event { + switch b { + case ansi.NUL: + if flags&FlagCtrlAt != 0 { + return KeyDownEvent{Rune: '@', Mod: Ctrl} + } + return KeyDownEvent{Rune: ' ', Sym: KeySpace, Mod: Ctrl} + case ansi.BS: + return KeyDownEvent{Rune: 'h', Mod: Ctrl} + case ansi.HT: + if flags&FlagCtrlI != 0 { + return KeyDownEvent{Rune: 'i', Mod: Ctrl} + } + return KeyDownEvent{Sym: KeyTab} + case ansi.CR: + if flags&FlagCtrlM != 0 { + return KeyDownEvent{Rune: 'm', Mod: Ctrl} + } + return KeyDownEvent{Sym: KeyEnter} + case ansi.ESC: + if flags&FlagCtrlOpenBracket != 0 { + return KeyDownEvent{Rune: '[', Mod: Ctrl} + } + return KeyDownEvent{Sym: KeyEscape} + case ansi.DEL: + if flags&FlagBackspace != 0 { + return KeyDownEvent{Sym: KeyDelete} + } + return KeyDownEvent{Sym: KeyBackspace} + case ansi.SP: + return KeyDownEvent{Sym: KeySpace, Rune: ' '} + default: + if b >= ansi.SOH && b <= ansi.SUB { + // Use lower case letters for control codes + return KeyDownEvent{Rune: rune(b + 0x60), Mod: Ctrl} + } else if b >= ansi.FS && b <= ansi.US { + return KeyDownEvent{Rune: rune(b + 0x40), Mod: Ctrl} + } + return UnknownEvent(b) + } +} diff --git a/vendor/github.com/charmbracelet/x/input/paste.go b/vendor/github.com/charmbracelet/x/input/paste.go new file mode 100644 index 00000000..079d3cb2 --- /dev/null +++ b/vendor/github.com/charmbracelet/x/input/paste.go @@ -0,0 +1,12 @@ +package input + +// PasteEvent is an event that is emitted when a terminal receives pasted text +// using bracketed-paste. +type PasteEvent string + +// PasteStartEvent is an event that is emitted when a terminal enters +// bracketed-paste mode. +type PasteStartEvent struct{} + +// PasteEvent is an event that is emitted when a terminal receives pasted text. +type PasteEndEvent struct{} diff --git a/vendor/github.com/charmbracelet/x/input/seq.go b/vendor/github.com/charmbracelet/x/input/seq.go new file mode 100644 index 00000000..460e12b8 --- /dev/null +++ b/vendor/github.com/charmbracelet/x/input/seq.go @@ -0,0 +1,45 @@ +package input + +import ( + "fmt" +) + +// UnknownCsiEvent represents an unknown CSI sequence event. +type UnknownCsiEvent string + +// String implements fmt.Stringer. +func (e UnknownCsiEvent) String() string { + return fmt.Sprintf("%q", string(e)) +} + +// UnknownOscEvent represents an unknown OSC sequence event. +type UnknownOscEvent string + +// String implements fmt.Stringer. +func (e UnknownOscEvent) String() string { + return fmt.Sprintf("%q", string(e)) +} + +// UnknownDcsEvent represents an unknown DCS sequence event. +type UnknownDcsEvent string + +// String implements fmt.Stringer. +func (e UnknownDcsEvent) String() string { + return fmt.Sprintf("%q", string(e)) +} + +// UnknownApcEvent represents an unknown APC sequence event. +type UnknownApcEvent string + +// String implements fmt.Stringer. +func (e UnknownApcEvent) String() string { + return fmt.Sprintf("%q", string(e)) +} + +// UnknownSs3Event represents an unknown SS3 sequence event. +type UnknownSs3Event string + +// String implements fmt.Stringer. +func (e UnknownSs3Event) String() string { + return fmt.Sprintf("%q", string(e)) +} diff --git a/vendor/github.com/charmbracelet/x/input/table.go b/vendor/github.com/charmbracelet/x/input/table.go new file mode 100644 index 00000000..5ce20307 --- /dev/null +++ b/vendor/github.com/charmbracelet/x/input/table.go @@ -0,0 +1,388 @@ +package input + +import ( + "strconv" + + "github.com/charmbracelet/x/ansi" +) + +func buildKeysTable(flags int, term string) map[string]Key { + nul := Key{Rune: ' ', Sym: KeySpace, Mod: Ctrl} // ctrl+@ or ctrl+space + if flags&FlagCtrlAt != 0 { + nul = Key{Rune: '@', Mod: Ctrl} + } + + tab := Key{Sym: KeyTab} // ctrl+i or tab + if flags&FlagCtrlI != 0 { + tab = Key{Rune: 'i', Mod: Ctrl} + } + + enter := Key{Sym: KeyEnter} // ctrl+m or enter + if flags&FlagCtrlM != 0 { + enter = Key{Rune: 'm', Mod: Ctrl} + } + + esc := Key{Sym: KeyEscape} // ctrl+[ or escape + if flags&FlagCtrlOpenBracket != 0 { + esc = Key{Rune: '[', Mod: Ctrl} // ctrl+[ or escape + } + + del := Key{Sym: KeyBackspace} + if flags&FlagBackspace != 0 { + del.Sym = KeyDelete + } + + find := Key{Sym: KeyHome} + if flags&FlagFind != 0 { + find.Sym = KeyFind + } + + sel := Key{Sym: KeyEnd} + if flags&FlagSelect != 0 { + sel.Sym = KeySelect + } + + // The following is a table of key sequences and their corresponding key + // events based on the VT100/VT200 terminal specs. + // + // See: https://vt100.net/docs/vt100-ug/chapter3.html#S3.2 + // See: https://vt100.net/docs/vt220-rm/chapter3.html + // + // XXX: These keys may be overwritten by other options like XTerm or + // Terminfo. + table := map[string]Key{ + // C0 control characters + string(byte(ansi.NUL)): nul, + string(byte(ansi.SOH)): {Rune: 'a', Mod: Ctrl}, + string(byte(ansi.STX)): {Rune: 'b', Mod: Ctrl}, + string(byte(ansi.ETX)): {Rune: 'c', Mod: Ctrl}, + string(byte(ansi.EOT)): {Rune: 'd', Mod: Ctrl}, + string(byte(ansi.ENQ)): {Rune: 'e', Mod: Ctrl}, + string(byte(ansi.ACK)): {Rune: 'f', Mod: Ctrl}, + string(byte(ansi.BEL)): {Rune: 'g', Mod: Ctrl}, + string(byte(ansi.BS)): {Rune: 'h', Mod: Ctrl}, + string(byte(ansi.HT)): tab, + string(byte(ansi.LF)): {Rune: 'j', Mod: Ctrl}, + string(byte(ansi.VT)): {Rune: 'k', Mod: Ctrl}, + string(byte(ansi.FF)): {Rune: 'l', Mod: Ctrl}, + string(byte(ansi.CR)): enter, + string(byte(ansi.SO)): {Rune: 'n', Mod: Ctrl}, + string(byte(ansi.SI)): {Rune: 'o', Mod: Ctrl}, + string(byte(ansi.DLE)): {Rune: 'p', Mod: Ctrl}, + string(byte(ansi.DC1)): {Rune: 'q', Mod: Ctrl}, + string(byte(ansi.DC2)): {Rune: 'r', Mod: Ctrl}, + string(byte(ansi.DC3)): {Rune: 's', Mod: Ctrl}, + string(byte(ansi.DC4)): {Rune: 't', Mod: Ctrl}, + string(byte(ansi.NAK)): {Rune: 'u', Mod: Ctrl}, + string(byte(ansi.SYN)): {Rune: 'v', Mod: Ctrl}, + string(byte(ansi.ETB)): {Rune: 'w', Mod: Ctrl}, + string(byte(ansi.CAN)): {Rune: 'x', Mod: Ctrl}, + string(byte(ansi.EM)): {Rune: 'y', Mod: Ctrl}, + string(byte(ansi.SUB)): {Rune: 'z', Mod: Ctrl}, + string(byte(ansi.ESC)): esc, + string(byte(ansi.FS)): {Rune: '\\', Mod: Ctrl}, + string(byte(ansi.GS)): {Rune: ']', Mod: Ctrl}, + string(byte(ansi.RS)): {Rune: '^', Mod: Ctrl}, + string(byte(ansi.US)): {Rune: '_', Mod: Ctrl}, + + // Special keys in G0 + string(byte(ansi.SP)): {Sym: KeySpace, Rune: ' '}, + string(byte(ansi.DEL)): del, + + // Special keys + + "\x1b[Z": {Sym: KeyTab, Mod: Shift}, + + "\x1b[1~": find, + "\x1b[2~": {Sym: KeyInsert}, + "\x1b[3~": {Sym: KeyDelete}, + "\x1b[4~": sel, + "\x1b[5~": {Sym: KeyPgUp}, + "\x1b[6~": {Sym: KeyPgDown}, + "\x1b[7~": {Sym: KeyHome}, + "\x1b[8~": {Sym: KeyEnd}, + + // Normal mode + "\x1b[A": {Sym: KeyUp}, + "\x1b[B": {Sym: KeyDown}, + "\x1b[C": {Sym: KeyRight}, + "\x1b[D": {Sym: KeyLeft}, + "\x1b[E": {Sym: KeyBegin}, + "\x1b[F": {Sym: KeyEnd}, + "\x1b[H": {Sym: KeyHome}, + "\x1b[P": {Sym: KeyF1}, + "\x1b[Q": {Sym: KeyF2}, + "\x1b[R": {Sym: KeyF3}, + "\x1b[S": {Sym: KeyF4}, + + // Application Cursor Key Mode (DECCKM) + "\x1bOA": {Sym: KeyUp}, + "\x1bOB": {Sym: KeyDown}, + "\x1bOC": {Sym: KeyRight}, + "\x1bOD": {Sym: KeyLeft}, + "\x1bOE": {Sym: KeyBegin}, + "\x1bOF": {Sym: KeyEnd}, + "\x1bOH": {Sym: KeyHome}, + "\x1bOP": {Sym: KeyF1}, + "\x1bOQ": {Sym: KeyF2}, + "\x1bOR": {Sym: KeyF3}, + "\x1bOS": {Sym: KeyF4}, + + // Keypad Application Mode (DECKPAM) + + "\x1bOM": {Sym: KeyKpEnter}, + "\x1bOX": {Sym: KeyKpEqual}, + "\x1bOj": {Sym: KeyKpMultiply}, + "\x1bOk": {Sym: KeyKpPlus}, + "\x1bOl": {Sym: KeyKpComma}, + "\x1bOm": {Sym: KeyKpMinus}, + "\x1bOn": {Sym: KeyKpDecimal}, + "\x1bOo": {Sym: KeyKpDivide}, + "\x1bOp": {Sym: KeyKp0}, + "\x1bOq": {Sym: KeyKp1}, + "\x1bOr": {Sym: KeyKp2}, + "\x1bOs": {Sym: KeyKp3}, + "\x1bOt": {Sym: KeyKp4}, + "\x1bOu": {Sym: KeyKp5}, + "\x1bOv": {Sym: KeyKp6}, + "\x1bOw": {Sym: KeyKp7}, + "\x1bOx": {Sym: KeyKp8}, + "\x1bOy": {Sym: KeyKp9}, + + // Function keys + + "\x1b[11~": {Sym: KeyF1}, + "\x1b[12~": {Sym: KeyF2}, + "\x1b[13~": {Sym: KeyF3}, + "\x1b[14~": {Sym: KeyF4}, + "\x1b[15~": {Sym: KeyF5}, + "\x1b[17~": {Sym: KeyF6}, + "\x1b[18~": {Sym: KeyF7}, + "\x1b[19~": {Sym: KeyF8}, + "\x1b[20~": {Sym: KeyF9}, + "\x1b[21~": {Sym: KeyF10}, + "\x1b[23~": {Sym: KeyF11}, + "\x1b[24~": {Sym: KeyF12}, + "\x1b[25~": {Sym: KeyF13}, + "\x1b[26~": {Sym: KeyF14}, + "\x1b[28~": {Sym: KeyF15}, + "\x1b[29~": {Sym: KeyF16}, + "\x1b[31~": {Sym: KeyF17}, + "\x1b[32~": {Sym: KeyF18}, + "\x1b[33~": {Sym: KeyF19}, + "\x1b[34~": {Sym: KeyF20}, + } + + // CSI ~ sequence keys + csiTildeKeys := map[string]Key{ + "1": find, "2": {Sym: KeyInsert}, + "3": {Sym: KeyDelete}, "4": sel, + "5": {Sym: KeyPgUp}, "6": {Sym: KeyPgDown}, + "7": {Sym: KeyHome}, "8": {Sym: KeyEnd}, + // There are no 9 and 10 keys + "11": {Sym: KeyF1}, "12": {Sym: KeyF2}, + "13": {Sym: KeyF3}, "14": {Sym: KeyF4}, + "15": {Sym: KeyF5}, "17": {Sym: KeyF6}, + "18": {Sym: KeyF7}, "19": {Sym: KeyF8}, + "20": {Sym: KeyF9}, "21": {Sym: KeyF10}, + "23": {Sym: KeyF11}, "24": {Sym: KeyF12}, + "25": {Sym: KeyF13}, "26": {Sym: KeyF14}, + "28": {Sym: KeyF15}, "29": {Sym: KeyF16}, + "31": {Sym: KeyF17}, "32": {Sym: KeyF18}, + "33": {Sym: KeyF19}, "34": {Sym: KeyF20}, + } + + // URxvt keys + // See https://manpages.ubuntu.com/manpages/trusty/man7/urxvt.7.html#key%20codes + table["\x1b[a"] = Key{Sym: KeyUp, Mod: Shift} + table["\x1b[b"] = Key{Sym: KeyDown, Mod: Shift} + table["\x1b[c"] = Key{Sym: KeyRight, Mod: Shift} + table["\x1b[d"] = Key{Sym: KeyLeft, Mod: Shift} + table["\x1bOa"] = Key{Sym: KeyUp, Mod: Ctrl} + table["\x1bOb"] = Key{Sym: KeyDown, Mod: Ctrl} + table["\x1bOc"] = Key{Sym: KeyRight, Mod: Ctrl} + table["\x1bOd"] = Key{Sym: KeyLeft, Mod: Ctrl} + // TODO: invistigate if shift-ctrl arrow keys collide with DECCKM keys i.e. + // "\x1bOA", "\x1bOB", "\x1bOC", "\x1bOD" + + // URxvt modifier CSI ~ keys + for k, v := range csiTildeKeys { + key := v + // Normal (no modifier) already defined part of VT100/VT200 + // Shift modifier + key.Mod = Shift + table["\x1b["+k+"$"] = key + // Ctrl modifier + key.Mod = Ctrl + table["\x1b["+k+"^"] = key + // Shift-Ctrl modifier + key.Mod = Shift | Ctrl + table["\x1b["+k+"@"] = key + } + + // URxvt F keys + // Note: Shift + F1-F10 generates F11-F20. + // This means Shift + F1 and Shift + F2 will generate F11 and F12, the same + // applies to Ctrl + Shift F1 & F2. + // + // P.S. Don't like this? Blame URxvt, configure your terminal to use + // different escapes like XTerm, or switch to a better terminal ¯\_(ツ)_/¯ + // + // See https://manpages.ubuntu.com/manpages/trusty/man7/urxvt.7.html#key%20codes + table["\x1b[23$"] = Key{Sym: KeyF11, Mod: Shift} + table["\x1b[24$"] = Key{Sym: KeyF12, Mod: Shift} + table["\x1b[25$"] = Key{Sym: KeyF13, Mod: Shift} + table["\x1b[26$"] = Key{Sym: KeyF14, Mod: Shift} + table["\x1b[28$"] = Key{Sym: KeyF15, Mod: Shift} + table["\x1b[29$"] = Key{Sym: KeyF16, Mod: Shift} + table["\x1b[31$"] = Key{Sym: KeyF17, Mod: Shift} + table["\x1b[32$"] = Key{Sym: KeyF18, Mod: Shift} + table["\x1b[33$"] = Key{Sym: KeyF19, Mod: Shift} + table["\x1b[34$"] = Key{Sym: KeyF20, Mod: Shift} + table["\x1b[11^"] = Key{Sym: KeyF1, Mod: Ctrl} + table["\x1b[12^"] = Key{Sym: KeyF2, Mod: Ctrl} + table["\x1b[13^"] = Key{Sym: KeyF3, Mod: Ctrl} + table["\x1b[14^"] = Key{Sym: KeyF4, Mod: Ctrl} + table["\x1b[15^"] = Key{Sym: KeyF5, Mod: Ctrl} + table["\x1b[17^"] = Key{Sym: KeyF6, Mod: Ctrl} + table["\x1b[18^"] = Key{Sym: KeyF7, Mod: Ctrl} + table["\x1b[19^"] = Key{Sym: KeyF8, Mod: Ctrl} + table["\x1b[20^"] = Key{Sym: KeyF9, Mod: Ctrl} + table["\x1b[21^"] = Key{Sym: KeyF10, Mod: Ctrl} + table["\x1b[23^"] = Key{Sym: KeyF11, Mod: Ctrl} + table["\x1b[24^"] = Key{Sym: KeyF12, Mod: Ctrl} + table["\x1b[25^"] = Key{Sym: KeyF13, Mod: Ctrl} + table["\x1b[26^"] = Key{Sym: KeyF14, Mod: Ctrl} + table["\x1b[28^"] = Key{Sym: KeyF15, Mod: Ctrl} + table["\x1b[29^"] = Key{Sym: KeyF16, Mod: Ctrl} + table["\x1b[31^"] = Key{Sym: KeyF17, Mod: Ctrl} + table["\x1b[32^"] = Key{Sym: KeyF18, Mod: Ctrl} + table["\x1b[33^"] = Key{Sym: KeyF19, Mod: Ctrl} + table["\x1b[34^"] = Key{Sym: KeyF20, Mod: Ctrl} + table["\x1b[23@"] = Key{Sym: KeyF11, Mod: Shift | Ctrl} + table["\x1b[24@"] = Key{Sym: KeyF12, Mod: Shift | Ctrl} + table["\x1b[25@"] = Key{Sym: KeyF13, Mod: Shift | Ctrl} + table["\x1b[26@"] = Key{Sym: KeyF14, Mod: Shift | Ctrl} + table["\x1b[28@"] = Key{Sym: KeyF15, Mod: Shift | Ctrl} + table["\x1b[29@"] = Key{Sym: KeyF16, Mod: Shift | Ctrl} + table["\x1b[31@"] = Key{Sym: KeyF17, Mod: Shift | Ctrl} + table["\x1b[32@"] = Key{Sym: KeyF18, Mod: Shift | Ctrl} + table["\x1b[33@"] = Key{Sym: KeyF19, Mod: Shift | Ctrl} + table["\x1b[34@"] = Key{Sym: KeyF20, Mod: Shift | Ctrl} + + // Register Alt + combinations + // XXX: this must come after URxvt but before XTerm keys to register URxvt + // keys with alt modifier + tmap := map[string]Key{} + for seq, key := range table { + key := key + key.Mod |= Alt + tmap["\x1b"+seq] = key + } + for seq, key := range tmap { + table[seq] = key + } + + // XTerm modifiers + // These are offset by 1 to be compatible with our Mod type. + // See https://invisible-island.net/xterm/ctlseqs/ctlseqs.html#h3-PC-Style-Function-Keys + modifiers := []KeyMod{ + Shift, // 1 + Alt, // 2 + Shift | Alt, // 3 + Ctrl, // 4 + Shift | Ctrl, // 5 + Alt | Ctrl, // 6 + Shift | Alt | Ctrl, // 7 + Meta, // 8 + Meta | Shift, // 9 + Meta | Alt, // 10 + Meta | Shift | Alt, // 11 + Meta | Ctrl, // 12 + Meta | Shift | Ctrl, // 13 + Meta | Alt | Ctrl, // 14 + Meta | Shift | Alt | Ctrl, // 15 + } + + // SS3 keypad function keys + ss3FuncKeys := map[string]Key{ + // These are defined in XTerm + // Taken from Foot keymap.h and XTerm modifyOtherKeys + // https://codeberg.org/dnkl/foot/src/branch/master/keymap.h + "M": {Sym: KeyKpEnter}, "X": {Sym: KeyKpEqual}, + "j": {Sym: KeyKpMultiply}, "k": {Sym: KeyKpPlus}, + "l": {Sym: KeyKpComma}, "m": {Sym: KeyKpMinus}, + "n": {Sym: KeyKpDecimal}, "o": {Sym: KeyKpDivide}, + "p": {Sym: KeyKp0}, "q": {Sym: KeyKp1}, + "r": {Sym: KeyKp2}, "s": {Sym: KeyKp3}, + "t": {Sym: KeyKp4}, "u": {Sym: KeyKp5}, + "v": {Sym: KeyKp6}, "w": {Sym: KeyKp7}, + "x": {Sym: KeyKp8}, "y": {Sym: KeyKp9}, + } + + // XTerm keys + csiFuncKeys := map[string]Key{ + "A": {Sym: KeyUp}, "B": {Sym: KeyDown}, + "C": {Sym: KeyRight}, "D": {Sym: KeyLeft}, + "E": {Sym: KeyBegin}, "F": {Sym: KeyEnd}, + "H": {Sym: KeyHome}, "P": {Sym: KeyF1}, + "Q": {Sym: KeyF2}, "R": {Sym: KeyF3}, + "S": {Sym: KeyF4}, + } + + // CSI 27 ; ; ~ keys defined in XTerm modifyOtherKeys + modifyOtherKeys := map[int]Key{ + ansi.BS: {Sym: KeyBackspace}, + ansi.HT: {Sym: KeyTab}, + ansi.CR: {Sym: KeyEnter}, + ansi.ESC: {Sym: KeyEscape}, + ansi.DEL: {Sym: KeyBackspace}, + } + + for _, m := range modifiers { + // XTerm modifier offset +1 + xtermMod := strconv.Itoa(int(m) + 1) + + // CSI 1 ; + for k, v := range csiFuncKeys { + // Functions always have a leading 1 param + seq := "\x1b[1;" + xtermMod + k + key := v + key.Mod = m + table[seq] = key + } + // SS3 + for k, v := range ss3FuncKeys { + seq := "\x1bO" + xtermMod + k + key := v + key.Mod = m + table[seq] = key + } + // CSI ; ~ + for k, v := range csiTildeKeys { + seq := "\x1b[" + k + ";" + xtermMod + "~" + key := v + key.Mod = m + table[seq] = key + } + // CSI 27 ; ; ~ + for k, v := range modifyOtherKeys { + code := strconv.Itoa(k) + seq := "\x1b[27;" + xtermMod + ";" + code + "~" + key := v + key.Mod = m + table[seq] = key + } + } + + // Register terminfo keys + // XXX: this might override keys already registered in table + if flags&FlagTerminfo != 0 { + titable := buildTerminfoKeys(flags, term) + for seq, key := range titable { + table[seq] = key + } + } + + return table +} diff --git a/vendor/github.com/charmbracelet/x/input/termcap.go b/vendor/github.com/charmbracelet/x/input/termcap.go new file mode 100644 index 00000000..82045b1d --- /dev/null +++ b/vendor/github.com/charmbracelet/x/input/termcap.go @@ -0,0 +1,49 @@ +package input + +import ( + "bytes" + "encoding/hex" +) + +// TermcapEvent represents a Termcap response event. Termcap responses are +// generated by the terminal in response to RequestTermcap (XTGETTCAP) +// requests. +// +// See: https://invisible-island.net/xterm/ctlseqs/ctlseqs.html#h3-Operating-System-Commands +type TermcapEvent struct { + Values map[string]string + IsValid bool +} + +func parseTermcap(data []byte) TermcapEvent { + // XTGETTCAP + if len(data) == 0 { + return TermcapEvent{} + } + + tc := TermcapEvent{Values: make(map[string]string)} + split := bytes.Split(data, []byte{';'}) + for _, s := range split { + parts := bytes.SplitN(s, []byte{'='}, 2) + if len(parts) == 0 { + return TermcapEvent{} + } + + name, err := hex.DecodeString(string(parts[0])) + if err != nil || len(name) == 0 { + continue + } + + var value []byte + if len(parts) > 1 { + value, err = hex.DecodeString(string(parts[1])) + if err != nil { + continue + } + } + + tc.Values[string(name)] = string(value) + } + + return tc +} diff --git a/vendor/github.com/charmbracelet/x/input/terminfo.go b/vendor/github.com/charmbracelet/x/input/terminfo.go new file mode 100644 index 00000000..debf8511 --- /dev/null +++ b/vendor/github.com/charmbracelet/x/input/terminfo.go @@ -0,0 +1,277 @@ +package input + +import ( + "strings" + + "github.com/xo/terminfo" +) + +func buildTerminfoKeys(flags int, term string) map[string]Key { + table := make(map[string]Key) + ti, _ := terminfo.Load(term) + if ti == nil { + return table + } + + tiTable := defaultTerminfoKeys(flags) + + // Default keys + for name, seq := range ti.StringCapsShort() { + if !strings.HasPrefix(name, "k") || len(seq) == 0 { + continue + } + + if k, ok := tiTable[name]; ok { + table[string(seq)] = k + } + } + + // Extended keys + for name, seq := range ti.ExtStringCapsShort() { + if !strings.HasPrefix(name, "k") || len(seq) == 0 { + continue + } + + if k, ok := tiTable[name]; ok { + table[string(seq)] = k + } + } + + return table +} + +// This returns a map of terminfo keys to key events. It's a mix of ncurses +// terminfo default and user-defined key capabilities. +// Upper-case caps that are defined in the default terminfo database are +// - kNXT +// - kPRV +// - kHOM +// - kEND +// - kDC +// - kIC +// - kLFT +// - kRIT +// +// See https://man7.org/linux/man-pages/man5/terminfo.5.html +// See https://github.com/mirror/ncurses/blob/master/include/Caps-ncurses +func defaultTerminfoKeys(flags int) map[string]Key { + keys := map[string]Key{ + "kcuu1": {Sym: KeyUp}, + "kUP": {Sym: KeyUp, Mod: Shift}, + "kUP3": {Sym: KeyUp, Mod: Alt}, + "kUP4": {Sym: KeyUp, Mod: Shift | Alt}, + "kUP5": {Sym: KeyUp, Mod: Ctrl}, + "kUP6": {Sym: KeyUp, Mod: Shift | Ctrl}, + "kUP7": {Sym: KeyUp, Mod: Alt | Ctrl}, + "kUP8": {Sym: KeyUp, Mod: Shift | Alt | Ctrl}, + "kcud1": {Sym: KeyDown}, + "kDN": {Sym: KeyDown, Mod: Shift}, + "kDN3": {Sym: KeyDown, Mod: Alt}, + "kDN4": {Sym: KeyDown, Mod: Shift | Alt}, + "kDN5": {Sym: KeyDown, Mod: Ctrl}, + "kDN7": {Sym: KeyDown, Mod: Alt | Ctrl}, + "kDN6": {Sym: KeyDown, Mod: Shift | Ctrl}, + "kDN8": {Sym: KeyDown, Mod: Shift | Alt | Ctrl}, + "kcub1": {Sym: KeyLeft}, + "kLFT": {Sym: KeyLeft, Mod: Shift}, + "kLFT3": {Sym: KeyLeft, Mod: Alt}, + "kLFT4": {Sym: KeyLeft, Mod: Shift | Alt}, + "kLFT5": {Sym: KeyLeft, Mod: Ctrl}, + "kLFT6": {Sym: KeyLeft, Mod: Shift | Ctrl}, + "kLFT7": {Sym: KeyLeft, Mod: Alt | Ctrl}, + "kLFT8": {Sym: KeyLeft, Mod: Shift | Alt | Ctrl}, + "kcuf1": {Sym: KeyRight}, + "kRIT": {Sym: KeyRight, Mod: Shift}, + "kRIT3": {Sym: KeyRight, Mod: Alt}, + "kRIT4": {Sym: KeyRight, Mod: Shift | Alt}, + "kRIT5": {Sym: KeyRight, Mod: Ctrl}, + "kRIT6": {Sym: KeyRight, Mod: Shift | Ctrl}, + "kRIT7": {Sym: KeyRight, Mod: Alt | Ctrl}, + "kRIT8": {Sym: KeyRight, Mod: Shift | Alt | Ctrl}, + "kich1": {Sym: KeyInsert}, + "kIC": {Sym: KeyInsert, Mod: Shift}, + "kIC3": {Sym: KeyInsert, Mod: Alt}, + "kIC4": {Sym: KeyInsert, Mod: Shift | Alt}, + "kIC5": {Sym: KeyInsert, Mod: Ctrl}, + "kIC6": {Sym: KeyInsert, Mod: Shift | Ctrl}, + "kIC7": {Sym: KeyInsert, Mod: Alt | Ctrl}, + "kIC8": {Sym: KeyInsert, Mod: Shift | Alt | Ctrl}, + "kdch1": {Sym: KeyDelete}, + "kDC": {Sym: KeyDelete, Mod: Shift}, + "kDC3": {Sym: KeyDelete, Mod: Alt}, + "kDC4": {Sym: KeyDelete, Mod: Shift | Alt}, + "kDC5": {Sym: KeyDelete, Mod: Ctrl}, + "kDC6": {Sym: KeyDelete, Mod: Shift | Ctrl}, + "kDC7": {Sym: KeyDelete, Mod: Alt | Ctrl}, + "kDC8": {Sym: KeyDelete, Mod: Shift | Alt | Ctrl}, + "khome": {Sym: KeyHome}, + "kHOM": {Sym: KeyHome, Mod: Shift}, + "kHOM3": {Sym: KeyHome, Mod: Alt}, + "kHOM4": {Sym: KeyHome, Mod: Shift | Alt}, + "kHOM5": {Sym: KeyHome, Mod: Ctrl}, + "kHOM6": {Sym: KeyHome, Mod: Shift | Ctrl}, + "kHOM7": {Sym: KeyHome, Mod: Alt | Ctrl}, + "kHOM8": {Sym: KeyHome, Mod: Shift | Alt | Ctrl}, + "kend": {Sym: KeyEnd}, + "kEND": {Sym: KeyEnd, Mod: Shift}, + "kEND3": {Sym: KeyEnd, Mod: Alt}, + "kEND4": {Sym: KeyEnd, Mod: Shift | Alt}, + "kEND5": {Sym: KeyEnd, Mod: Ctrl}, + "kEND6": {Sym: KeyEnd, Mod: Shift | Ctrl}, + "kEND7": {Sym: KeyEnd, Mod: Alt | Ctrl}, + "kEND8": {Sym: KeyEnd, Mod: Shift | Alt | Ctrl}, + "kpp": {Sym: KeyPgUp}, + "kprv": {Sym: KeyPgUp}, + "kPRV": {Sym: KeyPgUp, Mod: Shift}, + "kPRV3": {Sym: KeyPgUp, Mod: Alt}, + "kPRV4": {Sym: KeyPgUp, Mod: Shift | Alt}, + "kPRV5": {Sym: KeyPgUp, Mod: Ctrl}, + "kPRV6": {Sym: KeyPgUp, Mod: Shift | Ctrl}, + "kPRV7": {Sym: KeyPgUp, Mod: Alt | Ctrl}, + "kPRV8": {Sym: KeyPgUp, Mod: Shift | Alt | Ctrl}, + "knp": {Sym: KeyPgDown}, + "knxt": {Sym: KeyPgDown}, + "kNXT": {Sym: KeyPgDown, Mod: Shift}, + "kNXT3": {Sym: KeyPgDown, Mod: Alt}, + "kNXT4": {Sym: KeyPgDown, Mod: Shift | Alt}, + "kNXT5": {Sym: KeyPgDown, Mod: Ctrl}, + "kNXT6": {Sym: KeyPgDown, Mod: Shift | Ctrl}, + "kNXT7": {Sym: KeyPgDown, Mod: Alt | Ctrl}, + "kNXT8": {Sym: KeyPgDown, Mod: Shift | Alt | Ctrl}, + + "kbs": {Sym: KeyBackspace}, + "kcbt": {Sym: KeyTab, Mod: Shift}, + + // Function keys + // This only includes the first 12 function keys. The rest are treated + // as modifiers of the first 12. + // Take a look at XTerm modifyFunctionKeys + // + // XXX: To use unambiguous function keys, use fixterms or kitty clipboard. + // + // See https://invisible-island.net/xterm/manpage/xterm.html#VT100-Widget-Resources:modifyFunctionKeys + // See https://invisible-island.net/xterm/terminfo.html + + "kf1": {Sym: KeyF1}, + "kf2": {Sym: KeyF2}, + "kf3": {Sym: KeyF3}, + "kf4": {Sym: KeyF4}, + "kf5": {Sym: KeyF5}, + "kf6": {Sym: KeyF6}, + "kf7": {Sym: KeyF7}, + "kf8": {Sym: KeyF8}, + "kf9": {Sym: KeyF9}, + "kf10": {Sym: KeyF10}, + "kf11": {Sym: KeyF11}, + "kf12": {Sym: KeyF12}, + "kf13": {Sym: KeyF1, Mod: Shift}, + "kf14": {Sym: KeyF2, Mod: Shift}, + "kf15": {Sym: KeyF3, Mod: Shift}, + "kf16": {Sym: KeyF4, Mod: Shift}, + "kf17": {Sym: KeyF5, Mod: Shift}, + "kf18": {Sym: KeyF6, Mod: Shift}, + "kf19": {Sym: KeyF7, Mod: Shift}, + "kf20": {Sym: KeyF8, Mod: Shift}, + "kf21": {Sym: KeyF9, Mod: Shift}, + "kf22": {Sym: KeyF10, Mod: Shift}, + "kf23": {Sym: KeyF11, Mod: Shift}, + "kf24": {Sym: KeyF12, Mod: Shift}, + "kf25": {Sym: KeyF1, Mod: Ctrl}, + "kf26": {Sym: KeyF2, Mod: Ctrl}, + "kf27": {Sym: KeyF3, Mod: Ctrl}, + "kf28": {Sym: KeyF4, Mod: Ctrl}, + "kf29": {Sym: KeyF5, Mod: Ctrl}, + "kf30": {Sym: KeyF6, Mod: Ctrl}, + "kf31": {Sym: KeyF7, Mod: Ctrl}, + "kf32": {Sym: KeyF8, Mod: Ctrl}, + "kf33": {Sym: KeyF9, Mod: Ctrl}, + "kf34": {Sym: KeyF10, Mod: Ctrl}, + "kf35": {Sym: KeyF11, Mod: Ctrl}, + "kf36": {Sym: KeyF12, Mod: Ctrl}, + "kf37": {Sym: KeyF1, Mod: Shift | Ctrl}, + "kf38": {Sym: KeyF2, Mod: Shift | Ctrl}, + "kf39": {Sym: KeyF3, Mod: Shift | Ctrl}, + "kf40": {Sym: KeyF4, Mod: Shift | Ctrl}, + "kf41": {Sym: KeyF5, Mod: Shift | Ctrl}, + "kf42": {Sym: KeyF6, Mod: Shift | Ctrl}, + "kf43": {Sym: KeyF7, Mod: Shift | Ctrl}, + "kf44": {Sym: KeyF8, Mod: Shift | Ctrl}, + "kf45": {Sym: KeyF9, Mod: Shift | Ctrl}, + "kf46": {Sym: KeyF10, Mod: Shift | Ctrl}, + "kf47": {Sym: KeyF11, Mod: Shift | Ctrl}, + "kf48": {Sym: KeyF12, Mod: Shift | Ctrl}, + "kf49": {Sym: KeyF1, Mod: Alt}, + "kf50": {Sym: KeyF2, Mod: Alt}, + "kf51": {Sym: KeyF3, Mod: Alt}, + "kf52": {Sym: KeyF4, Mod: Alt}, + "kf53": {Sym: KeyF5, Mod: Alt}, + "kf54": {Sym: KeyF6, Mod: Alt}, + "kf55": {Sym: KeyF7, Mod: Alt}, + "kf56": {Sym: KeyF8, Mod: Alt}, + "kf57": {Sym: KeyF9, Mod: Alt}, + "kf58": {Sym: KeyF10, Mod: Alt}, + "kf59": {Sym: KeyF11, Mod: Alt}, + "kf60": {Sym: KeyF12, Mod: Alt}, + "kf61": {Sym: KeyF1, Mod: Shift | Alt}, + "kf62": {Sym: KeyF2, Mod: Shift | Alt}, + "kf63": {Sym: KeyF3, Mod: Shift | Alt}, + } + + // Preserve F keys from F13 to F63 instead of using them for F-keys + // modifiers. + if flags&FlagFKeys != 0 { + keys["kf13"] = Key{Sym: KeyF13} + keys["kf14"] = Key{Sym: KeyF14} + keys["kf15"] = Key{Sym: KeyF15} + keys["kf16"] = Key{Sym: KeyF16} + keys["kf17"] = Key{Sym: KeyF17} + keys["kf18"] = Key{Sym: KeyF18} + keys["kf19"] = Key{Sym: KeyF19} + keys["kf20"] = Key{Sym: KeyF20} + keys["kf21"] = Key{Sym: KeyF21} + keys["kf22"] = Key{Sym: KeyF22} + keys["kf23"] = Key{Sym: KeyF23} + keys["kf24"] = Key{Sym: KeyF24} + keys["kf25"] = Key{Sym: KeyF25} + keys["kf26"] = Key{Sym: KeyF26} + keys["kf27"] = Key{Sym: KeyF27} + keys["kf28"] = Key{Sym: KeyF28} + keys["kf29"] = Key{Sym: KeyF29} + keys["kf30"] = Key{Sym: KeyF30} + keys["kf31"] = Key{Sym: KeyF31} + keys["kf32"] = Key{Sym: KeyF32} + keys["kf33"] = Key{Sym: KeyF33} + keys["kf34"] = Key{Sym: KeyF34} + keys["kf35"] = Key{Sym: KeyF35} + keys["kf36"] = Key{Sym: KeyF36} + keys["kf37"] = Key{Sym: KeyF37} + keys["kf38"] = Key{Sym: KeyF38} + keys["kf39"] = Key{Sym: KeyF39} + keys["kf40"] = Key{Sym: KeyF40} + keys["kf41"] = Key{Sym: KeyF41} + keys["kf42"] = Key{Sym: KeyF42} + keys["kf43"] = Key{Sym: KeyF43} + keys["kf44"] = Key{Sym: KeyF44} + keys["kf45"] = Key{Sym: KeyF45} + keys["kf46"] = Key{Sym: KeyF46} + keys["kf47"] = Key{Sym: KeyF47} + keys["kf48"] = Key{Sym: KeyF48} + keys["kf49"] = Key{Sym: KeyF49} + keys["kf50"] = Key{Sym: KeyF50} + keys["kf51"] = Key{Sym: KeyF51} + keys["kf52"] = Key{Sym: KeyF52} + keys["kf53"] = Key{Sym: KeyF53} + keys["kf54"] = Key{Sym: KeyF54} + keys["kf55"] = Key{Sym: KeyF55} + keys["kf56"] = Key{Sym: KeyF56} + keys["kf57"] = Key{Sym: KeyF57} + keys["kf58"] = Key{Sym: KeyF58} + keys["kf59"] = Key{Sym: KeyF59} + keys["kf60"] = Key{Sym: KeyF60} + keys["kf61"] = Key{Sym: KeyF61} + keys["kf62"] = Key{Sym: KeyF62} + keys["kf63"] = Key{Sym: KeyF63} + } + + return keys +} diff --git a/vendor/github.com/charmbracelet/x/input/win32input.go b/vendor/github.com/charmbracelet/x/input/win32input.go new file mode 100644 index 00000000..6fb55acb --- /dev/null +++ b/vendor/github.com/charmbracelet/x/input/win32input.go @@ -0,0 +1,240 @@ +package input + +import ( + "unicode" + + "github.com/erikgeiser/coninput" +) + +func parseWin32InputKeyEvent(vkc coninput.VirtualKeyCode, _ coninput.VirtualKeyCode, r rune, keyDown bool, cks coninput.ControlKeyState, repeatCount uint16) Event { + var key Key + isCtrl := cks.Contains(coninput.LEFT_CTRL_PRESSED | coninput.RIGHT_CTRL_PRESSED) + switch vkc { + case coninput.VK_SHIFT: + // We currently ignore these keys when they are pressed alone. + return nil + case coninput.VK_MENU: + if cks.Contains(coninput.LEFT_ALT_PRESSED) { + key = Key{Sym: KeyLeftAlt} + } else if cks.Contains(coninput.RIGHT_ALT_PRESSED) { + key = Key{Sym: KeyRightAlt} + } else if !keyDown { + return nil + } + case coninput.VK_CONTROL: + if cks.Contains(coninput.LEFT_CTRL_PRESSED) { + key = Key{Sym: KeyLeftCtrl} + } else if cks.Contains(coninput.RIGHT_CTRL_PRESSED) { + key = Key{Sym: KeyRightCtrl} + } else if !keyDown { + return nil + } + case coninput.VK_CAPITAL: + key = Key{Sym: KeyCapsLock} + default: + var ok bool + key, ok = vkKeyEvent[vkc] + if !ok { + if isCtrl { + key = vkCtrlRune(key, r, vkc) + } else { + key = Key{Rune: r} + } + } + } + + if isCtrl { + key.Mod |= Ctrl + } + if cks.Contains(coninput.LEFT_ALT_PRESSED | coninput.RIGHT_ALT_PRESSED) { + key.Mod |= Alt + } + if cks.Contains(coninput.SHIFT_PRESSED) { + key.Mod |= Shift + } + if cks.Contains(coninput.CAPSLOCK_ON) { + key.Mod |= CapsLock + } + if cks.Contains(coninput.NUMLOCK_ON) { + key.Mod |= NumLock + } + if cks.Contains(coninput.SCROLLLOCK_ON) { + key.Mod |= ScrollLock + } + + // Use the unshifted key + if cks.Contains(coninput.SHIFT_PRESSED ^ coninput.CAPSLOCK_ON) { + key.AltRune = unicode.ToUpper(key.Rune) + } else { + key.AltRune = unicode.ToLower(key.Rune) + } + + var e Event = KeyDownEvent(key) + key.IsRepeat = repeatCount > 1 + if !keyDown { + e = KeyUpEvent(key) + } + + if repeatCount <= 1 { + return e + } + + var kevents []Event + for i := 0; i < int(repeatCount); i++ { + kevents = append(kevents, e) + } + + return MultiEvent(kevents) +} + +var vkKeyEvent = map[coninput.VirtualKeyCode]Key{ + coninput.VK_RETURN: {Sym: KeyEnter}, + coninput.VK_BACK: {Sym: KeyBackspace}, + coninput.VK_TAB: {Sym: KeyTab}, + coninput.VK_ESCAPE: {Sym: KeyEscape}, + coninput.VK_SPACE: {Sym: KeySpace, Rune: ' '}, + coninput.VK_UP: {Sym: KeyUp}, + coninput.VK_DOWN: {Sym: KeyDown}, + coninput.VK_RIGHT: {Sym: KeyRight}, + coninput.VK_LEFT: {Sym: KeyLeft}, + coninput.VK_HOME: {Sym: KeyHome}, + coninput.VK_END: {Sym: KeyEnd}, + coninput.VK_PRIOR: {Sym: KeyPgUp}, + coninput.VK_NEXT: {Sym: KeyPgDown}, + coninput.VK_DELETE: {Sym: KeyDelete}, + coninput.VK_SELECT: {Sym: KeySelect}, + coninput.VK_SNAPSHOT: {Sym: KeyPrintScreen}, + coninput.VK_INSERT: {Sym: KeyInsert}, + coninput.VK_LWIN: {Sym: KeyLeftSuper}, + coninput.VK_RWIN: {Sym: KeyRightSuper}, + coninput.VK_APPS: {Sym: KeyMenu}, + coninput.VK_NUMPAD0: {Sym: KeyKp0}, + coninput.VK_NUMPAD1: {Sym: KeyKp1}, + coninput.VK_NUMPAD2: {Sym: KeyKp2}, + coninput.VK_NUMPAD3: {Sym: KeyKp3}, + coninput.VK_NUMPAD4: {Sym: KeyKp4}, + coninput.VK_NUMPAD5: {Sym: KeyKp5}, + coninput.VK_NUMPAD6: {Sym: KeyKp6}, + coninput.VK_NUMPAD7: {Sym: KeyKp7}, + coninput.VK_NUMPAD8: {Sym: KeyKp8}, + coninput.VK_NUMPAD9: {Sym: KeyKp9}, + coninput.VK_MULTIPLY: {Sym: KeyKpMultiply}, + coninput.VK_ADD: {Sym: KeyKpPlus}, + coninput.VK_SEPARATOR: {Sym: KeyKpComma}, + coninput.VK_SUBTRACT: {Sym: KeyKpMinus}, + coninput.VK_DECIMAL: {Sym: KeyKpDecimal}, + coninput.VK_DIVIDE: {Sym: KeyKpDivide}, + coninput.VK_F1: {Sym: KeyF1}, + coninput.VK_F2: {Sym: KeyF2}, + coninput.VK_F3: {Sym: KeyF3}, + coninput.VK_F4: {Sym: KeyF4}, + coninput.VK_F5: {Sym: KeyF5}, + coninput.VK_F6: {Sym: KeyF6}, + coninput.VK_F7: {Sym: KeyF7}, + coninput.VK_F8: {Sym: KeyF8}, + coninput.VK_F9: {Sym: KeyF9}, + coninput.VK_F10: {Sym: KeyF10}, + coninput.VK_F11: {Sym: KeyF11}, + coninput.VK_F12: {Sym: KeyF12}, + coninput.VK_F13: {Sym: KeyF13}, + coninput.VK_F14: {Sym: KeyF14}, + coninput.VK_F15: {Sym: KeyF15}, + coninput.VK_F16: {Sym: KeyF16}, + coninput.VK_F17: {Sym: KeyF17}, + coninput.VK_F18: {Sym: KeyF18}, + coninput.VK_F19: {Sym: KeyF19}, + coninput.VK_F20: {Sym: KeyF20}, + coninput.VK_F21: {Sym: KeyF21}, + coninput.VK_F22: {Sym: KeyF22}, + coninput.VK_F23: {Sym: KeyF23}, + coninput.VK_F24: {Sym: KeyF24}, + coninput.VK_NUMLOCK: {Sym: KeyNumLock}, + coninput.VK_SCROLL: {Sym: KeyScrollLock}, + coninput.VK_LSHIFT: {Sym: KeyLeftShift}, + coninput.VK_RSHIFT: {Sym: KeyRightShift}, + coninput.VK_LCONTROL: {Sym: KeyLeftCtrl}, + coninput.VK_RCONTROL: {Sym: KeyRightCtrl}, + coninput.VK_LMENU: {Sym: KeyLeftAlt}, + coninput.VK_RMENU: {Sym: KeyRightAlt}, + coninput.VK_OEM_4: {Rune: '['}, + // TODO: add more keys +} + +func vkCtrlRune(k Key, r rune, kc coninput.VirtualKeyCode) Key { + switch r { + case '@': + k.Rune = '@' + case '\x01': + k.Rune = 'a' + case '\x02': + k.Rune = 'b' + case '\x03': + k.Rune = 'c' + case '\x04': + k.Rune = 'd' + case '\x05': + k.Rune = 'e' + case '\x06': + k.Rune = 'f' + case '\a': + k.Rune = 'g' + case '\b': + k.Rune = 'h' + case '\t': + k.Rune = 'i' + case '\n': + k.Rune = 'j' + case '\v': + k.Rune = 'k' + case '\f': + k.Rune = 'l' + case '\r': + k.Rune = 'm' + case '\x0e': + k.Rune = 'n' + case '\x0f': + k.Rune = 'o' + case '\x10': + k.Rune = 'p' + case '\x11': + k.Rune = 'q' + case '\x12': + k.Rune = 'r' + case '\x13': + k.Rune = 's' + case '\x14': + k.Rune = 't' + case '\x15': + k.Rune = 'u' + case '\x16': + k.Rune = 'v' + case '\x17': + k.Rune = 'w' + case '\x18': + k.Rune = 'x' + case '\x19': + k.Rune = 'y' + case '\x1a': + k.Rune = 'z' + case '\x1b': + k.Rune = ']' + case '\x1c': + k.Rune = '\\' + case '\x1f': + k.Rune = '_' + } + + switch kc { + case coninput.VK_OEM_4: + k.Rune = '[' + } + + // https://learn.microsoft.com/en-us/windows/win32/inputdev/virtual-key-codes + if k.Rune == 0 && + (kc >= 0x30 && kc <= 0x39) || + (kc >= 0x41 && kc <= 0x5a) { + k.Rune = rune(kc) + } + + return k +} diff --git a/vendor/github.com/charmbracelet/x/input/xterm.go b/vendor/github.com/charmbracelet/x/input/xterm.go new file mode 100644 index 00000000..f80ecf4b --- /dev/null +++ b/vendor/github.com/charmbracelet/x/input/xterm.go @@ -0,0 +1,40 @@ +package input + +import ( + "github.com/charmbracelet/x/ansi" +) + +func parseXTermModifyOtherKeys(csi *ansi.CsiSequence) Event { + // XTerm modify other keys starts with ESC [ 27 ; ; ~ + mod := KeyMod(csi.Param(1) - 1) + r := rune(csi.Param(2)) + + switch r { + case ansi.BS: + return KeyDownEvent{Mod: mod, Sym: KeyBackspace} + case ansi.HT: + return KeyDownEvent{Mod: mod, Sym: KeyTab} + case ansi.CR: + return KeyDownEvent{Mod: mod, Sym: KeyEnter} + case ansi.ESC: + return KeyDownEvent{Mod: mod, Sym: KeyEscape} + case ansi.DEL: + return KeyDownEvent{Mod: mod, Sym: KeyBackspace} + } + + // CSI 27 ; ; ~ keys defined in XTerm modifyOtherKeys + return KeyDownEvent{ + Mod: mod, + Rune: r, + } +} + +// ModifyOtherKeysEvent represents a modifyOtherKeys event. +// +// 0: disable +// 1: enable mode 1 +// 2: enable mode 2 +// +// See: https://invisible-island.net/xterm/ctlseqs/ctlseqs.html#h3-Functions-using-CSI-_-ordered-by-the-final-character_s_ +// See: https://invisible-island.net/xterm/manpage/xterm.html#VT100-Widget-Resources:modifyOtherKeys +type ModifyOtherKeysEvent uint8 diff --git a/vendor/github.com/charmbracelet/x/term/LICENSE b/vendor/github.com/charmbracelet/x/term/LICENSE new file mode 100644 index 00000000..65a5654e --- /dev/null +++ b/vendor/github.com/charmbracelet/x/term/LICENSE @@ -0,0 +1,21 @@ +MIT License + +Copyright (c) 2023 Charmbracelet, Inc. + +Permission is hereby granted, free of charge, to any person obtaining a copy +of this software and associated documentation files (the "Software"), to deal +in the Software without restriction, including without limitation the rights +to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +copies of the Software, and to permit persons to whom the Software is +furnished to do so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in all +copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE +SOFTWARE. diff --git a/vendor/github.com/charmbracelet/x/term/term.go b/vendor/github.com/charmbracelet/x/term/term.go new file mode 100644 index 00000000..58d6522c --- /dev/null +++ b/vendor/github.com/charmbracelet/x/term/term.go @@ -0,0 +1,49 @@ +package term + +// State contains platform-specific state of a terminal. +type State struct { + state +} + +// IsTerminal returns whether the given file descriptor is a terminal. +func IsTerminal(fd uintptr) bool { + return isTerminal(fd) +} + +// MakeRaw puts the terminal connected to the given file descriptor into raw +// mode and returns the previous state of the terminal so that it can be +// restored. +func MakeRaw(fd uintptr) (*State, error) { + return makeRaw(fd) +} + +// GetState returns the current state of a terminal which may be useful to +// restore the terminal after a signal. +func GetState(fd uintptr) (*State, error) { + return getState(fd) +} + +// SetState sets the given state of the terminal. +func SetState(fd uintptr, state *State) error { + return setState(fd, state) +} + +// Restore restores the terminal connected to the given file descriptor to a +// previous state. +func Restore(fd uintptr, oldState *State) error { + return restore(fd, oldState) +} + +// GetSize returns the visible dimensions of the given terminal. +// +// These dimensions don't include any scrollback buffer height. +func GetSize(fd uintptr) (width, height int, err error) { + return getSize(fd) +} + +// ReadPassword reads a line of input from a terminal without local echo. This +// is commonly used for inputting passwords and other sensitive data. The slice +// returned does not include the \n. +func ReadPassword(fd uintptr) ([]byte, error) { + return readPassword(fd) +} diff --git a/vendor/github.com/charmbracelet/x/term/term_other.go b/vendor/github.com/charmbracelet/x/term/term_other.go new file mode 100644 index 00000000..092c7e9d --- /dev/null +++ b/vendor/github.com/charmbracelet/x/term/term_other.go @@ -0,0 +1,39 @@ +//go:build !aix && !darwin && !dragonfly && !freebsd && !linux && !netbsd && !openbsd && !zos && !windows && !solaris && !plan9 +// +build !aix,!darwin,!dragonfly,!freebsd,!linux,!netbsd,!openbsd,!zos,!windows,!solaris,!plan9 + +package term + +import ( + "fmt" + "runtime" +) + +type state struct{} + +func isTerminal(fd uintptr) bool { + return false +} + +func makeRaw(fd uintptr) (*State, error) { + return nil, fmt.Errorf("terminal: MakeRaw not implemented on %s/%s", runtime.GOOS, runtime.GOARCH) +} + +func getState(fd uintptr) (*State, error) { + return nil, fmt.Errorf("terminal: GetState not implemented on %s/%s", runtime.GOOS, runtime.GOARCH) +} + +func restore(fd uintptr, state *State) error { + return fmt.Errorf("terminal: Restore not implemented on %s/%s", runtime.GOOS, runtime.GOARCH) +} + +func getSize(fd uintptr) (width, height int, err error) { + return 0, 0, fmt.Errorf("terminal: GetSize not implemented on %s/%s", runtime.GOOS, runtime.GOARCH) +} + +func setState(fd uintptr, state *State) error { + return fmt.Errorf("terminal: SetState not implemented on %s/%s", runtime.GOOS, runtime.GOARCH) +} + +func readPassword(fd uintptr) ([]byte, error) { + return nil, fmt.Errorf("terminal: ReadPassword not implemented on %s/%s", runtime.GOOS, runtime.GOARCH) +} diff --git a/vendor/github.com/charmbracelet/x/term/term_unix.go b/vendor/github.com/charmbracelet/x/term/term_unix.go new file mode 100644 index 00000000..1459cb1b --- /dev/null +++ b/vendor/github.com/charmbracelet/x/term/term_unix.go @@ -0,0 +1,96 @@ +//go:build aix || darwin || dragonfly || freebsd || linux || netbsd || openbsd || solaris || zos +// +build aix darwin dragonfly freebsd linux netbsd openbsd solaris zos + +package term + +import ( + "golang.org/x/sys/unix" +) + +type state struct { + unix.Termios +} + +func isTerminal(fd uintptr) bool { + _, err := unix.IoctlGetTermios(int(fd), ioctlReadTermios) + return err == nil +} + +func makeRaw(fd uintptr) (*State, error) { + termios, err := unix.IoctlGetTermios(int(fd), ioctlReadTermios) + if err != nil { + return nil, err + } + + oldState := State{state{Termios: *termios}} + + // This attempts to replicate the behaviour documented for cfmakeraw in + // the termios(3) manpage. + termios.Iflag &^= unix.IGNBRK | unix.BRKINT | unix.PARMRK | unix.ISTRIP | unix.INLCR | unix.IGNCR | unix.ICRNL | unix.IXON + termios.Oflag &^= unix.OPOST + termios.Lflag &^= unix.ECHO | unix.ECHONL | unix.ICANON | unix.ISIG | unix.IEXTEN + termios.Cflag &^= unix.CSIZE | unix.PARENB + termios.Cflag |= unix.CS8 + termios.Cc[unix.VMIN] = 1 + termios.Cc[unix.VTIME] = 0 + if err := unix.IoctlSetTermios(int(fd), ioctlWriteTermios, termios); err != nil { + return nil, err + } + + return &oldState, nil +} + +func setState(fd uintptr, state *State) error { + var termios *unix.Termios + if state != nil { + termios = &state.Termios + } + return unix.IoctlSetTermios(int(fd), ioctlWriteTermios, termios) +} + +func getState(fd uintptr) (*State, error) { + termios, err := unix.IoctlGetTermios(int(fd), ioctlReadTermios) + if err != nil { + return nil, err + } + + return &State{state{Termios: *termios}}, nil +} + +func restore(fd uintptr, state *State) error { + return unix.IoctlSetTermios(int(fd), ioctlWriteTermios, &state.Termios) +} + +func getSize(fd uintptr) (width, height int, err error) { + ws, err := unix.IoctlGetWinsize(int(fd), unix.TIOCGWINSZ) + if err != nil { + return 0, 0, err + } + return int(ws.Col), int(ws.Row), nil +} + +// passwordReader is an io.Reader that reads from a specific file descriptor. +type passwordReader int + +func (r passwordReader) Read(buf []byte) (int, error) { + return unix.Read(int(r), buf) +} + +func readPassword(fd uintptr) ([]byte, error) { + termios, err := unix.IoctlGetTermios(int(fd), ioctlReadTermios) + if err != nil { + return nil, err + } + + newState := *termios + newState.Lflag &^= unix.ECHO + newState.Lflag |= unix.ICANON | unix.ISIG + newState.Iflag |= unix.ICRNL + if err := unix.IoctlSetTermios(int(fd), ioctlWriteTermios, &newState); err != nil { + return nil, err + } + + defer unix.IoctlSetTermios(int(fd), ioctlWriteTermios, termios) + + return readPasswordLine(passwordReader(fd)) +} diff --git a/vendor/github.com/charmbracelet/x/term/term_unix_bsd.go b/vendor/github.com/charmbracelet/x/term/term_unix_bsd.go new file mode 100644 index 00000000..b435031a --- /dev/null +++ b/vendor/github.com/charmbracelet/x/term/term_unix_bsd.go @@ -0,0 +1,11 @@ +//go:build darwin || dragonfly || freebsd || netbsd || openbsd +// +build darwin dragonfly freebsd netbsd openbsd + +package term + +import "golang.org/x/sys/unix" + +const ( + ioctlReadTermios = unix.TIOCGETA + ioctlWriteTermios = unix.TIOCSETA +) diff --git a/vendor/github.com/charmbracelet/x/term/term_unix_other.go b/vendor/github.com/charmbracelet/x/term/term_unix_other.go new file mode 100644 index 00000000..ee2a29eb --- /dev/null +++ b/vendor/github.com/charmbracelet/x/term/term_unix_other.go @@ -0,0 +1,11 @@ +//go:build aix || linux || solaris || zos +// +build aix linux solaris zos + +package term + +import "golang.org/x/sys/unix" + +const ( + ioctlReadTermios = unix.TCGETS + ioctlWriteTermios = unix.TCSETS +) diff --git a/vendor/github.com/charmbracelet/x/term/term_windows.go b/vendor/github.com/charmbracelet/x/term/term_windows.go new file mode 100644 index 00000000..9e1c4740 --- /dev/null +++ b/vendor/github.com/charmbracelet/x/term/term_windows.go @@ -0,0 +1,86 @@ +//go:build windows +// +build windows + +package term + +import ( + "os" + + "golang.org/x/sys/windows" +) + +type state struct { + Mode uint32 +} + +func isTerminal(fd uintptr) bool { + var st uint32 + err := windows.GetConsoleMode(windows.Handle(fd), &st) + return err == nil +} + +func makeRaw(fd uintptr) (*State, error) { + var st uint32 + if err := windows.GetConsoleMode(windows.Handle(fd), &st); err != nil { + return nil, err + } + raw := st &^ (windows.ENABLE_ECHO_INPUT | windows.ENABLE_PROCESSED_INPUT | windows.ENABLE_LINE_INPUT | windows.ENABLE_PROCESSED_OUTPUT) + if err := windows.SetConsoleMode(windows.Handle(fd), raw); err != nil { + return nil, err + } + return &State{state{st}}, nil +} + +func setState(fd uintptr, state *State) error { + var mode uint32 + if state != nil { + mode = state.Mode + } + return windows.SetConsoleMode(windows.Handle(fd), mode) +} + +func getState(fd uintptr) (*State, error) { + var st uint32 + if err := windows.GetConsoleMode(windows.Handle(fd), &st); err != nil { + return nil, err + } + return &State{state{st}}, nil +} + +func restore(fd uintptr, state *State) error { + return windows.SetConsoleMode(windows.Handle(fd), state.Mode) +} + +func getSize(fd uintptr) (width, height int, err error) { + var info windows.ConsoleScreenBufferInfo + if err := windows.GetConsoleScreenBufferInfo(windows.Handle(fd), &info); err != nil { + return 0, 0, err + } + return int(info.Window.Right - info.Window.Left + 1), int(info.Window.Bottom - info.Window.Top + 1), nil +} + +func readPassword(fd uintptr) ([]byte, error) { + var st uint32 + if err := windows.GetConsoleMode(windows.Handle(fd), &st); err != nil { + return nil, err + } + old := st + + st &^= (windows.ENABLE_ECHO_INPUT | windows.ENABLE_LINE_INPUT) + st |= (windows.ENABLE_PROCESSED_OUTPUT | windows.ENABLE_PROCESSED_INPUT) + if err := windows.SetConsoleMode(windows.Handle(fd), st); err != nil { + return nil, err + } + + defer windows.SetConsoleMode(windows.Handle(fd), old) + + var h windows.Handle + p, _ := windows.GetCurrentProcess() + if err := windows.DuplicateHandle(p, windows.Handle(fd), p, &h, 0, false, windows.DUPLICATE_SAME_ACCESS); err != nil { + return nil, err + } + + f := os.NewFile(uintptr(h), "stdin") + defer f.Close() + return readPasswordLine(f) +} diff --git a/vendor/github.com/charmbracelet/x/term/terminal.go b/vendor/github.com/charmbracelet/x/term/terminal.go new file mode 100644 index 00000000..09d83f9a --- /dev/null +++ b/vendor/github.com/charmbracelet/x/term/terminal.go @@ -0,0 +1,126 @@ +package term + +import ( + "image/color" + "io" + "time" + + "github.com/charmbracelet/x/ansi" + "github.com/charmbracelet/x/input" +) + +// File represents a file that has a file descriptor and can be read from, +// written to, and closed. +type File interface { + io.ReadWriteCloser + Fd() uintptr +} + +// QueryBackgroundColor queries the terminal for the background color. +// If the terminal does not support querying the background color, nil is +// returned. +// +// Note: you will need to set the input to raw mode before calling this +// function. +// +// state, _ := term.MakeRaw(in.Fd()) +// defer term.Restore(in.Fd(), state) +func QueryBackgroundColor(in io.Reader, out io.Writer) (c color.Color, err error) { + // nolint: errcheck + err = QueryTerminal(in, out, defaultQueryTimeout, + func(events []input.Event) bool { + for _, e := range events { + switch e := e.(type) { + case input.BackgroundColorEvent: + c = e.Color + continue // we need to consume the next DA1 event + case input.PrimaryDeviceAttributesEvent: + return false + } + } + return true + }, ansi.RequestBackgroundColor+ansi.RequestPrimaryDeviceAttributes) + return +} + +// QueryKittyKeyboard returns the enabled Kitty keyboard protocol options. +// -1 means the terminal does not support the feature. +// +// Note: you will need to set the input to raw mode before calling this +// function. +// +// state, _ := term.MakeRaw(in.Fd()) +// defer term.Restore(in.Fd(), state) +func QueryKittyKeyboard(in io.Reader, out io.Writer) (flags int, err error) { + flags = -1 + // nolint: errcheck + err = QueryTerminal(in, out, defaultQueryTimeout, + func(events []input.Event) bool { + for _, e := range events { + switch event := e.(type) { + case input.KittyKeyboardEvent: + flags = int(event) + continue // we need to consume the next DA1 event + case input.PrimaryDeviceAttributesEvent: + return false + } + } + return true + }, ansi.RequestKittyKeyboard+ansi.RequestPrimaryDeviceAttributes) + return +} + +const defaultQueryTimeout = time.Second * 2 + +// QueryTerminalFilter is a function that filters input events using a type +// switch. If false is returned, the QueryTerminal function will stop reading +// input. +type QueryTerminalFilter func(events []input.Event) bool + +// QueryTerminal queries the terminal for support of various features and +// returns a list of response events. +// Most of the time, you will need to set stdin to raw mode before calling this +// function. +// Note: This function will block until the terminal responds or the timeout +// is reached. +func QueryTerminal( + in io.Reader, + out io.Writer, + timeout time.Duration, + filter QueryTerminalFilter, + query string, +) error { + rd, err := input.NewDriver(in, "", 0) + if err != nil { + return err + } + + defer rd.Close() // nolint: errcheck + + done := make(chan struct{}, 1) + defer close(done) + go func() { + select { + case <-done: + case <-time.After(timeout): + rd.Cancel() + } + }() + + if _, err := io.WriteString(out, query); err != nil { + return err + } + + for { + events, err := rd.ReadEvents() + if err != nil { + return err + } + + if !filter(events) { + break + } + } + + return nil +} diff --git a/vendor/github.com/charmbracelet/x/term/util.go b/vendor/github.com/charmbracelet/x/term/util.go new file mode 100644 index 00000000..b7313418 --- /dev/null +++ b/vendor/github.com/charmbracelet/x/term/util.go @@ -0,0 +1,47 @@ +package term + +import ( + "io" + "runtime" +) + +// readPasswordLine reads from reader until it finds \n or io.EOF. +// The slice returned does not include the \n. +// readPasswordLine also ignores any \r it finds. +// Windows uses \r as end of line. So, on Windows, readPasswordLine +// reads until it finds \r and ignores any \n it finds during processing. +func readPasswordLine(reader io.Reader) ([]byte, error) { + var buf [1]byte + var ret []byte + + for { + n, err := reader.Read(buf[:]) + if n > 0 { + switch buf[0] { + case '\b': + if len(ret) > 0 { + ret = ret[:len(ret)-1] + } + case '\n': + if runtime.GOOS != "windows" { + return ret, nil + } + // otherwise ignore \n + case '\r': + if runtime.GOOS == "windows" { + return ret, nil + } + // otherwise ignore \r + default: + ret = append(ret, buf[0]) + } + continue + } + if err != nil { + if err == io.EOF && len(ret) > 0 { + return ret, nil + } + return ret, err + } + } +} diff --git a/vendor/github.com/charmbracelet/x/windows/LICENSE b/vendor/github.com/charmbracelet/x/windows/LICENSE new file mode 100644 index 00000000..65a5654e --- /dev/null +++ b/vendor/github.com/charmbracelet/x/windows/LICENSE @@ -0,0 +1,21 @@ +MIT License + +Copyright (c) 2023 Charmbracelet, Inc. + +Permission is hereby granted, free of charge, to any person obtaining a copy +of this software and associated documentation files (the "Software"), to deal +in the Software without restriction, including without limitation the rights +to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +copies of the Software, and to permit persons to whom the Software is +furnished to do so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in all +copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE +SOFTWARE. diff --git a/vendor/github.com/charmbracelet/x/windows/doc.go b/vendor/github.com/charmbracelet/x/windows/doc.go new file mode 100644 index 00000000..15fc9ee9 --- /dev/null +++ b/vendor/github.com/charmbracelet/x/windows/doc.go @@ -0,0 +1,3 @@ +package windows + +//go generate go run golang.org/x/sys/windows/mkwinsyscall -output zsyscall_windows.go syscall_windows.go diff --git a/vendor/github.com/charmbracelet/x/windows/syscall_windows.go b/vendor/github.com/charmbracelet/x/windows/syscall_windows.go new file mode 100644 index 00000000..dd49431f --- /dev/null +++ b/vendor/github.com/charmbracelet/x/windows/syscall_windows.go @@ -0,0 +1,10 @@ +package windows + +import "golang.org/x/sys/windows" + +var NewLazySystemDLL = windows.NewLazySystemDLL + +type Handle = windows.Handle + +//sys GetKeyboardLayout(threadId uint32) (hkl Handle, err error) = user32.GetKeyboardLayout +//sys ToUnicodeEx(vkey uint32, scancode uint32, keystate *byte, pwszBuff *uint16, cchBuff int32, flags uint32, hkl Handle) (ret int32, err error) = user32.ToUnicodeEx diff --git a/vendor/github.com/charmbracelet/x/windows/zsyscall_windows.go b/vendor/github.com/charmbracelet/x/windows/zsyscall_windows.go new file mode 100644 index 00000000..97096a42 --- /dev/null +++ b/vendor/github.com/charmbracelet/x/windows/zsyscall_windows.go @@ -0,0 +1,61 @@ +// Code generated by 'go generate'; DO NOT EDIT. + +package windows + +import ( + "syscall" + "unsafe" +) + +var _ unsafe.Pointer + +// Do the interface allocations only once for common +// Errno values. +const ( + errnoERROR_IO_PENDING = 997 +) + +var ( + errERROR_IO_PENDING error = syscall.Errno(errnoERROR_IO_PENDING) + errERROR_EINVAL error = syscall.EINVAL +) + +// errnoErr returns common boxed Errno values, to prevent +// allocations at runtime. +func errnoErr(e syscall.Errno) error { + switch e { + case 0: + return errERROR_EINVAL + case errnoERROR_IO_PENDING: + return errERROR_IO_PENDING + } + // TODO: add more here, after collecting data on the common + // error values see on Windows. (perhaps when running + // all.bat?) + return e +} + +var ( + moduser32 = NewLazySystemDLL("user32.dll") + + procGetKeyboardLayout = moduser32.NewProc("GetKeyboardLayout") + procToUnicodeEx = moduser32.NewProc("ToUnicodeEx") +) + +func GetKeyboardLayout(threadId uint32) (hkl Handle, err error) { + r0, _, e1 := syscall.Syscall(procGetKeyboardLayout.Addr(), 1, uintptr(threadId), 0, 0) + hkl = Handle(r0) + if hkl == 0 { + err = errnoErr(e1) + } + return +} + +func ToUnicodeEx(vkey uint32, scancode uint32, keystate *byte, pwszBuff *uint16, cchBuff int32, flags uint32, hkl Handle) (ret int32, err error) { + r0, _, e1 := syscall.Syscall9(procToUnicodeEx.Addr(), 7, uintptr(vkey), uintptr(scancode), uintptr(unsafe.Pointer(keystate)), uintptr(unsafe.Pointer(pwszBuff)), uintptr(cchBuff), uintptr(flags), uintptr(hkl), 0, 0) + ret = int32(r0) + if ret == 0 { + err = errnoErr(e1) + } + return +} diff --git a/vendor/github.com/containerd/console/.golangci.yml b/vendor/github.com/containerd/console/.golangci.yml deleted file mode 100644 index abe3d84b..00000000 --- a/vendor/github.com/containerd/console/.golangci.yml +++ /dev/null @@ -1,20 +0,0 @@ -linters: - enable: - - gofmt - - goimports - - ineffassign - - misspell - - revive - - staticcheck - - structcheck - - unconvert - - unused - - varcheck - - vet - disable: - - errcheck - -run: - timeout: 3m - skip-dirs: - - vendor diff --git a/vendor/github.com/containerd/console/LICENSE b/vendor/github.com/containerd/console/LICENSE deleted file mode 100644 index 584149b6..00000000 --- a/vendor/github.com/containerd/console/LICENSE +++ /dev/null @@ -1,191 +0,0 @@ - - Apache License - Version 2.0, January 2004 - https://www.apache.org/licenses/ - - TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION - - 1. Definitions. - - "License" shall mean the terms and conditions for use, reproduction, - and distribution as defined by Sections 1 through 9 of this document. - - "Licensor" shall mean the copyright owner or entity authorized by - the copyright owner that is granting the License. - - "Legal Entity" shall mean the union of the acting entity and all - other entities that control, are controlled by, or are under common - control with that entity. For the purposes of this definition, - "control" means (i) the power, direct or indirect, to cause the - direction or management of such entity, whether by contract or - otherwise, or (ii) ownership of fifty percent (50%) or more of the - outstanding shares, or (iii) beneficial ownership of such entity. - - "You" (or "Your") shall mean an individual or Legal Entity - exercising permissions granted by this License. - - "Source" form shall mean the preferred form for making modifications, - including but not limited to software source code, documentation - source, and configuration files. - - "Object" form shall mean any form resulting from mechanical - transformation or translation of a Source form, including but - not limited to compiled object code, generated documentation, - and conversions to other media types. - - "Work" shall mean the work of authorship, whether in Source or - Object form, made available under the License, as indicated by a - copyright notice that is included in or attached to the work - (an example is provided in the Appendix below). - - "Derivative Works" shall mean any work, whether in Source or Object - form, that is based on (or derived from) the Work and for which the - editorial revisions, annotations, elaborations, or other modifications - represent, as a whole, an original work of authorship. For the purposes - of this License, Derivative Works shall not include works that remain - separable from, or merely link (or bind by name) to the interfaces of, - the Work and Derivative Works thereof. - - "Contribution" shall mean any work of authorship, including - the original version of the Work and any modifications or additions - to that Work or Derivative Works thereof, that is intentionally - submitted to Licensor for inclusion in the Work by the copyright owner - or by an individual or Legal Entity authorized to submit on behalf of - the copyright owner. For the purposes of this definition, "submitted" - means any form of electronic, verbal, or written communication sent - to the Licensor or its representatives, including but not limited to - communication on electronic mailing lists, source code control systems, - and issue tracking systems that are managed by, or on behalf of, the - Licensor for the purpose of discussing and improving the Work, but - excluding communication that is conspicuously marked or otherwise - designated in writing by the copyright owner as "Not a Contribution." - - "Contributor" shall mean Licensor and any individual or Legal Entity - on behalf of whom a Contribution has been received by Licensor and - subsequently incorporated within the Work. - - 2. Grant of Copyright License. Subject to the terms and conditions of - this License, each Contributor hereby grants to You a perpetual, - worldwide, non-exclusive, no-charge, royalty-free, irrevocable - copyright license to reproduce, prepare Derivative Works of, - publicly display, publicly perform, sublicense, and distribute the - Work and such Derivative Works in Source or Object form. - - 3. Grant of Patent License. Subject to the terms and conditions of - this License, each Contributor hereby grants to You a perpetual, - worldwide, non-exclusive, no-charge, royalty-free, irrevocable - (except as stated in this section) patent license to make, have made, - use, offer to sell, sell, import, and otherwise transfer the Work, - where such license applies only to those patent claims licensable - by such Contributor that are necessarily infringed by their - Contribution(s) alone or by combination of their Contribution(s) - with the Work to which such Contribution(s) was submitted. If You - institute patent litigation against any entity (including a - cross-claim or counterclaim in a lawsuit) alleging that the Work - or a Contribution incorporated within the Work constitutes direct - or contributory patent infringement, then any patent licenses - granted to You under this License for that Work shall terminate - as of the date such litigation is filed. - - 4. Redistribution. You may reproduce and distribute copies of the - Work or Derivative Works thereof in any medium, with or without - modifications, and in Source or Object form, provided that You - meet the following conditions: - - (a) You must give any other recipients of the Work or - Derivative Works a copy of this License; and - - (b) You must cause any modified files to carry prominent notices - stating that You changed the files; and - - (c) You must retain, in the Source form of any Derivative Works - that You distribute, all copyright, patent, trademark, and - attribution notices from the Source form of the Work, - excluding those notices that do not pertain to any part of - the Derivative Works; and - - (d) If the Work includes a "NOTICE" text file as part of its - distribution, then any Derivative Works that You distribute must - include a readable copy of the attribution notices contained - within such NOTICE file, excluding those notices that do not - pertain to any part of the Derivative Works, in at least one - of the following places: within a NOTICE text file distributed - as part of the Derivative Works; within the Source form or - documentation, if provided along with the Derivative Works; or, - within a display generated by the Derivative Works, if and - wherever such third-party notices normally appear. The contents - of the NOTICE file are for informational purposes only and - do not modify the License. You may add Your own attribution - notices within Derivative Works that You distribute, alongside - or as an addendum to the NOTICE text from the Work, provided - that such additional attribution notices cannot be construed - as modifying the License. - - You may add Your own copyright statement to Your modifications and - may provide additional or different license terms and conditions - for use, reproduction, or distribution of Your modifications, or - for any such Derivative Works as a whole, provided Your use, - reproduction, and distribution of the Work otherwise complies with - the conditions stated in this License. - - 5. Submission of Contributions. Unless You explicitly state otherwise, - any Contribution intentionally submitted for inclusion in the Work - by You to the Licensor shall be under the terms and conditions of - this License, without any additional terms or conditions. - Notwithstanding the above, nothing herein shall supersede or modify - the terms of any separate license agreement you may have executed - with Licensor regarding such Contributions. - - 6. Trademarks. This License does not grant permission to use the trade - names, trademarks, service marks, or product names of the Licensor, - except as required for reasonable and customary use in describing the - origin of the Work and reproducing the content of the NOTICE file. - - 7. Disclaimer of Warranty. Unless required by applicable law or - agreed to in writing, Licensor provides the Work (and each - Contributor provides its Contributions) on an "AS IS" BASIS, - WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or - implied, including, without limitation, any warranties or conditions - of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A - PARTICULAR PURPOSE. You are solely responsible for determining the - appropriateness of using or redistributing the Work and assume any - risks associated with Your exercise of permissions under this License. - - 8. Limitation of Liability. In no event and under no legal theory, - whether in tort (including negligence), contract, or otherwise, - unless required by applicable law (such as deliberate and grossly - negligent acts) or agreed to in writing, shall any Contributor be - liable to You for damages, including any direct, indirect, special, - incidental, or consequential damages of any character arising as a - result of this License or out of the use or inability to use the - Work (including but not limited to damages for loss of goodwill, - work stoppage, computer failure or malfunction, or any and all - other commercial damages or losses), even if such Contributor - has been advised of the possibility of such damages. - - 9. Accepting Warranty or Additional Liability. While redistributing - the Work or Derivative Works thereof, You may choose to offer, - and charge a fee for, acceptance of support, warranty, indemnity, - or other liability obligations and/or rights consistent with this - License. However, in accepting such obligations, You may act only - on Your own behalf and on Your sole responsibility, not on behalf - of any other Contributor, and only if You agree to indemnify, - defend, and hold each Contributor harmless for any liability - incurred by, or claims asserted against, such Contributor by reason - of your accepting any such warranty or additional liability. - - END OF TERMS AND CONDITIONS - - Copyright The containerd Authors - - Licensed under the Apache License, Version 2.0 (the "License"); - you may not use this file except in compliance with the License. - You may obtain a copy of the License at - - https://www.apache.org/licenses/LICENSE-2.0 - - Unless required by applicable law or agreed to in writing, software - distributed under the License is distributed on an "AS IS" BASIS, - WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - See the License for the specific language governing permissions and - limitations under the License. diff --git a/vendor/github.com/containerd/console/README.md b/vendor/github.com/containerd/console/README.md deleted file mode 100644 index 580b461a..00000000 --- a/vendor/github.com/containerd/console/README.md +++ /dev/null @@ -1,29 +0,0 @@ -# console - -[![PkgGoDev](https://pkg.go.dev/badge/github.com/containerd/console)](https://pkg.go.dev/github.com/containerd/console) -[![Build Status](https://github.com/containerd/console/workflows/CI/badge.svg)](https://github.com/containerd/console/actions?query=workflow%3ACI) -[![Go Report Card](https://goreportcard.com/badge/github.com/containerd/console)](https://goreportcard.com/report/github.com/containerd/console) - -Golang package for dealing with consoles. Light on deps and a simple API. - -## Modifying the current process - -```go -current := console.Current() -defer current.Reset() - -if err := current.SetRaw(); err != nil { -} -ws, err := current.Size() -current.Resize(ws) -``` - -## Project details - -console is a containerd sub-project, licensed under the [Apache 2.0 license](./LICENSE). -As a containerd sub-project, you will find the: - * [Project governance](https://github.com/containerd/project/blob/master/GOVERNANCE.md), - * [Maintainers](https://github.com/containerd/project/blob/master/MAINTAINERS), - * and [Contributing guidelines](https://github.com/containerd/project/blob/master/CONTRIBUTING.md) - -information in our [`containerd/project`](https://github.com/containerd/project) repository. diff --git a/vendor/github.com/containerd/console/console.go b/vendor/github.com/containerd/console/console.go deleted file mode 100644 index 810a71f4..00000000 --- a/vendor/github.com/containerd/console/console.go +++ /dev/null @@ -1,87 +0,0 @@ -/* - Copyright The containerd Authors. - - Licensed under the Apache License, Version 2.0 (the "License"); - you may not use this file except in compliance with the License. - You may obtain a copy of the License at - - http://www.apache.org/licenses/LICENSE-2.0 - - Unless required by applicable law or agreed to in writing, software - distributed under the License is distributed on an "AS IS" BASIS, - WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - See the License for the specific language governing permissions and - limitations under the License. -*/ - -package console - -import ( - "errors" - "io" - "os" -) - -var ErrNotAConsole = errors.New("provided file is not a console") - -type File interface { - io.ReadWriteCloser - - // Fd returns its file descriptor - Fd() uintptr - // Name returns its file name - Name() string -} - -type Console interface { - File - - // Resize resizes the console to the provided window size - Resize(WinSize) error - // ResizeFrom resizes the calling console to the size of the - // provided console - ResizeFrom(Console) error - // SetRaw sets the console in raw mode - SetRaw() error - // DisableEcho disables echo on the console - DisableEcho() error - // Reset restores the console to its orignal state - Reset() error - // Size returns the window size of the console - Size() (WinSize, error) -} - -// WinSize specifies the window size of the console -type WinSize struct { - // Height of the console - Height uint16 - // Width of the console - Width uint16 - x uint16 - y uint16 -} - -// Current returns the current process' console -func Current() (c Console) { - var err error - // Usually all three streams (stdin, stdout, and stderr) - // are open to the same console, but some might be redirected, - // so try all three. - for _, s := range []*os.File{os.Stderr, os.Stdout, os.Stdin} { - if c, err = ConsoleFromFile(s); err == nil { - return c - } - } - // One of the std streams should always be a console - // for the design of this function. - panic(err) -} - -// ConsoleFromFile returns a console using the provided file -// nolint:revive -func ConsoleFromFile(f File) (Console, error) { - if err := checkConsole(f); err != nil { - return nil, err - } - return newMaster(f) -} diff --git a/vendor/github.com/containerd/console/console_linux.go b/vendor/github.com/containerd/console/console_linux.go deleted file mode 100644 index 28b77b7a..00000000 --- a/vendor/github.com/containerd/console/console_linux.go +++ /dev/null @@ -1,281 +0,0 @@ -//go:build linux -// +build linux - -/* - Copyright The containerd Authors. - - Licensed under the Apache License, Version 2.0 (the "License"); - you may not use this file except in compliance with the License. - You may obtain a copy of the License at - - http://www.apache.org/licenses/LICENSE-2.0 - - Unless required by applicable law or agreed to in writing, software - distributed under the License is distributed on an "AS IS" BASIS, - WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - See the License for the specific language governing permissions and - limitations under the License. -*/ - -package console - -import ( - "io" - "os" - "sync" - - "golang.org/x/sys/unix" -) - -const ( - maxEvents = 128 -) - -// Epoller manages multiple epoll consoles using edge-triggered epoll api so we -// dont have to deal with repeated wake-up of EPOLLER or EPOLLHUP. -// For more details, see: -// - https://github.com/systemd/systemd/pull/4262 -// - https://github.com/moby/moby/issues/27202 -// -// Example usage of Epoller and EpollConsole can be as follow: -// -// epoller, _ := NewEpoller() -// epollConsole, _ := epoller.Add(console) -// go epoller.Wait() -// var ( -// b bytes.Buffer -// wg sync.WaitGroup -// ) -// wg.Add(1) -// go func() { -// io.Copy(&b, epollConsole) -// wg.Done() -// }() -// // perform I/O on the console -// epollConsole.Shutdown(epoller.CloseConsole) -// wg.Wait() -// epollConsole.Close() -type Epoller struct { - efd int - mu sync.Mutex - fdMapping map[int]*EpollConsole - closeOnce sync.Once -} - -// NewEpoller returns an instance of epoller with a valid epoll fd. -func NewEpoller() (*Epoller, error) { - efd, err := unix.EpollCreate1(unix.EPOLL_CLOEXEC) - if err != nil { - return nil, err - } - return &Epoller{ - efd: efd, - fdMapping: make(map[int]*EpollConsole), - }, nil -} - -// Add creates an epoll console based on the provided console. The console will -// be registered with EPOLLET (i.e. using edge-triggered notification) and its -// file descriptor will be set to non-blocking mode. After this, user should use -// the return console to perform I/O. -func (e *Epoller) Add(console Console) (*EpollConsole, error) { - sysfd := int(console.Fd()) - // Set sysfd to non-blocking mode - if err := unix.SetNonblock(sysfd, true); err != nil { - return nil, err - } - - ev := unix.EpollEvent{ - Events: unix.EPOLLIN | unix.EPOLLOUT | unix.EPOLLRDHUP | unix.EPOLLET, - Fd: int32(sysfd), - } - if err := unix.EpollCtl(e.efd, unix.EPOLL_CTL_ADD, sysfd, &ev); err != nil { - return nil, err - } - ef := &EpollConsole{ - Console: console, - sysfd: sysfd, - readc: sync.NewCond(&sync.Mutex{}), - writec: sync.NewCond(&sync.Mutex{}), - } - e.mu.Lock() - e.fdMapping[sysfd] = ef - e.mu.Unlock() - return ef, nil -} - -// Wait starts the loop to wait for its consoles' notifications and signal -// appropriate console that it can perform I/O. -func (e *Epoller) Wait() error { - events := make([]unix.EpollEvent, maxEvents) - for { - n, err := unix.EpollWait(e.efd, events, -1) - if err != nil { - // EINTR: The call was interrupted by a signal handler before either - // any of the requested events occurred or the timeout expired - if err == unix.EINTR { - continue - } - return err - } - for i := 0; i < n; i++ { - ev := &events[i] - // the console is ready to be read from - if ev.Events&(unix.EPOLLIN|unix.EPOLLHUP|unix.EPOLLERR) != 0 { - if epfile := e.getConsole(int(ev.Fd)); epfile != nil { - epfile.signalRead() - } - } - // the console is ready to be written to - if ev.Events&(unix.EPOLLOUT|unix.EPOLLHUP|unix.EPOLLERR) != 0 { - if epfile := e.getConsole(int(ev.Fd)); epfile != nil { - epfile.signalWrite() - } - } - } - } -} - -// CloseConsole unregisters the console's file descriptor from epoll interface -func (e *Epoller) CloseConsole(fd int) error { - e.mu.Lock() - defer e.mu.Unlock() - delete(e.fdMapping, fd) - return unix.EpollCtl(e.efd, unix.EPOLL_CTL_DEL, fd, &unix.EpollEvent{}) -} - -func (e *Epoller) getConsole(sysfd int) *EpollConsole { - e.mu.Lock() - f := e.fdMapping[sysfd] - e.mu.Unlock() - return f -} - -// Close closes the epoll fd -func (e *Epoller) Close() error { - closeErr := os.ErrClosed // default to "file already closed" - e.closeOnce.Do(func() { - closeErr = unix.Close(e.efd) - }) - return closeErr -} - -// EpollConsole acts like a console but registers its file descriptor with an -// epoll fd and uses epoll API to perform I/O. -type EpollConsole struct { - Console - readc *sync.Cond - writec *sync.Cond - sysfd int - closed bool -} - -// Read reads up to len(p) bytes into p. It returns the number of bytes read -// (0 <= n <= len(p)) and any error encountered. -// -// If the console's read returns EAGAIN or EIO, we assume that it's a -// temporary error because the other side went away and wait for the signal -// generated by epoll event to continue. -func (ec *EpollConsole) Read(p []byte) (n int, err error) { - var read int - ec.readc.L.Lock() - defer ec.readc.L.Unlock() - for { - read, err = ec.Console.Read(p[n:]) - n += read - if err != nil { - var hangup bool - if perr, ok := err.(*os.PathError); ok { - hangup = (perr.Err == unix.EAGAIN || perr.Err == unix.EIO) - } else { - hangup = (err == unix.EAGAIN || err == unix.EIO) - } - // if the other end disappear, assume this is temporary and wait for the - // signal to continue again. Unless we didnt read anything and the - // console is already marked as closed then we should exit - if hangup && !(n == 0 && len(p) > 0 && ec.closed) { - ec.readc.Wait() - continue - } - } - break - } - // if we didnt read anything then return io.EOF to end gracefully - if n == 0 && len(p) > 0 && err == nil { - err = io.EOF - } - // signal for others that we finished the read - ec.readc.Signal() - return n, err -} - -// Writes len(p) bytes from p to the console. It returns the number of bytes -// written from p (0 <= n <= len(p)) and any error encountered that caused -// the write to stop early. -// -// If writes to the console returns EAGAIN or EIO, we assume that it's a -// temporary error because the other side went away and wait for the signal -// generated by epoll event to continue. -func (ec *EpollConsole) Write(p []byte) (n int, err error) { - var written int - ec.writec.L.Lock() - defer ec.writec.L.Unlock() - for { - written, err = ec.Console.Write(p[n:]) - n += written - if err != nil { - var hangup bool - if perr, ok := err.(*os.PathError); ok { - hangup = (perr.Err == unix.EAGAIN || perr.Err == unix.EIO) - } else { - hangup = (err == unix.EAGAIN || err == unix.EIO) - } - // if the other end disappears, assume this is temporary and wait for the - // signal to continue again. - if hangup { - ec.writec.Wait() - continue - } - } - // unrecoverable error, break the loop and return the error - break - } - if n < len(p) && err == nil { - err = io.ErrShortWrite - } - // signal for others that we finished the write - ec.writec.Signal() - return n, err -} - -// Shutdown closes the file descriptor and signals call waiters for this fd. -// It accepts a callback which will be called with the console's fd. The -// callback typically will be used to do further cleanup such as unregister the -// console's fd from the epoll interface. -// User should call Shutdown and wait for all I/O operation to be finished -// before closing the console. -func (ec *EpollConsole) Shutdown(close func(int) error) error { - ec.readc.L.Lock() - defer ec.readc.L.Unlock() - ec.writec.L.Lock() - defer ec.writec.L.Unlock() - - ec.readc.Broadcast() - ec.writec.Broadcast() - ec.closed = true - return close(ec.sysfd) -} - -// signalRead signals that the console is readable. -func (ec *EpollConsole) signalRead() { - ec.readc.L.Lock() - ec.readc.Signal() - ec.readc.L.Unlock() -} - -// signalWrite signals that the console is writable. -func (ec *EpollConsole) signalWrite() { - ec.writec.L.Lock() - ec.writec.Signal() - ec.writec.L.Unlock() -} diff --git a/vendor/github.com/containerd/console/console_unix.go b/vendor/github.com/containerd/console/console_unix.go deleted file mode 100644 index 161f5d12..00000000 --- a/vendor/github.com/containerd/console/console_unix.go +++ /dev/null @@ -1,157 +0,0 @@ -//go:build darwin || freebsd || linux || netbsd || openbsd || zos -// +build darwin freebsd linux netbsd openbsd zos - -/* - Copyright The containerd Authors. - - Licensed under the Apache License, Version 2.0 (the "License"); - you may not use this file except in compliance with the License. - You may obtain a copy of the License at - - http://www.apache.org/licenses/LICENSE-2.0 - - Unless required by applicable law or agreed to in writing, software - distributed under the License is distributed on an "AS IS" BASIS, - WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - See the License for the specific language governing permissions and - limitations under the License. -*/ - -package console - -import ( - "golang.org/x/sys/unix" -) - -// NewPty creates a new pty pair -// The master is returned as the first console and a string -// with the path to the pty slave is returned as the second -func NewPty() (Console, string, error) { - f, err := openpt() - if err != nil { - return nil, "", err - } - slave, err := ptsname(f) - if err != nil { - return nil, "", err - } - if err := unlockpt(f); err != nil { - return nil, "", err - } - m, err := newMaster(f) - if err != nil { - return nil, "", err - } - return m, slave, nil -} - -type master struct { - f File - original *unix.Termios -} - -func (m *master) Read(b []byte) (int, error) { - return m.f.Read(b) -} - -func (m *master) Write(b []byte) (int, error) { - return m.f.Write(b) -} - -func (m *master) Close() error { - return m.f.Close() -} - -func (m *master) Resize(ws WinSize) error { - return tcswinsz(m.f.Fd(), ws) -} - -func (m *master) ResizeFrom(c Console) error { - ws, err := c.Size() - if err != nil { - return err - } - return m.Resize(ws) -} - -func (m *master) Reset() error { - if m.original == nil { - return nil - } - return tcset(m.f.Fd(), m.original) -} - -func (m *master) getCurrent() (unix.Termios, error) { - var termios unix.Termios - if err := tcget(m.f.Fd(), &termios); err != nil { - return unix.Termios{}, err - } - return termios, nil -} - -func (m *master) SetRaw() error { - rawState, err := m.getCurrent() - if err != nil { - return err - } - rawState = cfmakeraw(rawState) - rawState.Oflag = rawState.Oflag | unix.OPOST - return tcset(m.f.Fd(), &rawState) -} - -func (m *master) DisableEcho() error { - rawState, err := m.getCurrent() - if err != nil { - return err - } - rawState.Lflag = rawState.Lflag &^ unix.ECHO - return tcset(m.f.Fd(), &rawState) -} - -func (m *master) Size() (WinSize, error) { - return tcgwinsz(m.f.Fd()) -} - -func (m *master) Fd() uintptr { - return m.f.Fd() -} - -func (m *master) Name() string { - return m.f.Name() -} - -// checkConsole checks if the provided file is a console -func checkConsole(f File) error { - var termios unix.Termios - if tcget(f.Fd(), &termios) != nil { - return ErrNotAConsole - } - return nil -} - -func newMaster(f File) (Console, error) { - m := &master{ - f: f, - } - t, err := m.getCurrent() - if err != nil { - return nil, err - } - m.original = &t - return m, nil -} - -// ClearONLCR sets the necessary tty_ioctl(4)s to ensure that a pty pair -// created by us acts normally. In particular, a not-very-well-known default of -// Linux unix98 ptys is that they have +onlcr by default. While this isn't a -// problem for terminal emulators, because we relay data from the terminal we -// also relay that funky line discipline. -func ClearONLCR(fd uintptr) error { - return setONLCR(fd, false) -} - -// SetONLCR sets the necessary tty_ioctl(4)s to ensure that a pty pair -// created by us acts as intended for a terminal emulator. -func SetONLCR(fd uintptr) error { - return setONLCR(fd, true) -} diff --git a/vendor/github.com/containerd/console/console_windows.go b/vendor/github.com/containerd/console/console_windows.go deleted file mode 100644 index f48e4861..00000000 --- a/vendor/github.com/containerd/console/console_windows.go +++ /dev/null @@ -1,222 +0,0 @@ -/* - Copyright The containerd Authors. - - Licensed under the Apache License, Version 2.0 (the "License"); - you may not use this file except in compliance with the License. - You may obtain a copy of the License at - - http://www.apache.org/licenses/LICENSE-2.0 - - Unless required by applicable law or agreed to in writing, software - distributed under the License is distributed on an "AS IS" BASIS, - WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - See the License for the specific language governing permissions and - limitations under the License. -*/ - -package console - -import ( - "errors" - "fmt" - "os" - - "golang.org/x/sys/windows" -) - -var ( - vtInputSupported bool - ErrNotImplemented = errors.New("not implemented") -) - -func (m *master) initStdios() { - // Note: We discard console mode warnings, because in/out can be redirected. - // - // TODO: Investigate opening CONOUT$/CONIN$ to handle this correctly - - m.in = windows.Handle(os.Stdin.Fd()) - if err := windows.GetConsoleMode(m.in, &m.inMode); err == nil { - // Validate that windows.ENABLE_VIRTUAL_TERMINAL_INPUT is supported, but do not set it. - if err = windows.SetConsoleMode(m.in, m.inMode|windows.ENABLE_VIRTUAL_TERMINAL_INPUT); err == nil { - vtInputSupported = true - } - // Unconditionally set the console mode back even on failure because SetConsoleMode - // remembers invalid bits on input handles. - windows.SetConsoleMode(m.in, m.inMode) - } - - m.out = windows.Handle(os.Stdout.Fd()) - if err := windows.GetConsoleMode(m.out, &m.outMode); err == nil { - if err := windows.SetConsoleMode(m.out, m.outMode|windows.ENABLE_VIRTUAL_TERMINAL_PROCESSING); err == nil { - m.outMode |= windows.ENABLE_VIRTUAL_TERMINAL_PROCESSING - } else { - windows.SetConsoleMode(m.out, m.outMode) - } - } - - m.err = windows.Handle(os.Stderr.Fd()) - if err := windows.GetConsoleMode(m.err, &m.errMode); err == nil { - if err := windows.SetConsoleMode(m.err, m.errMode|windows.ENABLE_VIRTUAL_TERMINAL_PROCESSING); err == nil { - m.errMode |= windows.ENABLE_VIRTUAL_TERMINAL_PROCESSING - } else { - windows.SetConsoleMode(m.err, m.errMode) - } - } -} - -type master struct { - in windows.Handle - inMode uint32 - - out windows.Handle - outMode uint32 - - err windows.Handle - errMode uint32 -} - -func (m *master) SetRaw() error { - if err := makeInputRaw(m.in, m.inMode); err != nil { - return err - } - - // Set StdOut and StdErr to raw mode, we ignore failures since - // windows.DISABLE_NEWLINE_AUTO_RETURN might not be supported on this version of - // Windows. - - windows.SetConsoleMode(m.out, m.outMode|windows.DISABLE_NEWLINE_AUTO_RETURN) - - windows.SetConsoleMode(m.err, m.errMode|windows.DISABLE_NEWLINE_AUTO_RETURN) - - return nil -} - -func (m *master) Reset() error { - var errs []error - - for _, s := range []struct { - fd windows.Handle - mode uint32 - }{ - {m.in, m.inMode}, - {m.out, m.outMode}, - {m.err, m.errMode}, - } { - if err := windows.SetConsoleMode(s.fd, s.mode); err != nil { - // we can't just abort on the first error, otherwise we might leave - // the console in an unexpected state. - errs = append(errs, fmt.Errorf("unable to restore console mode: %w", err)) - } - } - - if len(errs) > 0 { - return errs[0] - } - - return nil -} - -func (m *master) Size() (WinSize, error) { - var info windows.ConsoleScreenBufferInfo - err := windows.GetConsoleScreenBufferInfo(m.out, &info) - if err != nil { - return WinSize{}, fmt.Errorf("unable to get console info: %w", err) - } - - winsize := WinSize{ - Width: uint16(info.Window.Right - info.Window.Left + 1), - Height: uint16(info.Window.Bottom - info.Window.Top + 1), - } - - return winsize, nil -} - -func (m *master) Resize(ws WinSize) error { - return ErrNotImplemented -} - -func (m *master) ResizeFrom(c Console) error { - return ErrNotImplemented -} - -func (m *master) DisableEcho() error { - mode := m.inMode &^ windows.ENABLE_ECHO_INPUT - mode |= windows.ENABLE_PROCESSED_INPUT - mode |= windows.ENABLE_LINE_INPUT - - if err := windows.SetConsoleMode(m.in, mode); err != nil { - return fmt.Errorf("unable to set console to disable echo: %w", err) - } - - return nil -} - -func (m *master) Close() error { - return nil -} - -func (m *master) Read(b []byte) (int, error) { - return os.Stdin.Read(b) -} - -func (m *master) Write(b []byte) (int, error) { - return os.Stdout.Write(b) -} - -func (m *master) Fd() uintptr { - return uintptr(m.in) -} - -// on windows, console can only be made from os.Std{in,out,err}, hence there -// isnt a single name here we can use. Return a dummy "console" value in this -// case should be sufficient. -func (m *master) Name() string { - return "console" -} - -// makeInputRaw puts the terminal (Windows Console) connected to the given -// file descriptor into raw mode -func makeInputRaw(fd windows.Handle, mode uint32) error { - // See - // -- https://msdn.microsoft.com/en-us/library/windows/desktop/ms686033(v=vs.85).aspx - // -- https://msdn.microsoft.com/en-us/library/windows/desktop/ms683462(v=vs.85).aspx - - // Disable these modes - mode &^= windows.ENABLE_ECHO_INPUT - mode &^= windows.ENABLE_LINE_INPUT - mode &^= windows.ENABLE_MOUSE_INPUT - mode &^= windows.ENABLE_WINDOW_INPUT - mode &^= windows.ENABLE_PROCESSED_INPUT - - // Enable these modes - mode |= windows.ENABLE_EXTENDED_FLAGS - mode |= windows.ENABLE_INSERT_MODE - mode |= windows.ENABLE_QUICK_EDIT_MODE - - if vtInputSupported { - mode |= windows.ENABLE_VIRTUAL_TERMINAL_INPUT - } - - if err := windows.SetConsoleMode(fd, mode); err != nil { - return fmt.Errorf("unable to set console to raw mode: %w", err) - } - - return nil -} - -func checkConsole(f File) error { - var mode uint32 - if err := windows.GetConsoleMode(windows.Handle(f.Fd()), &mode); err != nil { - return err - } - return nil -} - -func newMaster(f File) (Console, error) { - if f != os.Stdin && f != os.Stdout && f != os.Stderr { - return nil, errors.New("creating a console from a file is not supported on windows") - } - m := &master{} - m.initStdios() - return m, nil -} diff --git a/vendor/github.com/containerd/console/pty_freebsd_cgo.go b/vendor/github.com/containerd/console/pty_freebsd_cgo.go deleted file mode 100644 index 22368623..00000000 --- a/vendor/github.com/containerd/console/pty_freebsd_cgo.go +++ /dev/null @@ -1,46 +0,0 @@ -//go:build freebsd && cgo -// +build freebsd,cgo - -/* - Copyright The containerd Authors. - - Licensed under the Apache License, Version 2.0 (the "License"); - you may not use this file except in compliance with the License. - You may obtain a copy of the License at - - http://www.apache.org/licenses/LICENSE-2.0 - - Unless required by applicable law or agreed to in writing, software - distributed under the License is distributed on an "AS IS" BASIS, - WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - See the License for the specific language governing permissions and - limitations under the License. -*/ - -package console - -import ( - "fmt" - "os" -) - -/* -#include -#include -#include -*/ -import "C" - -// openpt allocates a new pseudo-terminal and establishes a connection with its -// control device. -func openpt() (*os.File, error) { - fd, err := C.posix_openpt(C.O_RDWR) - if err != nil { - return nil, fmt.Errorf("posix_openpt: %w", err) - } - if _, err := C.grantpt(fd); err != nil { - C.close(fd) - return nil, fmt.Errorf("grantpt: %w", err) - } - return os.NewFile(uintptr(fd), ""), nil -} diff --git a/vendor/github.com/containerd/console/pty_freebsd_nocgo.go b/vendor/github.com/containerd/console/pty_freebsd_nocgo.go deleted file mode 100644 index ceb90a47..00000000 --- a/vendor/github.com/containerd/console/pty_freebsd_nocgo.go +++ /dev/null @@ -1,37 +0,0 @@ -//go:build freebsd && !cgo -// +build freebsd,!cgo - -/* - Copyright The containerd Authors. - - Licensed under the Apache License, Version 2.0 (the "License"); - you may not use this file except in compliance with the License. - You may obtain a copy of the License at - - http://www.apache.org/licenses/LICENSE-2.0 - - Unless required by applicable law or agreed to in writing, software - distributed under the License is distributed on an "AS IS" BASIS, - WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - See the License for the specific language governing permissions and - limitations under the License. -*/ - -package console - -import ( - "os" -) - -// -// Implementing the functions below requires cgo support. Non-cgo stubs -// versions are defined below to enable cross-compilation of source code -// that depends on these functions, but the resultant cross-compiled -// binaries cannot actually be used. If the stub function(s) below are -// actually invoked they will display an error message and cause the -// calling process to exit. -// - -func openpt() (*os.File, error) { - panic("openpt() support requires cgo.") -} diff --git a/vendor/github.com/containerd/console/pty_unix.go b/vendor/github.com/containerd/console/pty_unix.go deleted file mode 100644 index f5a5b805..00000000 --- a/vendor/github.com/containerd/console/pty_unix.go +++ /dev/null @@ -1,31 +0,0 @@ -//go:build darwin || linux || netbsd || openbsd -// +build darwin linux netbsd openbsd - -/* - Copyright The containerd Authors. - - Licensed under the Apache License, Version 2.0 (the "License"); - you may not use this file except in compliance with the License. - You may obtain a copy of the License at - - http://www.apache.org/licenses/LICENSE-2.0 - - Unless required by applicable law or agreed to in writing, software - distributed under the License is distributed on an "AS IS" BASIS, - WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - See the License for the specific language governing permissions and - limitations under the License. -*/ - -package console - -import ( - "os" - - "golang.org/x/sys/unix" -) - -// openpt allocates a new pseudo-terminal by opening the /dev/ptmx device -func openpt() (*os.File, error) { - return os.OpenFile("/dev/ptmx", unix.O_RDWR|unix.O_NOCTTY|unix.O_CLOEXEC, 0) -} diff --git a/vendor/github.com/containerd/console/pty_zos.go b/vendor/github.com/containerd/console/pty_zos.go deleted file mode 100644 index 58f59aba..00000000 --- a/vendor/github.com/containerd/console/pty_zos.go +++ /dev/null @@ -1,43 +0,0 @@ -//go:build zos -// +build zos - -/* - Copyright The containerd Authors. - - Licensed under the Apache License, Version 2.0 (the "License"); - you may not use this file except in compliance with the License. - You may obtain a copy of the License at - - http://www.apache.org/licenses/LICENSE-2.0 - - Unless required by applicable law or agreed to in writing, software - distributed under the License is distributed on an "AS IS" BASIS, - WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - See the License for the specific language governing permissions and - limitations under the License. -*/ - -package console - -import ( - "fmt" - "os" -) - -// openpt allocates a new pseudo-terminal by opening the first available /dev/ptypXX device -func openpt() (*os.File, error) { - var f *os.File - var err error - for i := 0; ; i++ { - ptyp := fmt.Sprintf("/dev/ptyp%04d", i) - f, err = os.OpenFile(ptyp, os.O_RDWR, 0600) - if err == nil { - break - } - if os.IsNotExist(err) { - return nil, err - } - // else probably Resource Busy - } - return f, nil -} diff --git a/vendor/github.com/containerd/console/tc_darwin.go b/vendor/github.com/containerd/console/tc_darwin.go deleted file mode 100644 index 78715458..00000000 --- a/vendor/github.com/containerd/console/tc_darwin.go +++ /dev/null @@ -1,44 +0,0 @@ -/* - Copyright The containerd Authors. - - Licensed under the Apache License, Version 2.0 (the "License"); - you may not use this file except in compliance with the License. - You may obtain a copy of the License at - - http://www.apache.org/licenses/LICENSE-2.0 - - Unless required by applicable law or agreed to in writing, software - distributed under the License is distributed on an "AS IS" BASIS, - WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - See the License for the specific language governing permissions and - limitations under the License. -*/ - -package console - -import ( - "fmt" - "os" - - "golang.org/x/sys/unix" -) - -const ( - cmdTcGet = unix.TIOCGETA - cmdTcSet = unix.TIOCSETA -) - -// unlockpt unlocks the slave pseudoterminal device corresponding to the master pseudoterminal referred to by f. -// unlockpt should be called before opening the slave side of a pty. -func unlockpt(f *os.File) error { - return unix.IoctlSetPointerInt(int(f.Fd()), unix.TIOCPTYUNLK, 0) -} - -// ptsname retrieves the name of the first available pts for the given master. -func ptsname(f *os.File) (string, error) { - n, err := unix.IoctlGetInt(int(f.Fd()), unix.TIOCPTYGNAME) - if err != nil { - return "", err - } - return fmt.Sprintf("/dev/pts/%d", n), nil -} diff --git a/vendor/github.com/containerd/console/tc_freebsd_cgo.go b/vendor/github.com/containerd/console/tc_freebsd_cgo.go deleted file mode 100644 index 33282579..00000000 --- a/vendor/github.com/containerd/console/tc_freebsd_cgo.go +++ /dev/null @@ -1,58 +0,0 @@ -//go:build freebsd && cgo -// +build freebsd,cgo - -/* - Copyright The containerd Authors. - - Licensed under the Apache License, Version 2.0 (the "License"); - you may not use this file except in compliance with the License. - You may obtain a copy of the License at - - http://www.apache.org/licenses/LICENSE-2.0 - - Unless required by applicable law or agreed to in writing, software - distributed under the License is distributed on an "AS IS" BASIS, - WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - See the License for the specific language governing permissions and - limitations under the License. -*/ - -package console - -import ( - "fmt" - "os" - - "golang.org/x/sys/unix" -) - -/* -#include -#include -*/ -import "C" - -const ( - cmdTcGet = unix.TIOCGETA - cmdTcSet = unix.TIOCSETA -) - -// unlockpt unlocks the slave pseudoterminal device corresponding to the master pseudoterminal referred to by f. -// unlockpt should be called before opening the slave side of a pty. -func unlockpt(f *os.File) error { - fd := C.int(f.Fd()) - if _, err := C.unlockpt(fd); err != nil { - C.close(fd) - return fmt.Errorf("unlockpt: %w", err) - } - return nil -} - -// ptsname retrieves the name of the first available pts for the given master. -func ptsname(f *os.File) (string, error) { - n, err := unix.IoctlGetInt(int(f.Fd()), unix.TIOCGPTN) - if err != nil { - return "", err - } - return fmt.Sprintf("/dev/pts/%d", n), nil -} diff --git a/vendor/github.com/containerd/console/tc_freebsd_nocgo.go b/vendor/github.com/containerd/console/tc_freebsd_nocgo.go deleted file mode 100644 index 18a9b9cb..00000000 --- a/vendor/github.com/containerd/console/tc_freebsd_nocgo.go +++ /dev/null @@ -1,56 +0,0 @@ -//go:build freebsd && !cgo -// +build freebsd,!cgo - -/* - Copyright The containerd Authors. - - Licensed under the Apache License, Version 2.0 (the "License"); - you may not use this file except in compliance with the License. - You may obtain a copy of the License at - - http://www.apache.org/licenses/LICENSE-2.0 - - Unless required by applicable law or agreed to in writing, software - distributed under the License is distributed on an "AS IS" BASIS, - WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - See the License for the specific language governing permissions and - limitations under the License. -*/ - -package console - -import ( - "fmt" - "os" - - "golang.org/x/sys/unix" -) - -const ( - cmdTcGet = unix.TIOCGETA - cmdTcSet = unix.TIOCSETA -) - -// -// Implementing the functions below requires cgo support. Non-cgo stubs -// versions are defined below to enable cross-compilation of source code -// that depends on these functions, but the resultant cross-compiled -// binaries cannot actually be used. If the stub function(s) below are -// actually invoked they will display an error message and cause the -// calling process to exit. -// - -// unlockpt unlocks the slave pseudoterminal device corresponding to the master pseudoterminal referred to by f. -// unlockpt should be called before opening the slave side of a pty. -func unlockpt(f *os.File) error { - panic("unlockpt() support requires cgo.") -} - -// ptsname retrieves the name of the first available pts for the given master. -func ptsname(f *os.File) (string, error) { - n, err := unix.IoctlGetInt(int(f.Fd()), unix.TIOCGPTN) - if err != nil { - return "", err - } - return fmt.Sprintf("/dev/pts/%d", n), nil -} diff --git a/vendor/github.com/containerd/console/tc_linux.go b/vendor/github.com/containerd/console/tc_linux.go deleted file mode 100644 index 7d552ea4..00000000 --- a/vendor/github.com/containerd/console/tc_linux.go +++ /dev/null @@ -1,51 +0,0 @@ -/* - Copyright The containerd Authors. - - Licensed under the Apache License, Version 2.0 (the "License"); - you may not use this file except in compliance with the License. - You may obtain a copy of the License at - - http://www.apache.org/licenses/LICENSE-2.0 - - Unless required by applicable law or agreed to in writing, software - distributed under the License is distributed on an "AS IS" BASIS, - WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - See the License for the specific language governing permissions and - limitations under the License. -*/ - -package console - -import ( - "fmt" - "os" - "unsafe" - - "golang.org/x/sys/unix" -) - -const ( - cmdTcGet = unix.TCGETS - cmdTcSet = unix.TCSETS -) - -// unlockpt unlocks the slave pseudoterminal device corresponding to the master pseudoterminal referred to by f. -// unlockpt should be called before opening the slave side of a pty. -func unlockpt(f *os.File) error { - var u int32 - // XXX do not use unix.IoctlSetPointerInt here, see commit dbd69c59b81. - if _, _, err := unix.Syscall(unix.SYS_IOCTL, f.Fd(), unix.TIOCSPTLCK, uintptr(unsafe.Pointer(&u))); err != 0 { - return err - } - return nil -} - -// ptsname retrieves the name of the first available pts for the given master. -func ptsname(f *os.File) (string, error) { - var u uint32 - // XXX do not use unix.IoctlGetInt here, see commit dbd69c59b81. - if _, _, err := unix.Syscall(unix.SYS_IOCTL, f.Fd(), unix.TIOCGPTN, uintptr(unsafe.Pointer(&u))); err != 0 { - return "", err - } - return fmt.Sprintf("/dev/pts/%d", u), nil -} diff --git a/vendor/github.com/containerd/console/tc_netbsd.go b/vendor/github.com/containerd/console/tc_netbsd.go deleted file mode 100644 index 71227aef..00000000 --- a/vendor/github.com/containerd/console/tc_netbsd.go +++ /dev/null @@ -1,45 +0,0 @@ -/* - Copyright The containerd Authors. - - Licensed under the Apache License, Version 2.0 (the "License"); - you may not use this file except in compliance with the License. - You may obtain a copy of the License at - - http://www.apache.org/licenses/LICENSE-2.0 - - Unless required by applicable law or agreed to in writing, software - distributed under the License is distributed on an "AS IS" BASIS, - WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - See the License for the specific language governing permissions and - limitations under the License. -*/ - -package console - -import ( - "bytes" - "os" - - "golang.org/x/sys/unix" -) - -const ( - cmdTcGet = unix.TIOCGETA - cmdTcSet = unix.TIOCSETA -) - -// unlockpt unlocks the slave pseudoterminal device corresponding to the master pseudoterminal referred to by f. -// unlockpt should be called before opening the slave side of a pty. -// This does not exist on NetBSD, it does not allocate controlling terminals on open -func unlockpt(f *os.File) error { - return nil -} - -// ptsname retrieves the name of the first available pts for the given master. -func ptsname(f *os.File) (string, error) { - ptm, err := unix.IoctlGetPtmget(int(f.Fd()), unix.TIOCPTSNAME) - if err != nil { - return "", err - } - return string(ptm.Sn[:bytes.IndexByte(ptm.Sn[:], 0)]), nil -} diff --git a/vendor/github.com/containerd/console/tc_openbsd_cgo.go b/vendor/github.com/containerd/console/tc_openbsd_cgo.go deleted file mode 100644 index 0e76f6cc..00000000 --- a/vendor/github.com/containerd/console/tc_openbsd_cgo.go +++ /dev/null @@ -1,52 +0,0 @@ -//go:build openbsd && cgo -// +build openbsd,cgo - -/* - Copyright The containerd Authors. - - Licensed under the Apache License, Version 2.0 (the "License"); - you may not use this file except in compliance with the License. - You may obtain a copy of the License at - - http://www.apache.org/licenses/LICENSE-2.0 - - Unless required by applicable law or agreed to in writing, software - distributed under the License is distributed on an "AS IS" BASIS, - WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - See the License for the specific language governing permissions and - limitations under the License. -*/ - -package console - -import ( - "os" - - "golang.org/x/sys/unix" -) - -//#include -import "C" - -const ( - cmdTcGet = unix.TIOCGETA - cmdTcSet = unix.TIOCSETA -) - -// ptsname retrieves the name of the first available pts for the given master. -func ptsname(f *os.File) (string, error) { - ptspath, err := C.ptsname(C.int(f.Fd())) - if err != nil { - return "", err - } - return C.GoString(ptspath), nil -} - -// unlockpt unlocks the slave pseudoterminal device corresponding to the master pseudoterminal referred to by f. -// unlockpt should be called before opening the slave side of a pty. -func unlockpt(f *os.File) error { - if _, err := C.grantpt(C.int(f.Fd())); err != nil { - return err - } - return nil -} diff --git a/vendor/github.com/containerd/console/tc_openbsd_nocgo.go b/vendor/github.com/containerd/console/tc_openbsd_nocgo.go deleted file mode 100644 index dca92418..00000000 --- a/vendor/github.com/containerd/console/tc_openbsd_nocgo.go +++ /dev/null @@ -1,48 +0,0 @@ -//go:build openbsd && !cgo -// +build openbsd,!cgo - -/* - Copyright The containerd Authors. - - Licensed under the Apache License, Version 2.0 (the "License"); - you may not use this file except in compliance with the License. - You may obtain a copy of the License at - - http://www.apache.org/licenses/LICENSE-2.0 - - Unless required by applicable law or agreed to in writing, software - distributed under the License is distributed on an "AS IS" BASIS, - WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - See the License for the specific language governing permissions and - limitations under the License. -*/ - -// -// Implementing the functions below requires cgo support. Non-cgo stubs -// versions are defined below to enable cross-compilation of source code -// that depends on these functions, but the resultant cross-compiled -// binaries cannot actually be used. If the stub function(s) below are -// actually invoked they will display an error message and cause the -// calling process to exit. -// - -package console - -import ( - "os" - - "golang.org/x/sys/unix" -) - -const ( - cmdTcGet = unix.TIOCGETA - cmdTcSet = unix.TIOCSETA -) - -func ptsname(f *os.File) (string, error) { - panic("ptsname() support requires cgo.") -} - -func unlockpt(f *os.File) error { - panic("unlockpt() support requires cgo.") -} diff --git a/vendor/github.com/containerd/console/tc_unix.go b/vendor/github.com/containerd/console/tc_unix.go deleted file mode 100644 index f5053b2f..00000000 --- a/vendor/github.com/containerd/console/tc_unix.go +++ /dev/null @@ -1,92 +0,0 @@ -//go:build darwin || freebsd || linux || netbsd || openbsd || zos -// +build darwin freebsd linux netbsd openbsd zos - -/* - Copyright The containerd Authors. - - Licensed under the Apache License, Version 2.0 (the "License"); - you may not use this file except in compliance with the License. - You may obtain a copy of the License at - - http://www.apache.org/licenses/LICENSE-2.0 - - Unless required by applicable law or agreed to in writing, software - distributed under the License is distributed on an "AS IS" BASIS, - WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - See the License for the specific language governing permissions and - limitations under the License. -*/ - -package console - -import ( - "golang.org/x/sys/unix" -) - -func tcget(fd uintptr, p *unix.Termios) error { - termios, err := unix.IoctlGetTermios(int(fd), cmdTcGet) - if err != nil { - return err - } - *p = *termios - return nil -} - -func tcset(fd uintptr, p *unix.Termios) error { - return unix.IoctlSetTermios(int(fd), cmdTcSet, p) -} - -func tcgwinsz(fd uintptr) (WinSize, error) { - var ws WinSize - - uws, err := unix.IoctlGetWinsize(int(fd), unix.TIOCGWINSZ) - if err != nil { - return ws, err - } - - // Translate from unix.Winsize to console.WinSize - ws.Height = uws.Row - ws.Width = uws.Col - ws.x = uws.Xpixel - ws.y = uws.Ypixel - return ws, nil -} - -func tcswinsz(fd uintptr, ws WinSize) error { - // Translate from console.WinSize to unix.Winsize - - var uws unix.Winsize - uws.Row = ws.Height - uws.Col = ws.Width - uws.Xpixel = ws.x - uws.Ypixel = ws.y - - return unix.IoctlSetWinsize(int(fd), unix.TIOCSWINSZ, &uws) -} - -func setONLCR(fd uintptr, enable bool) error { - var termios unix.Termios - if err := tcget(fd, &termios); err != nil { - return err - } - if enable { - // Set +onlcr so we can act like a real terminal - termios.Oflag |= unix.ONLCR - } else { - // Set -onlcr so we don't have to deal with \r. - termios.Oflag &^= unix.ONLCR - } - return tcset(fd, &termios) -} - -func cfmakeraw(t unix.Termios) unix.Termios { - t.Iflag &^= (unix.IGNBRK | unix.BRKINT | unix.PARMRK | unix.ISTRIP | unix.INLCR | unix.IGNCR | unix.ICRNL | unix.IXON) - t.Oflag &^= unix.OPOST - t.Lflag &^= (unix.ECHO | unix.ECHONL | unix.ICANON | unix.ISIG | unix.IEXTEN) - t.Cflag &^= (unix.CSIZE | unix.PARENB) - t.Cflag &^= unix.CS8 - t.Cc[unix.VMIN] = 1 - t.Cc[unix.VTIME] = 0 - - return t -} diff --git a/vendor/github.com/containerd/console/tc_zos.go b/vendor/github.com/containerd/console/tc_zos.go deleted file mode 100644 index fc90ba5f..00000000 --- a/vendor/github.com/containerd/console/tc_zos.go +++ /dev/null @@ -1,39 +0,0 @@ -/* - Copyright The containerd Authors. - - Licensed under the Apache License, Version 2.0 (the "License"); - you may not use this file except in compliance with the License. - You may obtain a copy of the License at - - http://www.apache.org/licenses/LICENSE-2.0 - - Unless required by applicable law or agreed to in writing, software - distributed under the License is distributed on an "AS IS" BASIS, - WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - See the License for the specific language governing permissions and - limitations under the License. -*/ - -package console - -import ( - "os" - "strings" - - "golang.org/x/sys/unix" -) - -const ( - cmdTcGet = unix.TCGETS - cmdTcSet = unix.TCSETS -) - -// unlockpt is a no-op on zos. -func unlockpt(_ *os.File) error { - return nil -} - -// ptsname retrieves the name of the first available pts for the given master. -func ptsname(f *os.File) (string, error) { - return "/dev/ttyp" + strings.TrimPrefix(f.Name(), "/dev/ptyp"), nil -} diff --git a/vendor/github.com/erikgeiser/coninput/.gitignore b/vendor/github.com/erikgeiser/coninput/.gitignore new file mode 100644 index 00000000..66fd13c9 --- /dev/null +++ b/vendor/github.com/erikgeiser/coninput/.gitignore @@ -0,0 +1,15 @@ +# Binaries for programs and plugins +*.exe +*.exe~ +*.dll +*.so +*.dylib + +# Test binary, built with `go test -c` +*.test + +# Output of the go coverage tool, specifically when used with LiteIDE +*.out + +# Dependency directories (remove the comment below to include it) +# vendor/ diff --git a/vendor/github.com/erikgeiser/coninput/.golangci.yml b/vendor/github.com/erikgeiser/coninput/.golangci.yml new file mode 100644 index 00000000..d3ed7103 --- /dev/null +++ b/vendor/github.com/erikgeiser/coninput/.golangci.yml @@ -0,0 +1,24 @@ +linters: + enable-all: true + disable: + - golint + - interfacer + - scopelint + - maligned + - rowserrcheck + - funlen + - depguard + - goerr113 + - exhaustivestruct + - testpackage + - gochecknoglobals + - wrapcheck + - forbidigo + - ifshort + - cyclop + - gomoddirectives +linters-settings: + exhaustive: + default-signifies-exhaustive: true +issues: + exclude-use-default: false diff --git a/vendor/github.com/erikgeiser/coninput/LICENSE b/vendor/github.com/erikgeiser/coninput/LICENSE new file mode 100644 index 00000000..83c24408 --- /dev/null +++ b/vendor/github.com/erikgeiser/coninput/LICENSE @@ -0,0 +1,21 @@ +MIT License + +Copyright (c) 2021 Erik G. + +Permission is hereby granted, free of charge, to any person obtaining a copy +of this software and associated documentation files (the "Software"), to deal +in the Software without restriction, including without limitation the rights +to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +copies of the Software, and to permit persons to whom the Software is +furnished to do so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in all +copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE +SOFTWARE. diff --git a/vendor/github.com/erikgeiser/coninput/README.md b/vendor/github.com/erikgeiser/coninput/README.md new file mode 100644 index 00000000..22619d13 --- /dev/null +++ b/vendor/github.com/erikgeiser/coninput/README.md @@ -0,0 +1,2 @@ +# coninput +Go library for input handling using Windows Console API diff --git a/vendor/github.com/erikgeiser/coninput/keycodes.go b/vendor/github.com/erikgeiser/coninput/keycodes.go new file mode 100644 index 00000000..902ee1b7 --- /dev/null +++ b/vendor/github.com/erikgeiser/coninput/keycodes.go @@ -0,0 +1,205 @@ +package coninput + +// VirtualKeyCode holds a virtual key code (see +// https://docs.microsoft.com/en-us/windows/win32/inputdev/virtual-key-codes). +type VirtualKeyCode uint16 + +const ( + VK_LBUTTON VirtualKeyCode = 0x01 + VK_RBUTTON VirtualKeyCode = 0x02 + VK_CANCEL VirtualKeyCode = 0x03 + VK_MBUTTON VirtualKeyCode = 0x04 + VK_XBUTTON1 VirtualKeyCode = 0x05 + VK_XBUTTON2 VirtualKeyCode = 0x06 + VK_BACK VirtualKeyCode = 0x08 + VK_TAB VirtualKeyCode = 0x09 + VK_CLEAR VirtualKeyCode = 0x0C + VK_RETURN VirtualKeyCode = 0x0D + VK_SHIFT VirtualKeyCode = 0x10 + VK_CONTROL VirtualKeyCode = 0x11 + VK_MENU VirtualKeyCode = 0x12 + VK_PAUSE VirtualKeyCode = 0x13 + VK_CAPITAL VirtualKeyCode = 0x14 + VK_KANA VirtualKeyCode = 0x15 + VK_HANGEUL VirtualKeyCode = 0x15 + VK_HANGUL VirtualKeyCode = 0x15 + VK_IME_ON VirtualKeyCode = 0x16 + VK_JUNJA VirtualKeyCode = 0x17 + VK_FINAL VirtualKeyCode = 0x18 + VK_HANJA VirtualKeyCode = 0x19 + VK_KANJI VirtualKeyCode = 0x19 + VK_IME_OFF VirtualKeyCode = 0x1A + VK_ESCAPE VirtualKeyCode = 0x1B + VK_CONVERT VirtualKeyCode = 0x1C + VK_NONCONVERT VirtualKeyCode = 0x1D + VK_ACCEPT VirtualKeyCode = 0x1E + VK_MODECHANGE VirtualKeyCode = 0x1F + VK_SPACE VirtualKeyCode = 0x20 + VK_PRIOR VirtualKeyCode = 0x21 + VK_NEXT VirtualKeyCode = 0x22 + VK_END VirtualKeyCode = 0x23 + VK_HOME VirtualKeyCode = 0x24 + VK_LEFT VirtualKeyCode = 0x25 + VK_UP VirtualKeyCode = 0x26 + VK_RIGHT VirtualKeyCode = 0x27 + VK_DOWN VirtualKeyCode = 0x28 + VK_SELECT VirtualKeyCode = 0x29 + VK_PRINT VirtualKeyCode = 0x2A + VK_EXECUTE VirtualKeyCode = 0x2B + VK_SNAPSHOT VirtualKeyCode = 0x2C + VK_INSERT VirtualKeyCode = 0x2D + VK_DELETE VirtualKeyCode = 0x2E + VK_HELP VirtualKeyCode = 0x2F + VK_0 VirtualKeyCode = 0x30 + VK_1 VirtualKeyCode = 0x31 + VK_2 VirtualKeyCode = 0x32 + VK_3 VirtualKeyCode = 0x33 + VK_4 VirtualKeyCode = 0x34 + VK_5 VirtualKeyCode = 0x35 + VK_6 VirtualKeyCode = 0x36 + VK_7 VirtualKeyCode = 0x37 + VK_8 VirtualKeyCode = 0x38 + VK_9 VirtualKeyCode = 0x39 + VK_A VirtualKeyCode = 0x41 + VK_B VirtualKeyCode = 0x42 + VK_C VirtualKeyCode = 0x43 + VK_D VirtualKeyCode = 0x44 + VK_E VirtualKeyCode = 0x45 + VK_F VirtualKeyCode = 0x46 + VK_G VirtualKeyCode = 0x47 + VK_H VirtualKeyCode = 0x48 + VK_I VirtualKeyCode = 0x49 + VK_J VirtualKeyCode = 0x4A + VK_K VirtualKeyCode = 0x4B + VK_L VirtualKeyCode = 0x4C + VK_M VirtualKeyCode = 0x4D + VK_N VirtualKeyCode = 0x4E + VK_O VirtualKeyCode = 0x4F + VK_P VirtualKeyCode = 0x50 + VK_Q VirtualKeyCode = 0x51 + VK_R VirtualKeyCode = 0x52 + VK_S VirtualKeyCode = 0x53 + VK_T VirtualKeyCode = 0x54 + VK_U VirtualKeyCode = 0x55 + VK_V VirtualKeyCode = 0x56 + VK_W VirtualKeyCode = 0x57 + VK_X VirtualKeyCode = 0x58 + VK_Y VirtualKeyCode = 0x59 + VK_Z VirtualKeyCode = 0x5A + VK_LWIN VirtualKeyCode = 0x5B + VK_RWIN VirtualKeyCode = 0x5C + VK_APPS VirtualKeyCode = 0x5D + VK_SLEEP VirtualKeyCode = 0x5F + VK_NUMPAD0 VirtualKeyCode = 0x60 + VK_NUMPAD1 VirtualKeyCode = 0x61 + VK_NUMPAD2 VirtualKeyCode = 0x62 + VK_NUMPAD3 VirtualKeyCode = 0x63 + VK_NUMPAD4 VirtualKeyCode = 0x64 + VK_NUMPAD5 VirtualKeyCode = 0x65 + VK_NUMPAD6 VirtualKeyCode = 0x66 + VK_NUMPAD7 VirtualKeyCode = 0x67 + VK_NUMPAD8 VirtualKeyCode = 0x68 + VK_NUMPAD9 VirtualKeyCode = 0x69 + VK_MULTIPLY VirtualKeyCode = 0x6A + VK_ADD VirtualKeyCode = 0x6B + VK_SEPARATOR VirtualKeyCode = 0x6C + VK_SUBTRACT VirtualKeyCode = 0x6D + VK_DECIMAL VirtualKeyCode = 0x6E + VK_DIVIDE VirtualKeyCode = 0x6F + VK_F1 VirtualKeyCode = 0x70 + VK_F2 VirtualKeyCode = 0x71 + VK_F3 VirtualKeyCode = 0x72 + VK_F4 VirtualKeyCode = 0x73 + VK_F5 VirtualKeyCode = 0x74 + VK_F6 VirtualKeyCode = 0x75 + VK_F7 VirtualKeyCode = 0x76 + VK_F8 VirtualKeyCode = 0x77 + VK_F9 VirtualKeyCode = 0x78 + VK_F10 VirtualKeyCode = 0x79 + VK_F11 VirtualKeyCode = 0x7A + VK_F12 VirtualKeyCode = 0x7B + VK_F13 VirtualKeyCode = 0x7C + VK_F14 VirtualKeyCode = 0x7D + VK_F15 VirtualKeyCode = 0x7E + VK_F16 VirtualKeyCode = 0x7F + VK_F17 VirtualKeyCode = 0x80 + VK_F18 VirtualKeyCode = 0x81 + VK_F19 VirtualKeyCode = 0x82 + VK_F20 VirtualKeyCode = 0x83 + VK_F21 VirtualKeyCode = 0x84 + VK_F22 VirtualKeyCode = 0x85 + VK_F23 VirtualKeyCode = 0x86 + VK_F24 VirtualKeyCode = 0x87 + VK_NUMLOCK VirtualKeyCode = 0x90 + VK_SCROLL VirtualKeyCode = 0x91 + VK_OEM_NEC_EQUAL VirtualKeyCode = 0x92 + VK_OEM_FJ_JISHO VirtualKeyCode = 0x92 + VK_OEM_FJ_MASSHOU VirtualKeyCode = 0x93 + VK_OEM_FJ_TOUROKU VirtualKeyCode = 0x94 + VK_OEM_FJ_LOYA VirtualKeyCode = 0x95 + VK_OEM_FJ_ROYA VirtualKeyCode = 0x96 + VK_LSHIFT VirtualKeyCode = 0xA0 + VK_RSHIFT VirtualKeyCode = 0xA1 + VK_LCONTROL VirtualKeyCode = 0xA2 + VK_RCONTROL VirtualKeyCode = 0xA3 + VK_LMENU VirtualKeyCode = 0xA4 + VK_RMENU VirtualKeyCode = 0xA5 + VK_BROWSER_BACK VirtualKeyCode = 0xA6 + VK_BROWSER_FORWARD VirtualKeyCode = 0xA7 + VK_BROWSER_REFRESH VirtualKeyCode = 0xA8 + VK_BROWSER_STOP VirtualKeyCode = 0xA9 + VK_BROWSER_SEARCH VirtualKeyCode = 0xAA + VK_BROWSER_FAVORITES VirtualKeyCode = 0xAB + VK_BROWSER_HOME VirtualKeyCode = 0xAC + VK_VOLUME_MUTE VirtualKeyCode = 0xAD + VK_VOLUME_DOWN VirtualKeyCode = 0xAE + VK_VOLUME_UP VirtualKeyCode = 0xAF + VK_MEDIA_NEXT_TRACK VirtualKeyCode = 0xB0 + VK_MEDIA_PREV_TRACK VirtualKeyCode = 0xB1 + VK_MEDIA_STOP VirtualKeyCode = 0xB2 + VK_MEDIA_PLAY_PAUSE VirtualKeyCode = 0xB3 + VK_LAUNCH_MAIL VirtualKeyCode = 0xB4 + VK_LAUNCH_MEDIA_SELECT VirtualKeyCode = 0xB5 + VK_LAUNCH_APP1 VirtualKeyCode = 0xB6 + VK_LAUNCH_APP2 VirtualKeyCode = 0xB7 + VK_OEM_1 VirtualKeyCode = 0xBA + VK_OEM_PLUS VirtualKeyCode = 0xBB + VK_OEM_COMMA VirtualKeyCode = 0xBC + VK_OEM_MINUS VirtualKeyCode = 0xBD + VK_OEM_PERIOD VirtualKeyCode = 0xBE + VK_OEM_2 VirtualKeyCode = 0xBF + VK_OEM_3 VirtualKeyCode = 0xC0 + VK_OEM_4 VirtualKeyCode = 0xDB + VK_OEM_5 VirtualKeyCode = 0xDC + VK_OEM_6 VirtualKeyCode = 0xDD + VK_OEM_7 VirtualKeyCode = 0xDE + VK_OEM_8 VirtualKeyCode = 0xDF + VK_OEM_AX VirtualKeyCode = 0xE1 + VK_OEM_102 VirtualKeyCode = 0xE2 + VK_ICO_HELP VirtualKeyCode = 0xE3 + VK_ICO_00 VirtualKeyCode = 0xE4 + VK_PROCESSKEY VirtualKeyCode = 0xE5 + VK_ICO_CLEAR VirtualKeyCode = 0xE6 + VK_OEM_RESET VirtualKeyCode = 0xE9 + VK_OEM_JUMP VirtualKeyCode = 0xEA + VK_OEM_PA1 VirtualKeyCode = 0xEB + VK_OEM_PA2 VirtualKeyCode = 0xEC + VK_OEM_PA3 VirtualKeyCode = 0xED + VK_OEM_WSCTRL VirtualKeyCode = 0xEE + VK_OEM_CUSEL VirtualKeyCode = 0xEF + VK_OEM_ATTN VirtualKeyCode = 0xF0 + VK_OEM_FINISH VirtualKeyCode = 0xF1 + VK_OEM_COPY VirtualKeyCode = 0xF2 + VK_OEM_AUTO VirtualKeyCode = 0xF3 + VK_OEM_ENLW VirtualKeyCode = 0xF4 + VK_OEM_BACKTAB VirtualKeyCode = 0xF5 + VK_ATTN VirtualKeyCode = 0xF6 + VK_CRSEL VirtualKeyCode = 0xF7 + VK_EXSEL VirtualKeyCode = 0xF8 + VK_EREOF VirtualKeyCode = 0xF9 + VK_PLAY VirtualKeyCode = 0xFA + VK_ZOOM VirtualKeyCode = 0xFB + VK_NONAME VirtualKeyCode = 0xFC + VK_PA1 VirtualKeyCode = 0xFD + VK_OEM_CLEAR VirtualKeyCode = 0xFE +) diff --git a/vendor/github.com/erikgeiser/coninput/mode.go b/vendor/github.com/erikgeiser/coninput/mode.go new file mode 100644 index 00000000..e6d8bf44 --- /dev/null +++ b/vendor/github.com/erikgeiser/coninput/mode.go @@ -0,0 +1,82 @@ +//go:build windows +// +build windows + +package coninput + +import ( + "strings" + + "golang.org/x/sys/windows" +) + +// AddInputModes returns the given mode with one or more additional modes enabled. +func AddInputModes(mode uint32, enableModes ...uint32) uint32 { + for _, enableMode := range enableModes { + mode |= enableMode + } + + return mode +} + +// RemoveInputModes returns the given mode with one or more additional modes disabled. +func RemoveInputModes(mode uint32, disableModes ...uint32) uint32 { + for _, disableMode := range disableModes { + mode &^= disableMode + } + + return mode +} + +// ToggleInputModes returns the given mode with one or more additional modes toggeled. +func ToggleInputModes(mode uint32, toggleModes ...uint32) uint32 { + for _, toggeMode := range toggleModes { + mode ^= toggeMode + } + + return mode +} + +var inputModes = []struct { + mode uint32 + name string +}{ + {mode: windows.ENABLE_ECHO_INPUT, name: "ENABLE_ECHO_INPUT"}, + {mode: windows.ENABLE_INSERT_MODE, name: "ENABLE_INSERT_MODE"}, + {mode: windows.ENABLE_LINE_INPUT, name: "ENABLE_LINE_INPUT"}, + {mode: windows.ENABLE_MOUSE_INPUT, name: "ENABLE_MOUSE_INPUT"}, + {mode: windows.ENABLE_PROCESSED_INPUT, name: "ENABLE_PROCESSED_INPUT"}, + {mode: windows.ENABLE_QUICK_EDIT_MODE, name: "ENABLE_QUICK_EDIT_MODE"}, + {mode: windows.ENABLE_WINDOW_INPUT, name: "ENABLE_WINDOW_INPUT"}, + {mode: windows.ENABLE_VIRTUAL_TERMINAL_INPUT, name: "ENABLE_VIRTUAL_TERMINAL_INPUT"}, +} + +// ListInputMode returnes the isolated enabled input modes as a list. +func ListInputModes(mode uint32) []uint32 { + modes := []uint32{} + + for _, inputMode := range inputModes { + if mode&inputMode.mode > 0 { + modes = append(modes, inputMode.mode) + } + } + + return modes +} + +// ListInputMode returnes the isolated enabled input mode names as a list. +func ListInputModeNames(mode uint32) []string { + modes := []string{} + + for _, inputMode := range inputModes { + if mode&inputMode.mode > 0 { + modes = append(modes, inputMode.name) + } + } + + return modes +} + +// DescribeInputMode returns a string containing the names of each enabled input mode. +func DescribeInputMode(mode uint32) string { + return strings.Join(ListInputModeNames(mode), "|") +} diff --git a/vendor/github.com/erikgeiser/coninput/read.go b/vendor/github.com/erikgeiser/coninput/read.go new file mode 100644 index 00000000..b2dd82f1 --- /dev/null +++ b/vendor/github.com/erikgeiser/coninput/read.go @@ -0,0 +1,154 @@ +//go:build windows +// +build windows + +package coninput + +import ( + "fmt" + "syscall" + "unsafe" + + "golang.org/x/sys/windows" +) + +var ( + modkernel32 = windows.NewLazySystemDLL("kernel32.dll") + procReadConsoleInputW = modkernel32.NewProc("ReadConsoleInputW") + procPeekConsoleInputW = modkernel32.NewProc("PeekConsoleInputW") + procGetNumberOfConsoleInputEvents = modkernel32.NewProc("GetNumberOfConsoleInputEvents") + procFlushConsoleInputBuffer = modkernel32.NewProc("FlushConsoleInputBuffer") +) + +// NewStdinHandle is a shortcut for windows.GetStdHandle(windows.STD_INPUT_HANDLE). +func NewStdinHandle() (windows.Handle, error) { + return windows.GetStdHandle(windows.STD_INPUT_HANDLE) +} + +// WinReadConsoleInput is a thin wrapper around the Windows console API function +// ReadConsoleInput (see +// https://docs.microsoft.com/en-us/windows/console/readconsoleinput). In most +// cases it is more practical to either use ReadConsoleInput or +// ReadNConsoleInputs. +func WinReadConsoleInput(consoleInput windows.Handle, buffer *InputRecord, + length uint32, numberOfEventsRead *uint32) error { + r, _, e := syscall.Syscall6(procReadConsoleInputW.Addr(), 4, + uintptr(consoleInput), uintptr(unsafe.Pointer(buffer)), uintptr(length), + uintptr(unsafe.Pointer(numberOfEventsRead)), 0, 0) + if r == 0 { + return error(e) + } + + return nil +} + +// ReadNConsoleInputs is a wrapper around ReadConsoleInput (see +// https://docs.microsoft.com/en-us/windows/console/readconsoleinput) that +// automates the event buffer allocation in oder to provide io.Reader-like +// sematics. maxEvents must be greater than zero. +func ReadNConsoleInputs(console windows.Handle, maxEvents uint32) ([]InputRecord, error) { + if maxEvents == 0 { + return nil, fmt.Errorf("maxEvents cannot be zero") + } + + var inputRecords = make([]InputRecord, maxEvents) + n, err := ReadConsoleInput(console, inputRecords) + + return inputRecords[:n], err +} + +// ReadConsoleInput provides an ideomatic interface to the Windows console API +// function ReadConsoleInput (see +// https://docs.microsoft.com/en-us/windows/console/readconsoleinput). The size +// of inputRecords must be greater than zero. +func ReadConsoleInput(console windows.Handle, inputRecords []InputRecord) (uint32, error) { + if len(inputRecords) == 0 { + return 0, fmt.Errorf("size of input record buffer cannot be zero") + } + + var read uint32 + err := WinReadConsoleInput(console, &inputRecords[0], uint32(len(inputRecords)), &read) + + return read, err +} + +// WinPeekConsoleInput is a thin wrapper around the Windows console API function +// PeekConsoleInput (see +// https://docs.microsoft.com/en-us/windows/console/peekconsoleinput). In most +// cases it is more practical to either use PeekConsoleInput or +// PeekNConsoleInputs. +func WinPeekConsoleInput(consoleInput windows.Handle, buffer *InputRecord, + length uint32, numberOfEventsRead *uint32) error { + r, _, e := syscall.Syscall6(procPeekConsoleInputW.Addr(), 4, + uintptr(consoleInput), uintptr(unsafe.Pointer(buffer)), uintptr(length), + uintptr(unsafe.Pointer(numberOfEventsRead)), 0, 0) + if r == 0 { + return error(e) + } + + return nil + +} + +// PeekNConsoleInputs is a wrapper around PeekConsoleInput (see +// https://docs.microsoft.com/en-us/windows/console/peekconsoleinput) that +// automates the event buffer allocation in oder to provide io.Reader-like +// sematics. maxEvents must be greater than zero. +func PeekNConsoleInputs(console windows.Handle, maxEvents uint32) ([]InputRecord, error) { + if maxEvents == 0 { + return nil, fmt.Errorf("maxEvents cannot be zero") + } + + var inputRecords = make([]InputRecord, maxEvents) + n, err := PeekConsoleInput(console, inputRecords) + + return inputRecords[:n], err +} + +// PeekConsoleInput provides an ideomatic interface to the Windows console API +// function PeekConsoleInput (see +// https://docs.microsoft.com/en-us/windows/console/peekconsoleinput). The size +// of inputRecords must be greater than zero. +func PeekConsoleInput(console windows.Handle, inputRecords []InputRecord) (uint32, error) { + if len(inputRecords) == 0 { + return 0, fmt.Errorf("size of input record buffer cannot be zero") + } + + var read uint32 + + err := WinPeekConsoleInput(console, &inputRecords[0], uint32(len(inputRecords)), &read) + + return read, err +} + +// WinGetNumberOfConsoleInputEvents provides an ideomatic interface to the +// Windows console API function GetNumberOfConsoleInputEvents (see +// https://docs.microsoft.com/en-us/windows/console/getnumberofconsoleinputevents). +func WinGetNumberOfConsoleInputEvents(consoleInput windows.Handle, numberOfEvents *uint32) error { + r, _, e := syscall.Syscall6(procGetNumberOfConsoleInputEvents.Addr(), 2, + uintptr(consoleInput), uintptr(unsafe.Pointer(numberOfEvents)), 0, + 0, 0, 0) + if r == 0 { + return error(e) + } + + return nil +} + +// GetNumberOfConsoleInputEvents provides an ideomatic interface to the Windows +// console API function GetNumberOfConsoleInputEvents (see +// https://docs.microsoft.com/en-us/windows/console/getnumberofconsoleinputevents). +func GetNumberOfConsoleInputEvents(console windows.Handle) (uint32, error) { + var nEvents uint32 + err := WinGetNumberOfConsoleInputEvents(console, &nEvents) + + return nEvents, err +} + +func FlushConsoleInputBuffer(consoleInput windows.Handle) error { + r, _, e := syscall.Syscall(procFlushConsoleInputBuffer.Addr(), 1, uintptr(consoleInput), 0, 0) + if r == 0 { + return error(e) + } + + return nil +} diff --git a/vendor/github.com/erikgeiser/coninput/records.go b/vendor/github.com/erikgeiser/coninput/records.go new file mode 100644 index 00000000..cccf7fbf --- /dev/null +++ b/vendor/github.com/erikgeiser/coninput/records.go @@ -0,0 +1,486 @@ +package coninput + +import ( + "encoding/binary" + "fmt" + "strconv" + "strings" +) + +const ( + maxEventSize = 16 + wordPaddingBytes = 2 +) + +// EventType denots the type of an event +type EventType uint16 + +// EventUnion is the union data type that contains the data for any event. +type EventUnion [maxEventSize]byte + +// InputRecord corresponds to the INPUT_RECORD structure from the Windows +// console API (see +// https://docs.microsoft.com/en-us/windows/console/input-record-str). +type InputRecord struct { + // EventType specifies the type of event that helt in Event. + EventType EventType + + // Padding of the 16-bit EventType to a whole 32-bit dword. + _ [wordPaddingBytes]byte + + // Event holds the actual event data. Use Unrap to access it as its + // respective event type. + Event EventUnion +} + +// String implements fmt.Stringer for InputRecord. +func (ir InputRecord) String() string { + return ir.Unwrap().String() +} + +// Unwrap parses the event data into an EventRecord of the respective event +// type. The data in the returned EventRecord does not contain any references to +// the passed InputRecord. +func (ir InputRecord) Unwrap() EventRecord { + switch ir.EventType { + case FocusEventType: + return FocusEventRecord{SetFocus: ir.Event[0] > 0} + case KeyEventType: + return KeyEventRecord{ + KeyDown: binary.LittleEndian.Uint32(ir.Event[0:4]) > 0, + RepeatCount: binary.LittleEndian.Uint16(ir.Event[4:6]), + VirtualKeyCode: VirtualKeyCode(binary.LittleEndian.Uint16(ir.Event[6:8])), + VirtualScanCode: VirtualKeyCode(binary.LittleEndian.Uint16(ir.Event[8:10])), + Char: rune(binary.LittleEndian.Uint16(ir.Event[10:12])), + ControlKeyState: ControlKeyState(binary.LittleEndian.Uint32(ir.Event[12:16])), + } + case MouseEventType: + m := MouseEventRecord{ + MousePositon: Coord{ + X: binary.LittleEndian.Uint16(ir.Event[0:2]), + Y: binary.LittleEndian.Uint16(ir.Event[2:4]), + }, + ButtonState: ButtonState(binary.LittleEndian.Uint32(ir.Event[4:8])), + ControlKeyState: ControlKeyState(binary.LittleEndian.Uint32(ir.Event[8:12])), + EventFlags: EventFlags(binary.LittleEndian.Uint32(ir.Event[12:16])), + } + + if (m.EventFlags&MOUSE_WHEELED > 0) || (m.EventFlags&MOUSE_HWHEELED > 0) { + if int16(highWord(uint32(m.ButtonState))) > 0 { + m.WheelDirection = 1 + } else { + m.WheelDirection = -1 + } + } + + return m + case WindowBufferSizeEventType: + return WindowBufferSizeEventRecord{ + Size: Coord{ + X: binary.LittleEndian.Uint16(ir.Event[0:2]), + Y: binary.LittleEndian.Uint16(ir.Event[2:4]), + }, + } + case MenuEventType: + return MenuEventRecord{ + CommandID: binary.LittleEndian.Uint32(ir.Event[0:4]), + } + default: + return &UnknownEvent{InputRecord: ir} + } +} + +// EventRecord represents one of the following event types: +// TypeFocusEventRecord, TypeKeyEventRecord, TypeMouseEventRecord, +// TypeWindowBufferSizeEvent, TypeMenuEventRecord and UnknownEvent. +type EventRecord interface { + Type() string + fmt.Stringer +} + +// FocusEventType is the event type for a FocusEventRecord (see +// https://docs.microsoft.com/en-us/windows/console/input-record-str). +const FocusEventType EventType = 0x0010 + +// FocusEventRecord represent the FOCUS_EVENT_RECORD structure from the Windows +// console API (see +// https://docs.microsoft.com/en-us/windows/console/focus-event-record-str). +// These events are used internally by the Windows console API and should be +// ignored. +type FocusEventRecord struct { + // SetFocus is reserved and should not be used. + SetFocus bool +} + +// Ensure that FocusEventRecord satisfies EventRecord interface. +var _ EventRecord = FocusEventRecord{} + +// Type ensures that FocusEventRecord satisfies EventRecord interface. +func (e FocusEventRecord) Type() string { return "FocusEvent" } + +// String ensures that FocusEventRecord satisfies EventRecord and fmt.Stringer +// interfaces. +func (e FocusEventRecord) String() string { return fmt.Sprintf("%s[%v]", e.Type(), e.SetFocus) } + +// KeyEventType is the event type for a KeyEventRecord (see +// https://docs.microsoft.com/en-us/windows/console/input-record-str). +const KeyEventType EventType = 0x0001 + +// KeyEventRecord represent the KEY_EVENT_RECORD structure from the Windows +// console API (see +// https://docs.microsoft.com/en-us/windows/console/key-event-record-str). +type KeyEventRecord struct { + // KeyDown specified whether the key is pressed or released. + KeyDown bool + + // RepeatCount indicates that a key is being held down. For example, when a + // key is held down, five events with RepeatCount equal to 1 may be + // generated, one event with RepeatCount equal to 5, or multiple events + // with RepeatCount greater than or equal to 1. + RepeatCount uint16 + + // VirtualKeyCode identifies the given key in a device-independent manner + // (see + // https://docs.microsoft.com/en-us/windows/win32/inputdev/virtual-key-codes). + VirtualKeyCode VirtualKeyCode + + // VirtualScanCode represents the device-dependent value generated by the + // keyboard hardware. + VirtualScanCode VirtualKeyCode + + // Char is the character that corresponds to the pressed key. Char can be + // zero for some keys. + Char rune + + //ControlKeyState holds the state of the control keys. + ControlKeyState ControlKeyState +} + +// Ensure that KeyEventRecord satisfies EventRecord interface. +var _ EventRecord = KeyEventRecord{} + +// Type ensures that KeyEventRecord satisfies EventRecord interface. +func (e KeyEventRecord) Type() string { return "KeyEvent" } + +// String ensures that KeyEventRecord satisfies EventRecord and fmt.Stringer +// interfaces. +func (e KeyEventRecord) String() string { + infos := []string{} + + repeat := "" + if e.RepeatCount > 1 { + repeat = "x" + strconv.Itoa(int(e.RepeatCount)) + } + + infos = append(infos, fmt.Sprintf("%q%s", e.Char, repeat)) + + direction := "up" + if e.KeyDown { + direction = "down" + } + + infos = append(infos, direction) + + if e.ControlKeyState != NO_CONTROL_KEY { + infos = append(infos, e.ControlKeyState.String()) + } + + infos = append(infos, fmt.Sprintf("KeyCode: %d", e.VirtualKeyCode)) + infos = append(infos, fmt.Sprintf("ScanCode: %d", e.VirtualScanCode)) + + return fmt.Sprintf("%s[%s]", e.Type(), strings.Join(infos, ", ")) +} + +// MenuEventType is the event type for a MenuEventRecord (see +// https://docs.microsoft.com/en-us/windows/console/input-record-str). +const MenuEventType EventType = 0x0008 + +// MenuEventRecord represent the MENU_EVENT_RECORD structure from the Windows +// console API (see +// https://docs.microsoft.com/en-us/windows/console/menu-event-record-str). +// These events are deprecated by the Windows console API and should be ignored. +type MenuEventRecord struct { + CommandID uint32 +} + +// Ensure that MenuEventRecord satisfies EventRecord interface. +var _ EventRecord = MenuEventRecord{} + +// Type ensures that MenuEventRecord satisfies EventRecord interface. +func (e MenuEventRecord) Type() string { return "MenuEvent" } + +// String ensures that MenuEventRecord satisfies EventRecord and fmt.Stringer +// interfaces. +func (e MenuEventRecord) String() string { return fmt.Sprintf("MenuEvent[%d]", e.CommandID) } + +// MouseEventType is the event type for a MouseEventRecord (see +// https://docs.microsoft.com/en-us/windows/console/input-record-str). +const MouseEventType EventType = 0x0002 + +// MouseEventRecord represent the MOUSE_EVENT_RECORD structure from the Windows +// console API (see +// https://docs.microsoft.com/en-us/windows/console/mouse-event-record-str). +type MouseEventRecord struct { + // MousePosition contains the location of the cursor, in terms of the + // console screen buffer's character-cell coordinates. + MousePositon Coord + + // ButtonState holds the status of the mouse buttons. + ButtonState ButtonState + + // ControlKeyState holds the state of the control keys. + ControlKeyState ControlKeyState + + // EventFlags specify tge type of mouse event. + EventFlags EventFlags + + // WheelDirection specified the direction in which the mouse wheel is + // spinning when EventFlags contains MOUSE_HWHEELED or MOUSE_WHEELED. When + // the event flags specify MOUSE_WHEELED it is 1 if the wheel rotated + // forward (away from the user) or -1 when it rotates backwards. When + // MOUSE_HWHEELED is specified it is 1 when the wheel rotates right and -1 + // when it rotates left. When the EventFlags do not indicate a mouse wheel + // event it is 0. + WheelDirection int +} + +// Ensure that MouseEventRecord satisfies EventRecord interface. +var _ EventRecord = MouseEventRecord{} + +func (e MouseEventRecord) WheelDirectionName() string { + if e.EventFlags&MOUSE_WHEELED > 0 { + if e.WheelDirection > 0 { + return "Forward" + } + + return "Backward" + } else if e.EventFlags&MOUSE_HWHEELED > 0 { + if e.WheelDirection > 0 { + return "Right" + } + + return "Left" + } + + return "" +} + +// Type ensures that MouseEventRecord satisfies EventRecord interface. +func (e MouseEventRecord) Type() string { return "MouseEvent" } + +// String ensures that MouseEventRecord satisfies EventRecord and fmt.Stringer +// interfaces. +func (e MouseEventRecord) String() string { + infos := []string{e.MousePositon.String()} + + if e.ButtonState&0xFF != 0 { + infos = append(infos, e.ButtonState.String()) + } + + eventDescription := e.EventFlags.String() + + wheelDirection := e.WheelDirectionName() + if wheelDirection != "" { + eventDescription += "(" + wheelDirection + ")" + } + + infos = append(infos, eventDescription) + + if e.ControlKeyState != NO_CONTROL_KEY { + infos = append(infos, e.ControlKeyState.String()) + } + + return fmt.Sprintf("%s[%s]", e.Type(), strings.Join(infos, ", ")) +} + +// WindowBufferSizeEventType is the event type for a WindowBufferSizeEventRecord +// (see https://docs.microsoft.com/en-us/windows/console/input-record-str). +const WindowBufferSizeEventType EventType = 0x0004 + +// WindowBufferSizeEventRecord represent the WINDOW_BUFFER_SIZE_RECORD structure +// from the Windows console API (see +// https://docs.microsoft.com/en-us/windows/console/window-buffer-size-record-str). +type WindowBufferSizeEventRecord struct { + // Size contains the size of the console screen buffer, in character cell columns and rows. + Size Coord +} + +// Ensure that WindowBufferSizeEventRecord satisfies EventRecord interface. +var _ EventRecord = WindowBufferSizeEventRecord{} + +// Type ensures that WindowBufferSizeEventRecord satisfies EventRecord interface. +func (e WindowBufferSizeEventRecord) Type() string { return "WindowBufferSizeEvent" } + +// String ensures that WindowBufferSizeEventRecord satisfies EventRecord and fmt.Stringer +// interfaces. +func (e WindowBufferSizeEventRecord) String() string { + return fmt.Sprintf("WindowBufferSizeEvent[%s]", e.Size) +} + +// UnknownEvent is generated when the event type does not match one of the +// following types: TypeFocusEventRecord, TypeKeyEventRecord, +// TypeMouseEventRecord, TypeWindowBufferSizeEvent, TypeMenuEventRecord and +// UnknownEvent. +type UnknownEvent struct { + InputRecord +} + +// Ensure that UnknownEvent satisfies EventRecord interface. +var _ EventRecord = UnknownEvent{} + +// Type ensures that UnknownEvent satisfies EventRecord interface. +func (e UnknownEvent) Type() string { return "UnknownEvent" } + +// String ensures that UnknownEvent satisfies EventRecord and fmt.Stringer +// interfaces. +func (e UnknownEvent) String() string { + return fmt.Sprintf("%s[Type: %d, Data: %v]", e.Type(), e.InputRecord.EventType, e.InputRecord.Event[:]) +} + +// Coord represent the COORD structure from the Windows +// console API (see https://docs.microsoft.com/en-us/windows/console/coord-str). +type Coord struct { + // X is the horizontal coordinate or column value. The units depend on the function call. + X uint16 + // Y is the vertical coordinate or row value. The units depend on the function call. + Y uint16 +} + +// String ensures that Coord satisfies the fmt.Stringer interface. +func (c Coord) String() string { + return fmt.Sprintf("(%d, %d)", c.X, c.Y) +} + +// ButtonState holds the state of the mouse buttons (see +// https://docs.microsoft.com/en-us/windows/console/mouse-event-record-str). +type ButtonState uint32 + +func (bs ButtonState) Contains(state ButtonState) bool { + return bs&state > 0 +} + +// String ensures that ButtonState satisfies the fmt.Stringer interface. +func (bs ButtonState) String() string { + switch { + case bs&FROM_LEFT_1ST_BUTTON_PRESSED > 0: + return "Left" + case bs&FROM_LEFT_2ND_BUTTON_PRESSED > 0: + return "2" + case bs&FROM_LEFT_3RD_BUTTON_PRESSED > 0: + return "3" + case bs&FROM_LEFT_4TH_BUTTON_PRESSED > 0: + return "4" + case bs&RIGHTMOST_BUTTON_PRESSED > 0: + return "Right" + case bs&0xFF == 0: + return "No Button" + default: + return fmt.Sprintf("Unknown(%d)", bs) + } +} + +func (bs ButtonState) IsReleased() bool { + return bs&0xff > 0 +} + +// Valid values for ButtonState. +const ( + FROM_LEFT_1ST_BUTTON_PRESSED ButtonState = 0x0001 + RIGHTMOST_BUTTON_PRESSED ButtonState = 0x0002 + FROM_LEFT_2ND_BUTTON_PRESSED ButtonState = 0x0004 + FROM_LEFT_3RD_BUTTON_PRESSED ButtonState = 0x0008 + FROM_LEFT_4TH_BUTTON_PRESSED ButtonState = 0x0010 +) + +// ControlKeyState holds the state of the control keys for key and mouse events +// (see https://docs.microsoft.com/en-us/windows/console/key-event-record-str +// and https://docs.microsoft.com/en-us/windows/console/mouse-event-record-str). +type ControlKeyState uint32 + +func (cks ControlKeyState) Contains(state ControlKeyState) bool { + return cks&state > 0 +} + +// Valid values for ControlKeyState. +const ( + CAPSLOCK_ON ControlKeyState = 0x0080 + ENHANCED_KEY ControlKeyState = 0x0100 + LEFT_ALT_PRESSED ControlKeyState = 0x0002 + LEFT_CTRL_PRESSED ControlKeyState = 0x0008 + NUMLOCK_ON ControlKeyState = 0x0020 + RIGHT_ALT_PRESSED ControlKeyState = 0x0001 + RIGHT_CTRL_PRESSED ControlKeyState = 0x0004 + SCROLLLOCK_ON ControlKeyState = 0x0040 + SHIFT_PRESSED ControlKeyState = 0x0010 + NO_CONTROL_KEY ControlKeyState = 0x0000 +) + +// String ensures that ControlKeyState satisfies the fmt.Stringer interface. +func (cks ControlKeyState) String() string { + controlKeys := []string{} + + switch { + case cks&CAPSLOCK_ON > 0: + controlKeys = append(controlKeys, "CapsLock") + case cks&ENHANCED_KEY > 0: + controlKeys = append(controlKeys, "Enhanced") + case cks&LEFT_ALT_PRESSED > 0: + controlKeys = append(controlKeys, "Alt") + case cks&LEFT_CTRL_PRESSED > 0: + controlKeys = append(controlKeys, "CTRL") + case cks&NUMLOCK_ON > 0: + controlKeys = append(controlKeys, "NumLock") + case cks&RIGHT_ALT_PRESSED > 0: + controlKeys = append(controlKeys, "RightAlt") + case cks&RIGHT_CTRL_PRESSED > 0: + controlKeys = append(controlKeys, "RightCTRL") + case cks&SCROLLLOCK_ON > 0: + controlKeys = append(controlKeys, "ScrollLock") + case cks&SHIFT_PRESSED > 0: + controlKeys = append(controlKeys, "Shift") + case cks == NO_CONTROL_KEY: + default: + return fmt.Sprintf("Unknown(%d)", cks) + } + + return strings.Join(controlKeys, ",") +} + +// EventFlags specifies the type of a mouse event (see +// https://docs.microsoft.com/en-us/windows/console/mouse-event-record-str). +type EventFlags uint32 + +// String ensures that EventFlags satisfies the fmt.Stringer interface. +func (ef EventFlags) String() string { + switch { + case ef&DOUBLE_CLICK > 0: + return "DoubleClick" + case ef&MOUSE_WHEELED > 0: + return "Wheeled" + case ef&MOUSE_MOVED > 0: + return "Moved" + case ef&MOUSE_HWHEELED > 0: + return "HWheeld" + case ef == CLICK: + return "Click" + default: + return fmt.Sprintf("Unknown(%d)", ef) + } +} + +func (ef EventFlags) Contains(flag EventFlags) bool { + return ef&flag > 0 +} + +// Valid values for EventFlags. +const ( + CLICK EventFlags = 0x0000 + MOUSE_MOVED EventFlags = 0x0001 + DOUBLE_CLICK EventFlags = 0x0002 + MOUSE_WHEELED EventFlags = 0x0004 + MOUSE_HWHEELED EventFlags = 0x0008 +) + +func highWord(data uint32) uint16 { + return uint16((data & 0xFFFF0000) >> 16) +} diff --git a/vendor/github.com/muesli/ansi/README.md b/vendor/github.com/muesli/ansi/README.md index cb28c656..f9d0fe9a 100644 --- a/vendor/github.com/muesli/ansi/README.md +++ b/vendor/github.com/muesli/ansi/README.md @@ -1,2 +1,31 @@ # ansi + +[![Latest Release](https://img.shields.io/github/release/muesli/ansi.svg)](https://github.com/muesli/ansi/releases) +[![Build Status](https://github.com/muesli/ansi/workflows/build/badge.svg)](https://github.com/muesli/ansi/actions) +[![Coverage Status](https://coveralls.io/repos/github/muesli/ansi/badge.svg?branch=master)](https://coveralls.io/github/muesli/ansi?branch=master) +[![Go ReportCard](https://goreportcard.com/badge/muesli/ansi)](https://goreportcard.com/report/muesli/ansi) +[![GoDoc](https://godoc.org/github.com/golang/gddo?status.svg)](https://pkg.go.dev/github.com/muesli/ansi) + Raw ANSI sequence helpers + +## ANSI Writer + +```go +import "github.com/muesli/ansi" + +w := ansi.Writer{Forward: os.Stdout} +w.Write([]byte("\x1b[31mHello, world!\x1b[0m")) +w.Close() +``` + +## Compressor + +The ANSI compressor eliminates unnecessary/redundant ANSI sequences. + +```go +import "github.com/muesli/ansi/compressor" + +w := compressor.Writer{Forward: os.Stdout} +w.Write([]byte("\x1b[31mHello, world!\x1b[0m")) +w.Close() +``` diff --git a/vendor/github.com/xo/terminfo/.gitignore b/vendor/github.com/xo/terminfo/.gitignore new file mode 100644 index 00000000..368e0c06 --- /dev/null +++ b/vendor/github.com/xo/terminfo/.gitignore @@ -0,0 +1,9 @@ +/.cache/ + +/cmd/infocmp/infocmp +/cmd/infocmp/.out/ + +/infocmp +/.out/ + +*.txt diff --git a/vendor/github.com/xo/terminfo/LICENSE b/vendor/github.com/xo/terminfo/LICENSE new file mode 100644 index 00000000..197dadb1 --- /dev/null +++ b/vendor/github.com/xo/terminfo/LICENSE @@ -0,0 +1,21 @@ +The MIT License (MIT) + +Copyright (c) 2016 Anmol Sethi + +Permission is hereby granted, free of charge, to any person obtaining a copy +of this software and associated documentation files (the "Software"), to deal +in the Software without restriction, including without limitation the rights +to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +copies of the Software, and to permit persons to whom the Software is +furnished to do so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in all +copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE +SOFTWARE. diff --git a/vendor/github.com/xo/terminfo/README.md b/vendor/github.com/xo/terminfo/README.md new file mode 100644 index 00000000..e5002d23 --- /dev/null +++ b/vendor/github.com/xo/terminfo/README.md @@ -0,0 +1,139 @@ +# About terminfo [![GoDoc][1]][2] + +Package `terminfo` provides a pure-Go implementation of reading information +from the terminfo database. + +`terminfo` is meant as a replacement for `ncurses` in simple Go programs. + +## Installing + +Install in the usual Go way: + +```sh +$ go get -u github.com/xo/terminfo +``` + +## Using + +Please see the [GoDoc API listing][2] for more information on using `terminfo`. + +```go +// _examples/simple/main.go +package main + +import ( + "bytes" + "fmt" + "log" + "os" + "os/signal" + "strings" + "sync" + "syscall" + + "github.com/xo/terminfo" +) + +func main() { + //r := rand.New(nil) + + // load terminfo + ti, err := terminfo.LoadFromEnv() + if err != nil { + log.Fatal(err) + } + + // cleanup + defer func() { + err := recover() + termreset(ti) + if err != nil { + log.Fatal(err) + } + }() + + terminit(ti) + termtitle(ti, "simple example!") + termputs(ti, 3, 3, "Ctrl-C to exit") + maxColors := termcolors(ti) + if maxColors > 256 { + maxColors = 256 + } + for i := 0; i < maxColors; i++ { + termputs(ti, 5+i/16, 5+i%16, ti.Colorf(i, 0, "█")) + } + + // wait for signal + sigs := make(chan os.Signal, 1) + signal.Notify(sigs, syscall.SIGINT, syscall.SIGTERM) + <-sigs +} + +// terminit initializes the special CA mode on the terminal, and makes the +// cursor invisible. +func terminit(ti *terminfo.Terminfo) { + buf := new(bytes.Buffer) + // set the cursor invisible + ti.Fprintf(buf, terminfo.CursorInvisible) + // enter special mode + ti.Fprintf(buf, terminfo.EnterCaMode) + // clear the screen + ti.Fprintf(buf, terminfo.ClearScreen) + os.Stdout.Write(buf.Bytes()) +} + +// termreset is the inverse of terminit. +func termreset(ti *terminfo.Terminfo) { + buf := new(bytes.Buffer) + ti.Fprintf(buf, terminfo.ExitCaMode) + ti.Fprintf(buf, terminfo.CursorNormal) + os.Stdout.Write(buf.Bytes()) +} + +// termputs puts a string at row, col, interpolating v. +func termputs(ti *terminfo.Terminfo, row, col int, s string, v ...interface{}) { + buf := new(bytes.Buffer) + ti.Fprintf(buf, terminfo.CursorAddress, row, col) + fmt.Fprintf(buf, s, v...) + os.Stdout.Write(buf.Bytes()) +} + +// sl is the status line terminfo. +var sl *terminfo.Terminfo + +// termtitle sets the window title. +func termtitle(ti *terminfo.Terminfo, s string) { + var once sync.Once + once.Do(func() { + if ti.Has(terminfo.HasStatusLine) { + return + } + // load the sl xterm if terminal is an xterm or has COLORTERM + if strings.Contains(strings.ToLower(os.Getenv("TERM")), "xterm") || os.Getenv("COLORTERM") == "truecolor" { + sl, _ = terminfo.Load("xterm+sl") + } + }) + if sl != nil { + ti = sl + } + if !ti.Has(terminfo.HasStatusLine) { + return + } + buf := new(bytes.Buffer) + ti.Fprintf(buf, terminfo.ToStatusLine) + fmt.Fprint(buf, s) + ti.Fprintf(buf, terminfo.FromStatusLine) + os.Stdout.Write(buf.Bytes()) +} + +// termcolors returns the maximum colors available for the terminal. +func termcolors(ti *terminfo.Terminfo) int { + if colors := ti.Num(terminfo.MaxColors); colors > 0 { + return colors + } + return int(terminfo.ColorLevelBasic) +} +``` + +[1]: https://godoc.org/github.com/xo/terminfo?status.svg +[2]: https://godoc.org/github.com/xo/terminfo diff --git a/vendor/github.com/xo/terminfo/caps.go b/vendor/github.com/xo/terminfo/caps.go new file mode 100644 index 00000000..e5e1d41f --- /dev/null +++ b/vendor/github.com/xo/terminfo/caps.go @@ -0,0 +1,31 @@ +package terminfo + +// BoolCapName returns the bool capability name. +func BoolCapName(i int) string { + return boolCapNames[2*i] +} + +// BoolCapNameShort returns the short bool capability name. +func BoolCapNameShort(i int) string { + return boolCapNames[2*i+1] +} + +// NumCapName returns the num capability name. +func NumCapName(i int) string { + return numCapNames[2*i] +} + +// NumCapNameShort returns the short num capability name. +func NumCapNameShort(i int) string { + return numCapNames[2*i+1] +} + +// StringCapName returns the string capability name. +func StringCapName(i int) string { + return stringCapNames[2*i] +} + +// StringCapNameShort returns the short string capability name. +func StringCapNameShort(i int) string { + return stringCapNames[2*i+1] +} diff --git a/vendor/github.com/xo/terminfo/capvals.go b/vendor/github.com/xo/terminfo/capvals.go new file mode 100644 index 00000000..0c2274e3 --- /dev/null +++ b/vendor/github.com/xo/terminfo/capvals.go @@ -0,0 +1,1525 @@ +package terminfo + +// Code generated by gen.go. DO NOT EDIT. +// Bool capabilities. +const ( + // The AutoLeftMargin [auto_left_margin, bw] bool capability indicates cub1 wraps from column 0 to last column. + AutoLeftMargin = iota + // The AutoRightMargin [auto_right_margin, am] bool capability indicates terminal has automatic margins. + AutoRightMargin + // The NoEscCtlc [no_esc_ctlc, xsb] bool capability indicates beehive (f1=escape, f2=ctrl C). + NoEscCtlc + // The CeolStandoutGlitch [ceol_standout_glitch, xhp] bool capability indicates standout not erased by overwriting (hp). + CeolStandoutGlitch + // The EatNewlineGlitch [eat_newline_glitch, xenl] bool capability indicates newline ignored after 80 cols (concept). + EatNewlineGlitch + // The EraseOverstrike [erase_overstrike, eo] bool capability indicates can erase overstrikes with a blank. + EraseOverstrike + // The GenericType [generic_type, gn] bool capability indicates generic line type. + GenericType + // The HardCopy [hard_copy, hc] bool capability indicates hardcopy terminal. + HardCopy + // The HasMetaKey [has_meta_key, km] bool capability indicates Has a meta key (i.e., sets 8th-bit). + HasMetaKey + // The HasStatusLine [has_status_line, hs] bool capability indicates has extra status line. + HasStatusLine + // The InsertNullGlitch [insert_null_glitch, in] bool capability indicates insert mode distinguishes nulls. + InsertNullGlitch + // The MemoryAbove [memory_above, da] bool capability indicates display may be retained above the screen. + MemoryAbove + // The MemoryBelow [memory_below, db] bool capability indicates display may be retained below the screen. + MemoryBelow + // The MoveInsertMode [move_insert_mode, mir] bool capability indicates safe to move while in insert mode. + MoveInsertMode + // The MoveStandoutMode [move_standout_mode, msgr] bool capability indicates safe to move while in standout mode. + MoveStandoutMode + // The OverStrike [over_strike, os] bool capability indicates terminal can overstrike. + OverStrike + // The StatusLineEscOk [status_line_esc_ok, eslok] bool capability indicates escape can be used on the status line. + StatusLineEscOk + // The DestTabsMagicSmso [dest_tabs_magic_smso, xt] bool capability indicates tabs destructive, magic so char (t1061). + DestTabsMagicSmso + // The TildeGlitch [tilde_glitch, hz] bool capability indicates cannot print ~'s (Hazeltine). + TildeGlitch + // The TransparentUnderline [transparent_underline, ul] bool capability indicates underline character overstrikes. + TransparentUnderline + // The XonXoff [xon_xoff, xon] bool capability indicates terminal uses xon/xoff handshaking. + XonXoff + // The NeedsXonXoff [needs_xon_xoff, nxon] bool capability indicates padding will not work, xon/xoff required. + NeedsXonXoff + // The PrtrSilent [prtr_silent, mc5i] bool capability indicates printer will not echo on screen. + PrtrSilent + // The HardCursor [hard_cursor, chts] bool capability indicates cursor is hard to see. + HardCursor + // The NonRevRmcup [non_rev_rmcup, nrrmc] bool capability indicates smcup does not reverse rmcup. + NonRevRmcup + // The NoPadChar [no_pad_char, npc] bool capability indicates pad character does not exist. + NoPadChar + // The NonDestScrollRegion [non_dest_scroll_region, ndscr] bool capability indicates scrolling region is non-destructive. + NonDestScrollRegion + // The CanChange [can_change, ccc] bool capability indicates terminal can re-define existing colors. + CanChange + // The BackColorErase [back_color_erase, bce] bool capability indicates screen erased with background color. + BackColorErase + // The HueLightnessSaturation [hue_lightness_saturation, hls] bool capability indicates terminal uses only HLS color notation (Tektronix). + HueLightnessSaturation + // The ColAddrGlitch [col_addr_glitch, xhpa] bool capability indicates only positive motion for hpa/mhpa caps. + ColAddrGlitch + // The CrCancelsMicroMode [cr_cancels_micro_mode, crxm] bool capability indicates using cr turns off micro mode. + CrCancelsMicroMode + // The HasPrintWheel [has_print_wheel, daisy] bool capability indicates printer needs operator to change character set. + HasPrintWheel + // The RowAddrGlitch [row_addr_glitch, xvpa] bool capability indicates only positive motion for vpa/mvpa caps. + RowAddrGlitch + // The SemiAutoRightMargin [semi_auto_right_margin, sam] bool capability indicates printing in last column causes cr. + SemiAutoRightMargin + // The CpiChangesRes [cpi_changes_res, cpix] bool capability indicates changing character pitch changes resolution. + CpiChangesRes + // The LpiChangesRes [lpi_changes_res, lpix] bool capability indicates changing line pitch changes resolution. + LpiChangesRes + // The BackspacesWithBs [backspaces_with_bs, OTbs] bool capability indicates uses ^H to move left. + BackspacesWithBs + // The CrtNoScrolling [crt_no_scrolling, OTns] bool capability indicates crt cannot scroll. + CrtNoScrolling + // The NoCorrectlyWorkingCr [no_correctly_working_cr, OTnc] bool capability indicates no way to go to start of line. + NoCorrectlyWorkingCr + // The GnuHasMetaKey [gnu_has_meta_key, OTMT] bool capability indicates has meta key. + GnuHasMetaKey + // The LinefeedIsNewline [linefeed_is_newline, OTNL] bool capability indicates move down with \n. + LinefeedIsNewline + // The HasHardwareTabs [has_hardware_tabs, OTpt] bool capability indicates has 8-char tabs invoked with ^I. + HasHardwareTabs + // The ReturnDoesClrEol [return_does_clr_eol, OTxr] bool capability indicates return clears the line. + ReturnDoesClrEol +) + +// Num capabilities. +const ( + // The Columns [columns, cols] num capability is number of columns in a line. + Columns = iota + // The InitTabs [init_tabs, it] num capability is tabs initially every # spaces. + InitTabs + // The Lines [lines, lines] num capability is number of lines on screen or page. + Lines + // The LinesOfMemory [lines_of_memory, lm] num capability is lines of memory if > line. 0 means varies. + LinesOfMemory + // The MagicCookieGlitch [magic_cookie_glitch, xmc] num capability is number of blank characters left by smso or rmso. + MagicCookieGlitch + // The PaddingBaudRate [padding_baud_rate, pb] num capability is lowest baud rate where padding needed. + PaddingBaudRate + // The VirtualTerminal [virtual_terminal, vt] num capability is virtual terminal number (CB/unix). + VirtualTerminal + // The WidthStatusLine [width_status_line, wsl] num capability is number of columns in status line. + WidthStatusLine + // The NumLabels [num_labels, nlab] num capability is number of labels on screen. + NumLabels + // The LabelHeight [label_height, lh] num capability is rows in each label. + LabelHeight + // The LabelWidth [label_width, lw] num capability is columns in each label. + LabelWidth + // The MaxAttributes [max_attributes, ma] num capability is maximum combined attributes terminal can handle. + MaxAttributes + // The MaximumWindows [maximum_windows, wnum] num capability is maximum number of definable windows. + MaximumWindows + // The MaxColors [max_colors, colors] num capability is maximum number of colors on screen. + MaxColors + // The MaxPairs [max_pairs, pairs] num capability is maximum number of color-pairs on the screen. + MaxPairs + // The NoColorVideo [no_color_video, ncv] num capability is video attributes that cannot be used with colors. + NoColorVideo + // The BufferCapacity [buffer_capacity, bufsz] num capability is numbers of bytes buffered before printing. + BufferCapacity + // The DotVertSpacing [dot_vert_spacing, spinv] num capability is spacing of pins vertically in pins per inch. + DotVertSpacing + // The DotHorzSpacing [dot_horz_spacing, spinh] num capability is spacing of dots horizontally in dots per inch. + DotHorzSpacing + // The MaxMicroAddress [max_micro_address, maddr] num capability is maximum value in micro_..._address. + MaxMicroAddress + // The MaxMicroJump [max_micro_jump, mjump] num capability is maximum value in parm_..._micro. + MaxMicroJump + // The MicroColSize [micro_col_size, mcs] num capability is character step size when in micro mode. + MicroColSize + // The MicroLineSize [micro_line_size, mls] num capability is line step size when in micro mode. + MicroLineSize + // The NumberOfPins [number_of_pins, npins] num capability is numbers of pins in print-head. + NumberOfPins + // The OutputResChar [output_res_char, orc] num capability is horizontal resolution in units per line. + OutputResChar + // The OutputResLine [output_res_line, orl] num capability is vertical resolution in units per line. + OutputResLine + // The OutputResHorzInch [output_res_horz_inch, orhi] num capability is horizontal resolution in units per inch. + OutputResHorzInch + // The OutputResVertInch [output_res_vert_inch, orvi] num capability is vertical resolution in units per inch. + OutputResVertInch + // The PrintRate [print_rate, cps] num capability is print rate in characters per second. + PrintRate + // The WideCharSize [wide_char_size, widcs] num capability is character step size when in double wide mode. + WideCharSize + // The Buttons [buttons, btns] num capability is number of buttons on mouse. + Buttons + // The BitImageEntwining [bit_image_entwining, bitwin] num capability is number of passes for each bit-image row. + BitImageEntwining + // The BitImageType [bit_image_type, bitype] num capability is type of bit-image device. + BitImageType + // The MagicCookieGlitchUl [magic_cookie_glitch_ul, OTug] num capability is number of blanks left by ul. + MagicCookieGlitchUl + // The CarriageReturnDelay [carriage_return_delay, OTdC] num capability is pad needed for CR. + CarriageReturnDelay + // The NewLineDelay [new_line_delay, OTdN] num capability is pad needed for LF. + NewLineDelay + // The BackspaceDelay [backspace_delay, OTdB] num capability is padding required for ^H. + BackspaceDelay + // The HorizontalTabDelay [horizontal_tab_delay, OTdT] num capability is padding required for ^I. + HorizontalTabDelay + // The NumberOfFunctionKeys [number_of_function_keys, OTkn] num capability is count of function keys. + NumberOfFunctionKeys +) + +// String capabilities. +const ( + // The BackTab [back_tab, cbt] string capability is the back tab (P). + BackTab = iota + // The Bell [bell, bel] string capability is the audible signal (bell) (P). + Bell + // The CarriageReturn [carriage_return, cr] string capability is the carriage return (P*) (P*). + CarriageReturn + // The ChangeScrollRegion [change_scroll_region, csr] string capability is the change region to line #1 to line #2 (P). + ChangeScrollRegion + // The ClearAllTabs [clear_all_tabs, tbc] string capability is the clear all tab stops (P). + ClearAllTabs + // The ClearScreen [clear_screen, clear] string capability is the clear screen and home cursor (P*). + ClearScreen + // The ClrEol [clr_eol, el] string capability is the clear to end of line (P). + ClrEol + // The ClrEos [clr_eos, ed] string capability is the clear to end of screen (P*). + ClrEos + // The ColumnAddress [column_address, hpa] string capability is the horizontal position #1, absolute (P). + ColumnAddress + // The CommandCharacter [command_character, cmdch] string capability is the terminal settable cmd character in prototype !?. + CommandCharacter + // The CursorAddress [cursor_address, cup] string capability is the move to row #1 columns #2. + CursorAddress + // The CursorDown [cursor_down, cud1] string capability is the down one line. + CursorDown + // The CursorHome [cursor_home, home] string capability is the home cursor (if no cup). + CursorHome + // The CursorInvisible [cursor_invisible, civis] string capability is the make cursor invisible. + CursorInvisible + // The CursorLeft [cursor_left, cub1] string capability is the move left one space. + CursorLeft + // The CursorMemAddress [cursor_mem_address, mrcup] string capability is the memory relative cursor addressing, move to row #1 columns #2. + CursorMemAddress + // The CursorNormal [cursor_normal, cnorm] string capability is the make cursor appear normal (undo civis/cvvis). + CursorNormal + // The CursorRight [cursor_right, cuf1] string capability is the non-destructive space (move right one space). + CursorRight + // The CursorToLl [cursor_to_ll, ll] string capability is the last line, first column (if no cup). + CursorToLl + // The CursorUp [cursor_up, cuu1] string capability is the up one line. + CursorUp + // The CursorVisible [cursor_visible, cvvis] string capability is the make cursor very visible. + CursorVisible + // The DeleteCharacter [delete_character, dch1] string capability is the delete character (P*). + DeleteCharacter + // The DeleteLine [delete_line, dl1] string capability is the delete line (P*). + DeleteLine + // The DisStatusLine [dis_status_line, dsl] string capability is the disable status line. + DisStatusLine + // The DownHalfLine [down_half_line, hd] string capability is the half a line down. + DownHalfLine + // The EnterAltCharsetMode [enter_alt_charset_mode, smacs] string capability is the start alternate character set (P). + EnterAltCharsetMode + // The EnterBlinkMode [enter_blink_mode, blink] string capability is the turn on blinking. + EnterBlinkMode + // The EnterBoldMode [enter_bold_mode, bold] string capability is the turn on bold (extra bright) mode. + EnterBoldMode + // The EnterCaMode [enter_ca_mode, smcup] string capability is the string to start programs using cup. + EnterCaMode + // The EnterDeleteMode [enter_delete_mode, smdc] string capability is the enter delete mode. + EnterDeleteMode + // The EnterDimMode [enter_dim_mode, dim] string capability is the turn on half-bright mode. + EnterDimMode + // The EnterInsertMode [enter_insert_mode, smir] string capability is the enter insert mode. + EnterInsertMode + // The EnterSecureMode [enter_secure_mode, invis] string capability is the turn on blank mode (characters invisible). + EnterSecureMode + // The EnterProtectedMode [enter_protected_mode, prot] string capability is the turn on protected mode. + EnterProtectedMode + // The EnterReverseMode [enter_reverse_mode, rev] string capability is the turn on reverse video mode. + EnterReverseMode + // The EnterStandoutMode [enter_standout_mode, smso] string capability is the begin standout mode. + EnterStandoutMode + // The EnterUnderlineMode [enter_underline_mode, smul] string capability is the begin underline mode. + EnterUnderlineMode + // The EraseChars [erase_chars, ech] string capability is the erase #1 characters (P). + EraseChars + // The ExitAltCharsetMode [exit_alt_charset_mode, rmacs] string capability is the end alternate character set (P). + ExitAltCharsetMode + // The ExitAttributeMode [exit_attribute_mode, sgr0] string capability is the turn off all attributes. + ExitAttributeMode + // The ExitCaMode [exit_ca_mode, rmcup] string capability is the strings to end programs using cup. + ExitCaMode + // The ExitDeleteMode [exit_delete_mode, rmdc] string capability is the end delete mode. + ExitDeleteMode + // The ExitInsertMode [exit_insert_mode, rmir] string capability is the exit insert mode. + ExitInsertMode + // The ExitStandoutMode [exit_standout_mode, rmso] string capability is the exit standout mode. + ExitStandoutMode + // The ExitUnderlineMode [exit_underline_mode, rmul] string capability is the exit underline mode. + ExitUnderlineMode + // The FlashScreen [flash_screen, flash] string capability is the visible bell (may not move cursor). + FlashScreen + // The FormFeed [form_feed, ff] string capability is the hardcopy terminal page eject (P*). + FormFeed + // The FromStatusLine [from_status_line, fsl] string capability is the return from status line. + FromStatusLine + // The Init1string [init_1string, is1] string capability is the initialization string. + Init1string + // The Init2string [init_2string, is2] string capability is the initialization string. + Init2string + // The Init3string [init_3string, is3] string capability is the initialization string. + Init3string + // The InitFile [init_file, if] string capability is the name of initialization file. + InitFile + // The InsertCharacter [insert_character, ich1] string capability is the insert character (P). + InsertCharacter + // The InsertLine [insert_line, il1] string capability is the insert line (P*). + InsertLine + // The InsertPadding [insert_padding, ip] string capability is the insert padding after inserted character. + InsertPadding + // The KeyBackspace [key_backspace, kbs] string capability is the backspace key. + KeyBackspace + // The KeyCatab [key_catab, ktbc] string capability is the clear-all-tabs key. + KeyCatab + // The KeyClear [key_clear, kclr] string capability is the clear-screen or erase key. + KeyClear + // The KeyCtab [key_ctab, kctab] string capability is the clear-tab key. + KeyCtab + // The KeyDc [key_dc, kdch1] string capability is the delete-character key. + KeyDc + // The KeyDl [key_dl, kdl1] string capability is the delete-line key. + KeyDl + // The KeyDown [key_down, kcud1] string capability is the down-arrow key. + KeyDown + // The KeyEic [key_eic, krmir] string capability is the sent by rmir or smir in insert mode. + KeyEic + // The KeyEol [key_eol, kel] string capability is the clear-to-end-of-line key. + KeyEol + // The KeyEos [key_eos, ked] string capability is the clear-to-end-of-screen key. + KeyEos + // The KeyF0 [key_f0, kf0] string capability is the F0 function key. + KeyF0 + // The KeyF1 [key_f1, kf1] string capability is the F1 function key. + KeyF1 + // The KeyF10 [key_f10, kf10] string capability is the F10 function key. + KeyF10 + // The KeyF2 [key_f2, kf2] string capability is the F2 function key. + KeyF2 + // The KeyF3 [key_f3, kf3] string capability is the F3 function key. + KeyF3 + // The KeyF4 [key_f4, kf4] string capability is the F4 function key. + KeyF4 + // The KeyF5 [key_f5, kf5] string capability is the F5 function key. + KeyF5 + // The KeyF6 [key_f6, kf6] string capability is the F6 function key. + KeyF6 + // The KeyF7 [key_f7, kf7] string capability is the F7 function key. + KeyF7 + // The KeyF8 [key_f8, kf8] string capability is the F8 function key. + KeyF8 + // The KeyF9 [key_f9, kf9] string capability is the F9 function key. + KeyF9 + // The KeyHome [key_home, khome] string capability is the home key. + KeyHome + // The KeyIc [key_ic, kich1] string capability is the insert-character key. + KeyIc + // The KeyIl [key_il, kil1] string capability is the insert-line key. + KeyIl + // The KeyLeft [key_left, kcub1] string capability is the left-arrow key. + KeyLeft + // The KeyLl [key_ll, kll] string capability is the lower-left key (home down). + KeyLl + // The KeyNpage [key_npage, knp] string capability is the next-page key. + KeyNpage + // The KeyPpage [key_ppage, kpp] string capability is the previous-page key. + KeyPpage + // The KeyRight [key_right, kcuf1] string capability is the right-arrow key. + KeyRight + // The KeySf [key_sf, kind] string capability is the scroll-forward key. + KeySf + // The KeySr [key_sr, kri] string capability is the scroll-backward key. + KeySr + // The KeyStab [key_stab, khts] string capability is the set-tab key. + KeyStab + // The KeyUp [key_up, kcuu1] string capability is the up-arrow key. + KeyUp + // The KeypadLocal [keypad_local, rmkx] string capability is the leave 'keyboard_transmit' mode. + KeypadLocal + // The KeypadXmit [keypad_xmit, smkx] string capability is the enter 'keyboard_transmit' mode. + KeypadXmit + // The LabF0 [lab_f0, lf0] string capability is the label on function key f0 if not f0. + LabF0 + // The LabF1 [lab_f1, lf1] string capability is the label on function key f1 if not f1. + LabF1 + // The LabF10 [lab_f10, lf10] string capability is the label on function key f10 if not f10. + LabF10 + // The LabF2 [lab_f2, lf2] string capability is the label on function key f2 if not f2. + LabF2 + // The LabF3 [lab_f3, lf3] string capability is the label on function key f3 if not f3. + LabF3 + // The LabF4 [lab_f4, lf4] string capability is the label on function key f4 if not f4. + LabF4 + // The LabF5 [lab_f5, lf5] string capability is the label on function key f5 if not f5. + LabF5 + // The LabF6 [lab_f6, lf6] string capability is the label on function key f6 if not f6. + LabF6 + // The LabF7 [lab_f7, lf7] string capability is the label on function key f7 if not f7. + LabF7 + // The LabF8 [lab_f8, lf8] string capability is the label on function key f8 if not f8. + LabF8 + // The LabF9 [lab_f9, lf9] string capability is the label on function key f9 if not f9. + LabF9 + // The MetaOff [meta_off, rmm] string capability is the turn off meta mode. + MetaOff + // The MetaOn [meta_on, smm] string capability is the turn on meta mode (8th-bit on). + MetaOn + // The Newline [newline, nel] string capability is the newline (behave like cr followed by lf). + Newline + // The PadChar [pad_char, pad] string capability is the padding char (instead of null). + PadChar + // The ParmDch [parm_dch, dch] string capability is the delete #1 characters (P*). + ParmDch + // The ParmDeleteLine [parm_delete_line, dl] string capability is the delete #1 lines (P*). + ParmDeleteLine + // The ParmDownCursor [parm_down_cursor, cud] string capability is the down #1 lines (P*). + ParmDownCursor + // The ParmIch [parm_ich, ich] string capability is the insert #1 characters (P*). + ParmIch + // The ParmIndex [parm_index, indn] string capability is the scroll forward #1 lines (P). + ParmIndex + // The ParmInsertLine [parm_insert_line, il] string capability is the insert #1 lines (P*). + ParmInsertLine + // The ParmLeftCursor [parm_left_cursor, cub] string capability is the move #1 characters to the left (P). + ParmLeftCursor + // The ParmRightCursor [parm_right_cursor, cuf] string capability is the move #1 characters to the right (P*). + ParmRightCursor + // The ParmRindex [parm_rindex, rin] string capability is the scroll back #1 lines (P). + ParmRindex + // The ParmUpCursor [parm_up_cursor, cuu] string capability is the up #1 lines (P*). + ParmUpCursor + // The PkeyKey [pkey_key, pfkey] string capability is the program function key #1 to type string #2. + PkeyKey + // The PkeyLocal [pkey_local, pfloc] string capability is the program function key #1 to execute string #2. + PkeyLocal + // The PkeyXmit [pkey_xmit, pfx] string capability is the program function key #1 to transmit string #2. + PkeyXmit + // The PrintScreen [print_screen, mc0] string capability is the print contents of screen. + PrintScreen + // The PrtrOff [prtr_off, mc4] string capability is the turn off printer. + PrtrOff + // The PrtrOn [prtr_on, mc5] string capability is the turn on printer. + PrtrOn + // The RepeatChar [repeat_char, rep] string capability is the repeat char #1 #2 times (P*). + RepeatChar + // The Reset1string [reset_1string, rs1] string capability is the reset string. + Reset1string + // The Reset2string [reset_2string, rs2] string capability is the reset string. + Reset2string + // The Reset3string [reset_3string, rs3] string capability is the reset string. + Reset3string + // The ResetFile [reset_file, rf] string capability is the name of reset file. + ResetFile + // The RestoreCursor [restore_cursor, rc] string capability is the restore cursor to position of last save_cursor. + RestoreCursor + // The RowAddress [row_address, vpa] string capability is the vertical position #1 absolute (P). + RowAddress + // The SaveCursor [save_cursor, sc] string capability is the save current cursor position (P). + SaveCursor + // The ScrollForward [scroll_forward, ind] string capability is the scroll text up (P). + ScrollForward + // The ScrollReverse [scroll_reverse, ri] string capability is the scroll text down (P). + ScrollReverse + // The SetAttributes [set_attributes, sgr] string capability is the define video attributes #1-#9 (PG9). + SetAttributes + // The SetTab [set_tab, hts] string capability is the set a tab in every row, current columns. + SetTab + // The SetWindow [set_window, wind] string capability is the current window is lines #1-#2 cols #3-#4. + SetWindow + // The Tab [tab, ht] string capability is the tab to next 8-space hardware tab stop. + Tab + // The ToStatusLine [to_status_line, tsl] string capability is the move to status line, column #1. + ToStatusLine + // The UnderlineChar [underline_char, uc] string capability is the underline char and move past it. + UnderlineChar + // The UpHalfLine [up_half_line, hu] string capability is the half a line up. + UpHalfLine + // The InitProg [init_prog, iprog] string capability is the path name of program for initialization. + InitProg + // The KeyA1 [key_a1, ka1] string capability is the upper left of keypad. + KeyA1 + // The KeyA3 [key_a3, ka3] string capability is the upper right of keypad. + KeyA3 + // The KeyB2 [key_b2, kb2] string capability is the center of keypad. + KeyB2 + // The KeyC1 [key_c1, kc1] string capability is the lower left of keypad. + KeyC1 + // The KeyC3 [key_c3, kc3] string capability is the lower right of keypad. + KeyC3 + // The PrtrNon [prtr_non, mc5p] string capability is the turn on printer for #1 bytes. + PrtrNon + // The CharPadding [char_padding, rmp] string capability is the like ip but when in insert mode. + CharPadding + // The AcsChars [acs_chars, acsc] string capability is the graphics charset pairs, based on vt100. + AcsChars + // The PlabNorm [plab_norm, pln] string capability is the program label #1 to show string #2. + PlabNorm + // The KeyBtab [key_btab, kcbt] string capability is the back-tab key. + KeyBtab + // The EnterXonMode [enter_xon_mode, smxon] string capability is the turn on xon/xoff handshaking. + EnterXonMode + // The ExitXonMode [exit_xon_mode, rmxon] string capability is the turn off xon/xoff handshaking. + ExitXonMode + // The EnterAmMode [enter_am_mode, smam] string capability is the turn on automatic margins. + EnterAmMode + // The ExitAmMode [exit_am_mode, rmam] string capability is the turn off automatic margins. + ExitAmMode + // The XonCharacter [xon_character, xonc] string capability is the XON character. + XonCharacter + // The XoffCharacter [xoff_character, xoffc] string capability is the XOFF character. + XoffCharacter + // The EnaAcs [ena_acs, enacs] string capability is the enable alternate char set. + EnaAcs + // The LabelOn [label_on, smln] string capability is the turn on soft labels. + LabelOn + // The LabelOff [label_off, rmln] string capability is the turn off soft labels. + LabelOff + // The KeyBeg [key_beg, kbeg] string capability is the begin key. + KeyBeg + // The KeyCancel [key_cancel, kcan] string capability is the cancel key. + KeyCancel + // The KeyClose [key_close, kclo] string capability is the close key. + KeyClose + // The KeyCommand [key_command, kcmd] string capability is the command key. + KeyCommand + // The KeyCopy [key_copy, kcpy] string capability is the copy key. + KeyCopy + // The KeyCreate [key_create, kcrt] string capability is the create key. + KeyCreate + // The KeyEnd [key_end, kend] string capability is the end key. + KeyEnd + // The KeyEnter [key_enter, kent] string capability is the enter/send key. + KeyEnter + // The KeyExit [key_exit, kext] string capability is the exit key. + KeyExit + // The KeyFind [key_find, kfnd] string capability is the find key. + KeyFind + // The KeyHelp [key_help, khlp] string capability is the help key. + KeyHelp + // The KeyMark [key_mark, kmrk] string capability is the mark key. + KeyMark + // The KeyMessage [key_message, kmsg] string capability is the message key. + KeyMessage + // The KeyMove [key_move, kmov] string capability is the move key. + KeyMove + // The KeyNext [key_next, knxt] string capability is the next key. + KeyNext + // The KeyOpen [key_open, kopn] string capability is the open key. + KeyOpen + // The KeyOptions [key_options, kopt] string capability is the options key. + KeyOptions + // The KeyPrevious [key_previous, kprv] string capability is the previous key. + KeyPrevious + // The KeyPrint [key_print, kprt] string capability is the print key. + KeyPrint + // The KeyRedo [key_redo, krdo] string capability is the redo key. + KeyRedo + // The KeyReference [key_reference, kref] string capability is the reference key. + KeyReference + // The KeyRefresh [key_refresh, krfr] string capability is the refresh key. + KeyRefresh + // The KeyReplace [key_replace, krpl] string capability is the replace key. + KeyReplace + // The KeyRestart [key_restart, krst] string capability is the restart key. + KeyRestart + // The KeyResume [key_resume, kres] string capability is the resume key. + KeyResume + // The KeySave [key_save, ksav] string capability is the save key. + KeySave + // The KeySuspend [key_suspend, kspd] string capability is the suspend key. + KeySuspend + // The KeyUndo [key_undo, kund] string capability is the undo key. + KeyUndo + // The KeySbeg [key_sbeg, kBEG] string capability is the shifted begin key. + KeySbeg + // The KeyScancel [key_scancel, kCAN] string capability is the shifted cancel key. + KeyScancel + // The KeyScommand [key_scommand, kCMD] string capability is the shifted command key. + KeyScommand + // The KeyScopy [key_scopy, kCPY] string capability is the shifted copy key. + KeyScopy + // The KeyScreate [key_screate, kCRT] string capability is the shifted create key. + KeyScreate + // The KeySdc [key_sdc, kDC] string capability is the shifted delete-character key. + KeySdc + // The KeySdl [key_sdl, kDL] string capability is the shifted delete-line key. + KeySdl + // The KeySelect [key_select, kslt] string capability is the select key. + KeySelect + // The KeySend [key_send, kEND] string capability is the shifted end key. + KeySend + // The KeySeol [key_seol, kEOL] string capability is the shifted clear-to-end-of-line key. + KeySeol + // The KeySexit [key_sexit, kEXT] string capability is the shifted exit key. + KeySexit + // The KeySfind [key_sfind, kFND] string capability is the shifted find key. + KeySfind + // The KeyShelp [key_shelp, kHLP] string capability is the shifted help key. + KeyShelp + // The KeyShome [key_shome, kHOM] string capability is the shifted home key. + KeyShome + // The KeySic [key_sic, kIC] string capability is the shifted insert-character key. + KeySic + // The KeySleft [key_sleft, kLFT] string capability is the shifted left-arrow key. + KeySleft + // The KeySmessage [key_smessage, kMSG] string capability is the shifted message key. + KeySmessage + // The KeySmove [key_smove, kMOV] string capability is the shifted move key. + KeySmove + // The KeySnext [key_snext, kNXT] string capability is the shifted next key. + KeySnext + // The KeySoptions [key_soptions, kOPT] string capability is the shifted options key. + KeySoptions + // The KeySprevious [key_sprevious, kPRV] string capability is the shifted previous key. + KeySprevious + // The KeySprint [key_sprint, kPRT] string capability is the shifted print key. + KeySprint + // The KeySredo [key_sredo, kRDO] string capability is the shifted redo key. + KeySredo + // The KeySreplace [key_sreplace, kRPL] string capability is the shifted replace key. + KeySreplace + // The KeySright [key_sright, kRIT] string capability is the shifted right-arrow key. + KeySright + // The KeySrsume [key_srsume, kRES] string capability is the shifted resume key. + KeySrsume + // The KeySsave [key_ssave, kSAV] string capability is the shifted save key. + KeySsave + // The KeySsuspend [key_ssuspend, kSPD] string capability is the shifted suspend key. + KeySsuspend + // The KeySundo [key_sundo, kUND] string capability is the shifted undo key. + KeySundo + // The ReqForInput [req_for_input, rfi] string capability is the send next input char (for ptys). + ReqForInput + // The KeyF11 [key_f11, kf11] string capability is the F11 function key. + KeyF11 + // The KeyF12 [key_f12, kf12] string capability is the F12 function key. + KeyF12 + // The KeyF13 [key_f13, kf13] string capability is the F13 function key. + KeyF13 + // The KeyF14 [key_f14, kf14] string capability is the F14 function key. + KeyF14 + // The KeyF15 [key_f15, kf15] string capability is the F15 function key. + KeyF15 + // The KeyF16 [key_f16, kf16] string capability is the F16 function key. + KeyF16 + // The KeyF17 [key_f17, kf17] string capability is the F17 function key. + KeyF17 + // The KeyF18 [key_f18, kf18] string capability is the F18 function key. + KeyF18 + // The KeyF19 [key_f19, kf19] string capability is the F19 function key. + KeyF19 + // The KeyF20 [key_f20, kf20] string capability is the F20 function key. + KeyF20 + // The KeyF21 [key_f21, kf21] string capability is the F21 function key. + KeyF21 + // The KeyF22 [key_f22, kf22] string capability is the F22 function key. + KeyF22 + // The KeyF23 [key_f23, kf23] string capability is the F23 function key. + KeyF23 + // The KeyF24 [key_f24, kf24] string capability is the F24 function key. + KeyF24 + // The KeyF25 [key_f25, kf25] string capability is the F25 function key. + KeyF25 + // The KeyF26 [key_f26, kf26] string capability is the F26 function key. + KeyF26 + // The KeyF27 [key_f27, kf27] string capability is the F27 function key. + KeyF27 + // The KeyF28 [key_f28, kf28] string capability is the F28 function key. + KeyF28 + // The KeyF29 [key_f29, kf29] string capability is the F29 function key. + KeyF29 + // The KeyF30 [key_f30, kf30] string capability is the F30 function key. + KeyF30 + // The KeyF31 [key_f31, kf31] string capability is the F31 function key. + KeyF31 + // The KeyF32 [key_f32, kf32] string capability is the F32 function key. + KeyF32 + // The KeyF33 [key_f33, kf33] string capability is the F33 function key. + KeyF33 + // The KeyF34 [key_f34, kf34] string capability is the F34 function key. + KeyF34 + // The KeyF35 [key_f35, kf35] string capability is the F35 function key. + KeyF35 + // The KeyF36 [key_f36, kf36] string capability is the F36 function key. + KeyF36 + // The KeyF37 [key_f37, kf37] string capability is the F37 function key. + KeyF37 + // The KeyF38 [key_f38, kf38] string capability is the F38 function key. + KeyF38 + // The KeyF39 [key_f39, kf39] string capability is the F39 function key. + KeyF39 + // The KeyF40 [key_f40, kf40] string capability is the F40 function key. + KeyF40 + // The KeyF41 [key_f41, kf41] string capability is the F41 function key. + KeyF41 + // The KeyF42 [key_f42, kf42] string capability is the F42 function key. + KeyF42 + // The KeyF43 [key_f43, kf43] string capability is the F43 function key. + KeyF43 + // The KeyF44 [key_f44, kf44] string capability is the F44 function key. + KeyF44 + // The KeyF45 [key_f45, kf45] string capability is the F45 function key. + KeyF45 + // The KeyF46 [key_f46, kf46] string capability is the F46 function key. + KeyF46 + // The KeyF47 [key_f47, kf47] string capability is the F47 function key. + KeyF47 + // The KeyF48 [key_f48, kf48] string capability is the F48 function key. + KeyF48 + // The KeyF49 [key_f49, kf49] string capability is the F49 function key. + KeyF49 + // The KeyF50 [key_f50, kf50] string capability is the F50 function key. + KeyF50 + // The KeyF51 [key_f51, kf51] string capability is the F51 function key. + KeyF51 + // The KeyF52 [key_f52, kf52] string capability is the F52 function key. + KeyF52 + // The KeyF53 [key_f53, kf53] string capability is the F53 function key. + KeyF53 + // The KeyF54 [key_f54, kf54] string capability is the F54 function key. + KeyF54 + // The KeyF55 [key_f55, kf55] string capability is the F55 function key. + KeyF55 + // The KeyF56 [key_f56, kf56] string capability is the F56 function key. + KeyF56 + // The KeyF57 [key_f57, kf57] string capability is the F57 function key. + KeyF57 + // The KeyF58 [key_f58, kf58] string capability is the F58 function key. + KeyF58 + // The KeyF59 [key_f59, kf59] string capability is the F59 function key. + KeyF59 + // The KeyF60 [key_f60, kf60] string capability is the F60 function key. + KeyF60 + // The KeyF61 [key_f61, kf61] string capability is the F61 function key. + KeyF61 + // The KeyF62 [key_f62, kf62] string capability is the F62 function key. + KeyF62 + // The KeyF63 [key_f63, kf63] string capability is the F63 function key. + KeyF63 + // The ClrBol [clr_bol, el1] string capability is the Clear to beginning of line. + ClrBol + // The ClearMargins [clear_margins, mgc] string capability is the clear right and left soft margins. + ClearMargins + // The SetLeftMargin [set_left_margin, smgl] string capability is the set left soft margin at current column. (ML is not in BSD termcap). + SetLeftMargin + // The SetRightMargin [set_right_margin, smgr] string capability is the set right soft margin at current column. + SetRightMargin + // The LabelFormat [label_format, fln] string capability is the label format. + LabelFormat + // The SetClock [set_clock, sclk] string capability is the set clock, #1 hrs #2 mins #3 secs. + SetClock + // The DisplayClock [display_clock, dclk] string capability is the display clock. + DisplayClock + // The RemoveClock [remove_clock, rmclk] string capability is the remove clock. + RemoveClock + // The CreateWindow [create_window, cwin] string capability is the define a window #1 from #2,#3 to #4,#5. + CreateWindow + // The GotoWindow [goto_window, wingo] string capability is the go to window #1. + GotoWindow + // The Hangup [hangup, hup] string capability is the hang-up phone. + Hangup + // The DialPhone [dial_phone, dial] string capability is the dial number #1. + DialPhone + // The QuickDial [quick_dial, qdial] string capability is the dial number #1 without checking. + QuickDial + // The Tone [tone, tone] string capability is the select touch tone dialing. + Tone + // The Pulse [pulse, pulse] string capability is the select pulse dialing. + Pulse + // The FlashHook [flash_hook, hook] string capability is the flash switch hook. + FlashHook + // The FixedPause [fixed_pause, pause] string capability is the pause for 2-3 seconds. + FixedPause + // The WaitTone [wait_tone, wait] string capability is the wait for dial-tone. + WaitTone + // The User0 [user0, u0] string capability is the User string #0. + User0 + // The User1 [user1, u1] string capability is the User string #1. + User1 + // The User2 [user2, u2] string capability is the User string #2. + User2 + // The User3 [user3, u3] string capability is the User string #3. + User3 + // The User4 [user4, u4] string capability is the User string #4. + User4 + // The User5 [user5, u5] string capability is the User string #5. + User5 + // The User6 [user6, u6] string capability is the User string #6. + User6 + // The User7 [user7, u7] string capability is the User string #7. + User7 + // The User8 [user8, u8] string capability is the User string #8. + User8 + // The User9 [user9, u9] string capability is the User string #9. + User9 + // The OrigPair [orig_pair, op] string capability is the Set default pair to its original value. + OrigPair + // The OrigColors [orig_colors, oc] string capability is the Set all color pairs to the original ones. + OrigColors + // The InitializeColor [initialize_color, initc] string capability is the initialize color #1 to (#2,#3,#4). + InitializeColor + // The InitializePair [initialize_pair, initp] string capability is the Initialize color pair #1 to fg=(#2,#3,#4), bg=(#5,#6,#7). + InitializePair + // The SetColorPair [set_color_pair, scp] string capability is the Set current color pair to #1. + SetColorPair + // The SetForeground [set_foreground, setf] string capability is the Set foreground color #1. + SetForeground + // The SetBackground [set_background, setb] string capability is the Set background color #1. + SetBackground + // The ChangeCharPitch [change_char_pitch, cpi] string capability is the Change number of characters per inch to #1. + ChangeCharPitch + // The ChangeLinePitch [change_line_pitch, lpi] string capability is the Change number of lines per inch to #1. + ChangeLinePitch + // The ChangeResHorz [change_res_horz, chr] string capability is the Change horizontal resolution to #1. + ChangeResHorz + // The ChangeResVert [change_res_vert, cvr] string capability is the Change vertical resolution to #1. + ChangeResVert + // The DefineChar [define_char, defc] string capability is the Define a character #1, #2 dots wide, descender #3. + DefineChar + // The EnterDoublewideMode [enter_doublewide_mode, swidm] string capability is the Enter double-wide mode. + EnterDoublewideMode + // The EnterDraftQuality [enter_draft_quality, sdrfq] string capability is the Enter draft-quality mode. + EnterDraftQuality + // The EnterItalicsMode [enter_italics_mode, sitm] string capability is the Enter italic mode. + EnterItalicsMode + // The EnterLeftwardMode [enter_leftward_mode, slm] string capability is the Start leftward carriage motion. + EnterLeftwardMode + // The EnterMicroMode [enter_micro_mode, smicm] string capability is the Start micro-motion mode. + EnterMicroMode + // The EnterNearLetterQuality [enter_near_letter_quality, snlq] string capability is the Enter NLQ mode. + EnterNearLetterQuality + // The EnterNormalQuality [enter_normal_quality, snrmq] string capability is the Enter normal-quality mode. + EnterNormalQuality + // The EnterShadowMode [enter_shadow_mode, sshm] string capability is the Enter shadow-print mode. + EnterShadowMode + // The EnterSubscriptMode [enter_subscript_mode, ssubm] string capability is the Enter subscript mode. + EnterSubscriptMode + // The EnterSuperscriptMode [enter_superscript_mode, ssupm] string capability is the Enter superscript mode. + EnterSuperscriptMode + // The EnterUpwardMode [enter_upward_mode, sum] string capability is the Start upward carriage motion. + EnterUpwardMode + // The ExitDoublewideMode [exit_doublewide_mode, rwidm] string capability is the End double-wide mode. + ExitDoublewideMode + // The ExitItalicsMode [exit_italics_mode, ritm] string capability is the End italic mode. + ExitItalicsMode + // The ExitLeftwardMode [exit_leftward_mode, rlm] string capability is the End left-motion mode. + ExitLeftwardMode + // The ExitMicroMode [exit_micro_mode, rmicm] string capability is the End micro-motion mode. + ExitMicroMode + // The ExitShadowMode [exit_shadow_mode, rshm] string capability is the End shadow-print mode. + ExitShadowMode + // The ExitSubscriptMode [exit_subscript_mode, rsubm] string capability is the End subscript mode. + ExitSubscriptMode + // The ExitSuperscriptMode [exit_superscript_mode, rsupm] string capability is the End superscript mode. + ExitSuperscriptMode + // The ExitUpwardMode [exit_upward_mode, rum] string capability is the End reverse character motion. + ExitUpwardMode + // The MicroColumnAddress [micro_column_address, mhpa] string capability is the Like column_address in micro mode. + MicroColumnAddress + // The MicroDown [micro_down, mcud1] string capability is the Like cursor_down in micro mode. + MicroDown + // The MicroLeft [micro_left, mcub1] string capability is the Like cursor_left in micro mode. + MicroLeft + // The MicroRight [micro_right, mcuf1] string capability is the Like cursor_right in micro mode. + MicroRight + // The MicroRowAddress [micro_row_address, mvpa] string capability is the Like row_address #1 in micro mode. + MicroRowAddress + // The MicroUp [micro_up, mcuu1] string capability is the Like cursor_up in micro mode. + MicroUp + // The OrderOfPins [order_of_pins, porder] string capability is the Match software bits to print-head pins. + OrderOfPins + // The ParmDownMicro [parm_down_micro, mcud] string capability is the Like parm_down_cursor in micro mode. + ParmDownMicro + // The ParmLeftMicro [parm_left_micro, mcub] string capability is the Like parm_left_cursor in micro mode. + ParmLeftMicro + // The ParmRightMicro [parm_right_micro, mcuf] string capability is the Like parm_right_cursor in micro mode. + ParmRightMicro + // The ParmUpMicro [parm_up_micro, mcuu] string capability is the Like parm_up_cursor in micro mode. + ParmUpMicro + // The SelectCharSet [select_char_set, scs] string capability is the Select character set, #1. + SelectCharSet + // The SetBottomMargin [set_bottom_margin, smgb] string capability is the Set bottom margin at current line. + SetBottomMargin + // The SetBottomMarginParm [set_bottom_margin_parm, smgbp] string capability is the Set bottom margin at line #1 or (if smgtp is not given) #2 lines from bottom. + SetBottomMarginParm + // The SetLeftMarginParm [set_left_margin_parm, smglp] string capability is the Set left (right) margin at column #1. + SetLeftMarginParm + // The SetRightMarginParm [set_right_margin_parm, smgrp] string capability is the Set right margin at column #1. + SetRightMarginParm + // The SetTopMargin [set_top_margin, smgt] string capability is the Set top margin at current line. + SetTopMargin + // The SetTopMarginParm [set_top_margin_parm, smgtp] string capability is the Set top (bottom) margin at row #1. + SetTopMarginParm + // The StartBitImage [start_bit_image, sbim] string capability is the Start printing bit image graphics. + StartBitImage + // The StartCharSetDef [start_char_set_def, scsd] string capability is the Start character set definition #1, with #2 characters in the set. + StartCharSetDef + // The StopBitImage [stop_bit_image, rbim] string capability is the Stop printing bit image graphics. + StopBitImage + // The StopCharSetDef [stop_char_set_def, rcsd] string capability is the End definition of character set #1. + StopCharSetDef + // The SubscriptCharacters [subscript_characters, subcs] string capability is the List of subscriptable characters. + SubscriptCharacters + // The SuperscriptCharacters [superscript_characters, supcs] string capability is the List of superscriptable characters. + SuperscriptCharacters + // The TheseCauseCr [these_cause_cr, docr] string capability is the Printing any of these characters causes CR. + TheseCauseCr + // The ZeroMotion [zero_motion, zerom] string capability is the No motion for subsequent character. + ZeroMotion + // The CharSetNames [char_set_names, csnm] string capability is the Produce #1'th item from list of character set names. + CharSetNames + // The KeyMouse [key_mouse, kmous] string capability is the Mouse event has occurred. + KeyMouse + // The MouseInfo [mouse_info, minfo] string capability is the Mouse status information. + MouseInfo + // The ReqMousePos [req_mouse_pos, reqmp] string capability is the Request mouse position. + ReqMousePos + // The GetMouse [get_mouse, getm] string capability is the Curses should get button events, parameter #1 not documented. + GetMouse + // The SetAForeground [set_a_foreground, setaf] string capability is the Set foreground color to #1, using ANSI escape. + SetAForeground + // The SetABackground [set_a_background, setab] string capability is the Set background color to #1, using ANSI escape. + SetABackground + // The PkeyPlab [pkey_plab, pfxl] string capability is the Program function key #1 to type string #2 and show string #3. + PkeyPlab + // The DeviceType [device_type, devt] string capability is the Indicate language/codeset support. + DeviceType + // The CodeSetInit [code_set_init, csin] string capability is the Init sequence for multiple codesets. + CodeSetInit + // The Set0DesSeq [set0_des_seq, s0ds] string capability is the Shift to codeset 0 (EUC set 0, ASCII). + Set0DesSeq + // The Set1DesSeq [set1_des_seq, s1ds] string capability is the Shift to codeset 1. + Set1DesSeq + // The Set2DesSeq [set2_des_seq, s2ds] string capability is the Shift to codeset 2. + Set2DesSeq + // The Set3DesSeq [set3_des_seq, s3ds] string capability is the Shift to codeset 3. + Set3DesSeq + // The SetLrMargin [set_lr_margin, smglr] string capability is the Set both left and right margins to #1, #2. (ML is not in BSD termcap). + SetLrMargin + // The SetTbMargin [set_tb_margin, smgtb] string capability is the Sets both top and bottom margins to #1, #2. + SetTbMargin + // The BitImageRepeat [bit_image_repeat, birep] string capability is the Repeat bit image cell #1 #2 times. + BitImageRepeat + // The BitImageNewline [bit_image_newline, binel] string capability is the Move to next row of the bit image. + BitImageNewline + // The BitImageCarriageReturn [bit_image_carriage_return, bicr] string capability is the Move to beginning of same row. + BitImageCarriageReturn + // The ColorNames [color_names, colornm] string capability is the Give name for color #1. + ColorNames + // The DefineBitImageRegion [define_bit_image_region, defbi] string capability is the Define rectangular bit image region. + DefineBitImageRegion + // The EndBitImageRegion [end_bit_image_region, endbi] string capability is the End a bit-image region. + EndBitImageRegion + // The SetColorBand [set_color_band, setcolor] string capability is the Change to ribbon color #1. + SetColorBand + // The SetPageLength [set_page_length, slines] string capability is the Set page length to #1 lines. + SetPageLength + // The DisplayPcChar [display_pc_char, dispc] string capability is the Display PC character #1. + DisplayPcChar + // The EnterPcCharsetMode [enter_pc_charset_mode, smpch] string capability is the Enter PC character display mode. + EnterPcCharsetMode + // The ExitPcCharsetMode [exit_pc_charset_mode, rmpch] string capability is the Exit PC character display mode. + ExitPcCharsetMode + // The EnterScancodeMode [enter_scancode_mode, smsc] string capability is the Enter PC scancode mode. + EnterScancodeMode + // The ExitScancodeMode [exit_scancode_mode, rmsc] string capability is the Exit PC scancode mode. + ExitScancodeMode + // The PcTermOptions [pc_term_options, pctrm] string capability is the PC terminal options. + PcTermOptions + // The ScancodeEscape [scancode_escape, scesc] string capability is the Escape for scancode emulation. + ScancodeEscape + // The AltScancodeEsc [alt_scancode_esc, scesa] string capability is the Alternate escape for scancode emulation. + AltScancodeEsc + // The EnterHorizontalHlMode [enter_horizontal_hl_mode, ehhlm] string capability is the Enter horizontal highlight mode. + EnterHorizontalHlMode + // The EnterLeftHlMode [enter_left_hl_mode, elhlm] string capability is the Enter left highlight mode. + EnterLeftHlMode + // The EnterLowHlMode [enter_low_hl_mode, elohlm] string capability is the Enter low highlight mode. + EnterLowHlMode + // The EnterRightHlMode [enter_right_hl_mode, erhlm] string capability is the Enter right highlight mode. + EnterRightHlMode + // The EnterTopHlMode [enter_top_hl_mode, ethlm] string capability is the Enter top highlight mode. + EnterTopHlMode + // The EnterVerticalHlMode [enter_vertical_hl_mode, evhlm] string capability is the Enter vertical highlight mode. + EnterVerticalHlMode + // The SetAAttributes [set_a_attributes, sgr1] string capability is the Define second set of video attributes #1-#6. + SetAAttributes + // The SetPglenInch [set_pglen_inch, slength] string capability is the Set page length to #1 hundredth of an inch (some implementations use sL for termcap). + SetPglenInch + // The TermcapInit2 [termcap_init2, OTi2] string capability is the secondary initialization string. + TermcapInit2 + // The TermcapReset [termcap_reset, OTrs] string capability is the terminal reset string. + TermcapReset + // The LinefeedIfNotLf [linefeed_if_not_lf, OTnl] string capability is the use to move down. + LinefeedIfNotLf + // The BackspaceIfNotBs [backspace_if_not_bs, OTbc] string capability is the move left, if not ^H. + BackspaceIfNotBs + // The OtherNonFunctionKeys [other_non_function_keys, OTko] string capability is the list of self-mapped keycaps. + OtherNonFunctionKeys + // The ArrowKeyMap [arrow_key_map, OTma] string capability is the map motion-keys for vi version 2. + ArrowKeyMap + // The AcsUlcorner [acs_ulcorner, OTG2] string capability is the single upper left. + AcsUlcorner + // The AcsLlcorner [acs_llcorner, OTG3] string capability is the single lower left. + AcsLlcorner + // The AcsUrcorner [acs_urcorner, OTG1] string capability is the single upper right. + AcsUrcorner + // The AcsLrcorner [acs_lrcorner, OTG4] string capability is the single lower right. + AcsLrcorner + // The AcsLtee [acs_ltee, OTGR] string capability is the tee pointing right. + AcsLtee + // The AcsRtee [acs_rtee, OTGL] string capability is the tee pointing left. + AcsRtee + // The AcsBtee [acs_btee, OTGU] string capability is the tee pointing up. + AcsBtee + // The AcsTtee [acs_ttee, OTGD] string capability is the tee pointing down. + AcsTtee + // The AcsHline [acs_hline, OTGH] string capability is the single horizontal line. + AcsHline + // The AcsVline [acs_vline, OTGV] string capability is the single vertical line. + AcsVline + // The AcsPlus [acs_plus, OTGC] string capability is the single intersection. + AcsPlus + // The MemoryLock [memory_lock, meml] string capability is the lock memory above cursor. + MemoryLock + // The MemoryUnlock [memory_unlock, memu] string capability is the unlock memory. + MemoryUnlock + // The BoxChars1 [box_chars_1, box1] string capability is the box characters primary set. + BoxChars1 +) +const ( + // CapCountBool is the count of bool capabilities. + CapCountBool = ReturnDoesClrEol + 1 + // CapCountNum is the count of num capabilities. + CapCountNum = NumberOfFunctionKeys + 1 + // CapCountString is the count of string capabilities. + CapCountString = BoxChars1 + 1 +) + +// boolCapNames are the bool term cap names. +var boolCapNames = [...]string{ + "auto_left_margin", "bw", + "auto_right_margin", "am", + "no_esc_ctlc", "xsb", + "ceol_standout_glitch", "xhp", + "eat_newline_glitch", "xenl", + "erase_overstrike", "eo", + "generic_type", "gn", + "hard_copy", "hc", + "has_meta_key", "km", + "has_status_line", "hs", + "insert_null_glitch", "in", + "memory_above", "da", + "memory_below", "db", + "move_insert_mode", "mir", + "move_standout_mode", "msgr", + "over_strike", "os", + "status_line_esc_ok", "eslok", + "dest_tabs_magic_smso", "xt", + "tilde_glitch", "hz", + "transparent_underline", "ul", + "xon_xoff", "xon", + "needs_xon_xoff", "nxon", + "prtr_silent", "mc5i", + "hard_cursor", "chts", + "non_rev_rmcup", "nrrmc", + "no_pad_char", "npc", + "non_dest_scroll_region", "ndscr", + "can_change", "ccc", + "back_color_erase", "bce", + "hue_lightness_saturation", "hls", + "col_addr_glitch", "xhpa", + "cr_cancels_micro_mode", "crxm", + "has_print_wheel", "daisy", + "row_addr_glitch", "xvpa", + "semi_auto_right_margin", "sam", + "cpi_changes_res", "cpix", + "lpi_changes_res", "lpix", + "backspaces_with_bs", "OTbs", + "crt_no_scrolling", "OTns", + "no_correctly_working_cr", "OTnc", + "gnu_has_meta_key", "OTMT", + "linefeed_is_newline", "OTNL", + "has_hardware_tabs", "OTpt", + "return_does_clr_eol", "OTxr", +} + +// numCapNames are the num term cap names. +var numCapNames = [...]string{ + "columns", "cols", + "init_tabs", "it", + "lines", "lines", + "lines_of_memory", "lm", + "magic_cookie_glitch", "xmc", + "padding_baud_rate", "pb", + "virtual_terminal", "vt", + "width_status_line", "wsl", + "num_labels", "nlab", + "label_height", "lh", + "label_width", "lw", + "max_attributes", "ma", + "maximum_windows", "wnum", + "max_colors", "colors", + "max_pairs", "pairs", + "no_color_video", "ncv", + "buffer_capacity", "bufsz", + "dot_vert_spacing", "spinv", + "dot_horz_spacing", "spinh", + "max_micro_address", "maddr", + "max_micro_jump", "mjump", + "micro_col_size", "mcs", + "micro_line_size", "mls", + "number_of_pins", "npins", + "output_res_char", "orc", + "output_res_line", "orl", + "output_res_horz_inch", "orhi", + "output_res_vert_inch", "orvi", + "print_rate", "cps", + "wide_char_size", "widcs", + "buttons", "btns", + "bit_image_entwining", "bitwin", + "bit_image_type", "bitype", + "magic_cookie_glitch_ul", "OTug", + "carriage_return_delay", "OTdC", + "new_line_delay", "OTdN", + "backspace_delay", "OTdB", + "horizontal_tab_delay", "OTdT", + "number_of_function_keys", "OTkn", +} + +// stringCapNames are the string term cap names. +var stringCapNames = [...]string{ + "back_tab", "cbt", + "bell", "bel", + "carriage_return", "cr", + "change_scroll_region", "csr", + "clear_all_tabs", "tbc", + "clear_screen", "clear", + "clr_eol", "el", + "clr_eos", "ed", + "column_address", "hpa", + "command_character", "cmdch", + "cursor_address", "cup", + "cursor_down", "cud1", + "cursor_home", "home", + "cursor_invisible", "civis", + "cursor_left", "cub1", + "cursor_mem_address", "mrcup", + "cursor_normal", "cnorm", + "cursor_right", "cuf1", + "cursor_to_ll", "ll", + "cursor_up", "cuu1", + "cursor_visible", "cvvis", + "delete_character", "dch1", + "delete_line", "dl1", + "dis_status_line", "dsl", + "down_half_line", "hd", + "enter_alt_charset_mode", "smacs", + "enter_blink_mode", "blink", + "enter_bold_mode", "bold", + "enter_ca_mode", "smcup", + "enter_delete_mode", "smdc", + "enter_dim_mode", "dim", + "enter_insert_mode", "smir", + "enter_secure_mode", "invis", + "enter_protected_mode", "prot", + "enter_reverse_mode", "rev", + "enter_standout_mode", "smso", + "enter_underline_mode", "smul", + "erase_chars", "ech", + "exit_alt_charset_mode", "rmacs", + "exit_attribute_mode", "sgr0", + "exit_ca_mode", "rmcup", + "exit_delete_mode", "rmdc", + "exit_insert_mode", "rmir", + "exit_standout_mode", "rmso", + "exit_underline_mode", "rmul", + "flash_screen", "flash", + "form_feed", "ff", + "from_status_line", "fsl", + "init_1string", "is1", + "init_2string", "is2", + "init_3string", "is3", + "init_file", "if", + "insert_character", "ich1", + "insert_line", "il1", + "insert_padding", "ip", + "key_backspace", "kbs", + "key_catab", "ktbc", + "key_clear", "kclr", + "key_ctab", "kctab", + "key_dc", "kdch1", + "key_dl", "kdl1", + "key_down", "kcud1", + "key_eic", "krmir", + "key_eol", "kel", + "key_eos", "ked", + "key_f0", "kf0", + "key_f1", "kf1", + "key_f10", "kf10", + "key_f2", "kf2", + "key_f3", "kf3", + "key_f4", "kf4", + "key_f5", "kf5", + "key_f6", "kf6", + "key_f7", "kf7", + "key_f8", "kf8", + "key_f9", "kf9", + "key_home", "khome", + "key_ic", "kich1", + "key_il", "kil1", + "key_left", "kcub1", + "key_ll", "kll", + "key_npage", "knp", + "key_ppage", "kpp", + "key_right", "kcuf1", + "key_sf", "kind", + "key_sr", "kri", + "key_stab", "khts", + "key_up", "kcuu1", + "keypad_local", "rmkx", + "keypad_xmit", "smkx", + "lab_f0", "lf0", + "lab_f1", "lf1", + "lab_f10", "lf10", + "lab_f2", "lf2", + "lab_f3", "lf3", + "lab_f4", "lf4", + "lab_f5", "lf5", + "lab_f6", "lf6", + "lab_f7", "lf7", + "lab_f8", "lf8", + "lab_f9", "lf9", + "meta_off", "rmm", + "meta_on", "smm", + "newline", "nel", + "pad_char", "pad", + "parm_dch", "dch", + "parm_delete_line", "dl", + "parm_down_cursor", "cud", + "parm_ich", "ich", + "parm_index", "indn", + "parm_insert_line", "il", + "parm_left_cursor", "cub", + "parm_right_cursor", "cuf", + "parm_rindex", "rin", + "parm_up_cursor", "cuu", + "pkey_key", "pfkey", + "pkey_local", "pfloc", + "pkey_xmit", "pfx", + "print_screen", "mc0", + "prtr_off", "mc4", + "prtr_on", "mc5", + "repeat_char", "rep", + "reset_1string", "rs1", + "reset_2string", "rs2", + "reset_3string", "rs3", + "reset_file", "rf", + "restore_cursor", "rc", + "row_address", "vpa", + "save_cursor", "sc", + "scroll_forward", "ind", + "scroll_reverse", "ri", + "set_attributes", "sgr", + "set_tab", "hts", + "set_window", "wind", + "tab", "ht", + "to_status_line", "tsl", + "underline_char", "uc", + "up_half_line", "hu", + "init_prog", "iprog", + "key_a1", "ka1", + "key_a3", "ka3", + "key_b2", "kb2", + "key_c1", "kc1", + "key_c3", "kc3", + "prtr_non", "mc5p", + "char_padding", "rmp", + "acs_chars", "acsc", + "plab_norm", "pln", + "key_btab", "kcbt", + "enter_xon_mode", "smxon", + "exit_xon_mode", "rmxon", + "enter_am_mode", "smam", + "exit_am_mode", "rmam", + "xon_character", "xonc", + "xoff_character", "xoffc", + "ena_acs", "enacs", + "label_on", "smln", + "label_off", "rmln", + "key_beg", "kbeg", + "key_cancel", "kcan", + "key_close", "kclo", + "key_command", "kcmd", + "key_copy", "kcpy", + "key_create", "kcrt", + "key_end", "kend", + "key_enter", "kent", + "key_exit", "kext", + "key_find", "kfnd", + "key_help", "khlp", + "key_mark", "kmrk", + "key_message", "kmsg", + "key_move", "kmov", + "key_next", "knxt", + "key_open", "kopn", + "key_options", "kopt", + "key_previous", "kprv", + "key_print", "kprt", + "key_redo", "krdo", + "key_reference", "kref", + "key_refresh", "krfr", + "key_replace", "krpl", + "key_restart", "krst", + "key_resume", "kres", + "key_save", "ksav", + "key_suspend", "kspd", + "key_undo", "kund", + "key_sbeg", "kBEG", + "key_scancel", "kCAN", + "key_scommand", "kCMD", + "key_scopy", "kCPY", + "key_screate", "kCRT", + "key_sdc", "kDC", + "key_sdl", "kDL", + "key_select", "kslt", + "key_send", "kEND", + "key_seol", "kEOL", + "key_sexit", "kEXT", + "key_sfind", "kFND", + "key_shelp", "kHLP", + "key_shome", "kHOM", + "key_sic", "kIC", + "key_sleft", "kLFT", + "key_smessage", "kMSG", + "key_smove", "kMOV", + "key_snext", "kNXT", + "key_soptions", "kOPT", + "key_sprevious", "kPRV", + "key_sprint", "kPRT", + "key_sredo", "kRDO", + "key_sreplace", "kRPL", + "key_sright", "kRIT", + "key_srsume", "kRES", + "key_ssave", "kSAV", + "key_ssuspend", "kSPD", + "key_sundo", "kUND", + "req_for_input", "rfi", + "key_f11", "kf11", + "key_f12", "kf12", + "key_f13", "kf13", + "key_f14", "kf14", + "key_f15", "kf15", + "key_f16", "kf16", + "key_f17", "kf17", + "key_f18", "kf18", + "key_f19", "kf19", + "key_f20", "kf20", + "key_f21", "kf21", + "key_f22", "kf22", + "key_f23", "kf23", + "key_f24", "kf24", + "key_f25", "kf25", + "key_f26", "kf26", + "key_f27", "kf27", + "key_f28", "kf28", + "key_f29", "kf29", + "key_f30", "kf30", + "key_f31", "kf31", + "key_f32", "kf32", + "key_f33", "kf33", + "key_f34", "kf34", + "key_f35", "kf35", + "key_f36", "kf36", + "key_f37", "kf37", + "key_f38", "kf38", + "key_f39", "kf39", + "key_f40", "kf40", + "key_f41", "kf41", + "key_f42", "kf42", + "key_f43", "kf43", + "key_f44", "kf44", + "key_f45", "kf45", + "key_f46", "kf46", + "key_f47", "kf47", + "key_f48", "kf48", + "key_f49", "kf49", + "key_f50", "kf50", + "key_f51", "kf51", + "key_f52", "kf52", + "key_f53", "kf53", + "key_f54", "kf54", + "key_f55", "kf55", + "key_f56", "kf56", + "key_f57", "kf57", + "key_f58", "kf58", + "key_f59", "kf59", + "key_f60", "kf60", + "key_f61", "kf61", + "key_f62", "kf62", + "key_f63", "kf63", + "clr_bol", "el1", + "clear_margins", "mgc", + "set_left_margin", "smgl", + "set_right_margin", "smgr", + "label_format", "fln", + "set_clock", "sclk", + "display_clock", "dclk", + "remove_clock", "rmclk", + "create_window", "cwin", + "goto_window", "wingo", + "hangup", "hup", + "dial_phone", "dial", + "quick_dial", "qdial", + "tone", "tone", + "pulse", "pulse", + "flash_hook", "hook", + "fixed_pause", "pause", + "wait_tone", "wait", + "user0", "u0", + "user1", "u1", + "user2", "u2", + "user3", "u3", + "user4", "u4", + "user5", "u5", + "user6", "u6", + "user7", "u7", + "user8", "u8", + "user9", "u9", + "orig_pair", "op", + "orig_colors", "oc", + "initialize_color", "initc", + "initialize_pair", "initp", + "set_color_pair", "scp", + "set_foreground", "setf", + "set_background", "setb", + "change_char_pitch", "cpi", + "change_line_pitch", "lpi", + "change_res_horz", "chr", + "change_res_vert", "cvr", + "define_char", "defc", + "enter_doublewide_mode", "swidm", + "enter_draft_quality", "sdrfq", + "enter_italics_mode", "sitm", + "enter_leftward_mode", "slm", + "enter_micro_mode", "smicm", + "enter_near_letter_quality", "snlq", + "enter_normal_quality", "snrmq", + "enter_shadow_mode", "sshm", + "enter_subscript_mode", "ssubm", + "enter_superscript_mode", "ssupm", + "enter_upward_mode", "sum", + "exit_doublewide_mode", "rwidm", + "exit_italics_mode", "ritm", + "exit_leftward_mode", "rlm", + "exit_micro_mode", "rmicm", + "exit_shadow_mode", "rshm", + "exit_subscript_mode", "rsubm", + "exit_superscript_mode", "rsupm", + "exit_upward_mode", "rum", + "micro_column_address", "mhpa", + "micro_down", "mcud1", + "micro_left", "mcub1", + "micro_right", "mcuf1", + "micro_row_address", "mvpa", + "micro_up", "mcuu1", + "order_of_pins", "porder", + "parm_down_micro", "mcud", + "parm_left_micro", "mcub", + "parm_right_micro", "mcuf", + "parm_up_micro", "mcuu", + "select_char_set", "scs", + "set_bottom_margin", "smgb", + "set_bottom_margin_parm", "smgbp", + "set_left_margin_parm", "smglp", + "set_right_margin_parm", "smgrp", + "set_top_margin", "smgt", + "set_top_margin_parm", "smgtp", + "start_bit_image", "sbim", + "start_char_set_def", "scsd", + "stop_bit_image", "rbim", + "stop_char_set_def", "rcsd", + "subscript_characters", "subcs", + "superscript_characters", "supcs", + "these_cause_cr", "docr", + "zero_motion", "zerom", + "char_set_names", "csnm", + "key_mouse", "kmous", + "mouse_info", "minfo", + "req_mouse_pos", "reqmp", + "get_mouse", "getm", + "set_a_foreground", "setaf", + "set_a_background", "setab", + "pkey_plab", "pfxl", + "device_type", "devt", + "code_set_init", "csin", + "set0_des_seq", "s0ds", + "set1_des_seq", "s1ds", + "set2_des_seq", "s2ds", + "set3_des_seq", "s3ds", + "set_lr_margin", "smglr", + "set_tb_margin", "smgtb", + "bit_image_repeat", "birep", + "bit_image_newline", "binel", + "bit_image_carriage_return", "bicr", + "color_names", "colornm", + "define_bit_image_region", "defbi", + "end_bit_image_region", "endbi", + "set_color_band", "setcolor", + "set_page_length", "slines", + "display_pc_char", "dispc", + "enter_pc_charset_mode", "smpch", + "exit_pc_charset_mode", "rmpch", + "enter_scancode_mode", "smsc", + "exit_scancode_mode", "rmsc", + "pc_term_options", "pctrm", + "scancode_escape", "scesc", + "alt_scancode_esc", "scesa", + "enter_horizontal_hl_mode", "ehhlm", + "enter_left_hl_mode", "elhlm", + "enter_low_hl_mode", "elohlm", + "enter_right_hl_mode", "erhlm", + "enter_top_hl_mode", "ethlm", + "enter_vertical_hl_mode", "evhlm", + "set_a_attributes", "sgr1", + "set_pglen_inch", "slength", + "termcap_init2", "OTi2", + "termcap_reset", "OTrs", + "linefeed_if_not_lf", "OTnl", + "backspace_if_not_bs", "OTbc", + "other_non_function_keys", "OTko", + "arrow_key_map", "OTma", + "acs_ulcorner", "OTG2", + "acs_llcorner", "OTG3", + "acs_urcorner", "OTG1", + "acs_lrcorner", "OTG4", + "acs_ltee", "OTGR", + "acs_rtee", "OTGL", + "acs_btee", "OTGU", + "acs_ttee", "OTGD", + "acs_hline", "OTGH", + "acs_vline", "OTGV", + "acs_plus", "OTGC", + "memory_lock", "meml", + "memory_unlock", "memu", + "box_chars_1", "box1", +} diff --git a/vendor/github.com/xo/terminfo/color.go b/vendor/github.com/xo/terminfo/color.go new file mode 100644 index 00000000..76c439fc --- /dev/null +++ b/vendor/github.com/xo/terminfo/color.go @@ -0,0 +1,88 @@ +package terminfo + +import ( + "os" + "strconv" + "strings" +) + +// ColorLevel is the color level supported by a terminal. +type ColorLevel uint + +// ColorLevel values. +const ( + ColorLevelNone ColorLevel = iota + ColorLevelBasic + ColorLevelHundreds + ColorLevelMillions +) + +// String satisfies the Stringer interface. +func (c ColorLevel) String() string { + switch c { + case ColorLevelBasic: + return "basic" + case ColorLevelHundreds: + return "hundreds" + case ColorLevelMillions: + return "millions" + } + return "none" +} + +// ChromaFormatterName returns the github.com/alecthomas/chroma compatible +// formatter name for the color level. +func (c ColorLevel) ChromaFormatterName() string { + switch c { + case ColorLevelBasic: + return "terminal" + case ColorLevelHundreds: + return "terminal256" + case ColorLevelMillions: + return "terminal16m" + } + return "noop" +} + +// ColorLevelFromEnv returns the color level COLORTERM, FORCE_COLOR, +// TERM_PROGRAM, or determined from the TERM environment variable. +func ColorLevelFromEnv() (ColorLevel, error) { + // check for overriding environment variables + colorTerm, termProg, forceColor := os.Getenv("COLORTERM"), os.Getenv("TERM_PROGRAM"), os.Getenv("FORCE_COLOR") + switch { + case strings.Contains(colorTerm, "truecolor") || strings.Contains(colorTerm, "24bit") || termProg == "Hyper": + return ColorLevelMillions, nil + case colorTerm != "" || forceColor != "": + return ColorLevelBasic, nil + case termProg == "Apple_Terminal": + return ColorLevelHundreds, nil + case termProg == "iTerm.app": + ver := os.Getenv("TERM_PROGRAM_VERSION") + if ver == "" { + return ColorLevelHundreds, nil + } + i, err := strconv.Atoi(strings.Split(ver, ".")[0]) + if err != nil { + return ColorLevelNone, ErrInvalidTermProgramVersion + } + if i == 3 { + return ColorLevelMillions, nil + } + return ColorLevelHundreds, nil + } + // otherwise determine from TERM's max_colors capability + if term := os.Getenv("TERM"); term != "" { + ti, err := Load(term) + if err != nil { + return ColorLevelNone, err + } + v, ok := ti.Nums[MaxColors] + switch { + case !ok || v <= 16: + return ColorLevelNone, nil + case ok && v >= 256: + return ColorLevelHundreds, nil + } + } + return ColorLevelBasic, nil +} diff --git a/vendor/github.com/xo/terminfo/dec.go b/vendor/github.com/xo/terminfo/dec.go new file mode 100644 index 00000000..dacc88e3 --- /dev/null +++ b/vendor/github.com/xo/terminfo/dec.go @@ -0,0 +1,245 @@ +package terminfo + +import ( + "sort" +) + +const ( + // maxFileLength is the max file length. + maxFileLength = 4096 + // magic is the file magic for terminfo files. + magic = 0o432 + // magicExtended is the file magic for terminfo files with the extended + // number format. + magicExtended = 0o1036 +) + +// header fields. +const ( + fieldMagic = iota + fieldNameSize + fieldBoolCount + fieldNumCount + fieldStringCount + fieldTableSize +) + +// header extended fields. +const ( + fieldExtBoolCount = iota + fieldExtNumCount + fieldExtStringCount + fieldExtOffsetCount + fieldExtTableSize +) + +// hasInvalidCaps determines if the capabilities in h are invalid. +func hasInvalidCaps(h []int) bool { + return h[fieldBoolCount] > CapCountBool || + h[fieldNumCount] > CapCountNum || + h[fieldStringCount] > CapCountString +} + +// capLength returns the total length of the capabilities in bytes. +func capLength(h []int) int { + return h[fieldNameSize] + + h[fieldBoolCount] + + (h[fieldNameSize]+h[fieldBoolCount])%2 + // account for word align + h[fieldNumCount]*2 + + h[fieldStringCount]*2 + + h[fieldTableSize] +} + +// hasInvalidExtOffset determines if the extended offset field is valid. +func hasInvalidExtOffset(h []int) bool { + return h[fieldExtBoolCount]+ + h[fieldExtNumCount]+ + h[fieldExtStringCount]*2 != h[fieldExtOffsetCount] +} + +// extCapLength returns the total length of extended capabilities in bytes. +func extCapLength(h []int, numWidth int) int { + return h[fieldExtBoolCount] + + h[fieldExtBoolCount]%2 + // account for word align + h[fieldExtNumCount]*(numWidth/8) + + h[fieldExtOffsetCount]*2 + + h[fieldExtTableSize] +} + +// findNull finds the position of null in buf. +func findNull(buf []byte, i int) int { + for ; i < len(buf); i++ { + if buf[i] == 0 { + return i + } + } + return -1 +} + +// readStrings decodes n strings from string data table buf using the indexes in idx. +func readStrings(idx []int, buf []byte, n int) (map[int][]byte, int, error) { + var last int + m := make(map[int][]byte) + for i := 0; i < n; i++ { + start := idx[i] + if start < 0 { + continue + } + if end := findNull(buf, start); end != -1 { + m[i], last = buf[start:end], end+1 + } else { + return nil, 0, ErrInvalidStringTable + } + } + return m, last, nil +} + +// decoder holds state info while decoding a terminfo file. +type decoder struct { + buf []byte + pos int + n int +} + +// readBytes reads the next n bytes of buf, incrementing pos by n. +func (d *decoder) readBytes(n int) ([]byte, error) { + if d.n < d.pos+n { + return nil, ErrUnexpectedFileEnd + } + n, d.pos = d.pos, d.pos+n + return d.buf[n:d.pos], nil +} + +// readInts reads n number of ints with width w. +func (d *decoder) readInts(n, w int) ([]int, error) { + w /= 8 + l := n * w + buf, err := d.readBytes(l) + if err != nil { + return nil, err + } + // align + d.pos += d.pos % 2 + z := make([]int, n) + for i, j := 0, 0; i < l; i, j = i+w, j+1 { + switch w { + case 1: + z[i] = int(buf[i]) + case 2: + z[j] = int(int16(buf[i+1])<<8 | int16(buf[i])) + case 4: + z[j] = int(buf[i+3])<<24 | int(buf[i+2])<<16 | int(buf[i+1])<<8 | int(buf[i]) + } + } + return z, nil +} + +// readBools reads the next n bools. +func (d *decoder) readBools(n int) (map[int]bool, map[int]bool, error) { + buf, err := d.readInts(n, 8) + if err != nil { + return nil, nil, err + } + // process + bools, boolsM := make(map[int]bool), make(map[int]bool) + for i, b := range buf { + bools[i] = b == 1 + if int8(b) == -2 { + boolsM[i] = true + } + } + return bools, boolsM, nil +} + +// readNums reads the next n nums. +func (d *decoder) readNums(n, w int) (map[int]int, map[int]bool, error) { + buf, err := d.readInts(n, w) + if err != nil { + return nil, nil, err + } + // process + nums, numsM := make(map[int]int), make(map[int]bool) + for i := 0; i < n; i++ { + nums[i] = buf[i] + if buf[i] == -2 { + numsM[i] = true + } + } + return nums, numsM, nil +} + +// readStringTable reads the string data for n strings and the accompanying data +// table of length sz. +func (d *decoder) readStringTable(n, sz int) ([][]byte, []int, error) { + buf, err := d.readInts(n, 16) + if err != nil { + return nil, nil, err + } + // read string data table + data, err := d.readBytes(sz) + if err != nil { + return nil, nil, err + } + // align + d.pos += d.pos % 2 + // process + s := make([][]byte, n) + var m []int + for i := 0; i < n; i++ { + start := buf[i] + if start == -2 { + m = append(m, i) + } else if start >= 0 { + if end := findNull(data, start); end != -1 { + s[i] = data[start:end] + } else { + return nil, nil, ErrInvalidStringTable + } + } + } + return s, m, nil +} + +// readStrings reads the next n strings and processes the string data table of +// length sz. +func (d *decoder) readStrings(n, sz int) (map[int][]byte, map[int]bool, error) { + s, m, err := d.readStringTable(n, sz) + if err != nil { + return nil, nil, err + } + strs := make(map[int][]byte) + for k, v := range s { + if k == AcsChars { + v = canonicalizeAscChars(v) + } + strs[k] = v + } + strsM := make(map[int]bool, len(m)) + for _, k := range m { + strsM[k] = true + } + return strs, strsM, nil +} + +// canonicalizeAscChars reorders chars to be unique, in order. +// +// see repair_ascc in ncurses-6.3/progs/dump_entry.c +func canonicalizeAscChars(z []byte) []byte { + var c []byte + enc := make(map[byte]byte, len(z)/2) + for i := 0; i < len(z); i += 2 { + if _, ok := enc[z[i]]; !ok { + a, b := z[i], z[i+1] + // log.Printf(">>> a: %d %c, b: %d %c", a, a, b, b) + c, enc[a] = append(c, b), b + } + } + sort.Slice(c, func(i, j int) bool { + return c[i] < c[j] + }) + r := make([]byte, 2*len(c)) + for i := 0; i < len(c); i++ { + r[i*2], r[i*2+1] = c[i], enc[c[i]] + } + return r +} diff --git a/vendor/github.com/xo/terminfo/load.go b/vendor/github.com/xo/terminfo/load.go new file mode 100644 index 00000000..d7cd266c --- /dev/null +++ b/vendor/github.com/xo/terminfo/load.go @@ -0,0 +1,64 @@ +package terminfo + +import ( + "os" + "os/user" + "path" + "strings" + "sync" +) + +// termCache is the terminfo cache. +var termCache = struct { + db map[string]*Terminfo + sync.RWMutex +}{ + db: make(map[string]*Terminfo), +} + +// Load follows the behavior described in terminfo(5) to find correct the +// terminfo file using the name, reads the file and then returns a Terminfo +// struct that describes the file. +func Load(name string) (*Terminfo, error) { + if name == "" { + return nil, ErrEmptyTermName + } + termCache.RLock() + ti, ok := termCache.db[name] + termCache.RUnlock() + if ok { + return ti, nil + } + var checkDirs []string + // check $TERMINFO + if dir := os.Getenv("TERMINFO"); dir != "" { + checkDirs = append(checkDirs, dir) + } + // check $HOME/.terminfo + u, err := user.Current() + if err != nil { + return nil, err + } + checkDirs = append(checkDirs, path.Join(u.HomeDir, ".terminfo")) + // check $TERMINFO_DIRS + if dirs := os.Getenv("TERMINFO_DIRS"); dirs != "" { + checkDirs = append(checkDirs, strings.Split(dirs, ":")...) + } + // check fallback directories + checkDirs = append(checkDirs, "/etc/terminfo", "/lib/terminfo", "/usr/share/terminfo") + for _, dir := range checkDirs { + ti, err = Open(dir, name) + if err != nil && err != ErrFileNotFound && !os.IsNotExist(err) { + return nil, err + } else if err == nil { + return ti, nil + } + } + return nil, ErrDatabaseDirectoryNotFound +} + +// LoadFromEnv loads the terminal info based on the name contained in +// environment variable TERM. +func LoadFromEnv() (*Terminfo, error) { + return Load(os.Getenv("TERM")) +} diff --git a/vendor/github.com/xo/terminfo/param.go b/vendor/github.com/xo/terminfo/param.go new file mode 100644 index 00000000..ed4cb86b --- /dev/null +++ b/vendor/github.com/xo/terminfo/param.go @@ -0,0 +1,405 @@ +package terminfo + +import ( + "bytes" + "fmt" + "io" + "strconv" + "strings" + "sync" +) + +// parametizer represents the a scan state for a parameterized string. +type parametizer struct { + // z is the string to parameterize + z []byte + // pos is the current position in s. + pos int + // nest is the current nest level. + nest int + // s is the variable stack. + s stack + // skipElse keeps the state of skipping else. + skipElse bool + // buf is the result buffer. + buf *bytes.Buffer + // params are the parameters to interpolate. + params [9]interface{} + // vars are dynamic variables. + vars [26]interface{} +} + +// staticVars are the static, global variables. +var staticVars = struct { + vars [26]interface{} + sync.Mutex +}{} + +var parametizerPool = sync.Pool{ + New: func() interface{} { + p := new(parametizer) + p.buf = bytes.NewBuffer(make([]byte, 0, 45)) + return p + }, +} + +// newParametizer returns a new initialized parametizer from the pool. +func newParametizer(z []byte) *parametizer { + p := parametizerPool.Get().(*parametizer) + p.z = z + return p +} + +// reset resets the parametizer. +func (p *parametizer) reset() { + p.pos, p.nest = 0, 0 + p.s.reset() + p.buf.Reset() + p.params, p.vars = [9]interface{}{}, [26]interface{}{} + parametizerPool.Put(p) +} + +// stateFn represents the state of the scanner as a function that returns the +// next state. +type stateFn func() stateFn + +// exec executes the parameterizer, interpolating the supplied parameters. +func (p *parametizer) exec() string { + for state := p.scanTextFn; state != nil; { + state = state() + } + return p.buf.String() +} + +// peek returns the next byte. +func (p *parametizer) peek() (byte, error) { + if p.pos >= len(p.z) { + return 0, io.EOF + } + return p.z[p.pos], nil +} + +// writeFrom writes the characters from ppos to pos to the buffer. +func (p *parametizer) writeFrom(ppos int) { + if p.pos > ppos { + // append remaining characters. + p.buf.Write(p.z[ppos:p.pos]) + } +} + +func (p *parametizer) scanTextFn() stateFn { + ppos := p.pos + for { + ch, err := p.peek() + if err != nil { + p.writeFrom(ppos) + return nil + } + if ch == '%' { + p.writeFrom(ppos) + p.pos++ + return p.scanCodeFn + } + p.pos++ + } +} + +func (p *parametizer) scanCodeFn() stateFn { + ch, err := p.peek() + if err != nil { + return nil + } + switch ch { + case '%': + p.buf.WriteByte('%') + case ':': + // this character is used to avoid interpreting "%-" and "%+" as operators. + // the next character is where the format really begins. + p.pos++ + _, err = p.peek() + if err != nil { + return nil + } + return p.scanFormatFn + case '#', ' ', '0', '1', '2', '3', '4', '5', '6', '7', '8', '9', '.': + return p.scanFormatFn + case 'o': + p.buf.WriteString(strconv.FormatInt(int64(p.s.popInt()), 8)) + case 'd': + p.buf.WriteString(strconv.Itoa(p.s.popInt())) + case 'x': + p.buf.WriteString(strconv.FormatInt(int64(p.s.popInt()), 16)) + case 'X': + p.buf.WriteString(strings.ToUpper(strconv.FormatInt(int64(p.s.popInt()), 16))) + case 's': + p.buf.WriteString(p.s.popString()) + case 'c': + p.buf.WriteByte(p.s.popByte()) + case 'p': + p.pos++ + return p.pushParamFn + case 'P': + p.pos++ + return p.setDsVarFn + case 'g': + p.pos++ + return p.getDsVarFn + case '\'': + p.pos++ + ch, err = p.peek() + if err != nil { + return nil + } + p.s.push(ch) + // skip the '\'' + p.pos++ + case '{': + p.pos++ + return p.pushIntfn + case 'l': + p.s.push(len(p.s.popString())) + case '+': + bi, ai := p.s.popInt(), p.s.popInt() + p.s.push(ai + bi) + case '-': + bi, ai := p.s.popInt(), p.s.popInt() + p.s.push(ai - bi) + case '*': + bi, ai := p.s.popInt(), p.s.popInt() + p.s.push(ai * bi) + case '/': + bi, ai := p.s.popInt(), p.s.popInt() + if bi != 0 { + p.s.push(ai / bi) + } else { + p.s.push(0) + } + case 'm': + bi, ai := p.s.popInt(), p.s.popInt() + if bi != 0 { + p.s.push(ai % bi) + } else { + p.s.push(0) + } + case '&': + bi, ai := p.s.popInt(), p.s.popInt() + p.s.push(ai & bi) + case '|': + bi, ai := p.s.popInt(), p.s.popInt() + p.s.push(ai | bi) + case '^': + bi, ai := p.s.popInt(), p.s.popInt() + p.s.push(ai ^ bi) + case '=': + bi, ai := p.s.popInt(), p.s.popInt() + p.s.push(ai == bi) + case '>': + bi, ai := p.s.popInt(), p.s.popInt() + p.s.push(ai > bi) + case '<': + bi, ai := p.s.popInt(), p.s.popInt() + p.s.push(ai < bi) + case 'A': + bi, ai := p.s.popBool(), p.s.popBool() + p.s.push(ai && bi) + case 'O': + bi, ai := p.s.popBool(), p.s.popBool() + p.s.push(ai || bi) + case '!': + p.s.push(!p.s.popBool()) + case '~': + p.s.push(^p.s.popInt()) + case 'i': + for i := range p.params[:2] { + if n, ok := p.params[i].(int); ok { + p.params[i] = n + 1 + } + } + case '?', ';': + case 't': + return p.scanThenFn + case 'e': + p.skipElse = true + return p.skipTextFn + } + p.pos++ + return p.scanTextFn +} + +func (p *parametizer) scanFormatFn() stateFn { + // the character was already read, so no need to check the error. + ch, _ := p.peek() + // 6 should be the maximum length of a format string, for example "%:-9.9d". + f := []byte{'%', ch, 0, 0, 0, 0} + var err error + for { + p.pos++ + ch, err = p.peek() + if err != nil { + return nil + } + f = append(f, ch) + switch ch { + case 'o', 'd', 'x', 'X': + fmt.Fprintf(p.buf, string(f), p.s.popInt()) + break + case 's': + fmt.Fprintf(p.buf, string(f), p.s.popString()) + break + case 'c': + fmt.Fprintf(p.buf, string(f), p.s.popByte()) + break + } + } + p.pos++ + return p.scanTextFn +} + +func (p *parametizer) pushParamFn() stateFn { + ch, err := p.peek() + if err != nil { + return nil + } + if ai := int(ch - '1'); ai >= 0 && ai < len(p.params) { + p.s.push(p.params[ai]) + } else { + p.s.push(0) + } + // skip the '}' + p.pos++ + return p.scanTextFn +} + +func (p *parametizer) setDsVarFn() stateFn { + ch, err := p.peek() + if err != nil { + return nil + } + if ch >= 'A' && ch <= 'Z' { + staticVars.Lock() + staticVars.vars[int(ch-'A')] = p.s.pop() + staticVars.Unlock() + } else if ch >= 'a' && ch <= 'z' { + p.vars[int(ch-'a')] = p.s.pop() + } + p.pos++ + return p.scanTextFn +} + +func (p *parametizer) getDsVarFn() stateFn { + ch, err := p.peek() + if err != nil { + return nil + } + var a byte + if ch >= 'A' && ch <= 'Z' { + a = 'A' + } else if ch >= 'a' && ch <= 'z' { + a = 'a' + } + staticVars.Lock() + p.s.push(staticVars.vars[int(ch-a)]) + staticVars.Unlock() + p.pos++ + return p.scanTextFn +} + +func (p *parametizer) pushIntfn() stateFn { + var ai int + for { + ch, err := p.peek() + if err != nil { + return nil + } + p.pos++ + if ch < '0' || ch > '9' { + p.s.push(ai) + return p.scanTextFn + } + ai = (ai * 10) + int(ch-'0') + } +} + +func (p *parametizer) scanThenFn() stateFn { + p.pos++ + if p.s.popBool() { + return p.scanTextFn + } + p.skipElse = false + return p.skipTextFn +} + +func (p *parametizer) skipTextFn() stateFn { + for { + ch, err := p.peek() + if err != nil { + return nil + } + p.pos++ + if ch == '%' { + break + } + } + if p.skipElse { + return p.skipElseFn + } + return p.skipThenFn +} + +func (p *parametizer) skipThenFn() stateFn { + ch, err := p.peek() + if err != nil { + return nil + } + p.pos++ + switch ch { + case ';': + if p.nest == 0 { + return p.scanTextFn + } + p.nest-- + case '?': + p.nest++ + case 'e': + if p.nest == 0 { + return p.scanTextFn + } + } + return p.skipTextFn +} + +func (p *parametizer) skipElseFn() stateFn { + ch, err := p.peek() + if err != nil { + return nil + } + p.pos++ + switch ch { + case ';': + if p.nest == 0 { + return p.scanTextFn + } + p.nest-- + case '?': + p.nest++ + } + return p.skipTextFn +} + +// Printf evaluates a parameterized terminfo value z, interpolating params. +func Printf(z []byte, params ...interface{}) string { + p := newParametizer(z) + defer p.reset() + // make sure we always have 9 parameters -- makes it easier + // later to skip checks and its faster + for i := 0; i < len(p.params) && i < len(params); i++ { + p.params[i] = params[i] + } + return p.exec() +} + +// Fprintf evaluates a parameterized terminfo value z, interpolating params and +// writing to w. +func Fprintf(w io.Writer, z []byte, params ...interface{}) { + w.Write([]byte(Printf(z, params...))) +} diff --git a/vendor/github.com/xo/terminfo/stack.go b/vendor/github.com/xo/terminfo/stack.go new file mode 100644 index 00000000..a6de3950 --- /dev/null +++ b/vendor/github.com/xo/terminfo/stack.go @@ -0,0 +1,48 @@ +package terminfo + +type stack []interface{} + +func (s *stack) push(v interface{}) { + *s = append(*s, v) +} + +func (s *stack) pop() interface{} { + if len(*s) == 0 { + return nil + } + v := (*s)[len(*s)-1] + *s = (*s)[:len(*s)-1] + return v +} + +func (s *stack) popInt() int { + if i, ok := s.pop().(int); ok { + return i + } + return 0 +} + +func (s *stack) popBool() bool { + if b, ok := s.pop().(bool); ok { + return b + } + return false +} + +func (s *stack) popByte() byte { + if b, ok := s.pop().(byte); ok { + return b + } + return 0 +} + +func (s *stack) popString() string { + if a, ok := s.pop().(string); ok { + return a + } + return "" +} + +func (s *stack) reset() { + *s = (*s)[:0] +} diff --git a/vendor/github.com/xo/terminfo/terminfo.go b/vendor/github.com/xo/terminfo/terminfo.go new file mode 100644 index 00000000..69e3b606 --- /dev/null +++ b/vendor/github.com/xo/terminfo/terminfo.go @@ -0,0 +1,479 @@ +// Package terminfo implements reading terminfo files in pure go. +package terminfo + +//go:generate go run gen.go + +import ( + "io" + "io/ioutil" + "path" + "strconv" + "strings" +) + +// Error is a terminfo error. +type Error string + +// Error satisfies the error interface. +func (err Error) Error() string { + return string(err) +} + +const ( + // ErrInvalidFileSize is the invalid file size error. + ErrInvalidFileSize Error = "invalid file size" + // ErrUnexpectedFileEnd is the unexpected file end error. + ErrUnexpectedFileEnd Error = "unexpected file end" + // ErrInvalidStringTable is the invalid string table error. + ErrInvalidStringTable Error = "invalid string table" + // ErrInvalidMagic is the invalid magic error. + ErrInvalidMagic Error = "invalid magic" + // ErrInvalidHeader is the invalid header error. + ErrInvalidHeader Error = "invalid header" + // ErrInvalidNames is the invalid names error. + ErrInvalidNames Error = "invalid names" + // ErrInvalidExtendedHeader is the invalid extended header error. + ErrInvalidExtendedHeader Error = "invalid extended header" + // ErrEmptyTermName is the empty term name error. + ErrEmptyTermName Error = "empty term name" + // ErrDatabaseDirectoryNotFound is the database directory not found error. + ErrDatabaseDirectoryNotFound Error = "database directory not found" + // ErrFileNotFound is the file not found error. + ErrFileNotFound Error = "file not found" + // ErrInvalidTermProgramVersion is the invalid TERM_PROGRAM_VERSION error. + ErrInvalidTermProgramVersion Error = "invalid TERM_PROGRAM_VERSION" +) + +// Terminfo describes a terminal's capabilities. +type Terminfo struct { + // File is the original source file. + File string + // Names are the provided cap names. + Names []string + // Bools are the bool capabilities. + Bools map[int]bool + // BoolsM are the missing bool capabilities. + BoolsM map[int]bool + // Nums are the num capabilities. + Nums map[int]int + // NumsM are the missing num capabilities. + NumsM map[int]bool + // Strings are the string capabilities. + Strings map[int][]byte + // StringsM are the missing string capabilities. + StringsM map[int]bool + // ExtBools are the extended bool capabilities. + ExtBools map[int]bool + // ExtBoolsNames is the map of extended bool capabilities to their index. + ExtBoolNames map[int][]byte + // ExtNums are the extended num capabilities. + ExtNums map[int]int + // ExtNumsNames is the map of extended num capabilities to their index. + ExtNumNames map[int][]byte + // ExtStrings are the extended string capabilities. + ExtStrings map[int][]byte + // ExtStringsNames is the map of extended string capabilities to their index. + ExtStringNames map[int][]byte +} + +// Decode decodes the terminfo data contained in buf. +func Decode(buf []byte) (*Terminfo, error) { + var err error + // check max file length + if len(buf) >= maxFileLength { + return nil, ErrInvalidFileSize + } + d := &decoder{ + buf: buf, + n: len(buf), + } + // read header + h, err := d.readInts(6, 16) + if err != nil { + return nil, err + } + var numWidth int + // check magic + switch { + case h[fieldMagic] == magic: + numWidth = 16 + case h[fieldMagic] == magicExtended: + numWidth = 32 + default: + return nil, ErrInvalidMagic + } + // check header + if hasInvalidCaps(h) { + return nil, ErrInvalidHeader + } + // check remaining length + if d.n-d.pos < capLength(h) { + return nil, ErrUnexpectedFileEnd + } + // read names + names, err := d.readBytes(h[fieldNameSize]) + if err != nil { + return nil, err + } + // check name is terminated properly + i := findNull(names, 0) + if i == -1 { + return nil, ErrInvalidNames + } + names = names[:i] + // read bool caps + bools, boolsM, err := d.readBools(h[fieldBoolCount]) + if err != nil { + return nil, err + } + // read num caps + nums, numsM, err := d.readNums(h[fieldNumCount], numWidth) + if err != nil { + return nil, err + } + // read string caps + strs, strsM, err := d.readStrings(h[fieldStringCount], h[fieldTableSize]) + if err != nil { + return nil, err + } + ti := &Terminfo{ + Names: strings.Split(string(names), "|"), + Bools: bools, + BoolsM: boolsM, + Nums: nums, + NumsM: numsM, + Strings: strs, + StringsM: strsM, + } + // at the end of file, so no extended caps + if d.pos >= d.n { + return ti, nil + } + // decode extended header + eh, err := d.readInts(5, 16) + if err != nil { + return nil, err + } + // check extended offset field + if hasInvalidExtOffset(eh) { + return nil, ErrInvalidExtendedHeader + } + // check extended cap lengths + if d.n-d.pos != extCapLength(eh, numWidth) { + return nil, ErrInvalidExtendedHeader + } + // read extended bool caps + ti.ExtBools, _, err = d.readBools(eh[fieldExtBoolCount]) + if err != nil { + return nil, err + } + // read extended num caps + ti.ExtNums, _, err = d.readNums(eh[fieldExtNumCount], numWidth) + if err != nil { + return nil, err + } + // read extended string data table indexes + extIndexes, err := d.readInts(eh[fieldExtOffsetCount], 16) + if err != nil { + return nil, err + } + // read string data table + extData, err := d.readBytes(eh[fieldExtTableSize]) + if err != nil { + return nil, err + } + // precautionary check that exactly at end of file + if d.pos != d.n { + return nil, ErrUnexpectedFileEnd + } + var last int + // read extended string caps + ti.ExtStrings, last, err = readStrings(extIndexes, extData, eh[fieldExtStringCount]) + if err != nil { + return nil, err + } + extIndexes, extData = extIndexes[eh[fieldExtStringCount]:], extData[last:] + // read extended bool names + ti.ExtBoolNames, _, err = readStrings(extIndexes, extData, eh[fieldExtBoolCount]) + if err != nil { + return nil, err + } + extIndexes = extIndexes[eh[fieldExtBoolCount]:] + // read extended num names + ti.ExtNumNames, _, err = readStrings(extIndexes, extData, eh[fieldExtNumCount]) + if err != nil { + return nil, err + } + extIndexes = extIndexes[eh[fieldExtNumCount]:] + // read extended string names + ti.ExtStringNames, _, err = readStrings(extIndexes, extData, eh[fieldExtStringCount]) + if err != nil { + return nil, err + } + // extIndexes = extIndexes[eh[fieldExtStringCount]:] + return ti, nil +} + +// Open reads the terminfo file name from the specified directory dir. +func Open(dir, name string) (*Terminfo, error) { + var err error + var buf []byte + var filename string + for _, f := range []string{ + path.Join(dir, name[0:1], name), + path.Join(dir, strconv.FormatUint(uint64(name[0]), 16), name), + } { + buf, err = ioutil.ReadFile(f) + if err == nil { + filename = f + break + } + } + if buf == nil { + return nil, ErrFileNotFound + } + // decode + ti, err := Decode(buf) + if err != nil { + return nil, err + } + // save original file name + ti.File = filename + // add to cache + termCache.Lock() + for _, n := range ti.Names { + termCache.db[n] = ti + } + termCache.Unlock() + return ti, nil +} + +// boolCaps returns all bool and extended capabilities using f to format the +// index key. +func (ti *Terminfo) boolCaps(f func(int) string, extended bool) map[string]bool { + m := make(map[string]bool, len(ti.Bools)+len(ti.ExtBools)) + if !extended { + for k, v := range ti.Bools { + m[f(k)] = v + } + } else { + for k, v := range ti.ExtBools { + m[string(ti.ExtBoolNames[k])] = v + } + } + return m +} + +// BoolCaps returns all bool capabilities. +func (ti *Terminfo) BoolCaps() map[string]bool { + return ti.boolCaps(BoolCapName, false) +} + +// BoolCapsShort returns all bool capabilities, using the short name as the +// index. +func (ti *Terminfo) BoolCapsShort() map[string]bool { + return ti.boolCaps(BoolCapNameShort, false) +} + +// ExtBoolCaps returns all extended bool capabilities. +func (ti *Terminfo) ExtBoolCaps() map[string]bool { + return ti.boolCaps(BoolCapName, true) +} + +// ExtBoolCapsShort returns all extended bool capabilities, using the short +// name as the index. +func (ti *Terminfo) ExtBoolCapsShort() map[string]bool { + return ti.boolCaps(BoolCapNameShort, true) +} + +// numCaps returns all num and extended capabilities using f to format the +// index key. +func (ti *Terminfo) numCaps(f func(int) string, extended bool) map[string]int { + m := make(map[string]int, len(ti.Nums)+len(ti.ExtNums)) + if !extended { + for k, v := range ti.Nums { + m[f(k)] = v + } + } else { + for k, v := range ti.ExtNums { + m[string(ti.ExtNumNames[k])] = v + } + } + return m +} + +// NumCaps returns all num capabilities. +func (ti *Terminfo) NumCaps() map[string]int { + return ti.numCaps(NumCapName, false) +} + +// NumCapsShort returns all num capabilities, using the short name as the +// index. +func (ti *Terminfo) NumCapsShort() map[string]int { + return ti.numCaps(NumCapNameShort, false) +} + +// ExtNumCaps returns all extended num capabilities. +func (ti *Terminfo) ExtNumCaps() map[string]int { + return ti.numCaps(NumCapName, true) +} + +// ExtNumCapsShort returns all extended num capabilities, using the short +// name as the index. +func (ti *Terminfo) ExtNumCapsShort() map[string]int { + return ti.numCaps(NumCapNameShort, true) +} + +// stringCaps returns all string and extended capabilities using f to format the +// index key. +func (ti *Terminfo) stringCaps(f func(int) string, extended bool) map[string][]byte { + m := make(map[string][]byte, len(ti.Strings)+len(ti.ExtStrings)) + if !extended { + for k, v := range ti.Strings { + m[f(k)] = v + } + } else { + for k, v := range ti.ExtStrings { + m[string(ti.ExtStringNames[k])] = v + } + } + return m +} + +// StringCaps returns all string capabilities. +func (ti *Terminfo) StringCaps() map[string][]byte { + return ti.stringCaps(StringCapName, false) +} + +// StringCapsShort returns all string capabilities, using the short name as the +// index. +func (ti *Terminfo) StringCapsShort() map[string][]byte { + return ti.stringCaps(StringCapNameShort, false) +} + +// ExtStringCaps returns all extended string capabilities. +func (ti *Terminfo) ExtStringCaps() map[string][]byte { + return ti.stringCaps(StringCapName, true) +} + +// ExtStringCapsShort returns all extended string capabilities, using the short +// name as the index. +func (ti *Terminfo) ExtStringCapsShort() map[string][]byte { + return ti.stringCaps(StringCapNameShort, true) +} + +// Has determines if the bool cap i is present. +func (ti *Terminfo) Has(i int) bool { + return ti.Bools[i] +} + +// Num returns the num cap i, or -1 if not present. +func (ti *Terminfo) Num(i int) int { + n, ok := ti.Nums[i] + if !ok { + return -1 + } + return n +} + +// Printf formats the string cap i, interpolating parameters v. +func (ti *Terminfo) Printf(i int, v ...interface{}) string { + return Printf(ti.Strings[i], v...) +} + +// Fprintf prints the string cap i to writer w, interpolating parameters v. +func (ti *Terminfo) Fprintf(w io.Writer, i int, v ...interface{}) { + Fprintf(w, ti.Strings[i], v...) +} + +// Color takes a foreground and background color and returns string that sets +// them for this terminal. +func (ti *Terminfo) Colorf(fg, bg int, str string) string { + maxColors := int(ti.Nums[MaxColors]) + // map bright colors to lower versions if the color table only holds 8. + if maxColors == 8 { + if fg > 7 && fg < 16 { + fg -= 8 + } + if bg > 7 && bg < 16 { + bg -= 8 + } + } + var s string + if maxColors > fg && fg >= 0 { + s += ti.Printf(SetAForeground, fg) + } + if maxColors > bg && bg >= 0 { + s += ti.Printf(SetABackground, bg) + } + return s + str + ti.Printf(ExitAttributeMode) +} + +// Goto returns a string suitable for addressing the cursor at the given +// row and column. The origin 0, 0 is in the upper left corner of the screen. +func (ti *Terminfo) Goto(row, col int) string { + return Printf(ti.Strings[CursorAddress], row, col) +} + +// Puts emits the string to the writer, but expands inline padding indications +// (of the form $<[delay]> where [delay] is msec) to a suitable number of +// padding characters (usually null bytes) based upon the supplied baud. At +// high baud rates, more padding characters will be inserted. +/*func (ti *Terminfo) Puts(w io.Writer, s string, lines, baud int) (int, error) { + var err error + for { + start := strings.Index(s, "$<") + if start == -1 { + // most strings don't need padding, which is good news! + return io.WriteString(w, s) + } + end := strings.Index(s, ">") + if end == -1 { + // unterminated... just emit bytes unadulterated. + return io.WriteString(w, "$<"+s) + } + var c int + c, err = io.WriteString(w, s[:start]) + if err != nil { + return n + c, err + } + n += c + s = s[start+2:] + val := s[:end] + s = s[end+1:] + var ms int + var dot, mandatory, asterisk bool + unit := 1000 + for _, ch := range val { + switch { + case ch >= '0' && ch <= '9': + ms = (ms * 10) + int(ch-'0') + if dot { + unit *= 10 + } + case ch == '.' && !dot: + dot = true + case ch == '*' && !asterisk: + ms *= lines + asterisk = true + case ch == '/': + mandatory = true + default: + break + } + } + z, pad := ((baud/8)/unit)*ms, ti.Strings[PadChar] + b := make([]byte, len(pad)*z) + for bp := copy(b, pad); bp < len(b); bp *= 2 { + copy(b[bp:], b[:bp]) + } + if (!ti.Bools[XonXoff] && baud > int(ti.Nums[PaddingBaudRate])) || mandatory { + c, err = w.Write(b) + if err != nil { + return n + c, err + } + n += c + } + } + return n, nil +}*/ diff --git a/vendor/modules.txt b/vendor/modules.txt index e635074d..1408f65c 100644 --- a/vendor/modules.txt +++ b/vendor/modules.txt @@ -62,8 +62,8 @@ github.com/charmbracelet/bubbles/runeutil github.com/charmbracelet/bubbles/spinner github.com/charmbracelet/bubbles/textinput github.com/charmbracelet/bubbles/viewport -# github.com/charmbracelet/bubbletea v0.25.0 -## explicit; go 1.17 +# github.com/charmbracelet/bubbletea v0.26.6 +## explicit; go 1.18 github.com/charmbracelet/bubbletea # github.com/charmbracelet/glamour v0.6.0 ## explicit; go 1.13 @@ -72,9 +72,19 @@ github.com/charmbracelet/glamour/ansi # github.com/charmbracelet/lipgloss v0.10.0 ## explicit; go 1.18 github.com/charmbracelet/lipgloss -# github.com/containerd/console v1.0.4-0.20230313162750-1ae8d489ac81 -## explicit; go 1.13 -github.com/containerd/console +# github.com/charmbracelet/x/ansi v0.1.2 +## explicit; go 1.18 +github.com/charmbracelet/x/ansi +github.com/charmbracelet/x/ansi/parser +# github.com/charmbracelet/x/input v0.1.0 +## explicit; go 1.18 +github.com/charmbracelet/x/input +# github.com/charmbracelet/x/term v0.1.1 +## explicit; go 1.18 +github.com/charmbracelet/x/term +# github.com/charmbracelet/x/windows v0.1.0 +## explicit; go 1.18 +github.com/charmbracelet/x/windows # github.com/davecgh/go-spew v1.1.2-0.20180830191138-d8f796af33cc ## explicit github.com/davecgh/go-spew/spew @@ -82,6 +92,9 @@ github.com/davecgh/go-spew/spew ## explicit github.com/dlclark/regexp2 github.com/dlclark/regexp2/syntax +# github.com/erikgeiser/coninput v0.0.0-20211004153227-1c3628e74d0f +## explicit; go 1.16 +github.com/erikgeiser/coninput # github.com/felixge/httpsnoop v1.0.3 ## explicit; go 1.13 github.com/felixge/httpsnoop @@ -231,7 +244,7 @@ github.com/mitchellh/mapstructure # github.com/mohae/deepcopy v0.0.0-20170929034955-c48cc78d4826 ## explicit github.com/mohae/deepcopy -# github.com/muesli/ansi v0.0.0-20211018074035-2e021307bc4b +# github.com/muesli/ansi v0.0.0-20230316100256-276c6243b2f6 ## explicit; go 1.17 github.com/muesli/ansi github.com/muesli/ansi/compressor @@ -339,6 +352,9 @@ github.com/stretchr/testify/require # github.com/subosito/gotenv v1.6.0 ## explicit; go 1.18 github.com/subosito/gotenv +# github.com/xo/terminfo v0.0.0-20220910002029-abceb7e1c41e +## explicit; go 1.19 +github.com/xo/terminfo # github.com/yuin/goldmark v1.5.2 ## explicit; go 1.18 github.com/yuin/goldmark