diff options
author | Péter Szilágyi <peterke@gmail.com> | 2016-02-11 22:16:52 +0800 |
---|---|---|
committer | Péter Szilágyi <peterke@gmail.com> | 2016-02-11 22:16:52 +0800 |
commit | b019f3ee29ce55c3d515ee8bafe0f4bb14221c0a (patch) | |
tree | 26e023be6c99a10e82a5a0ebadd1e42cefe9bd3c /Godeps/_workspace/src/github.com/peterh | |
parent | b05e472c076d30035233d6a8b5fb3360b236e3ff (diff) | |
download | dexon-b019f3ee29ce55c3d515ee8bafe0f4bb14221c0a.tar dexon-b019f3ee29ce55c3d515ee8bafe0f4bb14221c0a.tar.gz dexon-b019f3ee29ce55c3d515ee8bafe0f4bb14221c0a.tar.bz2 dexon-b019f3ee29ce55c3d515ee8bafe0f4bb14221c0a.tar.lz dexon-b019f3ee29ce55c3d515ee8bafe0f4bb14221c0a.tar.xz dexon-b019f3ee29ce55c3d515ee8bafe0f4bb14221c0a.tar.zst dexon-b019f3ee29ce55c3d515ee8bafe0f4bb14221c0a.zip |
Godeps: update all dependencies to latest code
Diffstat (limited to 'Godeps/_workspace/src/github.com/peterh')
13 files changed, 308 insertions, 371 deletions
diff --git a/Godeps/_workspace/src/github.com/peterh/liner/README.md b/Godeps/_workspace/src/github.com/peterh/liner/README.md index 99027c6e2..9148b2492 100644 --- a/Godeps/_workspace/src/github.com/peterh/liner/README.md +++ b/Godeps/_workspace/src/github.com/peterh/liner/README.md @@ -21,8 +21,8 @@ Ctrl-A, Home | Move cursor to beginning of line Ctrl-E, End | Move cursor to end of line Ctrl-B, Left | Move cursor one character left Ctrl-F, Right| Move cursor one character right -Ctrl-Left | Move cursor to previous word -Ctrl-Right | Move cursor to next word +Ctrl-Left, Alt-B | Move cursor to previous word +Ctrl-Right, Alt-F | Move cursor to next word Ctrl-D, Del | (if line is *not* empty) Delete character under cursor Ctrl-D | (if line *is* empty) End of File - usually quits application Ctrl-C | Reset input (create new empty prompt) @@ -48,13 +48,14 @@ package main import ( "log" "os" + "path/filepath" "strings" "github.com/peterh/liner" ) var ( - history_fn = "/tmp/.liner_history" + history_fn = filepath.Join(os.TempDir(), ".liner_example_history") names = []string{"john", "james", "mary", "nancy"} ) @@ -62,6 +63,8 @@ func main() { line := liner.NewLiner() defer line.Close() + line.SetCtrlCAborts(true) + line.SetCompleter(func(line string) (c []string) { for _, n := range names { if strings.HasPrefix(n, strings.ToLower(line)) { @@ -76,11 +79,13 @@ func main() { f.Close() } - if name, err := line.Prompt("What is your name? "); err != nil { - log.Print("Error reading line: ", err) - } else { + if name, err := line.Prompt("What is your name? "); err == nil { log.Print("Got: ", name) line.AppendHistory(name) + } else if err == liner.ErrPromptAborted { + log.Print("Aborted") + } else { + log.Print("Error reading line: ", err) } if f, err := os.Create(history_fn); err != nil { diff --git a/Godeps/_workspace/src/github.com/peterh/liner/common.go b/Godeps/_workspace/src/github.com/peterh/liner/common.go index f8753a195..b6162b624 100644 --- a/Godeps/_workspace/src/github.com/peterh/liner/common.go +++ b/Godeps/_workspace/src/github.com/peterh/liner/common.go @@ -7,7 +7,6 @@ package liner import ( "bufio" - "bytes" "container/ring" "errors" "fmt" @@ -29,6 +28,10 @@ type commonState struct { ctrlCAborts bool r *bufio.Reader tabStyle TabStyle + multiLineMode bool + cursorRows int + maxRows int + shouldRestart ShouldRestart } // TabStyle is used to select how tab completions are displayed. @@ -174,7 +177,7 @@ func (s *State) SetCompleter(f Completer) { return } s.completer = func(line string, pos int) (string, []string, string) { - return "", f(line[:pos]), line[pos:] + return "", f(string([]rune(line)[:pos])), string([]rune(line)[pos:]) } } @@ -207,13 +210,28 @@ func (s *State) SetCtrlCAborts(aborts bool) { s.ctrlCAborts = aborts } +// SetMultiLineMode sets whether line is auto-wrapped. The default is false (single line). +func (s *State) SetMultiLineMode(mlmode bool) { + s.multiLineMode = mlmode +} + +// ShouldRestart is passed the error generated by readNext and returns true if +// the the read should be restarted or false if the error should be returned. +type ShouldRestart func(err error) bool + +// SetShouldRestart sets the restart function that Liner will call to determine +// whether to retry the call to, or return the error returned by, readNext. +func (s *State) SetShouldRestart(f ShouldRestart) { + s.shouldRestart = f +} + func (s *State) promptUnsupported(p string) (string, error) { - if !s.inputRedirected { + if !s.inputRedirected || !s.terminalSupported { fmt.Print(p) } linebuf, _, err := s.r.ReadLine() if err != nil { return "", err } - return string(bytes.TrimSpace(linebuf)), nil + return string(linebuf), nil } diff --git a/Godeps/_workspace/src/github.com/peterh/liner/input.go b/Godeps/_workspace/src/github.com/peterh/liner/input.go index cf71d2bce..c80c85f69 100644 --- a/Godeps/_workspace/src/github.com/peterh/liner/input.go +++ b/Godeps/_workspace/src/github.com/peterh/liner/input.go @@ -44,14 +44,15 @@ func NewLiner() *State { if m, err := TerminalMode(); err == nil { s.origMode = *m.(*termios) } else { - s.terminalSupported = false s.inputRedirected = true } if _, err := getMode(syscall.Stdout); err != 0 { - s.terminalSupported = false s.outputRedirected = true } - if s.terminalSupported { + if s.inputRedirected && s.outputRedirected { + s.terminalSupported = false + } + if s.terminalSupported && !s.inputRedirected && !s.outputRedirected { mode := s.origMode mode.Iflag &^= icrnl | inpck | istrip | ixon mode.Cflag |= cs8 @@ -328,6 +329,12 @@ func (s *State) readNext() (interface{}, error) { default: return unknown, nil } + case 'b': + s.pending = s.pending[:0] // escape code complete + return altB, nil + case 'f': + s.pending = s.pending[:0] // escape code complete + return altF, nil case 'y': s.pending = s.pending[:0] // escape code complete return altY, nil @@ -344,7 +351,7 @@ func (s *State) readNext() (interface{}, error) { // Close returns the terminal to its previous mode func (s *State) Close() error { stopSignal(s.winch) - if s.terminalSupported { + if !s.inputRedirected { s.origMode.ApplyMode() } return nil @@ -353,6 +360,8 @@ func (s *State) Close() error { // TerminalSupported returns true if the current terminal supports // line editing features, and false if liner will use the 'dumb' // fallback for input. +// Note that TerminalSupported does not check all factors that may +// cause liner to not fully support the terminal (such as stdin redirection) func TerminalSupported() bool { bad := map[string]bool{"": true, "dumb": true, "cons25": true} return !bad[strings.ToLower(os.Getenv("TERM"))] diff --git a/Godeps/_workspace/src/github.com/peterh/liner/input_test.go b/Godeps/_workspace/src/github.com/peterh/liner/input_test.go deleted file mode 100644 index e515a4894..000000000 --- a/Godeps/_workspace/src/github.com/peterh/liner/input_test.go +++ /dev/null @@ -1,61 +0,0 @@ -// +build !windows - -package liner - -import ( - "bufio" - "bytes" - "testing" -) - -func (s *State) expectRune(t *testing.T, r rune) { - item, err := s.readNext() - if err != nil { - t.Fatalf("Expected rune '%c', got error %s\n", r, err) - } - if v, ok := item.(rune); !ok { - t.Fatalf("Expected rune '%c', got non-rune %v\n", r, v) - } else { - if v != r { - t.Fatalf("Expected rune '%c', got rune '%c'\n", r, v) - } - } -} - -func (s *State) expectAction(t *testing.T, a action) { - item, err := s.readNext() - if err != nil { - t.Fatalf("Expected Action %d, got error %s\n", a, err) - } - if v, ok := item.(action); !ok { - t.Fatalf("Expected Action %d, got non-Action %v\n", a, v) - } else { - if v != a { - t.Fatalf("Expected Action %d, got Action %d\n", a, v) - } - } -} - -func TestTypes(t *testing.T) { - input := []byte{'A', 27, 'B', 27, 91, 68, 27, '[', '1', ';', '5', 'D', 'e'} - var s State - s.r = bufio.NewReader(bytes.NewBuffer(input)) - - next := make(chan nexter) - go func() { - for { - var n nexter - n.r, _, n.err = s.r.ReadRune() - next <- n - } - }() - s.next = next - - s.expectRune(t, 'A') - s.expectRune(t, 27) - s.expectRune(t, 'B') - s.expectAction(t, left) - s.expectAction(t, wordLeft) - - s.expectRune(t, 'e') -} diff --git a/Godeps/_workspace/src/github.com/peterh/liner/input_windows.go b/Godeps/_workspace/src/github.com/peterh/liner/input_windows.go index cc98719c1..9dcc3115c 100644 --- a/Godeps/_workspace/src/github.com/peterh/liner/input_windows.go +++ b/Godeps/_workspace/src/github.com/peterh/liner/input_windows.go @@ -132,6 +132,8 @@ const ( vk_f10 = 0x79 vk_f11 = 0x7a vk_f12 = 0x7b + bKey = 0x42 + fKey = 0x46 yKey = 0x59 ) @@ -178,6 +180,12 @@ func (s *State) readNext() (interface{}, error) { if ke.VirtualKeyCode == vk_tab && ke.ControlKeyState&modKeys == shiftPressed { s.key = shiftTab + } else if ke.VirtualKeyCode == bKey && (ke.ControlKeyState&modKeys == leftAltPressed || + ke.ControlKeyState&modKeys == rightAltPressed) { + s.key = altB + } else if ke.VirtualKeyCode == fKey && (ke.ControlKeyState&modKeys == leftAltPressed || + ke.ControlKeyState&modKeys == rightAltPressed) { + s.key = altF } else if ke.VirtualKeyCode == yKey && (ke.ControlKeyState&modKeys == leftAltPressed || ke.ControlKeyState&modKeys == rightAltPressed) { s.key = altY diff --git a/Godeps/_workspace/src/github.com/peterh/liner/line.go b/Godeps/_workspace/src/github.com/peterh/liner/line.go index a70fb59e5..95364ae56 100644 --- a/Godeps/_workspace/src/github.com/peterh/liner/line.go +++ b/Godeps/_workspace/src/github.com/peterh/liner/line.go @@ -37,6 +37,8 @@ const ( f10 f11 f12 + altB + altF altY shiftTab wordLeft @@ -88,6 +90,14 @@ const ( ) func (s *State) refresh(prompt []rune, buf []rune, pos int) error { + if s.multiLineMode { + return s.refreshMultiLine(prompt, buf, pos) + } else { + return s.refreshSingleLine(prompt, buf, pos) + } +} + +func (s *State) refreshSingleLine(prompt []rune, buf []rune, pos int) error { s.cursorPos(0) _, err := fmt.Print(string(prompt)) if err != nil { @@ -143,6 +153,82 @@ func (s *State) refresh(prompt []rune, buf []rune, pos int) error { return err } +func (s *State) refreshMultiLine(prompt []rune, buf []rune, pos int) error { + promptColumns := countMultiLineGlyphs(prompt, s.columns, 0) + totalColumns := countMultiLineGlyphs(buf, s.columns, promptColumns) + totalRows := (totalColumns + s.columns - 1) / s.columns + maxRows := s.maxRows + if totalRows > s.maxRows { + s.maxRows = totalRows + } + cursorRows := s.cursorRows + if cursorRows == 0 { + cursorRows = 1 + } + + /* First step: clear all the lines used before. To do so start by + * going to the last row. */ + if maxRows-cursorRows > 0 { + s.moveDown(maxRows - cursorRows) + } + + /* Now for every row clear it, go up. */ + for i := 0; i < maxRows-1; i++ { + s.cursorPos(0) + s.eraseLine() + s.moveUp(1) + } + + /* Clean the top line. */ + s.cursorPos(0) + s.eraseLine() + + /* Write the prompt and the current buffer content */ + if _, err := fmt.Print(string(prompt)); err != nil { + return err + } + if _, err := fmt.Print(string(buf)); err != nil { + return err + } + + /* If we are at the very end of the screen with our prompt, we need to + * emit a newline and move the prompt to the first column. */ + cursorColumns := countMultiLineGlyphs(buf[:pos], s.columns, promptColumns) + if cursorColumns == totalColumns && totalColumns%s.columns == 0 { + s.emitNewLine() + s.cursorPos(0) + totalRows++ + if totalRows > s.maxRows { + s.maxRows = totalRows + } + } + + /* Move cursor to right position. */ + cursorRows = (cursorColumns + s.columns) / s.columns + if s.cursorRows > 0 && totalRows-cursorRows > 0 { + s.moveUp(totalRows - cursorRows) + } + /* Set column. */ + s.cursorPos(cursorColumns % s.columns) + + s.cursorRows = cursorRows + return nil +} + +func (s *State) resetMultiLine(prompt []rune, buf []rune, pos int) { + columns := countMultiLineGlyphs(prompt, s.columns, 0) + columns = countMultiLineGlyphs(buf[:pos], s.columns, columns) + columns += 2 // ^C + cursorRows := (columns + s.columns) / s.columns + if s.maxRows-cursorRows > 0 { + for i := 0; i < s.maxRows-cursorRows; i++ { + fmt.Println() // always moves the cursor down or scrolls the window up as needed + } + } + s.maxRows = 1 + s.cursorRows = 0 +} + func longestCommonPrefix(strs []string) string { if len(strs) == 0 { return "" @@ -179,6 +265,29 @@ func (s *State) circularTabs(items []string) func(tabDirection) (string, error) } } +func calculateColumns(screenWidth int, items []string) (numColumns, numRows, maxWidth int) { + for _, item := range items { + if len(item) >= screenWidth { + return 1, len(items), screenWidth - 1 + } + if len(item) >= maxWidth { + maxWidth = len(item) + 1 + } + } + + numColumns = screenWidth / maxWidth + numRows = len(items) / numColumns + if len(items)%numColumns > 0 { + numRows++ + } + + if len(items) <= numColumns { + maxWidth = 0 + } + + return +} + func (s *State) printedTabs(items []string) func(tabDirection) (string, error) { numTabs := 1 prefix := longestCommonPrefix(items) @@ -190,6 +299,7 @@ func (s *State) printedTabs(items []string) func(tabDirection) (string, error) { if numTabs == 2 { if len(items) > 100 { fmt.Printf("\nDisplay all %d possibilities? (y or n) ", len(items)) + prompt: for { next, err := s.readNext() if err != nil { @@ -197,36 +307,26 @@ func (s *State) printedTabs(items []string) func(tabDirection) (string, error) { } if key, ok := next.(rune); ok { - if unicode.ToLower(key) == 'n' { + switch key { + case 'n', 'N': return prefix, nil - } else if unicode.ToLower(key) == 'y' { - break + case 'y', 'Y': + break prompt + case ctrlC, ctrlD, cr, lf: + s.restartPrompt() } } } } fmt.Println("") - maxWidth := 0 - for _, item := range items { - if len(item) >= maxWidth { - maxWidth = len(item) + 1 - } - } - numColumns := s.columns / maxWidth - numRows := len(items) / numColumns - if len(items)%numColumns > 0 { - numRows++ - } + numColumns, numRows, maxWidth := calculateColumns(s.columns, items) - if len(items) <= numColumns { - maxWidth = 0 - } for i := 0; i < numRows; i++ { for j := 0; j < numColumns*numRows; j += numRows { if i+j < len(items) { if maxWidth > 0 { - fmt.Printf("%-*s", maxWidth, items[i+j]) + fmt.Printf("%-*.[1]*s", maxWidth, items[i+j]) } else { fmt.Printf("%v ", items[i+j]) } @@ -460,26 +560,20 @@ func (s *State) yank(p []rune, text []rune, pos int) ([]rune, int, interface{}, return line, pos, esc, nil } -// Prompt displays p, and then waits for user input. Prompt allows line editing -// if the terminal supports it. +// Prompt displays p and 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. Prompt allows line editing if the terminal supports it. func (s *State) Prompt(prompt string) (string, error) { - if s.inputRedirected { + if s.inputRedirected || !s.terminalSupported { return s.promptUnsupported(prompt) } if s.outputRedirected { return "", ErrNotTerminalOutput } - if !s.terminalSupported { - return s.promptUnsupported(prompt) - } s.historyMutex.RLock() defer s.historyMutex.RUnlock() - s.startPrompt() - defer s.stopPrompt() - s.getColumns() - fmt.Print(prompt) p := []rune(prompt) var line []rune @@ -489,11 +583,21 @@ func (s *State) Prompt(prompt string) (string, error) { historyPos := len(prefixHistory) historyAction := false // used to mark history related actions killAction := 0 // used to mark kill related actions + + defer s.stopPrompt() + +restart: + s.startPrompt() + s.getColumns() + mainLoop: for { next, err := s.readNext() haveNext: if err != nil { + if s.shouldRestart != nil && s.shouldRestart(err) { + goto restart + } return "", err } @@ -502,6 +606,9 @@ mainLoop: case rune: switch v { case cr, lf: + if s.multiLineMode { + s.resetMultiLine(p, line, pos) + } fmt.Println() break mainLoop case ctrlA: // Start of line @@ -603,6 +710,9 @@ mainLoop: s.refresh(p, line, pos) case ctrlC: // reset fmt.Println("^C") + if s.multiLineMode { + s.resetMultiLine(p, line, pos) + } if s.ctrlCAborts { return "", ErrPromptAborted } @@ -687,7 +797,7 @@ mainLoop: case 0, 28, 29, 30, 31: fmt.Print(beep) default: - if pos == len(line) && len(p)+len(line) < s.columns-1 { + if pos == len(line) && !s.multiLineMode && countGlyphs(p)+countGlyphs(line) < s.columns-1 { line = append(line, v) fmt.Printf("%c", v) pos++ @@ -712,11 +822,21 @@ mainLoop: } else { fmt.Print(beep) } - case wordLeft: + case wordLeft, altB: if pos > 0 { + var spaceHere, spaceLeft, leftKnown bool for { pos-- - if pos == 0 || unicode.IsSpace(line[pos-1]) { + if pos == 0 { + break + } + if leftKnown { + spaceHere = spaceLeft + } else { + spaceHere = unicode.IsSpace(line[pos]) + } + spaceLeft, leftKnown = unicode.IsSpace(line[pos-1]), true + if !spaceHere && spaceLeft { break } } @@ -729,11 +849,21 @@ mainLoop: } else { fmt.Print(beep) } - case wordRight: + case wordRight, altF: if pos < len(line) { + var spaceHere, spaceLeft, hereKnown bool for { pos++ - if pos == len(line) || unicode.IsSpace(line[pos]) { + if pos == len(line) { + break + } + if hereKnown { + spaceLeft = spaceHere + } else { + spaceLeft = unicode.IsSpace(line[pos-1]) + } + spaceHere, hereKnown = unicode.IsSpace(line[pos]), true + if spaceHere && !spaceLeft { break } } @@ -769,6 +899,19 @@ mainLoop: pos = 0 case end: // End of line pos = len(line) + case winch: // Window change + if s.multiLineMode { + if s.maxRows-s.cursorRows > 0 { + s.moveDown(s.maxRows - s.cursorRows) + } + for i := 0; i < s.maxRows-1; i++ { + s.cursorPos(0) + s.eraseLine() + s.moveUp(1) + } + s.maxRows = 1 + s.cursorRows = 1 + } } s.refresh(p, line, pos) } @@ -786,18 +929,20 @@ 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) { + if !s.terminalSupported { + return "", errors.New("liner: function not supported in this terminal") + } if s.inputRedirected { return s.promptUnsupported(prompt) } if s.outputRedirected { return "", ErrNotTerminalOutput } - if !s.terminalSupported { - return "", errors.New("liner: function not supported in this terminal") - } - s.startPrompt() defer s.stopPrompt() + +restart: + s.startPrompt() s.getColumns() fmt.Print(prompt) @@ -809,6 +954,9 @@ mainLoop: for { next, err := s.readNext() if err != nil { + if s.shouldRestart != nil && s.shouldRestart(err) { + goto restart + } return "", err } @@ -816,6 +964,9 @@ mainLoop: case rune: switch v { case cr, lf: + if s.multiLineMode { + s.resetMultiLine(p, line, pos) + } fmt.Println() break mainLoop case ctrlD: // del @@ -840,6 +991,9 @@ mainLoop: } case ctrlC: fmt.Println("^C") + if s.multiLineMode { + s.resetMultiLine(p, line, pos) + } if s.ctrlCAborts { return "", ErrPromptAborted } diff --git a/Godeps/_workspace/src/github.com/peterh/liner/line_test.go b/Godeps/_workspace/src/github.com/peterh/liner/line_test.go deleted file mode 100644 index 727da6ce7..000000000 --- a/Godeps/_workspace/src/github.com/peterh/liner/line_test.go +++ /dev/null @@ -1,90 +0,0 @@ -package liner - -import ( - "bytes" - "strings" - "testing" -) - -func TestAppend(t *testing.T) { - var s State - s.AppendHistory("foo") - s.AppendHistory("bar") - - var out bytes.Buffer - num, err := s.WriteHistory(&out) - if err != nil { - t.Fatal("Unexpected error writing history", err) - } - if num != 2 { - t.Fatalf("Expected 2 history entries, got %d", num) - } - - s.AppendHistory("baz") - num, err = s.WriteHistory(&out) - if err != nil { - t.Fatal("Unexpected error writing history", err) - } - if num != 3 { - t.Fatalf("Expected 3 history entries, got %d", num) - } - - s.AppendHistory("baz") - num, err = s.WriteHistory(&out) - if err != nil { - t.Fatal("Unexpected error writing history", err) - } - if num != 3 { - t.Fatalf("Expected 3 history entries after duplicate append, got %d", num) - } - - s.AppendHistory("baz") - -} - -func TestHistory(t *testing.T) { - input := `foo -bar -baz -quux -dingle` - - var s State - num, err := s.ReadHistory(strings.NewReader(input)) - if err != nil { - t.Fatal("Unexpected error reading history", err) - } - if num != 5 { - t.Fatal("Wrong number of history entries read") - } - - var out bytes.Buffer - num, err = s.WriteHistory(&out) - if err != nil { - t.Fatal("Unexpected error writing history", err) - } - if num != 5 { - t.Fatal("Wrong number of history entries written") - } - if strings.TrimSpace(out.String()) != input { - t.Fatal("Round-trip failure") - } - - // Test reading with a trailing newline present - var s2 State - num, err = s2.ReadHistory(&out) - if err != nil { - t.Fatal("Unexpected error reading history the 2nd time", err) - } - if num != 5 { - t.Fatal("Wrong number of history entries read the 2nd time") - } - - num, err = s.ReadHistory(strings.NewReader(input + "\n\xff")) - if err == nil { - t.Fatal("Unexpected success reading corrupted history", err) - } - if num != 5 { - t.Fatal("Wrong number of history entries read the 3rd time") - } -} diff --git a/Godeps/_workspace/src/github.com/peterh/liner/output.go b/Godeps/_workspace/src/github.com/peterh/liner/output.go index e91f4ea81..049273b53 100644 --- a/Godeps/_workspace/src/github.com/peterh/liner/output.go +++ b/Godeps/_workspace/src/github.com/peterh/liner/output.go @@ -31,6 +31,18 @@ func (s *State) eraseScreen() { fmt.Print("\x1b[H\x1b[2J") } +func (s *State) moveUp(lines int) { + fmt.Printf("\x1b[%dA", lines) +} + +func (s *State) moveDown(lines int) { + fmt.Printf("\x1b[%dB", lines) +} + +func (s *State) emitNewLine() { + fmt.Print("\n") +} + type winSize struct { row, col uint16 xpixel, ypixel uint16 diff --git a/Godeps/_workspace/src/github.com/peterh/liner/output_windows.go b/Godeps/_workspace/src/github.com/peterh/liner/output_windows.go index 27ae55a14..45cd978c9 100644 --- a/Godeps/_workspace/src/github.com/peterh/liner/output_windows.go +++ b/Godeps/_workspace/src/github.com/peterh/liner/output_windows.go @@ -47,6 +47,24 @@ func (s *State) eraseScreen() { procSetConsoleCursorPosition.Call(uintptr(s.hOut), 0) } +func (s *State) moveUp(lines int) { + var sbi consoleScreenBufferInfo + procGetConsoleScreenBufferInfo.Call(uintptr(s.hOut), uintptr(unsafe.Pointer(&sbi))) + procSetConsoleCursorPosition.Call(uintptr(s.hOut), + uintptr(int(sbi.dwCursorPosition.x)&0xFFFF|(int(sbi.dwCursorPosition.y)-lines)<<16)) +} + +func (s *State) moveDown(lines int) { + var sbi consoleScreenBufferInfo + procGetConsoleScreenBufferInfo.Call(uintptr(s.hOut), uintptr(unsafe.Pointer(&sbi))) + procSetConsoleCursorPosition.Call(uintptr(s.hOut), + uintptr(int(sbi.dwCursorPosition.x)&0xFFFF|(int(sbi.dwCursorPosition.y)+lines)<<16)) +} + +func (s *State) emitNewLine() { + // windows doesn't need to omit a new line +} + func (s *State) getColumns() { var sbi consoleScreenBufferInfo procGetConsoleScreenBufferInfo.Call(uintptr(s.hOut), uintptr(unsafe.Pointer(&sbi))) diff --git a/Godeps/_workspace/src/github.com/peterh/liner/prefix_test.go b/Godeps/_workspace/src/github.com/peterh/liner/prefix_test.go deleted file mode 100644 index c826d6c3b..000000000 --- a/Godeps/_workspace/src/github.com/peterh/liner/prefix_test.go +++ /dev/null @@ -1,37 +0,0 @@ -// +build windows linux darwin openbsd freebsd netbsd - -package liner - -import "testing" - -type testItem struct { - list []string - prefix string -} - -func TestPrefix(t *testing.T) { - list := []testItem{ - {[]string{"food", "foot"}, "foo"}, - {[]string{"foo", "foot"}, "foo"}, - {[]string{"food", "foo"}, "foo"}, - {[]string{"food", "foe", "foot"}, "fo"}, - {[]string{"food", "foot", "barbeque"}, ""}, - {[]string{"cafeteria", "café"}, "caf"}, - {[]string{"cafe", "café"}, "caf"}, - {[]string{"cafè", "café"}, "caf"}, - {[]string{"cafés", "café"}, "café"}, - {[]string{"áéíóú", "áéíóú"}, "áéíóú"}, - {[]string{"éclairs", "éclairs"}, "éclairs"}, - {[]string{"éclairs are the best", "éclairs are great", "éclairs"}, "éclairs"}, - {[]string{"éclair", "éclairs"}, "éclair"}, - {[]string{"éclairs", "éclair"}, "éclair"}, - {[]string{"éclair", "élan"}, "é"}, - } - - for _, test := range list { - lcp := longestCommonPrefix(test.list) - if lcp != test.prefix { - t.Errorf("%s != %s for %+v", lcp, test.prefix, test.list) - } - } -} diff --git a/Godeps/_workspace/src/github.com/peterh/liner/race_test.go b/Godeps/_workspace/src/github.com/peterh/liner/race_test.go deleted file mode 100644 index e320849c7..000000000 --- a/Godeps/_workspace/src/github.com/peterh/liner/race_test.go +++ /dev/null @@ -1,44 +0,0 @@ -// +build race - -package liner - -import ( - "io/ioutil" - "os" - "sync" - "testing" -) - -func TestWriteHistory(t *testing.T) { - oldout := os.Stdout - defer func() { os.Stdout = oldout }() - oldin := os.Stdout - defer func() { os.Stdin = oldin }() - - newinr, newinw, err := os.Pipe() - if err != nil { - t.Fatal(err) - } - os.Stdin = newinr - newoutr, newoutw, err := os.Pipe() - if err != nil { - t.Fatal(err) - } - defer newoutr.Close() - os.Stdout = newoutw - - var wait sync.WaitGroup - wait.Add(1) - s := NewLiner() - go func() { - s.AppendHistory("foo") - s.AppendHistory("bar") - s.Prompt("") - wait.Done() - }() - - s.WriteHistory(ioutil.Discard) - - newinw.Close() - wait.Wait() -} diff --git a/Godeps/_workspace/src/github.com/peterh/liner/width.go b/Godeps/_workspace/src/github.com/peterh/liner/width.go index 02cfb5e1b..d8984aae9 100644 --- a/Godeps/_workspace/src/github.com/peterh/liner/width.go +++ b/Godeps/_workspace/src/github.com/peterh/liner/width.go @@ -13,10 +13,42 @@ 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 { n := 0 for _, r := range s { - if !unicode.IsOneOf(zeroWidth, r) { + switch { + case unicode.IsOneOf(zeroWidth, r): + case unicode.IsOneOf(doubleWidth, r): + n += 2 + default: + n++ + } + } + return n +} + +func countMultiLineGlyphs(s []rune, columns int, start int) int { + n := start + for _, r := range s { + switch { + case unicode.IsOneOf(zeroWidth, r): + case unicode.IsOneOf(doubleWidth, r): + 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++ } } diff --git a/Godeps/_workspace/src/github.com/peterh/liner/width_test.go b/Godeps/_workspace/src/github.com/peterh/liner/width_test.go deleted file mode 100644 index 134920a4b..000000000 --- a/Godeps/_workspace/src/github.com/peterh/liner/width_test.go +++ /dev/null @@ -1,87 +0,0 @@ -package liner - -import ( - "strconv" - "testing" -) - -func accent(in []rune) []rune { - var out []rune - for _, r := range in { - out = append(out, r) - out = append(out, '\u0301') - } - return out -} - -var testString = []rune("query") - -func TestCountGlyphs(t *testing.T) { - count := countGlyphs(testString) - if count != len(testString) { - t.Errorf("ASCII count incorrect. %d != %d", count, len(testString)) - } - count = countGlyphs(accent(testString)) - if count != len(testString) { - t.Errorf("Accent count incorrect. %d != %d", count, len(testString)) - } -} - -func compare(a, b []rune, name string, t *testing.T) { - if len(a) != len(b) { - t.Errorf(`"%s" != "%s" in %s"`, string(a), string(b), name) - return - } - for i := range a { - if a[i] != b[i] { - t.Errorf(`"%s" != "%s" in %s"`, string(a), string(b), name) - return - } - } -} - -func TestPrefixGlyphs(t *testing.T) { - for i := 0; i <= len(testString); i++ { - iter := strconv.Itoa(i) - out := getPrefixGlyphs(testString, i) - compare(out, testString[:i], "ascii prefix "+iter, t) - out = getPrefixGlyphs(accent(testString), i) - compare(out, accent(testString[:i]), "accent prefix "+iter, t) - } - out := getPrefixGlyphs(testString, 999) - compare(out, testString, "ascii prefix overflow", t) - out = getPrefixGlyphs(accent(testString), 999) - compare(out, accent(testString), "accent prefix overflow", t) - - out = getPrefixGlyphs(testString, -3) - if len(out) != 0 { - t.Error("ascii prefix negative") - } - out = getPrefixGlyphs(accent(testString), -3) - if len(out) != 0 { - t.Error("accent prefix negative") - } -} - -func TestSuffixGlyphs(t *testing.T) { - for i := 0; i <= len(testString); i++ { - iter := strconv.Itoa(i) - out := getSuffixGlyphs(testString, i) - compare(out, testString[len(testString)-i:], "ascii suffix "+iter, t) - out = getSuffixGlyphs(accent(testString), i) - compare(out, accent(testString[len(testString)-i:]), "accent suffix "+iter, t) - } - out := getSuffixGlyphs(testString, 999) - compare(out, testString, "ascii suffix overflow", t) - out = getSuffixGlyphs(accent(testString), 999) - compare(out, accent(testString), "accent suffix overflow", t) - - out = getSuffixGlyphs(testString, -3) - if len(out) != 0 { - t.Error("ascii suffix negative") - } - out = getSuffixGlyphs(accent(testString), -3) - if len(out) != 0 { - t.Error("accent suffix negative") - } -} |