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,2013 @@
1
+ --- *mini.bracketed* Go forward/backward with square brackets
2
+ ---
3
+ --- MIT License Copyright (c) 2023 Evgeni Chasnovski
4
+
5
+ --- Features:
6
+ --- - Configurable Lua functions to go forward/backward to a certain target.
7
+ --- Each function can be customized with:
8
+ --- - Direction. One of "forward", "backward", "first" (forward starting
9
+ --- from first one), "last" (backward starting from last one).
10
+ --- - Number of times to go.
11
+ --- - Whether to wrap on edges (going forward on last one goes to first).
12
+ --- - Some other target specific options.
13
+ ---
14
+ --- - Mappings using square brackets. They are created using configurable
15
+ --- target suffix and can be selectively disabled.
16
+ ---
17
+ --- Each mapping supports |[count]|. Mappings are created in Normal mode; for
18
+ --- targets which move cursor in current buffer also Visual and
19
+ --- Operator-pending (with dot-repeat) modes are supported.
20
+ ---
21
+ --- Using `lower-suffix` and `upper-suffix` (lower and upper case suffix) for
22
+ --- a single target the following mappings are created:
23
+ --- - `[` + `upper-suffix` : go first.
24
+ --- - `[` + `lower-suffix` : go backward.
25
+ --- - `]` + `lower-suffix` : go forward.
26
+ --- - `]` + `upper-suffix` : go last.
27
+ ---
28
+ --- - Supported targets (for more information see help for corresponding Lua
29
+ --- function):
30
+ ---
31
+ --- `Target` `Mappings` `Lua function`
32
+ --- Buffer ......................... `[B` `[b` `]b` `]B` .... |MiniBracketed.buffer()|
33
+ --- Comment block .................. `[C` `[c` `]c` `]C` .... |MiniBracketed.comment()|
34
+ --- Conflict marker ................ `[X` `[x` `]x` `]X` .... |MiniBracketed.conflict()|
35
+ --- Diagnostic ..................... `[D` `[d` `]d` `]D` .... |MiniBracketed.diagnostic()|
36
+ --- File on disk ................... `[F` `[f` `]f` `]F` .... |MiniBracketed.file()|
37
+ --- Indent change .................. `[I` `[i` `]i` `]I` .... |MiniBracketed.indent()|
38
+ --- Jump inside current buffer ..... `[J` `[j` `]j` `]J` .... |MiniBracketed.jump()|
39
+ --- Location from |location-list| .... `[L` `[l` `]l` `]L` .... |MiniBracketed.location()|
40
+ --- Old files ...................... `[O` `[o` `]o` `]O` .... |MiniBracketed.oldfile()|
41
+ --- Quickfix entry from |Quickfix| ... `[Q` `[q` `]q` `]Q` .... |MiniBracketed.quickfix()|
42
+ --- Tree-sitter node and parents ... `[T` `[t` `]t` `]T` .... |MiniBracketed.treesitter()|
43
+ --- Undo from linear history ....... `[U` `[u` `]u` `]U` .... |MiniBracketed.undo()|
44
+ --- Window in current tab .......... `[W` `[w` `]w` `]W` .... |MiniBracketed.window()|
45
+ --- Yank entry over put region ..... `[Y` `[y` `]y` `]Y` .... |MiniBracketed.yank()|
46
+ ---
47
+ --- Notes:
48
+ --- - The `undo` target remaps |u| and |CTRL-R| keys to register undo state
49
+ --- after undo and redo respectively. If this conflicts with your setup,
50
+ --- either disable `undo` target or make your remaps after calling
51
+ --- |MiniBracketed.setup()|. To use `undo` target, remap your undo/redo keys
52
+ --- to call |MiniBracketed.register_undo_state()| after the action.
53
+ ---
54
+ --- # Setup ~
55
+ ---
56
+ --- This module needs a setup with `require('mini.bracketed').setup({})` (replace
57
+ --- `{}` with your `config` table). It will create global Lua table `MiniBracketed`
58
+ --- which you can use for scripting or manually (with `:lua MiniBracketed.*`).
59
+ ---
60
+ --- See |MiniBracketed.config| for available config settings.
61
+ ---
62
+ --- You can override runtime config settings (like target options) locally
63
+ --- to buffer inside `vim.b.minibracketed_config` which should have same structure
64
+ --- as `MiniBracketed.config`. See |mini.nvim-buffer-local-config| for more details.
65
+ ---
66
+ --- # Comparisons ~
67
+ ---
68
+ --- - [tpope/vim-unimpaired](https://github.com/tpope/vim-unimpaired):
69
+ --- - Supports buffer, conflict, file, location, and quickfix targets mostly
70
+ --- via built-in commands (like |:bprevious|, etc.) without configuration.
71
+ --- - Supports files from argument list and tags. This module does not.
72
+ --- - Doesn't support most other this module's targets (comment, indent, ...).
73
+ --- - |mini.indentscope|:
74
+ --- - Target |MiniBracketed.indent()| target can go to "first" and "last"
75
+ --- indent change. It also can go not only to line with smaller indent,
76
+ --- but also bigger or different one.
77
+ --- - Mappings from |mini.indentscope| have more flexibility in computation of
78
+ --- indent scope, like how to treat empty lines near border or whether to
79
+ --- compute indent at cursor.
80
+ ---
81
+ --- # Disabling ~
82
+ ---
83
+ --- To disable, set `vim.g.minibracketed_disable` (globally) or
84
+ --- `vim.b.minibracketed_disable` (for a buffer) to `true`. Considering high
85
+ --- number of different scenarios and customization intentions, writing exact
86
+ --- rules for disabling module's functionality is left to user. See
87
+ --- |mini.nvim-disabling-recipes| for common recipes.
88
+ ---@tag MiniBracketed
89
+
90
+ ---@diagnostic disable:luadoc-miss-type-name
91
+ ---@alias __bracketed_direction string One of "first", "backward", "forward", "last".
92
+ ---@alias __bracketed_add_to_jumplist - <add_to_jumplist> (`boolean`) - Whether to add current position to jumplist.
93
+ --- Default: `false`.
94
+ ---@alias __bracketed_opts table|nil Options. A table with fields:
95
+ --- - <n_times> `(number)` - Number of times to advance. Default: |v:count1|.
96
+ --- - <wrap> `(boolean)` - Whether to wrap around edges. Default: `true`.
97
+
98
+ ---@diagnostic disable:undefined-field
99
+
100
+ -- Module definition ==========================================================
101
+ local MiniBracketed = {}
102
+ local H = {}
103
+
104
+ --- Module setup
105
+ ---
106
+ ---@param config table|nil Module config table. See |MiniBracketed.config|.
107
+ ---
108
+ ---@usage >lua
109
+ --- require('mini.bracketed').setup() -- use default config
110
+ --- -- OR
111
+ --- require('mini.bracketed').setup({}) -- replace {} with your config table
112
+ --- <
113
+ MiniBracketed.setup = function(config)
114
+ -- TODO: Remove after Neovim=0.9 support is dropped
115
+ if vim.fn.has('nvim-0.10') == 0 then
116
+ vim.notify(
117
+ '(mini.bracketed) Neovim<0.10 is soft deprecated (module works but is not supported).'
118
+ .. " It will be deprecated after the next 'mini.nvim' release (module might not work)."
119
+ .. ' Please update your Neovim version.'
120
+ )
121
+ end
122
+
123
+ -- Export module
124
+ _G.MiniBracketed = MiniBracketed
125
+
126
+ -- Setup config
127
+ config = H.setup_config(config)
128
+
129
+ -- Apply config
130
+ H.apply_config(config)
131
+
132
+ -- Define behavior
133
+ H.create_autocommands()
134
+ end
135
+
136
+ --stylua: ignore
137
+ --- Defaults ~
138
+ ---@eval return MiniDoc.afterlines_to_code(MiniDoc.current.eval_section)
139
+ ---@text Options ~
140
+ ---
141
+ --- Each entry configures target with the same name and can have data configuring
142
+ --- mapping suffix and target options.
143
+ ---
144
+ --- Example of configuration: >lua
145
+ ---
146
+ --- require('mini.bracketed').setup({
147
+ --- -- Map [N, [n, ]n, ]N for conflict marker like in 'tpope/vim-unimpaired'
148
+ --- conflict = { suffix = 'n' },
149
+ ---
150
+ --- -- Make diagnostic advance only by errors
151
+ --- diagnostic = { options = { severity = vim.diagnostic.severity.ERROR } },
152
+ ---
153
+ --- -- Disable creation of mappings for `indent` target (for example,
154
+ --- -- in favor of ones from |mini.indentscope|)
155
+ --- indent = { suffix = '' },
156
+ ---
157
+ --- -- Disable mappings for `window` target in favor of custom ones
158
+ --- window = { suffix = '' },
159
+ --- })
160
+ ---
161
+ --- -- Create custom `window` mappings
162
+ --- local map = vim.keymap.set
163
+ --- map('n', '<Leader>wH', "<Cmd>lua MiniBracketed.window('first')<CR>")
164
+ --- map('n', '<Leader>wh', "<Cmd>lua MiniBracketed.window('backward')<CR>")
165
+ --- map('n', '<Leader>wl', "<Cmd>lua MiniBracketed.window('forward')<CR>")
166
+ --- map('n', '<Leader>wL', "<Cmd>lua MiniBracketed.window('last')<CR>")
167
+ --- <
168
+ --- ## Suffix ~
169
+ ---
170
+ --- The `suffix` key is used to create target mappings.
171
+ ---
172
+ --- Supply empty string to disable mapping creation for that particular target.
173
+ --- To create a completely different mapping (like with |<Leader>|) use target
174
+ --- function manually.
175
+ ---
176
+ --- Using `lower-suffix` and `upper-suffix` (lower and upper case suffix) for
177
+ --- a single target the following mappings are created:
178
+ --- - `[` + `upper-suffix` : go first.
179
+ --- - `[` + `lower-suffix` : go backward.
180
+ --- - `]` + `lower-suffix` : go forward.
181
+ --- - `]` + `upper-suffix` : go last.
182
+ ---
183
+ --- When supplied with a non-letter, only forward/backward mappings are created.
184
+ ---
185
+ --- ## Options ~
186
+ ---
187
+ --- The `options` key is directly forwarded as `opts` to corresponding Lua function.
188
+ MiniBracketed.config = {
189
+ -- First-level elements are tables describing behavior of a target:
190
+ --
191
+ -- - <suffix> - single character suffix. Used after `[` / `]` in mappings.
192
+ -- For example, with `b` creates `[B`, `[b`, `]b`, `]B` mappings.
193
+ -- Supply empty string `''` to not create mappings.
194
+ --
195
+ -- - <options> - table overriding target options.
196
+ --
197
+ -- See `:h MiniBracketed.config` for more info.
198
+
199
+ buffer = { suffix = 'b', options = {} },
200
+ comment = { suffix = 'c', options = {} },
201
+ conflict = { suffix = 'x', options = {} },
202
+ diagnostic = { suffix = 'd', options = {} },
203
+ file = { suffix = 'f', options = {} },
204
+ indent = { suffix = 'i', options = {} },
205
+ jump = { suffix = 'j', options = {} },
206
+ location = { suffix = 'l', options = {} },
207
+ oldfile = { suffix = 'o', options = {} },
208
+ quickfix = { suffix = 'q', options = {} },
209
+ treesitter = { suffix = 't', options = {} },
210
+ undo = { suffix = 'u', options = {} },
211
+ window = { suffix = 'w', options = {} },
212
+ yank = { suffix = 'y', options = {} },
213
+ }
214
+ --minidoc_afterlines_end
215
+
216
+ --- Listed buffer
217
+ ---
218
+ --- Go to next/previous listed buffer. Order by their number (see |bufnr()|).
219
+ ---
220
+ --- Direction "forward" increases number, "backward" - decreases.
221
+ ---
222
+ ---@param direction __bracketed_direction
223
+ ---@param opts __bracketed_opts
224
+ MiniBracketed.buffer = function(direction, opts)
225
+ if H.is_disabled() then return end
226
+
227
+ H.validate_direction(direction, { 'first', 'backward', 'forward', 'last' }, 'buffer')
228
+ opts =
229
+ vim.tbl_deep_extend('force', { n_times = vim.v.count1, wrap = true }, H.get_config().buffer.options, opts or {})
230
+
231
+ -- Define iterator that traverses all valid listed buffers
232
+ -- (should be same as `:bnext` / `:bprev`)
233
+ local buf_list = vim.api.nvim_list_bufs()
234
+ local is_listed = function(buf_id) return vim.api.nvim_buf_is_valid(buf_id) and vim.bo[buf_id].buflisted end
235
+
236
+ local iterator = {}
237
+
238
+ iterator.next = function(buf_id)
239
+ for id = buf_id + 1, buf_list[#buf_list] do
240
+ if is_listed(id) then return id end
241
+ end
242
+ end
243
+
244
+ iterator.prev = function(buf_id)
245
+ for id = buf_id - 1, buf_list[1], -1 do
246
+ if is_listed(id) then return id end
247
+ end
248
+ end
249
+
250
+ iterator.state = vim.api.nvim_get_current_buf()
251
+ iterator.start_edge = buf_list[1] - 1
252
+ iterator.end_edge = buf_list[#buf_list] + 1
253
+
254
+ -- Iterate
255
+ local res_buf_id = MiniBracketed.advance(iterator, direction, opts)
256
+ if res_buf_id == iterator.state then return end
257
+
258
+ -- Apply
259
+ vim.api.nvim_set_current_buf(res_buf_id)
260
+ end
261
+
262
+ --- Comment block
263
+ ---
264
+ --- Go to next/previous comment block. Only linewise comments using
265
+ --- |'commentstring'| are recognized.
266
+ ---
267
+ --- Direction "forward" increases line number, "backward" - decreases.
268
+ ---
269
+ ---@param direction __bracketed_direction
270
+ ---@param opts __bracketed_opts
271
+ --- __bracketed_add_to_jumplist
272
+ --- - <block_side> `(string)` - which side of comment block to use. One of
273
+ --- "near" (default; use nearest side), "start" (use first line), "end"
274
+ --- (use last line), "both" (use both first and last lines).
275
+ MiniBracketed.comment = function(direction, opts)
276
+ if H.is_disabled() then return end
277
+
278
+ H.validate_direction(direction, { 'first', 'backward', 'forward', 'last' }, 'comment')
279
+ opts = vim.tbl_deep_extend(
280
+ 'force',
281
+ { add_to_jumplist = false, block_side = 'near', n_times = vim.v.count1, wrap = true },
282
+ H.get_config().comment.options,
283
+ opts or {}
284
+ )
285
+
286
+ -- Compute loop data to traverse target commented lines in current buffer
287
+ local is_commented = H.make_comment_checker()
288
+ if is_commented == nil then return end
289
+
290
+ local predicate = ({
291
+ near = function(_, cur, _, recent) return cur and not recent end,
292
+ start = function(above, cur, _, _) return cur and not above end,
293
+ ['end'] = function(_, cur, below, _) return cur and not below end,
294
+ both = function(above, cur, below, _) return cur and not (above and below) end,
295
+ })[opts.block_side]
296
+ if predicate == nil then return end
297
+
298
+ -- Define iterator
299
+ local iterator = {}
300
+
301
+ local n_lines = vim.api.nvim_buf_line_count(0)
302
+ iterator.next = function(line_num)
303
+ local above, cur = is_commented(line_num), is_commented(line_num + 1)
304
+ for lnum = line_num + 1, n_lines do
305
+ local below = is_commented(lnum + 1)
306
+ if predicate(above, cur, below, above) then return lnum end
307
+ above, cur = cur, below
308
+ end
309
+ end
310
+
311
+ iterator.prev = function(line_num)
312
+ local cur, below = is_commented(line_num - 1), is_commented(line_num)
313
+ for lnum = line_num - 1, 1, -1 do
314
+ local above = is_commented(lnum - 1)
315
+ if predicate(above, cur, below, below) then return lnum end
316
+ below, cur = cur, above
317
+ end
318
+ end
319
+
320
+ iterator.state = vim.fn.line('.')
321
+ iterator.start_edge = 0
322
+ iterator.end_edge = n_lines + 1
323
+
324
+ -- Iterate
325
+ local res_line_num = MiniBracketed.advance(iterator, direction, opts)
326
+ local is_outside = res_line_num <= 0 or n_lines < res_line_num
327
+ if res_line_num == nil or res_line_num == iterator.state or is_outside then return end
328
+
329
+ -- Possibly add current position to jumplist
330
+ if opts.add_to_jumplist then H.add_to_jumplist() end
331
+
332
+ -- Apply. Open just enough folds and put cursor on first non-blank.
333
+ H.set_cursor(res_line_num, 0)
334
+ vim.cmd('normal! zv^')
335
+ end
336
+
337
+ --- Git conflict marker
338
+ ---
339
+ --- Go to next/previous lines containing Git conflict marker. That is, if it
340
+ --- starts with "<<<<<<< ", ">>>>>>> ", or is "=======".
341
+ ---
342
+ --- Direction "forward" increases line number, "backward" - decreases.
343
+ ---
344
+ --- Notes:
345
+ --- - Using this target in Operator-pending mode allows the following approach
346
+ --- at resolving merge conflicts:
347
+ --- - Place cursor on `=======` line.
348
+ --- - Execute one of these: `d]x[xdd` (choose upper part) or
349
+ --- `d[x]xdd` (choose lower part).
350
+ ---
351
+ ---@param direction __bracketed_direction
352
+ ---@param opts __bracketed_opts
353
+ --- __bracketed_add_to_jumplist
354
+ MiniBracketed.conflict = function(direction, opts)
355
+ if H.is_disabled() then return end
356
+
357
+ H.validate_direction(direction, { 'first', 'backward', 'forward', 'last' }, 'conflict')
358
+ opts = vim.tbl_deep_extend(
359
+ 'force',
360
+ { add_to_jumplist = false, n_times = vim.v.count1, wrap = true },
361
+ H.get_config().conflict.options,
362
+ opts or {}
363
+ )
364
+
365
+ -- Define iterator that traverses all conflict markers in current buffer
366
+ local n_lines = vim.api.nvim_buf_line_count(0)
367
+
368
+ local iterator = {}
369
+
370
+ iterator.next = function(line_num)
371
+ for lnum = line_num + 1, n_lines do
372
+ if H.is_conflict_mark(lnum) then return lnum end
373
+ end
374
+ end
375
+
376
+ iterator.prev = function(line_num)
377
+ for lnum = line_num - 1, 1, -1 do
378
+ if H.is_conflict_mark(lnum) then return lnum end
379
+ end
380
+ end
381
+
382
+ iterator.state = vim.fn.line('.')
383
+ iterator.start_edge = 0
384
+ iterator.end_edge = n_lines + 1
385
+
386
+ -- Iterate
387
+ local res_line_num = MiniBracketed.advance(iterator, direction, opts)
388
+ local is_outside = res_line_num <= 0 or n_lines < res_line_num
389
+ if res_line_num == nil or res_line_num == iterator.state or is_outside then return end
390
+
391
+ -- Possibly add current position to jumplist
392
+ if opts.add_to_jumplist then H.add_to_jumplist() end
393
+
394
+ -- Apply. Open just enough folds and put cursor on first non-blank.
395
+ H.set_cursor(res_line_num, 0)
396
+ vim.cmd('normal! zv^')
397
+ end
398
+
399
+ --- Diagnostic
400
+ ---
401
+ --- Go to next/previous diagnostic. This is mostly similar to built-in
402
+ --- |vim.diagnostic.jump()| (on Neovim<0.11 it is |vim.diagnostic.goto_next()| and
403
+ --- |vim.diagnostic.goto_prev()|) which has an interface and behavior
404
+ --- consistent with other methods of the module.
405
+ ---
406
+ --- Direction "forward" increases line number, "backward" - decreases.
407
+ ---
408
+ --- Notes:
409
+ --- - Using `severity` option, this target can be used in mappings like "go to
410
+ --- next/previous error" (), etc. Using code similar to this: >lua
411
+ ---
412
+ --- local severity_error = vim.diagnostic.severity.ERROR
413
+ --- -- Use these inside custom mappings
414
+ --- MiniBracketed.diagnostic('forward', { severity = severity_error })
415
+ --- MiniBracketed.diagnostic('backward', { severity = severity_error })
416
+ --- <
417
+ ---@param direction __bracketed_direction
418
+ ---@param opts __bracketed_opts
419
+ --- - <float> `(boolean|table)` - control floating window after movement.
420
+ --- For available values see |vim.diagnostic.goto_next()|.
421
+ --- - <severity> `(string|table)` - which severity to use.
422
+ --- For available values see |diagnostic-severity|.
423
+ MiniBracketed.diagnostic = function(direction, opts)
424
+ if H.is_disabled() then return end
425
+
426
+ H.validate_direction(direction, { 'first', 'backward', 'forward', 'last' }, 'diagnostic')
427
+ local default_opts = { float = vim.diagnostic.config().float, n_times = vim.v.count1, severity = nil, wrap = true }
428
+ local buf_id = vim.api.nvim_get_current_buf()
429
+ if vim.is_callable(default_opts.float) then default_opts.float = default_opts.float(nil, buf_id) end
430
+ opts = vim.tbl_extend('force', default_opts, H.get_config().diagnostic.options, opts or {})
431
+
432
+ -- Define iterator that traverses all diagnostic entries in current buffer
433
+ local to_cursor_pos = function(pos) return { pos[1] + 1, pos[2] } end
434
+ local pos_field = vim.fn.has('nvim-0.11') == 1 and 'pos' or 'cursor_position'
435
+ local iterator = {}
436
+
437
+ iterator.next = function(position)
438
+ local next_opts = { [pos_field] = to_cursor_pos(position), severity = opts.severity, wrap = false }
439
+ local next = vim.diagnostic.get_next(next_opts)
440
+ if next == nil then return end
441
+ return { next.lnum, next.col }
442
+ end
443
+
444
+ iterator.prev = function(position)
445
+ local prev_opts = { [pos_field] = to_cursor_pos(position), severity = opts.severity, wrap = false }
446
+ local prev = vim.diagnostic.get_prev(prev_opts)
447
+ if prev == nil then return end
448
+ return { prev.lnum, prev.col }
449
+ end
450
+
451
+ -- - Define states with zero-based indexing as used in `vim.diagnostic`.
452
+ -- - Go outside of proper buffer position for `start_edge` and `end_edge` to
453
+ -- correctly spot diagnostic entry right and start and end of buffer.
454
+ local cursor_pos = vim.api.nvim_win_get_cursor(0)
455
+ iterator.state = { cursor_pos[1] - 1, cursor_pos[2] }
456
+
457
+ iterator.start_edge = { 0, -1 }
458
+
459
+ local last_line = vim.api.nvim_buf_line_count(0)
460
+ iterator.end_edge = { last_line - 1, vim.fn.col({ last_line, '$' }) - 1 }
461
+
462
+ -- Iterate
463
+ local res_pos = MiniBracketed.advance(iterator, direction, opts)
464
+ if res_pos == nil or res_pos == iterator.state then return end
465
+
466
+ -- Apply. Use built-in jump with offsetted cursor position to make it respect
467
+ -- `vim.diagnostic.config()`.
468
+ H.diagnostic_jump({ res_pos[1] + 1, res_pos[2] - 1 }, opts.float, opts.severity)
469
+ end
470
+
471
+ --- File on disk
472
+ ---
473
+ --- Go to next/previous file on disk alphabetically. Files are taken from
474
+ --- directory of file in current buffer (or current working directory if buffer
475
+ --- doesn't contain a readable file). Only first-level files are used, i.e. it
476
+ --- doesn't go inside subdirectories.
477
+ ---
478
+ --- Direction "forward" goes forward alphabetically, "backward" - backward.
479
+ ---
480
+ ---@param direction __bracketed_direction
481
+ ---@param opts __bracketed_opts
482
+ MiniBracketed.file = function(direction, opts)
483
+ if H.is_disabled() then return end
484
+
485
+ H.validate_direction(direction, { 'first', 'backward', 'forward', 'last' }, 'file')
486
+ opts = vim.tbl_deep_extend('force', { n_times = vim.v.count1, wrap = true }, H.get_config().file.options, opts or {})
487
+
488
+ -- Get file data
489
+ local file_data = H.get_file_data()
490
+ if file_data == nil then return end
491
+ local file_basenames, directory = file_data.file_basenames, file_data.directory
492
+
493
+ -- Define iterator that traverses all found files
494
+ local iterator = {}
495
+ local n_files = #file_basenames
496
+
497
+ iterator.next = function(ind)
498
+ -- Allow advance in untrackable current buffer
499
+ if ind == nil then return 1 end
500
+ if n_files <= ind then return end
501
+ return ind + 1
502
+ end
503
+
504
+ iterator.prev = function(ind)
505
+ -- Allow advance in untrackable current buffer
506
+ if ind == nil then return n_files end
507
+ if ind <= 1 then return end
508
+ return ind - 1
509
+ end
510
+
511
+ -- - Find filename array index of current buffer
512
+ local cur_basename = vim.fn.fnamemodify(vim.api.nvim_buf_get_name(0), ':t')
513
+ local cur_basename_ind
514
+ if cur_basename ~= '' then
515
+ for i, f in ipairs(file_basenames) do
516
+ if cur_basename == f then
517
+ cur_basename_ind = i
518
+ break
519
+ end
520
+ end
521
+ end
522
+
523
+ iterator.state = cur_basename_ind
524
+ iterator.start_edge = 0
525
+ iterator.end_edge = n_files + 1
526
+
527
+ -- Iterate
528
+ local res_ind = MiniBracketed.advance(iterator, direction, opts)
529
+ if res_ind == iterator.state then return end
530
+
531
+ -- Apply. Open target_path.
532
+ local path_sep = package.config:sub(1, 1)
533
+ local target_path = directory .. path_sep .. file_basenames[res_ind]
534
+ H.edit(target_path)
535
+ end
536
+
537
+ --- Indent change
538
+ ---
539
+ --- Go to next/previous line with different indent (see |indent()|).
540
+ --- Can be used to go to lines with smaller, bigger, or different indent.
541
+ ---
542
+ --- Notes:
543
+ --- - Directions "first" and "last" work differently from most other targets
544
+ --- for performance reasons. They are essentially "backward" and "forward"
545
+ --- with very big `n_times` option.
546
+ --- - For similar reasons, `wrap` is not supported.
547
+ --- - Blank line inherit indent from near non-blank line in direction of movement.
548
+ ---
549
+ --- Direction "forward" increases line number, "backward" - decreases.
550
+ ---
551
+ ---@param direction __bracketed_direction
552
+ ---@param opts table|nil Options. A table with fields:
553
+ --- - <n_times> `(number)` - Number of times to advance. Default: |v:count1|.
554
+ --- __bracketed_add_to_jumplist
555
+ --- - <change_type> `(string)` - which type of indent change to use.
556
+ --- One of "less" (default; smaller indent), "more" (bigger indent),
557
+ --- "diff" (different indent).
558
+ MiniBracketed.indent = function(direction, opts)
559
+ if H.is_disabled() then return end
560
+
561
+ H.validate_direction(direction, { 'first', 'backward', 'forward', 'last' }, 'indent')
562
+ opts = vim.tbl_deep_extend(
563
+ 'force',
564
+ { add_to_jumplist = false, change_type = 'less', n_times = vim.v.count1 },
565
+ H.get_config().indent.options,
566
+ opts or {}
567
+ )
568
+
569
+ opts.wrap = false
570
+
571
+ if direction == 'first' then
572
+ -- For some reason using `n_times = math.huge` leads to infinite loop
573
+ direction, opts.n_times = 'backward', vim.api.nvim_buf_line_count(0) + 1
574
+ end
575
+ if direction == 'last' then
576
+ direction, opts.n_times = 'forward', vim.api.nvim_buf_line_count(0) + 1
577
+ end
578
+
579
+ -- Compute loop data to traverse target commented lines in current buffer
580
+ local predicate = ({
581
+ less = function(new, cur) return new < cur or cur == 0 end,
582
+ more = function(new, cur) return new > cur end,
583
+ diff = function(new, cur) return new ~= cur end,
584
+ })[opts.change_type]
585
+ if predicate == nil then return end
586
+
587
+ -- Define iterator
588
+ local iterator = {}
589
+
590
+ iterator.next = function(cur_lnum)
591
+ -- Correctly process empty current line
592
+ cur_lnum = vim.fn.nextnonblank(cur_lnum)
593
+ local cur_indent = vim.fn.indent(cur_lnum)
594
+
595
+ local new_lnum, new_indent = cur_lnum, cur_indent
596
+ -- Check with `new_lnum > 0` because `nextnonblank()` returns -1 if line is
597
+ -- outside of line range
598
+ while new_lnum > 0 do
599
+ new_indent = vim.fn.indent(new_lnum)
600
+ if predicate(new_indent, cur_indent) then return new_lnum end
601
+ new_lnum = vim.fn.nextnonblank(new_lnum + 1)
602
+ end
603
+ end
604
+
605
+ iterator.prev = function(cur_lnum)
606
+ cur_lnum = vim.fn.prevnonblank(cur_lnum)
607
+ local cur_indent = vim.fn.indent(cur_lnum)
608
+
609
+ local new_lnum, new_indent = cur_lnum, cur_indent
610
+ while new_lnum > 0 do
611
+ new_indent = vim.fn.indent(new_lnum)
612
+ if predicate(new_indent, cur_indent) then return new_lnum end
613
+ new_lnum = vim.fn.prevnonblank(new_lnum - 1)
614
+ end
615
+ end
616
+
617
+ -- - Don't add first and last states as there is no wrapping around edges
618
+ iterator.state = vim.fn.line('.')
619
+
620
+ -- Iterate
621
+ local res_line_num = MiniBracketed.advance(iterator, direction, opts)
622
+ if res_line_num == nil or res_line_num == iterator.state then return end
623
+
624
+ -- Possibly add current position to jumplist
625
+ if opts.add_to_jumplist then H.add_to_jumplist() end
626
+
627
+ -- Apply. Open just enough folds and put cursor on first non-blank.
628
+ H.set_cursor(res_line_num, 0)
629
+ vim.cmd('normal! zv^')
630
+ end
631
+
632
+ --- Jump inside current buffer
633
+ ---
634
+ --- Go to next/previous jump from |jumplist| which is inside current buffer.
635
+ ---
636
+ --- Notes:
637
+ --- - There are no Visual mode mappings due to implementation problems.
638
+ ---
639
+ --- Direction "forward" increases jump number, "backward" - decreases.
640
+ ---
641
+ ---@param direction __bracketed_direction
642
+ ---@param opts __bracketed_opts
643
+ MiniBracketed.jump = function(direction, opts)
644
+ if H.is_disabled() then return end
645
+
646
+ H.validate_direction(direction, { 'first', 'backward', 'forward', 'last' }, 'jump')
647
+ opts = vim.tbl_deep_extend('force', { n_times = vim.v.count1, wrap = true }, H.get_config().jump.options, opts or {})
648
+
649
+ -- Define iterator that traverses all jumplist entries inside current buffer
650
+ local cur_buf_id = vim.api.nvim_get_current_buf()
651
+ local jump_list, cur_jump_num = unpack(vim.fn.getjumplist())
652
+ local n_list = #jump_list
653
+ if n_list == 0 then return end
654
+ -- - Correct for zero-based indexing
655
+ cur_jump_num = cur_jump_num + 1
656
+
657
+ local iterator = {}
658
+
659
+ local is_jump_num_from_current_buffer = function(jump_num)
660
+ local jump_entry = jump_list[jump_num]
661
+ if jump_entry == nil then return end
662
+ return jump_entry.bufnr == cur_buf_id
663
+ end
664
+
665
+ iterator.next = function(jump_num)
666
+ for num = jump_num + 1, n_list do
667
+ if is_jump_num_from_current_buffer(num) then return num end
668
+ end
669
+ end
670
+
671
+ iterator.prev = function(jump_num)
672
+ for num = jump_num - 1, 1, -1 do
673
+ if is_jump_num_from_current_buffer(num) then return num end
674
+ end
675
+ end
676
+
677
+ iterator.state = cur_jump_num
678
+ iterator.start_edge = 0
679
+ iterator.end_edge = n_list + 1
680
+
681
+ -- Iterate
682
+ local res_jump_num = MiniBracketed.advance(iterator, direction, opts)
683
+ if res_jump_num == nil or jump_list[res_jump_num] == nil then return end
684
+
685
+ -- Apply. Make jump. Allow jumping to current jump entry as it might be
686
+ -- different from current cursor position.
687
+ H.make_jump(jump_list, cur_jump_num, res_jump_num)
688
+ end
689
+
690
+ --- Location from location list
691
+ ---
692
+ --- Go to next/previous location from |location-list|. This is similar to
693
+ --- |:lfirst|, |:lprevious|, |:lnext|, and |:llast| but with support of
694
+ --- wrapping around edges and |[count]| for "first"/"last" direction.
695
+ ---
696
+ --- Direction "forward" increases location number, "backward" - decreases.
697
+ ---
698
+ ---@param direction __bracketed_direction
699
+ ---@param opts __bracketed_opts
700
+ MiniBracketed.location = function(direction, opts)
701
+ if H.is_disabled() then return end
702
+
703
+ H.validate_direction(direction, { 'first', 'backward', 'forward', 'last' }, 'location')
704
+ opts =
705
+ vim.tbl_deep_extend('force', { n_times = vim.v.count1, wrap = true }, H.get_config().location.options, opts or {})
706
+
707
+ H.qf_loc_implementation('location', direction, opts)
708
+ end
709
+
710
+ --- Old files from previous and current sessions
711
+ ---
712
+ --- Go to older/newer readable file either from previous session (see |v:oldfiles|)
713
+ --- or the current one (updated automatically after |MiniBracketed.setup()| call).
714
+ ---
715
+ --- Direction "forward" goes to more recent files, "backward" - to older.
716
+ ---
717
+ --- Notes:
718
+ --- - In current session it tracks only normal buffers (see |'buftype'|) for
719
+ --- some readable file.
720
+ --- - No new file is tracked when advancing this target. Only after buffer
721
+ --- change is done not through this target (like with |MiniBracketed.buffer()|),
722
+ --- it updates recency of last advanced and new buffers.
723
+ ---
724
+ ---@param direction __bracketed_direction
725
+ ---@param opts __bracketed_opts
726
+ MiniBracketed.oldfile = function(direction, opts)
727
+ if H.is_disabled() then return end
728
+
729
+ H.validate_direction(direction, { 'first', 'backward', 'forward', 'last' }, 'oldfile')
730
+ opts =
731
+ vim.tbl_deep_extend('force', { n_times = vim.v.count1, wrap = true }, H.get_config().oldfile.options, opts or {})
732
+
733
+ -- Define iterator that traverses all old files
734
+ local cur_path = vim.api.nvim_buf_get_name(0)
735
+
736
+ H.oldfile_normalize()
737
+ local oldfile_arr = H.oldfile_get_array()
738
+ local n_oldfiles = #oldfile_arr
739
+
740
+ local iterator = {}
741
+
742
+ iterator.next = function(ind)
743
+ -- Allow advance in untrackable current buffer
744
+ if ind == nil then return 1 end
745
+ if n_oldfiles <= ind then return end
746
+ return ind + 1
747
+ end
748
+
749
+ iterator.prev = function(ind)
750
+ -- Allow advance in untrackable current buffer
751
+ if ind == nil then return n_oldfiles end
752
+ if ind <= 1 then return end
753
+ return ind - 1
754
+ end
755
+
756
+ iterator.state = H.cache.oldfile.recency[cur_path]
757
+ iterator.start_edge = 0
758
+ iterator.end_edge = n_oldfiles + 1
759
+
760
+ -- Iterate
761
+ local res_arr_ind = MiniBracketed.advance(iterator, direction, opts)
762
+ if res_arr_ind == nil or res_arr_ind == iterator.state then return end
763
+
764
+ -- Apply. Edit file at path while marking it not for tracking.
765
+ H.cache.oldfile.is_advancing = true
766
+ H.edit(oldfile_arr[res_arr_ind])
767
+ end
768
+
769
+ --- Quickfix from quickfix list
770
+ ---
771
+ --- Go to next/previous entry from |quickfix| list. This is similar to
772
+ --- |:cfirst|, |:cprevious|, |:cnext|, and |:clast| but with support of
773
+ --- wrapping around edges and |[count]| for "first"/"last" direction.
774
+ ---
775
+ --- Direction "forward" increases location number, "backward" - decreases.
776
+ ---
777
+ ---@param direction __bracketed_direction
778
+ ---@param opts __bracketed_opts
779
+ MiniBracketed.quickfix = function(direction, opts)
780
+ if H.is_disabled() then return end
781
+
782
+ H.validate_direction(direction, { 'first', 'backward', 'forward', 'last' }, 'quickfix')
783
+ opts =
784
+ vim.tbl_deep_extend('force', { n_times = vim.v.count1, wrap = true }, H.get_config().quickfix.options, opts or {})
785
+
786
+ H.qf_loc_implementation('quickfix', direction, opts)
787
+ end
788
+
789
+ --- Tree-sitter node
790
+ ---
791
+ --- Go to end/start of current tree-sitter node and its parents (except root).
792
+ ---
793
+ --- Notes:
794
+ --- - Requires loaded tree-sitter parser in the current buffer.
795
+ --- - Directions "first" and "last" work differently from most other targets
796
+ --- for performance reasons. They are essentially "backward" and "forward"
797
+ --- with very big `n_times` option.
798
+ --- - For similar reasons, `wrap` is not supported.
799
+ ---
800
+ --- Direction "forward" moves cursor forward to node's end, "backward" - backward
801
+ --- to node's start.
802
+ ---
803
+ ---@param direction __bracketed_direction
804
+ ---@param opts table|nil Options. A table with fields:
805
+ --- - <n_times> `(number)` - Number of times to advance. Default: |v:count1|.
806
+ --- __bracketed_add_to_jumplist
807
+ MiniBracketed.treesitter = function(direction, opts)
808
+ if H.is_disabled() then return end
809
+
810
+ H.validate_direction(direction, { 'first', 'backward', 'forward', 'last' }, 'treesitter')
811
+ opts = vim.tbl_deep_extend(
812
+ 'force',
813
+ { add_to_jumplist = false, n_times = vim.v.count1 },
814
+ H.get_config().treesitter.options,
815
+ opts or {}
816
+ )
817
+
818
+ opts.wrap = false
819
+
820
+ if direction == 'first' then
821
+ direction, opts.n_times = 'backward', math.huge
822
+ end
823
+ if direction == 'last' then
824
+ direction, opts.n_times = 'forward', math.huge
825
+ end
826
+
827
+ -- Define iterator that traverses current node and its parents (except root)
828
+ local is_bad_node = function(node) return node == nil or node:parent() == nil end
829
+ local is_after = function(row_new, col_new, row_ref, col_ref)
830
+ return row_ref < row_new or (row_ref == row_new and col_ref < col_new)
831
+ end
832
+ local is_before = function(row_new, col_new, row_ref, col_ref) return is_after(row_ref, col_ref, row_new, col_new) end
833
+
834
+ local iterator = {}
835
+
836
+ -- Traverse node and parents until node's end is after current position
837
+ iterator.next = function(node_pos)
838
+ local node = node_pos.node
839
+ if is_bad_node(node) then return nil end
840
+
841
+ local init_row, init_col = node_pos.pos[1], node_pos.pos[2]
842
+ local cur_row, cur_col, cur_node = init_row, init_col, node
843
+
844
+ repeat
845
+ if is_bad_node(cur_node) then break end
846
+
847
+ cur_row, cur_col = cur_node:end_()
848
+ -- Correct for end-exclusiveness
849
+ cur_col = cur_col - 1
850
+ cur_node = cur_node:parent()
851
+ until is_after(cur_row, cur_col, init_row, init_col)
852
+
853
+ if not is_after(cur_row, cur_col, init_row, init_col) then return end
854
+
855
+ return { node = cur_node, pos = { cur_row, cur_col } }
856
+ end
857
+
858
+ -- Traverse node and parents until node's start is before current position
859
+ iterator.prev = function(node_pos)
860
+ local node = node_pos.node
861
+ if is_bad_node(node) then return nil end
862
+
863
+ local init_row, init_col = node_pos.pos[1], node_pos.pos[2]
864
+ local cur_row, cur_col, cur_node = init_row, init_col, node
865
+
866
+ repeat
867
+ if is_bad_node(cur_node) then break end
868
+
869
+ cur_row, cur_col = cur_node:start()
870
+ cur_node = cur_node:parent()
871
+ until is_before(cur_row, cur_col, init_row, init_col)
872
+
873
+ if not is_before(cur_row, cur_col, init_row, init_col) then return end
874
+
875
+ return { node = cur_node, pos = { cur_row, cur_col } }
876
+ end
877
+
878
+ local cur_pos = vim.api.nvim_win_get_cursor(0)
879
+ local ok, node = pcall(H.get_treesitter_node, cur_pos[1] - 1, cur_pos[2])
880
+ if not ok then
881
+ H.error(
882
+ 'In `treesitter()` target can not find tree-sitter node under cursor.'
883
+ .. ' Do you have tree-sitter enabled in current buffer?'
884
+ )
885
+ end
886
+ iterator.state = { pos = { cur_pos[1] - 1, cur_pos[2] }, node = node }
887
+
888
+ -- Iterate
889
+ local res_node_pos = MiniBracketed.advance(iterator, direction, opts)
890
+ if res_node_pos == nil then return end
891
+
892
+ -- Possibly add current position to jumplist
893
+ if opts.add_to_jumplist then H.add_to_jumplist() end
894
+
895
+ -- Apply
896
+ H.set_cursor(res_node_pos.pos[1] + 1, res_node_pos.pos[2])
897
+ end
898
+
899
+ --- Undo along a tracked linear history
900
+ ---
901
+ --- In a nutshell:
902
+ --- - Keys |u| and |CTRL-R| (although remapped) can be used as usual, but every
903
+ --- their execution new state is recorded in this module's linear undo history.
904
+ --- - Advancing this target goes along linear undo history revealing undo states
905
+ --- IN ORDER THEY ACTUALLY APPEARED.
906
+ --- - One big difference with built-in methods is that tracked linear history
907
+ --- can repeat undo states (not consecutively, though).
908
+ ---
909
+ --- Neovim's default way of managing undo history is through branches (see
910
+ --- |undo-branches|). Basically it means that if you undo several changes and then
911
+ --- make new ones, it creates new undo branch while usually (see |'undolevels'|)
912
+ --- saving previous buffer states in another branch. While there are commands
913
+ --- to navigate by time of undo state creation (like |:earlier| and |:later|),
914
+ --- there is no intuitive way to cycle through them. Existing |g-| and |g+|
915
+ --- cycle through undo states BASED ON THEIR CREATION TIME, which often
916
+ --- gets confusing really guickly in extensively edited buffer.
917
+ ---
918
+ --- This `undo()` target provides a way to cycle through linear undo history
919
+ --- IN ORDER STATES ACTUALLY APPEARED. It does so by registering any new undo
920
+ --- states plus every time |MiniBracketed.register_undo_state()| is called. To have
921
+ --- more "out of the box" experience, |u| and |CTRL-R| are remapped to call it after
922
+ --- they perform their undo/redo.
923
+ ---
924
+ --- Example:
925
+ ---
926
+ --- To show more clearly the difference between advancing this target and using
927
+ --- built-in functionality, here is an example:
928
+ ---
929
+ --- - Create undo history in a new buffer (|:new|):
930
+ --- - Enter `one two three` text.
931
+ --- - Delete first word with `daw` and undo the change with `u`.
932
+ --- - Delete second word with `daw` and undo the change with `u`.
933
+ --- - Delete third word with `daw` and undo the change with `u`.
934
+ ---
935
+ --- - Now try one of the following (each one after performing previous steps in
936
+ --- separate new buffer):
937
+ --- - Press `u`. It goes back to empty buffer. Press `<C-R>` twice and it
938
+ --- goes to the latest change (`one two`). No way to get to other states
939
+ --- (like `two three` or `one three`) with these two keys.
940
+ ---
941
+ --- - Press `g-`. It goes to an empty buffer. Press `g+` 4 times. It cycles
942
+ --- through all available undo states IN ORDER THEY WERE CREATED.
943
+ ---
944
+ --- - Finally, press `[u`. It goes back to `one two` - state which was
945
+ --- PREVIOUSLY VISITED by the user. Another `[u` restores `one two three`.
946
+ --- Use `]U` to go to latest visited undo state.
947
+ ---
948
+ ---@param direction __bracketed_direction
949
+ ---@param opts __bracketed_opts
950
+ MiniBracketed.undo = function(direction, opts)
951
+ if H.is_disabled() then return end
952
+
953
+ H.validate_direction(direction, { 'first', 'backward', 'forward', 'last' }, 'undo')
954
+ opts = vim.tbl_deep_extend('force', { n_times = vim.v.count1, wrap = true }, H.get_config().undo.options, opts or {})
955
+
956
+ -- Define iterator that traverses undo states in order they appeared
957
+ local buf_id = vim.api.nvim_get_current_buf()
958
+ H.undo_sync(buf_id, vim.fn.undotree())
959
+
960
+ local iterator = {}
961
+ local buf_history = H.cache.undo[buf_id]
962
+ local n = #buf_history
963
+
964
+ iterator.next = function(id)
965
+ if id == nil or n <= id then return end
966
+ return id + 1
967
+ end
968
+
969
+ iterator.prev = function(id)
970
+ if id == nil or id <= 1 then return end
971
+ return id - 1
972
+ end
973
+
974
+ iterator.state = buf_history.current_id
975
+ iterator.start_edge = 0
976
+ iterator.end_edge = n + 1
977
+
978
+ -- Iterate
979
+ local res_id = MiniBracketed.advance(iterator, direction, opts)
980
+ if res_id == nil or res_id == iterator.state then return end
981
+
982
+ -- Apply. Move to undo state by number while recording current history id
983
+ buf_history.is_advancing = true
984
+ vim.cmd('undo ' .. buf_history[res_id])
985
+
986
+ buf_history.current_id = res_id
987
+ end
988
+
989
+ --- Register state for undo target
990
+ ---
991
+ --- Use this function to add current undo state to this module's linear undo
992
+ --- history. It is used in |MiniBracketed.setup()| to remap |u| and |CTRL-R| keys
993
+ --- to add their new state to linear undo history.
994
+ MiniBracketed.register_undo_state = function()
995
+ local buf_id = vim.api.nvim_get_current_buf()
996
+ local tree = vim.fn.undotree()
997
+
998
+ -- Synchronize undo history and stop advancing
999
+ H.undo_sync(buf_id, tree, false)
1000
+
1001
+ -- Append new undo state to linear history
1002
+ local buf_history = H.cache.undo[buf_id]
1003
+ H.undo_append_state(buf_history, tree.seq_cur)
1004
+ buf_history.current_id = #buf_history
1005
+ end
1006
+
1007
+ --- Normal window
1008
+ ---
1009
+ --- Go to next/previous normal window. Order by their number (see |winnr()|).
1010
+ ---
1011
+ --- Direction "forward" increases window number, "backward" - decreases.
1012
+ ---
1013
+ --- Only normal (non-floating) windows are used.
1014
+ ---
1015
+ ---@param direction __bracketed_direction
1016
+ ---@param opts __bracketed_opts
1017
+ MiniBracketed.window = function(direction, opts)
1018
+ if H.is_disabled() then return end
1019
+
1020
+ H.validate_direction(direction, { 'first', 'backward', 'forward', 'last' }, 'window')
1021
+ opts =
1022
+ vim.tbl_deep_extend('force', { n_times = vim.v.count1, wrap = true }, H.get_config().window.options, opts or {})
1023
+
1024
+ -- Define iterator that traverses all normal windows in "natural" order
1025
+ local is_normal = function(win_nr)
1026
+ local win_id = vim.fn.win_getid(win_nr)
1027
+ return vim.api.nvim_win_get_config(win_id).relative == ''
1028
+ end
1029
+
1030
+ local iterator = {}
1031
+
1032
+ iterator.next = function(win_nr)
1033
+ for nr = win_nr + 1, vim.fn.winnr('$') do
1034
+ if is_normal(nr) then return nr end
1035
+ end
1036
+ end
1037
+
1038
+ iterator.prev = function(win_nr)
1039
+ for nr = win_nr - 1, 1, -1 do
1040
+ if is_normal(nr) then return nr end
1041
+ end
1042
+ end
1043
+
1044
+ iterator.state = vim.fn.winnr()
1045
+ iterator.start_edge = 0
1046
+ iterator.end_edge = vim.fn.winnr('$') + 1
1047
+
1048
+ -- Iterate
1049
+ local res_win_nr = MiniBracketed.advance(iterator, direction, opts)
1050
+ if res_win_nr == iterator.state then return end
1051
+
1052
+ -- Apply
1053
+ vim.api.nvim_set_current_win(vim.fn.win_getid(res_win_nr))
1054
+ end
1055
+
1056
+ --- Replace "latest put region" with yank history entry
1057
+ ---
1058
+ --- After |MiniBracketed.setup()| is called, on every yank/delete/change operation
1059
+ --- (technically, every trigger of |TextYankPost| event) the object of operation
1060
+ --- is added to yank history. Advancing this target will replace the region of
1061
+ --- latest put operation with entry from yank history.
1062
+ ---
1063
+ --- By default works best if called RIGHT AFTER text paste (like with |p| or |P|).
1064
+ ---
1065
+ --- To better detect "latest put region", use |MiniBracketed.register_put_region()|
1066
+ --- as described later.
1067
+ ---
1068
+ --- Direction "forward" goes to newer yank history entry, "backward" - to older.
1069
+ ---
1070
+ --- Example:
1071
+ ---
1072
+ --- - Type `one two three`.
1073
+ --- - Yank each word with `yiw`.
1074
+ --- - Create new line and press `p`. This should paste `three`.
1075
+ --- - Type `[y`. This should replace latest `three` with `two`.
1076
+ ---
1077
+ --- # Latest put region ~
1078
+ ---
1079
+ --- "Latest put region" is (in order of decreasing priority):
1080
+ --- - The one from latest advance of this target.
1081
+ --- - The one registered by user with |MiniBracketed.register_put_region()|.
1082
+ --- - The one taken from |'[| and |']| marks.
1083
+ ---
1084
+ --- For users there are these approaches to manage which region will be used:
1085
+ --- - Do nothing. In this case region between `[` / `]` marks will always be used
1086
+ --- for first `yank` advance.
1087
+ --- Although doable, this has several drawbacks: it will use latest yanked or
1088
+ --- changed region or the entire buffer if marks are not set.
1089
+ --- If remember to advance this target only after recent put operation, this
1090
+ --- should work as expected.
1091
+ ---
1092
+ --- - Remap common put operations to use |MiniBracketed.register_put_region()|.
1093
+ --- After that, only regions from mapped put operations will be used for first
1094
+ --- advance. Example of custom mappings (note use of |:map-expression|): >lua
1095
+ ---
1096
+ --- local put_keys = { 'p', 'P' }
1097
+ --- for _, lhs in ipairs(put_keys) do
1098
+ --- local rhs = 'v:lua.MiniBracketed.register_put_region("' .. lhs .. '")'
1099
+ --- vim.keymap.set({ 'n', 'x' }, lhs, rhs, { expr = true })
1100
+ --- end
1101
+ --- <
1102
+ ---@param direction __bracketed_direction
1103
+ ---@param opts __bracketed_opts
1104
+ --- - <operators> `(table)` - array of operator names ("c", "d", or "y") for
1105
+ --- which yank entry should be used to advance. For example, use `{ "y" }`
1106
+ --- to advance only by entries actually resulted from yank operation with |y|.
1107
+ --- Default: `{ 'c', 'd', 'y' }`.
1108
+ MiniBracketed.yank = function(direction, opts)
1109
+ if H.is_disabled() then return end
1110
+
1111
+ H.validate_direction(direction, { 'first', 'backward', 'forward', 'last' }, 'yank')
1112
+ -- NOTE: Don't use `tbl_deep_extend` to prefer full input `operators` array
1113
+ local default_opts = { n_times = vim.v.count1, operators = { 'c', 'd', 'y' }, wrap = true }
1114
+ opts = vim.tbl_extend('force', default_opts, H.get_config().yank.options, opts or {})
1115
+
1116
+ -- Update yank history data
1117
+ local cache_yank, history = H.cache.yank, H.cache.yank.history
1118
+ local n_history = #history
1119
+ local cur_state = H.get_yank_state()
1120
+ if not vim.deep_equal(cur_state, cache_yank.state) then H.yank_stop_advancing() end
1121
+
1122
+ -- Define iterator that traverses yank history for entry with proper operator
1123
+ local iterator = {}
1124
+
1125
+ iterator.next = function(id)
1126
+ for i = id + 1, n_history do
1127
+ if vim.tbl_contains(opts.operators, history[i].operator) then return i end
1128
+ end
1129
+ end
1130
+
1131
+ iterator.prev = function(id)
1132
+ for i = id - 1, 1, -1 do
1133
+ if vim.tbl_contains(opts.operators, history[i].operator) then return i end
1134
+ end
1135
+ end
1136
+
1137
+ iterator.state = cache_yank.current_id
1138
+ iterator.start_edge = 0
1139
+ iterator.end_edge = n_history + 1
1140
+
1141
+ -- Iterate
1142
+ local res_id = MiniBracketed.advance(iterator, direction, opts)
1143
+ if res_id == nil then return end
1144
+
1145
+ -- Apply. Replace latest put region with yank history entry
1146
+ -- - Account for possible errors when latest region became out of bounds
1147
+ local ok, _ = pcall(H.replace_latest_put_region, cache_yank.history[res_id])
1148
+ if not ok then return end
1149
+
1150
+ cache_yank.current_id = res_id
1151
+ cache_yank.is_advancing = true
1152
+ cache_yank.state = H.get_yank_state()
1153
+ end
1154
+
1155
+ --- Register "latest put region"
1156
+ ---
1157
+ --- This function should be called after put register becomes relevant
1158
+ --- (|v:register| is appropriately set) but before put operation takes place
1159
+ --- (|'[| and |']| marks become relevant).
1160
+ ---
1161
+ --- Designed to be used in a user-facing expression mapping (see |:map-expression|).
1162
+ --- For mapping examples see |MiniBracketed.yank()|.
1163
+ ---
1164
+ ---@param put_key string Put keys to be remapped.
1165
+ ---
1166
+ ---@return string Returns `put_key` for a better usage inside expression mappings.
1167
+ MiniBracketed.register_put_region = function(put_key)
1168
+ local buf_id = vim.api.nvim_get_current_buf()
1169
+
1170
+ -- Compute mode of register **before** putting (while it is still relevant)
1171
+ local mode = H.get_register_mode(vim.v.register)
1172
+
1173
+ -- Register latest put region **after** it is done (when it becomes relevant)
1174
+ vim.schedule(function() H.cache.yank.user_put_regions[buf_id] = H.get_latest_region(mode) end)
1175
+
1176
+ return put_key
1177
+ end
1178
+
1179
+ --- Advance iterator
1180
+ ---
1181
+ --- This is the main function which performs any forward/backward/first/last
1182
+ --- advance in this module. Its basic idea is to take iterator (object containing
1183
+ --- information about current state and how to go to next/previous one) and go
1184
+ --- in certain direction until needed/allowed.
1185
+ ---
1186
+ --- Notes:
1187
+ --- - Directions "first" and "last" are convenience wrappers for "forward" and
1188
+ --- "backward" with pre-setting initial state to `start_edge` and `end_edge`.
1189
+ --- - Iterators `next()` and `prev()` methods should be able to handle `nil` as input.
1190
+ --- - This function only returns new state and doesn't modify `iterator.state`.
1191
+ ---
1192
+ ---@param iterator table Table:
1193
+ --- - Methods:
1194
+ --- - <next> - given state, return state in forward direction (no wrap).
1195
+ --- - <prev> - given state, return state in backward direction (no wrap).
1196
+ --- - Fields:
1197
+ --- - <state> - object describing current state.
1198
+ --- - <start_edge> (optional) - object with `forward(start_edge)` describing
1199
+ --- first state. If `nil`, can't wrap forward or use direction "first".
1200
+ --- - <end_edge> (optional) - object with `backward(end_edge)` describing
1201
+ --- last state. If `nil`, can't wrap backward or use direction "last".
1202
+ ---@param direction string Direction. One of "first", "backward", "forward", "last".
1203
+ ---@param opts table|nil Options with the following keys:
1204
+ --- - <n_times> `(number)` - number of times to go in input direction.
1205
+ --- Default: `v:count1`.
1206
+ --- - <wrap> `(boolean)` - whether to wrap around edges when `next()` or
1207
+ --- `prev()` return `nil`. Default: `true`.
1208
+ ---
1209
+ ---@return any Result state. If `nil`, could not reach any valid result state.
1210
+ MiniBracketed.advance = function(iterator, direction, opts)
1211
+ opts = vim.tbl_deep_extend('force', { n_times = vim.v.count1, wrap = true }, opts or {})
1212
+
1213
+ -- Use two states: "result" will be used as result, "current" will be used
1214
+ -- for iteration. Separation is needed at least for two reasons:
1215
+ -- - Allow partial reach of `n_times`.
1216
+ -- - Don't allow `start_edge` and `end_edge` be the output.
1217
+ local res_state = iterator.state
1218
+
1219
+ -- Compute loop data
1220
+ local iter_method = 'next'
1221
+ local cur_state = res_state
1222
+
1223
+ if direction == 'backward' then iter_method = 'prev' end
1224
+
1225
+ if direction == 'first' then
1226
+ cur_state, iter_method = iterator.start_edge, 'next'
1227
+ end
1228
+
1229
+ if direction == 'last' then
1230
+ cur_state, iter_method = iterator.end_edge, 'prev'
1231
+ end
1232
+
1233
+ -- Loop
1234
+ local iter = iterator[iter_method]
1235
+ for _ = 1, opts.n_times do
1236
+ -- Advance
1237
+ cur_state = iter(cur_state)
1238
+
1239
+ if cur_state == nil then
1240
+ -- Stop if can't wrap around edges
1241
+ if not opts.wrap then break end
1242
+
1243
+ -- Wrap around edge
1244
+ local edge = iterator.start_edge
1245
+ if iter_method == 'prev' then edge = iterator.end_edge end
1246
+ if edge == nil then break end
1247
+
1248
+ cur_state = iter(edge)
1249
+
1250
+ -- Ensure non-nil new state (can happen when there are no targets)
1251
+ if cur_state == nil then break end
1252
+ end
1253
+
1254
+ -- Allow only partial reach of `n_times`
1255
+ res_state = cur_state
1256
+ end
1257
+
1258
+ return res_state
1259
+ end
1260
+
1261
+ -- Helper data ================================================================
1262
+ -- Module default config
1263
+ H.default_config = vim.deepcopy(MiniBracketed.config)
1264
+
1265
+ H.cache = {
1266
+ -- Tracking of old files for `oldfile()` (this data structure is designed to be
1267
+ -- fast to add new file; initially `nil` to postpone initialization from
1268
+ -- `v:oldfiles` up until it is actually needed):
1269
+ -- - `recency` is a table with file paths as fields and numerical values
1270
+ -- indicating how recent file was accessed (higher - more recent).
1271
+ -- - `max_recency` is a maximum currently used `recency`. Used to add new file.
1272
+ -- - `is_advancing` is an indicator that buffer change was done inside
1273
+ -- `oldfile()` function. It is a key to enabling moving along old files
1274
+ -- (and not just going back and forth between two files because they swap
1275
+ -- places as two most recent files).
1276
+ -- - `last_advanced_bufname` - name of last advanced buffer. Used to update
1277
+ -- recency of only the last buffer entered during advancing.
1278
+ oldfile = nil,
1279
+
1280
+ -- Per buffer history of visited undo states. A table for each buffer id:
1281
+ -- - Numerical fields indicate actual history of visited undo states (from
1282
+ -- oldest to latest).
1283
+ -- - <current_id> - identifier of current history entry (used for iteration).
1284
+ -- - <seq_last> - latest recorded state (`seq_last` from `undotree()`).
1285
+ -- - <is_advancing> - whether currently advancing. Used to allow consecutive
1286
+ -- advances along tracked undo history.
1287
+ undo = {},
1288
+
1289
+ -- Cache for `yank` targets
1290
+ yank = {
1291
+ -- Per-buffer region of latest advance. Used to correctly determine range
1292
+ -- and mode of latest advanced region.
1293
+ advance_put_regions = {},
1294
+ -- Current id of yank entry in yank history
1295
+ current_id = 0,
1296
+ -- Yank history. Each element contains data necessary to replace latest put
1297
+ -- region with yanked one. See `track_yank()`.
1298
+ history = {},
1299
+ -- Whether currently advancing
1300
+ is_advancing = false,
1301
+ -- State of latest yank advancement to determine of currently advancing
1302
+ state = {},
1303
+ -- Per-buffer region registered by user as "latest put region". Used to
1304
+ -- overcome limitations of automatic detection of latest put region (like
1305
+ -- not reliable mode detection when pasting from register; respecting not
1306
+ -- only regions of put operations, but also yank and change).
1307
+ user_put_regions = {},
1308
+ },
1309
+ }
1310
+
1311
+ -- Helper functionality =======================================================
1312
+ -- Settings -------------------------------------------------------------------
1313
+ H.setup_config = function(config)
1314
+ H.check_type('config', config, 'table', true)
1315
+ config = vim.tbl_deep_extend('force', vim.deepcopy(H.default_config), config or {})
1316
+
1317
+ H.check_type('buffer', config.buffer, 'table')
1318
+ H.check_type('buffer.suffix', config.buffer.suffix, 'string')
1319
+ H.check_type('buffer.options', config.buffer.options, 'table')
1320
+
1321
+ H.check_type('comment', config.comment, 'table')
1322
+ H.check_type('comment.suffix', config.comment.suffix, 'string')
1323
+ H.check_type('comment.options', config.comment.options, 'table')
1324
+
1325
+ H.check_type('conflict', config.conflict, 'table')
1326
+ H.check_type('conflict.suffix', config.conflict.suffix, 'string')
1327
+ H.check_type('conflict.options', config.conflict.options, 'table')
1328
+
1329
+ H.check_type('diagnostic', config.diagnostic, 'table')
1330
+ H.check_type('diagnostic.suffix', config.diagnostic.suffix, 'string')
1331
+ H.check_type('diagnostic.options', config.diagnostic.options, 'table')
1332
+
1333
+ H.check_type('file', config.file, 'table')
1334
+ H.check_type('file.suffix', config.file.suffix, 'string')
1335
+ H.check_type('file.options', config.file.options, 'table')
1336
+
1337
+ H.check_type('indent', config.indent, 'table')
1338
+ H.check_type('indent.suffix', config.indent.suffix, 'string')
1339
+ H.check_type('indent.options', config.indent.options, 'table')
1340
+
1341
+ H.check_type('jump', config.jump, 'table')
1342
+ H.check_type('jump.suffix', config.jump.suffix, 'string')
1343
+ H.check_type('jump.options', config.jump.options, 'table')
1344
+
1345
+ H.check_type('location', config.location, 'table')
1346
+ H.check_type('location.suffix', config.location.suffix, 'string')
1347
+ H.check_type('location.options', config.location.options, 'table')
1348
+
1349
+ H.check_type('oldfile', config.oldfile, 'table')
1350
+ H.check_type('oldfile.suffix', config.oldfile.suffix, 'string')
1351
+ H.check_type('oldfile.options', config.oldfile.options, 'table')
1352
+
1353
+ H.check_type('quickfix', config.quickfix, 'table')
1354
+ H.check_type('quickfix.suffix', config.quickfix.suffix, 'string')
1355
+ H.check_type('quickfix.options', config.quickfix.options, 'table')
1356
+
1357
+ H.check_type('treesitter', config.treesitter, 'table')
1358
+ H.check_type('treesitter.suffix', config.treesitter.suffix, 'string')
1359
+ H.check_type('treesitter.options', config.treesitter.options, 'table')
1360
+
1361
+ H.check_type('undo', config.undo, 'table')
1362
+ H.check_type('undo.suffix', config.undo.suffix, 'string')
1363
+ H.check_type('undo.options', config.undo.options, 'table')
1364
+
1365
+ H.check_type('window', config.window, 'table')
1366
+ H.check_type('window.suffix', config.window.suffix, 'string')
1367
+ H.check_type('window.options', config.window.options, 'table')
1368
+
1369
+ H.check_type('yank', config.yank, 'table')
1370
+ H.check_type('yank.suffix', config.yank.suffix, 'string')
1371
+ H.check_type('yank.options', config.yank.options, 'table')
1372
+
1373
+ return config
1374
+ end
1375
+
1376
+ --stylua: ignore
1377
+ H.apply_config = function(config)
1378
+ MiniBracketed.config = config
1379
+
1380
+ -- Make mappings. NOTE: make 'forward'/'backward' *after* 'first'/'last' to
1381
+ -- allow non-letter suffixes define 'forward'/'backward'.
1382
+ if config.buffer.suffix ~= '' then
1383
+ local low, up = H.get_suffix_variants(config.buffer.suffix)
1384
+ H.map('n', '[' .. up, "<Cmd>lua MiniBracketed.buffer('first')<CR>", { desc = 'Buffer first' })
1385
+ H.map('n', ']' .. up, "<Cmd>lua MiniBracketed.buffer('last')<CR>", { desc = 'Buffer last' })
1386
+ H.map('n', '[' .. low, "<Cmd>lua MiniBracketed.buffer('backward')<CR>", { desc = 'Buffer backward' })
1387
+ H.map('n', ']' .. low, "<Cmd>lua MiniBracketed.buffer('forward')<CR>", { desc = 'Buffer forward' })
1388
+ end
1389
+
1390
+ if config.comment.suffix ~= '' then
1391
+ local low, up = H.get_suffix_variants(config.comment.suffix)
1392
+ H.map('n', '[' .. up, "<Cmd>lua MiniBracketed.comment('first')<CR>", { desc = 'Comment first' })
1393
+ H.map('x', '[' .. up, "<Cmd>lua MiniBracketed.comment('first')<CR>", { desc = 'Comment first' })
1394
+ H.map('o', '[' .. up, "V<Cmd>lua MiniBracketed.comment('first')<CR>", { desc = 'Comment first' })
1395
+
1396
+ H.map('n', ']' .. up, "<Cmd>lua MiniBracketed.comment('last')<CR>", { desc = 'Comment last' })
1397
+ H.map('x', ']' .. up, "<Cmd>lua MiniBracketed.comment('last')<CR>", { desc = 'Comment last' })
1398
+ H.map('o', ']' .. up, "V<Cmd>lua MiniBracketed.comment('last')<CR>", { desc = 'Comment last' })
1399
+
1400
+ H.map('n', '[' .. low, "<Cmd>lua MiniBracketed.comment('backward')<CR>", { desc = 'Comment backward' })
1401
+ H.map('x', '[' .. low, "<Cmd>lua MiniBracketed.comment('backward')<CR>", { desc = 'Comment backward' })
1402
+ H.map('o', '[' .. low, "V<Cmd>lua MiniBracketed.comment('backward')<CR>", { desc = 'Comment backward' })
1403
+
1404
+ H.map('n', ']' .. low, "<Cmd>lua MiniBracketed.comment('forward')<CR>", { desc = 'Comment forward' })
1405
+ H.map('x', ']' .. low, "<Cmd>lua MiniBracketed.comment('forward')<CR>", { desc = 'Comment forward' })
1406
+ H.map('o', ']' .. low, "V<Cmd>lua MiniBracketed.comment('forward')<CR>", { desc = 'Comment forward' })
1407
+ end
1408
+
1409
+ if config.conflict.suffix ~= '' then
1410
+ local low, up = H.get_suffix_variants(config.conflict.suffix)
1411
+ H.map('n', '[' .. up, "<Cmd>lua MiniBracketed.conflict('first')<CR>", { desc = 'Conflict first' })
1412
+ H.map('x', '[' .. up, "<Cmd>lua MiniBracketed.conflict('first')<CR>", { desc = 'Conflict first' })
1413
+ H.map('o', '[' .. up, "V<Cmd>lua MiniBracketed.conflict('first')<CR>", { desc = 'Conflict first' })
1414
+
1415
+ H.map('n', ']' .. up, "<Cmd>lua MiniBracketed.conflict('last')<CR>", { desc = 'Conflict last' })
1416
+ H.map('x', ']' .. up, "<Cmd>lua MiniBracketed.conflict('last')<CR>", { desc = 'Conflict last' })
1417
+ H.map('o', ']' .. up, "V<Cmd>lua MiniBracketed.conflict('last')<CR>", { desc = 'Conflict last' })
1418
+
1419
+ H.map('n', '[' .. low, "<Cmd>lua MiniBracketed.conflict('backward')<CR>", { desc = 'Conflict backward' })
1420
+ H.map('x', '[' .. low, "<Cmd>lua MiniBracketed.conflict('backward')<CR>", { desc = 'Conflict backward' })
1421
+ H.map('o', '[' .. low, "V<Cmd>lua MiniBracketed.conflict('backward')<CR>", { desc = 'Conflict backward' })
1422
+
1423
+ H.map('n', ']' .. low, "<Cmd>lua MiniBracketed.conflict('forward')<CR>", { desc = 'Conflict forward' })
1424
+ H.map('x', ']' .. low, "<Cmd>lua MiniBracketed.conflict('forward')<CR>", { desc = 'Conflict forward' })
1425
+ H.map('o', ']' .. low, "V<Cmd>lua MiniBracketed.conflict('forward')<CR>", { desc = 'Conflict forward' })
1426
+ end
1427
+
1428
+ if config.diagnostic.suffix ~= '' then
1429
+ local low, up = H.get_suffix_variants(config.diagnostic.suffix)
1430
+ H.map('n', '[' .. up, "<Cmd>lua MiniBracketed.diagnostic('first')<CR>", { desc = 'Diagnostic first' })
1431
+ H.map('x', '[' .. up, "<Cmd>lua MiniBracketed.diagnostic('first')<CR>", { desc = 'Diagnostic first' })
1432
+ H.map('o', '[' .. up, "v<Cmd>lua MiniBracketed.diagnostic('first')<CR>", { desc = 'Diagnostic first' })
1433
+
1434
+ H.map('n', ']' .. up, "<Cmd>lua MiniBracketed.diagnostic('last')<CR>", { desc = 'Diagnostic last' })
1435
+ H.map('x', ']' .. up, "<Cmd>lua MiniBracketed.diagnostic('last')<CR>", { desc = 'Diagnostic last' })
1436
+ H.map('o', ']' .. up, "v<Cmd>lua MiniBracketed.diagnostic('last')<CR>", { desc = 'Diagnostic last' })
1437
+
1438
+ H.map('n', '[' .. low, "<Cmd>lua MiniBracketed.diagnostic('backward')<CR>", { desc = 'Diagnostic backward' })
1439
+ H.map('x', '[' .. low, "<Cmd>lua MiniBracketed.diagnostic('backward')<CR>", { desc = 'Diagnostic backward' })
1440
+ H.map('o', '[' .. low, "v<Cmd>lua MiniBracketed.diagnostic('backward')<CR>", { desc = 'Diagnostic backward' })
1441
+
1442
+ H.map('n', ']' .. low, "<Cmd>lua MiniBracketed.diagnostic('forward')<CR>", { desc = 'Diagnostic forward' })
1443
+ H.map('x', ']' .. low, "<Cmd>lua MiniBracketed.diagnostic('forward')<CR>", { desc = 'Diagnostic forward' })
1444
+ H.map('o', ']' .. low, "v<Cmd>lua MiniBracketed.diagnostic('forward')<CR>", { desc = 'Diagnostic forward' })
1445
+ end
1446
+
1447
+ if config.file.suffix ~= '' then
1448
+ local low, up = H.get_suffix_variants(config.file.suffix)
1449
+ H.map('n', '[' .. up, "<Cmd>lua MiniBracketed.file('first')<CR>", { desc = 'File first' })
1450
+ H.map('n', ']' .. up, "<Cmd>lua MiniBracketed.file('last')<CR>", { desc = 'File last' })
1451
+ H.map('n', '[' .. low, "<Cmd>lua MiniBracketed.file('backward')<CR>", { desc = 'File backward' })
1452
+ H.map('n', ']' .. low, "<Cmd>lua MiniBracketed.file('forward')<CR>", { desc = 'File forward' })
1453
+ end
1454
+
1455
+ if config.indent.suffix ~= '' then
1456
+ local low, up = H.get_suffix_variants(config.indent.suffix)
1457
+ H.map('n', '[' .. up, "<Cmd>lua MiniBracketed.indent('first')<CR>", { desc = 'Indent first' })
1458
+ H.map('x', '[' .. up, "<Cmd>lua MiniBracketed.indent('first')<CR>", { desc = 'Indent first' })
1459
+ H.map('o', '[' .. up, "V<Cmd>lua MiniBracketed.indent('first')<CR>", { desc = 'Indent first' })
1460
+
1461
+ H.map('n', ']' .. up, "<Cmd>lua MiniBracketed.indent('last')<CR>", { desc = 'Indent last' })
1462
+ H.map('x', ']' .. up, "<Cmd>lua MiniBracketed.indent('last')<CR>", { desc = 'Indent last' })
1463
+ H.map('o', ']' .. up, "V<Cmd>lua MiniBracketed.indent('last')<CR>", { desc = 'Indent last' })
1464
+
1465
+ H.map('n', '[' .. low, "<Cmd>lua MiniBracketed.indent('backward')<CR>", { desc = 'Indent backward' })
1466
+ H.map('x', '[' .. low, "<Cmd>lua MiniBracketed.indent('backward')<CR>", { desc = 'Indent backward' })
1467
+ H.map('o', '[' .. low, "V<Cmd>lua MiniBracketed.indent('backward')<CR>", { desc = 'Indent backward' })
1468
+
1469
+ H.map('n', ']' .. low, "<Cmd>lua MiniBracketed.indent('forward')<CR>", { desc = 'Indent forward' })
1470
+ H.map('x', ']' .. low, "<Cmd>lua MiniBracketed.indent('forward')<CR>", { desc = 'Indent forward' })
1471
+ H.map('o', ']' .. low, "V<Cmd>lua MiniBracketed.indent('forward')<CR>", { desc = 'Indent forward' })
1472
+ end
1473
+
1474
+ if config.jump.suffix ~= '' then
1475
+ -- No Visual mode mappings due to implementation problems ()
1476
+ local low, up = H.get_suffix_variants(config.jump.suffix)
1477
+ H.map('n', '[' .. up, "<Cmd>lua MiniBracketed.jump('first')<CR>", { desc = 'Jump first' })
1478
+ H.map('o', '[' .. up, "v<Cmd>lua MiniBracketed.jump('first')<CR>", { desc = 'Jump first' })
1479
+
1480
+ H.map('n', ']' .. up, "<Cmd>lua MiniBracketed.jump('last')<CR>", { desc = 'Jump last' })
1481
+ H.map('o', ']' .. up, "v<Cmd>lua MiniBracketed.jump('last')<CR>", { desc = 'Jump last' })
1482
+
1483
+ H.map('n', '[' .. low, "<Cmd>lua MiniBracketed.jump('backward')<CR>", { desc = 'Jump backward' })
1484
+ H.map('o', '[' .. low, "v<Cmd>lua MiniBracketed.jump('backward')<CR>", { desc = 'Jump backward' })
1485
+
1486
+ H.map('n', ']' .. low, "<Cmd>lua MiniBracketed.jump('forward')<CR>", { desc = 'Jump forward' })
1487
+ H.map('o', ']' .. low, "v<Cmd>lua MiniBracketed.jump('forward')<CR>", { desc = 'Jump forward' })
1488
+ end
1489
+
1490
+ if config.oldfile.suffix ~= '' then
1491
+ local low, up = H.get_suffix_variants(config.oldfile.suffix)
1492
+ H.map('n', '[' .. up, "<Cmd>lua MiniBracketed.oldfile('first')<CR>", { desc = 'Oldfile first' })
1493
+ H.map('n', ']' .. up, "<Cmd>lua MiniBracketed.oldfile('last')<CR>", { desc = 'Oldfile last' })
1494
+ H.map('n', '[' .. low, "<Cmd>lua MiniBracketed.oldfile('backward')<CR>", { desc = 'Oldfile backward' })
1495
+ H.map('n', ']' .. low, "<Cmd>lua MiniBracketed.oldfile('forward')<CR>", { desc = 'Oldfile forward' })
1496
+ end
1497
+
1498
+ if config.location.suffix ~= '' then
1499
+ local low, up = H.get_suffix_variants(config.location.suffix)
1500
+ H.map('n', '[' .. up, "<Cmd>lua MiniBracketed.location('first')<CR>", { desc = 'Location first' })
1501
+ H.map('n', ']' .. up, "<Cmd>lua MiniBracketed.location('last')<CR>", { desc = 'Location last' })
1502
+ H.map('n', '[' .. low, "<Cmd>lua MiniBracketed.location('backward')<CR>", { desc = 'Location backward' })
1503
+ H.map('n', ']' .. low, "<Cmd>lua MiniBracketed.location('forward')<CR>", { desc = 'Location forward' })
1504
+ end
1505
+
1506
+ if config.quickfix.suffix ~= '' then
1507
+ local low, up = H.get_suffix_variants(config.quickfix.suffix)
1508
+ H.map('n', '[' .. up, "<Cmd>lua MiniBracketed.quickfix('first')<CR>", { desc = 'Quickfix first' })
1509
+ H.map('n', ']' .. up, "<Cmd>lua MiniBracketed.quickfix('last')<CR>", { desc = 'Quickfix last' })
1510
+ H.map('n', '[' .. low, "<Cmd>lua MiniBracketed.quickfix('backward')<CR>", { desc = 'Quickfix backward' })
1511
+ H.map('n', ']' .. low, "<Cmd>lua MiniBracketed.quickfix('forward')<CR>", { desc = 'Quickfix forward' })
1512
+ end
1513
+
1514
+ if config.treesitter.suffix ~= '' then
1515
+ local low, up = H.get_suffix_variants(config.treesitter.suffix)
1516
+ H.map('n', '[' .. up, "<Cmd>lua MiniBracketed.treesitter('first')<CR>", { desc = 'Treesitter first' })
1517
+ H.map('x', '[' .. up, "<Cmd>lua MiniBracketed.treesitter('first')<CR>", { desc = 'Treesitter first' })
1518
+ H.map('o', '[' .. up, "v<Cmd>lua MiniBracketed.treesitter('first')<CR>", { desc = 'Treesitter first' })
1519
+
1520
+ H.map('n', ']' .. up, "<Cmd>lua MiniBracketed.treesitter('last')<CR>", { desc = 'Treesitter last' })
1521
+ H.map('x', ']' .. up, "<Cmd>lua MiniBracketed.treesitter('last')<CR>", { desc = 'Treesitter last' })
1522
+ H.map('o', ']' .. up, "v<Cmd>lua MiniBracketed.treesitter('last')<CR>", { desc = 'Treesitter last' })
1523
+
1524
+ H.map('n', '[' .. low, "<Cmd>lua MiniBracketed.treesitter('backward')<CR>", { desc = 'Treesitter backward' })
1525
+ H.map('x', '[' .. low, "<Cmd>lua MiniBracketed.treesitter('backward')<CR>", { desc = 'Treesitter backward' })
1526
+ H.map('o', '[' .. low, "v<Cmd>lua MiniBracketed.treesitter('backward')<CR>", { desc = 'Treesitter backward' })
1527
+
1528
+ H.map('n', ']' .. low, "<Cmd>lua MiniBracketed.treesitter('forward')<CR>", { desc = 'Treesitter forward' })
1529
+ H.map('x', ']' .. low, "<Cmd>lua MiniBracketed.treesitter('forward')<CR>", { desc = 'Treesitter forward' })
1530
+ H.map('o', ']' .. low, "v<Cmd>lua MiniBracketed.treesitter('forward')<CR>", { desc = 'Treesitter forward' })
1531
+ end
1532
+
1533
+ if config.undo.suffix ~= '' then
1534
+ local low, up = H.get_suffix_variants(config.undo.suffix)
1535
+ H.map('n', '[' .. up, "<Cmd>lua MiniBracketed.undo('first')<CR>", { desc = 'Undo first' })
1536
+ H.map('n', ']' .. up, "<Cmd>lua MiniBracketed.undo('last')<CR>", { desc = 'Undo last' })
1537
+ H.map('n', '[' .. low, "<Cmd>lua MiniBracketed.undo('backward')<CR>", { desc = 'Undo backward' })
1538
+ H.map('n', ']' .. low, "<Cmd>lua MiniBracketed.undo('forward')<CR>", { desc = 'Undo forward' })
1539
+
1540
+ H.map('n', 'u', 'u<Cmd>lua MiniBracketed.register_undo_state()<CR>')
1541
+ H.map('n', '<C-R>', '<C-R><Cmd>lua MiniBracketed.register_undo_state()<CR>')
1542
+ end
1543
+
1544
+ if config.window.suffix ~= '' then
1545
+ local low, up = H.get_suffix_variants(config.window.suffix)
1546
+ H.map('n', '[' .. up, "<Cmd>lua MiniBracketed.window('first')<CR>", { desc = 'Window first' })
1547
+ H.map('n', ']' .. up, "<Cmd>lua MiniBracketed.window('last')<CR>", { desc = 'Window last' })
1548
+ H.map('n', '[' .. low, "<Cmd>lua MiniBracketed.window('backward')<CR>", { desc = 'Window backward' })
1549
+ H.map('n', ']' .. low, "<Cmd>lua MiniBracketed.window('forward')<CR>", { desc = 'Window forward' })
1550
+ end
1551
+
1552
+ if config.yank.suffix ~= '' then
1553
+ local low, up = H.get_suffix_variants(config.yank.suffix)
1554
+ H.map('n', '[' .. up, "<Cmd>lua MiniBracketed.yank('first')<CR>", { desc = 'Yank first' })
1555
+ H.map('n', ']' .. up, "<Cmd>lua MiniBracketed.yank('last')<CR>", { desc = 'Yank last' })
1556
+ H.map('n', '[' .. low, "<Cmd>lua MiniBracketed.yank('backward')<CR>", { desc = 'Yank backward' })
1557
+ H.map('n', ']' .. low, "<Cmd>lua MiniBracketed.yank('forward')<CR>", { desc = 'Yank forward' })
1558
+ end
1559
+ end
1560
+
1561
+ H.get_suffix_variants = function(char) return char:lower(), char:upper() end
1562
+
1563
+ H.create_autocommands = function()
1564
+ local gr = vim.api.nvim_create_augroup('MiniBracketed', {})
1565
+
1566
+ local au = function(event, pattern, callback, desc)
1567
+ vim.api.nvim_create_autocmd(event, { group = gr, pattern = pattern, callback = callback, desc = desc })
1568
+ end
1569
+
1570
+ au('BufEnter', '*', H.track_oldfile, 'Track oldfile')
1571
+ au('TextYankPost', '*', H.track_yank, 'Track yank')
1572
+ end
1573
+
1574
+ H.is_disabled = function() return vim.g.minibracketed_disable == true or vim.b.minibracketed_disable == true end
1575
+
1576
+ H.get_config = function(config)
1577
+ return vim.tbl_deep_extend('force', MiniBracketed.config, vim.b.minibracketed_config or {}, config or {})
1578
+ end
1579
+
1580
+ -- Autocommands ---------------------------------------------------------------
1581
+ H.track_oldfile = function()
1582
+ if H.is_disabled() then return end
1583
+
1584
+ -- Ensure tracking data is initialized
1585
+ H.oldfile_ensure_initialized()
1586
+
1587
+ -- Reset tracking indicator to allow proper tracking of next buffer
1588
+ local is_advancing = H.cache.oldfile.is_advancing
1589
+ H.cache.oldfile.is_advancing = false
1590
+
1591
+ -- Track only appropriate buffers (normal buffers with path)
1592
+ local path = vim.api.nvim_buf_get_name(0)
1593
+ local is_proper_buffer = path ~= '' and vim.bo.buftype == ''
1594
+ if not is_proper_buffer then return end
1595
+
1596
+ -- If advancing, don't touch tracking data to be able to consecutively move
1597
+ -- along recent files. Cache advanced buffer name to later update recency of
1598
+ -- the last one (just before buffer switching outside of `oldfile()`)
1599
+ local cache_oldfile = H.cache.oldfile
1600
+
1601
+ if is_advancing then
1602
+ cache_oldfile.last_advanced_bufname = path
1603
+ return
1604
+ end
1605
+
1606
+ -- If not advancing, update recency of a single latest advanced buffer (if
1607
+ -- present) and then update recency of current buffer
1608
+ if cache_oldfile.last_advanced_bufname ~= nil then
1609
+ H.oldfile_update_recency(cache_oldfile.last_advanced_bufname)
1610
+ cache_oldfile.last_advanced_bufname = nil
1611
+ end
1612
+
1613
+ H.oldfile_update_recency(path)
1614
+ end
1615
+
1616
+ H.track_yank = function()
1617
+ -- Don't track if asked not to. Allows other functionality to disable
1618
+ -- tracking (like in 'mini.move').
1619
+ if H.is_disabled() then return end
1620
+
1621
+ -- Track all `TextYankPost` events without exceptions. This leads to a better
1622
+ -- handling of charwise/linewise/blockwise selection detection.
1623
+ local event = vim.v.event
1624
+ table.insert(
1625
+ H.cache.yank.history,
1626
+ { operator = event.operator, regcontents = event.regcontents, regtype = event.regtype }
1627
+ )
1628
+
1629
+ H.yank_stop_advancing()
1630
+ end
1631
+
1632
+ -- Comments -------------------------------------------------------------------
1633
+ H.make_comment_checker = function()
1634
+ local left, right = unpack(vim.fn.split(vim.bo.commentstring, '%s'))
1635
+ left, right = left or '', right or ''
1636
+ if left == '' and right == '' then return nil end
1637
+
1638
+ -- String is commented if it has structure:
1639
+ -- <space> <left> <anything> <right> <space>
1640
+ local regex = string.format('^%%s-%s.*%s%%s-$', vim.pesc(vim.trim(left)), vim.pesc(vim.trim(right)))
1641
+
1642
+ -- Check if line with number `line_num` is a comment. NOTE: `getline()`
1643
+ -- return empty string for invalid line number, which makes them *not
1644
+ -- commented*.
1645
+ return function(line_num) return vim.fn.getline(line_num):find(regex) ~= nil end
1646
+ end
1647
+
1648
+ -- Conflicts ------------------------------------------------------------------
1649
+ H.is_conflict_mark = function(line_num)
1650
+ local l_start = vim.fn.getline(line_num):sub(1, 8)
1651
+ return l_start == '<<<<<<< ' or l_start == '=======' or l_start == '>>>>>>> '
1652
+ end
1653
+
1654
+ -- Diagnostic -----------------------------------------------------------------
1655
+ H.diagnostic_jump = function(pos, float, severity)
1656
+ vim.diagnostic.jump({ count = 1, pos = pos, float = float, severity = severity })
1657
+ end
1658
+ if vim.fn.has('nvim-0.11') == 0 then
1659
+ H.diagnostic_jump = function(pos, float, severity)
1660
+ vim.diagnostic.goto_next({ cursor_position = pos, float = float, severity = severity })
1661
+ end
1662
+ end
1663
+
1664
+ -- Files ----------------------------------------------------------------------
1665
+ H.get_file_data = function()
1666
+ -- Compute target directory
1667
+ local cur_buf_path = vim.api.nvim_buf_get_name(0)
1668
+ local dir_path = cur_buf_path ~= '' and vim.fn.fnamemodify(cur_buf_path, ':p:h') or vim.fn.getcwd()
1669
+
1670
+ -- Compute sorted array of all files in target directory
1671
+ local dir_handle = vim.loop.fs_scandir(dir_path)
1672
+ local files_stream = function() return vim.loop.fs_scandir_next(dir_handle) end
1673
+
1674
+ local files = {}
1675
+ for basename, fs_type in files_stream do
1676
+ if fs_type == 'file' then table.insert(files, basename) end
1677
+ end
1678
+
1679
+ -- - Sort files ignoring case
1680
+ table.sort(files, function(x, y) return x:lower() < y:lower() end)
1681
+
1682
+ if #files == 0 then return end
1683
+ return { directory = dir_path, file_basenames = files }
1684
+ end
1685
+
1686
+ -- Jumps ----------------------------------------------------------------------
1687
+ H.make_jump = function(jump_list, cur_jump_num, new_jump_num)
1688
+ local num_diff = new_jump_num - cur_jump_num
1689
+
1690
+ if num_diff == 0 then
1691
+ -- Perform jump manually to always jump. Example: move to last jump and
1692
+ -- move manually; then jump with "last" direction should move to last jump.
1693
+ local jump_entry = jump_list[new_jump_num]
1694
+ pcall(vim.fn.cursor, { jump_entry.lnum, jump_entry.col + 1, jump_entry.coladd })
1695
+ else
1696
+ -- Use builtin mappings to also update current jump entry
1697
+ local key = num_diff > 0 and '\t' or '\15'
1698
+ vim.cmd('normal! ' .. math.abs(num_diff) .. key)
1699
+ end
1700
+
1701
+ -- Open just enough folds
1702
+ vim.cmd('normal! zv')
1703
+ end
1704
+
1705
+ -- Oldfile --------------------------------------------------------------------
1706
+ H.oldfile_normalize = function()
1707
+ -- Ensure that tracking data is initialized
1708
+ H.oldfile_ensure_initialized()
1709
+
1710
+ -- Order currently readable paths in increasing order of recency
1711
+ local recency_pairs = {}
1712
+ for path, rec in pairs(H.cache.oldfile.recency) do
1713
+ if vim.fn.filereadable(path) == 1 then table.insert(recency_pairs, { path, rec }) end
1714
+ end
1715
+ table.sort(recency_pairs, function(x, y) return x[2] < y[2] end)
1716
+
1717
+ -- Construct new tracking data with recency from 1 to number of entries
1718
+ local new_recency = {}
1719
+ for i, pair in ipairs(recency_pairs) do
1720
+ new_recency[pair[1]] = i
1721
+ end
1722
+
1723
+ H.cache.oldfile = { recency = new_recency, max_recency = #recency_pairs, is_advancing = H.cache.oldfile.is_advancing }
1724
+ end
1725
+
1726
+ H.oldfile_ensure_initialized = function()
1727
+ if H.cache.oldfile ~= nil or vim.v.oldfiles == nil then return end
1728
+
1729
+ local n = #vim.v.oldfiles
1730
+ local recency = {}
1731
+ for i, path in ipairs(vim.v.oldfiles) do
1732
+ recency[path] = n - i + 1
1733
+ end
1734
+
1735
+ H.cache.oldfile = { recency = recency, max_recency = n, is_advancing = false }
1736
+ end
1737
+
1738
+ H.oldfile_get_array = function()
1739
+ local res = {}
1740
+ for path, i in pairs(H.cache.oldfile.recency) do
1741
+ res[i] = path
1742
+ end
1743
+ return res
1744
+ end
1745
+
1746
+ H.oldfile_update_recency = function(path)
1747
+ local n = H.cache.oldfile.max_recency + 1
1748
+ H.cache.oldfile.recency[path] = n
1749
+ H.cache.oldfile.max_recency = n
1750
+ end
1751
+
1752
+ -- Quickfix/Location lists ----------------------------------------------------
1753
+ H.qf_loc_implementation = function(list_type, direction, opts)
1754
+ local get_list, goto_command = vim.fn.getqflist, 'cc'
1755
+ if list_type == 'location' then
1756
+ get_list, goto_command = function(...) return vim.fn.getloclist(0, ...) end, 'll'
1757
+ end
1758
+
1759
+ -- Define iterator that traverses quickfix/location list entries
1760
+ local list = get_list()
1761
+ local n_list = #list
1762
+ if n_list == 0 then return end
1763
+
1764
+ local iterator = {}
1765
+
1766
+ iterator.next = function(ind)
1767
+ if ind == nil or n_list <= ind then return end
1768
+ return ind + 1
1769
+ end
1770
+
1771
+ iterator.prev = function(ind)
1772
+ if ind == nil or ind <= 1 then return end
1773
+ return ind - 1
1774
+ end
1775
+
1776
+ iterator.state = get_list({ idx = 0 }).idx
1777
+ iterator.start_edge = 0
1778
+ iterator.end_edge = n_list + 1
1779
+
1780
+ -- Iterate
1781
+ local res_ind = MiniBracketed.advance(iterator, direction, opts)
1782
+
1783
+ -- Apply. Focus target entry, open enough folds and center. Allow jumping to
1784
+ -- current quickfix/loclist entry as it might be different from current
1785
+ -- cursor position.
1786
+ vim.cmd(goto_command .. ' ' .. res_ind)
1787
+ vim.cmd('normal! zvzz')
1788
+ end
1789
+
1790
+ -- Treesitter -----------------------------------------------------------------
1791
+ H.get_treesitter_node = function(row, col) return vim.treesitter.get_node({ pos = { row, col } }) end
1792
+
1793
+ -- Undo -----------------------------------------------------------------------
1794
+ H.undo_sync = function(buf_id, tree, is_advancing)
1795
+ -- Get or initialize buffer history of visited undo states
1796
+ local prev_buf_history = H.cache.undo[buf_id] or H.undo_init(tree)
1797
+ if is_advancing == nil then is_advancing = prev_buf_history.is_advancing end
1798
+
1799
+ -- Prune current buffer history to contain only allowed state numbers. This
1800
+ -- assumes that once undo state is not allowed, it will always be not
1801
+ -- allowed. This step is needed because allowed undo state numbers can:
1802
+ -- - Not start from 1 due to 'undolevels'.
1803
+ -- - Contain range of missing state numbers due to `:undo!`.
1804
+ --
1805
+ -- Do this even if advancing because `:undo!` can be executed at any time.
1806
+ local allowed_states = H.undo_get_allowed_state_numbers(tree)
1807
+
1808
+ local buf_history = {}
1809
+ for i, state_num in ipairs(prev_buf_history) do
1810
+ -- Use only allowed states
1811
+ if allowed_states[state_num] then H.undo_append_state(buf_history, state_num) end
1812
+
1813
+ -- Correctly track current id when advancing
1814
+ if i == prev_buf_history.current_id then buf_history.current_id = #buf_history end
1815
+ end
1816
+ buf_history.current_id = buf_history.current_id or #buf_history
1817
+ buf_history.is_advancing = prev_buf_history.is_advancing
1818
+ buf_history.seq_last = prev_buf_history.seq_last
1819
+
1820
+ H.cache.undo[buf_id] = buf_history
1821
+
1822
+ -- Continue only if not actually advancing: either if set so manually *or* if
1823
+ -- there were additions to undo history *or* some states became not allowed
1824
+ -- (due to `:undo!`).
1825
+ if is_advancing and tree.seq_last <= buf_history.seq_last and #buf_history == #prev_buf_history then return end
1826
+
1827
+ -- Register current undo state (if not equal to last).
1828
+ -- Usually it is a result of advance but also can be due to `:undo`/`:undo!`.
1829
+ H.undo_append_state(buf_history, tree.seq_cur)
1830
+
1831
+ -- Add all new *allowed* undo states created since last sync
1832
+ for new_state = buf_history.seq_last + 1, tree.seq_last do
1833
+ if allowed_states[new_state] then H.undo_append_state(buf_history, new_state) end
1834
+ end
1835
+
1836
+ -- Update data to be most recent
1837
+ buf_history.current_id = #buf_history
1838
+ buf_history.is_advancing = false
1839
+ buf_history.seq_last = tree.seq_last
1840
+ end
1841
+
1842
+ H.undo_append_state = function(buf_history, state_num)
1843
+ -- Ensure that there are no two consecutive equal states
1844
+ if state_num == nil or buf_history[#buf_history] == state_num then return end
1845
+
1846
+ table.insert(buf_history, state_num)
1847
+ end
1848
+
1849
+ H.undo_init = function(tree)
1850
+ -- Assume all previous states are allowed
1851
+ local res = {}
1852
+ for i = 0, tree.seq_last do
1853
+ res[i + 1] = i
1854
+ end
1855
+ res.current_id = #res
1856
+ res.is_advancing = false
1857
+ res.seq_last = tree.seq_last
1858
+
1859
+ return res
1860
+ end
1861
+
1862
+ H.undo_get_allowed_state_numbers = function(tree)
1863
+ -- `:undo 0` is always possible (goes to *before* the first allowed state).
1864
+ local res = { [0] = true }
1865
+ local traverse
1866
+ traverse = function(entries)
1867
+ for _, e in ipairs(entries) do
1868
+ if e.alt ~= nil then traverse(e.alt) end
1869
+ res[e.seq] = true
1870
+ end
1871
+ end
1872
+
1873
+ traverse(tree.entries)
1874
+ return res
1875
+ end
1876
+
1877
+ -- Yank -----------------------------------------------------------------------
1878
+ H.yank_stop_advancing = function()
1879
+ H.cache.yank.current_id = #H.cache.yank.history
1880
+ H.cache.yank.is_advancing = false
1881
+ H.cache.yank.advance_put_regions[vim.api.nvim_get_current_buf()] = nil
1882
+ end
1883
+
1884
+ H.get_yank_state = function() return { buf_id = vim.api.nvim_get_current_buf(), changedtick = vim.b.changedtick } end
1885
+
1886
+ H.replace_latest_put_region = function(yank_data)
1887
+ -- Squash all yank advancing in a single undo block
1888
+ local normal_command = (H.cache.yank.is_advancing and 'undojoin | ' or '') .. 'silent normal! '
1889
+ local normal_fun = function(x) vim.cmd(normal_command .. x) end
1890
+
1891
+ -- Compute latest put region: from latest `yank` advance; or from user's
1892
+ -- latest put; or from `[`/`]` marks
1893
+ local cache_yank = H.cache.yank
1894
+ local buf_id = vim.api.nvim_get_current_buf()
1895
+ local latest_region = cache_yank.advance_put_regions[buf_id]
1896
+ or cache_yank.user_put_regions[buf_id]
1897
+ or H.get_latest_region()
1898
+
1899
+ -- Compute modes for replaced and new regions.
1900
+ local latest_mode = latest_region.mode
1901
+ local new_mode = yank_data.regtype:sub(1, 1)
1902
+
1903
+ -- Compute later put key based on replaced and new regions.
1904
+ -- Prefer `P` but use `p` in cases replaced region was on the edge: last line
1905
+ -- for linewise-linewise replace or last column for nonlinewise-nonlinewise.
1906
+ local is_linewise = (latest_mode == 'V' and new_mode == 'V')
1907
+ local is_edge_line = is_linewise and latest_region.to.line == vim.fn.line('$')
1908
+
1909
+ local is_charblockwise = (latest_mode ~= 'V' and new_mode ~= 'V')
1910
+ local is_edge_col = is_charblockwise and latest_region.to.col == vim.fn.getline(latest_region.to.line):len()
1911
+
1912
+ local is_edge = is_edge_line or is_edge_col
1913
+ local put_key = is_edge and 'p' or 'P'
1914
+
1915
+ -- Delete latest region
1916
+ H.region_delete(latest_region, normal_fun)
1917
+
1918
+ -- Paste yank data using temporary register
1919
+ local cache_z_reg = vim.fn.getreg('z')
1920
+ vim.fn.setreg('z', yank_data.regcontents, yank_data.regtype)
1921
+
1922
+ normal_fun('"z' .. put_key)
1923
+
1924
+ vim.fn.setreg('z', cache_z_reg)
1925
+
1926
+ -- Register newly put region for correct further advancing
1927
+ cache_yank.advance_put_regions[buf_id] = H.get_latest_region(new_mode)
1928
+ end
1929
+
1930
+ H.get_latest_region = function(mode)
1931
+ local left, right = vim.fn.getpos("'["), vim.fn.getpos("']")
1932
+ return {
1933
+ from = { line = left[2], col = left[3] },
1934
+ to = { line = right[2], col = right[3] },
1935
+ -- Mode should be one of 'v', 'V', or '\22' ('<C-v>')
1936
+ -- By default use mode of current or unnamed register
1937
+ -- NOTE: this breaks if latest paste was not from unnamed register.
1938
+ -- To account for that, use `register_put_region()`.
1939
+ mode = mode or H.get_register_mode(vim.v.register),
1940
+ }
1941
+ end
1942
+
1943
+ H.region_delete = function(region, normal_fun)
1944
+ -- Start with `to` to have cursor positioned on region start after deletion
1945
+ H.set_cursor(region.to.line, region.to.col - 1)
1946
+
1947
+ -- Do nothing more if region is empty (or leads to unnecessary line deletion)
1948
+ local is_empty = region.from.line == region.to.line
1949
+ and region.from.col == region.to.col
1950
+ and vim.fn.getline(region.from.line) == ''
1951
+
1952
+ if is_empty then return end
1953
+
1954
+ -- Select region in correct Visual mode
1955
+ normal_fun(region.mode)
1956
+ H.set_cursor(region.from.line, region.from.col - 1)
1957
+
1958
+ -- Delete region in "black hole" register
1959
+ -- - NOTE: it doesn't affect history as `"_` doesn't trigger `TextYankPost`
1960
+ normal_fun('"_d')
1961
+ end
1962
+
1963
+ H.get_register_mode = function(register)
1964
+ -- Use only first character to correctly get '\22' in blockwise mode
1965
+ return vim.fn.getregtype(register):sub(1, 1)
1966
+ end
1967
+
1968
+ -- Utilities ------------------------------------------------------------------
1969
+ H.error = function(msg) error('(mini.bracketed) ' .. msg, 0) end
1970
+
1971
+ H.check_type = function(name, val, ref, allow_nil)
1972
+ if type(val) == ref or (ref == 'callable' and vim.is_callable(val)) or (allow_nil and val == nil) then return end
1973
+ H.error(string.format('`%s` should be %s, not %s', name, ref, type(val)))
1974
+ end
1975
+
1976
+ H.validate_direction = function(direction, choices, fun_name)
1977
+ if not vim.tbl_contains(choices, direction) then
1978
+ local choices_string = "'" .. table.concat(choices, "', '") .. "'"
1979
+ local error_text = string.format('In `%s()` argument `direction` should be one of %s.', fun_name, choices_string)
1980
+ H.error(error_text)
1981
+ end
1982
+ end
1983
+
1984
+ H.map = function(mode, lhs, rhs, opts)
1985
+ if lhs == '' then return end
1986
+ opts = vim.tbl_deep_extend('force', { silent = true }, opts or {})
1987
+ vim.keymap.set(mode, lhs, rhs, opts)
1988
+ end
1989
+
1990
+ H.edit = function(path, win_id)
1991
+ if type(path) ~= 'string' then return end
1992
+ local b = vim.api.nvim_win_get_buf(win_id or 0)
1993
+ local try_mimic_buf_reuse = (vim.fn.bufname(b) == '' and vim.bo[b].buftype ~= 'quickfix' and not vim.bo[b].modified)
1994
+ and (#vim.fn.win_findbuf(b) == 1 and vim.deep_equal(vim.fn.getbufline(b, 1, '$'), { '' }))
1995
+ local buf_id = vim.fn.bufadd(vim.fn.fnamemodify(path, ':.'))
1996
+ -- Showing in window also loads. Use `pcall` to not error with swap messages.
1997
+ pcall(vim.api.nvim_win_set_buf, win_id or 0, buf_id)
1998
+ vim.bo[buf_id].buflisted = true
1999
+ if try_mimic_buf_reuse then pcall(vim.api.nvim_buf_delete, b, { unload = false }) end
2000
+ return buf_id
2001
+ end
2002
+
2003
+ H.add_to_jumplist = function() vim.cmd([[normal! m']]) end
2004
+
2005
+ H.set_cursor = function(row, col)
2006
+ if row <= 0 then return vim.api.nvim_win_set_cursor(0, { 1, 0 }) end
2007
+ local n_lines = vim.api.nvim_buf_line_count(0)
2008
+ if n_lines < row then return vim.api.nvim_win_set_cursor(0, { n_lines, vim.fn.getline(n_lines):len() - 1 }) end
2009
+ col = math.min(math.max(col, 0), vim.fn.getline(row):len())
2010
+ return vim.api.nvim_win_set_cursor(0, { row, col })
2011
+ end
2012
+
2013
+ return MiniBracketed