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.

531 lines
10 KiB

  1. package readline
  2. import (
  3. "errors"
  4. "io"
  5. "sync"
  6. )
  7. var (
  8. ErrInterrupt = errors.New("Interrupt")
  9. )
  10. type InterruptError struct {
  11. Line []rune
  12. }
  13. func (*InterruptError) Error() string {
  14. return "Interrupted"
  15. }
  16. type Operation struct {
  17. m sync.Mutex
  18. cfg *Config
  19. t *Terminal
  20. buf *RuneBuffer
  21. outchan chan []rune
  22. errchan chan error
  23. w io.Writer
  24. history *opHistory
  25. *opSearch
  26. *opCompleter
  27. *opPassword
  28. *opVim
  29. }
  30. func (o *Operation) SetBuffer(what string) {
  31. o.buf.Set([]rune(what))
  32. }
  33. type wrapWriter struct {
  34. r *Operation
  35. t *Terminal
  36. target io.Writer
  37. }
  38. func (w *wrapWriter) Write(b []byte) (int, error) {
  39. if !w.t.IsReading() {
  40. return w.target.Write(b)
  41. }
  42. var (
  43. n int
  44. err error
  45. )
  46. w.r.buf.Refresh(func() {
  47. n, err = w.target.Write(b)
  48. })
  49. if w.r.IsSearchMode() {
  50. w.r.SearchRefresh(-1)
  51. }
  52. if w.r.IsInCompleteMode() {
  53. w.r.CompleteRefresh()
  54. }
  55. return n, err
  56. }
  57. func NewOperation(t *Terminal, cfg *Config) *Operation {
  58. width := cfg.FuncGetWidth()
  59. op := &Operation{
  60. t: t,
  61. buf: NewRuneBuffer(t, cfg.Prompt, cfg, width),
  62. outchan: make(chan []rune),
  63. errchan: make(chan error, 1),
  64. }
  65. op.w = op.buf.w
  66. op.SetConfig(cfg)
  67. op.opVim = newVimMode(op)
  68. op.opCompleter = newOpCompleter(op.buf.w, op, width)
  69. op.opPassword = newOpPassword(op)
  70. op.cfg.FuncOnWidthChanged(func() {
  71. newWidth := cfg.FuncGetWidth()
  72. op.opCompleter.OnWidthChange(newWidth)
  73. op.opSearch.OnWidthChange(newWidth)
  74. op.buf.OnWidthChange(newWidth)
  75. })
  76. go op.ioloop()
  77. return op
  78. }
  79. func (o *Operation) SetPrompt(s string) {
  80. o.buf.SetPrompt(s)
  81. }
  82. func (o *Operation) SetMaskRune(r rune) {
  83. o.buf.SetMask(r)
  84. }
  85. func (o *Operation) GetConfig() *Config {
  86. o.m.Lock()
  87. cfg := *o.cfg
  88. o.m.Unlock()
  89. return &cfg
  90. }
  91. func (o *Operation) ioloop() {
  92. for {
  93. keepInSearchMode := false
  94. keepInCompleteMode := false
  95. r := o.t.ReadRune()
  96. if o.GetConfig().FuncFilterInputRune != nil {
  97. var process bool
  98. r, process = o.GetConfig().FuncFilterInputRune(r)
  99. if !process {
  100. o.buf.Refresh(nil) // to refresh the line
  101. continue // ignore this rune
  102. }
  103. }
  104. if r == 0 { // io.EOF
  105. if o.buf.Len() == 0 {
  106. o.buf.Clean()
  107. select {
  108. case o.errchan <- io.EOF:
  109. }
  110. break
  111. } else {
  112. // if stdin got io.EOF and there is something left in buffer,
  113. // let's flush them by sending CharEnter.
  114. // And we will got io.EOF int next loop.
  115. r = CharEnter
  116. }
  117. }
  118. isUpdateHistory := true
  119. if o.IsInCompleteSelectMode() {
  120. keepInCompleteMode = o.HandleCompleteSelect(r)
  121. if keepInCompleteMode {
  122. continue
  123. }
  124. o.buf.Refresh(nil)
  125. switch r {
  126. case CharEnter, CharCtrlJ:
  127. o.history.Update(o.buf.Runes(), false)
  128. fallthrough
  129. case CharInterrupt:
  130. o.t.KickRead()
  131. fallthrough
  132. case CharBell:
  133. continue
  134. }
  135. }
  136. if o.IsEnableVimMode() {
  137. r = o.HandleVim(r, o.t.ReadRune)
  138. if r == 0 {
  139. continue
  140. }
  141. }
  142. switch r {
  143. case CharBell:
  144. if o.IsSearchMode() {
  145. o.ExitSearchMode(true)
  146. o.buf.Refresh(nil)
  147. }
  148. if o.IsInCompleteMode() {
  149. o.ExitCompleteMode(true)
  150. o.buf.Refresh(nil)
  151. }
  152. case CharTab:
  153. if o.GetConfig().AutoComplete == nil {
  154. o.t.Bell()
  155. break
  156. }
  157. if o.OnComplete() {
  158. keepInCompleteMode = true
  159. } else {
  160. o.t.Bell()
  161. break
  162. }
  163. case CharBckSearch:
  164. if !o.SearchMode(S_DIR_BCK) {
  165. o.t.Bell()
  166. break
  167. }
  168. keepInSearchMode = true
  169. case CharCtrlU:
  170. o.buf.KillFront()
  171. case CharFwdSearch:
  172. if !o.SearchMode(S_DIR_FWD) {
  173. o.t.Bell()
  174. break
  175. }
  176. keepInSearchMode = true
  177. case CharKill:
  178. o.buf.Kill()
  179. keepInCompleteMode = true
  180. case MetaForward:
  181. o.buf.MoveToNextWord()
  182. case CharTranspose:
  183. o.buf.Transpose()
  184. case MetaBackward:
  185. o.buf.MoveToPrevWord()
  186. case MetaDelete:
  187. o.buf.DeleteWord()
  188. case CharLineStart:
  189. o.buf.MoveToLineStart()
  190. case CharLineEnd:
  191. o.buf.MoveToLineEnd()
  192. case CharBackspace, CharCtrlH:
  193. if o.IsSearchMode() {
  194. o.SearchBackspace()
  195. keepInSearchMode = true
  196. break
  197. }
  198. if o.buf.Len() == 0 {
  199. o.t.Bell()
  200. break
  201. }
  202. o.buf.Backspace()
  203. if o.IsInCompleteMode() {
  204. o.OnComplete()
  205. }
  206. case CharCtrlZ:
  207. o.buf.Clean()
  208. o.t.SleepToResume()
  209. o.Refresh()
  210. case CharCtrlL:
  211. ClearScreen(o.w)
  212. o.Refresh()
  213. case MetaBackspace, CharCtrlW:
  214. o.buf.BackEscapeWord()
  215. case CharCtrlY:
  216. o.buf.Yank()
  217. case CharEnter, CharCtrlJ:
  218. if o.IsSearchMode() {
  219. o.ExitSearchMode(false)
  220. }
  221. o.buf.MoveToLineEnd()
  222. var data []rune
  223. if !o.GetConfig().UniqueEditLine {
  224. o.buf.WriteRune('\n')
  225. data = o.buf.Reset()
  226. data = data[:len(data)-1] // trim \n
  227. } else {
  228. o.buf.Clean()
  229. data = o.buf.Reset()
  230. }
  231. o.outchan <- data
  232. if !o.GetConfig().DisableAutoSaveHistory {
  233. // ignore IO error
  234. _ = o.history.New(data)
  235. } else {
  236. isUpdateHistory = false
  237. }
  238. case CharBackward:
  239. o.buf.MoveBackward()
  240. case CharForward:
  241. o.buf.MoveForward()
  242. case CharPrev:
  243. buf := o.history.Prev()
  244. if buf != nil {
  245. o.buf.Set(buf)
  246. } else {
  247. o.t.Bell()
  248. }
  249. case CharNext:
  250. buf, ok := o.history.Next()
  251. if ok {
  252. o.buf.Set(buf)
  253. } else {
  254. o.t.Bell()
  255. }
  256. case CharDelete:
  257. if o.buf.Len() > 0 || !o.IsNormalMode() {
  258. o.t.KickRead()
  259. if !o.buf.Delete() {
  260. o.t.Bell()
  261. }
  262. break
  263. }
  264. // treat as EOF
  265. if !o.GetConfig().UniqueEditLine {
  266. o.buf.WriteString(o.GetConfig().EOFPrompt + "\n")
  267. }
  268. o.buf.Reset()
  269. isUpdateHistory = false
  270. o.history.Revert()
  271. o.errchan <- io.EOF
  272. if o.GetConfig().UniqueEditLine {
  273. o.buf.Clean()
  274. }
  275. case CharInterrupt:
  276. if o.IsSearchMode() {
  277. o.t.KickRead()
  278. o.ExitSearchMode(true)
  279. break
  280. }
  281. if o.IsInCompleteMode() {
  282. o.t.KickRead()
  283. o.ExitCompleteMode(true)
  284. o.buf.Refresh(nil)
  285. break
  286. }
  287. o.buf.MoveToLineEnd()
  288. o.buf.Refresh(nil)
  289. hint := o.GetConfig().InterruptPrompt + "\n"
  290. if !o.GetConfig().UniqueEditLine {
  291. o.buf.WriteString(hint)
  292. }
  293. remain := o.buf.Reset()
  294. if !o.GetConfig().UniqueEditLine {
  295. remain = remain[:len(remain)-len([]rune(hint))]
  296. }
  297. isUpdateHistory = false
  298. o.history.Revert()
  299. o.errchan <- &InterruptError{remain}
  300. default:
  301. if o.IsSearchMode() {
  302. o.SearchChar(r)
  303. keepInSearchMode = true
  304. break
  305. }
  306. o.buf.WriteRune(r)
  307. if o.IsInCompleteMode() {
  308. o.OnComplete()
  309. keepInCompleteMode = true
  310. }
  311. }
  312. listener := o.GetConfig().Listener
  313. if listener != nil {
  314. newLine, newPos, ok := listener.OnChange(o.buf.Runes(), o.buf.Pos(), r)
  315. if ok {
  316. o.buf.SetWithIdx(newPos, newLine)
  317. }
  318. }
  319. o.m.Lock()
  320. if !keepInSearchMode && o.IsSearchMode() {
  321. o.ExitSearchMode(false)
  322. o.buf.Refresh(nil)
  323. } else if o.IsInCompleteMode() {
  324. if !keepInCompleteMode {
  325. o.ExitCompleteMode(false)
  326. o.Refresh()
  327. } else {
  328. o.buf.Refresh(nil)
  329. o.CompleteRefresh()
  330. }
  331. }
  332. if isUpdateHistory && !o.IsSearchMode() {
  333. // it will cause null history
  334. o.history.Update(o.buf.Runes(), false)
  335. }
  336. o.m.Unlock()
  337. }
  338. }
  339. func (o *Operation) Stderr() io.Writer {
  340. return &wrapWriter{target: o.GetConfig().Stderr, r: o, t: o.t}
  341. }
  342. func (o *Operation) Stdout() io.Writer {
  343. return &wrapWriter{target: o.GetConfig().Stdout, r: o, t: o.t}
  344. }
  345. func (o *Operation) String() (string, error) {
  346. r, err := o.Runes()
  347. return string(r), err
  348. }
  349. func (o *Operation) Runes() ([]rune, error) {
  350. o.t.EnterRawMode()
  351. defer o.t.ExitRawMode()
  352. listener := o.GetConfig().Listener
  353. if listener != nil {
  354. listener.OnChange(nil, 0, 0)
  355. }
  356. o.buf.Refresh(nil) // print prompt
  357. o.t.KickRead()
  358. select {
  359. case r := <-o.outchan:
  360. return r, nil
  361. case err := <-o.errchan:
  362. if e, ok := err.(*InterruptError); ok {
  363. return e.Line, ErrInterrupt
  364. }
  365. return nil, err
  366. }
  367. }
  368. func (o *Operation) PasswordEx(prompt string, l Listener) ([]byte, error) {
  369. cfg := o.GenPasswordConfig()
  370. cfg.Prompt = prompt
  371. cfg.Listener = l
  372. return o.PasswordWithConfig(cfg)
  373. }
  374. func (o *Operation) GenPasswordConfig() *Config {
  375. return o.opPassword.PasswordConfig()
  376. }
  377. func (o *Operation) PasswordWithConfig(cfg *Config) ([]byte, error) {
  378. if err := o.opPassword.EnterPasswordMode(cfg); err != nil {
  379. return nil, err
  380. }
  381. defer o.opPassword.ExitPasswordMode()
  382. return o.Slice()
  383. }
  384. func (o *Operation) Password(prompt string) ([]byte, error) {
  385. return o.PasswordEx(prompt, nil)
  386. }
  387. func (o *Operation) SetTitle(t string) {
  388. o.w.Write([]byte("\033[2;" + t + "\007"))
  389. }
  390. func (o *Operation) Slice() ([]byte, error) {
  391. r, err := o.Runes()
  392. if err != nil {
  393. return nil, err
  394. }
  395. return []byte(string(r)), nil
  396. }
  397. func (o *Operation) Close() {
  398. o.history.Close()
  399. }
  400. func (o *Operation) SetHistoryPath(path string) {
  401. if o.history != nil {
  402. o.history.Close()
  403. }
  404. o.cfg.HistoryFile = path
  405. o.history = newOpHistory(o.cfg)
  406. }
  407. func (o *Operation) IsNormalMode() bool {
  408. return !o.IsInCompleteMode() && !o.IsSearchMode()
  409. }
  410. func (op *Operation) SetConfig(cfg *Config) (*Config, error) {
  411. op.m.Lock()
  412. defer op.m.Unlock()
  413. if op.cfg == cfg {
  414. return op.cfg, nil
  415. }
  416. if err := cfg.Init(); err != nil {
  417. return op.cfg, err
  418. }
  419. old := op.cfg
  420. op.cfg = cfg
  421. op.SetPrompt(cfg.Prompt)
  422. op.SetMaskRune(cfg.MaskRune)
  423. op.buf.SetConfig(cfg)
  424. width := op.cfg.FuncGetWidth()
  425. if cfg.opHistory == nil {
  426. op.SetHistoryPath(cfg.HistoryFile)
  427. cfg.opHistory = op.history
  428. cfg.opSearch = newOpSearch(op.buf.w, op.buf, op.history, cfg, width)
  429. }
  430. op.history = cfg.opHistory
  431. // SetHistoryPath will close opHistory which already exists
  432. // so if we use it next time, we need to reopen it by `InitHistory()`
  433. op.history.Init()
  434. if op.cfg.AutoComplete != nil {
  435. op.opCompleter = newOpCompleter(op.buf.w, op, width)
  436. }
  437. op.opSearch = cfg.opSearch
  438. return old, nil
  439. }
  440. func (o *Operation) ResetHistory() {
  441. o.history.Reset()
  442. }
  443. // if err is not nil, it just mean it fail to write to file
  444. // other things goes fine.
  445. func (o *Operation) SaveHistory(content string) error {
  446. return o.history.New([]rune(content))
  447. }
  448. func (o *Operation) Refresh() {
  449. if o.t.IsReading() {
  450. o.buf.Refresh(nil)
  451. }
  452. }
  453. func (o *Operation) Clean() {
  454. o.buf.Clean()
  455. }
  456. func FuncListener(f func(line []rune, pos int, key rune) (newLine []rune, newPos int, ok bool)) Listener {
  457. return &DumpListener{f: f}
  458. }
  459. type DumpListener struct {
  460. f func(line []rune, pos int, key rune) (newLine []rune, newPos int, ok bool)
  461. }
  462. func (d *DumpListener) OnChange(line []rune, pos int, key rune) (newLine []rune, newPos int, ok bool) {
  463. return d.f(line, pos, key)
  464. }
  465. type Listener interface {
  466. OnChange(line []rune, pos int, key rune) (newLine []rune, newPos int, ok bool)
  467. }
  468. type Painter interface {
  469. Paint(line []rune, pos int) []rune
  470. }
  471. type defaultPainter struct{}
  472. func (p *defaultPainter) Paint(line []rune, _ int) []rune {
  473. return line
  474. }