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,1132 @@
1
+ --- *mini.splitjoin* Split and join arguments
2
+ ---
3
+ --- MIT License Copyright (c) 2023 Evgeni Chasnovski
4
+
5
+ --- Features:
6
+ --- - Mappings and Lua functions that modify arguments (regions inside brackets
7
+ --- between allowed separators) under cursor.
8
+ ---
9
+ --- Supported actions:
10
+ --- - Toggle - split if arguments are on single line, join otherwise.
11
+ --- Main supported function of the module. See |MiniSplitjoin.toggle()|.
12
+ --- - Split - make every argument separator be on end of separate line.
13
+ --- See |MiniSplitjoin.split()|.
14
+ --- - Join - make all arguments be on single line.
15
+ --- See |MiniSplitjoin.join()|.
16
+ ---
17
+ --- - Mappings are dot-repeatable in Normal mode and work in Visual mode.
18
+ ---
19
+ --- - Customizable argument detection (see |MiniSplitjoin.config.detect|):
20
+ --- - Which brackets can contain arguments.
21
+ --- - Which strings can separate arguments.
22
+ --- - Which regions are excluded when looking for separators (like inside
23
+ --- nested brackets or quotes).
24
+ ---
25
+ --- - Customizable pre and post hooks for both split and join. See `split` and
26
+ --- `join` in |MiniSplitjoin.config|. There are several built-in ones
27
+ --- in |MiniSplitjoin.gen_hook|.
28
+ ---
29
+ --- - Works inside comments by using modified notion of indent.
30
+ --- See |MiniSplitjoin.get_indent_part()|.
31
+ ---
32
+ --- - Provides low-level Lua functions for split and join at positions.
33
+ --- See |MiniSplitjoin.split_at()| and |MiniSplitjoin.join_at()|.
34
+ ---
35
+ --- Notes:
36
+ --- - Search for arguments is done using Lua patterns (regex-like approach).
37
+ --- Certain amount of false positives is to be expected.
38
+ ---
39
+ --- - This module is mostly designed around |MiniSplitjoin.toggle()|. If target
40
+ --- split positions are on different lines, join first and then split.
41
+ ---
42
+ --- - Actions can be done on Visual mode selection, which mostly present as
43
+ --- a safety route in case of incorrect detection of initial region.
44
+ --- It uses |MiniSplitjoin.get_visual_region()| which treats selection as full
45
+ --- brackets (include brackets in selection).
46
+ ---
47
+ --- # Setup ~
48
+ ---
49
+ --- This module needs a setup with `require('mini.splitjoin').setup({})` (replace
50
+ --- `{}` with your `config` table). It will create global Lua table `MiniSplitjoin`
51
+ --- which you can use for scripting or manually (with `:lua MiniSplitjoin.*`).
52
+ ---
53
+ --- See |MiniSplitjoin.config| for available config settings.
54
+ ---
55
+ --- You can override runtime config settings (like action hooks) locally to
56
+ --- buffer inside `vim.b.minisplitjoin_config` which should have same structure
57
+ --- as `MiniSplitjoin.config`. See |mini.nvim-buffer-local-config| for more details.
58
+ ---
59
+ --- # Comparisons ~
60
+ ---
61
+ --- - [FooSoft/vim-argwrap](https://github.com/FooSoft/vim-argwrap):
62
+ --- - Mostly has the same design as this module.
63
+ --- - Doesn't work inside comments, while this module does.
64
+ --- - Has more built-in ways to control split and join, while this module
65
+ --- intentionally provides only handful.
66
+ --- - [AndrewRadev/splitjoin.vim](https://github.com/AndrewRadev/splitjoin.vim):
67
+ --- - More oriented towards language-depended transformations, while this
68
+ --- module intntionally deals with more generic text-related functionality.
69
+ --- - [Wansmer/treesj](https://github.com/Wansmer/treesj):
70
+ --- - Operates based on tree-sitter nodes. This is more accurate in
71
+ --- some edge cases, but REQUIRES tree-sitter parser.
72
+ --- - Doesn't work inside comments or strings.
73
+ ---
74
+ --- # Disabling ~
75
+ ---
76
+ --- To disable, set `g:minisplitjoin_disable` (globally) or `b:minisplitjoin_disable`
77
+ --- (for a buffer) to `v:true`. Considering high number of different scenarios
78
+ --- and customization intentions, writing exact rules for disabling module's
79
+ --- functionality is left to user. See |mini.nvim-disabling-recipes| for common
80
+ --- recipes.
81
+ ---@tag MiniSplitjoin
82
+
83
+ --- POSITION ~
84
+ --- Table with fields <line> and <col> containing line and column numbers
85
+ --- respectively. Both are 1-indexed. Example: `{ line = 2, col = 1 }`.
86
+ ---
87
+ --- REGION ~
88
+ --- Table representing region in a buffer. Fields: <from> and <to> for
89
+ --- inclusive start and end positions. Example: >lua
90
+ ---
91
+ --- { from = { line = 1, col = 1 }, to = { line = 2, col = 1 } }
92
+ --- <
93
+ ---@tag MiniSplitjoin-glossary
94
+
95
+ ---@alias __splitjoin_options table|nil Options. Has structure from |MiniSplitjoin.config|
96
+ --- inheriting its default values.
97
+ ---
98
+ --- Following extra optional fields are allowed:
99
+ --- - <position> `(table)` - position at which to find smallest bracket region.
100
+ --- See |MiniSplitjoin-glossary| for the structure.
101
+ --- Default: cursor position.
102
+ --- - <region> `(table)` - region at which to perform action. Assumes inclusive
103
+ --- both start at left bracket and end at right bracket.
104
+ --- See |MiniSplitjoin-glossary| for the structure.
105
+ --- Default: `nil` to automatically detect region.
106
+ ---@alias __splitjoin_hook_brackets - <brackets> `(table)` - array of bracket patterns indicating on which
107
+ --- brackets action should be made. Has same structure as `brackets`
108
+ --- in |MiniSplitjoin.config.detect|.
109
+ --- Default: `MiniSplitjoin.config.detect.brackets`.
110
+
111
+ ---@diagnostic disable:undefined-field
112
+ ---@diagnostic disable:discard-returns
113
+ ---@diagnostic disable:unused-local
114
+
115
+ -- Module definition ==========================================================
116
+ local MiniSplitjoin = {}
117
+ local H = {}
118
+
119
+ --- Module setup
120
+ ---
121
+ ---@param config table|nil Module config table. See |MiniSplitjoin.config|.
122
+ ---
123
+ ---@usage >lua
124
+ --- require('mini.splitjoin').setup() -- use default config
125
+ --- -- OR
126
+ --- require('mini.splitjoin').setup({}) -- replace {} with your config table
127
+ --- <
128
+ MiniSplitjoin.setup = function(config)
129
+ -- TODO: Remove after Neovim=0.9 support is dropped
130
+ if vim.fn.has('nvim-0.10') == 0 then
131
+ vim.notify(
132
+ '(mini.splitjoin) Neovim<0.10 is soft deprecated (module works but is not supported).'
133
+ .. " It will be deprecated after the next 'mini.nvim' release (module might not work)."
134
+ .. ' Please update your Neovim version.'
135
+ )
136
+ end
137
+
138
+ -- Export module
139
+ _G.MiniSplitjoin = MiniSplitjoin
140
+
141
+ -- Setup config
142
+ config = H.setup_config(config)
143
+
144
+ -- Apply config
145
+ H.apply_config(config)
146
+ end
147
+
148
+ --- Defaults ~
149
+ ---@eval return MiniDoc.afterlines_to_code(MiniDoc.current.eval_section)
150
+ ---@text # Detection ~
151
+ --- *MiniSplitjoin.config.detect*
152
+ ---
153
+ --- The table at `config.detect` controls how arguments are detected using Lua
154
+ --- patterns. General idea is to convert whole buffer into a single line,
155
+ --- perform string search, and convert results back into 2d positions.
156
+ ---
157
+ --- Example configuration: >lua
158
+ ---
159
+ --- require('mini.splitjoin').setup({
160
+ --- detect = {
161
+ --- -- Detect only inside balanced parenthesis
162
+ --- brackets = { '%b()' },
163
+ ---
164
+ --- -- Allow both `,` and `;` to separate arguments
165
+ --- separator = '[,;]',
166
+ ---
167
+ --- -- Make any separator define an argument
168
+ --- exclude_regions = {},
169
+ --- },
170
+ --- })
171
+ --- <
172
+ --- ## Outer brackets ~
173
+ ---
174
+ --- `detect.brackets` is an array of Lua patterns used to find enclosing region.
175
+ --- It is done by traversing whole buffer to find the smallest region matching
176
+ --- any supplied pattern.
177
+ ---
178
+ --- Default: `nil`, inferred as `{ '%b()', '%b[]', '%b{}' }`.
179
+ --- So an argument can be inside a balanced `()`, `[]`, or `{}`.
180
+ ---
181
+ --- Example: `brackets = { '%b()' }` will search for arguments only inside
182
+ --- balanced `()`.
183
+ ---
184
+ --- ## Separator ~
185
+ ---
186
+ --- `detect.separator` is a single Lua pattern defining which strings should be
187
+ --- treated as argument separators.
188
+ ---
189
+ --- Empty string in `detect.separator` will result in only surrounding brackets
190
+ --- used as separators.
191
+ ---
192
+ --- Only end of pattern match will be used as split/join positions.
193
+ ---
194
+ --- Default: `','`. So an argument can be separated only with comma.
195
+ ---
196
+ --- Example: `separator = { '[,;]' }` will treat both `,` and `;` as separators.
197
+ ---
198
+ --- ## Excluded regions ~
199
+ ---
200
+ --- `detect.exclude_regions` is an array of Lua patterns for sub-regions from which
201
+ --- to exclude separators. Enables correct detection in case of nested brackets
202
+ --- and quotes.
203
+ ---
204
+ --- Default: `nil`; inferred as `{ '%b()', '%b[]', '%b{}', '%b""', "%b''" }`.
205
+ --- So a separator CAN NOT be inside a balanced `()`, `[]`, `{}` (representing
206
+ --- nested argument regions) or `""`, `''` (representing strings).
207
+ ---
208
+ --- Example: `exclude_regions = {}` will not exclude any regions. So in case of
209
+ --- `f(a, { b, c })` it will detect both commas as argument separators.
210
+ ---
211
+ --- # Hooks ~
212
+ ---
213
+ --- `split.hooks_pre`, `split.hooks_post`, `join.hooks_pre`, and `join.hooks_post`
214
+ --- are arrays of hook functions. If empty (default) no hook is applied.
215
+ ---
216
+ --- Hooks should take and return array of positions. See |MiniSplitjoin-glossary|.
217
+ ---
218
+ --- They can be used to tweak actions:
219
+ ---
220
+ --- - Pre-hooks are called before action. Each is applied on the output of
221
+ --- previous one. Input of first hook are detected split/join positions.
222
+ --- Output of last one is actually used to perform split/join.
223
+ ---
224
+ --- - Post-hooks are called after action. Each is applied on the output of
225
+ --- previous one. Input of first hook are split/join positions from actual
226
+ --- action plus its region's right end as last position (for easier hook code).
227
+ --- Output of last one is used as action return value.
228
+ ---
229
+ --- For more specific details see |MiniSplitjoin.split()| and |MiniSplitjoin.join()|.
230
+ ---
231
+ --- See |MiniSplitjoin.gen_hook| for generating common hooks with examples.
232
+ MiniSplitjoin.config = {
233
+ -- Module mappings. Use `''` (empty string) to disable one.
234
+ -- Created for both Normal and Visual modes.
235
+ mappings = {
236
+ toggle = 'gS',
237
+ split = '',
238
+ join = '',
239
+ },
240
+
241
+ -- Detection options: where split/join should be done
242
+ detect = {
243
+ -- Array of Lua patterns to detect region with arguments.
244
+ -- Default: { '%b()', '%b[]', '%b{}' }
245
+ brackets = nil,
246
+
247
+ -- String Lua pattern defining argument separator
248
+ separator = ',',
249
+
250
+ -- Array of Lua patterns for sub-regions to exclude separators from.
251
+ -- Enables correct detection in presence of nested brackets and quotes.
252
+ -- Default: { '%b()', '%b[]', '%b{}', '%b""', "%b''" }
253
+ exclude_regions = nil,
254
+ },
255
+
256
+ -- Split options
257
+ split = {
258
+ hooks_pre = {},
259
+ hooks_post = {},
260
+ },
261
+
262
+ -- Join options
263
+ join = {
264
+ hooks_pre = {},
265
+ hooks_post = {},
266
+ },
267
+ }
268
+ --minidoc_afterlines_end
269
+
270
+ --- Toggle arguments
271
+ ---
272
+ --- Overview:
273
+ --- - Detect region at input position: either by using supplied `opts.region` or
274
+ --- by finding smallest bracketed region surrounding position.
275
+ --- See |MiniSplitjoin.config.detect| for more details.
276
+ --- - If region spans single line, use |MiniSplitjoin.split()| with found region.
277
+ --- Otherwise use |MiniSplitjoin.join()|.
278
+ ---
279
+ ---@param opts __splitjoin_options
280
+ ---
281
+ ---@return any Output of chosen `split()` or `join()` action.
282
+ MiniSplitjoin.toggle = function(opts)
283
+ if H.is_disabled() then return end
284
+
285
+ opts = H.get_opts(opts)
286
+
287
+ local region = opts.region or H.find_smallest_bracket_region(opts.position, opts.detect.brackets)
288
+ if region == nil then return end
289
+
290
+ opts.region = region
291
+ if region.from.line == region.to.line then
292
+ return MiniSplitjoin.split(opts)
293
+ else
294
+ return MiniSplitjoin.join(opts)
295
+ end
296
+ end
297
+
298
+ --- Split arguments
299
+ ---
300
+ --- Overview:
301
+ --- - Detect region: either by using supplied `opts.region` or by finding smallest
302
+ --- bracketed region surrounding input position (cursor position by default).
303
+ --- See |MiniSplitjoin.config.detect| for more details.
304
+ ---
305
+ --- - Find separator positions using `separator` and `exclude_regions` from `opts`.
306
+ --- Both brackets are treated as separators.
307
+ --- See |MiniSplitjoin.config.detect| for more details.
308
+ --- Note: stop if no separator positions are found.
309
+ ---
310
+ --- - Modify separator positions to represent split positions. Last split position
311
+ --- (which is inferred from right bracket) is moved one column to left so that
312
+ --- right bracket would move on new line.
313
+ ---
314
+ --- - Apply all hooks from `opts.split.hooks_pre`. Each is applied on the output of
315
+ --- previous one. Input of first hook is split positions from previous step.
316
+ --- Output of last one is used as split positions in next step.
317
+ ---
318
+ --- - Split and update split positions with |MiniSplitjoin.split_at()|.
319
+ ---
320
+ --- - Apply all hooks from `opts.split.hooks_post`. Each is applied on the output of
321
+ --- previous one. Input of first hook is split positions from previous step plus
322
+ --- region's right end (for easier hook code).
323
+ --- Output of last one is used as function return value.
324
+ ---
325
+ --- Note:
326
+ --- - By design, it doesn't detect if argument SHOULD be split, so application
327
+ --- on arguments spanning multiple lines can lead to undesirable result.
328
+ ---
329
+ ---@param opts __splitjoin_options
330
+ ---
331
+ ---@return any Output of last `opts.split.hooks_post` or `nil` if no split positions
332
+ --- found. Default: return value of |MiniSplitjoin.split_at()| application.
333
+ MiniSplitjoin.split = function(opts)
334
+ if H.is_disabled() then return end
335
+
336
+ opts = H.get_opts(opts)
337
+
338
+ local region = opts.region or H.find_smallest_bracket_region(opts.position, opts.detect.brackets)
339
+ if region == nil then return nil end
340
+
341
+ local positions = H.find_split_positions(region, opts.detect.separator, opts.detect.exclude_regions)
342
+ if #positions == 0 then return nil end
343
+
344
+ -- Call pre-hooks
345
+ for _, hook in ipairs(opts.split.hooks_pre) do
346
+ positions = hook(positions)
347
+ end
348
+
349
+ -- Split at positions
350
+ local split_positions = MiniSplitjoin.split_at(positions)
351
+
352
+ -- Call post-hooks to tweak splits. Add right bracket for easier hook code.
353
+ local last = split_positions[#split_positions]
354
+ local last_next_line = vim.fn.getline(last.line + 1)
355
+ local new_col = MiniSplitjoin.get_indent_part(last_next_line):len() + 1
356
+ table.insert(split_positions, { line = last.line + 1, col = new_col })
357
+
358
+ for _, hook in ipairs(opts.split.hooks_post) do
359
+ split_positions = hook(split_positions)
360
+ end
361
+
362
+ return split_positions
363
+ end
364
+
365
+ --- Join arguments
366
+ ---
367
+ --- Overview:
368
+ --- - Detect region: either by using supplied `opts.region` or by finding smallest
369
+ --- bracketed region surrounding input position (cursor position by default).
370
+ --- See |MiniSplitjoin.config.detect| for more details.
371
+ ---
372
+ --- - Compute join positions to be line ends of all but last region lines.
373
+ --- Note: stop if no join positions are found.
374
+ ---
375
+ --- - Apply all hooks from `opts.join.hooks_pre`. Each is applied on the output
376
+ --- of previous one. Input of first hook is join positions from previous step.
377
+ --- Output of last one is used as join positions in next step.
378
+ ---
379
+ --- - Join and update join positions with |MiniSplitjoin.join_at()|.
380
+ ---
381
+ --- - Apply all hooks from `opts.join.hooks_post`. Each is applied on the output
382
+ --- of previous one. Input of first hook is join positions from previous step
383
+ --- plus region's right end for easier hook code.
384
+ --- Output of last one is used as function return value.
385
+ ---
386
+ ---@param opts __splitjoin_options
387
+ ---
388
+ ---@return any Output of last `opts.split.hooks_post` or `nil` of no join positions
389
+ --- found. Default: return value of |MiniSplitjoin.join_at()| application.
390
+ MiniSplitjoin.join = function(opts)
391
+ if H.is_disabled() then return end
392
+
393
+ opts = H.get_opts(opts)
394
+
395
+ local region = opts.region or H.find_smallest_bracket_region(opts.position, opts.detect.brackets)
396
+ if region == nil then return nil end
397
+
398
+ local positions = H.find_join_positions(region)
399
+ if #positions == 0 then return nil end
400
+
401
+ -- Call pre-hooks
402
+ for _, hook in ipairs(opts.join.hooks_pre) do
403
+ positions = hook(positions)
404
+ end
405
+
406
+ -- Join at positions
407
+ local join_positions = MiniSplitjoin.join_at(positions)
408
+
409
+ -- Call post-hooks to tweak joins. Add right bracket for easier hook code.
410
+ local last = join_positions[#join_positions]
411
+ table.insert(join_positions, { line = last.line, col = last.col + 1 })
412
+
413
+ for _, hook in ipairs(opts.join.hooks_post) do
414
+ join_positions = hook(join_positions)
415
+ end
416
+
417
+ return join_positions
418
+ end
419
+
420
+ --- Generate common hooks
421
+ ---
422
+ --- This is a table with function elements. Call to actually get hook.
423
+ ---
424
+ --- All generated post-hooks return updated versions of their input reflecting
425
+ --- changes done inside hook.
426
+ ---
427
+ --- Example for `lua` filetype (place it in `lua.lua` filetype plugin, |ftplugin|): >lua
428
+ ---
429
+ --- local gen_hook = MiniSplitjoin.gen_hook
430
+ --- local curly = { brackets = { '%b{}' } }
431
+ ---
432
+ --- -- Add trailing comma when splitting inside curly brackets
433
+ --- local add_comma_curly = gen_hook.add_trailing_separator(curly)
434
+ ---
435
+ --- -- Delete trailing comma when joining inside curly brackets
436
+ --- local del_comma_curly = gen_hook.del_trailing_separator(curly)
437
+ ---
438
+ --- -- Pad curly brackets with single space after join
439
+ --- local pad_curly = gen_hook.pad_brackets(curly)
440
+ ---
441
+ --- -- Create buffer-local config
442
+ --- vim.b.minisplitjoin_config = {
443
+ --- split = { hooks_post = { add_comma_curly } },
444
+ --- join = { hooks_post = { del_comma_curly, pad_curly } },
445
+ --- }
446
+ --- <
447
+ MiniSplitjoin.gen_hook = {}
448
+
449
+ --- Generate hook to pad brackets
450
+ ---
451
+ --- This is a join post-hook. Use in `join.hooks_post` of |MiniSplitjoin.config|.
452
+ ---
453
+ ---@param opts table|nil Options. Possible fields:
454
+ --- - <pad> `(string)` - pad to add after first and before last join positions.
455
+ --- Default: `' '` (single space).
456
+ --- __splitjoin_hook_brackets
457
+ ---
458
+ ---@return function A hook which adds inner pad to first and last join positions and
459
+ --- returns updated input join positions.
460
+ MiniSplitjoin.gen_hook.pad_brackets = function(opts)
461
+ opts = opts or {}
462
+ local pad = opts.pad or ' '
463
+ local brackets = opts.brackets or H.get_opts(opts).detect.brackets
464
+ local n_pad = pad:len()
465
+
466
+ return function(join_positions)
467
+ -- Act only on actual join
468
+ local n_pos = #join_positions
469
+ if n_pos == 0 or pad == '' then return join_positions end
470
+
471
+ -- Act only if brackets are matched. First join position should be exactly
472
+ -- on left bracket, last - just before right bracket.
473
+ local first, last = join_positions[1], join_positions[n_pos]
474
+ local brackets_matched = H.is_positions_inside_brackets(first, last, brackets)
475
+ if not brackets_matched then return join_positions end
476
+
477
+ -- Pad only in case of non-trivial join
478
+ if first.line == last.line and (last.col - first.col) <= 1 then return join_positions end
479
+
480
+ -- Add pad after left and before right edges
481
+ H.set_text(first.line - 1, last.col - 1, first.line - 1, last.col - 1, { pad })
482
+ H.set_text(first.line - 1, first.col, first.line - 1, first.col, { pad })
483
+
484
+ -- Update `join_positions` to reflect text change
485
+ -- - Account for left pad
486
+ for i = 2, n_pos do
487
+ join_positions[i].col = join_positions[i].col + n_pad
488
+ end
489
+ -- - Account for right pad
490
+ join_positions[n_pos].col = join_positions[n_pos].col + n_pad
491
+
492
+ return join_positions
493
+ end
494
+ end
495
+
496
+ --- Generate hook to add trailing separator
497
+ ---
498
+ --- This is a split post-hook. Use in `split.hooks_post` of |MiniSplitjoin.config|.
499
+ ---
500
+ ---@param opts table|nil Options. Possible fields:
501
+ --- - <sep> `(string)` - separator to add before last split position.
502
+ --- Default: `','`.
503
+ --- __splitjoin_hook_brackets
504
+ ---
505
+ ---@return function A hook which adds separator before last split position and
506
+ --- returns updated input split positions.
507
+ MiniSplitjoin.gen_hook.add_trailing_separator = function(opts)
508
+ opts = opts or {}
509
+ local sep = opts.sep or ','
510
+ local brackets = opts.brackets or H.get_opts(opts).detect.brackets
511
+
512
+ return function(split_positions)
513
+ -- Add only in case there is at least one argument
514
+ local n_pos = #split_positions
515
+ if n_pos < 3 then return split_positions end
516
+
517
+ -- Act only if brackets are matched
518
+ local first, last = split_positions[1], split_positions[n_pos]
519
+ local brackets_matched = H.is_positions_inside_brackets(first, last, brackets)
520
+ if not brackets_matched then return split_positions end
521
+
522
+ -- Act only if there is no trailing separator already
523
+ local target_line = vim.fn.getline(last.line - 1)
524
+ local target_col = target_line:find(vim.pesc(sep) .. '$')
525
+ if target_col ~= nil then return split_positions end
526
+
527
+ -- Add trailing separator
528
+ local col = target_line:len()
529
+ H.set_text(last.line - 2, col, last.line - 2, col, { sep })
530
+
531
+ -- Don't update `split_positions`, as appending to line has no effect
532
+ return split_positions
533
+ end
534
+ end
535
+
536
+ --- Generate hook to delete trailing separator
537
+ ---
538
+ --- This is a join post-hook. Use in `join.hooks_post` of |MiniSplitjoin.config|.
539
+ ---
540
+ ---@param opts table|nil Options. Possible fields:
541
+ --- - <sep> `(string)` - separator to remove before last join position.
542
+ --- Default: `','`.
543
+ --- __splitjoin_hook_brackets
544
+ ---
545
+ ---@return function A hook which adds separator before last split position and
546
+ --- returns updated input split positions.
547
+ MiniSplitjoin.gen_hook.del_trailing_separator = function(opts)
548
+ opts = opts or {}
549
+ local sep = opts.sep or ','
550
+ local brackets = opts.brackets or H.get_opts(opts).detect.brackets
551
+ local n_sep = sep:len()
552
+
553
+ return function(join_positions)
554
+ -- Act only on actual join
555
+ local n_pos = #join_positions
556
+ if n_pos == 0 then return join_positions end
557
+
558
+ -- Act only if brackets are matched
559
+ local first, last = join_positions[1], join_positions[n_pos]
560
+ local brackets_matched = H.is_positions_inside_brackets(first, last, brackets)
561
+ if not brackets_matched then return join_positions end
562
+
563
+ -- Act only if there is matched trailing separator
564
+ local target_line = vim.fn.getline(last.line):sub(1, last.col - 1)
565
+ local target_col = target_line:find(vim.pesc(sep) .. '%s*$')
566
+ if target_col == nil then return join_positions end
567
+
568
+ -- Remove trailing separator
569
+ H.set_text(last.line - 1, target_col - 1, last.line - 1, target_col - 1 + n_sep, {})
570
+
571
+ -- Update `join_positions` to reflect text change. Update last as it moved.
572
+ -- Do not update second to last because it didn't affect what was tracked.
573
+ join_positions[n_pos] = { line = last.line, col = last.col - n_sep }
574
+
575
+ return join_positions
576
+ end
577
+ end
578
+
579
+ --- Split at positions
580
+ ---
581
+ --- Overview:
582
+ --- - For each position move all characters after it to next line and make it have
583
+ --- same indent as current one (see |MiniSplitjoin.get_indent_part()|).
584
+ --- Also remove trailing whitespace at position line.
585
+ ---
586
+ --- - Increase indent of inner lines by a single pad: tab in case of |'noexpandtab'|
587
+ --- or |shiftwidth()| number of spaces otherwise.
588
+ ---
589
+ --- Notes:
590
+ --- - Cursor is adjusted to follow text updates.
591
+ --- - Use output of this function to keep track of input positions.
592
+ ---
593
+ ---@param positions table Array of positions at which to perform split.
594
+ --- See |MiniSplitjoin-glossary| for their structure. Note: they don't have
595
+ --- to be ordered, but first and last ones will be used to infer lines for
596
+ --- which indent will be increased.
597
+ ---
598
+ ---@return table Array of new positions to where input `positions` were moved.
599
+ MiniSplitjoin.split_at = function(positions)
600
+ local n_pos = #positions
601
+ if n_pos == 0 then return {} end
602
+
603
+ -- Cache values that might change
604
+ local cursor_extmark = H.put_extmark_at_positions({ H.get_cursor_pos() })[1]
605
+ local input_extmarks = H.put_extmark_at_positions(positions)
606
+
607
+ -- Split at extmark positions
608
+ for i = 1, n_pos do
609
+ H.split_at_extmark(input_extmarks[i])
610
+ end
611
+
612
+ -- Increase indent of inner lines
613
+ local first_new_pos = H.get_extmark_pos(input_extmarks[1])
614
+ local last_new_pos = H.get_extmark_pos(input_extmarks[n_pos])
615
+ H.increase_indent(first_new_pos.line + 1, last_new_pos.line)
616
+
617
+ -- Put cursor back on tracked position
618
+ H.put_cursor_at_extmark(cursor_extmark)
619
+
620
+ -- Reconstruct input positions
621
+ local res = vim.tbl_map(H.get_extmark_pos, input_extmarks)
622
+ vim.api.nvim_buf_clear_namespace(0, H.ns_id, 0, -1)
623
+ return res
624
+ end
625
+
626
+ --- Join at positions
627
+ ---
628
+ --- Overview:
629
+ --- - For each position join its line with the next line. Joining is done by
630
+ --- replacing trailing whitespace of the line and indent of its next line
631
+ --- (see |MiniSplitjoin.get_indent_part()|) with a pad string (single space except
632
+ --- empty string for first and last positions). To adjust this, use hooks
633
+ --- (for example, see |MiniSplitjoin.gen_hook.pad_brackets()|).
634
+ ---
635
+ --- Notes:
636
+ --- - Cursor is adjusted to follow text updates.
637
+ --- - Use output of this function to keep track of input positions.
638
+ ---
639
+ ---@param positions table Array of positions at which to perform join.
640
+ --- See |MiniSplitjoin-glossary| for their structure. Note: they don't have
641
+ --- to be ordered, but first and last ones will have different pad string.
642
+ ---
643
+ ---@return table Array of new positions to where input `positions` were moved.
644
+ MiniSplitjoin.join_at = function(positions)
645
+ local n_pos = #positions
646
+ if n_pos == 0 then return {} end
647
+
648
+ -- Cache values that might change
649
+ local cursor_extmark = H.put_extmark_at_positions({ H.get_cursor_pos() })[1]
650
+ local input_extmarks = H.put_extmark_at_positions(positions)
651
+
652
+ -- Join at positions which are changing following extmarks
653
+ for i = 1, n_pos do
654
+ local cur_pad_string = (i == 1 or i == n_pos) and '' or ' '
655
+ H.join_at_extmark(input_extmarks[i], cur_pad_string)
656
+ end
657
+
658
+ -- Put cursor back on tracked position
659
+ H.put_cursor_at_extmark(cursor_extmark)
660
+
661
+ -- Reconstruct input positions
662
+ local res = vim.tbl_map(H.get_extmark_pos, input_extmarks)
663
+ vim.api.nvim_buf_clear_namespace(0, H.ns_id, 0, -1)
664
+ return res
665
+ end
666
+
667
+ --- Get previous visual region
668
+ ---
669
+ --- Get previous visual selection using |'<| and |'>| marks in the format of
670
+ --- region (see |MiniSplitjoin-glossary|). Used in Visual mode mappings.
671
+ ---
672
+ --- Note:
673
+ --- - Both marks are included in region.
674
+ --- - In linewise mode start is at column 1 and end is at line's last character.
675
+ ---
676
+ ---@return table A region. See |MiniSplitjoin-glossary| for exact structure.
677
+ MiniSplitjoin.get_visual_region = function()
678
+ local from_pos, to_pos = vim.fn.getpos("'<"), vim.fn.getpos("'>")
679
+ local from, to = { line = from_pos[2], col = from_pos[3] }, { line = to_pos[2], col = to_pos[3] }
680
+ -- Tweak for linewise Visual selection
681
+ if vim.fn.visualmode() == 'V' then
682
+ from.col, to.col = 1, vim.fn.col({ to.line, '$' }) - 1
683
+ end
684
+
685
+ return { from = from, to = to }
686
+ end
687
+
688
+ --- Get string's indent part
689
+ ---
690
+ ---@param line string String for which to compute indent.
691
+ ---@param respect_comments boolean|nil Whether to respect comments as indent part.
692
+ --- Default: `true`.
693
+ ---
694
+ ---@return string Part of input representing line's indent. Can be empty string.
695
+ --- Use `string.len()` to compute indent in bytes.
696
+ MiniSplitjoin.get_indent_part = function(line, respect_comments)
697
+ if respect_comments == nil then respect_comments = true end
698
+ if not respect_comments then return line:match('^%s*') end
699
+
700
+ -- Make it respect various comment leaders
701
+ local comment_indent = H.get_comment_indent(line, H.get_comment_leaders())
702
+ if comment_indent ~= '' then return comment_indent end
703
+
704
+ return line:match('^%s*')
705
+ end
706
+
707
+ --- Operator for Normal mode mappings
708
+ ---
709
+ --- Main function to be used in expression mappings. No need to use it
710
+ --- directly, everything is setup in |MiniSplitjoin.setup()|.
711
+ ---
712
+ ---@param task string Name of task.
713
+ MiniSplitjoin.operator = function(task)
714
+ local is_init_call = task == 'toggle' or task == 'split' or task == 'join'
715
+ if not is_init_call then
716
+ MiniSplitjoin[H.cache.operator_task]()
717
+ return ''
718
+ end
719
+
720
+ if H.is_disabled() then
721
+ -- Using `<Esc>` prevents moving cursor caused by current implementation
722
+ -- detail of adding `' '` inside expression mapping
723
+ return [[\<Esc>]]
724
+ end
725
+
726
+ H.cache.operator_task = task
727
+ vim.o.operatorfunc = 'v:lua.MiniSplitjoin.operator'
728
+ return 'g@'
729
+ end
730
+
731
+ -- Helper data ================================================================
732
+ -- Module default config
733
+ H.default_config = vim.deepcopy(MiniSplitjoin.config)
734
+
735
+ H.ns_id = vim.api.nvim_create_namespace('MiniSplitjoin')
736
+
737
+ H.cache = { operator_task = nil }
738
+
739
+ -- Helper functionality =======================================================
740
+ -- Settings -------------------------------------------------------------------
741
+ H.setup_config = function(config)
742
+ H.check_type('config', config, 'table', true)
743
+ config = vim.tbl_deep_extend('force', vim.deepcopy(H.default_config), config or {})
744
+
745
+ H.check_type('mappings', config.mappings, 'table')
746
+ H.check_type('mappings.toggle', config.mappings.toggle, 'string', true)
747
+ H.check_type('mappings.split', config.mappings.split, 'string')
748
+ H.check_type('mappings.join', config.mappings.join, 'string', true)
749
+
750
+ H.check_type('detect', config.detect, 'table')
751
+ H.check_type('detect.brackets', config.detect.brackets, 'table', true)
752
+ H.check_type('detect.separator', config.detect.separator, 'string')
753
+ H.check_type('detect.exclude_regions', config.detect.exclude_regions, 'table', true)
754
+
755
+ H.check_type('split', config.split, 'table')
756
+ H.check_type('split.hooks_pre', config.split.hooks_pre, 'table')
757
+ H.check_type('split.hooks_post', config.split.hooks_post, 'table')
758
+
759
+ H.check_type('join', config.join, 'table')
760
+ H.check_type('join.hooks_pre', config.join.hooks_pre, 'table')
761
+ H.check_type('join.hooks_post', config.join.hooks_post, 'table')
762
+
763
+ return config
764
+ end
765
+
766
+ --stylua: ignore
767
+ H.apply_config = function(config)
768
+ MiniSplitjoin.config = config
769
+
770
+ -- Make mappings
771
+ local maps = config.mappings
772
+
773
+ H.map('n', maps.toggle, 'v:lua.MiniSplitjoin.operator("toggle") . " "', { expr = true, desc = 'Toggle arguments' })
774
+ H.map('n', maps.split, 'v:lua.MiniSplitjoin.operator("split") . " "', { expr = true, desc = 'Split arguments' })
775
+ H.map('n', maps.join, 'v:lua.MiniSplitjoin.operator("join") . " "', { expr = true, desc = 'Join arguments' })
776
+
777
+ H.map('x', maps.toggle, ':<C-u>lua MiniSplitjoin.toggle({ region = MiniSplitjoin.get_visual_region() })<CR>', { desc = 'Toggle arguments' })
778
+ H.map('x', maps.split, ':<C-u>lua MiniSplitjoin.split({ region = MiniSplitjoin.get_visual_region() })<CR>', { desc = 'Split arguments' })
779
+ H.map('x', maps.join, ':<C-u>lua MiniSplitjoin.join({ region = MiniSplitjoin.get_visual_region() })<CR>', { desc = 'Join arguments' })
780
+ end
781
+
782
+ H.is_disabled = function() return vim.g.minisplitjoin_disable == true or vim.b.minisplitjoin_disable == true end
783
+
784
+ H.get_config = function(config)
785
+ return vim.tbl_deep_extend('force', MiniSplitjoin.config, vim.b.minisplitjoin_config or {}, config or {})
786
+ end
787
+
788
+ H.get_opts = function(opts)
789
+ opts = opts or {}
790
+
791
+ -- Infer detect options. Can't use usual `vim.tbl_deep_extend()` because it
792
+ -- doesn't work properly on arrays
793
+ local default_detect = {
794
+ brackets = { '%b()', '%b[]', '%b{}' },
795
+ separator = ',',
796
+ exclude_regions = { '%b()', '%b[]', '%b{}', '%b""', "%b''" },
797
+ }
798
+ local config = H.get_config()
799
+
800
+ return {
801
+ position = opts.position or H.get_cursor_pos(),
802
+ region = opts.region,
803
+ -- Extend `detect` not deeply to avoid unwanted values from longer defaults
804
+ detect = vim.tbl_extend('force', default_detect, config.detect, opts.detect or {}),
805
+ split = vim.tbl_deep_extend('force', config.split, opts.split or {}),
806
+ join = vim.tbl_deep_extend('force', config.join, opts.join or {}),
807
+ }
808
+ end
809
+
810
+ -- Split ----------------------------------------------------------------------
811
+ H.split_at_extmark = function(extmark_id)
812
+ local pos = H.get_extmark_pos(extmark_id)
813
+
814
+ -- Split
815
+ H.set_text(pos.line - 1, pos.col, pos.line - 1, pos.col, { '', '' })
816
+
817
+ -- Remove trailing whitespace on split line
818
+ local split_line = vim.fn.getline(pos.line)
819
+ local start_of_trailspace = split_line:find('%s*$')
820
+ H.set_text(pos.line - 1, start_of_trailspace - 1, pos.line - 1, split_line:len(), {})
821
+
822
+ -- Adjust indent on new line
823
+ local cur_indent = MiniSplitjoin.get_indent_part(vim.fn.getline(pos.line + 1))
824
+ local new_indent = MiniSplitjoin.get_indent_part(split_line)
825
+ H.set_text(pos.line, 0, pos.line, cur_indent:len(), { new_indent })
826
+ end
827
+
828
+ H.find_split_positions = function(region, separator, exclude_regions)
829
+ local sep_positions = H.find_separator_positions(region, separator, exclude_regions)
830
+ local n_pos = #sep_positions
831
+
832
+ sep_positions[n_pos].col = sep_positions[n_pos].col - 1
833
+ return sep_positions
834
+ end
835
+
836
+ -- Join -----------------------------------------------------------------------
837
+ H.join_at_extmark = function(extmark_id, pad)
838
+ local line_num = H.get_extmark_pos(extmark_id).line
839
+ if vim.api.nvim_buf_line_count(0) <= line_num then return end
840
+
841
+ -- Join by replacing trailing whitespace of current line and indent of next
842
+ -- one with `pad`
843
+ local lines = vim.api.nvim_buf_get_lines(0, line_num - 1, line_num + 1, true)
844
+ local above_start_col = lines[1]:len() - lines[1]:match('%s*$'):len()
845
+ local below_end_col = MiniSplitjoin.get_indent_part(lines[2]):len()
846
+
847
+ H.set_text(line_num - 1, above_start_col, line_num, below_end_col, { pad })
848
+ end
849
+
850
+ H.find_join_positions = function(region, separator, exclude_regions)
851
+ local lines = vim.api.nvim_buf_get_lines(0, region.from.line - 1, region.to.line, true)
852
+
853
+ -- Join whole region into single line
854
+ local res = {}
855
+ local init_line = region.from.line - 1
856
+ for i = 1, #lines - 1 do
857
+ table.insert(res, { line = init_line + i, col = lines[i]:len() })
858
+ end
859
+ return res
860
+ end
861
+
862
+ -- Detect ---------------------------------------------------------------------
863
+ H.find_smallest_bracket_region = function(position, brackets)
864
+ local neigh = H.get_neighborhood()
865
+ local cur_offset = neigh.pos_to_offset(position)
866
+
867
+ local best_span = H.find_smallest_covering(neigh['1d'], cur_offset, brackets)
868
+ if best_span == nil then return nil end
869
+
870
+ return neigh.span_to_region(best_span)
871
+ end
872
+
873
+ H.find_smallest_covering = function(line, ref_offset, patterns)
874
+ local res, min_width = nil, math.huge
875
+ for _, pattern in ipairs(patterns) do
876
+ local cur_init = 0
877
+ local left, right = string.find(line, pattern, cur_init)
878
+ while left do
879
+ if left <= ref_offset and ref_offset <= right and (right - left) < min_width then
880
+ res, min_width = { from = left, to = right }, right - left
881
+ end
882
+
883
+ cur_init = left + 1
884
+ left, right = string.find(line, pattern, cur_init)
885
+ end
886
+ end
887
+
888
+ return res
889
+ end
890
+
891
+ H.find_separator_positions = function(region, separator, exclude_regions)
892
+ if separator == '' then return { region.from, region.to } end
893
+
894
+ local neigh = H.get_neighborhood()
895
+ local region_span = neigh.region_to_span(region)
896
+ local region_s = neigh['1d']:sub(region_span.from, region_span.to)
897
+
898
+ -- Match separator endings
899
+ local seps = {}
900
+ region_s:gsub(separator .. '()', function(r) table.insert(seps, r - 1) end)
901
+
902
+ -- Remove separators that are in excluded regions.
903
+ local inner_string, forbidden = region_s:sub(2, -2), {}
904
+ local add_to_forbidden = function(l, r) table.insert(forbidden, { from = l + 1, to = r }) end
905
+
906
+ for _, pat in ipairs(exclude_regions) do
907
+ inner_string:gsub('()' .. pat .. '()', add_to_forbidden)
908
+ end
909
+
910
+ -- - Also exclude trailing separator
911
+ inner_string:gsub('()' .. separator .. '%s*()$', add_to_forbidden)
912
+
913
+ local sub_offsets = vim.tbl_filter(function(x) return not H.is_offset_inside_spans(x, forbidden) end, seps)
914
+
915
+ -- Treat enclosing brackets as separators
916
+ if region_s:len() > 2 then
917
+ -- Use only last bracket in case of empty brackets
918
+ table.insert(sub_offsets, 1, 1)
919
+ end
920
+ table.insert(sub_offsets, region_s:len())
921
+
922
+ -- Convert offsets to positions
923
+ local start_offset = region_span.from
924
+ return vim.tbl_map(function(sub_off) return neigh.offset_to_pos(start_offset + sub_off - 1) end, sub_offsets)
925
+ end
926
+
927
+ H.is_offset_inside_spans = function(ref_point, spans)
928
+ for _, span in ipairs(spans) do
929
+ if span.from <= ref_point and ref_point <= span.to then return true end
930
+ end
931
+ return false
932
+ end
933
+
934
+ H.is_positions_inside_brackets = function(from_pos, to_pos, brackets)
935
+ local text_lines = vim.api.nvim_buf_get_text(0, from_pos.line - 1, from_pos.col - 1, to_pos.line - 1, to_pos.col, {})
936
+ local text = table.concat(text_lines, '\n')
937
+
938
+ for _, b in ipairs(brackets) do
939
+ if text:find('^' .. b .. '$') ~= nil then return true end
940
+ end
941
+ return false
942
+ end
943
+
944
+ H.is_char_at_position = function(position, char)
945
+ local present_char = vim.fn.getline(position.line):sub(position.col, position.col)
946
+ return present_char == char
947
+ end
948
+
949
+ -- Simplified version of "neighborhood" from 'mini.ai':
950
+ -- - Use whol buffer.
951
+ -- - No empty regions or spans.
952
+ --
953
+ -- NOTEs:
954
+ -- - `region = { from = { line = a, col = b }, to = { line = c, col = d } }`.
955
+ -- End-inclusive charwise selection. All `a`, `b`, `c`, `d` are 1-indexed.
956
+ -- - `offset` is the number between 1 to `neigh1d:len()`.
957
+ H.get_neighborhood = function()
958
+ local neigh2d = vim.api.nvim_buf_get_lines(0, 0, -1, false)
959
+ -- Append 'newline' character to distinguish between lines in 1d case
960
+ -- (crucial for handling empty lines)
961
+ for k, v in pairs(neigh2d) do
962
+ neigh2d[k] = v .. '\n'
963
+ end
964
+ local neigh1d = table.concat(neigh2d, '')
965
+ local n_lines = #neigh2d
966
+
967
+ -- Compute offsets for just before line starts
968
+ local line_offsets = {}
969
+ local cur_offset = 0
970
+ for i = 1, n_lines do
971
+ line_offsets[i] = cur_offset
972
+ cur_offset = cur_offset + neigh2d[i]:len()
973
+ end
974
+
975
+ -- Convert 2d buffer position to 1d offset
976
+ local pos_to_offset = function(pos) return line_offsets[pos.line] + pos.col end
977
+
978
+ -- Convert 1d offset to 2d buffer position
979
+ local offset_to_pos = function(offset)
980
+ for i = 1, n_lines - 1 do
981
+ if line_offsets[i] < offset and offset <= line_offsets[i + 1] then
982
+ return { line = i, col = offset - line_offsets[i] }
983
+ end
984
+ end
985
+
986
+ return { line = n_lines, col = offset - line_offsets[n_lines] }
987
+ end
988
+
989
+ -- Convert 2d region to 1d span
990
+ local region_to_span = function(region) return { from = pos_to_offset(region.from), to = pos_to_offset(region.to) } end
991
+
992
+ -- Convert 1d span to 2d region
993
+ local span_to_region = function(span) return { from = offset_to_pos(span.from), to = offset_to_pos(span.to) } end
994
+
995
+ return {
996
+ ['1d'] = neigh1d,
997
+ ['2d'] = neigh2d,
998
+ pos_to_offset = pos_to_offset,
999
+ offset_to_pos = offset_to_pos,
1000
+ region_to_span = region_to_span,
1001
+ span_to_region = span_to_region,
1002
+ }
1003
+ end
1004
+
1005
+ -- Extmarks -------------------------------------------------------------------
1006
+ H.put_extmark_at_positions = function(positions)
1007
+ return vim.tbl_map(
1008
+ function(pos) return vim.api.nvim_buf_set_extmark(0, H.ns_id, pos.line - 1, pos.col - 1, {}) end,
1009
+ positions
1010
+ )
1011
+ end
1012
+
1013
+ H.get_extmark_pos = function(extmark_id)
1014
+ local res = vim.api.nvim_buf_get_extmark_by_id(0, H.ns_id, extmark_id, {})
1015
+ return { line = res[1] + 1, col = res[2] + 1 }
1016
+ end
1017
+
1018
+ H.get_cursor_pos = function()
1019
+ local cur_pos = vim.api.nvim_win_get_cursor(0)
1020
+ return { line = cur_pos[1], col = cur_pos[2] + 1 }
1021
+ end
1022
+
1023
+ H.put_cursor_at_extmark = function(id)
1024
+ local new_pos = vim.api.nvim_buf_get_extmark_by_id(0, H.ns_id, id, {})
1025
+ vim.api.nvim_win_set_cursor(0, { new_pos[1] + 1, new_pos[2] })
1026
+ vim.api.nvim_buf_del_extmark(0, H.ns_id, id)
1027
+ end
1028
+
1029
+ -- Indent ---------------------------------------------------------------------
1030
+ H.increase_indent = function(from_line, to_line)
1031
+ local lines = vim.api.nvim_buf_get_lines(0, from_line - 1, to_line, true)
1032
+
1033
+ -- Respect comment leaders only if all lines are commented
1034
+ local comment_leaders = H.get_comment_leaders()
1035
+ local respect_comments = H.is_comment_block(lines, comment_leaders)
1036
+
1037
+ -- Increase indent of all lines (end-inclusive)
1038
+ local pad = vim.bo.expandtab and string.rep(' ', vim.fn.shiftwidth()) or '\t'
1039
+ for i, l in ipairs(lines) do
1040
+ local n_indent = MiniSplitjoin.get_indent_part(l, respect_comments):len()
1041
+
1042
+ -- Don't increase indent of blank lines (possibly respecting comments)
1043
+ local cur_by_string = l:len() == n_indent and '' or pad
1044
+
1045
+ local line_num = from_line + i - 1
1046
+ H.set_text(line_num - 1, n_indent, line_num - 1, n_indent, { cur_by_string })
1047
+ end
1048
+ end
1049
+
1050
+ H.get_comment_indent = function(line, comment_leaders)
1051
+ local res = ''
1052
+
1053
+ for _, leader in ipairs(comment_leaders) do
1054
+ local cur_match = line:match('^%s*' .. vim.pesc(leader) .. '%s*')
1055
+ -- Use biggest match in case of several matches. Allows respecting "nested"
1056
+ -- comment leaders like "---" and "--".
1057
+ if type(cur_match) == 'string' and res:len() < cur_match:len() then res = cur_match end
1058
+ end
1059
+
1060
+ return res
1061
+ end
1062
+
1063
+ -- Comments -------------------------------------------------------------------
1064
+ H.get_comment_leaders = function()
1065
+ local res = {}
1066
+
1067
+ -- From 'commentstring'
1068
+ local main_leader = vim.split(vim.bo.commentstring, '%%s')[1]
1069
+ -- - Ensure there is no whitespace before or after
1070
+ table.insert(res, vim.trim(main_leader))
1071
+
1072
+ -- From 'comments'
1073
+ for _, comment_part in ipairs(vim.opt_local.comments:get()) do
1074
+ local prefix, suffix = comment_part:match('^(.*):(.*)$')
1075
+
1076
+ -- Control whitespace around suffix
1077
+ suffix = vim.trim(suffix)
1078
+
1079
+ if prefix:find('b') then
1080
+ -- Respect `b` flag (for blank) requiring space, tab or EOL after it
1081
+ table.insert(res, suffix .. ' ')
1082
+ table.insert(res, suffix .. '\t')
1083
+ elseif prefix:find('f') == nil then
1084
+ -- Add otherwise ignoring `f` flag (only first line should have it)
1085
+ table.insert(res, suffix)
1086
+ end
1087
+ end
1088
+
1089
+ return res
1090
+ end
1091
+
1092
+ H.is_comment_block = function(lines, comment_leaders)
1093
+ for _, l in ipairs(lines) do
1094
+ if not H.is_commented(l, comment_leaders) then return false end
1095
+ end
1096
+ return true
1097
+ end
1098
+
1099
+ H.is_commented = function(line, comment_leaders)
1100
+ for _, leader in ipairs(comment_leaders) do
1101
+ if line:find('^%s*' .. vim.pesc(leader) .. '%s*') ~= nil then return true end
1102
+ end
1103
+ return false
1104
+ end
1105
+
1106
+ -- Utilities ------------------------------------------------------------------
1107
+ H.error = function(msg) error('(mini.splitjoin) ' .. msg, 0) end
1108
+
1109
+ H.check_type = function(name, val, ref, allow_nil)
1110
+ if type(val) == ref or (ref == 'callable' and vim.is_callable(val)) or (allow_nil and val == nil) then return end
1111
+ H.error(string.format('`%s` should be %s, not %s', name, ref, type(val)))
1112
+ end
1113
+
1114
+ H.map = function(mode, lhs, rhs, opts)
1115
+ if lhs == '' then return end
1116
+ opts = vim.tbl_deep_extend('force', { silent = true }, opts or {})
1117
+ vim.keymap.set(mode, lhs, rhs, opts)
1118
+ end
1119
+
1120
+ H.set_text = function(start_row, start_col, end_row, end_col, replacement)
1121
+ local ok = pcall(vim.api.nvim_buf_set_text, 0, start_row, start_col, end_row, end_col, replacement)
1122
+ if not ok or #replacement == 0 then return end
1123
+
1124
+ -- Fix cursor position if it was exactly on start position.
1125
+ -- See https://github.com/neovim/neovim/issues/22526.
1126
+ local cursor = vim.api.nvim_win_get_cursor(0)
1127
+ if (start_row + 1) == cursor[1] and start_col == cursor[2] then
1128
+ vim.api.nvim_win_set_cursor(0, { cursor[1], cursor[2] + replacement[1]:len() })
1129
+ end
1130
+ end
1131
+
1132
+ return MiniSplitjoin