mirror of
https://github.com/rancher/cli.git
synced 2026-02-05 18:48:50 +01:00
Vendor update
This commit is contained in:
committed by
Craig Jellick
parent
2d711d35c9
commit
4fcbd64fcb
@@ -16,7 +16,8 @@ github.com/fatih/color 67c513e5729f918f5e69786686770c27141a4490
|
||||
github.com/mattn/go-colorable ad5389df28cdac544c99bd7b9161a0b5b6ca9d1b
|
||||
github.com/mattn/go-isatty fc9e8d8ef48496124e79ae0df75490096eccf6fe
|
||||
gopkg.in/check.v1 4f90aeace3a26ad7021961c297b22c42160c7b25
|
||||
github.com/c-bata/go-prompt ba233229562fbeebe1328788a6245cda9124af27
|
||||
github.com/c-bata/go-prompt f329ebd2409de559ba256325b8a18b13a145b5d5
|
||||
github.com/mattn/go-tty 061c12e2dc3ef933c21c2249823e6f42e6935c40
|
||||
github.com/pkg/term b1f72af2d63057363398bec5873d16a98b453312
|
||||
github.com/Masterminds/sprig c974324bb59b465f00b128a055656d44f60e4ffc
|
||||
github.com/fatih/structs dc3312cb1a4513a366c4c9e622ad55c32df12ed3
|
||||
|
||||
38
vendor/github.com/c-bata/go-prompt/CHANGELOG.md
generated
vendored
38
vendor/github.com/c-bata/go-prompt/CHANGELOG.md
generated
vendored
@@ -1,14 +1,46 @@
|
||||
# Change Log
|
||||
|
||||
## v0.2.0 (2017/??/??)
|
||||
## v0.2.2 (2018/??/??)
|
||||
|
||||
**New Features**
|
||||
### Fixed
|
||||
|
||||
* Fix a bug in docker container [issue #39](https://github.com/c-bata/go-prompt/issues/39)
|
||||
|
||||
## v0.2.1 (2018/02/14)
|
||||
|
||||
### What's New?
|
||||
|
||||
* ~~It seems that windows support is almost perfect.~~
|
||||
* A critical bug is found :( When you change a terminal window size, the layout will be broken because current implementation cannot catch signal for updating window size on Windows.
|
||||
|
||||
### Fixed
|
||||
|
||||
* Fix a Shift+Tab handling on Windows.
|
||||
* Fix 4-dimension arrow keys handling on Windows.
|
||||
|
||||
## v0.2.0 (2018/02/13)
|
||||
|
||||
### What's New?
|
||||
|
||||
* Supports scrollbar when there are too many matched suggestions
|
||||
* Windows support (but please caution because this is still not perfect).
|
||||
* Add OptionLivePrefix to update the prefix dynamically
|
||||
* Implement clear screen by `Ctrl+L`.
|
||||
|
||||
**Fixed**
|
||||
### Fixed
|
||||
|
||||
* Fix the behavior of `Ctrl+W` keybind.
|
||||
* Fix the panic because when running on a docker container (please see [here](https://github.com/c-bata/go-prompt/pull/32) for details).
|
||||
* Fix panic when making terminal window small size after input 2 lines of texts. See [here](https://github.com/c-bata/go-prompt/issues/37) for details).
|
||||
* And also fixed many bugs that layout is broken when using Terminal.app, GNU Terminal and a Goland(IntelliJ).
|
||||
|
||||
### News
|
||||
|
||||
New core developers are joined (alphabetical order).
|
||||
|
||||
* Nao Yonashiro (Github @orisano)
|
||||
* Ryoma Abe (Github @Allajah)
|
||||
* Yusuke Nakamura (Github @unasuke)
|
||||
|
||||
|
||||
## v0.1.0 (2017/08/15)
|
||||
|
||||
150
vendor/github.com/c-bata/go-prompt/DEVELOPER_GUIDE.md
generated
vendored
150
vendor/github.com/c-bata/go-prompt/DEVELOPER_GUIDE.md
generated
vendored
@@ -1,150 +0,0 @@
|
||||
# Developer Guide
|
||||
|
||||
## Getting Started
|
||||
|
||||
Most simple example is below.
|
||||
|
||||
```go
|
||||
package main
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
|
||||
"github.com/c-bata/go-prompt"
|
||||
)
|
||||
|
||||
// executor executes command and print the output.
|
||||
func executor(in string) {
|
||||
fmt.Println("Your input: " + in)
|
||||
}
|
||||
|
||||
// completer returns the completion items from user input.
|
||||
func completer(d prompt.Document) []prompt.Suggest {
|
||||
s := []prompt.Suggest{
|
||||
{Text: "users", Description: "user table"},
|
||||
{Text: "sites", Description: "sites table"},
|
||||
{Text: "articles", Description: "articles table"},
|
||||
{Text: "comments", Description: "comments table"},
|
||||
}
|
||||
return prompt.FilterHasPrefix(s, d.GetWordBeforeCursor(), true)
|
||||
}
|
||||
|
||||
func main() {
|
||||
p := prompt.New(
|
||||
executor,
|
||||
completer,
|
||||
prompt.OptionPrefix(">>> "),
|
||||
prompt.OptionTitle("sql-prompt"),
|
||||
)
|
||||
p.Run()
|
||||
}
|
||||
```
|
||||
|
||||
If you want to create CLI using go-prompt, I recommend you to look at the [source code of kube-prompt](https://github.com/c-bata/kube-prompt).
|
||||
It is the most practical example.
|
||||
|
||||
|
||||
## Options
|
||||
|
||||
go-prompt has many color options.
|
||||
It is difficult to describe by text. So please see below figure:
|
||||
|
||||

|
||||
|
||||
* **OptionPrefixTextColor(prompt.Color)** : default `prompt.Blue`
|
||||
* **OptionPrefixBackgroundColor(prompt.Color)** : default `prompt.DefaultColor`
|
||||
* **OptionInputTextColor(prompt.Color)** : default `prompt.DefaultColor`
|
||||
* **OptionInputBGColor(prompt.Color)** : default `prompt.DefaultColor`
|
||||
* **OptionPreviewSuggestionTextColor(prompt.Color)** : default `prompt.Green`
|
||||
* **OptionPreviewSuggestionBGColor(prompt.Color)** : default `prompt.DefaultColor`
|
||||
* **OptionSuggestionTextColor(prompt.Color)** : default `prompt.White`
|
||||
* **OptionSuggestionBGColor(prompt.Color)** : default `prompt.Cyan`
|
||||
* **OptionSelectedSuggestionTextColor(prompt.Color)** : `default prompt.Black`
|
||||
* **OptionSelectedSuggestionBGColor(prompt.Color)** : `default prompt.DefaultColor`
|
||||
* **OptionDescriptionTextColor(prompt.Color)** : default `prompt.Black`
|
||||
* **OptionDescriptionBGColor(prompt.Color)** : default `prompt.Turquoise`
|
||||
* **OptionSelectedDescriptionTextColor(prompt.Color)** : default `prompt.White`
|
||||
* **OptionSelectedDescriptionBGColor(prompt.Color)** : default `prompt.Cyan`
|
||||
|
||||
**Other Options**
|
||||
|
||||
#### `OptionTitle(string)` : default `""`
|
||||
Option to set title displayed at the header bar of terminal.
|
||||
|
||||
#### `OptionHistory([]string)` : default `[]string{}`
|
||||
Option to set history.
|
||||
|
||||
#### `OptionPrefix(string)` : default `"> "`
|
||||
Option to set prefix string.
|
||||
|
||||
#### `OptionMaxSuggestions(x uint16)` : default `6`
|
||||
The max number of displayed suggestions.
|
||||
|
||||
#### `OptionParser(prompt.ConsoleParser)` : default `VT100Parser`
|
||||
To set a custom ConsoleParser object.
|
||||
An argument should implement ConsoleParser interface.
|
||||
|
||||
#### `OptionWriter(prompt.ConsoleWriter)` : default `VT100Writer`
|
||||
To set a custom ConsoleWriter object.
|
||||
An argument should implement ConsoleWriter interace.
|
||||
|
||||
#### `SwitchKeyBindMode(prompt.KeyBindMode)` : default `prompt.EmacsKeyBindMode`
|
||||
To set a key bind mode.
|
||||
|
||||
#### `OptionAddKeyBind(...KeyBind)` : default `[]KeyBind{}`
|
||||
To set a custom key bind.
|
||||
|
||||
## Architecture of go-prompt
|
||||
|
||||
*Caution: This section is WIP.*
|
||||
|
||||
This is a short description of go-prompt implementation.
|
||||
go-prompt consists of three parts.
|
||||
|
||||
1. Input parser
|
||||
2. Emulate user input with Buffer object.
|
||||
3. Render buffer object.
|
||||
|
||||
### Input Parser
|
||||
|
||||

|
||||
|
||||
Input Parser only supports only vt100 compatible console now.
|
||||
|
||||
* Set raw mode.
|
||||
* Read standard input.
|
||||
* Parse byte array
|
||||
|
||||
### Emulate user input
|
||||
|
||||
go-prompt contains Buffer class.
|
||||
It represents input state by handling user input key.
|
||||
|
||||
`Buffer` object has text and cursor position.
|
||||
|
||||
**TODO prepare the sample of buffer**
|
||||
|
||||
```go
|
||||
package main
|
||||
|
||||
import "github.com/c-bata/go-prompt"
|
||||
|
||||
func main() {
|
||||
b := prompt.NewBuffer()
|
||||
... wip
|
||||
}
|
||||
```
|
||||
|
||||
### Renderer
|
||||
|
||||
`Renderer` object renders a buffer object.
|
||||
|
||||
**TODO prepare the sample of brender**
|
||||
|
||||
```go
|
||||
package main
|
||||
```
|
||||
|
||||
the output is below:
|
||||
|
||||
**TODO prepare a screen shot**
|
||||
39
vendor/github.com/c-bata/go-prompt/Gopkg.lock
generated
vendored
Normal file
39
vendor/github.com/c-bata/go-prompt/Gopkg.lock
generated
vendored
Normal file
@@ -0,0 +1,39 @@
|
||||
# This file is autogenerated, do not edit; changes may be undone by the next 'dep ensure'.
|
||||
|
||||
|
||||
[[projects]]
|
||||
name = "github.com/mattn/go-colorable"
|
||||
packages = ["."]
|
||||
revision = "167de6bfdfba052fa6b2d3664c8f5272e23c9072"
|
||||
version = "v0.0.9"
|
||||
|
||||
[[projects]]
|
||||
name = "github.com/mattn/go-isatty"
|
||||
packages = ["."]
|
||||
revision = "0360b2af4f38e8d38c7fce2a9f4e702702d73a39"
|
||||
version = "v0.0.3"
|
||||
|
||||
[[projects]]
|
||||
branch = "master"
|
||||
name = "github.com/mattn/go-tty"
|
||||
packages = ["."]
|
||||
revision = "061c12e2dc3ef933c21c2249823e6f42e6935c40"
|
||||
|
||||
[[projects]]
|
||||
branch = "master"
|
||||
name = "github.com/pkg/term"
|
||||
packages = ["termios"]
|
||||
revision = "b1f72af2d63057363398bec5873d16a98b453312"
|
||||
|
||||
[[projects]]
|
||||
branch = "master"
|
||||
name = "golang.org/x/sys"
|
||||
packages = ["unix"]
|
||||
revision = "37707fdb30a5b38865cfb95e5aab41707daec7fd"
|
||||
|
||||
[solve-meta]
|
||||
analyzer-name = "dep"
|
||||
analyzer-version = 1
|
||||
inputs-digest = "6c442f55617e93df75aa1ee2fd2b36cfab23003fded4723be56c6b6fd0545c56"
|
||||
solver-name = "gps-cdcl"
|
||||
solver-version = 1
|
||||
34
vendor/github.com/c-bata/go-prompt/Gopkg.toml
generated
vendored
Normal file
34
vendor/github.com/c-bata/go-prompt/Gopkg.toml
generated
vendored
Normal file
@@ -0,0 +1,34 @@
|
||||
|
||||
# Gopkg.toml example
|
||||
#
|
||||
# Refer to https://github.com/golang/dep/blob/master/docs/Gopkg.toml.md
|
||||
# for detailed Gopkg.toml documentation.
|
||||
#
|
||||
# required = ["github.com/user/thing/cmd/thing"]
|
||||
# ignored = ["github.com/user/project/pkgX", "bitbucket.org/user/project/pkgA/pkgY"]
|
||||
#
|
||||
# [[constraint]]
|
||||
# name = "github.com/user/project"
|
||||
# version = "1.0.0"
|
||||
#
|
||||
# [[constraint]]
|
||||
# name = "github.com/user/project2"
|
||||
# branch = "dev"
|
||||
# source = "github.com/myfork/project2"
|
||||
#
|
||||
# [[override]]
|
||||
# name = "github.com/x/y"
|
||||
# version = "2.4.0"
|
||||
|
||||
|
||||
[[constraint]]
|
||||
name = "github.com/mattn/go-colorable"
|
||||
version = "0.0.9"
|
||||
|
||||
[[constraint]]
|
||||
branch = "master"
|
||||
name = "github.com/mattn/go-tty"
|
||||
|
||||
[[constraint]]
|
||||
branch = "master"
|
||||
name = "github.com/pkg/term"
|
||||
12
vendor/github.com/c-bata/go-prompt/Makefile
generated
vendored
12
vendor/github.com/c-bata/go-prompt/Makefile
generated
vendored
@@ -5,7 +5,8 @@ setup: ## Setup for required tools.
|
||||
go get github.com/golang/lint/golint
|
||||
go get golang.org/x/tools/cmd/goimports
|
||||
go get golang.org/x/tools/cmd/stringer
|
||||
go get github.com/pkg/term
|
||||
go get -u github.com/golang/dep/cmd/dep
|
||||
dep ensure
|
||||
|
||||
.PHONY: fmt
|
||||
fmt: ## Formatting source codes.
|
||||
@@ -20,6 +21,15 @@ lint: ## Run golint and go vet.
|
||||
test: ## Run the tests.
|
||||
@go test .
|
||||
|
||||
.PHONY: coverage
|
||||
cover: ## Run the tests.
|
||||
@go test -coverprofile=coverage.o
|
||||
@go tool cover -func=coverage.o
|
||||
|
||||
.PHONY: race-test
|
||||
race-test: ## Checking the race condition.
|
||||
@go test -race .
|
||||
|
||||
.PHONY: help
|
||||
help: ## Show help text
|
||||
@echo "Commands:"
|
||||
|
||||
28
vendor/github.com/c-bata/go-prompt/README.md
generated
vendored
28
vendor/github.com/c-bata/go-prompt/README.md
generated
vendored
@@ -1,7 +1,10 @@
|
||||
# go-prompt
|
||||
|
||||
[](https://goreportcard.com/report/github.com/c-bata/go-prompt)
|
||||

|
||||
|
||||
Library for building a powerful interactive prompt, inspired by [python-prompt-toolkit](https://github.com/jonathanslenders/python-prompt-toolkit).
|
||||
Easy building a multi-platform binary of the command line tools because written in Golang.
|
||||
More easy to build a multi-platform binary of the command line tools because written in Golang.
|
||||
|
||||
```go
|
||||
package main
|
||||
@@ -30,7 +33,11 @@ func main() {
|
||||
|
||||
#### Projects using go-prompt
|
||||
|
||||
* [kube-prompt : An interactive kubernetes client featuring auto-complete written in Go.](https://github.com/c-bata/kube-prompt)
|
||||
* [c-bata/kube-prompt : An interactive kubernetes client featuring auto-complete written in Go.](https://github.com/c-bata/kube-prompt)
|
||||
* [rancher/cli : The Rancher Command Line Interface (CLI)is a unified tool to manage your Rancher server](https://github.com/rancher/cli)
|
||||
* [kris-nova/kubicorn : Simple. Cloud Native. Kubernetes. Infrastructure.](https://github.com/kris-nova/kubicorn)
|
||||
* [cch123/asm-cli : Interactive shell of assembly language(X86/X64) based on unicorn and rasm2](https://github.com/cch123/asm-cli)
|
||||
* [ktr0731/evans : more expressive universal gRPC client](https://github.com/ktr0731/evans)
|
||||
* (If you create a CLI using go-prompt and want your own project to be listed here, Please submit a Github Issue.)
|
||||
|
||||
## Features
|
||||
@@ -43,7 +50,7 @@ func main() {
|
||||
|
||||
### Flexible options
|
||||
|
||||
go-prompt provides many options. All options are listed in [Developer Guide](./DEVELOPER_GUIDE.md).
|
||||
go-prompt provides many options. Please check [option section of GoDoc](https://godoc.org/github.com/c-bata/go-prompt#Option) for more details.
|
||||
|
||||
[](#flexible-options)
|
||||
|
||||
@@ -67,6 +74,7 @@ KeyBinding | Description
|
||||
<kbd>Ctrl + W</kbd> | Cut the Word before the cursor to the clipboard.
|
||||
<kbd>Ctrl + K</kbd> | Cut the Line after the cursor to the clipboard.
|
||||
<kbd>Ctrl + U</kbd> | Cut/delete the Line before the cursor to the clipboard.
|
||||
<kbd>Ctrl + L</kbd> | Clear the screen
|
||||
|
||||
### History
|
||||
|
||||
@@ -75,11 +83,21 @@ You can use up-arrow and down-arrow to walk through the history of commands exec
|
||||
[](#history)
|
||||
|
||||
|
||||
### Multiple platform support
|
||||
|
||||
We confirmed go-prompt works fine on following terminals:
|
||||
|
||||
* iTerm2 (macOS)
|
||||
* Terminal.app (macOS)
|
||||
* Command Prompt (Windows)
|
||||
* GNU Terminal (Ubuntu)
|
||||
|
||||
|
||||
## Links
|
||||
|
||||
* [Developer Guide](./DEVELOPER_GUIDE.md).
|
||||
* [Change Log](./CHANGELOG.md)
|
||||
* [GoDoc](http://godoc.org/github.com/c-bata/go-prompt).
|
||||
* [GoDoc](http://godoc.org/github.com/c-bata/go-prompt)
|
||||
* [gocover.io](https://gocover.io/github.com/c-bata/go-prompt)
|
||||
|
||||
## Author
|
||||
|
||||
|
||||
27
vendor/github.com/c-bata/go-prompt/bisect.go
generated
vendored
27
vendor/github.com/c-bata/go-prompt/bisect.go
generated
vendored
@@ -1,27 +0,0 @@
|
||||
package prompt
|
||||
|
||||
import "sort"
|
||||
|
||||
// BisectLeft to Locate the insertion point for v in a to maintain sorted order.
|
||||
func BisectLeft(a []int, v int) int {
|
||||
return bisectLeftRange(a, v, 0, len(a))
|
||||
}
|
||||
|
||||
func bisectLeftRange(a []int, v int, lo, hi int) int {
|
||||
s := a[lo:hi]
|
||||
return sort.Search(len(s), func(i int) bool {
|
||||
return s[i] >= v
|
||||
})
|
||||
}
|
||||
|
||||
// BisectRight to Locate the insertion point for v in a to maintain sorted order.
|
||||
func BisectRight(a []int, v int) int {
|
||||
return bisectRightRange(a, v, 0, len(a))
|
||||
}
|
||||
|
||||
func bisectRightRange(a []int, v int, lo, hi int) int {
|
||||
s := a[lo:hi]
|
||||
return sort.Search(len(s), func(i int) bool {
|
||||
return s[i] > v
|
||||
})
|
||||
}
|
||||
19
vendor/github.com/c-bata/go-prompt/completion.go
generated
vendored
19
vendor/github.com/c-bata/go-prompt/completion.go
generated
vendored
@@ -28,6 +28,8 @@ type CompletionManager struct {
|
||||
tmp []Suggest
|
||||
max uint16
|
||||
completer Completer
|
||||
|
||||
verticalScroll int
|
||||
}
|
||||
|
||||
// GetSelectedSuggestion returns the selected item.
|
||||
@@ -42,7 +44,7 @@ func (c *CompletionManager) GetSelectedSuggestion() (s Suggest, ok bool) {
|
||||
return c.tmp[c.selected], true
|
||||
}
|
||||
|
||||
// GetSelectedSuggestion returns the list of suggestion.
|
||||
// GetSuggestions returns the list of suggestion.
|
||||
func (c *CompletionManager) GetSuggestions() []Suggest {
|
||||
return c.tmp
|
||||
}
|
||||
@@ -50,6 +52,7 @@ func (c *CompletionManager) GetSuggestions() []Suggest {
|
||||
// Reset to select nothing.
|
||||
func (c *CompletionManager) Reset() {
|
||||
c.selected = -1
|
||||
c.verticalScroll = 0
|
||||
c.Update(*NewDocument())
|
||||
return
|
||||
}
|
||||
@@ -62,6 +65,9 @@ func (c *CompletionManager) Update(in Document) {
|
||||
|
||||
// Previous to select the previous suggestion item.
|
||||
func (c *CompletionManager) Previous() {
|
||||
if c.verticalScroll == c.selected && c.selected > 0 {
|
||||
c.verticalScroll--
|
||||
}
|
||||
c.selected--
|
||||
c.update()
|
||||
return
|
||||
@@ -69,6 +75,9 @@ func (c *CompletionManager) Previous() {
|
||||
|
||||
// Next to select the next suggestion item.
|
||||
func (c *CompletionManager) Next() {
|
||||
if c.verticalScroll+int(c.max)-1 == c.selected {
|
||||
c.verticalScroll++
|
||||
}
|
||||
c.selected++
|
||||
c.update()
|
||||
return
|
||||
@@ -84,10 +93,12 @@ func (c *CompletionManager) update() {
|
||||
if len(c.tmp) < max {
|
||||
max = len(c.tmp)
|
||||
}
|
||||
if c.selected >= max {
|
||||
|
||||
if c.selected >= len(c.tmp) {
|
||||
c.Reset()
|
||||
} else if c.selected < -1 {
|
||||
c.selected = max - 1
|
||||
c.selected = len(c.tmp) - 1
|
||||
c.verticalScroll = len(c.tmp) - max
|
||||
}
|
||||
}
|
||||
|
||||
@@ -160,5 +171,7 @@ func NewCompletionManager(completer Completer, max uint16) *CompletionManager {
|
||||
selected: -1,
|
||||
max: max,
|
||||
completer: completer,
|
||||
|
||||
verticalScroll: 0,
|
||||
}
|
||||
}
|
||||
|
||||
61
vendor/github.com/c-bata/go-prompt/console_interface.go
generated
vendored
61
vendor/github.com/c-bata/go-prompt/console_interface.go
generated
vendored
@@ -1,89 +1,140 @@
|
||||
package prompt
|
||||
|
||||
// WinSize represents the width and height of terminal.
|
||||
type WinSize struct {
|
||||
Row uint16
|
||||
Col uint16
|
||||
}
|
||||
|
||||
// Color represents color on terminal.
|
||||
type Color int
|
||||
|
||||
const (
|
||||
// DefaultColor represents a default color.
|
||||
DefaultColor Color = iota
|
||||
|
||||
// Low intensity
|
||||
|
||||
// Black represents a black.
|
||||
Black
|
||||
// DarkRed represents a dark red.
|
||||
DarkRed
|
||||
// DarkGreen represents a dark green.
|
||||
DarkGreen
|
||||
// Brown represents a brown.
|
||||
Brown
|
||||
// DarkBlue represents a dark blue.
|
||||
DarkBlue
|
||||
// Purple represents a purple.
|
||||
Purple
|
||||
// Cyan represents a cyan.
|
||||
Cyan
|
||||
// LightGray represents a light gray.
|
||||
LightGray
|
||||
|
||||
// High intensity
|
||||
|
||||
// DarkGray represents a dark gray.
|
||||
DarkGray
|
||||
// Red represents a red.
|
||||
Red
|
||||
// Green represents a green.
|
||||
Green
|
||||
// Yellow represents a yellow.
|
||||
Yellow
|
||||
// Blue represents a blue.
|
||||
Blue
|
||||
// Fuchsia represents a fuchsia.
|
||||
Fuchsia
|
||||
// Turquoise represents a turquoise.
|
||||
Turquoise
|
||||
// White represents a white.
|
||||
White
|
||||
)
|
||||
|
||||
// ConsoleParser is an interface to abstract input layer.
|
||||
type ConsoleParser interface {
|
||||
// Setup
|
||||
// Setup should be called before starting input
|
||||
Setup() error
|
||||
// TearDown
|
||||
// TearDown should be called after stopping input
|
||||
TearDown() error
|
||||
// GetSCIICode returns ASCIICode correspond to input byte codes.
|
||||
// GetKey returns Key correspond to input byte codes.
|
||||
GetKey(b []byte) Key
|
||||
// GetWinSize returns winsize struct which is the response of ioctl(2).
|
||||
// GetWinSize returns WinSize object to represent width and height of terminal.
|
||||
GetWinSize() *WinSize
|
||||
// Read returns byte array.
|
||||
Read() ([]byte, error)
|
||||
}
|
||||
|
||||
// ConsoleWriter is an interface to abstract output layer.
|
||||
type ConsoleWriter interface {
|
||||
/* Write */
|
||||
|
||||
// WriteRaw to write raw byte array.
|
||||
WriteRaw(data []byte)
|
||||
// Write to write safety byte array by removing control sequences.
|
||||
Write(data []byte)
|
||||
WriteStr(data string)
|
||||
// WriteStr to write raw string.
|
||||
WriteRawStr(data string)
|
||||
// WriteStr to write safety string by removing control sequences.
|
||||
WriteStr(data string)
|
||||
// Flush to flush buffer.
|
||||
Flush() error
|
||||
|
||||
/* Erasing */
|
||||
|
||||
// EraseScreen erases the screen with the background colour and moves the cursor to home.
|
||||
EraseScreen()
|
||||
// EraseUp erases the screen from the current line up to the top of the screen.
|
||||
EraseUp()
|
||||
// EraseDown erases the screen from the current line down to the bottom of the screen.
|
||||
EraseDown()
|
||||
// EraseStartOfLine erases from the current cursor position to the start of the current line.
|
||||
EraseStartOfLine()
|
||||
// EraseEndOfLine erases from the current cursor position to the end of the current line.
|
||||
EraseEndOfLine()
|
||||
// EraseLine erases the entire current line.
|
||||
EraseLine()
|
||||
|
||||
/* Cursor */
|
||||
|
||||
// ShowCursor stops blinking cursor and show.
|
||||
ShowCursor()
|
||||
// HideCursor hides cursor.
|
||||
HideCursor()
|
||||
// CursorGoTo sets the cursor position where subsequent text will begin.
|
||||
CursorGoTo(row, col int)
|
||||
// CursorUp moves the cursor up by 'n' rows; the default count is 1.
|
||||
CursorUp(n int)
|
||||
// CursorDown moves the cursor down by 'n' rows; the default count is 1.
|
||||
CursorDown(n int)
|
||||
// CursorForward moves the cursor forward by 'n' columns; the default count is 1.
|
||||
CursorForward(n int)
|
||||
// CursorBackward moves the cursor backward by 'n' columns; the default count is 1.
|
||||
CursorBackward(n int)
|
||||
// AskForCPR asks for a cursor position report (CPR).
|
||||
AskForCPR()
|
||||
// SaveCursor saves current cursor position.
|
||||
SaveCursor()
|
||||
// UnSaveCursor restores cursor position after a Save Cursor.
|
||||
UnSaveCursor()
|
||||
|
||||
/* Scrolling */
|
||||
|
||||
// ScrollDown scrolls display down one line.
|
||||
ScrollDown()
|
||||
// ScrollUp scroll display up one line.
|
||||
ScrollUp()
|
||||
|
||||
/* Title */
|
||||
|
||||
// SetTitle sets a title of terminal window.
|
||||
SetTitle(title string)
|
||||
// ClearTitle clears a title of terminal window.
|
||||
ClearTitle()
|
||||
|
||||
/* Font */
|
||||
|
||||
// SetColor sets text and background colors. and specify whether text is bold.
|
||||
SetColor(fg, bg Color, bold bool)
|
||||
}
|
||||
|
||||
15
vendor/github.com/c-bata/go-prompt/document.go
generated
vendored
15
vendor/github.com/c-bata/go-prompt/document.go
generated
vendored
@@ -1,6 +1,7 @@
|
||||
package prompt
|
||||
|
||||
import (
|
||||
"sort"
|
||||
"strings"
|
||||
"unicode/utf8"
|
||||
)
|
||||
@@ -140,7 +141,7 @@ func (d *Document) lineStartIndexes() []int {
|
||||
// the first character on that line.
|
||||
func (d *Document) findLineStartIndex(index int) (pos int, lineStartIndex int) {
|
||||
indexes := d.lineStartIndexes()
|
||||
pos = BisectRight(indexes, index) - 1
|
||||
pos = bisectRight(indexes, index) - 1
|
||||
lineStartIndex = indexes[pos]
|
||||
return
|
||||
}
|
||||
@@ -280,3 +281,15 @@ func (d *Document) leadingWhitespaceInCurrentLine() (margin string) {
|
||||
margin = d.CurrentLine()[:len(d.CurrentLine())-len(trimmed)]
|
||||
return
|
||||
}
|
||||
|
||||
// bisectRight to Locate the insertion point for v in a to maintain sorted order.
|
||||
func bisectRight(a []int, v int) int {
|
||||
return bisectRightRange(a, v, 0, len(a))
|
||||
}
|
||||
|
||||
func bisectRightRange(a []int, v int, lo, hi int) int {
|
||||
s := a[lo:hi]
|
||||
return sort.Search(len(s), func(i int) bool {
|
||||
return s[i] > v
|
||||
})
|
||||
}
|
||||
|
||||
7
vendor/github.com/c-bata/go-prompt/emacs.go
generated
vendored
7
vendor/github.com/c-bata/go-prompt/emacs.go
generated
vendored
@@ -1,9 +1,5 @@
|
||||
package prompt
|
||||
|
||||
import (
|
||||
"syscall"
|
||||
)
|
||||
|
||||
/*
|
||||
|
||||
========
|
||||
@@ -114,9 +110,10 @@ var emacsKeyBindings = []KeyBind{
|
||||
{
|
||||
Key: ControlL,
|
||||
Fn: func(buf *Buffer) {
|
||||
out := &VT100Writer{fd: syscall.Stdout}
|
||||
out := NewStandardOutputWriter()
|
||||
out.EraseScreen()
|
||||
out.CursorGoTo(0, 0)
|
||||
out.Flush()
|
||||
},
|
||||
},
|
||||
}
|
||||
|
||||
19
vendor/github.com/c-bata/go-prompt/history.go
generated
vendored
19
vendor/github.com/c-bata/go-prompt/history.go
generated
vendored
@@ -1,26 +1,31 @@
|
||||
package prompt
|
||||
|
||||
import "log"
|
||||
|
||||
// History stores the texts that are entered.
|
||||
type History struct {
|
||||
histories []string
|
||||
tmp []string
|
||||
selected int
|
||||
}
|
||||
|
||||
// Add to add text in history.
|
||||
func (h *History) Add(input string) {
|
||||
h.histories = append(h.histories, input)
|
||||
h.Clear()
|
||||
}
|
||||
|
||||
// Clear to clear the history.
|
||||
func (h *History) Clear() {
|
||||
copy(h.tmp, h.histories)
|
||||
h.tmp = make([]string, len(h.histories))
|
||||
for i := range h.histories {
|
||||
h.tmp[i] = h.histories[i]
|
||||
}
|
||||
h.tmp = append(h.tmp, "")
|
||||
h.selected = len(h.tmp) - 1
|
||||
}
|
||||
|
||||
// Older saves a buffer of current line and get a buffer of previous line by up-arrow.
|
||||
// The changes of line buffers are stored until new history is created.
|
||||
func (h *History) Older(buf *Buffer) (new *Buffer, changed bool) {
|
||||
log.Printf("[DEBUG] Before %#v\n", h)
|
||||
if len(h.tmp) == 1 || h.selected == 0 {
|
||||
return buf, false
|
||||
}
|
||||
@@ -29,12 +34,12 @@ func (h *History) Older(buf *Buffer) (new *Buffer, changed bool) {
|
||||
h.selected--
|
||||
new = NewBuffer()
|
||||
new.InsertText(h.tmp[h.selected], false, true)
|
||||
log.Printf("[DEBUG] After %#v\n", h)
|
||||
return new, true
|
||||
}
|
||||
|
||||
// Newer saves a buffer of current line and get a buffer of next line by up-arrow.
|
||||
// The changes of line buffers are stored until new history is created.
|
||||
func (h *History) Newer(buf *Buffer) (new *Buffer, changed bool) {
|
||||
log.Printf("[DEBUG] Before %#v\n", h)
|
||||
if h.selected >= len(h.tmp)-1 {
|
||||
return buf, false
|
||||
}
|
||||
@@ -43,10 +48,10 @@ func (h *History) Newer(buf *Buffer) (new *Buffer, changed bool) {
|
||||
h.selected++
|
||||
new = NewBuffer()
|
||||
new.InsertText(h.tmp[h.selected], false, true)
|
||||
log.Printf("[DEBUG] After %#v\n", h)
|
||||
return new, true
|
||||
}
|
||||
|
||||
// NewHistory returns new history object.
|
||||
func NewHistory() *History {
|
||||
return &History{
|
||||
histories: []string{},
|
||||
|
||||
3
vendor/github.com/c-bata/go-prompt/key.go
generated
vendored
3
vendor/github.com/c-bata/go-prompt/key.go
generated
vendored
@@ -1,3 +1,6 @@
|
||||
// Code generated "This is a fake comment to avoid golint errors"; DO NOT EDIT.
|
||||
// FIXME: This is a little bit stupid, but there are many public constants which is no value for writing godoc comment.
|
||||
|
||||
package prompt
|
||||
|
||||
// Key is the type express the key inserted from user.
|
||||
|
||||
7
vendor/github.com/c-bata/go-prompt/key_bind.go
generated
vendored
7
vendor/github.com/c-bata/go-prompt/key_bind.go
generated
vendored
@@ -1,17 +1,22 @@
|
||||
package prompt
|
||||
|
||||
// KeyBindFunc receives buffer and processed it.
|
||||
type KeyBindFunc func(*Buffer)
|
||||
|
||||
// KeyBind represents which key should do what operation.
|
||||
type KeyBind struct {
|
||||
Key Key
|
||||
Fn KeyBindFunc
|
||||
}
|
||||
|
||||
// KeyBindMode to switch a key binding flexibly.
|
||||
type KeyBindMode string
|
||||
|
||||
const (
|
||||
// CommonKeyBind is a mode without any keyboard shortcut
|
||||
CommonKeyBind KeyBindMode = "common"
|
||||
EmacsKeyBind KeyBindMode = "emacs"
|
||||
// EmacsKeyBind is a mode to use emacs-like keyboard shortcut
|
||||
EmacsKeyBind KeyBindMode = "emacs"
|
||||
)
|
||||
|
||||
var commonKeyBindings = []KeyBind{
|
||||
|
||||
10
vendor/github.com/c-bata/go-prompt/key_string.go
generated
vendored
10
vendor/github.com/c-bata/go-prompt/key_string.go
generated
vendored
@@ -1,16 +1,16 @@
|
||||
// Code generated by "stringer -type Key key.go"; DO NOT EDIT
|
||||
// Code generated by "stringer -type=Key"; DO NOT EDIT.
|
||||
|
||||
package prompt
|
||||
|
||||
import "fmt"
|
||||
import "strconv"
|
||||
|
||||
const _Key_name = "EscapeControlAControlBControlCControlDControlEControlFControlGControlHControlIControlJControlKControlLControlMControlNControlOControlPControlQControlRControlSControlTControlUControlVControlWControlXControlYControlZControlSpaceControlBackslashControlSquareCloseControlCircumflexControlUnderscoreControlLeftControlRightControlUpControlDownUpDownRightLeftShiftLeftShiftUpShiftDownShiftRightHomeEndDeleteShiftDeleteControlDeletePageUpPageDownBackTabInsertBackspaceTabEnterF1F2F3F4F5F6F7F8F9F10F11F12F13F14F15F16F17F18F19F20F21F22F23F24AnyCPRResponseVt100MouseEventWindowsMouseEventBracketedPasteIgnore"
|
||||
const _Key_name = "EscapeControlAControlBControlCControlDControlEControlFControlGControlHControlIControlJControlKControlLControlMControlNControlOControlPControlQControlRControlSControlTControlUControlVControlWControlXControlYControlZControlSpaceControlBackslashControlSquareCloseControlCircumflexControlUnderscoreControlLeftControlRightControlUpControlDownUpDownRightLeftShiftLeftShiftUpShiftDownShiftRightHomeEndDeleteShiftDeleteControlDeletePageUpPageDownBackTabInsertBackspaceTabEnterF1F2F3F4F5F6F7F8F9F10F11F12F13F14F15F16F17F18F19F20F21F22F23F24AnyCPRResponseVt100MouseEventWindowsMouseEventBracketedPasteIgnoreNotDefined"
|
||||
|
||||
var _Key_index = [...]uint16{0, 6, 14, 22, 30, 38, 46, 54, 62, 70, 78, 86, 94, 102, 110, 118, 126, 134, 142, 150, 158, 166, 174, 182, 190, 198, 206, 214, 226, 242, 260, 277, 294, 305, 317, 326, 337, 339, 343, 348, 352, 361, 368, 377, 387, 391, 394, 400, 411, 424, 430, 438, 445, 451, 460, 463, 468, 470, 472, 474, 476, 478, 480, 482, 484, 486, 489, 492, 495, 498, 501, 504, 507, 510, 513, 516, 519, 522, 525, 528, 531, 534, 545, 560, 577, 591, 597}
|
||||
var _Key_index = [...]uint16{0, 6, 14, 22, 30, 38, 46, 54, 62, 70, 78, 86, 94, 102, 110, 118, 126, 134, 142, 150, 158, 166, 174, 182, 190, 198, 206, 214, 226, 242, 260, 277, 294, 305, 317, 326, 337, 339, 343, 348, 352, 361, 368, 377, 387, 391, 394, 400, 411, 424, 430, 438, 445, 451, 460, 463, 468, 470, 472, 474, 476, 478, 480, 482, 484, 486, 489, 492, 495, 498, 501, 504, 507, 510, 513, 516, 519, 522, 525, 528, 531, 534, 545, 560, 577, 591, 597, 607}
|
||||
|
||||
func (i Key) String() string {
|
||||
if i < 0 || i >= Key(len(_Key_index)-1) {
|
||||
return fmt.Sprintf("Key(%d)", i)
|
||||
return "Key(" + strconv.FormatInt(int64(i), 10) + ")"
|
||||
}
|
||||
return _Key_name[_Key_index[i]:_Key_index[i+1]]
|
||||
}
|
||||
|
||||
49
vendor/github.com/c-bata/go-prompt/option.go
generated
vendored
49
vendor/github.com/c-bata/go-prompt/option.go
generated
vendored
@@ -1,7 +1,5 @@
|
||||
package prompt
|
||||
|
||||
import "syscall"
|
||||
|
||||
// Option is the type to replace default parameters.
|
||||
// prompt.New accepts any number of options (this is functional option pattern).
|
||||
type Option func(prompt *Prompt) error
|
||||
@@ -14,7 +12,7 @@ func OptionParser(x ConsoleParser) Option {
|
||||
}
|
||||
}
|
||||
|
||||
// OptionWriter to set a custom ConsoleWriter object. An argument should implement ConsoleWriter interace.
|
||||
// OptionWriter to set a custom ConsoleWriter object. An argument should implement ConsoleWriter interface.
|
||||
func OptionWriter(x ConsoleWriter) Option {
|
||||
return func(p *Prompt) error {
|
||||
p.renderer.out = x
|
||||
@@ -38,6 +36,15 @@ func OptionPrefix(x string) Option {
|
||||
}
|
||||
}
|
||||
|
||||
// OptionLivePrefix to change the prefix dynamically by callback function
|
||||
func OptionLivePrefix(f func() (prefix string, useLivePrefix bool)) Option {
|
||||
return func(p *Prompt) error {
|
||||
p.renderer.livePrefixCallback = f
|
||||
return nil
|
||||
}
|
||||
}
|
||||
|
||||
// OptionPrefixTextColor change a text color of prefix string
|
||||
func OptionPrefixTextColor(x Color) Option {
|
||||
return func(p *Prompt) error {
|
||||
p.renderer.prefixTextColor = x
|
||||
@@ -45,6 +52,7 @@ func OptionPrefixTextColor(x Color) Option {
|
||||
}
|
||||
}
|
||||
|
||||
// OptionPrefixBackgroundColor to change a background color of prefix string
|
||||
func OptionPrefixBackgroundColor(x Color) Option {
|
||||
return func(p *Prompt) error {
|
||||
p.renderer.prefixBGColor = x
|
||||
@@ -52,6 +60,7 @@ func OptionPrefixBackgroundColor(x Color) Option {
|
||||
}
|
||||
}
|
||||
|
||||
// OptionInputTextColor to change a color of text which is input by user
|
||||
func OptionInputTextColor(x Color) Option {
|
||||
return func(p *Prompt) error {
|
||||
p.renderer.inputTextColor = x
|
||||
@@ -59,6 +68,7 @@ func OptionInputTextColor(x Color) Option {
|
||||
}
|
||||
}
|
||||
|
||||
// OptionInputBGColor to change a color of background which is input by user
|
||||
func OptionInputBGColor(x Color) Option {
|
||||
return func(p *Prompt) error {
|
||||
p.renderer.inputBGColor = x
|
||||
@@ -66,6 +76,7 @@ func OptionInputBGColor(x Color) Option {
|
||||
}
|
||||
}
|
||||
|
||||
// OptionPreviewSuggestionTextColor to change a text color which is completed
|
||||
func OptionPreviewSuggestionTextColor(x Color) Option {
|
||||
return func(p *Prompt) error {
|
||||
p.renderer.previewSuggestionTextColor = x
|
||||
@@ -73,6 +84,7 @@ func OptionPreviewSuggestionTextColor(x Color) Option {
|
||||
}
|
||||
}
|
||||
|
||||
// OptionPreviewSuggestionBGColor to change a background color which is completed
|
||||
func OptionPreviewSuggestionBGColor(x Color) Option {
|
||||
return func(p *Prompt) error {
|
||||
p.renderer.previewSuggestionBGColor = x
|
||||
@@ -80,6 +92,7 @@ func OptionPreviewSuggestionBGColor(x Color) Option {
|
||||
}
|
||||
}
|
||||
|
||||
// OptionSuggestionTextColor to change a text color in drop down suggestions.
|
||||
func OptionSuggestionTextColor(x Color) Option {
|
||||
return func(p *Prompt) error {
|
||||
p.renderer.suggestionTextColor = x
|
||||
@@ -87,6 +100,7 @@ func OptionSuggestionTextColor(x Color) Option {
|
||||
}
|
||||
}
|
||||
|
||||
// OptionSuggestionBGColor change a background color in drop down suggestions.
|
||||
func OptionSuggestionBGColor(x Color) Option {
|
||||
return func(p *Prompt) error {
|
||||
p.renderer.suggestionBGColor = x
|
||||
@@ -94,6 +108,7 @@ func OptionSuggestionBGColor(x Color) Option {
|
||||
}
|
||||
}
|
||||
|
||||
// OptionSelectedSuggestionTextColor to change a text color for completed text which is selected inside suggestions drop down box.
|
||||
func OptionSelectedSuggestionTextColor(x Color) Option {
|
||||
return func(p *Prompt) error {
|
||||
p.renderer.selectedSuggestionTextColor = x
|
||||
@@ -101,6 +116,7 @@ func OptionSelectedSuggestionTextColor(x Color) Option {
|
||||
}
|
||||
}
|
||||
|
||||
// OptionSelectedSuggestionBGColor to change a background color for completed text which is selected inside suggestions drop down box.
|
||||
func OptionSelectedSuggestionBGColor(x Color) Option {
|
||||
return func(p *Prompt) error {
|
||||
p.renderer.selectedSuggestionBGColor = x
|
||||
@@ -108,6 +124,7 @@ func OptionSelectedSuggestionBGColor(x Color) Option {
|
||||
}
|
||||
}
|
||||
|
||||
// OptionDescriptionTextColor to change a background color of description text in drop down suggestions.
|
||||
func OptionDescriptionTextColor(x Color) Option {
|
||||
return func(p *Prompt) error {
|
||||
p.renderer.descriptionTextColor = x
|
||||
@@ -115,6 +132,7 @@ func OptionDescriptionTextColor(x Color) Option {
|
||||
}
|
||||
}
|
||||
|
||||
// OptionDescriptionBGColor to change a background color of description text in drop down suggestions.
|
||||
func OptionDescriptionBGColor(x Color) Option {
|
||||
return func(p *Prompt) error {
|
||||
p.renderer.descriptionBGColor = x
|
||||
@@ -122,6 +140,7 @@ func OptionDescriptionBGColor(x Color) Option {
|
||||
}
|
||||
}
|
||||
|
||||
// OptionSelectedDescriptionTextColor to change a text color of description which is selected inside suggestions drop down box.
|
||||
func OptionSelectedDescriptionTextColor(x Color) Option {
|
||||
return func(p *Prompt) error {
|
||||
p.renderer.selectedDescriptionTextColor = x
|
||||
@@ -129,6 +148,7 @@ func OptionSelectedDescriptionTextColor(x Color) Option {
|
||||
}
|
||||
}
|
||||
|
||||
// OptionSelectedDescriptionBGColor to change a background color of description which is selected inside suggestions drop down box.
|
||||
func OptionSelectedDescriptionBGColor(x Color) Option {
|
||||
return func(p *Prompt) error {
|
||||
p.renderer.selectedDescriptionBGColor = x
|
||||
@@ -136,6 +156,22 @@ func OptionSelectedDescriptionBGColor(x Color) Option {
|
||||
}
|
||||
}
|
||||
|
||||
// OptionScrollbarThumbColor to change a thumb color on scrollbar.
|
||||
func OptionScrollbarThumbColor(x Color) Option {
|
||||
return func(p *Prompt) error {
|
||||
p.renderer.scrollbarThumbColor = x
|
||||
return nil
|
||||
}
|
||||
}
|
||||
|
||||
// OptionScrollbarBGColor to change a background color of scrollbar.
|
||||
func OptionScrollbarBGColor(x Color) Option {
|
||||
return func(p *Prompt) error {
|
||||
p.renderer.scrollbarBGColor = x
|
||||
return nil
|
||||
}
|
||||
}
|
||||
|
||||
// OptionMaxSuggestion specify the max number of displayed suggestions.
|
||||
func OptionMaxSuggestion(x uint16) Option {
|
||||
return func(p *Prompt) error {
|
||||
@@ -176,10 +212,11 @@ func OptionAddKeyBind(b ...KeyBind) Option {
|
||||
// New returns a Prompt with powerful auto-completion.
|
||||
func New(executor Executor, completer Completer, opts ...Option) *Prompt {
|
||||
pt := &Prompt{
|
||||
in: &VT100Parser{fd: syscall.Stdin},
|
||||
in: NewStandardInputParser(),
|
||||
renderer: &Render{
|
||||
prefix: "> ",
|
||||
out: &VT100Writer{fd: syscall.Stdout},
|
||||
out: NewStandardOutputWriter(),
|
||||
livePrefixCallback: func() (string, bool) { return "", false },
|
||||
prefixTextColor: Blue,
|
||||
prefixBGColor: DefaultColor,
|
||||
inputTextColor: DefaultColor,
|
||||
@@ -194,6 +231,8 @@ func New(executor Executor, completer Completer, opts ...Option) *Prompt {
|
||||
descriptionBGColor: Turquoise,
|
||||
selectedDescriptionTextColor: White,
|
||||
selectedDescriptionBGColor: Cyan,
|
||||
scrollbarThumbColor: DarkGray,
|
||||
scrollbarBGColor: Cyan,
|
||||
},
|
||||
buf: NewBuffer(),
|
||||
executor: executor,
|
||||
|
||||
265
vendor/github.com/c-bata/go-prompt/posix_input.go
generated
vendored
Normal file
265
vendor/github.com/c-bata/go-prompt/posix_input.go
generated
vendored
Normal file
@@ -0,0 +1,265 @@
|
||||
// +build !windows
|
||||
|
||||
package prompt
|
||||
|
||||
import (
|
||||
"bytes"
|
||||
"log"
|
||||
"syscall"
|
||||
"unsafe"
|
||||
|
||||
"github.com/pkg/term/termios"
|
||||
)
|
||||
|
||||
const maxReadBytes = 1024
|
||||
|
||||
// PosixParser is a ConsoleParser implementation for POSIX environment.
|
||||
type PosixParser struct {
|
||||
fd int
|
||||
origTermios syscall.Termios
|
||||
}
|
||||
|
||||
// Setup should be called before starting input
|
||||
func (t *PosixParser) Setup() error {
|
||||
// Set NonBlocking mode because if syscall.Read block this goroutine, it cannot receive data from stopCh.
|
||||
if err := syscall.SetNonblock(t.fd, true); err != nil {
|
||||
log.Println("[ERROR] Cannot set non blocking mode.")
|
||||
return err
|
||||
}
|
||||
if err := t.setRawMode(); err != nil {
|
||||
log.Println("[ERROR] Cannot set raw mode.")
|
||||
return err
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
// TearDown should be called after stopping input
|
||||
func (t *PosixParser) TearDown() error {
|
||||
if err := syscall.SetNonblock(t.fd, false); err != nil {
|
||||
log.Println("[ERROR] Cannot set blocking mode.")
|
||||
return err
|
||||
}
|
||||
if err := t.resetRawMode(); err != nil {
|
||||
log.Println("[ERROR] Cannot reset from raw mode.")
|
||||
return err
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
// Read returns byte array.
|
||||
func (t *PosixParser) Read() ([]byte, error) {
|
||||
buf := make([]byte, maxReadBytes)
|
||||
n, err := syscall.Read(syscall.Stdin, buf)
|
||||
if err != nil {
|
||||
return []byte{}, err
|
||||
}
|
||||
return buf[:n], nil
|
||||
}
|
||||
|
||||
func (t *PosixParser) setRawMode() error {
|
||||
x := t.origTermios.Lflag
|
||||
if x &^= syscall.ICANON; x != 0 && x == t.origTermios.Lflag {
|
||||
// fd is already raw mode
|
||||
return nil
|
||||
}
|
||||
var n syscall.Termios
|
||||
if err := termios.Tcgetattr(uintptr(t.fd), &t.origTermios); err != nil {
|
||||
return err
|
||||
}
|
||||
n = t.origTermios
|
||||
// "&^=" used like: https://play.golang.org/p/8eJw3JxS4O
|
||||
n.Lflag &^= syscall.ECHO | syscall.ICANON | syscall.IEXTEN | syscall.ISIG
|
||||
n.Cc[syscall.VMIN] = 1
|
||||
n.Cc[syscall.VTIME] = 0
|
||||
termios.Tcsetattr(uintptr(t.fd), termios.TCSANOW, &n)
|
||||
return nil
|
||||
}
|
||||
|
||||
func (t *PosixParser) resetRawMode() error {
|
||||
if t.origTermios.Lflag == 0 {
|
||||
return nil
|
||||
}
|
||||
return termios.Tcsetattr(uintptr(t.fd), termios.TCSANOW, &t.origTermios)
|
||||
}
|
||||
|
||||
// GetKey returns Key correspond to input byte codes.
|
||||
func (t *PosixParser) GetKey(b []byte) Key {
|
||||
for _, k := range asciiSequences {
|
||||
if bytes.Equal(k.ASCIICode, b) {
|
||||
return k.Key
|
||||
}
|
||||
}
|
||||
return NotDefined
|
||||
}
|
||||
|
||||
// winsize is winsize struct got from the ioctl(2) system call.
|
||||
type ioctlWinsize struct {
|
||||
Row uint16
|
||||
Col uint16
|
||||
X uint16 // pixel value
|
||||
Y uint16 // pixel value
|
||||
}
|
||||
|
||||
// GetWinSize returns WinSize object to represent width and height of terminal.
|
||||
func (t *PosixParser) GetWinSize() *WinSize {
|
||||
ws := &ioctlWinsize{}
|
||||
retCode, _, errno := syscall.Syscall(
|
||||
syscall.SYS_IOCTL,
|
||||
uintptr(t.fd),
|
||||
uintptr(syscall.TIOCGWINSZ),
|
||||
uintptr(unsafe.Pointer(ws)))
|
||||
|
||||
if int(retCode) == -1 {
|
||||
panic(errno)
|
||||
}
|
||||
return &WinSize{
|
||||
Row: ws.Row,
|
||||
Col: ws.Col,
|
||||
}
|
||||
}
|
||||
|
||||
var asciiSequences = []*ASCIICode{
|
||||
{Key: Escape, ASCIICode: []byte{0x1b}},
|
||||
|
||||
{Key: ControlSpace, ASCIICode: []byte{0x00}},
|
||||
{Key: ControlA, ASCIICode: []byte{0x1}},
|
||||
{Key: ControlB, ASCIICode: []byte{0x2}},
|
||||
{Key: ControlC, ASCIICode: []byte{0x3}},
|
||||
{Key: ControlD, ASCIICode: []byte{0x4}},
|
||||
{Key: ControlE, ASCIICode: []byte{0x5}},
|
||||
{Key: ControlF, ASCIICode: []byte{0x6}},
|
||||
{Key: ControlG, ASCIICode: []byte{0x7}},
|
||||
{Key: ControlH, ASCIICode: []byte{0x8}},
|
||||
//{Key: ControlI, ASCIICode: []byte{0x9}},
|
||||
//{Key: ControlJ, ASCIICode: []byte{0xa}},
|
||||
{Key: ControlK, ASCIICode: []byte{0xb}},
|
||||
{Key: ControlL, ASCIICode: []byte{0xc}},
|
||||
{Key: ControlM, ASCIICode: []byte{0xd}},
|
||||
{Key: ControlN, ASCIICode: []byte{0xe}},
|
||||
{Key: ControlO, ASCIICode: []byte{0xf}},
|
||||
{Key: ControlP, ASCIICode: []byte{0x10}},
|
||||
{Key: ControlQ, ASCIICode: []byte{0x11}},
|
||||
{Key: ControlR, ASCIICode: []byte{0x12}},
|
||||
{Key: ControlS, ASCIICode: []byte{0x13}},
|
||||
{Key: ControlT, ASCIICode: []byte{0x14}},
|
||||
{Key: ControlU, ASCIICode: []byte{0x15}},
|
||||
{Key: ControlV, ASCIICode: []byte{0x16}},
|
||||
{Key: ControlW, ASCIICode: []byte{0x17}},
|
||||
{Key: ControlX, ASCIICode: []byte{0x18}},
|
||||
{Key: ControlY, ASCIICode: []byte{0x19}},
|
||||
{Key: ControlZ, ASCIICode: []byte{0x1a}},
|
||||
|
||||
{Key: ControlBackslash, ASCIICode: []byte{0x1c}},
|
||||
{Key: ControlSquareClose, ASCIICode: []byte{0x1d}},
|
||||
{Key: ControlCircumflex, ASCIICode: []byte{0x1e}},
|
||||
{Key: ControlUnderscore, ASCIICode: []byte{0x1f}},
|
||||
{Key: Backspace, ASCIICode: []byte{0x7f}},
|
||||
|
||||
{Key: Up, ASCIICode: []byte{0x1b, 0x5b, 0x41}},
|
||||
{Key: Down, ASCIICode: []byte{0x1b, 0x5b, 0x42}},
|
||||
{Key: Right, ASCIICode: []byte{0x1b, 0x5b, 0x43}},
|
||||
{Key: Left, ASCIICode: []byte{0x1b, 0x5b, 0x44}},
|
||||
{Key: Home, ASCIICode: []byte{0x1b, 0x5b, 0x48}},
|
||||
{Key: Home, ASCIICode: []byte{0x1b, 0x30, 0x48}},
|
||||
{Key: End, ASCIICode: []byte{0x1b, 0x5b, 0x46}},
|
||||
{Key: End, ASCIICode: []byte{0x1b, 0x30, 0x46}},
|
||||
|
||||
{Key: Enter, ASCIICode: []byte{0xa}},
|
||||
{Key: Delete, ASCIICode: []byte{0x1b, 0x5b, 0x33, 0x7e}},
|
||||
{Key: ShiftDelete, ASCIICode: []byte{0x1b, 0x5b, 0x33, 0x3b, 0x32, 0x7e}},
|
||||
{Key: ControlDelete, ASCIICode: []byte{0x1b, 0x5b, 0x33, 0x3b, 0x35, 0x7e}},
|
||||
{Key: Home, ASCIICode: []byte{0x1b, 0x5b, 0x31, 0x7e}},
|
||||
{Key: End, ASCIICode: []byte{0x1b, 0x5b, 0x34, 0x7e}},
|
||||
{Key: PageUp, ASCIICode: []byte{0x1b, 0x5b, 0x35, 0x7e}},
|
||||
{Key: PageDown, ASCIICode: []byte{0x1b, 0x5b, 0x36, 0x7e}},
|
||||
{Key: Home, ASCIICode: []byte{0x1b, 0x5b, 0x37, 0x7e}},
|
||||
{Key: End, ASCIICode: []byte{0x1b, 0x5b, 0x38, 0x7e}},
|
||||
{Key: Tab, ASCIICode: []byte{0x9}},
|
||||
{Key: BackTab, ASCIICode: []byte{0x1b, 0x5b, 0x5a}},
|
||||
{Key: Insert, ASCIICode: []byte{0x1b, 0x5b, 0x32, 0x7e}},
|
||||
|
||||
{Key: F1, ASCIICode: []byte{0x1b, 0x4f, 0x50}},
|
||||
{Key: F2, ASCIICode: []byte{0x1b, 0x4f, 0x51}},
|
||||
{Key: F3, ASCIICode: []byte{0x1b, 0x4f, 0x52}},
|
||||
{Key: F4, ASCIICode: []byte{0x1b, 0x4f, 0x53}},
|
||||
|
||||
{Key: F1, ASCIICode: []byte{0x1b, 0x4f, 0x50, 0x41}}, // Linux console
|
||||
{Key: F2, ASCIICode: []byte{0x1b, 0x5b, 0x5b, 0x42}}, // Linux console
|
||||
{Key: F3, ASCIICode: []byte{0x1b, 0x5b, 0x5b, 0x43}}, // Linux console
|
||||
{Key: F4, ASCIICode: []byte{0x1b, 0x5b, 0x5b, 0x44}}, // Linux console
|
||||
{Key: F5, ASCIICode: []byte{0x1b, 0x5b, 0x5b, 0x45}}, // Linux console
|
||||
|
||||
{Key: F1, ASCIICode: []byte{0x1b, 0x5b, 0x11, 0x7e}}, // rxvt-unicode
|
||||
{Key: F2, ASCIICode: []byte{0x1b, 0x5b, 0x12, 0x7e}}, // rxvt-unicode
|
||||
{Key: F3, ASCIICode: []byte{0x1b, 0x5b, 0x13, 0x7e}}, // rxvt-unicode
|
||||
{Key: F4, ASCIICode: []byte{0x1b, 0x5b, 0x14, 0x7e}}, // rxvt-unicode
|
||||
|
||||
{Key: F5, ASCIICode: []byte{0x1b, 0x5b, 0x31, 0x35, 0x7e}},
|
||||
{Key: F6, ASCIICode: []byte{0x1b, 0x5b, 0x31, 0x37, 0x7e}},
|
||||
{Key: F7, ASCIICode: []byte{0x1b, 0x5b, 0x31, 0x38, 0x7e}},
|
||||
{Key: F8, ASCIICode: []byte{0x1b, 0x5b, 0x31, 0x39, 0x7e}},
|
||||
{Key: F9, ASCIICode: []byte{0x1b, 0x5b, 0x32, 0x30, 0x7e}},
|
||||
{Key: F10, ASCIICode: []byte{0x1b, 0x5b, 0x32, 0x31, 0x7e}},
|
||||
{Key: F11, ASCIICode: []byte{0x1b, 0x5b, 0x32, 0x32, 0x7e}},
|
||||
{Key: F12, ASCIICode: []byte{0x1b, 0x5b, 0x32, 0x34, 0x7e, 0x8}},
|
||||
{Key: F13, ASCIICode: []byte{0x1b, 0x5b, 0x25, 0x7e}},
|
||||
{Key: F14, ASCIICode: []byte{0x1b, 0x5b, 0x26, 0x7e}},
|
||||
{Key: F15, ASCIICode: []byte{0x1b, 0x5b, 0x28, 0x7e}},
|
||||
{Key: F16, ASCIICode: []byte{0x1b, 0x5b, 0x29, 0x7e}},
|
||||
{Key: F17, ASCIICode: []byte{0x1b, 0x5b, 0x31, 0x7e}},
|
||||
{Key: F18, ASCIICode: []byte{0x1b, 0x5b, 0x32, 0x7e}},
|
||||
{Key: F19, ASCIICode: []byte{0x1b, 0x5b, 0x33, 0x7e}},
|
||||
{Key: F20, ASCIICode: []byte{0x1b, 0x5b, 0x34, 0x7e}},
|
||||
|
||||
// Xterm
|
||||
{Key: F13, ASCIICode: []byte{0x1b, 0x5b, 0x31, 0x3b, 0x32, 0x50}},
|
||||
{Key: F14, ASCIICode: []byte{0x1b, 0x5b, 0x31, 0x3b, 0x32, 0x51}},
|
||||
// &ASCIICode{Key: F15, ASCIICode: []byte{0x1b, 0x5b, 0x31, 0x3b, 0x32, 0x52}}, // Conflicts with CPR response
|
||||
{Key: F16, ASCIICode: []byte{0x1b, 0x5b, 0x31, 0x3b, 0x32, 0x52}},
|
||||
{Key: F17, ASCIICode: []byte{0x1b, 0x5b, 0x15, 0x3b, 0x32, 0x7e}},
|
||||
{Key: F18, ASCIICode: []byte{0x1b, 0x5b, 0x17, 0x3b, 0x32, 0x7e}},
|
||||
{Key: F19, ASCIICode: []byte{0x1b, 0x5b, 0x18, 0x3b, 0x32, 0x7e}},
|
||||
{Key: F20, ASCIICode: []byte{0x1b, 0x5b, 0x19, 0x3b, 0x32, 0x7e}},
|
||||
{Key: F21, ASCIICode: []byte{0x1b, 0x5b, 0x20, 0x3b, 0x32, 0x7e}},
|
||||
{Key: F22, ASCIICode: []byte{0x1b, 0x5b, 0x21, 0x3b, 0x32, 0x7e}},
|
||||
{Key: F23, ASCIICode: []byte{0x1b, 0x5b, 0x23, 0x3b, 0x32, 0x7e}},
|
||||
{Key: F24, ASCIICode: []byte{0x1b, 0x5b, 0x24, 0x3b, 0x32, 0x7e}},
|
||||
|
||||
{Key: ControlUp, ASCIICode: []byte{0x1b, 0x5b, 0x31, 0x3b, 0x35, 0x41}},
|
||||
{Key: ControlDown, ASCIICode: []byte{0x1b, 0x5b, 0x31, 0x3b, 0x35, 0x42}},
|
||||
{Key: ControlRight, ASCIICode: []byte{0x1b, 0x5b, 0x31, 0x3b, 0x35, 0x43}},
|
||||
{Key: ControlLeft, ASCIICode: []byte{0x1b, 0x5b, 0x31, 0x3b, 0x35, 0x44}},
|
||||
|
||||
{Key: ShiftUp, ASCIICode: []byte{0x1b, 0x5b, 0x31, 0x3b, 0x32, 0x41}},
|
||||
{Key: ShiftDown, ASCIICode: []byte{0x1b, 0x5b, 0x31, 0x3b, 0x32, 0x42}},
|
||||
{Key: ShiftRight, ASCIICode: []byte{0x1b, 0x5b, 0x31, 0x3b, 0x32, 0x43}},
|
||||
{Key: ShiftLeft, ASCIICode: []byte{0x1b, 0x5b, 0x31, 0x3b, 0x32, 0x44}},
|
||||
|
||||
// Tmux sends following keystrokes when control+arrow is pressed, but for
|
||||
// Emacs ansi-term sends the same sequences for normal arrow keys. Consider
|
||||
// it a normal arrow press, because that's more important.
|
||||
{Key: Up, ASCIICode: []byte{0x1b, 0x4f, 0x41}},
|
||||
{Key: Down, ASCIICode: []byte{0x1b, 0x4f, 0x42}},
|
||||
{Key: Right, ASCIICode: []byte{0x1b, 0x4f, 0x43}},
|
||||
{Key: Left, ASCIICode: []byte{0x1b, 0x4f, 0x44}},
|
||||
|
||||
{Key: ControlUp, ASCIICode: []byte{0x1b, 0x5b, 0x35, 0x41}},
|
||||
{Key: ControlDown, ASCIICode: []byte{0x1b, 0x5b, 0x35, 0x42}},
|
||||
{Key: ControlRight, ASCIICode: []byte{0x1b, 0x5b, 0x35, 0x43}},
|
||||
{Key: ControlLeft, ASCIICode: []byte{0x1b, 0x5b, 0x35, 0x44}},
|
||||
|
||||
{Key: ControlRight, ASCIICode: []byte{0x1b, 0x5b, 0x4f, 0x63}}, // rxvt
|
||||
{Key: ControlLeft, ASCIICode: []byte{0x1b, 0x5b, 0x4f, 0x64}}, // rxvt
|
||||
|
||||
{Key: Ignore, ASCIICode: []byte{0x1b, 0x5b, 0x45}}, // Xterm
|
||||
{Key: Ignore, ASCIICode: []byte{0x1b, 0x5b, 0x46}}, // Linux console
|
||||
}
|
||||
|
||||
var _ ConsoleParser = &PosixParser{}
|
||||
|
||||
// NewStandardInputParser returns ConsoleParser object to read from stdin.
|
||||
func NewStandardInputParser() *PosixParser {
|
||||
return &PosixParser{
|
||||
fd: syscall.Stdin,
|
||||
}
|
||||
}
|
||||
@@ -1,38 +1,46 @@
|
||||
// +build !windows
|
||||
|
||||
package prompt
|
||||
|
||||
import (
|
||||
"bytes"
|
||||
"strconv"
|
||||
"syscall"
|
||||
)
|
||||
|
||||
type VT100Writer struct {
|
||||
// PosixWriter is a ConsoleWriter implementation for POSIX environment.
|
||||
// To control terminal emulator, this outputs VT100 escape sequences.
|
||||
type PosixWriter struct {
|
||||
fd int
|
||||
buffer []byte
|
||||
}
|
||||
|
||||
func (w *VT100Writer) WriteRaw(data []byte) {
|
||||
// WriteRaw to write raw byte array
|
||||
func (w *PosixWriter) WriteRaw(data []byte) {
|
||||
w.buffer = append(w.buffer, data...)
|
||||
// Flush because sometimes the render is broken when a large amount data in buffer.
|
||||
w.Flush()
|
||||
return
|
||||
}
|
||||
|
||||
func (w *VT100Writer) Write(data []byte) {
|
||||
w.WriteRaw(byteFilter(data, writeFilter))
|
||||
// Write to write safety byte array by removing control sequences.
|
||||
func (w *PosixWriter) Write(data []byte) {
|
||||
w.WriteRaw(bytes.Replace(data, []byte{0x1b}, []byte{'?'}, -1))
|
||||
return
|
||||
}
|
||||
|
||||
func (w *VT100Writer) WriteRawStr(data string) {
|
||||
// WriteRawStr to write raw string
|
||||
func (w *PosixWriter) WriteRawStr(data string) {
|
||||
w.WriteRaw([]byte(data))
|
||||
return
|
||||
}
|
||||
|
||||
func (w *VT100Writer) WriteStr(data string) {
|
||||
// WriteStr to write safety string by removing control sequences.
|
||||
func (w *PosixWriter) WriteStr(data string) {
|
||||
w.Write([]byte(data))
|
||||
return
|
||||
}
|
||||
|
||||
func (w *VT100Writer) Flush() error {
|
||||
// Flush to flush buffer
|
||||
func (w *PosixWriter) Flush() error {
|
||||
_, err := syscall.Write(w.fd, w.buffer)
|
||||
if err != nil {
|
||||
return err
|
||||
@@ -43,48 +51,62 @@ func (w *VT100Writer) Flush() error {
|
||||
|
||||
/* Erase */
|
||||
|
||||
func (w *VT100Writer) EraseScreen() {
|
||||
// EraseScreen erases the screen with the background colour and moves the cursor to home.
|
||||
func (w *PosixWriter) EraseScreen() {
|
||||
w.WriteRaw([]byte{0x1b, 0x5b, 0x32, 0x4a})
|
||||
return
|
||||
}
|
||||
|
||||
func (w *VT100Writer) EraseUp() {
|
||||
// EraseUp erases the screen from the current line up to the top of the screen.
|
||||
func (w *PosixWriter) EraseUp() {
|
||||
w.WriteRaw([]byte{0x1b, 0x5b, 0x31, 0x4a})
|
||||
return
|
||||
}
|
||||
|
||||
func (w *VT100Writer) EraseDown() {
|
||||
// EraseDown erases the screen from the current line down to the bottom of the screen.
|
||||
func (w *PosixWriter) EraseDown() {
|
||||
w.WriteRaw([]byte{0x1b, 0x5b, 0x4a})
|
||||
return
|
||||
}
|
||||
|
||||
func (w *VT100Writer) EraseStartOfLine() {
|
||||
// EraseStartOfLine erases from the current cursor position to the start of the current line.
|
||||
func (w *PosixWriter) EraseStartOfLine() {
|
||||
w.WriteRaw([]byte{0x1b, 0x5b, 0x31, 0x4b})
|
||||
return
|
||||
}
|
||||
|
||||
func (w *VT100Writer) EraseEndOfLine() {
|
||||
// EraseEndOfLine erases from the current cursor position to the end of the current line.
|
||||
func (w *PosixWriter) EraseEndOfLine() {
|
||||
w.WriteRaw([]byte{0x1b, 0x5b, 0x4b})
|
||||
return
|
||||
}
|
||||
|
||||
func (w *VT100Writer) EraseLine() {
|
||||
// EraseLine erases the entire current line.
|
||||
func (w *PosixWriter) EraseLine() {
|
||||
w.WriteRaw([]byte{0x1b, 0x5b, 0x32, 0x4b})
|
||||
return
|
||||
}
|
||||
|
||||
/* Cursor */
|
||||
|
||||
func (w *VT100Writer) ShowCursor() {
|
||||
// ShowCursor stops blinking cursor and show.
|
||||
func (w *PosixWriter) ShowCursor() {
|
||||
w.WriteRaw([]byte{0x1b, 0x5b, 0x3f, 0x31, 0x32, 0x6c, 0x1b, 0x5b, 0x3f, 0x32, 0x35, 0x68})
|
||||
}
|
||||
|
||||
func (w *VT100Writer) HideCursor() {
|
||||
// HideCursor hides cursor.
|
||||
func (w *PosixWriter) HideCursor() {
|
||||
w.WriteRaw([]byte{0x1b, 0x5b, 0x3f, 0x32, 0x35, 0x6c})
|
||||
return
|
||||
}
|
||||
|
||||
func (w *VT100Writer) CursorGoTo(row, col int) {
|
||||
// CursorGoTo sets the cursor position where subsequent text will begin.
|
||||
func (w *PosixWriter) CursorGoTo(row, col int) {
|
||||
if row == 0 && col == 0 {
|
||||
// If no row/column parameters are provided (ie. <ESC>[H), the cursor will move to the home position.
|
||||
w.WriteRaw([]byte{0x1b, 0x5b, 0x3b, 0x48})
|
||||
return
|
||||
}
|
||||
r := strconv.Itoa(row)
|
||||
c := strconv.Itoa(col)
|
||||
w.WriteRaw([]byte{0x1b, 0x5b})
|
||||
@@ -95,9 +117,12 @@ func (w *VT100Writer) CursorGoTo(row, col int) {
|
||||
return
|
||||
}
|
||||
|
||||
func (w *VT100Writer) CursorUp(n int) {
|
||||
if n < 0 {
|
||||
w.CursorDown(n)
|
||||
// CursorUp moves the cursor up by 'n' rows; the default count is 1.
|
||||
func (w *PosixWriter) CursorUp(n int) {
|
||||
if n == 0 {
|
||||
return
|
||||
} else if n < 0 {
|
||||
w.CursorDown(-n)
|
||||
return
|
||||
}
|
||||
s := strconv.Itoa(n)
|
||||
@@ -107,9 +132,12 @@ func (w *VT100Writer) CursorUp(n int) {
|
||||
return
|
||||
}
|
||||
|
||||
func (w *VT100Writer) CursorDown(n int) {
|
||||
if n < 0 {
|
||||
w.CursorUp(n)
|
||||
// CursorDown moves the cursor down by 'n' rows; the default count is 1.
|
||||
func (w *PosixWriter) CursorDown(n int) {
|
||||
if n == 0 {
|
||||
return
|
||||
} else if n < 0 {
|
||||
w.CursorUp(-n)
|
||||
return
|
||||
}
|
||||
s := strconv.Itoa(n)
|
||||
@@ -119,7 +147,8 @@ func (w *VT100Writer) CursorDown(n int) {
|
||||
return
|
||||
}
|
||||
|
||||
func (w *VT100Writer) CursorForward(n int) {
|
||||
// CursorForward moves the cursor forward by 'n' columns; the default count is 1.
|
||||
func (w *PosixWriter) CursorForward(n int) {
|
||||
if n == 0 {
|
||||
return
|
||||
} else if n < 0 {
|
||||
@@ -133,7 +162,8 @@ func (w *VT100Writer) CursorForward(n int) {
|
||||
return
|
||||
}
|
||||
|
||||
func (w *VT100Writer) CursorBackward(n int) {
|
||||
// CursorBackward moves the cursor backward by 'n' columns; the default count is 1.
|
||||
func (w *PosixWriter) CursorBackward(n int) {
|
||||
if n == 0 {
|
||||
return
|
||||
} else if n < 0 {
|
||||
@@ -147,52 +177,78 @@ func (w *VT100Writer) CursorBackward(n int) {
|
||||
return
|
||||
}
|
||||
|
||||
func (w *VT100Writer) AskForCPR() {
|
||||
// AskForCPR asks for a cursor position report (CPR).
|
||||
func (w *PosixWriter) AskForCPR() {
|
||||
// CPR: Cursor Position Request.
|
||||
w.WriteRaw([]byte{0x1b, 0x5b, 0x36, 0x6e})
|
||||
w.Flush()
|
||||
return
|
||||
}
|
||||
|
||||
func (w *VT100Writer) SaveCursor() {
|
||||
// SaveCursor saves current cursor position.
|
||||
func (w *PosixWriter) SaveCursor() {
|
||||
w.WriteRaw([]byte{0x1b, 0x5b, 0x73})
|
||||
return
|
||||
}
|
||||
|
||||
func (w *VT100Writer) UnSaveCursor() {
|
||||
// UnSaveCursor restores cursor position after a Save Cursor.
|
||||
func (w *PosixWriter) UnSaveCursor() {
|
||||
w.WriteRaw([]byte{0x1b, 0x5b, 0x75})
|
||||
return
|
||||
}
|
||||
|
||||
/* Scrolling */
|
||||
|
||||
func (w *VT100Writer) ScrollDown() {
|
||||
// ScrollDown scrolls display down one line.
|
||||
func (w *PosixWriter) ScrollDown() {
|
||||
w.WriteRaw([]byte{0x1b, 0x44})
|
||||
return
|
||||
}
|
||||
|
||||
func (w *VT100Writer) ScrollUp() {
|
||||
// ScrollUp scroll display up one line.
|
||||
func (w *PosixWriter) ScrollUp() {
|
||||
w.WriteRaw([]byte{0x1b, 0x4d})
|
||||
return
|
||||
}
|
||||
|
||||
/* Title */
|
||||
|
||||
func (w *VT100Writer) SetTitle(title string) {
|
||||
// SetTitle sets a title of terminal window.
|
||||
func (w *PosixWriter) SetTitle(title string) {
|
||||
titleBytes := []byte(title)
|
||||
patterns := []struct {
|
||||
from []byte
|
||||
to []byte
|
||||
}{
|
||||
{
|
||||
from: []byte{0x13},
|
||||
to: []byte{},
|
||||
},
|
||||
{
|
||||
from: []byte{0x07},
|
||||
to: []byte{},
|
||||
},
|
||||
}
|
||||
for i := range patterns {
|
||||
titleBytes = bytes.Replace(titleBytes, patterns[i].from, patterns[i].to, -1)
|
||||
}
|
||||
|
||||
w.WriteRaw([]byte{0x1b, 0x5d, 0x32, 0x3b})
|
||||
w.WriteRaw(byteFilter([]byte(title), setTextFilter))
|
||||
w.WriteRaw(titleBytes)
|
||||
w.WriteRaw([]byte{0x07})
|
||||
return
|
||||
}
|
||||
|
||||
func (w *VT100Writer) ClearTitle() {
|
||||
// ClearTitle clears a title of terminal window.
|
||||
func (w *PosixWriter) ClearTitle() {
|
||||
w.WriteRaw([]byte{0x1b, 0x5d, 0x32, 0x3b, 0x07})
|
||||
return
|
||||
}
|
||||
|
||||
/* Font */
|
||||
|
||||
func (w *VT100Writer) SetColor(fg, bg Color, bold bool) {
|
||||
// SetColor sets text and background colors. and specify whether text is bold.
|
||||
func (w *PosixWriter) SetColor(fg, bg Color, bold bool) {
|
||||
f, ok := foregroundANSIColors[fg]
|
||||
if !ok {
|
||||
f = foregroundANSIColors[DefaultColor]
|
||||
@@ -201,7 +257,6 @@ func (w *VT100Writer) SetColor(fg, bg Color, bold bool) {
|
||||
if !ok {
|
||||
b = backgroundANSIColors[DefaultColor]
|
||||
}
|
||||
syscall.Write(syscall.Stdout, []byte{0x1b, 0x5b, 0x33, 0x39, 0x3b, 0x34, 0x39, 0x6d})
|
||||
w.WriteRaw([]byte{0x1b, 0x5b})
|
||||
if !bold {
|
||||
w.WriteRaw([]byte{0x30, 0x3b})
|
||||
@@ -264,32 +319,11 @@ var backgroundANSIColors = map[Color][]byte{
|
||||
White: {0x31, 0x30, 0x37}, // 107
|
||||
}
|
||||
|
||||
func writeFilter(buf byte) bool {
|
||||
return buf != 0x1b && buf != 0x3f
|
||||
}
|
||||
var _ ConsoleWriter = &PosixWriter{}
|
||||
|
||||
func setTextFilter(buf byte) bool {
|
||||
return buf != 0x1b && buf != 0x07
|
||||
}
|
||||
|
||||
func byteFilter(buf []byte, fn ...func(b byte) bool) []byte {
|
||||
if len(fn) == 0 {
|
||||
return buf
|
||||
}
|
||||
ret := make([]byte, 0, len(buf))
|
||||
f := fn[0]
|
||||
for i, n := range buf {
|
||||
if f(n) {
|
||||
ret = append(ret, buf[i])
|
||||
}
|
||||
}
|
||||
return byteFilter(ret, fn[1:]...)
|
||||
}
|
||||
|
||||
var _ ConsoleWriter = &VT100Writer{}
|
||||
|
||||
func NewVT100StandardOutputWriter() *VT100Writer {
|
||||
return &VT100Writer{
|
||||
// NewStandardOutputWriter returns ConsoleWriter object to write to stdout.
|
||||
func NewStandardOutputWriter() *PosixWriter {
|
||||
return &PosixWriter{
|
||||
fd: syscall.Stdout,
|
||||
}
|
||||
}
|
||||
155
vendor/github.com/c-bata/go-prompt/prompt.go
generated
vendored
155
vendor/github.com/c-bata/go-prompt/prompt.go
generated
vendored
@@ -4,14 +4,11 @@ import (
|
||||
"io/ioutil"
|
||||
"log"
|
||||
"os"
|
||||
"os/signal"
|
||||
"syscall"
|
||||
"time"
|
||||
)
|
||||
|
||||
const (
|
||||
logfile = "/tmp/go-prompt-debug.log"
|
||||
envEnableLog = "GO_PROMPT_ENABLE_LOG"
|
||||
envDebugLogPath = "GO_PROMPT_LOG_PATH"
|
||||
)
|
||||
|
||||
// Executor is called when user input something text.
|
||||
@@ -39,10 +36,9 @@ type Exec struct {
|
||||
|
||||
// Run starts prompt.
|
||||
func (p *Prompt) Run() {
|
||||
// Logging
|
||||
if os.Getenv(envEnableLog) != "true" {
|
||||
if l := os.Getenv(envDebugLogPath); l == "" {
|
||||
log.SetOutput(ioutil.Discard)
|
||||
} else if f, err := os.OpenFile(logfile, os.O_WRONLY|os.O_CREATE|os.O_APPEND, 0666); err != nil {
|
||||
} else if f, err := os.OpenFile(l, os.O_WRONLY|os.O_CREATE|os.O_APPEND, 0666); err != nil {
|
||||
log.SetOutput(ioutil.Discard)
|
||||
} else {
|
||||
defer f.Close()
|
||||
@@ -57,12 +53,12 @@ func (p *Prompt) Run() {
|
||||
|
||||
bufCh := make(chan []byte, 128)
|
||||
stopReadBufCh := make(chan struct{})
|
||||
go readBuffer(bufCh, stopReadBufCh)
|
||||
go p.readBuffer(bufCh, stopReadBufCh)
|
||||
|
||||
exitCh := make(chan int)
|
||||
winSizeCh := make(chan *WinSize)
|
||||
stopHandleSignalCh := make(chan struct{})
|
||||
go handleSignals(p.in, exitCh, winSizeCh, stopHandleSignalCh)
|
||||
go p.handleSignals(exitCh, winSizeCh, stopHandleSignalCh)
|
||||
|
||||
for {
|
||||
select {
|
||||
@@ -85,8 +81,8 @@ func (p *Prompt) Run() {
|
||||
|
||||
// Set raw mode
|
||||
p.in.Setup()
|
||||
go readBuffer(bufCh, stopReadBufCh)
|
||||
go handleSignals(p.in, exitCh, winSizeCh, stopHandleSignalCh)
|
||||
go p.readBuffer(bufCh, stopReadBufCh)
|
||||
go p.handleSignals(exitCh, winSizeCh, stopHandleSignalCh)
|
||||
} else {
|
||||
p.completion.Update(*p.buf.Document())
|
||||
p.renderer.Render(p.buf, p.completion)
|
||||
@@ -109,32 +105,10 @@ func (p *Prompt) feed(b []byte) (shouldExit bool, exec *Exec) {
|
||||
|
||||
// completion
|
||||
completing := p.completion.Completing()
|
||||
switch key {
|
||||
case Down:
|
||||
if completing {
|
||||
p.completion.Next()
|
||||
}
|
||||
case Tab, ControlI:
|
||||
p.completion.Next()
|
||||
case Up:
|
||||
if completing {
|
||||
p.completion.Previous()
|
||||
}
|
||||
case BackTab:
|
||||
p.completion.Previous()
|
||||
default:
|
||||
if s, ok := p.completion.GetSelectedSuggestion(); ok {
|
||||
w := p.buf.Document().GetWordBeforeCursor()
|
||||
if w != "" {
|
||||
p.buf.DeleteBeforeCursor(len([]rune(w)))
|
||||
}
|
||||
p.buf.InsertText(s.Text, false, true)
|
||||
}
|
||||
p.completion.Reset()
|
||||
}
|
||||
p.handleCompletionKeyBinding(key, completing)
|
||||
|
||||
switch key {
|
||||
case Enter, ControlJ:
|
||||
case Enter, ControlJ, ControlM:
|
||||
p.renderer.BreakLine(p.buf)
|
||||
|
||||
exec = &Exec{input: p.buf.Text()}
|
||||
@@ -169,7 +143,37 @@ func (p *Prompt) feed(b []byte) (shouldExit bool, exec *Exec) {
|
||||
p.buf.InsertText(string(b), false, true)
|
||||
}
|
||||
|
||||
// Key bindings
|
||||
p.handleKeyBinding(key)
|
||||
return
|
||||
}
|
||||
|
||||
func (p *Prompt) handleCompletionKeyBinding(key Key, completing bool) {
|
||||
switch key {
|
||||
case Down:
|
||||
if completing {
|
||||
p.completion.Next()
|
||||
}
|
||||
case Tab, ControlI:
|
||||
p.completion.Next()
|
||||
case Up:
|
||||
if completing {
|
||||
p.completion.Previous()
|
||||
}
|
||||
case BackTab:
|
||||
p.completion.Previous()
|
||||
default:
|
||||
if s, ok := p.completion.GetSelectedSuggestion(); ok {
|
||||
w := p.buf.Document().GetWordBeforeCursor()
|
||||
if w != "" {
|
||||
p.buf.DeleteBeforeCursor(len([]rune(w)))
|
||||
}
|
||||
p.buf.InsertText(s.Text, false, true)
|
||||
}
|
||||
p.completion.Reset()
|
||||
}
|
||||
}
|
||||
|
||||
func (p *Prompt) handleKeyBinding(key Key) {
|
||||
for i := range commonKeyBindings {
|
||||
kb := commonKeyBindings[i]
|
||||
if kb.Key == key {
|
||||
@@ -193,15 +197,13 @@ func (p *Prompt) feed(b []byte) (shouldExit bool, exec *Exec) {
|
||||
kb.Fn(p.buf)
|
||||
}
|
||||
}
|
||||
return
|
||||
}
|
||||
|
||||
// Input just returns user input text.
|
||||
func (p *Prompt) Input() string {
|
||||
// Logging
|
||||
if os.Getenv(envEnableLog) != "true" {
|
||||
if l := os.Getenv(envDebugLogPath); l == "" {
|
||||
log.SetOutput(ioutil.Discard)
|
||||
} else if f, err := os.OpenFile(logfile, os.O_WRONLY|os.O_CREATE|os.O_APPEND, 0666); err != nil {
|
||||
} else if f, err := os.OpenFile(l, os.O_WRONLY|os.O_CREATE|os.O_APPEND, 0666); err != nil {
|
||||
log.SetOutput(ioutil.Discard)
|
||||
} else {
|
||||
defer f.Close()
|
||||
@@ -215,7 +217,7 @@ func (p *Prompt) Input() string {
|
||||
p.renderer.Render(p.buf, p.completion)
|
||||
bufCh := make(chan []byte, 128)
|
||||
stopReadBufCh := make(chan struct{})
|
||||
go readBuffer(bufCh, stopReadBufCh)
|
||||
go p.readBuffer(bufCh, stopReadBufCh)
|
||||
|
||||
for {
|
||||
select {
|
||||
@@ -237,6 +239,22 @@ func (p *Prompt) Input() string {
|
||||
}
|
||||
}
|
||||
|
||||
func (p *Prompt) readBuffer(bufCh chan []byte, stopCh chan struct{}) {
|
||||
log.Printf("[INFO] readBuffer start")
|
||||
for {
|
||||
select {
|
||||
case <-stopCh:
|
||||
log.Print("[INFO] stop readBuffer")
|
||||
return
|
||||
default:
|
||||
if b, err := p.in.Read(); err == nil && !(len(b) == 1 && b[0] == 0) {
|
||||
bufCh <- b
|
||||
}
|
||||
}
|
||||
time.Sleep(10 * time.Millisecond)
|
||||
}
|
||||
}
|
||||
|
||||
func (p *Prompt) setUp() {
|
||||
p.in.Setup()
|
||||
p.renderer.Setup()
|
||||
@@ -247,58 +265,3 @@ func (p *Prompt) tearDown() {
|
||||
p.in.TearDown()
|
||||
p.renderer.TearDown()
|
||||
}
|
||||
|
||||
func readBuffer(bufCh chan []byte, stopCh chan struct{}) {
|
||||
buf := make([]byte, 1024)
|
||||
|
||||
log.Printf("[INFO] readBuffer start")
|
||||
for {
|
||||
time.Sleep(10 * time.Millisecond)
|
||||
select {
|
||||
case <-stopCh:
|
||||
log.Print("[INFO] stop readBuffer")
|
||||
return
|
||||
default:
|
||||
if n, err := syscall.Read(syscall.Stdin, buf); err == nil {
|
||||
bufCh <- buf[:n]
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
func handleSignals(in ConsoleParser, exitCh chan int, winSizeCh chan *WinSize, stop chan struct{}) {
|
||||
sigCh := make(chan os.Signal, 1)
|
||||
signal.Notify(
|
||||
sigCh,
|
||||
syscall.SIGINT,
|
||||
syscall.SIGTERM,
|
||||
syscall.SIGQUIT,
|
||||
syscall.SIGWINCH,
|
||||
)
|
||||
|
||||
for {
|
||||
select {
|
||||
case <- stop:
|
||||
log.Println("[INFO] stop handleSignals")
|
||||
return
|
||||
case s := <-sigCh:
|
||||
switch s {
|
||||
case syscall.SIGINT: // kill -SIGINT XXXX or Ctrl+c
|
||||
log.Println("[SIGNAL] Catch SIGINT")
|
||||
exitCh <- 0
|
||||
|
||||
case syscall.SIGTERM: // kill -SIGTERM XXXX
|
||||
log.Println("[SIGNAL] Catch SIGTERM")
|
||||
exitCh <- 1
|
||||
|
||||
case syscall.SIGQUIT: // kill -SIGQUIT XXXX
|
||||
log.Println("[SIGNAL] Catch SIGQUIT")
|
||||
exitCh <- 0
|
||||
|
||||
case syscall.SIGWINCH:
|
||||
log.Println("[SIGNAL] Catch SIGWINCH")
|
||||
winSizeCh <- in.GetWinSize()
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
207
vendor/github.com/c-bata/go-prompt/render.go
generated
vendored
207
vendor/github.com/c-bata/go-prompt/render.go
generated
vendored
@@ -1,13 +1,21 @@
|
||||
package prompt
|
||||
|
||||
import (
|
||||
"runtime"
|
||||
)
|
||||
|
||||
// Render to render prompt information from state of Buffer.
|
||||
type Render struct {
|
||||
out ConsoleWriter
|
||||
prefix string
|
||||
title string
|
||||
row uint16
|
||||
col uint16
|
||||
// colors
|
||||
out ConsoleWriter
|
||||
prefix string
|
||||
livePrefixCallback func() (prefix string, useLivePrefix bool)
|
||||
title string
|
||||
row uint16
|
||||
col uint16
|
||||
|
||||
previousCursor int
|
||||
|
||||
// colors,
|
||||
prefixTextColor Color
|
||||
prefixBGColor Color
|
||||
inputTextColor Color
|
||||
@@ -22,6 +30,8 @@ type Render struct {
|
||||
descriptionBGColor Color
|
||||
selectedDescriptionTextColor Color
|
||||
selectedDescriptionBGColor Color
|
||||
scrollbarThumbColor Color
|
||||
scrollbarBGColor Color
|
||||
}
|
||||
|
||||
// Setup to initialize console output.
|
||||
@@ -32,9 +42,18 @@ func (r *Render) Setup() {
|
||||
}
|
||||
}
|
||||
|
||||
// getCurrentPrefix to get current prefix.
|
||||
// If live-prefix is enabled, return live-prefix.
|
||||
func (r *Render) getCurrentPrefix() string {
|
||||
if prefix, ok := r.livePrefixCallback(); ok {
|
||||
return prefix
|
||||
}
|
||||
return r.prefix
|
||||
}
|
||||
|
||||
func (r *Render) renderPrefix() {
|
||||
r.out.SetColor(r.prefixTextColor, r.prefixBGColor, false)
|
||||
r.out.WriteStr(r.prefix)
|
||||
r.out.WriteStr(r.getCurrentPrefix())
|
||||
r.out.SetColor(DefaultColor, DefaultColor, false)
|
||||
}
|
||||
|
||||
@@ -67,107 +86,199 @@ func (r *Render) renderWindowTooSmall() {
|
||||
r.out.EraseScreen()
|
||||
r.out.SetColor(DarkRed, White, false)
|
||||
r.out.WriteStr("Your console window is too small...")
|
||||
r.out.Flush()
|
||||
return
|
||||
}
|
||||
|
||||
func (r *Render) renderCompletion(buf *Buffer, completions *CompletionManager) {
|
||||
max := completions.max
|
||||
if max > r.row {
|
||||
max = r.row
|
||||
}
|
||||
|
||||
suggestions := completions.GetSuggestions()
|
||||
if l := len(completions.GetSuggestions()); l == 0 {
|
||||
if len(completions.GetSuggestions()) == 0 {
|
||||
return
|
||||
} else if l > int(max) {
|
||||
suggestions = suggestions[:max]
|
||||
}
|
||||
|
||||
prefix := r.getCurrentPrefix()
|
||||
formatted, width := formatSuggestions(
|
||||
suggestions,
|
||||
int(r.col)-len(r.prefix),
|
||||
int(r.col)-len(prefix)-1, // -1 means a width of scrollbar
|
||||
)
|
||||
l := len(formatted)
|
||||
r.prepareArea(l)
|
||||
// +1 means a width of scrollbar.
|
||||
width++
|
||||
|
||||
d := (len(r.prefix) + len(buf.Document().TextBeforeCursor())) % int(r.col)
|
||||
if d == 0 { // the cursor is on right end.
|
||||
r.out.CursorBackward(width)
|
||||
} else if d+width > int(r.col) {
|
||||
r.out.CursorBackward(d + width - int(r.col))
|
||||
windowHeight := len(formatted)
|
||||
if windowHeight > int(completions.max) {
|
||||
windowHeight = int(completions.max)
|
||||
}
|
||||
formatted = formatted[completions.verticalScroll : completions.verticalScroll+windowHeight]
|
||||
r.prepareArea(windowHeight)
|
||||
|
||||
cursor := len(prefix) + len(buf.Document().TextBeforeCursor())
|
||||
x, _ := r.toPos(cursor)
|
||||
if x+width >= int(r.col) {
|
||||
cursor = r.backward(cursor, x+width-int(r.col))
|
||||
}
|
||||
|
||||
contentHeight := len(completions.tmp)
|
||||
|
||||
fractionVisible := float64(windowHeight) / float64(contentHeight)
|
||||
fractionAbove := float64(completions.verticalScroll) / float64(contentHeight)
|
||||
|
||||
scrollbarHeight := int(clamp(float64(windowHeight), 1, float64(windowHeight)*fractionVisible))
|
||||
scrollbarTop := int(float64(windowHeight) * fractionAbove)
|
||||
|
||||
isScrollThumb := func(row int) bool {
|
||||
return scrollbarTop <= row && row <= scrollbarTop+scrollbarHeight
|
||||
}
|
||||
|
||||
selected := completions.selected - completions.verticalScroll
|
||||
r.out.SetColor(White, Cyan, false)
|
||||
for i := 0; i < l; i++ {
|
||||
for i := 0; i < windowHeight; i++ {
|
||||
r.out.CursorDown(1)
|
||||
if i == completions.selected {
|
||||
if i == selected {
|
||||
r.out.SetColor(r.selectedSuggestionTextColor, r.selectedSuggestionBGColor, true)
|
||||
} else {
|
||||
r.out.SetColor(r.suggestionTextColor, r.suggestionBGColor, false)
|
||||
}
|
||||
r.out.WriteStr(formatted[i].Text)
|
||||
|
||||
if i == completions.selected {
|
||||
if i == selected {
|
||||
r.out.SetColor(r.selectedDescriptionTextColor, r.selectedDescriptionBGColor, false)
|
||||
} else {
|
||||
r.out.SetColor(r.descriptionTextColor, r.descriptionBGColor, false)
|
||||
}
|
||||
r.out.WriteStr(formatted[i].Description)
|
||||
r.out.CursorBackward(width)
|
||||
}
|
||||
if d == 0 { // the cursor is on right end.
|
||||
// DON'T CURSOR DOWN HERE. Because the line doesn't erase properly.
|
||||
r.out.CursorForward(width + 1)
|
||||
} else if d+width > int(r.col) {
|
||||
r.out.CursorForward(d + width - int(r.col))
|
||||
|
||||
if isScrollThumb(i) {
|
||||
r.out.SetColor(DefaultColor, r.scrollbarThumbColor, false)
|
||||
} else {
|
||||
r.out.SetColor(DefaultColor, r.scrollbarBGColor, false)
|
||||
}
|
||||
r.out.WriteStr(" ")
|
||||
r.out.SetColor(DefaultColor, DefaultColor, false)
|
||||
|
||||
r.lineWrap(cursor + width)
|
||||
r.backward(cursor+width, width)
|
||||
}
|
||||
|
||||
r.out.CursorUp(l)
|
||||
if x+width >= int(r.col) {
|
||||
r.out.CursorForward(x + width - int(r.col))
|
||||
}
|
||||
|
||||
r.out.CursorUp(windowHeight)
|
||||
r.out.SetColor(DefaultColor, DefaultColor, false)
|
||||
return
|
||||
}
|
||||
|
||||
// Render renders to the console.
|
||||
func (r *Render) Render(buffer *Buffer, completion *CompletionManager) {
|
||||
// Erasing
|
||||
r.out.CursorBackward(int(r.col) + len(buffer.Text()) + len(r.prefix))
|
||||
r.out.EraseDown()
|
||||
// In situations where a pseudo tty is allocated (e.g. within a docker container),
|
||||
// window size via TIOCGWINSZ is not immediately available and will result in 0,0 dimensions.
|
||||
if r.col == 0 {
|
||||
return
|
||||
}
|
||||
defer r.out.Flush()
|
||||
r.move(r.previousCursor, 0)
|
||||
|
||||
line := buffer.Text()
|
||||
prefix := r.getCurrentPrefix()
|
||||
cursor := len(prefix) + len(line)
|
||||
|
||||
// prepare area
|
||||
line := buffer.Text()
|
||||
h := ((len(r.prefix) + len(line)) / int(r.col)) + 1 + int(completion.max)
|
||||
_, y := r.toPos(cursor)
|
||||
|
||||
h := y + 1 + int(completion.max)
|
||||
if h > int(r.row) || completionMargin > int(r.col) {
|
||||
r.renderWindowTooSmall()
|
||||
return
|
||||
}
|
||||
|
||||
// Rendering
|
||||
r.renderPrefix()
|
||||
r.out.HideCursor()
|
||||
defer r.out.ShowCursor()
|
||||
|
||||
r.renderPrefix()
|
||||
r.out.SetColor(r.inputTextColor, r.inputBGColor, false)
|
||||
r.out.WriteStr(line)
|
||||
r.out.SetColor(DefaultColor, DefaultColor, false)
|
||||
r.out.CursorBackward(len(line) - buffer.CursorPosition)
|
||||
r.lineWrap(cursor)
|
||||
|
||||
r.out.EraseDown()
|
||||
|
||||
cursor = r.backward(cursor, len(line)-buffer.CursorPosition)
|
||||
|
||||
r.renderCompletion(buffer, completion)
|
||||
if suggest, ok := completion.GetSelectedSuggestion(); ok {
|
||||
r.out.CursorBackward(len([]rune(buffer.Document().GetWordBeforeCursor())))
|
||||
cursor = r.backward(cursor, len(buffer.Document().GetWordBeforeCursor()))
|
||||
|
||||
r.out.SetColor(r.previewSuggestionTextColor, r.previewSuggestionBGColor, false)
|
||||
r.out.WriteStr(suggest.Text)
|
||||
r.out.SetColor(DefaultColor, DefaultColor, false)
|
||||
cursor += len(suggest.Text)
|
||||
|
||||
rest := buffer.Document().TextAfterCursor()
|
||||
r.out.WriteStr(rest)
|
||||
cursor += len(rest)
|
||||
r.lineWrap(cursor)
|
||||
|
||||
cursor = r.backward(cursor, len(rest))
|
||||
}
|
||||
r.out.Flush()
|
||||
r.previousCursor = cursor
|
||||
}
|
||||
|
||||
// BreakLine to break line.
|
||||
func (r *Render) BreakLine(buffer *Buffer) {
|
||||
// CR
|
||||
r.out.CursorBackward(int(r.col) + len(buffer.Text()) + len(r.prefix))
|
||||
// Erasing and Render
|
||||
r.out.EraseDown()
|
||||
cursor := len(buffer.Document().TextBeforeCursor()) + len(r.getCurrentPrefix())
|
||||
r.clear(cursor)
|
||||
r.renderPrefix()
|
||||
r.out.SetColor(r.inputTextColor, r.inputBGColor, false)
|
||||
r.out.WriteStr(buffer.Document().Text + "\n")
|
||||
r.out.SetColor(DefaultColor, DefaultColor, false)
|
||||
r.out.Flush()
|
||||
|
||||
r.previousCursor = 0
|
||||
}
|
||||
|
||||
// clear erases the screen from a beginning of input
|
||||
// even if there is line break which means input length exceeds a window's width.
|
||||
func (r *Render) clear(cursor int) {
|
||||
r.move(cursor, 0)
|
||||
r.out.EraseDown()
|
||||
}
|
||||
|
||||
// backward moves cursor to backward from a current cursor position
|
||||
// regardless there is a line break.
|
||||
func (r *Render) backward(from, n int) int {
|
||||
return r.move(from, from-n)
|
||||
}
|
||||
|
||||
// move moves cursor to specified position from the beginning of input
|
||||
// even if there is a line break.
|
||||
func (r *Render) move(from, to int) int {
|
||||
fromX, fromY := r.toPos(from)
|
||||
toX, toY := r.toPos(to)
|
||||
|
||||
r.out.CursorUp(fromY - toY)
|
||||
r.out.CursorBackward(fromX - toX)
|
||||
return to
|
||||
}
|
||||
|
||||
// toPos returns the relative position from the beginning of the string.
|
||||
func (r *Render) toPos(cursor int) (x, y int) {
|
||||
col := int(r.col)
|
||||
return cursor % col, cursor / col
|
||||
}
|
||||
|
||||
func (r *Render) lineWrap(cursor int) {
|
||||
if runtime.GOOS != "windows" && cursor > 0 && cursor%int(r.col) == 0 {
|
||||
r.out.WriteRaw([]byte{'\n'})
|
||||
}
|
||||
}
|
||||
|
||||
func clamp(high, low, x float64) float64 {
|
||||
switch {
|
||||
case high < x:
|
||||
return high
|
||||
case x < low:
|
||||
return low
|
||||
default:
|
||||
return x
|
||||
}
|
||||
}
|
||||
|
||||
48
vendor/github.com/c-bata/go-prompt/signal_posix.go
generated
vendored
Normal file
48
vendor/github.com/c-bata/go-prompt/signal_posix.go
generated
vendored
Normal file
@@ -0,0 +1,48 @@
|
||||
// +build !windows
|
||||
|
||||
package prompt
|
||||
|
||||
import (
|
||||
"log"
|
||||
"os"
|
||||
"os/signal"
|
||||
"syscall"
|
||||
)
|
||||
|
||||
func (p *Prompt) handleSignals(exitCh chan int, winSizeCh chan *WinSize, stop chan struct{}) {
|
||||
in := p.in
|
||||
sigCh := make(chan os.Signal, 1)
|
||||
signal.Notify(
|
||||
sigCh,
|
||||
syscall.SIGINT,
|
||||
syscall.SIGTERM,
|
||||
syscall.SIGQUIT,
|
||||
syscall.SIGWINCH,
|
||||
)
|
||||
|
||||
for {
|
||||
select {
|
||||
case <-stop:
|
||||
log.Println("[INFO] stop handleSignals")
|
||||
return
|
||||
case s := <-sigCh:
|
||||
switch s {
|
||||
case syscall.SIGINT: // kill -SIGINT XXXX or Ctrl+c
|
||||
log.Println("[SIGNAL] Catch SIGINT")
|
||||
exitCh <- 0
|
||||
|
||||
case syscall.SIGTERM: // kill -SIGTERM XXXX
|
||||
log.Println("[SIGNAL] Catch SIGTERM")
|
||||
exitCh <- 1
|
||||
|
||||
case syscall.SIGQUIT: // kill -SIGQUIT XXXX
|
||||
log.Println("[SIGNAL] Catch SIGQUIT")
|
||||
exitCh <- 0
|
||||
|
||||
case syscall.SIGWINCH:
|
||||
log.Println("[SIGNAL] Catch SIGWINCH")
|
||||
winSizeCh <- in.GetWinSize()
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
43
vendor/github.com/c-bata/go-prompt/signal_windows.go
generated
vendored
Normal file
43
vendor/github.com/c-bata/go-prompt/signal_windows.go
generated
vendored
Normal file
@@ -0,0 +1,43 @@
|
||||
// +build windows
|
||||
|
||||
package prompt
|
||||
|
||||
import (
|
||||
"log"
|
||||
"os"
|
||||
"os/signal"
|
||||
"syscall"
|
||||
)
|
||||
|
||||
func (p *Prompt) handleSignals(exitCh chan int, winSizeCh chan *WinSize, stop chan struct{}) {
|
||||
sigCh := make(chan os.Signal, 1)
|
||||
signal.Notify(
|
||||
sigCh,
|
||||
syscall.SIGINT,
|
||||
syscall.SIGTERM,
|
||||
syscall.SIGQUIT,
|
||||
)
|
||||
|
||||
for {
|
||||
select {
|
||||
case <-stop:
|
||||
log.Println("[INFO] stop handleSignals")
|
||||
return
|
||||
case s := <-sigCh:
|
||||
switch s {
|
||||
|
||||
case syscall.SIGINT: // kill -SIGINT XXXX or Ctrl+c
|
||||
log.Println("[SIGNAL] Catch SIGINT")
|
||||
exitCh <- 0
|
||||
|
||||
case syscall.SIGTERM: // kill -SIGTERM XXXX
|
||||
log.Println("[SIGNAL] Catch SIGTERM")
|
||||
exitCh <- 1
|
||||
|
||||
case syscall.SIGQUIT: // kill -SIGQUIT XXXX
|
||||
log.Println("[SIGNAL] Catch SIGQUIT")
|
||||
exitCh <- 0
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -1,106 +1,94 @@
|
||||
// +build windows
|
||||
|
||||
package prompt
|
||||
|
||||
import (
|
||||
"bytes"
|
||||
"log"
|
||||
"errors"
|
||||
"syscall"
|
||||
"unicode/utf8"
|
||||
"unsafe"
|
||||
|
||||
"github.com/pkg/term/termios"
|
||||
"github.com/mattn/go-tty"
|
||||
)
|
||||
|
||||
type VT100Parser struct {
|
||||
fd int
|
||||
origTermios syscall.Termios
|
||||
const maxReadBytes = 1024
|
||||
|
||||
var kernel32 = syscall.NewLazyDLL("kernel32.dll")
|
||||
|
||||
var procGetNumberOfConsoleInputEvents = kernel32.NewProc("GetNumberOfConsoleInputEvents")
|
||||
|
||||
// WindowsParser is a ConsoleParser implementation for Win32 console.
|
||||
type WindowsParser struct {
|
||||
tty *tty.TTY
|
||||
}
|
||||
|
||||
func (t *VT100Parser) Setup() error {
|
||||
// Set NonBlocking mode because if syscall.Read block this goroutine, it cannot receive data from stopCh.
|
||||
if err := syscall.SetNonblock(t.fd, true); err != nil {
|
||||
log.Println("[ERROR] Cannot set non blocking mode.")
|
||||
return err
|
||||
}
|
||||
if err := t.setRawMode(); err != nil {
|
||||
log.Println("[ERROR] Cannot set raw mode.")
|
||||
// Setup should be called before starting input
|
||||
func (p *WindowsParser) Setup() error {
|
||||
t, err := tty.Open()
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
p.tty = t
|
||||
return nil
|
||||
}
|
||||
|
||||
func (t *VT100Parser) TearDown() error {
|
||||
if err := syscall.SetNonblock(t.fd, false); err != nil {
|
||||
log.Println("[ERROR] Cannot set blocking mode.")
|
||||
return err
|
||||
}
|
||||
if err := t.resetRawMode(); err != nil {
|
||||
log.Println("[ERROR] Cannot reset from raw mode.")
|
||||
return err
|
||||
}
|
||||
return nil
|
||||
// TearDown should be called after stopping input
|
||||
func (p *WindowsParser) TearDown() error {
|
||||
return p.tty.Close()
|
||||
}
|
||||
|
||||
func (t *VT100Parser) setRawMode() error {
|
||||
x := t.origTermios.Lflag
|
||||
if x &^= syscall.ICANON; x != 0 && x == t.origTermios.Lflag {
|
||||
// fd is already raw mode
|
||||
return nil
|
||||
}
|
||||
var n syscall.Termios
|
||||
if err := termios.Tcgetattr(uintptr(t.fd), &t.origTermios); err != nil {
|
||||
return err
|
||||
}
|
||||
n = t.origTermios
|
||||
// "&^=" used like: https://play.golang.org/p/8eJw3JxS4O
|
||||
n.Lflag &^= syscall.ECHO | syscall.ICANON | syscall.IEXTEN | syscall.ISIG
|
||||
n.Cc[syscall.VMIN] = 1
|
||||
n.Cc[syscall.VTIME] = 0
|
||||
termios.Tcsetattr(uintptr(t.fd), termios.TCSANOW, &n)
|
||||
return nil
|
||||
}
|
||||
|
||||
func (t *VT100Parser) resetRawMode() error {
|
||||
if t.origTermios.Lflag == 0 {
|
||||
return nil
|
||||
}
|
||||
return termios.Tcsetattr(uintptr(t.fd), termios.TCSANOW, &t.origTermios)
|
||||
}
|
||||
|
||||
func (t *VT100Parser) GetKey(b []byte) Key {
|
||||
// GetKey returns Key correspond to input byte codes.
|
||||
func (p *WindowsParser) GetKey(b []byte) Key {
|
||||
for _, k := range asciiSequences {
|
||||
if bytes.Equal(k.ASCIICode, b) {
|
||||
if bytes.Compare(k.ASCIICode, b) == 0 {
|
||||
return k.Key
|
||||
}
|
||||
}
|
||||
return NotDefined
|
||||
}
|
||||
|
||||
// winsize is winsize struct got from the ioctl(2) system call.
|
||||
type ioctlWinsize struct {
|
||||
Row uint16
|
||||
Col uint16
|
||||
X uint16 // pixel value
|
||||
Y uint16 // pixel value
|
||||
// Read returns byte array.
|
||||
func (p *WindowsParser) Read() ([]byte, error) {
|
||||
var ev uint32
|
||||
r0, _, err := procGetNumberOfConsoleInputEvents.Call(p.tty.Input().Fd(), uintptr(unsafe.Pointer(&ev)))
|
||||
if r0 == 0 {
|
||||
return nil, err
|
||||
}
|
||||
if ev == 0 {
|
||||
return nil, errors.New("EAGAIN")
|
||||
}
|
||||
|
||||
r, err := p.tty.ReadRune()
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
buf := make([]byte, maxReadBytes)
|
||||
n := utf8.EncodeRune(buf[:], r)
|
||||
for p.tty.Buffered() && n < maxReadBytes {
|
||||
r, err := p.tty.ReadRune()
|
||||
if err != nil {
|
||||
break
|
||||
}
|
||||
n += utf8.EncodeRune(buf[n:], r)
|
||||
}
|
||||
return buf[:n], nil
|
||||
}
|
||||
|
||||
// GetWinSize returns winsize struct which is the response of ioctl(2).
|
||||
func (t *VT100Parser) GetWinSize() *WinSize {
|
||||
ws := &ioctlWinsize{}
|
||||
retCode, _, errno := syscall.Syscall(
|
||||
syscall.SYS_IOCTL,
|
||||
uintptr(t.fd),
|
||||
uintptr(syscall.TIOCGWINSZ),
|
||||
uintptr(unsafe.Pointer(ws)))
|
||||
|
||||
if int(retCode) == -1 {
|
||||
panic(errno)
|
||||
// GetWinSize returns WinSize object to represent width and height of terminal.
|
||||
func (p *WindowsParser) GetWinSize() *WinSize {
|
||||
w, h, err := p.tty.Size()
|
||||
if err != nil {
|
||||
panic(err)
|
||||
}
|
||||
return &WinSize{
|
||||
Row: ws.Row,
|
||||
Col: ws.Col,
|
||||
Row: uint16(h),
|
||||
Col: uint16(w),
|
||||
}
|
||||
}
|
||||
|
||||
var asciiSequences []*ASCIICode = []*ASCIICode{
|
||||
var asciiSequences = []*ASCIICode{
|
||||
{Key: Escape, ASCIICode: []byte{0x1b}},
|
||||
|
||||
{Key: ControlSpace, ASCIICode: []byte{0x00}},
|
||||
@@ -237,10 +225,7 @@ var asciiSequences []*ASCIICode = []*ASCIICode{
|
||||
{Key: Ignore, ASCIICode: []byte{0x1b, 0x5b, 0x46}}, // Linux console
|
||||
}
|
||||
|
||||
var _ ConsoleParser = &VT100Parser{}
|
||||
|
||||
func NewVT100StandardInputParser() *VT100Parser {
|
||||
return &VT100Parser{
|
||||
fd: syscall.Stdin,
|
||||
}
|
||||
// NewStandardInputParser returns ConsoleParser object to read from stdin.
|
||||
func NewStandardInputParser() *WindowsParser {
|
||||
return &WindowsParser{}
|
||||
}
|
||||
327
vendor/github.com/c-bata/go-prompt/windows_output.go
generated
vendored
Normal file
327
vendor/github.com/c-bata/go-prompt/windows_output.go
generated
vendored
Normal file
@@ -0,0 +1,327 @@
|
||||
// +build windows
|
||||
|
||||
package prompt
|
||||
|
||||
import (
|
||||
"bytes"
|
||||
"io"
|
||||
"strconv"
|
||||
|
||||
"github.com/mattn/go-colorable"
|
||||
)
|
||||
|
||||
// WindowsWriter is a ConsoleWriter implementation for Win32 console.
|
||||
// Output is converted from VT100 escape sequences by mattn/go-colorable.
|
||||
type WindowsWriter struct {
|
||||
out io.Writer
|
||||
buffer []byte
|
||||
}
|
||||
|
||||
// WriteRaw to write raw byte array
|
||||
func (w *WindowsWriter) WriteRaw(data []byte) {
|
||||
w.buffer = append(w.buffer, data...)
|
||||
return
|
||||
}
|
||||
|
||||
// Write to write safety byte array by removing control sequences.
|
||||
func (w *WindowsWriter) Write(data []byte) {
|
||||
w.WriteRaw(bytes.Replace(data, []byte{0x1b}, []byte{'?'}, -1))
|
||||
return
|
||||
}
|
||||
|
||||
// WriteRawStr to write raw string
|
||||
func (w *WindowsWriter) WriteRawStr(data string) {
|
||||
w.WriteRaw([]byte(data))
|
||||
return
|
||||
}
|
||||
|
||||
// WriteStr to write safety string by removing control sequences.
|
||||
func (w *WindowsWriter) WriteStr(data string) {
|
||||
w.Write([]byte(data))
|
||||
return
|
||||
}
|
||||
|
||||
// Flush to flush buffer
|
||||
func (w *WindowsWriter) Flush() error {
|
||||
_, err := w.out.Write(w.buffer)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
w.buffer = []byte{}
|
||||
return nil
|
||||
}
|
||||
|
||||
/* Erase */
|
||||
|
||||
// EraseScreen erases the screen with the background colour and moves the cursor to home.
|
||||
func (w *WindowsWriter) EraseScreen() {
|
||||
w.WriteRaw([]byte{0x1b, 0x5b, 0x32, 0x4a})
|
||||
return
|
||||
}
|
||||
|
||||
// EraseUp erases the screen from the current line up to the top of the screen.
|
||||
func (w *WindowsWriter) EraseUp() {
|
||||
w.WriteRaw([]byte{0x1b, 0x5b, 0x31, 0x4a})
|
||||
return
|
||||
}
|
||||
|
||||
// EraseDown erases the screen from the current line down to the bottom of the screen.
|
||||
func (w *WindowsWriter) EraseDown() {
|
||||
w.WriteRaw([]byte{0x1b, 0x5b, 0x4a})
|
||||
return
|
||||
}
|
||||
|
||||
// EraseStartOfLine erases from the current cursor position to the start of the current line.
|
||||
func (w *WindowsWriter) EraseStartOfLine() {
|
||||
w.WriteRaw([]byte{0x1b, 0x5b, 0x31, 0x4b})
|
||||
return
|
||||
}
|
||||
|
||||
// EraseEndOfLine erases from the current cursor position to the end of the current line.
|
||||
func (w *WindowsWriter) EraseEndOfLine() {
|
||||
w.WriteRaw([]byte{0x1b, 0x5b, 0x4b})
|
||||
return
|
||||
}
|
||||
|
||||
// EraseLine erases the entire current line.
|
||||
func (w *WindowsWriter) EraseLine() {
|
||||
w.WriteRaw([]byte{0x1b, 0x5b, 0x32, 0x4b})
|
||||
return
|
||||
}
|
||||
|
||||
/* Cursor */
|
||||
|
||||
// ShowCursor stops blinking cursor and show.
|
||||
func (w *WindowsWriter) ShowCursor() {
|
||||
w.WriteRaw([]byte{0x1b, 0x5b, 0x3f, 0x31, 0x32, 0x6c, 0x1b, 0x5b, 0x3f, 0x32, 0x35, 0x68})
|
||||
}
|
||||
|
||||
// HideCursor hides cursor.
|
||||
func (w *WindowsWriter) HideCursor() {
|
||||
w.WriteRaw([]byte{0x1b, 0x5b, 0x3f, 0x32, 0x35, 0x6c})
|
||||
return
|
||||
}
|
||||
|
||||
// CursorGoTo sets the cursor position where subsequent text will begin.
|
||||
func (w *WindowsWriter) CursorGoTo(row, col int) {
|
||||
if row == 0 && col == 0 {
|
||||
// If no row/column parameters are provided (ie. <ESC>[H), the cursor will move to the home position.
|
||||
w.WriteRaw([]byte{0x1b, 0x5b, 0x3b, 0x48})
|
||||
return
|
||||
}
|
||||
r := strconv.Itoa(row)
|
||||
c := strconv.Itoa(col)
|
||||
w.WriteRaw([]byte{0x1b, 0x5b})
|
||||
w.WriteRaw([]byte(r))
|
||||
w.WriteRaw([]byte{0x3b})
|
||||
w.WriteRaw([]byte(c))
|
||||
w.WriteRaw([]byte{0x48})
|
||||
return
|
||||
}
|
||||
|
||||
// CursorUp moves the cursor up by 'n' rows; the default count is 1.
|
||||
func (w *WindowsWriter) CursorUp(n int) {
|
||||
if n < 0 {
|
||||
w.CursorDown(n)
|
||||
return
|
||||
}
|
||||
s := strconv.Itoa(n)
|
||||
w.WriteRaw([]byte{0x1b, 0x5b})
|
||||
w.WriteRaw([]byte(s))
|
||||
w.WriteRaw([]byte{0x41})
|
||||
return
|
||||
}
|
||||
|
||||
// CursorDown moves the cursor down by 'n' rows; the default count is 1.
|
||||
func (w *WindowsWriter) CursorDown(n int) {
|
||||
if n < 0 {
|
||||
w.CursorUp(n)
|
||||
return
|
||||
}
|
||||
s := strconv.Itoa(n)
|
||||
w.WriteRaw([]byte{0x1b, 0x5b})
|
||||
w.WriteRaw([]byte(s))
|
||||
w.WriteRaw([]byte{0x42})
|
||||
return
|
||||
}
|
||||
|
||||
// CursorForward moves the cursor forward by 'n' columns; the default count is 1.
|
||||
func (w *WindowsWriter) CursorForward(n int) {
|
||||
if n == 0 {
|
||||
return
|
||||
} else if n < 0 {
|
||||
w.CursorBackward(-n)
|
||||
return
|
||||
}
|
||||
s := strconv.Itoa(n)
|
||||
w.WriteRaw([]byte{0x1b, 0x5b})
|
||||
w.WriteRaw([]byte(s))
|
||||
w.WriteRaw([]byte{0x43})
|
||||
return
|
||||
}
|
||||
|
||||
// CursorBackward moves the cursor backward by 'n' columns; the default count is 1.
|
||||
func (w *WindowsWriter) CursorBackward(n int) {
|
||||
if n == 0 {
|
||||
return
|
||||
} else if n < 0 {
|
||||
w.CursorForward(-n)
|
||||
return
|
||||
}
|
||||
s := strconv.Itoa(n)
|
||||
w.WriteRaw([]byte{0x1b, 0x5b})
|
||||
w.WriteRaw([]byte(s))
|
||||
w.WriteRaw([]byte{0x44})
|
||||
return
|
||||
}
|
||||
|
||||
// AskForCPR asks for a cursor position report (CPR).
|
||||
func (w *WindowsWriter) AskForCPR() {
|
||||
// CPR: Cursor Position Request.
|
||||
w.WriteRaw([]byte{0x1b, 0x5b, 0x36, 0x6e})
|
||||
w.Flush()
|
||||
return
|
||||
}
|
||||
|
||||
// SaveCursor saves current cursor position.
|
||||
func (w *WindowsWriter) SaveCursor() {
|
||||
w.WriteRaw([]byte{0x1b, 0x5b, 0x73})
|
||||
return
|
||||
}
|
||||
|
||||
// UnSaveCursor restores cursor position after a Save Cursor.
|
||||
func (w *WindowsWriter) UnSaveCursor() {
|
||||
w.WriteRaw([]byte{0x1b, 0x5b, 0x75})
|
||||
return
|
||||
}
|
||||
|
||||
/* Scrolling */
|
||||
|
||||
// ScrollDown scrolls display down one line.
|
||||
func (w *WindowsWriter) ScrollDown() {
|
||||
w.WriteRaw([]byte{0x1b, 0x44})
|
||||
return
|
||||
}
|
||||
|
||||
// ScrollUp scroll display up one line.
|
||||
func (w *WindowsWriter) ScrollUp() {
|
||||
w.WriteRaw([]byte{0x1b, 0x4d})
|
||||
return
|
||||
}
|
||||
|
||||
/* Title */
|
||||
|
||||
// SetTitle sets a title of terminal window.
|
||||
func (w *WindowsWriter) SetTitle(title string) {
|
||||
titleBytes := []byte(title)
|
||||
patterns := []struct {
|
||||
from []byte
|
||||
to []byte
|
||||
}{
|
||||
{
|
||||
from: []byte{0x13},
|
||||
to: []byte{},
|
||||
},
|
||||
{
|
||||
from: []byte{0x07},
|
||||
to: []byte{},
|
||||
},
|
||||
}
|
||||
for i := range patterns {
|
||||
titleBytes = bytes.Replace(titleBytes, patterns[i].from, patterns[i].to, -1)
|
||||
}
|
||||
|
||||
w.WriteRaw([]byte{0x1b, 0x5d, 0x32, 0x3b})
|
||||
w.WriteRaw(titleBytes)
|
||||
w.WriteRaw([]byte{0x07})
|
||||
return
|
||||
}
|
||||
|
||||
// ClearTitle clears a title of terminal window.
|
||||
func (w *WindowsWriter) ClearTitle() {
|
||||
w.WriteRaw([]byte{0x1b, 0x5d, 0x32, 0x3b, 0x07})
|
||||
return
|
||||
}
|
||||
|
||||
/* Font */
|
||||
|
||||
// SetColor sets text and background colors. and specify whether text is bold.
|
||||
func (w *WindowsWriter) SetColor(fg, bg Color, bold bool) {
|
||||
f, ok := foregroundANSIColors[fg]
|
||||
if !ok {
|
||||
f, _ = foregroundANSIColors[DefaultColor]
|
||||
}
|
||||
b, ok := backgroundANSIColors[bg]
|
||||
if !ok {
|
||||
b, _ = backgroundANSIColors[DefaultColor]
|
||||
}
|
||||
w.WriteRaw([]byte{0x1b, 0x5b})
|
||||
if !bold {
|
||||
w.WriteRaw([]byte{0x30, 0x3b})
|
||||
}
|
||||
w.WriteRaw(f)
|
||||
w.WriteRaw([]byte{0x3b})
|
||||
w.WriteRaw(b)
|
||||
if bold {
|
||||
w.WriteRaw([]byte{0x3b, 0x31})
|
||||
}
|
||||
w.WriteRaw([]byte{0x6d})
|
||||
return
|
||||
}
|
||||
|
||||
var foregroundANSIColors = map[Color][]byte{
|
||||
DefaultColor: {0x33, 0x39}, // 39
|
||||
|
||||
// Low intensity.
|
||||
Black: {0x33, 0x30}, // 30
|
||||
DarkRed: {0x33, 0x31}, // 31
|
||||
DarkGreen: {0x33, 0x32}, // 32
|
||||
Brown: {0x33, 0x33}, // 33
|
||||
DarkBlue: {0x33, 0x34}, // 34
|
||||
Purple: {0x33, 0x35}, // 35
|
||||
Cyan: {0x33, 0x36}, //36
|
||||
LightGray: {0x33, 0x37}, //37
|
||||
|
||||
// High intensity.
|
||||
DarkGray: {0x39, 0x30}, // 90
|
||||
Red: {0x39, 0x31}, // 91
|
||||
Green: {0x39, 0x32}, // 92
|
||||
Yellow: {0x39, 0x33}, // 93
|
||||
Blue: {0x39, 0x34}, // 94
|
||||
Fuchsia: {0x39, 0x35}, // 95
|
||||
Turquoise: {0x39, 0x36}, // 96
|
||||
White: {0x39, 0x37}, // 97
|
||||
}
|
||||
|
||||
var backgroundANSIColors = map[Color][]byte{
|
||||
DefaultColor: {0x34, 0x39}, // 49
|
||||
|
||||
// Low intensity.
|
||||
Black: {0x34, 0x30}, // 40
|
||||
DarkRed: {0x34, 0x31}, // 41
|
||||
DarkGreen: {0x34, 0x32}, // 42
|
||||
Brown: {0x34, 0x33}, // 43
|
||||
DarkBlue: {0x34, 0x34}, // 44
|
||||
Purple: {0x34, 0x35}, // 45
|
||||
Cyan: {0x34, 0x36}, // 46
|
||||
LightGray: {0x34, 0x37}, // 47
|
||||
|
||||
// High intensity
|
||||
DarkGray: {0x31, 0x30, 0x30}, // 100
|
||||
Red: {0x31, 0x30, 0x31}, // 101
|
||||
Green: {0x31, 0x30, 0x32}, // 102
|
||||
Yellow: {0x31, 0x30, 0x33}, // 103
|
||||
Blue: {0x31, 0x30, 0x34}, // 104
|
||||
Fuchsia: {0x31, 0x30, 0x35}, // 105
|
||||
Turquoise: {0x31, 0x30, 0x36}, // 106
|
||||
White: {0x31, 0x30, 0x37}, // 107
|
||||
}
|
||||
|
||||
var _ ConsoleWriter = &WindowsWriter{}
|
||||
|
||||
// NewStandardOutputWriter returns ConsoleWriter object to write to stdout.
|
||||
func NewStandardOutputWriter() *WindowsWriter {
|
||||
return &WindowsWriter{
|
||||
out: colorable.NewColorableStdout(),
|
||||
}
|
||||
}
|
||||
4
vendor/github.com/mattn/go-tty/.travis.yml
generated
vendored
Normal file
4
vendor/github.com/mattn/go-tty/.travis.yml
generated
vendored
Normal file
@@ -0,0 +1,4 @@
|
||||
language: go
|
||||
sudo: false
|
||||
go:
|
||||
- tip
|
||||
49
vendor/github.com/mattn/go-tty/README.md
generated
vendored
Normal file
49
vendor/github.com/mattn/go-tty/README.md
generated
vendored
Normal file
@@ -0,0 +1,49 @@
|
||||
# go-tty
|
||||
|
||||
Simple tty utility
|
||||
|
||||
## Usage
|
||||
|
||||
```go
|
||||
tty, err := tty.Open()
|
||||
if err != nil {
|
||||
log.Fatal(err)
|
||||
}
|
||||
defer tty.Close()
|
||||
|
||||
for {
|
||||
r, err := tty.ReadRune()
|
||||
if err != nil {
|
||||
log.Fatal(err)
|
||||
}
|
||||
// handle key event
|
||||
}
|
||||
```
|
||||
|
||||
if you are on windows and want to display ANSI colors, use <a href="https://github.com/mattn/go-colorable">go-colorable</a>.
|
||||
|
||||
```go
|
||||
tty, err := tty.Open()
|
||||
if err != nil {
|
||||
log.Fatal(err)
|
||||
}
|
||||
defer tty.Close()
|
||||
|
||||
out := colorable.NewColorable(tty.Output())
|
||||
|
||||
fmt.Fprintln(out, "\x1b[2J")
|
||||
```
|
||||
|
||||
## Installation
|
||||
|
||||
```
|
||||
$ go get github.com/mattn/go-tty
|
||||
```
|
||||
|
||||
## License
|
||||
|
||||
MIT
|
||||
|
||||
## Author
|
||||
|
||||
Yasuhiro Matsumoto (a.k.a mattn)
|
||||
120
vendor/github.com/mattn/go-tty/tty.go
generated
vendored
Normal file
120
vendor/github.com/mattn/go-tty/tty.go
generated
vendored
Normal file
@@ -0,0 +1,120 @@
|
||||
package tty
|
||||
|
||||
import (
|
||||
"os"
|
||||
"strings"
|
||||
"unicode"
|
||||
)
|
||||
|
||||
func Open() (*TTY, error) {
|
||||
return open()
|
||||
}
|
||||
|
||||
func (tty *TTY) Raw() (func() error, error) {
|
||||
return tty.raw()
|
||||
}
|
||||
|
||||
func (tty *TTY) MustRaw() func() error {
|
||||
f, err := tty.raw()
|
||||
if err != nil {
|
||||
panic(err.Error())
|
||||
}
|
||||
return f
|
||||
}
|
||||
|
||||
func (tty *TTY) Buffered() bool {
|
||||
return tty.buffered()
|
||||
}
|
||||
|
||||
func (tty *TTY) ReadRune() (rune, error) {
|
||||
return tty.readRune()
|
||||
}
|
||||
|
||||
func (tty *TTY) Close() error {
|
||||
return tty.close()
|
||||
}
|
||||
|
||||
func (tty *TTY) Size() (int, int, error) {
|
||||
return tty.size()
|
||||
}
|
||||
|
||||
func (tty *TTY) Input() *os.File {
|
||||
return tty.input()
|
||||
}
|
||||
|
||||
func (tty *TTY) Output() *os.File {
|
||||
return tty.output()
|
||||
}
|
||||
|
||||
// Display types.
|
||||
const (
|
||||
displayNone = iota
|
||||
displayRune
|
||||
displayMask
|
||||
)
|
||||
|
||||
func (tty *TTY) readString(displayType int) (string, error) {
|
||||
rs := []rune{}
|
||||
loop:
|
||||
for {
|
||||
r, err := tty.readRune()
|
||||
if err != nil {
|
||||
return "", err
|
||||
}
|
||||
switch r {
|
||||
case 13:
|
||||
break loop
|
||||
case 8, 127:
|
||||
if len(rs) > 0 {
|
||||
rs = rs[:len(rs)-1]
|
||||
if displayType != displayNone {
|
||||
tty.Output().WriteString("\b \b")
|
||||
}
|
||||
}
|
||||
default:
|
||||
if unicode.IsPrint(r) {
|
||||
rs = append(rs, r)
|
||||
switch displayType {
|
||||
case displayRune:
|
||||
tty.Output().WriteString(string(r))
|
||||
case displayMask:
|
||||
tty.Output().WriteString("*")
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
return string(rs), nil
|
||||
}
|
||||
|
||||
func (tty *TTY) ReadString() (string, error) {
|
||||
defer tty.Output().WriteString("\n")
|
||||
return tty.readString(displayRune)
|
||||
}
|
||||
|
||||
func (tty *TTY) ReadPassword() (string, error) {
|
||||
defer tty.Output().WriteString("\n")
|
||||
return tty.readString(displayMask)
|
||||
}
|
||||
|
||||
func (tty *TTY) ReadPasswordNoEcho() (string, error) {
|
||||
defer tty.Output().WriteString("\n")
|
||||
return tty.readString(displayNone)
|
||||
}
|
||||
|
||||
func (tty *TTY) ReadPasswordClear() (string, error) {
|
||||
s, err := tty.readString(displayMask)
|
||||
tty.Output().WriteString(
|
||||
strings.Repeat("\b", len(s)) +
|
||||
strings.Repeat(" ", len(s)) +
|
||||
strings.Repeat("\b", len(s)))
|
||||
return s, err
|
||||
}
|
||||
|
||||
type WINSIZE struct {
|
||||
W int
|
||||
H int
|
||||
}
|
||||
|
||||
func (tty *TTY) SIGWINCH() chan WINSIZE {
|
||||
return tty.sigwinch()
|
||||
}
|
||||
12
vendor/github.com/mattn/go-tty/tty_bsd.go
generated
vendored
Normal file
12
vendor/github.com/mattn/go-tty/tty_bsd.go
generated
vendored
Normal file
@@ -0,0 +1,12 @@
|
||||
// +build darwin dragonfly freebsd netbsd openbsd
|
||||
|
||||
package tty
|
||||
|
||||
import (
|
||||
"syscall"
|
||||
)
|
||||
|
||||
const (
|
||||
ioctlReadTermios = syscall.TIOCGETA
|
||||
ioctlWriteTermios = syscall.TIOCSETA
|
||||
)
|
||||
8
vendor/github.com/mattn/go-tty/tty_linux.go
generated
vendored
Normal file
8
vendor/github.com/mattn/go-tty/tty_linux.go
generated
vendored
Normal file
@@ -0,0 +1,8 @@
|
||||
// +build linux
|
||||
|
||||
package tty
|
||||
|
||||
const (
|
||||
ioctlReadTermios = 0x5401 // syscall.TCGETS
|
||||
ioctlWriteTermios = 0x5402 // syscall.TCSETS
|
||||
)
|
||||
63
vendor/github.com/mattn/go-tty/tty_plan9.go
generated
vendored
Normal file
63
vendor/github.com/mattn/go-tty/tty_plan9.go
generated
vendored
Normal file
@@ -0,0 +1,63 @@
|
||||
package tty
|
||||
|
||||
import (
|
||||
"bufio"
|
||||
"os"
|
||||
"syscall"
|
||||
)
|
||||
|
||||
type TTY struct {
|
||||
in *os.File
|
||||
bin *bufio.Reader
|
||||
out *os.File
|
||||
}
|
||||
|
||||
func open() (*TTY, error) {
|
||||
tty := new(TTY)
|
||||
|
||||
in, err := os.Open("/dev/cons")
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
tty.in = in
|
||||
tty.bin = bufio.NewReader(in)
|
||||
|
||||
out, err := os.OpenFile("/dev/cons", syscall.O_WRONLY, 0)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
tty.out = out
|
||||
|
||||
return tty, nil
|
||||
}
|
||||
|
||||
func (tty *TTY) buffered() bool {
|
||||
return tty.bin.Buffered() > 0
|
||||
}
|
||||
|
||||
func (tty *TTY) readRune() (rune, error) {
|
||||
r, _, err := tty.bin.ReadRune()
|
||||
return r, err
|
||||
}
|
||||
|
||||
func (tty *TTY) close() (err error) {
|
||||
if err2 := tty.in.Close(); err2 != nil {
|
||||
err = err2
|
||||
}
|
||||
if err2 := tty.out.Close(); err2 != nil {
|
||||
err = err2
|
||||
}
|
||||
return
|
||||
}
|
||||
|
||||
func (tty *TTY) size() (int, int, error) {
|
||||
return 80, 24, nil
|
||||
}
|
||||
|
||||
func (tty *TTY) input() *os.File {
|
||||
return tty.in
|
||||
}
|
||||
|
||||
func (tty *TTY) output() *os.File {
|
||||
return tty.out
|
||||
}
|
||||
126
vendor/github.com/mattn/go-tty/tty_unix.go
generated
vendored
Normal file
126
vendor/github.com/mattn/go-tty/tty_unix.go
generated
vendored
Normal file
@@ -0,0 +1,126 @@
|
||||
// +build !windows
|
||||
// +build !plan9
|
||||
|
||||
package tty
|
||||
|
||||
import (
|
||||
"bufio"
|
||||
"os"
|
||||
"os/signal"
|
||||
"syscall"
|
||||
"unsafe"
|
||||
|
||||
"golang.org/x/sys/unix"
|
||||
)
|
||||
|
||||
type TTY struct {
|
||||
in *os.File
|
||||
bin *bufio.Reader
|
||||
out *os.File
|
||||
termios syscall.Termios
|
||||
ws chan WINSIZE
|
||||
ss chan os.Signal
|
||||
}
|
||||
|
||||
func open() (*TTY, error) {
|
||||
tty := new(TTY)
|
||||
|
||||
in, err := os.Open("/dev/tty")
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
tty.in = in
|
||||
tty.bin = bufio.NewReader(in)
|
||||
|
||||
out, err := os.OpenFile("/dev/tty", syscall.O_WRONLY, 0)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
tty.out = out
|
||||
|
||||
if _, _, err := syscall.Syscall6(syscall.SYS_IOCTL, uintptr(tty.in.Fd()), ioctlReadTermios, uintptr(unsafe.Pointer(&tty.termios)), 0, 0, 0); err != 0 {
|
||||
return nil, err
|
||||
}
|
||||
newios := tty.termios
|
||||
newios.Iflag &^= syscall.ISTRIP | syscall.INLCR | syscall.ICRNL | syscall.IGNCR | syscall.IXON | syscall.IXOFF
|
||||
newios.Lflag &^= syscall.ECHO | syscall.ICANON /*| syscall.ISIG*/
|
||||
if _, _, err := syscall.Syscall6(syscall.SYS_IOCTL, uintptr(tty.in.Fd()), ioctlWriteTermios, uintptr(unsafe.Pointer(&newios)), 0, 0, 0); err != 0 {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
tty.ws = make(chan WINSIZE)
|
||||
tty.ss = make(chan os.Signal, 1)
|
||||
signal.Notify(tty.ss, syscall.SIGWINCH)
|
||||
go func() {
|
||||
for sig := range tty.ss {
|
||||
switch sig {
|
||||
case syscall.SIGWINCH:
|
||||
if w, h, err := tty.size(); err == nil {
|
||||
tty.ws <- WINSIZE{
|
||||
W: w,
|
||||
H: h,
|
||||
}
|
||||
}
|
||||
default:
|
||||
}
|
||||
}
|
||||
}()
|
||||
return tty, nil
|
||||
}
|
||||
|
||||
func (tty *TTY) buffered() bool {
|
||||
return tty.bin.Buffered() > 0
|
||||
}
|
||||
|
||||
func (tty *TTY) readRune() (rune, error) {
|
||||
r, _, err := tty.bin.ReadRune()
|
||||
return r, err
|
||||
}
|
||||
|
||||
func (tty *TTY) close() error {
|
||||
close(tty.ss)
|
||||
close(tty.ws)
|
||||
_, _, err := syscall.Syscall6(syscall.SYS_IOCTL, uintptr(tty.in.Fd()), ioctlWriteTermios, uintptr(unsafe.Pointer(&tty.termios)), 0, 0, 0)
|
||||
return err
|
||||
}
|
||||
|
||||
func (tty *TTY) size() (int, int, error) {
|
||||
var dim [4]uint16
|
||||
if _, _, err := syscall.Syscall6(syscall.SYS_IOCTL, uintptr(tty.out.Fd()), uintptr(syscall.TIOCGWINSZ), uintptr(unsafe.Pointer(&dim)), 0, 0, 0); err != 0 {
|
||||
return -1, -1, err
|
||||
}
|
||||
return int(dim[1]), int(dim[0]), nil
|
||||
}
|
||||
|
||||
func (tty *TTY) input() *os.File {
|
||||
return tty.in
|
||||
}
|
||||
|
||||
func (tty *TTY) output() *os.File {
|
||||
return tty.out
|
||||
}
|
||||
|
||||
func (tty *TTY) raw() (func() error, error) {
|
||||
termios, err := unix.IoctlGetTermios(int(tty.in.Fd()), ioctlReadTermios)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
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(tty.in.Fd()), ioctlWriteTermios, termios); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
return func() error {
|
||||
if err := unix.IoctlSetTermios(int(tty.in.Fd()), ioctlWriteTermios, termios); err != nil {
|
||||
return err
|
||||
}
|
||||
return nil
|
||||
}, nil
|
||||
}
|
||||
323
vendor/github.com/mattn/go-tty/tty_windows.go
generated
vendored
Normal file
323
vendor/github.com/mattn/go-tty/tty_windows.go
generated
vendored
Normal file
@@ -0,0 +1,323 @@
|
||||
// +build windows
|
||||
|
||||
package tty
|
||||
|
||||
import (
|
||||
"os"
|
||||
"syscall"
|
||||
"unsafe"
|
||||
|
||||
"github.com/mattn/go-isatty"
|
||||
)
|
||||
|
||||
const (
|
||||
rightAltPressed = 1
|
||||
leftAltPressed = 2
|
||||
rightCtrlPressed = 4
|
||||
leftCtrlPressed = 8
|
||||
shiftPressed = 0x0010
|
||||
ctrlPressed = rightCtrlPressed | leftCtrlPressed
|
||||
altPressed = rightAltPressed | leftAltPressed
|
||||
)
|
||||
|
||||
const (
|
||||
enableProcessedInput = 0x1
|
||||
enableLineInput = 0x2
|
||||
enableEchoInput = 0x4
|
||||
enableWindowInput = 0x8
|
||||
enableMouseInput = 0x10
|
||||
enableInsertMode = 0x20
|
||||
enableQuickEditMode = 0x40
|
||||
enableExtendedFlag = 0x80
|
||||
|
||||
enableProcessedOutput = 1
|
||||
enableWrapAtEolOutput = 2
|
||||
|
||||
keyEvent = 0x1
|
||||
mouseEvent = 0x2
|
||||
windowBufferSizeEvent = 0x4
|
||||
)
|
||||
|
||||
var kernel32 = syscall.NewLazyDLL("kernel32.dll")
|
||||
|
||||
var (
|
||||
procAllocConsole = kernel32.NewProc("AllocConsole")
|
||||
procSetStdHandle = kernel32.NewProc("SetStdHandle")
|
||||
procGetStdHandle = kernel32.NewProc("GetStdHandle")
|
||||
procSetConsoleScreenBufferSize = kernel32.NewProc("SetConsoleScreenBufferSize")
|
||||
procCreateConsoleScreenBuffer = kernel32.NewProc("CreateConsoleScreenBuffer")
|
||||
procGetConsoleScreenBufferInfo = kernel32.NewProc("GetConsoleScreenBufferInfo")
|
||||
procWriteConsoleOutputCharacter = kernel32.NewProc("WriteConsoleOutputCharacterW")
|
||||
procWriteConsoleOutputAttribute = kernel32.NewProc("WriteConsoleOutputAttribute")
|
||||
procGetConsoleCursorInfo = kernel32.NewProc("GetConsoleCursorInfo")
|
||||
procSetConsoleCursorInfo = kernel32.NewProc("SetConsoleCursorInfo")
|
||||
procSetConsoleCursorPosition = kernel32.NewProc("SetConsoleCursorPosition")
|
||||
procReadConsoleInput = kernel32.NewProc("ReadConsoleInputW")
|
||||
procGetConsoleMode = kernel32.NewProc("GetConsoleMode")
|
||||
procSetConsoleMode = kernel32.NewProc("SetConsoleMode")
|
||||
procFillConsoleOutputCharacter = kernel32.NewProc("FillConsoleOutputCharacterW")
|
||||
procFillConsoleOutputAttribute = kernel32.NewProc("FillConsoleOutputAttribute")
|
||||
procScrollConsoleScreenBuffer = kernel32.NewProc("ScrollConsoleScreenBufferW")
|
||||
)
|
||||
|
||||
type wchar uint16
|
||||
type short int16
|
||||
type dword uint32
|
||||
type word uint16
|
||||
|
||||
type coord struct {
|
||||
x short
|
||||
y short
|
||||
}
|
||||
|
||||
type smallRect struct {
|
||||
left short
|
||||
top short
|
||||
right short
|
||||
bottom short
|
||||
}
|
||||
|
||||
type consoleScreenBufferInfo struct {
|
||||
size coord
|
||||
cursorPosition coord
|
||||
attributes word
|
||||
window smallRect
|
||||
maximumWindowSize coord
|
||||
}
|
||||
|
||||
type consoleCursorInfo struct {
|
||||
size dword
|
||||
visible int32
|
||||
}
|
||||
|
||||
type inputRecord struct {
|
||||
eventType word
|
||||
_ [2]byte
|
||||
event [16]byte
|
||||
}
|
||||
|
||||
type keyEventRecord struct {
|
||||
keyDown int32
|
||||
repeatCount word
|
||||
virtualKeyCode word
|
||||
virtualScanCode word
|
||||
unicodeChar wchar
|
||||
controlKeyState dword
|
||||
}
|
||||
|
||||
type windowBufferSizeRecord struct {
|
||||
size coord
|
||||
}
|
||||
|
||||
type mouseEventRecord struct {
|
||||
mousePos coord
|
||||
buttonState dword
|
||||
controlKeyState dword
|
||||
eventFlags dword
|
||||
}
|
||||
|
||||
type charInfo struct {
|
||||
unicodeChar wchar
|
||||
attributes word
|
||||
}
|
||||
|
||||
type TTY struct {
|
||||
in *os.File
|
||||
out *os.File
|
||||
st uint32
|
||||
rs []rune
|
||||
ws chan WINSIZE
|
||||
}
|
||||
|
||||
func readConsoleInput(fd uintptr, record *inputRecord) (err error) {
|
||||
var w uint32
|
||||
r1, _, err := procReadConsoleInput.Call(fd, uintptr(unsafe.Pointer(record)), 1, uintptr(unsafe.Pointer(&w)))
|
||||
if r1 == 0 {
|
||||
return err
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
func open() (*TTY, error) {
|
||||
tty := new(TTY)
|
||||
if false && isatty.IsTerminal(os.Stdin.Fd()) {
|
||||
tty.in = os.Stdin
|
||||
} else {
|
||||
conin, err := os.Open("CONIN$")
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
tty.in = conin
|
||||
}
|
||||
|
||||
if isatty.IsTerminal(os.Stdout.Fd()) {
|
||||
tty.out = os.Stdout
|
||||
} else {
|
||||
procAllocConsole.Call()
|
||||
out, err := syscall.Open("CONOUT$", syscall.O_RDWR, 0)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
tty.out = os.NewFile(uintptr(out), "/dev/tty")
|
||||
}
|
||||
|
||||
h := tty.in.Fd()
|
||||
var st uint32
|
||||
r1, _, err := procGetConsoleMode.Call(h, uintptr(unsafe.Pointer(&st)))
|
||||
if r1 == 0 {
|
||||
return nil, err
|
||||
}
|
||||
tty.st = st
|
||||
|
||||
st &^= enableEchoInput
|
||||
st &^= enableInsertMode
|
||||
st &^= enableLineInput
|
||||
st &^= enableMouseInput
|
||||
st &^= enableWindowInput
|
||||
st &^= enableExtendedFlag
|
||||
st &^= enableQuickEditMode
|
||||
st |= enableProcessedInput
|
||||
|
||||
// ignore error
|
||||
procSetConsoleMode.Call(h, uintptr(st))
|
||||
|
||||
tty.ws = make(chan WINSIZE)
|
||||
|
||||
return tty, nil
|
||||
}
|
||||
|
||||
func (tty *TTY) buffered() bool {
|
||||
return len(tty.rs) > 0
|
||||
}
|
||||
|
||||
func (tty *TTY) readRune() (rune, error) {
|
||||
if len(tty.rs) > 0 {
|
||||
r := tty.rs[0]
|
||||
tty.rs = tty.rs[1:]
|
||||
return r, nil
|
||||
}
|
||||
var ir inputRecord
|
||||
err := readConsoleInput(tty.in.Fd(), &ir)
|
||||
if err != nil {
|
||||
return 0, err
|
||||
}
|
||||
|
||||
switch ir.eventType {
|
||||
case windowBufferSizeEvent:
|
||||
wr := (*windowBufferSizeRecord)(unsafe.Pointer(&ir.event))
|
||||
tty.ws <- WINSIZE{
|
||||
W: int(wr.size.x),
|
||||
H: int(wr.size.y),
|
||||
}
|
||||
case keyEvent:
|
||||
kr := (*keyEventRecord)(unsafe.Pointer(&ir.event))
|
||||
if kr.keyDown != 0 {
|
||||
if kr.controlKeyState&altPressed != 0 && kr.unicodeChar > 0 {
|
||||
tty.rs = []rune{rune(kr.unicodeChar)}
|
||||
return rune(0x1b), nil
|
||||
}
|
||||
if kr.unicodeChar > 0 {
|
||||
if kr.controlKeyState&shiftPressed != 0 {
|
||||
switch kr.unicodeChar {
|
||||
case 0x09:
|
||||
tty.rs = []rune{0x5b, 0x5a}
|
||||
return rune(0x1b), nil
|
||||
}
|
||||
}
|
||||
return rune(kr.unicodeChar), nil
|
||||
}
|
||||
vk := kr.virtualKeyCode
|
||||
switch vk {
|
||||
case 0x21: // page-up
|
||||
tty.rs = []rune{0x5b, 0x35, 0x7e}
|
||||
return rune(0x1b), nil
|
||||
case 0x22: // page-down
|
||||
tty.rs = []rune{0x5b, 0x36, 0x7e}
|
||||
return rune(0x1b), nil
|
||||
case 0x23: // end
|
||||
tty.rs = []rune{0x5b, 0x46}
|
||||
return rune(0x1b), nil
|
||||
case 0x24: // home
|
||||
tty.rs = []rune{0x5b, 0x48}
|
||||
return rune(0x1b), nil
|
||||
case 0x25: // left
|
||||
tty.rs = []rune{0x5b, 0x44}
|
||||
return rune(0x1b), nil
|
||||
case 0x26: // up
|
||||
tty.rs = []rune{0x5b, 0x41}
|
||||
return rune(0x1b), nil
|
||||
case 0x27: // right
|
||||
tty.rs = []rune{0x5b, 0x43}
|
||||
return rune(0x1b), nil
|
||||
case 0x28: // down
|
||||
tty.rs = []rune{0x5b, 0x42}
|
||||
return rune(0x1b), nil
|
||||
case 0x2e: // delete
|
||||
tty.rs = []rune{0x5b, 0x33, 0x7e}
|
||||
return rune(0x1b), nil
|
||||
case 0x70, 0x71, 0x72, 0x73: // F1,F2,F3,F4
|
||||
tty.rs = []rune{0x5b, 0x4f, rune(vk) - 0x20}
|
||||
return rune(0x1b), nil
|
||||
case 0x074, 0x75, 0x76, 0x77: // F5,F6,F7,F8
|
||||
tty.rs = []rune{0x5b, 0x31, rune(vk) - 0x3f, 0x7e}
|
||||
return rune(0x1b), nil
|
||||
case 0x78, 0x79: // F9,F10
|
||||
tty.rs = []rune{0x5b, 0x32, rune(vk) - 0x48, 0x7e}
|
||||
return rune(0x1b), nil
|
||||
case 0x7a, 0x7b: // F11,F12
|
||||
tty.rs = []rune{0x5b, 0x32, rune(vk) - 0x47, 0x7e}
|
||||
return rune(0x1b), nil
|
||||
}
|
||||
return 0, nil
|
||||
}
|
||||
}
|
||||
return 0, nil
|
||||
}
|
||||
|
||||
func (tty *TTY) close() error {
|
||||
close(tty.ws)
|
||||
procSetConsoleMode.Call(tty.in.Fd(), uintptr(tty.st))
|
||||
return nil
|
||||
}
|
||||
|
||||
func (tty *TTY) size() (int, int, error) {
|
||||
var csbi consoleScreenBufferInfo
|
||||
r1, _, err := procGetConsoleScreenBufferInfo.Call(tty.out.Fd(), uintptr(unsafe.Pointer(&csbi)))
|
||||
if r1 == 0 {
|
||||
return 0, 0, err
|
||||
}
|
||||
return int(csbi.window.right - csbi.window.left + 1), int(csbi.window.bottom - csbi.window.top + 1), nil
|
||||
}
|
||||
|
||||
func (tty *TTY) input() *os.File {
|
||||
return tty.in
|
||||
}
|
||||
|
||||
func (tty *TTY) output() *os.File {
|
||||
return tty.out
|
||||
}
|
||||
|
||||
func (tty *TTY) raw() (func() error, error) {
|
||||
var st uint32
|
||||
r1, _, err := procGetConsoleMode.Call(tty.in.Fd(), uintptr(unsafe.Pointer(&st)))
|
||||
if r1 == 0 {
|
||||
return nil, err
|
||||
}
|
||||
mode := st &^ (enableEchoInput | enableProcessedInput | enableLineInput | enableProcessedOutput)
|
||||
r1, _, err = procSetConsoleMode.Call(tty.in.Fd(), uintptr(mode))
|
||||
if r1 == 0 {
|
||||
return nil, err
|
||||
}
|
||||
return func() error {
|
||||
r1, _, err := procSetConsoleMode.Call(tty.in.Fd(), uintptr(st))
|
||||
if r1 == 0 {
|
||||
return err
|
||||
}
|
||||
return nil
|
||||
}, nil
|
||||
}
|
||||
|
||||
func (tty *TTY) sigwinch() chan WINSIZE {
|
||||
return tty.ws
|
||||
}
|
||||
Reference in New Issue
Block a user