2015-09-20 18:14:29 +03:00
|
|
|
package readline
|
|
|
|
|
|
|
|
import (
|
2015-10-26 11:41:48 +03:00
|
|
|
"bufio"
|
2016-03-05 10:27:12 +03:00
|
|
|
"bytes"
|
2015-09-29 16:01:17 +03:00
|
|
|
"strconv"
|
2016-04-17 16:05:00 +03:00
|
|
|
"sync"
|
|
|
|
"time"
|
2015-09-20 18:14:29 +03:00
|
|
|
)
|
|
|
|
|
2015-09-28 04:46:33 +03:00
|
|
|
var (
|
2015-09-29 16:01:17 +03:00
|
|
|
isWindows = false
|
2015-09-28 04:46:33 +03:00
|
|
|
)
|
|
|
|
|
2016-04-17 16:05:00 +03:00
|
|
|
// WaitForResume need to call before current process got suspend.
|
|
|
|
// It will run a ticker until a long duration is occurs,
|
|
|
|
// which means this process is resumed.
|
|
|
|
func WaitForResume() chan struct{} {
|
|
|
|
ch := make(chan struct{})
|
|
|
|
var wg sync.WaitGroup
|
|
|
|
wg.Add(1)
|
|
|
|
go func() {
|
|
|
|
ticker := time.NewTicker(10 * time.Millisecond)
|
|
|
|
t := time.Now()
|
|
|
|
wg.Done()
|
|
|
|
for {
|
|
|
|
now := <-ticker.C
|
|
|
|
if now.Sub(t) > 100*time.Millisecond {
|
|
|
|
break
|
|
|
|
}
|
|
|
|
t = now
|
|
|
|
}
|
|
|
|
ticker.Stop()
|
|
|
|
ch <- struct{}{}
|
|
|
|
}()
|
|
|
|
wg.Wait()
|
|
|
|
return ch
|
|
|
|
}
|
|
|
|
|
2016-07-21 19:31:42 +03:00
|
|
|
func Restore(fd int, state *State) error {
|
|
|
|
err := restoreTerm(fd, state)
|
2015-09-24 19:16:49 +03:00
|
|
|
if err != nil {
|
2015-09-27 05:50:14 +03:00
|
|
|
// errno 0 means everything is ok :)
|
2015-09-24 19:16:49 +03:00
|
|
|
if err.Error() == "errno 0" {
|
|
|
|
err = nil
|
|
|
|
}
|
|
|
|
}
|
|
|
|
return nil
|
2015-09-20 18:14:29 +03:00
|
|
|
}
|
|
|
|
|
|
|
|
func IsPrintable(key rune) bool {
|
|
|
|
isInSurrogateArea := key >= 0xd800 && key <= 0xdbff
|
|
|
|
return key >= 32 && !isInSurrogateArea
|
|
|
|
}
|
|
|
|
|
2015-09-27 05:50:14 +03:00
|
|
|
// translate Esc[X
|
2015-10-26 11:41:48 +03:00
|
|
|
func escapeExKey(r rune, reader *bufio.Reader) rune {
|
2015-09-21 16:14:05 +03:00
|
|
|
switch r {
|
|
|
|
case 'D':
|
|
|
|
r = CharBackward
|
|
|
|
case 'C':
|
|
|
|
r = CharForward
|
|
|
|
case 'A':
|
|
|
|
r = CharPrev
|
|
|
|
case 'B':
|
|
|
|
r = CharNext
|
2015-10-23 14:16:45 +03:00
|
|
|
case 'H':
|
|
|
|
r = CharLineStart
|
|
|
|
case 'F':
|
|
|
|
r = CharLineEnd
|
2015-10-28 04:02:52 +03:00
|
|
|
default:
|
|
|
|
if r == '3' && reader != nil {
|
|
|
|
d, _, _ := reader.ReadRune()
|
|
|
|
if d == '~' {
|
|
|
|
r = CharDelete
|
|
|
|
} else {
|
|
|
|
reader.UnreadRune()
|
|
|
|
}
|
|
|
|
}
|
2015-09-21 16:14:05 +03:00
|
|
|
}
|
|
|
|
return r
|
|
|
|
}
|
|
|
|
|
2015-09-27 05:50:14 +03:00
|
|
|
// translate EscX to Meta+X
|
2016-06-21 12:53:12 +03:00
|
|
|
func escapeKey(r rune, reader *bufio.Reader) rune {
|
2015-09-20 18:14:29 +03:00
|
|
|
switch r {
|
|
|
|
case 'b':
|
2015-10-01 17:44:43 +03:00
|
|
|
r = MetaBackward
|
2015-09-20 18:14:29 +03:00
|
|
|
case 'f':
|
2015-10-01 17:44:43 +03:00
|
|
|
r = MetaForward
|
2015-09-20 18:14:29 +03:00
|
|
|
case 'd':
|
|
|
|
r = MetaDelete
|
2015-09-23 08:03:13 +03:00
|
|
|
case CharTranspose:
|
|
|
|
r = MetaTranspose
|
2015-09-21 16:00:48 +03:00
|
|
|
case CharBackspace:
|
|
|
|
r = MetaBackspace
|
2016-06-21 12:53:12 +03:00
|
|
|
case 'O':
|
|
|
|
d, _, _ := reader.ReadRune()
|
|
|
|
switch d {
|
|
|
|
case 'H':
|
|
|
|
r = CharLineStart
|
|
|
|
case 'F':
|
|
|
|
r = CharLineEnd
|
|
|
|
default:
|
|
|
|
reader.UnreadRune()
|
|
|
|
}
|
2015-09-23 06:46:56 +03:00
|
|
|
case CharEsc:
|
2015-09-21 08:13:30 +03:00
|
|
|
|
2015-09-20 18:14:29 +03:00
|
|
|
}
|
|
|
|
return r
|
|
|
|
}
|
|
|
|
|
2016-03-05 10:27:12 +03:00
|
|
|
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
|
|
|
|
}
|
|
|
|
|
2015-09-27 05:50:14 +03:00
|
|
|
// calculate how many lines for N character
|
2016-03-05 10:27:12 +03:00
|
|
|
func LineCount(screenWidth, w int) int {
|
2015-09-22 18:01:15 +03:00
|
|
|
r := w / screenWidth
|
|
|
|
if w%screenWidth != 0 {
|
|
|
|
r++
|
|
|
|
}
|
|
|
|
return r
|
|
|
|
}
|
|
|
|
|
2015-09-23 06:59:39 +03:00
|
|
|
func IsWordBreak(i rune) bool {
|
2016-03-25 12:07:55 +03:00
|
|
|
switch {
|
|
|
|
case i >= 'a' && i <= 'z':
|
|
|
|
case i >= 'A' && i <= 'Z':
|
|
|
|
case i >= '0' && i <= '9':
|
|
|
|
default:
|
|
|
|
return true
|
2015-09-23 06:59:39 +03:00
|
|
|
}
|
2016-03-25 12:07:55 +03:00
|
|
|
return false
|
2015-09-23 06:59:39 +03:00
|
|
|
}
|
2015-09-25 07:45:39 +03:00
|
|
|
|
2015-09-29 16:01:17 +03:00
|
|
|
func GetInt(s []string, def int) int {
|
|
|
|
if len(s) == 0 {
|
|
|
|
return def
|
|
|
|
}
|
|
|
|
c, err := strconv.Atoi(s[0])
|
|
|
|
if err != nil {
|
|
|
|
return def
|
|
|
|
}
|
|
|
|
return c
|
|
|
|
}
|
2016-03-05 05:46:11 +03:00
|
|
|
|
2016-03-13 13:32:48 +03:00
|
|
|
type RawMode struct {
|
2016-07-21 19:31:42 +03:00
|
|
|
state *State
|
2016-03-13 13:32:48 +03:00
|
|
|
}
|
|
|
|
|
|
|
|
func (r *RawMode) Enter() (err error) {
|
|
|
|
r.state, err = MakeRaw(GetStdin())
|
|
|
|
return err
|
|
|
|
}
|
|
|
|
|
|
|
|
func (r *RawMode) Exit() error {
|
|
|
|
if r.state == nil {
|
|
|
|
return nil
|
2016-03-05 05:46:11 +03:00
|
|
|
}
|
2016-03-13 13:32:48 +03:00
|
|
|
return Restore(GetStdin(), r.state)
|
2016-03-05 05:46:11 +03:00
|
|
|
}
|