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,2096 @@
|
|
|
1
|
+
--- *mini.clue* Show next key clues
|
|
2
|
+
---
|
|
3
|
+
--- MIT License Copyright (c) 2023 Evgeni Chasnovski
|
|
4
|
+
|
|
5
|
+
--- Features:
|
|
6
|
+
--- - Implement custom key query process to reach target key combination:
|
|
7
|
+
--- - Starts after customizable opt-in triggers (mode + keys).
|
|
8
|
+
---
|
|
9
|
+
--- - Each key press narrows down set of possible targets.
|
|
10
|
+
--- Pressing `<BS>` removes previous user entry.
|
|
11
|
+
--- Pressing `<Esc>` or `<C-c>` leads to an early stop.
|
|
12
|
+
--- Doesn't depend on |'timeoutlen'| and has basic support for |'langmap'|.
|
|
13
|
+
---
|
|
14
|
+
--- - Ends when there is at most one target left or user pressed `<CR>`.
|
|
15
|
+
--- Results into emulating pressing all query keys plus possible postkeys.
|
|
16
|
+
---
|
|
17
|
+
--- - Show window (after configurable delay) with clues. It lists available
|
|
18
|
+
--- next keys along with their descriptions (auto generated from descriptions
|
|
19
|
+
--- present keymaps and user-supplied clues; preferring the former).
|
|
20
|
+
---
|
|
21
|
+
--- - Configurable "postkeys" for key combinations - keys which will be emulated
|
|
22
|
+
--- after combination is reached during key query process.
|
|
23
|
+
---
|
|
24
|
+
--- - Provide customizable sets of clues for common built-in keys/concepts:
|
|
25
|
+
--- - `g` key.
|
|
26
|
+
--- - `z` key.
|
|
27
|
+
--- - Window commands.
|
|
28
|
+
--- - Built-in completion.
|
|
29
|
+
--- - Marks.
|
|
30
|
+
--- - Registers.
|
|
31
|
+
---
|
|
32
|
+
--- - Lua functions to disable/enable triggers globally or per buffer.
|
|
33
|
+
---
|
|
34
|
+
--- For more details see:
|
|
35
|
+
--- - |MiniClue-key-query-process|.
|
|
36
|
+
--- - |MiniClue-examples|.
|
|
37
|
+
--- - |MiniClue.config|.
|
|
38
|
+
--- - |MiniClue.gen_clues|.
|
|
39
|
+
---
|
|
40
|
+
--- Notes:
|
|
41
|
+
--- - There is no functionality to create mappings while defining clues.
|
|
42
|
+
--- This is done to clearly separate these two different actions.
|
|
43
|
+
--- The best suggested practice is to manually create mappings with
|
|
44
|
+
--- descriptions (`desc` field in options), as they will be automatically
|
|
45
|
+
--- used inside clue window.
|
|
46
|
+
---
|
|
47
|
+
--- - Triggers are implemented as special buffer-local mappings. This leads to
|
|
48
|
+
--- several caveats:
|
|
49
|
+
--- - They will override same regular buffer-local mappings and have
|
|
50
|
+
--- precedence over global one.
|
|
51
|
+
---
|
|
52
|
+
--- Example: having set `<C-w>` as Normal mode trigger means that
|
|
53
|
+
--- there should not be another `<C-w>` mapping.
|
|
54
|
+
---
|
|
55
|
+
--- - They need to be the latest created buffer-local mappings or they will
|
|
56
|
+
--- not function properly. Most common indicator of this is that some
|
|
57
|
+
--- mapping starts to work only after clue window is shown.
|
|
58
|
+
---
|
|
59
|
+
--- Example: `g` is set as Normal mode trigger, but `gcc` from |mini.comment|
|
|
60
|
+
--- doesn't work right away. This is probably because there are some
|
|
61
|
+
--- other buffer-local mappings starting with `g` which were created after
|
|
62
|
+
--- mapping for `g` trigger. Most common places for this are in LSP server's
|
|
63
|
+
--- `on_attach` or during tree-sitter start in buffer.
|
|
64
|
+
---
|
|
65
|
+
--- To check if trigger is the most recent buffer-local mapping, execute
|
|
66
|
+
--- `:<mode-char>map <trigger-keys>` (like `:nmap g` for previous example).
|
|
67
|
+
--- Mapping for trigger should be the first listed.
|
|
68
|
+
---
|
|
69
|
+
--- This module makes the best effort to work out of the box and cover
|
|
70
|
+
--- most common cases, but it is not foolproof. The solution here is to
|
|
71
|
+
--- ensure that triggers are created after making all buffer-local mappings:
|
|
72
|
+
--- run either |MiniClue.setup()| or |MiniClue.ensure_buf_triggers()|.
|
|
73
|
+
---
|
|
74
|
+
--- - Descriptions from existing mappings take precedence over user-supplied
|
|
75
|
+
--- clues. This is to ensure that information shown in clue window is as
|
|
76
|
+
--- relevant as possible. To add/customize description of an already existing
|
|
77
|
+
--- mapping, use |MiniClue.set_mapping_desc()|.
|
|
78
|
+
---
|
|
79
|
+
--- - Due to technical difficulties, there is no foolproof support for
|
|
80
|
+
--- Operator-pending mode triggers (like `a`/`i` from |mini.ai|):
|
|
81
|
+
--- - Doesn't work as part of a command in "temporary Normal mode" (like
|
|
82
|
+
--- after |i_CTRL-O|) due to implementation difficulties.
|
|
83
|
+
--- - Can have unexpected behavior with custom operators.
|
|
84
|
+
---
|
|
85
|
+
--- - Has (mostly solved) issues with macros:
|
|
86
|
+
--- - All triggers are disabled during macro recording due to technical
|
|
87
|
+
--- reasons.
|
|
88
|
+
--- - The `@` and `Q` keys are specially mapped inside |MiniClue.setup()|
|
|
89
|
+
--- (if the key is not already mapped) to temporarily disable triggers.
|
|
90
|
+
---
|
|
91
|
+
--- # Setup ~
|
|
92
|
+
---
|
|
93
|
+
--- This module needs a setup with `require('mini.clue').setup({})` (replace
|
|
94
|
+
--- `{}` with your `config` table). It will create global Lua table `MiniClue`
|
|
95
|
+
--- which you can use for scripting or manually (with `:lua MiniClue.*`).
|
|
96
|
+
---
|
|
97
|
+
--- Config table NEEDS TO HAVE TRIGGERS CONFIGURED, none is set up by default.
|
|
98
|
+
---
|
|
99
|
+
--- See |MiniClue.config| for available config settings.
|
|
100
|
+
---
|
|
101
|
+
--- You can override runtime config settings (like clues or window options)
|
|
102
|
+
--- locally to a buffer inside `vim.b.miniclue_config` which should have same
|
|
103
|
+
--- structure as `MiniClue.config`. See |mini.nvim-buffer-local-config| for
|
|
104
|
+
--- more details.
|
|
105
|
+
---
|
|
106
|
+
--- # Comparisons ~
|
|
107
|
+
---
|
|
108
|
+
--- - [folke/which-key.nvim](https://github.com/folke/which-key.nvim):
|
|
109
|
+
--- - Both have the same main goal: show available next keys along with
|
|
110
|
+
--- their customizable descriptions.
|
|
111
|
+
--- - Has different UI and content layout.
|
|
112
|
+
--- - Allows creating mappings inside its configuration, while this module
|
|
113
|
+
--- doesn't have this by design (to clearly separate two different tasks).
|
|
114
|
+
--- - Doesn't allow creating submodes, while this module does (via `postkeys`).
|
|
115
|
+
---
|
|
116
|
+
--- - [anuvyklack/hydra.nvim](https://github.com/anuvyklack/hydra.nvim):
|
|
117
|
+
--- - Both allow creating submodes: state which starts at certain key
|
|
118
|
+
--- combination; treats some keys differently; ends after `<Esc>`.
|
|
119
|
+
--- - Doesn't show information about available next keys (outside of
|
|
120
|
+
--- submodes), while that is this module's main goal.
|
|
121
|
+
---
|
|
122
|
+
--- # Highlight groups ~
|
|
123
|
+
--- *MiniClue-hl-groups*
|
|
124
|
+
---
|
|
125
|
+
--- - `MiniClueBorder` - window border.
|
|
126
|
+
--- - `MiniClueDescGroup` - group description in clue window.
|
|
127
|
+
--- - `MiniClueDescSingle` - single target description in clue window.
|
|
128
|
+
--- - `MiniClueNextKey` - next key label in clue window.
|
|
129
|
+
--- - `MiniClueNextKeyWithPostkeys` - next key label with postkeys in clue window.
|
|
130
|
+
--- - `MiniClueSeparator` - separator in clue window.
|
|
131
|
+
--- - `MiniClueTitle` - window title.
|
|
132
|
+
---
|
|
133
|
+
--- To change any highlight group, set it directly with |nvim_set_hl()|.
|
|
134
|
+
---
|
|
135
|
+
--- # Disabling ~
|
|
136
|
+
---
|
|
137
|
+
--- To disable creating triggers, set `vim.g.miniclue_disable` (globally) or
|
|
138
|
+
--- `vim.b.miniclue_disable` (for a buffer) to `true`. Considering high number
|
|
139
|
+
--- of different scenarios and customization intentions, writing exact rules
|
|
140
|
+
--- for disabling module's functionality is left to user. See
|
|
141
|
+
--- |mini.nvim-disabling-recipes| for common recipes.
|
|
142
|
+
---@tag MiniClue
|
|
143
|
+
|
|
144
|
+
--- # General info ~
|
|
145
|
+
---
|
|
146
|
+
--- This module implements custom key query process imitating a usual built-in
|
|
147
|
+
--- mechanism of user pressing keys in order to execute a mapping. General idea
|
|
148
|
+
--- is the same: narrow down key combinations until the target is reached.
|
|
149
|
+
---
|
|
150
|
+
--- Main goals of its existence are:
|
|
151
|
+
---
|
|
152
|
+
--- - Allow reaching certain mappings be independent of |'timeoutlen'|. That is,
|
|
153
|
+
--- there is no fixed timeout after which currently typed keys are executed.
|
|
154
|
+
---
|
|
155
|
+
--- - Enable automated showing of next key clues after user-supplied delay
|
|
156
|
+
--- (also independent of |'timeoutlen'|).
|
|
157
|
+
---
|
|
158
|
+
--- - Allow emulating configurable key presses after certain key combination is
|
|
159
|
+
--- reached. This granular control allows creating so called "submodes".
|
|
160
|
+
--- See more at |MiniClue-examples-submodes|.
|
|
161
|
+
---
|
|
162
|
+
--- This process is primarily designed for nested `<Leader>` mappings in Normal
|
|
163
|
+
--- mode but works in all other main modes: Visual, Insert, Operator-pending
|
|
164
|
+
--- (with caveats; no foolproof guarantees), Command-line, Terminal.
|
|
165
|
+
---
|
|
166
|
+
--- # Lifecycle ~
|
|
167
|
+
---
|
|
168
|
+
--- - Key query process starts when user types a trigger: certain keys in certain
|
|
169
|
+
--- mode. Those keys are put into key query as a single user input. All possible
|
|
170
|
+
--- mode key combinations are filtered to ones starting with the trigger keys.
|
|
171
|
+
---
|
|
172
|
+
--- Note: trigger is implemented as a regular mapping, so if it has at least
|
|
173
|
+
--- two keys, they should be pressed within |'timeoutlen'| milliseconds.
|
|
174
|
+
---
|
|
175
|
+
--- - Wait (indefinitely) for user to press a key. Advance depending on the key:
|
|
176
|
+
---
|
|
177
|
+
--- - Special key:
|
|
178
|
+
---
|
|
179
|
+
--- - If `<Esc>` or `<C-c>`, stop the process without any action.
|
|
180
|
+
---
|
|
181
|
+
--- - If `<CR>`, stop the process and execute current key query, meaning
|
|
182
|
+
--- emulate (with |nvim_feedkeys()|) user pressing those keys.
|
|
183
|
+
---
|
|
184
|
+
--- - If `<BS>`, remove previous user input from the query. If query becomes
|
|
185
|
+
--- empty, stop the process without any action.
|
|
186
|
+
---
|
|
187
|
+
--- - If a key for scrolling clue window (`scroll_down` / `scroll_up`
|
|
188
|
+
--- in `config.window`; `<C-d>` / `<C-u>` by default), scroll clue window
|
|
189
|
+
--- and wait for the next user key.
|
|
190
|
+
--- Note: if clue window is not shown, treated as a not special key.
|
|
191
|
+
---
|
|
192
|
+
--- - Not special key. Add key to the query while filtering all available
|
|
193
|
+
--- key combinations to start with the current key query. Advance:
|
|
194
|
+
---
|
|
195
|
+
--- - If there is a single available key combination matching current
|
|
196
|
+
--- key query, execute it.
|
|
197
|
+
---
|
|
198
|
+
--- - If there is no key combinations starting with the current query,
|
|
199
|
+
--- execute it. This, for instance, allows a seamless execution of
|
|
200
|
+
--- operators in presence of a longer key combinations. Example: with
|
|
201
|
+
--- `g` as trigger in Normal mode and available mappings `gc` / `gcc`
|
|
202
|
+
--- (like from |mini.comment|), this allows typing `gcip` to comment
|
|
203
|
+
--- current paragraph, although there are no key combinations
|
|
204
|
+
--- starting with `gci`.
|
|
205
|
+
---
|
|
206
|
+
--- - Otherwise wait for the new user key press.
|
|
207
|
+
---
|
|
208
|
+
--- # Clue window ~
|
|
209
|
+
---
|
|
210
|
+
--- After initiating key query process and after each key press, a timer is
|
|
211
|
+
--- started to show a clue window: floating window with information about
|
|
212
|
+
--- available next keys along with their descriptions. Note: if window is
|
|
213
|
+
--- already shown, its content is updated right away.
|
|
214
|
+
---
|
|
215
|
+
--- Clues can have these types:
|
|
216
|
+
---
|
|
217
|
+
--- - "Terminal next key": when pressed, will lead to query execution.
|
|
218
|
+
---
|
|
219
|
+
--- - "Terminal next key with postkeys": when pressed, will lead to query
|
|
220
|
+
--- execution plus some configured postkeys.
|
|
221
|
+
---
|
|
222
|
+
--- - "Group next key": when pressed, will narrow down available key combinations
|
|
223
|
+
--- and wait for another key press. Note: can have configured description
|
|
224
|
+
--- (inside `config.clues`) or it will be auto generated based on the number of
|
|
225
|
+
--- available key combinations.
|
|
226
|
+
---@tag MiniClue-key-query-process
|
|
227
|
+
|
|
228
|
+
--- # Full starter example ~
|
|
229
|
+
---
|
|
230
|
+
--- If not sure where to start, try this example with all provided clues from
|
|
231
|
+
--- this module plus all |<Leader>| mappings in Normal and Visual modes: >lua
|
|
232
|
+
---
|
|
233
|
+
--- local miniclue = require('mini.clue')
|
|
234
|
+
--- miniclue.setup({
|
|
235
|
+
--- triggers = {
|
|
236
|
+
--- -- Leader triggers
|
|
237
|
+
--- { mode = { 'n', 'x' }, keys = '<Leader>' },
|
|
238
|
+
---
|
|
239
|
+
--- -- `[` and `]` keys
|
|
240
|
+
--- { mode = 'n', keys = '[' },
|
|
241
|
+
--- { mode = 'n', keys = ']' },
|
|
242
|
+
---
|
|
243
|
+
--- -- Built-in completion
|
|
244
|
+
--- { mode = 'i', keys = '<C-x>' },
|
|
245
|
+
---
|
|
246
|
+
--- -- `g` key
|
|
247
|
+
--- { mode = { 'n', 'x' }, keys = 'g' },
|
|
248
|
+
---
|
|
249
|
+
--- -- Marks
|
|
250
|
+
--- { mode = { 'n', 'x' }, keys = "'" },
|
|
251
|
+
--- { mode = { 'n', 'x' }, keys = '`' },
|
|
252
|
+
---
|
|
253
|
+
--- -- Registers
|
|
254
|
+
--- { mode = { 'n', 'x' }, keys = '"' },
|
|
255
|
+
--- { mode = { 'i', 'c' }, keys = '<C-r>' },
|
|
256
|
+
---
|
|
257
|
+
--- -- Window commands
|
|
258
|
+
--- { mode = 'n', keys = '<C-w>' },
|
|
259
|
+
---
|
|
260
|
+
--- -- `z` key
|
|
261
|
+
--- { mode = { 'n', 'x' }, keys = 'z' },
|
|
262
|
+
--- },
|
|
263
|
+
---
|
|
264
|
+
--- clues = {
|
|
265
|
+
--- -- Enhance this by adding descriptions for <Leader> mapping groups
|
|
266
|
+
--- miniclue.gen_clues.square_brackets(),
|
|
267
|
+
--- miniclue.gen_clues.builtin_completion(),
|
|
268
|
+
--- miniclue.gen_clues.g(),
|
|
269
|
+
--- miniclue.gen_clues.marks(),
|
|
270
|
+
--- miniclue.gen_clues.registers(),
|
|
271
|
+
--- miniclue.gen_clues.windows(),
|
|
272
|
+
--- miniclue.gen_clues.z(),
|
|
273
|
+
--- },
|
|
274
|
+
--- })
|
|
275
|
+
--- <
|
|
276
|
+
--- # Leader clues ~
|
|
277
|
+
---
|
|
278
|
+
--- Assume there are these |<Leader>| mappings set up: >lua
|
|
279
|
+
---
|
|
280
|
+
--- -- Set `<Leader>` before making any mappings and configuring 'mini.clue'
|
|
281
|
+
--- vim.g.mapleader = ' '
|
|
282
|
+
---
|
|
283
|
+
--- local nmap_leader = function(suffix, rhs, desc)
|
|
284
|
+
--- vim.keymap.set('n', '<Leader>' .. suffix, rhs, { desc = desc })
|
|
285
|
+
--- end
|
|
286
|
+
--- local xmap_leader = function(suffix, rhs, desc)
|
|
287
|
+
--- vim.keymap.set('x', '<Leader>' .. suffix, rhs, { desc = desc })
|
|
288
|
+
--- end
|
|
289
|
+
---
|
|
290
|
+
--- nmap_leader('bd', '<Cmd>lua MiniBufremove.delete()<CR>', 'Delete')
|
|
291
|
+
--- nmap_leader('bw', '<Cmd>lua MiniBufremove.wipeout()<CR>', 'Wipeout')
|
|
292
|
+
---
|
|
293
|
+
--- nmap_leader('lf', '<Cmd>lua vim.lsp.buf.format()<CR>', 'Format')
|
|
294
|
+
--- xmap_leader('lf', '<Cmd>lua vim.lsp.buf.format()<CR>', 'Format')
|
|
295
|
+
--- nmap_leader('lr', '<Cmd>lua vim.lsp.buf.rename()<CR>', 'Rename')
|
|
296
|
+
--- nmap_leader('lR', '<Cmd>lua vim.lsp.buf.references()<CR>', 'References')
|
|
297
|
+
--- <
|
|
298
|
+
--- The following setup will enable |<Leader>| as trigger in Normal and Visual
|
|
299
|
+
--- modes and add descriptions to mapping groups: >lua
|
|
300
|
+
---
|
|
301
|
+
--- require('mini.clue').setup({
|
|
302
|
+
--- -- Register `<Leader>` as trigger
|
|
303
|
+
--- triggers = {
|
|
304
|
+
--- { mode = { 'n', 'x' }, keys = '<Leader>' },
|
|
305
|
+
--- },
|
|
306
|
+
---
|
|
307
|
+
--- -- Add descriptions for mapping groups
|
|
308
|
+
--- clues = {
|
|
309
|
+
--- { mode = 'n', keys = '<Leader>b', desc = '+Buffers' },
|
|
310
|
+
--- { mode = 'n', keys = '<Leader>l', desc = '+LSP' },
|
|
311
|
+
--- },
|
|
312
|
+
--- })
|
|
313
|
+
--- <
|
|
314
|
+
--- # Clues without mappings ~
|
|
315
|
+
---
|
|
316
|
+
--- Clues can be shown not only for actually present mappings. This is helpful for
|
|
317
|
+
--- showing clues for built-in key combinations. Here is an example of clues for
|
|
318
|
+
--- a subset of built-in completion (see |MiniClue.gen_clues.builtin_completion()|
|
|
319
|
+
--- to generate clues for all available completion sources): >lua
|
|
320
|
+
---
|
|
321
|
+
--- require('mini.clue').setup({
|
|
322
|
+
--- -- Make `<C-x>` a trigger. Otherwise, key query process won't start.
|
|
323
|
+
--- triggers = {
|
|
324
|
+
--- { mode = 'i', keys = '<C-x>' },
|
|
325
|
+
--- },
|
|
326
|
+
---
|
|
327
|
+
--- -- Register custom clues
|
|
328
|
+
--- clues = {
|
|
329
|
+
--- { mode = 'i', keys = '<C-x><C-f>', desc = 'File names' },
|
|
330
|
+
--- { mode = 'i', keys = '<C-x><C-l>', desc = 'Whole lines' },
|
|
331
|
+
--- { mode = 'i', keys = '<C-x><C-o>', desc = 'Omni completion' },
|
|
332
|
+
--- { mode = 'i', keys = '<C-x><C-s>', desc = 'Spelling suggestions' },
|
|
333
|
+
--- { mode = 'i', keys = '<C-x><C-u>', desc = "With 'completefunc'" },
|
|
334
|
+
--- }
|
|
335
|
+
--- })
|
|
336
|
+
--- <
|
|
337
|
+
--- # Triggers in special buffers ~
|
|
338
|
+
---
|
|
339
|
+
--- By default triggers are automatically created in listed (|'buflisted'|) and some
|
|
340
|
+
--- special non-listed buffers. Use |MiniClue.ensure_buf_triggers()| to manually
|
|
341
|
+
--- enable in when you need them. For example: >vim
|
|
342
|
+
---
|
|
343
|
+
--- au FileType special_ft lua MiniClue.ensure_buf_triggers()
|
|
344
|
+
--- <
|
|
345
|
+
--- # Submodes ~
|
|
346
|
+
--- *MiniClue-examples-submodes*
|
|
347
|
+
---
|
|
348
|
+
--- Submode is a state initiated after pressing certain key combination ("prefix")
|
|
349
|
+
--- during which some keys are interpreted differently.
|
|
350
|
+
---
|
|
351
|
+
--- In this module submode can be implemented following these steps:
|
|
352
|
+
---
|
|
353
|
+
--- - Create mappings for each key inside submode. Left hand side of mappings
|
|
354
|
+
--- should consist from prefix followed by the key.
|
|
355
|
+
---
|
|
356
|
+
--- - Create clue for each key inside submode with `postkeys` value equal to
|
|
357
|
+
--- prefix. It would mean that after executing particular key combination from
|
|
358
|
+
--- this submode, pressing its prefix will be automatically emulated (leading
|
|
359
|
+
--- back to being inside submode).
|
|
360
|
+
---
|
|
361
|
+
--- - Register submode prefix (or some of its starting part) as trigger. Do not
|
|
362
|
+
--- register "overlapping" triggers, like `<Leader>` and `<Leader>m`.
|
|
363
|
+
---
|
|
364
|
+
--- ## Submode examples ~
|
|
365
|
+
---
|
|
366
|
+
--- - Submode for moving with |mini.move|:
|
|
367
|
+
--- - Press `<Leader>m` to start submode.
|
|
368
|
+
--- - Press any of `h`/`j`/`k`/`l` to move selection/line.
|
|
369
|
+
--- - Press `<Esc>` to stop submode.
|
|
370
|
+
---
|
|
371
|
+
--- The code: >lua
|
|
372
|
+
---
|
|
373
|
+
--- require('mini.move').setup({
|
|
374
|
+
--- mappings = {
|
|
375
|
+
--- left = '<Leader>mh',
|
|
376
|
+
--- right = '<Leader>ml',
|
|
377
|
+
--- down = '<Leader>mj',
|
|
378
|
+
--- up = '<Leader>mk',
|
|
379
|
+
--- line_left = '<Leader>mh',
|
|
380
|
+
--- line_right = '<Leader>ml',
|
|
381
|
+
--- line_down = '<Leader>mj',
|
|
382
|
+
--- line_up = '<Leader>mk',
|
|
383
|
+
--- },
|
|
384
|
+
--- })
|
|
385
|
+
---
|
|
386
|
+
--- require('mini.clue').setup({
|
|
387
|
+
--- triggers = {
|
|
388
|
+
--- -- This can also set up directly `<Leader>m` as a trigger, but make
|
|
389
|
+
--- -- sure to not also use `<Leader>`, as they would "overlap"
|
|
390
|
+
--- { mode = { 'n', 'x' }, keys = '<Leader>' },
|
|
391
|
+
--- },
|
|
392
|
+
--- clues = {
|
|
393
|
+
--- { mode = 'n', keys = '<Leader>m', desc = '+Move' },
|
|
394
|
+
---
|
|
395
|
+
--- { mode = { 'n', 'x' }, keys = '<Leader>mh', postkeys = '<Leader>m' },
|
|
396
|
+
--- { mode = { 'n', 'x' }, keys = '<Leader>mj', postkeys = '<Leader>m' },
|
|
397
|
+
--- { mode = { 'n', 'x' }, keys = '<Leader>mk', postkeys = '<Leader>m' },
|
|
398
|
+
--- { mode = { 'n', 'x' }, keys = '<Leader>ml', postkeys = '<Leader>m' },
|
|
399
|
+
--- },
|
|
400
|
+
--- })
|
|
401
|
+
--- <
|
|
402
|
+
--- - Submode for iterating buffers and windows with |mini.bracketed|:
|
|
403
|
+
--- - Press `[` or `]` to start key query process for certain direction.
|
|
404
|
+
--- - Press `b` / `w` to iterate buffers/windows until reach target one.
|
|
405
|
+
--- - Press `<Esc>` to stop submode.
|
|
406
|
+
---
|
|
407
|
+
--- The code: >lua
|
|
408
|
+
---
|
|
409
|
+
--- require('mini.bracketed').setup()
|
|
410
|
+
---
|
|
411
|
+
--- require('mini.clue').setup({
|
|
412
|
+
--- triggers = {
|
|
413
|
+
--- { mode = 'n', keys = ']' },
|
|
414
|
+
--- { mode = 'n', keys = '[' },
|
|
415
|
+
--- },
|
|
416
|
+
--- clues = {
|
|
417
|
+
--- { mode = 'n', keys = ']b', postkeys = ']' },
|
|
418
|
+
--- { mode = 'n', keys = ']w', postkeys = ']' },
|
|
419
|
+
---
|
|
420
|
+
--- { mode = 'n', keys = '[b', postkeys = '[' },
|
|
421
|
+
--- { mode = 'n', keys = '[w', postkeys = '[' },
|
|
422
|
+
--- },
|
|
423
|
+
--- })
|
|
424
|
+
--- <
|
|
425
|
+
--- - Submode for window commands using |MiniClue.gen_clues.windows()|:
|
|
426
|
+
--- - Press `<C-w>` to start key query process.
|
|
427
|
+
--- - Press keys which move / change focus / resize windows.
|
|
428
|
+
--- - Press `<Esc>` to stop submode.
|
|
429
|
+
---
|
|
430
|
+
--- The code: >lua
|
|
431
|
+
---
|
|
432
|
+
--- local miniclue = require('mini.clue')
|
|
433
|
+
--- miniclue.setup({
|
|
434
|
+
--- triggers = {
|
|
435
|
+
--- { mode = 'n', keys = '<C-w>' },
|
|
436
|
+
--- },
|
|
437
|
+
--- clues = {
|
|
438
|
+
--- miniclue.gen_clues.windows({
|
|
439
|
+
--- submode_move = true,
|
|
440
|
+
--- submode_navigate = true,
|
|
441
|
+
--- submode_resize = true,
|
|
442
|
+
--- })
|
|
443
|
+
--- },
|
|
444
|
+
--- })
|
|
445
|
+
--- <
|
|
446
|
+
--- # Window config ~
|
|
447
|
+
--- >lua
|
|
448
|
+
--- require('mini.clue').setup({
|
|
449
|
+
--- triggers = { { mode = 'n', keys = '<Leader>' } },
|
|
450
|
+
---
|
|
451
|
+
--- window = {
|
|
452
|
+
--- -- Show window immediately
|
|
453
|
+
--- delay = 0,
|
|
454
|
+
---
|
|
455
|
+
--- config = {
|
|
456
|
+
--- -- Compute window width automatically
|
|
457
|
+
--- width = 'auto',
|
|
458
|
+
---
|
|
459
|
+
--- -- Use double-line border
|
|
460
|
+
--- border = 'double',
|
|
461
|
+
--- },
|
|
462
|
+
--- },
|
|
463
|
+
--- })
|
|
464
|
+
--- <
|
|
465
|
+
---@tag MiniClue-examples
|
|
466
|
+
|
|
467
|
+
---@diagnostic disable:undefined-field
|
|
468
|
+
---@diagnostic disable:discard-returns
|
|
469
|
+
---@diagnostic disable:unused-local
|
|
470
|
+
---@diagnostic disable:cast-local-type
|
|
471
|
+
|
|
472
|
+
-- Module definition ==========================================================
|
|
473
|
+
local MiniClue = {}
|
|
474
|
+
local H = {}
|
|
475
|
+
|
|
476
|
+
--- Module setup
|
|
477
|
+
---
|
|
478
|
+
---@param config table|nil Module config table. See |MiniClue.config|.
|
|
479
|
+
---
|
|
480
|
+
---@usage >lua
|
|
481
|
+
--- require('mini.clue').setup({}) -- replace {} with your config table
|
|
482
|
+
--- -- needs `triggers` field present
|
|
483
|
+
--- <
|
|
484
|
+
MiniClue.setup = function(config)
|
|
485
|
+
-- TODO: Remove after Neovim=0.9 support is dropped
|
|
486
|
+
if vim.fn.has('nvim-0.10') == 0 then
|
|
487
|
+
vim.notify(
|
|
488
|
+
'(mini.clue) Neovim<0.10 is soft deprecated (module works but is not supported).'
|
|
489
|
+
.. " It will be deprecated after the next 'mini.nvim' release (module might not work)."
|
|
490
|
+
.. ' Please update your Neovim version.'
|
|
491
|
+
)
|
|
492
|
+
end
|
|
493
|
+
|
|
494
|
+
-- Export module
|
|
495
|
+
_G.MiniClue = MiniClue
|
|
496
|
+
|
|
497
|
+
-- Setup config
|
|
498
|
+
config = H.setup_config(config)
|
|
499
|
+
|
|
500
|
+
-- Apply config
|
|
501
|
+
H.apply_config(config)
|
|
502
|
+
|
|
503
|
+
-- Define behavior
|
|
504
|
+
H.create_autocommands()
|
|
505
|
+
|
|
506
|
+
-- Create default highlighting
|
|
507
|
+
H.create_default_hl()
|
|
508
|
+
end
|
|
509
|
+
|
|
510
|
+
--stylua: ignore
|
|
511
|
+
--- Defaults ~
|
|
512
|
+
---@eval return MiniDoc.afterlines_to_code(MiniDoc.current.eval_section)
|
|
513
|
+
---@text # General info ~
|
|
514
|
+
---
|
|
515
|
+
--- - To use |<Leader>| as part of the config (either as trigger or inside clues),
|
|
516
|
+
--- set it prior to running |MiniClue.setup()|.
|
|
517
|
+
---
|
|
518
|
+
--- - See |MiniClue-examples| for examples.
|
|
519
|
+
---
|
|
520
|
+
--- # Clues ~
|
|
521
|
+
---
|
|
522
|
+
--- `config.clues` is an array with extra information about key combinations.
|
|
523
|
+
--- Each element can be one of:
|
|
524
|
+
--- - Clue table.
|
|
525
|
+
--- - Array (possibly nested) of clue tables.
|
|
526
|
+
--- - Callable (function) returning either of the previous two.
|
|
527
|
+
---
|
|
528
|
+
--- A clue table is a table with the following fields:
|
|
529
|
+
--- - <mode> `(string|table)` - single character describing mode short-name of
|
|
530
|
+
--- key combination as in `nvim_set_keymap()` (`'n'`, `'x'`, `'i'`, `'o'`, `'c'`, etc.),
|
|
531
|
+
--- or a array thereof.
|
|
532
|
+
--- - <keys> `(string)` - key combination for which clue will be shown.
|
|
533
|
+
--- "Human-readable" key names as in |key-notation| (like "<Leader>", "<Space>",
|
|
534
|
+
--- "<Tab>", etc.) are allowed.
|
|
535
|
+
--- - <desc> `(string|function|nil)` - optional key combination description which is
|
|
536
|
+
--- shown in clue window. If function, should return string description.
|
|
537
|
+
--- - <postkeys> `(string|nil)` - optional postkeys which will be executed
|
|
538
|
+
--- automatically after `keys`. Allows creation of submodes
|
|
539
|
+
--- (see |MiniClue-examples-submodes|).
|
|
540
|
+
---
|
|
541
|
+
--- Notes:
|
|
542
|
+
--- - Postkeys are literal simulation of keypresses with |nvim_feedkeys()|.
|
|
543
|
+
---
|
|
544
|
+
--- - Suggested approach to configuring clues is to create mappings with `desc`
|
|
545
|
+
--- field while supplying to `config.clues` only elements describing groups,
|
|
546
|
+
--- postkeys, and built-in mappings.
|
|
547
|
+
---
|
|
548
|
+
--- # Triggers ~
|
|
549
|
+
---
|
|
550
|
+
--- `config.triggers` is an array with information when |MiniClue-key-query-process|
|
|
551
|
+
--- should start. Each element is a trigger table with the fields <mode> and
|
|
552
|
+
--- <keys> which are treated the same as in clue table.
|
|
553
|
+
---
|
|
554
|
+
--- # Window ~
|
|
555
|
+
---
|
|
556
|
+
--- `config.window` defines behavior of clue window.
|
|
557
|
+
---
|
|
558
|
+
--- `config.window.delay` is a number of milliseconds after which clue window will
|
|
559
|
+
--- appear. Can be 0 to show immediately.
|
|
560
|
+
---
|
|
561
|
+
--- `config.window.config` is a table defining floating window characteristics
|
|
562
|
+
--- or a callable returning such table (will be called with identifier of
|
|
563
|
+
--- window's buffer already showing all clues). It should have the same
|
|
564
|
+
--- structure as in |nvim_open_win()| with the following enhancements:
|
|
565
|
+
--- - <width> field can be equal to `"auto"` leading to window width being
|
|
566
|
+
--- computed automatically based on its content. Default is fixed width of 30.
|
|
567
|
+
--- - <row> and <col> can be equal to `"auto"` in which case they will be
|
|
568
|
+
--- computed to "stick" to set anchor ("SE" by default; see |nvim_open_win()|).
|
|
569
|
+
--- This allows changing corner in which window is shown: >lua
|
|
570
|
+
---
|
|
571
|
+
--- -- Pick one anchor
|
|
572
|
+
--- local anchor = 'NW' -- top-left
|
|
573
|
+
--- local anchor = 'NE' -- top-right
|
|
574
|
+
--- local anchor = 'SW' -- bottom-left
|
|
575
|
+
--- local anchor = 'SE' -- bottom-right
|
|
576
|
+
---
|
|
577
|
+
--- require('mini.clue').setup({
|
|
578
|
+
--- window = {
|
|
579
|
+
--- config = { anchor = anchor, row = 'auto', col = 'auto' },
|
|
580
|
+
--- },
|
|
581
|
+
--- })
|
|
582
|
+
--- <
|
|
583
|
+
--- `config.window.scroll_down` / `config.window.scroll_up` are strings defining
|
|
584
|
+
--- keys which will scroll clue window down / up which is useful in case not
|
|
585
|
+
--- all clues fit in current window height. Set to empty string `''` to disable
|
|
586
|
+
--- either of them.
|
|
587
|
+
MiniClue.config = {
|
|
588
|
+
-- Array of extra clues to show
|
|
589
|
+
clues = {},
|
|
590
|
+
|
|
591
|
+
-- Array of opt-in triggers which start custom key query process.
|
|
592
|
+
-- **Needs to have something in order to show clues**.
|
|
593
|
+
triggers = {},
|
|
594
|
+
|
|
595
|
+
-- Clue window settings
|
|
596
|
+
window = {
|
|
597
|
+
-- Floating window config
|
|
598
|
+
config = {},
|
|
599
|
+
|
|
600
|
+
-- Delay before showing clue window
|
|
601
|
+
delay = 1000,
|
|
602
|
+
|
|
603
|
+
-- Keys to scroll inside the clue window
|
|
604
|
+
scroll_down = '<C-d>',
|
|
605
|
+
scroll_up = '<C-u>',
|
|
606
|
+
},
|
|
607
|
+
}
|
|
608
|
+
--minidoc_afterlines_end
|
|
609
|
+
|
|
610
|
+
--- Enable triggers in loaded listed and some special buffers
|
|
611
|
+
MiniClue.enable_all_triggers = function()
|
|
612
|
+
local loaded_bufs = vim.tbl_filter(vim.api.nvim_buf_is_loaded, vim.api.nvim_list_bufs())
|
|
613
|
+
for _, buf_id in ipairs(loaded_bufs) do
|
|
614
|
+
-- Map only inside valid listed buffers and ones with special filetypes
|
|
615
|
+
local is_special = H.ft_to_enable[vim.bo[buf_id].filetype]
|
|
616
|
+
if vim.fn.buflisted(buf_id) == 1 or is_special then H.map_buf_triggers(buf_id) end
|
|
617
|
+
end
|
|
618
|
+
end
|
|
619
|
+
|
|
620
|
+
--- Enable triggers in buffer
|
|
621
|
+
---
|
|
622
|
+
---@param buf_id number|nil Buffer identifier. Default: current buffer.
|
|
623
|
+
MiniClue.enable_buf_triggers = function(buf_id)
|
|
624
|
+
buf_id = (buf_id == nil or buf_id == 0) and vim.api.nvim_get_current_buf() or buf_id
|
|
625
|
+
if not H.is_valid_buf(buf_id) then H.error('`buf_id` should be a valid buffer identifier.') end
|
|
626
|
+
H.map_buf_triggers(buf_id)
|
|
627
|
+
end
|
|
628
|
+
|
|
629
|
+
--- Disable triggers in loaded buffers
|
|
630
|
+
MiniClue.disable_all_triggers = function()
|
|
631
|
+
local loaded_bufs = vim.tbl_filter(vim.api.nvim_buf_is_loaded, vim.api.nvim_list_bufs())
|
|
632
|
+
for _, buf_id in ipairs(loaded_bufs) do
|
|
633
|
+
H.unmap_buf_triggers(buf_id)
|
|
634
|
+
end
|
|
635
|
+
end
|
|
636
|
+
|
|
637
|
+
--- Disable triggers in buffer
|
|
638
|
+
---
|
|
639
|
+
---@param buf_id number|nil Buffer identifier. Default: current buffer.
|
|
640
|
+
MiniClue.disable_buf_triggers = function(buf_id)
|
|
641
|
+
buf_id = (buf_id == nil or buf_id == 0) and vim.api.nvim_get_current_buf() or buf_id
|
|
642
|
+
if not H.is_valid_buf(buf_id) then H.error('`buf_id` should be a valid buffer identifier.') end
|
|
643
|
+
H.unmap_buf_triggers(buf_id)
|
|
644
|
+
end
|
|
645
|
+
|
|
646
|
+
--- Ensure all triggers are valid
|
|
647
|
+
MiniClue.ensure_all_triggers = function()
|
|
648
|
+
MiniClue.disable_all_triggers()
|
|
649
|
+
MiniClue.enable_all_triggers()
|
|
650
|
+
end
|
|
651
|
+
|
|
652
|
+
--- Ensure buffer triggers are valid
|
|
653
|
+
---
|
|
654
|
+
---@param buf_id number|nil Buffer identifier. Default: current buffer.
|
|
655
|
+
MiniClue.ensure_buf_triggers = function(buf_id)
|
|
656
|
+
MiniClue.disable_buf_triggers(buf_id)
|
|
657
|
+
MiniClue.enable_buf_triggers(buf_id)
|
|
658
|
+
end
|
|
659
|
+
|
|
660
|
+
--- Update description of an existing mapping
|
|
661
|
+
---
|
|
662
|
+
--- Notes:
|
|
663
|
+
--- - Uses buffer-local mapping in case there are both global and buffer-local
|
|
664
|
+
--- mappings with same mode and LHS. Similar to |maparg()|.
|
|
665
|
+
---
|
|
666
|
+
---@param mode string Mapping mode (as in `maparg()`).
|
|
667
|
+
---@param lhs string Mapping left hand side (as `name` in `maparg()`).
|
|
668
|
+
---@param desc string New description to set.
|
|
669
|
+
MiniClue.set_mapping_desc = function(mode, lhs, desc)
|
|
670
|
+
if type(mode) ~= 'string' then H.error('`mode` should be string.') end
|
|
671
|
+
if type(lhs) ~= 'string' then H.error('`lhs` should be string.') end
|
|
672
|
+
if type(desc) ~= 'string' then H.error('`desc` should be string.') end
|
|
673
|
+
|
|
674
|
+
local ok_get, map_data = pcall(vim.fn.maparg, lhs, mode, false, true)
|
|
675
|
+
if not ok_get or vim.tbl_count(map_data) == 0 then
|
|
676
|
+
local msg = string.format('No mapping found for mode %s and LHS %s.', vim.inspect(mode), vim.inspect(lhs))
|
|
677
|
+
H.error(msg)
|
|
678
|
+
end
|
|
679
|
+
|
|
680
|
+
map_data.desc = desc
|
|
681
|
+
local ok_set = pcall(vim.fn.mapset, mode, false, map_data)
|
|
682
|
+
if not ok_set then H.error(vim.inspect(desc) .. ' is not a valid description.') end
|
|
683
|
+
end
|
|
684
|
+
|
|
685
|
+
--- Generate pre-configured clues
|
|
686
|
+
---
|
|
687
|
+
--- This is a table with function elements. Call to actually get array of clues.
|
|
688
|
+
MiniClue.gen_clues = {}
|
|
689
|
+
|
|
690
|
+
--- Generate clues for built-in completion
|
|
691
|
+
---
|
|
692
|
+
--- Contains clues for the following triggers: >lua
|
|
693
|
+
---
|
|
694
|
+
--- { mode = 'i', keys = '<C-x>' }
|
|
695
|
+
--- <
|
|
696
|
+
---@return table Array of clues.
|
|
697
|
+
MiniClue.gen_clues.builtin_completion = function()
|
|
698
|
+
--stylua: ignore
|
|
699
|
+
return {
|
|
700
|
+
{ mode = 'i', keys = '<C-x><C-d>', desc = 'Defined identifiers' },
|
|
701
|
+
{ mode = 'i', keys = '<C-x><C-e>', desc = 'Scroll up' },
|
|
702
|
+
{ mode = 'i', keys = '<C-x><C-f>', desc = 'File names' },
|
|
703
|
+
{ mode = 'i', keys = '<C-x><C-i>', desc = 'Identifiers' },
|
|
704
|
+
{ mode = 'i', keys = '<C-x><C-k>', desc = 'Identifiers from dictionary' },
|
|
705
|
+
{ mode = 'i', keys = '<C-x><C-l>', desc = 'Whole lines' },
|
|
706
|
+
{ mode = 'i', keys = '<C-x><C-n>', desc = 'Next completion' },
|
|
707
|
+
{ mode = 'i', keys = '<C-x><C-o>', desc = 'Omni completion' },
|
|
708
|
+
{ mode = 'i', keys = '<C-x><C-p>', desc = 'Previous completion' },
|
|
709
|
+
{ mode = 'i', keys = '<C-x><C-s>', desc = 'Spelling suggestions' },
|
|
710
|
+
{ mode = 'i', keys = '<C-x><C-t>', desc = 'Identifiers from thesaurus' },
|
|
711
|
+
{ mode = 'i', keys = '<C-x><C-y>', desc = 'Scroll down' },
|
|
712
|
+
{ mode = 'i', keys = '<C-x><C-u>', desc = "With 'completefunc'" },
|
|
713
|
+
{ mode = 'i', keys = '<C-x><C-v>', desc = 'Like in command line' },
|
|
714
|
+
{ mode = 'i', keys = '<C-x><C-z>', desc = 'Stop completion' },
|
|
715
|
+
{ mode = 'i', keys = '<C-x><C-]>', desc = 'Tags' },
|
|
716
|
+
{ mode = 'i', keys = '<C-x>s', desc = 'Spelling suggestions' },
|
|
717
|
+
}
|
|
718
|
+
end
|
|
719
|
+
|
|
720
|
+
--- Generate clues for `g` key
|
|
721
|
+
---
|
|
722
|
+
--- Contains clues for the following triggers: >lua
|
|
723
|
+
---
|
|
724
|
+
--- { mode = { 'n', 'x' }, keys = 'g' }
|
|
725
|
+
--- <
|
|
726
|
+
---@return table Array of clues.
|
|
727
|
+
MiniClue.gen_clues.g = function()
|
|
728
|
+
local gr_clue = vim.fn.has('nvim-0.11') == 1 and { mode = 'n', keys = 'gr', desc = '+LSP' }
|
|
729
|
+
or { mode = 'n', keys = 'gr', desc = 'Virtual replace with character' }
|
|
730
|
+
local gr_clue_viz = vim.fn.has('nvim-0.11') == 1 and { mode = 'x', keys = 'gr', desc = '+LSP' } or {}
|
|
731
|
+
|
|
732
|
+
--stylua: ignore
|
|
733
|
+
return {
|
|
734
|
+
{ mode = 'n', keys = 'g0', desc = 'Go to leftmost visible column' },
|
|
735
|
+
{ mode = 'n', keys = 'g8', desc = 'Print hex value of char under cursor' },
|
|
736
|
+
{ mode = 'n', keys = 'ga', desc = 'Print ascii value' },
|
|
737
|
+
{ mode = 'n', keys = 'gD', desc = 'Go to definition in file' },
|
|
738
|
+
{ mode = 'n', keys = 'gd', desc = 'Go to definition in function' },
|
|
739
|
+
{ mode = 'n', keys = 'gE', desc = 'Go backwards to end of previous WORD' },
|
|
740
|
+
{ mode = 'n', keys = 'ge', desc = 'Go backwards to end of previous word' },
|
|
741
|
+
{ mode = 'n', keys = 'gF', desc = 'Edit file under cursor + jump line' },
|
|
742
|
+
{ mode = 'n', keys = 'gf', desc = 'Edit file under cursor' },
|
|
743
|
+
{ mode = 'n', keys = 'gg', desc = 'Go to line (def: first)' },
|
|
744
|
+
{ mode = 'n', keys = 'gH', desc = 'Start Select line mode' },
|
|
745
|
+
{ mode = 'n', keys = 'gh', desc = 'Start Select mode' },
|
|
746
|
+
{ mode = 'n', keys = 'gI', desc = 'Start Insert at column 1' },
|
|
747
|
+
{ mode = 'n', keys = 'gi', desc = 'Start Insert where it stopped' },
|
|
748
|
+
{ mode = 'n', keys = 'gJ', desc = 'Join lines without extra spaces' },
|
|
749
|
+
{ mode = 'n', keys = 'gj', desc = 'Go down by screen lines' },
|
|
750
|
+
{ mode = 'n', keys = 'gk', desc = 'Go up by screen lines' },
|
|
751
|
+
{ mode = 'n', keys = 'gM', desc = 'Go to middle of text line' },
|
|
752
|
+
{ mode = 'n', keys = 'gm', desc = 'Go to middle of screen line' },
|
|
753
|
+
{ mode = 'n', keys = 'gN', desc = 'Select previous search match' },
|
|
754
|
+
{ mode = 'n', keys = 'gn', desc = 'Select next search match' },
|
|
755
|
+
{ mode = 'n', keys = 'go', desc = 'Go to byte' },
|
|
756
|
+
{ mode = 'n', keys = 'gP', desc = 'Put text before cursor + stay after it' },
|
|
757
|
+
{ mode = 'n', keys = 'gp', desc = 'Put text after cursor + stay after it' },
|
|
758
|
+
{ mode = 'n', keys = 'gQ', desc = 'Switch to "Ex" mode' },
|
|
759
|
+
{ mode = 'n', keys = 'gq', desc = 'Format text (operator)' },
|
|
760
|
+
{ mode = 'n', keys = 'gR', desc = 'Enter Virtual Replace mode' },
|
|
761
|
+
gr_clue,
|
|
762
|
+
{ mode = 'n', keys = 'gs', desc = 'Sleep' },
|
|
763
|
+
{ mode = 'n', keys = 'gT', desc = 'Go to previous tabpage' },
|
|
764
|
+
{ mode = 'n', keys = 'gt', desc = 'Go to next tabpage' },
|
|
765
|
+
{ mode = 'n', keys = 'gU', desc = 'Make uppercase (operator)' },
|
|
766
|
+
{ mode = 'n', keys = 'gu', desc = 'Make lowercase (operator)' },
|
|
767
|
+
{ mode = 'n', keys = 'gV', desc = 'Avoid reselect' },
|
|
768
|
+
{ mode = 'n', keys = 'gv', desc = 'Reselect previous Visual area' },
|
|
769
|
+
{ mode = 'n', keys = 'gw', desc = 'Format text + keep cursor (operator)' },
|
|
770
|
+
{ mode = 'n', keys = 'gx', desc = 'Execute app for file under cursor' },
|
|
771
|
+
{ mode = 'n', keys = 'g<C-]>', desc = '`:tjump` to tag under cursor' },
|
|
772
|
+
{ mode = 'n', keys = 'g<C-a>', desc = 'Dump a memory profile' },
|
|
773
|
+
{ mode = 'n', keys = 'g<C-g>', desc = 'Show information about cursor' },
|
|
774
|
+
{ mode = 'n', keys = 'g<C-h>', desc = 'Start Select block mode' },
|
|
775
|
+
{ mode = 'n', keys = 'g<Tab>', desc = 'Go to last accessed tabpage' },
|
|
776
|
+
{ mode = 'n', keys = "g'", desc = "Jump to mark (don't affect jumplist)" },
|
|
777
|
+
{ mode = 'n', keys = 'g#', desc = 'Search backwards word under cursor' },
|
|
778
|
+
{ mode = 'n', keys = 'g$', desc = 'Go to rightmost visible column' },
|
|
779
|
+
{ mode = 'n', keys = 'g%', desc = 'Cycle through matching groups' },
|
|
780
|
+
{ mode = 'n', keys = 'g&', desc = 'Repeat last `:s` on all lines' },
|
|
781
|
+
{ mode = 'n', keys = 'g*', desc = 'Search word under cursor' },
|
|
782
|
+
{ mode = 'n', keys = 'g+', desc = 'Go to newer text state' },
|
|
783
|
+
{ mode = 'n', keys = 'g,', desc = 'Go to newer position in change list' },
|
|
784
|
+
{ mode = 'n', keys = 'g-', desc = 'Go to older text state' },
|
|
785
|
+
{ mode = 'n', keys = 'g;', desc = 'Go to older position in change list' },
|
|
786
|
+
{ mode = 'n', keys = 'g<', desc = 'Display previous command output' },
|
|
787
|
+
{ mode = 'n', keys = 'g?', desc = 'Rot13 encode (operator)' },
|
|
788
|
+
{ mode = 'n', keys = 'g@', desc = "Call 'operatorfunc' (operator)" },
|
|
789
|
+
{ mode = 'n', keys = 'g]', desc = '`:tselect` tag under cursor' },
|
|
790
|
+
{ mode = 'n', keys = 'g^', desc = 'Go to leftmost visible non-whitespace' },
|
|
791
|
+
{ mode = 'n', keys = 'g_', desc = 'Go to lower line' },
|
|
792
|
+
{ mode = 'n', keys = 'g`', desc = "Jump to mark (don't affect jumplist)" },
|
|
793
|
+
{ mode = 'n', keys = 'g~', desc = 'Swap case (operator)' },
|
|
794
|
+
|
|
795
|
+
{ mode = 'x', keys = 'gf', desc = 'Edit selected file' },
|
|
796
|
+
{ mode = 'x', keys = 'gJ', desc = 'Join selected lines without extra spaces' },
|
|
797
|
+
{ mode = 'x', keys = 'gq', desc = 'Format selection' },
|
|
798
|
+
gr_clue_viz,
|
|
799
|
+
{ mode = 'x', keys = 'gV', desc = 'Avoid reselect' },
|
|
800
|
+
{ mode = 'x', keys = 'gw', desc = 'Format selection + keep cursor' },
|
|
801
|
+
{ mode = 'x', keys = 'g<C-]>', desc = '`:tjump` to selected tag' },
|
|
802
|
+
{ mode = 'x', keys = 'g<C-a>', desc = 'Increment with compound' },
|
|
803
|
+
{ mode = 'x', keys = 'g<C-g>', desc = 'Show information about selection' },
|
|
804
|
+
{ mode = 'x', keys = 'g<C-x>', desc = 'Decrement with compound' },
|
|
805
|
+
{ mode = 'x', keys = 'g]', desc = '`:tselect` selected tag' },
|
|
806
|
+
{ mode = 'x', keys = 'g?', desc = 'Rot13 encode selection' },
|
|
807
|
+
}
|
|
808
|
+
end
|
|
809
|
+
|
|
810
|
+
--- Generate clues for `[` and `]` keys
|
|
811
|
+
---
|
|
812
|
+
--- Contains clues for the following triggers: >lua
|
|
813
|
+
---
|
|
814
|
+
--- { mode = 'n', keys = '[' }
|
|
815
|
+
--- { mode = 'n', keys = ']' }
|
|
816
|
+
--- <
|
|
817
|
+
---@return table Array of clues.
|
|
818
|
+
MiniClue.gen_clues.square_brackets = function()
|
|
819
|
+
--stylua: ignore
|
|
820
|
+
return {
|
|
821
|
+
{ mode = 'n', keys = '[<C-D>', desc = 'Go to first macro def with cursor word', },
|
|
822
|
+
{ mode = 'n', keys = '[<C-I>', desc = 'Go to first match with cursor word', },
|
|
823
|
+
{ mode = 'n', keys = '[%', desc = 'Go to previous unmatched group' },
|
|
824
|
+
{ mode = 'n', keys = '[#', desc = 'Go to previous unmatched #if/#else/#ifdef' },
|
|
825
|
+
{ mode = 'n', keys = "['", desc = 'Go to previous mark, first non-blank' },
|
|
826
|
+
{ mode = 'n', keys = '[`', desc = 'Go to previous mark' },
|
|
827
|
+
{ mode = 'n', keys = '[(', desc = "Go to previous unmatched '('" },
|
|
828
|
+
{ mode = 'n', keys = '[/', desc = 'Go to previous C comment start' },
|
|
829
|
+
{ mode = 'n', keys = '[*', desc = 'Go to previous C comment start' },
|
|
830
|
+
{ mode = 'n', keys = '[I', desc = 'Show lines with cursor word', },
|
|
831
|
+
{ mode = 'n', keys = '[D', desc = 'Show macro defs with cursor word' },
|
|
832
|
+
{ mode = 'n', keys = '[p', desc = 'Paste with current indent' },
|
|
833
|
+
{ mode = 'n', keys = '[P', desc = 'Paste with current indent' },
|
|
834
|
+
{ mode = 'n', keys = '[[', desc = 'Go to previous section' },
|
|
835
|
+
{ mode = 'n', keys = '[]', desc = 'Go to previous SECTION' },
|
|
836
|
+
{ mode = 'n', keys = '[c', desc = 'Go to previous change' },
|
|
837
|
+
{ mode = 'n', keys = '[d', desc = 'Show first macro def with cursor word' },
|
|
838
|
+
{ mode = 'n', keys = '[f', desc = 'Edit file under cursor' },
|
|
839
|
+
{ mode = 'n', keys = '[i', desc = 'Show first line with cursor word', },
|
|
840
|
+
{ mode = 'n', keys = '[m', desc = 'Go to previous method start' },
|
|
841
|
+
{ mode = 'n', keys = '[M', desc = 'Go to previous method end' },
|
|
842
|
+
{ mode = 'n', keys = '[s', desc = 'Go to previous misspelled word' },
|
|
843
|
+
{ mode = 'n', keys = '[z', desc = 'Go to current open fold start' },
|
|
844
|
+
{ mode = 'n', keys = '[{', desc = "Go to previous unmatched '{'" },
|
|
845
|
+
{ mode = 'n', keys = ']<C-D>', desc = 'Go to next macro def with cursor word', },
|
|
846
|
+
{ mode = 'n', keys = ']<C-I>', desc = 'Go to next match with cursor word', },
|
|
847
|
+
{ mode = 'n', keys = ']%', desc = 'Go to next unmatched group' },
|
|
848
|
+
{ mode = 'n', keys = ']#', desc = 'Go to next unmatched #if/#else/#ifdef' },
|
|
849
|
+
{ mode = 'n', keys = "]'", desc = "Go to next mark, first non-blank" },
|
|
850
|
+
{ mode = 'n', keys = ']`', desc = 'Go to next mark' },
|
|
851
|
+
{ mode = 'n', keys = '])', desc = "Go to next unmatched ')'" },
|
|
852
|
+
{ mode = 'n', keys = ']/', desc = 'Go to next C comment end' },
|
|
853
|
+
{ mode = 'n', keys = ']*', desc = 'Go to next C comment end' },
|
|
854
|
+
{ mode = 'n', keys = ']D', desc = 'Show below macro defs with cursor word' },
|
|
855
|
+
{ mode = 'n', keys = ']I', desc = 'Show below lines with cursor word', },
|
|
856
|
+
{ mode = 'n', keys = ']P', desc = 'Paste with current indent' },
|
|
857
|
+
{ mode = 'n', keys = '][', desc = 'Go to next SECTION' },
|
|
858
|
+
{ mode = 'n', keys = ']]', desc = 'Go to next section' },
|
|
859
|
+
{ mode = 'n', keys = ']c', desc = 'Go to next change' },
|
|
860
|
+
{ mode = 'n', keys = ']d', desc = 'Show next macro def with cursor word' },
|
|
861
|
+
{ mode = 'n', keys = ']f', desc = 'Edit file under cursor' },
|
|
862
|
+
{ mode = 'n', keys = ']i', desc = 'Show next line with cursor word', },
|
|
863
|
+
{ mode = 'n', keys = ']m', desc = 'Go to next method start' },
|
|
864
|
+
{ mode = 'n', keys = ']M', desc = 'Go to next method end' },
|
|
865
|
+
{ mode = 'n', keys = ']p', desc = 'Paste with current indent' },
|
|
866
|
+
{ mode = 'n', keys = ']s', desc = 'Go to next misspelled word' },
|
|
867
|
+
{ mode = 'n', keys = ']z', desc = 'Go to current open fold end' },
|
|
868
|
+
{ mode = 'n', keys = ']}', desc = "Go to next unmatched '}'" },
|
|
869
|
+
}
|
|
870
|
+
end
|
|
871
|
+
|
|
872
|
+
--- Generate clues for marks
|
|
873
|
+
---
|
|
874
|
+
--- Contains clues for the following triggers: >lua
|
|
875
|
+
---
|
|
876
|
+
--- { mode = { 'n', 'x' }, keys = "'" }
|
|
877
|
+
--- { mode = { 'n', 'x' }, keys = "g'" }
|
|
878
|
+
--- { mode = { 'n', 'x' }, keys = '`' }
|
|
879
|
+
--- { mode = { 'n', 'x' }, keys = 'g`' }
|
|
880
|
+
--- <
|
|
881
|
+
--- Note: if you use `g` as trigger (like to enable |MiniClue.gen_clues.g()|),
|
|
882
|
+
--- don't add |g'| and |g`| as triggers: they already will be taken into account.
|
|
883
|
+
---
|
|
884
|
+
---@return table Array of clues.
|
|
885
|
+
---
|
|
886
|
+
---@seealso |mark-motions|
|
|
887
|
+
MiniClue.gen_clues.marks = function()
|
|
888
|
+
local describe_marks = function(mode, prefix)
|
|
889
|
+
local make_clue = function(register, desc) return { mode = mode, keys = prefix .. register, desc = desc } end
|
|
890
|
+
|
|
891
|
+
return {
|
|
892
|
+
make_clue('^', 'Latest insert position'),
|
|
893
|
+
make_clue('.', 'Latest change'),
|
|
894
|
+
make_clue('"', 'Latest exited position'),
|
|
895
|
+
make_clue("'", 'Line before jump'),
|
|
896
|
+
make_clue('`', 'Position before jump'),
|
|
897
|
+
make_clue('[', 'Start of latest changed or yanked text'),
|
|
898
|
+
make_clue(']', 'End of latest changed or yanked text'),
|
|
899
|
+
make_clue('(', 'Start of sentence'),
|
|
900
|
+
make_clue(')', 'End of sentence'),
|
|
901
|
+
make_clue('{', 'Start of paragraph'),
|
|
902
|
+
make_clue('}', 'End of paragraph'),
|
|
903
|
+
make_clue('<', 'Start of latest visual selection'),
|
|
904
|
+
make_clue('>', 'End of latest visual selection'),
|
|
905
|
+
}
|
|
906
|
+
end
|
|
907
|
+
|
|
908
|
+
--stylua: ignore
|
|
909
|
+
return {
|
|
910
|
+
-- Normal and Visual mode
|
|
911
|
+
describe_marks({ 'n', 'x' }, "'"),
|
|
912
|
+
describe_marks({ 'n', 'x' }, "g'"),
|
|
913
|
+
describe_marks({ 'n', 'x' }, "`"),
|
|
914
|
+
describe_marks({ 'n', 'x' }, "g`"),
|
|
915
|
+
}
|
|
916
|
+
end
|
|
917
|
+
|
|
918
|
+
--- Generate clues for registers
|
|
919
|
+
---
|
|
920
|
+
--- Contains clues for the following triggers: >lua
|
|
921
|
+
---
|
|
922
|
+
--- { mode = { 'n', 'x' }, keys = '"' }
|
|
923
|
+
--- { mode = { 'i', 'c' }, keys = '<C-r>' }
|
|
924
|
+
--- <
|
|
925
|
+
---@param opts table|nil Options. Possible keys:
|
|
926
|
+
--- - <show_contents> `(boolean)` - whether to show contents of all possible
|
|
927
|
+
--- registers. If `false`, only description of special registers is shown.
|
|
928
|
+
--- Default: `false`.
|
|
929
|
+
---
|
|
930
|
+
---@return table Array of clues.
|
|
931
|
+
---
|
|
932
|
+
---@seealso |registers|
|
|
933
|
+
MiniClue.gen_clues.registers = function(opts)
|
|
934
|
+
opts = vim.tbl_deep_extend('force', { show_contents = false }, opts or {})
|
|
935
|
+
|
|
936
|
+
local describe_registers
|
|
937
|
+
if opts.show_contents then
|
|
938
|
+
describe_registers = H.make_clues_with_register_contents
|
|
939
|
+
else
|
|
940
|
+
describe_registers = function(mode, prefix)
|
|
941
|
+
local make_clue = function(register, desc) return { mode = mode, keys = prefix .. register, desc = desc } end
|
|
942
|
+
return {
|
|
943
|
+
make_clue('0', 'Latest yank'),
|
|
944
|
+
make_clue('1', 'Latest big delete'),
|
|
945
|
+
make_clue('"', 'Default register'),
|
|
946
|
+
make_clue('#', 'Alternate buffer'),
|
|
947
|
+
make_clue('%', 'Name of the current file'),
|
|
948
|
+
make_clue('*', 'Selection clipboard'),
|
|
949
|
+
make_clue('+', 'System clipboard'),
|
|
950
|
+
make_clue('-', 'Latest small delete'),
|
|
951
|
+
make_clue('.', 'Latest inserted text'),
|
|
952
|
+
make_clue('/', 'Latest search pattern'),
|
|
953
|
+
make_clue(':', 'Latest executed command'),
|
|
954
|
+
make_clue('=', 'Result of expression'),
|
|
955
|
+
make_clue('_', 'Black hole'),
|
|
956
|
+
}
|
|
957
|
+
end
|
|
958
|
+
end
|
|
959
|
+
|
|
960
|
+
--stylua: ignore
|
|
961
|
+
return {
|
|
962
|
+
-- Normal and Visual mode
|
|
963
|
+
describe_registers({ 'n', 'x' }, '"'),
|
|
964
|
+
|
|
965
|
+
-- Insert mode
|
|
966
|
+
describe_registers('i', '<C-r>'),
|
|
967
|
+
|
|
968
|
+
{ mode = 'i', keys = '<C-r><C-r>', desc = '+Insert literally' },
|
|
969
|
+
describe_registers('i', '<C-r><C-r>'),
|
|
970
|
+
|
|
971
|
+
{ mode = 'i', keys = '<C-r><C-o>', desc = '+Insert literally + not auto-indent' },
|
|
972
|
+
describe_registers('i', '<C-r><C-o>'),
|
|
973
|
+
|
|
974
|
+
{ mode = 'i', keys = '<C-r><C-p>', desc = '+Insert + fix indent' },
|
|
975
|
+
describe_registers('i', '<C-r><C-p>'),
|
|
976
|
+
|
|
977
|
+
-- Command-line mode
|
|
978
|
+
describe_registers('c', '<C-r>'),
|
|
979
|
+
|
|
980
|
+
{ mode = 'c', keys = '<C-r><C-r>', desc = '+Insert literally' },
|
|
981
|
+
describe_registers('c', '<C-r><C-r>'),
|
|
982
|
+
|
|
983
|
+
{ mode = 'c', keys = '<C-r><C-o>', desc = '+Insert literally' },
|
|
984
|
+
describe_registers('c', '<C-r><C-o>'),
|
|
985
|
+
}
|
|
986
|
+
end
|
|
987
|
+
|
|
988
|
+
--- Generate clues for window commands
|
|
989
|
+
---
|
|
990
|
+
--- Contains clues for the following triggers: >lua
|
|
991
|
+
---
|
|
992
|
+
--- { mode = 'n', keys = '<C-w>' }
|
|
993
|
+
--- <
|
|
994
|
+
--- Note: only non-duplicated commands are included. For full list see |CTRL-W|.
|
|
995
|
+
---
|
|
996
|
+
---@param opts table|nil Options. Possible keys:
|
|
997
|
+
--- - <submode_move> `(boolean)` - whether to make move (change layout)
|
|
998
|
+
--- commands a submode by using `postkeys` field. Default: `false`.
|
|
999
|
+
--- - <submode_navigate> `(boolean)` - whether to make navigation (change
|
|
1000
|
+
--- focus) commands a submode by using `postkeys` field. Default: `false`.
|
|
1001
|
+
--- - <submode_resize> `(boolean)` - whether to make resize (change size)
|
|
1002
|
+
--- commands a submode by using `postkeys` field. Default: `false`.
|
|
1003
|
+
---
|
|
1004
|
+
---@return table Array of clues.
|
|
1005
|
+
MiniClue.gen_clues.windows = function(opts)
|
|
1006
|
+
local default_opts = { submode_navigate = false, submode_move = false, submode_resize = false }
|
|
1007
|
+
opts = vim.tbl_deep_extend('force', default_opts, opts or {})
|
|
1008
|
+
|
|
1009
|
+
local postkeys_move, postkeys_navigate, postkeys_resize = nil, nil, nil
|
|
1010
|
+
if opts.submode_move then postkeys_move = '<C-w>' end
|
|
1011
|
+
if opts.submode_navigate then postkeys_navigate = '<C-w>' end
|
|
1012
|
+
if opts.submode_resize then postkeys_resize = '<C-w>' end
|
|
1013
|
+
|
|
1014
|
+
--stylua: ignore
|
|
1015
|
+
return {
|
|
1016
|
+
{ mode = 'n', keys = '<C-w>+', desc = 'Increase height', postkeys = postkeys_resize },
|
|
1017
|
+
{ mode = 'n', keys = '<C-w>-', desc = 'Decrease height', postkeys = postkeys_resize },
|
|
1018
|
+
{ mode = 'n', keys = '<C-w><', desc = 'Decrease width', postkeys = postkeys_resize },
|
|
1019
|
+
{ mode = 'n', keys = '<C-w>>', desc = 'Increase width', postkeys = postkeys_resize },
|
|
1020
|
+
{ mode = 'n', keys = '<C-w>=', desc = 'Make windows same dimensions' },
|
|
1021
|
+
{ mode = 'n', keys = '<C-w>]', desc = 'Split + jump to tag' },
|
|
1022
|
+
{ mode = 'n', keys = '<C-w>^', desc = 'Split + edit alternate file' },
|
|
1023
|
+
{ mode = 'n', keys = '<C-w>_', desc = 'Set height (def: very high)' },
|
|
1024
|
+
{ mode = 'n', keys = '<C-w>|', desc = 'Set width (def: very wide)' },
|
|
1025
|
+
{ mode = 'n', keys = '<C-w>}', desc = 'Show tag in preview' },
|
|
1026
|
+
{ mode = 'n', keys = '<C-w>b', desc = 'Focus bottom', postkeys = postkeys_navigate },
|
|
1027
|
+
{ mode = 'n', keys = '<C-w>c', desc = 'Close' },
|
|
1028
|
+
{ mode = 'n', keys = '<C-w>d', desc = 'Split + jump to definition' },
|
|
1029
|
+
{ mode = 'n', keys = '<C-w>F', desc = 'Split + edit file name + jump' },
|
|
1030
|
+
{ mode = 'n', keys = '<C-w>f', desc = 'Split + edit file name' },
|
|
1031
|
+
{ mode = 'n', keys = '<C-w>g', desc = '+Extra actions' },
|
|
1032
|
+
{ mode = 'n', keys = '<C-w>g]', desc = 'Split + list tags' },
|
|
1033
|
+
{ mode = 'n', keys = '<C-w>g}', desc = 'Do `:ptjump`' },
|
|
1034
|
+
{ mode = 'n', keys = '<C-w>g<C-]>', desc = 'Split + jump to tag with `:tjump`' },
|
|
1035
|
+
{ mode = 'n', keys = '<C-w>g<Tab>', desc = 'Focus last accessed tab', postkeys = postkeys_navigate },
|
|
1036
|
+
{ mode = 'n', keys = '<C-w>gF', desc = 'New tabpage + edit file name + jump' },
|
|
1037
|
+
{ mode = 'n', keys = '<C-w>gf', desc = 'New tabpage + edit file name' },
|
|
1038
|
+
{ mode = 'n', keys = '<C-w>gT', desc = 'Focus previous tabpage', postkeys = postkeys_navigate },
|
|
1039
|
+
{ mode = 'n', keys = '<C-w>gt', desc = 'Focus next tabpage', postkeys = postkeys_navigate },
|
|
1040
|
+
{ mode = 'n', keys = '<C-w>H', desc = 'Move to very left', postkeys = postkeys_move },
|
|
1041
|
+
{ mode = 'n', keys = '<C-w>h', desc = 'Focus left', postkeys = postkeys_navigate },
|
|
1042
|
+
{ mode = 'n', keys = '<C-w>i', desc = 'Split + jump to declaration' },
|
|
1043
|
+
{ mode = 'n', keys = '<C-w>J', desc = 'Move to very bottom', postkeys = postkeys_move },
|
|
1044
|
+
{ mode = 'n', keys = '<C-w>j', desc = 'Focus down', postkeys = postkeys_navigate },
|
|
1045
|
+
{ mode = 'n', keys = '<C-w>K', desc = 'Move to very top', postkeys = postkeys_move },
|
|
1046
|
+
{ mode = 'n', keys = '<C-w>k', desc = 'Focus up', postkeys = postkeys_navigate },
|
|
1047
|
+
{ mode = 'n', keys = '<C-w>L', desc = 'Move to very right', postkeys = postkeys_move },
|
|
1048
|
+
{ mode = 'n', keys = '<C-w>l', desc = 'Focus right', postkeys = postkeys_navigate },
|
|
1049
|
+
{ mode = 'n', keys = '<C-w>n', desc = 'Open new' },
|
|
1050
|
+
{ mode = 'n', keys = '<C-w>o', desc = 'Close all but current' },
|
|
1051
|
+
{ mode = 'n', keys = '<C-w>P', desc = 'Focus preview', postkeys = postkeys_navigate },
|
|
1052
|
+
{ mode = 'n', keys = '<C-w>p', desc = 'Focus last accessed', postkeys = postkeys_navigate },
|
|
1053
|
+
{ mode = 'n', keys = '<C-w>q', desc = 'Quit current' },
|
|
1054
|
+
{ mode = 'n', keys = '<C-w>R', desc = 'Rotate up/left', postkeys = postkeys_move },
|
|
1055
|
+
{ mode = 'n', keys = '<C-w>r', desc = 'Rotate down/right', postkeys = postkeys_move },
|
|
1056
|
+
{ mode = 'n', keys = '<C-w>s', desc = 'Split horizontally' },
|
|
1057
|
+
{ mode = 'n', keys = '<C-w>T', desc = 'Create new tabpage + move' },
|
|
1058
|
+
{ mode = 'n', keys = '<C-w>t', desc = 'Focus top', postkeys = postkeys_navigate },
|
|
1059
|
+
{ mode = 'n', keys = '<C-w>v', desc = 'Split vertically' },
|
|
1060
|
+
{ mode = 'n', keys = '<C-w>W', desc = 'Focus previous', postkeys = postkeys_navigate },
|
|
1061
|
+
{ mode = 'n', keys = '<C-w>w', desc = 'Focus next', postkeys = postkeys_navigate },
|
|
1062
|
+
{ mode = 'n', keys = '<C-w>x', desc = 'Exchange windows', postkeys = postkeys_move },
|
|
1063
|
+
{ mode = 'n', keys = '<C-w>z', desc = 'Close preview' },
|
|
1064
|
+
}
|
|
1065
|
+
end
|
|
1066
|
+
|
|
1067
|
+
--- Generate clues for `z` key
|
|
1068
|
+
---
|
|
1069
|
+
--- Contains clues for the following triggers: >lua
|
|
1070
|
+
---
|
|
1071
|
+
--- { mode = { 'n', 'x' }, keys = 'z' }
|
|
1072
|
+
--- <
|
|
1073
|
+
---@return table Array of clues.
|
|
1074
|
+
MiniClue.gen_clues.z = function()
|
|
1075
|
+
--stylua: ignore
|
|
1076
|
+
return {
|
|
1077
|
+
{ mode = 'n', keys = 'zA', desc = 'Toggle folds recursively' },
|
|
1078
|
+
{ mode = 'n', keys = 'za', desc = 'Toggle fold' },
|
|
1079
|
+
{ mode = 'n', keys = 'zb', desc = 'Redraw at bottom' },
|
|
1080
|
+
{ mode = 'n', keys = 'zC', desc = 'Close folds recursively' },
|
|
1081
|
+
{ mode = 'n', keys = 'zc', desc = 'Close fold' },
|
|
1082
|
+
{ mode = 'n', keys = 'zD', desc = 'Delete folds recursively' },
|
|
1083
|
+
{ mode = 'n', keys = 'zd', desc = 'Delete fold' },
|
|
1084
|
+
{ mode = 'n', keys = 'zE', desc = 'Eliminate all folds' },
|
|
1085
|
+
{ mode = 'n', keys = 'ze', desc = 'Scroll to cursor on right screen side' },
|
|
1086
|
+
{ mode = 'n', keys = 'zF', desc = 'Create fold' },
|
|
1087
|
+
{ mode = 'n', keys = 'zf', desc = 'Create fold (operator)' },
|
|
1088
|
+
{ mode = 'n', keys = 'zG', desc = 'Temporarily mark as correctly spelled' },
|
|
1089
|
+
{ mode = 'n', keys = 'zg', desc = 'Permanently mark as correctly spelled' },
|
|
1090
|
+
{ mode = 'n', keys = 'zH', desc = 'Scroll left half screen' },
|
|
1091
|
+
{ mode = 'n', keys = 'zh', desc = 'Scroll left' },
|
|
1092
|
+
{ mode = 'n', keys = 'zi', desc = "Toggle 'foldenable'" },
|
|
1093
|
+
{ mode = 'n', keys = 'zj', desc = 'Move to start of next fold' },
|
|
1094
|
+
{ mode = 'n', keys = 'zk', desc = 'Move to end of previous fold' },
|
|
1095
|
+
{ mode = 'n', keys = 'zL', desc = 'Scroll right half screen' },
|
|
1096
|
+
{ mode = 'n', keys = 'zl', desc = 'Scroll right' },
|
|
1097
|
+
{ mode = 'n', keys = 'zM', desc = 'Close all folds' },
|
|
1098
|
+
{ mode = 'n', keys = 'zm', desc = 'Fold more' },
|
|
1099
|
+
{ mode = 'n', keys = 'zN', desc = "Set 'foldenable'" },
|
|
1100
|
+
{ mode = 'n', keys = 'zn', desc = "Reset 'foldenable'" },
|
|
1101
|
+
{ mode = 'n', keys = 'zO', desc = 'Open folds recursively' },
|
|
1102
|
+
{ mode = 'n', keys = 'zo', desc = 'Open fold' },
|
|
1103
|
+
{ mode = 'n', keys = 'zP', desc = 'Paste without trailspace' },
|
|
1104
|
+
{ mode = 'n', keys = 'zp', desc = 'Paste without trailspace' },
|
|
1105
|
+
{ mode = 'n', keys = 'zR', desc = 'Open all folds' },
|
|
1106
|
+
{ mode = 'n', keys = 'zr', desc = 'Fold less' },
|
|
1107
|
+
{ mode = 'n', keys = 'zs', desc = 'Scroll to cursor on left screen side' },
|
|
1108
|
+
{ mode = 'n', keys = 'zt', desc = 'Redraw at top' },
|
|
1109
|
+
{ mode = 'n', keys = 'zu', desc = '+Undo spelling commands' },
|
|
1110
|
+
{ mode = 'n', keys = 'zug', desc = 'Undo `zg`' },
|
|
1111
|
+
{ mode = 'n', keys = 'zuG', desc = 'Undo `zG`' },
|
|
1112
|
+
{ mode = 'n', keys = 'zuw', desc = 'Undo `zw`' },
|
|
1113
|
+
{ mode = 'n', keys = 'zuW', desc = 'Undo `zW`' },
|
|
1114
|
+
{ mode = 'n', keys = 'zv', desc = 'Open enough folds' },
|
|
1115
|
+
{ mode = 'n', keys = 'zW', desc = 'Temporarily mark as incorrectly spelled' },
|
|
1116
|
+
{ mode = 'n', keys = 'zw', desc = 'Permanently mark as incorrectly spelled' },
|
|
1117
|
+
{ mode = 'n', keys = 'zX', desc = 'Update folds' },
|
|
1118
|
+
{ mode = 'n', keys = 'zx', desc = 'Update folds + open enough folds' },
|
|
1119
|
+
{ mode = 'n', keys = 'zy', desc = 'Yank without trailing spaces (operator)' },
|
|
1120
|
+
{ mode = 'n', keys = 'zz', desc = 'Redraw at center' },
|
|
1121
|
+
{ mode = 'n', keys = 'z+', desc = 'Redraw under bottom at top' },
|
|
1122
|
+
{ mode = 'n', keys = 'z-', desc = 'Redraw at bottom + cursor on first non-blank' },
|
|
1123
|
+
{ mode = 'n', keys = 'z.', desc = 'Redraw at center + cursor on first non-blank' },
|
|
1124
|
+
{ mode = 'n', keys = 'z=', desc = 'Show spelling suggestions' },
|
|
1125
|
+
{ mode = 'n', keys = 'z^', desc = 'Redraw above top at bottom' },
|
|
1126
|
+
|
|
1127
|
+
{ mode = 'x', keys = 'zf', desc = 'Create fold from selection' },
|
|
1128
|
+
}
|
|
1129
|
+
end
|
|
1130
|
+
|
|
1131
|
+
-- Helper data ================================================================
|
|
1132
|
+
-- Module default config
|
|
1133
|
+
H.default_config = vim.deepcopy(MiniClue.config)
|
|
1134
|
+
|
|
1135
|
+
-- Namespaces
|
|
1136
|
+
H.ns_id = {
|
|
1137
|
+
highlight = vim.api.nvim_create_namespace('MiniClueHighlight'),
|
|
1138
|
+
}
|
|
1139
|
+
|
|
1140
|
+
-- State of user input
|
|
1141
|
+
H.state = {
|
|
1142
|
+
trigger = nil,
|
|
1143
|
+
-- Array of raw keys
|
|
1144
|
+
query = {},
|
|
1145
|
+
clues = {},
|
|
1146
|
+
timer = vim.loop.new_timer(),
|
|
1147
|
+
buf_id = nil,
|
|
1148
|
+
win_id = nil,
|
|
1149
|
+
is_after_postkeys = false,
|
|
1150
|
+
}
|
|
1151
|
+
|
|
1152
|
+
-- Default window config
|
|
1153
|
+
H.default_win_config = {
|
|
1154
|
+
anchor = 'SE',
|
|
1155
|
+
focusable = false,
|
|
1156
|
+
relative = 'editor',
|
|
1157
|
+
style = 'minimal',
|
|
1158
|
+
width = 30,
|
|
1159
|
+
-- Use high enough value to be on top of built-in windows (pmenu, etc.)
|
|
1160
|
+
zindex = 251,
|
|
1161
|
+
}
|
|
1162
|
+
|
|
1163
|
+
-- Precomputed raw keys
|
|
1164
|
+
H.keys = {
|
|
1165
|
+
bs = vim.api.nvim_replace_termcodes('<BS>', true, true, true),
|
|
1166
|
+
cr = vim.api.nvim_replace_termcodes('<CR>', true, true, true),
|
|
1167
|
+
exit = vim.api.nvim_replace_termcodes([[<C-\><C-n>]], true, true, true),
|
|
1168
|
+
ctrl_d = vim.api.nvim_replace_termcodes('<C-d>', true, true, true),
|
|
1169
|
+
ctrl_u = vim.api.nvim_replace_termcodes('<C-u>', true, true, true),
|
|
1170
|
+
}
|
|
1171
|
+
|
|
1172
|
+
-- Special filetypes for which to enable triggers. These are common interactive
|
|
1173
|
+
-- not listed filetypes. NOTE: no 'minifiles' as `'` trigger conflicts with its
|
|
1174
|
+
-- local `'`. Plus it pollutes `g?` content.
|
|
1175
|
+
H.ft_to_enable = { help = true, git = true, ministarter = true }
|
|
1176
|
+
|
|
1177
|
+
-- Timers
|
|
1178
|
+
H.timers = {
|
|
1179
|
+
getcharstr = vim.loop.new_timer(),
|
|
1180
|
+
}
|
|
1181
|
+
|
|
1182
|
+
-- Undo autocommand to be created for several operator tweaks
|
|
1183
|
+
H.undo_autocommand = 'au ModeChanged * ++once undo!'
|
|
1184
|
+
|
|
1185
|
+
-- Helper functionality =======================================================
|
|
1186
|
+
-- Settings -------------------------------------------------------------------
|
|
1187
|
+
H.setup_config = function(config)
|
|
1188
|
+
H.check_type('config', config, 'table', true)
|
|
1189
|
+
config = vim.tbl_deep_extend('force', vim.deepcopy(H.default_config), config or {})
|
|
1190
|
+
|
|
1191
|
+
H.check_type('clues', config.clues, 'table')
|
|
1192
|
+
H.check_type('triggers', config.triggers, 'table')
|
|
1193
|
+
|
|
1194
|
+
H.check_type('window', config.window, 'table')
|
|
1195
|
+
if not (type(config.window.config) == 'table' or vim.is_callable(config.window.config)) then
|
|
1196
|
+
H.error('`window.config` should be table or callable, not ' .. type(config.window.config))
|
|
1197
|
+
end
|
|
1198
|
+
H.check_type('window.delay', config.window.delay, 'number')
|
|
1199
|
+
H.check_type('window.scroll_down', config.window.scroll_down, 'string')
|
|
1200
|
+
H.check_type('window.scroll_up', config.window.scroll_up, 'string')
|
|
1201
|
+
|
|
1202
|
+
return config
|
|
1203
|
+
end
|
|
1204
|
+
|
|
1205
|
+
H.apply_config = function(config)
|
|
1206
|
+
MiniClue.config = config
|
|
1207
|
+
|
|
1208
|
+
-- Create trigger keymaps for all existing buffers
|
|
1209
|
+
MiniClue.enable_all_triggers()
|
|
1210
|
+
|
|
1211
|
+
-- Tweak macro execution
|
|
1212
|
+
local exec_macro = function(key, register)
|
|
1213
|
+
if register == nil then return end
|
|
1214
|
+
MiniClue.disable_all_triggers()
|
|
1215
|
+
vim.schedule(function() MiniClue.enable_all_triggers() end)
|
|
1216
|
+
-- NOTE: Use `t` flag for "Handle as if typed" for better integration with
|
|
1217
|
+
-- other modules/plugins (like 'mini.jump').
|
|
1218
|
+
pcall(vim.api.nvim_feedkeys, vim.v.count1 .. key .. register, 'nt', false)
|
|
1219
|
+
end
|
|
1220
|
+
|
|
1221
|
+
local macro_keymap_opts = { nowait = true, desc = "Execute macro without 'mini.clue' triggers" }
|
|
1222
|
+
|
|
1223
|
+
local exec_register_macro = function() exec_macro('@', H.getcharstr()) end
|
|
1224
|
+
if vim.fn.maparg('@', 'n') == '' then vim.keymap.set('n', '@', exec_register_macro, macro_keymap_opts) end
|
|
1225
|
+
|
|
1226
|
+
local exec_latest_macro = function() exec_macro('Q', '') end
|
|
1227
|
+
if vim.fn.maparg('Q', 'n') == '' then vim.keymap.set('n', 'Q', exec_latest_macro, macro_keymap_opts) end
|
|
1228
|
+
end
|
|
1229
|
+
|
|
1230
|
+
H.is_disabled = function(buf_id)
|
|
1231
|
+
local buf_disable = H.get_buf_var(buf_id, 'miniclue_disable')
|
|
1232
|
+
return vim.g.miniclue_disable == true or buf_disable == true
|
|
1233
|
+
end
|
|
1234
|
+
|
|
1235
|
+
H.create_autocommands = function()
|
|
1236
|
+
local gr = vim.api.nvim_create_augroup('MiniClue', {})
|
|
1237
|
+
|
|
1238
|
+
local au = function(event, pattern, callback, desc)
|
|
1239
|
+
vim.api.nvim_create_autocmd(event, { group = gr, pattern = pattern, callback = callback, desc = desc })
|
|
1240
|
+
end
|
|
1241
|
+
|
|
1242
|
+
-- Ensure buffer-local mappings for triggers are the latest ones to fully
|
|
1243
|
+
-- utilize `<nowait>`. Use `vim.schedule_wrap` to allow other events to
|
|
1244
|
+
-- create `vim.b.miniclue_config` and `vim.b.miniclue_disable`.
|
|
1245
|
+
-- Check for listed buffer in `BufWinEnter` (instead of using `BufAdd`) to
|
|
1246
|
+
-- delay acting until buffer is loaded (otherwise buffer-local options can be
|
|
1247
|
+
-- prematurely "finalized"). Process it at most once for performance.
|
|
1248
|
+
local did_ensure = {}
|
|
1249
|
+
local ensure_triggers = vim.schedule_wrap(function(ev)
|
|
1250
|
+
if not H.is_valid_buf(ev.buf) then return end
|
|
1251
|
+
local skip_triggers = ev.event == 'BufWinEnter' and (did_ensure[ev.buf] or vim.fn.buflisted(ev.buf) ~= 1)
|
|
1252
|
+
did_ensure[ev.buf] = true
|
|
1253
|
+
if skip_triggers then return end
|
|
1254
|
+
MiniClue.ensure_buf_triggers(ev.buf)
|
|
1255
|
+
end)
|
|
1256
|
+
-- - Respect `LspAttach` as it is a common source of buffer-local mappings
|
|
1257
|
+
au({ 'BufWinEnter', 'LspAttach' }, '*', ensure_triggers, 'Ensure buffer-local trigger keymaps')
|
|
1258
|
+
au('BufUnload', '*', function(ev) did_ensure[ev.buf] = nil end, 'Track buffer-local trigger keymaps')
|
|
1259
|
+
au('Filetype', vim.tbl_keys(H.ft_to_enable), ensure_triggers, 'Ensure buffer-local trigger keymaps')
|
|
1260
|
+
|
|
1261
|
+
-- Disable all triggers (current and future) when recording macro as they
|
|
1262
|
+
-- interfere with what is actually recorded
|
|
1263
|
+
local cache_disable
|
|
1264
|
+
local disable_all_plus = function()
|
|
1265
|
+
MiniClue.disable_all_triggers()
|
|
1266
|
+
cache_disable = vim.g.miniclue_disable
|
|
1267
|
+
vim.g.miniclue_disable = true
|
|
1268
|
+
end
|
|
1269
|
+
local enable_all_plus = function()
|
|
1270
|
+
vim.g.miniclue_disable = cache_disable
|
|
1271
|
+
MiniClue.enable_all_triggers()
|
|
1272
|
+
end
|
|
1273
|
+
au('RecordingEnter', '*', disable_all_plus, 'Disable all triggers')
|
|
1274
|
+
au('RecordingLeave', '*', enable_all_plus, 'Enable all triggers')
|
|
1275
|
+
|
|
1276
|
+
au('VimResized', '*', H.window_update, 'Update window on resize')
|
|
1277
|
+
au('ColorScheme', '*', H.create_default_hl, 'Ensure colors')
|
|
1278
|
+
end
|
|
1279
|
+
|
|
1280
|
+
--stylua: ignore
|
|
1281
|
+
H.create_default_hl = function()
|
|
1282
|
+
local hi = function(name, opts)
|
|
1283
|
+
opts.default = true
|
|
1284
|
+
vim.api.nvim_set_hl(0, name, opts)
|
|
1285
|
+
end
|
|
1286
|
+
|
|
1287
|
+
hi('MiniClueBorder', { link = 'FloatBorder' })
|
|
1288
|
+
hi('MiniClueDescGroup', { link = 'DiagnosticFloatingWarn' })
|
|
1289
|
+
hi('MiniClueDescSingle', { link = 'NormalFloat' })
|
|
1290
|
+
hi('MiniClueNextKey', { link = 'DiagnosticFloatingHint' })
|
|
1291
|
+
hi('MiniClueNextKeyWithPostkeys', { link = 'DiagnosticFloatingError' })
|
|
1292
|
+
hi('MiniClueSeparator', { link = 'DiagnosticFloatingInfo' })
|
|
1293
|
+
hi('MiniClueTitle', { link = 'FloatTitle' })
|
|
1294
|
+
end
|
|
1295
|
+
|
|
1296
|
+
H.get_config = function(config, buf_id)
|
|
1297
|
+
config = config or {}
|
|
1298
|
+
local buf_config = H.get_buf_var(buf_id, 'miniclue_config') or {}
|
|
1299
|
+
local global_config = MiniClue.config
|
|
1300
|
+
|
|
1301
|
+
-- Manually reconstruct to allow array elements to be concatenated
|
|
1302
|
+
local res = {
|
|
1303
|
+
clues = H.list_concat(global_config.clues, buf_config.clues, config.clues),
|
|
1304
|
+
triggers = H.list_concat(global_config.triggers, buf_config.triggers, config.triggers),
|
|
1305
|
+
window = vim.tbl_deep_extend('force', global_config.window, buf_config.window or {}, config.window or {}),
|
|
1306
|
+
}
|
|
1307
|
+
return res
|
|
1308
|
+
end
|
|
1309
|
+
|
|
1310
|
+
H.get_buf_var = function(buf_id, name)
|
|
1311
|
+
buf_id = buf_id or vim.api.nvim_get_current_buf()
|
|
1312
|
+
if not H.is_valid_buf(buf_id) then return nil end
|
|
1313
|
+
return vim.b[buf_id][name]
|
|
1314
|
+
end
|
|
1315
|
+
|
|
1316
|
+
-- Triggers -------------------------------------------------------------------
|
|
1317
|
+
H.map_buf_triggers = function(buf_id)
|
|
1318
|
+
if not H.is_valid_buf(buf_id) or H.is_disabled(buf_id) then return end
|
|
1319
|
+
|
|
1320
|
+
for _, trigger in ipairs(H.get_config(nil, buf_id).triggers) do
|
|
1321
|
+
local modes = type(trigger.mode) == 'table' and trigger.mode or { trigger.mode }
|
|
1322
|
+
for _, mode in ipairs(modes) do
|
|
1323
|
+
H.map_trigger(buf_id, { mode = mode, keys = trigger.keys })
|
|
1324
|
+
end
|
|
1325
|
+
end
|
|
1326
|
+
end
|
|
1327
|
+
|
|
1328
|
+
H.unmap_buf_triggers = function(buf_id)
|
|
1329
|
+
if not H.is_valid_buf(buf_id) or H.is_disabled(buf_id) then return end
|
|
1330
|
+
|
|
1331
|
+
for _, trigger in ipairs(H.get_config(nil, buf_id).triggers) do
|
|
1332
|
+
local modes = type(trigger.mode) == 'table' and trigger.mode or { trigger.mode }
|
|
1333
|
+
for _, mode in ipairs(modes) do
|
|
1334
|
+
H.unmap_trigger(buf_id, { mode = mode, keys = trigger.keys })
|
|
1335
|
+
end
|
|
1336
|
+
end
|
|
1337
|
+
end
|
|
1338
|
+
|
|
1339
|
+
H.map_trigger = function(buf_id, trigger)
|
|
1340
|
+
if not H.is_valid_buf(buf_id) then return end
|
|
1341
|
+
|
|
1342
|
+
-- Compute mapping RHS
|
|
1343
|
+
trigger.keys = H.replace_termcodes(trigger.keys)
|
|
1344
|
+
local lhs = H.keytrans(trigger.keys)
|
|
1345
|
+
|
|
1346
|
+
local is_ministarter_map = vim.bo[buf_id].filetype == 'ministarter'
|
|
1347
|
+
and vim.api.nvim_buf_call(buf_id, function() return vim.fn.maparg(lhs, trigger.mode) ~= '' end)
|
|
1348
|
+
if is_ministarter_map then return end
|
|
1349
|
+
|
|
1350
|
+
local rhs = function()
|
|
1351
|
+
-- Don't act if for some reason entered the same trigger during state exec
|
|
1352
|
+
local is_in_exec = type(H.exec_trigger) == 'table'
|
|
1353
|
+
and H.exec_trigger.mode == trigger.mode
|
|
1354
|
+
and H.exec_trigger.keys == trigger.keys
|
|
1355
|
+
if is_in_exec then
|
|
1356
|
+
H.exec_trigger = nil
|
|
1357
|
+
return
|
|
1358
|
+
end
|
|
1359
|
+
|
|
1360
|
+
-- Start user query
|
|
1361
|
+
H.state_set(trigger, { trigger.keys })
|
|
1362
|
+
|
|
1363
|
+
-- Do not advance if no other clues to query. NOTE: it is `<= 1` and not
|
|
1364
|
+
-- `<= 0` because the "init query" mapping should match.
|
|
1365
|
+
if vim.tbl_count(H.state.clues) <= 1 then return H.state_exec() end
|
|
1366
|
+
|
|
1367
|
+
H.state_advance()
|
|
1368
|
+
end
|
|
1369
|
+
|
|
1370
|
+
-- Use buffer-local mappings and `nowait` to make it a primary source of
|
|
1371
|
+
-- keymap execution
|
|
1372
|
+
local desc = string.format('Query keys after "%s"', lhs)
|
|
1373
|
+
local opts = { buffer = buf_id, nowait = true, desc = desc }
|
|
1374
|
+
|
|
1375
|
+
-- Create mapping. Use translated variant to make it work with <F*> keys.
|
|
1376
|
+
vim.keymap.set(trigger.mode, lhs, rhs, opts)
|
|
1377
|
+
end
|
|
1378
|
+
|
|
1379
|
+
H.unmap_trigger = function(buf_id, trigger)
|
|
1380
|
+
if not H.is_valid_buf(buf_id) then return end
|
|
1381
|
+
pcall(vim.keymap.del, trigger.mode, H.keytrans(trigger.keys), { buffer = buf_id })
|
|
1382
|
+
end
|
|
1383
|
+
|
|
1384
|
+
-- State ----------------------------------------------------------------------
|
|
1385
|
+
H.state_advance = function(opts)
|
|
1386
|
+
opts = opts or {}
|
|
1387
|
+
local config_window = H.get_config().window
|
|
1388
|
+
|
|
1389
|
+
-- Show clues: delay (debounce) first show; update immediately if shown or
|
|
1390
|
+
-- after postkeys (for visual feedback that extra key is needed to stop)
|
|
1391
|
+
H.state.timer:stop()
|
|
1392
|
+
local show_immediately = H.is_valid_win(H.state.win_id) or H.state.is_after_postkeys
|
|
1393
|
+
local delay = show_immediately and 0 or config_window.delay
|
|
1394
|
+
H.state.timer:start(delay, 0, function() H.window_update(opts.same_content) end)
|
|
1395
|
+
|
|
1396
|
+
-- Reset postkeys right now to not flicker when trying to close window during
|
|
1397
|
+
-- "not querying" check
|
|
1398
|
+
H.state.is_after_postkeys = false
|
|
1399
|
+
|
|
1400
|
+
-- Query user for new key
|
|
1401
|
+
local key = H.getcharstr()
|
|
1402
|
+
|
|
1403
|
+
-- Handle key
|
|
1404
|
+
if key == nil then return H.state_reset() end
|
|
1405
|
+
|
|
1406
|
+
if key == H.keys.cr then return H.state_exec() end
|
|
1407
|
+
|
|
1408
|
+
local is_window_shown = H.is_valid_win(H.state.win_id)
|
|
1409
|
+
local is_scroll_down = key == H.replace_termcodes(config_window.scroll_down)
|
|
1410
|
+
local is_scroll_up = key == H.replace_termcodes(config_window.scroll_up)
|
|
1411
|
+
if is_window_shown and (is_scroll_down or is_scroll_up) then
|
|
1412
|
+
H.window_scroll(is_scroll_down)
|
|
1413
|
+
return H.state_advance({ same_content = true })
|
|
1414
|
+
end
|
|
1415
|
+
|
|
1416
|
+
if key == H.keys.bs then
|
|
1417
|
+
H.state_pop()
|
|
1418
|
+
else
|
|
1419
|
+
H.state_push(key)
|
|
1420
|
+
end
|
|
1421
|
+
|
|
1422
|
+
-- Advance state
|
|
1423
|
+
-- - Execute if reached single target keymap
|
|
1424
|
+
if H.state_is_at_target() then return H.state_exec() end
|
|
1425
|
+
|
|
1426
|
+
-- - Reset if there are no keys (like after `<BS>`)
|
|
1427
|
+
if #H.state.query == 0 then return H.state_reset() end
|
|
1428
|
+
|
|
1429
|
+
-- - Query user for more information if there is not enough
|
|
1430
|
+
-- NOTE: still advance even if there is single clue because it is still not
|
|
1431
|
+
-- a target but can be one.
|
|
1432
|
+
if vim.tbl_count(H.state.clues) >= 1 then return H.state_advance() end
|
|
1433
|
+
|
|
1434
|
+
-- - Fall back for executing what user typed
|
|
1435
|
+
H.state_exec()
|
|
1436
|
+
end
|
|
1437
|
+
|
|
1438
|
+
H.state_set = function(trigger, query)
|
|
1439
|
+
H.state.trigger = trigger
|
|
1440
|
+
H.state.query = query
|
|
1441
|
+
H.state.clues = H.clues_filter(H.clues_get_all(trigger.mode), query)
|
|
1442
|
+
end
|
|
1443
|
+
|
|
1444
|
+
H.state_reset = function(keep_window)
|
|
1445
|
+
H.state.trigger = nil
|
|
1446
|
+
H.state.query = {}
|
|
1447
|
+
H.state.clues = {}
|
|
1448
|
+
H.state.is_after_postkeys = false
|
|
1449
|
+
|
|
1450
|
+
H.state.timer:stop()
|
|
1451
|
+
if not keep_window then H.window_close() end
|
|
1452
|
+
end
|
|
1453
|
+
|
|
1454
|
+
H.state_exec = function()
|
|
1455
|
+
-- Compute keys to type
|
|
1456
|
+
local keys_to_type = H.compute_exec_keys()
|
|
1457
|
+
|
|
1458
|
+
-- Add extra (redundant) safety flag to try to avoid infinite recursion
|
|
1459
|
+
local trigger, clue = H.state.trigger, H.state_get_query_clue()
|
|
1460
|
+
H.exec_trigger = trigger
|
|
1461
|
+
vim.schedule(function() H.exec_trigger = nil end)
|
|
1462
|
+
|
|
1463
|
+
-- Reset state
|
|
1464
|
+
local has_postkeys = (clue or {}).postkeys ~= nil
|
|
1465
|
+
H.state_reset(has_postkeys)
|
|
1466
|
+
|
|
1467
|
+
-- Disable trigger !!!VERY IMPORTANT!!!
|
|
1468
|
+
-- This is a workaround against infinite recursion (like if `g` is trigger
|
|
1469
|
+
-- then typing `gg`/`g~` would introduce infinite recursion).
|
|
1470
|
+
local buf_id = vim.api.nvim_get_current_buf()
|
|
1471
|
+
H.unmap_trigger(buf_id, trigger)
|
|
1472
|
+
|
|
1473
|
+
-- Execute keys. The `i` flag is used to fully support Operator-pending mode.
|
|
1474
|
+
-- Flag `t` imitates keys as if user typed, which is reasonable but has small
|
|
1475
|
+
-- downside with edge cases of 'langmap' (like ':\;;\;:') as it "inverts" key
|
|
1476
|
+
-- meaning second time (at least in Normal mode).
|
|
1477
|
+
vim.api.nvim_feedkeys(keys_to_type, 'mit', false)
|
|
1478
|
+
|
|
1479
|
+
-- Enable trigger back after it can no longer harm
|
|
1480
|
+
vim.schedule(function() H.map_trigger(buf_id, trigger) end)
|
|
1481
|
+
|
|
1482
|
+
-- Apply postkeys (in scheduled fashion)
|
|
1483
|
+
if has_postkeys then H.state_apply_postkeys(clue.postkeys) end
|
|
1484
|
+
end
|
|
1485
|
+
|
|
1486
|
+
H.state_push = function(keys)
|
|
1487
|
+
table.insert(H.state.query, keys)
|
|
1488
|
+
H.state.clues = H.clues_filter(H.state.clues, H.state.query)
|
|
1489
|
+
end
|
|
1490
|
+
|
|
1491
|
+
H.state_pop = function()
|
|
1492
|
+
H.state.query[#H.state.query] = nil
|
|
1493
|
+
H.state.clues = H.clues_filter(H.clues_get_all(H.state.trigger.mode), H.state.query)
|
|
1494
|
+
end
|
|
1495
|
+
|
|
1496
|
+
H.state_apply_postkeys = vim.schedule_wrap(function(postkeys)
|
|
1497
|
+
-- Register that possible future querying is a result of postkeys.
|
|
1498
|
+
-- This enables (keep) showing window immediately.
|
|
1499
|
+
H.state.is_after_postkeys = true
|
|
1500
|
+
|
|
1501
|
+
-- Use `nvim_feedkeys()` because using `state_set()` and
|
|
1502
|
+
-- `state_advance()` directly does not work: it doesn't guarantee to be
|
|
1503
|
+
-- executed **after** keys from `nvim_feedkeys()`.
|
|
1504
|
+
vim.api.nvim_feedkeys(postkeys, 'mit', false)
|
|
1505
|
+
|
|
1506
|
+
-- Defer check of whether postkeys resulted into window.
|
|
1507
|
+
-- Could not find proper way to check this which guarantees to be executed
|
|
1508
|
+
-- after `nvim_feedkeys()` takes effect **end** doesn't result into flicker
|
|
1509
|
+
-- when consecutively applying "submode" keys.
|
|
1510
|
+
vim.defer_fn(function()
|
|
1511
|
+
if #H.state.query == 0 then H.window_close() end
|
|
1512
|
+
end, 50)
|
|
1513
|
+
end)
|
|
1514
|
+
|
|
1515
|
+
H.state_is_at_target = function()
|
|
1516
|
+
return vim.tbl_count(H.state.clues) == 1 and H.state.clues[H.query_to_keys(H.state.query)] ~= nil
|
|
1517
|
+
end
|
|
1518
|
+
|
|
1519
|
+
H.state_get_query_clue = function()
|
|
1520
|
+
local keys = H.query_to_keys(H.state.query)
|
|
1521
|
+
return H.state.clues[keys]
|
|
1522
|
+
end
|
|
1523
|
+
|
|
1524
|
+
H.compute_exec_keys = function()
|
|
1525
|
+
local keys_count = vim.v.count > 0 and vim.v.count or ''
|
|
1526
|
+
local keys_query = H.query_to_keys(H.state.query)
|
|
1527
|
+
local res = keys_count .. keys_query
|
|
1528
|
+
|
|
1529
|
+
local cur_mode = vim.fn.mode(1)
|
|
1530
|
+
|
|
1531
|
+
-- Using `feedkeys()` inside Operator-pending mode leads to its cancel into
|
|
1532
|
+
-- Normal/Insert mode so extra work should be done to rebuild all keys
|
|
1533
|
+
if vim.startswith(cur_mode, 'no') then
|
|
1534
|
+
local operator_tweak = H.operator_tweaks[vim.v.operator] or function(x) return x end
|
|
1535
|
+
res = operator_tweak(vim.v.operator .. H.get_forced_submode() .. res)
|
|
1536
|
+
elseif not vim.startswith(cur_mode, 'i') and H.get_default_register() ~= vim.v.register then
|
|
1537
|
+
-- Force non-default register but not in Insert mode
|
|
1538
|
+
local expr_reg_keys = vim.v.register == '=' and (vim.fn.getreginfo('=').regcontents[1] .. '\r') or ''
|
|
1539
|
+
res = '"' .. vim.v.register .. expr_reg_keys .. res
|
|
1540
|
+
end
|
|
1541
|
+
|
|
1542
|
+
-- `feedkeys()` inside "temporary" Normal mode is executed **after** it is
|
|
1543
|
+
-- already back from Normal mode. Go into it again with `<C-o>` ('\15').
|
|
1544
|
+
-- NOTE: This only works when Normal mode trigger is triggered in
|
|
1545
|
+
-- "temporary" Normal mode. Still doesn't work when Operator-pending mode is
|
|
1546
|
+
-- triggered afterwards (like in `<C-o>gUiw` with 'i' as trigger).
|
|
1547
|
+
if cur_mode:find('^ni') ~= nil then res = '\15' .. res end
|
|
1548
|
+
|
|
1549
|
+
return res
|
|
1550
|
+
end
|
|
1551
|
+
|
|
1552
|
+
-- Some operators needs special tweaking due to their nature:
|
|
1553
|
+
-- - Some operators perform on register. Solution: add register explicitly.
|
|
1554
|
+
-- - Some operators end up changing mode which affects `feedkeys()`.
|
|
1555
|
+
-- Solution: explicitly exit to Normal mode with '<C-\><C-n>'.
|
|
1556
|
+
-- - Some operators still perform some redundant operation before `feedkeys()`
|
|
1557
|
+
-- takes effect. Solution: add one-shot autocommand undoing that.
|
|
1558
|
+
H.operator_tweaks = {
|
|
1559
|
+
['c'] = function(keys)
|
|
1560
|
+
-- Doing '<C-\><C-n>' moves cursor one space to left (same as `i<Esc>`).
|
|
1561
|
+
-- Solution: add one-shot autocommand correcting cursor position.
|
|
1562
|
+
vim.cmd('au InsertLeave * ++once normal! l')
|
|
1563
|
+
return H.keys.exit .. '"' .. vim.v.register .. keys
|
|
1564
|
+
end,
|
|
1565
|
+
['d'] = function(keys) return '"' .. vim.v.register .. keys end,
|
|
1566
|
+
['y'] = function(keys) return '"' .. vim.v.register .. keys end,
|
|
1567
|
+
['~'] = function(keys)
|
|
1568
|
+
if vim.fn.col('.') == 1 then vim.cmd(H.undo_autocommand) end
|
|
1569
|
+
return keys
|
|
1570
|
+
end,
|
|
1571
|
+
['g~'] = function(keys)
|
|
1572
|
+
if vim.fn.col('.') == 1 then vim.cmd(H.undo_autocommand) end
|
|
1573
|
+
return keys
|
|
1574
|
+
end,
|
|
1575
|
+
['g?'] = function(keys)
|
|
1576
|
+
if vim.fn.col('.') == 1 then vim.cmd(H.undo_autocommand) end
|
|
1577
|
+
return keys
|
|
1578
|
+
end,
|
|
1579
|
+
['!'] = function(keys) return H.keys.exit .. keys end,
|
|
1580
|
+
['>'] = function(keys)
|
|
1581
|
+
vim.cmd(H.undo_autocommand)
|
|
1582
|
+
return keys
|
|
1583
|
+
end,
|
|
1584
|
+
['<'] = function(keys)
|
|
1585
|
+
vim.cmd(H.undo_autocommand)
|
|
1586
|
+
return keys
|
|
1587
|
+
end,
|
|
1588
|
+
['g@'] = function(keys)
|
|
1589
|
+
-- Cancelling in-process `g@` operator seems to be particularly hard.
|
|
1590
|
+
-- Not even sure why specifically this combination works, but having `x`
|
|
1591
|
+
-- flag in `feedkeys()` is crucial.
|
|
1592
|
+
vim.api.nvim_feedkeys(H.keys.exit, 'nx', false)
|
|
1593
|
+
return H.keys.exit .. keys
|
|
1594
|
+
end,
|
|
1595
|
+
}
|
|
1596
|
+
|
|
1597
|
+
H.query_to_keys = function(query) return table.concat(query, '') end
|
|
1598
|
+
|
|
1599
|
+
-- Window ---------------------------------------------------------------------
|
|
1600
|
+
H.window_update = vim.schedule_wrap(function(same_content)
|
|
1601
|
+
-- Make sure that outdated windows are not shown
|
|
1602
|
+
if #H.state.query == 0 then return H.window_close() end
|
|
1603
|
+
local win_id = H.state.win_id
|
|
1604
|
+
|
|
1605
|
+
-- Close window if it is not in current tabpage (as only window is tracked)
|
|
1606
|
+
local is_different_tabpage = H.is_valid_win(win_id)
|
|
1607
|
+
and vim.api.nvim_win_get_tabpage(win_id) ~= vim.api.nvim_get_current_tabpage()
|
|
1608
|
+
if is_different_tabpage then H.window_close() end
|
|
1609
|
+
|
|
1610
|
+
-- Create-update buffer showing clues
|
|
1611
|
+
if not same_content then H.state.buf_id = H.buffer_update() end
|
|
1612
|
+
|
|
1613
|
+
-- Create-update window showing buffer
|
|
1614
|
+
local win_config = H.window_get_config()
|
|
1615
|
+
if not H.is_valid_win(win_id) then
|
|
1616
|
+
win_config.noautocmd = true
|
|
1617
|
+
win_id = H.window_open(win_config)
|
|
1618
|
+
H.state.win_id = win_id
|
|
1619
|
+
else
|
|
1620
|
+
vim.api.nvim_win_set_config(win_id, win_config)
|
|
1621
|
+
vim.wo[win_id].list = true
|
|
1622
|
+
end
|
|
1623
|
+
|
|
1624
|
+
-- Make scroll not persist. NOTE: Don't use 'normal! gg' inside target window
|
|
1625
|
+
-- as it resets `v:count` and `v:register` which results into invalid keys
|
|
1626
|
+
-- reproduction in Operator-pending mode.
|
|
1627
|
+
if not same_content then vim.api.nvim_win_set_cursor(win_id, { 1, 0 }) end
|
|
1628
|
+
|
|
1629
|
+
-- Add redraw because Neovim won't do it when `getcharstr()` is active
|
|
1630
|
+
vim.cmd('redraw')
|
|
1631
|
+
end)
|
|
1632
|
+
|
|
1633
|
+
H.window_scroll = function(is_scroll_down)
|
|
1634
|
+
local scroll_key = is_scroll_down and H.keys.ctrl_d or H.keys.ctrl_u
|
|
1635
|
+
local f = function()
|
|
1636
|
+
local cache_scroll, bot_line, n_lines = vim.wo.scroll, vim.fn.line('w$'), vim.api.nvim_buf_line_count(0)
|
|
1637
|
+
-- Do not scroll past the end of buffer
|
|
1638
|
+
local scroll_count = is_scroll_down and math.min(cache_scroll, n_lines - bot_line) or cache_scroll
|
|
1639
|
+
if scroll_count > 0 then pcall(vim.cmd, 'normal! ' .. scroll_count .. scroll_key) end
|
|
1640
|
+
vim.wo.scroll = cache_scroll
|
|
1641
|
+
end
|
|
1642
|
+
vim.api.nvim_win_call(H.state.win_id, f)
|
|
1643
|
+
end
|
|
1644
|
+
|
|
1645
|
+
H.window_open = function(config)
|
|
1646
|
+
local win_id = vim.api.nvim_open_win(H.state.buf_id, false, config)
|
|
1647
|
+
|
|
1648
|
+
vim.wo[win_id].foldenable = false
|
|
1649
|
+
vim.wo[win_id].foldmethod = 'manual'
|
|
1650
|
+
vim.wo[win_id].wrap = false
|
|
1651
|
+
vim.wo[win_id].list = true
|
|
1652
|
+
vim.wo[win_id].listchars = 'extends:…'
|
|
1653
|
+
|
|
1654
|
+
local win_hl = 'FloatBorder:MiniClueBorder,FloatTitle:MiniClueTitle'
|
|
1655
|
+
vim.wo[win_id].winhighlight = win_hl
|
|
1656
|
+
|
|
1657
|
+
return win_id
|
|
1658
|
+
end
|
|
1659
|
+
|
|
1660
|
+
H.window_close = function()
|
|
1661
|
+
-- Closing floating window when Command-line window is active is not allowed
|
|
1662
|
+
-- on Neovim<0.10. Make sure it is closed after leaving it.
|
|
1663
|
+
-- See https://github.com/neovim/neovim/issues/24452
|
|
1664
|
+
local win_id = H.state.win_id
|
|
1665
|
+
if vim.fn.has('nvim-0.10') == 0 and vim.fn.getcmdwintype() ~= '' then
|
|
1666
|
+
vim.api.nvim_create_autocmd(
|
|
1667
|
+
'CmdwinLeave',
|
|
1668
|
+
{ once = true, callback = function() pcall(vim.api.nvim_win_close, win_id, true) end }
|
|
1669
|
+
)
|
|
1670
|
+
return
|
|
1671
|
+
else
|
|
1672
|
+
pcall(vim.api.nvim_win_close, win_id, true)
|
|
1673
|
+
end
|
|
1674
|
+
|
|
1675
|
+
H.state.win_id = nil
|
|
1676
|
+
end
|
|
1677
|
+
|
|
1678
|
+
H.window_get_config = function()
|
|
1679
|
+
local has_statusline = vim.o.laststatus > 0
|
|
1680
|
+
local has_tabline = vim.o.showtabline == 2 or (vim.o.showtabline == 1 and #vim.api.nvim_list_tabpages() > 1)
|
|
1681
|
+
-- Remove 2 from maximum height to account for top and bottom borders
|
|
1682
|
+
local max_height = vim.o.lines - vim.o.cmdheight - (has_tabline and 1 or 0) - (has_statusline and 1 or 0) - 2
|
|
1683
|
+
max_height = math.max(max_height, 1)
|
|
1684
|
+
|
|
1685
|
+
local keys = H.query_to_keys(H.state.query)
|
|
1686
|
+
local query_clue = (H.state.clues[keys] or {}).desc or ''
|
|
1687
|
+
local title = (#H.state.query <= 1 or query_clue == '') and H.keytrans(keys) or query_clue
|
|
1688
|
+
|
|
1689
|
+
local buf_id = H.state.buf_id
|
|
1690
|
+
local cur_config_fields = {
|
|
1691
|
+
row = vim.o.lines - vim.o.cmdheight - (has_statusline and 1 or 0),
|
|
1692
|
+
col = vim.o.columns,
|
|
1693
|
+
height = math.min(vim.api.nvim_buf_line_count(buf_id), max_height),
|
|
1694
|
+
title = ' ' .. title .. ' ',
|
|
1695
|
+
border = (vim.fn.exists('+winborder') == 0 or vim.o.winborder == '') and 'single' or nil,
|
|
1696
|
+
}
|
|
1697
|
+
local user_config = H.expand_callable(H.get_config().window.config, buf_id) or {}
|
|
1698
|
+
local res = vim.tbl_deep_extend('force', H.default_win_config, cur_config_fields, user_config)
|
|
1699
|
+
|
|
1700
|
+
-- Tweak "auto" fields
|
|
1701
|
+
if res.width == 'auto' then res.width = H.buffer_get_width() + 1 end
|
|
1702
|
+
|
|
1703
|
+
if res.row == 'auto' then
|
|
1704
|
+
local is_on_top = res.anchor == 'NW' or res.anchor == 'NE'
|
|
1705
|
+
res.row = is_on_top and (has_tabline and 1 or 0) or cur_config_fields.row
|
|
1706
|
+
end
|
|
1707
|
+
|
|
1708
|
+
if res.col == 'auto' then
|
|
1709
|
+
local is_on_left = res.anchor == 'NW' or res.anchor == 'SW'
|
|
1710
|
+
res.col = is_on_left and 0 or cur_config_fields.col
|
|
1711
|
+
end
|
|
1712
|
+
|
|
1713
|
+
-- Ensure proper config
|
|
1714
|
+
res.width = math.min(math.max(res.width, 1), vim.o.columns - 2)
|
|
1715
|
+
if type(res.title) == 'string' then res.title = H.fit_to_width(res.title, res.width) end
|
|
1716
|
+
|
|
1717
|
+
return res
|
|
1718
|
+
end
|
|
1719
|
+
|
|
1720
|
+
-- Buffer ---------------------------------------------------------------------
|
|
1721
|
+
H.buffer_update = function()
|
|
1722
|
+
local buf_id = H.state.buf_id
|
|
1723
|
+
if not H.is_loaded_buf(buf_id) then
|
|
1724
|
+
pcall(vim.api.nvim_buf_delete, buf_id, { force = true })
|
|
1725
|
+
buf_id = vim.api.nvim_create_buf(false, true)
|
|
1726
|
+
H.set_buf_name(buf_id, 'content')
|
|
1727
|
+
end
|
|
1728
|
+
|
|
1729
|
+
-- Compute content data
|
|
1730
|
+
local keys = H.query_to_keys(H.state.query)
|
|
1731
|
+
local content = H.clues_to_buffer_content(H.state.clues, keys)
|
|
1732
|
+
|
|
1733
|
+
-- Add lines
|
|
1734
|
+
local lines = {}
|
|
1735
|
+
for _, line_content in ipairs(content) do
|
|
1736
|
+
table.insert(lines, string.format(' %s │ %s', line_content.next_key, line_content.desc))
|
|
1737
|
+
end
|
|
1738
|
+
vim.api.nvim_buf_set_lines(buf_id, 0, -1, false, lines)
|
|
1739
|
+
|
|
1740
|
+
-- Add highlighting
|
|
1741
|
+
local ns_id = H.ns_id.highlight
|
|
1742
|
+
vim.api.nvim_buf_clear_namespace(buf_id, ns_id, 0, -1)
|
|
1743
|
+
|
|
1744
|
+
local set_hl = function(hl_group, line_from, col_from, line_to, col_to)
|
|
1745
|
+
local opts = { end_row = line_to, end_col = col_to, hl_group = hl_group, hl_eol = true }
|
|
1746
|
+
vim.api.nvim_buf_set_extmark(buf_id, ns_id, line_from, col_from, opts)
|
|
1747
|
+
end
|
|
1748
|
+
|
|
1749
|
+
for i, line_content in ipairs(content) do
|
|
1750
|
+
local sep_start = line_content.next_key:len() + 3
|
|
1751
|
+
local next_key_hl_group = line_content.has_postkeys and 'MiniClueNextKeyWithPostkeys' or 'MiniClueNextKey'
|
|
1752
|
+
set_hl(next_key_hl_group, i - 1, 0, i - 1, sep_start - 1)
|
|
1753
|
+
|
|
1754
|
+
-- NOTE: Separator '│' is 3 bytes long
|
|
1755
|
+
set_hl('MiniClueSeparator', i - 1, sep_start - 1, i - 1, sep_start + 2)
|
|
1756
|
+
|
|
1757
|
+
local desc_hl_group = line_content.is_group and 'MiniClueDescGroup' or 'MiniClueDescSingle'
|
|
1758
|
+
set_hl(desc_hl_group, i - 1, sep_start + 2, i, 0)
|
|
1759
|
+
end
|
|
1760
|
+
|
|
1761
|
+
return buf_id
|
|
1762
|
+
end
|
|
1763
|
+
|
|
1764
|
+
H.buffer_get_width = function()
|
|
1765
|
+
if not H.is_valid_buf(H.state.buf_id) then return end
|
|
1766
|
+
local lines = vim.api.nvim_buf_get_lines(H.state.buf_id, 0, -1, false)
|
|
1767
|
+
local res = 0
|
|
1768
|
+
for _, l in ipairs(lines) do
|
|
1769
|
+
res = math.max(res, vim.fn.strdisplaywidth(l))
|
|
1770
|
+
end
|
|
1771
|
+
return res
|
|
1772
|
+
end
|
|
1773
|
+
|
|
1774
|
+
-- Clues ----------------------------------------------------------------------
|
|
1775
|
+
H.clues_get_all = function(mode)
|
|
1776
|
+
local res = {}
|
|
1777
|
+
|
|
1778
|
+
-- Order of clue precedence: config clues < buffer mappings < global mappings
|
|
1779
|
+
local config_clues = H.clues_normalize(H.get_config().clues) or {}
|
|
1780
|
+
local mode_filter = function(x) return type(x.mode) == 'table' and vim.tbl_contains(x.mode, mode) or x.mode == mode end
|
|
1781
|
+
local mode_clues = vim.tbl_filter(mode_filter, config_clues)
|
|
1782
|
+
for _, clue in ipairs(mode_clues) do
|
|
1783
|
+
local lhsraw = H.replace_termcodes(clue.keys)
|
|
1784
|
+
|
|
1785
|
+
local res_data = res[lhsraw] or {}
|
|
1786
|
+
|
|
1787
|
+
-- - Allow callable clue description
|
|
1788
|
+
local desc = H.expand_callable(clue.desc)
|
|
1789
|
+
-- - Fall back to possibly already present fields to allow partial
|
|
1790
|
+
-- overwrite in later clues. Like to add `postkeys` and inherit `desc`.
|
|
1791
|
+
res_data.desc = desc or res_data.desc
|
|
1792
|
+
res_data.postkeys = H.replace_termcodes(clue.postkeys) or res_data.postkeys
|
|
1793
|
+
|
|
1794
|
+
res[lhsraw] = res_data
|
|
1795
|
+
end
|
|
1796
|
+
|
|
1797
|
+
for _, map_data in ipairs(vim.api.nvim_get_keymap(mode)) do
|
|
1798
|
+
local lhsraw = H.replace_termcodes(map_data.lhs)
|
|
1799
|
+
local res_data = res[lhsraw] or {}
|
|
1800
|
+
res_data.desc = map_data.desc or ''
|
|
1801
|
+
res[lhsraw] = res_data
|
|
1802
|
+
end
|
|
1803
|
+
|
|
1804
|
+
for _, map_data in ipairs(vim.api.nvim_buf_get_keymap(0, mode)) do
|
|
1805
|
+
local lhsraw = H.replace_termcodes(map_data.lhs)
|
|
1806
|
+
local res_data = res[lhsraw] or {}
|
|
1807
|
+
res_data.desc = map_data.desc or ''
|
|
1808
|
+
res[lhsraw] = res_data
|
|
1809
|
+
end
|
|
1810
|
+
|
|
1811
|
+
return res
|
|
1812
|
+
end
|
|
1813
|
+
|
|
1814
|
+
H.clues_normalize = function(clues)
|
|
1815
|
+
local res = {}
|
|
1816
|
+
local process
|
|
1817
|
+
process = function(x)
|
|
1818
|
+
x = H.expand_callable(x)
|
|
1819
|
+
if H.is_clue(x) then return table.insert(res, x) end
|
|
1820
|
+
if not H.islist(x) then return nil end
|
|
1821
|
+
for _, y in ipairs(x) do
|
|
1822
|
+
process(y)
|
|
1823
|
+
end
|
|
1824
|
+
end
|
|
1825
|
+
|
|
1826
|
+
process(clues)
|
|
1827
|
+
return res
|
|
1828
|
+
end
|
|
1829
|
+
|
|
1830
|
+
H.clues_filter = function(clues, query)
|
|
1831
|
+
local keys = H.query_to_keys(query)
|
|
1832
|
+
for clue_keys, _ in pairs(clues) do
|
|
1833
|
+
if not vim.startswith(clue_keys, keys) then clues[clue_keys] = nil end
|
|
1834
|
+
end
|
|
1835
|
+
return clues
|
|
1836
|
+
end
|
|
1837
|
+
|
|
1838
|
+
H.clues_to_buffer_content = function(clues, keys)
|
|
1839
|
+
-- Use translated keys to properly handle cases like `<Del>`, `<End>`, etc.
|
|
1840
|
+
keys = H.keytrans(keys)
|
|
1841
|
+
|
|
1842
|
+
-- Gather clue data
|
|
1843
|
+
local keys_len = keys:len()
|
|
1844
|
+
local keys_pattern = string.format('^%s(.+)$', vim.pesc(keys))
|
|
1845
|
+
|
|
1846
|
+
local next_key_data, next_key_max_width = {}, 0
|
|
1847
|
+
for clue_keys, clue_data in pairs(clues) do
|
|
1848
|
+
local left, _, rest_keys = H.keytrans(clue_keys):find(keys_pattern)
|
|
1849
|
+
|
|
1850
|
+
-- Add non-trivial next key data only if clue matches current keys plus
|
|
1851
|
+
-- something more
|
|
1852
|
+
if left ~= nil then
|
|
1853
|
+
local next_key = H.clues_get_first_key(rest_keys)
|
|
1854
|
+
|
|
1855
|
+
-- Update description data
|
|
1856
|
+
local data = next_key_data[next_key] or {}
|
|
1857
|
+
data.n_choices = (data.n_choices or 0) + 1
|
|
1858
|
+
|
|
1859
|
+
-- - Add description directly if it is group clue with description or
|
|
1860
|
+
-- a non-group clue
|
|
1861
|
+
if next_key == rest_keys then
|
|
1862
|
+
data.desc = clue_data.desc or ''
|
|
1863
|
+
data.has_postkeys = clue_data.postkeys ~= nil
|
|
1864
|
+
end
|
|
1865
|
+
|
|
1866
|
+
next_key_data[next_key] = data
|
|
1867
|
+
|
|
1868
|
+
-- Update width data
|
|
1869
|
+
local next_key_width = vim.fn.strchars(next_key)
|
|
1870
|
+
data.next_key_width = next_key_width
|
|
1871
|
+
next_key_max_width = math.max(next_key_max_width, next_key_width)
|
|
1872
|
+
end
|
|
1873
|
+
end
|
|
1874
|
+
|
|
1875
|
+
-- Convert to array sorted by keys and finalize content
|
|
1876
|
+
local next_keys_extra = vim.tbl_map(
|
|
1877
|
+
function(x) return { key = x, keytype = H.clues_get_next_key_type(x) } end,
|
|
1878
|
+
vim.tbl_keys(next_key_data)
|
|
1879
|
+
)
|
|
1880
|
+
table.sort(next_keys_extra, H.clues_compare_next_key)
|
|
1881
|
+
local next_keys = vim.tbl_map(function(x) return x.key end, next_keys_extra)
|
|
1882
|
+
|
|
1883
|
+
local res = {}
|
|
1884
|
+
for _, key in ipairs(next_keys) do
|
|
1885
|
+
local data = next_key_data[key]
|
|
1886
|
+
local is_group = data.n_choices > 1
|
|
1887
|
+
local desc = data.desc or string.format('+%d choice%s', data.n_choices, is_group and 's' or '')
|
|
1888
|
+
local next_key = key .. string.rep(' ', next_key_max_width - data.next_key_width)
|
|
1889
|
+
table.insert(res, { next_key = next_key, desc = desc, is_group = is_group, has_postkeys = data.has_postkeys })
|
|
1890
|
+
end
|
|
1891
|
+
|
|
1892
|
+
return res
|
|
1893
|
+
end
|
|
1894
|
+
|
|
1895
|
+
H.clues_get_first_key = function(keys)
|
|
1896
|
+
-- `keys` are assumed to be translated
|
|
1897
|
+
-- Special keys
|
|
1898
|
+
local special = keys:match('^(%b<>)')
|
|
1899
|
+
if special ~= nil then return special end
|
|
1900
|
+
|
|
1901
|
+
-- <
|
|
1902
|
+
if keys:find('^<') ~= nil then return '<' end
|
|
1903
|
+
|
|
1904
|
+
-- Other characters
|
|
1905
|
+
return vim.fn.strcharpart(keys, 0, 1)
|
|
1906
|
+
end
|
|
1907
|
+
|
|
1908
|
+
H.clues_get_next_key_type = function(x)
|
|
1909
|
+
if x:find('^%w$') ~= nil then return 'alphanum' end
|
|
1910
|
+
if x:find('^<.*>$') ~= nil then return 'mod' end
|
|
1911
|
+
return 'other'
|
|
1912
|
+
end
|
|
1913
|
+
|
|
1914
|
+
H.clues_compare_next_key = function(a, b)
|
|
1915
|
+
local a_type, b_type = a.keytype, b.keytype
|
|
1916
|
+
if a_type == b_type then
|
|
1917
|
+
local cmp = vim.stricmp(a.key, b.key)
|
|
1918
|
+
return cmp == -1 or (cmp == 0 and a.key < b.key)
|
|
1919
|
+
end
|
|
1920
|
+
|
|
1921
|
+
if a_type == 'alphanum' then return true end
|
|
1922
|
+
if b_type == 'alphanum' then return false end
|
|
1923
|
+
|
|
1924
|
+
if a_type == 'mod' then return true end
|
|
1925
|
+
if b_type == 'mod' then return false end
|
|
1926
|
+
end
|
|
1927
|
+
|
|
1928
|
+
-- Clue generators ------------------------------------------------------------
|
|
1929
|
+
H.make_clues_with_register_contents = function(mode, prefix)
|
|
1930
|
+
local make_register_desc = function(register)
|
|
1931
|
+
return function()
|
|
1932
|
+
local ok, value = pcall(vim.fn.getreg, register, 1)
|
|
1933
|
+
if not ok or value == '' then return nil end
|
|
1934
|
+
return vim.inspect(value)
|
|
1935
|
+
end
|
|
1936
|
+
end
|
|
1937
|
+
|
|
1938
|
+
local all_registers = vim.split('0123456789abcdefghijklmnopqrstuvwxyz*+"-:.%/#', '')
|
|
1939
|
+
|
|
1940
|
+
local res = {}
|
|
1941
|
+
for _, register in ipairs(all_registers) do
|
|
1942
|
+
table.insert(res, { mode = mode, keys = prefix .. register, desc = make_register_desc(register) })
|
|
1943
|
+
end
|
|
1944
|
+
table.insert(res, { mode = mode, keys = prefix .. '=', desc = 'Result of expression' })
|
|
1945
|
+
|
|
1946
|
+
return res
|
|
1947
|
+
end
|
|
1948
|
+
|
|
1949
|
+
-- Predicates -----------------------------------------------------------------
|
|
1950
|
+
H.is_trigger = function(x)
|
|
1951
|
+
return type(x) == 'table' and (type(x.mode) == 'string' or type(x.mode) == 'table') and type(x.keys) == 'string'
|
|
1952
|
+
end
|
|
1953
|
+
|
|
1954
|
+
H.is_clue = function(x)
|
|
1955
|
+
if type(x) ~= 'table' then return false end
|
|
1956
|
+
local mandatory = (type(x.mode) == 'string' or type(x.mode) == 'table') and type(x.keys) == 'string'
|
|
1957
|
+
local extra = (x.desc == nil or type(x.desc) == 'string' or vim.is_callable(x.desc))
|
|
1958
|
+
and (x.postkeys == nil or type(x.postkeys) == 'string')
|
|
1959
|
+
return mandatory and extra
|
|
1960
|
+
end
|
|
1961
|
+
|
|
1962
|
+
H.is_array_of = function(x, predicate)
|
|
1963
|
+
if not H.islist(x) then return false end
|
|
1964
|
+
for _, v in ipairs(x) do
|
|
1965
|
+
if not predicate(v) then return false end
|
|
1966
|
+
end
|
|
1967
|
+
return true
|
|
1968
|
+
end
|
|
1969
|
+
|
|
1970
|
+
-- Utilities ------------------------------------------------------------------
|
|
1971
|
+
H.error = function(msg) error('(mini.clue) ' .. msg, 0) end
|
|
1972
|
+
|
|
1973
|
+
H.check_type = function(name, val, ref, allow_nil)
|
|
1974
|
+
if type(val) == ref or (ref == 'callable' and vim.is_callable(val)) or (allow_nil and val == nil) then return end
|
|
1975
|
+
H.error(string.format('`%s` should be %s, not %s', name, ref, type(val)))
|
|
1976
|
+
end
|
|
1977
|
+
|
|
1978
|
+
H.set_buf_name = function(buf_id, name) vim.api.nvim_buf_set_name(buf_id, 'miniclue://' .. buf_id .. '/' .. name) end
|
|
1979
|
+
|
|
1980
|
+
H.map = function(mode, lhs, rhs, opts)
|
|
1981
|
+
if lhs == '' then return end
|
|
1982
|
+
opts = vim.tbl_deep_extend('force', { silent = true }, opts or {})
|
|
1983
|
+
vim.keymap.set(mode, lhs, rhs, opts)
|
|
1984
|
+
end
|
|
1985
|
+
|
|
1986
|
+
H.replace_termcodes = function(x)
|
|
1987
|
+
if x == nil then return nil end
|
|
1988
|
+
-- Use `keytrans` prior replacing termcodes to work correctly on already
|
|
1989
|
+
-- replaced variant of `<F*>` keys
|
|
1990
|
+
return vim.api.nvim_replace_termcodes(H.keytrans(x), true, true, true)
|
|
1991
|
+
end
|
|
1992
|
+
|
|
1993
|
+
H.keytrans = function(x)
|
|
1994
|
+
local res = vim.fn.keytrans(x):gsub('<NL>', '<C-J>'):gsub('<S%-NL>', '<C-S-J>'):gsub('<M%-NL>', '<C-M-J>')
|
|
1995
|
+
return (res:gsub('<lt>', '<'))
|
|
1996
|
+
end
|
|
1997
|
+
|
|
1998
|
+
H.get_forced_submode = function()
|
|
1999
|
+
local mode = vim.fn.mode(1)
|
|
2000
|
+
if not mode:sub(1, 2) == 'no' then return '' end
|
|
2001
|
+
return mode:sub(3)
|
|
2002
|
+
end
|
|
2003
|
+
|
|
2004
|
+
H.get_default_register = function()
|
|
2005
|
+
local clipboard = vim.o.clipboard
|
|
2006
|
+
if clipboard:find('unnamedplus') ~= nil then return '+' end
|
|
2007
|
+
if clipboard:find('unnamed') ~= nil then return '*' end
|
|
2008
|
+
return '"'
|
|
2009
|
+
end
|
|
2010
|
+
|
|
2011
|
+
H.is_valid_buf = function(buf_id) return type(buf_id) == 'number' and vim.api.nvim_buf_is_valid(buf_id) end
|
|
2012
|
+
|
|
2013
|
+
H.is_loaded_buf = function(buf_id) return type(buf_id) == 'number' and vim.api.nvim_buf_is_loaded(buf_id) end
|
|
2014
|
+
|
|
2015
|
+
H.is_valid_win = function(win_id) return type(win_id) == 'number' and vim.api.nvim_win_is_valid(win_id) end
|
|
2016
|
+
|
|
2017
|
+
H.fit_to_width = function(text, width)
|
|
2018
|
+
local t_width = vim.fn.strchars(text)
|
|
2019
|
+
return t_width <= width and text or ('…' .. vim.fn.strcharpart(text, t_width - width + 1, width - 1))
|
|
2020
|
+
end
|
|
2021
|
+
|
|
2022
|
+
H.expand_callable = function(x, ...)
|
|
2023
|
+
if vim.is_callable(x) then return x(...) end
|
|
2024
|
+
return x
|
|
2025
|
+
end
|
|
2026
|
+
|
|
2027
|
+
H.redraw_scheduled = vim.schedule_wrap(function() vim.cmd('redraw') end)
|
|
2028
|
+
|
|
2029
|
+
H.getcharstr = function()
|
|
2030
|
+
-- Ensure redraws still happen. This is needed to not block "interactive"
|
|
2031
|
+
-- behavior (smooth scrolling) and not immediate submodes (scheduled DAP).
|
|
2032
|
+
-- TODO: Use "do not block redraw" `getcharstr()` option if it ever happens
|
|
2033
|
+
H.timers.getcharstr:start(0, 50, H.redraw_scheduled)
|
|
2034
|
+
local ok, char = pcall(vim.fn.getcharstr)
|
|
2035
|
+
H.timers.getcharstr:stop()
|
|
2036
|
+
-- Terminate if couldn't get input (like with <C-c>) or on `<Esc>`
|
|
2037
|
+
if not ok or char == '' or char == '\3' or char == '\27' then return nil end
|
|
2038
|
+
return H.get_langmap()[char] or char
|
|
2039
|
+
end
|
|
2040
|
+
|
|
2041
|
+
H.get_langmap = function()
|
|
2042
|
+
if vim.o.langmap == '' then return {} end
|
|
2043
|
+
|
|
2044
|
+
-- Get langmap parts by splitting at "," not preceded by "\"
|
|
2045
|
+
local langmap_parts = vim.fn.split(vim.o.langmap, '[^\\\\]\\zs,')
|
|
2046
|
+
|
|
2047
|
+
-- Process each langmap part
|
|
2048
|
+
local res = {}
|
|
2049
|
+
for _, part in ipairs(langmap_parts) do
|
|
2050
|
+
H.process_langmap_part(res, part)
|
|
2051
|
+
end
|
|
2052
|
+
return res
|
|
2053
|
+
end
|
|
2054
|
+
|
|
2055
|
+
H.process_langmap_part = function(res, part)
|
|
2056
|
+
local semicolon_byte_ind = vim.fn.match(part, '[^\\\\]\\zs;') + 1
|
|
2057
|
+
|
|
2058
|
+
-- Part is without ';', like 'aAbB'
|
|
2059
|
+
if semicolon_byte_ind == 0 then
|
|
2060
|
+
-- Drop backslash escapes
|
|
2061
|
+
part = part:gsub('\\([^\\])', '%1')
|
|
2062
|
+
|
|
2063
|
+
for i = 1, vim.fn.strchars(part), 2 do
|
|
2064
|
+
-- `strcharpart()` has 0-based indexes
|
|
2065
|
+
local from, to = vim.fn.strcharpart(part, i - 1, 1), vim.fn.strcharpart(part, i, 1)
|
|
2066
|
+
if from ~= '' and to ~= '' then res[from] = to end
|
|
2067
|
+
end
|
|
2068
|
+
|
|
2069
|
+
return
|
|
2070
|
+
end
|
|
2071
|
+
|
|
2072
|
+
-- Part is with ';', like 'ab;AB'
|
|
2073
|
+
-- - Drop backslash escape
|
|
2074
|
+
local left = part:sub(1, semicolon_byte_ind - 1):gsub('\\([^\\])', '%1')
|
|
2075
|
+
local right = part:sub(semicolon_byte_ind + 1):gsub('\\([^\\])', '%1')
|
|
2076
|
+
|
|
2077
|
+
for i = 1, vim.fn.strchars(left) do
|
|
2078
|
+
local from, to = vim.fn.strcharpart(left, i - 1, 1), vim.fn.strcharpart(right, i - 1, 1)
|
|
2079
|
+
if from ~= '' and to ~= '' then res[from] = to end
|
|
2080
|
+
end
|
|
2081
|
+
end
|
|
2082
|
+
|
|
2083
|
+
H.list_concat = function(...)
|
|
2084
|
+
local res = {}
|
|
2085
|
+
for i = 1, select('#', ...) do
|
|
2086
|
+
for _, x in ipairs(select(i, ...) or {}) do
|
|
2087
|
+
table.insert(res, x)
|
|
2088
|
+
end
|
|
2089
|
+
end
|
|
2090
|
+
return res
|
|
2091
|
+
end
|
|
2092
|
+
|
|
2093
|
+
-- TODO: Remove after compatibility with Neovim=0.9 is dropped
|
|
2094
|
+
H.islist = vim.fn.has('nvim-0.10') == 1 and vim.islist or vim.tbl_islist
|
|
2095
|
+
|
|
2096
|
+
return MiniClue
|