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() {
|
if o.IsInCompleteSelectMode() {
|
||||||
o.doSelect()
|
o.doSelect()
|
||||||
return
|
return true
|
||||||
}
|
}
|
||||||
|
|
||||||
buf := o.op.buf
|
buf := o.op.buf
|
||||||
|
@ -70,7 +73,7 @@ func (o *opCompleter) OnComplete() {
|
||||||
if o.IsInCompleteMode() && o.candidateSource != nil && runes.Equal(rs, o.candidateSource) {
|
if o.IsInCompleteMode() && o.candidateSource != nil && runes.Equal(rs, o.candidateSource) {
|
||||||
o.EnterCompleteSelectMode()
|
o.EnterCompleteSelectMode()
|
||||||
o.doSelect()
|
o.doSelect()
|
||||||
return
|
return true
|
||||||
}
|
}
|
||||||
|
|
||||||
o.ExitCompleteSelectMode()
|
o.ExitCompleteSelectMode()
|
||||||
|
@ -78,7 +81,7 @@ func (o *opCompleter) OnComplete() {
|
||||||
newLines, offset := o.op.cfg.AutoComplete.Do(rs, buf.idx)
|
newLines, offset := o.op.cfg.AutoComplete.Do(rs, buf.idx)
|
||||||
if len(newLines) == 0 {
|
if len(newLines) == 0 {
|
||||||
o.ExitCompleteMode(false)
|
o.ExitCompleteMode(false)
|
||||||
return
|
return true
|
||||||
}
|
}
|
||||||
|
|
||||||
// only Aggregate candidates in non-complete mode
|
// only Aggregate candidates in non-complete mode
|
||||||
|
@ -86,18 +89,19 @@ func (o *opCompleter) OnComplete() {
|
||||||
if len(newLines) == 1 {
|
if len(newLines) == 1 {
|
||||||
buf.WriteRunes(newLines[0])
|
buf.WriteRunes(newLines[0])
|
||||||
o.ExitCompleteMode(false)
|
o.ExitCompleteMode(false)
|
||||||
return
|
return true
|
||||||
}
|
}
|
||||||
|
|
||||||
same, size := runes.Aggregate(newLines)
|
same, size := runes.Aggregate(newLines)
|
||||||
if size > 0 {
|
if size > 0 {
|
||||||
buf.WriteRunes(same)
|
buf.WriteRunes(same)
|
||||||
o.ExitCompleteMode(false)
|
o.ExitCompleteMode(false)
|
||||||
return
|
return true
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
o.EnterCompleteMode(offset, newLines)
|
o.EnterCompleteMode(offset, newLines)
|
||||||
|
return true
|
||||||
}
|
}
|
||||||
|
|
||||||
func (o *opCompleter) IsInCompleteSelectMode() bool {
|
func (o *opCompleter) IsInCompleteSelectMode() bool {
|
||||||
|
|
20
operation.go
20
operation.go
|
@ -153,15 +153,26 @@ func (o *Operation) ioloop() {
|
||||||
o.t.Bell()
|
o.t.Bell()
|
||||||
break
|
break
|
||||||
}
|
}
|
||||||
o.OnComplete()
|
if o.OnComplete() {
|
||||||
keepInCompleteMode = true
|
keepInCompleteMode = true
|
||||||
|
} else {
|
||||||
|
o.t.Bell()
|
||||||
|
break
|
||||||
|
}
|
||||||
|
|
||||||
case CharBckSearch:
|
case CharBckSearch:
|
||||||
o.SearchMode(S_DIR_BCK)
|
if !o.SearchMode(S_DIR_BCK) {
|
||||||
|
o.t.Bell()
|
||||||
|
break
|
||||||
|
}
|
||||||
keepInSearchMode = true
|
keepInSearchMode = true
|
||||||
case CharCtrlU:
|
case CharCtrlU:
|
||||||
o.buf.KillFront()
|
o.buf.KillFront()
|
||||||
case CharFwdSearch:
|
case CharFwdSearch:
|
||||||
o.SearchMode(S_DIR_FWD)
|
if !o.SearchMode(S_DIR_FWD) {
|
||||||
|
o.t.Bell()
|
||||||
|
break
|
||||||
|
}
|
||||||
keepInSearchMode = true
|
keepInSearchMode = true
|
||||||
case CharKill:
|
case CharKill:
|
||||||
o.buf.Kill()
|
o.buf.Kill()
|
||||||
|
@ -345,6 +356,7 @@ func (o *Operation) Runes() ([]rune, error) {
|
||||||
if o.cfg.Listener != nil {
|
if o.cfg.Listener != nil {
|
||||||
o.cfg.Listener.OnChange(nil, 0, 0)
|
o.cfg.Listener.OnChange(nil, 0, 0)
|
||||||
}
|
}
|
||||||
|
|
||||||
o.buf.Refresh(nil) // print prompt
|
o.buf.Refresh(nil) // print prompt
|
||||||
o.t.KickRead()
|
o.t.KickRead()
|
||||||
select {
|
select {
|
||||||
|
|
36
runebuf.go
36
runebuf.go
|
@ -5,6 +5,7 @@ import (
|
||||||
"bytes"
|
"bytes"
|
||||||
"io"
|
"io"
|
||||||
"strings"
|
"strings"
|
||||||
|
"sync"
|
||||||
)
|
)
|
||||||
|
|
||||||
type runeBufferBck struct {
|
type runeBufferBck struct {
|
||||||
|
@ -25,6 +26,10 @@ type RuneBuffer struct {
|
||||||
width int
|
width int
|
||||||
|
|
||||||
bck *runeBufferBck
|
bck *runeBufferBck
|
||||||
|
|
||||||
|
offset string
|
||||||
|
|
||||||
|
sync.Mutex
|
||||||
}
|
}
|
||||||
|
|
||||||
func (r *RuneBuffer) OnWidthChange(newWidth int) {
|
func (r *RuneBuffer) OnWidthChange(newWidth int) {
|
||||||
|
@ -370,6 +375,9 @@ func (r *RuneBuffer) getSplitByLine(rs []rune) []string {
|
||||||
}
|
}
|
||||||
|
|
||||||
func (r *RuneBuffer) IdxLine(width int) int {
|
func (r *RuneBuffer) IdxLine(width int) int {
|
||||||
|
if width == 0 {
|
||||||
|
return 0
|
||||||
|
}
|
||||||
sp := r.getSplitByLine(r.buf[:r.idx])
|
sp := r.getSplitByLine(r.buf[:r.idx])
|
||||||
return len(sp) - 1
|
return len(sp) - 1
|
||||||
}
|
}
|
||||||
|
@ -379,12 +387,16 @@ func (r *RuneBuffer) CursorLineCount() int {
|
||||||
}
|
}
|
||||||
|
|
||||||
func (r *RuneBuffer) Refresh(f func()) {
|
func (r *RuneBuffer) Refresh(f func()) {
|
||||||
|
r.Lock()
|
||||||
|
defer r.Unlock()
|
||||||
|
|
||||||
if !r.interactive {
|
if !r.interactive {
|
||||||
if f != nil {
|
if f != nil {
|
||||||
f()
|
f()
|
||||||
}
|
}
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
r.Clean()
|
r.Clean()
|
||||||
if f != nil {
|
if f != nil {
|
||||||
f()
|
f()
|
||||||
|
@ -392,6 +404,12 @@ func (r *RuneBuffer) Refresh(f func()) {
|
||||||
r.print()
|
r.print()
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func (r *RuneBuffer) SetOffset(offset string) {
|
||||||
|
r.Lock()
|
||||||
|
r.offset = offset
|
||||||
|
r.Unlock()
|
||||||
|
}
|
||||||
|
|
||||||
func (r *RuneBuffer) print() {
|
func (r *RuneBuffer) print() {
|
||||||
r.w.Write(r.output())
|
r.w.Write(r.output())
|
||||||
r.hadClean = false
|
r.hadClean = false
|
||||||
|
@ -473,15 +491,21 @@ func (r *RuneBuffer) SetPrompt(prompt string) {
|
||||||
|
|
||||||
func (r *RuneBuffer) cleanOutput(w io.Writer, idxLine int) {
|
func (r *RuneBuffer) cleanOutput(w io.Writer, idxLine int) {
|
||||||
buf := bufio.NewWriter(w)
|
buf := bufio.NewWriter(w)
|
||||||
buf.Write([]byte("\033[J")) // just like ^k :)
|
|
||||||
|
|
||||||
if idxLine == 0 {
|
if r.width == 0 {
|
||||||
io.WriteString(buf, "\033[2K\r")
|
buf.WriteString(strings.Repeat("\r\b", len(r.buf)+r.PromptLen()))
|
||||||
|
buf.Write([]byte("\033[J"))
|
||||||
} else {
|
} else {
|
||||||
for i := 0; i < idxLine; i++ {
|
buf.Write([]byte("\033[J")) // just like ^k :)
|
||||||
io.WriteString(buf, "\033[2K\r\033[A")
|
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()
|
buf.Flush()
|
||||||
return
|
return
|
||||||
|
|
|
@ -96,7 +96,10 @@ func (o *opSearch) SearchChar(r rune) {
|
||||||
o.search(true)
|
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
|
alreadyInMode := o.inMode
|
||||||
o.inMode = true
|
o.inMode = true
|
||||||
o.dir = dir
|
o.dir = dir
|
||||||
|
@ -106,6 +109,7 @@ func (o *opSearch) SearchMode(dir int) {
|
||||||
} else {
|
} else {
|
||||||
o.SearchRefresh(-1)
|
o.SearchRefresh(-1)
|
||||||
}
|
}
|
||||||
|
return true
|
||||||
}
|
}
|
||||||
|
|
||||||
func (o *opSearch) ExitSearchMode(revert bool) {
|
func (o *opSearch) ExitSearchMode(revert bool) {
|
||||||
|
|
34
terminal.go
34
terminal.go
|
@ -17,6 +17,8 @@ type Terminal struct {
|
||||||
wg sync.WaitGroup
|
wg sync.WaitGroup
|
||||||
isReading int32
|
isReading int32
|
||||||
sleeping int32
|
sleeping int32
|
||||||
|
|
||||||
|
sizeChan chan string
|
||||||
}
|
}
|
||||||
|
|
||||||
func NewTerminal(cfg *Config) (*Terminal, error) {
|
func NewTerminal(cfg *Config) (*Terminal, error) {
|
||||||
|
@ -28,6 +30,7 @@ func NewTerminal(cfg *Config) (*Terminal, error) {
|
||||||
kickChan: make(chan struct{}, 1),
|
kickChan: make(chan struct{}, 1),
|
||||||
outchan: make(chan rune),
|
outchan: make(chan rune),
|
||||||
stopChan: make(chan struct{}, 1),
|
stopChan: make(chan struct{}, 1),
|
||||||
|
sizeChan: make(chan string, 1),
|
||||||
}
|
}
|
||||||
|
|
||||||
go t.ioloop()
|
go t.ioloop()
|
||||||
|
@ -60,6 +63,18 @@ func (t *Terminal) Write(b []byte) (int, error) {
|
||||||
return t.cfg.Stdout.Write(b)
|
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) {
|
func (t *Terminal) Print(s string) {
|
||||||
fmt.Fprintf(t.cfg.Stdout, "%s", s)
|
fmt.Fprintf(t.cfg.Stdout, "%s", s)
|
||||||
}
|
}
|
||||||
|
@ -132,7 +147,24 @@ func (t *Terminal) ioloop() {
|
||||||
r = escapeKey(r, buf)
|
r = escapeKey(r, buf)
|
||||||
} else if isEscapeEx {
|
} else if isEscapeEx {
|
||||||
isEscapeEx = false
|
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
|
expectNextChar = true
|
||||||
|
|
57
utils.go
57
utils.go
|
@ -4,8 +4,10 @@ import (
|
||||||
"bufio"
|
"bufio"
|
||||||
"bytes"
|
"bytes"
|
||||||
"strconv"
|
"strconv"
|
||||||
|
"strings"
|
||||||
"sync"
|
"sync"
|
||||||
"time"
|
"time"
|
||||||
|
"unicode"
|
||||||
)
|
)
|
||||||
|
|
||||||
var (
|
var (
|
||||||
|
@ -54,8 +56,9 @@ func IsPrintable(key rune) bool {
|
||||||
}
|
}
|
||||||
|
|
||||||
// translate Esc[X
|
// translate Esc[X
|
||||||
func escapeExKey(r rune, reader *bufio.Reader) rune {
|
func escapeExKey(key *escapeKeyPair) rune {
|
||||||
switch r {
|
var r rune
|
||||||
|
switch key.typ {
|
||||||
case 'D':
|
case 'D':
|
||||||
r = CharBackward
|
r = CharBackward
|
||||||
case 'C':
|
case 'C':
|
||||||
|
@ -68,19 +71,53 @@ func escapeExKey(r rune, reader *bufio.Reader) rune {
|
||||||
r = CharLineStart
|
r = CharLineStart
|
||||||
case 'F':
|
case 'F':
|
||||||
r = CharLineEnd
|
r = CharLineEnd
|
||||||
default:
|
case '~':
|
||||||
if r == '3' && reader != nil {
|
if key.attr == "3" {
|
||||||
d, _, _ := reader.ReadRune()
|
r = CharDelete
|
||||||
if d == '~' {
|
|
||||||
r = CharDelete
|
|
||||||
} else {
|
|
||||||
reader.UnreadRune()
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
default:
|
||||||
}
|
}
|
||||||
return r
|
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
|
// translate EscX to Meta+X
|
||||||
func escapeKey(r rune, reader *bufio.Reader) rune {
|
func escapeKey(r rune, reader *bufio.Reader) rune {
|
||||||
switch r {
|
switch r {
|
||||||
|
|
Loading…
Reference in New Issue