Merge pull request #3231 from fjl/liner-update
console, vendor: update liner and enable multiline mode
This commit is contained in:
commit
e0e18f3841
|
@ -95,7 +95,7 @@ func newTerminalPrompter() *terminalPrompter {
|
||||||
}
|
}
|
||||||
p.SetCtrlCAborts(true)
|
p.SetCtrlCAborts(true)
|
||||||
p.SetTabCompletionStyle(liner.TabPrints)
|
p.SetTabCompletionStyle(liner.TabPrints)
|
||||||
|
p.SetMultiLineMode(true)
|
||||||
return p
|
return p
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -18,7 +18,7 @@ github.com/mattn/go-runewidth v0.0.1-10-g737072b
|
||||||
github.com/mitchellh/go-wordwrap ad45545
|
github.com/mitchellh/go-wordwrap ad45545
|
||||||
github.com/nsf/termbox-go b6acae5
|
github.com/nsf/termbox-go b6acae5
|
||||||
github.com/pborman/uuid v1.0-17-g3d4f2ba
|
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/rcrowley/go-metrics ab2277b
|
||||||
github.com/rjeczalik/notify 7e20c15
|
github.com/rjeczalik/notify 7e20c15
|
||||||
github.com/robertkrimen/otto bf1c379
|
github.com/robertkrimen/otto bf1c379
|
||||||
|
|
|
@ -32,6 +32,7 @@ type commonState struct {
|
||||||
cursorRows int
|
cursorRows int
|
||||||
maxRows int
|
maxRows int
|
||||||
shouldRestart ShouldRestart
|
shouldRestart ShouldRestart
|
||||||
|
needRefresh bool
|
||||||
}
|
}
|
||||||
|
|
||||||
// TabStyle is used to select how tab completions are displayed.
|
// 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
|
// platform is normally supported, but stdout has been redirected
|
||||||
var ErrNotTerminalOutput = errors.New("standard output is not a terminal")
|
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
|
const KillRingMax = 60
|
||||||
|
|
||||||
// HistoryLimit is the maximum number of entries saved in the scrollback history.
|
// 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
|
// Returns the history lines starting with prefix
|
||||||
func (s *State) getHistoryByPrefix(prefix string) (ph []string) {
|
func (s *State) getHistoryByPrefix(prefix string) (ph []string) {
|
||||||
for _, h := range s.history {
|
for _, h := range s.history {
|
||||||
|
|
|
@ -31,11 +31,6 @@ type State struct {
|
||||||
|
|
||||||
// NewLiner initializes a new *State, and sets the terminal into raw mode. To
|
// NewLiner initializes a new *State, and sets the terminal into raw mode. To
|
||||||
// restore the terminal to its previous state, call State.Close().
|
// 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 {
|
func NewLiner() *State {
|
||||||
var s State
|
var s State
|
||||||
s.r = bufio.NewReader(os.Stdin)
|
s.r = bufio.NewReader(os.Stdin)
|
||||||
|
@ -87,8 +82,12 @@ func (s *State) startPrompt() {
|
||||||
s.restartPrompt()
|
s.restartPrompt()
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func (s *State) inputWaiting() bool {
|
||||||
|
return len(s.next) > 0
|
||||||
|
}
|
||||||
|
|
||||||
func (s *State) restartPrompt() {
|
func (s *State) restartPrompt() {
|
||||||
next := make(chan nexter)
|
next := make(chan nexter, 200)
|
||||||
go func() {
|
go func() {
|
||||||
for {
|
for {
|
||||||
var n nexter
|
var n nexter
|
||||||
|
@ -126,8 +125,6 @@ func (s *State) nextPending(timeout <-chan time.Time) (rune, error) {
|
||||||
s.pending = s.pending[1:]
|
s.pending = s.pending[1:]
|
||||||
return rv, errTimedOut
|
return rv, errTimedOut
|
||||||
}
|
}
|
||||||
// not reached
|
|
||||||
return 0, nil
|
|
||||||
}
|
}
|
||||||
|
|
||||||
func (s *State) readNext() (interface{}, error) {
|
func (s *State) readNext() (interface{}, error) {
|
||||||
|
@ -349,7 +346,7 @@ func (s *State) readNext() (interface{}, error) {
|
||||||
|
|
||||||
// Close returns the terminal to its previous mode
|
// Close returns the terminal to its previous mode
|
||||||
func (s *State) Close() error {
|
func (s *State) Close() error {
|
||||||
stopSignal(s.winch)
|
signal.Stop(s.winch)
|
||||||
if !s.inputRedirected {
|
if !s.inputRedirected {
|
||||||
s.origMode.ApplyMode()
|
s.origMode.ApplyMode()
|
||||||
}
|
}
|
||||||
|
|
|
@ -12,6 +12,7 @@ var (
|
||||||
|
|
||||||
procGetStdHandle = kernel32.NewProc("GetStdHandle")
|
procGetStdHandle = kernel32.NewProc("GetStdHandle")
|
||||||
procReadConsoleInput = kernel32.NewProc("ReadConsoleInputW")
|
procReadConsoleInput = kernel32.NewProc("ReadConsoleInputW")
|
||||||
|
procGetNumberOfConsoleInputEvents = kernel32.NewProc("GetNumberOfConsoleInputEvents")
|
||||||
procGetConsoleMode = kernel32.NewProc("GetConsoleMode")
|
procGetConsoleMode = kernel32.NewProc("GetConsoleMode")
|
||||||
procSetConsoleMode = kernel32.NewProc("SetConsoleMode")
|
procSetConsoleMode = kernel32.NewProc("SetConsoleMode")
|
||||||
procSetConsoleCursorPosition = kernel32.NewProc("SetConsoleCursorPosition")
|
procSetConsoleCursorPosition = kernel32.NewProc("SetConsoleCursorPosition")
|
||||||
|
@ -147,6 +148,21 @@ const (
|
||||||
modKeys = shiftPressed | leftAltPressed | rightAltPressed | leftCtrlPressed | rightCtrlPressed
|
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) {
|
func (s *State) readNext() (interface{}, error) {
|
||||||
if s.repeat > 0 {
|
if s.repeat > 0 {
|
||||||
s.repeat--
|
s.repeat--
|
||||||
|
@ -263,7 +279,6 @@ func (s *State) readNext() (interface{}, error) {
|
||||||
}
|
}
|
||||||
return s.key, nil
|
return s.key, nil
|
||||||
}
|
}
|
||||||
return unknown, nil
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// Close returns the terminal to its previous mode
|
// Close returns the terminal to its previous mode
|
||||||
|
|
|
@ -90,11 +90,11 @@ const (
|
||||||
)
|
)
|
||||||
|
|
||||||
func (s *State) refresh(prompt []rune, buf []rune, pos int) error {
|
func (s *State) refresh(prompt []rune, buf []rune, pos int) error {
|
||||||
|
s.needRefresh = false
|
||||||
if s.multiLineMode {
|
if s.multiLineMode {
|
||||||
return s.refreshMultiLine(prompt, buf, pos)
|
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 {
|
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
|
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.
|
// 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
|
// 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
|
// including a trailing newline character. An io.EOF error is returned if the user
|
||||||
// signals end-of-file by pressing Ctrl-D.
|
// signals end-of-file by pressing Ctrl-D.
|
||||||
func (s *State) PromptWithSuggestion(prompt string, text string, pos int) (string, error) {
|
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 {
|
if s.inputRedirected || !s.terminalSupported {
|
||||||
return s.promptUnsupported(prompt)
|
return s.promptUnsupported(prompt)
|
||||||
}
|
}
|
||||||
|
@ -587,8 +588,9 @@ func (s *State) PromptWithSuggestion(prompt string, text string, pos int) (strin
|
||||||
p := []rune(prompt)
|
p := []rune(prompt)
|
||||||
var line = []rune(text)
|
var line = []rune(text)
|
||||||
historyEnd := ""
|
historyEnd := ""
|
||||||
prefixHistory := s.getHistoryByPrefix(string(line))
|
var historyPrefix []string
|
||||||
historyPos := len(prefixHistory)
|
historyPos := 0
|
||||||
|
historyStale := true
|
||||||
historyAction := false // used to mark history related actions
|
historyAction := false // used to mark history related actions
|
||||||
killAction := 0 // used to mark kill related actions
|
killAction := 0 // used to mark kill related actions
|
||||||
|
|
||||||
|
@ -628,21 +630,21 @@ mainLoop:
|
||||||
break mainLoop
|
break mainLoop
|
||||||
case ctrlA: // Start of line
|
case ctrlA: // Start of line
|
||||||
pos = 0
|
pos = 0
|
||||||
s.refresh(p, line, pos)
|
s.needRefresh = true
|
||||||
case ctrlE: // End of line
|
case ctrlE: // End of line
|
||||||
pos = len(line)
|
pos = len(line)
|
||||||
s.refresh(p, line, pos)
|
s.needRefresh = true
|
||||||
case ctrlB: // left
|
case ctrlB: // left
|
||||||
if pos > 0 {
|
if pos > 0 {
|
||||||
pos -= len(getSuffixGlyphs(line[:pos], 1))
|
pos -= len(getSuffixGlyphs(line[:pos], 1))
|
||||||
s.refresh(p, line, pos)
|
s.needRefresh = true
|
||||||
} else {
|
} else {
|
||||||
fmt.Print(beep)
|
fmt.Print(beep)
|
||||||
}
|
}
|
||||||
case ctrlF: // right
|
case ctrlF: // right
|
||||||
if pos < len(line) {
|
if pos < len(line) {
|
||||||
pos += len(getPrefixGlyphs(line[pos:], 1))
|
pos += len(getPrefixGlyphs(line[pos:], 1))
|
||||||
s.refresh(p, line, pos)
|
s.needRefresh = true
|
||||||
} else {
|
} else {
|
||||||
fmt.Print(beep)
|
fmt.Print(beep)
|
||||||
}
|
}
|
||||||
|
@ -661,7 +663,7 @@ mainLoop:
|
||||||
} else {
|
} else {
|
||||||
n := len(getPrefixGlyphs(line[pos:], 1))
|
n := len(getPrefixGlyphs(line[pos:], 1))
|
||||||
line = append(line[:pos], line[pos+n:]...)
|
line = append(line[:pos], line[pos+n:]...)
|
||||||
s.refresh(p, line, pos)
|
s.needRefresh = true
|
||||||
}
|
}
|
||||||
case ctrlK: // delete remainder of line
|
case ctrlK: // delete remainder of line
|
||||||
if pos >= len(line) {
|
if pos >= len(line) {
|
||||||
|
@ -675,32 +677,42 @@ mainLoop:
|
||||||
|
|
||||||
killAction = 2 // Mark that there was a kill action
|
killAction = 2 // Mark that there was a kill action
|
||||||
line = line[:pos]
|
line = line[:pos]
|
||||||
s.refresh(p, line, pos)
|
s.needRefresh = true
|
||||||
}
|
}
|
||||||
case ctrlP: // up
|
case ctrlP: // up
|
||||||
historyAction = true
|
historyAction = true
|
||||||
|
if historyStale {
|
||||||
|
historyPrefix = s.getHistoryByPrefix(string(line))
|
||||||
|
historyPos = len(historyPrefix)
|
||||||
|
historyStale = false
|
||||||
|
}
|
||||||
if historyPos > 0 {
|
if historyPos > 0 {
|
||||||
if historyPos == len(prefixHistory) {
|
if historyPos == len(historyPrefix) {
|
||||||
historyEnd = string(line)
|
historyEnd = string(line)
|
||||||
}
|
}
|
||||||
historyPos--
|
historyPos--
|
||||||
line = []rune(prefixHistory[historyPos])
|
line = []rune(historyPrefix[historyPos])
|
||||||
pos = len(line)
|
pos = len(line)
|
||||||
s.refresh(p, line, pos)
|
s.needRefresh = true
|
||||||
} else {
|
} else {
|
||||||
fmt.Print(beep)
|
fmt.Print(beep)
|
||||||
}
|
}
|
||||||
case ctrlN: // down
|
case ctrlN: // down
|
||||||
historyAction = true
|
historyAction = true
|
||||||
if historyPos < len(prefixHistory) {
|
if historyStale {
|
||||||
|
historyPrefix = s.getHistoryByPrefix(string(line))
|
||||||
|
historyPos = len(historyPrefix)
|
||||||
|
historyStale = false
|
||||||
|
}
|
||||||
|
if historyPos < len(historyPrefix) {
|
||||||
historyPos++
|
historyPos++
|
||||||
if historyPos == len(prefixHistory) {
|
if historyPos == len(historyPrefix) {
|
||||||
line = []rune(historyEnd)
|
line = []rune(historyEnd)
|
||||||
} else {
|
} else {
|
||||||
line = []rune(prefixHistory[historyPos])
|
line = []rune(historyPrefix[historyPos])
|
||||||
}
|
}
|
||||||
pos = len(line)
|
pos = len(line)
|
||||||
s.refresh(p, line, pos)
|
s.needRefresh = true
|
||||||
} else {
|
} else {
|
||||||
fmt.Print(beep)
|
fmt.Print(beep)
|
||||||
}
|
}
|
||||||
|
@ -718,11 +730,11 @@ mainLoop:
|
||||||
copy(line[pos-len(prev):], next)
|
copy(line[pos-len(prev):], next)
|
||||||
copy(line[pos-len(prev)+len(next):], scratch)
|
copy(line[pos-len(prev)+len(next):], scratch)
|
||||||
pos += len(next)
|
pos += len(next)
|
||||||
s.refresh(p, line, pos)
|
s.needRefresh = true
|
||||||
}
|
}
|
||||||
case ctrlL: // clear screen
|
case ctrlL: // clear screen
|
||||||
s.eraseScreen()
|
s.eraseScreen()
|
||||||
s.refresh(p, line, pos)
|
s.needRefresh = true
|
||||||
case ctrlC: // reset
|
case ctrlC: // reset
|
||||||
fmt.Println("^C")
|
fmt.Println("^C")
|
||||||
if s.multiLineMode {
|
if s.multiLineMode {
|
||||||
|
@ -742,7 +754,7 @@ mainLoop:
|
||||||
n := len(getSuffixGlyphs(line[:pos], 1))
|
n := len(getSuffixGlyphs(line[:pos], 1))
|
||||||
line = append(line[:pos-n], line[pos:]...)
|
line = append(line[:pos-n], line[pos:]...)
|
||||||
pos -= n
|
pos -= n
|
||||||
s.refresh(p, line, pos)
|
s.needRefresh = true
|
||||||
}
|
}
|
||||||
case ctrlU: // Erase line before cursor
|
case ctrlU: // Erase line before cursor
|
||||||
if killAction > 0 {
|
if killAction > 0 {
|
||||||
|
@ -754,7 +766,7 @@ mainLoop:
|
||||||
killAction = 2 // Mark that there was some killing
|
killAction = 2 // Mark that there was some killing
|
||||||
line = line[pos:]
|
line = line[pos:]
|
||||||
pos = 0
|
pos = 0
|
||||||
s.refresh(p, line, pos)
|
s.needRefresh = true
|
||||||
case ctrlW: // Erase word
|
case ctrlW: // Erase word
|
||||||
if pos == 0 {
|
if pos == 0 {
|
||||||
fmt.Print(beep)
|
fmt.Print(beep)
|
||||||
|
@ -791,13 +803,13 @@ mainLoop:
|
||||||
}
|
}
|
||||||
killAction = 2 // Mark that there was some killing
|
killAction = 2 // Mark that there was some killing
|
||||||
|
|
||||||
s.refresh(p, line, pos)
|
s.needRefresh = true
|
||||||
case ctrlY: // Paste from Yank buffer
|
case ctrlY: // Paste from Yank buffer
|
||||||
line, pos, next, err = s.yank(p, line, pos)
|
line, pos, next, err = s.yank(p, line, pos)
|
||||||
goto haveNext
|
goto haveNext
|
||||||
case ctrlR: // Reverse Search
|
case ctrlR: // Reverse Search
|
||||||
line, pos, next, err = s.reverseISearch(line, pos)
|
line, pos, next, err = s.reverseISearch(line, pos)
|
||||||
s.refresh(p, line, pos)
|
s.needRefresh = true
|
||||||
goto haveNext
|
goto haveNext
|
||||||
case tab: // Tab completion
|
case tab: // Tab completion
|
||||||
line, pos, next, err = s.tabComplete(p, line, pos)
|
line, pos, next, err = s.tabComplete(p, line, pos)
|
||||||
|
@ -812,14 +824,16 @@ mainLoop:
|
||||||
case 0, 28, 29, 30, 31:
|
case 0, 28, 29, 30, 31:
|
||||||
fmt.Print(beep)
|
fmt.Print(beep)
|
||||||
default:
|
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)
|
line = append(line, v)
|
||||||
fmt.Printf("%c", v)
|
fmt.Printf("%c", v)
|
||||||
pos++
|
pos++
|
||||||
} else {
|
} else {
|
||||||
line = append(line[:pos], append([]rune{v}, line[pos:]...)...)
|
line = append(line[:pos], append([]rune{v}, line[pos:]...)...)
|
||||||
pos++
|
pos++
|
||||||
s.refresh(p, line, pos)
|
s.needRefresh = true
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
case action:
|
case action:
|
||||||
|
@ -887,24 +901,34 @@ mainLoop:
|
||||||
}
|
}
|
||||||
case up:
|
case up:
|
||||||
historyAction = true
|
historyAction = true
|
||||||
|
if historyStale {
|
||||||
|
historyPrefix = s.getHistoryByPrefix(string(line))
|
||||||
|
historyPos = len(historyPrefix)
|
||||||
|
historyStale = false
|
||||||
|
}
|
||||||
if historyPos > 0 {
|
if historyPos > 0 {
|
||||||
if historyPos == len(prefixHistory) {
|
if historyPos == len(historyPrefix) {
|
||||||
historyEnd = string(line)
|
historyEnd = string(line)
|
||||||
}
|
}
|
||||||
historyPos--
|
historyPos--
|
||||||
line = []rune(prefixHistory[historyPos])
|
line = []rune(historyPrefix[historyPos])
|
||||||
pos = len(line)
|
pos = len(line)
|
||||||
} else {
|
} else {
|
||||||
fmt.Print(beep)
|
fmt.Print(beep)
|
||||||
}
|
}
|
||||||
case down:
|
case down:
|
||||||
historyAction = true
|
historyAction = true
|
||||||
if historyPos < len(prefixHistory) {
|
if historyStale {
|
||||||
|
historyPrefix = s.getHistoryByPrefix(string(line))
|
||||||
|
historyPos = len(historyPrefix)
|
||||||
|
historyStale = false
|
||||||
|
}
|
||||||
|
if historyPos < len(historyPrefix) {
|
||||||
historyPos++
|
historyPos++
|
||||||
if historyPos == len(prefixHistory) {
|
if historyPos == len(historyPrefix) {
|
||||||
line = []rune(historyEnd)
|
line = []rune(historyEnd)
|
||||||
} else {
|
} else {
|
||||||
line = []rune(prefixHistory[historyPos])
|
line = []rune(historyPrefix[historyPos])
|
||||||
}
|
}
|
||||||
pos = len(line)
|
pos = len(line)
|
||||||
} else {
|
} else {
|
||||||
|
@ -928,11 +952,13 @@ mainLoop:
|
||||||
s.cursorRows = 1
|
s.cursorRows = 1
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
s.needRefresh = true
|
||||||
|
}
|
||||||
|
if s.needRefresh && !s.inputWaiting() {
|
||||||
s.refresh(p, line, pos)
|
s.refresh(p, line, pos)
|
||||||
}
|
}
|
||||||
if !historyAction {
|
if !historyAction {
|
||||||
prefixHistory = s.getHistoryByPrefix(string(line))
|
historyStale = true
|
||||||
historyPos = len(prefixHistory)
|
|
||||||
}
|
}
|
||||||
if killAction > 0 {
|
if killAction > 0 {
|
||||||
killAction--
|
killAction--
|
||||||
|
@ -944,6 +970,11 @@ mainLoop:
|
||||||
// PasswordPrompt displays p, and then waits for user input. The input typed by
|
// PasswordPrompt displays p, and then waits for user input. The input typed by
|
||||||
// the user is not displayed in the terminal.
|
// the user is not displayed in the terminal.
|
||||||
func (s *State) PasswordPrompt(prompt string) (string, error) {
|
func (s *State) PasswordPrompt(prompt string) (string, error) {
|
||||||
|
for _, r := range prompt {
|
||||||
|
if unicode.Is(unicode.C, r) {
|
||||||
|
return "", ErrInvalidPrompt
|
||||||
|
}
|
||||||
|
}
|
||||||
if !s.terminalSupported {
|
if !s.terminalSupported {
|
||||||
return "", errors.New("liner: function not supported in this terminal")
|
return "", errors.New("liner: function not supported in this terminal")
|
||||||
}
|
}
|
||||||
|
|
|
@ -1,12 +0,0 @@
|
||||||
// +build go1.1,!windows
|
|
||||||
|
|
||||||
package liner
|
|
||||||
|
|
||||||
import (
|
|
||||||
"os"
|
|
||||||
"os/signal"
|
|
||||||
)
|
|
||||||
|
|
||||||
func stopSignal(c chan<- os.Signal) {
|
|
||||||
signal.Stop(c)
|
|
||||||
}
|
|
|
@ -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
|
|
||||||
}
|
|
|
@ -25,6 +25,12 @@ var doubleWidth = []*unicode.RangeTable{
|
||||||
func countGlyphs(s []rune) int {
|
func countGlyphs(s []rune) int {
|
||||||
n := 0
|
n := 0
|
||||||
for _, r := range s {
|
for _, r := range s {
|
||||||
|
// speed up the common case
|
||||||
|
if r < 127 {
|
||||||
|
n++
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
|
||||||
switch {
|
switch {
|
||||||
case unicode.IsOneOf(zeroWidth, r):
|
case unicode.IsOneOf(zeroWidth, r):
|
||||||
case unicode.IsOneOf(doubleWidth, r):
|
case unicode.IsOneOf(doubleWidth, r):
|
||||||
|
@ -39,6 +45,10 @@ func countGlyphs(s []rune) int {
|
||||||
func countMultiLineGlyphs(s []rune, columns int, start int) int {
|
func countMultiLineGlyphs(s []rune, columns int, start int) int {
|
||||||
n := start
|
n := start
|
||||||
for _, r := range s {
|
for _, r := range s {
|
||||||
|
if r < 127 {
|
||||||
|
n++
|
||||||
|
continue
|
||||||
|
}
|
||||||
switch {
|
switch {
|
||||||
case unicode.IsOneOf(zeroWidth, r):
|
case unicode.IsOneOf(zeroWidth, r):
|
||||||
case unicode.IsOneOf(doubleWidth, 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 {
|
func getPrefixGlyphs(s []rune, num int) []rune {
|
||||||
p := 0
|
p := 0
|
||||||
for n := 0; n < num && p < len(s); p++ {
|
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]) {
|
if !unicode.IsOneOf(zeroWidth, s[p]) {
|
||||||
n++
|
n++
|
||||||
}
|
}
|
||||||
|
@ -71,6 +86,11 @@ func getPrefixGlyphs(s []rune, num int) []rune {
|
||||||
func getSuffixGlyphs(s []rune, num int) []rune {
|
func getSuffixGlyphs(s []rune, num int) []rune {
|
||||||
p := len(s)
|
p := len(s)
|
||||||
for n := 0; n < num && p > 0; p-- {
|
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]) {
|
if !unicode.IsOneOf(zeroWidth, s[p-1]) {
|
||||||
n++
|
n++
|
||||||
}
|
}
|
||||||
|
|
Loading…
Reference in New Issue