fix multiline

This commit is contained in:
Cheney 2016-03-05 15:27:12 +08:00
parent c4ec21b1c6
commit 4ae9d7e0fd
2 changed files with 66 additions and 23 deletions

View File

@ -2,6 +2,7 @@ package readline
import ( import (
"bytes" "bytes"
"fmt"
"io" "io"
"strings" "strings"
@ -293,7 +294,8 @@ func (r *RuneBuffer) MoveToLineEnd() {
} }
func (r *RuneBuffer) LineCount() int { func (r *RuneBuffer) LineCount() int {
return LineCount(r.cfg.FuncGetWidth, runes.WidthAll(r.buf)+r.PromptLen()) return LineCount(r.cfg.FuncGetWidth(),
runes.WidthAll(r.buf)+r.PromptLen())
} }
func (r *RuneBuffer) MoveTo(ch rune, prevChar, reverse bool) (success bool) { func (r *RuneBuffer) MoveTo(ch rune, prevChar, reverse bool) (success bool) {
@ -326,22 +328,9 @@ func (r *RuneBuffer) MoveTo(ch rune, prevChar, reverse bool) (success bool) {
} }
func (r *RuneBuffer) IdxLine() int { func (r *RuneBuffer) IdxLine() int {
totalWidth := runes.WidthAll(r.buf[:r.idx]) + r.PromptLen() sw := r.cfg.FuncGetWidth()
w := r.cfg.FuncGetWidth() sp := SplitByLine(r.PromptLen(), sw, r.buf[:r.idx])
if w <= 0 { return len(sp) - 1
return -1
}
line := totalWidth / w
// if cursor is in last colmun and not any character behind it
// the cursor will in the first line, otherwise will in the second line
// this situation only occurs in golang's Stdout
// TODO: figure out why
if totalWidth%w == 0 && len(r.buf) == r.idx && !isWindows {
line--
}
return line
} }
func (r *RuneBuffer) CursorLineCount() int { func (r *RuneBuffer) CursorLineCount() int {
@ -373,11 +362,45 @@ func (r *RuneBuffer) output() []byte {
} else { } else {
buf.Write([]byte(string(r.cfg.MaskRune))) buf.Write([]byte(string(r.cfg.MaskRune)))
} }
if len(r.buf) > r.idx {
buf.Write(runes.Backspace(r.buf[r.idx:]))
}
} else { } else {
buf.Write([]byte(string(r.buf))) sw := r.cfg.FuncGetWidth()
} sp := SplitByLine(r.PromptLen(), sw, r.buf)
if len(r.buf) > r.idx { written := 0
buf.Write(runes.Backspace(r.buf[r.idx:])) idxInLine := 0
for idx, s := range sp {
buf.Write([]byte(s))
if r.idx > written && r.idx < written+len(s) {
idxInLine = r.idx - written
}
written += len(s)
if idx < len(sp)-1 && !isWindows {
buf.Write([]byte{'\n'})
}
}
if len(r.buf) > r.idx {
targetLine := r.IdxLine()
currentLine := len(sp) - 1
// assert currentLine >= targetLine
if targetLine == 0 {
idxInLine += r.PromptLen()
}
buf.WriteString("\r")
if currentLine > targetLine {
buf.WriteString(fmt.Sprintf(
"\033[%vA", currentLine-targetLine,
))
}
if idxInLine > 0 {
buf.WriteString(fmt.Sprintf(
"\033[%vC", idxInLine,
))
}
}
} }
return buf.Bytes() return buf.Bytes()
} }

View File

@ -2,9 +2,12 @@ package readline
import ( import (
"bufio" "bufio"
"bytes"
"strconv" "strconv"
"syscall" "syscall"
"github.com/chzyer/readline/runes"
"golang.org/x/crypto/ssh/terminal" "golang.org/x/crypto/ssh/terminal"
) )
@ -86,9 +89,26 @@ func escapeKey(r rune) rune {
return r return r
} }
func SplitByLine(start, screenWidth int, rs []rune) []string {
var ret []string
buf := bytes.NewBuffer(nil)
currentWidth := start
for _, r := range rs {
w := runes.Width(r)
currentWidth += w
buf.WriteRune(r)
if currentWidth >= screenWidth {
ret = append(ret, buf.String())
buf.Reset()
currentWidth = 0
}
}
ret = append(ret, buf.String())
return ret
}
// calculate how many lines for N character // calculate how many lines for N character
func LineCount(getWidth func() int, w int) int { func LineCount(screenWidth, w int) int {
screenWidth := getWidth()
r := w / screenWidth r := w / screenWidth
if w%screenWidth != 0 { if w%screenWidth != 0 {
r++ r++