diff --git a/vendor.conf b/vendor.conf index 6d979d64..2fd9d8e7 100644 --- a/vendor.conf +++ b/vendor.conf @@ -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 diff --git a/vendor/github.com/c-bata/go-prompt/CHANGELOG.md b/vendor/github.com/c-bata/go-prompt/CHANGELOG.md index 039f22b7..74eb86d9 100644 --- a/vendor/github.com/c-bata/go-prompt/CHANGELOG.md +++ b/vendor/github.com/c-bata/go-prompt/CHANGELOG.md @@ -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) diff --git a/vendor/github.com/c-bata/go-prompt/DEVELOPER_GUIDE.md b/vendor/github.com/c-bata/go-prompt/DEVELOPER_GUIDE.md deleted file mode 100644 index 8ccdf2f5..00000000 --- a/vendor/github.com/c-bata/go-prompt/DEVELOPER_GUIDE.md +++ /dev/null @@ -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: - -![options](https://github.com/c-bata/assets/raw/master/go-prompt/prompt-options.png) - -* **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 animation](https://github.com/c-bata/assets/raw/master/go-prompt/input-parser.gif) - -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** diff --git a/vendor/github.com/c-bata/go-prompt/Gopkg.lock b/vendor/github.com/c-bata/go-prompt/Gopkg.lock new file mode 100644 index 00000000..b6e96ffa --- /dev/null +++ b/vendor/github.com/c-bata/go-prompt/Gopkg.lock @@ -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 diff --git a/vendor/github.com/c-bata/go-prompt/Gopkg.toml b/vendor/github.com/c-bata/go-prompt/Gopkg.toml new file mode 100644 index 00000000..fe31c575 --- /dev/null +++ b/vendor/github.com/c-bata/go-prompt/Gopkg.toml @@ -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" diff --git a/vendor/github.com/c-bata/go-prompt/Makefile b/vendor/github.com/c-bata/go-prompt/Makefile index 10efb5cb..3cb6edfe 100644 --- a/vendor/github.com/c-bata/go-prompt/Makefile +++ b/vendor/github.com/c-bata/go-prompt/Makefile @@ -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:" diff --git a/vendor/github.com/c-bata/go-prompt/README.md b/vendor/github.com/c-bata/go-prompt/README.md index 31e9eacb..2a36e842 100644 --- a/vendor/github.com/c-bata/go-prompt/README.md +++ b/vendor/github.com/c-bata/go-prompt/README.md @@ -1,7 +1,10 @@ # go-prompt +[![Go Report Card](https://goreportcard.com/badge/github.com/c-bata/go-prompt)](https://goreportcard.com/report/github.com/c-bata/go-prompt) +![Software License](https://img.shields.io/badge/license-MIT-brightgreen.svg?style=flat-square) + 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. [![options](https://github.com/c-bata/assets/raw/master/go-prompt/prompt-options.png)](#flexible-options) @@ -67,6 +74,7 @@ KeyBinding | Description Ctrl + W | Cut the Word before the cursor to the clipboard. Ctrl + K | Cut the Line after the cursor to the clipboard. Ctrl + U | Cut/delete the Line before the cursor to the clipboard. +Ctrl + L | 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](https://github.com/c-bata/assets/raw/master/go-prompt/history.gif)](#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 diff --git a/vendor/github.com/c-bata/go-prompt/bisect.go b/vendor/github.com/c-bata/go-prompt/bisect.go deleted file mode 100644 index 2bcef3a5..00000000 --- a/vendor/github.com/c-bata/go-prompt/bisect.go +++ /dev/null @@ -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 - }) -} diff --git a/vendor/github.com/c-bata/go-prompt/completion.go b/vendor/github.com/c-bata/go-prompt/completion.go index 01417dac..0c0f54bf 100644 --- a/vendor/github.com/c-bata/go-prompt/completion.go +++ b/vendor/github.com/c-bata/go-prompt/completion.go @@ -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, } } diff --git a/vendor/github.com/c-bata/go-prompt/console_interface.go b/vendor/github.com/c-bata/go-prompt/console_interface.go index bfa7f1ef..1de107f9 100644 --- a/vendor/github.com/c-bata/go-prompt/console_interface.go +++ b/vendor/github.com/c-bata/go-prompt/console_interface.go @@ -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) } diff --git a/vendor/github.com/c-bata/go-prompt/document.go b/vendor/github.com/c-bata/go-prompt/document.go index 94a410e2..33928885 100644 --- a/vendor/github.com/c-bata/go-prompt/document.go +++ b/vendor/github.com/c-bata/go-prompt/document.go @@ -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 + }) +} diff --git a/vendor/github.com/c-bata/go-prompt/emacs.go b/vendor/github.com/c-bata/go-prompt/emacs.go index 87138a30..9dc71edc 100644 --- a/vendor/github.com/c-bata/go-prompt/emacs.go +++ b/vendor/github.com/c-bata/go-prompt/emacs.go @@ -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() }, }, } diff --git a/vendor/github.com/c-bata/go-prompt/history.go b/vendor/github.com/c-bata/go-prompt/history.go index d1837131..e75c6459 100644 --- a/vendor/github.com/c-bata/go-prompt/history.go +++ b/vendor/github.com/c-bata/go-prompt/history.go @@ -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{}, diff --git a/vendor/github.com/c-bata/go-prompt/key.go b/vendor/github.com/c-bata/go-prompt/key.go index 9f79185f..068b70e9 100644 --- a/vendor/github.com/c-bata/go-prompt/key.go +++ b/vendor/github.com/c-bata/go-prompt/key.go @@ -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. diff --git a/vendor/github.com/c-bata/go-prompt/key_bind.go b/vendor/github.com/c-bata/go-prompt/key_bind.go index 0cfcab44..91bbe4c5 100644 --- a/vendor/github.com/c-bata/go-prompt/key_bind.go +++ b/vendor/github.com/c-bata/go-prompt/key_bind.go @@ -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{ diff --git a/vendor/github.com/c-bata/go-prompt/key_string.go b/vendor/github.com/c-bata/go-prompt/key_string.go index 24780f4e..fce6ea31 100644 --- a/vendor/github.com/c-bata/go-prompt/key_string.go +++ b/vendor/github.com/c-bata/go-prompt/key_string.go @@ -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]] } diff --git a/vendor/github.com/c-bata/go-prompt/option.go b/vendor/github.com/c-bata/go-prompt/option.go index e457c1c9..10f760f3 100644 --- a/vendor/github.com/c-bata/go-prompt/option.go +++ b/vendor/github.com/c-bata/go-prompt/option.go @@ -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, diff --git a/vendor/github.com/c-bata/go-prompt/posix_input.go b/vendor/github.com/c-bata/go-prompt/posix_input.go new file mode 100644 index 00000000..ead496d9 --- /dev/null +++ b/vendor/github.com/c-bata/go-prompt/posix_input.go @@ -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, + } +} diff --git a/vendor/github.com/c-bata/go-prompt/vt100_output.go b/vendor/github.com/c-bata/go-prompt/posix_output.go similarity index 50% rename from vendor/github.com/c-bata/go-prompt/vt100_output.go rename to vendor/github.com/c-bata/go-prompt/posix_output.go index 12c6fd93..e94ae6ff 100644 --- a/vendor/github.com/c-bata/go-prompt/vt100_output.go +++ b/vendor/github.com/c-bata/go-prompt/posix_output.go @@ -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. [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, } } diff --git a/vendor/github.com/c-bata/go-prompt/prompt.go b/vendor/github.com/c-bata/go-prompt/prompt.go index 9db1f296..008799c4 100644 --- a/vendor/github.com/c-bata/go-prompt/prompt.go +++ b/vendor/github.com/c-bata/go-prompt/prompt.go @@ -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() - } - } - } -} diff --git a/vendor/github.com/c-bata/go-prompt/render.go b/vendor/github.com/c-bata/go-prompt/render.go index ce0eef2e..9432f357 100644 --- a/vendor/github.com/c-bata/go-prompt/render.go +++ b/vendor/github.com/c-bata/go-prompt/render.go @@ -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 + } } diff --git a/vendor/github.com/c-bata/go-prompt/signal_posix.go b/vendor/github.com/c-bata/go-prompt/signal_posix.go new file mode 100644 index 00000000..cff13271 --- /dev/null +++ b/vendor/github.com/c-bata/go-prompt/signal_posix.go @@ -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() + } + } + } +} diff --git a/vendor/github.com/c-bata/go-prompt/signal_windows.go b/vendor/github.com/c-bata/go-prompt/signal_windows.go new file mode 100644 index 00000000..5c34a63c --- /dev/null +++ b/vendor/github.com/c-bata/go-prompt/signal_windows.go @@ -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 + } + } + } +} diff --git a/vendor/github.com/c-bata/go-prompt/vt100_input.go b/vendor/github.com/c-bata/go-prompt/windows_input.go similarity index 75% rename from vendor/github.com/c-bata/go-prompt/vt100_input.go rename to vendor/github.com/c-bata/go-prompt/windows_input.go index 155f3cce..2de11f62 100644 --- a/vendor/github.com/c-bata/go-prompt/vt100_input.go +++ b/vendor/github.com/c-bata/go-prompt/windows_input.go @@ -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{} } diff --git a/vendor/github.com/c-bata/go-prompt/windows_output.go b/vendor/github.com/c-bata/go-prompt/windows_output.go new file mode 100644 index 00000000..d3360030 --- /dev/null +++ b/vendor/github.com/c-bata/go-prompt/windows_output.go @@ -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. [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(), + } +} diff --git a/vendor/github.com/mattn/go-tty/.travis.yml b/vendor/github.com/mattn/go-tty/.travis.yml new file mode 100644 index 00000000..63b9f708 --- /dev/null +++ b/vendor/github.com/mattn/go-tty/.travis.yml @@ -0,0 +1,4 @@ +language: go +sudo: false +go: + - tip diff --git a/vendor/github.com/mattn/go-tty/README.md b/vendor/github.com/mattn/go-tty/README.md new file mode 100644 index 00000000..6712eb49 --- /dev/null +++ b/vendor/github.com/mattn/go-tty/README.md @@ -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 go-colorable. + +```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) diff --git a/vendor/github.com/mattn/go-tty/tty.go b/vendor/github.com/mattn/go-tty/tty.go new file mode 100644 index 00000000..8747638b --- /dev/null +++ b/vendor/github.com/mattn/go-tty/tty.go @@ -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() +} diff --git a/vendor/github.com/mattn/go-tty/tty_bsd.go b/vendor/github.com/mattn/go-tty/tty_bsd.go new file mode 100644 index 00000000..e0a51fc0 --- /dev/null +++ b/vendor/github.com/mattn/go-tty/tty_bsd.go @@ -0,0 +1,12 @@ +// +build darwin dragonfly freebsd netbsd openbsd + +package tty + +import ( + "syscall" +) + +const ( + ioctlReadTermios = syscall.TIOCGETA + ioctlWriteTermios = syscall.TIOCSETA +) diff --git a/vendor/github.com/mattn/go-tty/tty_linux.go b/vendor/github.com/mattn/go-tty/tty_linux.go new file mode 100644 index 00000000..1b9e8cef --- /dev/null +++ b/vendor/github.com/mattn/go-tty/tty_linux.go @@ -0,0 +1,8 @@ +// +build linux + +package tty + +const ( + ioctlReadTermios = 0x5401 // syscall.TCGETS + ioctlWriteTermios = 0x5402 // syscall.TCSETS +) diff --git a/vendor/github.com/mattn/go-tty/tty_plan9.go b/vendor/github.com/mattn/go-tty/tty_plan9.go new file mode 100644 index 00000000..e880e514 --- /dev/null +++ b/vendor/github.com/mattn/go-tty/tty_plan9.go @@ -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 +} diff --git a/vendor/github.com/mattn/go-tty/tty_unix.go b/vendor/github.com/mattn/go-tty/tty_unix.go new file mode 100644 index 00000000..4cb680b5 --- /dev/null +++ b/vendor/github.com/mattn/go-tty/tty_unix.go @@ -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 +} diff --git a/vendor/github.com/mattn/go-tty/tty_windows.go b/vendor/github.com/mattn/go-tty/tty_windows.go new file mode 100644 index 00000000..a7db0e07 --- /dev/null +++ b/vendor/github.com/mattn/go-tty/tty_windows.go @@ -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 +}