runes_editor.go 6.5 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309
  1. package hhc_common
  2. import (
  3. "fmt"
  4. "io"
  5. "strings"
  6. )
  7. type RunesEditor struct {
  8. currentLine []rune
  9. history [][]rune
  10. insertPosition int
  11. historyPosition int
  12. cleanLineOffset int
  13. cleanLineCount int
  14. lastTermWidth int
  15. lastTermPeekOut []string
  16. }
  17. func NewRunesEditor() *RunesEditor {
  18. re := &RunesEditor{}
  19. re.ClearAll()
  20. return re
  21. }
  22. func (re *RunesEditor) ClearHistory() {
  23. re.history = make([][]rune, 0)
  24. re.historyPosition = 0
  25. }
  26. func (re *RunesEditor) ClearCurrentLine(saveHistory bool) {
  27. if saveHistory {
  28. if len(re.currentLine) > 0 {
  29. l := len(re.history)
  30. if l > 0 {
  31. if !TestRuneSlice(re.history[l-1], re.currentLine) {
  32. re.history = append(re.history, re.currentLine)
  33. }
  34. } else {
  35. re.history = append(re.history, re.currentLine)
  36. }
  37. }
  38. }
  39. re.historyPosition = len(re.history)
  40. re.currentLine = []rune{}
  41. re.insertPosition = 0
  42. }
  43. func (re *RunesEditor) ClearAll() {
  44. re.ClearCurrentLine(false)
  45. re.ClearHistory()
  46. }
  47. func (re *RunesEditor) fixInsertPos() {
  48. bl := len(re.currentLine)
  49. if re.insertPosition > bl {
  50. re.insertPosition = bl
  51. }
  52. if re.insertPosition < 0 {
  53. re.insertPosition = 0
  54. }
  55. }
  56. func (re *RunesEditor) InsertString(s string) {
  57. rs := []rune(s)
  58. for _, v := range rs {
  59. re.InsertRune(v)
  60. }
  61. }
  62. func (re *RunesEditor) InsertRune(r rune) {
  63. re.fixInsertPos()
  64. p := re.insertPosition
  65. switch {
  66. case p == 0:
  67. nrs := make([]rune, 1, len(re.currentLine)+1)
  68. nrs[0] = r
  69. re.currentLine = append(nrs, re.currentLine...)
  70. break
  71. case p == len(re.currentLine):
  72. re.currentLine = append(re.currentLine, r)
  73. break
  74. default:
  75. left := re.currentLine[:p]
  76. right := re.currentLine[p:]
  77. nrs := make([]rune, 0, len(re.currentLine)+1)
  78. nrs = append(nrs, left...)
  79. nrs = append(nrs, r)
  80. re.currentLine = append(nrs, right...)
  81. break
  82. }
  83. re.insertPosition++
  84. re.fixInsertPos()
  85. }
  86. func (re *RunesEditor) MoveInsertPosition(offset int) {
  87. re.insertPosition += offset
  88. re.fixInsertPos()
  89. }
  90. func (re *RunesEditor) SetInsertPosition(pos int) {
  91. re.insertPosition = pos
  92. re.fixInsertPos()
  93. }
  94. func (re *RunesEditor) Backspace() {
  95. re.fixInsertPos()
  96. l := len(re.currentLine)
  97. switch {
  98. case re.insertPosition <= 0:
  99. re.insertPosition = 0
  100. return
  101. case l == 0:
  102. return
  103. case re.insertPosition == l:
  104. re.currentLine = re.currentLine[:l-1]
  105. break
  106. default:
  107. p := re.insertPosition
  108. left := re.currentLine[:p-1]
  109. right := re.currentLine[p:]
  110. nrs := make([]rune, 0, len(re.currentLine)-1)
  111. nrs = append(nrs, left...)
  112. re.currentLine = append(nrs, right...)
  113. break
  114. }
  115. re.insertPosition--
  116. re.fixInsertPos()
  117. }
  118. func (re *RunesEditor) Delete() {
  119. re.fixInsertPos()
  120. l := len(re.currentLine)
  121. switch {
  122. case re.insertPosition == l:
  123. return
  124. default:
  125. p := re.insertPosition
  126. left := re.currentLine[:p]
  127. right := re.currentLine[p+1:]
  128. nrs := make([]rune, 0, len(re.currentLine)-1)
  129. nrs = append(nrs, left...)
  130. re.currentLine = append(nrs, right...)
  131. break
  132. }
  133. re.fixInsertPos()
  134. }
  135. func (re *RunesEditor) Enter() []rune {
  136. ret := re.currentLine
  137. re.ClearCurrentLine(true)
  138. return ret
  139. }
  140. func (re *RunesEditor) Peek() []rune {
  141. return re.currentLine
  142. }
  143. func (re *RunesEditor) fixHistoryPos() {
  144. l := len(re.history)
  145. if re.historyPosition < 0 {
  146. re.historyPosition = 0
  147. }
  148. if re.historyPosition > l {
  149. re.historyPosition = l
  150. }
  151. }
  152. func (re *RunesEditor) LoadNextHistory() {
  153. l := len(re.history)
  154. if re.historyPosition == l {
  155. return
  156. }
  157. re.historyPosition++
  158. re.fixHistoryPos()
  159. if re.historyPosition == l {
  160. re.ClearCurrentLine(false)
  161. return
  162. }
  163. re.currentLine = re.history[re.historyPosition]
  164. re.MoveInsertPositionToEnd()
  165. }
  166. func (re *RunesEditor) LoadPreviousHistory() {
  167. l := len(re.history)
  168. if l == 0 {
  169. return
  170. }
  171. re.historyPosition--
  172. re.fixHistoryPos()
  173. if re.historyPosition == l {
  174. re.ClearCurrentLine(false)
  175. return
  176. }
  177. re.currentLine = re.history[re.historyPosition]
  178. re.MoveInsertPositionToEnd()
  179. }
  180. func (re *RunesEditor) MoveInsertPositionToEnd() {
  181. re.insertPosition = len(re.currentLine)
  182. }
  183. func (re *RunesEditor) MoveInsertPositionToHome() {
  184. re.insertPosition = 0
  185. }
  186. func (re *RunesEditor) GetInsertPosition() int {
  187. return re.insertPosition
  188. }
  189. func (re *RunesEditor) CleanTerminalPeek(wr io.Writer) {
  190. switch {
  191. case re.cleanLineOffset > 0:
  192. _, _ = fmt.Fprintf(wr, "\033[%dA", re.cleanLineOffset)
  193. break
  194. case re.cleanLineOffset < 0:
  195. _, _ = fmt.Fprintf(wr, "\033[%dB", -re.cleanLineOffset)
  196. break
  197. }
  198. for i := 0; i < re.cleanLineCount; i++ {
  199. _, _ = fmt.Fprint(wr, "\r\033[K")
  200. if i != re.cleanLineCount-1 {
  201. _, _ = fmt.Fprint(wr, "\033[1A")
  202. }
  203. }
  204. }
  205. func (re *RunesEditor) backTerminalPeekCursorToHome(wr io.Writer) {
  206. switch {
  207. case re.cleanLineOffset > 0:
  208. _, _ = fmt.Fprintf(wr, "\033[%dA", re.cleanLineOffset)
  209. break
  210. case re.cleanLineOffset < 0:
  211. _, _ = fmt.Fprintf(wr, "\033[%dB", -re.cleanLineOffset)
  212. break
  213. }
  214. for i := 0; i < re.cleanLineCount; i++ {
  215. if i != re.cleanLineCount-1 {
  216. _, _ = fmt.Fprint(wr, "\033[1A")
  217. } else {
  218. _, _ = fmt.Fprint(wr, "\r")
  219. }
  220. }
  221. }
  222. func (re *RunesEditor) RefreshTerminal(wr io.Writer, prefix string, termWidth int, diffRefresh bool) {
  223. if diffRefresh {
  224. if termWidth != re.lastTermWidth {
  225. re.hardRefreshTerminal(wr, prefix, termWidth)
  226. return
  227. }
  228. // TODO: Support for diffRefresh
  229. // Now temporary using hardRefresh instead.
  230. re.hardRefreshTerminal(wr, prefix, termWidth)
  231. return
  232. } else {
  233. re.hardRefreshTerminal(wr, prefix, termWidth)
  234. }
  235. }
  236. func (re *RunesEditor) hardRefreshTerminal(wr io.Writer, prefix string, termWidth int) {
  237. re.CleanTerminalPeek(wr)
  238. re.PeekIntoTerminal(prefix, termWidth, wr)
  239. }
  240. func (re *RunesEditor) PeekIntoTerminal(prefix string, termWidth int, wr io.Writer) {
  241. re.lastTermWidth = termWidth
  242. re.fixInsertPos()
  243. rc := re.Peek()
  244. sc := string(rc)
  245. psb := strings.Builder{}
  246. psb.WriteString(prefix)
  247. psb.WriteString(sc)
  248. ws := psb.String()
  249. lpre := len([]rune(prefix))
  250. curpos := lpre + re.insertPosition
  251. lines, cy, cx := PrintRunesIntoLinesWithCursor([]rune(ws), termWidth, curpos)
  252. re.lastTermPeekOut = lines
  253. re.cleanLineCount = len(lines)
  254. lastln := re.cleanLineCount - 1
  255. lastline := lines[lastln]
  256. lll := len(lastline)
  257. for i, line := range lines {
  258. _, _ = fmt.Fprint(wr, line)
  259. if i != lastln {
  260. _, _ = fmt.Fprint(wr, "\r\n")
  261. }
  262. }
  263. bky := lastln - cy
  264. bkx := lll - cx + 1
  265. re.cleanLineOffset = -bky
  266. //fmt.Println("BKY=", bky, "BKX=", bkx)
  267. switch {
  268. case bky > 0:
  269. _, _ = fmt.Fprintf(wr, "\033[%dA", bky)
  270. break
  271. case bky < 0:
  272. _, _ = fmt.Fprintf(wr, "\033[%dB", -bky)
  273. break
  274. }
  275. switch {
  276. case bkx > 0:
  277. _, _ = fmt.Fprintf(wr, "\033[%dD", bkx)
  278. break
  279. case bkx < 0:
  280. _, _ = fmt.Fprintf(wr, "\033[%dC", -bkx)
  281. break
  282. }
  283. }