From ac9013162e51aad9525fcac084973a0cfbc1575a Mon Sep 17 00:00:00 2001 From: Felix Lange Date: Thu, 3 Nov 2016 19:51:19 +0100 Subject: [PATCH 1/2] vendor: update github.com/peterh/liner --- vendor.conf | 2 +- vendor/github.com/peterh/liner/common.go | 15 ++- vendor/github.com/peterh/liner/input.go | 15 +-- .../github.com/peterh/liner/input_windows.go | 31 ++++-- vendor/github.com/peterh/liner/line.go | 103 ++++++++++++------ vendor/github.com/peterh/liner/signal.go | 12 -- .../github.com/peterh/liner/signal_legacy.go | 11 -- vendor/github.com/peterh/liner/width.go | 20 ++++ 8 files changed, 131 insertions(+), 78 deletions(-) delete mode 100644 vendor/github.com/peterh/liner/signal.go delete mode 100644 vendor/github.com/peterh/liner/signal_legacy.go diff --git a/vendor.conf b/vendor.conf index 71b0ce004..b3a5a9d5e 100644 --- a/vendor.conf +++ b/vendor.conf @@ -18,7 +18,7 @@ github.com/mattn/go-runewidth v0.0.1-10-g737072b github.com/mitchellh/go-wordwrap ad45545 github.com/nsf/termbox-go b6acae5 github.com/pborman/uuid v1.0-17-g3d4f2ba -github.com/peterh/liner 8975875 +github.com/peterh/liner 3c5f577 github.com/rcrowley/go-metrics ab2277b github.com/rjeczalik/notify 7e20c15 github.com/robertkrimen/otto bf1c379 diff --git a/vendor/github.com/peterh/liner/common.go b/vendor/github.com/peterh/liner/common.go index b6162b624..e5b8fc280 100644 --- a/vendor/github.com/peterh/liner/common.go +++ b/vendor/github.com/peterh/liner/common.go @@ -32,6 +32,7 @@ type commonState struct { cursorRows int maxRows int shouldRestart ShouldRestart + needRefresh bool } // TabStyle is used to select how tab completions are displayed. @@ -58,7 +59,12 @@ var ErrPromptAborted = errors.New("prompt aborted") // platform is normally supported, but stdout has been redirected var ErrNotTerminalOutput = errors.New("standard output is not a terminal") -// Max elements to save on the killring +// ErrInvalidPrompt is returned from Prompt or PasswordPrompt if the +// prompt contains any unprintable runes (including substrings that could +// be colour codes on some platforms). +var ErrInvalidPrompt = errors.New("invalid prompt") + +// KillRingMax is the max number of elements to save on the killring. const KillRingMax = 60 // HistoryLimit is the maximum number of entries saved in the scrollback history. @@ -133,6 +139,13 @@ func (s *State) AppendHistory(item string) { } } +// ClearHistory clears the scroollback history. +func (s *State) ClearHistory() { + s.historyMutex.Lock() + defer s.historyMutex.Unlock() + s.history = nil +} + // Returns the history lines starting with prefix func (s *State) getHistoryByPrefix(prefix string) (ph []string) { for _, h := range s.history { diff --git a/vendor/github.com/peterh/liner/input.go b/vendor/github.com/peterh/liner/input.go index 0ee6be7af..904fbf663 100644 --- a/vendor/github.com/peterh/liner/input.go +++ b/vendor/github.com/peterh/liner/input.go @@ -31,11 +31,6 @@ type State struct { // NewLiner initializes a new *State, and sets the terminal into raw mode. To // restore the terminal to its previous state, call State.Close(). -// -// Note if you are still using Go 1.0: NewLiner handles SIGWINCH, so it will -// leak a channel every time you call it. Therefore, it is recommened that you -// upgrade to a newer release of Go, or ensure that NewLiner is only called -// once. func NewLiner() *State { var s State s.r = bufio.NewReader(os.Stdin) @@ -87,8 +82,12 @@ func (s *State) startPrompt() { s.restartPrompt() } +func (s *State) inputWaiting() bool { + return len(s.next) > 0 +} + func (s *State) restartPrompt() { - next := make(chan nexter) + next := make(chan nexter, 200) go func() { for { var n nexter @@ -126,8 +125,6 @@ func (s *State) nextPending(timeout <-chan time.Time) (rune, error) { s.pending = s.pending[1:] return rv, errTimedOut } - // not reached - return 0, nil } func (s *State) readNext() (interface{}, error) { @@ -349,7 +346,7 @@ func (s *State) readNext() (interface{}, error) { // Close returns the terminal to its previous mode func (s *State) Close() error { - stopSignal(s.winch) + signal.Stop(s.winch) if !s.inputRedirected { s.origMode.ApplyMode() } diff --git a/vendor/github.com/peterh/liner/input_windows.go b/vendor/github.com/peterh/liner/input_windows.go index 199b9428e..a48eb0f1d 100644 --- a/vendor/github.com/peterh/liner/input_windows.go +++ b/vendor/github.com/peterh/liner/input_windows.go @@ -10,13 +10,14 @@ import ( var ( kernel32 = syscall.NewLazyDLL("kernel32.dll") - procGetStdHandle = kernel32.NewProc("GetStdHandle") - procReadConsoleInput = kernel32.NewProc("ReadConsoleInputW") - procGetConsoleMode = kernel32.NewProc("GetConsoleMode") - procSetConsoleMode = kernel32.NewProc("SetConsoleMode") - procSetConsoleCursorPosition = kernel32.NewProc("SetConsoleCursorPosition") - procGetConsoleScreenBufferInfo = kernel32.NewProc("GetConsoleScreenBufferInfo") - procFillConsoleOutputCharacter = kernel32.NewProc("FillConsoleOutputCharacterW") + procGetStdHandle = kernel32.NewProc("GetStdHandle") + procReadConsoleInput = kernel32.NewProc("ReadConsoleInputW") + procGetNumberOfConsoleInputEvents = kernel32.NewProc("GetNumberOfConsoleInputEvents") + procGetConsoleMode = kernel32.NewProc("GetConsoleMode") + procSetConsoleMode = kernel32.NewProc("SetConsoleMode") + procSetConsoleCursorPosition = kernel32.NewProc("SetConsoleCursorPosition") + procGetConsoleScreenBufferInfo = kernel32.NewProc("GetConsoleScreenBufferInfo") + procFillConsoleOutputCharacter = kernel32.NewProc("FillConsoleOutputCharacterW") ) // These names are from the Win32 api, so they use underscores (contrary to @@ -147,6 +148,21 @@ const ( modKeys = shiftPressed | leftAltPressed | rightAltPressed | leftCtrlPressed | rightCtrlPressed ) +// inputWaiting only returns true if the next call to readNext will return immediately. +func (s *State) inputWaiting() bool { + var num uint32 + ok, _, _ := procGetNumberOfConsoleInputEvents.Call(uintptr(s.handle), uintptr(unsafe.Pointer(&num))) + if ok == 0 { + // call failed, so we cannot guarantee a non-blocking readNext + return false + } + + // during a "paste" input events are always an odd number, and + // the last one results in a blocking readNext, so return false + // when num is 1 or 0. + return num > 1 +} + func (s *State) readNext() (interface{}, error) { if s.repeat > 0 { s.repeat-- @@ -263,7 +279,6 @@ func (s *State) readNext() (interface{}, error) { } return s.key, nil } - return unknown, nil } // Close returns the terminal to its previous mode diff --git a/vendor/github.com/peterh/liner/line.go b/vendor/github.com/peterh/liner/line.go index 903dd240d..5e111ac69 100644 --- a/vendor/github.com/peterh/liner/line.go +++ b/vendor/github.com/peterh/liner/line.go @@ -90,11 +90,11 @@ const ( ) func (s *State) refresh(prompt []rune, buf []rune, pos int) error { + s.needRefresh = false if s.multiLineMode { return s.refreshMultiLine(prompt, buf, pos) - } else { - return s.refreshSingleLine(prompt, buf, pos) } + return s.refreshSingleLine(prompt, buf, pos) } func (s *State) refreshSingleLine(prompt []rune, buf []rune, pos int) error { @@ -387,8 +387,6 @@ func (s *State) tabComplete(p []rune, line []rune, pos int) ([]rune, int, interf } return []rune(head + pick + tail), hl + utf8.RuneCountInString(pick), next, nil } - // Not reached - return line, pos, rune(esc), nil } // reverse intelligent search, implements a bash-like history search. @@ -556,8 +554,6 @@ func (s *State) yank(p []rune, text []rune, pos int) ([]rune, int, interface{}, } } } - - return line, pos, esc, nil } // Prompt displays p and returns a line of user input, not including a trailing @@ -573,6 +569,11 @@ func (s *State) Prompt(prompt string) (string, error) { // including a trailing newline character. An io.EOF error is returned if the user // signals end-of-file by pressing Ctrl-D. func (s *State) PromptWithSuggestion(prompt string, text string, pos int) (string, error) { + for _, r := range prompt { + if unicode.Is(unicode.C, r) { + return "", ErrInvalidPrompt + } + } if s.inputRedirected || !s.terminalSupported { return s.promptUnsupported(prompt) } @@ -587,8 +588,9 @@ func (s *State) PromptWithSuggestion(prompt string, text string, pos int) (strin p := []rune(prompt) var line = []rune(text) historyEnd := "" - prefixHistory := s.getHistoryByPrefix(string(line)) - historyPos := len(prefixHistory) + var historyPrefix []string + historyPos := 0 + historyStale := true historyAction := false // used to mark history related actions killAction := 0 // used to mark kill related actions @@ -628,21 +630,21 @@ mainLoop: break mainLoop case ctrlA: // Start of line pos = 0 - s.refresh(p, line, pos) + s.needRefresh = true case ctrlE: // End of line pos = len(line) - s.refresh(p, line, pos) + s.needRefresh = true case ctrlB: // left if pos > 0 { pos -= len(getSuffixGlyphs(line[:pos], 1)) - s.refresh(p, line, pos) + s.needRefresh = true } else { fmt.Print(beep) } case ctrlF: // right if pos < len(line) { pos += len(getPrefixGlyphs(line[pos:], 1)) - s.refresh(p, line, pos) + s.needRefresh = true } else { fmt.Print(beep) } @@ -661,7 +663,7 @@ mainLoop: } else { n := len(getPrefixGlyphs(line[pos:], 1)) line = append(line[:pos], line[pos+n:]...) - s.refresh(p, line, pos) + s.needRefresh = true } case ctrlK: // delete remainder of line if pos >= len(line) { @@ -675,32 +677,42 @@ mainLoop: killAction = 2 // Mark that there was a kill action line = line[:pos] - s.refresh(p, line, pos) + s.needRefresh = true } case ctrlP: // up historyAction = true + if historyStale { + historyPrefix = s.getHistoryByPrefix(string(line)) + historyPos = len(historyPrefix) + historyStale = false + } if historyPos > 0 { - if historyPos == len(prefixHistory) { + if historyPos == len(historyPrefix) { historyEnd = string(line) } historyPos-- - line = []rune(prefixHistory[historyPos]) + line = []rune(historyPrefix[historyPos]) pos = len(line) - s.refresh(p, line, pos) + s.needRefresh = true } else { fmt.Print(beep) } case ctrlN: // down historyAction = true - if historyPos < len(prefixHistory) { + if historyStale { + historyPrefix = s.getHistoryByPrefix(string(line)) + historyPos = len(historyPrefix) + historyStale = false + } + if historyPos < len(historyPrefix) { historyPos++ - if historyPos == len(prefixHistory) { + if historyPos == len(historyPrefix) { line = []rune(historyEnd) } else { - line = []rune(prefixHistory[historyPos]) + line = []rune(historyPrefix[historyPos]) } pos = len(line) - s.refresh(p, line, pos) + s.needRefresh = true } else { fmt.Print(beep) } @@ -718,11 +730,11 @@ mainLoop: copy(line[pos-len(prev):], next) copy(line[pos-len(prev)+len(next):], scratch) pos += len(next) - s.refresh(p, line, pos) + s.needRefresh = true } case ctrlL: // clear screen s.eraseScreen() - s.refresh(p, line, pos) + s.needRefresh = true case ctrlC: // reset fmt.Println("^C") if s.multiLineMode { @@ -742,7 +754,7 @@ mainLoop: n := len(getSuffixGlyphs(line[:pos], 1)) line = append(line[:pos-n], line[pos:]...) pos -= n - s.refresh(p, line, pos) + s.needRefresh = true } case ctrlU: // Erase line before cursor if killAction > 0 { @@ -754,7 +766,7 @@ mainLoop: killAction = 2 // Mark that there was some killing line = line[pos:] pos = 0 - s.refresh(p, line, pos) + s.needRefresh = true case ctrlW: // Erase word if pos == 0 { fmt.Print(beep) @@ -791,13 +803,13 @@ mainLoop: } killAction = 2 // Mark that there was some killing - s.refresh(p, line, pos) + s.needRefresh = true case ctrlY: // Paste from Yank buffer line, pos, next, err = s.yank(p, line, pos) goto haveNext case ctrlR: // Reverse Search line, pos, next, err = s.reverseISearch(line, pos) - s.refresh(p, line, pos) + s.needRefresh = true goto haveNext case tab: // Tab completion line, pos, next, err = s.tabComplete(p, line, pos) @@ -812,14 +824,16 @@ mainLoop: case 0, 28, 29, 30, 31: fmt.Print(beep) default: - if pos == len(line) && !s.multiLineMode && countGlyphs(p)+countGlyphs(line) < s.columns-1 { + if pos == len(line) && !s.multiLineMode && + len(p)+len(line) < s.columns*4 && // Avoid countGlyphs on large lines + countGlyphs(p)+countGlyphs(line) < s.columns-1 { line = append(line, v) fmt.Printf("%c", v) pos++ } else { line = append(line[:pos], append([]rune{v}, line[pos:]...)...) pos++ - s.refresh(p, line, pos) + s.needRefresh = true } } case action: @@ -887,24 +901,34 @@ mainLoop: } case up: historyAction = true + if historyStale { + historyPrefix = s.getHistoryByPrefix(string(line)) + historyPos = len(historyPrefix) + historyStale = false + } if historyPos > 0 { - if historyPos == len(prefixHistory) { + if historyPos == len(historyPrefix) { historyEnd = string(line) } historyPos-- - line = []rune(prefixHistory[historyPos]) + line = []rune(historyPrefix[historyPos]) pos = len(line) } else { fmt.Print(beep) } case down: historyAction = true - if historyPos < len(prefixHistory) { + if historyStale { + historyPrefix = s.getHistoryByPrefix(string(line)) + historyPos = len(historyPrefix) + historyStale = false + } + if historyPos < len(historyPrefix) { historyPos++ - if historyPos == len(prefixHistory) { + if historyPos == len(historyPrefix) { line = []rune(historyEnd) } else { - line = []rune(prefixHistory[historyPos]) + line = []rune(historyPrefix[historyPos]) } pos = len(line) } else { @@ -928,11 +952,13 @@ mainLoop: s.cursorRows = 1 } } + s.needRefresh = true + } + if s.needRefresh && !s.inputWaiting() { s.refresh(p, line, pos) } if !historyAction { - prefixHistory = s.getHistoryByPrefix(string(line)) - historyPos = len(prefixHistory) + historyStale = true } if killAction > 0 { killAction-- @@ -944,6 +970,11 @@ mainLoop: // PasswordPrompt displays p, and then waits for user input. The input typed by // the user is not displayed in the terminal. func (s *State) PasswordPrompt(prompt string) (string, error) { + for _, r := range prompt { + if unicode.Is(unicode.C, r) { + return "", ErrInvalidPrompt + } + } if !s.terminalSupported { return "", errors.New("liner: function not supported in this terminal") } diff --git a/vendor/github.com/peterh/liner/signal.go b/vendor/github.com/peterh/liner/signal.go deleted file mode 100644 index 0cba79e7f..000000000 --- a/vendor/github.com/peterh/liner/signal.go +++ /dev/null @@ -1,12 +0,0 @@ -// +build go1.1,!windows - -package liner - -import ( - "os" - "os/signal" -) - -func stopSignal(c chan<- os.Signal) { - signal.Stop(c) -} diff --git a/vendor/github.com/peterh/liner/signal_legacy.go b/vendor/github.com/peterh/liner/signal_legacy.go deleted file mode 100644 index fa3672daa..000000000 --- a/vendor/github.com/peterh/liner/signal_legacy.go +++ /dev/null @@ -1,11 +0,0 @@ -// +build !go1.1,!windows - -package liner - -import ( - "os" -) - -func stopSignal(c chan<- os.Signal) { - // signal.Stop does not exist before Go 1.1 -} diff --git a/vendor/github.com/peterh/liner/width.go b/vendor/github.com/peterh/liner/width.go index d8984aae9..42e899983 100644 --- a/vendor/github.com/peterh/liner/width.go +++ b/vendor/github.com/peterh/liner/width.go @@ -25,6 +25,12 @@ var doubleWidth = []*unicode.RangeTable{ func countGlyphs(s []rune) int { n := 0 for _, r := range s { + // speed up the common case + if r < 127 { + n++ + continue + } + switch { case unicode.IsOneOf(zeroWidth, r): case unicode.IsOneOf(doubleWidth, r): @@ -39,6 +45,10 @@ func countGlyphs(s []rune) int { func countMultiLineGlyphs(s []rune, columns int, start int) int { n := start for _, r := range s { + if r < 127 { + n++ + continue + } switch { case unicode.IsOneOf(zeroWidth, r): case unicode.IsOneOf(doubleWidth, r): @@ -58,6 +68,11 @@ func countMultiLineGlyphs(s []rune, columns int, start int) int { func getPrefixGlyphs(s []rune, num int) []rune { p := 0 for n := 0; n < num && p < len(s); p++ { + // speed up the common case + if s[p] < 127 { + n++ + continue + } if !unicode.IsOneOf(zeroWidth, s[p]) { n++ } @@ -71,6 +86,11 @@ func getPrefixGlyphs(s []rune, num int) []rune { func getSuffixGlyphs(s []rune, num int) []rune { p := len(s) for n := 0; n < num && p > 0; p-- { + // speed up the common case + if s[p-1] < 127 { + n++ + continue + } if !unicode.IsOneOf(zeroWidth, s[p-1]) { n++ } From 0b8a14fed70e6e7107094c96858a36784444326b Mon Sep 17 00:00:00 2001 From: Felix Lange Date: Thu, 3 Nov 2016 19:51:37 +0100 Subject: [PATCH 2/2] console: enable multiline mode --- console/prompter.go | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/console/prompter.go b/console/prompter.go index 0e4a8a53e..5946d9ece 100644 --- a/console/prompter.go +++ b/console/prompter.go @@ -95,7 +95,7 @@ func newTerminalPrompter() *terminalPrompter { } p.SetCtrlCAborts(true) p.SetTabCompletionStyle(liner.TabPrints) - + p.SetMultiLineMode(true) return p }