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,1244 @@
1
+ --- *mini.jump2d* Jump within visible lines
2
+ ---
3
+ --- MIT License Copyright (c) 2022 Evgeni Chasnovski
4
+
5
+ --- Jump within visible lines via iterative label filtering.
6
+ ---
7
+ --- Features:
8
+ --- - Make jump by iterative filtering of possible, equally considered jump
9
+ --- spots until there is only one. Filtering is done by typing a label
10
+ --- character that is visualized at jump spot.
11
+ ---
12
+ --- - Customizable (see |MiniJump2d.config|):
13
+ --- - Way of computing possible jump spots with opinionated default.
14
+ --- - Characters used to label jump spots during iterative filtering.
15
+ --- - Visual effects: how many steps ahead to show; dim lines with spots.
16
+ --- - Action hooks to be executed at certain events during jump.
17
+ --- - Allowed windows: current and/or not current.
18
+ --- - Allowed lines: whether to process blank or folded lines, lines
19
+ --- before/at/after cursor line, etc. Example: user can configure to look
20
+ --- for spots only inside current window at or after cursor line.
21
+ --- Example: user can configure to look for word starts only inside current
22
+ --- window at or after cursor line with `j` and `k` labels performing some
23
+ --- action after jump.
24
+ ---
25
+ --- - Works in Visual and Operator-pending (with dot-repeat) modes.
26
+ ---
27
+ --- - Preconfigured ways of computing jump spots (see |MiniJump2d.builtin_opts|):
28
+ --- - Starts of lines.
29
+ --- - Starts of words.
30
+ --- - Single character from user input.
31
+ --- - Variable length query from user input.
32
+ ---
33
+ --- - Works with multibyte characters.
34
+ ---
35
+ --- General overview of how jump is intended to be performed:
36
+ --- - Lock eyes on desired location ("spot") recognizable by future jump.
37
+ --- Should be within visible lines at place where cursor can be placed.
38
+ ---
39
+ --- - Initiate jump. Either by custom keybinding or with a call to
40
+ --- |MiniJump2d.start()| (allows customization options). This will highlight
41
+ --- all possible jump spots with their labels (letters from "a" to "z" by
42
+ --- default). For more details, read |MiniJump2d.start()| and |MiniJump2d.config|.
43
+ ---
44
+ --- - Type character that appeared over desired location. If its label was
45
+ --- unique, jump is performed. If it wasn't unique, possible jump spots are
46
+ --- filtered to those having the same label character.
47
+ ---
48
+ --- - Repeat previous step until there is only one possible jump spot or type <CR>
49
+ --- to jump to first available jump spot. Typing anything else stops jumping
50
+ --- without moving cursor.
51
+ ---
52
+ --- # Setup ~
53
+ ---
54
+ --- This module needs a setup with `require('mini.jump2d').setup({})` (replace
55
+ --- `{}` with your `config` table). It will create global Lua table
56
+ --- `MiniJump2d` which you can use for scripting or manually (with
57
+ --- `:lua MiniJump2d.*`).
58
+ ---
59
+ --- See |MiniJump2d.config| for available config settings.
60
+ ---
61
+ --- You can override runtime config settings locally to buffer inside
62
+ --- `vim.b.minijump2d_config` which should have same structure as
63
+ --- `MiniJump2d.config`. See |mini.nvim-buffer-local-config| for more details.
64
+ ---
65
+ --- To stop module from showing non-error feedback, set `config.silent = true`.
66
+ ---
67
+ --- # Example usage ~
68
+ ---
69
+ --- - Modify default jumping to use only current window at or after cursor line: >lua
70
+ ---
71
+ --- require('mini.jump2d').setup({
72
+ --- allowed_lines = { cursor_before = false },
73
+ --- allowed_windows = { not_current = false },
74
+ --- })
75
+ --- <
76
+ --- - Jump to line start using combination of options supplied in
77
+ --- |MiniJump2d.config| and |MiniJump2d.builtin_opts.line_start|: >vim
78
+ ---
79
+ --- :lua MiniJump2d.start(MiniJump2d.builtin_opts.line_start)
80
+ --- <
81
+ --- - Jump to a single character typed after executing this command: >vim
82
+ ---
83
+ --- :lua MiniJump2d.start(MiniJump2d.builtin_opts.single_character)
84
+ --- <
85
+ --- - See more examples in |MiniJump2d.start()| and |MiniJump2d.builtin_opts|.
86
+ ---
87
+ --- # Comparisons ~
88
+ ---
89
+ --- - [phaazon/hop.nvim](https://github.com/phaazon/hop.nvim):
90
+ --- - Both are fast, customizable, and extensible (user can write their own
91
+ --- ways to define jump spots).
92
+ --- - `hop.nvim` visualizes all steps at once. While this module can show
93
+ --- configurable number of steps ahead.
94
+ --- - Both have several builtin ways to specify type of jump (word start,
95
+ --- line start, one character or query based on user input). `hop.nvim`
96
+ --- does that by exporting many targeted Neovim commands, while this
97
+ --- module has preconfigured basic options leaving others to
98
+ --- customization with Lua code (see |MiniJump2d.builtin_opts|).
99
+ --- - `hop.nvim` computes labels (called "hints") differently. Contrary to
100
+ --- this module deliberately not having preference of one jump spot over
101
+ --- another, `hop.nvim` uses specialized algorithm that produces sequence
102
+ --- of keys in a slightly biased manner: some sequences are intentionally
103
+ --- shorter than the others (leading to fewer average keystrokes). They
104
+ --- are put near cursor (by default) and highlighted differently. Final
105
+ --- order of sequences is based on distance to the cursor.
106
+ --- - |mini.jump2d| has opinionated default algorithm of computing jump
107
+ --- spots. See |MiniJump2d.default_spotter()|.
108
+ ---
109
+ --- # Highlight groups ~
110
+ --- *MiniJump2d-hl-groups*
111
+ ---
112
+ --- - `MiniJump2dSpot` - highlighting of jump spot's next step. By default it
113
+ --- uses label with highest contrast while not being too visually demanding:
114
+ --- white on black for dark |'background'|, black on white for light. If it
115
+ --- doesn't suit your liking, try couple of these alternatives (or choose
116
+ --- your own, of course): >lua
117
+ ---
118
+ --- -- Reverse underlying colors (mostly *very* visible in any colorscheme)
119
+ --- vim.api.nvim_set_hl(0, 'MiniJump2dSpot', { reverse = true })
120
+ ---
121
+ --- -- Bold italic
122
+ --- vim.api.nvim_set_hl(0, 'MiniJump2dSpot', { bold = true, italic = true })
123
+ ---
124
+ --- -- Red undercurl
125
+ --- vim.api.nvim_set_hl(0, 'MiniJump2dSpot', { sp = 'Red', undercurl = true })
126
+ --- <
127
+ --- - `MiniJump2dSpotUnique` - highlighting of jump spot's next step if it has
128
+ --- unique label. By default links to `MiniJump2dSpot`.
129
+ ---
130
+ --- - `MiniJump2dSpotAhead` - highlighting of jump spot's future steps. By default
131
+ --- similar to `MiniJump2dSpot` but with less contrast and visibility.
132
+ ---
133
+ --- - `MiniJump2dDim` - highlighting of lines with at least one jump spot.
134
+ --- Make it non-bright in order for jump spot labels to be more visible.
135
+ --- By default linked to `Comment` highlight group.
136
+ ---
137
+ --- To change any highlight group, set it directly with |nvim_set_hl()|.
138
+ ---
139
+ --- # Disabling ~
140
+ ---
141
+ --- To disable, set `vim.g.minijump2d_disable` (globally) or
142
+ --- `vim.b.minijump2d_disable` (for a buffer) to `true`. Considering high
143
+ --- number of different scenarios and customization intentions, writing exact
144
+ --- rules for disabling module's functionality is left to user. See
145
+ --- |mini.nvim-disabling-recipes| for common recipes.
146
+ ---@tag MiniJump2d
147
+
148
+ -- Module definition ==========================================================
149
+ local MiniJump2d = {}
150
+ local H = {}
151
+
152
+ --- Module setup
153
+ ---
154
+ ---@param config table|nil Module config table. See |MiniJump2d.config|.
155
+ ---
156
+ ---@usage >lua
157
+ --- require('mini.jump2d').setup() -- use default config
158
+ --- -- OR
159
+ --- require('mini.jump2d').setup({}) -- replace {} with your config table
160
+ --- <
161
+ MiniJump2d.setup = function(config)
162
+ -- TODO: Remove after Neovim=0.9 support is dropped
163
+ if vim.fn.has('nvim-0.10') == 0 then
164
+ vim.notify(
165
+ '(mini.jump2d) Neovim<0.10 is soft deprecated (module works but is not supported).'
166
+ .. " It will be deprecated after the next 'mini.nvim' release (module might not work)."
167
+ .. ' Please update your Neovim version.'
168
+ )
169
+ end
170
+
171
+ -- Export module
172
+ _G.MiniJump2d = MiniJump2d
173
+
174
+ -- Setup config
175
+ config = H.setup_config(config)
176
+
177
+ -- Apply config
178
+ H.apply_config(config)
179
+
180
+ -- Define behavior
181
+ H.create_autocommands(config)
182
+
183
+ -- Create default highlighting
184
+ H.create_default_hl()
185
+ end
186
+
187
+ --- Defaults ~
188
+ ---@eval return MiniDoc.afterlines_to_code(MiniDoc.current.eval_section)
189
+ ---@text # Spotter function ~
190
+ ---
191
+ --- Actual computation of possible jump spots is done through spotter function.
192
+ --- It should have the following arguments:
193
+ --- - `line_num` is a line number inside buffer.
194
+ --- - `args` - table with additional arguments:
195
+ --- - {win_id} - identifier of a window where input line number is from.
196
+ --- - {win_id_init} - identifier of a window which was current when
197
+ --- `MiniJump2d.start()` was called.
198
+ ---
199
+ --- Its output is a list of byte-indexed positions that should be considered as
200
+ --- possible jump spots for this particular line in this particular window.
201
+ --- Note: for a more aligned visualization this list should be (but not
202
+ --- strictly necessary) sorted increasingly.
203
+ ---
204
+ --- Note: spotter function is always called with `win_id` window being
205
+ --- "temporary current" (see |nvim_win_call()|). This allows using builtin
206
+ --- Vimscript functions that operate only inside current window.
207
+ ---
208
+ --- # View ~
209
+ ---
210
+ --- Option `view.n_steps_ahead` controls how many steps ahead to show along
211
+ --- with the currently required label. Those future steps are shown with
212
+ --- different (less visible) highlight group ("MiniJump2dSpotAhead"). Usually
213
+ --- it is a good idea to use this with a spotter which doesn't result into many
214
+ --- jump spots (like, for example, |MiniJump2d.builtin_opts.word_start|).
215
+ --- Default is 0 to not show anything ahead as it reduces visual noise.
216
+ ---
217
+ --- Option `view.dim` controls whether to dim lines with at least one jump spot.
218
+ --- Dimming is done by applying "MiniJump2dDim" highlight group to the whole line.
219
+ ---
220
+ --- # Allowed lines ~
221
+ ---
222
+ --- Option `allowed_lines` controls which lines will be used for computing
223
+ --- possible jump spots:
224
+ --- - If `blank` or `fold` is `true`, it is possible to jump to first column of blank
225
+ --- line (determined by |prevnonblank()|) or first folded one (determined by
226
+ --- |foldclosed()|) respectively. Otherwise they are skipped. These lines are
227
+ --- not processed by spotter function even if the option is `true`.
228
+ --- - If `cursor_before`, (`cursor_at`, `cursor_after`) is `true`, lines before
229
+ --- (at, after) cursor line of all processed windows are forwarded to spotter
230
+ --- function. Otherwise, they don't. This allows control of jump "direction".
231
+ ---
232
+ --- # Hooks ~
233
+ ---
234
+ --- Following hook functions can be used to further tweak jumping experience:
235
+ --- - `before_start` - called without arguments first thing when jump starts.
236
+ --- One of the possible use cases is to ask for user input and update spotter
237
+ --- function with it.
238
+ --- - `after_jump` - called after jump was actually done. Useful to make
239
+ --- post-adjustments (like move cursor to first non-whitespace character).
240
+ MiniJump2d.config = {
241
+ -- Function producing jump spots (byte indexed) for a particular line.
242
+ -- For more information see |MiniJump2d.start()|.
243
+ -- If `nil` (default) - use |MiniJump2d.default_spotter()|
244
+ spotter = nil,
245
+
246
+ -- Characters used for labels of jump spots (in supplied order)
247
+ labels = 'abcdefghijklmnopqrstuvwxyz',
248
+
249
+ -- Options for visual effects
250
+ view = {
251
+ -- Whether to dim lines with at least one jump spot
252
+ dim = false,
253
+
254
+ -- How many steps ahead to show. Set to big number to show all steps.
255
+ n_steps_ahead = 0,
256
+ },
257
+
258
+ -- Which lines are used for computing spots
259
+ allowed_lines = {
260
+ blank = true, -- Blank line (not sent to spotter even if `true`)
261
+ cursor_before = true, -- Lines before cursor line
262
+ cursor_at = true, -- Cursor line
263
+ cursor_after = true, -- Lines after cursor line
264
+ fold = true, -- Start of fold (not sent to spotter even if `true`)
265
+ },
266
+
267
+ -- Which windows from current tabpage are used for visible lines
268
+ allowed_windows = {
269
+ current = true,
270
+ not_current = true,
271
+ },
272
+
273
+ -- Functions to be executed at certain events
274
+ hooks = {
275
+ before_start = nil, -- Before jump start
276
+ after_jump = nil, -- After jump was actually done
277
+ },
278
+
279
+ -- Module mappings. Use `''` (empty string) to disable one.
280
+ mappings = {
281
+ start_jumping = '<CR>',
282
+ },
283
+
284
+ -- Whether to disable showing non-error feedback
285
+ -- This also affects (purely informational) helper messages shown after
286
+ -- idle time if user input is required.
287
+ silent = false,
288
+ }
289
+ --minidoc_afterlines_end
290
+
291
+ -- Module functionality =======================================================
292
+ --- Start jumping
293
+ ---
294
+ --- Compute possible jump spots, visualize them and wait for iterative filtering.
295
+ ---
296
+ --- # First computation of possible jump spots ~
297
+ ---
298
+ --- - Process allowed windows (current and/or not current; controlled by
299
+ --- `allowed_windows` option) by visible lines from top to bottom. For each
300
+ --- one see if it is allowed (controlled by `allowed_lines` option). If not
301
+ --- allowed, then do nothing. If allowed and should be processed by
302
+ --- `spotter`, process it.
303
+ --- - Apply spotter function from `spotter` option for each appropriate line
304
+ --- and concatenate outputs. This means that eventual order of jump spots
305
+ --- aligns with lexicographical order within "window id" - "line number" -
306
+ --- "position in `spotter` output" tuples.
307
+ --- - For each possible jump compute its label: a single character from
308
+ --- `labels` option used to filter jump spots. Each possible label character
309
+ --- might be used more than once to label several "consecutive" jump spots.
310
+ --- It is done in an optimal way under assumption of no preference of one
311
+ --- spot over another. Basically, it means "use all labels at each step of
312
+ --- iterative filtering as equally as possible".
313
+ ---
314
+ --- # Visualization ~
315
+ ---
316
+ --- Current label for each possible jump spot is shown at that position
317
+ --- overriding everything underneath it.
318
+ ---
319
+ --- # Iterative filtering ~
320
+ ---
321
+ --- Labels of possible jump spots are computed in order to use them as equally
322
+ --- as possible.
323
+ ---
324
+ --- Example:
325
+ --- - With `abc` as `labels` option, initial labels for 10 possible jumps
326
+ --- are "aaaabbbccc". As there are 10 spots which should be "coded" with 3
327
+ --- symbols, at least 2 symbols need 3 steps to filter them out. With current
328
+ --- implementation those are always the "first ones".
329
+ --- - After typing `a`, it filters first four jump spots and recomputes its
330
+ --- labels to be "aabc".
331
+ --- - After typing `a` again, it filters first two spots and recomputes its
332
+ --- labels to be "ab".
333
+ --- - After typing either `a` or `b` it filters single spot and makes jump.
334
+ ---
335
+ --- With default 26 labels for most real-world cases 2 steps is enough for
336
+ --- default spotter function. Rarely 3 steps are needed with several windows.
337
+ ---
338
+ ---@param opts table|nil Configuration of jumping, overriding global and buffer
339
+ --- local values. Has the same structure as |MiniJump2d.config|
340
+ --- without <mappings> field. Extra allowed fields:
341
+ --- - <hl_group> - highlight group for first step.
342
+ --- Default: `"MiniJump2dSpot"`.
343
+ --- - <hl_group_ahead> - highlight group for second and later steps.
344
+ --- Default: `"MiniJump2dSpotAhead"`.
345
+ --- - <hl_group_dim> - highlight group for dimming used lines.
346
+ --- Default: `"MiniJump2dDim"`.
347
+ --- - <hl_group_unique> - highlight group for unique next step.
348
+ --- Default: `"MiniJump2dSpotUnique"`.
349
+ ---
350
+ ---@usage >lua
351
+ --- -- Start default jumping
352
+ --- MiniJump2d.start()
353
+ ---
354
+ --- -- Jump to word start
355
+ --- MiniJump2d.start(MiniJump2d.builtin_opts.word_start)
356
+ ---
357
+ --- -- Jump to single character from user input (follow by typing one character)
358
+ --- MiniJump2d.start(MiniJump2d.builtin_opts.single_character)
359
+ ---
360
+ --- -- Jump to first character of punctuation group only inside current window
361
+ --- -- which is placed at cursor line; visualize with `Search`
362
+ --- MiniJump2d.start({
363
+ --- spotter = MiniJump2d.gen_spotter.pattern('%p+'),
364
+ --- allowed_lines = { cursor_before = false, cursor_after = false },
365
+ --- allowed_windows = { not_current = false },
366
+ --- hl_group = 'Search'
367
+ --- })
368
+ --- <
369
+ ---@seealso |MiniJump2d.config|
370
+ MiniJump2d.start = function(opts)
371
+ if H.is_disabled() then return end
372
+
373
+ opts = opts or {}
374
+
375
+ -- Apply `before_start` before `tbl_deep_extend` to allow it modify options
376
+ -- inside it (notably `spotter`). Example: `builtins.single_character`.
377
+ local before_start = (opts.hooks or {}).before_start
378
+ or ((vim.b.minijump2d_config or {}).hooks or {}).before_start
379
+ or MiniJump2d.config.hooks.before_start
380
+ if before_start ~= nil then before_start() end
381
+
382
+ opts = H.get_config(opts)
383
+ opts.spotter = opts.spotter or MiniJump2d.default_spotter
384
+ opts.hl_group = opts.hl_group or 'MiniJump2dSpot'
385
+ opts.hl_group_ahead = opts.hl_group_ahead or 'MiniJump2dSpotAhead'
386
+ opts.hl_group_unique = opts.hl_group_unique or 'MiniJump2dSpotUnique'
387
+ opts.hl_group_dim = opts.hl_group_dim or 'MiniJump2dDim'
388
+
389
+ local spots = H.spots_compute(opts)
390
+ if #spots == 0 then
391
+ H.message('No spots to show.')
392
+ return
393
+ end
394
+ if #spots == 1 then
395
+ H.perform_jump(spots[1], opts.hooks.after_jump)
396
+ return
397
+ end
398
+
399
+ local label_tbl = vim.split(opts.labels, '')
400
+ spots = H.spots_add_steps(spots, label_tbl, opts.view.n_steps_ahead)
401
+
402
+ H.spots_show(spots, opts)
403
+
404
+ H.cache.spots = spots
405
+
406
+ H.advance_jump(opts)
407
+ end
408
+
409
+ --- Stop jumping
410
+ MiniJump2d.stop = function()
411
+ H.spots_unshow()
412
+ H.cache.spots = nil
413
+ H.cache.msg_shown = false
414
+ vim.cmd('redraw')
415
+
416
+ if H.cache.is_in_getcharstr then vim.api.nvim_input('<C-c>') end
417
+ end
418
+
419
+ --- Generate spotter
420
+ ---
421
+ --- This is a table with function elements. Call to actually get a spotter.
422
+ MiniJump2d.gen_spotter = {}
423
+
424
+ --- Generate spotter for Lua pattern
425
+ ---
426
+ ---@param pattern string|nil Lua pattern. Default: `'[^%s%p]+'` which matches group
427
+ --- of "non-whitespace non-punctuation characters" (basically a way of saying
428
+ --- "group of alphanumeric characters" that works with multibyte characters).
429
+ ---@param side string|nil Which side of pattern match should be considered as
430
+ --- jumping spot. Should be one of `'start'` (start of match, default), `'end'`
431
+ --- (inclusive end of match), or `'none'` (match for spot is done manually
432
+ --- inside pattern with plain `()` matching group).
433
+ ---
434
+ ---@return function Spotter function.
435
+ ---
436
+ ---@usage >lua
437
+ --- -- Match any punctuation
438
+ --- MiniJump2d.gen_spotter.pattern('%p')
439
+ ---
440
+ --- -- Match first from line start non-whitespace character
441
+ --- MiniJump2d.gen_spotter.pattern('^%s*%S', 'end')
442
+ ---
443
+ --- -- Match start of last word
444
+ --- MiniJump2d.gen_spotter.pattern('[^%s%p]+[%s%p]-$', 'start')
445
+ ---
446
+ --- -- Match letter followed by another letter (example of manual matching
447
+ --- -- inside pattern)
448
+ --- MiniJump2d.gen_spotter.pattern('%a()%a', 'none')
449
+ --- <
450
+ MiniJump2d.gen_spotter.pattern = function(pattern, side)
451
+ -- Don't use `%w` to account for multibyte characters
452
+ pattern = pattern or '[^%s%p]+'
453
+ side = side or 'start'
454
+
455
+ -- Process anchored patterns separately because:
456
+ -- - `gmatch()` doesn't work if pattern start with `^`.
457
+ -- - Manual adding of `()` will conflict with anchors.
458
+ local is_anchored = pattern:sub(1, 1) == '^' or pattern:sub(-1, -1) == '$'
459
+ if is_anchored then
460
+ return function(line_num, args)
461
+ local line = vim.fn.getline(line_num)
462
+ local s, e, m = line:find(pattern)
463
+ return { ({ ['start'] = s, ['end'] = e, ['none'] = m })[side] }
464
+ end
465
+ end
466
+
467
+ -- Handle `side = 'end'` later by appending length of match to match start.
468
+ -- This, unlike appending `()` to end of pattern, makes output spot to be
469
+ -- inside matched pattern and on its exact right.
470
+ -- Having `(%s)` for `side = 'none'` is for compatibility with later `gmatch`
471
+ local pattern_template = side == 'none' and '(%s)' or '(()%s)'
472
+ pattern = pattern_template:format(pattern)
473
+
474
+ return function(line_num, args)
475
+ local line = vim.fn.getline(line_num)
476
+ local res = {}
477
+ -- NOTE: maybe a more straightforward approach would be a series of
478
+ -- `line:find(original_pattern, init)` with moving `init`, but it has some
479
+ -- weird behavior with quantifiers.
480
+ -- For example: `string.find(' --', '%s*', 4)` returns `4 3`.
481
+ for whole, spot in string.gmatch(line, pattern) do
482
+ -- Possibly correct spot to be index of last matched position
483
+ if side == 'end' then spot = spot + math.max(whole:len() - 1, 0) end
484
+
485
+ -- Ensure that index is strictly within line length (which can be not
486
+ -- true in case of weird pattern, like when using frontier `%f[%W]`)
487
+ spot = math.min(math.max(spot, 0), line:len())
488
+
489
+ -- Unify how spot is chosen in case of multibyte characters
490
+ -- Use `+-1` to make sure that result is at start of multibyte character
491
+ local utf_index = H.str_utfindex(line, spot) - 1
492
+ spot = H.str_byteindex(line, utf_index) + 1
493
+
494
+ -- Add spot only if it referces new actually visible column
495
+ if spot ~= res[#res] then table.insert(res, spot) end
496
+ end
497
+ return res
498
+ end
499
+ end
500
+
501
+ -- TODO: Remove after releasing 'mini.nvim' 0.17.0
502
+ MiniJump2d.gen_pattern_spotter = function(pattern, side)
503
+ local msg = '`gen_pattern_spotter` is moved to `gen_spotter.pattern` for consistency with other modules.'
504
+ .. ' It still works for now, but will stop working after the next release.'
505
+ .. ' Sorry for the inconvenience.'
506
+ H.notify(msg, 'WARN')
507
+ return MiniJump2d.gen_spotter.pattern(pattern, side)
508
+ end
509
+
510
+ --- Generate spotter for Vimscript pattern
511
+ ---
512
+ ---@param pattern string|nil Vimscript |pattern|. Default: `\k\+` to match group
513
+ --- of "keyword characters" (see |'iskeyword'|).
514
+ ---
515
+ ---@return function Spotter function.
516
+ ---
517
+ ---@usage >lua
518
+ --- -- Match start of a keyword
519
+ --- MiniJump2d.gen_spotter.vimpattern('\\k\\+')
520
+ ---
521
+ --- -- Match end of a keyword
522
+ --- MiniJump2d.gen_spotter.vimpattern('\\k*\\zs\\k')
523
+ --- <
524
+ MiniJump2d.gen_spotter.vimpattern = function(pattern)
525
+ pattern = pattern or '\\k\\+'
526
+ if type(pattern) ~= 'string' then H.error('`pattern` should be string') end
527
+ local r = vim.regex(pattern)
528
+ local is_anchored = pattern:sub(1, 1) == '^' or pattern:sub(-1, -1) == '$'
529
+
530
+ return function(line_num, _)
531
+ local res, l, start = {}, vim.fn.getline(line_num), 1
532
+ local n = is_anchored and 1 or (l:len() + 1)
533
+ for _ = 1, n do
534
+ local from, to = r:match_str(l)
535
+ if from == nil then break end
536
+ table.insert(res, from + start)
537
+ l, start = l:sub(to + 1), start + to
538
+ end
539
+ return res
540
+ end
541
+ end
542
+
543
+ --- Generate union of spotters
544
+ ---
545
+ ---@param ... any Each argument should be a valid spotter.
546
+ --- See |MiniJump2d.config| for more details.
547
+ ---
548
+ ---@return function Spotter producing union of spots.
549
+ ---
550
+ ---@usage >lua
551
+ --- -- Match start and end of non-blank character groups:
552
+ --- local nonblank_start = MiniJump2d.gen_spotter.pattern('%S+', 'start')
553
+ --- local nonblank_end = MiniJump2d.gen_spotter.pattern('%S+', 'end')
554
+ --- local spotter = MiniJump2d.gen_spotter.union(nonblank_start, nonblank_end)
555
+ --- <
556
+ MiniJump2d.gen_spotter.union = function(...)
557
+ local spotters = { ... }
558
+ if #spotters == 0 then
559
+ return function() return {} end
560
+ end
561
+
562
+ local is_all_callable = true
563
+ for _, x in ipairs(spotters) do
564
+ if not vim.is_callable(x) then is_all_callable = false end
565
+ end
566
+
567
+ if not is_all_callable then H.error('All `gen_spotter.union()` arguments should be callable elements.') end
568
+
569
+ return function(line_num, args)
570
+ local res = spotters[1](line_num, args)
571
+ for i = 2, #spotters do
572
+ res = H.merge_unique(res, spotters[i](line_num, args))
573
+ end
574
+ return res
575
+ end
576
+ end
577
+
578
+ -- TODO: Remove after releasing 'mini.nvim' 0.17.0
579
+ MiniJump2d.gen_union_spotter = function(...)
580
+ local msg = '`gen_union_spotter` is moved to `gen_spotter.union` for consistency with other modules.'
581
+ .. ' It still works for now, but will stop working after the next release.'
582
+ .. ' Sorry for the inconvenience.'
583
+ H.notify(msg, 'WARN')
584
+ return MiniJump2d.gen_spotter.union(...)
585
+ end
586
+
587
+ --- Default spotter function
588
+ ---
589
+ --- Spot is possible for jump if it is one of the following:
590
+ --- - Start or end of non-whitespace character group.
591
+ --- - Alphanumeric character followed or preceded by punctuation (useful for
592
+ --- snake case names).
593
+ --- - Start of uppercase character group (useful for camel case names). Usually
594
+ --- only Latin alphabet is recognized due to Lua patterns shortcomings.
595
+ ---
596
+ --- These rules are derived in an attempt to balance between two intentions:
597
+ --- - Allow as much useful jumping spots as possible.
598
+ --- - Make labeled jump spots easily distinguishable.
599
+ ---
600
+ --- Usually takes from 2 to 3 keystrokes to get to destination.
601
+ ---@tag MiniJump2d.default_spotter()
602
+ MiniJump2d.default_spotter = (function()
603
+ -- NOTE: not using `MiniJump2d.gen_spotter.union()` due to slightly better
604
+ -- algorithmic complexity merging small arrays first.
605
+ local nonblank_start = MiniJump2d.gen_spotter.pattern('%S+', 'start')
606
+ local nonblank_end = MiniJump2d.gen_spotter.pattern('%S+', 'end')
607
+ -- Use `[^%s%p]` as "alphanumeric" to allow working with multibyte characters
608
+ local alphanum_before_punct = MiniJump2d.gen_spotter.pattern('[^%s%p]%p', 'start')
609
+ local alphanum_after_punct = MiniJump2d.gen_spotter.pattern('%p[^%s%p]', 'end')
610
+ -- NOTE: works only with Latin alphabet
611
+ local upper_start = MiniJump2d.gen_spotter.pattern('%u+', 'start')
612
+
613
+ return function(line_num, args)
614
+ local res_1 = H.merge_unique(nonblank_start(line_num, args), nonblank_end(line_num, args))
615
+ local res_2 = H.merge_unique(alphanum_before_punct(line_num, args), alphanum_after_punct(line_num, args))
616
+ local res = H.merge_unique(res_1, res_2)
617
+ return H.merge_unique(res, upper_start(line_num, args))
618
+ end
619
+ end)()
620
+
621
+ --- Table with builtin `opts` values for |MiniJump2d.start()|
622
+ ---
623
+ --- Each element of table is itself a table defining one or several options for
624
+ --- `MiniJump2d.start()`. Read help description to see which options it defines
625
+ --- (like in |MiniJump2d.builtin_opts.line_start|).
626
+ ---
627
+ ---@usage >lua
628
+ --- -- Using `MiniJump2d.builtin_opts.line_start` as example:
629
+ --- -- Command
630
+ --- :lua MiniJump2d.start(MiniJump2d.builtin_opts.line_start)
631
+ ---
632
+ --- -- Custom mapping
633
+ --- vim.keymap.set(
634
+ --- 'n', '<CR>',
635
+ --- '<Cmd>lua MiniJump2d.start(MiniJump2d.builtin_opts.line_start)<CR>'
636
+ --- )
637
+ ---
638
+ --- -- Inside `MiniJump2d.setup()` (make sure to use all defined options)
639
+ --- local jump2d = require('mini.jump2d')
640
+ --- local jump_line_start = jump2d.builtin_opts.line_start
641
+ --- jump2d.setup({
642
+ --- spotter = jump_line_start.spotter,
643
+ --- hooks = { after_jump = jump_line_start.hooks.after_jump }
644
+ --- })
645
+ --- <
646
+ MiniJump2d.builtin_opts = {}
647
+
648
+ --- Jump with |MiniJump2d.default_spotter()|
649
+ ---
650
+ --- Defines `spotter`.
651
+ MiniJump2d.builtin_opts.default = { spotter = MiniJump2d.default_spotter }
652
+
653
+ --- Jump to line start
654
+ ---
655
+ --- Defines `spotter` and `hooks.after_jump`.
656
+ MiniJump2d.builtin_opts.line_start = {
657
+ spotter = function(line_num, args) return { 1 } end,
658
+ hooks = {
659
+ after_jump = function()
660
+ -- Move to first non-blank character
661
+ vim.cmd('normal! ^')
662
+ end,
663
+ },
664
+ }
665
+
666
+ --- Jump to word start
667
+ ---
668
+ --- Respects |'iskeyword'| when computing word start.
669
+ ---
670
+ --- Defines `spotter`.
671
+ MiniJump2d.builtin_opts.word_start = { spotter = MiniJump2d.gen_spotter.vimpattern('\\k\\+') }
672
+
673
+ -- Produce `opts` which modifies spotter based on user input
674
+ local function user_input_opts(input_fun)
675
+ local res = {
676
+ spotter = function() return {} end,
677
+ allowed_lines = { blank = false, fold = false },
678
+ }
679
+
680
+ local before_start = function()
681
+ local input = input_fun()
682
+ -- Allow user to cancel input and not show any jumping spots
683
+ if input == nil then return end
684
+ res.spotter = MiniJump2d.gen_spotter.pattern(vim.pesc(input))
685
+ end
686
+ res.hooks = { before_start = before_start }
687
+
688
+ return res
689
+ end
690
+
691
+ --- Jump to single character taken from user input
692
+ ---
693
+ --- Defines `spotter`, `allowed_lines.blank`, `allowed_lines.fold`, and
694
+ --- `hooks.before_start`.
695
+ MiniJump2d.builtin_opts.single_character = user_input_opts(
696
+ function() return H.getcharstr('Reminder to press a character to search') end
697
+ )
698
+
699
+ --- Jump to query taken from user input
700
+ ---
701
+ --- Defines `spotter`, `allowed_lines.blank`, `allowed_lines.fold`, and
702
+ --- `hooks.before_start`.
703
+ MiniJump2d.builtin_opts.query = user_input_opts(function() return H.user_input('Enter query to search') end)
704
+
705
+ -- Helper data ================================================================
706
+ -- Module default config
707
+ H.default_config = vim.deepcopy(MiniJump2d.config)
708
+
709
+ -- Namespaces to be used within module
710
+ H.ns_id = {
711
+ dim = vim.api.nvim_create_namespace('MiniJump2dDim'),
712
+ spots = vim.api.nvim_create_namespace('MiniJump2dSpots'),
713
+ input = vim.api.nvim_create_namespace('MiniJump2dInput'),
714
+ }
715
+
716
+ -- Table with current relevant data:
717
+ H.cache = {
718
+ -- Array of shown spots
719
+ spots = nil,
720
+
721
+ -- Indicator of whether Neovim is currently in "getcharstr" mode
722
+ is_in_getcharstr = false,
723
+
724
+ -- Whether helper message was shown
725
+ msg_shown = false,
726
+ }
727
+
728
+ -- Table with special keys
729
+ H.keys = {
730
+ esc = vim.api.nvim_replace_termcodes('<Esc>', true, true, true),
731
+ cr = vim.api.nvim_replace_termcodes('<CR>', true, true, true),
732
+ block_operator_pending = vim.api.nvim_replace_termcodes('no<C-V>', true, true, true),
733
+ }
734
+
735
+ -- Helper functionality =======================================================
736
+ -- Settings -------------------------------------------------------------------
737
+ H.setup_config = function(config)
738
+ H.check_type('config', config, 'table', true)
739
+ config = vim.tbl_deep_extend('force', vim.deepcopy(H.default_config), config or {})
740
+
741
+ H.check_type('spotter', config.spotter, 'function', true)
742
+ H.check_type('labels', config.labels, 'string')
743
+
744
+ H.check_type('view', config.view, 'table')
745
+ H.check_type('view.dim', config.view.dim, 'boolean')
746
+ H.check_type('view.n_steps_ahead', config.view.n_steps_ahead, 'number')
747
+
748
+ H.check_type('allowed_lines', config.allowed_lines, 'table')
749
+ H.check_type('allowed_lines.blank', config.allowed_lines.blank, 'boolean')
750
+ H.check_type('allowed_lines.cursor_before', config.allowed_lines.cursor_before, 'boolean')
751
+ H.check_type('allowed_lines.cursor_at', config.allowed_lines.cursor_at, 'boolean')
752
+ H.check_type('allowed_lines.cursor_after', config.allowed_lines.cursor_after, 'boolean')
753
+ H.check_type('allowed_lines.fold', config.allowed_lines.fold, 'boolean')
754
+
755
+ H.check_type('allowed_windows', config.allowed_windows, 'table')
756
+ H.check_type('allowed_windows.current', config.allowed_windows.current, 'boolean')
757
+ H.check_type('allowed_windows.not_current', config.allowed_windows.not_current, 'boolean')
758
+
759
+ H.check_type('hooks', config.hooks, 'table')
760
+ H.check_type('hooks.before_start', config.hooks.before_start, 'function', true)
761
+ H.check_type('hooks.after_jump', config.hooks.after_jump, 'function', true)
762
+
763
+ H.check_type('mappings', config.mappings, 'table')
764
+ H.check_type('mappings.start_jumping', config.mappings.start_jumping, 'string')
765
+
766
+ H.check_type('silent', config.silent, 'boolean')
767
+
768
+ return config
769
+ end
770
+
771
+ H.apply_config = function(config)
772
+ MiniJump2d.config = config
773
+
774
+ -- Apply mappings
775
+ local keymap = config.mappings.start_jumping
776
+ H.map('n', keymap, MiniJump2d.start, { desc = 'Start 2d jumping' })
777
+ H.map('x', keymap, MiniJump2d.start, { desc = 'Start 2d jumping' })
778
+ -- Use `<Cmd>...<CR>` to have proper dot-repeat
779
+ -- See https://github.com/neovim/neovim/issues/23406
780
+ -- TODO: use local functions if/when that issue is resolved
781
+ H.map('o', keymap, '<Cmd>lua MiniJump2d.start()<CR>', { desc = 'Start 2d jumping' })
782
+ end
783
+
784
+ H.create_autocommands = function(config)
785
+ local gr = vim.api.nvim_create_augroup('MiniJump2d', {})
786
+
787
+ local au = function(event, pattern, callback, desc)
788
+ vim.api.nvim_create_autocmd(event, { pattern = pattern, group = gr, callback = callback, desc = desc })
789
+ end
790
+
791
+ -- Corrections for default `<CR>` mapping to not interfere with popular usages
792
+ if config.mappings.start_jumping == '<CR>' then
793
+ local revert_cr = function() vim.keymap.set('n', '<CR>', '<CR>', { buffer = true }) end
794
+ au('FileType', 'qf', revert_cr, 'Revert <CR>')
795
+ au('CmdwinEnter', '*', revert_cr, 'Revert <CR>')
796
+ end
797
+
798
+ au('ColorScheme', '*', H.create_default_hl, 'Ensure colors')
799
+ end
800
+
801
+ --stylua: ignore
802
+ H.create_default_hl = function()
803
+ local set_default_hl = function(name, data)
804
+ data.default = true
805
+ vim.api.nvim_set_hl(0, name, data)
806
+ end
807
+
808
+ local is_light_bg = vim.o.background == 'light'
809
+ local bg_color = is_light_bg and 'white' or 'black'
810
+ local fg_color = is_light_bg and 'black' or 'white'
811
+
812
+ set_default_hl('MiniJump2dSpot', { fg = fg_color, bg = bg_color, bold = true, nocombine = true })
813
+ set_default_hl('MiniJump2dSpotAhead', { fg = 'grey', bg = bg_color, nocombine = true })
814
+ set_default_hl('MiniJump2dSpotUnique', { link = 'MiniJump2dSpot' })
815
+ set_default_hl('MiniJump2dDim', { link = 'Comment' })
816
+ end
817
+
818
+ H.is_disabled = function() return vim.g.minijump2d_disable == true or vim.b.minijump2d_disable == true end
819
+
820
+ H.get_config = function(config)
821
+ return vim.tbl_deep_extend('force', MiniJump2d.config, vim.b.minijump2d_config or {}, config or {})
822
+ end
823
+
824
+ -- Jump spots -----------------------------------------------------------------
825
+ H.spots_compute = function(opts)
826
+ local win_id_init, allowed = vim.api.nvim_get_current_win(), opts.allowed_windows
827
+ local win_id_arr = vim.tbl_filter(function(win_id)
828
+ if not vim.api.nvim_win_get_config(win_id).focusable then return false end
829
+ if win_id == win_id_init then return allowed.current end
830
+ return allowed.not_current
831
+ end, H.tabpage_list_wins(0))
832
+
833
+ local res = {}
834
+ for _, win_id in ipairs(win_id_arr) do
835
+ vim.api.nvim_win_call(win_id, function()
836
+ local cursor_pos = vim.api.nvim_win_get_cursor(win_id)
837
+ local spotter_args = { win_id = win_id, win_id_init = win_id_init }
838
+ local buf_id = vim.api.nvim_win_get_buf(win_id)
839
+
840
+ -- Use all currently visible lines
841
+ for i = vim.fn.line('w0'), vim.fn.line('w$') do
842
+ local columns = H.spot_find_in_line(i, spotter_args, opts, cursor_pos)
843
+ -- Use all returned columns for particular line
844
+ for _, col in ipairs(columns) do
845
+ table.insert(res, { line = i, column = col, buf_id = buf_id, win_id = win_id })
846
+ end
847
+ end
848
+ end)
849
+ end
850
+ return res
851
+ end
852
+
853
+ H.spots_add_steps = function(spots, label_tbl, n_steps_ahead)
854
+ -- Compute all required steps
855
+ local steps = {}
856
+ for _ = 1, #spots do
857
+ table.insert(steps, {})
858
+ end
859
+
860
+ H.populate_spot_steps(steps, label_tbl, 1, n_steps_ahead + 1)
861
+
862
+ for i, spot in ipairs(spots) do
863
+ spot.steps = steps[i]
864
+ end
865
+
866
+ return spots
867
+ end
868
+
869
+ ---@param spot_steps_arr table Array of step arrays. Single step array consists
870
+ --- from labels user needs to press in order to filter out the spot. Example:
871
+ --- { { 'a', 'a' }, { 'a', 'b' }, { 'b' } }
872
+ ---
873
+ ---@return nil Modifies `spot_steps_arr` in place.
874
+ ---@private
875
+ H.populate_spot_steps = function(spot_steps_arr, label_tbl, cur_step, max_step)
876
+ local n_spots, n_label_chars = #spot_steps_arr, #label_tbl
877
+ if n_spots <= 1 or max_step < cur_step then return end
878
+
879
+ -- Adding labels for specific step is done by distributing all available
880
+ -- labels as equally as possible by repeating labels in their order.
881
+ -- Example: with 3 label characters labels should evolve with progressing
882
+ -- number of spots like this: 'a', 'ab', 'abc', 'aabc', 'aabbc', 'aabbcc',
883
+ -- 'aaabbcc', 'aaabbbcc', 'aaabbbccc', etc.
884
+ local base, extra = math.floor(n_spots / n_label_chars), n_spots % n_label_chars
885
+
886
+ -- `cur_label_spot_steps` is an array of spot steps which are expanded with
887
+ -- the same label. It is used to initiate computing all steps needed.
888
+ local label_id, cur_label_spot_steps = 1, {}
889
+ local label_max_count = base + (label_id <= extra and 1 or 0)
890
+ for _, spot_steps in ipairs(spot_steps_arr) do
891
+ table.insert(spot_steps, label_tbl[label_id])
892
+ table.insert(cur_label_spot_steps, spot_steps)
893
+
894
+ if #cur_label_spot_steps >= label_max_count then
895
+ H.populate_spot_steps(cur_label_spot_steps, label_tbl, cur_step + 1, max_step)
896
+ label_id, cur_label_spot_steps = label_id + 1, {}
897
+ label_max_count = base + (label_id <= extra and 1 or 0)
898
+ end
899
+ end
900
+ end
901
+
902
+ H.spots_show = function(spots, opts)
903
+ spots = spots or H.cache.spots or {}
904
+
905
+ local set_extmark = vim.api.nvim_buf_set_extmark
906
+
907
+ -- Add extmark with proper virtual text to jump spots
908
+ local dim_buf_lines = {}
909
+ for _, extmark in ipairs(H.spots_to_extmarks(spots, opts)) do
910
+ local extmark_opts = {
911
+ hl_mode = 'combine',
912
+ -- Use very high priority
913
+ priority = 1000,
914
+ virt_text = extmark.virt_text,
915
+ virt_text_pos = 'overlay',
916
+ }
917
+ local buf_id, line = extmark.buf_id, extmark.line
918
+ pcall(set_extmark, buf_id, H.ns_id.spots, line, extmark.col, extmark_opts)
919
+
920
+ -- Register lines to dim
921
+ local lines = dim_buf_lines[buf_id] or {}
922
+ lines[line] = true
923
+ dim_buf_lines[buf_id] = lines
924
+ end
925
+
926
+ -- Possibly dim used lines
927
+ if opts.view.dim then
928
+ local extmark_opts = { end_col = 0, hl_eol = true, hl_group = opts.hl_group_dim, priority = 999 }
929
+ for buf_id, lines in pairs(dim_buf_lines) do
930
+ for _, l_num in ipairs(vim.tbl_keys(lines)) do
931
+ extmark_opts.end_line = l_num + 1
932
+ pcall(set_extmark, buf_id, H.ns_id.dim, l_num, 0, extmark_opts)
933
+ end
934
+ end
935
+ end
936
+
937
+ -- Redraw to force showing marks
938
+ vim.cmd('redraw')
939
+ end
940
+
941
+ H.spots_unshow = function(spots)
942
+ spots = spots or H.cache.spots or {}
943
+
944
+ -- Remove spot extmarks from all buffers they are present
945
+ local buf_ids = {}
946
+ for _, s in ipairs(spots) do
947
+ buf_ids[s.buf_id] = true
948
+ end
949
+
950
+ for _, buf_id in ipairs(vim.tbl_keys(buf_ids)) do
951
+ pcall(vim.api.nvim_buf_clear_namespace, buf_id, H.ns_id.spots, 0, -1)
952
+ pcall(vim.api.nvim_buf_clear_namespace, buf_id, H.ns_id.dim, 0, -1)
953
+ end
954
+ end
955
+
956
+ --- Convert consecutive spot labels into single extmark
957
+ ---
958
+ --- This considerably increases performance in case of many spots.
959
+ ---@private
960
+ H.spots_to_extmarks = function(spots, opts)
961
+ if #spots == 0 then return {} end
962
+
963
+ local hl_group, hl_group_ahead, hl_group_unique = opts.hl_group, opts.hl_group_ahead, opts.hl_group_unique
964
+
965
+ -- Compute counts for first step in order to distinguish which highlight
966
+ -- group to use: `hl_group` or `hl_group_unique`
967
+ local first_step_counts = {}
968
+ for _, s in ipairs(spots) do
969
+ local cur_first_step = s.steps[1]
970
+ local cur_count = first_step_counts[cur_first_step] or 0
971
+ first_step_counts[cur_first_step] = cur_count + 1
972
+ end
973
+
974
+ -- Define how steps for single spot are added to virtual text
975
+ local append_to_virt_text = function(virt_text_arr, steps, n_steps_to_show)
976
+ -- Use special group if current first step is unique
977
+ local first_hl_group = first_step_counts[steps[1]] == 1 and hl_group_unique or hl_group
978
+ table.insert(virt_text_arr, { steps[1], first_hl_group })
979
+
980
+ -- Add ahead steps only if they are present
981
+ local ahead_label = table.concat(steps):sub(2, n_steps_to_show)
982
+ if ahead_label ~= '' then table.insert(virt_text_arr, { ahead_label, hl_group_ahead }) end
983
+ end
984
+
985
+ -- Convert all spots to array of extmarks
986
+ local res = {}
987
+ local buf_id, line, col, virt_text = spots[1].buf_id, spots[1].line - 1, spots[1].column - 1, {}
988
+
989
+ for i = 1, #spots - 1 do
990
+ local cur_spot, next_spot = spots[i], spots[i + 1]
991
+ local n_steps = #cur_spot.steps
992
+
993
+ -- Find which spot steps can be shown
994
+ local is_in_same_line = cur_spot.buf_id == next_spot.buf_id and cur_spot.line == next_spot.line
995
+ local max_allowed_steps = is_in_same_line and (next_spot.column - cur_spot.column) or math.huge
996
+ local n_steps_to_show = math.min(n_steps, max_allowed_steps)
997
+
998
+ -- Add text for shown steps
999
+ append_to_virt_text(virt_text, cur_spot.steps, n_steps_to_show)
1000
+
1001
+ -- Finish creating extmark if next spot is far enough
1002
+ local next_is_close = is_in_same_line and n_steps == max_allowed_steps
1003
+ if not next_is_close then
1004
+ table.insert(res, { buf_id = buf_id, line = line, col = col, virt_text = virt_text })
1005
+ buf_id, line, col, virt_text = next_spot.buf_id, next_spot.line - 1, next_spot.column - 1, {}
1006
+ end
1007
+ end
1008
+
1009
+ local last_steps = spots[#spots].steps
1010
+ append_to_virt_text(virt_text, last_steps, #last_steps)
1011
+ table.insert(res, { buf_id = buf_id, line = line, col = col, virt_text = virt_text })
1012
+
1013
+ return res
1014
+ end
1015
+
1016
+ H.spot_find_in_line = function(line_num, spotter_args, opts, cursor_pos)
1017
+ local allowed = opts.allowed_lines
1018
+
1019
+ -- Adjust for cursor line
1020
+ local cur_line = cursor_pos[1]
1021
+ if
1022
+ (not allowed.cursor_before and line_num < cur_line)
1023
+ or (not allowed.cursor_at and line_num == cur_line)
1024
+ or (not allowed.cursor_after and line_num > cur_line)
1025
+ then
1026
+ return {}
1027
+ end
1028
+
1029
+ -- Process folds
1030
+ local fold_indicator = vim.fn.foldclosed(line_num)
1031
+ if fold_indicator ~= -1 then return (allowed.fold and fold_indicator == line_num) and { 1 } or {} end
1032
+
1033
+ -- Process blank lines
1034
+ if vim.fn.prevnonblank(line_num) ~= line_num then return allowed.blank and { 1 } or {} end
1035
+
1036
+ -- Finally apply spotter
1037
+ return opts.spotter(line_num, spotter_args)
1038
+ end
1039
+
1040
+ -- Jump state -----------------------------------------------------------------
1041
+ H.advance_jump = function(opts)
1042
+ local label_tbl = vim.split(opts.labels, '')
1043
+
1044
+ local spots = H.cache.spots
1045
+ local n_steps_ahead = opts.view.n_steps_ahead
1046
+
1047
+ if type(spots) ~= 'table' or #spots < 1 then
1048
+ H.spots_unshow(spots)
1049
+ H.cache.spots = nil
1050
+ return
1051
+ end
1052
+
1053
+ local key = H.getcharstr('Reminder to press encoding symbol to advance jumping')
1054
+
1055
+ if vim.tbl_contains(label_tbl, key) then
1056
+ H.spots_unshow(spots)
1057
+ spots = vim.tbl_filter(function(x) return x.steps[1] == key end, spots)
1058
+
1059
+ if #spots > 1 then
1060
+ spots = H.spots_add_steps(spots, label_tbl, n_steps_ahead)
1061
+ H.spots_show(spots, opts)
1062
+ H.cache.spots = spots
1063
+
1064
+ H.advance_jump(opts)
1065
+ end
1066
+ end
1067
+
1068
+ if #spots == 1 or key == H.keys.cr then H.perform_jump(spots[1], opts.hooks.after_jump) end
1069
+
1070
+ MiniJump2d.stop()
1071
+ end
1072
+
1073
+ H.perform_jump = function(spot, after_hook)
1074
+ -- Add to jumplist
1075
+ vim.cmd('normal! m`')
1076
+
1077
+ vim.api.nvim_set_current_win(spot.win_id)
1078
+ vim.api.nvim_win_set_cursor(spot.win_id, { spot.line, spot.column - 1 })
1079
+
1080
+ -- Possibly unfold to see cursor
1081
+ vim.cmd('normal! zv')
1082
+
1083
+ if after_hook ~= nil then after_hook() end
1084
+ end
1085
+
1086
+ -- Utilities ------------------------------------------------------------------
1087
+ H.error = function(msg) error('(mini.jump2d) ' .. msg, 0) end
1088
+
1089
+ H.check_type = function(name, val, ref, allow_nil)
1090
+ if type(val) == ref or (ref == 'callable' and vim.is_callable(val)) or (allow_nil and val == nil) then return end
1091
+ H.error(string.format('`%s` should be %s, not %s', name, ref, type(val)))
1092
+ end
1093
+
1094
+ H.notify = function(msg, level_name, silent)
1095
+ if not silent then vim.notify('(mini.jump2d) ' .. msg, vim.log.levels[level_name]) end
1096
+ end
1097
+
1098
+ H.echo = function(msg, is_important)
1099
+ if H.get_config().silent then return end
1100
+
1101
+ -- Construct message chunks
1102
+ msg = type(msg) == 'string' and { { msg } } or msg
1103
+ table.insert(msg, 1, { '(mini.jump2d) ', 'WarningMsg' })
1104
+
1105
+ -- Avoid hit-enter-prompt
1106
+ local max_width = vim.o.columns * math.max(vim.o.cmdheight - 1, 0) + vim.v.echospace
1107
+ local chunks, tot_width = {}, 0
1108
+ for _, ch in ipairs(msg) do
1109
+ local new_ch = { vim.fn.strcharpart(ch[1], 0, max_width - tot_width), ch[2] }
1110
+ table.insert(chunks, new_ch)
1111
+ tot_width = tot_width + vim.fn.strdisplaywidth(new_ch[1])
1112
+ if tot_width >= max_width then break end
1113
+ end
1114
+
1115
+ -- Echo. Force redraw to ensure that it is effective (`:h echo-redraw`)
1116
+ vim.cmd([[echo '' | redraw]])
1117
+ vim.api.nvim_echo(chunks, is_important, {})
1118
+ end
1119
+
1120
+ H.unecho = function()
1121
+ if H.cache.msg_shown then vim.cmd([[echo '' | redraw]]) end
1122
+ end
1123
+
1124
+ H.message = function(msg) H.echo(msg, true) end
1125
+
1126
+ H.is_operator_pending = function()
1127
+ return vim.tbl_contains({ 'no', 'noV', H.keys.block_operator_pending }, vim.fn.mode(1))
1128
+ end
1129
+
1130
+ H.getcharstr = function(msg)
1131
+ local needs_reminder = true
1132
+ if msg ~= nil then
1133
+ vim.defer_fn(function()
1134
+ if not needs_reminder then return end
1135
+ H.echo(msg)
1136
+ H.cache.msg_shown = true
1137
+ end, 1000)
1138
+ end
1139
+
1140
+ H.cache.is_in_getcharstr = true
1141
+ local ok, char = pcall(vim.fn.getcharstr)
1142
+ H.cache.is_in_getcharstr = false
1143
+ needs_reminder = false
1144
+ H.unecho()
1145
+
1146
+ -- Terminate if couldn't get input (like with <C-c>) or on `<Esc>`
1147
+ if not ok or char == '' or char == '\3' or char == '\27' then return nil end
1148
+ return char
1149
+ end
1150
+
1151
+ H.user_input = function(prompt, text)
1152
+ prompt = '(mini.jump2d) ' .. prompt
1153
+ if _G.MiniInput ~= nil then return MiniInput.get({ prompt = prompt, scope = 'cursor', init_keys = { text } }) end
1154
+
1155
+ -- Use `on_key` to distinguish cancel with `<Esc>` and immediate `<CR>`
1156
+ local was_cancelled = false
1157
+ vim.on_key(function(key) was_cancelled = was_cancelled or key == '\27' end, H.ns_id.input)
1158
+
1159
+ -- Ask for input. Use `pcall` to allow `<C-c>` to cancel user input
1160
+ vim.cmd('echohl Question')
1161
+ local ok, res = pcall(vim.fn.input, { prompt = prompt .. ': ', default = text or '' })
1162
+ vim.cmd('echohl None | echo "" | redraw')
1163
+
1164
+ vim.on_key(nil, H.ns_id.input)
1165
+ return (ok and not was_cancelled) and res or nil
1166
+ end
1167
+
1168
+ --- This ensures order of windows based on their layout
1169
+ ---
1170
+ --- This is already done by default in `nvim_tabpage_list_wins()`, but is not
1171
+ --- documented, so can break any time.
1172
+ ---
1173
+ ---@private
1174
+ H.tabpage_list_wins = function(tabpage_id)
1175
+ local wins = vim.api.nvim_tabpage_list_wins(tabpage_id)
1176
+ local wins_pos = {}
1177
+ for _, win_id in ipairs(wins) do
1178
+ local pos = vim.api.nvim_win_get_position(win_id)
1179
+ local config = vim.api.nvim_win_get_config(win_id)
1180
+ wins_pos[win_id] = { row = pos[1], col = pos[2], zindex = config.zindex or 0 }
1181
+ end
1182
+
1183
+ -- Sort windows by their position: top to bottom, left to right, low to high
1184
+ table.sort(wins, function(a, b)
1185
+ -- Put higher window further to have them processed later. This means that
1186
+ -- in case of same buffer in floating and underlying regular windows,
1187
+ -- floating will have "the latest" extmarks (think like `MiniMisc.zoom()`).
1188
+ if wins_pos[a].zindex < wins_pos[b].zindex then return true end
1189
+ if wins_pos[a].zindex > wins_pos[b].zindex then return false end
1190
+
1191
+ if wins_pos[a].col < wins_pos[b].col then return true end
1192
+ if wins_pos[a].col > wins_pos[b].col then return false end
1193
+
1194
+ return wins_pos[a].row < wins_pos[b].row
1195
+ end)
1196
+
1197
+ return wins
1198
+ end
1199
+
1200
+ H.map = function(mode, lhs, rhs, opts)
1201
+ if lhs == '' then return end
1202
+ opts = vim.tbl_deep_extend('force', { silent = true }, opts or {})
1203
+ vim.keymap.set(mode, lhs, rhs, opts)
1204
+ end
1205
+
1206
+ H.merge_unique = function(tbl_1, tbl_2)
1207
+ if type(tbl_1) == 'table' and type(tbl_2) ~= 'table' then return tbl_1 end
1208
+ if type(tbl_1) ~= 'table' and type(tbl_2) == 'table' then return tbl_2 end
1209
+
1210
+ local n_1, n_2 = #tbl_1, #tbl_2
1211
+ local res, i, j = {}, 1, 1
1212
+ local to_add
1213
+ while i <= n_1 and j <= n_2 do
1214
+ if tbl_1[i] < tbl_2[j] then
1215
+ to_add = tbl_1[i]
1216
+ i = i + 1
1217
+ else
1218
+ to_add = tbl_2[j]
1219
+ j = j + 1
1220
+ end
1221
+ if res[#res] ~= to_add then table.insert(res, to_add) end
1222
+ end
1223
+
1224
+ while i <= n_1 do
1225
+ to_add = tbl_1[i]
1226
+ if res[#res] ~= to_add then table.insert(res, to_add) end
1227
+ i = i + 1
1228
+ end
1229
+ while j <= n_2 do
1230
+ to_add = tbl_2[j]
1231
+ if res[#res] ~= to_add then table.insert(res, to_add) end
1232
+ j = j + 1
1233
+ end
1234
+
1235
+ return res
1236
+ end
1237
+
1238
+ H.str_utfindex = function(s, i) return vim.str_utfindex(s, 'utf-32', i) end
1239
+ if vim.fn.has('nvim-0.11') == 0 then H.str_utfindex = function(s, i) return (vim.str_utfindex(s, i)) end end
1240
+
1241
+ H.str_byteindex = function(s, i) return vim.str_byteindex(s, 'utf-32', i) end
1242
+ if vim.fn.has('nvim-0.11') == 0 then H.str_byteindex = function(s, i) return vim.str_byteindex(s, i) end end
1243
+
1244
+ return MiniJump2d