api.go 6.4 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285
  1. // License: GPLv3 Copyright: 2022, Kovid Goyal, <kovid at kovidgoyal.net>
  2. package readline
  3. import (
  4. "container/list"
  5. "fmt"
  6. "strings"
  7. "kitty/tools/cli"
  8. "kitty/tools/cli/markup"
  9. "kitty/tools/tui/loop"
  10. "kitty/tools/wcswidth"
  11. )
  12. var _ = fmt.Print
  13. const ST = "\x1b\\"
  14. const PROMPT_MARK = "\x1b]133;"
  15. type SyntaxHighlightFunction = func(text string, x, y int) string
  16. type CompleterFunction = func(before_cursor, after_cursor string) *cli.Completions
  17. type RlInit struct {
  18. Prompt string
  19. HistoryPath string
  20. HistoryCount int
  21. ContinuationPrompt string
  22. EmptyContinuationPrompt bool
  23. DontMarkPrompts bool
  24. SyntaxHighlighter SyntaxHighlightFunction
  25. Completer CompleterFunction
  26. }
  27. type Position struct {
  28. X int
  29. Y int
  30. }
  31. func (self Position) Less(other Position) bool {
  32. return self.Y < other.Y || (self.Y == other.Y && self.X < other.X)
  33. }
  34. type kill_ring struct {
  35. items *list.List
  36. }
  37. func (self *kill_ring) append_to_existing_item(text string) {
  38. e := self.items.Front()
  39. if e == nil {
  40. self.add_new_item(text)
  41. }
  42. e.Value = e.Value.(string) + text
  43. }
  44. func (self *kill_ring) add_new_item(text string) {
  45. if text != "" {
  46. self.items.PushFront(text)
  47. }
  48. }
  49. func (self *kill_ring) yank() string {
  50. e := self.items.Front()
  51. if e == nil {
  52. return ""
  53. }
  54. return e.Value.(string)
  55. }
  56. func (self *kill_ring) pop_yank() string {
  57. e := self.items.Front()
  58. if e == nil {
  59. return ""
  60. }
  61. self.items.MoveToBack(e)
  62. return self.yank()
  63. }
  64. func (self *kill_ring) clear() {
  65. self.items = self.items.Init()
  66. }
  67. type Prompt struct {
  68. Text string
  69. Length int
  70. }
  71. type InputState struct {
  72. // Input lines
  73. lines []string
  74. // The cursor position in the text
  75. cursor Position
  76. }
  77. func (self InputState) copy() InputState {
  78. ans := self
  79. l := make([]string, len(self.lines))
  80. copy(l, self.lines)
  81. ans.lines = l
  82. return ans
  83. }
  84. type syntax_highlighted struct {
  85. lines []string
  86. src_for_last_highlight string
  87. highlighter SyntaxHighlightFunction
  88. last_highlighter_name string
  89. }
  90. type Readline struct {
  91. prompt, continuation_prompt Prompt
  92. mark_prompts bool
  93. loop *loop.Loop
  94. history *History
  95. kill_ring kill_ring
  96. input_state InputState
  97. // The number of lines after the initial line on the screen
  98. cursor_y int
  99. screen_width, screen_height int
  100. last_yank_extent struct {
  101. start, end Position
  102. }
  103. bracketed_paste_buffer strings.Builder
  104. last_action Action
  105. history_matches *HistoryMatches
  106. history_search *HistorySearch
  107. keyboard_state KeyboardState
  108. fmt_ctx *markup.Context
  109. text_to_be_added string
  110. syntax_highlighted syntax_highlighted
  111. completions completions
  112. }
  113. func (self *Readline) make_prompt(text string, is_secondary bool) Prompt {
  114. if self.mark_prompts {
  115. m := PROMPT_MARK + "A"
  116. if is_secondary {
  117. m += ";k=s"
  118. }
  119. text = m + ST + text
  120. }
  121. return Prompt{Text: text, Length: wcswidth.Stringwidth(text)}
  122. }
  123. func New(loop *loop.Loop, r RlInit) *Readline {
  124. hc := r.HistoryCount
  125. if hc == 0 {
  126. hc = 8192
  127. }
  128. ans := &Readline{
  129. mark_prompts: !r.DontMarkPrompts, fmt_ctx: markup.New(true),
  130. loop: loop, input_state: InputState{lines: []string{""}}, history: NewHistory(r.HistoryPath, hc),
  131. syntax_highlighted: syntax_highlighted{highlighter: r.SyntaxHighlighter},
  132. completions: completions{completer: r.Completer},
  133. kill_ring: kill_ring{items: list.New().Init()},
  134. }
  135. if ans.completions.completer == nil && r.HistoryPath != "" {
  136. ans.completions.completer = ans.HistoryCompleter
  137. }
  138. ans.prompt = ans.make_prompt(r.Prompt, false)
  139. t := ""
  140. if r.ContinuationPrompt != "" || !r.EmptyContinuationPrompt {
  141. t = r.ContinuationPrompt
  142. if t == "" {
  143. t = ans.fmt_ctx.Yellow(">") + " "
  144. }
  145. }
  146. ans.continuation_prompt = ans.make_prompt(t, true)
  147. return ans
  148. }
  149. func (self *Readline) HistoryCompleter(before_cursor, after_cursor string) *cli.Completions {
  150. return self.history_completer(before_cursor, after_cursor)
  151. }
  152. func (self *Readline) SetPrompt(prompt string) {
  153. self.prompt = self.make_prompt(prompt, false)
  154. }
  155. func (self *Readline) Shutdown() {
  156. self.history.Shutdown()
  157. }
  158. func (self *Readline) AddHistoryItem(hi HistoryItem) {
  159. self.history.merge_items(hi)
  160. }
  161. func (self *Readline) ResetText() {
  162. self.input_state = InputState{lines: []string{""}}
  163. self.last_action = ActionNil
  164. self.keyboard_state = KeyboardState{}
  165. self.history_search = nil
  166. self.completions.current = completion{}
  167. self.cursor_y = 0
  168. }
  169. func (self *Readline) ChangeLoopAndResetText(lp *loop.Loop) {
  170. self.loop = lp
  171. self.ResetText()
  172. }
  173. func (self *Readline) Start() {
  174. self.loop.SetCursorShape(loop.BAR_CURSOR, true)
  175. self.loop.StartBracketedPaste()
  176. self.Redraw()
  177. }
  178. func (self *Readline) End() {
  179. self.loop.SetCursorShape(loop.BLOCK_CURSOR, true)
  180. self.loop.EndBracketedPaste()
  181. self.loop.QueueWriteString("\r\n")
  182. if self.mark_prompts {
  183. self.loop.QueueWriteString(PROMPT_MARK + "C" + ST)
  184. }
  185. }
  186. func MarkOutputStart() string {
  187. return PROMPT_MARK + "C" + ST
  188. }
  189. func (self *Readline) Redraw() {
  190. self.loop.StartAtomicUpdate()
  191. self.RedrawNonAtomic()
  192. self.loop.EndAtomicUpdate()
  193. }
  194. func (self *Readline) RedrawNonAtomic() {
  195. self.redraw()
  196. }
  197. func (self *Readline) OnKeyEvent(event *loop.KeyEvent) error {
  198. err := self.handle_key_event(event)
  199. if err == ErrCouldNotPerformAction {
  200. err = nil
  201. self.loop.Beep()
  202. }
  203. return err
  204. }
  205. func (self *Readline) OnText(text string, from_key_event bool, in_bracketed_paste bool) error {
  206. if in_bracketed_paste {
  207. self.bracketed_paste_buffer.WriteString(text)
  208. return nil
  209. }
  210. if self.bracketed_paste_buffer.Len() > 0 {
  211. self.bracketed_paste_buffer.WriteString(text)
  212. text = self.bracketed_paste_buffer.String()
  213. self.bracketed_paste_buffer.Reset()
  214. }
  215. self.text_to_be_added = text
  216. return self.dispatch_key_action(ActionAddText)
  217. }
  218. func (self *Readline) TextBeforeCursor() string {
  219. return self.text_upto_cursor_pos()
  220. }
  221. func (self *Readline) TextAfterCursor() string {
  222. return self.text_after_cursor_pos()
  223. }
  224. func (self *Readline) AllText() string {
  225. return self.all_text()
  226. }
  227. func (self *Readline) SetText(text string) {
  228. self.set_text(text)
  229. }
  230. func (self *Readline) MoveCursorToEnd() bool {
  231. return self.move_to_end()
  232. }
  233. func (self *Readline) CursorAtEndOfLine() bool {
  234. return self.input_state.cursor.X >= len(self.input_state.lines[self.input_state.cursor.Y])
  235. }
  236. func (self *Readline) OnResize(old_size loop.ScreenSize, new_size loop.ScreenSize) error {
  237. self.screen_width, self.screen_height = 0, 0
  238. self.Redraw()
  239. return nil
  240. }