retrovim 0.0.3

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (218) hide show
  1. package/.conda/retrovim.yaml +259 -0
  2. package/.conda/terax.icns +0 -0
  3. package/.conda/terax.ico +0 -0
  4. package/.conda/terax.png +0 -0
  5. package/.github/workflows/release.yml +95 -0
  6. package/.gitmodules +3 -0
  7. package/.npm/kanata_touchcursor.js +28 -0
  8. package/.npm/nvim.js +26 -0
  9. package/.npm/yazi.js +26 -0
  10. package/.npm/zsh.js +17 -0
  11. package/.pip/kanata_touchcursor/__init__.py +28 -0
  12. package/.pip/nvim/__init__.py +31 -0
  13. package/.pip/yazi/__init__.py +31 -0
  14. package/.pip/zsh/__init__.py +22 -0
  15. package/CHANGELOG.md +26 -0
  16. package/LICENSE +674 -0
  17. package/README.md +336 -0
  18. package/kanata/touchcursor.kbd +164 -0
  19. package/nvim/init.lua +813 -0
  20. package/nvim/plugins/mini.nvim/.github/DISCUSSION_TEMPLATE/q-a.yml +72 -0
  21. package/nvim/plugins/mini.nvim/.github/ISSUE_TEMPLATE/bug-report.yml +136 -0
  22. package/nvim/plugins/mini.nvim/.github/ISSUE_TEMPLATE/config.yml +5 -0
  23. package/nvim/plugins/mini.nvim/.github/ISSUE_TEMPLATE/feature-request.yml +75 -0
  24. package/nvim/plugins/mini.nvim/.github/PULL_REQUEST_TEMPLATE.md +2 -0
  25. package/nvim/plugins/mini.nvim/.github/workflows/quality-control.yml +150 -0
  26. package/nvim/plugins/mini.nvim/.pre-commit-config.yaml +20 -0
  27. package/nvim/plugins/mini.nvim/.stylua.toml +7 -0
  28. package/nvim/plugins/mini.nvim/.styluaignore +1 -0
  29. package/nvim/plugins/mini.nvim/CHANGELOG.md +1894 -0
  30. package/nvim/plugins/mini.nvim/CODE_OF_CONDUCT.md +132 -0
  31. package/nvim/plugins/mini.nvim/CONTRIBUTING.md +331 -0
  32. package/nvim/plugins/mini.nvim/LICENSE +21 -0
  33. package/nvim/plugins/mini.nvim/MAINTAINING.md +330 -0
  34. package/nvim/plugins/mini.nvim/Makefile +51 -0
  35. package/nvim/plugins/mini.nvim/README.md +261 -0
  36. package/nvim/plugins/mini.nvim/TESTING.md +1011 -0
  37. package/nvim/plugins/mini.nvim/benchmarks/starter/README.md +35 -0
  38. package/nvim/plugins/mini.nvim/benchmarks/starter/benchmark.sh +56 -0
  39. package/nvim/plugins/mini.nvim/benchmarks/starter/init-files/init_dashboard-alpha.lua +21 -0
  40. package/nvim/plugins/mini.nvim/benchmarks/starter/init-files/init_dashboard-original.lua +17 -0
  41. package/nvim/plugins/mini.nvim/benchmarks/starter/init-files/init_dashboard-starter.lua +18 -0
  42. package/nvim/plugins/mini.nvim/benchmarks/starter/init-files/init_empty.lua +4 -0
  43. package/nvim/plugins/mini.nvim/benchmarks/starter/init-files/init_starter-default.lua +7 -0
  44. package/nvim/plugins/mini.nvim/benchmarks/starter/init-files/init_startify-alpha.lua +10 -0
  45. package/nvim/plugins/mini.nvim/benchmarks/starter/init-files/init_startify-original.lua +7 -0
  46. package/nvim/plugins/mini.nvim/benchmarks/starter/init-files/init_startify-starter.lua +20 -0
  47. package/nvim/plugins/mini.nvim/benchmarks/starter/install.sh +10 -0
  48. package/nvim/plugins/mini.nvim/benchmarks/starter/make_summary.py +68 -0
  49. package/nvim/plugins/mini.nvim/benchmarks/starter/startup-summary.md +10 -0
  50. package/nvim/plugins/mini.nvim/colors/miniautumn.lua +55 -0
  51. package/nvim/plugins/mini.nvim/colors/minicyan.lua +95 -0
  52. package/nvim/plugins/mini.nvim/colors/minischeme.lua +95 -0
  53. package/nvim/plugins/mini.nvim/colors/minispring.lua +53 -0
  54. package/nvim/plugins/mini.nvim/colors/minisummer.lua +49 -0
  55. package/nvim/plugins/mini.nvim/colors/miniwinter.lua +51 -0
  56. package/nvim/plugins/mini.nvim/colors/randomhue.lua +16 -0
  57. package/nvim/plugins/mini.nvim/doc/mini-ai.txt +889 -0
  58. package/nvim/plugins/mini.nvim/doc/mini-align.txt +923 -0
  59. package/nvim/plugins/mini.nvim/doc/mini-animate.txt +937 -0
  60. package/nvim/plugins/mini.nvim/doc/mini-base16.txt +285 -0
  61. package/nvim/plugins/mini.nvim/doc/mini-basics.txt +359 -0
  62. package/nvim/plugins/mini.nvim/doc/mini-bracketed.txt +622 -0
  63. package/nvim/plugins/mini.nvim/doc/mini-bufremove.txt +123 -0
  64. package/nvim/plugins/mini.nvim/doc/mini-clue.txt +760 -0
  65. package/nvim/plugins/mini.nvim/doc/mini-cmdline.txt +392 -0
  66. package/nvim/plugins/mini.nvim/doc/mini-colors.txt +880 -0
  67. package/nvim/plugins/mini.nvim/doc/mini-comment.txt +230 -0
  68. package/nvim/plugins/mini.nvim/doc/mini-completion.txt +477 -0
  69. package/nvim/plugins/mini.nvim/doc/mini-cursorword.txt +105 -0
  70. package/nvim/plugins/mini.nvim/doc/mini-deps.txt +590 -0
  71. package/nvim/plugins/mini.nvim/doc/mini-diff.txt +661 -0
  72. package/nvim/plugins/mini.nvim/doc/mini-doc.txt +434 -0
  73. package/nvim/plugins/mini.nvim/doc/mini-extra.txt +856 -0
  74. package/nvim/plugins/mini.nvim/doc/mini-files.txt +1018 -0
  75. package/nvim/plugins/mini.nvim/doc/mini-fuzzy.txt +160 -0
  76. package/nvim/plugins/mini.nvim/doc/mini-git.txt +425 -0
  77. package/nvim/plugins/mini.nvim/doc/mini-hipatterns.txt +489 -0
  78. package/nvim/plugins/mini.nvim/doc/mini-hues.txt +420 -0
  79. package/nvim/plugins/mini.nvim/doc/mini-icons.txt +508 -0
  80. package/nvim/plugins/mini.nvim/doc/mini-indentscope.txt +485 -0
  81. package/nvim/plugins/mini.nvim/doc/mini-input.txt +932 -0
  82. package/nvim/plugins/mini.nvim/doc/mini-jump.txt +202 -0
  83. package/nvim/plugins/mini.nvim/doc/mini-jump2d.txt +525 -0
  84. package/nvim/plugins/mini.nvim/doc/mini-keymap.txt +449 -0
  85. package/nvim/plugins/mini.nvim/doc/mini-map.txt +717 -0
  86. package/nvim/plugins/mini.nvim/doc/mini-misc.txt +431 -0
  87. package/nvim/plugins/mini.nvim/doc/mini-move.txt +181 -0
  88. package/nvim/plugins/mini.nvim/doc/mini-notify.txt +434 -0
  89. package/nvim/plugins/mini.nvim/doc/mini-nvim.txt +431 -0
  90. package/nvim/plugins/mini.nvim/doc/mini-operators.txt +472 -0
  91. package/nvim/plugins/mini.nvim/doc/mini-pairs.txt +346 -0
  92. package/nvim/plugins/mini.nvim/doc/mini-pick.txt +1535 -0
  93. package/nvim/plugins/mini.nvim/doc/mini-sessions.txt +239 -0
  94. package/nvim/plugins/mini.nvim/doc/mini-snippets.txt +1291 -0
  95. package/nvim/plugins/mini.nvim/doc/mini-splitjoin.txt +536 -0
  96. package/nvim/plugins/mini.nvim/doc/mini-starter.txt +618 -0
  97. package/nvim/plugins/mini.nvim/doc/mini-statusline.txt +361 -0
  98. package/nvim/plugins/mini.nvim/doc/mini-surround.txt +863 -0
  99. package/nvim/plugins/mini.nvim/doc/mini-tabline.txt +151 -0
  100. package/nvim/plugins/mini.nvim/doc/mini-test.txt +966 -0
  101. package/nvim/plugins/mini.nvim/doc/mini-trailspace.txt +91 -0
  102. package/nvim/plugins/mini.nvim/doc/mini-visits.txt +937 -0
  103. package/nvim/plugins/mini.nvim/logo.png +0 -0
  104. package/nvim/plugins/mini.nvim/lua/mini/ai.lua +2244 -0
  105. package/nvim/plugins/mini.nvim/lua/mini/align.lua +2076 -0
  106. package/nvim/plugins/mini.nvim/lua/mini/animate.lua +2127 -0
  107. package/nvim/plugins/mini.nvim/lua/mini/base16.lua +1752 -0
  108. package/nvim/plugins/mini.nvim/lua/mini/basics.lua +772 -0
  109. package/nvim/plugins/mini.nvim/lua/mini/bracketed.lua +2013 -0
  110. package/nvim/plugins/mini.nvim/lua/mini/bufremove.lua +283 -0
  111. package/nvim/plugins/mini.nvim/lua/mini/clue.lua +2096 -0
  112. package/nvim/plugins/mini.nvim/lua/mini/cmdline.lua +1182 -0
  113. package/nvim/plugins/mini.nvim/lua/mini/colors.lua +2483 -0
  114. package/nvim/plugins/mini.nvim/lua/mini/comment.lua +586 -0
  115. package/nvim/plugins/mini.nvim/lua/mini/completion.lua +2094 -0
  116. package/nvim/plugins/mini.nvim/lua/mini/cursorword.lua +311 -0
  117. package/nvim/plugins/mini.nvim/lua/mini/deps.lua +1660 -0
  118. package/nvim/plugins/mini.nvim/lua/mini/diff.lua +1938 -0
  119. package/nvim/plugins/mini.nvim/lua/mini/doc.lua +1371 -0
  120. package/nvim/plugins/mini.nvim/lua/mini/extra.lua +2290 -0
  121. package/nvim/plugins/mini.nvim/lua/mini/files.lua +3145 -0
  122. package/nvim/plugins/mini.nvim/lua/mini/fuzzy.lua +356 -0
  123. package/nvim/plugins/mini.nvim/lua/mini/git.lua +1737 -0
  124. package/nvim/plugins/mini.nvim/lua/mini/hipatterns.lua +1042 -0
  125. package/nvim/plugins/mini.nvim/lua/mini/hues.lua +2088 -0
  126. package/nvim/plugins/mini.nvim/lua/mini/icons.lua +2254 -0
  127. package/nvim/plugins/mini.nvim/lua/mini/indentscope.lua +1158 -0
  128. package/nvim/plugins/mini.nvim/lua/mini/init.lua +427 -0
  129. package/nvim/plugins/mini.nvim/lua/mini/input.lua +2183 -0
  130. package/nvim/plugins/mini.nvim/lua/mini/jump.lua +609 -0
  131. package/nvim/plugins/mini.nvim/lua/mini/jump2d.lua +1244 -0
  132. package/nvim/plugins/mini.nvim/lua/mini/keymap.lua +880 -0
  133. package/nvim/plugins/mini.nvim/lua/mini/map.lua +1709 -0
  134. package/nvim/plugins/mini.nvim/lua/mini/misc.lua +969 -0
  135. package/nvim/plugins/mini.nvim/lua/mini/move.lua +509 -0
  136. package/nvim/plugins/mini.nvim/lua/mini/notify.lua +938 -0
  137. package/nvim/plugins/mini.nvim/lua/mini/operators.lua +1349 -0
  138. package/nvim/plugins/mini.nvim/lua/mini/pairs.lua +657 -0
  139. package/nvim/plugins/mini.nvim/lua/mini/pick.lua +3876 -0
  140. package/nvim/plugins/mini.nvim/lua/mini/sessions.lua +689 -0
  141. package/nvim/plugins/mini.nvim/lua/mini/snippets.lua +2828 -0
  142. package/nvim/plugins/mini.nvim/lua/mini/splitjoin.lua +1132 -0
  143. package/nvim/plugins/mini.nvim/lua/mini/starter.lua +1599 -0
  144. package/nvim/plugins/mini.nvim/lua/mini/statusline.lua +686 -0
  145. package/nvim/plugins/mini.nvim/lua/mini/surround.lua +2388 -0
  146. package/nvim/plugins/mini.nvim/lua/mini/tabline.lua +564 -0
  147. package/nvim/plugins/mini.nvim/lua/mini/test.lua +2516 -0
  148. package/nvim/plugins/mini.nvim/lua/mini/trailspace.lua +215 -0
  149. package/nvim/plugins/mini.nvim/lua/mini/visits.lua +1577 -0
  150. package/nvim/plugins/mini.nvim/readmes/mini-ai.md +190 -0
  151. package/nvim/plugins/mini.nvim/readmes/mini-align.md +203 -0
  152. package/nvim/plugins/mini.nvim/readmes/mini-animate.md +214 -0
  153. package/nvim/plugins/mini.nvim/readmes/mini-base16.md +198 -0
  154. package/nvim/plugins/mini.nvim/readmes/mini-basics.md +184 -0
  155. package/nvim/plugins/mini.nvim/readmes/mini-bracketed.md +193 -0
  156. package/nvim/plugins/mini.nvim/readmes/mini-bufremove.md +139 -0
  157. package/nvim/plugins/mini.nvim/readmes/mini-clue.md +260 -0
  158. package/nvim/plugins/mini.nvim/readmes/mini-cmdline.md +183 -0
  159. package/nvim/plugins/mini.nvim/readmes/mini-colors.md +169 -0
  160. package/nvim/plugins/mini.nvim/readmes/mini-comment.md +183 -0
  161. package/nvim/plugins/mini.nvim/readmes/mini-completion.md +217 -0
  162. package/nvim/plugins/mini.nvim/readmes/mini-cursorword.md +137 -0
  163. package/nvim/plugins/mini.nvim/readmes/mini-deps.md +271 -0
  164. package/nvim/plugins/mini.nvim/readmes/mini-diff.md +289 -0
  165. package/nvim/plugins/mini.nvim/readmes/mini-doc.md +198 -0
  166. package/nvim/plugins/mini.nvim/readmes/mini-extra.md +158 -0
  167. package/nvim/plugins/mini.nvim/readmes/mini-files.md +247 -0
  168. package/nvim/plugins/mini.nvim/readmes/mini-fuzzy.md +134 -0
  169. package/nvim/plugins/mini.nvim/readmes/mini-git.md +168 -0
  170. package/nvim/plugins/mini.nvim/readmes/mini-hipatterns.md +176 -0
  171. package/nvim/plugins/mini.nvim/readmes/mini-hues.md +285 -0
  172. package/nvim/plugins/mini.nvim/readmes/mini-icons.md +183 -0
  173. package/nvim/plugins/mini.nvim/readmes/mini-indentscope.md +191 -0
  174. package/nvim/plugins/mini.nvim/readmes/mini-input.md +163 -0
  175. package/nvim/plugins/mini.nvim/readmes/mini-jump.md +161 -0
  176. package/nvim/plugins/mini.nvim/readmes/mini-jump2d.md +197 -0
  177. package/nvim/plugins/mini.nvim/readmes/mini-keymap.md +205 -0
  178. package/nvim/plugins/mini.nvim/readmes/mini-map.md +191 -0
  179. package/nvim/plugins/mini.nvim/readmes/mini-misc.md +145 -0
  180. package/nvim/plugins/mini.nvim/readmes/mini-move.md +163 -0
  181. package/nvim/plugins/mini.nvim/readmes/mini-notify.md +175 -0
  182. package/nvim/plugins/mini.nvim/readmes/mini-operators.md +193 -0
  183. package/nvim/plugins/mini.nvim/readmes/mini-pairs.md +158 -0
  184. package/nvim/plugins/mini.nvim/readmes/mini-pick.md +340 -0
  185. package/nvim/plugins/mini.nvim/readmes/mini-sessions.md +164 -0
  186. package/nvim/plugins/mini.nvim/readmes/mini-snippets.md +373 -0
  187. package/nvim/plugins/mini.nvim/readmes/mini-splitjoin.md +194 -0
  188. package/nvim/plugins/mini.nvim/readmes/mini-starter.md +176 -0
  189. package/nvim/plugins/mini.nvim/readmes/mini-statusline.md +155 -0
  190. package/nvim/plugins/mini.nvim/readmes/mini-surround.md +190 -0
  191. package/nvim/plugins/mini.nvim/readmes/mini-tabline.md +156 -0
  192. package/nvim/plugins/mini.nvim/readmes/mini-test.md +175 -0
  193. package/nvim/plugins/mini.nvim/readmes/mini-trailspace.md +137 -0
  194. package/nvim/plugins/mini.nvim/readmes/mini-visits.md +243 -0
  195. package/nvim/plugins/mini.nvim/scripts/dual_log.sh +13 -0
  196. package/nvim/plugins/mini.nvim/scripts/dual_push.sh +15 -0
  197. package/nvim/plugins/mini.nvim/scripts/dual_release.sh +102 -0
  198. package/nvim/plugins/mini.nvim/scripts/dual_sync.sh +115 -0
  199. package/nvim/plugins/mini.nvim/scripts/init-deps-example.lua +59 -0
  200. package/nvim/plugins/mini.nvim/scripts/lint-filename-length.sh +16 -0
  201. package/nvim/plugins/mini.nvim/scripts/lintcommit-ci.sh +18 -0
  202. package/nvim/plugins/mini.nvim/scripts/lintcommit.lua +393 -0
  203. package/nvim/plugins/mini.nvim/scripts/minidoc.lua +57 -0
  204. package/nvim/plugins/mini.nvim/scripts/minimal_init.lua +28 -0
  205. package/nvim/plugins/mini.nvim/scripts/minitest.lua +4 -0
  206. package/package.json +16 -0
  207. package/pyproject.toml +43 -0
  208. package/yazi/init.lua +12 -0
  209. package/yazi/keymap.toml +114 -0
  210. package/yazi/lesskey +5 -0
  211. package/yazi/plugins/bookmarks.yazi/main.lua +86 -0
  212. package/yazi/plugins/cd-git-root.yazi/main.lua +31 -0
  213. package/yazi/plugins/smart-enter.yazi/main.lua +11 -0
  214. package/yazi/theme.toml +26 -0
  215. package/yazi/yazi.toml +15 -0
  216. package/zsh/.zshrc +145 -0
  217. package/zsh/starship.toml +20 -0
  218. package/zsh/zsh-patina.toml +2 -0
@@ -0,0 +1,2183 @@
1
+ --- *mini.input* Get user input
2
+ ---
3
+ --- MIT License Copyright (c) 2026 Evgeni Chasnovski
4
+
5
+ --- Features:
6
+ ---
7
+ --- - Get user input with fully customizable key and view handling.
8
+ ---
9
+ --- - Built-in configurable views as floating window, statusline/tabline/winbar,
10
+ --- virtual line/text.
11
+ ---
12
+ --- - Implementation is non-blocking but waits to return the input. It also works
13
+ --- in any mode without requiring mode change. See |MiniInput-lifecycle|.
14
+ ---
15
+ --- - |vim.ui.input()| implementation. To adjust, use |MiniInput.ui_input()| or
16
+ --- save-restore `vim.ui.input` manually after calling |MiniInput.setup()|.
17
+ ---
18
+ --- Sources with more details:
19
+ --- - |MiniInput.get()|
20
+ --- - |MiniInput.default_key()|
21
+ --- - |MiniInput-state|
22
+ --- - |MiniInput-examples|
23
+ --- - |MiniInput-in-other-plugins| (for plugin authors)
24
+ ---
25
+ --- # Setup ~
26
+ ---
27
+ --- This module needs a setup with `require('mini.input').setup({})` (replace `{}`
28
+ --- with your `config` table). It will create global Lua table `MiniInput` which
29
+ --- you can use for scripting or manually (with `:lua MiniInput.*`).
30
+ ---
31
+ --- See |MiniInput.config| for `config` structure and default values.
32
+ ---
33
+ --- You can override runtime config settings locally to buffer inside
34
+ --- `vim.b.miniinput_config` which should have same structure as
35
+ --- `MiniInput.config`. See |mini.nvim-buffer-local-config| for more details.
36
+ ---
37
+ --- # Comparisons ~
38
+ ---
39
+ --- - [folke/snacks.nvim#input](https://github.com/folke/snacks.nvim):
40
+ --- - Both provide |vim.ui.input()| implementation.
41
+ --- - Has asynchronous implementation (i.e. does not wait for user to finish
42
+ --- input), while this module has synchronous non-blocking implementation.
43
+ --- - Uses floating window and forced Insert mode. This module allows more
44
+ --- view customizations and can be used in any mode without interruptions.
45
+ ---
46
+ --- - |input()|:
47
+ --- - Both are synchronous.
48
+ --- - Both allow custom highlight and completion.
49
+ --- - This module also allows supplying input scope and visibility,
50
+ --- customizing keys and view.
51
+ ---
52
+ --- # Highlight groups ~
53
+ --- *MiniInput-hl-groups*
54
+ ---
55
+ --- - `MiniInputAdded` - added text during completion navigation.
56
+ --- - `MiniInputBorder` - border of a |MiniInput.gen_view.floatwin()| handler.
57
+ --- - `MiniInputCaret` - caret symbol shown in a prompt area.
58
+ --- - `MiniInputHide` - input is hidden, usually used instead of `MiniInputPrompt`.
59
+ --- - `MiniInputHint` - hints shown during completion navigation.
60
+ --- - `MiniInputNormal` - basic foreground/background.
61
+ --- - `MiniInputPrompt` - input prompt (intention of the input).
62
+ --- - `MiniInputSpecial` - special keys (like literal `\t`, `\n`, etc.) in input.
63
+ ---
64
+ --- # Using in other plugins ~
65
+ --- *MiniInput-in-other-plugins*
66
+ ---
67
+ --- - Prefer using |vim.ui.input()| for more user coverage. Use |MiniInput.get()|
68
+ --- only when getting input synchronously is absolutely necessary.
69
+ ---
70
+ --- - Perform a `_G.MiniInput ~= nil` check before using any feature. This ensures
71
+ --- that user explicitly set up the module.
72
+ ---@tag MiniInput
73
+
74
+ --- Information about the state of the input. It is passed as a handler argument.
75
+ --- Use |MiniInput.get_state()| to get information about the current state (if any).
76
+ --- A table with the following fields:
77
+ ---
78
+ --- - <caret> `(number)` - character (not byte) index at which to modify input.
79
+ --- Should be from 1 (prepend input) to "input width plus 1" (append input).
80
+ ---
81
+ --- - <complete> `(table|nil)` - information about active completion navigation.
82
+ --- If present, it means that completion navigation is in action.
83
+ --- Its fields describe the state of navigation:
84
+ --- - <base> `(string)` - reference text to the left of caret at the start
85
+ --- of completion. Used to compute candidates. Can be empty string.
86
+ --- - <id> `(number)` - identifier of current completion item. Can be zero to
87
+ --- mean that the base is shown. If not zero, must mean that a `items[id]`
88
+ --- candidate is now shown to the left of caret as the part of the input.
89
+ --- - <items> `(table)` - string array of completion candidates. May be empty.
90
+ --- Are intended to fully replace <base>, so should contain it in any sense.
91
+ --- - <method> `(string)` - completion method. Like `"default"`, `"history"`, etc.
92
+ --- Can be `""` (empty string) to mean "default method of complete handler".
93
+ ---
94
+ --- - <data> `(table)` - any information to be reused within the same input session.
95
+ --- Note: handlers should not change fields that they don't "own".
96
+ ---
97
+ --- - <errmsg> `(errmsg)` - first error message caught during input process.
98
+ ---
99
+ --- - <highlight> `(table|nil)` - information about current input highlighting.
100
+ --- If absent, the whole input is highlighted using `MiniInputNormal` group.
101
+ --- Should be an array of highlight ranges. They might be not ordered, overlap,
102
+ --- go outside of input width. It is up to the view handler to decide how to
103
+ --- interpret them. See |MiniInput.state_to_chunks()| for a helper.
104
+ --- Fields of a single highlight range:
105
+ --- - <from> `(number)` - character index (one-indexed) of range start.
106
+ --- - <to> `(number)` - character index (one-indexed) of range end (inclusive).
107
+ --- Should not be smaller than <from>. Can be |math.huge|.
108
+ --- - <hl> `(string)` - highlight group to use for highlighting.
109
+ ---
110
+ --- - <input> `(string)` - current user input. Returned by |MiniInput.get()|.
111
+ ---
112
+ --- - <opts> `(table)` - input options, same as in |MiniInput.get()|.
113
+ ---
114
+ --- - <prompt> `(string)` - intention of the input. May be empty.
115
+ ---
116
+ --- - <status> `(string)` - one of `"start"`, `"progress"`, `"accept"`, `"cancel"`.
117
+ ---@tag MiniInput-state
118
+
119
+ --- # General ~
120
+ ---
121
+ --- ## Initial input ~
122
+ ---
123
+ --- Use `opts.init_keys` to imitate the initial state of the input: >lua
124
+ ---
125
+ --- MiniInput.get({ init_keys = { 'Default' } })
126
+ --- <
127
+ --- ## Custom mappings ~
128
+ ---
129
+ --- Override `handlers.key` in |MiniInput.config|: >lua
130
+ ---
131
+ --- local key_handler = function(state, key)
132
+ --- -- <C-a> - move caret to start of line
133
+ --- if key == '\1' then
134
+ --- state.caret = 1
135
+ --- -- <S-BS> - clear all input
136
+ --- elseif key == vim.keycode('<S-BS>') then
137
+ --- state.input, state.caret = '', 1
138
+ --- else
139
+ --- -- IMPORTANT: Fall back to processing as usual
140
+ --- return MiniInput.default_key(state, key)
141
+ --- end
142
+ --- end
143
+ ---
144
+ --- require('mini.input').setup({ handlers = { key = key_handler } })
145
+ --- <
146
+ --- ## Basic command line ~
147
+ ---
148
+ --- An alternative |Command-line| with highlighting and completion: >lua
149
+ ---
150
+ --- -- Construct reusable `MiniInput.get()` options
151
+ --- local cmdline_opts = { prompt = 'Command', scope = 'editor' }
152
+ --- -- - Highlight using bundled Vim tree-sitter parser and default handler
153
+ --- local highlight_vim = MiniInput.gen_highlight.treesitter('vim')
154
+ --- local highlight_cmdline = function(state)
155
+ --- state = highlight_vim(state) or state
156
+ --- return MiniInput.default_highlight(state) or state
157
+ --- end
158
+ --- cmdline_opts.handlers = { highlight = highlight_cmdline }
159
+ --- -- - Complete as if it is Command line input
160
+ --- cmdline_opts.completion = 'cmdline'
161
+ ---
162
+ --- -- Create a mapping for `:`
163
+ --- local input_cmdline = function()
164
+ --- local cmd = MiniInput.get(cmdline_opts)
165
+ --- if cmd ~= nil then vim.cmd(cmd) end
166
+ --- end
167
+ --- vim.keymap.set('n', ':', input_cmdline)
168
+ --- <
169
+ --- # Handlers ~
170
+ ---
171
+ --- ## Key ~
172
+ ---
173
+ --- Perform custom actions based on arbitrary conditions: >lua
174
+ ---
175
+ --- local key_handler = function(state, key)
176
+ --- -- Adjust prompt
177
+ --- state.opts.prompt = state.opts.prompt:gsub('[?:]%s*$', '')
178
+ ---
179
+ --- -- Adjust scope
180
+ --- if state.opts.prompt == 'Editor action' then
181
+ --- state.opts.scope = 'editor'
182
+ --- end
183
+ ---
184
+ --- -- Hide from view and history
185
+ --- if state.opts.prompt:find('[Pp]assword') ~= nil then
186
+ --- state.opts.hide = true
187
+ --- end
188
+ ---
189
+ --- -- IMPORTANT: Process as usual
190
+ --- state = MiniInput.default_key(state, key) or state
191
+ ---
192
+ --- -- Auto fill and accept
193
+ --- if state.input == 'AF' then
194
+ --- state.input, state.status = 'Autofilled input', 'accept'
195
+ --- end
196
+ --- end
197
+ --- require('mini.input').setup({ handlers = { key = key_handler } })
198
+ --- <
199
+ --- ## View ~
200
+ ---
201
+ --- Show no view: >lua
202
+ ---
203
+ --- require('mini.input').setup({ handlers = { view = function() end } })
204
+ --- <
205
+ --- Compute initial style depending on scope: >lua
206
+ ---
207
+ --- local input = require('mini.input')
208
+ --- local view_virtline = input.gen_view.virtual({ style = 'above' })
209
+ --- local view_tabline = input.gen_view.uiline({ style = 'tabline' })
210
+ --- local view_winbar = input.gen_view.uiline({ style = 'winbar' })
211
+ --- local view_handler = function(state)
212
+ --- -- NOTE: does not support interactive scope change
213
+ --- local scope, view = state.opts.scope, view_tabline
214
+ --- if scope == 'buffer' or scope == 'window' then view = view_winbar end
215
+ --- if scope == 'cursor' or scope == 'line' then view = view_virtline end
216
+ --- return view(state)
217
+ --- end
218
+ ---
219
+ --- input.setup({ handlers = { view = view_handler } })
220
+ --- <
221
+ --- Change symbols for caret and hidden input: see |MiniInput.gen_view|.
222
+ ---@tag MiniInput-examples
223
+
224
+ ---@alias __input_to_chunks - <to_chunks> `(function)` - a function that takes |MiniInput-state| and
225
+ --- `max_width` arguments and returns an array of `{ text, hl }` chunks that fit
226
+ --- into `max_width` display width. See |MiniInput.state_to_chunks()|.
227
+
228
+ ---@diagnostic disable:undefined-field
229
+ ---@diagnostic disable:discard-returns
230
+ ---@diagnostic disable:unused-local
231
+
232
+ -- Module definition ==========================================================
233
+ local MiniInput = {}
234
+ local H = {}
235
+
236
+ --- Module setup
237
+ ---
238
+ ---@param config table|nil Module config table. See |MiniInput.config|.
239
+ ---
240
+ ---@usage >lua
241
+ --- require('mini.input').setup() -- use default config
242
+ --- -- OR
243
+ --- require('mini.input').setup({}) -- replace {} with your config table
244
+ --- <
245
+ MiniInput.setup = function(config)
246
+ -- TODO: Remove after Neovim=0.9 support is dropped
247
+ if vim.fn.has('nvim-0.10') == 0 then
248
+ vim.notify(
249
+ '(mini.input) Neovim<0.10 is soft deprecated (module works but is not supported).'
250
+ .. " It will be deprecated after the next 'mini.nvim' release (module might not work)."
251
+ .. ' Please update your Neovim version.'
252
+ )
253
+ end
254
+
255
+ -- Export module
256
+ _G.MiniInput = MiniInput
257
+
258
+ -- Setup config
259
+ config = H.setup_config(config)
260
+
261
+ -- Apply config
262
+ H.apply_config(config)
263
+
264
+ -- Define behavior
265
+ H.create_autocommands()
266
+
267
+ -- Create default highlighting
268
+ H.create_default_hl()
269
+
270
+ -- Set custom implementation
271
+ vim.ui.input = MiniInput.ui_input
272
+
273
+ -- Adjust terminal emulator's pasting with active input
274
+ H.adjust_vim_paste()
275
+ end
276
+
277
+ --- Defaults ~
278
+ ---@eval return MiniDoc.afterlines_to_code(MiniDoc.current.eval_section)
279
+ ---@text # Handlers ~
280
+ --- *MiniInput.config.handlers*
281
+ ---
282
+ --- `config.handlers` defines functions that are applied during |MiniInput-lifecycle|.
283
+ --- Every handler takes |MiniInput-state| as the first argument and is expected to
284
+ --- either modify it in place or return a new state table.
285
+ ---
286
+ --- Use |MiniInput.apply_handler()| to apply a handler for a given |MiniInput-state|.
287
+ --- They can be set up as part of the config (will be used as default) or passed
288
+ --- directly as a part of |MiniInput.get()| call.
289
+ ---
290
+ --- ## Complete ~
291
+ ---
292
+ --- `handlers.complete` is a handler intended to compute completion suggestions.
293
+ --- Takes |MiniInput-state| and `method` as arguments and is expected to modify
294
+ --- <complete> field. Default: |MiniInput.default_complete()|.
295
+ ---
296
+ --- Only `complete.base` and `complete.items` are expected to be set by a complete
297
+ --- handler. Setting and modifying other fields (`complete.id` and `complete.method`)
298
+ --- is done in other handlers and as part of |MiniInput.apply_handler()|. Actually
299
+ --- showing completion information is up to the view handler.
300
+ ---
301
+ --- This handler is usually applied manually inside a key handler via using
302
+ --- `MiniInput.apply_handler(state, 'complete', method)`. Like, for example,
303
+ --- in |MiniInput.default_key()| after <Tab> or <Up>.
304
+ ---
305
+ --- Here is an example of a simple demo complete handler: >lua
306
+ ---
307
+ --- local complete_handler = function(state, method)
308
+ --- if method == '' or method == 'xy' then
309
+ --- local text = vim.fn.strcharpart(state.input, 0, state.caret - 1)
310
+ --- local base = text:match('%S*$')
311
+ --- state.complete = { base = base, items = { base .. 'x', base .. 'y' } }
312
+ --- return
313
+ --- end
314
+ --- return MiniInput.default_complete(state, method)
315
+ --- end
316
+ ---
317
+ --- require('mini.input').setup({ handlers = { complete = complete_handler } })
318
+ --- <
319
+ --- ## Key ~
320
+ ---
321
+ --- `handlers.key` is a handler intended to process every user key press.
322
+ --- Takes |MiniInput-state| and `key` as arguments. Argument `key` represents a key
323
+ --- that needs to be processed: a string if from the user input or `nil` if input
324
+ --- needs to be set up, refreshed, or torn down. Default: |MiniInput.default_key()|.
325
+ ---
326
+ --- A string `key` can be two kinds:
327
+ --- - Forwarded from |getcharstr()| verbatim as a result of interactive key press.
328
+ --- Meaning it will be in escaped form and not as a |key-notation|: i.e. `"\r"`
329
+ --- and not `"<CR>"`. It also means that all kinds of combos (`<M-...>`, `<C-S-...>`),
330
+ --- mouse clicks, and wheel scrolls are also forwarded to key handler.
331
+ --- - Any string as part of `opts.init_keys` in |MiniInput.get()|. If a string
332
+ --- doesn't look like it came from |getcharstr()|, it is usually a good idea
333
+ --- to insert this string at caret as is.
334
+ ---
335
+ --- The suggested overall approach for custom key handler is "if `key` is special -
336
+ --- act on it, if can be used in the input - insert at caret, ignore otherwise".
337
+ --- It is also important to never change the current mode (as in |vim-modes|)
338
+ --- for |MiniInput.get()| to work as expected.
339
+ ---
340
+ --- Here is an example of a basic custom key handler: >lua
341
+ ---
342
+ --- local custom_actions = {
343
+ --- [vim.keycode('<Left>')] = function(state)
344
+ --- state.caret = math.max(state.caret - 1, 1)
345
+ --- end,
346
+ --- [vim.keycode('<Right>')] = function(state)
347
+ --- local input_width = vim.fn.strchars(state.input)
348
+ --- state.caret = math.min(state.caret + 1, input_width + 1)
349
+ --- end,
350
+ --- [vim.keycode('<CR>')] = function(state) state.status = 'accept' end,
351
+ --- [vim.keycode('<Esc>')] = function(state) state.status = 'cancel' end,
352
+ --- }
353
+ ---
354
+ --- local key_handler = function(state, key)
355
+ --- -- No need for special setup or teardown
356
+ --- if key == nil then return end
357
+ ---
358
+ --- -- If key is special - act on it
359
+ --- if custom_actions[key] then return custom_actions[key](state) end
360
+ ---
361
+ --- -- If key is not printable - do nothing
362
+ --- if vim.fn.match(key, '^[[:print:]]\\+$') < 0 then return end
363
+ ---
364
+ --- -- Insert at caret
365
+ --- local caret, input = state.caret, state.input
366
+ --- local before_caret = vim.fn.strcharpart(input, 0, caret - 1)
367
+ --- local after_caret = vim.fn.strcharpart(input, caret - 1)
368
+ --- state.input = before_caret .. key .. after_caret
369
+ --- state.caret = caret + vim.fn.strchars(key)
370
+ ---
371
+ --- -- No need to return anything as `state` is modified in place
372
+ --- end
373
+ ---
374
+ --- require('mini.input').setup({ handlers = { key = key_handler } })
375
+ --- <
376
+ --- ## Highlight ~
377
+ ---
378
+ --- `handlers.highlight` is a handler intended to compute and set highlight info
379
+ --- about the current input. Takes |MiniInput-state| as the only argument and is
380
+ --- expected to modify <highlight> field. Default: |MiniInput.default_highlight()|.
381
+ ---
382
+ --- See |MiniInput.gen_highlight| for built-in highlight handler generators.
383
+ ---
384
+ --- It is usually a good idea to append to a <highlight> if it already exists.
385
+ --- This makes it work more robustly when combining highlights.
386
+ ---
387
+ --- Here is a basic example that highlights all letters `a`: >lua
388
+ ---
389
+ --- local hl_handler = function(state)
390
+ --- local highlight = {}
391
+ --- for col in string.gmatch(state.input, '()a') do
392
+ --- -- NOTE: range should use character (not byte) indexes
393
+ --- local char_col = vim.fn.charidx(state.input, col)
394
+ --- local range = { from = char_col, to = char_col, hl = 'Special' }
395
+ --- table.insert(highlight, range)
396
+ --- end
397
+ ---
398
+ --- state.highlight = vim.list_extend(state.highlight or {}, highlight)
399
+ ---
400
+ --- -- Possibly also apply default handler afterwards
401
+ --- return MiniInput.default_highlight(state)
402
+ --- end
403
+ ---
404
+ --- require('mini.input').setup({ handlers = { highlight = hl_handler } })
405
+ --- <
406
+ --- ## View ~
407
+ ---
408
+ --- `handlers.view` is a handler intended to show the input state on screen.
409
+ --- Takes |MiniInput-state| as the only argument. Default: |MiniInput.default_view()|.
410
+ ---
411
+ --- See |MiniInput.gen_view| for built-in view handler generators.
412
+ ---
413
+ --- Example of view that uses |nvim_echo()| to show the input: >lua
414
+ ---
415
+ --- local view_handler = function(state)
416
+ --- -- Process start and end of the input lifecycle
417
+ --- local is_start = state.status == 'start'
418
+ --- local is_end = state.status == 'accept' or state.status == 'cancel'
419
+ --- if is_start or is_end then vim.cmd('mode') end
420
+ --- if is_end then return end
421
+ ---
422
+ --- -- Compute text-hl chunks that fit and show them
423
+ --- local chunks = MiniInput.state_to_chunks(state, vim.v.echospace)
424
+ --- vim.api.nvim_echo(chunks, false, {})
425
+ --- end
426
+ ---
427
+ --- require('mini.input').setup({ handlers = { view = view_handler } })
428
+ --- <
429
+ --- # Scope ~
430
+ ---
431
+ --- `config.scope` is a string that defines an input scope. It is meant as an extra
432
+ --- information for handlers to tweak their behavior (`view` style, etc.). Possible
433
+ --- values: `"cursor"`, `"line"`, `"buffer"`, `"window"`, `"tabpage"`, `"editor"`, `"project"`.
434
+ MiniInput.config = {
435
+ -- Functions that control input lifecycle
436
+ handlers = {
437
+ -- Compute completion candidates
438
+ complete = nil,
439
+
440
+ -- Compute highlighting of current input
441
+ highlight = nil,
442
+
443
+ -- Handle input start, every key press, and input end
444
+ key = nil,
445
+
446
+ -- Show current input state
447
+ view = nil,
448
+ },
449
+
450
+ -- Default input scope: cursor/line/buffer/window/tabpage/editor/project
451
+ scope = 'editor',
452
+ }
453
+ --minidoc_afterlines_end
454
+
455
+ --- Get input from the user
456
+ ---
457
+ --- # Lifecycle ~
458
+ --- *MiniInput-lifecycle*
459
+ ---
460
+ --- This module implements custom lifecycle to interact with the user. It starts
461
+ --- when calling |MiniInput.get()| and ends when a value is returned.
462
+ --- Only one active input is allowed simultaneously.
463
+ ---
464
+ --- The basic cycle unit is a step that processes a `key` (string or `nil`). It is
465
+ --- done by calling relevant handlers in order: key handler with `key` as a second
466
+ --- argument, highlight handler, view handler. |MiniInput.apply_handler()| is used
467
+ --- to apply each handler and the output of one is used as the input for the next.
468
+ ---
469
+ --- During a step some state fields are automatically removed:
470
+ --- - <highlight> is removed before applying a key handler to always have the most
471
+ --- up to date highlighting.
472
+ --- - <complete> is removed if it was not changed after applying a key handler but
473
+ --- something else in the state besides <highlight> did change. This is meant as
474
+ --- an automatic stop of completion when it is not advancing.
475
+ ---
476
+ --- If a step sets an ending state <status> (i.e. `"accept"` or `"cancel"`), the input
477
+ --- is finished by extra finishing step (see below).
478
+ ---
479
+ --- The order of operations is as follows:
480
+ --- - Create initial |MiniInput-state| based on the input `opts`, with defaults
481
+ --- inferred from |MiniInput.config|, and `vim.b.miniinput_config`.
482
+ --- - Set <status> to `"start"`.
483
+ --- - Advance one step with `key=nil`. This is meant as a "setup" step for handlers.
484
+ --- - Set <status> to `"progress"`.
485
+ --- - Process `opts.init_keys` one item per step with `key` set to the string item.
486
+ --- - Wait for user to press a key (via |getcharstr()|). The key string (in escaped
487
+ --- form and not as a |key-notation|; i.e. `"\r"` and not `"<CR>"`) is then used
488
+ --- to advance a step. Note: <C-c> is hard coded to cancel the input.
489
+ --- - Repeat previous step until the ending <status> (`"accept"` or `"cancel"`).
490
+ --- - Finish the input:
491
+ --- - Perform a "teardown" step with `key=nil`.
492
+ --- - If <errmsg> is set, throw an |error()|.
493
+ --- - If input is accepted (even if empty) and not hidden, add <input> to
494
+ --- the history. Get the whole history with |MiniInput.get_history()|.
495
+ --- - Return <input> if <status> is `"accept"`, `nil` otherwise.
496
+ ---
497
+ ---@param opts table|nil Options. Possible fields:
498
+ --- - <completion> `(string)` - completion method. Default: `''` to use default
499
+ --- completion method of the `complete` handler.
500
+ --- - <handlers> `(table)` - same as in |MiniInput.config.handlers|, used only for
501
+ --- the duration of the current input.
502
+ --- - <hide> `(boolean)` - whether input should be hidden. Default: `false`.
503
+ --- Note: this does not guarantee a total security of the input, only that
504
+ --- the typed characters are expected to not be shown on screen and not added
505
+ --- to the history. If set:
506
+ --- - The `view` handler is expected to not directly show current input.
507
+ --- Like replace characters with pre-defined string or fully not show.
508
+ --- - The `complete` and `highlight` handlers are not called.
509
+ --- - Accepted input will not be added to the history.
510
+ --- - <init_keys> `(table)` - array of string keys that are emulated before asking
511
+ --- for the user input. Using values that can be an output of |getcharstr()|
512
+ --- should be preferred, but a key handler should work with any string.
513
+ --- Default: `{}`.
514
+ --- - <prompt> `(string)` - intention of the input, same as in |input()|.
515
+ --- Default: `"Input"`.
516
+ --- - <scope> `(string)` - same as in |MiniInput.config|. Default: the value from
517
+ --- `MiniInput.config` with some hard coded exceptions (on Neovim<0.12.3):
518
+ --- - |vim.lsp.buf.rename()| will use `"cursor"` if no `new_name` is supplied.
519
+ ---
520
+ ---@return string|nil User input (from the <input> state field) if accepted, even if
521
+ --- empty. `nil` if canceled or there was an active input.
522
+ ---
523
+ ---@usage >lua
524
+ --- local input = MiniInput.get({
525
+ --- -- Intention of the input
526
+ --- prompt = 'New value',
527
+ --- -- The input is for something at cursor
528
+ --- scope = 'cursor',
529
+ --- -- Emulate pressing `a`, `<BS>`, and `b`
530
+ --- init_keys = { 'a', vim.keycode('<BS>'), 'b' },
531
+ --- })
532
+ --- <
533
+ MiniInput.get = function(opts)
534
+ H.check_type('opts', opts, 'table', true)
535
+ opts = opts or {}
536
+ opts.scope = opts.scope or (_G.MiniInput or {})._temp_default_scope
537
+
538
+ -- Only allow one input at a time
539
+ if H.state ~= nil then return nil end
540
+
541
+ local init_state = H.state_new(H.get_config(opts))
542
+ H.state_validate(init_state)
543
+ H.state = init_state
544
+
545
+ H.handle_step(nil)
546
+ if H.state_is_end() then return H.state_finish() end
547
+ H.state.status = 'progress'
548
+
549
+ for _, k in ipairs(H.state.opts.init_keys) do
550
+ H.handle_step(k, true)
551
+ if H.state_is_end() then return H.state_finish() end
552
+ end
553
+ H.redraw()
554
+
555
+ local lmap = H.get_lmap()
556
+ for _ = 1, 1000000 do
557
+ local key = H.getcharstr(lmap)
558
+ if key == nil and not H.state_is_end() then H.state.status = 'cancel' end
559
+ if H.state_is_end() then break end
560
+ H.handle_step(key)
561
+ if H.state_is_end() then break end
562
+ end
563
+
564
+ return H.state_finish()
565
+ end
566
+
567
+ --- A |vim.ui.input()| implementation
568
+ ---
569
+ --- Function which can be used to directly override |vim.ui.input()| to use this
570
+ --- module functionality. Set automatically in |MiniInput.setup()|.
571
+ ---
572
+ ---@usage To preserve original `vim.ui.input()`: >lua
573
+ ---
574
+ --- local ui_input_orig = vim.ui.input
575
+ --- require('mini.input').setup()
576
+ --- vim.ui.input = ui_input_orig
577
+ --- <
578
+ MiniInput.ui_input = function(opts, on_confirm)
579
+ opts = opts or {}
580
+ local input_opts = { completion = opts.completion, prompt = opts.prompt, scope = opts.scope }
581
+ input_opts.handlers = { highlight = H.make_ui_select_hl_fun(opts.highlight) }
582
+ input_opts.init_keys = { opts.default }
583
+
584
+ on_confirm(MiniInput.get(input_opts))
585
+ end
586
+
587
+ --- Get current input state
588
+ ---
589
+ ---@return table|nil Current |MiniInput-state| if input is active, `nil` otherwise. Notes:
590
+ --- - For hidden input (`state.opts.hide=true`), both <caret> and <input> are `nil`
591
+ --- to actually hide the input.
592
+ MiniInput.get_state = function()
593
+ if H.state == nil then return nil end
594
+ local res = H.copy_tables(H.state)
595
+ if res.opts.hide then
596
+ res.input, res.caret = nil, nil
597
+ end
598
+ return res
599
+ end
600
+
601
+ --- Get input history
602
+ ---
603
+ ---@return table Array with data about all previous non-hidden inputs (from earliest
604
+ --- to latest). Each element is a table with the following fields:
605
+ --- - <cwd> `(string)` - |current-directory| at the time of input's end.
606
+ --- - <input> `(string)` - input result.
607
+ --- - <prompt> `(string)` - `opts.prompt` supplied in |MiniInput.get()|.
608
+ --- - <scope> `(string)` - `opts.scope` supplied in |MiniInput.get()|.
609
+ MiniInput.get_history = function() return vim.deepcopy(H.history) end
610
+
611
+ --- Set input history
612
+ ---
613
+ ---@param history table Array describing all previous inputs. Same structure
614
+ --- as |MiniInput.get_history()| output.
615
+ MiniInput.set_history = function(history)
616
+ H.check_array_of('history', history, 'table')
617
+ for i, h in ipairs(history) do
618
+ local item = string.format('history[%d]', i)
619
+ H.check_type(item .. '.cwd', h.cwd, 'string')
620
+ H.check_type(item .. '.input', h.input, 'string')
621
+ H.check_type(item .. '.prompt', h.prompt, 'string')
622
+ H.check_one_of(item .. '.scope', h.scope, H.allowed_scopes)
623
+ end
624
+
625
+ H.history = vim.deepcopy(history)
626
+ end
627
+
628
+ --- Refresh active input
629
+ ---
630
+ --- Performs one step of |MiniInput-lifecycle| with `key=nil`.
631
+ MiniInput.refresh = function()
632
+ if H.state == nil then return end
633
+ H.handle_step(nil)
634
+ if H.state_is_end() then H.state_finish() end
635
+ vim.schedule(H.redraw)
636
+ end
637
+
638
+ --- Highlight generators
639
+ ---
640
+ --- This is a table with function elements. Call to actually get a view function.
641
+ MiniInput.gen_highlight = {}
642
+
643
+ --- Highlight with tree-sitter
644
+ ---
645
+ ---@param lang string A language of tree-sitter parser to use.
646
+ ---
647
+ ---@return function A highlight handler. Seem |MiniInput.config.handlers|.
648
+ MiniInput.gen_highlight.treesitter = function(lang)
649
+ local append_ts_hl_range = function(arr, line, tstree, tree)
650
+ local query = tstree and vim.treesitter.query.get(tree:lang(), 'highlights')
651
+ if query == nil then return end
652
+ for capture, node in query:iter_captures(tstree:root(), line) do
653
+ -- Ignore private captures
654
+ if query.captures[capture]:sub(1, 1) ~= '_' then
655
+ local _, from, _, to = node:range()
656
+ local hl = string.format('@%s.%s', query.captures[capture], query.lang)
657
+ from, to = vim.fn.charidx(line, from) + 1, vim.fn.charidx(line, to)
658
+ if from <= to then table.insert(arr, { from = from, to = to, hl = hl }) end
659
+ end
660
+ end
661
+ end
662
+
663
+ return function(state)
664
+ local line = state.input
665
+ local ok, parser = pcall(vim.treesitter.get_string_parser, line, lang)
666
+ if not ok or parser == nil then return end
667
+
668
+ -- Traverse all trees and compute highlight ranges
669
+ parser:parse(true)
670
+ local highlight = {}
671
+ parser:for_each_tree(function(tstree, tree) append_ts_hl_range(highlight, line, tstree, tree) end)
672
+
673
+ -- Respect maybe already present <highlight>
674
+ state.highlight = vim.list_extend(state.highlight or {}, highlight)
675
+ end
676
+ end
677
+
678
+ --- View generators
679
+ ---
680
+ --- This is a table with function elements. Call to actually get a view function.
681
+ ---
682
+ --- Each element accepts <style> option which fine tunes the input view.
683
+ --- With default key handler (|MiniInput.default_key()|) it can be adjusted
684
+ --- interactively by pressing <C-s>.
685
+ ---
686
+ --- Each element also accepts <to_chunks> option. It is a function that takes
687
+ --- a |MiniInput-state| and `max_width` arguments and returns an array of `{ text, hl }`
688
+ --- chunks that fit into `max_width` display width (as in |strdisplaywidth()|).
689
+ ---
690
+ --- This is a way to adjust how input is shown. Like caret/hide symbols, etc.
691
+ --- By default uses |MiniInput.state_to_chunks()|, which also means:
692
+ --- - All control characters (like literal `\t`, `\n`, etc.) will be translated
693
+ --- via |keytrans()|.
694
+ ---
695
+ --- Example: >lua
696
+ ---
697
+ --- local input = require('mini.input')
698
+ ---
699
+ --- -- Adjust how state is converted to text-hl chunks
700
+ --- local to_chunks_opts = {
701
+ --- symbol_caret = '_',
702
+ --- symbol_hide = '*',
703
+ --- -- Do not include prompt and hint in `floatwin` handler
704
+ --- include_prompt = false,
705
+ --- include_hint = false,
706
+ --- }
707
+ --- local to_chunks = function(state, max_width)
708
+ --- return MiniInput.state_to_chunks(state, max_width, to_chunks_opts)
709
+ --- end
710
+ ---
711
+ --- -- Supply custom `to_chunks` as an option
712
+ --- local view_opts = { to_chunks = to_chunks }
713
+ --- input.setup({ handlers = { view = input.gen_view.floatwin(view_opts) } })
714
+ --- <
715
+ MiniInput.gen_view = {}
716
+
717
+ --- Floating window view
718
+ ---
719
+ --- Show input inside a floating window. Prompt and completion hints are shown
720
+ --- in title and footer.
721
+ ---
722
+ --- Window position and dimensions are computed based on state's <scope> and
723
+ --- `opts.style`:
724
+ --- - `scope="cursor"` is shown near the cursor. For example, `style="BL"` will
725
+ --- have bottom left corner at nearest top right cell relative to the cursor.
726
+ --- - `scope="line"` is shown near the current line. For example, `style="BL"` will
727
+ --- have bottom left corner at left side above the line.
728
+ --- - `scope="buffer"` and `scope="window"` will be shown relative to the current
729
+ --- window. For example, `style="BL"` will be shown in the bottom left corner.
730
+ --- - `scope="tabpage"`, `scope="editor"`, `scope="project"` will be shown relative to
731
+ --- the overall Neovim instance. For example, `style="BL"` will be shown in the
732
+ --- bottom left corner.
733
+ ---
734
+ --- Identifiers of floating window and its buffer are stored as `floatwin_win_id`
735
+ --- and `floatwin_buf_id` in |MiniInput-state| <data> field. Floating window is
736
+ --- not focused and cursor position is not the same as caret position.
737
+ ---
738
+ ---@param opts table|nil Options. Possible fields:
739
+ --- - <adjust_config> `(function)` - function to adjust default config. Will be
740
+ --- called with two arguments: current |MiniInput-state| and a window config
741
+ --- (always with `relative="editor"` and `anchor="NW"`) computed based
742
+ --- on `opts.style`. Should return an adjusted window config.
743
+ --- Default: `function(state, config) return config end`.
744
+ ---
745
+ --- - <style> `(string)` - a two character description of how to show the window.
746
+ --- Default: `"BL"`.
747
+ ---
748
+ --- First character describes vertical position:
749
+ --- - `"T"` - top window border will be at scope's reference top border.
750
+ --- - `"M"` - middle between top and bottom window borders will be at the
751
+ --- middle of scope's reference top and bottom borders.
752
+ --- - `"B"` - bottom window border will be at scope's reference bottom border.
753
+ ---
754
+ --- Second character describes horizontal position:
755
+ --- - `"L"` - left window border will be at scope's reference left border.
756
+ --- - `"M"` - middle between left and right window borders will be at the
757
+ --- middle of scope's reference left and right borders.
758
+ --- - `"R"` - right window border will be at scope's reference right border.
759
+ ---
760
+ --- __input_to_chunks
761
+ ---
762
+ ---@usage >lua
763
+ --- local input = require('mini.input')
764
+ ---
765
+ --- -- Use border different from 'winborder'
766
+ --- local adjust_config = function(_, config)
767
+ --- config.border = 'double'
768
+ --- return config
769
+ --- end
770
+ ---
771
+ --- -- Choose initial style based on the scope
772
+ --- local floatwin = input.gen_view.floatwin
773
+ --- local view_tm = floatwin({ style = 'TM', adjust_config = adjust_config })
774
+ --- local view_bl = floatwin({ style = 'BL', adjust_config = adjust_config })
775
+ --- local view_handler = function(state)
776
+ --- local scope, view = state.opts.scope, view_tm
777
+ --- if scope == 'cursor' or scope == 'line' then view = view_bl end
778
+ --- return view(state)
779
+ --- end
780
+ ---
781
+ --- input.setup({ handlers = { view = view_handler } })
782
+ --- <
783
+ MiniInput.gen_view.floatwin = function(opts)
784
+ local default_opts = { style = 'BL' }
785
+ default_opts.adjust_config = function(_, config) return config end
786
+ local default_to_chunks_opts = { include_prompt = false, include_hint = false }
787
+ default_opts.to_chunks = function(state, max_width)
788
+ return MiniInput.state_to_chunks(state, max_width, default_to_chunks_opts)
789
+ end
790
+ opts = vim.tbl_extend('force', default_opts, opts or {})
791
+ H.check_type('opts.adjust_config', opts.adjust_config, 'callable')
792
+ H.check_one_of('opts.style', opts.style, { 'TL', 'TM', 'TR', 'ML', 'MM', 'MR', 'BL', 'BM', 'BR' })
793
+ H.check_type('opts.to_chunks', opts.to_chunks, 'callable')
794
+
795
+ -- Change style in vertical-horizontal order
796
+ local all_styles = { 'BL', 'ML', 'TL', 'BM', 'MM', 'TM', 'BR', 'MR', 'TR' }
797
+
798
+ return function(state)
799
+ if H.state_is_end(state) then
800
+ pcall(vim.api.nvim_win_close, state.data.floatwin_win_id, true)
801
+ pcall(vim.api.nvim_buf_delete, state.data.floatwin_buf_id, { force = true })
802
+ return
803
+ end
804
+ local style, _ = H.handle_view_style(state, opts.style, all_styles)
805
+
806
+ -- Try to fit all chunks first, but later still truncate to window width
807
+ local winborder = vim.fn.exists('+winborder') == 0 and '' or vim.o.winborder
808
+ default_to_chunks_opts = { include_prompt = winborder == 'none', include_hint = winborder == 'none' }
809
+ local chunks = H.get_chunks(opts, state)
810
+ local default_config = H.default_floatwin_config(state, style, H.get_chunks_displaywidth(chunks))
811
+ local config = opts.adjust_config(state, default_config)
812
+ chunks = H.get_chunks(opts, state, config.width)
813
+
814
+ H.ensure_floatwin_buf(state, chunks)
815
+ H.ensure_floatwin_win(state, config)
816
+ end
817
+ end
818
+
819
+ --- UI line (statusline, tabline, winbar) view
820
+ ---
821
+ ---@param opts table|nil Options. Possible fields:
822
+ --- - <style> `(string)` - which UI line to use. One of `"statusline"`,
823
+ --- `"tabline"`, `"winbar"`. Default: `"statusline"`.
824
+ ---
825
+ --- __input_to_chunks
826
+ ---
827
+ ---@usage >lua
828
+ --- local input = require('mini.input')
829
+ ---
830
+ --- -- Choose initial style based on the scope
831
+ --- local view_tabline = input.gen_view.uiline({ style = 'tabline' })
832
+ --- local view_winbar = input.gen_view.uiline({ style = 'winbar' })
833
+ --- local view_handler = function(state)
834
+ --- local scope, view = state.opts.scope, view_winbar
835
+ --- if scope == 'tabpage' or scope == 'editor' or scope == 'project' then
836
+ --- view = view_tabline
837
+ --- end
838
+ --- return view(state)
839
+ --- end
840
+ ---
841
+ --- input.setup({ handlers = { view = view_handler } })
842
+ --- <
843
+ MiniInput.gen_view.uiline = function(opts)
844
+ opts = vim.tbl_extend('force', { style = 'statusline', to_chunks = MiniInput.state_to_chunks }, opts or {})
845
+ H.check_one_of('opts.style', opts.style, { 'statusline', 'tabline', 'winbar' })
846
+ H.check_type('opts.to_chunks', opts.to_chunks, 'callable')
847
+
848
+ local all_styles = { 'statusline', 'winbar', 'tabline' }
849
+ local escape_stl = function(x) return (x:gsub('%%', '%%%%')) end
850
+
851
+ return function(state)
852
+ local style, style_is_new = H.handle_view_style(state, opts.style, all_styles)
853
+ H.uiline_handle_option_values(state, style_is_new)
854
+ if H.state_is_end(state) then return end
855
+
856
+ local max_width = style == 'tabline' and vim.o.columns or vim.fn.winwidth(0)
857
+ local chunks = H.get_chunks(opts, state, max_width)
858
+ local parts = vim.tbl_map(function(c) return '%#' .. escape_stl(c[2]) .. '#' .. escape_stl(c[1]) end, chunks)
859
+ local uiline_value = table.concat(parts) .. '%#MiniInputNormal#'
860
+
861
+ local opt = style == 'tabline' and vim.o or vim.wo
862
+ opt[style] = uiline_value
863
+ if style == 'statusline' and vim.o.laststatus < 2 then vim.o.laststatus = 2 end
864
+ if style == 'tabline' and vim.o.showtabline < 2 then vim.o.showtabline = 2 end
865
+ end
866
+ end
867
+
868
+ --- Virtual (line, text) view
869
+ ---
870
+ ---@param opts table|nil Options. Possible fields:
871
+ --- - <style> `(string)` - how to display virtual text. One of `"above"`, `"below"`,
872
+ --- `"inline"`. Default: `"above"`.
873
+ ---
874
+ --- __input_to_chunks
875
+ ---
876
+ ---@usage >lua
877
+ --- -- Choose different initial style
878
+ --- local input = require('mini.input')
879
+ --- local view_handler = input.gen_view.virtual({ style = 'inline' })
880
+ --- input.setup({ handlers = { view = view_handler } })
881
+ --- <
882
+ MiniInput.gen_view.virtual = function(opts)
883
+ opts = vim.tbl_extend('force', { style = 'above', to_chunks = MiniInput.state_to_chunks }, opts or {})
884
+
885
+ H.check_one_of('opts.style', opts.style, { 'above', 'below', 'inline' })
886
+ H.check_type('opts.to_chunks', opts.to_chunks, 'callable')
887
+
888
+ local ns_id = H.ns_id.view
889
+ local all_styles = { 'above', 'below', 'inline' }
890
+
891
+ return function(state)
892
+ -- Cleanup
893
+ local buf_id, extmark_id = vim.api.nvim_get_current_buf(), state.data.extmark_id
894
+ if H.state_is_end(state) then
895
+ pcall(vim.api.nvim_buf_del_extmark, state.data.buf_id, ns_id, extmark_id)
896
+ return
897
+ end
898
+ local ok_extmark = pcall(vim.api.nvim_get_extmark_by_id, state.data.buf_id, ns_id, extmark_id, {})
899
+ if not (buf_id == state.data.buf_id and ok_extmark) then
900
+ pcall(vim.api.nvim_buf_del_extmark, state.data.buf_id, ns_id, extmark_id)
901
+ end
902
+ state.data.buf_id = buf_id
903
+
904
+ local style, style_is_new = H.handle_view_style(state, opts.style, all_styles)
905
+ local is_virtline = style == 'above' or style == 'below'
906
+
907
+ -- Get chunks
908
+ local win_info = H.get_curwin_info()
909
+ local max_width = win_info.width - win_info.textoff
910
+ local chunks = H.get_chunks(opts, state, max_width)
911
+ local chunks_width = H.get_chunks_displaywidth(chunks)
912
+ if is_virtline and chunks_width < max_width then
913
+ chunks = vim.deepcopy(chunks)
914
+ table.insert(chunks, { string.rep(' ', max_width - chunks_width), 'MiniInputNormal' })
915
+ end
916
+
917
+ -- Set
918
+ local extmark_opts = { id = state.data.extmark_id, priority = 4096 }
919
+ if is_virtline then
920
+ extmark_opts.virt_lines = { chunks }
921
+ extmark_opts.virt_lines_above = style == 'above'
922
+ else
923
+ extmark_opts.virt_text = chunks
924
+ extmark_opts.virt_text_pos = 'inline'
925
+ end
926
+ local cur_pos = vim.api.nvim_win_get_cursor(0)
927
+ state.data.extmark_id = vim.api.nvim_buf_set_extmark(0, ns_id, cur_pos[1] - 1, cur_pos[2], extmark_opts)
928
+
929
+ -- Ensure that both current and virtual lines are visible
930
+ if is_virtline and (state.status == 'start' or style_is_new) then
931
+ local win_line, win_height = vim.fn.winline(), vim.fn.winheight(0)
932
+ if win_line == 1 and style == 'above' then vim.cmd('normal! \25') end
933
+ -- With 'above' the cursor line is moved down, with 'below' - no move
934
+ -- local is_below_screen = style == 'above' and win_line > win_height or win_line >= win_height
935
+ local is_below_screen = (win_line - (style == 'above' and 1 or 0)) >= win_height
936
+ if is_below_screen then vim.cmd('normal! \5') end
937
+ end
938
+ end
939
+ end
940
+
941
+ --- Default key handler
942
+ ---
943
+ --- Emulates most of |Command-line-mode| editing (|cmdline-editing|):
944
+ ---
945
+ --- - Accept: <CR>. To insert literal newline, type `<C-j>`.
946
+ ---
947
+ --- - Cancel: <Esc> or <C-c>.
948
+ ---
949
+ --- - Move caret:
950
+ --- - <Left>, <Right> - one character to left / right.
951
+ --- - <M-h>, <M-l> - one character to left / right.
952
+ --- - <S-Left>, <S-Right> - one word to left / right.
953
+ --- - <C-b>, <C-e> (if no completion) - to start / end of input.
954
+ --- - <Home>, <End> - to start / end of input.
955
+ ---
956
+ --- - Delete:
957
+ --- - <BS> / <C-h> - to caret's left. If `opts.autopair` is enabled, also delete
958
+ --- a character to caret's right if it formed a respected character pair.
959
+ --- See "Autopair".
960
+ --- - <Del> - at caret.
961
+ --- - <C-u> - from start to caret. As |c_CTRL-U|.
962
+ --- - <C-w> - contiguous keyword or non-keyword to caret's left. As |c_CTRL-W|.
963
+ ---
964
+ --- - Insert at caret:
965
+ --- - <C-k> - digraph based on the next two pressed keys. As |c_CTRL-K|.
966
+ --- - <C-r> - content of a register. As |c_CTRL-R| including support for
967
+ --- special <C-a>, <C-f>, <C-l>, <C-w> keys for a register.
968
+ --- - <C-v>, <C-q> - next key literally. As |c_CTRL-V| and |i_CTRL-V_digit|
969
+ --- (all digits must be typed in full or stopped with <C-c>).
970
+ --- - Pasting from system |clipboard| is supported for "non-streaming" paste (as
971
+ --- described in |vim.paste()|).
972
+ ---
973
+ --- - Autopair (if `opts.autopair` is set) is similar to |mini.pairs|:
974
+ --- - Opening characters `(`, `[`, `{` always insert a `()`, `[]`, `{}` pair and places
975
+ --- caret inside of it.
976
+ --- - Closing characters `)`, `]`, `}` move caret to the right if there is the same
977
+ --- character to the right.
978
+ --- - Closeopen characters single+double quotes and backtick perform "close"
979
+ --- action if possible and "open" action if not.
980
+ --- - In all cases press <C-v> before special character to insert it verbatim.
981
+ ---
982
+ --- - Completion:
983
+ --- - <Tab>, <S-Tab> - start with `state.opts.completion` method if not active
984
+ --- and advance through active completion (i.e. replace currently displayed
985
+ --- at caret item with the next one).
986
+ --- Note: type `<C-v><Tab>` to insert literal `\t`.
987
+ --- - <C-n>, <C-p>, <Up>, <Down> - start with `"history"` method if not active
988
+ --- and advance through active completion.
989
+ --- - <C-e> - cancel and return to initial input and caret.
990
+ --- - <C-y> - accept current candidate. Note: it will also be accepted after
991
+ --- any key that doesn't advance completion.
992
+ ---
993
+ --- - Miscellaneous:
994
+ --- - <C-o> - change scope of the input. Cycles through all available ones.
995
+ --- - <C-s> - change view style. Works only with |MiniInput.gen_view| view
996
+ --- handlers. Cycles through all available ones.
997
+ --- - <C-x> - toggle hide/unhide of the input.
998
+ ---
999
+ --- - Special keys (combo, mouse, but not whitespace) not listed above - ignored.
1000
+ --- Anything else (even more than a single character) - inserted at caret.
1001
+ ---
1002
+ ---@param state table Current |MiniInput-state|.
1003
+ ---@param key string|nil A key to process. Should be escaped (`"\r"`, not `"<CR>"`).
1004
+ --- See |vim.keycode()|.
1005
+ ---@param opts table|nil Options. Possible fields:
1006
+ --- - <autopair> `(boolean)` - whether perform add autopair. Default: `false`.
1007
+ MiniInput.default_key = function(state, key, opts)
1008
+ opts = opts or {}
1009
+ if key == nil or H.state_is_end(state) then return end
1010
+ if opts.autopair and H.key_methods_autopair[key] ~= nil then return H.key_methods_autopair[key](state) end
1011
+ if H.key_methods[key] ~= nil then return H.key_methods[key](state, opts) end
1012
+ if H.is_special_char(key) then return end
1013
+ H.insert_at_caret(state, key)
1014
+ end
1015
+
1016
+ --- Default highlight handler
1017
+ ---
1018
+ --- During active completion highlights added (computed via |matchfuzzy()|)
1019
+ --- characters with `MiniInputAdded` group.
1020
+ ---
1021
+ ---@param state table Current |MiniInput-state|.
1022
+ MiniInput.default_highlight = function(state)
1023
+ if H.state_is_end(state) then return end
1024
+
1025
+ -- Only show characters added during completion navigation
1026
+ if state.complete == nil or state.complete.id == 0 then return end
1027
+
1028
+ local cur_item = state.complete.items[state.complete.id]
1029
+ local base = state.complete.base
1030
+ local pos = base == '' and {} or vim.fn.matchfuzzypos({ cur_item }, base, { matchseq = 1 })[2][1]
1031
+ if pos == nil then return end
1032
+
1033
+ -- Matches are increasing character zero-based indexes. Highlight ranges
1034
+ -- between them, including start and end edges.
1035
+ local cur_item_width = vim.fn.strchars(cur_item)
1036
+ table.insert(pos, 1, -1)
1037
+ table.insert(pos, cur_item_width)
1038
+ local offset, ranges = state.caret - cur_item_width - 1, {}
1039
+ for i = 2, #pos do
1040
+ if pos[i] - pos[i - 1] > 1 then
1041
+ local from = offset + pos[i - 1] + 1
1042
+ local to = offset + pos[i] - 1
1043
+ table.insert(ranges, { from = from + 1, to = to + 1, hl = 'MiniInputAdded' })
1044
+ end
1045
+ end
1046
+
1047
+ -- Respect maybe already present <highlight>
1048
+ state.highlight = vim.list_extend(state.highlight or {}, ranges)
1049
+ end
1050
+
1051
+ --- Default view handler
1052
+ ---
1053
+ --- Same as |MiniInput.gen_view.floatwin()| with default options.
1054
+ ---
1055
+ ---@param state table Current |MiniInput-state|.
1056
+ MiniInput.default_view = function(state) end -- Generated later
1057
+
1058
+ --- Default complete handler
1059
+ ---
1060
+ --- Supported methods:
1061
+ --- - `""` (default) - complete with buffer keywords (|'iskeyword'|) that match
1062
+ --- a keyword at caret's left.
1063
+ --- - `"history"` - complete with |MiniInput.get_history()| inputs that have current
1064
+ --- input to caret's left as a prefix. If `opts.precise_history`, only use history
1065
+ --- entries that have all info fields same as the current state. Orders from
1066
+ --- earliest to latest. To initiate with the latest history match, press
1067
+ --- <Up> or <C-p> with default key handler.
1068
+ --- - `"cmdline"` - treat input as |Command-line| text and show completions at caret.
1069
+ --- Note: as there is no |getcmdcomplpat()| variant to use outside of Command-line
1070
+ --- mode, the base is inferred from |getcompletion()| output as the widest text to
1071
+ --- caret's left that matches all candidates: fuzzily if |'wildoptions'| contains
1072
+ --- `fuzzy`, as a prefix otherwise. If none - empty string is used. This approach
1073
+ --- has limitations, but it is good enough.
1074
+ --- - Every method supported by |getcompletion()|. Base is computed as the keyword
1075
+ --- at caret's left.
1076
+ ---
1077
+ ---@param state table Current |MiniInput-state|.
1078
+ ---@param method string Completion method.
1079
+ ---@param opts table|nil Options. Possible fields:
1080
+ --- - <precise_history> `(boolean)` - whether for `method='history'` try to match
1081
+ --- only entries that have <cwd> as |current-directory|, same <scope> and
1082
+ --- <prompt> as in current state. Default: `true`.
1083
+ ---
1084
+ ---@usage >lua
1085
+ --- -- Do not match history precisely
1086
+ --- local complete_opts = { precise_history = false }
1087
+ --- local complete_handler = function(state, method)
1088
+ --- return MiniInput.default_complete(state, method, complete_opts)
1089
+ --- end
1090
+ --- require('mini.input').setup({ handlers = { complete = complete_handler } })
1091
+ --- <
1092
+ MiniInput.default_complete = function(state, method, opts)
1093
+ opts = vim.tbl_extend('force', { precise_history = true }, opts or {})
1094
+
1095
+ if H.state_is_end(state) then return end
1096
+ if method == 'history' then return H.complete_history(state, opts.precise_history) end
1097
+ method = method or state.opts.completion or ''
1098
+
1099
+ local caret, input = state.caret, state.input
1100
+ local text = vim.fn.strcharpart(input, 0, caret - 1)
1101
+ local is_fs_method = method == 'file' or method == 'file_in_path' or method == 'dir' or method == 'dir_in_path'
1102
+ local base_pattern = is_fs_method and '[[:fname:]]*$' or '[[:keyword:]]*$'
1103
+ local base = vim.fn.strcharpart(text, vim.fn.match(text, base_pattern))
1104
+
1105
+ if method == '' then
1106
+ state.complete = { items = H.complete_buf_words(base), base = base }
1107
+ return
1108
+ end
1109
+
1110
+ local pat = method == 'cmdline' and vim.fn.strcharpart(input, 0, caret - 1) or base
1111
+ local ok, items = pcall(vim.fn.getcompletion, pat, method)
1112
+ if not ok then return end
1113
+ base = method == 'cmdline' and H.get_base_from_items(text, items) or base
1114
+ state.complete = { items = items, base = base }
1115
+ end
1116
+
1117
+ --- Convert state into text-hl chunks
1118
+ ---
1119
+ --- - Treat `state.highlight` elements in increasing priority, i.e. later ones
1120
+ --- are placed "on top" of the previous ones if they overlap.
1121
+ --- - Uses |MiniInput-hl-groups|.
1122
+ --- - Multiline input (i.e. with newline characters `\n`) is shown as a single line.
1123
+ --- - Treats `max_width` as display width (|strdisplaywidth()|).
1124
+ --- - Truncates output chunks trying to center the caret.
1125
+ ---
1126
+ ---@param state table A |MiniInput-state|.
1127
+ ---@param max_width number|nil Maximum allowed display width. Can be |math.huge|.
1128
+ ---@param opts table|nil Options. Possible fields:
1129
+ --- - <keytrans> `(boolean)` - whether to translate control characters (escaped
1130
+ --- version of `<C-...>`). Default: `true`.
1131
+ --- - <include_prompt> `(boolean)` - whether to start chunks with state prompt.
1132
+ --- Default: `true`.
1133
+ --- - <include_hint> `(boolean)` - whether to show complete hint to caret's right.
1134
+ --- Default: `true`.
1135
+ --- - <symbol_caret> `(string)` - string to use for caret. Default: `"▏"`.
1136
+ --- - <symbol_hide> `(string)` - string to use for each input character when
1137
+ --- input is hidden. Default: `"•"`.
1138
+ ---
1139
+ ---@return table An array of text-hl chunks that fit `max_width` display width.
1140
+ MiniInput.state_to_chunks = function(state, max_width, opts)
1141
+ H.check_type('max_width', max_width, 'number', true)
1142
+ local default_opts = { keytrans = true, include_prompt = true, include_hint = true }
1143
+ default_opts.symbol_caret = '▏'
1144
+ default_opts.symbol_hide = '•'
1145
+ opts = vim.tbl_extend('force', default_opts, opts or {})
1146
+ H.check_type('opts.keytrans', opts.keytrans, 'boolean')
1147
+ H.check_type('opts.include_prompt', opts.include_prompt, 'boolean')
1148
+ H.check_type('opts.include_hint', opts.include_hint, 'boolean')
1149
+ H.check_type('opts.symbol_caret', opts.symbol_caret, 'string')
1150
+ H.check_type('opts.symbol_hide', opts.symbol_hide, 'string')
1151
+
1152
+ -- Precompute state data
1153
+ local hide, symbol_hide = state.opts.hide, opts.symbol_hide
1154
+ local input = hide and string.rep(symbol_hide, vim.fn.strchars(state.input)) or state.input
1155
+ local caret = (hide and vim.fn.strchars(symbol_hide) or 1) * (state.caret - 1) + 1
1156
+
1157
+ -- Compute input chunks to left and right of caret
1158
+ local max_to = vim.fn.strchars(input)
1159
+ local hl_ranges = { { from = 1, to = max_to, hl = 'MiniInputNormal' } }
1160
+ for _, range in ipairs(state.highlight or {}) do
1161
+ hl_ranges = H.insert_hl_range(hl_ranges, max_to, range.from, range.to, range.hl)
1162
+ end
1163
+
1164
+ local input_chunks = {}
1165
+ for i, r in ipairs(hl_ranges) do
1166
+ input_chunks[i] = { vim.fn.strcharpart(input, r.from - 1, r.to - r.from + 1), r.hl }
1167
+ end
1168
+ local input_chunks_left, input_chunks_right = H.split_chunks_at(input_chunks, caret - 1)
1169
+
1170
+ -- Compute chunks in specific order:
1171
+ -- prompt - input_left - caret - complete_hint - input_right
1172
+ local chunks, keytrans = {}, opts.keytrans
1173
+ if opts.include_prompt and state.opts.prompt ~= '' then
1174
+ local prompt = vim.trim(state.opts.prompt)
1175
+ local prompt_hl = hide and 'MiniInputHide' or 'MiniInputPrompt'
1176
+ local new = { { prompt, prompt_hl }, { ' ', 'MiniInputNormal' } }
1177
+ H.append_chunks(chunks, new, keytrans)
1178
+ end
1179
+
1180
+ H.append_chunks(chunks, input_chunks_left, keytrans)
1181
+
1182
+ if opts.symbol_caret ~= '' then H.append_chunks(chunks, { { opts.symbol_caret, 'MiniInputCaret' } }, keytrans) end
1183
+ local caret_offset = H.get_chunks_displaywidth(chunks)
1184
+
1185
+ if opts.include_hint and state.complete ~= nil then
1186
+ local hint = string.format('(%d/%d)', state.complete.id, #state.complete.items)
1187
+ H.append_chunks(chunks, { { hint, 'MiniInputHint' } }, keytrans)
1188
+ end
1189
+
1190
+ H.append_chunks(chunks, input_chunks_right, keytrans)
1191
+
1192
+ return H.fit_chunks_to_displaywidth(chunks, max_width, caret_offset)
1193
+ end
1194
+
1195
+ --- Apply state's handler
1196
+ ---
1197
+ --- Given |MiniInput-state|, apply its handler (from `state.opts.handlers`) and
1198
+ --- return the modified state.
1199
+ ---
1200
+ --- Notes:
1201
+ --- - Nothing is done for highlight and complete handlers when input is hidden.
1202
+ --- - Applying complete handler ensures that <complete> state field has <id> set
1203
+ --- to 0 (as it assumes new completion) and <method> to the input argument
1204
+ --- (empty string if there was no argument).
1205
+ --- - Error during handler application is stored as <errmsg> field of output state
1206
+ --- if there is an active input. It is propagated via |error()| otherwise.
1207
+ --- - Handler output is validated to be a valid |MiniInput-state|. If not - return
1208
+ --- input `state`. Note: it still could be changed if handler modified in place.
1209
+ ---
1210
+ ---@param state table A |MiniInput-state|.
1211
+ ---@param name string Valid handler name.
1212
+ ---@param arg any Extra handler argument: like `key` for key handler and `method`
1213
+ --- for complete handler.
1214
+ ---
1215
+ ---@return table A |MiniInput-state| as a result of applied handler. If handler returned
1216
+ --- nothing, the input `state` is returned (assumes it was modified in place).
1217
+ MiniInput.apply_handler = function(state, name, arg)
1218
+ H.check_one_of('name', name, { 'complete', 'highlight', 'key', 'view' })
1219
+ H.check_type('arg', arg, 'string', true)
1220
+ if arg == nil and name == 'complete' then arg = '' end
1221
+
1222
+ if state.opts.hide and (name == 'highlight' or name == 'complete') then return state end
1223
+ local ok_handler, res = pcall(state.opts.handlers[name], state, arg)
1224
+ if not ok_handler then H.cache_error(state, 'Error applying `' .. name .. '` handler: ' .. res) end
1225
+
1226
+ local new_state = (ok_handler and res or nil) or state
1227
+ if name == 'complete' and type(new_state.complete) == 'table' then
1228
+ new_state.complete.id = 0
1229
+ new_state.complete.method = arg
1230
+ end
1231
+
1232
+ local ok_state, msg = pcall(H.state_validate, new_state)
1233
+ if not ok_state then H.cache_error(state, msg) end
1234
+
1235
+ return ok_state and new_state or state
1236
+ end
1237
+
1238
+ -- Helper data ================================================================
1239
+ -- Module default config
1240
+ H.default_config = vim.deepcopy(MiniInput.config)
1241
+
1242
+ -- Current state of user input
1243
+ H.state = nil
1244
+
1245
+ -- History of inputs
1246
+ H.history = {}
1247
+
1248
+ -- Supported scopes
1249
+ H.allowed_scopes = { 'cursor', 'line', 'buffer', 'window', 'tabpage', 'editor', 'project' }
1250
+
1251
+ -- Various cache
1252
+ H.cache = {}
1253
+
1254
+ -- Namespaces
1255
+ H.ns_id = {
1256
+ view = vim.api.nvim_create_namespace('MiniInputView'),
1257
+ }
1258
+
1259
+ -- Helper functionality =======================================================
1260
+ -- Settings -------------------------------------------------------------------
1261
+ H.setup_config = function(config)
1262
+ H.check_type('config', config, 'table', true)
1263
+ config = vim.tbl_deep_extend('force', vim.deepcopy(H.default_config), config or {})
1264
+
1265
+ H.check_type('handlers', config.handlers, 'table')
1266
+ H.check_type('handlers.complete', config.handlers.complete, 'function', true)
1267
+ H.check_type('handlers.highlight', config.handlers.highlight, 'function', true)
1268
+ H.check_type('handlers.key', config.handlers.key, 'function', true)
1269
+ H.check_type('handlers.view', config.handlers.view, 'function', true)
1270
+
1271
+ H.check_type('scope', config.scope, 'string')
1272
+
1273
+ return config
1274
+ end
1275
+
1276
+ H.apply_config = function(config)
1277
+ MiniInput.config = config
1278
+
1279
+ -- Ensure clear history
1280
+ H.history = {}
1281
+
1282
+ -- Ensure default scope for some core functions
1283
+ if vim.fn.has('nvim-0.12.3') == 0 then
1284
+ -- NOTE: Use `vim.schedule` to not load `vim.lsp` during startup
1285
+ vim.schedule(function() vim.lsp.buf.rename = H.mock_lsp_buf_rename(vim.lsp.buf.rename) end)
1286
+ end
1287
+ end
1288
+
1289
+ H.create_autocommands = function()
1290
+ local gr = vim.api.nvim_create_augroup('MiniInput', {})
1291
+
1292
+ local au = function(event, pattern, callback, desc)
1293
+ vim.api.nvim_create_autocmd(event, { group = gr, pattern = pattern, callback = callback, desc = desc })
1294
+ end
1295
+
1296
+ au('VimResized', '*', function() MiniInput.refresh() end, 'Refresh on resize')
1297
+ au('ColorScheme', '*', H.create_default_hl, 'Ensure colors')
1298
+ end
1299
+
1300
+ H.create_default_hl = function()
1301
+ local hi = function(name, opts)
1302
+ opts.default = true
1303
+ vim.api.nvim_set_hl(0, name, opts)
1304
+ end
1305
+
1306
+ hi('MiniInputAdded', { link = 'DiagnosticFloatingOk' })
1307
+ hi('MiniInputBorder', { link = 'FloatBorder' })
1308
+ hi('MiniInputCaret', { link = 'MiniInputPrompt' })
1309
+ hi('MiniInputHide', { link = 'DiagnosticFloatingWarn' })
1310
+ hi('MiniInputHint', { link = 'DiagnosticFloatingHint' })
1311
+ hi('MiniInputNormal', { link = 'NormalFloat' })
1312
+ hi('MiniInputPrompt', { link = 'DiagnosticFloatingInfo' })
1313
+ hi('MiniInputSpecial', { link = 'DiagnosticFloatingWarn' })
1314
+ end
1315
+
1316
+ H.adjust_vim_paste = function()
1317
+ local paste_orig = vim.paste
1318
+ vim.paste = function(lines, phase)
1319
+ if MiniInput.get_state() == nil then return paste_orig(lines, phase) end
1320
+ if phase ~= -1 then return H.notify('There is no streaming paste support. Use `<C-r>+` or `<C-r>*`.', 'WARN') end
1321
+ H.insert_at_caret(H.state, table.concat(lines, '\n'))
1322
+ H.handle_step(nil)
1323
+ end
1324
+ end
1325
+
1326
+ H.get_config = function(config)
1327
+ return vim.tbl_deep_extend('force', MiniInput.config, vim.b.miniinput_config or {}, config or {})
1328
+ end
1329
+
1330
+ -- State ----------------------------------------------------------------------
1331
+ H.state_new = function(opts)
1332
+ opts.completion = opts.completion or ''
1333
+ H.check_type('state.opts.handlers', opts.handlers, 'table')
1334
+ opts.handlers.complete = opts.handlers.complete or MiniInput.default_complete
1335
+ opts.handlers.highlight = opts.handlers.highlight or MiniInput.default_highlight
1336
+ opts.handlers.key = opts.handlers.key or MiniInput.default_key
1337
+ opts.handlers.view = opts.handlers.view or MiniInput.default_view
1338
+ if opts.hide == nil then opts.hide = false end
1339
+ opts.init_keys = opts.init_keys or {}
1340
+ opts.prompt = opts.prompt or 'Input'
1341
+ -- opts.scope should already be precomputed
1342
+
1343
+ return { caret = 1, data = {}, input = '', opts = opts, status = 'start' }
1344
+ end
1345
+
1346
+ H.state_validate = function(x)
1347
+ H.check_type('state', x, 'table')
1348
+
1349
+ H.check_type('state.caret', x.caret, 'number')
1350
+ H.check_type('state.data', x.data, 'table')
1351
+ H.check_type('state.input', x.input, 'string')
1352
+ H.check_one_of('state.status', x.status, { 'start', 'progress', 'accept', 'cancel' })
1353
+
1354
+ if not (1 <= x.caret and x.caret <= (vim.fn.strchars(x.input) + 1)) then
1355
+ H.error('`state.caret` should be between 1 and input width plus 1')
1356
+ end
1357
+
1358
+ if x.complete ~= nil then
1359
+ H.check_type('state.complete', x.complete, 'table')
1360
+ H.check_type('state.complete.base', x.complete.base, 'string')
1361
+ H.check_array_of('state.complete.items', x.complete.items, 'string')
1362
+ H.check_type('state.complete.id', x.complete.id, 'number')
1363
+ H.check_type('state.complete.method', x.complete.method, 'string')
1364
+ end
1365
+
1366
+ if x.highlight ~= nil then
1367
+ H.check_array_of('state.highlight', x.highlight, 'table')
1368
+ for i, h in ipairs(x.highlight) do
1369
+ local item = string.format('state.highlight[%d]', i)
1370
+ H.check_type(item .. '.from', h.from, 'number')
1371
+ H.check_type(item .. '.to', h.to, 'number')
1372
+ H.check_type(item .. '.hl', h.hl, 'string')
1373
+ if h.from > h.to then H.error(string.format('%s.from is bigger than %s.to', item, item)) end
1374
+ end
1375
+ end
1376
+
1377
+ H.check_type('state.opts.completion', x.opts.completion, 'string')
1378
+ H.check_type('state.opts.handlers.complete', x.opts.handlers.complete, 'function')
1379
+ H.check_type('state.opts.handlers.highlight', x.opts.handlers.highlight, 'function')
1380
+ H.check_type('state.opts.handlers.key', x.opts.handlers.key, 'function')
1381
+ H.check_type('state.opts.handlers.view', x.opts.handlers.view, 'function')
1382
+ H.check_type('state.opts.hide', x.opts.hide, 'boolean')
1383
+ H.check_array_of('state.opts.init_keys', x.opts.init_keys, 'string')
1384
+ H.check_type('state.opts.prompt', x.opts.prompt, 'string')
1385
+ H.check_one_of('state.opts.scope', x.opts.scope, H.allowed_scopes)
1386
+ end
1387
+
1388
+ H.state_finish = function()
1389
+ if H.cache.is_in_getcharstr then return vim.api.nvim_feedkeys('\3', 't', true) end
1390
+
1391
+ H.handle_step(nil)
1392
+
1393
+ local res = H.state.status == 'accept' and H.state.input or nil
1394
+ local errmsg, opts = H.state.errmsg, vim.deepcopy(H.state.opts)
1395
+ H.state, H.cache = nil, {}
1396
+ (_G.MiniInput or {})._temp_default_scope = nil
1397
+
1398
+ if errmsg ~= nil then error(errmsg) end
1399
+
1400
+ if res ~= nil and not opts.hide then
1401
+ local hist = { input = res, prompt = opts.prompt, scope = opts.scope, cwd = vim.fn.getcwd() }
1402
+ table.insert(H.history, hist)
1403
+ end
1404
+ return res
1405
+ end
1406
+
1407
+ H.state_is_end = function(state)
1408
+ state = state or H.state
1409
+ return state.status == 'accept' or state.status == 'cancel'
1410
+ end
1411
+
1412
+ H.mock_lsp_buf_rename = function(f)
1413
+ return function(new_name, opts)
1414
+ if new_name == nil then (_G.MiniInput or {})._temp_default_scope = 'cursor' end
1415
+ return f(new_name, opts)
1416
+ end
1417
+ end
1418
+
1419
+ -- Handlers -------------------------------------------------------------------
1420
+ H.handle_step = function(key, skip_redraw)
1421
+ local state, ref_state = H.copy_tables(H.state), H.copy_tables(H.state)
1422
+
1423
+ -- Stop previous highlighting, as it is not up to date
1424
+ state.highlight, ref_state.highlight = nil, nil
1425
+
1426
+ state = MiniInput.apply_handler(state, 'key', key)
1427
+
1428
+ -- Stop completion if something has changed outside of completion
1429
+ local is_complete_stop = vim.deep_equal(state.complete, ref_state.complete) and not vim.deep_equal(state, ref_state)
1430
+ if is_complete_stop then state.complete = nil end
1431
+
1432
+ state = MiniInput.apply_handler(state, 'highlight')
1433
+ state = MiniInput.apply_handler(state, 'view')
1434
+
1435
+ H.state = state
1436
+ if not skip_redraw then H.redraw() end
1437
+ end
1438
+
1439
+ -- Default key handler --------------------------------------------------------
1440
+ H.key_methods = {}
1441
+
1442
+ H.keycode = vim.fn.has('nvim-0.10') == 1 and vim.keycode
1443
+ or function(s) return vim.api.nvim_replace_termcodes(s, true, true, true) end
1444
+ local kc = H.keycode
1445
+
1446
+ -- General
1447
+ H.key_methods[kc('<CR>')] = function(state, _) state.status = 'accept' end
1448
+ H.key_methods[kc('<Esc>')] = function(state, _) state.status = 'cancel' end
1449
+ H.key_methods[kc('<C-c>')] = H.key_methods[kc('<Esc>')]
1450
+
1451
+ -- Caret movement
1452
+ H.key_methods[kc('<Left>')] = function(state, _)
1453
+ state.caret = H.clamp(state.caret - 1, 1, vim.fn.strchars(state.input) + 1)
1454
+ end
1455
+ H.key_methods[kc('<Right>')] = function(state, _)
1456
+ state.caret = H.clamp(state.caret + 1, 1, vim.fn.strchars(state.input) + 1)
1457
+ end
1458
+ H.key_methods[kc('<M-h>')] = H.key_methods[kc('<Left>')]
1459
+ H.key_methods[kc('<M-l>')] = H.key_methods[kc('<Right>')]
1460
+ H.key_methods[kc('<S-Left>')] = function(state, _)
1461
+ local caret, input = state.caret, state.input
1462
+ local to = H.match_keyword_chars(input, caret, 'left')
1463
+ state.caret = H.clamp(to + 1, 1, vim.fn.strchars(state.input) + 1)
1464
+ end
1465
+ H.key_methods[kc('<S-Right>')] = function(state, _)
1466
+ local caret, input = state.caret, state.input
1467
+ local to = H.match_keyword_chars(input, caret, 'right')
1468
+ state.caret = H.clamp(to + 1, 1, vim.fn.strchars(state.input) + 1)
1469
+ end
1470
+ H.key_methods[kc('<C-b>')] = function(state, _) state.caret = 1 end
1471
+ H.key_methods[kc('<C-e>')] = function(state, _)
1472
+ if state.complete == nil then
1473
+ state.caret = vim.fn.strchars(state.input) + 1
1474
+ return
1475
+ end
1476
+ H.advance_state_complete(state, -state.complete.id)
1477
+ state.complete = nil
1478
+ end
1479
+ H.key_methods[kc('<Home>')] = H.key_methods[kc('<C-b>')]
1480
+ H.key_methods[kc('<End>')] = function(state, _) state.caret = vim.fn.strchars(state.input) + 1 end
1481
+
1482
+ -- Delete
1483
+ H.key_methods[kc('<BS>')] = function(state, opts)
1484
+ local caret, input = state.caret, state.input
1485
+ if caret <= 1 then return end
1486
+ local right_offset = (opts.autopair and H.is_inside_pair(state)) and 1 or 0
1487
+ state.input = vim.fn.strcharpart(input, 0, caret - 2) .. vim.fn.strcharpart(input, caret - 1 + right_offset)
1488
+ state.caret = caret - 1
1489
+ end
1490
+ H.key_methods[kc('<C-h>')] = H.key_methods[kc('<BS>')]
1491
+ H.key_methods[kc('<Del>')] = function(state, _)
1492
+ local caret, input = state.caret, state.input
1493
+ if caret > vim.fn.strchars(input) then return end
1494
+ state.input = vim.fn.strcharpart(input, 0, caret - 1) .. vim.fn.strcharpart(input, caret)
1495
+ end
1496
+ H.key_methods[kc('<C-u>')] = function(state, _)
1497
+ state.input = vim.fn.strcharpart(state.input, state.caret - 1)
1498
+ state.caret = 1
1499
+ end
1500
+ H.key_methods[kc('<C-w>')] = function(state, _)
1501
+ local caret, input = state.caret, state.input
1502
+ local left_to = H.match_keyword_chars(input, caret, 'left')
1503
+ state.input = vim.fn.strcharpart(input, 0, left_to) .. vim.fn.strcharpart(input, caret - 1)
1504
+ state.caret = H.clamp(left_to + 1, 1, vim.fn.strchars(state.input) + 1)
1505
+ end
1506
+
1507
+ -- Special insert
1508
+ H.key_methods[kc('<C-k>')] = function(state, _)
1509
+ local ok, new = pcall(vim.fn.digraph_get, H.getcharstr_many(2, { ['\27'] = true }))
1510
+ if not ok then return end
1511
+ H.insert_at_caret(state, new)
1512
+ end
1513
+
1514
+ H.key_methods[kc('<C-r>')] = function(state, _)
1515
+ -- Get register content
1516
+ local register = H.getcharstr()
1517
+ if register == nil then return end
1518
+ -- - Mimic some "insert object under cursor" behavior of Command-line mode
1519
+ local reg_content
1520
+ local expand_var = ({ ['\1'] = '<cWORD>', ['\6'] = '<cfile>', ['\23'] = '<cword>' })[register]
1521
+ if expand_var then reg_content = vim.fn.expand(expand_var) end
1522
+ if register == '\f' then reg_content = vim.fn.getline('.') end
1523
+ if reg_content == nil then
1524
+ local has_register, r = pcall(vim.fn.getreg, register)
1525
+ reg_content = has_register and r or ''
1526
+ end
1527
+
1528
+ H.insert_at_caret(state, reg_content)
1529
+ end
1530
+
1531
+ H.key_methods[kc('<C-q>')] = function(state, _)
1532
+ local char = H.getcharstr({}, true)
1533
+ if char == nil then return end
1534
+
1535
+ -- See `:h i_CTRL-V_digit`
1536
+ if char:find('^[%doOxXuU]$') ~= nil then
1537
+ local new_text = H.get_ctrl_v_digits(char)
1538
+ if new_text ~= nil then H.insert_at_caret(state, new_text) end
1539
+ return
1540
+ end
1541
+
1542
+ local ok, new_text = pcall(vim.fn.keytrans, char)
1543
+ if not ok or (ok and vim.fn.char2nr(char) <= 31) then new_text = char end
1544
+ H.insert_at_caret(state, new_text)
1545
+ end
1546
+ H.key_methods[kc('<C-v>')] = H.key_methods[kc('<C-q>')]
1547
+
1548
+ -- History navigation
1549
+ H.key_methods[kc('<Up>')] = function(state, _)
1550
+ if state.complete == nil then state = MiniInput.apply_handler(state, 'complete', 'history') end
1551
+ return H.advance_state_complete(state, -1)
1552
+ end
1553
+ H.key_methods[kc('<Down>')] = function(state, _)
1554
+ if state.complete == nil then state = MiniInput.apply_handler(state, 'complete', 'history') end
1555
+ return H.advance_state_complete(state, 1)
1556
+ end
1557
+ H.key_methods[kc('<C-n>')] = H.key_methods[kc('<Down>')]
1558
+ H.key_methods[kc('<C-p>')] = H.key_methods[kc('<Up>')]
1559
+ H.key_methods[kc('<C-y>')] = function(state, _) state.complete = nil end
1560
+
1561
+ -- Completion navigation
1562
+ H.key_methods[kc('<Tab>')] = function(state, _)
1563
+ if state.complete == nil then state = MiniInput.apply_handler(state, 'complete', state.opts.completion) end
1564
+ return H.advance_state_complete(state, 1)
1565
+ end
1566
+ H.key_methods[kc('<S-Tab>')] = function(state, _)
1567
+ if state.complete == nil then state = MiniInput.apply_handler(state, 'complete', state.opts.completion) end
1568
+ return H.advance_state_complete(state, -1)
1569
+ end
1570
+
1571
+ -- Miscellaneous
1572
+ H.key_methods[kc('<C-o>')] = function(state, _)
1573
+ state.opts.scope = H.advance_from_choices(H.allowed_scopes, state.opts.scope, 'scope')
1574
+ end
1575
+ H.key_methods[kc('<C-s>')] = function(state, _)
1576
+ state.data.new_style = H.advance_from_choices(state.data.all_styles, state.data.style, 'style')
1577
+ end
1578
+ H.key_methods[kc('<C-x>')] = function(state, _)
1579
+ state.opts.hide = not state.opts.hide
1580
+ H.notify('Input is ' .. (state.opts.hide and '' or 'not ') .. 'hidden')
1581
+ end
1582
+
1583
+ -- Autopair keys
1584
+ H.key_methods_autopair = {}
1585
+ H.key_methods_autopair['('] = function(state) H.autopair_open(state, '()') end
1586
+ H.key_methods_autopair['['] = function(state) H.autopair_open(state, '[]') end
1587
+ H.key_methods_autopair['{'] = function(state) H.autopair_open(state, '{}') end
1588
+ H.key_methods_autopair[')'] = function(state) H.autopair_close(state, ')') end
1589
+ H.key_methods_autopair[']'] = function(state) H.autopair_close(state, ']') end
1590
+ H.key_methods_autopair['}'] = function(state) H.autopair_close(state, '}') end
1591
+ H.key_methods_autopair['"'] = function(state) H.autopair_closeopen(state, '""', '"') end
1592
+ H.key_methods_autopair["'"] = function(state) H.autopair_closeopen(state, "''", "'") end
1593
+ H.key_methods_autopair['`'] = function(state) H.autopair_closeopen(state, '``', '`') end
1594
+
1595
+ -- Local helpers
1596
+ H.insert_at_caret = function(state, new_text)
1597
+ local caret, input = state.caret, state.input
1598
+ state.input = vim.fn.strcharpart(input, 0, caret - 1) .. new_text .. vim.fn.strcharpart(input, caret - 1)
1599
+ state.caret = caret + vim.fn.strchars(new_text)
1600
+ end
1601
+
1602
+ H.match_keyword_chars = function(input, caret, side)
1603
+ local text = side == 'left' and vim.fn.strcharpart(input, 0, caret - 1) or vim.fn.strcharpart(input, caret - 1)
1604
+ local pat_keyword = side == 'left' and '[[:keyword:]]\\+$' or '^[[:keyword:]]\\+\\zs'
1605
+ local pat_non_keyword = side == 'left' and '[^[:keyword:]]\\+$' or '^[^[:keyword:]]\\+\\zs'
1606
+
1607
+ local res = math.max(vim.fn.match(text, pat_keyword), vim.fn.match(text, pat_non_keyword), 0)
1608
+ return vim.fn.charidx(text, res) + (side == 'left' and 0 or (caret - 1))
1609
+ end
1610
+
1611
+ H.get_ctrl_v_digits = function(submode)
1612
+ local n_extra = ({ o = 3, O = 3, u = 4, U = 8 })[submode] or 2
1613
+ local digits = H.getcharstr_many(n_extra, { ['\27'] = true })
1614
+ if digits == nil then return nil end
1615
+ digits = (submode:find('%d') ~= nil and submode or '') .. digits
1616
+
1617
+ local base = ({ o = 8, O = 8, x = 16, X = 16, u = 16, U = 16 })[submode] or 10
1618
+ local ok, res = pcall(vim.fn.nr2char, tonumber(digits, base))
1619
+ return ok and res or nil
1620
+ end
1621
+
1622
+ H.advance_state_complete = function(state, increment)
1623
+ local no_complete = H.state_is_end(state) or not (type(state.complete) == 'table' and #state.complete.items > 0)
1624
+ if no_complete then return state end
1625
+
1626
+ local old_id, base = state.complete.id, state.complete.base
1627
+ local old = state.complete.items[old_id] or base
1628
+ local new_id = (old_id + increment) % (#state.complete.items + 1)
1629
+ local new = state.complete.items[new_id] or base
1630
+ state.complete.id = new_id
1631
+
1632
+ -- Replace currently shown candidate with the new one
1633
+ local caret, input = state.caret, state.input
1634
+ local old_len = vim.fn.strchars(old)
1635
+ state.input = vim.fn.strcharpart(input, 0, caret - old_len - 1) .. new .. vim.fn.strcharpart(input, caret - 1)
1636
+ state.caret = caret + (vim.fn.strchars(new) - old_len)
1637
+
1638
+ return state
1639
+ end
1640
+
1641
+ H.advance_from_choices = function(all, cur, name)
1642
+ local new, n = nil, #all
1643
+ for i, v in ipairs(all) do
1644
+ if v == cur then new = all[i % n + 1] end
1645
+ end
1646
+ if not (new == nil or new == cur) then H.notify(string.format('Changed %s to %s', name, vim.inspect(new))) end
1647
+ return new
1648
+ end
1649
+
1650
+ H.autopair_open = function(state, pair)
1651
+ H.insert_at_caret(state, pair)
1652
+ state.caret = state.caret - 1
1653
+ end
1654
+
1655
+ H.autopair_close = function(state, close_char)
1656
+ local is_before_close = vim.fn.strcharpart(state.input, state.caret - 1, 1) == close_char
1657
+ if is_before_close then state.caret = state.caret + 1 end
1658
+ if not is_before_close then H.insert_at_caret(state, close_char) end
1659
+ end
1660
+
1661
+ H.autopair_closeopen = function(state, pair, close_char)
1662
+ local is_before_close = vim.fn.strcharpart(state.input, state.caret - 1, 1) == close_char
1663
+ if is_before_close then H.autopair_close(state, close_char) end
1664
+ if not is_before_close then H.autopair_open(state, pair) end
1665
+ end
1666
+
1667
+ H.is_inside_pair = function(state)
1668
+ local neigh = vim.fn.strcharpart(state.input, state.caret - 2, 2)
1669
+ return ({ ['()'] = true, ['[]'] = true, ['{}'] = true, ['""'] = true, ["''"] = true, ['``'] = true })[neigh] ~= nil
1670
+ end
1671
+
1672
+ -- Default complete handler ---------------------------------------------------
1673
+ H.complete_history = function(state, precise_history)
1674
+ local cwd, scope, prompt = vim.fn.getcwd(), state.opts.scope, state.opts.prompt
1675
+ local is_precise = function(h) return h.cwd == cwd and h.scope == scope and h.prompt == prompt end
1676
+ if not precise_history then is_precise = function(x) return true end end
1677
+
1678
+ local base = vim.fn.strcharpart(state.input, 0, state.caret - 1)
1679
+ local is_match = function(x) return vim.startswith(x, base) and x ~= base end
1680
+
1681
+ local raw, matched, seen = MiniInput.get_history(), {}, {}
1682
+ for i = #raw, 1, -1 do
1683
+ local val = raw[i].input
1684
+ if not seen[val] and is_match(val) and is_precise(raw[i]) then table.insert(matched, val) end
1685
+ seen[val] = true
1686
+ end
1687
+ local items, n = {}, #matched
1688
+ for i = 1, #raw do
1689
+ items[n - i + 1] = matched[i]
1690
+ end
1691
+ state.complete = { base = base, items = items }
1692
+ end
1693
+
1694
+ H.complete_buf_words = function(base)
1695
+ -- Do not match all words for performance reasons
1696
+ if base == '' then return {} end
1697
+
1698
+ -- Get keywords in current buffer that fuzzy match base
1699
+ local pattern_parts = {}
1700
+ for i = 1, vim.fn.strchars(base) do
1701
+ table.insert(pattern_parts, vim.fn.strcharpart(base, i - 1, 1))
1702
+ end
1703
+ -- NOTE: This looks hacky, but it is very fast in common cases and already
1704
+ -- respects 'ignorecase' and 'smartcase'
1705
+ local pattern = '\\V\\k\\*' .. table.concat(pattern_parts, '\\k\\*') .. '\\k\\*'
1706
+ vim.g._miniinput_matches = {}
1707
+
1708
+ local cache_hlsearch, cache_cursor = vim.v.hlsearch, vim.api.nvim_win_get_cursor(0)
1709
+ local search_cmd = 'silent! keeppatterns %s/' .. pattern .. '/\\=add(g:_miniinput_matches, submatch(0))/gn'
1710
+ vim.cmd(search_cmd)
1711
+ -- Here `vim.v` doesn't work: https://github.com/neovim/neovim/issues/25294
1712
+ vim.cmd('let v:hlsearch=' .. cache_hlsearch)
1713
+ vim.api.nvim_win_set_cursor(0, cache_cursor)
1714
+ local matches = vim.g._miniinput_matches
1715
+ vim.g._miniinput_matches = nil
1716
+
1717
+ -- Compute final unique matches sorted by how good fuzzy match is
1718
+ local uniq, seen = {}, { [base] = true }
1719
+ for _, m in ipairs(matches) do
1720
+ if not seen[m] then table.insert(uniq, m) end
1721
+ seen[m] = true
1722
+ end
1723
+ local ref = (vim.o.ignorecase and not vim.o.smartcase) and vim.fn.tolower(base) or base
1724
+ return vim.fn.matchfuzzy(uniq, ref, { matchseq = 1 })
1725
+ end
1726
+
1727
+ H.get_base_from_items = function(text, items)
1728
+ if #items == 0 then return '' end
1729
+
1730
+ local is_match = function(base_cur)
1731
+ for _, item in ipairs(items) do
1732
+ if not vim.startswith(item, base_cur) then return false end
1733
+ end
1734
+ return true
1735
+ end
1736
+ if vim.o.wildoptions:find('fuzzy') ~= nil then
1737
+ is_match = function(base_cur) return #vim.fn.matchfuzzy(items, base_cur, { matchseq = 1 }) == #items end
1738
+ end
1739
+
1740
+ local res
1741
+ for i = vim.fn.strchars(text), 1, -1 do
1742
+ local base_cur = vim.fn.strcharpart(text, i - 1)
1743
+ if is_match(base_cur) then res = base_cur end
1744
+ end
1745
+ return res or ''
1746
+ end
1747
+
1748
+ -- Views ----------------------------------------------------------------------
1749
+ H.handle_view_style = function(state, init_style, all_styles)
1750
+ if state.status == 'start' then state.data.all_styles = all_styles end
1751
+ local style_is_new = state.data.new_style ~= nil and state.data.new_style ~= state.data.style
1752
+ state.data.style = state.data.new_style or state.data.style or init_style
1753
+ state.data.new_style = nil
1754
+ return state.data.style, style_is_new
1755
+ end
1756
+
1757
+ H.default_floatwin_config = function(state, style, target_width)
1758
+ local ver, hor = style:sub(1, 1), style:sub(2, 2)
1759
+ local wininfo = H.get_curwin_info()
1760
+
1761
+ local has_tabline = vim.o.showtabline == 2 or (vim.o.showtabline == 1 and #vim.api.nvim_list_tabpages() > 1)
1762
+ local has_statusline = vim.o.laststatus > 0
1763
+ local max_height = vim.o.lines - vim.o.cmdheight - (has_tabline and 1 or 0) - (has_statusline and 1 or 0)
1764
+ local max_width = vim.o.columns
1765
+
1766
+ -- Compute window config
1767
+ local winborder = vim.fn.exists('+winborder') == 0 and '' or vim.o.winborder
1768
+ local border = winborder == '' and 'single' or nil
1769
+ local has_border = winborder ~= 'none'
1770
+
1771
+ local prompt = state.opts.prompt:gsub('[\t\n%z]', ' ')
1772
+ local title_text = prompt == '' and '' or (' ' .. vim.trim(prompt) .. ' ')
1773
+ local footer_text, complete = '', state.complete
1774
+ if complete ~= nil then
1775
+ local method_prefix = complete.method == '' and '' or (' ' .. vim.trim(complete.method))
1776
+ footer_text = string.format('%s %d/%d ', (method_prefix:gsub('[\t\n%z]', ' ')), complete.id, #complete.items)
1777
+ end
1778
+ local title_width, footer_width = vim.fn.strdisplaywidth(title_text), vim.fn.strdisplaywidth(footer_text)
1779
+
1780
+ local width = H.clamp(target_width, math.max(title_width, footer_width), max_width)
1781
+ local height = 1
1782
+ local width_offset = has_border and 2 or 0
1783
+ local height_offset = has_border and 2 or 0
1784
+
1785
+ local config = { relative = 'editor', anchor = 'NW', border = border, style = 'minimal', noautocmd = true }
1786
+ -- - Make sure the window is shown on top of other 'mini.nvim' windows
1787
+ config.zindex = 252
1788
+
1789
+ -- Compute position and dimensions based on scope
1790
+ local ref_rect = { row = has_tabline and 1 or 0, col = 0, width = max_width, height = max_height }
1791
+ local scope = state.opts.scope
1792
+ if not (scope == 'editor' or scope == 'tabpage' or scope == 'project') then
1793
+ -- NOTE: use window for "cursor" as it works when called from command line
1794
+ local winwidth, winheight = wininfo.width, wininfo.height
1795
+ local win_config = vim.api.nvim_win_get_config(0)
1796
+ local win_no_border = win_config.relative == '' or win_config.border == 'none' or win_config.border == nil
1797
+ local win_row = wininfo.winrow - (win_no_border and 1 or 0)
1798
+ local win_col = wininfo.wincol - (win_no_border and 1 or 0)
1799
+ ref_rect = { row = win_row, col = win_col, width = winwidth, height = winheight }
1800
+
1801
+ -- - Nudges to not cover cursor if not 'center'
1802
+ local row_nudge = (ver == 'T' and 1 or ver == 'B' and -1 or 0)
1803
+ local col_nudge = (hor == 'L' and 1 or hor == 'R' and -1 or 0)
1804
+
1805
+ if scope == 'cursor' then
1806
+ local cur_row = win_row + vim.fn.winline() - 1 + row_nudge
1807
+ local cur_col = win_col + vim.fn.wincol() - 1 + col_nudge
1808
+ ref_rect = { row = cur_row, col = cur_col, width = 1, height = 1 }
1809
+ end
1810
+ if scope == 'line' then
1811
+ local cur_row = win_row + vim.fn.winline() - 1 + row_nudge
1812
+ local cur_col = win_col + wininfo.textoff
1813
+ ref_rect = { row = cur_row, col = cur_col, width = winwidth - wininfo.textoff, height = 1 }
1814
+ end
1815
+
1816
+ -- Ensure floating window does not go outside of reference rectangle
1817
+ if scope ~= 'cursor' then width = H.clamp(width, 1, ref_rect.width - width_offset) end
1818
+ end
1819
+
1820
+ local ver_coef = ver == 'T' and 0 or (ver == 'M' and 0.5 or 1)
1821
+ local hor_coef = hor == 'L' and 0 or (hor == 'M' and 0.5 or 1)
1822
+
1823
+ config.row = ref_rect.row + math.floor(ver_coef * (ref_rect.height - height - height_offset))
1824
+ config.col = ref_rect.col + math.floor(hor_coef * (ref_rect.width - width - width_offset))
1825
+ config.height = math.min(height, max_height - height_offset)
1826
+ config.width = math.min(width, max_width - width_offset)
1827
+
1828
+ -- Set title and footer (even if empty string, to remove previous one)
1829
+ if has_border then
1830
+ local title_hl = state.opts.hide and 'MiniInputHide' or 'MiniInputPrompt'
1831
+ config.title = { { H.fit_to_displaywidth(title_text, config.width), title_hl } }
1832
+ config.title_pos = 'left'
1833
+ if vim.fn.has('nvim-0.10') == 1 then
1834
+ config.footer = { { H.fit_to_displaywidth(footer_text, config.width), 'MiniInputHint' } }
1835
+ config.footer_pos = 'right'
1836
+ end
1837
+ end
1838
+
1839
+ return config
1840
+ end
1841
+
1842
+ H.ensure_floatwin_buf = function(state, chunks)
1843
+ local buf_id = state.data.floatwin_buf_id
1844
+ if H.is_valid_buf(buf_id) then
1845
+ vim.api.nvim_buf_clear_namespace(buf_id, H.ns_id.view, 0, -1)
1846
+ else
1847
+ buf_id = vim.api.nvim_create_buf(false, true)
1848
+ H.set_buf_name(buf_id, 'content')
1849
+ vim.bo[buf_id].filetype = 'miniinput'
1850
+ state.data.floatwin_buf_id = buf_id
1851
+ end
1852
+
1853
+ local text_arr, extmark_data, cur_len = {}, {}, 0
1854
+ for _, ch in ipairs(chunks) do
1855
+ table.insert(text_arr, ch[1])
1856
+ local new_len = cur_len + ch[1]:len()
1857
+ table.insert(extmark_data, { cur_len, { end_row = 0, end_col = new_len, hl_group = ch[2] } })
1858
+ cur_len = new_len
1859
+ end
1860
+
1861
+ -- Show input as a single line with special chars taken care of
1862
+ vim.api.nvim_buf_set_lines(buf_id, 0, -1, false, { H.get_chunks_text(chunks) })
1863
+ for _, ext in ipairs(extmark_data) do
1864
+ pcall(vim.api.nvim_buf_set_extmark, buf_id, H.ns_id.view, 0, ext[1], ext[2])
1865
+ end
1866
+ end
1867
+
1868
+ H.ensure_floatwin_win = function(state, config)
1869
+ local buf_id = state.data.floatwin_buf_id
1870
+ local win_id = state.data.floatwin_win_id
1871
+ if H.is_valid_win(win_id) then
1872
+ config.noautocmd = nil
1873
+ vim.api.nvim_win_set_config(win_id, config)
1874
+ else
1875
+ win_id = vim.api.nvim_open_win(buf_id, false, config)
1876
+ vim.wo[win_id].list = vim.go.list
1877
+ vim.wo[win_id].listchars = vim.go.listchars
1878
+ vim.wo[win_id].winhighlight = 'NormalFloat:MiniInputNormal,FloatBorder:MiniInputBorder'
1879
+ vim.wo[win_id].wrap = false
1880
+ state.data.floatwin_win_id = win_id
1881
+ end
1882
+
1883
+ return win_id
1884
+ end
1885
+
1886
+ H.uiline_handle_option_values = function(state, style_was_changed)
1887
+ local win_id = vim.api.nvim_get_current_win()
1888
+ local needs_reset = win_id ~= state.data.win_id or style_was_changed
1889
+ if not (H.state_is_end(state) or needs_reset) then return end
1890
+
1891
+ -- Unset all previously set option values
1892
+ H.uiline_unset_option_value(state, 'laststatus')
1893
+ H.uiline_unset_option_value(state, 'showtabline')
1894
+ H.uiline_unset_option_value(state, 'statusline', win_id)
1895
+ H.uiline_unset_option_value(state, 'tabline')
1896
+ H.uiline_unset_option_value(state, 'winbar', win_id)
1897
+
1898
+ if not needs_reset then return end
1899
+
1900
+ -- Cache all necessary values
1901
+ state.data.laststatus = vim.o.laststatus
1902
+ state.data.showtabline = vim.o.showtabline
1903
+ state.data.statusline = vim.wo.statusline
1904
+ state.data.status_height = H.get_curwin_info().status_height
1905
+ state.data.tabline = vim.o.tabline
1906
+ state.data.winbar = vim.wo.winbar
1907
+
1908
+ state.data.win_id = win_id
1909
+ end
1910
+
1911
+ H.uiline_unset_option_value = function(state, option_name, win_id)
1912
+ -- Make sure to not set 'statusline' if it was not previously used
1913
+ local new_val = state.data[option_name]
1914
+ if option_name == 'statusline' and (state.data.status_height or 1) == 0 and state.data.laststatus < 3 then
1915
+ new_val = nil
1916
+ end
1917
+ local option_opts = { scope = win_id ~= nil and 'local' or nil, win = win_id }
1918
+ local cur_val = vim.api.nvim_get_option_value(option_name, option_opts)
1919
+ if cur_val == state.data[option_name] then return end
1920
+ pcall(vim.api.nvim_set_option_value, option_name, new_val, option_opts)
1921
+ end
1922
+
1923
+ -- Chunks ---------------------------------------------------------------------
1924
+ H.get_chunks = function(opts, state, max_width)
1925
+ local res = opts.to_chunks(state, max_width)
1926
+
1927
+ -- Validate
1928
+ local name = string.format('opts.to_chunks(state, %s)', vim.inspect(max_width))
1929
+ H.check_array_of(name, res, 'table')
1930
+ for i, ch in ipairs(res) do
1931
+ local item = string.format('%s[%d]', name, i)
1932
+ H.check_type(item .. '[1]', ch[1], 'string')
1933
+ H.check_type(item .. '[2]', ch[2], 'string')
1934
+ end
1935
+
1936
+ return res
1937
+ end
1938
+
1939
+ H.append_chunks = function(arr, new, keytrans)
1940
+ if not keytrans then return vim.list_extend(arr, new) end
1941
+
1942
+ -- Sanitize chunks for view to display special keys in special way
1943
+ for _, ch in ipairs(new) do
1944
+ for s, special in string.gmatch(ch[1], '(%C*)(%c*)') do
1945
+ if s ~= '' then table.insert(arr, { s, ch[2] }) end
1946
+ if special ~= '' then table.insert(arr, { vim.fn.keytrans(special), 'MiniInputSpecial' }) end
1947
+ end
1948
+ end
1949
+ end
1950
+
1951
+ -- Split chunks at character index
1952
+ -- All chunks for text to the left of and including `at` index go to left.
1953
+ -- The rest - to right. Split chunk if `at` is in between.
1954
+ ---@private
1955
+ H.split_chunks_at = function(chunks, at)
1956
+ local left, right, cur_width = {}, {}, 0
1957
+ for _, ch in ipairs(chunks) do
1958
+ local w = vim.fn.strchars(ch[1])
1959
+ local to_left, to_right = at - cur_width, cur_width + w - at
1960
+ cur_width = cur_width + w
1961
+
1962
+ if to_right <= 0 then table.insert(left, ch) end
1963
+ if to_left <= 0 then table.insert(right, ch) end
1964
+ if to_left > 0 and to_right > 0 then
1965
+ table.insert(left, { vim.fn.strcharpart(ch[1], 0, to_left), ch[2] })
1966
+ table.insert(right, { vim.fn.strcharpart(ch[1], to_left), ch[2] })
1967
+ end
1968
+ end
1969
+
1970
+ return left, right
1971
+ end
1972
+
1973
+ H.fit_chunks_to_displaywidth = function(chunks, width, center_offset)
1974
+ local chunks_text = H.get_chunks_text(chunks)
1975
+ local chunks_width = vim.fn.strdisplaywidth(chunks_text)
1976
+ if width == nil or chunks_width < width then return chunks end
1977
+
1978
+ -- Show center with as much and as equally as possible to left and right
1979
+ local right = math.min(chunks_width, math.floor(center_offset + 0.5 * width))
1980
+ local left = math.max(1, right - width + 1)
1981
+ right = left + math.min(width, chunks_width) - 1
1982
+
1983
+ -- Convert display cols into char cols (matters with double width characters)
1984
+ local left_char, right_char, cur_w = nil, nil, 0
1985
+ for i = 1, vim.fn.strchars(chunks_text) do
1986
+ local w = vim.fn.strdisplaywidth(vim.fn.strcharpart(chunks_text, i - 1, 1))
1987
+
1988
+ -- `left_char` = first char id with end at `left` or more, but only if the
1989
+ -- start is within `width` from center offset (otherwise - next char).
1990
+ if left_char == nil and left <= (cur_w + w) then
1991
+ local is_within_width_from_center = center_offset - cur_w <= width
1992
+ left_char = i + (is_within_width_from_center and 0 or 1)
1993
+ -- Update `left` display column to point at left character start
1994
+ left = cur_w + (is_within_width_from_center and 0 or w) + 1
1995
+ end
1996
+
1997
+ -- `right_char` = last char id with end at most `width` from updated `left`
1998
+ if (cur_w + w - left + 1) <= width then right_char = i end
1999
+
2000
+ cur_w = cur_w + w
2001
+ end
2002
+
2003
+ local res, _ = H.split_chunks_at(chunks, right_char)
2004
+ _, res = H.split_chunks_at(res, left_char - 1)
2005
+ return res
2006
+ end
2007
+
2008
+ H.get_chunks_text = function(chunks)
2009
+ return table.concat(vim.tbl_map(function(ch) return ch[1] end, chunks))
2010
+ end
2011
+
2012
+ H.get_chunks_displaywidth = function(chunks) return vim.fn.strdisplaywidth(H.get_chunks_text(chunks)) end
2013
+
2014
+ -- Highlight ranges -----------------------------------------------------------
2015
+ --- Add new proper highlight range on top of an array of ranges that don't
2016
+ --- intersect and are ordered from left to right.
2017
+ ---@private
2018
+ H.insert_hl_range = function(arr, max_to, from, to, hl)
2019
+ -- Insert only proper range that fits constraints
2020
+ if from == nil or to == nil or to < 1 or from > max_to then return arr end
2021
+ from, to = H.clamp(from, 1, max_to), H.clamp(to, 1, max_to)
2022
+ if from > to then return arr end
2023
+
2024
+ local res, was_inserted = {}, false
2025
+ for _, cur in ipairs(arr) do
2026
+ -- No overlap
2027
+ if cur.to < from or to < cur.from then table.insert(res, cur) end
2028
+ -- Overlap with outside left part of current
2029
+ if cur.from < from and from <= cur.to then table.insert(res, { from = cur.from, to = from - 1, hl = cur.hl }) end
2030
+ -- Overlap
2031
+ if from <= cur.to and cur.from <= to and not was_inserted then
2032
+ table.insert(res, { from = from, to = to, hl = hl })
2033
+ was_inserted = true
2034
+ end
2035
+ -- Overlap with outside right part of current
2036
+ if cur.from <= to and to < cur.to then table.insert(res, { from = to + 1, to = cur.to, hl = cur.hl }) end
2037
+ end
2038
+ if not was_inserted then table.insert(res, { from = from, to = to, hl = hl }) end
2039
+ return res
2040
+ end
2041
+
2042
+ H.make_ui_select_hl_fun = function(hl_fun)
2043
+ if not vim.is_callable(hl_fun) then return end
2044
+
2045
+ local hl_handler = H.get_config().handlers.highlight or MiniInput.default_highlight
2046
+ return function(state)
2047
+ local input = state.input
2048
+ local hl_ranges = hl_fun(input)
2049
+ if not H.islist(hl_ranges) then hl_ranges = {} end
2050
+
2051
+ -- Convert from Vim ranges `r` to 'mini.input' ranges:
2052
+ -- - r[1] - zero-based byte index at first multibyte start
2053
+ -- - r[2] - zero-based byte index one more than last multibyte end
2054
+ -- - 'mini.input' ranges: named fields, 1-based char indexes
2055
+ local highlight = {}
2056
+ for i, r in ipairs(hl_ranges) do
2057
+ if type(r) == 'table' and type(r[1]) == 'number' and type(r[2]) == 'number' and type(r[3]) == 'string' then
2058
+ highlight[i] = { from = vim.fn.charidx(input, r[1]) + 1, to = vim.fn.charidx(input, r[2]), hl = r[3] }
2059
+ end
2060
+ end
2061
+ state.highlight = highlight
2062
+
2063
+ -- Combine with user-defined handler, like to show added completion chars
2064
+ hl_handler(state)
2065
+ end
2066
+ end
2067
+
2068
+ -- Utilities ------------------------------------------------------------------
2069
+ H.error = function(msg) error('(mini.input) ' .. msg, 0) end
2070
+
2071
+ H.check_type = function(name, val, ref, allow_nil)
2072
+ if type(val) == ref or (ref == 'callable' and vim.is_callable(val)) or (allow_nil and val == nil) then return end
2073
+ H.error(string.format('`%s` should be %s, not %s', name, ref, type(val)))
2074
+ end
2075
+
2076
+ H.check_one_of = function(name, x, choices)
2077
+ if vim.tbl_contains(choices, x) then return end
2078
+ local choices_string = table.concat(vim.tbl_map(vim.inspect, choices), ', ')
2079
+ local msg = string.format('`%s` should be one of %s', name, choices_string)
2080
+ H.error(msg)
2081
+ end
2082
+
2083
+ H.check_array_of = function(name, x, ref_type)
2084
+ if not H.islist(x) then H.error('`' .. name .. '` should be array') end
2085
+ for i, k in ipairs(x) do
2086
+ if type(k) ~= ref_type then H.error('Every `' .. name .. '` item should be ' .. ref_type) end
2087
+ end
2088
+ end
2089
+
2090
+ H.set_buf_name = function(buf_id, name) vim.api.nvim_buf_set_name(buf_id, 'miniinput://' .. buf_id .. '/' .. name) end
2091
+
2092
+ H.notify = function(msg, level_name) vim.notify('(mini.input) ' .. msg, vim.log.levels[level_name]) end
2093
+
2094
+ H.getcharstr = function(lmap, allow_ctrl_c)
2095
+ H.cache.is_in_getcharstr = true
2096
+ local ok, char = H.safe_fn_getcharstr()
2097
+ H.cache.is_in_getcharstr = nil
2098
+
2099
+ -- Cache possible error if it doesn't come from pressing <C-c>
2100
+ local is_ctrl_c = (not ok and char == 'Keyboard interrupt') or (ok and char == '\3')
2101
+ if not ok and not is_ctrl_c then H.cache_error(H.state, char) end
2102
+
2103
+ -- Terminate if no input or on hard-coded <C-c>
2104
+ if allow_ctrl_c and is_ctrl_c then return '\3' end
2105
+ if not ok or char == '' or char == '\3' then return nil end
2106
+
2107
+ -- Respect language mappings only if needed
2108
+ return vim.o.iminsert == 0 and char or ((lmap or {})[char] or char)
2109
+ end
2110
+
2111
+ -- TODO: Remove after compatibility with Neovim=0.10 is dropped
2112
+ H.safe_fn_getcharstr = function() return pcall(vim.fn.getcharstr, -1, { cursor = 'hide' }) end
2113
+ if vim.fn.has('nvim-0.11') == 0 then H.safe_fn_getcharstr = function() return pcall(vim.fn.getcharstr) end end
2114
+
2115
+ H.getcharstr_many = function(n, early_stop)
2116
+ local res = {}
2117
+ for i = 1, n do
2118
+ res[i] = H.getcharstr()
2119
+ if res[i] == nil or early_stop[res[i]] then return nil end
2120
+ end
2121
+ return table.concat(res)
2122
+ end
2123
+
2124
+ H.is_special_char = function(x)
2125
+ -- Remove allowed character that are specially translated
2126
+ x = x:gsub('[%s<]', '')
2127
+ return vim.fn.keytrans(x) ~= x
2128
+ end
2129
+
2130
+ H.cache_error = function(state, msg)
2131
+ if not vim.startswith(msg, '(mini.input) ') then msg = '(mini.input) ' .. msg end
2132
+ if H.state == nil then error(msg) end
2133
+ state.errmsg = state.errmsg or msg
2134
+ state.status = 'cancel'
2135
+ end
2136
+
2137
+ H.redraw = function() vim.cmd('redraw!') end
2138
+
2139
+ H.clamp = function(x, from, to) return math.min(math.max(x, from), to) end
2140
+
2141
+ H.is_valid_buf = function(buf_id) return type(buf_id) == 'number' and vim.api.nvim_buf_is_valid(buf_id) end
2142
+
2143
+ H.is_valid_win = function(win_id) return type(win_id) == 'number' and vim.api.nvim_win_is_valid(win_id) end
2144
+
2145
+ H.is_valid_char = function(char) return vim.fn.strchars(char) == 1 and vim.fn.char2nr(char) > 31 end
2146
+
2147
+ H.get_curwin_info = function()
2148
+ local res = vim.fn.getwininfo(vim.api.nvim_get_current_win())[1]
2149
+ -- TODO: Remove after compatibility with Neovim=0.11 is dropped
2150
+ res.status_height = res.status_height or (vim.api.nvim_win_get_config(0).relative == '' and 1 or 0)
2151
+ return res
2152
+ end
2153
+
2154
+ H.fit_to_displaywidth = function(text, width)
2155
+ if vim.fn.strdisplaywidth(text) <= width then return text end
2156
+ for i = 1, vim.fn.strchars(text) do
2157
+ local truncated = '…' .. vim.fn.strcharpart(text, i)
2158
+ if vim.fn.strdisplaywidth(truncated) <= width then return truncated end
2159
+ end
2160
+ return ''
2161
+ end
2162
+
2163
+ -- TODO: Remove after compatibility with Neovim=0.9 is dropped
2164
+ H.islist = vim.fn.has('nvim-0.10') == 1 and vim.islist or vim.tbl_islist
2165
+
2166
+ H.get_lmap = function()
2167
+ local lmap = {}
2168
+ for _, map in ipairs(vim.fn.maplist()) do
2169
+ -- NOTE: Account only for characters that resolve to proper input character
2170
+ local is_query_lmap = map.mode == 'l' and H.is_valid_char(map.rhs)
2171
+ if is_query_lmap then lmap[map.lhs] = map.rhs end
2172
+ end
2173
+ return lmap
2174
+ end
2175
+ if vim.fn.has('nvim-0.10') == 0 then H.get_lmap = function() return {} end end
2176
+
2177
+ -- A copy of `vim.deepcopy()` that doesn't error on userdata and threads
2178
+ H.copy_tables = function(x)
2179
+ return type(x) == 'table' and setmetatable(vim.tbl_map(H.copy_tables, x), getmetatable(x)) or x
2180
+ end
2181
+
2182
+ MiniInput.default_view = MiniInput.gen_view.floatwin()
2183
+ return MiniInput