forked from mirror/readline
Fix: a backup plan when can't get size of terminal (#71)
* fix (0,0) size * fix
This commit is contained in:
parent
8159bd380c
commit
27fdf0b285
16
complete.go
16
complete.go
|
@ -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 {
|
||||
|
|
20
operation.go
20
operation.go
|
@ -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 {
|
||||
|
|
36
runebuf.go
36
runebuf.go
|
@ -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
|
||||
|
|
|
@ -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) {
|
||||
|
|
34
terminal.go
34
terminal.go
|
@ -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
|
||||
|
|
57
utils.go
57
utils.go
|
@ -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 {
|
||||
|
|
Loading…
Reference in New Issue