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.
- package/.conda/retrovim.yaml +259 -0
- package/.conda/terax.icns +0 -0
- package/.conda/terax.ico +0 -0
- package/.conda/terax.png +0 -0
- package/.github/workflows/release.yml +95 -0
- package/.gitmodules +3 -0
- package/.npm/kanata_touchcursor.js +28 -0
- package/.npm/nvim.js +26 -0
- package/.npm/yazi.js +26 -0
- package/.npm/zsh.js +17 -0
- package/.pip/kanata_touchcursor/__init__.py +28 -0
- package/.pip/nvim/__init__.py +31 -0
- package/.pip/yazi/__init__.py +31 -0
- package/.pip/zsh/__init__.py +22 -0
- package/CHANGELOG.md +26 -0
- package/LICENSE +674 -0
- package/README.md +336 -0
- package/kanata/touchcursor.kbd +164 -0
- package/nvim/init.lua +813 -0
- package/nvim/plugins/mini.nvim/.github/DISCUSSION_TEMPLATE/q-a.yml +72 -0
- package/nvim/plugins/mini.nvim/.github/ISSUE_TEMPLATE/bug-report.yml +136 -0
- package/nvim/plugins/mini.nvim/.github/ISSUE_TEMPLATE/config.yml +5 -0
- package/nvim/plugins/mini.nvim/.github/ISSUE_TEMPLATE/feature-request.yml +75 -0
- package/nvim/plugins/mini.nvim/.github/PULL_REQUEST_TEMPLATE.md +2 -0
- package/nvim/plugins/mini.nvim/.github/workflows/quality-control.yml +150 -0
- package/nvim/plugins/mini.nvim/.pre-commit-config.yaml +20 -0
- package/nvim/plugins/mini.nvim/.stylua.toml +7 -0
- package/nvim/plugins/mini.nvim/.styluaignore +1 -0
- package/nvim/plugins/mini.nvim/CHANGELOG.md +1894 -0
- package/nvim/plugins/mini.nvim/CODE_OF_CONDUCT.md +132 -0
- package/nvim/plugins/mini.nvim/CONTRIBUTING.md +331 -0
- package/nvim/plugins/mini.nvim/LICENSE +21 -0
- package/nvim/plugins/mini.nvim/MAINTAINING.md +330 -0
- package/nvim/plugins/mini.nvim/Makefile +51 -0
- package/nvim/plugins/mini.nvim/README.md +261 -0
- package/nvim/plugins/mini.nvim/TESTING.md +1011 -0
- package/nvim/plugins/mini.nvim/benchmarks/starter/README.md +35 -0
- package/nvim/plugins/mini.nvim/benchmarks/starter/benchmark.sh +56 -0
- package/nvim/plugins/mini.nvim/benchmarks/starter/init-files/init_dashboard-alpha.lua +21 -0
- package/nvim/plugins/mini.nvim/benchmarks/starter/init-files/init_dashboard-original.lua +17 -0
- package/nvim/plugins/mini.nvim/benchmarks/starter/init-files/init_dashboard-starter.lua +18 -0
- package/nvim/plugins/mini.nvim/benchmarks/starter/init-files/init_empty.lua +4 -0
- package/nvim/plugins/mini.nvim/benchmarks/starter/init-files/init_starter-default.lua +7 -0
- package/nvim/plugins/mini.nvim/benchmarks/starter/init-files/init_startify-alpha.lua +10 -0
- package/nvim/plugins/mini.nvim/benchmarks/starter/init-files/init_startify-original.lua +7 -0
- package/nvim/plugins/mini.nvim/benchmarks/starter/init-files/init_startify-starter.lua +20 -0
- package/nvim/plugins/mini.nvim/benchmarks/starter/install.sh +10 -0
- package/nvim/plugins/mini.nvim/benchmarks/starter/make_summary.py +68 -0
- package/nvim/plugins/mini.nvim/benchmarks/starter/startup-summary.md +10 -0
- package/nvim/plugins/mini.nvim/colors/miniautumn.lua +55 -0
- package/nvim/plugins/mini.nvim/colors/minicyan.lua +95 -0
- package/nvim/plugins/mini.nvim/colors/minischeme.lua +95 -0
- package/nvim/plugins/mini.nvim/colors/minispring.lua +53 -0
- package/nvim/plugins/mini.nvim/colors/minisummer.lua +49 -0
- package/nvim/plugins/mini.nvim/colors/miniwinter.lua +51 -0
- package/nvim/plugins/mini.nvim/colors/randomhue.lua +16 -0
- package/nvim/plugins/mini.nvim/doc/mini-ai.txt +889 -0
- package/nvim/plugins/mini.nvim/doc/mini-align.txt +923 -0
- package/nvim/plugins/mini.nvim/doc/mini-animate.txt +937 -0
- package/nvim/plugins/mini.nvim/doc/mini-base16.txt +285 -0
- package/nvim/plugins/mini.nvim/doc/mini-basics.txt +359 -0
- package/nvim/plugins/mini.nvim/doc/mini-bracketed.txt +622 -0
- package/nvim/plugins/mini.nvim/doc/mini-bufremove.txt +123 -0
- package/nvim/plugins/mini.nvim/doc/mini-clue.txt +760 -0
- package/nvim/plugins/mini.nvim/doc/mini-cmdline.txt +392 -0
- package/nvim/plugins/mini.nvim/doc/mini-colors.txt +880 -0
- package/nvim/plugins/mini.nvim/doc/mini-comment.txt +230 -0
- package/nvim/plugins/mini.nvim/doc/mini-completion.txt +477 -0
- package/nvim/plugins/mini.nvim/doc/mini-cursorword.txt +105 -0
- package/nvim/plugins/mini.nvim/doc/mini-deps.txt +590 -0
- package/nvim/plugins/mini.nvim/doc/mini-diff.txt +661 -0
- package/nvim/plugins/mini.nvim/doc/mini-doc.txt +434 -0
- package/nvim/plugins/mini.nvim/doc/mini-extra.txt +856 -0
- package/nvim/plugins/mini.nvim/doc/mini-files.txt +1018 -0
- package/nvim/plugins/mini.nvim/doc/mini-fuzzy.txt +160 -0
- package/nvim/plugins/mini.nvim/doc/mini-git.txt +425 -0
- package/nvim/plugins/mini.nvim/doc/mini-hipatterns.txt +489 -0
- package/nvim/plugins/mini.nvim/doc/mini-hues.txt +420 -0
- package/nvim/plugins/mini.nvim/doc/mini-icons.txt +508 -0
- package/nvim/plugins/mini.nvim/doc/mini-indentscope.txt +485 -0
- package/nvim/plugins/mini.nvim/doc/mini-input.txt +932 -0
- package/nvim/plugins/mini.nvim/doc/mini-jump.txt +202 -0
- package/nvim/plugins/mini.nvim/doc/mini-jump2d.txt +525 -0
- package/nvim/plugins/mini.nvim/doc/mini-keymap.txt +449 -0
- package/nvim/plugins/mini.nvim/doc/mini-map.txt +717 -0
- package/nvim/plugins/mini.nvim/doc/mini-misc.txt +431 -0
- package/nvim/plugins/mini.nvim/doc/mini-move.txt +181 -0
- package/nvim/plugins/mini.nvim/doc/mini-notify.txt +434 -0
- package/nvim/plugins/mini.nvim/doc/mini-nvim.txt +431 -0
- package/nvim/plugins/mini.nvim/doc/mini-operators.txt +472 -0
- package/nvim/plugins/mini.nvim/doc/mini-pairs.txt +346 -0
- package/nvim/plugins/mini.nvim/doc/mini-pick.txt +1535 -0
- package/nvim/plugins/mini.nvim/doc/mini-sessions.txt +239 -0
- package/nvim/plugins/mini.nvim/doc/mini-snippets.txt +1291 -0
- package/nvim/plugins/mini.nvim/doc/mini-splitjoin.txt +536 -0
- package/nvim/plugins/mini.nvim/doc/mini-starter.txt +618 -0
- package/nvim/plugins/mini.nvim/doc/mini-statusline.txt +361 -0
- package/nvim/plugins/mini.nvim/doc/mini-surround.txt +863 -0
- package/nvim/plugins/mini.nvim/doc/mini-tabline.txt +151 -0
- package/nvim/plugins/mini.nvim/doc/mini-test.txt +966 -0
- package/nvim/plugins/mini.nvim/doc/mini-trailspace.txt +91 -0
- package/nvim/plugins/mini.nvim/doc/mini-visits.txt +937 -0
- package/nvim/plugins/mini.nvim/logo.png +0 -0
- package/nvim/plugins/mini.nvim/lua/mini/ai.lua +2244 -0
- package/nvim/plugins/mini.nvim/lua/mini/align.lua +2076 -0
- package/nvim/plugins/mini.nvim/lua/mini/animate.lua +2127 -0
- package/nvim/plugins/mini.nvim/lua/mini/base16.lua +1752 -0
- package/nvim/plugins/mini.nvim/lua/mini/basics.lua +772 -0
- package/nvim/plugins/mini.nvim/lua/mini/bracketed.lua +2013 -0
- package/nvim/plugins/mini.nvim/lua/mini/bufremove.lua +283 -0
- package/nvim/plugins/mini.nvim/lua/mini/clue.lua +2096 -0
- package/nvim/plugins/mini.nvim/lua/mini/cmdline.lua +1182 -0
- package/nvim/plugins/mini.nvim/lua/mini/colors.lua +2483 -0
- package/nvim/plugins/mini.nvim/lua/mini/comment.lua +586 -0
- package/nvim/plugins/mini.nvim/lua/mini/completion.lua +2094 -0
- package/nvim/plugins/mini.nvim/lua/mini/cursorword.lua +311 -0
- package/nvim/plugins/mini.nvim/lua/mini/deps.lua +1660 -0
- package/nvim/plugins/mini.nvim/lua/mini/diff.lua +1938 -0
- package/nvim/plugins/mini.nvim/lua/mini/doc.lua +1371 -0
- package/nvim/plugins/mini.nvim/lua/mini/extra.lua +2290 -0
- package/nvim/plugins/mini.nvim/lua/mini/files.lua +3145 -0
- package/nvim/plugins/mini.nvim/lua/mini/fuzzy.lua +356 -0
- package/nvim/plugins/mini.nvim/lua/mini/git.lua +1737 -0
- package/nvim/plugins/mini.nvim/lua/mini/hipatterns.lua +1042 -0
- package/nvim/plugins/mini.nvim/lua/mini/hues.lua +2088 -0
- package/nvim/plugins/mini.nvim/lua/mini/icons.lua +2254 -0
- package/nvim/plugins/mini.nvim/lua/mini/indentscope.lua +1158 -0
- package/nvim/plugins/mini.nvim/lua/mini/init.lua +427 -0
- package/nvim/plugins/mini.nvim/lua/mini/input.lua +2183 -0
- package/nvim/plugins/mini.nvim/lua/mini/jump.lua +609 -0
- package/nvim/plugins/mini.nvim/lua/mini/jump2d.lua +1244 -0
- package/nvim/plugins/mini.nvim/lua/mini/keymap.lua +880 -0
- package/nvim/plugins/mini.nvim/lua/mini/map.lua +1709 -0
- package/nvim/plugins/mini.nvim/lua/mini/misc.lua +969 -0
- package/nvim/plugins/mini.nvim/lua/mini/move.lua +509 -0
- package/nvim/plugins/mini.nvim/lua/mini/notify.lua +938 -0
- package/nvim/plugins/mini.nvim/lua/mini/operators.lua +1349 -0
- package/nvim/plugins/mini.nvim/lua/mini/pairs.lua +657 -0
- package/nvim/plugins/mini.nvim/lua/mini/pick.lua +3876 -0
- package/nvim/plugins/mini.nvim/lua/mini/sessions.lua +689 -0
- package/nvim/plugins/mini.nvim/lua/mini/snippets.lua +2828 -0
- package/nvim/plugins/mini.nvim/lua/mini/splitjoin.lua +1132 -0
- package/nvim/plugins/mini.nvim/lua/mini/starter.lua +1599 -0
- package/nvim/plugins/mini.nvim/lua/mini/statusline.lua +686 -0
- package/nvim/plugins/mini.nvim/lua/mini/surround.lua +2388 -0
- package/nvim/plugins/mini.nvim/lua/mini/tabline.lua +564 -0
- package/nvim/plugins/mini.nvim/lua/mini/test.lua +2516 -0
- package/nvim/plugins/mini.nvim/lua/mini/trailspace.lua +215 -0
- package/nvim/plugins/mini.nvim/lua/mini/visits.lua +1577 -0
- package/nvim/plugins/mini.nvim/readmes/mini-ai.md +190 -0
- package/nvim/plugins/mini.nvim/readmes/mini-align.md +203 -0
- package/nvim/plugins/mini.nvim/readmes/mini-animate.md +214 -0
- package/nvim/plugins/mini.nvim/readmes/mini-base16.md +198 -0
- package/nvim/plugins/mini.nvim/readmes/mini-basics.md +184 -0
- package/nvim/plugins/mini.nvim/readmes/mini-bracketed.md +193 -0
- package/nvim/plugins/mini.nvim/readmes/mini-bufremove.md +139 -0
- package/nvim/plugins/mini.nvim/readmes/mini-clue.md +260 -0
- package/nvim/plugins/mini.nvim/readmes/mini-cmdline.md +183 -0
- package/nvim/plugins/mini.nvim/readmes/mini-colors.md +169 -0
- package/nvim/plugins/mini.nvim/readmes/mini-comment.md +183 -0
- package/nvim/plugins/mini.nvim/readmes/mini-completion.md +217 -0
- package/nvim/plugins/mini.nvim/readmes/mini-cursorword.md +137 -0
- package/nvim/plugins/mini.nvim/readmes/mini-deps.md +271 -0
- package/nvim/plugins/mini.nvim/readmes/mini-diff.md +289 -0
- package/nvim/plugins/mini.nvim/readmes/mini-doc.md +198 -0
- package/nvim/plugins/mini.nvim/readmes/mini-extra.md +158 -0
- package/nvim/plugins/mini.nvim/readmes/mini-files.md +247 -0
- package/nvim/plugins/mini.nvim/readmes/mini-fuzzy.md +134 -0
- package/nvim/plugins/mini.nvim/readmes/mini-git.md +168 -0
- package/nvim/plugins/mini.nvim/readmes/mini-hipatterns.md +176 -0
- package/nvim/plugins/mini.nvim/readmes/mini-hues.md +285 -0
- package/nvim/plugins/mini.nvim/readmes/mini-icons.md +183 -0
- package/nvim/plugins/mini.nvim/readmes/mini-indentscope.md +191 -0
- package/nvim/plugins/mini.nvim/readmes/mini-input.md +163 -0
- package/nvim/plugins/mini.nvim/readmes/mini-jump.md +161 -0
- package/nvim/plugins/mini.nvim/readmes/mini-jump2d.md +197 -0
- package/nvim/plugins/mini.nvim/readmes/mini-keymap.md +205 -0
- package/nvim/plugins/mini.nvim/readmes/mini-map.md +191 -0
- package/nvim/plugins/mini.nvim/readmes/mini-misc.md +145 -0
- package/nvim/plugins/mini.nvim/readmes/mini-move.md +163 -0
- package/nvim/plugins/mini.nvim/readmes/mini-notify.md +175 -0
- package/nvim/plugins/mini.nvim/readmes/mini-operators.md +193 -0
- package/nvim/plugins/mini.nvim/readmes/mini-pairs.md +158 -0
- package/nvim/plugins/mini.nvim/readmes/mini-pick.md +340 -0
- package/nvim/plugins/mini.nvim/readmes/mini-sessions.md +164 -0
- package/nvim/plugins/mini.nvim/readmes/mini-snippets.md +373 -0
- package/nvim/plugins/mini.nvim/readmes/mini-splitjoin.md +194 -0
- package/nvim/plugins/mini.nvim/readmes/mini-starter.md +176 -0
- package/nvim/plugins/mini.nvim/readmes/mini-statusline.md +155 -0
- package/nvim/plugins/mini.nvim/readmes/mini-surround.md +190 -0
- package/nvim/plugins/mini.nvim/readmes/mini-tabline.md +156 -0
- package/nvim/plugins/mini.nvim/readmes/mini-test.md +175 -0
- package/nvim/plugins/mini.nvim/readmes/mini-trailspace.md +137 -0
- package/nvim/plugins/mini.nvim/readmes/mini-visits.md +243 -0
- package/nvim/plugins/mini.nvim/scripts/dual_log.sh +13 -0
- package/nvim/plugins/mini.nvim/scripts/dual_push.sh +15 -0
- package/nvim/plugins/mini.nvim/scripts/dual_release.sh +102 -0
- package/nvim/plugins/mini.nvim/scripts/dual_sync.sh +115 -0
- package/nvim/plugins/mini.nvim/scripts/init-deps-example.lua +59 -0
- package/nvim/plugins/mini.nvim/scripts/lint-filename-length.sh +16 -0
- package/nvim/plugins/mini.nvim/scripts/lintcommit-ci.sh +18 -0
- package/nvim/plugins/mini.nvim/scripts/lintcommit.lua +393 -0
- package/nvim/plugins/mini.nvim/scripts/minidoc.lua +57 -0
- package/nvim/plugins/mini.nvim/scripts/minimal_init.lua +28 -0
- package/nvim/plugins/mini.nvim/scripts/minitest.lua +4 -0
- package/package.json +16 -0
- package/pyproject.toml +43 -0
- package/yazi/init.lua +12 -0
- package/yazi/keymap.toml +114 -0
- package/yazi/lesskey +5 -0
- package/yazi/plugins/bookmarks.yazi/main.lua +86 -0
- package/yazi/plugins/cd-git-root.yazi/main.lua +31 -0
- package/yazi/plugins/smart-enter.yazi/main.lua +11 -0
- package/yazi/theme.toml +26 -0
- package/yazi/yazi.toml +15 -0
- package/zsh/.zshrc +145 -0
- package/zsh/starship.toml +20 -0
- 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
|