aboutsummaryrefslogtreecommitdiffstats
path: root/Godeps/_workspace/src/github.com/peterh/liner/line.go
diff options
context:
space:
mode:
Diffstat (limited to 'Godeps/_workspace/src/github.com/peterh/liner/line.go')
-rw-r--r--Godeps/_workspace/src/github.com/peterh/liner/line.go228
1 files changed, 191 insertions, 37 deletions
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
}