From 0b274ded147e04adc1437715dacef0587d92e3a5 Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Sun, 26 Feb 2023 20:52:33 +0000 Subject: [PATCH] build(deps): bump github.com/peterh/liner Bumps [github.com/peterh/liner](https://github.com/peterh/liner) from 0.0.0-20170211195444-bf27d3ba8e1d to 1.2.2. - [Release notes](https://github.com/peterh/liner/releases) - [Commits](https://github.com/peterh/liner/commits/v1.2.2) --- updated-dependencies: - dependency-name: github.com/peterh/liner dependency-type: direct:production update-type: version-update:semver-major ... Signed-off-by: dependabot[bot] --- go.mod | 2 +- go.sum | 6 +- vendor/github.com/peterh/liner/README.md | 16 +- vendor/github.com/peterh/liner/bsdinput.go | 1 + vendor/github.com/peterh/liner/common.go | 16 +- .../github.com/peterh/liner/fallbackinput.go | 5 +- vendor/github.com/peterh/liner/input.go | 21 +- .../github.com/peterh/liner/input_darwin.go | 1 + vendor/github.com/peterh/liner/input_linux.go | 1 + .../github.com/peterh/liner/input_solaris.go | 26 ++ .../github.com/peterh/liner/input_windows.go | 44 ++- vendor/github.com/peterh/liner/line.go | 266 ++++++++++++------ vendor/github.com/peterh/liner/output.go | 19 +- .../github.com/peterh/liner/output_solaris.go | 14 + vendor/github.com/peterh/liner/output_unix.go | 20 ++ .../github.com/peterh/liner/output_windows.go | 4 - vendor/github.com/peterh/liner/unixmode.go | 16 +- .../peterh/liner/unixmode_solaris.go | 22 ++ vendor/github.com/peterh/liner/width.go | 31 +- vendor/modules.txt | 2 +- 20 files changed, 379 insertions(+), 154 deletions(-) create mode 100644 vendor/github.com/peterh/liner/input_solaris.go create mode 100644 vendor/github.com/peterh/liner/output_solaris.go create mode 100644 vendor/github.com/peterh/liner/output_unix.go create mode 100644 vendor/github.com/peterh/liner/unixmode_solaris.go diff --git a/go.mod b/go.mod index 25dfdb524b2..43a7726c66c 100644 --- a/go.mod +++ b/go.mod @@ -21,7 +21,7 @@ require ( github.com/olekukonko/tablewriter v0.0.5 github.com/opencontainers/go-digest v1.0.0 github.com/opencontainers/image-spec v1.1.0-rc2 - github.com/peterh/liner v0.0.0-20170211195444-bf27d3ba8e1d + github.com/peterh/liner v1.2.2 github.com/prometheus/client_golang v1.14.0 github.com/rcrowley/go-metrics v0.0.0-20200313005456-10cdbea86bc0 github.com/sirupsen/logrus v1.9.0 diff --git a/go.sum b/go.sum index abafdad71b9..3a096a5c5f8 100644 --- a/go.sum +++ b/go.sum @@ -239,6 +239,7 @@ github.com/kr/text v0.1.0/go.mod h1:4Jbv+DJW3UT/LiOwJeYQe1efqtUx/iVham/4vfdArNI= github.com/kr/text v0.2.0 h1:5Nx0Ya0ZqY2ygV366QzturHI13Jq95ApcVaJBhpS+AY= github.com/kr/text v0.2.0/go.mod h1:eLer722TekiGuMkidMxC/pM04lWEeraHUUmBw8l2grE= github.com/magiconair/properties v1.8.0/go.mod h1:PppfXfuXeibc/6YijjN8zIbojt8czPbwD3XqdrwzmxQ= +github.com/mattn/go-runewidth v0.0.3/go.mod h1:LwmH8dsx7+W8Uxz3IHJYH5QSwggIsqBzpuz5H//U1FU= github.com/mattn/go-runewidth v0.0.9 h1:Lm995f3rfxdpd6TSmuVCHVb/QhupuXlYr8sCI/QdE+0= github.com/mattn/go-runewidth v0.0.9/go.mod h1:H031xJmbD/WCDINGzjvQ9THkh0rPKHF+m2gUSrubnMI= github.com/matttproud/golang_protobuf_extensions v1.0.1/go.mod h1:D8He9yQNgCq6Z5Ld7szi9bcBfOoFv/3dc6xSMkL2PC0= @@ -265,8 +266,8 @@ github.com/opencontainers/go-digest v1.0.0/go.mod h1:0JzlMkj0TRzQZfJkVvzbP0HBR3I github.com/opencontainers/image-spec v1.1.0-rc2 h1:2zx/Stx4Wc5pIPDvIxHXvXtQFW/7XWJGmnM7r3wg034= github.com/opencontainers/image-spec v1.1.0-rc2/go.mod h1:3OVijpioIKYWTqjiG0zfF6wvoJ4fAXGbjdZuI2NgsRQ= github.com/pelletier/go-toml v1.2.0/go.mod h1:5z9KED0ma1S8pY6P1sdut58dfprrGBbd/94hg7ilaic= -github.com/peterh/liner v0.0.0-20170211195444-bf27d3ba8e1d h1:zapSxdmZYY6vJWXFKLQ+MkI+agc+HQyfrCGowDSHiKs= -github.com/peterh/liner v0.0.0-20170211195444-bf27d3ba8e1d/go.mod h1:xIteQHvHuaLYG9IFj6mSxM0fCKrs34IrEQUhOYuGPHc= +github.com/peterh/liner v1.2.2 h1:aJ4AOodmL+JxOZZEL2u9iJf8omNRpqHc/EbrK+3mAXw= +github.com/peterh/liner v1.2.2/go.mod h1:xFwJyiKIXJZUKItq5dGHZSTBRAuG/CpeNpWLyiNRNwI= github.com/pkg/errors v0.8.0/go.mod h1:bwawxfHBFNV+L2hUp1rHADufV3IMtnDRdf1r5NINEl0= github.com/pkg/errors v0.8.1/go.mod h1:bwawxfHBFNV+L2hUp1rHADufV3IMtnDRdf1r5NINEl0= github.com/pkg/errors v0.9.1 h1:FEBLx1zS214owpjy7qsBeixbURkuhQAwrK5UwLGTwt4= @@ -525,6 +526,7 @@ golang.org/x/sys v0.0.0-20210423082822-04245dca01da/go.mod h1:h1NjWce9XRLGQEsW7w golang.org/x/sys v0.0.0-20210510120138-977fb7262007/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/sys v0.0.0-20210603081109-ebe580a85c40/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/sys v0.0.0-20210615035016-665e8c7367d1/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= +golang.org/x/sys v0.0.0-20211117180635-dee7805ff2e1/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/sys v0.0.0-20211216021012-1d35b9e2eb4e/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/sys v0.0.0-20220114195835-da31bd327af9/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/sys v0.0.0-20220715151400-c0bba94af5f8/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= diff --git a/vendor/github.com/peterh/liner/README.md b/vendor/github.com/peterh/liner/README.md index 9148b249298..04f5bbe7cc2 100644 --- a/vendor/github.com/peterh/liner/README.md +++ b/vendor/github.com/peterh/liner/README.md @@ -6,6 +6,13 @@ everything Unix-like is a VT100 (or is trying very hard to be). If your terminal is not pretending to be a VT100, change it. Liner also support Windows. +Liner is intended for use by cross-platform applications. Therefore, the +decision was made to write it in pure Go, avoiding cgo, for ease of cross +compilation. Furthermore, features only supported on some platforms have +been intentionally omitted. For example, Ctrl-Z is "suspend" on Unix, but +"EOF" on Windows. In the interest of making an application behave the same +way on every supported platform, Ctrl-Z is ignored by Liner. + Liner is released under the X11 license (which is similar to the new BSD license). @@ -29,7 +36,8 @@ Ctrl-C | Reset input (create new empty prompt) Ctrl-L | Clear screen (line is unmodified) Ctrl-T | Transpose previous character with current character Ctrl-H, BackSpace | Delete character before cursor -Ctrl-W | Delete word leading up to cursor +Ctrl-W, Alt-BackSpace | Delete word leading up to cursor +Alt-D | Delete word following cursor Ctrl-K | Delete from cursor to end of line Ctrl-U | Delete from start of line to cursor Ctrl-P, Up | Previous match from history @@ -39,6 +47,12 @@ Ctrl-Y | Paste from Yank buffer (Alt-Y to paste next yank instead) Tab | Next completion Shift-Tab | (after Tab) Previous completion +Note that "Previous" and "Next match from history" will retain the part of +the line that the user has already typed, similar to zsh's +"up-line-or-beginning-search" (which is the default on some systems) or +bash's "history-search-backward" (which is my preferred behaviour, but does +not appear to be the default `Up` keybinding on any system). + Getting started ----------------- diff --git a/vendor/github.com/peterh/liner/bsdinput.go b/vendor/github.com/peterh/liner/bsdinput.go index 35933982f18..0a2abfb546a 100644 --- a/vendor/github.com/peterh/liner/bsdinput.go +++ b/vendor/github.com/peterh/liner/bsdinput.go @@ -1,3 +1,4 @@ +//go:build openbsd || freebsd || netbsd // +build openbsd freebsd netbsd package liner diff --git a/vendor/github.com/peterh/liner/common.go b/vendor/github.com/peterh/liner/common.go index e5b8fc28079..a85b025b4fe 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 + noBeep bool needRefresh bool } @@ -64,6 +65,11 @@ var ErrNotTerminalOutput = errors.New("standard output is not a terminal") // be colour codes on some platforms). var ErrInvalidPrompt = errors.New("invalid prompt") +// ErrInternal is returned when liner experiences an error that it cannot +// handle. For example, if the number of colums becomes zero during an +// active call to Prompt +var ErrInternal = errors.New("liner: internal error") + // KillRingMax is the max number of elements to save on the killring. const KillRingMax = 60 @@ -139,7 +145,7 @@ func (s *State) AppendHistory(item string) { } } -// ClearHistory clears the scroollback history. +// ClearHistory clears the scrollback history. func (s *State) ClearHistory() { s.historyMutex.Lock() defer s.historyMutex.Unlock() @@ -156,7 +162,7 @@ func (s *State) getHistoryByPrefix(prefix string) (ph []string) { return } -// Returns the history lines matching the inteligent search +// Returns the history lines matching the intelligent search func (s *State) getHistoryByPattern(pattern string) (ph []string, pos []int) { if pattern == "" { return @@ -238,6 +244,12 @@ func (s *State) SetShouldRestart(f ShouldRestart) { s.shouldRestart = f } +// SetBeep sets whether liner should beep the terminal at various times (output +// ASCII BEL, 0x07). Default is true (will beep). +func (s *State) SetBeep(beep bool) { + s.noBeep = !beep +} + func (s *State) promptUnsupported(p string) (string, error) { if !s.inputRedirected || !s.terminalSupported { fmt.Print(p) diff --git a/vendor/github.com/peterh/liner/fallbackinput.go b/vendor/github.com/peterh/liner/fallbackinput.go index d9eb79d9e05..81a10a4378d 100644 --- a/vendor/github.com/peterh/liner/fallbackinput.go +++ b/vendor/github.com/peterh/liner/fallbackinput.go @@ -1,4 +1,5 @@ -// +build !windows,!linux,!darwin,!openbsd,!freebsd,!netbsd +//go:build !windows && !linux && !darwin && !openbsd && !freebsd && !netbsd && !solaris +// +build !windows,!linux,!darwin,!openbsd,!freebsd,!netbsd,!solaris package liner @@ -55,3 +56,5 @@ func (n noopMode) ApplyMode() error { func TerminalMode() (ModeApplier, error) { return noopMode{}, nil } + +const cursorColumn = true diff --git a/vendor/github.com/peterh/liner/input.go b/vendor/github.com/peterh/liner/input.go index 904fbf66388..be8a61e15a7 100644 --- a/vendor/github.com/peterh/liner/input.go +++ b/vendor/github.com/peterh/liner/input.go @@ -1,4 +1,5 @@ -// +build linux darwin openbsd freebsd netbsd +//go:build linux || darwin || openbsd || freebsd || netbsd || solaris +// +build linux darwin openbsd freebsd netbsd solaris package liner @@ -41,7 +42,7 @@ func NewLiner() *State { } else { s.inputRedirected = true } - if _, err := getMode(syscall.Stdout); err != 0 { + if _, err := getMode(syscall.Stdout); err != nil { s.outputRedirected = true } if s.inputRedirected && s.outputRedirected { @@ -52,6 +53,8 @@ func NewLiner() *State { mode.Iflag &^= icrnl | inpck | istrip | ixon mode.Cflag |= cs8 mode.Lflag &^= syscall.ECHO | icanon | iexten + mode.Cc[syscall.VMIN] = 1 + mode.Cc[syscall.VTIME] = 0 mode.ApplyMode() winch := make(chan os.Signal, 1) @@ -113,7 +116,7 @@ func (s *State) nextPending(timeout <-chan time.Time) (rune, error) { select { case thing, ok := <-s.next: if !ok { - return 0, errors.New("liner: internal error") + return 0, ErrInternal } if thing.err != nil { return 0, thing.err @@ -137,7 +140,7 @@ func (s *State) readNext() (interface{}, error) { select { case thing, ok := <-s.next: if !ok { - return 0, errors.New("liner: internal error") + return 0, ErrInternal } if thing.err != nil { return nil, thing.err @@ -264,9 +267,9 @@ func (s *State) readNext() (interface{}, error) { return pageUp, nil case 6: return pageDown, nil - case 7: + case 1, 7: return home, nil - case 8: + case 4, 8: return end, nil case 15: return f5, nil @@ -328,6 +331,12 @@ func (s *State) readNext() (interface{}, error) { case 'b': s.pending = s.pending[:0] // escape code complete return altB, nil + case 'd': + s.pending = s.pending[:0] // escape code complete + return altD, nil + case bs: + s.pending = s.pending[:0] // escape code complete + return altBs, nil case 'f': s.pending = s.pending[:0] // escape code complete return altF, nil diff --git a/vendor/github.com/peterh/liner/input_darwin.go b/vendor/github.com/peterh/liner/input_darwin.go index e98ab4a49fe..a91dc01497b 100644 --- a/vendor/github.com/peterh/liner/input_darwin.go +++ b/vendor/github.com/peterh/liner/input_darwin.go @@ -1,3 +1,4 @@ +//go:build darwin // +build darwin package liner diff --git a/vendor/github.com/peterh/liner/input_linux.go b/vendor/github.com/peterh/liner/input_linux.go index 56ed185fadb..95dab81848d 100644 --- a/vendor/github.com/peterh/liner/input_linux.go +++ b/vendor/github.com/peterh/liner/input_linux.go @@ -1,3 +1,4 @@ +//go:build linux // +build linux package liner diff --git a/vendor/github.com/peterh/liner/input_solaris.go b/vendor/github.com/peterh/liner/input_solaris.go new file mode 100644 index 00000000000..34c72aee9f2 --- /dev/null +++ b/vendor/github.com/peterh/liner/input_solaris.go @@ -0,0 +1,26 @@ +package liner + +import ( + "golang.org/x/sys/unix" +) + +const ( + getTermios = unix.TCGETS + setTermios = unix.TCSETS +) + +const ( + icrnl = unix.ICRNL + inpck = unix.INPCK + istrip = unix.ISTRIP + ixon = unix.IXON + opost = unix.OPOST + cs8 = unix.CS8 + isig = unix.ISIG + icanon = unix.ICANON + iexten = unix.IEXTEN +) + +type termios unix.Termios + +const cursorColumn = false diff --git a/vendor/github.com/peterh/liner/input_windows.go b/vendor/github.com/peterh/liner/input_windows.go index a48eb0f1d2c..2f308b34f80 100644 --- a/vendor/github.com/peterh/liner/input_windows.go +++ b/vendor/github.com/peterh/liner/input_windows.go @@ -4,6 +4,7 @@ import ( "bufio" "os" "syscall" + "unicode/utf16" "unsafe" ) @@ -103,14 +104,16 @@ type key_event_record struct { RepeatCount uint16 VirtualKeyCode uint16 VirtualScanCode uint16 - Char int16 + Char uint16 ControlKeyState uint32 } // These names are from the Win32 api, so they use underscores (contrary to // what golint suggests) const ( + vk_back = 0x08 vk_tab = 0x09 + vk_menu = 0x12 // ALT key vk_prior = 0x21 vk_next = 0x22 vk_end = 0x23 @@ -134,6 +137,7 @@ const ( vk_f11 = 0x7a vk_f12 = 0x7b bKey = 0x42 + dKey = 0x44 fKey = 0x46 yKey = 0x59 ) @@ -170,12 +174,13 @@ func (s *State) readNext() (interface{}, error) { } var input input_record - pbuf := uintptr(unsafe.Pointer(&input)) var rv uint32 - prv := uintptr(unsafe.Pointer(&rv)) + + var surrogate uint16 for { - ok, _, err := procReadConsoleInput.Call(uintptr(s.handle), pbuf, 1, prv) + ok, _, err := procReadConsoleInput.Call(uintptr(s.handle), + uintptr(unsafe.Pointer(&input)), 1, uintptr(unsafe.Pointer(&rv))) if ok == 0 { return nil, err @@ -184,9 +189,6 @@ func (s *State) readNext() (interface{}, error) { if input.eventType == window_buffer_size_event { xy := (*coord)(unsafe.Pointer(&input.blob[0])) s.columns = int(xy.x) - if s.columns > 1 { - s.columns-- - } return winch, nil } if input.eventType != key_event { @@ -194,14 +196,31 @@ func (s *State) readNext() (interface{}, error) { } ke := (*key_event_record)(unsafe.Pointer(&input.blob[0])) if ke.KeyDown == 0 { + if ke.VirtualKeyCode == vk_menu && ke.Char > 0 { + // paste of unicode (eg. via ALT-numpad) + if surrogate > 0 { + return utf16.DecodeRune(rune(surrogate), rune(ke.Char)), nil + } else if utf16.IsSurrogate(rune(ke.Char)) { + surrogate = ke.Char + continue + } else { + return rune(ke.Char), nil + } + } continue } if ke.VirtualKeyCode == vk_tab && ke.ControlKeyState&modKeys == shiftPressed { s.key = shiftTab + } else if ke.VirtualKeyCode == vk_back && (ke.ControlKeyState&modKeys == leftAltPressed || + ke.ControlKeyState&modKeys == rightAltPressed) { + s.key = altBs } else if ke.VirtualKeyCode == bKey && (ke.ControlKeyState&modKeys == leftAltPressed || ke.ControlKeyState&modKeys == rightAltPressed) { s.key = altB + } else if ke.VirtualKeyCode == dKey && (ke.ControlKeyState&modKeys == leftAltPressed || + ke.ControlKeyState&modKeys == rightAltPressed) { + s.key = altD } else if ke.VirtualKeyCode == fKey && (ke.ControlKeyState&modKeys == leftAltPressed || ke.ControlKeyState&modKeys == rightAltPressed) { s.key = altF @@ -209,7 +228,14 @@ func (s *State) readNext() (interface{}, error) { ke.ControlKeyState&modKeys == rightAltPressed) { s.key = altY } else if ke.Char > 0 { - s.key = rune(ke.Char) + if surrogate > 0 { + s.key = utf16.DecodeRune(rune(surrogate), rune(ke.Char)) + } else if utf16.IsSurrogate(rune(ke.Char)) { + surrogate = ke.Char + continue + } else { + s.key = rune(ke.Char) + } } else { switch ke.VirtualKeyCode { case vk_prior: @@ -337,3 +363,5 @@ func TerminalMode() (ModeApplier, error) { } return mode, err } + +const cursorColumn = true diff --git a/vendor/github.com/peterh/liner/line.go b/vendor/github.com/peterh/liner/line.go index cc147d608ac..417bb90e025 100644 --- a/vendor/github.com/peterh/liner/line.go +++ b/vendor/github.com/peterh/liner/line.go @@ -1,12 +1,15 @@ -// +build windows linux darwin openbsd freebsd netbsd +//go:build windows || linux || darwin || openbsd || freebsd || netbsd || solaris +// +build windows linux darwin openbsd freebsd netbsd solaris package liner import ( + "bufio" "container/ring" "errors" "fmt" "io" + "os" "strings" "unicode" "unicode/utf8" @@ -38,6 +41,8 @@ const ( f11 f12 altB + altBs // Alt+Backspace + altD altF altY shiftTab @@ -90,6 +95,10 @@ const ( ) func (s *State) refresh(prompt []rune, buf []rune, pos int) error { + if s.columns == 0 { + return ErrInternal + } + s.needRefresh = false if s.multiLineMode { return s.refreshMultiLine(prompt, buf, pos) @@ -106,6 +115,10 @@ func (s *State) refreshSingleLine(prompt []rune, buf []rune, pos int) error { pLen := countGlyphs(prompt) bLen := countGlyphs(buf) + // on some OS / terminals extra column is needed to place the cursor char + if cursorColumn { + bLen++ + } pos = countGlyphs(buf[:pos]) if pLen+bLen < s.columns { _, err = fmt.Print(string(buf)) @@ -156,6 +169,14 @@ func (s *State) refreshSingleLine(prompt []rune, buf []rune, pos int) error { func (s *State) refreshMultiLine(prompt []rune, buf []rune, pos int) error { promptColumns := countMultiLineGlyphs(prompt, s.columns, 0) totalColumns := countMultiLineGlyphs(buf, s.columns, promptColumns) + // on some OS / terminals extra column is needed to place the cursor char + // if cursorColumn { + // totalColumns++ + // } + + // it looks like Multiline mode always assume that a cursor need an extra column, + // and always emit a newline if we are at the screen end, so no worarounds needed there + totalRows := (totalColumns + s.columns - 1) / s.columns maxRows := s.maxRows if totalRows > s.maxRows { @@ -351,8 +372,8 @@ func (s *State) tabComplete(p []rune, line []rune, pos int) ([]rune, int, interf } hl := utf8.RuneCountInString(head) if len(list) == 1 { - s.refresh(p, []rune(head+list[0]+tail), hl+utf8.RuneCountInString(list[0])) - return []rune(head + list[0] + tail), hl + utf8.RuneCountInString(list[0]), rune(esc), nil + err := s.refresh(p, []rune(head+list[0]+tail), hl+utf8.RuneCountInString(list[0])) + return []rune(head + list[0] + tail), hl + utf8.RuneCountInString(list[0]), rune(esc), err } direction := tabForward @@ -366,7 +387,10 @@ func (s *State) tabComplete(p []rune, line []rune, pos int) ([]rune, int, interf if err != nil { return line, pos, rune(esc), err } - s.refresh(p, []rune(head+pick+tail), hl+utf8.RuneCountInString(pick)) + err = s.refresh(p, []rune(head+pick+tail), hl+utf8.RuneCountInString(pick)) + if err != nil { + return line, pos, rune(esc), err + } next, err := s.readNext() if err != nil { @@ -392,7 +416,10 @@ func (s *State) tabComplete(p []rune, line []rune, pos int) ([]rune, int, interf // reverse intelligent search, implements a bash-like history search. func (s *State) reverseISearch(origLine []rune, origPos int) ([]rune, int, interface{}, error) { p := "(reverse-i-search)`': " - s.refresh([]rune(p), origLine, origPos) + err := s.refresh([]rune(p), origLine, origPos) + if err != nil { + return origLine, origPos, rune(esc), err + } line := []rune{} pos := 0 @@ -423,7 +450,7 @@ func (s *State) reverseISearch(origLine []rune, origPos int) ([]rune, int, inter foundLine = history[historyPos] foundPos = positions[historyPos] } else { - fmt.Print(beep) + s.doBeep() } case ctrlS: // Search forward if historyPos < len(history)-1 && historyPos >= 0 { @@ -431,11 +458,11 @@ func (s *State) reverseISearch(origLine []rune, origPos int) ([]rune, int, inter foundLine = history[historyPos] foundPos = positions[historyPos] } else { - fmt.Print(beep) + s.doBeep() } case ctrlH, bs: // Backspace if pos <= 0 { - fmt.Print(beep) + s.doBeep() } else { n := len(getSuffixGlyphs(line[:pos], 1)) line = append(line[:pos-n], line[pos:]...) @@ -478,7 +505,10 @@ func (s *State) reverseISearch(origLine []rune, origPos int) ([]rune, int, inter case action: return []rune(foundLine), foundPos, next, err } - s.refresh(getLine()) + err = s.refresh(getLine()) + if err != nil { + return []rune(foundLine), foundPos, rune(esc), err + } } } @@ -535,7 +565,10 @@ func (s *State) yank(p []rune, text []rune, pos int) ([]rune, int, interface{}, line = append(line, lineEnd...) pos = len(lineStart) + len(value) - s.refresh(p, line, pos) + err := s.refresh(p, line, pos) + if err != nil { + return line, pos, 0, err + } next, err := s.readNext() if err != nil { @@ -565,7 +598,7 @@ func (s *State) Prompt(prompt string) (string, error) { // PromptWithSuggestion displays prompt and an editable text with cursor at // given position. The cursor will be set to the end of the line if given position -// is negative or greater than length of text. Returns a line of user input, not +// is negative or greater than length of text (in runes). Returns a line of user input, not // 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) { @@ -577,6 +610,11 @@ func (s *State) PromptWithSuggestion(prompt string, text string, pos int) (strin if s.inputRedirected || !s.terminalSupported { return s.promptUnsupported(prompt) } + p := []rune(prompt) + const minWorkingSpace = 10 + if s.columns < countGlyphs(p)+minWorkingSpace { + return s.tooNarrow(prompt) + } if s.outputRedirected { return "", ErrNotTerminalOutput } @@ -585,7 +623,6 @@ func (s *State) PromptWithSuggestion(prompt string, text string, pos int) (strin defer s.historyMutex.RUnlock() fmt.Print(prompt) - p := []rune(prompt) var line = []rune(text) historyEnd := "" var historyPrefix []string @@ -596,11 +633,14 @@ func (s *State) PromptWithSuggestion(prompt string, text string, pos int) (strin defer s.stopPrompt() - if pos < 0 || len(text) < pos { - pos = len(text) + if pos < 0 || len(line) < pos { + pos = len(line) } if len(line) > 0 { - s.refresh(p, line, pos) + err := s.refresh(p, line, pos) + if err != nil { + return "", err + } } restart: @@ -624,7 +664,10 @@ mainLoop: switch v { case cr, lf: if s.needRefresh { - s.refresh(p, line, pos) + err := s.refresh(p, line, pos) + if err != nil { + return "", err + } } if s.multiLineMode { s.resetMultiLine(p, line, pos) @@ -642,14 +685,14 @@ mainLoop: pos -= len(getSuffixGlyphs(line[:pos], 1)) s.needRefresh = true } else { - fmt.Print(beep) + s.doBeep() } case ctrlF: // right if pos < len(line) { pos += len(getPrefixGlyphs(line[pos:], 1)) s.needRefresh = true } else { - fmt.Print(beep) + s.doBeep() } case ctrlD: // del if pos == 0 && len(line) == 0 { @@ -662,7 +705,7 @@ mainLoop: s.restartPrompt() if pos >= len(line) { - fmt.Print(beep) + s.doBeep() } else { n := len(getPrefixGlyphs(line[pos:], 1)) line = append(line[:pos], line[pos+n:]...) @@ -670,7 +713,7 @@ mainLoop: } case ctrlK: // delete remainder of line if pos >= len(line) { - fmt.Print(beep) + s.doBeep() } else { if killAction > 0 { s.addToKillRing(line[pos:], 1) // Add in apend mode @@ -698,7 +741,7 @@ mainLoop: pos = len(line) s.needRefresh = true } else { - fmt.Print(beep) + s.doBeep() } case ctrlN: // down historyAction = true @@ -717,11 +760,11 @@ mainLoop: pos = len(line) s.needRefresh = true } else { - fmt.Print(beep) + s.doBeep() } case ctrlT: // transpose prev glyph with glyph under cursor if len(line) < 2 || pos < 1 { - fmt.Print(beep) + s.doBeep() } else { if pos == len(line) { pos -= len(getSuffixGlyphs(line, 1)) @@ -752,7 +795,7 @@ mainLoop: s.restartPrompt() case ctrlH, bs: // Backspace if pos <= 0 { - fmt.Print(beep) + s.doBeep() } else { n := len(getSuffixGlyphs(line[:pos], 1)) line = append(line[:pos-n], line[pos:]...) @@ -771,42 +814,7 @@ mainLoop: pos = 0 s.needRefresh = true case ctrlW: // Erase word - if pos == 0 { - fmt.Print(beep) - break - } - // Remove whitespace to the left - var buf []rune // Store the deleted chars in a buffer - for { - if pos == 0 || !unicode.IsSpace(line[pos-1]) { - break - } - buf = append(buf, line[pos-1]) - line = append(line[:pos-1], line[pos:]...) - pos-- - } - // Remove non-whitespace to the left - for { - if pos == 0 || unicode.IsSpace(line[pos-1]) { - break - } - buf = append(buf, line[pos-1]) - line = append(line[:pos-1], line[pos:]...) - pos-- - } - // Invert the buffer and save the result on the killRing - var newBuf []rune - for i := len(buf) - 1; i >= 0; i-- { - newBuf = append(newBuf, buf[i]) - } - if killAction > 0 { - s.addToKillRing(newBuf, 2) // Add in prepend mode - } else { - s.addToKillRing(newBuf, 0) // Add in normal mode - } - killAction = 2 // Mark that there was some killing - - s.needRefresh = true + pos, line, killAction = s.eraseWord(pos, line, killAction) case ctrlY: // Paste from Yank buffer line, pos, next, err = s.yank(p, line, pos) goto haveNext @@ -825,7 +833,7 @@ mainLoop: fallthrough // Catch unhandled control codes (anything <= 31) case 0, 28, 29, 30, 31: - fmt.Print(beep) + s.doBeep() default: if pos == len(line) && !s.multiLineMode && len(p)+len(line) < s.columns*4 && // Avoid countGlyphs on large lines @@ -843,7 +851,7 @@ mainLoop: switch v { case del: if pos >= len(line) { - fmt.Print(beep) + s.doBeep() } else { n := len(getPrefixGlyphs(line[pos:], 1)) line = append(line[:pos], line[pos+n:]...) @@ -852,7 +860,7 @@ mainLoop: if pos > 0 { pos -= len(getSuffixGlyphs(line[:pos], 1)) } else { - fmt.Print(beep) + s.doBeep() } case wordLeft, altB: if pos > 0 { @@ -873,13 +881,13 @@ mainLoop: } } } else { - fmt.Print(beep) + s.doBeep() } case right: if pos < len(line) { pos += len(getPrefixGlyphs(line[pos:], 1)) } else { - fmt.Print(beep) + s.doBeep() } case wordRight, altF: if pos < len(line) { @@ -900,7 +908,7 @@ mainLoop: } } } else { - fmt.Print(beep) + s.doBeep() } case up: historyAction = true @@ -917,7 +925,7 @@ mainLoop: line = []rune(historyPrefix[historyPos]) pos = len(line) } else { - fmt.Print(beep) + s.doBeep() } case down: historyAction = true @@ -935,12 +943,43 @@ mainLoop: } pos = len(line) } else { - fmt.Print(beep) + s.doBeep() } case home: // Start of line pos = 0 case end: // End of line pos = len(line) + case altD: // Delete next word + if pos == len(line) { + s.doBeep() + break + } + // Remove whitespace to the right + var buf []rune // Store the deleted chars in a buffer + for { + if pos == len(line) || !unicode.IsSpace(line[pos]) { + break + } + buf = append(buf, line[pos]) + line = append(line[:pos], line[pos+1:]...) + } + // Remove non-whitespace to the right + for { + if pos == len(line) || unicode.IsSpace(line[pos]) { + break + } + buf = append(buf, line[pos]) + line = append(line[:pos], line[pos+1:]...) + } + // Save the result on the killRing + if killAction > 0 { + s.addToKillRing(buf, 2) // Add in prepend mode + } else { + s.addToKillRing(buf, 0) // Add in normal mode + } + killAction = 2 // Mark that there was some killing + case altBs: // Erase word + pos, line, killAction = s.eraseWord(pos, line, killAction) case winch: // Window change if s.multiLineMode { if s.maxRows-s.cursorRows > 0 { @@ -958,7 +997,10 @@ mainLoop: s.needRefresh = true } if s.needRefresh && !s.inputWaiting() { - s.refresh(p, line, pos) + err := s.refresh(p, line, pos) + if err != nil { + return "", err + } } if !historyAction { historyStale = true @@ -978,7 +1020,7 @@ func (s *State) PasswordPrompt(prompt string) (string, error) { return "", ErrInvalidPrompt } } - if !s.terminalSupported { + if !s.terminalSupported || s.columns == 0 { return "", errors.New("liner: function not supported in this terminal") } if s.inputRedirected { @@ -988,6 +1030,8 @@ func (s *State) PasswordPrompt(prompt string) (string, error) { return "", ErrNotTerminalOutput } + p := []rune(prompt) + defer s.stopPrompt() restart: @@ -995,7 +1039,6 @@ restart: s.getColumns() fmt.Print(prompt) - p := []rune(prompt) var line []rune pos := 0 @@ -1013,12 +1056,6 @@ mainLoop: case rune: switch v { case cr, lf: - if s.needRefresh { - s.refresh(p, line, pos) - } - if s.multiLineMode { - s.resetMultiLine(p, line, pos) - } fmt.Println() break mainLoop case ctrlD: // del @@ -1032,10 +1069,13 @@ mainLoop: s.restartPrompt() case ctrlL: // clear screen s.eraseScreen() - s.refresh(p, []rune{}, 0) + err := s.refresh(p, []rune{}, 0) + if err != nil { + return "", err + } case ctrlH, bs: // Backspace if pos <= 0 { - fmt.Print(beep) + s.doBeep() } else { n := len(getSuffixGlyphs(line[:pos], 1)) line = append(line[:pos-n], line[pos:]...) @@ -1043,9 +1083,6 @@ mainLoop: } case ctrlC: fmt.Println("^C") - if s.multiLineMode { - s.resetMultiLine(p, line, pos) - } if s.ctrlCAborts { return "", ErrPromptAborted } @@ -1059,7 +1096,7 @@ mainLoop: fallthrough // Catch unhandled control codes (anything <= 31) case 0, 28, 29, 30, 31: - fmt.Print(beep) + s.doBeep() default: line = append(line[:pos], append([]rune{v}, line[pos:]...)...) pos++ @@ -1068,3 +1105,66 @@ mainLoop: } return string(line), nil } + +func (s *State) tooNarrow(prompt string) (string, error) { + // Docker and OpenWRT and etc sometimes return 0 column width + // Reset mode temporarily. Restore baked mode in case the terminal + // is wide enough for the next Prompt attempt. + m, merr := TerminalMode() + s.origMode.ApplyMode() + if merr == nil { + defer m.ApplyMode() + } + if s.r == nil { + // Windows does not always set s.r + s.r = bufio.NewReader(os.Stdin) + defer func() { s.r = nil }() + } + return s.promptUnsupported(prompt) +} + +func (s *State) eraseWord(pos int, line []rune, killAction int) (int, []rune, int) { + if pos == 0 { + s.doBeep() + return pos, line, killAction + } + // Remove whitespace to the left + var buf []rune // Store the deleted chars in a buffer + for { + if pos == 0 || !unicode.IsSpace(line[pos-1]) { + break + } + buf = append(buf, line[pos-1]) + line = append(line[:pos-1], line[pos:]...) + pos-- + } + // Remove non-whitespace to the left + for { + if pos == 0 || unicode.IsSpace(line[pos-1]) { + break + } + buf = append(buf, line[pos-1]) + line = append(line[:pos-1], line[pos:]...) + pos-- + } + // Invert the buffer and save the result on the killRing + var newBuf []rune + for i := len(buf) - 1; i >= 0; i-- { + newBuf = append(newBuf, buf[i]) + } + if killAction > 0 { + s.addToKillRing(newBuf, 2) // Add in prepend mode + } else { + s.addToKillRing(newBuf, 0) // Add in normal mode + } + killAction = 2 // Mark that there was some killing + + s.needRefresh = true + return pos, line, killAction +} + +func (s *State) doBeep() { + if !s.noBeep { + fmt.Print(beep) + } +} diff --git a/vendor/github.com/peterh/liner/output.go b/vendor/github.com/peterh/liner/output.go index 6d83d4ebfd7..f1ac510e6ab 100644 --- a/vendor/github.com/peterh/liner/output.go +++ b/vendor/github.com/peterh/liner/output.go @@ -1,4 +1,5 @@ -// +build linux darwin openbsd freebsd netbsd +//go:build linux || darwin || openbsd || freebsd || netbsd || solaris +// +build linux darwin openbsd freebsd netbsd solaris package liner @@ -6,8 +7,6 @@ import ( "fmt" "os" "strings" - "syscall" - "unsafe" ) func (s *State) cursorPos(x int) { @@ -48,20 +47,6 @@ type winSize struct { xpixel, ypixel uint16 } -func (s *State) getColumns() bool { - var ws winSize - ok, _, _ := syscall.Syscall(syscall.SYS_IOCTL, uintptr(syscall.Stdout), - syscall.TIOCGWINSZ, uintptr(unsafe.Pointer(&ws))) - if int(ok) < 0 { - return false - } - s.columns = int(ws.col) - if cursorColumn && s.columns > 1 { - s.columns-- - } - return true -} - func (s *State) checkOutput() { // xterm is known to support CHA if strings.Contains(strings.ToLower(os.Getenv("TERM")), "xterm") { diff --git a/vendor/github.com/peterh/liner/output_solaris.go b/vendor/github.com/peterh/liner/output_solaris.go new file mode 100644 index 00000000000..2b8adf2ad0f --- /dev/null +++ b/vendor/github.com/peterh/liner/output_solaris.go @@ -0,0 +1,14 @@ +package liner + +import ( + "golang.org/x/sys/unix" +) + +func (s *State) getColumns() bool { + ws, err := unix.IoctlGetWinsize(unix.Stdout, unix.TIOCGWINSZ) + if err != nil { + return false + } + s.columns = int(ws.Col) + return true +} diff --git a/vendor/github.com/peterh/liner/output_unix.go b/vendor/github.com/peterh/liner/output_unix.go new file mode 100644 index 00000000000..8f0ba7191eb --- /dev/null +++ b/vendor/github.com/peterh/liner/output_unix.go @@ -0,0 +1,20 @@ +//go:build linux || darwin || openbsd || freebsd || netbsd +// +build linux darwin openbsd freebsd netbsd + +package liner + +import ( + "syscall" + "unsafe" +) + +func (s *State) getColumns() bool { + var ws winSize + ok, _, _ := syscall.Syscall(syscall.SYS_IOCTL, uintptr(syscall.Stdout), + syscall.TIOCGWINSZ, uintptr(unsafe.Pointer(&ws))) + if int(ok) < 0 { + return false + } + s.columns = int(ws.col) + return true +} diff --git a/vendor/github.com/peterh/liner/output_windows.go b/vendor/github.com/peterh/liner/output_windows.go index 63c9c5d7571..45cd978c96d 100644 --- a/vendor/github.com/peterh/liner/output_windows.go +++ b/vendor/github.com/peterh/liner/output_windows.go @@ -69,8 +69,4 @@ func (s *State) getColumns() { var sbi consoleScreenBufferInfo procGetConsoleScreenBufferInfo.Call(uintptr(s.hOut), uintptr(unsafe.Pointer(&sbi))) s.columns = int(sbi.dwSize.x) - if s.columns > 1 { - // Windows 10 needs a spare column for the cursor - s.columns-- - } } diff --git a/vendor/github.com/peterh/liner/unixmode.go b/vendor/github.com/peterh/liner/unixmode.go index 9838923f5db..59b3d4681e4 100644 --- a/vendor/github.com/peterh/liner/unixmode.go +++ b/vendor/github.com/peterh/liner/unixmode.go @@ -1,3 +1,4 @@ +//go:build linux || darwin || freebsd || openbsd || netbsd // +build linux darwin freebsd openbsd netbsd package liner @@ -21,17 +22,16 @@ func (mode *termios) ApplyMode() error { // This function is provided for convenience, and should // not be necessary for most users of liner. func TerminalMode() (ModeApplier, error) { - mode, errno := getMode(syscall.Stdin) - - if errno != 0 { - return nil, errno - } - return mode, nil + return getMode(syscall.Stdin) } -func getMode(handle int) (*termios, syscall.Errno) { +func getMode(handle int) (*termios, error) { var mode termios + var err error _, _, errno := syscall.Syscall(syscall.SYS_IOCTL, uintptr(handle), getTermios, uintptr(unsafe.Pointer(&mode))) + if errno != 0 { + err = errno + } - return &mode, errno + return &mode, err } diff --git a/vendor/github.com/peterh/liner/unixmode_solaris.go b/vendor/github.com/peterh/liner/unixmode_solaris.go new file mode 100644 index 00000000000..4926d0b5a6b --- /dev/null +++ b/vendor/github.com/peterh/liner/unixmode_solaris.go @@ -0,0 +1,22 @@ +package liner + +import ( + "golang.org/x/sys/unix" +) + +func (mode *termios) ApplyMode() error { + return unix.IoctlSetTermios(unix.Stdin, setTermios, (*unix.Termios)(mode)) +} + +// TerminalMode returns the current terminal input mode as an InputModeSetter. +// +// This function is provided for convenience, and should +// not be necessary for most users of liner. +func TerminalMode() (ModeApplier, error) { + return getMode(unix.Stdin) +} + +func getMode(handle int) (*termios, error) { + tos, err := unix.IoctlGetTermios(handle, getTermios) + return (*termios)(tos), err +} diff --git a/vendor/github.com/peterh/liner/width.go b/vendor/github.com/peterh/liner/width.go index 42e89998341..0395f3a8be6 100644 --- a/vendor/github.com/peterh/liner/width.go +++ b/vendor/github.com/peterh/liner/width.go @@ -1,6 +1,10 @@ package liner -import "unicode" +import ( + "unicode" + + "github.com/mattn/go-runewidth" +) // These character classes are mostly zero width (when combined). // A few might not be, depending on the user's font. Fixing this @@ -13,13 +17,6 @@ var zeroWidth = []*unicode.RangeTable{ unicode.Cf, } -var doubleWidth = []*unicode.RangeTable{ - unicode.Han, - unicode.Hangul, - unicode.Hiragana, - unicode.Katakana, -} - // countGlyphs considers zero-width characters to be zero glyphs wide, // and members of Chinese, Japanese, and Korean scripts to be 2 glyphs wide. func countGlyphs(s []rune) int { @@ -31,13 +28,7 @@ func countGlyphs(s []rune) int { continue } - switch { - case unicode.IsOneOf(zeroWidth, r): - case unicode.IsOneOf(doubleWidth, r): - n += 2 - default: - n++ - } + n += runewidth.RuneWidth(r) } return n } @@ -49,17 +40,17 @@ func countMultiLineGlyphs(s []rune, columns int, start int) int { n++ continue } - switch { - case unicode.IsOneOf(zeroWidth, r): - case unicode.IsOneOf(doubleWidth, r): + switch runewidth.RuneWidth(r) { + case 0: + case 1: + n++ + case 2: n += 2 // no room for a 2-glyphs-wide char in the ending // so skip a column and display it at the beginning if n%columns == 1 { n++ } - default: - n++ } } return n diff --git a/vendor/modules.txt b/vendor/modules.txt index 13833020648..2ea9a3fa624 100644 --- a/vendor/modules.txt +++ b/vendor/modules.txt @@ -177,7 +177,7 @@ github.com/opencontainers/go-digest ## explicit; go 1.17 github.com/opencontainers/image-spec/specs-go github.com/opencontainers/image-spec/specs-go/v1 -# github.com/peterh/liner v0.0.0-20170211195444-bf27d3ba8e1d +# github.com/peterh/liner v1.2.2 ## explicit github.com/peterh/liner # github.com/pkg/errors v0.9.1