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,1158 @@
1
+ --- *mini.indentscope* Visualize and work with indent scope
2
+ ---
3
+ --- MIT License Copyright (c) 2022 Evgeni Chasnovski
4
+
5
+ --- Indent scope (or just "scope") is a maximum set of consecutive lines which
6
+ --- contains certain reference line (cursor line by default) and every member
7
+ --- has indent not less than certain reference indent ("indent at cursor" by
8
+ --- default: minimum between cursor column and indent of cursor line).
9
+ ---
10
+ --- Features:
11
+ --- - Visualize scope with animated vertical line. It is very fast and done
12
+ --- automatically in a non-blocking way (other operations can be performed,
13
+ --- like moving cursor). You can customize debounce delay and animation rule.
14
+ ---
15
+ --- - Customization of scope computation options can be done on global level
16
+ --- (in |MiniIndentscope.config|), for a certain buffer (using
17
+ --- `vim.b.miniindentscope_config` buffer variable), or within a call (using
18
+ --- `opts` variable in |MiniIndentscope.get_scope()|).
19
+ ---
20
+ --- - Customizable notion of a border: which adjacent lines with strictly lower
21
+ --- indent are recognized as such. This is useful for a certain filetypes
22
+ --- (for example, Python or plain text).
23
+ ---
24
+ --- - Customizable way of line to be considered "border first". This is useful
25
+ --- if you want to place cursor on function header and get scope of its body.
26
+ ---
27
+ --- - There are textobjects and motions to operate on scope. Support |count|
28
+ --- and dot-repeat (in operator pending mode).
29
+ ---
30
+ --- # Setup ~
31
+ ---
32
+ --- This module needs a setup with `require('mini.indentscope').setup({})`
33
+ --- (replace `{}` with your `config` table). It will create global Lua table
34
+ --- `MiniIndentscope` which you can use for scripting or manually (with
35
+ --- `:lua MiniIndentscope.*`).
36
+ ---
37
+ --- See |MiniIndentscope.config| for available config settings.
38
+ ---
39
+ --- You can override runtime config settings locally to buffer inside
40
+ --- `vim.b.miniindentscope_config` which should have same structure as
41
+ --- `MiniIndentscope.config`. See |mini.nvim-buffer-local-config| for more
42
+ --- details.
43
+ ---
44
+ --- # Comparisons ~
45
+ ---
46
+ --- - [lukas-reineke/indent-blankline.nvim](https://github.com/lukas-reineke/indent-blankline.nvim):
47
+ --- - Its main functionality is about showing static guides of indent levels.
48
+ --- - Implementation of |mini.indentscope| is similar to
49
+ --- `indent-blankline.nvim` (using |extmarks| on first column to be shown
50
+ --- even on blank lines). They can be used simultaneously, but it will
51
+ --- lead to one of the visualizations being on top (hiding) of another.
52
+ ---
53
+ --- # Highlight groups ~
54
+ --- *MiniIndentscope-hl-groups*
55
+ ---
56
+ --- - `MiniIndentscopeSymbol` - symbol showing on every line of scope if its
57
+ --- indent is multiple of |'shiftwidth'|.
58
+ --- - `MiniIndentscopeSymbolOff` - symbol showing on every line of scope if its
59
+ --- indent is not multiple of |'shiftwidth'|.
60
+ --- Default: links to `MiniIndentscopeSymbol`.
61
+ ---
62
+ --- To change any highlight group, set it directly with |nvim_set_hl()|.
63
+ ---
64
+ --- # Disabling ~
65
+ ---
66
+ --- To disable autodrawing, set `vim.g.miniindentscope_disable` (globally) or
67
+ --- `vim.b.miniindentscope_disable` (for a buffer) to `true`. Considering high
68
+ --- number of different scenarios and customization intentions, writing exact
69
+ --- rules for disabling module's functionality is left to user. See
70
+ --- |mini.nvim-disabling-recipes| for common recipes.
71
+ ---@tag MiniIndentscope
72
+
73
+ --- Draw of scope indicator is done as iterative animation. It has the
74
+ --- following design:
75
+ --- - Draw indicator on origin line (where cursor is at) immediately. Indicator
76
+ --- is visualized as `MiniIndentscope.config.symbol` placed to the right of
77
+ --- scope's border indent. This creates a line from top to bottom scope edges.
78
+ --- - Draw upward and downward concurrently per one line. Progression by one
79
+ --- line in both direction is considered to be one step of animation.
80
+ --- - Before each step wait certain amount of time, which is decided by
81
+ --- "animation function". It takes next and total step numbers (both are one
82
+ --- or bigger) and returns number of milliseconds to wait before drawing next
83
+ --- step. Comparing to a more popular "easing functions" in animation (input:
84
+ --- duration since animation start; output: percent of animation done), it is
85
+ --- a discrete inverse version of its derivative. Such interface proved to be
86
+ --- more appropriate for kind of task at hand.
87
+ ---
88
+ --- # Special cases ~
89
+ ---
90
+ --- - When scope to be drawn intersects (same indent, ranges overlap) currently
91
+ --- visible one (at process or finished drawing), drawing is done immediately
92
+ --- without animation. With most common example being typing new text, this
93
+ --- feels more natural.
94
+ --- - Scope for the whole buffer is not drawn as it is isually redundant.
95
+ --- Technically, it can be thought as drawn at column 0 (because border
96
+ --- indent is -1) which is not visible.
97
+ ---@tag MiniIndentscope-drawing
98
+
99
+ -- Module definition ==========================================================
100
+ local MiniIndentscope = {}
101
+ local H = {}
102
+
103
+ --- Module setup
104
+ ---
105
+ ---@param config table|nil Module config table. See |MiniIndentscope.config|.
106
+ ---
107
+ ---@usage >lua
108
+ --- require('mini.indentscope').setup() -- use default config
109
+ --- -- OR
110
+ --- require('mini.indentscope').setup({}) -- replace {} with your config table
111
+ --- <
112
+ MiniIndentscope.setup = function(config)
113
+ -- TODO: Remove after Neovim=0.9 support is dropped
114
+ if vim.fn.has('nvim-0.10') == 0 then
115
+ vim.notify(
116
+ '(mini.indentscope) Neovim<0.10 is soft deprecated (module works but is not supported).'
117
+ .. " It will be deprecated after the next 'mini.nvim' release (module might not work)."
118
+ .. ' Please update your Neovim version.'
119
+ )
120
+ end
121
+
122
+ -- Export module
123
+ _G.MiniIndentscope = MiniIndentscope
124
+
125
+ -- Setup config
126
+ config = H.setup_config(config)
127
+
128
+ -- Apply config
129
+ H.apply_config(config)
130
+
131
+ -- Define behavior
132
+ H.create_autocommands()
133
+
134
+ -- Create default highlighting
135
+ H.create_default_hl()
136
+ end
137
+
138
+ --- Defaults ~
139
+ ---@eval return MiniDoc.afterlines_to_code(MiniDoc.current.eval_section)
140
+ ---@text # Options ~
141
+ ---
142
+ --- ## Border ~
143
+ ---
144
+ --- Field `border` controls which line(s) with smaller indent to categorize
145
+ --- as border. This matters for textobjects and motions.
146
+ --- It also controls how empty lines are treated: they are included in scope
147
+ --- only if followed by a border. Another way of looking at it is that indent
148
+ --- of blank line is computed based on value of `border` option.
149
+ --- Here is an illustration of how `border` works in presence of empty lines: >
150
+ ---
151
+ --- |both|bottom|top|none|
152
+ --- 1|function foo() | 0 | 0 | 0 | 0 |
153
+ --- 2| | 4 | 0 | 4 | 0 |
154
+ --- 3| print('Hello world') | 4 | 4 | 4 | 4 |
155
+ --- 4| | 4 | 4 | 2 | 2 |
156
+ --- 5| end | 2 | 2 | 2 | 2 |
157
+ --- <
158
+ --- Numbers inside a table are indent values of a line computed with certain
159
+ --- value of `border`. So, for example, a scope with reference line 3 and
160
+ --- right-most column has body range depending on value of `border` option:
161
+ --- - `border` is "both": range is 2-4, border is 1 and 5 with indent 2.
162
+ --- - `border` is "top": range is 2-3, border is 1 with indent 0.
163
+ --- - `border` is "bottom": range is 3-4, border is 5 with indent 0.
164
+ --- - `border` is "none": range is 3-3, border is empty with indent `nil`.
165
+ ---
166
+ --- ## Indent at cursor ~
167
+ ---
168
+ --- Field `indent_at_cursor` controls if cursor position should affect computation
169
+ --- of scope. If `true`, reference indent is a minimum of reference line's indent
170
+ --- and cursor column. In main example, here how scope's body range differs
171
+ --- depending on cursor column and `indent_at_cursor` value (assuming cursor is
172
+ --- on line 3 and it is whole buffer): >
173
+ ---
174
+ --- Column\Option true|false
175
+ --- 1 and 2 2-5 | 2-4
176
+ --- 3 and more 2-4 | 2-4
177
+ --- <
178
+ --- ## Number of lines ~
179
+ ---
180
+ --- Field `n_lines` defines |MiniIndentscope.get_scope()| behavior for how many
181
+ --- lines above/below to check before iteration is stopped. Scope that reached
182
+ --- computation limit has <is_incomplete> field set to `true`. It will also not
183
+ --- be auto drawn with default `config.draw.predicate`.
184
+ ---
185
+ --- Lower values will result in better overall performance in exchange for more
186
+ --- frequent incomplete scope computation. Set to `math.huge` for no restriction.
187
+ ---
188
+ --- ## Try as border ~
189
+ ---
190
+ --- Field `try_as_border` controls how to act when input line can be recognized
191
+ --- as a border of some neighbor indent scope. In main example, when input line
192
+ --- is 1 and can be recognized as border for inner scope, value `try_as_border=true`
193
+ --- means that inner scope will be returned. Similar, for input line 5 inner scope
194
+ --- will be returned if it is recognized as border.
195
+ MiniIndentscope.config = {
196
+ -- Draw options
197
+ draw = {
198
+ -- Delay (in ms) between event and start of drawing scope indicator
199
+ delay = 100,
200
+
201
+ -- Animation rule for scope's first drawing. A function which, given
202
+ -- next and total step numbers, returns wait time (in ms). See
203
+ -- |MiniIndentscope.gen_animation| for builtin options. To disable
204
+ -- animation, use `require('mini.indentscope').gen_animation.none()`.
205
+ --minidoc_replace_start animation = --<function: implements constant 20ms between steps>,
206
+ animation = function(s, n) return 20 end,
207
+ --minidoc_replace_end
208
+
209
+ -- Whether to auto draw scope: return `true` to draw, `false` otherwise.
210
+ -- Default draws only fully computed scope (see `options.n_lines`).
211
+ predicate = function(scope) return not scope.body.is_incomplete end,
212
+
213
+ -- Symbol priority. Increase to display on top of more symbols.
214
+ priority = 2,
215
+ },
216
+
217
+ -- Module mappings. Use `''` (empty string) to disable one.
218
+ mappings = {
219
+ -- Textobjects
220
+ object_scope = 'ii',
221
+ object_scope_with_border = 'ai',
222
+
223
+ -- Motions (jump to respective border line; if not present - body line)
224
+ goto_top = '[i',
225
+ goto_bottom = ']i',
226
+ },
227
+
228
+ -- Options which control scope computation
229
+ options = {
230
+ -- Type of scope's border: which line(s) with smaller indent to
231
+ -- categorize as border. Can be one of: 'both', 'top', 'bottom', 'none'.
232
+ border = 'both',
233
+
234
+ -- Whether to use cursor column when computing reference indent.
235
+ -- Useful to see incremental scopes with horizontal cursor movements.
236
+ indent_at_cursor = true,
237
+
238
+ -- Maximum number of lines above or below within which scope is computed
239
+ n_lines = 10000,
240
+
241
+ -- Whether to first check input line to be a border of adjacent scope.
242
+ -- Use it if you want to place cursor on function header to get scope of
243
+ -- its body.
244
+ try_as_border = false,
245
+ },
246
+
247
+ -- Which character to use for drawing scope indicator
248
+ symbol = '╎',
249
+ }
250
+ --minidoc_afterlines_end
251
+
252
+ -- Module functionality =======================================================
253
+ --- Compute indent scope
254
+ ---
255
+ --- Indent scope (or just "scope") is a maximum set of consecutive lines which
256
+ --- contains certain reference line (cursor line by default) and every member
257
+ --- has indent not less than certain reference indent ("indent at column" by
258
+ --- default). Here "indent at column" means minimum between input column value
259
+ --- and indent of reference line. When using cursor column, this allows for a
260
+ --- useful interactive view of nested indent scopes by making horizontal
261
+ --- movements within line.
262
+ ---
263
+ --- Options controlling actual computation is taken from these places in order:
264
+ --- - Argument `opts`. Use it to ensure independence from other sources.
265
+ --- - Buffer local variable `vim.b.miniindentscope_config` (`options` field).
266
+ --- Useful to define local behavior (for example, for a certain filetype).
267
+ --- - Global options from |MiniIndentscope.config|.
268
+ ---
269
+ --- # Algorithm overview ~
270
+ ---
271
+ --- - Compute reference "indent at column". Reference line is an input `line`
272
+ --- which might be modified to one of its neighbors if `try_as_border` option
273
+ --- is `true`: if it can be viewed as border of some neighbor scope, it will.
274
+ --- - Process upwards and downwards from reference line searching for line with
275
+ --- indent strictly less than reference one. This is like casting rays up and
276
+ --- down from reference line and reference indent until meeting "a wall"
277
+ --- (character to the right of indent or buffer edge). Latest line before
278
+ --- meeting a wall is a respective end of scope body. It always exists because
279
+ --- reference line is a such one.
280
+ --- Casting ray is forced to stop if it goes over `opts.n_lines` lines.
281
+ --- - Based on top and bottom lines with strictly lower indent, construct
282
+ --- scopes's border. The way it is computed is decided based on `border`
283
+ --- option (see |MiniIndentscope.config| for more information).
284
+ --- - Compute border indent as maximum indent of border lines (or reference
285
+ --- indent minus one in case of no border). This is used during drawing
286
+ --- visual indicator.
287
+ ---
288
+ --- # Indent computation ~
289
+ ---
290
+ --- For every line indent is intended to be computed unambiguously:
291
+ --- - For "normal" lines indent is an output of |indent()|.
292
+ --- - Indent is `-1` for imaginary lines 0 and past last line.
293
+ --- - For blank and empty lines indent is computed based on previous
294
+ --- (|prevnonblank()|) and next (|nextnonblank()|) non-blank lines. The way
295
+ --- it is computed is decided based on `border` in order to not include blank
296
+ --- lines at edge of scope's body if there is no border there. See
297
+ --- |MiniIndentscope.config| for a details example.
298
+ ---
299
+ ---@param line number|nil Input line number (starts from 1). Can be modified to a
300
+ --- neighbor if `try_as_border` is `true`. Default: cursor line.
301
+ ---@param col number|nil Column number (starts from 1). Default: if
302
+ --- `indent_at_cursor` option is `true` - cursor column from `curswant` of
303
+ --- |getcurpos()| (allows for more natural behavior on empty lines);
304
+ --- `math.huge` otherwise in order to not incorporate cursor in computation.
305
+ ---@param opts table|nil Options to override global or buffer local ones (see
306
+ --- |MiniIndentscope.config|).
307
+ ---
308
+ ---@return table Table with scope information:
309
+ --- - <body> - table with <top> (top line of scope, inclusive), <bottom>
310
+ --- (bottom line of scope, inclusive), and <indent> (minimum indent within
311
+ --- scope) keys. Line numbers start at 1. Can also have <is_incomplete> key
312
+ --- set to `true` if computation was stopped due to `opts.n_lines` restriction.
313
+ --- - <border> - table with <top> (line of top border, might be `nil`),
314
+ --- <bottom> (line of bottom border, might be `nil`), and <indent> (indent
315
+ --- of border) keys. Line numbers start at 1.
316
+ --- - <buf_id> - identifier of current buffer.
317
+ --- - <reference> - table with <line> (reference line), <column> (reference
318
+ --- column), and <indent> ("indent at column") keys.
319
+ MiniIndentscope.get_scope = function(line, col, opts)
320
+ opts = H.get_config({ options = opts }).options
321
+
322
+ -- Compute default `line` and\or `col`
323
+ if not (line and col) then
324
+ local curpos = vim.fn.getcurpos()
325
+
326
+ line = line or curpos[2]
327
+ line = opts.try_as_border and H.border_correctors[opts.border](line, opts) or line
328
+
329
+ -- Use `curpos[5]` (`curswant`, see `:h getcurpos()`) to account for blank
330
+ -- and empty lines.
331
+ col = col or (opts.indent_at_cursor and curpos[5] or math.huge)
332
+ end
333
+
334
+ -- Compute "indent at column"
335
+ local line_indent = H.get_line_indent(line, opts)
336
+ local indent = math.min(col, line_indent)
337
+
338
+ -- Make early return
339
+ local body = { indent = indent }
340
+ if indent <= 0 then
341
+ body.top, body.bottom, body.indent = 1, vim.fn.line('$'), line_indent
342
+ else
343
+ local up_line, up_min_indent, up_is_incomplete = H.cast_ray(line, indent, 'up', opts)
344
+ local down_line, down_min_indent, down_is_incomplete = H.cast_ray(line, indent, 'down', opts)
345
+ body.top, body.bottom = up_line, down_line
346
+ body.indent = math.min(line_indent, up_min_indent, down_min_indent)
347
+ body.is_incomplete = up_is_incomplete or down_is_incomplete
348
+ end
349
+
350
+ return {
351
+ body = body,
352
+ border = H.border_from_body[opts.border](body, opts),
353
+ buf_id = vim.api.nvim_get_current_buf(),
354
+ reference = { line = line, column = col, indent = indent },
355
+ }
356
+ end
357
+
358
+ --- Draw scope manually
359
+ ---
360
+ --- Scope is visualized as a vertical line within scope's body range at column
361
+ --- equal to border indent plus one (or body indent if border is absent).
362
+ --- Numbering starts from one.
363
+ ---
364
+ ---@param scope table|nil Scope. Default: output of |MiniIndentscope.get_scope()|
365
+ --- with default arguments.
366
+ ---@param opts table|nil Options. Currently supported:
367
+ --- - <animation_fun> - animation function for drawing. See
368
+ --- |MiniIndentscope-drawing| and |MiniIndentscope.gen_animation|.
369
+ --- - <priority> - priority number for visualization. See `priority` option
370
+ --- for |nvim_buf_set_extmark()|.
371
+ MiniIndentscope.draw = function(scope, opts)
372
+ scope = scope or MiniIndentscope.get_scope()
373
+ local config = H.get_config()
374
+ local draw_opts =
375
+ vim.tbl_deep_extend('force', { animation_fun = config.draw.animation, priority = config.draw.priority }, opts or {})
376
+
377
+ H.undraw_scope()
378
+
379
+ H.current.scope = scope
380
+ H.draw_scope(scope, draw_opts)
381
+ end
382
+
383
+ --- Undraw currently visible scope manually
384
+ MiniIndentscope.undraw = function() H.undraw_scope() end
385
+
386
+ --- Generate builtin animation function
387
+ ---
388
+ --- This is a builtin source to generate animation function for usage in
389
+ --- `MiniIndentscope.config.draw.animation`. Most of them are variations of
390
+ --- common easing functions, which provide certain type of progression for
391
+ --- revealing scope visual indicator.
392
+ ---
393
+ --- Each field corresponds to one family of progression which can be customized
394
+ --- further by supplying appropriate arguments.
395
+ ---
396
+ --- Examples:
397
+ --- - Don't use animation: `MiniIndentscope.gen_animation.none()`
398
+ --- - Use quadratic "out" easing with total duration of 1000 ms: >lua
399
+ ---
400
+ --- gen_animation.quadratic({ easing = 'out', duration = 1000, unit = 'total' })
401
+ --- <
402
+ ---@seealso |MiniIndentscope-drawing| for more information about how drawing is done.
403
+ MiniIndentscope.gen_animation = {}
404
+
405
+ ---@alias __indentscope_animation_opts table|nil Options that control progression. Possible keys:
406
+ --- - <easing> `(string)` - a subtype of progression. One of "in"
407
+ --- (accelerating from zero speed), "out" (decelerating to zero speed),
408
+ --- "in-out" (default; accelerating halfway, decelerating after).
409
+ --- - <duration> `(number)` - duration (in ms) of a unit. Default: 20.
410
+ --- - <unit> `(string)` - which unit's duration `opts.duration` controls. One
411
+ --- of "step" (default; ensures average duration of step to be `opts.duration`)
412
+ --- or "total" (ensures fixed total duration regardless of scope's range).
413
+ ---@alias __indentscope_animation_return function Animation function (see |MiniIndentscope-drawing|).
414
+
415
+ --- Generate no animation
416
+ ---
417
+ --- Show indicator immediately. Same as animation function always returning 0.
418
+ MiniIndentscope.gen_animation.none = function()
419
+ return function() return 0 end
420
+ end
421
+
422
+ --- Generate linear progression
423
+ ---
424
+ ---@param opts __indentscope_animation_opts
425
+ ---
426
+ ---@return __indentscope_animation_return
427
+ MiniIndentscope.gen_animation.linear = function(opts)
428
+ return H.animation_arithmetic_powers(0, H.normalize_animation_opts(opts))
429
+ end
430
+
431
+ --- Generate quadratic progression
432
+ ---
433
+ ---@param opts __indentscope_animation_opts
434
+ ---
435
+ ---@return __indentscope_animation_return
436
+ MiniIndentscope.gen_animation.quadratic = function(opts)
437
+ return H.animation_arithmetic_powers(1, H.normalize_animation_opts(opts))
438
+ end
439
+
440
+ --- Generate cubic progression
441
+ ---
442
+ ---@param opts __indentscope_animation_opts
443
+ ---
444
+ ---@return __indentscope_animation_return
445
+ MiniIndentscope.gen_animation.cubic = function(opts)
446
+ return H.animation_arithmetic_powers(2, H.normalize_animation_opts(opts))
447
+ end
448
+
449
+ --- Generate quartic progression
450
+ ---
451
+ ---@param opts __indentscope_animation_opts
452
+ ---
453
+ ---@return __indentscope_animation_return
454
+ MiniIndentscope.gen_animation.quartic = function(opts)
455
+ return H.animation_arithmetic_powers(3, H.normalize_animation_opts(opts))
456
+ end
457
+
458
+ --- Generate exponential progression
459
+ ---
460
+ ---@param opts __indentscope_animation_opts
461
+ ---
462
+ ---@return __indentscope_animation_return
463
+ MiniIndentscope.gen_animation.exponential = function(opts)
464
+ return H.animation_geometrical_powers(H.normalize_animation_opts(opts))
465
+ end
466
+
467
+ --- Move cursor within scope
468
+ ---
469
+ --- Cursor is placed on a first non-blank character of target line.
470
+ ---
471
+ ---@param side string One of "top" or "bottom".
472
+ ---@param use_border boolean|nil Whether to move to border or within scope's body.
473
+ --- If particular border is absent, body is used.
474
+ ---@param scope table|nil Scope to use. Default: output of |MiniIndentscope.get_scope()|.
475
+ MiniIndentscope.move_cursor = function(side, use_border, scope)
476
+ scope = scope or MiniIndentscope.get_scope()
477
+
478
+ -- This defaults to body's side if it is not present in border
479
+ local target_line = use_border and scope.border[side] or scope.body[side]
480
+ target_line = math.min(math.max(target_line, 1), vim.fn.line('$'))
481
+
482
+ vim.api.nvim_win_set_cursor(0, { target_line, 0 })
483
+ -- Move to first non-blank character to allow chaining scopes
484
+ vim.cmd('normal! ^')
485
+ end
486
+
487
+ --- Function for motion mappings
488
+ ---
489
+ --- Move to a certain side of border. Respects |count| and dot-repeat (in
490
+ --- operator-pending mode). Doesn't move cursor for scope that is not shown
491
+ --- (drawing indent less that zero).
492
+ ---
493
+ ---@param side string One of "top" or "bottom".
494
+ ---@param add_to_jumplist boolean|nil Whether to add movement to jump list. It is
495
+ --- `true` only for Normal mode mappings.
496
+ MiniIndentscope.operator = function(side, add_to_jumplist)
497
+ local scope = MiniIndentscope.get_scope()
498
+
499
+ -- Don't support scope that can't be shown
500
+ if H.scope_get_draw_indent(scope) < 0 then return end
501
+
502
+ -- Add movement to jump list. Needs remembering `count1` before that because
503
+ -- it seems to reset it to 1.
504
+ local count = vim.v.count1
505
+ if add_to_jumplist then vim.cmd('normal! m`') end
506
+
507
+ -- Make sequence of jumps
508
+ for _ = 1, count do
509
+ MiniIndentscope.move_cursor(side, true, scope)
510
+ -- Use `try_as_border = false` to enable chaining
511
+ scope = MiniIndentscope.get_scope(nil, nil, { try_as_border = false })
512
+
513
+ -- Don't support scope that can't be shown
514
+ if H.scope_get_draw_indent(scope) < 0 then return end
515
+ end
516
+ end
517
+
518
+ --- Function for textobject mappings
519
+ ---
520
+ --- Respects |count| and dot-repeat (in operator-pending mode). Doesn't work
521
+ --- for scope that is not shown (drawing indent less that zero).
522
+ ---
523
+ ---@param use_border boolean|nil Whether to include border in textobject. When
524
+ --- `true` and `try_as_border` option is `false`, allows "chaining" calls for
525
+ --- incremental selection.
526
+ MiniIndentscope.textobject = function(use_border)
527
+ local scope = MiniIndentscope.get_scope()
528
+
529
+ -- Don't support scope that can't be shown
530
+ if H.scope_get_draw_indent(scope) < 0 then return end
531
+
532
+ -- Allow chaining only if using border
533
+ local count = use_border and vim.v.count1 or 1
534
+
535
+ -- Make sequence of incremental selections
536
+ for _ = 1, count do
537
+ -- Try finish cursor on border
538
+ local start, finish = 'top', 'bottom'
539
+ if use_border and scope.border.bottom == nil then
540
+ start, finish = 'bottom', 'top'
541
+ end
542
+
543
+ -- Take into account forced Operator-pending modes ('nov', 'noV', 'no<C-V>')
544
+ local vis_mode = vim.fn.mode(1):match('^no(.)') or 'V'
545
+ H.exit_visual_mode()
546
+
547
+ MiniIndentscope.move_cursor(start, use_border, scope)
548
+ vim.cmd('normal! ' .. vis_mode)
549
+ MiniIndentscope.move_cursor(finish, use_border, scope)
550
+ vim.cmd('normal! g_')
551
+
552
+ -- Use `try_as_border = false` to enable chaining
553
+ scope = MiniIndentscope.get_scope(nil, nil, { try_as_border = false })
554
+
555
+ -- Don't support scope that can't be shown
556
+ if H.scope_get_draw_indent(scope) < 0 then return end
557
+ end
558
+ end
559
+
560
+ -- Helper data ================================================================
561
+ -- Module default config
562
+ H.default_config = vim.deepcopy(MiniIndentscope.config)
563
+
564
+ -- Namespace for drawing vertical line
565
+ H.ns_id = vim.api.nvim_create_namespace('MiniIndentscope')
566
+
567
+ -- Timer for doing animation
568
+ H.timer = vim.loop.new_timer()
569
+
570
+ -- Table with current relevalnt data:
571
+ -- - `event_id` - counter for events.
572
+ -- - `scope` - latest drawn scope.
573
+ -- - `draw_status` - status of current drawing.
574
+ H.current = { event_id = 0, scope = {}, draw_status = 'none' }
575
+
576
+ -- Functions to compute indent in ambiguous cases
577
+ H.indent_funs = {
578
+ ['min'] = function(top_indent, bottom_indent) return math.min(top_indent, bottom_indent) end,
579
+ ['max'] = function(top_indent, bottom_indent) return math.max(top_indent, bottom_indent) end,
580
+ ['top'] = function(top_indent, bottom_indent) return top_indent end,
581
+ ['bottom'] = function(top_indent, bottom_indent) return bottom_indent end,
582
+ }
583
+
584
+ -- Functions to compute indent of blank line to satisfy `config.options.border`
585
+ H.blank_indent_funs = {
586
+ ['none'] = H.indent_funs.min,
587
+ ['top'] = H.indent_funs.bottom,
588
+ ['bottom'] = H.indent_funs.top,
589
+ ['both'] = H.indent_funs.max,
590
+ }
591
+
592
+ -- Functions to compute border from body
593
+ H.border_from_body = {
594
+ ['none'] = function(body, opts) return {} end,
595
+ ['top'] = function(body, opts) return { top = body.top - 1, indent = H.get_line_indent(body.top - 1, opts) } end,
596
+ ['bottom'] = function(body, opts)
597
+ return { bottom = body.bottom + 1, indent = H.get_line_indent(body.bottom + 1, opts) }
598
+ end,
599
+ ['both'] = function(body, opts)
600
+ return {
601
+ top = body.top - 1,
602
+ bottom = body.bottom + 1,
603
+ indent = math.max(H.get_line_indent(body.top - 1, opts), H.get_line_indent(body.bottom + 1, opts)),
604
+ }
605
+ end,
606
+ }
607
+
608
+ -- Functions to correct line in case it is a border
609
+ H.border_correctors = {
610
+ ['none'] = function(line, opts) return line end,
611
+ ['top'] = function(line, opts)
612
+ local cur_indent, next_indent = H.get_line_indent(line, opts), H.get_line_indent(line + 1, opts)
613
+ return (cur_indent < next_indent) and (line + 1) or line
614
+ end,
615
+ ['bottom'] = function(line, opts)
616
+ local prev_indent, cur_indent = H.get_line_indent(line - 1, opts), H.get_line_indent(line, opts)
617
+ return (cur_indent < prev_indent) and (line - 1) or line
618
+ end,
619
+ ['both'] = function(line, opts)
620
+ local prev_indent, cur_indent, next_indent =
621
+ H.get_line_indent(line - 1, opts), H.get_line_indent(line, opts), H.get_line_indent(line + 1, opts)
622
+
623
+ if prev_indent <= cur_indent and next_indent <= cur_indent then return line end
624
+
625
+ -- If prev and next indents are equal and bigger than current, prefer next
626
+ if prev_indent <= next_indent then return line + 1 end
627
+
628
+ return line - 1
629
+ end,
630
+ }
631
+
632
+ -- Whether or not Nvim supports the virt_text_repeat_linebreak extmark feature
633
+ H.has_wrapped_virt_text = vim.fn.has('nvim-0.10') == 1
634
+
635
+ -- Helper functionality =======================================================
636
+ -- Settings -------------------------------------------------------------------
637
+ H.setup_config = function(config)
638
+ H.check_type('config', config, 'table', true)
639
+ config = vim.tbl_deep_extend('force', vim.deepcopy(H.default_config), config or {})
640
+
641
+ H.check_type('draw', config.draw, 'table')
642
+ H.check_type('draw.delay', config.draw.delay, 'number')
643
+ H.check_type('draw.animation', config.draw.animation, 'function')
644
+ H.check_type('draw.predicate', config.draw.predicate, 'function')
645
+ H.check_type('draw.priority', config.draw.priority, 'number')
646
+
647
+ H.check_type('mappings', config.mappings, 'table')
648
+ H.check_type('mappings.object_scope', config.mappings.object_scope, 'string')
649
+ H.check_type('mappings.object_scope_with_border', config.mappings.object_scope_with_border, 'string')
650
+ H.check_type('mappings.goto_top', config.mappings.goto_top, 'string')
651
+ H.check_type('mappings.goto_bottom', config.mappings.goto_bottom, 'string')
652
+
653
+ H.check_type('options', config.options, 'table')
654
+ H.check_type('options.border', config.options.border, 'string')
655
+ H.check_type('options.indent_at_cursor', config.options.indent_at_cursor, 'boolean')
656
+ H.check_type('options.n_lines', config.options.n_lines, 'number')
657
+ H.check_type('options.try_as_border', config.options.try_as_border, 'boolean')
658
+
659
+ H.check_type('symbol', config.symbol, 'string')
660
+
661
+ return config
662
+ end
663
+
664
+ H.apply_config = function(config)
665
+ MiniIndentscope.config = config
666
+ local maps = config.mappings
667
+
668
+ --stylua: ignore start
669
+ H.map('n', maps.goto_top, [[<Cmd>lua MiniIndentscope.operator('top', true)<CR>]], { desc = 'Go to indent scope top' })
670
+ H.map('n', maps.goto_bottom, [[<Cmd>lua MiniIndentscope.operator('bottom', true)<CR>]], { desc = 'Go to indent scope bottom' })
671
+
672
+ H.map('x', maps.goto_top, [[<Cmd>lua MiniIndentscope.operator('top')<CR>]], { desc = 'Go to indent scope top' })
673
+ H.map('x', maps.goto_bottom, [[<Cmd>lua MiniIndentscope.operator('bottom')<CR>]], { desc = 'Go to indent scope bottom' })
674
+ H.map('x', maps.object_scope, '<Cmd>lua MiniIndentscope.textobject(false)<CR>', { desc = 'Object scope' })
675
+ H.map('x', maps.object_scope_with_border, '<Cmd>lua MiniIndentscope.textobject(true)<CR>', { desc = 'Object scope with border' })
676
+
677
+ -- Use `<Cmd>...<CR>` to have proper dot-repeat
678
+ -- See https://github.com/neovim/neovim/issues/23406
679
+ -- TODO: use local functions if/when that issue is resolved
680
+ H.map('o', maps.goto_top, [[<Cmd>lua MiniIndentscope.operator('top')<CR>]], { desc = 'Go to indent scope top' })
681
+ H.map('o', maps.goto_bottom, [[<Cmd>lua MiniIndentscope.operator('bottom')<CR>]], { desc = 'Go to indent scope bottom' })
682
+ H.map('o', maps.object_scope, '<Cmd>lua MiniIndentscope.textobject(false)<CR>', { desc = 'Object scope' })
683
+ H.map('o', maps.object_scope_with_border, '<Cmd>lua MiniIndentscope.textobject(true)<CR>', { desc = 'Object scope with border' })
684
+ --stylua: ignore start
685
+ end
686
+
687
+ H.create_autocommands = function()
688
+ local gr = vim.api.nvim_create_augroup('MiniIndentscope', {})
689
+
690
+ local au = function(event, pattern, callback, desc)
691
+ vim.api.nvim_create_autocmd(event, { group = gr, pattern = pattern, callback = callback, desc = desc })
692
+ end
693
+
694
+ local lazy_events = { 'CursorMoved', 'CursorMovedI', 'ModeChanged' }
695
+ au(lazy_events, '*', function() H.auto_draw({ lazy = true }) end, 'Auto draw indentscope lazily')
696
+ local now_events = { 'TextChanged', 'TextChangedI', 'TextChangedP', 'WinScrolled' }
697
+ au(now_events, '*', function() H.auto_draw() end, 'Auto draw indentscope')
698
+
699
+ au('ColorScheme', '*', H.create_default_hl, 'Ensure colors')
700
+ end
701
+
702
+ --stylua: ignore
703
+ H.create_default_hl = function()
704
+ vim.api.nvim_set_hl(0, 'MiniIndentscopeSymbol', { default = true, link = 'Delimiter' })
705
+ vim.api.nvim_set_hl(0, 'MiniIndentscopeSymbolOff', { default = true, link = 'MiniIndentscopeSymbol' })
706
+ end
707
+
708
+ H.is_disabled = function() return vim.g.miniindentscope_disable == true or vim.b.miniindentscope_disable == true end
709
+
710
+ H.get_config = function(config)
711
+ return vim.tbl_deep_extend('force', MiniIndentscope.config, vim.b.miniindentscope_config or {}, config or {})
712
+ end
713
+
714
+ -- Autocommands ---------------------------------------------------------------
715
+ H.auto_draw = function(opts)
716
+ if H.is_disabled() then
717
+ H.undraw_scope()
718
+ return
719
+ end
720
+
721
+ opts = opts or {}
722
+ local scope = MiniIndentscope.get_scope()
723
+
724
+ -- Make early return if nothing has to be done. Doing this before updating
725
+ -- event id allows to not interrupt ongoing animation.
726
+ if opts.lazy and H.current.draw_status ~= 'none' and H.scope_is_equal(scope, H.current.scope) then return end
727
+
728
+ -- Account for current event
729
+ local local_event_id = H.current.event_id + 1
730
+ H.current.event_id = local_event_id
731
+
732
+ -- Compute drawing options for current event
733
+ local draw_opts = H.make_autodraw_opts(scope)
734
+
735
+ -- Allow delay
736
+ if draw_opts.delay > 0 then H.undraw_scope(draw_opts) end
737
+
738
+ -- Use `defer_fn()` even if `delay` is 0 to draw indicator only after all
739
+ -- events are processed (stops flickering)
740
+ vim.defer_fn(function()
741
+ if H.current.event_id ~= local_event_id then return end
742
+
743
+ H.undraw_scope(draw_opts)
744
+
745
+ -- Don't autodraw if not asked to
746
+ if not H.get_config().draw.predicate(scope) then return end
747
+
748
+ H.current.scope = scope
749
+ H.draw_scope(scope, draw_opts)
750
+ end, draw_opts.delay)
751
+ end
752
+
753
+ -- Scope ----------------------------------------------------------------------
754
+ -- Line indent:
755
+ -- - Equals output of `vim.fn.indent()` in case of non-blank line.
756
+ -- - Depends on `MiniIndentscope.config.options.border` in such way so as to
757
+ -- ignore blank lines before line not recognized as border.
758
+ H.get_line_indent = function(line, opts)
759
+ local prev_nonblank = vim.fn.prevnonblank(line)
760
+ local res = vim.fn.indent(prev_nonblank)
761
+
762
+ -- Compute indent of blank line depending on `border` option
763
+ local border = opts.border
764
+ if line ~= prev_nonblank then
765
+ local next_indent = vim.fn.indent(vim.fn.nextnonblank(line))
766
+ local blank_rule = H.blank_indent_funs[border]
767
+ res = blank_rule(res, next_indent)
768
+ end
769
+
770
+ return res
771
+ end
772
+
773
+ H.cast_ray = function(line, indent, direction, opts)
774
+ local final_line, increment = 1, -1
775
+ if direction == 'down' then
776
+ final_line, increment = vim.fn.line('$'), 1
777
+ end
778
+
779
+ local is_incomplete
780
+ if math.abs(line - final_line) > opts.n_lines then
781
+ final_line, is_incomplete = line + increment * opts.n_lines, true
782
+ end
783
+
784
+ local min_indent = math.huge
785
+ for l = line, final_line, increment do
786
+ local new_indent = H.get_line_indent(l + increment, opts)
787
+ if new_indent < indent then return l, min_indent, nil end
788
+ if new_indent < min_indent then min_indent = new_indent end
789
+ end
790
+
791
+ return final_line, min_indent, is_incomplete
792
+ end
793
+
794
+ H.scope_get_draw_indent = function(scope) return scope.border.indent or (scope.body.indent - 1) end
795
+
796
+ H.scope_is_equal = function(scope_1, scope_2)
797
+ if type(scope_1) ~= 'table' or type(scope_2) ~= 'table' then return false end
798
+
799
+ return scope_1.buf_id == scope_2.buf_id
800
+ and H.scope_get_draw_indent(scope_1) == H.scope_get_draw_indent(scope_2)
801
+ and scope_1.body.top == scope_2.body.top
802
+ and scope_1.body.bottom == scope_2.body.bottom
803
+ and scope_1.body.is_incomplete == scope_2.body.is_incomplete
804
+ end
805
+
806
+ H.scope_has_intersect = function(scope_1, scope_2)
807
+ if type(scope_1) ~= 'table' or type(scope_2) ~= 'table' then return false end
808
+ if (scope_1.buf_id ~= scope_2.buf_id) or (H.scope_get_draw_indent(scope_1) ~= H.scope_get_draw_indent(scope_2)) then
809
+ return false
810
+ end
811
+
812
+ local body_1, body_2 = scope_1.body, scope_2.body
813
+ return (body_2.top <= body_1.top and body_1.top <= body_2.bottom)
814
+ or (body_1.top <= body_2.top and body_2.top <= body_1.bottom)
815
+ end
816
+
817
+ -- Indicator ------------------------------------------------------------------
818
+ --- Compute indicator of scope to be displayed
819
+ ---
820
+ --- Indicator is visual representation of scope in current window view using
821
+ --- extmarks. Currently only needed because Neovim can't correctly process
822
+ --- horizontal window scroll (Neovim issue:
823
+ --- https://github.com/neovim/neovim/issues/14050)
824
+ ---
825
+ ---@return table|nil Table with indicator info or empty one in case indicator
826
+ --- shouldn't be drawn.
827
+ ---@private
828
+ H.indicator_compute = function(scope)
829
+ scope = scope or H.current.scope
830
+ local indent = H.scope_get_draw_indent(scope)
831
+
832
+ -- Don't draw indicator that should be outside of screen. This condition is
833
+ -- (perpusfully) "responsible" for not drawing indicator spanning whole file.
834
+ if indent < 0 then return {} end
835
+
836
+ -- Text indentation should depend on current window view because it will use
837
+ -- `virt_text_win_col` attribute of extmark options (the only way to reliably
838
+ -- put it anywhere on screen; important to show properly on empty lines).
839
+ local col = indent - vim.fn.winsaveview().leftcol
840
+ if col < 0 then return {} end
841
+
842
+ -- Pick highlight group based on if indent is a multiple of shiftwidth.
843
+ -- This adds visual indicator of whether indent is "correct".
844
+ local hl_group = (indent % vim.fn.shiftwidth() == 0) and 'MiniIndentscopeSymbol' or 'MiniIndentscopeSymbolOff'
845
+ local virt_text = { { H.get_config().symbol, hl_group } }
846
+
847
+ return {
848
+ buf_id = vim.api.nvim_get_current_buf(),
849
+ virt_text = virt_text,
850
+ virt_text_win_col = col,
851
+ top = scope.body.top,
852
+ bottom = scope.body.bottom,
853
+ }
854
+ end
855
+
856
+ -- Drawing --------------------------------------------------------------------
857
+ H.draw_scope = function(scope, opts)
858
+ scope = scope or {}
859
+ opts = opts or {}
860
+
861
+ local indicator = H.indicator_compute(scope)
862
+
863
+ -- Don't draw anything if nothing to be displayed
864
+ if indicator.virt_text == nil or #indicator.virt_text == 0 then
865
+ H.current.draw_status = 'finished'
866
+ return
867
+ end
868
+
869
+ -- Make drawing function
870
+ local draw_fun = H.make_draw_function(indicator, opts)
871
+
872
+ -- Perform drawing
873
+ H.current.draw_status = 'drawing'
874
+ H.draw_indicator_animation(indicator, draw_fun, opts.animation_fun)
875
+ end
876
+
877
+ H.draw_indicator_animation = function(indicator, draw_fun, animation_fun)
878
+ -- Draw from origin (cursor line but within indicator range)
879
+ local top, bottom = indicator.top, indicator.bottom
880
+ local origin = math.min(math.max(vim.fn.line('.'), top), bottom)
881
+
882
+ local step = 0
883
+ local n_steps = math.max(origin - top, bottom - origin)
884
+ local wait_time = 0
885
+
886
+ local draw_step
887
+ draw_step = vim.schedule_wrap(function()
888
+ -- Check for not drawing outside of interval is done inside `draw_fun`
889
+ local success = draw_fun(origin - step)
890
+ if step > 0 then success = success and draw_fun(origin + step) end
891
+
892
+ if not success or step == n_steps then
893
+ H.current.draw_status = step == n_steps and 'finished' or H.current.draw_status
894
+ H.timer:stop()
895
+ return
896
+ end
897
+
898
+ step = step + 1
899
+ wait_time = wait_time + animation_fun(step, n_steps)
900
+
901
+ -- Repeat value of `timer` seems to be rounded down to milliseconds. This
902
+ -- means that values less than 1 will lead to timer stop repeating. Instead
903
+ -- call next step function directly.
904
+ if wait_time < 1 then
905
+ H.timer:set_repeat(0)
906
+ -- Use `return` to make this proper "tail call"
907
+ return draw_step()
908
+ else
909
+ H.timer:set_repeat(wait_time)
910
+
911
+ -- Restart `wait_time` only if it is actually used. Do this accounting
912
+ -- actually set repeat time.
913
+ wait_time = wait_time - H.timer:get_repeat()
914
+
915
+ -- Usage of `again()` is needed to overcome the fact that it is called
916
+ -- inside callback and to restart initial timer. Mainly this is needed
917
+ -- only in case of transition from 'non-repeating' timer to 'repeating'
918
+ -- one in case of complex animation functions. See
919
+ -- https://docs.libuv.org/en/v1.x/timer.html#api
920
+ H.timer:again()
921
+ end
922
+ end)
923
+
924
+ -- Start non-repeating timer without callback execution. This shouldn't be
925
+ -- `timer:start(0, 0, draw_step)` because it will execute `draw_step` on the
926
+ -- next redraw (flickers on window scroll).
927
+ H.timer:start(10000000, 0, draw_step)
928
+
929
+ -- Draw step zero (at origin) immediately
930
+ draw_step()
931
+ end
932
+
933
+ H.undraw_scope = function(opts)
934
+ opts = opts or {}
935
+
936
+ -- Don't operate outside of current event if able to verify
937
+ if opts.event_id and opts.event_id ~= H.current.event_id then return end
938
+
939
+ pcall(vim.api.nvim_buf_clear_namespace, H.current.scope.buf_id or 0, H.ns_id, 0, -1)
940
+
941
+ H.current.draw_status = 'none'
942
+ H.current.scope = {}
943
+ end
944
+
945
+ H.make_autodraw_opts = function(scope)
946
+ local config = H.get_config()
947
+ local res = {
948
+ event_id = H.current.event_id,
949
+ type = 'animation',
950
+ delay = config.draw.delay,
951
+ animation_fun = config.draw.animation,
952
+ priority = config.draw.priority,
953
+ }
954
+
955
+ if H.current.draw_status == 'none' then return res end
956
+
957
+ -- Draw immediately scope which intersects (same indent, overlapping ranges)
958
+ -- currently drawn or finished. This is more natural when typing text.
959
+ if H.scope_has_intersect(scope, H.current.scope) then
960
+ res.type = 'immediate'
961
+ res.delay = 0
962
+ res.animation_fun = MiniIndentscope.gen_animation.none()
963
+ return res
964
+ end
965
+
966
+ return res
967
+ end
968
+
969
+ H.make_draw_function = function(indicator, opts)
970
+ local extmark_opts = {
971
+ hl_mode = 'combine',
972
+ priority = opts.priority,
973
+ right_gravity = false,
974
+ virt_text = indicator.virt_text,
975
+ virt_text_win_col = indicator.virt_text_win_col,
976
+ virt_text_pos = 'overlay',
977
+ }
978
+
979
+ if H.has_wrapped_virt_text and vim.wo.breakindent and vim.wo.showbreak == '' then
980
+ extmark_opts.virt_text_repeat_linebreak = true
981
+ end
982
+
983
+ local current_event_id = opts.event_id
984
+
985
+ return function(l)
986
+ -- Don't draw if outdated
987
+ if H.current.event_id ~= current_event_id and current_event_id ~= nil then return false end
988
+
989
+ -- Don't draw if disabled
990
+ if H.is_disabled() then return false end
991
+
992
+ -- Don't put extmark outside of indicator range
993
+ if not (indicator.top <= l and l <= indicator.bottom) then return true end
994
+
995
+ return pcall(vim.api.nvim_buf_set_extmark, indicator.buf_id, H.ns_id, l - 1, 0, extmark_opts)
996
+ end
997
+ end
998
+
999
+ -- Animations -----------------------------------------------------------------
1000
+ --- Imitate common power easing function
1001
+ ---
1002
+ --- Every step is preceded by waiting time decreasing/increasing in power
1003
+ --- series fashion (`d` is "delta", ensures total duration time):
1004
+ --- - "in": d*n^p; d*(n-1)^p; ... ; d*2^p; d*1^p
1005
+ --- - "out": d*1^p; d*2^p; ... ; d*(n-1)^p; d*n^p
1006
+ --- - "in-out": "in" until 0.5*n, "out" afterwards
1007
+ ---
1008
+ --- This way it imitates `power + 1` common easing function because animation
1009
+ --- progression behaves as sum of `power` elements.
1010
+ ---
1011
+ ---@param power number Power of series.
1012
+ ---@param opts table Options from `MiniIndentscope.gen_animation` entry.
1013
+ ---@private
1014
+ H.animation_arithmetic_powers = function(power, opts)
1015
+ -- Sum of first `n_steps` natural numbers raised to `power`
1016
+ local arith_power_sum = ({
1017
+ [0] = function(n_steps) return n_steps end,
1018
+ [1] = function(n_steps) return n_steps * (n_steps + 1) / 2 end,
1019
+ [2] = function(n_steps) return n_steps * (n_steps + 1) * (2 * n_steps + 1) / 6 end,
1020
+ [3] = function(n_steps) return n_steps ^ 2 * (n_steps + 1) ^ 2 / 4 end,
1021
+ })[power]
1022
+
1023
+ -- Function which computes common delta so that overall duration will have
1024
+ -- desired value (based on supplied `opts`)
1025
+ local duration_unit, duration_value = opts.unit, opts.duration
1026
+ local make_delta = function(n_steps, is_in_out)
1027
+ local total_time = duration_unit == 'total' and duration_value or (duration_value * n_steps)
1028
+ local total_parts
1029
+ if is_in_out then
1030
+ -- Examples:
1031
+ -- - n_steps=5: 3^d, 2^d, 1^d, 2^d, 3^d
1032
+ -- - n_steps=6: 3^d, 2^d, 1^d, 1^d, 2^d, 3^d
1033
+ total_parts = 2 * arith_power_sum(math.ceil(0.5 * n_steps)) - (n_steps % 2 == 1 and 1 or 0)
1034
+ else
1035
+ total_parts = arith_power_sum(n_steps)
1036
+ end
1037
+ return total_time / total_parts
1038
+ end
1039
+
1040
+ return ({
1041
+ ['in'] = function(s, n) return make_delta(n) * (n - s + 1) ^ power end,
1042
+ ['out'] = function(s, n) return make_delta(n) * s ^ power end,
1043
+ ['in-out'] = function(s, n)
1044
+ local n_half = math.ceil(0.5 * n)
1045
+ local s_halved
1046
+ if n % 2 == 0 then
1047
+ s_halved = s <= n_half and (n_half - s + 1) or (s - n_half)
1048
+ else
1049
+ s_halved = s < n_half and (n_half - s + 1) or (s - n_half + 1)
1050
+ end
1051
+ return make_delta(n, true) * s_halved ^ power
1052
+ end,
1053
+ })[opts.easing]
1054
+ end
1055
+
1056
+ --- Imitate common exponential easing function
1057
+ ---
1058
+ --- Every step is preceded by waiting time decreasing/increasing in geometric
1059
+ --- progression fashion (`d` is 'delta', ensures total duration time):
1060
+ --- - 'in': (d-1)*d^(n-1); (d-1)*d^(n-2); ...; (d-1)*d^1; (d-1)*d^0
1061
+ --- - 'out': (d-1)*d^0; (d-1)*d^1; ...; (d-1)*d^(n-2); (d-1)*d^(n-1)
1062
+ --- - 'in-out': 'in' until 0.5*n, 'out' afterwards
1063
+ ---
1064
+ ---@param opts table Options from `MiniIndentscope.gen_animation` entry.
1065
+ ---@private
1066
+ H.animation_geometrical_powers = function(opts)
1067
+ -- Function which computes common delta so that overall duration will have
1068
+ -- desired value (based on supplied `opts`)
1069
+ local duration_unit, duration_value = opts.unit, opts.duration
1070
+ local make_delta = function(n_steps, is_in_out)
1071
+ local total_time = duration_unit == 'step' and (duration_value * n_steps) or duration_value
1072
+ -- Exact solution to avoid possible (bad) approximation
1073
+ if n_steps == 1 then return total_time + 1 end
1074
+ if is_in_out then
1075
+ local n_half = math.ceil(0.5 * n_steps)
1076
+ -- Example for n_steps=6:
1077
+ -- Steps: (d-1)*d^2, (d-1)*d^1, (d-1)*d^0, (d-1)*d^0, (d-1)*d^1, (d-1)*d^2
1078
+ -- Sum: 2 * (d - 1) * (d^0 + d^1 + d^2) = 2 * (d^3 - 1)
1079
+ -- Solution: 2 * (d^3 - 1) = total_time =>
1080
+ -- d = math.pow(0.5 * total_time + 1, 1 / 3)
1081
+ --
1082
+ -- Example for n_steps=5:
1083
+ -- Steps: (d-1)*d^2, (d-1)*d^1, (d-1)*d^0, (d-1)*d^1, (d-1)*d^2
1084
+ -- Sum: 2 * (d - 1) * (d^0 + d^1 + d^2) - (d - 1) = 2 * (d^3 - 1) - (d - 1)
1085
+ -- Solution: 2 * (d^3 - 1) - (d - 1) = total_time =>
1086
+ -- As there is no general explicit solution, use approximation =>
1087
+ -- (Exact solution without `- (d-1)`):
1088
+ -- d_0 = math.pow(0.5 * total_time + 1, 1 / 3);
1089
+ -- (Correction by solving exactly withtou `- (d-1)` for
1090
+ -- `total_time_corr = total_time + (d_0 - 1)`):
1091
+ -- d_1 = math.pow(0.5 * total_time_corr + 1, 1 / 3)
1092
+ if n_steps % 2 == 1 then total_time = total_time + math.pow(0.5 * total_time + 1, 1 / n_half) - 1 end
1093
+ return math.pow(0.5 * total_time + 1, 1 / n_half)
1094
+ end
1095
+ return math.pow(total_time + 1, 1 / n_steps)
1096
+ end
1097
+
1098
+ return ({
1099
+ ['in'] = function(s, n)
1100
+ local delta = make_delta(n)
1101
+ return (delta - 1) * delta ^ (n - s)
1102
+ end,
1103
+ ['out'] = function(s, n)
1104
+ local delta = make_delta(n)
1105
+ return (delta - 1) * delta ^ (s - 1)
1106
+ end,
1107
+ ['in-out'] = function(s, n)
1108
+ local n_half, delta = math.ceil(0.5 * n), make_delta(n, true)
1109
+ local s_halved
1110
+ if n % 2 == 0 then
1111
+ s_halved = s <= n_half and (n_half - s) or (s - n_half - 1)
1112
+ else
1113
+ s_halved = s < n_half and (n_half - s) or (s - n_half)
1114
+ end
1115
+ return (delta - 1) * delta ^ s_halved
1116
+ end,
1117
+ })[opts.easing]
1118
+ end
1119
+
1120
+ H.normalize_animation_opts = function(x)
1121
+ x = vim.tbl_deep_extend('force', { easing = 'in-out', duration = 20, unit = 'step' }, x or {})
1122
+
1123
+ if not vim.tbl_contains({ 'in', 'out', 'in-out' }, x.easing) then
1124
+ H.error([[In `gen_animation` option `easing` should be one of 'in', 'out', or 'in-out'.]])
1125
+ end
1126
+
1127
+ if type(x.duration) ~= 'number' or x.duration < 0 then
1128
+ H.error([[In `gen_animation` option `duration` should be a positive number.]])
1129
+ end
1130
+
1131
+ if not vim.tbl_contains({ 'total', 'step' }, x.unit) then
1132
+ H.error([[In `gen_animation` option `unit` should be one of 'step' or 'total'.]])
1133
+ end
1134
+
1135
+ return x
1136
+ end
1137
+
1138
+ -- Utilities ------------------------------------------------------------------
1139
+ H.error = function(msg) error('(mini.indentscope) ' .. msg, 0) end
1140
+
1141
+ H.check_type = function(name, val, ref, allow_nil)
1142
+ if type(val) == ref or (ref == 'callable' and vim.is_callable(val)) or (allow_nil and val == nil) then return end
1143
+ H.error(string.format('`%s` should be %s, not %s', name, ref, type(val)))
1144
+ end
1145
+
1146
+ H.map = function(mode, lhs, rhs, opts)
1147
+ if lhs == '' then return end
1148
+ opts = vim.tbl_deep_extend('force', { silent = true }, opts or {})
1149
+ vim.keymap.set(mode, lhs, rhs, opts)
1150
+ end
1151
+
1152
+ H.exit_visual_mode = function()
1153
+ local ctrl_v = vim.api.nvim_replace_termcodes('<C-v>', true, true, true)
1154
+ local cur_mode = vim.fn.mode()
1155
+ if cur_mode == 'v' or cur_mode == 'V' or cur_mode == ctrl_v then vim.cmd('normal! ' .. cur_mode) end
1156
+ end
1157
+
1158
+ return MiniIndentscope