未验证 提交 9f8b352a 编写于 作者: D Davide Masserut 提交者: GitHub

vendor: update liner dependency (#2700)

This change update liner to the latest release that adds support to
delete the next and the previous word with Alt-d and Alt-BackSpace
plus a small amount of fixes.
上级 a3897eaf
......@@ -11,7 +11,7 @@ require (
github.com/hashicorp/golang-lru v0.5.4
github.com/mattn/go-colorable v0.0.9
github.com/mattn/go-isatty v0.0.3
github.com/peterh/liner v0.0.0-20170317030525-88609521dc4b
github.com/peterh/liner v1.2.1
github.com/sirupsen/logrus v1.6.0
github.com/spf13/cobra v1.1.3
go.starlark.net v0.0.0-20200821142938-949cc6f4b097
......
......@@ -128,6 +128,8 @@ github.com/mattn/go-colorable v0.0.9 h1:UVL0vNpWh04HeJXV0KLcaT7r06gOH2l4OW6ddYRU
github.com/mattn/go-colorable v0.0.9/go.mod h1:9vuHe8Xs5qXnSaW/c/ABM9alt+Vo+STaOChaDxuIBZU=
github.com/mattn/go-isatty v0.0.3 h1:ns/ykhmWi7G9O+8a448SecJU3nSMBXJfqQkl0upE1jI=
github.com/mattn/go-isatty v0.0.3/go.mod h1:M+lRXTBqGeGNdLjl/ufCoiOlB5xdOkqRJdNxMWT7Zi4=
github.com/mattn/go-runewidth v0.0.3 h1:a+kO+98RDGEfo6asOGMmpodZq4FNtnGP54yps8BzLR4=
github.com/mattn/go-runewidth v0.0.3/go.mod h1:LwmH8dsx7+W8Uxz3IHJYH5QSwggIsqBzpuz5H//U1FU=
github.com/matttproud/golang_protobuf_extensions v1.0.1/go.mod h1:D8He9yQNgCq6Z5Ld7szi9bcBfOoFv/3dc6xSMkL2PC0=
github.com/miekg/dns v1.0.14/go.mod h1:W1PPwlIAgtquWBMBEV9nkV9Cazfe8ScdGz/Lj7v3Nrg=
github.com/mitchellh/cli v1.0.0/go.mod h1:hNIlj7HEI86fIcpObd7a0FcrxTWetlwJDGcceTlRvqc=
......@@ -144,8 +146,8 @@ github.com/mwitkow/go-conntrack v0.0.0-20161129095857-cc309e4a2223/go.mod h1:qRW
github.com/oklog/ulid v1.3.1/go.mod h1:CirwcVhetQ6Lv90oh/F+FBtV6XMibvdAFo93nm5qn4U=
github.com/pascaldekloe/goe v0.0.0-20180627143212-57f6aae5913c/go.mod h1:lzWF7FIEvWOWxwDKqyGYQf6ZUaNfKdP144TG7ZOy1lc=
github.com/pelletier/go-toml v1.2.0/go.mod h1:5z9KED0ma1S8pY6P1sdut58dfprrGBbd/94hg7ilaic=
github.com/peterh/liner v0.0.0-20170317030525-88609521dc4b h1:8uaXtUkxiy+T/zdLWuxa/PG4so0TPZDZfafFNNSaptE=
github.com/peterh/liner v0.0.0-20170317030525-88609521dc4b/go.mod h1:xIteQHvHuaLYG9IFj6mSxM0fCKrs34IrEQUhOYuGPHc=
github.com/peterh/liner v1.2.1 h1:O4BlKaq/LWu6VRWmol4ByWfzx6MfXc5Op5HETyIy5yg=
github.com/peterh/liner v1.2.1/go.mod h1:CRroGNssyjTd/qIG2FyxByd2S8JEAZXBl4qUrZf8GS0=
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/pmezard/go-difflib v1.0.0 h1:4DBwDE0NGyQoBHbLQYPwSUPoCMWR5BEzIk/f1lZbAQM=
......
language: go
go:
- tip
before_install:
- go get github.com/mattn/goveralls
- go get golang.org/x/tools/cmd/cover
script:
- $HOME/gopath/bin/goveralls -repotoken lAKAWPzcGsD3A8yBX3BGGtRUdJ6CaGERL
The MIT License (MIT)
Copyright (c) 2016 Yasuhiro Matsumoto
Permission is hereby granted, free of charge, to any person obtaining a copy
of this software and associated documentation files (the "Software"), to deal
in the Software without restriction, including without limitation the rights
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
copies of the Software, and to permit persons to whom the Software is
furnished to do so, subject to the following conditions:
The above copyright notice and this permission notice shall be included in all
copies or substantial portions of the Software.
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
SOFTWARE.
go-runewidth
============
[![Build Status](https://travis-ci.org/mattn/go-runewidth.png?branch=master)](https://travis-ci.org/mattn/go-runewidth)
[![Coverage Status](https://coveralls.io/repos/mattn/go-runewidth/badge.png?branch=HEAD)](https://coveralls.io/r/mattn/go-runewidth?branch=HEAD)
[![GoDoc](https://godoc.org/github.com/mattn/go-runewidth?status.svg)](http://godoc.org/github.com/mattn/go-runewidth)
[![Go Report Card](https://goreportcard.com/badge/github.com/mattn/go-runewidth)](https://goreportcard.com/report/github.com/mattn/go-runewidth)
Provides functions to get fixed width of the character or string.
Usage
-----
```go
runewidth.StringWidth("つのだ☆HIRO") == 12
```
Author
------
Yasuhiro Matsumoto
License
-------
under the MIT License: http://mattn.mit-license.org/2013
此差异已折叠。
// +build js
package runewidth
func IsEastAsian() bool {
// TODO: Implement this for the web. Detect east asian in a compatible way, and return true.
return false
}
// +build !windows,!js
package runewidth
import (
"os"
"regexp"
"strings"
)
var reLoc = regexp.MustCompile(`^[a-z][a-z][a-z]?(?:_[A-Z][A-Z])?\.(.+)`)
var mblenTable = map[string]int{
"utf-8": 6,
"utf8": 6,
"jis": 8,
"eucjp": 3,
"euckr": 2,
"euccn": 2,
"sjis": 2,
"cp932": 2,
"cp51932": 2,
"cp936": 2,
"cp949": 2,
"cp950": 2,
"big5": 2,
"gbk": 2,
"gb2312": 2,
}
func isEastAsian(locale string) bool {
charset := strings.ToLower(locale)
r := reLoc.FindStringSubmatch(locale)
if len(r) == 2 {
charset = strings.ToLower(r[1])
}
if strings.HasSuffix(charset, "@cjk_narrow") {
return false
}
for pos, b := range []byte(charset) {
if b == '@' {
charset = charset[:pos]
break
}
}
max := 1
if m, ok := mblenTable[charset]; ok {
max = m
}
if max > 1 && (charset[0] != 'u' ||
strings.HasPrefix(locale, "ja") ||
strings.HasPrefix(locale, "ko") ||
strings.HasPrefix(locale, "zh")) {
return true
}
return false
}
// IsEastAsian return true if the current locale is CJK
func IsEastAsian() bool {
locale := os.Getenv("LC_CTYPE")
if locale == "" {
locale = os.Getenv("LANG")
}
// ignore C locale
if locale == "POSIX" || locale == "C" {
return false
}
if len(locale) > 1 && locale[0] == 'C' && (locale[1] == '.' || locale[1] == '-') {
return false
}
return isEastAsian(locale)
}
package runewidth
import (
"syscall"
)
var (
kernel32 = syscall.NewLazyDLL("kernel32")
procGetConsoleOutputCP = kernel32.NewProc("GetConsoleOutputCP")
)
// IsEastAsian return true if the current locale is CJK
func IsEastAsian() bool {
r1, _, _ := procGetConsoleOutputCP.Call()
if r1 == 0 {
return false
}
switch int(r1) {
case 932, 51932, 936, 949, 950:
return true
}
return false
}
......@@ -29,7 +29,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
......
......@@ -32,6 +32,7 @@ type commonState struct {
cursorRows int
maxRows int
shouldRestart ShouldRestart
noBeep bool
needRefresh bool
}
......@@ -144,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()
......@@ -161,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
......@@ -243,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)
......
......@@ -55,3 +55,5 @@ func (n noopMode) ApplyMode() error {
func TerminalMode() (ModeApplier, error) {
return noopMode{}, nil
}
const cursorColumn = true
module github.com/peterh/liner
require github.com/mattn/go-runewidth v0.0.3
github.com/mattn/go-runewidth v0.0.3 h1:a+kO+98RDGEfo6asOGMmpodZq4FNtnGP54yps8BzLR4=
github.com/mattn/go-runewidth v0.0.3/go.mod h1:LwmH8dsx7+W8Uxz3IHJYH5QSwggIsqBzpuz5H//U1FU=
......@@ -264,9 +264,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 +328,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
......
......@@ -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
)
......@@ -174,6 +178,8 @@ func (s *State) readNext() (interface{}, error) {
var rv uint32
prv := uintptr(unsafe.Pointer(&rv))
var surrogate uint16
for {
ok, _, err := procReadConsoleInput.Call(uintptr(s.handle), pbuf, 1, prv)
......@@ -184,9 +190,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 +197,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 +229,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 +364,5 @@ func TerminalMode() (ModeApplier, error) {
}
return mode, err
}
const cursorColumn = true
......@@ -40,6 +40,8 @@ const (
f11
f12
altB
altBs // Alt+Backspace
altD
altF
altY
shiftTab
......@@ -112,6 +114,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))
......@@ -162,6 +168,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 {
......@@ -435,7 +449,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 {
......@@ -443,11 +457,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:]...)
......@@ -583,7 +597,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) {
......@@ -618,8 +632,8 @@ 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 {
err := s.refresh(p, line, pos)
......@@ -670,14 +684,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 {
......@@ -690,7 +704,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:]...)
......@@ -698,7 +712,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
......@@ -726,7 +740,7 @@ mainLoop:
pos = len(line)
s.needRefresh = true
} else {
fmt.Print(beep)
s.doBeep()
}
case ctrlN: // down
historyAction = true
......@@ -745,11 +759,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))
......@@ -780,7 +794,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:]...)
......@@ -799,42 +813,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
......@@ -853,7 +832,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
......@@ -871,7 +850,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:]...)
......@@ -880,7 +859,7 @@ mainLoop:
if pos > 0 {
pos -= len(getSuffixGlyphs(line[:pos], 1))
} else {
fmt.Print(beep)
s.doBeep()
}
case wordLeft, altB:
if pos > 0 {
......@@ -901,13 +880,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) {
......@@ -928,7 +907,7 @@ mainLoop:
}
}
} else {
fmt.Print(beep)
s.doBeep()
}
case up:
historyAction = true
......@@ -945,7 +924,7 @@ mainLoop:
line = []rune(historyPrefix[historyPos])
pos = len(line)
} else {
fmt.Print(beep)
s.doBeep()
}
case down:
historyAction = true
......@@ -963,12 +942,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 {
......@@ -1020,10 +1030,6 @@ func (s *State) PasswordPrompt(prompt string) (string, error) {
}
p := []rune(prompt)
const minWorkingSpace = 1
if s.columns < countGlyphs(p)+minWorkingSpace {
return s.tooNarrow(prompt)
}
defer s.stopPrompt()
......@@ -1049,15 +1055,6 @@ mainLoop:
case rune:
switch v {
case cr, lf:
if s.needRefresh {
err := s.refresh(p, line, pos)
if err != nil {
return "", err
}
}
if s.multiLineMode {
s.resetMultiLine(p, line, pos)
}
fmt.Println()
break mainLoop
case ctrlD: // del
......@@ -1077,7 +1074,7 @@ mainLoop:
}
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:]...)
......@@ -1085,9 +1082,6 @@ mainLoop:
}
case ctrlC:
fmt.Println("^C")
if s.multiLineMode {
s.resetMultiLine(p, line, pos)
}
if s.ctrlCAborts {
return "", ErrPromptAborted
}
......@@ -1101,7 +1095,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++
......@@ -1127,3 +1121,49 @@ func (s *State) tooNarrow(prompt string) (string, error) {
}
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)
}
}
......@@ -56,9 +56,6 @@ func (s *State) getColumns() bool {
return false
}
s.columns = int(ws.col)
if cursorColumn && s.columns > 1 {
s.columns--
}
return true
}
......
......@@ -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--
}
}
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
......
......@@ -29,7 +29,9 @@ github.com/mattn/go-colorable
# github.com/mattn/go-isatty v0.0.3
## explicit
github.com/mattn/go-isatty
# github.com/peterh/liner v0.0.0-20170317030525-88609521dc4b
# github.com/mattn/go-runewidth v0.0.3
github.com/mattn/go-runewidth
# github.com/peterh/liner v1.2.1
## explicit
github.com/peterh/liner
# github.com/russross/blackfriday/v2 v2.0.1
......
Markdown is supported
0% .
You are about to add 0 people to the discussion. Proceed with caution.
先完成此消息的编辑!
想要评论请 注册