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.

164 lines
3.1 KiB

  1. package readline
  2. import (
  3. "bytes"
  4. "container/list"
  5. "fmt"
  6. "io"
  7. )
  8. const (
  9. S_STATE_FOUND = iota
  10. S_STATE_FAILING
  11. )
  12. const (
  13. S_DIR_BCK = iota
  14. S_DIR_FWD
  15. )
  16. type opSearch struct {
  17. inMode bool
  18. state int
  19. dir int
  20. source *list.Element
  21. w io.Writer
  22. buf *RuneBuffer
  23. data []rune
  24. history *opHistory
  25. cfg *Config
  26. markStart int
  27. markEnd int
  28. width int
  29. }
  30. func newOpSearch(w io.Writer, buf *RuneBuffer, history *opHistory, cfg *Config, width int) *opSearch {
  31. return &opSearch{
  32. w: w,
  33. buf: buf,
  34. cfg: cfg,
  35. history: history,
  36. width: width,
  37. }
  38. }
  39. func (o *opSearch) OnWidthChange(newWidth int) {
  40. o.width = newWidth
  41. }
  42. func (o *opSearch) IsSearchMode() bool {
  43. return o.inMode
  44. }
  45. func (o *opSearch) SearchBackspace() {
  46. if len(o.data) > 0 {
  47. o.data = o.data[:len(o.data)-1]
  48. o.search(true)
  49. }
  50. }
  51. func (o *opSearch) findHistoryBy(isNewSearch bool) (int, *list.Element) {
  52. if o.dir == S_DIR_BCK {
  53. return o.history.FindBck(isNewSearch, o.data, o.buf.idx)
  54. }
  55. return o.history.FindFwd(isNewSearch, o.data, o.buf.idx)
  56. }
  57. func (o *opSearch) search(isChange bool) bool {
  58. if len(o.data) == 0 {
  59. o.state = S_STATE_FOUND
  60. o.SearchRefresh(-1)
  61. return true
  62. }
  63. idx, elem := o.findHistoryBy(isChange)
  64. if elem == nil {
  65. o.SearchRefresh(-2)
  66. return false
  67. }
  68. o.history.current = elem
  69. item := o.history.showItem(o.history.current.Value)
  70. start, end := 0, 0
  71. if o.dir == S_DIR_BCK {
  72. start, end = idx, idx+len(o.data)
  73. } else {
  74. start, end = idx, idx+len(o.data)
  75. idx += len(o.data)
  76. }
  77. o.buf.SetWithIdx(idx, item)
  78. o.markStart, o.markEnd = start, end
  79. o.SearchRefresh(idx)
  80. return true
  81. }
  82. func (o *opSearch) SearchChar(r rune) {
  83. o.data = append(o.data, r)
  84. o.search(true)
  85. }
  86. func (o *opSearch) SearchMode(dir int) bool {
  87. if o.width == 0 {
  88. return false
  89. }
  90. alreadyInMode := o.inMode
  91. o.inMode = true
  92. o.dir = dir
  93. o.source = o.history.current
  94. if alreadyInMode {
  95. o.search(false)
  96. } else {
  97. o.SearchRefresh(-1)
  98. }
  99. return true
  100. }
  101. func (o *opSearch) ExitSearchMode(revert bool) {
  102. if revert {
  103. o.history.current = o.source
  104. o.buf.Set(o.history.showItem(o.history.current.Value))
  105. }
  106. o.markStart, o.markEnd = 0, 0
  107. o.state = S_STATE_FOUND
  108. o.inMode = false
  109. o.source = nil
  110. o.data = nil
  111. }
  112. func (o *opSearch) SearchRefresh(x int) {
  113. if x == -2 {
  114. o.state = S_STATE_FAILING
  115. } else if x >= 0 {
  116. o.state = S_STATE_FOUND
  117. }
  118. if x < 0 {
  119. x = o.buf.idx
  120. }
  121. x = o.buf.CurrentWidth(x)
  122. x += o.buf.PromptLen()
  123. x = x % o.width
  124. if o.markStart > 0 {
  125. o.buf.SetStyle(o.markStart, o.markEnd, "4")
  126. }
  127. lineCnt := o.buf.CursorLineCount()
  128. buf := bytes.NewBuffer(nil)
  129. buf.Write(bytes.Repeat([]byte("\n"), lineCnt))
  130. buf.WriteString("\033[J")
  131. if o.state == S_STATE_FAILING {
  132. buf.WriteString("failing ")
  133. }
  134. if o.dir == S_DIR_BCK {
  135. buf.WriteString("bck")
  136. } else if o.dir == S_DIR_FWD {
  137. buf.WriteString("fwd")
  138. }
  139. buf.WriteString("-i-search: ")
  140. buf.WriteString(string(o.data)) // keyword
  141. buf.WriteString("\033[4m \033[0m") // _
  142. fmt.Fprintf(buf, "\r\033[%dA", lineCnt) // move prev
  143. if x > 0 {
  144. fmt.Fprintf(buf, "\033[%dC", x) // move forward
  145. }
  146. o.w.Write(buf.Bytes())
  147. }