Fix: a backup plan when can't get size of terminal (#71)

* fix (0,0) size

* fix
This commit is contained in:
chzyer 2016-08-31 23:51:28 +08:00 committed by GitHub
parent 8159bd380c
commit 27fdf0b285
6 changed files with 141 additions and 28 deletions

View File

@ -58,10 +58,13 @@ func (o *opCompleter) nextCandidate(i int) {
}
}
func (o *opCompleter) OnComplete() {
func (o *opCompleter) OnComplete() bool {
if o.width == 0 {
return false
}
if o.IsInCompleteSelectMode() {
o.doSelect()
return
return true
}
buf := o.op.buf
@ -70,7 +73,7 @@ func (o *opCompleter) OnComplete() {
if o.IsInCompleteMode() && o.candidateSource != nil && runes.Equal(rs, o.candidateSource) {
o.EnterCompleteSelectMode()
o.doSelect()
return
return true
}
o.ExitCompleteSelectMode()
@ -78,7 +81,7 @@ func (o *opCompleter) OnComplete() {
newLines, offset := o.op.cfg.AutoComplete.Do(rs, buf.idx)
if len(newLines) == 0 {
o.ExitCompleteMode(false)
return
return true
}
// only Aggregate candidates in non-complete mode
@ -86,18 +89,19 @@ func (o *opCompleter) OnComplete() {
if len(newLines) == 1 {
buf.WriteRunes(newLines[0])
o.ExitCompleteMode(false)
return
return true
}
same, size := runes.Aggregate(newLines)
if size > 0 {
buf.WriteRunes(same)
o.ExitCompleteMode(false)
return
return true
}
}
o.EnterCompleteMode(offset, newLines)
return true
}
func (o *opCompleter) IsInCompleteSelectMode() bool {

View File

@ -153,15 +153,26 @@ func (o *Operation) ioloop() {
o.t.Bell()
break
}
o.OnComplete()
keepInCompleteMode = true
if o.OnComplete() {
keepInCompleteMode = true
} else {
o.t.Bell()
break
}
case CharBckSearch:
o.SearchMode(S_DIR_BCK)
if !o.SearchMode(S_DIR_BCK) {
o.t.Bell()
break
}
keepInSearchMode = true
case CharCtrlU:
o.buf.KillFront()
case CharFwdSearch:
o.SearchMode(S_DIR_FWD)
if !o.SearchMode(S_DIR_FWD) {
o.t.Bell()
break
}
keepInSearchMode = true
case CharKill:
o.buf.Kill()
@ -345,6 +356,7 @@ func (o *Operation) Runes() ([]rune, error) {
if o.cfg.Listener != nil {
o.cfg.Listener.OnChange(nil, 0, 0)
}
o.buf.Refresh(nil) // print prompt
o.t.KickRead()
select {

View File

@ -5,6 +5,7 @@ import (
"bytes"
"io"
"strings"
"sync"
)
type runeBufferBck struct {
@ -25,6 +26,10 @@ type RuneBuffer struct {
width int
bck *runeBufferBck
offset string
sync.Mutex
}
func (r *RuneBuffer) OnWidthChange(newWidth int) {
@ -370,6 +375,9 @@ func (r *RuneBuffer) getSplitByLine(rs []rune) []string {
}
func (r *RuneBuffer) IdxLine(width int) int {
if width == 0 {
return 0
}
sp := r.getSplitByLine(r.buf[:r.idx])
return len(sp) - 1
}
@ -379,12 +387,16 @@ func (r *RuneBuffer) CursorLineCount() int {
}
func (r *RuneBuffer) Refresh(f func()) {
r.Lock()
defer r.Unlock()
if !r.interactive {
if f != nil {
f()
}
return
}
r.Clean()
if f != nil {
f()
@ -392,6 +404,12 @@ func (r *RuneBuffer) Refresh(f func()) {
r.print()
}
func (r *RuneBuffer) SetOffset(offset string) {
r.Lock()
r.offset = offset
r.Unlock()
}
func (r *RuneBuffer) print() {
r.w.Write(r.output())
r.hadClean = false
@ -473,15 +491,21 @@ func (r *RuneBuffer) SetPrompt(prompt string) {
func (r *RuneBuffer) cleanOutput(w io.Writer, idxLine int) {
buf := bufio.NewWriter(w)
buf.Write([]byte("\033[J")) // just like ^k :)
if idxLine == 0 {
io.WriteString(buf, "\033[2K\r")
if r.width == 0 {
buf.WriteString(strings.Repeat("\r\b", len(r.buf)+r.PromptLen()))
buf.Write([]byte("\033[J"))
} else {
for i := 0; i < idxLine; i++ {
io.WriteString(buf, "\033[2K\r\033[A")
buf.Write([]byte("\033[J")) // just like ^k :)
if idxLine == 0 {
buf.WriteString("\033[2K")
buf.WriteString("\r")
} else {
for i := 0; i < idxLine; i++ {
io.WriteString(buf, "\033[2K\r\033[A")
}
io.WriteString(buf, "\033[2K\r")
}
io.WriteString(buf, "\033[2K\r")
}
buf.Flush()
return

View File

@ -96,7 +96,10 @@ func (o *opSearch) SearchChar(r rune) {
o.search(true)
}
func (o *opSearch) SearchMode(dir int) {
func (o *opSearch) SearchMode(dir int) bool {
if o.width == 0 {
return false
}
alreadyInMode := o.inMode
o.inMode = true
o.dir = dir
@ -106,6 +109,7 @@ func (o *opSearch) SearchMode(dir int) {
} else {
o.SearchRefresh(-1)
}
return true
}
func (o *opSearch) ExitSearchMode(revert bool) {

View File

@ -17,6 +17,8 @@ type Terminal struct {
wg sync.WaitGroup
isReading int32
sleeping int32
sizeChan chan string
}
func NewTerminal(cfg *Config) (*Terminal, error) {
@ -28,6 +30,7 @@ func NewTerminal(cfg *Config) (*Terminal, error) {
kickChan: make(chan struct{}, 1),
outchan: make(chan rune),
stopChan: make(chan struct{}, 1),
sizeChan: make(chan string, 1),
}
go t.ioloop()
@ -60,6 +63,18 @@ func (t *Terminal) Write(b []byte) (int, error) {
return t.cfg.Stdout.Write(b)
}
type termSize struct {
left int
top int
}
func (t *Terminal) GetOffset(f func(offset string)) {
go func() {
f(<-t.sizeChan)
}()
t.Write([]byte("\033[6n"))
}
func (t *Terminal) Print(s string) {
fmt.Fprintf(t.cfg.Stdout, "%s", s)
}
@ -132,7 +147,24 @@ func (t *Terminal) ioloop() {
r = escapeKey(r, buf)
} else if isEscapeEx {
isEscapeEx = false
r = escapeExKey(r, buf)
if key := readEscKey(r, buf); key != nil {
r = escapeExKey(key)
// offset
if key.typ == 'R' {
if _, _, ok := key.Get2(); ok {
select {
case t.sizeChan <- key.attr:
default:
}
}
expectNextChar = true
continue
}
}
if r == 0 {
expectNextChar = true
continue
}
}
expectNextChar = true

View File

@ -4,8 +4,10 @@ import (
"bufio"
"bytes"
"strconv"
"strings"
"sync"
"time"
"unicode"
)
var (
@ -54,8 +56,9 @@ func IsPrintable(key rune) bool {
}
// translate Esc[X
func escapeExKey(r rune, reader *bufio.Reader) rune {
switch r {
func escapeExKey(key *escapeKeyPair) rune {
var r rune
switch key.typ {
case 'D':
r = CharBackward
case 'C':
@ -68,19 +71,53 @@ func escapeExKey(r rune, reader *bufio.Reader) rune {
r = CharLineStart
case 'F':
r = CharLineEnd
default:
if r == '3' && reader != nil {
d, _, _ := reader.ReadRune()
if d == '~' {
r = CharDelete
} else {
reader.UnreadRune()
}
case '~':
if key.attr == "3" {
r = CharDelete
}
default:
}
return r
}
type escapeKeyPair struct {
attr string
typ rune
}
func (e *escapeKeyPair) Get2() (int, int, bool) {
sp := strings.Split(e.attr, ";")
if len(sp) < 2 {
return -1, -1, false
}
s1, err := strconv.Atoi(sp[0])
if err != nil {
return -1, -1, false
}
s2, err := strconv.Atoi(sp[1])
if err != nil {
return -1, -1, false
}
return s1, s2, true
}
func readEscKey(r rune, reader *bufio.Reader) *escapeKeyPair {
p := escapeKeyPair{}
buf := bytes.NewBuffer(nil)
for {
if r == ';' {
} else if unicode.IsNumber(r) {
} else {
p.typ = r
break
}
buf.WriteRune(r)
r, _, _ = reader.ReadRune()
}
p.attr = buf.String()
return &p
}
// translate EscX to Meta+X
func escapeKey(r rune, reader *bufio.Reader) rune {
switch r {