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.

326 lines
6.9 KiB

  1. // Readline is a pure go implementation for GNU-Readline kind library.
  2. //
  3. // example:
  4. // rl, err := readline.New("> ")
  5. // if err != nil {
  6. // panic(err)
  7. // }
  8. // defer rl.Close()
  9. //
  10. // for {
  11. // line, err := rl.Readline()
  12. // if err != nil { // io.EOF
  13. // break
  14. // }
  15. // println(line)
  16. // }
  17. //
  18. package readline
  19. import "io"
  20. type Instance struct {
  21. Config *Config
  22. Terminal *Terminal
  23. Operation *Operation
  24. }
  25. type Config struct {
  26. // prompt supports ANSI escape sequence, so we can color some characters even in windows
  27. Prompt string
  28. // readline will persist historys to file where HistoryFile specified
  29. HistoryFile string
  30. // specify the max length of historys, it's 500 by default, set it to -1 to disable history
  31. HistoryLimit int
  32. DisableAutoSaveHistory bool
  33. // enable case-insensitive history searching
  34. HistorySearchFold bool
  35. // AutoCompleter will called once user press TAB
  36. AutoComplete AutoCompleter
  37. // Any key press will pass to Listener
  38. // NOTE: Listener will be triggered by (nil, 0, 0) immediately
  39. Listener Listener
  40. Painter Painter
  41. // If VimMode is true, readline will in vim.insert mode by default
  42. VimMode bool
  43. InterruptPrompt string
  44. EOFPrompt string
  45. FuncGetWidth func() int
  46. Stdin io.ReadCloser
  47. StdinWriter io.Writer
  48. Stdout io.Writer
  49. Stderr io.Writer
  50. EnableMask bool
  51. MaskRune rune
  52. // erase the editing line after user submited it
  53. // it use in IM usually.
  54. UniqueEditLine bool
  55. // filter input runes (may be used to disable CtrlZ or for translating some keys to different actions)
  56. // -> output = new (translated) rune and true/false if continue with processing this one
  57. FuncFilterInputRune func(rune) (rune, bool)
  58. // force use interactive even stdout is not a tty
  59. FuncIsTerminal func() bool
  60. FuncMakeRaw func() error
  61. FuncExitRaw func() error
  62. FuncOnWidthChanged func(func())
  63. ForceUseInteractive bool
  64. // private fields
  65. inited bool
  66. opHistory *opHistory
  67. opSearch *opSearch
  68. }
  69. func (c *Config) useInteractive() bool {
  70. if c.ForceUseInteractive {
  71. return true
  72. }
  73. return c.FuncIsTerminal()
  74. }
  75. func (c *Config) Init() error {
  76. if c.inited {
  77. return nil
  78. }
  79. c.inited = true
  80. if c.Stdin == nil {
  81. c.Stdin = NewCancelableStdin(Stdin)
  82. }
  83. c.Stdin, c.StdinWriter = NewFillableStdin(c.Stdin)
  84. if c.Stdout == nil {
  85. c.Stdout = Stdout
  86. }
  87. if c.Stderr == nil {
  88. c.Stderr = Stderr
  89. }
  90. if c.HistoryLimit == 0 {
  91. c.HistoryLimit = 500
  92. }
  93. if c.InterruptPrompt == "" {
  94. c.InterruptPrompt = "^C"
  95. } else if c.InterruptPrompt == "\n" {
  96. c.InterruptPrompt = ""
  97. }
  98. if c.EOFPrompt == "" {
  99. c.EOFPrompt = "^D"
  100. } else if c.EOFPrompt == "\n" {
  101. c.EOFPrompt = ""
  102. }
  103. if c.AutoComplete == nil {
  104. c.AutoComplete = &TabCompleter{}
  105. }
  106. if c.FuncGetWidth == nil {
  107. c.FuncGetWidth = GetScreenWidth
  108. }
  109. if c.FuncIsTerminal == nil {
  110. c.FuncIsTerminal = DefaultIsTerminal
  111. }
  112. rm := new(RawMode)
  113. if c.FuncMakeRaw == nil {
  114. c.FuncMakeRaw = rm.Enter
  115. }
  116. if c.FuncExitRaw == nil {
  117. c.FuncExitRaw = rm.Exit
  118. }
  119. if c.FuncOnWidthChanged == nil {
  120. c.FuncOnWidthChanged = DefaultOnWidthChanged
  121. }
  122. return nil
  123. }
  124. func (c Config) Clone() *Config {
  125. c.opHistory = nil
  126. c.opSearch = nil
  127. return &c
  128. }
  129. func (c *Config) SetListener(f func(line []rune, pos int, key rune) (newLine []rune, newPos int, ok bool)) {
  130. c.Listener = FuncListener(f)
  131. }
  132. func (c *Config) SetPainter(p Painter) {
  133. c.Painter = p
  134. }
  135. func NewEx(cfg *Config) (*Instance, error) {
  136. t, err := NewTerminal(cfg)
  137. if err != nil {
  138. return nil, err
  139. }
  140. rl := t.Readline()
  141. if cfg.Painter == nil {
  142. cfg.Painter = &defaultPainter{}
  143. }
  144. return &Instance{
  145. Config: cfg,
  146. Terminal: t,
  147. Operation: rl,
  148. }, nil
  149. }
  150. func New(prompt string) (*Instance, error) {
  151. return NewEx(&Config{Prompt: prompt})
  152. }
  153. func (i *Instance) ResetHistory() {
  154. i.Operation.ResetHistory()
  155. }
  156. func (i *Instance) SetPrompt(s string) {
  157. i.Operation.SetPrompt(s)
  158. }
  159. func (i *Instance) SetMaskRune(r rune) {
  160. i.Operation.SetMaskRune(r)
  161. }
  162. // change history persistence in runtime
  163. func (i *Instance) SetHistoryPath(p string) {
  164. i.Operation.SetHistoryPath(p)
  165. }
  166. // readline will refresh automatic when write through Stdout()
  167. func (i *Instance) Stdout() io.Writer {
  168. return i.Operation.Stdout()
  169. }
  170. // readline will refresh automatic when write through Stdout()
  171. func (i *Instance) Stderr() io.Writer {
  172. return i.Operation.Stderr()
  173. }
  174. // switch VimMode in runtime
  175. func (i *Instance) SetVimMode(on bool) {
  176. i.Operation.SetVimMode(on)
  177. }
  178. func (i *Instance) IsVimMode() bool {
  179. return i.Operation.IsEnableVimMode()
  180. }
  181. func (i *Instance) GenPasswordConfig() *Config {
  182. return i.Operation.GenPasswordConfig()
  183. }
  184. // we can generate a config by `i.GenPasswordConfig()`
  185. func (i *Instance) ReadPasswordWithConfig(cfg *Config) ([]byte, error) {
  186. return i.Operation.PasswordWithConfig(cfg)
  187. }
  188. func (i *Instance) ReadPasswordEx(prompt string, l Listener) ([]byte, error) {
  189. return i.Operation.PasswordEx(prompt, l)
  190. }
  191. func (i *Instance) ReadPassword(prompt string) ([]byte, error) {
  192. return i.Operation.Password(prompt)
  193. }
  194. type Result struct {
  195. Line string
  196. Error error
  197. }
  198. func (l *Result) CanContinue() bool {
  199. return len(l.Line) != 0 && l.Error == ErrInterrupt
  200. }
  201. func (l *Result) CanBreak() bool {
  202. return !l.CanContinue() && l.Error != nil
  203. }
  204. func (i *Instance) Line() *Result {
  205. ret, err := i.Readline()
  206. return &Result{ret, err}
  207. }
  208. // err is one of (nil, io.EOF, readline.ErrInterrupt)
  209. func (i *Instance) Readline() (string, error) {
  210. return i.Operation.String()
  211. }
  212. func (i *Instance) ReadlineWithDefault(what string) (string, error) {
  213. i.Operation.SetBuffer(what)
  214. return i.Operation.String()
  215. }
  216. func (i *Instance) SaveHistory(content string) error {
  217. return i.Operation.SaveHistory(content)
  218. }
  219. // same as readline
  220. func (i *Instance) ReadSlice() ([]byte, error) {
  221. return i.Operation.Slice()
  222. }
  223. // we must make sure that call Close() before process exit.
  224. func (i *Instance) Close() error {
  225. if err := i.Terminal.Close(); err != nil {
  226. return err
  227. }
  228. i.Config.Stdin.Close()
  229. i.Operation.Close()
  230. return nil
  231. }
  232. func (i *Instance) Clean() {
  233. i.Operation.Clean()
  234. }
  235. func (i *Instance) Write(b []byte) (int, error) {
  236. return i.Stdout().Write(b)
  237. }
  238. // WriteStdin prefill the next Stdin fetch
  239. // Next time you call ReadLine() this value will be writen before the user input
  240. // ie :
  241. // i := readline.New()
  242. // i.WriteStdin([]byte("test"))
  243. // _, _= i.Readline()
  244. //
  245. // gives
  246. //
  247. // > test[cursor]
  248. func (i *Instance) WriteStdin(val []byte) (int, error) {
  249. return i.Terminal.WriteStdin(val)
  250. }
  251. func (i *Instance) SetConfig(cfg *Config) *Config {
  252. if i.Config == cfg {
  253. return cfg
  254. }
  255. old := i.Config
  256. i.Config = cfg
  257. i.Operation.SetConfig(cfg)
  258. i.Terminal.SetConfig(cfg)
  259. return old
  260. }
  261. func (i *Instance) Refresh() {
  262. i.Operation.Refresh()
  263. }
  264. // HistoryDisable the save of the commands into the history
  265. func (i *Instance) HistoryDisable() {
  266. i.Operation.history.Disable()
  267. }
  268. // HistoryEnable the save of the commands into the history (default on)
  269. func (i *Instance) HistoryEnable() {
  270. i.Operation.history.Enable()
  271. }