You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.

277 lines
4.6 KiB

  1. package readline
  2. import (
  3. "bufio"
  4. "bytes"
  5. "container/list"
  6. "fmt"
  7. "os"
  8. "strconv"
  9. "strings"
  10. "sync"
  11. "time"
  12. "unicode"
  13. )
  14. var (
  15. isWindows = false
  16. )
  17. const (
  18. CharLineStart = 1
  19. CharBackward = 2
  20. CharInterrupt = 3
  21. CharDelete = 4
  22. CharLineEnd = 5
  23. CharForward = 6
  24. CharBell = 7
  25. CharCtrlH = 8
  26. CharTab = 9
  27. CharCtrlJ = 10
  28. CharKill = 11
  29. CharCtrlL = 12
  30. CharEnter = 13
  31. CharNext = 14
  32. CharPrev = 16
  33. CharBckSearch = 18
  34. CharFwdSearch = 19
  35. CharTranspose = 20
  36. CharCtrlU = 21
  37. CharCtrlW = 23
  38. CharCtrlY = 25
  39. CharCtrlZ = 26
  40. CharEsc = 27
  41. CharEscapeEx = 91
  42. CharBackspace = 127
  43. )
  44. const (
  45. MetaBackward rune = -iota - 1
  46. MetaForward
  47. MetaDelete
  48. MetaBackspace
  49. MetaTranspose
  50. )
  51. // WaitForResume need to call before current process got suspend.
  52. // It will run a ticker until a long duration is occurs,
  53. // which means this process is resumed.
  54. func WaitForResume() chan struct{} {
  55. ch := make(chan struct{})
  56. var wg sync.WaitGroup
  57. wg.Add(1)
  58. go func() {
  59. ticker := time.NewTicker(10 * time.Millisecond)
  60. t := time.Now()
  61. wg.Done()
  62. for {
  63. now := <-ticker.C
  64. if now.Sub(t) > 100*time.Millisecond {
  65. break
  66. }
  67. t = now
  68. }
  69. ticker.Stop()
  70. ch <- struct{}{}
  71. }()
  72. wg.Wait()
  73. return ch
  74. }
  75. func Restore(fd int, state *State) error {
  76. err := restoreTerm(fd, state)
  77. if err != nil {
  78. // errno 0 means everything is ok :)
  79. if err.Error() == "errno 0" {
  80. return nil
  81. } else {
  82. return err
  83. }
  84. }
  85. return nil
  86. }
  87. func IsPrintable(key rune) bool {
  88. isInSurrogateArea := key >= 0xd800 && key <= 0xdbff
  89. return key >= 32 && !isInSurrogateArea
  90. }
  91. // translate Esc[X
  92. func escapeExKey(key *escapeKeyPair) rune {
  93. var r rune
  94. switch key.typ {
  95. case 'D':
  96. r = CharBackward
  97. case 'C':
  98. r = CharForward
  99. case 'A':
  100. r = CharPrev
  101. case 'B':
  102. r = CharNext
  103. case 'H':
  104. r = CharLineStart
  105. case 'F':
  106. r = CharLineEnd
  107. case '~':
  108. if key.attr == "3" {
  109. r = CharDelete
  110. }
  111. default:
  112. }
  113. return r
  114. }
  115. type escapeKeyPair struct {
  116. attr string
  117. typ rune
  118. }
  119. func (e *escapeKeyPair) Get2() (int, int, bool) {
  120. sp := strings.Split(e.attr, ";")
  121. if len(sp) < 2 {
  122. return -1, -1, false
  123. }
  124. s1, err := strconv.Atoi(sp[0])
  125. if err != nil {
  126. return -1, -1, false
  127. }
  128. s2, err := strconv.Atoi(sp[1])
  129. if err != nil {
  130. return -1, -1, false
  131. }
  132. return s1, s2, true
  133. }
  134. func readEscKey(r rune, reader *bufio.Reader) *escapeKeyPair {
  135. p := escapeKeyPair{}
  136. buf := bytes.NewBuffer(nil)
  137. for {
  138. if r == ';' {
  139. } else if unicode.IsNumber(r) {
  140. } else {
  141. p.typ = r
  142. break
  143. }
  144. buf.WriteRune(r)
  145. r, _, _ = reader.ReadRune()
  146. }
  147. p.attr = buf.String()
  148. return &p
  149. }
  150. // translate EscX to Meta+X
  151. func escapeKey(r rune, reader *bufio.Reader) rune {
  152. switch r {
  153. case 'b':
  154. r = MetaBackward
  155. case 'f':
  156. r = MetaForward
  157. case 'd':
  158. r = MetaDelete
  159. case CharTranspose:
  160. r = MetaTranspose
  161. case CharBackspace:
  162. r = MetaBackspace
  163. case 'O':
  164. d, _, _ := reader.ReadRune()
  165. switch d {
  166. case 'H':
  167. r = CharLineStart
  168. case 'F':
  169. r = CharLineEnd
  170. default:
  171. reader.UnreadRune()
  172. }
  173. case CharEsc:
  174. }
  175. return r
  176. }
  177. func SplitByLine(start, screenWidth int, rs []rune) []string {
  178. var ret []string
  179. buf := bytes.NewBuffer(nil)
  180. currentWidth := start
  181. for _, r := range rs {
  182. w := runes.Width(r)
  183. currentWidth += w
  184. buf.WriteRune(r)
  185. if currentWidth >= screenWidth {
  186. ret = append(ret, buf.String())
  187. buf.Reset()
  188. currentWidth = 0
  189. }
  190. }
  191. ret = append(ret, buf.String())
  192. return ret
  193. }
  194. // calculate how many lines for N character
  195. func LineCount(screenWidth, w int) int {
  196. r := w / screenWidth
  197. if w%screenWidth != 0 {
  198. r++
  199. }
  200. return r
  201. }
  202. func IsWordBreak(i rune) bool {
  203. switch {
  204. case i >= 'a' && i <= 'z':
  205. case i >= 'A' && i <= 'Z':
  206. case i >= '0' && i <= '9':
  207. default:
  208. return true
  209. }
  210. return false
  211. }
  212. func GetInt(s []string, def int) int {
  213. if len(s) == 0 {
  214. return def
  215. }
  216. c, err := strconv.Atoi(s[0])
  217. if err != nil {
  218. return def
  219. }
  220. return c
  221. }
  222. type RawMode struct {
  223. state *State
  224. }
  225. func (r *RawMode) Enter() (err error) {
  226. r.state, err = MakeRaw(GetStdin())
  227. return err
  228. }
  229. func (r *RawMode) Exit() error {
  230. if r.state == nil {
  231. return nil
  232. }
  233. return Restore(GetStdin(), r.state)
  234. }
  235. // -----------------------------------------------------------------------------
  236. func sleep(n int) {
  237. Debug(n)
  238. time.Sleep(2000 * time.Millisecond)
  239. }
  240. // print a linked list to Debug()
  241. func debugList(l *list.List) {
  242. idx := 0
  243. for e := l.Front(); e != nil; e = e.Next() {
  244. Debug(idx, fmt.Sprintf("%+v", e.Value))
  245. idx++
  246. }
  247. }
  248. // append log info to another file
  249. func Debug(o ...interface{}) {
  250. f, _ := os.OpenFile("debug.tmp", os.O_RDWR|os.O_CREATE|os.O_APPEND, 0666)
  251. fmt.Fprintln(f, o...)
  252. f.Close()
  253. }