readline/utils.go

178 lines
2.9 KiB
Go
Raw Normal View History

2015-09-20 18:14:29 +03:00
package readline
import (
"bufio"
2016-03-05 10:27:12 +03:00
"bytes"
"strconv"
"sync"
"time"
2015-09-20 18:14:29 +03:00
"golang.org/x/crypto/ssh/terminal"
)
2015-09-28 04:46:33 +03:00
var (
isWindows = false
2015-09-28 04:46:33 +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
}
2015-09-20 18:14:29 +03:00
// IsTerminal returns true if the given file descriptor is a terminal.
func IsTerminal(fd int) bool {
return terminal.IsTerminal(fd)
}
func MakeRaw(fd int) (*terminal.State, error) {
return terminal.MakeRaw(fd)
}
func Restore(fd int, state *terminal.State) error {
2015-09-24 19:16:49 +03:00
err := terminal.Restore(fd, state)
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
func escapeExKey(r rune, reader *bufio.Reader) rune {
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()
}
}
}
return r
}
2015-09-27 05:50:14 +03:00
// translate EscX to Meta+X
func escapeKey(r rune) 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
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
}
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 {
state *terminal.State
}
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
}