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.

330 lines
6.0 KiB

  1. package readline
  2. import (
  3. "bufio"
  4. "container/list"
  5. "fmt"
  6. "os"
  7. "strings"
  8. "sync"
  9. )
  10. type hisItem struct {
  11. Source []rune
  12. Version int64
  13. Tmp []rune
  14. }
  15. func (h *hisItem) Clean() {
  16. h.Source = nil
  17. h.Tmp = nil
  18. }
  19. type opHistory struct {
  20. cfg *Config
  21. history *list.List
  22. historyVer int64
  23. current *list.Element
  24. fd *os.File
  25. fdLock sync.Mutex
  26. enable bool
  27. }
  28. func newOpHistory(cfg *Config) (o *opHistory) {
  29. o = &opHistory{
  30. cfg: cfg,
  31. history: list.New(),
  32. enable: true,
  33. }
  34. return o
  35. }
  36. func (o *opHistory) Reset() {
  37. o.history = list.New()
  38. o.current = nil
  39. }
  40. func (o *opHistory) IsHistoryClosed() bool {
  41. o.fdLock.Lock()
  42. defer o.fdLock.Unlock()
  43. return o.fd.Fd() == ^(uintptr(0))
  44. }
  45. func (o *opHistory) Init() {
  46. if o.IsHistoryClosed() {
  47. o.initHistory()
  48. }
  49. }
  50. func (o *opHistory) initHistory() {
  51. if o.cfg.HistoryFile != "" {
  52. o.historyUpdatePath(o.cfg.HistoryFile)
  53. }
  54. }
  55. // only called by newOpHistory
  56. func (o *opHistory) historyUpdatePath(path string) {
  57. o.fdLock.Lock()
  58. defer o.fdLock.Unlock()
  59. f, err := os.OpenFile(path, os.O_APPEND|os.O_CREATE|os.O_RDWR, 0666)
  60. if err != nil {
  61. return
  62. }
  63. o.fd = f
  64. r := bufio.NewReader(o.fd)
  65. total := 0
  66. for ; ; total++ {
  67. line, err := r.ReadString('\n')
  68. if err != nil {
  69. break
  70. }
  71. // ignore the empty line
  72. line = strings.TrimSpace(line)
  73. if len(line) == 0 {
  74. continue
  75. }
  76. o.Push([]rune(line))
  77. o.Compact()
  78. }
  79. if total > o.cfg.HistoryLimit {
  80. o.rewriteLocked()
  81. }
  82. o.historyVer++
  83. o.Push(nil)
  84. return
  85. }
  86. func (o *opHistory) Compact() {
  87. for o.history.Len() > o.cfg.HistoryLimit && o.history.Len() > 0 {
  88. o.history.Remove(o.history.Front())
  89. }
  90. }
  91. func (o *opHistory) Rewrite() {
  92. o.fdLock.Lock()
  93. defer o.fdLock.Unlock()
  94. o.rewriteLocked()
  95. }
  96. func (o *opHistory) rewriteLocked() {
  97. if o.cfg.HistoryFile == "" {
  98. return
  99. }
  100. tmpFile := o.cfg.HistoryFile + ".tmp"
  101. fd, err := os.OpenFile(tmpFile, os.O_CREATE|os.O_WRONLY|os.O_TRUNC|os.O_APPEND, 0666)
  102. if err != nil {
  103. return
  104. }
  105. buf := bufio.NewWriter(fd)
  106. for elem := o.history.Front(); elem != nil; elem = elem.Next() {
  107. buf.WriteString(string(elem.Value.(*hisItem).Source) + "\n")
  108. }
  109. buf.Flush()
  110. // replace history file
  111. if err = os.Rename(tmpFile, o.cfg.HistoryFile); err != nil {
  112. fd.Close()
  113. return
  114. }
  115. if o.fd != nil {
  116. o.fd.Close()
  117. }
  118. // fd is write only, just satisfy what we need.
  119. o.fd = fd
  120. }
  121. func (o *opHistory) Close() {
  122. o.fdLock.Lock()
  123. defer o.fdLock.Unlock()
  124. if o.fd != nil {
  125. o.fd.Close()
  126. }
  127. }
  128. func (o *opHistory) FindBck(isNewSearch bool, rs []rune, start int) (int, *list.Element) {
  129. for elem := o.current; elem != nil; elem = elem.Prev() {
  130. item := o.showItem(elem.Value)
  131. if isNewSearch {
  132. start += len(rs)
  133. }
  134. if elem == o.current {
  135. if len(item) >= start {
  136. item = item[:start]
  137. }
  138. }
  139. idx := runes.IndexAllBckEx(item, rs, o.cfg.HistorySearchFold)
  140. if idx < 0 {
  141. continue
  142. }
  143. return idx, elem
  144. }
  145. return -1, nil
  146. }
  147. func (o *opHistory) FindFwd(isNewSearch bool, rs []rune, start int) (int, *list.Element) {
  148. for elem := o.current; elem != nil; elem = elem.Next() {
  149. item := o.showItem(elem.Value)
  150. if isNewSearch {
  151. start -= len(rs)
  152. if start < 0 {
  153. start = 0
  154. }
  155. }
  156. if elem == o.current {
  157. if len(item)-1 >= start {
  158. item = item[start:]
  159. } else {
  160. continue
  161. }
  162. }
  163. idx := runes.IndexAllEx(item, rs, o.cfg.HistorySearchFold)
  164. if idx < 0 {
  165. continue
  166. }
  167. if elem == o.current {
  168. idx += start
  169. }
  170. return idx, elem
  171. }
  172. return -1, nil
  173. }
  174. func (o *opHistory) showItem(obj interface{}) []rune {
  175. item := obj.(*hisItem)
  176. if item.Version == o.historyVer {
  177. return item.Tmp
  178. }
  179. return item.Source
  180. }
  181. func (o *opHistory) Prev() []rune {
  182. if o.current == nil {
  183. return nil
  184. }
  185. current := o.current.Prev()
  186. if current == nil {
  187. return nil
  188. }
  189. o.current = current
  190. return runes.Copy(o.showItem(current.Value))
  191. }
  192. func (o *opHistory) Next() ([]rune, bool) {
  193. if o.current == nil {
  194. return nil, false
  195. }
  196. current := o.current.Next()
  197. if current == nil {
  198. return nil, false
  199. }
  200. o.current = current
  201. return runes.Copy(o.showItem(current.Value)), true
  202. }
  203. // Disable the current history
  204. func (o *opHistory) Disable() {
  205. o.enable = false
  206. }
  207. // Enable the current history
  208. func (o *opHistory) Enable() {
  209. o.enable = true
  210. }
  211. func (o *opHistory) debug() {
  212. Debug("-------")
  213. for item := o.history.Front(); item != nil; item = item.Next() {
  214. Debug(fmt.Sprintf("%+v", item.Value))
  215. }
  216. }
  217. // save history
  218. func (o *opHistory) New(current []rune) (err error) {
  219. // history deactivated
  220. if !o.enable {
  221. return nil
  222. }
  223. current = runes.Copy(current)
  224. // if just use last command without modify
  225. // just clean lastest history
  226. if back := o.history.Back(); back != nil {
  227. prev := back.Prev()
  228. if prev != nil {
  229. if runes.Equal(current, prev.Value.(*hisItem).Source) {
  230. o.current = o.history.Back()
  231. o.current.Value.(*hisItem).Clean()
  232. o.historyVer++
  233. return nil
  234. }
  235. }
  236. }
  237. if len(current) == 0 {
  238. o.current = o.history.Back()
  239. if o.current != nil {
  240. o.current.Value.(*hisItem).Clean()
  241. o.historyVer++
  242. return nil
  243. }
  244. }
  245. if o.current != o.history.Back() {
  246. // move history item to current command
  247. currentItem := o.current.Value.(*hisItem)
  248. // set current to last item
  249. o.current = o.history.Back()
  250. current = runes.Copy(currentItem.Tmp)
  251. }
  252. // err only can be a IO error, just report
  253. err = o.Update(current, true)
  254. // push a new one to commit current command
  255. o.historyVer++
  256. o.Push(nil)
  257. return
  258. }
  259. func (o *opHistory) Revert() {
  260. o.historyVer++
  261. o.current = o.history.Back()
  262. }
  263. func (o *opHistory) Update(s []rune, commit bool) (err error) {
  264. o.fdLock.Lock()
  265. defer o.fdLock.Unlock()
  266. s = runes.Copy(s)
  267. if o.current == nil {
  268. o.Push(s)
  269. o.Compact()
  270. return
  271. }
  272. r := o.current.Value.(*hisItem)
  273. r.Version = o.historyVer
  274. if commit {
  275. r.Source = s
  276. if o.fd != nil {
  277. // just report the error
  278. _, err = o.fd.Write([]byte(string(r.Source) + "\n"))
  279. }
  280. } else {
  281. r.Tmp = append(r.Tmp[:0], s...)
  282. }
  283. o.current.Value = r
  284. o.Compact()
  285. return
  286. }
  287. func (o *opHistory) Push(s []rune) {
  288. s = runes.Copy(s)
  289. elem := o.history.PushBack(&hisItem{Source: s})
  290. o.current = elem
  291. }