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,1349 @@
|
|
|
1
|
+
--- *mini.operators* Text edit operators
|
|
2
|
+
---
|
|
3
|
+
--- MIT License Copyright (c) 2023 Evgeni Chasnovski
|
|
4
|
+
|
|
5
|
+
--- Features:
|
|
6
|
+
--- - Operators:
|
|
7
|
+
--- - Evaluate text and replace with output.
|
|
8
|
+
--- - Exchange text regions.
|
|
9
|
+
--- - Multiply (duplicate) text.
|
|
10
|
+
--- - Replace text with register.
|
|
11
|
+
--- - Sort text.
|
|
12
|
+
---
|
|
13
|
+
--- - Automated configurable mappings to operate on textobject, line, selection.
|
|
14
|
+
--- Can be disabled in favor of more control with |MiniOperators.make_mappings()|.
|
|
15
|
+
---
|
|
16
|
+
--- - All operators support |[count]| and dot-repeat.
|
|
17
|
+
---
|
|
18
|
+
--- See |MiniOperators-overview| and |MiniOperators.config| for more details.
|
|
19
|
+
---
|
|
20
|
+
--- # Setup ~
|
|
21
|
+
---
|
|
22
|
+
--- This module needs a setup with `require('mini.operators').setup({})` (replace
|
|
23
|
+
--- `{}` with your `config` table). It will create global Lua table `MiniOperators`
|
|
24
|
+
--- which you can use for scripting or manually (with `:lua MiniOperators.*`).
|
|
25
|
+
---
|
|
26
|
+
--- See |MiniOperators.config| for available config settings.
|
|
27
|
+
---
|
|
28
|
+
--- You can override runtime config settings (but not `config.mappings`) locally
|
|
29
|
+
--- to buffer inside `vim.b.minioperators_config` which should have same structure
|
|
30
|
+
--- as `MiniOperators.config`. See |mini.nvim-buffer-local-config| for more details.
|
|
31
|
+
---
|
|
32
|
+
--- # Comparisons ~
|
|
33
|
+
---
|
|
34
|
+
--- - [gbprod/substitute.nvim](https://github.com/gbprod/substitute.nvim):
|
|
35
|
+
--- - Has "replace" and "exchange" variants, but not others from this module.
|
|
36
|
+
--- - Has "replace/substitute" over range functionality, while this module
|
|
37
|
+
--- does not by design (it is similar to |:s| functionality while not
|
|
38
|
+
--- offering significantly lower mental complexity).
|
|
39
|
+
--- - "Replace" highlights pasted text, while in this module it doesn't.
|
|
40
|
+
--- - "Exchange" doesn't work across buffers, while in this module it does.
|
|
41
|
+
---
|
|
42
|
+
--- - [svermeulen/vim-subversive](https://github.com/svermeulen/vim-subversive):
|
|
43
|
+
--- - Main inspiration for "replace" functionality, so they are mostly similar
|
|
44
|
+
--- for this operator.
|
|
45
|
+
--- - Has "replace/substitute" over range functionality, while this module
|
|
46
|
+
--- does not by design.
|
|
47
|
+
---
|
|
48
|
+
--- - [tommcdo/vim-exchange](https://github.com/tommcdo/vim-exchange):
|
|
49
|
+
--- - Main inspiration for "exchange" functionality, so they are mostly
|
|
50
|
+
--- similar for this operator.
|
|
51
|
+
--- - Doesn't work across buffers, while this module does.
|
|
52
|
+
---
|
|
53
|
+
--- - [christoomey/vim-sort-motion](https://github.com/christoomey/vim-sort-motion):
|
|
54
|
+
--- - Uses |:sort| for linewise sorting, while this module uses consistent
|
|
55
|
+
--- sorting algorithm (by default, see |MiniOperators.default_sort_func()|).
|
|
56
|
+
--- - Sorting algorithm can't be customized, while this module allows this
|
|
57
|
+
--- (see `sort.func` in |MiniOperators.config|).
|
|
58
|
+
--- - For charwise region uses only commas as separators, while this module
|
|
59
|
+
--- can also separate by semicolon or whitespace (by default,
|
|
60
|
+
--- see |MiniOperators.default_sort_func()|).
|
|
61
|
+
---
|
|
62
|
+
--- # Highlight groups ~
|
|
63
|
+
--- *MiniOperators-hl-groups*
|
|
64
|
+
---
|
|
65
|
+
--- - `MiniOperatorsExchangeFrom` - first region to exchange.
|
|
66
|
+
---
|
|
67
|
+
--- To change any highlight group, set it directly with |nvim_set_hl()|.
|
|
68
|
+
---
|
|
69
|
+
--- # Disabling ~
|
|
70
|
+
---
|
|
71
|
+
--- To disable main functionality, set `vim.g.minioperators_disable` (globally) or
|
|
72
|
+
--- `vim.b.minioperators_disable` (for a buffer) to `true`. Considering high number
|
|
73
|
+
--- of different scenarios and customization intentions, writing exact rules
|
|
74
|
+
--- for disabling module's functionality is left to user. See
|
|
75
|
+
--- |mini.nvim-disabling-recipes| for common recipes.
|
|
76
|
+
---@tag MiniOperators
|
|
77
|
+
|
|
78
|
+
--- Operator defines an action that will be performed on a textobject, motion,
|
|
79
|
+
--- or visual selection (similar to |d|, |c|, etc.). When makes sense, it can also
|
|
80
|
+
--- respect supplied register (like "replace" operator).
|
|
81
|
+
---
|
|
82
|
+
--- This module implements each operator in a separate dedicated function
|
|
83
|
+
--- (like |MiniOperators.replace()| for "replace" operator). Each such function
|
|
84
|
+
--- takes `mode` as argument and acts depending on it:
|
|
85
|
+
---
|
|
86
|
+
--- - If `mode` is `nil` (or not explicitly supplied), it sets |'operatorfunc'|
|
|
87
|
+
--- to this dedicated function and returns `g@` assuming being called from
|
|
88
|
+
--- expression mapping. See |:map-operator| and |:map-expression| for more details.
|
|
89
|
+
---
|
|
90
|
+
--- - If `mode` is "char", "line", or "block", it acts as `operatorfunc` and performs
|
|
91
|
+
--- action for region between |`[| and |`]| marks.
|
|
92
|
+
---
|
|
93
|
+
--- - If `mode` is "visual", it performs action for region between |`<| and |`>| marks.
|
|
94
|
+
---
|
|
95
|
+
--- For more details about specific operator, see help for its function:
|
|
96
|
+
---
|
|
97
|
+
--- - Evaluate: |MiniOperators.evaluate()|
|
|
98
|
+
--- - Exchange: |MiniOperators.exchange()|
|
|
99
|
+
--- - Multiply: |MiniOperators.multiply()|
|
|
100
|
+
--- - Replace: |MiniOperators.replace()|
|
|
101
|
+
--- - Sort: |MiniOperators.sort()|
|
|
102
|
+
---
|
|
103
|
+
--- # Mappings ~
|
|
104
|
+
--- *MiniOperators-mappings*
|
|
105
|
+
---
|
|
106
|
+
--- All operators are automatically mapped during |MiniOperators.setup()| execution.
|
|
107
|
+
--- Mappings keys are deduced from `prefix` field of corresponding `config` entry.
|
|
108
|
+
--- All built-in conflicting mappings are removed (like |gra|, |grn| in Neovim>=0.11).
|
|
109
|
+
--- Both |gx| and |v_gx| are remapped to `gX` (if that is not already taken).
|
|
110
|
+
---
|
|
111
|
+
--- For each operator the following mappings are created:
|
|
112
|
+
---
|
|
113
|
+
--- - In Normal mode to operate on textobject. Uses `prefix` directly.
|
|
114
|
+
--- - In Normal mode to operate on line. Appends to `prefix` the last character.
|
|
115
|
+
--- This aligns with |operator-doubled| and established patterns for operators
|
|
116
|
+
--- with more than two characters, like |guu|, |gUU|, etc.
|
|
117
|
+
--- - In Visual mode to operate on visual selection. Uses `prefix` directly.
|
|
118
|
+
---
|
|
119
|
+
--- Example of default mappings for "replace":
|
|
120
|
+
--- - `gr` in Normal mode for operating on textobject.
|
|
121
|
+
--- Example of usage: `griw` replaces "inner word" with default register.
|
|
122
|
+
--- - `grr` in Normal mode for operating on line.
|
|
123
|
+
--- Example of usage: `grr` replaces current line.
|
|
124
|
+
--- - `gr` in Visual mode for operating on visual selection.
|
|
125
|
+
--- Example of usage: `viw` selects "inner word" and `gr` replaces it.
|
|
126
|
+
---
|
|
127
|
+
--- There are two suggested ways to customize mappings:
|
|
128
|
+
---
|
|
129
|
+
--- - Change `prefix` in |MiniOperators.setup()| call. For example, doing >lua
|
|
130
|
+
---
|
|
131
|
+
--- require('mini.operators').setup({ replace = { prefix = 'cr' } })
|
|
132
|
+
--- <
|
|
133
|
+
--- will make mappings for `cr` / `crr` / `cr` instead of `gr` / `grr` / `gr`.
|
|
134
|
+
---
|
|
135
|
+
--- - Disable automated mapping creation by supplying empty string as prefix and
|
|
136
|
+
--- use |MiniOperators.make_mappings()| directly. For example: >lua
|
|
137
|
+
---
|
|
138
|
+
--- -- Disable automated creation of "replace"
|
|
139
|
+
--- local operators = require('mini.operators')
|
|
140
|
+
--- operators.setup({ replace = { prefix = '' } })
|
|
141
|
+
---
|
|
142
|
+
--- -- Make custom mappings
|
|
143
|
+
--- operators.make_mappings(
|
|
144
|
+
--- 'replace',
|
|
145
|
+
--- { textobject = 'cr', line = 'crr', selection = 'cr' }
|
|
146
|
+
--- )
|
|
147
|
+
--- <
|
|
148
|
+
---@tag MiniOperators-overview
|
|
149
|
+
|
|
150
|
+
---@alias __operators_mode string|nil One of `nil`, `'char'`, `'line'`, `'block'`, `'visual'`.
|
|
151
|
+
---@alias __operators_content table Table with the following fields:
|
|
152
|
+
--- - <lines> `(table)` - array with content lines.
|
|
153
|
+
--- - <submode> `(string)` - region submode. One of `'v'`, `'V'`, `'<C-v>'` (escaped).
|
|
154
|
+
|
|
155
|
+
---@diagnostic disable:undefined-field
|
|
156
|
+
---@diagnostic disable:discard-returns
|
|
157
|
+
---@diagnostic disable:unused-local
|
|
158
|
+
---@diagnostic disable:cast-local-type
|
|
159
|
+
|
|
160
|
+
-- Module definition ==========================================================
|
|
161
|
+
local MiniOperators = {}
|
|
162
|
+
local H = {}
|
|
163
|
+
|
|
164
|
+
--- Module setup
|
|
165
|
+
---
|
|
166
|
+
---@param config table|nil Module config table. See |MiniOperators.config|.
|
|
167
|
+
---
|
|
168
|
+
---@usage >lua
|
|
169
|
+
--- require('mini.operators').setup() -- use default config
|
|
170
|
+
--- -- OR
|
|
171
|
+
--- require('mini.operators').setup({}) -- replace {} with your config table
|
|
172
|
+
--- <
|
|
173
|
+
MiniOperators.setup = function(config)
|
|
174
|
+
-- TODO: Remove after Neovim=0.9 support is dropped
|
|
175
|
+
if vim.fn.has('nvim-0.10') == 0 then
|
|
176
|
+
vim.notify(
|
|
177
|
+
'(mini.operators) Neovim<0.10 is soft deprecated (module works but is not supported).'
|
|
178
|
+
.. " It will be deprecated after the next 'mini.nvim' release (module might not work)."
|
|
179
|
+
.. ' Please update your Neovim version.'
|
|
180
|
+
)
|
|
181
|
+
end
|
|
182
|
+
|
|
183
|
+
-- Export module
|
|
184
|
+
_G.MiniOperators = MiniOperators
|
|
185
|
+
|
|
186
|
+
-- Setup config
|
|
187
|
+
config = H.setup_config(config)
|
|
188
|
+
|
|
189
|
+
-- Apply config
|
|
190
|
+
H.apply_config(config)
|
|
191
|
+
|
|
192
|
+
-- Define behavior
|
|
193
|
+
H.create_autocommands()
|
|
194
|
+
|
|
195
|
+
-- Create default highlighting
|
|
196
|
+
H.create_default_hl()
|
|
197
|
+
end
|
|
198
|
+
|
|
199
|
+
--stylua: ignore
|
|
200
|
+
--- Defaults ~
|
|
201
|
+
---@eval return MiniDoc.afterlines_to_code(MiniDoc.current.eval_section)
|
|
202
|
+
---@text # Evaluate ~
|
|
203
|
+
---
|
|
204
|
+
--- `evaluate.prefix` is a string used to automatically infer operator mappings keys
|
|
205
|
+
--- during |MiniOperators.setup()|. See |MiniOperators-mappings|.
|
|
206
|
+
---
|
|
207
|
+
--- `evaluate.func` is a function used to actually evaluate text region.
|
|
208
|
+
--- If `nil` (default), |MiniOperators.default_evaluate_func()| is used.
|
|
209
|
+
---
|
|
210
|
+
--- This function will take content table representing selected text as input
|
|
211
|
+
--- and should return array of lines as output (each item per line).
|
|
212
|
+
--- Content table has fields `lines`, array of region lines, and `submode`,
|
|
213
|
+
--- one of `v`, `V`, `\22` (escaped `<C-v>`) for charwise, linewise, and blockwise.
|
|
214
|
+
---
|
|
215
|
+
--- To customize evaluation per language, set `evaluate.func` in buffer-local
|
|
216
|
+
--- config (`vim.b.minioperators_config`; see |mini.nvim-buffer-local-config|).
|
|
217
|
+
---
|
|
218
|
+
--- # Exchange ~
|
|
219
|
+
---
|
|
220
|
+
--- `exchange.prefix` is a string used to automatically infer operator mappings keys
|
|
221
|
+
--- during |MiniOperators.setup()|. See |MiniOperators-mappings|.
|
|
222
|
+
---
|
|
223
|
+
--- Note: default value "gx" overrides |gx| / |v_gx|. Instead they are remapped
|
|
224
|
+
--- to `gX` (if that is not already taken). To keep using `gx` with built-in
|
|
225
|
+
--- feature (open URL at cursor) choose different `config.prefix`.
|
|
226
|
+
---
|
|
227
|
+
--- `exchange.reindent_linewise` is a boolean indicating whether newly put linewise
|
|
228
|
+
--- text should preserve indent of replaced text. In other words, if `false`,
|
|
229
|
+
--- regions are exchanged preserving their indents; if `true` - without them.
|
|
230
|
+
---
|
|
231
|
+
--- # Multiply ~
|
|
232
|
+
---
|
|
233
|
+
--- `multiply.prefix` is a string used to automatically infer operator mappings keys
|
|
234
|
+
--- during |MiniOperators.setup()|. See |MiniOperators-mappings|.
|
|
235
|
+
---
|
|
236
|
+
--- `multiply.func` is a function used to optionally update multiplied text.
|
|
237
|
+
--- If `nil` (default), text used as is.
|
|
238
|
+
---
|
|
239
|
+
--- Takes content table as input (see "Evaluate" section) and should return
|
|
240
|
+
--- array of lines as output.
|
|
241
|
+
---
|
|
242
|
+
--- # Replace ~
|
|
243
|
+
---
|
|
244
|
+
--- `replace.prefix` is a string used to automatically infer operator mappings keys
|
|
245
|
+
--- during |MiniOperators.setup()|. See |MiniOperators-mappings|.
|
|
246
|
+
---
|
|
247
|
+
--- `replace.reindent_linewise` is a boolean indicating whether newly put linewise
|
|
248
|
+
--- text should preserve indent of replaced text.
|
|
249
|
+
---
|
|
250
|
+
--- # Sort ~
|
|
251
|
+
---
|
|
252
|
+
--- `sort.prefix` is a string used to automatically infer operator mappings keys
|
|
253
|
+
--- during |MiniOperators.setup()|. See |MiniOperators-mappings|.
|
|
254
|
+
---
|
|
255
|
+
--- `sort.func` is a function used to actually sort text region.
|
|
256
|
+
--- If `nil` (default), |MiniOperators.default_sort_func()| is used.
|
|
257
|
+
---
|
|
258
|
+
--- Takes content table as input (see "Evaluate" section) and should return
|
|
259
|
+
--- array of lines as output.
|
|
260
|
+
---
|
|
261
|
+
--- Example of `sort.func` which asks user for custom delimiter for charwise region: >lua
|
|
262
|
+
---
|
|
263
|
+
--- local sort_func = function(content)
|
|
264
|
+
--- local opts = {}
|
|
265
|
+
--- if content.submode == 'v' then
|
|
266
|
+
--- -- Ask for delimiter to be treated as is (not as Lua pattern)
|
|
267
|
+
--- local delimiter = vim.fn.input('Sort delimiter: ')
|
|
268
|
+
--- -- Treat surrounding whitespace as part of split
|
|
269
|
+
--- opts.split_patterns = { '%s*' .. vim.pesc(delimiter) .. '%s*' }
|
|
270
|
+
--- end
|
|
271
|
+
--- return MiniOperators.default_sort_func(content, opts)
|
|
272
|
+
--- end
|
|
273
|
+
---
|
|
274
|
+
--- require('mini.operators').setup({ sort = { func = sort_func } })
|
|
275
|
+
--- <
|
|
276
|
+
MiniOperators.config = {
|
|
277
|
+
-- Each entry configures one operator.
|
|
278
|
+
-- `prefix` defines keys mapped during `setup()`: in Normal mode
|
|
279
|
+
-- to operate on textobject and line, in Visual - on selection.
|
|
280
|
+
|
|
281
|
+
-- Evaluate text and replace with output
|
|
282
|
+
evaluate = {
|
|
283
|
+
prefix = 'g=',
|
|
284
|
+
|
|
285
|
+
-- Function which does the evaluation
|
|
286
|
+
func = nil,
|
|
287
|
+
},
|
|
288
|
+
|
|
289
|
+
-- Exchange text regions
|
|
290
|
+
exchange = {
|
|
291
|
+
-- NOTE: Default `gx` is remapped to `gX`
|
|
292
|
+
prefix = 'gx',
|
|
293
|
+
|
|
294
|
+
-- Whether to reindent new text to match previous indent
|
|
295
|
+
reindent_linewise = true,
|
|
296
|
+
},
|
|
297
|
+
|
|
298
|
+
-- Multiply (duplicate) text
|
|
299
|
+
multiply = {
|
|
300
|
+
prefix = 'gm',
|
|
301
|
+
|
|
302
|
+
-- Function which can modify text before multiplying
|
|
303
|
+
func = nil,
|
|
304
|
+
},
|
|
305
|
+
|
|
306
|
+
-- Replace text with register
|
|
307
|
+
replace = {
|
|
308
|
+
-- NOTE: Default `gr*` LSP mappings are removed
|
|
309
|
+
prefix = 'gr',
|
|
310
|
+
|
|
311
|
+
-- Whether to reindent new text to match previous indent
|
|
312
|
+
reindent_linewise = true,
|
|
313
|
+
},
|
|
314
|
+
|
|
315
|
+
-- Sort text
|
|
316
|
+
sort = {
|
|
317
|
+
prefix = 'gs',
|
|
318
|
+
|
|
319
|
+
-- Function which does the sort
|
|
320
|
+
func = nil,
|
|
321
|
+
}
|
|
322
|
+
}
|
|
323
|
+
--minidoc_afterlines_end
|
|
324
|
+
|
|
325
|
+
--- Evaluate text and replace with output
|
|
326
|
+
---
|
|
327
|
+
--- It replaces the region with the output of `config.evaluate.func`.
|
|
328
|
+
--- By default it is |MiniOperators.default_evaluate_func()| which evaluates
|
|
329
|
+
--- text as Lua code depending on the region submode.
|
|
330
|
+
---
|
|
331
|
+
---@param mode __operators_mode
|
|
332
|
+
MiniOperators.evaluate = function(mode)
|
|
333
|
+
if H.is_disabled() or not vim.bo.modifiable then return '' end
|
|
334
|
+
|
|
335
|
+
-- If used without arguments inside expression mapping, set it as
|
|
336
|
+
-- 'operatorfunc' and call it again as a result of expression mapping.
|
|
337
|
+
if mode == nil then
|
|
338
|
+
vim.o.operatorfunc = 'v:lua.MiniOperators.evaluate'
|
|
339
|
+
return 'g@'
|
|
340
|
+
end
|
|
341
|
+
|
|
342
|
+
local evaluate_func = H.get_config().evaluate.func or MiniOperators.default_evaluate_func
|
|
343
|
+
local data = H.get_region_data(mode)
|
|
344
|
+
if data == nil then return end
|
|
345
|
+
data.reindent_linewise = true
|
|
346
|
+
H.apply_content_func(evaluate_func, data)
|
|
347
|
+
end
|
|
348
|
+
|
|
349
|
+
--- Exchange text regions
|
|
350
|
+
---
|
|
351
|
+
--- Has two-step logic:
|
|
352
|
+
--- - First call remembers the region as the one to be exchanged and highlights it
|
|
353
|
+
--- with `MiniOperatorsExchangeFrom` highlight group.
|
|
354
|
+
--- - Second call performs the exchange. Basically, a two substeps action:
|
|
355
|
+
--- "yank both regions" and replace each one with another.
|
|
356
|
+
---
|
|
357
|
+
--- Notes:
|
|
358
|
+
--- - Use `<C-c>` to stop exchanging after the first step.
|
|
359
|
+
---
|
|
360
|
+
--- - Exchanged regions can have different (char,line,block)-wise submodes.
|
|
361
|
+
---
|
|
362
|
+
--- - Works with most cases of intersecting regions, but not officially supported.
|
|
363
|
+
---
|
|
364
|
+
---@param mode __operators_mode
|
|
365
|
+
MiniOperators.exchange = function(mode)
|
|
366
|
+
if H.is_disabled() or not vim.bo.modifiable then return '' end
|
|
367
|
+
|
|
368
|
+
-- If used without arguments inside expression mapping, set it as
|
|
369
|
+
-- 'operatorfunc' and call it again as a result of expression mapping.
|
|
370
|
+
if mode == nil then
|
|
371
|
+
vim.o.operatorfunc = 'v:lua.MiniOperators.exchange'
|
|
372
|
+
return 'g@'
|
|
373
|
+
end
|
|
374
|
+
|
|
375
|
+
-- Depending on present cache data, perform exchange step
|
|
376
|
+
if not H.exchange_has_step_one() then
|
|
377
|
+
-- Store data about first region
|
|
378
|
+
H.cache.exchange.step_one = H.exchange_set_region_extmark(mode, true)
|
|
379
|
+
if H.cache.exchange.step_one == nil then return end
|
|
380
|
+
|
|
381
|
+
-- Temporarily remap `<C-c>` to stop the exchange
|
|
382
|
+
H.exchange_set_stop_mapping()
|
|
383
|
+
else
|
|
384
|
+
-- Store data about second region
|
|
385
|
+
H.cache.exchange.step_two = H.exchange_set_region_extmark(mode, false)
|
|
386
|
+
if H.cache.exchange.step_two == nil then return end
|
|
387
|
+
|
|
388
|
+
-- Do exchange
|
|
389
|
+
H.exchange_do()
|
|
390
|
+
|
|
391
|
+
-- Stop exchange
|
|
392
|
+
H.exchange_stop()
|
|
393
|
+
end
|
|
394
|
+
end
|
|
395
|
+
|
|
396
|
+
--- Multiply (duplicate) text
|
|
397
|
+
---
|
|
398
|
+
--- Copies a region (without affecting registers) and puts it directly after.
|
|
399
|
+
---
|
|
400
|
+
--- Notes:
|
|
401
|
+
--- - Supports two types of |[count]|: `[count1]gm[count2][textobject]` with default
|
|
402
|
+
--- `config.multiply.prefix` makes `[count1]` copies of region defined by
|
|
403
|
+
--- `[count2][textobject]`. Example: `2gm3aw` - 2 copies of `3aw`.
|
|
404
|
+
---
|
|
405
|
+
--- - |[count]| for "line" mapping (`gmm` by default) is treated as `[count1]` from
|
|
406
|
+
--- previous note.
|
|
407
|
+
---
|
|
408
|
+
--- - Advantages of using this instead of "yank" + "paste":
|
|
409
|
+
--- - Doesn't modify any register, while separate steps need some register to
|
|
410
|
+
--- hold multiplied text.
|
|
411
|
+
--- - In most cases separate steps would be "yank" + "move cursor" + "paste",
|
|
412
|
+
--- while "multiply" makes it at once.
|
|
413
|
+
---
|
|
414
|
+
---@param mode __operators_mode
|
|
415
|
+
MiniOperators.multiply = function(mode)
|
|
416
|
+
if H.is_disabled() or not vim.bo.modifiable then return '' end
|
|
417
|
+
|
|
418
|
+
-- If used without arguments inside expression mapping, set it as
|
|
419
|
+
-- 'operatorfunc' and call it again as a result of expression mapping.
|
|
420
|
+
if mode == nil then
|
|
421
|
+
vim.o.operatorfunc = 'v:lua.MiniOperators.multiply'
|
|
422
|
+
H.cache.multiply = { count = vim.v.count1 }
|
|
423
|
+
|
|
424
|
+
-- Reset count to allow two counts: first for paste, second for textobject
|
|
425
|
+
return vim.api.nvim_replace_termcodes('<Cmd>redraw<CR>g@', true, true, true)
|
|
426
|
+
end
|
|
427
|
+
|
|
428
|
+
local count = mode == 'visual' and vim.v.count1 or H.cache.multiply.count
|
|
429
|
+
local data = H.get_region_data(mode)
|
|
430
|
+
if data == nil then return end
|
|
431
|
+
local mark_from, mark_to, submode = data.mark_from, data.mark_to, data.submode
|
|
432
|
+
|
|
433
|
+
H.with_temp_context({ registers = { 'x', '"' } }, function()
|
|
434
|
+
-- Yank to temporary "x" register
|
|
435
|
+
local yank_data = { mark_from = mark_from, mark_to = mark_to, submode = submode, mode = mode, register = 'x' }
|
|
436
|
+
H.do_between_marks('y', yank_data)
|
|
437
|
+
|
|
438
|
+
-- Modify lines in "x" register
|
|
439
|
+
local func = H.get_config().multiply.func or function(content) return content.lines end
|
|
440
|
+
local x_reginfo = vim.fn.getreginfo('x')
|
|
441
|
+
x_reginfo.regcontents = func({ lines = x_reginfo.regcontents, submode = submode })
|
|
442
|
+
vim.fn.setreg('x', x_reginfo)
|
|
443
|
+
|
|
444
|
+
-- Adjust cursor for a proper paste
|
|
445
|
+
local ref_coords = H.multiply_get_ref_coords(mark_from, mark_to, submode)
|
|
446
|
+
vim.api.nvim_win_set_cursor(0, ref_coords)
|
|
447
|
+
|
|
448
|
+
-- Paste after textobject from temporary register
|
|
449
|
+
H.cmd_normal(count .. '"xp')
|
|
450
|
+
|
|
451
|
+
-- Adjust cursor to be at start of pasted text. Not in linewise mode as it
|
|
452
|
+
-- already is at first non-blank, while this moves to first column.
|
|
453
|
+
if submode ~= 'V' then vim.cmd('normal! `[') end
|
|
454
|
+
end)
|
|
455
|
+
end
|
|
456
|
+
|
|
457
|
+
--- Replace text with register
|
|
458
|
+
---
|
|
459
|
+
--- Notes:
|
|
460
|
+
--- - Supports two types of |[count]|: `[count1]gr[count2][textobject]` with default
|
|
461
|
+
--- `config.replace.prefix` puts `[count1]` contents of register over region defined
|
|
462
|
+
--- by `[count2][textobject]`. Example: `2gr3aw` - 2 register contents over `3aw`.
|
|
463
|
+
---
|
|
464
|
+
--- - |[count]| for "line" mapping (`grr` by default) is treated as `[count1]` from
|
|
465
|
+
--- previous note.
|
|
466
|
+
---
|
|
467
|
+
--- - Advantages of using this instead of "visually select" + "paste with |v_P|":
|
|
468
|
+
--- - As operator it is dot-repeatable which has cumulative gain in case of
|
|
469
|
+
--- multiple replacing is needed.
|
|
470
|
+
--- - Can automatically reindent.
|
|
471
|
+
---
|
|
472
|
+
---@param mode __operators_mode
|
|
473
|
+
MiniOperators.replace = function(mode)
|
|
474
|
+
if H.is_disabled() or not vim.bo.modifiable then return '' end
|
|
475
|
+
|
|
476
|
+
-- If used without arguments inside expression mapping, set it as
|
|
477
|
+
-- 'operatorfunc' and call it again as a result of expression mapping.
|
|
478
|
+
if mode == nil then
|
|
479
|
+
vim.o.operatorfunc = 'v:lua.MiniOperators.replace'
|
|
480
|
+
H.cache.replace = { count = vim.v.count1, register = vim.v.register }
|
|
481
|
+
|
|
482
|
+
-- Reset count to allow two counts: first for paste, second for textobject
|
|
483
|
+
return vim.api.nvim_replace_termcodes('<Cmd>redraw<CR>g@', true, true, true)
|
|
484
|
+
end
|
|
485
|
+
|
|
486
|
+
-- Do replace
|
|
487
|
+
-- - Compute `count` and `register` prior getting region data because it
|
|
488
|
+
-- invalidates them for active Visual mode
|
|
489
|
+
local count = mode == 'visual' and vim.v.count1 or H.cache.replace.count
|
|
490
|
+
local register = mode == 'visual' and vim.v.register or H.cache.replace.register
|
|
491
|
+
local data = H.get_region_data(mode)
|
|
492
|
+
if data == nil then return '' end
|
|
493
|
+
data.count = count
|
|
494
|
+
data.register = register
|
|
495
|
+
data.reindent_linewise = H.get_config().replace.reindent_linewise
|
|
496
|
+
|
|
497
|
+
H.replace_do(data)
|
|
498
|
+
|
|
499
|
+
return ''
|
|
500
|
+
end
|
|
501
|
+
|
|
502
|
+
--- Sort text
|
|
503
|
+
---
|
|
504
|
+
--- It replaces the region with the output of `config.sort.func`.
|
|
505
|
+
--- By default it is |MiniOperators.default_sort_func()| which sorts the text
|
|
506
|
+
--- depending on submode.
|
|
507
|
+
---
|
|
508
|
+
--- Notes:
|
|
509
|
+
--- - "line" mapping is charwise (as there is not much sense in sorting
|
|
510
|
+
--- linewise a single line). This also results into no |[count]| support.
|
|
511
|
+
---
|
|
512
|
+
---@param mode __operators_mode
|
|
513
|
+
MiniOperators.sort = function(mode)
|
|
514
|
+
if H.is_disabled() or not vim.bo.modifiable then return '' end
|
|
515
|
+
|
|
516
|
+
-- If used without arguments inside expression mapping, set it as
|
|
517
|
+
-- 'operatorfunc' and call it again as a result of expression mapping.
|
|
518
|
+
if mode == nil then
|
|
519
|
+
vim.o.operatorfunc = 'v:lua.MiniOperators.sort'
|
|
520
|
+
return 'g@'
|
|
521
|
+
end
|
|
522
|
+
|
|
523
|
+
local sort_func = H.get_config().sort.func or MiniOperators.default_sort_func
|
|
524
|
+
H.apply_content_func(sort_func, H.get_region_data(mode))
|
|
525
|
+
end
|
|
526
|
+
|
|
527
|
+
--- Make operator mappings
|
|
528
|
+
---
|
|
529
|
+
---@param operator_name string Name of existing operator from this module.
|
|
530
|
+
---@param lhs_tbl table Table with mappings keys. Should have these fields:
|
|
531
|
+
--- - <textobject> `(string)` - Normal mode mapping to operate on textobject.
|
|
532
|
+
--- - <line> `(string)` - Normal mode mapping to operate on line.
|
|
533
|
+
--- Usually an alias for textobject mapping followed by |_|.
|
|
534
|
+
--- For "sort" it operates charwise on whole line without left and right
|
|
535
|
+
--- whitespace (as there is not much sense in sorting linewise a single line).
|
|
536
|
+
--- - <selection> `(string)` - Visual mode mapping to operate on selection.
|
|
537
|
+
---
|
|
538
|
+
--- Supply empty string to not create particular mapping. Note: creating `line`
|
|
539
|
+
--- mapping needs `textobject` mapping to be set.
|
|
540
|
+
---
|
|
541
|
+
---@usage >lua
|
|
542
|
+
--- require('mini.operators').make_mappings(
|
|
543
|
+
--- 'replace',
|
|
544
|
+
--- { textobject = 'cr', line = 'crr', selection = 'cr' }
|
|
545
|
+
--- )
|
|
546
|
+
--- <
|
|
547
|
+
MiniOperators.make_mappings = function(operator_name, lhs_tbl)
|
|
548
|
+
-- Validate arguments
|
|
549
|
+
if not (type(operator_name) == 'string' and MiniOperators[operator_name] ~= nil) then
|
|
550
|
+
H.error('`operator_name` should be a valid operator name.')
|
|
551
|
+
end
|
|
552
|
+
local is_keys_tbl = type(lhs_tbl) == 'table'
|
|
553
|
+
and type(lhs_tbl.textobject) == 'string'
|
|
554
|
+
and type(lhs_tbl.line) == 'string'
|
|
555
|
+
and type(lhs_tbl.selection) == 'string'
|
|
556
|
+
if not is_keys_tbl then H.error('`lhs_tbl` should be a valid table of keys.') end
|
|
557
|
+
|
|
558
|
+
if lhs_tbl.line ~= '' and lhs_tbl.textobject == '' then
|
|
559
|
+
H.error('Creating mapping for `line` needs mapping for `textobject`.')
|
|
560
|
+
end
|
|
561
|
+
|
|
562
|
+
-- Make mappings
|
|
563
|
+
local operator_desc = operator_name:sub(1, 1):upper() .. operator_name:sub(2)
|
|
564
|
+
|
|
565
|
+
local expr_opts = { expr = true, replace_keycodes = false, desc = operator_desc }
|
|
566
|
+
H.map('n', lhs_tbl.textobject, string.format('v:lua.MiniOperators.%s()', operator_name), expr_opts)
|
|
567
|
+
|
|
568
|
+
local rhs = lhs_tbl.textobject .. '_'
|
|
569
|
+
-- - Make `sort()` line mapping to be charwise
|
|
570
|
+
if operator_name == 'sort' then rhs = '^' .. lhs_tbl.textobject .. 'g_' end
|
|
571
|
+
H.map('n', lhs_tbl.line, rhs, { remap = true, desc = operator_desc .. ' line' })
|
|
572
|
+
|
|
573
|
+
local visual_rhs = string.format([[<Cmd>lua MiniOperators.%s('visual')<CR>]], operator_name)
|
|
574
|
+
H.map('x', lhs_tbl.selection, visual_rhs, { desc = operator_desc .. ' selection' })
|
|
575
|
+
end
|
|
576
|
+
|
|
577
|
+
--- Default evaluate function
|
|
578
|
+
---
|
|
579
|
+
--- Evaluate text as Lua code and return object from last line (like if last
|
|
580
|
+
--- line is prepended with `return` if it is not already).
|
|
581
|
+
---
|
|
582
|
+
--- Behavior depends on region submode:
|
|
583
|
+
---
|
|
584
|
+
--- - For charwise and linewise regions, text evaluated as is.
|
|
585
|
+
---
|
|
586
|
+
--- - For blockwise region, lines are evaluated per line using only first lines
|
|
587
|
+
--- of outputs. This allows separate execution of lines in order to provide
|
|
588
|
+
--- something different compared to linewise region.
|
|
589
|
+
---
|
|
590
|
+
---@param content __operators_content
|
|
591
|
+
MiniOperators.default_evaluate_func = function(content)
|
|
592
|
+
if not H.is_content(content) then H.error('`content` should be a content table.') end
|
|
593
|
+
|
|
594
|
+
local lines, submode = content.lines, content.submode
|
|
595
|
+
|
|
596
|
+
-- In non-blockwise mode return the result of the last line
|
|
597
|
+
if submode ~= H.submode_keys.block then return H.eval_lua_lines(lines) end
|
|
598
|
+
|
|
599
|
+
-- In blockwise selection evaluate and return each line separately
|
|
600
|
+
return vim.tbl_map(function(l) return H.eval_lua_lines({ l })[1] end, lines)
|
|
601
|
+
end
|
|
602
|
+
|
|
603
|
+
--- Default sort function
|
|
604
|
+
---
|
|
605
|
+
--- Sort text based on region submode:
|
|
606
|
+
---
|
|
607
|
+
--- - For charwise region, split by separator pattern, sort parts, merge back
|
|
608
|
+
--- with separators. Actual pattern is inferred based on the array of patterns
|
|
609
|
+
--- from `opts.split_patterns`: whichever element is present in the text is
|
|
610
|
+
--- used, preferring the earlier one if several are present.
|
|
611
|
+
--- Example: sorting "c, b; a" line with default `opts.split_patterns` results
|
|
612
|
+
--- into "b; a, c" as it is split only by comma.
|
|
613
|
+
---
|
|
614
|
+
--- - For linewise and blockwise regions sort lines as is.
|
|
615
|
+
---
|
|
616
|
+
--- Notes:
|
|
617
|
+
--- - Sort is done with |table.sort()| on an array of lines, which doesn't treat
|
|
618
|
+
--- whitespace or digits specially. Use |:sort| for more complicated tasks.
|
|
619
|
+
---
|
|
620
|
+
--- - Pattern is allowed to be an empty string in which case split results into
|
|
621
|
+
--- all characters as parts.
|
|
622
|
+
---
|
|
623
|
+
--- - Pad pattern in `split_patterns` with `%s*` to include whitespace into separator.
|
|
624
|
+
--- Example: line "b _ a" with "_" pattern will be sorted as " a_b " (because
|
|
625
|
+
--- it is split as "b ", "_", " a" ) while with `%s*_%s*` pattern it results
|
|
626
|
+
--- into "a _ b" (split as "b", " _ ", "a").
|
|
627
|
+
---
|
|
628
|
+
---@param content __operators_content
|
|
629
|
+
---@param opts table|nil Options. Possible fields:
|
|
630
|
+
--- - <compare_fun> `(function)` - compare function compatible with |table.sort()|.
|
|
631
|
+
--- Default: direct compare with `<`.
|
|
632
|
+
--- - <split_patterns> `(table)` - array of split Lua patterns to be used for
|
|
633
|
+
--- charwise submode. Order is important.
|
|
634
|
+
--- Default: `{ '%s*,%s*', '%s*;%s*', '%s+', '' }`.
|
|
635
|
+
MiniOperators.default_sort_func = function(content, opts)
|
|
636
|
+
if not H.is_content(content) then H.error('`content` should be a content table.') end
|
|
637
|
+
|
|
638
|
+
opts = vim.tbl_deep_extend('force', { compare_fun = nil, split_patterns = nil }, opts or {})
|
|
639
|
+
|
|
640
|
+
local compare_fun = opts.compare_fun or function(a, b) return a < b end
|
|
641
|
+
if not vim.is_callable(compare_fun) then H.error('`opts.compare_fun` should be callable.') end
|
|
642
|
+
|
|
643
|
+
local split_patterns = opts.split_patterns or { '%s*,%s*', '%s*;%s*', '%s+', '' }
|
|
644
|
+
if not H.islist(split_patterns) then H.error('`opts.split_patterns` should be array.') end
|
|
645
|
+
|
|
646
|
+
-- Prepare lines to sort
|
|
647
|
+
local lines, submode = content.lines, content.submode
|
|
648
|
+
|
|
649
|
+
if submode ~= 'v' then
|
|
650
|
+
table.sort(lines, compare_fun)
|
|
651
|
+
return lines
|
|
652
|
+
end
|
|
653
|
+
|
|
654
|
+
local parts, seps = H.sort_charwise_split(lines, split_patterns)
|
|
655
|
+
table.sort(parts, compare_fun)
|
|
656
|
+
return H.sort_charwise_unsplit(parts, seps)
|
|
657
|
+
end
|
|
658
|
+
|
|
659
|
+
-- Helper data ================================================================
|
|
660
|
+
-- Module default config
|
|
661
|
+
H.default_config = vim.deepcopy(MiniOperators.config)
|
|
662
|
+
|
|
663
|
+
-- Namespaces
|
|
664
|
+
H.ns_id = {
|
|
665
|
+
exchange = vim.api.nvim_create_namespace('MiniOperatorsExchange'),
|
|
666
|
+
}
|
|
667
|
+
|
|
668
|
+
-- Cache for all operators
|
|
669
|
+
H.cache = {
|
|
670
|
+
exchange = {},
|
|
671
|
+
multiply = {},
|
|
672
|
+
replace = {},
|
|
673
|
+
}
|
|
674
|
+
|
|
675
|
+
-- Submode keys for
|
|
676
|
+
H.submode_keys = {
|
|
677
|
+
char = 'v',
|
|
678
|
+
line = 'V',
|
|
679
|
+
block = vim.api.nvim_replace_termcodes('<C-v>', true, true, true),
|
|
680
|
+
}
|
|
681
|
+
|
|
682
|
+
-- Helper functionality =======================================================
|
|
683
|
+
-- Settings -------------------------------------------------------------------
|
|
684
|
+
H.setup_config = function(config)
|
|
685
|
+
H.check_type('config', config, 'table', true)
|
|
686
|
+
config = vim.tbl_deep_extend('force', vim.deepcopy(H.default_config), config or {})
|
|
687
|
+
|
|
688
|
+
H.check_type('evaluate', config.evaluate, 'table')
|
|
689
|
+
H.check_type('evaluate.prefix', config.evaluate.prefix, 'string')
|
|
690
|
+
H.check_type('evaluate.func', config.evaluate.func, 'function', true)
|
|
691
|
+
|
|
692
|
+
H.check_type('exchange', config.exchange, 'table')
|
|
693
|
+
H.check_type('exchange.prefix', config.exchange.prefix, 'string')
|
|
694
|
+
H.check_type('exchange.reindent_linewise', config.exchange.reindent_linewise, 'boolean')
|
|
695
|
+
|
|
696
|
+
H.check_type('multiply', config.multiply, 'table')
|
|
697
|
+
H.check_type('multiply.prefix', config.multiply.prefix, 'string')
|
|
698
|
+
H.check_type('multiply.func', config.multiply.func, 'function', true)
|
|
699
|
+
|
|
700
|
+
H.check_type('replace', config.replace, 'table')
|
|
701
|
+
H.check_type('replace.prefix', config.replace.prefix, 'string')
|
|
702
|
+
H.check_type('replace.reindent_linewise', config.replace.reindent_linewise, 'boolean')
|
|
703
|
+
|
|
704
|
+
H.check_type('sort', config.sort, 'table')
|
|
705
|
+
H.check_type('sort.prefix', config.sort.prefix, 'string')
|
|
706
|
+
H.check_type('sort.func', config.sort.func, 'function', true)
|
|
707
|
+
|
|
708
|
+
return config
|
|
709
|
+
end
|
|
710
|
+
|
|
711
|
+
H.apply_config = function(config)
|
|
712
|
+
MiniOperators.config = config
|
|
713
|
+
|
|
714
|
+
local remove_lsp_mapping = function(mode, lhs)
|
|
715
|
+
local map_desc = vim.fn.maparg(lhs, mode, false, true).desc
|
|
716
|
+
if map_desc == nil or string.find(map_desc, 'vim%.lsp') == nil then return end
|
|
717
|
+
vim.keymap.del(mode, lhs)
|
|
718
|
+
end
|
|
719
|
+
|
|
720
|
+
local remap_builtin_gx = function(mode)
|
|
721
|
+
if vim.fn.maparg('gX', mode) ~= '' then return end
|
|
722
|
+
local keymap = vim.fn.maparg('gx', mode, false, true)
|
|
723
|
+
local rhs = keymap.callback or keymap.rhs
|
|
724
|
+
if rhs == nil or (keymap.desc or ''):find('URI under cursor') == nil then return end
|
|
725
|
+
vim.keymap.set(mode, 'gX', rhs, { desc = keymap.desc })
|
|
726
|
+
end
|
|
727
|
+
|
|
728
|
+
-- Make mappings
|
|
729
|
+
local map_all = function(operator_name)
|
|
730
|
+
-- Map only valid LHS
|
|
731
|
+
local prefix = config[operator_name].prefix
|
|
732
|
+
if type(prefix) ~= 'string' or prefix == '' then return end
|
|
733
|
+
|
|
734
|
+
-- Remove conflicting built-in mappings
|
|
735
|
+
if prefix == 'gr' and vim.fn.has('nvim-0.11') == 1 then
|
|
736
|
+
remove_lsp_mapping('n', 'gra')
|
|
737
|
+
remove_lsp_mapping('x', 'gra')
|
|
738
|
+
remove_lsp_mapping('n', 'gri')
|
|
739
|
+
remove_lsp_mapping('n', 'grn')
|
|
740
|
+
remove_lsp_mapping('n', 'grr')
|
|
741
|
+
remove_lsp_mapping('n', 'grt')
|
|
742
|
+
remove_lsp_mapping('n', 'grx')
|
|
743
|
+
end
|
|
744
|
+
|
|
745
|
+
if prefix == 'gx' and vim.fn.has('nvim-0.10') == 1 then
|
|
746
|
+
remap_builtin_gx('n')
|
|
747
|
+
remap_builtin_gx('x')
|
|
748
|
+
end
|
|
749
|
+
|
|
750
|
+
local lhs_tbl = {
|
|
751
|
+
textobject = prefix,
|
|
752
|
+
line = prefix .. vim.fn.strcharpart(prefix, vim.fn.strchars(prefix) - 1, 1),
|
|
753
|
+
selection = prefix,
|
|
754
|
+
}
|
|
755
|
+
MiniOperators.make_mappings(operator_name, lhs_tbl)
|
|
756
|
+
end
|
|
757
|
+
|
|
758
|
+
map_all('evaluate')
|
|
759
|
+
map_all('exchange')
|
|
760
|
+
map_all('multiply')
|
|
761
|
+
map_all('replace')
|
|
762
|
+
map_all('sort')
|
|
763
|
+
end
|
|
764
|
+
|
|
765
|
+
H.is_disabled = function() return vim.g.minioperators_disable == true or vim.b.minioperators_disable == true end
|
|
766
|
+
|
|
767
|
+
H.get_config = function(config)
|
|
768
|
+
return vim.tbl_deep_extend('force', MiniOperators.config, vim.b.minioperators_config or {}, config or {})
|
|
769
|
+
end
|
|
770
|
+
|
|
771
|
+
H.create_autocommands = function()
|
|
772
|
+
local gr = vim.api.nvim_create_augroup('MiniOperators', {})
|
|
773
|
+
vim.api.nvim_create_autocmd('ColorScheme', { group = gr, callback = H.create_default_hl, desc = 'Ensure colors' })
|
|
774
|
+
end
|
|
775
|
+
|
|
776
|
+
H.create_default_hl = function()
|
|
777
|
+
vim.api.nvim_set_hl(0, 'MiniOperatorsExchangeFrom', { default = true, link = 'IncSearch' })
|
|
778
|
+
end
|
|
779
|
+
|
|
780
|
+
-- Evaluate -------------------------------------------------------------------
|
|
781
|
+
H.eval_lua_lines = function(lines)
|
|
782
|
+
-- Copy to not modify input
|
|
783
|
+
local lines_copy, n = vim.deepcopy(lines), #lines
|
|
784
|
+
lines_copy[n] = (lines_copy[n]:find('^%s*return%s+') == nil and 'return ' or '') .. lines_copy[n]
|
|
785
|
+
|
|
786
|
+
local str_to_eval = table.concat(lines_copy, '\n')
|
|
787
|
+
|
|
788
|
+
-- Allow returning tuple with any value(s) being `nil`
|
|
789
|
+
return H.inspect_objects(assert(loadstring(str_to_eval))())
|
|
790
|
+
end
|
|
791
|
+
|
|
792
|
+
H.inspect_objects = function(...)
|
|
793
|
+
local objects = {}
|
|
794
|
+
-- Not using `{...}` because it removes `nil` input
|
|
795
|
+
for i = 1, select('#', ...) do
|
|
796
|
+
local v = select(i, ...)
|
|
797
|
+
table.insert(objects, vim.inspect(v))
|
|
798
|
+
end
|
|
799
|
+
|
|
800
|
+
return vim.split(table.concat(objects, '\n'), '\n')
|
|
801
|
+
end
|
|
802
|
+
|
|
803
|
+
-- Exchange -------------------------------------------------------------------
|
|
804
|
+
H.exchange_do = function()
|
|
805
|
+
local step_one, step_two = H.cache.exchange.step_one, H.cache.exchange.step_two
|
|
806
|
+
|
|
807
|
+
-- Do nothing if regions are the same
|
|
808
|
+
if H.exchange_is_same_steps(step_one, step_two) then return end
|
|
809
|
+
|
|
810
|
+
-- Save temporary registers
|
|
811
|
+
local reg_one, reg_two = vim.fn.getreginfo('a'), vim.fn.getreginfo('b')
|
|
812
|
+
|
|
813
|
+
-- Create step temporary contexts (data that should not change)
|
|
814
|
+
local context_one = { buf_id = step_one.buf_id, marks = { 'x', 'y' }, registers = { '"' } }
|
|
815
|
+
local context_two = { buf_id = step_two.buf_id, marks = { 'x', 'y' }, registers = { '"' } }
|
|
816
|
+
|
|
817
|
+
-- Put regions into registers. NOTE: do it before actual exchange to allow
|
|
818
|
+
-- intersecting regions.
|
|
819
|
+
local populating_register = function(step, register)
|
|
820
|
+
return function()
|
|
821
|
+
H.exchange_set_step_marks(step, { 'x', 'y' })
|
|
822
|
+
local yank_data =
|
|
823
|
+
{ mark_from = 'x', mark_to = 'y', submode = step.submode, mode = step.mode, register = register }
|
|
824
|
+
H.do_between_marks('y', yank_data)
|
|
825
|
+
end
|
|
826
|
+
end
|
|
827
|
+
|
|
828
|
+
H.with_temp_context(context_one, populating_register(step_one, 'a'))
|
|
829
|
+
H.with_temp_context(context_two, populating_register(step_two, 'b'))
|
|
830
|
+
|
|
831
|
+
-- Sequentially replace
|
|
832
|
+
local replacing = function(step, register)
|
|
833
|
+
return function()
|
|
834
|
+
H.exchange_set_step_marks(step, { 'x', 'y' })
|
|
835
|
+
|
|
836
|
+
local replace_data = {
|
|
837
|
+
count = 1,
|
|
838
|
+
mark_from = 'x',
|
|
839
|
+
mark_to = 'y',
|
|
840
|
+
mode = step.mode,
|
|
841
|
+
register = register,
|
|
842
|
+
reindent_linewise = H.get_config().exchange.reindent_linewise,
|
|
843
|
+
submode = step.submode,
|
|
844
|
+
}
|
|
845
|
+
H.replace_do(replace_data)
|
|
846
|
+
end
|
|
847
|
+
end
|
|
848
|
+
|
|
849
|
+
H.with_temp_context(context_one, replacing(step_one, 'b'))
|
|
850
|
+
H.with_temp_context(context_two, replacing(step_two, 'a'))
|
|
851
|
+
|
|
852
|
+
-- Restore temporary registers
|
|
853
|
+
vim.fn.setreg('a', reg_one)
|
|
854
|
+
vim.fn.setreg('b', reg_two)
|
|
855
|
+
end
|
|
856
|
+
|
|
857
|
+
H.exchange_has_step_one = function()
|
|
858
|
+
local step_one = H.cache.exchange.step_one
|
|
859
|
+
if type(step_one) ~= 'table' then return false end
|
|
860
|
+
|
|
861
|
+
if not vim.api.nvim_buf_is_valid(step_one.buf_id) then
|
|
862
|
+
H.exchange_stop()
|
|
863
|
+
return false
|
|
864
|
+
end
|
|
865
|
+
return true
|
|
866
|
+
end
|
|
867
|
+
|
|
868
|
+
H.exchange_set_region_extmark = function(mode, add_highlight)
|
|
869
|
+
local ns_id = H.ns_id.exchange
|
|
870
|
+
|
|
871
|
+
-- Compute regular marks for target region
|
|
872
|
+
local region_data = H.get_region_data(mode)
|
|
873
|
+
if region_data == nil then return end
|
|
874
|
+
local submode = region_data.submode
|
|
875
|
+
local markcoords_from, markcoords_to = H.get_mark(region_data.mark_from), H.get_mark(region_data.mark_to)
|
|
876
|
+
|
|
877
|
+
-- Compute extmark's range for target region
|
|
878
|
+
local extmark_from = { markcoords_from[1] - 1, markcoords_from[2] }
|
|
879
|
+
local extmark_to = { markcoords_to[1] - 1, H.get_next_char_bytecol(markcoords_to) }
|
|
880
|
+
|
|
881
|
+
-- Adjust for visual selection in case of 'selection=exclusive'
|
|
882
|
+
if region_data.mark_to == '>' and vim.o.selection == 'exclusive' then extmark_to[2] = extmark_to[2] - 1 end
|
|
883
|
+
|
|
884
|
+
-- - Tweak columns for linewise marks
|
|
885
|
+
if submode == 'V' then
|
|
886
|
+
extmark_from[2] = 0
|
|
887
|
+
extmark_to[2] = vim.fn.col({ extmark_to[1] + 1, '$' }) - 1
|
|
888
|
+
end
|
|
889
|
+
|
|
890
|
+
-- Set extmark to represent region. Add highlighting inside of it only if
|
|
891
|
+
-- needed and not in blockwise submode (can't highlight that way).
|
|
892
|
+
local buf_id = vim.api.nvim_get_current_buf()
|
|
893
|
+
|
|
894
|
+
local extmark_hl_group
|
|
895
|
+
if add_highlight and submode ~= H.submode_keys.block then extmark_hl_group = 'MiniOperatorsExchangeFrom' end
|
|
896
|
+
|
|
897
|
+
local extmark_opts = {
|
|
898
|
+
end_row = extmark_to[1],
|
|
899
|
+
end_col = extmark_to[2],
|
|
900
|
+
hl_group = extmark_hl_group,
|
|
901
|
+
-- Using this gravity is better for handling empty lines in linewise mode
|
|
902
|
+
end_right_gravity = mode == 'line',
|
|
903
|
+
}
|
|
904
|
+
local region_extmark_id = vim.api.nvim_buf_set_extmark(buf_id, ns_id, extmark_from[1], extmark_from[2], extmark_opts)
|
|
905
|
+
|
|
906
|
+
-- - Possibly add highlighting for blockwise mode
|
|
907
|
+
if add_highlight and extmark_hl_group == nil then
|
|
908
|
+
-- Highlighting blockwise region needs full register type with width
|
|
909
|
+
local opts = { regtype = H.exchange_get_blockwise_regtype(markcoords_from, markcoords_to) }
|
|
910
|
+
H.highlight_range(buf_id, ns_id, 'MiniOperatorsExchangeFrom', extmark_from, extmark_to, opts)
|
|
911
|
+
end
|
|
912
|
+
|
|
913
|
+
-- Return data to cache
|
|
914
|
+
return { buf_id = buf_id, mode = mode, submode = submode, extmark_id = region_extmark_id }
|
|
915
|
+
end
|
|
916
|
+
|
|
917
|
+
H.exchange_get_region_extmark = function(step)
|
|
918
|
+
return vim.api.nvim_buf_get_extmark_by_id(step.buf_id, H.ns_id.exchange, step.extmark_id, { details = true })
|
|
919
|
+
end
|
|
920
|
+
|
|
921
|
+
H.exchange_set_step_marks = function(step, mark_names)
|
|
922
|
+
local extmark_details = H.exchange_get_region_extmark(step)
|
|
923
|
+
|
|
924
|
+
H.set_mark(mark_names[1], { extmark_details[1] + 1, extmark_details[2] })
|
|
925
|
+
|
|
926
|
+
-- Unadjust for visual selection in case of 'selection=exclusive'
|
|
927
|
+
local should_unadjust = step.mode == 'visual' and vim.o.selection == 'exclusive'
|
|
928
|
+
local col_offset = should_unadjust and 1 or 0
|
|
929
|
+
|
|
930
|
+
H.set_mark(mark_names[2], { extmark_details[3].end_row + 1, extmark_details[3].end_col - 1 + col_offset })
|
|
931
|
+
end
|
|
932
|
+
|
|
933
|
+
H.exchange_get_blockwise_regtype = function(markcoords_from, markcoords_to)
|
|
934
|
+
local f = function()
|
|
935
|
+
-- Yank into "z" register and return its blockwise type
|
|
936
|
+
H.set_mark('x', markcoords_from)
|
|
937
|
+
H.set_mark('y', markcoords_to)
|
|
938
|
+
local yank_data = { mark_from = 'x', mark_to = 'y', submode = H.submode_keys.block, mode = 'block', register = 't' }
|
|
939
|
+
H.do_between_marks('y', yank_data)
|
|
940
|
+
|
|
941
|
+
return vim.fn.getregtype('t')
|
|
942
|
+
end
|
|
943
|
+
|
|
944
|
+
return H.with_temp_context({ buf_id = 0, marks = { 'x', 'y' }, registers = { 't' } }, f)
|
|
945
|
+
end
|
|
946
|
+
|
|
947
|
+
H.exchange_stop = function()
|
|
948
|
+
H.exchange_del_stop_mapping()
|
|
949
|
+
|
|
950
|
+
local cur, ns_id = H.cache.exchange, H.ns_id.exchange
|
|
951
|
+
if cur.step_one ~= nil then pcall(vim.api.nvim_buf_clear_namespace, cur.step_one.buf_id, ns_id, 0, -1) end
|
|
952
|
+
if cur.step_two ~= nil then pcall(vim.api.nvim_buf_clear_namespace, cur.step_two.buf_id, ns_id, 0, -1) end
|
|
953
|
+
H.cache.exchange = {}
|
|
954
|
+
end
|
|
955
|
+
|
|
956
|
+
H.exchange_set_stop_mapping = function()
|
|
957
|
+
local lhs = '<C-c>'
|
|
958
|
+
H.cache.exchange.stop_restore_map_data = vim.fn.maparg(lhs, 'n', false, true)
|
|
959
|
+
vim.keymap.set('n', lhs, H.exchange_stop, { desc = 'Stop exchange' })
|
|
960
|
+
end
|
|
961
|
+
|
|
962
|
+
H.exchange_del_stop_mapping = function()
|
|
963
|
+
local map_data = H.cache.exchange.stop_restore_map_data
|
|
964
|
+
if map_data == nil then return end
|
|
965
|
+
|
|
966
|
+
-- Try restore previous mapping if it was set
|
|
967
|
+
if vim.tbl_count(map_data) > 0 then
|
|
968
|
+
vim.fn.mapset('n', false, map_data)
|
|
969
|
+
else
|
|
970
|
+
vim.keymap.del('n', map_data.lhs or '<C-c>')
|
|
971
|
+
end
|
|
972
|
+
end
|
|
973
|
+
|
|
974
|
+
H.exchange_is_same_steps = function(step_one, step_two)
|
|
975
|
+
if step_one.buf_id ~= step_two.buf_id or step_one.submode ~= step_two.submode then return false end
|
|
976
|
+
-- Region's start and end should be the same
|
|
977
|
+
local one, two = H.exchange_get_region_extmark(step_one), H.exchange_get_region_extmark(step_two)
|
|
978
|
+
return one[1] == two[1] and one[2] == two[2] and one[3].end_row == two[3].end_row and one[3].end_col == two[3].end_col
|
|
979
|
+
end
|
|
980
|
+
|
|
981
|
+
-- Multiply -------------------------------------------------------------------
|
|
982
|
+
H.multiply_get_ref_coords = function(mark_from, mark_to, submode)
|
|
983
|
+
local markcoords_from, markcoords_to = H.get_mark(mark_from), H.get_mark(mark_to)
|
|
984
|
+
if mark_to == '>' and vim.o.selection == 'exclusive' then markcoords_to[2] = markcoords_to[2] - 1 end
|
|
985
|
+
|
|
986
|
+
if submode ~= H.submode_keys.block then return markcoords_to end
|
|
987
|
+
|
|
988
|
+
-- In blockwise selection go to top right corner (allowing for presence of
|
|
989
|
+
-- multibyte characters)
|
|
990
|
+
local row = math.min(markcoords_from[1], markcoords_to[1])
|
|
991
|
+
|
|
992
|
+
-- - "from"/"to" may not only be "top-left"/"bottom-right" but also
|
|
993
|
+
-- "top-right" and "bottom-left"
|
|
994
|
+
local virtcol_from = vim.fn.virtcol({ markcoords_from[1], markcoords_from[2] + 1 })
|
|
995
|
+
local virtcol_to = vim.fn.virtcol({ markcoords_to[1], markcoords_to[2] + 1 })
|
|
996
|
+
local virtcol = math.max(virtcol_from, virtcol_to)
|
|
997
|
+
|
|
998
|
+
local col = vim.fn.virtcol2col(0, row, virtcol)
|
|
999
|
+
|
|
1000
|
+
return { row, col - 1 }
|
|
1001
|
+
end
|
|
1002
|
+
|
|
1003
|
+
-- Replace --------------------------------------------------------------------
|
|
1004
|
+
--- Delete region between two marks and paste from register
|
|
1005
|
+
---
|
|
1006
|
+
---@param data table Fields:
|
|
1007
|
+
--- - <count> (optional) - Number of times to paste.
|
|
1008
|
+
--- - <mark_from> - Name of "from" mark.
|
|
1009
|
+
--- - <mark_to> - Name of "to" mark.
|
|
1010
|
+
--- - <mode> - Operator mode. One of 'visual', 'char', 'line', 'block'.
|
|
1011
|
+
--- - <register> - Name of register from which to paste.
|
|
1012
|
+
--- - <submode> - Region submode. One of 'v', 'V', '\22'.
|
|
1013
|
+
---@private
|
|
1014
|
+
H.replace_do = function(data)
|
|
1015
|
+
-- NOTE: Ideally, implementation would leverage "Visually select - press `P`"
|
|
1016
|
+
-- approach, but it has issues with dot-repeat. The `cancel_redo()` approach
|
|
1017
|
+
-- doesn't work probably because `P` implementation uses more than one
|
|
1018
|
+
-- dot-repeat overwrite.
|
|
1019
|
+
local register, submode = data.register, data.submode
|
|
1020
|
+
local mark_from, mark_to = data.mark_from, data.mark_to
|
|
1021
|
+
|
|
1022
|
+
-- Do nothing with invalid register (don't allow A-Z because they are used to
|
|
1023
|
+
-- append to lowercase register and have no use here)
|
|
1024
|
+
local reg_is_invalid = string.find(register, '^[0-9a-z"%-:.%%#=*+_/]$') == nil
|
|
1025
|
+
if reg_is_invalid then H.error('Register ' .. vim.inspect(register) .. ' is invalid.') end
|
|
1026
|
+
|
|
1027
|
+
-- Get reginfo and infer missing data (can be empty for special registers)
|
|
1028
|
+
local reg_info = vim.fn.getreginfo(register)
|
|
1029
|
+
if reg_info.regcontents == nil then H.error('Register ' .. vim.inspect(register) .. ' is empty.') end
|
|
1030
|
+
reg_info.regtype = reg_info.regtype or 'v'
|
|
1031
|
+
|
|
1032
|
+
-- Determine if region is at edge which is needed for the correct paste key
|
|
1033
|
+
local from_line, from_col = unpack(H.get_mark(mark_from))
|
|
1034
|
+
local to_line, to_col = unpack(H.get_mark(mark_to))
|
|
1035
|
+
local edge_to_col = vim.fn.col({ to_line, '$' }) - 1 - (vim.o.selection == 'exclusive' and 0 or 1)
|
|
1036
|
+
|
|
1037
|
+
local is_edge_line = submode == 'V' and to_line == vim.fn.line('$')
|
|
1038
|
+
local is_edge_col = submode ~= 'V' and to_col == edge_to_col and vim.o.virtualedit ~= 'all'
|
|
1039
|
+
local is_edge = is_edge_line or is_edge_col
|
|
1040
|
+
|
|
1041
|
+
local covers_linewise_all_buffer = is_edge_line and from_line == 1
|
|
1042
|
+
|
|
1043
|
+
-- Compute current indent if needed
|
|
1044
|
+
local init_indent
|
|
1045
|
+
local should_reindent = data.reindent_linewise and data.submode == 'V' and vim.o.equalprg == ''
|
|
1046
|
+
if should_reindent then init_indent = H.get_region_indent(mark_from, mark_to) end
|
|
1047
|
+
|
|
1048
|
+
-- Delete region to black whole register
|
|
1049
|
+
-- - Delete single character in blockwise submode with inclusive motion.
|
|
1050
|
+
-- See https://github.com/neovim/neovim/issues/24613
|
|
1051
|
+
local is_blockwise_single_cell = submode == H.submode_keys.block and from_line == to_line and from_col == to_col
|
|
1052
|
+
local forced_motion = is_blockwise_single_cell and 'v' or submode
|
|
1053
|
+
|
|
1054
|
+
local delete_data =
|
|
1055
|
+
{ mark_from = mark_from, mark_to = mark_to, submode = forced_motion, mode = data.mode, register = '_' }
|
|
1056
|
+
H.do_between_marks('d', delete_data)
|
|
1057
|
+
|
|
1058
|
+
-- Set temporary register data to have proper submode and indent
|
|
1059
|
+
-- NOTE: use dedicated temporary register to workaround not being able to
|
|
1060
|
+
-- write register data into readonly registers ('%', '#', '.').
|
|
1061
|
+
local tmp_register, tmp_reg_info = register == '=' and '=' or 'x', vim.deepcopy(reg_info)
|
|
1062
|
+
if tmp_reg_info.regtype:sub(1, 1) ~= submode then tmp_reg_info.regtype = submode end
|
|
1063
|
+
if should_reindent then tmp_reg_info.regcontents = H.update_indent(tmp_reg_info.regcontents, init_indent) end
|
|
1064
|
+
|
|
1065
|
+
local cache_reg_info = vim.fn.getreginfo(tmp_register)
|
|
1066
|
+
vim.fn.setreg(tmp_register, tmp_reg_info)
|
|
1067
|
+
|
|
1068
|
+
-- Paste
|
|
1069
|
+
local expr_reg_keys = tmp_register == '=' and (reg_info.regcontents[1] .. '\r') or ''
|
|
1070
|
+
local paste_keys = (data.count or 1) .. '"' .. tmp_register .. expr_reg_keys .. (is_edge and 'p' or 'P')
|
|
1071
|
+
H.cmd_normal(paste_keys)
|
|
1072
|
+
|
|
1073
|
+
-- Restore temporary register data
|
|
1074
|
+
vim.fn.setreg(tmp_register, cache_reg_info)
|
|
1075
|
+
|
|
1076
|
+
-- Adjust cursor to be at start mark
|
|
1077
|
+
vim.api.nvim_win_set_cursor(0, { from_line, from_col })
|
|
1078
|
+
|
|
1079
|
+
-- Adjust for extra empty line after pasting inside empty buffer
|
|
1080
|
+
if covers_linewise_all_buffer then vim.api.nvim_buf_set_lines(0, 0, 1, true, {}) end
|
|
1081
|
+
end
|
|
1082
|
+
|
|
1083
|
+
-- Sort -----------------------------------------------------------------------
|
|
1084
|
+
H.sort_charwise_split = function(lines, split_patterns)
|
|
1085
|
+
local lines_str = table.concat(lines, '\n')
|
|
1086
|
+
|
|
1087
|
+
local pat
|
|
1088
|
+
for _, pattern in ipairs(split_patterns) do
|
|
1089
|
+
if lines_str:find(pattern) ~= nil then
|
|
1090
|
+
pat = pattern
|
|
1091
|
+
break
|
|
1092
|
+
end
|
|
1093
|
+
end
|
|
1094
|
+
|
|
1095
|
+
if pat == nil then return lines, {} end
|
|
1096
|
+
|
|
1097
|
+
-- Allow pattern to be an empty string to get every character
|
|
1098
|
+
if pat == '' then
|
|
1099
|
+
local parts = vim.split(lines_str, '')
|
|
1100
|
+
local seps = vim.fn['repeat']({ '' }, #parts - 1)
|
|
1101
|
+
return parts, seps
|
|
1102
|
+
end
|
|
1103
|
+
|
|
1104
|
+
-- Split into parts and separators
|
|
1105
|
+
local parts, seps = {}, {}
|
|
1106
|
+
local init, n = 1, lines_str:len()
|
|
1107
|
+
while init < n do
|
|
1108
|
+
local sep_from, sep_to = string.find(lines_str, pat, init)
|
|
1109
|
+
if sep_from == nil then break end
|
|
1110
|
+
table.insert(parts, lines_str:sub(init, sep_from - 1))
|
|
1111
|
+
table.insert(seps, lines_str:sub(sep_from, sep_to))
|
|
1112
|
+
init = sep_to + 1
|
|
1113
|
+
end
|
|
1114
|
+
table.insert(parts, lines_str:sub(init, n))
|
|
1115
|
+
|
|
1116
|
+
return parts, seps
|
|
1117
|
+
end
|
|
1118
|
+
|
|
1119
|
+
H.sort_charwise_unsplit = function(parts, seps)
|
|
1120
|
+
local all = {}
|
|
1121
|
+
for i = 1, #parts do
|
|
1122
|
+
table.insert(all, parts[i])
|
|
1123
|
+
table.insert(all, seps[i] or '')
|
|
1124
|
+
end
|
|
1125
|
+
|
|
1126
|
+
return vim.split(table.concat(all, ''), '\n')
|
|
1127
|
+
end
|
|
1128
|
+
|
|
1129
|
+
-- General --------------------------------------------------------------------
|
|
1130
|
+
H.apply_content_func = function(content_func, data)
|
|
1131
|
+
if data == nil then return end
|
|
1132
|
+
local mark_from, mark_to, submode = data.mark_from, data.mark_to, data.submode
|
|
1133
|
+
local reindent_linewise = data.reindent_linewise
|
|
1134
|
+
|
|
1135
|
+
H.with_temp_context({ marks = { '>' }, registers = { 'x', '"' } }, function()
|
|
1136
|
+
-- Yank effective region content into "x" register.
|
|
1137
|
+
data.register = 'x'
|
|
1138
|
+
H.do_between_marks('y', data)
|
|
1139
|
+
|
|
1140
|
+
-- Apply content function to register content
|
|
1141
|
+
local reg_info = vim.fn.getreginfo('x')
|
|
1142
|
+
local content_init = { lines = reg_info.regcontents, submode = submode }
|
|
1143
|
+
reg_info.regcontents = content_func(content_init)
|
|
1144
|
+
vim.fn.setreg('x', reg_info)
|
|
1145
|
+
|
|
1146
|
+
-- Replace region with new register content
|
|
1147
|
+
local replace_data = {
|
|
1148
|
+
count = 1,
|
|
1149
|
+
mark_from = mark_from,
|
|
1150
|
+
mark_to = mark_to,
|
|
1151
|
+
mode = data.mode,
|
|
1152
|
+
register = 'x',
|
|
1153
|
+
reindent_linewise = reindent_linewise,
|
|
1154
|
+
submode = submode,
|
|
1155
|
+
}
|
|
1156
|
+
H.replace_do(replace_data)
|
|
1157
|
+
end)
|
|
1158
|
+
end
|
|
1159
|
+
|
|
1160
|
+
H.do_between_marks = function(operator, data)
|
|
1161
|
+
-- Force 'inclusive' selection as `<C-v>` submode does not force it (while
|
|
1162
|
+
-- `v` does). This means that in case of 'selection=exclusive' marks should
|
|
1163
|
+
-- be adjusted prior to this.
|
|
1164
|
+
local cache_selection = vim.o.selection
|
|
1165
|
+
if data.mode == 'block' and vim.o.selection == 'exclusive' then vim.o.selection = 'inclusive' end
|
|
1166
|
+
|
|
1167
|
+
-- Allow positioning cursor past line end to work for regions with newline
|
|
1168
|
+
local cache_virtualedit = vim.o.virtualedit
|
|
1169
|
+
vim.o.virtualedit = 'onemore'
|
|
1170
|
+
|
|
1171
|
+
-- Don't trigger `TextYankPost` event as these yanks are not user-facing
|
|
1172
|
+
local is_yank = operator == 'y'
|
|
1173
|
+
local cache_eventignore = vim.o.eventignore
|
|
1174
|
+
if is_yank then vim.o.eventignore = 'TextYankPost' end
|
|
1175
|
+
|
|
1176
|
+
-- Make sure that marks `[` and `]` don't change after `y`
|
|
1177
|
+
local context_marks = { '<', '>' }
|
|
1178
|
+
if is_yank then context_marks = vim.list_extend(context_marks, { '[', ']' }) end
|
|
1179
|
+
H.with_temp_context({ marks = context_marks }, function()
|
|
1180
|
+
local mark_from, mark_to, submode, register = data.mark_from, data.mark_to, data.submode, data.register
|
|
1181
|
+
local keys
|
|
1182
|
+
if data.mode == 'visual' and vim.o.selection == 'exclusive' then
|
|
1183
|
+
keys = ('`' .. mark_from) .. submode .. ('`' .. mark_to) .. ('"' .. register .. operator)
|
|
1184
|
+
else
|
|
1185
|
+
keys = ('`' .. mark_from) .. ('"' .. register .. operator .. submode) .. ('`' .. mark_to)
|
|
1186
|
+
end
|
|
1187
|
+
|
|
1188
|
+
-- Make sure that outer action is dot-repeatable by cancelling effect of
|
|
1189
|
+
-- `d` or dot-repeatable `y`
|
|
1190
|
+
local cancel_redo = operator == 'd' or (operator == 'y' and vim.o.cpoptions:find('y') ~= nil)
|
|
1191
|
+
H.cmd_normal(keys, { cancel_redo = cancel_redo })
|
|
1192
|
+
end)
|
|
1193
|
+
|
|
1194
|
+
vim.o.selection = cache_selection
|
|
1195
|
+
vim.o.virtualedit = cache_virtualedit
|
|
1196
|
+
if is_yank then vim.o.eventignore = cache_eventignore end
|
|
1197
|
+
end
|
|
1198
|
+
|
|
1199
|
+
H.is_content = function(x) return type(x) == 'table' and H.islist(x.lines) and type(x.submode) == 'string' end
|
|
1200
|
+
|
|
1201
|
+
-- Marks ----------------------------------------------------------------------
|
|
1202
|
+
H.get_region_data = function(mode)
|
|
1203
|
+
local submode = H.get_submode(mode)
|
|
1204
|
+
local selection_is_visual = mode == 'visual'
|
|
1205
|
+
|
|
1206
|
+
-- Make sure that visual selection marks are relevant
|
|
1207
|
+
if selection_is_visual and H.is_visual_mode() then vim.cmd('normal! \27') end
|
|
1208
|
+
|
|
1209
|
+
local mark_from = selection_is_visual and '<' or '['
|
|
1210
|
+
local mark_to = selection_is_visual and '>' or ']'
|
|
1211
|
+
|
|
1212
|
+
-- Detect empty region. NOTE: This doesn't work when cursor is on first line
|
|
1213
|
+
-- and first column, but there doesn't seem to be a better way to do that.
|
|
1214
|
+
local pos_from, pos_to = H.get_mark(mark_from), H.get_mark(mark_to)
|
|
1215
|
+
if pos_to[1] < pos_from[1] or (pos_to[1] == pos_from[1] and pos_to[2] < pos_from[2]) then return end
|
|
1216
|
+
|
|
1217
|
+
return { mode = mode, submode = submode, mark_from = mark_from, mark_to = mark_to }
|
|
1218
|
+
end
|
|
1219
|
+
|
|
1220
|
+
H.get_region_indent = function(mark_from, mark_to)
|
|
1221
|
+
local l_from, l_to = H.get_mark(mark_from)[1], H.get_mark(mark_to)[1]
|
|
1222
|
+
local lines = vim.api.nvim_buf_get_lines(0, l_from - 1, l_to, true)
|
|
1223
|
+
return H.compute_indent(lines)
|
|
1224
|
+
end
|
|
1225
|
+
|
|
1226
|
+
H.get_mark = function(mark_name) return vim.api.nvim_buf_get_mark(0, mark_name) end
|
|
1227
|
+
|
|
1228
|
+
H.set_mark = function(mark_name, mark_data) vim.api.nvim_buf_set_mark(0, mark_name, mark_data[1], mark_data[2], {}) end
|
|
1229
|
+
|
|
1230
|
+
H.str_utfindex = function(s, i) return vim.str_utfindex(s, 'utf-32', i) end
|
|
1231
|
+
if vim.fn.has('nvim-0.11') == 0 then H.str_utfindex = function(s, i) return (vim.str_utfindex(s, i)) end end
|
|
1232
|
+
|
|
1233
|
+
H.str_byteindex = function(s, i) return vim.str_byteindex(s, 'utf-32', i) end
|
|
1234
|
+
if vim.fn.has('nvim-0.11') == 0 then H.str_byteindex = function(s, i) return vim.str_byteindex(s, i) end end
|
|
1235
|
+
|
|
1236
|
+
H.get_next_char_bytecol = function(markcoords)
|
|
1237
|
+
local line = vim.fn.getline(markcoords[1])
|
|
1238
|
+
local utf_index = H.str_utfindex(line, math.min(line:len(), markcoords[2] + 1))
|
|
1239
|
+
return H.str_byteindex(line, utf_index)
|
|
1240
|
+
end
|
|
1241
|
+
|
|
1242
|
+
-- Indent ---------------------------------------------------------------------
|
|
1243
|
+
H.compute_indent = function(lines)
|
|
1244
|
+
local res_indent, res_indent_width = nil, math.huge
|
|
1245
|
+
local blank_indent, blank_indent_width = nil, math.huge
|
|
1246
|
+
for _, l in ipairs(lines) do
|
|
1247
|
+
local cur_indent = l:match('^%s*')
|
|
1248
|
+
local cur_indent_width = cur_indent:len()
|
|
1249
|
+
local is_blank = cur_indent_width == l:len()
|
|
1250
|
+
if not is_blank and cur_indent_width < res_indent_width then
|
|
1251
|
+
res_indent, res_indent_width = cur_indent, cur_indent_width
|
|
1252
|
+
elseif is_blank and cur_indent_width < blank_indent_width then
|
|
1253
|
+
blank_indent, blank_indent_width = cur_indent, cur_indent_width
|
|
1254
|
+
end
|
|
1255
|
+
end
|
|
1256
|
+
|
|
1257
|
+
return res_indent or blank_indent or ''
|
|
1258
|
+
end
|
|
1259
|
+
|
|
1260
|
+
H.update_indent = function(lines, new_indent)
|
|
1261
|
+
-- Replace current indent with new indent without affecting blank lines
|
|
1262
|
+
local n_cur_indent = H.compute_indent(lines):len()
|
|
1263
|
+
return vim.tbl_map(function(l)
|
|
1264
|
+
if l:find('^%s*$') ~= nil then return l end
|
|
1265
|
+
return new_indent .. l:sub(n_cur_indent + 1)
|
|
1266
|
+
end, lines)
|
|
1267
|
+
end
|
|
1268
|
+
|
|
1269
|
+
-- Utilities ------------------------------------------------------------------
|
|
1270
|
+
H.error = function(msg) error('(mini.operators) ' .. msg, 0) end
|
|
1271
|
+
|
|
1272
|
+
H.check_type = function(name, val, ref, allow_nil)
|
|
1273
|
+
if type(val) == ref or (ref == 'callable' and vim.is_callable(val)) or (allow_nil and val == nil) then return end
|
|
1274
|
+
H.error(string.format('`%s` should be %s, not %s', name, ref, type(val)))
|
|
1275
|
+
end
|
|
1276
|
+
|
|
1277
|
+
H.map = function(mode, lhs, rhs, opts)
|
|
1278
|
+
if lhs == '' then return end
|
|
1279
|
+
opts = vim.tbl_deep_extend('force', { silent = true }, opts or {})
|
|
1280
|
+
vim.keymap.set(mode, lhs, rhs, opts)
|
|
1281
|
+
end
|
|
1282
|
+
|
|
1283
|
+
H.get_submode = function(mode)
|
|
1284
|
+
if mode == 'visual' then return H.is_visual_mode() and vim.fn.mode() or vim.fn.visualmode() end
|
|
1285
|
+
return H.submode_keys[mode]
|
|
1286
|
+
end
|
|
1287
|
+
|
|
1288
|
+
H.is_visual_mode = function()
|
|
1289
|
+
local cur_mode = vim.fn.mode()
|
|
1290
|
+
return cur_mode == 'v' or cur_mode == 'V' or cur_mode == H.submode_keys.block
|
|
1291
|
+
end
|
|
1292
|
+
|
|
1293
|
+
H.with_temp_context = function(context, f)
|
|
1294
|
+
local res
|
|
1295
|
+
vim.api.nvim_buf_call(context.buf_id or 0, function()
|
|
1296
|
+
-- Cache temporary data
|
|
1297
|
+
local marks_data = {}
|
|
1298
|
+
for _, mark_name in ipairs(context.marks or {}) do
|
|
1299
|
+
marks_data[mark_name] = H.get_mark(mark_name)
|
|
1300
|
+
end
|
|
1301
|
+
|
|
1302
|
+
local reg_data = {}
|
|
1303
|
+
for _, reg_name in ipairs(context.registers or {}) do
|
|
1304
|
+
reg_data[reg_name] = vim.fn.getreginfo(reg_name)
|
|
1305
|
+
end
|
|
1306
|
+
|
|
1307
|
+
-- Perform action
|
|
1308
|
+
res = f()
|
|
1309
|
+
|
|
1310
|
+
-- Restore data
|
|
1311
|
+
for mark_name, data in pairs(marks_data) do
|
|
1312
|
+
pcall(H.set_mark, mark_name, data)
|
|
1313
|
+
end
|
|
1314
|
+
for reg_name, data in pairs(reg_data) do
|
|
1315
|
+
pcall(vim.fn.setreg, reg_name, data)
|
|
1316
|
+
end
|
|
1317
|
+
end)
|
|
1318
|
+
|
|
1319
|
+
return res
|
|
1320
|
+
end
|
|
1321
|
+
|
|
1322
|
+
-- A hack to restore previous dot-repeat action
|
|
1323
|
+
H.cancel_redo = function() end
|
|
1324
|
+
(function()
|
|
1325
|
+
local has_ffi, ffi = pcall(require, 'ffi')
|
|
1326
|
+
if not has_ffi then return end
|
|
1327
|
+
local has_cancel_redo = pcall(ffi.cdef, 'void CancelRedo(void)')
|
|
1328
|
+
if not has_cancel_redo then return end
|
|
1329
|
+
H.cancel_redo = function() pcall(ffi.C.CancelRedo) end
|
|
1330
|
+
end)()
|
|
1331
|
+
|
|
1332
|
+
H.cmd_normal = function(command, opts)
|
|
1333
|
+
opts = opts or {}
|
|
1334
|
+
local cancel_redo = opts.cancel_redo
|
|
1335
|
+
if cancel_redo == nil then cancel_redo = true end
|
|
1336
|
+
|
|
1337
|
+
vim.cmd('silent keepjumps normal! ' .. command)
|
|
1338
|
+
|
|
1339
|
+
if cancel_redo then H.cancel_redo() end
|
|
1340
|
+
end
|
|
1341
|
+
|
|
1342
|
+
-- TODO: Remove after compatibility with Neovim=0.9 is dropped
|
|
1343
|
+
H.islist = vim.fn.has('nvim-0.10') == 1 and vim.islist or vim.tbl_islist
|
|
1344
|
+
|
|
1345
|
+
-- TODO: Remove after compatibility with Neovim=0.10 is dropped
|
|
1346
|
+
H.highlight_range = function(...) vim.hl.range(...) end
|
|
1347
|
+
if vim.fn.has('nvim-0.11') == 0 then H.highlight_range = function(...) vim.highlight.range(...) end end
|
|
1348
|
+
|
|
1349
|
+
return MiniOperators
|