retrovim 0.0.3

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (218) hide show
  1. package/.conda/retrovim.yaml +259 -0
  2. package/.conda/terax.icns +0 -0
  3. package/.conda/terax.ico +0 -0
  4. package/.conda/terax.png +0 -0
  5. package/.github/workflows/release.yml +95 -0
  6. package/.gitmodules +3 -0
  7. package/.npm/kanata_touchcursor.js +28 -0
  8. package/.npm/nvim.js +26 -0
  9. package/.npm/yazi.js +26 -0
  10. package/.npm/zsh.js +17 -0
  11. package/.pip/kanata_touchcursor/__init__.py +28 -0
  12. package/.pip/nvim/__init__.py +31 -0
  13. package/.pip/yazi/__init__.py +31 -0
  14. package/.pip/zsh/__init__.py +22 -0
  15. package/CHANGELOG.md +26 -0
  16. package/LICENSE +674 -0
  17. package/README.md +336 -0
  18. package/kanata/touchcursor.kbd +164 -0
  19. package/nvim/init.lua +813 -0
  20. package/nvim/plugins/mini.nvim/.github/DISCUSSION_TEMPLATE/q-a.yml +72 -0
  21. package/nvim/plugins/mini.nvim/.github/ISSUE_TEMPLATE/bug-report.yml +136 -0
  22. package/nvim/plugins/mini.nvim/.github/ISSUE_TEMPLATE/config.yml +5 -0
  23. package/nvim/plugins/mini.nvim/.github/ISSUE_TEMPLATE/feature-request.yml +75 -0
  24. package/nvim/plugins/mini.nvim/.github/PULL_REQUEST_TEMPLATE.md +2 -0
  25. package/nvim/plugins/mini.nvim/.github/workflows/quality-control.yml +150 -0
  26. package/nvim/plugins/mini.nvim/.pre-commit-config.yaml +20 -0
  27. package/nvim/plugins/mini.nvim/.stylua.toml +7 -0
  28. package/nvim/plugins/mini.nvim/.styluaignore +1 -0
  29. package/nvim/plugins/mini.nvim/CHANGELOG.md +1894 -0
  30. package/nvim/plugins/mini.nvim/CODE_OF_CONDUCT.md +132 -0
  31. package/nvim/plugins/mini.nvim/CONTRIBUTING.md +331 -0
  32. package/nvim/plugins/mini.nvim/LICENSE +21 -0
  33. package/nvim/plugins/mini.nvim/MAINTAINING.md +330 -0
  34. package/nvim/plugins/mini.nvim/Makefile +51 -0
  35. package/nvim/plugins/mini.nvim/README.md +261 -0
  36. package/nvim/plugins/mini.nvim/TESTING.md +1011 -0
  37. package/nvim/plugins/mini.nvim/benchmarks/starter/README.md +35 -0
  38. package/nvim/plugins/mini.nvim/benchmarks/starter/benchmark.sh +56 -0
  39. package/nvim/plugins/mini.nvim/benchmarks/starter/init-files/init_dashboard-alpha.lua +21 -0
  40. package/nvim/plugins/mini.nvim/benchmarks/starter/init-files/init_dashboard-original.lua +17 -0
  41. package/nvim/plugins/mini.nvim/benchmarks/starter/init-files/init_dashboard-starter.lua +18 -0
  42. package/nvim/plugins/mini.nvim/benchmarks/starter/init-files/init_empty.lua +4 -0
  43. package/nvim/plugins/mini.nvim/benchmarks/starter/init-files/init_starter-default.lua +7 -0
  44. package/nvim/plugins/mini.nvim/benchmarks/starter/init-files/init_startify-alpha.lua +10 -0
  45. package/nvim/plugins/mini.nvim/benchmarks/starter/init-files/init_startify-original.lua +7 -0
  46. package/nvim/plugins/mini.nvim/benchmarks/starter/init-files/init_startify-starter.lua +20 -0
  47. package/nvim/plugins/mini.nvim/benchmarks/starter/install.sh +10 -0
  48. package/nvim/plugins/mini.nvim/benchmarks/starter/make_summary.py +68 -0
  49. package/nvim/plugins/mini.nvim/benchmarks/starter/startup-summary.md +10 -0
  50. package/nvim/plugins/mini.nvim/colors/miniautumn.lua +55 -0
  51. package/nvim/plugins/mini.nvim/colors/minicyan.lua +95 -0
  52. package/nvim/plugins/mini.nvim/colors/minischeme.lua +95 -0
  53. package/nvim/plugins/mini.nvim/colors/minispring.lua +53 -0
  54. package/nvim/plugins/mini.nvim/colors/minisummer.lua +49 -0
  55. package/nvim/plugins/mini.nvim/colors/miniwinter.lua +51 -0
  56. package/nvim/plugins/mini.nvim/colors/randomhue.lua +16 -0
  57. package/nvim/plugins/mini.nvim/doc/mini-ai.txt +889 -0
  58. package/nvim/plugins/mini.nvim/doc/mini-align.txt +923 -0
  59. package/nvim/plugins/mini.nvim/doc/mini-animate.txt +937 -0
  60. package/nvim/plugins/mini.nvim/doc/mini-base16.txt +285 -0
  61. package/nvim/plugins/mini.nvim/doc/mini-basics.txt +359 -0
  62. package/nvim/plugins/mini.nvim/doc/mini-bracketed.txt +622 -0
  63. package/nvim/plugins/mini.nvim/doc/mini-bufremove.txt +123 -0
  64. package/nvim/plugins/mini.nvim/doc/mini-clue.txt +760 -0
  65. package/nvim/plugins/mini.nvim/doc/mini-cmdline.txt +392 -0
  66. package/nvim/plugins/mini.nvim/doc/mini-colors.txt +880 -0
  67. package/nvim/plugins/mini.nvim/doc/mini-comment.txt +230 -0
  68. package/nvim/plugins/mini.nvim/doc/mini-completion.txt +477 -0
  69. package/nvim/plugins/mini.nvim/doc/mini-cursorword.txt +105 -0
  70. package/nvim/plugins/mini.nvim/doc/mini-deps.txt +590 -0
  71. package/nvim/plugins/mini.nvim/doc/mini-diff.txt +661 -0
  72. package/nvim/plugins/mini.nvim/doc/mini-doc.txt +434 -0
  73. package/nvim/plugins/mini.nvim/doc/mini-extra.txt +856 -0
  74. package/nvim/plugins/mini.nvim/doc/mini-files.txt +1018 -0
  75. package/nvim/plugins/mini.nvim/doc/mini-fuzzy.txt +160 -0
  76. package/nvim/plugins/mini.nvim/doc/mini-git.txt +425 -0
  77. package/nvim/plugins/mini.nvim/doc/mini-hipatterns.txt +489 -0
  78. package/nvim/plugins/mini.nvim/doc/mini-hues.txt +420 -0
  79. package/nvim/plugins/mini.nvim/doc/mini-icons.txt +508 -0
  80. package/nvim/plugins/mini.nvim/doc/mini-indentscope.txt +485 -0
  81. package/nvim/plugins/mini.nvim/doc/mini-input.txt +932 -0
  82. package/nvim/plugins/mini.nvim/doc/mini-jump.txt +202 -0
  83. package/nvim/plugins/mini.nvim/doc/mini-jump2d.txt +525 -0
  84. package/nvim/plugins/mini.nvim/doc/mini-keymap.txt +449 -0
  85. package/nvim/plugins/mini.nvim/doc/mini-map.txt +717 -0
  86. package/nvim/plugins/mini.nvim/doc/mini-misc.txt +431 -0
  87. package/nvim/plugins/mini.nvim/doc/mini-move.txt +181 -0
  88. package/nvim/plugins/mini.nvim/doc/mini-notify.txt +434 -0
  89. package/nvim/plugins/mini.nvim/doc/mini-nvim.txt +431 -0
  90. package/nvim/plugins/mini.nvim/doc/mini-operators.txt +472 -0
  91. package/nvim/plugins/mini.nvim/doc/mini-pairs.txt +346 -0
  92. package/nvim/plugins/mini.nvim/doc/mini-pick.txt +1535 -0
  93. package/nvim/plugins/mini.nvim/doc/mini-sessions.txt +239 -0
  94. package/nvim/plugins/mini.nvim/doc/mini-snippets.txt +1291 -0
  95. package/nvim/plugins/mini.nvim/doc/mini-splitjoin.txt +536 -0
  96. package/nvim/plugins/mini.nvim/doc/mini-starter.txt +618 -0
  97. package/nvim/plugins/mini.nvim/doc/mini-statusline.txt +361 -0
  98. package/nvim/plugins/mini.nvim/doc/mini-surround.txt +863 -0
  99. package/nvim/plugins/mini.nvim/doc/mini-tabline.txt +151 -0
  100. package/nvim/plugins/mini.nvim/doc/mini-test.txt +966 -0
  101. package/nvim/plugins/mini.nvim/doc/mini-trailspace.txt +91 -0
  102. package/nvim/plugins/mini.nvim/doc/mini-visits.txt +937 -0
  103. package/nvim/plugins/mini.nvim/logo.png +0 -0
  104. package/nvim/plugins/mini.nvim/lua/mini/ai.lua +2244 -0
  105. package/nvim/plugins/mini.nvim/lua/mini/align.lua +2076 -0
  106. package/nvim/plugins/mini.nvim/lua/mini/animate.lua +2127 -0
  107. package/nvim/plugins/mini.nvim/lua/mini/base16.lua +1752 -0
  108. package/nvim/plugins/mini.nvim/lua/mini/basics.lua +772 -0
  109. package/nvim/plugins/mini.nvim/lua/mini/bracketed.lua +2013 -0
  110. package/nvim/plugins/mini.nvim/lua/mini/bufremove.lua +283 -0
  111. package/nvim/plugins/mini.nvim/lua/mini/clue.lua +2096 -0
  112. package/nvim/plugins/mini.nvim/lua/mini/cmdline.lua +1182 -0
  113. package/nvim/plugins/mini.nvim/lua/mini/colors.lua +2483 -0
  114. package/nvim/plugins/mini.nvim/lua/mini/comment.lua +586 -0
  115. package/nvim/plugins/mini.nvim/lua/mini/completion.lua +2094 -0
  116. package/nvim/plugins/mini.nvim/lua/mini/cursorword.lua +311 -0
  117. package/nvim/plugins/mini.nvim/lua/mini/deps.lua +1660 -0
  118. package/nvim/plugins/mini.nvim/lua/mini/diff.lua +1938 -0
  119. package/nvim/plugins/mini.nvim/lua/mini/doc.lua +1371 -0
  120. package/nvim/plugins/mini.nvim/lua/mini/extra.lua +2290 -0
  121. package/nvim/plugins/mini.nvim/lua/mini/files.lua +3145 -0
  122. package/nvim/plugins/mini.nvim/lua/mini/fuzzy.lua +356 -0
  123. package/nvim/plugins/mini.nvim/lua/mini/git.lua +1737 -0
  124. package/nvim/plugins/mini.nvim/lua/mini/hipatterns.lua +1042 -0
  125. package/nvim/plugins/mini.nvim/lua/mini/hues.lua +2088 -0
  126. package/nvim/plugins/mini.nvim/lua/mini/icons.lua +2254 -0
  127. package/nvim/plugins/mini.nvim/lua/mini/indentscope.lua +1158 -0
  128. package/nvim/plugins/mini.nvim/lua/mini/init.lua +427 -0
  129. package/nvim/plugins/mini.nvim/lua/mini/input.lua +2183 -0
  130. package/nvim/plugins/mini.nvim/lua/mini/jump.lua +609 -0
  131. package/nvim/plugins/mini.nvim/lua/mini/jump2d.lua +1244 -0
  132. package/nvim/plugins/mini.nvim/lua/mini/keymap.lua +880 -0
  133. package/nvim/plugins/mini.nvim/lua/mini/map.lua +1709 -0
  134. package/nvim/plugins/mini.nvim/lua/mini/misc.lua +969 -0
  135. package/nvim/plugins/mini.nvim/lua/mini/move.lua +509 -0
  136. package/nvim/plugins/mini.nvim/lua/mini/notify.lua +938 -0
  137. package/nvim/plugins/mini.nvim/lua/mini/operators.lua +1349 -0
  138. package/nvim/plugins/mini.nvim/lua/mini/pairs.lua +657 -0
  139. package/nvim/plugins/mini.nvim/lua/mini/pick.lua +3876 -0
  140. package/nvim/plugins/mini.nvim/lua/mini/sessions.lua +689 -0
  141. package/nvim/plugins/mini.nvim/lua/mini/snippets.lua +2828 -0
  142. package/nvim/plugins/mini.nvim/lua/mini/splitjoin.lua +1132 -0
  143. package/nvim/plugins/mini.nvim/lua/mini/starter.lua +1599 -0
  144. package/nvim/plugins/mini.nvim/lua/mini/statusline.lua +686 -0
  145. package/nvim/plugins/mini.nvim/lua/mini/surround.lua +2388 -0
  146. package/nvim/plugins/mini.nvim/lua/mini/tabline.lua +564 -0
  147. package/nvim/plugins/mini.nvim/lua/mini/test.lua +2516 -0
  148. package/nvim/plugins/mini.nvim/lua/mini/trailspace.lua +215 -0
  149. package/nvim/plugins/mini.nvim/lua/mini/visits.lua +1577 -0
  150. package/nvim/plugins/mini.nvim/readmes/mini-ai.md +190 -0
  151. package/nvim/plugins/mini.nvim/readmes/mini-align.md +203 -0
  152. package/nvim/plugins/mini.nvim/readmes/mini-animate.md +214 -0
  153. package/nvim/plugins/mini.nvim/readmes/mini-base16.md +198 -0
  154. package/nvim/plugins/mini.nvim/readmes/mini-basics.md +184 -0
  155. package/nvim/plugins/mini.nvim/readmes/mini-bracketed.md +193 -0
  156. package/nvim/plugins/mini.nvim/readmes/mini-bufremove.md +139 -0
  157. package/nvim/plugins/mini.nvim/readmes/mini-clue.md +260 -0
  158. package/nvim/plugins/mini.nvim/readmes/mini-cmdline.md +183 -0
  159. package/nvim/plugins/mini.nvim/readmes/mini-colors.md +169 -0
  160. package/nvim/plugins/mini.nvim/readmes/mini-comment.md +183 -0
  161. package/nvim/plugins/mini.nvim/readmes/mini-completion.md +217 -0
  162. package/nvim/plugins/mini.nvim/readmes/mini-cursorword.md +137 -0
  163. package/nvim/plugins/mini.nvim/readmes/mini-deps.md +271 -0
  164. package/nvim/plugins/mini.nvim/readmes/mini-diff.md +289 -0
  165. package/nvim/plugins/mini.nvim/readmes/mini-doc.md +198 -0
  166. package/nvim/plugins/mini.nvim/readmes/mini-extra.md +158 -0
  167. package/nvim/plugins/mini.nvim/readmes/mini-files.md +247 -0
  168. package/nvim/plugins/mini.nvim/readmes/mini-fuzzy.md +134 -0
  169. package/nvim/plugins/mini.nvim/readmes/mini-git.md +168 -0
  170. package/nvim/plugins/mini.nvim/readmes/mini-hipatterns.md +176 -0
  171. package/nvim/plugins/mini.nvim/readmes/mini-hues.md +285 -0
  172. package/nvim/plugins/mini.nvim/readmes/mini-icons.md +183 -0
  173. package/nvim/plugins/mini.nvim/readmes/mini-indentscope.md +191 -0
  174. package/nvim/plugins/mini.nvim/readmes/mini-input.md +163 -0
  175. package/nvim/plugins/mini.nvim/readmes/mini-jump.md +161 -0
  176. package/nvim/plugins/mini.nvim/readmes/mini-jump2d.md +197 -0
  177. package/nvim/plugins/mini.nvim/readmes/mini-keymap.md +205 -0
  178. package/nvim/plugins/mini.nvim/readmes/mini-map.md +191 -0
  179. package/nvim/plugins/mini.nvim/readmes/mini-misc.md +145 -0
  180. package/nvim/plugins/mini.nvim/readmes/mini-move.md +163 -0
  181. package/nvim/plugins/mini.nvim/readmes/mini-notify.md +175 -0
  182. package/nvim/plugins/mini.nvim/readmes/mini-operators.md +193 -0
  183. package/nvim/plugins/mini.nvim/readmes/mini-pairs.md +158 -0
  184. package/nvim/plugins/mini.nvim/readmes/mini-pick.md +340 -0
  185. package/nvim/plugins/mini.nvim/readmes/mini-sessions.md +164 -0
  186. package/nvim/plugins/mini.nvim/readmes/mini-snippets.md +373 -0
  187. package/nvim/plugins/mini.nvim/readmes/mini-splitjoin.md +194 -0
  188. package/nvim/plugins/mini.nvim/readmes/mini-starter.md +176 -0
  189. package/nvim/plugins/mini.nvim/readmes/mini-statusline.md +155 -0
  190. package/nvim/plugins/mini.nvim/readmes/mini-surround.md +190 -0
  191. package/nvim/plugins/mini.nvim/readmes/mini-tabline.md +156 -0
  192. package/nvim/plugins/mini.nvim/readmes/mini-test.md +175 -0
  193. package/nvim/plugins/mini.nvim/readmes/mini-trailspace.md +137 -0
  194. package/nvim/plugins/mini.nvim/readmes/mini-visits.md +243 -0
  195. package/nvim/plugins/mini.nvim/scripts/dual_log.sh +13 -0
  196. package/nvim/plugins/mini.nvim/scripts/dual_push.sh +15 -0
  197. package/nvim/plugins/mini.nvim/scripts/dual_release.sh +102 -0
  198. package/nvim/plugins/mini.nvim/scripts/dual_sync.sh +115 -0
  199. package/nvim/plugins/mini.nvim/scripts/init-deps-example.lua +59 -0
  200. package/nvim/plugins/mini.nvim/scripts/lint-filename-length.sh +16 -0
  201. package/nvim/plugins/mini.nvim/scripts/lintcommit-ci.sh +18 -0
  202. package/nvim/plugins/mini.nvim/scripts/lintcommit.lua +393 -0
  203. package/nvim/plugins/mini.nvim/scripts/minidoc.lua +57 -0
  204. package/nvim/plugins/mini.nvim/scripts/minimal_init.lua +28 -0
  205. package/nvim/plugins/mini.nvim/scripts/minitest.lua +4 -0
  206. package/package.json +16 -0
  207. package/pyproject.toml +43 -0
  208. package/yazi/init.lua +12 -0
  209. package/yazi/keymap.toml +114 -0
  210. package/yazi/lesskey +5 -0
  211. package/yazi/plugins/bookmarks.yazi/main.lua +86 -0
  212. package/yazi/plugins/cd-git-root.yazi/main.lua +31 -0
  213. package/yazi/plugins/smart-enter.yazi/main.lua +11 -0
  214. package/yazi/theme.toml +26 -0
  215. package/yazi/yazi.toml +15 -0
  216. package/zsh/.zshrc +145 -0
  217. package/zsh/starship.toml +20 -0
  218. package/zsh/zsh-patina.toml +2 -0
@@ -0,0 +1,1660 @@
1
+ --- *mini.deps* Plugin manager
2
+ ---
3
+ --- MIT License Copyright (c) 2024 Evgeni Chasnovski
4
+
5
+ --- *MiniDeps-dev-freeze*
6
+ --- As Neovim=0.12 introduced a built-in plugin manager |vim.pack| (which used this
7
+ --- module as a design reference), development of |mini.deps| is frozen. It means:
8
+ ---
9
+ --- - On Neovim>=0.12 it is recommended to use |vim.pack| for plugin management
10
+ --- and |MiniMisc.safely()| as |MiniDeps.now()| and |MiniDeps.later()| alternative.
11
+ --- See https://echasnovski.com/vim-pack-guide for more details, including
12
+ --- recommendations about migration from |mini.deps|.
13
+ ---
14
+ --- - This module will remain a part of |mini.nvim| at least until Neovim=0.11 is
15
+ --- supported. It might receive critical fixes, but likely no new features.
16
+ ---
17
+ --- Features:
18
+ ---
19
+ --- - Manage plugins utilizing Git and built-in |packages| with these actions:
20
+ --- - Add plugin to current session, download if absent. See |MiniDeps.add()|.
21
+ --- - Update with/without confirm, with/without parallel download of new data.
22
+ --- See |MiniDeps.update()|.
23
+ --- - Delete unused plugins with/without confirm. See |MiniDeps.clean()|.
24
+ --- - Get / set / save / load snapshot. See `MiniDeps.snap_*()` functions.
25
+ ---
26
+ --- All main actions are available both as Lua functions and user commands
27
+ --- (see |MiniDeps-commands|).
28
+ ---
29
+ --- - Minimal yet flexible plugin |MiniDeps-plugin-specification|:
30
+ --- - Plugin source.
31
+ --- - Name of target plugin directory.
32
+ --- - Checkout target: branch, commit, tag, etc.
33
+ --- - Monitor branch to track updates without checking out.
34
+ --- - Dependencies to be set up prior to the target plugin.
35
+ --- - Hooks to call before/after plugin is created/changed.
36
+ ---
37
+ --- - Helpers implementing two-stage startup: |MiniDeps.now()| and |MiniDeps.later()|.
38
+ --- See |MiniDeps-overview| for how to implement basic lazy loading with them.
39
+ ---
40
+ --- What it doesn't do:
41
+ ---
42
+ --- - Manage plugins which are developed without Git. The suggested approach is
43
+ --- to create a separate package (see |packages|).
44
+ ---
45
+ --- - Provide ways to completely remove or update plugin's functionality in
46
+ --- current session. Although this is partially doable, it can not be done
47
+ --- in full (yet) because plugins can have untraceable side effects
48
+ --- (autocmmands, mappings, etc.).
49
+ --- The suggested approach is to restart Nvim.
50
+ ---
51
+ --- Sources with more details:
52
+ --- - |MiniDeps-overview|
53
+ --- - |MiniDeps-plugin-specification|
54
+ --- - |MiniDeps-commands|
55
+ ---
56
+ --- # Dependencies ~
57
+ ---
58
+ --- For most of its functionality this plugin relies on `git` CLI tool.
59
+ --- See https://git-scm.com/ for more information about how to install it.
60
+ --- Actual knowledge of Git is not required but helpful.
61
+ ---
62
+ --- # Setup ~
63
+ ---
64
+ --- This module needs a setup with `require('mini.deps').setup({})` (replace
65
+ --- `{}` with your `config` table). It will create global Lua table `MiniDeps`
66
+ --- which you can use for scripting or manually (with `:lua MiniDeps.*`).
67
+ ---
68
+ --- See |MiniDeps.config| for `config` structure and default values.
69
+ ---
70
+ --- You can override runtime config settings locally to buffer inside
71
+ --- `vim.b.minideps_config` which should have same structure as
72
+ --- `MiniDeps.config`. See |mini.nvim-buffer-local-config| for more details.
73
+ ---
74
+ --- # Comparisons ~
75
+ ---
76
+ --- - [folke/lazy.nvim](https://github.com/folke/lazy.nvim):
77
+ --- - More feature-rich and complex.
78
+ --- - Uses table specification with dedicated functions to add plugins,
79
+ --- while this module uses direct function call approach
80
+ --- (calling |MiniDeps.add()| ensures that plugin is usable).
81
+ --- - Uses version tags by default, while this module is more designed towards
82
+ --- tracking branches. Using tags is possible too (see |MiniDeps-overview|).
83
+ ---
84
+ --- - [savq/paq-nvim](https://github.com/savq/paq-nvim):
85
+ --- - Overall less feature-rich than this module (by design).
86
+ --- - Uses array of plugin specifications inside `setup()` call to define which
87
+ --- plugins should be installed. Requires separate `:PaqInstall` call to
88
+ --- actually install them. This module ensures installation on first load.
89
+ ---
90
+ --- - [junegunn/vim-plug](https://github.com/junegunn/vim-plug):
91
+ --- - Written in Vimscript, while this module is in Lua.
92
+ --- - Similar approach to defining and installing plugins as `savq/paq-nvim`.
93
+ --- - Has basic lazy-loading built-in, while this module does not (by design).
94
+ ---
95
+ --- # Highlight groups ~
96
+ --- *MiniDeps-hl-groups*
97
+ ---
98
+ --- Highlight groups are used inside confirmation buffers after
99
+ --- default |MiniDeps.update()| and |MiniDeps.clean()|.
100
+ ---
101
+ --- - `MiniDepsChangeAdded` - added change (commit) during update.
102
+ --- - `MiniDepsChangeRemoved` - removed change (commit) during update.
103
+ --- - `MiniDepsHint` - various hints.
104
+ --- - `MiniDepsInfo` - various information.
105
+ --- - `MiniDepsMsgBreaking` - message for (conventional commit) breaking change.
106
+ --- - `MiniDepsPlaceholder` - placeholder when there is no valuable information.
107
+ --- - `MiniDepsTitle` - various titles.
108
+ --- - `MiniDepsTitleError` - title when plugin had errors during update.
109
+ --- - `MiniDepsTitleSame` - title when plugin has no changes to update.
110
+ --- - `MiniDepsTitleUpdate` - title when plugin has changes to update.
111
+ ---
112
+ --- To change any highlight group, set it directly with |nvim_set_hl()|.
113
+ ---@tag MiniDeps
114
+
115
+ --- # Directory structure ~
116
+ ---
117
+ --- This module uses built-in |packages| to make plugins usable in current session.
118
+ --- It works with "pack/deps" package inside `config.path.package` directory.
119
+ ---
120
+ --- By default "opt" subdirectory is used to install optional plugins which are
121
+ --- loaded on demand with |MiniDeps.add()|.
122
+ --- Non-optional plugins in "start" subdirectory are supported but only if moved
123
+ --- there manually after initial install. Use it if you know what you are doing.
124
+ ---
125
+ --- # Add plugin ~
126
+ ---
127
+ --- Use |MiniDeps.add()| to add plugin to current session. Supply plugin's URL
128
+ --- source as a string or |MiniDeps-plugin-specification| in general. If plugin is
129
+ --- not present in "pack/deps" package, it will be created (a.k.a. installed)
130
+ --- before processing anything else.
131
+ ---
132
+ --- The recommended way of adding a plugin is by calling |MiniDeps.add()| in the
133
+ --- |init.lua| file (make sure |MiniDeps.setup()| is called prior): >lua
134
+ ---
135
+ --- local add = MiniDeps.add
136
+ ---
137
+ --- -- Add to current session (install if absent)
138
+ --- add({
139
+ --- source = 'neovim/nvim-lspconfig',
140
+ --- -- Supply dependencies near target plugin
141
+ --- depends = { 'williamboman/mason.nvim' },
142
+ --- })
143
+ ---
144
+ --- add({
145
+ --- source = 'nvim-treesitter/nvim-treesitter',
146
+ --- -- Use 'master' while monitoring updates in 'main'
147
+ --- checkout = 'master',
148
+ --- monitor = 'main',
149
+ --- -- Perform action after every checkout
150
+ --- hooks = { post_checkout = function() vim.cmd('TSUpdate') end },
151
+ --- })
152
+ --- -- Possible to immediately execute code which depends on the added plugin
153
+ --- require('nvim-treesitter.configs').setup({
154
+ --- ensure_installed = { 'lua', 'vimdoc' },
155
+ --- highlight = { enable = true },
156
+ --- })
157
+ --- <
158
+ --- NOTE:
159
+ --- - To increase performance, `add()` only ensures presence on disk and
160
+ --- nothing else. In particular, it doesn't ensure `opts.checkout` state.
161
+ --- Update or modify plugin state explicitly (see later sections).
162
+ ---
163
+ --- # Lazy loading ~
164
+ ---
165
+ --- Any lazy-loading is assumed to be done manually by calling |MiniDeps.add()|
166
+ --- at appropriate time. This module provides helpers implementing special safe
167
+ --- two-stage loading:
168
+ --- - |MiniDeps.now()| safely executes code immediately. Use it to load plugins
169
+ --- with UI necessary to make initial screen draw.
170
+ --- - |MiniDeps.later()| schedules code to be safely executed later, preserving
171
+ --- order. Use it (with caution) for everything else which doesn't need
172
+ --- precisely timed effect, as it will be executed some time soon on one of
173
+ --- the next event loops. >lua
174
+ ---
175
+ --- local now, later = MiniDeps.now, MiniDeps.later
176
+ ---
177
+ --- -- Safely execute immediately
178
+ --- now(function() vim.cmd('colorscheme miniwinter') end)
179
+ --- now(function() require('mini.statusline').setup() end)
180
+ ---
181
+ --- -- Safely execute later
182
+ --- later(function() require('mini.pick').setup() end)
183
+ --- <
184
+ --- # Update ~
185
+ ---
186
+ --- To update plugins from current session with new data from their sources,
187
+ --- use |:DepsUpdate|. This will download updates (utilizing multiple cores) and
188
+ --- show confirmation buffer. Follow instructions at its top to finish an update.
189
+ ---
190
+ --- NOTE: This updates plugins on disk which most likely won't affect current
191
+ --- session. Restart Nvim to have them properly loaded.
192
+ ---
193
+ --- # Modify ~
194
+ ---
195
+ --- To change plugin's specification (like set different `checkout`, etc.):
196
+ --- - Update corresponding |MiniDeps.add()| call.
197
+ --- - Run `:DepsUpdateOffline <plugin_name>`.
198
+ --- - Review changes and confirm.
199
+ --- - Restart Nvim.
200
+ ---
201
+ --- NOTE: if `add()` prior used a single source string, make sure to convert
202
+ --- its argument to `{ source = '<previous_argument>', checkout = '<state>'}`
203
+ ---
204
+ --- # Snapshots ~
205
+ ---
206
+ --- Use |:DepsSnapSave| to save state of all plugins from current session into
207
+ --- a snapshot file (see `config.path.snapshot`).
208
+ ---
209
+ --- Use |:DepsSnapLoad| to load snapshot. This will change (without confirmation)
210
+ --- state on disk. Plugins present in both snapshot file and current session
211
+ --- will be affected. Restart Nvim to see the effect.
212
+ ---
213
+ --- NOTE: loading snapshot does not change plugin's specification defined inside
214
+ --- |MiniDeps.add()| call. This means that next update might change plugin's state.
215
+ --- To make it permanent, freeze plugin in target state manually.
216
+ ---
217
+ --- # Freeze ~
218
+ ---
219
+ --- Modify plugin's specification to have `checkout` pointing to a static
220
+ --- target: tag, state (commit hash), or `'HEAD'` (to freeze in current state).
221
+ ---
222
+ --- Frozen plugins will not receive updates. You can monitor any new changes from
223
+ --- its source by "subscribing" to `monitor` branch which will be shown inside
224
+ --- confirmation buffer after |:DepsUpdate|.
225
+ ---
226
+ --- Example: use `checkout = 'v0.10.0'` to freeze plugin at tag "v0.10.0" while
227
+ --- monitoring new versions in the log from `monitor` (usually default) branch.
228
+ ---
229
+ --- # Rollback ~
230
+ ---
231
+ --- To roll back after an unfortunate update:
232
+ --- - Get identifier of latest working state:
233
+ --- - Use |:DepsShowLog| to see update log, look for plugin's name, and copy
234
+ --- identifier listed as "State before:".
235
+ --- - See previously saved snapshot file for plugin's name and copy
236
+ --- identifier next to it.
237
+ --- - Freeze plugin at that state while monitoring appropriate branch.
238
+ --- Revert to previous shape of |MiniDeps.add()| call to resume updating.
239
+ ---
240
+ --- # Remove ~
241
+ ---
242
+ --- - Make sure that target plugin is not registered in current session.
243
+ --- Usually it means removing corresponding |MiniDeps.add()| call.
244
+ --- - Run |:DepsClean|. This will show confirmation buffer with a list of plugins to
245
+ --- be deleted from disk. Follow instructions at its top to finish cleaning.
246
+ ---
247
+ --- Alternatively, manually delete plugin's directory from "pack/deps" package.
248
+ ---@tag MiniDeps-overview
249
+
250
+ --- Each plugin dependency is managed based on its specification (a.k.a. "spec").
251
+ --- See |MiniDeps-overview| for some examples.
252
+ ---
253
+ --- Specification can be a single string which is inferred as:
254
+ --- - Plugin <name> if it doesn't contain "/".
255
+ --- - Plugin <source> otherwise.
256
+ ---
257
+ --- Primarily, specification is a table with the following fields:
258
+ ---
259
+ --- - <source> `(string|nil)` - field with URI of plugin source used during creation
260
+ --- or update. Can be anything allowed by `git clone`.
261
+ --- Default: `nil` to rely on source set up during install.
262
+ --- Notes:
263
+ --- - It is required for creating plugin, but can be omitted afterwards.
264
+ --- - As the most common case, URI of the format "user/repo" (if it contains
265
+ --- valid characters) is transformed into "https://github.com/user/repo".
266
+ ---
267
+ --- - <name> `(string|nil)` - directory basename of where to put plugin source.
268
+ --- It is put in "pack/deps/opt" subdirectory of `config.path.package`.
269
+ --- Default: basename of <source> if it is present, otherwise should be
270
+ --- provided explicitly.
271
+ ---
272
+ --- - <checkout> `(string|nil)` - checkout target used to set state during update.
273
+ --- Can be anything supported by `git checkout` - branch, commit, tag, etc.
274
+ --- Default: `nil` for default branch (usually "main" or "master").
275
+ ---
276
+ --- - <monitor> `(string|nil)` - monitor branch used to track new changes from
277
+ --- different target than `checkout`. Should be a name of present Git branch.
278
+ --- Default: `nil` for default branch (usually "main" or "master").
279
+ ---
280
+ --- - <depends> `(table|nil)` - array of plugin specifications (strings or tables)
281
+ --- to be added prior to the target.
282
+ --- Default: `nil` for no dependencies.
283
+ ---
284
+ --- - <hooks> `(table|nil)` - table with callable hooks to call on certain events.
285
+ --- Possible hook names:
286
+ --- - <pre_install> - before creating plugin directory.
287
+ --- - <post_install> - after creating plugin directory (before |:packadd|).
288
+ --- - <pre_checkout> - before making change in existing plugin.
289
+ --- - <post_checkout> - after making change in existing plugin.
290
+ --- Each hook is executed with the following table as an argument:
291
+ --- - <path> (`string`) - absolute path to plugin's directory
292
+ --- (might not yet exist on disk).
293
+ --- - <source> (`string`) - resolved <source> from spec.
294
+ --- - <name> (`string`) - resolved <name> from spec.
295
+ --- Default: `nil` for no hooks.
296
+ ---@tag MiniDeps-plugin-specification
297
+
298
+ --- Note: Most commands have a Lua function alternative which they rely on.
299
+ --- Like |:DepsAdd| uses |MiniDeps.add()|, etc.
300
+ ---
301
+ --- # :DepsAdd ~
302
+ ---
303
+ --- *:DepsAdd* with `user/repo` argument makes plugin https://github.com/user/repo
304
+ --- available in the current session (also creates it, if it is not present).
305
+ --- `:DepsAdd name` adds already installed plugin `name` to current session.
306
+ --- Accepts only single string compatible with |MiniDeps-plugin-specification|.
307
+ --- To add plugin in every session, put |MiniDeps.add()| in |init.lua|.
308
+ ---
309
+ --- # :DepsUpdate ~
310
+ ---
311
+ --- *:DepsUpdate* synchronizes plugins with their session specifications and
312
+ --- updates them with new changes from sources. It shows confirmation buffer in
313
+ --- a separate |tabpage| with information about an upcoming update to review
314
+ --- and (selectively) apply. See |MiniDeps.update()| for more info.
315
+ ---
316
+ --- `:DepsUpdate name` updates plugin `name`. Any number of names is allowed.
317
+ ---
318
+ --- `:DepsUpdate!` and `:DepsUpdate! name` update without confirmation.
319
+ --- You can see what was done in the log file afterwards (|:DepsShowLog|).
320
+ ---
321
+ --- # :DepsUpdateOffline ~
322
+ ---
323
+ --- *:DepsUpdateOffline* is same as |:DepsUpdate| but doesn't download new updates
324
+ --- from sources. Useful to only synchronize plugin specification in code and
325
+ --- on disk without unnecessary downloads.
326
+ ---
327
+ --- # :DepsShowLog ~
328
+ ---
329
+ --- *:DepsShowLog* opens log file to review.
330
+ ---
331
+ --- # :DepsClean ~
332
+ ---
333
+ --- *:DepsClean* deletes plugins from disk not added to current session. It shows
334
+ --- confirmation buffer in a separate |tabpage| with information about an upcoming
335
+ --- deletes to review and (selectively) apply. See |MiniDeps.clean()| for more info.
336
+ ---
337
+ --- `:DepsClean!` deletes plugins without confirmation.
338
+ ---
339
+ --- # :DepsSnapSave ~
340
+ ---
341
+ --- *:DepsSnapSave* creates snapshot file in default location (see |MiniDeps.config|).
342
+ --- `:DepsSnapSave path` creates snapshot file at `path`.
343
+ ---
344
+ --- # :DepsSnapLoad ~
345
+ ---
346
+ --- *:DepsSnapLoad* loads snapshot file from default location (see |MiniDeps.config|).
347
+ --- `:DepsSnapLoad path` loads snapshot file at `path`.
348
+ ---@tag MiniDeps-commands
349
+
350
+ ---@diagnostic disable:undefined-field
351
+ ---@diagnostic disable:discard-returns
352
+ ---@diagnostic disable:unused-local
353
+ ---@diagnostic disable:cast-local-type
354
+ ---@diagnostic disable:undefined-doc-name
355
+ ---@diagnostic disable:luadoc-miss-type-name
356
+
357
+ -- Module definition ==========================================================
358
+ local MiniDeps = {}
359
+ local H = {}
360
+
361
+ --- Module setup
362
+ ---
363
+ --- Calling this function creates user commands described in |MiniDeps-commands|.
364
+ ---
365
+ ---@param config table|nil Module config table. See |MiniDeps.config|.
366
+ ---
367
+ ---@usage >lua
368
+ --- require('mini.deps').setup() -- use default config
369
+ --- -- OR
370
+ --- require('mini.deps').setup({}) -- replace {} with your config table
371
+ --- <
372
+ MiniDeps.setup = function(config)
373
+ -- TODO: Remove after Neovim=0.9 support is dropped
374
+ if vim.fn.has('nvim-0.10') == 0 then
375
+ vim.notify(
376
+ '(mini.deps) Neovim<0.10 is soft deprecated (module works but is not supported).'
377
+ .. " It will be deprecated after the next 'mini.nvim' release (module might not work)."
378
+ .. ' Please update your Neovim version.'
379
+ )
380
+ end
381
+
382
+ -- Export module
383
+ _G.MiniDeps = MiniDeps
384
+
385
+ -- Setup config
386
+ config = H.setup_config(config)
387
+
388
+ -- Apply config
389
+ H.apply_config(config)
390
+
391
+ -- Define behavior
392
+ H.create_autocommands()
393
+
394
+ -- Create default highlighting
395
+ H.create_default_hl()
396
+
397
+ -- Create user commands
398
+ H.create_user_commands()
399
+ end
400
+
401
+ --stylua: ignore
402
+ --- Defaults ~
403
+ ---@eval return MiniDoc.afterlines_to_code(MiniDoc.current.eval_section)
404
+ ---@text # Job ~
405
+ ---
406
+ --- `config.job` defines how CLI jobs are run.
407
+ ---
408
+ --- `job.n_threads` is a maximum number of parallel jobs used when needed.
409
+ --- Default: 80% of all available.
410
+ ---
411
+ --- `job.timeout` is a duration (in ms) from job start until it is forced to stop.
412
+ --- Default: 30000.
413
+ ---
414
+ --- # Paths ~
415
+ ---
416
+ --- `config.path` defines main paths used in this module.
417
+ ---
418
+ --- `path.package` is a string with path inside which "pack/deps" package is stored
419
+ --- (see |MiniDeps-overview|).
420
+ --- Default: "site" subdirectory of "data" standard path (see |stdpath()|).
421
+ ---
422
+ --- `path.snapshot` is a string with default path for snapshot.
423
+ --- See |:DepsSnapSave| and |:DepsSnapLoad|.
424
+ --- Default: "mini-deps-snap" file in "config" standard path (see |stdpath()|).
425
+ ---
426
+ --- `path.log` is a string with path containing log of operations done by module.
427
+ --- In particular, it contains all changes done after making an update.
428
+ --- Default: "mini-deps.log" file in "log" standard path (see |stdpath()|).
429
+ ---
430
+ --- # Silent ~
431
+ ---
432
+ --- `config.silent` is a boolean controlling whether to suppress non-error feedback.
433
+ --- Default: `false`.
434
+ MiniDeps.config = {
435
+ -- Parameters of CLI jobs
436
+ job = {
437
+ -- Number of parallel threads to use. Default: 80% of all available.
438
+ n_threads = nil,
439
+
440
+ -- Timeout (in ms) for each job before force quit
441
+ timeout = 30000,
442
+ },
443
+
444
+ -- Paths describing where to store data
445
+ path = {
446
+ -- Directory for built-in package.
447
+ -- All plugins are actually stored in 'pack/deps' subdirectory.
448
+ package = vim.fn.stdpath('data') .. '/site',
449
+
450
+ -- Default file path for a snapshot
451
+ snapshot = vim.fn.stdpath('config') .. '/mini-deps-snap',
452
+
453
+ -- Log file
454
+ --minidoc_replace_start log = vim.fn.stdpath('log') .. '/mini-deps.log'
455
+ log = vim.fn.stdpath('log') .. '/mini-deps.log',
456
+ --minidoc_replace_end
457
+ },
458
+
459
+ -- Whether to disable showing non-error feedback
460
+ silent = false,
461
+ }
462
+ --minidoc_afterlines_end
463
+
464
+ --- Add plugin to current session
465
+ ---
466
+ --- - Process specification by expanding dependencies into single spec array.
467
+ --- - Ensure plugin is present on disk along with its dependencies by installing
468
+ --- (in parallel) absent ones:
469
+ --- - Execute `opts.hooks.pre_install`.
470
+ --- - Use `git clone` to clone plugin from its source URI into "pack/deps/opt".
471
+ --- - Set state according to `opts.checkout`.
472
+ --- - Execute `opts.hooks.post_install`.
473
+ --- - Register spec(s) in current session.
474
+ --- - Make sure plugin(s) can be used in current session (see |:packadd|).
475
+ --- - If not during startup and is needed, source all "after/plugin/" scripts.
476
+ ---
477
+ --- Notes:
478
+ --- - Presence of plugin is checked by its name which is the same as the name
479
+ --- of its directory inside "pack/deps" package (see |MiniDeps-overview|).
480
+ --- - To increase performance, this function only ensures presence on disk and
481
+ --- nothing else. In particular, it doesn't ensure `opts.checkout` state.
482
+ --- Use |MiniDeps.update()| or |:DepsUpdateOffline| explicitly.
483
+ --- - Adding plugin several times updates its session specs.
484
+ ---
485
+ ---@param spec table|string Plugin specification. See |MiniDeps-plugin-specification|.
486
+ ---@param opts table|nil Options. Possible fields:
487
+ --- - <bang> `(boolean)` - whether to use `:packadd!` instead of plain |:packadd|.
488
+ MiniDeps.add = function(spec, opts)
489
+ opts = opts or {}
490
+ if type(opts) ~= 'table' then H.error('`opts` should be table.') end
491
+ if opts.source or opts.name or opts.checkout then H.error('`add()` accepts only single spec.') end
492
+
493
+ -- Normalize
494
+ local plugs = {}
495
+ H.expand_spec(plugs, spec)
496
+
497
+ -- Process
498
+ local plugs_to_install = {}
499
+ for i, p in ipairs(plugs) do
500
+ local path, is_present = H.get_plugin_path(p.name)
501
+ p.path = path
502
+ if not is_present then table.insert(plugs_to_install, vim.deepcopy(p)) end
503
+ end
504
+
505
+ -- Install
506
+ if #plugs_to_install > 0 then
507
+ H.ensure_git_exec()
508
+ for _, p in ipairs(plugs_to_install) do
509
+ p.job = H.cli_new_job({}, vim.fn.getcwd())
510
+ end
511
+
512
+ H.notify(string.format('Installing `%s`', plugs[#plugs].name))
513
+ H.plugs_exec_hooks(plugs_to_install, 'pre_install')
514
+ H.plugs_install(plugs_to_install)
515
+ H.plugs_exec_hooks(plugs_to_install, 'post_install')
516
+ end
517
+
518
+ -- Add plugins to current session
519
+ local cmd = 'packadd' .. (opts.bang and '!' or '') .. ' '
520
+ for _, p in ipairs(plugs) do
521
+ -- Register in 'mini.deps' session
522
+ table.insert(H.session, p)
523
+
524
+ -- Add to 'runtimepath'
525
+ vim.cmd(cmd .. p.name)
526
+ end
527
+
528
+ -- Execute 'after/' scripts if not during startup (when they will be sourced
529
+ -- automatically), as `:packadd` only sources plain 'plugin/' files.
530
+ -- See https://github.com/vim/vim/issues/1994.
531
+ -- Deliberately do so after executing all currently known 'plugin/' files.
532
+ local should_load_after_dir = vim.v.vim_did_enter == 1 and not opts.bang and vim.o.loadplugins
533
+ if not should_load_after_dir then return end
534
+ for _, p in ipairs(plugs) do
535
+ -- NOTE: This sources first lua and then vim, not how it is done during
536
+ -- startup (`:h loadplugins`) for speed (one `glob()` instead of two).
537
+ local after_paths = vim.fn.glob(p.path .. '/after/plugin/**/*.{vim,lua}', false, true)
538
+ vim.tbl_map(H.source, after_paths)
539
+ end
540
+ end
541
+
542
+ --- Update plugins
543
+ ---
544
+ --- - Synchronize specs with state of plugins on disk (set `source`, etc.).
545
+ --- - Infer data before downloading updates.
546
+ --- - If not offline, download updates (in parallel).
547
+ --- - Infer data after downloading updates.
548
+ --- - If update is forced, apply all changes immediately while updating log
549
+ --- file (at `config.path.log`; use |:DepsShowLog| to review).
550
+ --- Otherwise show confirmation buffer with instructions on how to proceed.
551
+ ---
552
+ ---@param names table|nil Array of plugin names to update.
553
+ --- Default: all plugins from current session (see |MiniDeps.get_session()|).
554
+ ---@param opts table|nil Options. Possible fields:
555
+ --- - <force> `(boolean)` - whether to force update without confirmation.
556
+ --- Default: `false`.
557
+ --- - <offline> `(boolean)` - whether to skip downloading updates from sources.
558
+ --- Default: `false`.
559
+ MiniDeps.update = function(names, opts)
560
+ opts = vim.tbl_deep_extend('force', { force = false, offline = false }, opts or {})
561
+
562
+ -- Compute array of plugin data to be reused in update. Each contains a CLI
563
+ -- job "assigned" to plugin's path which stops execution after first error.
564
+ local plugs = H.plugs_from_names(names)
565
+ if #plugs == 0 then return H.notify('Nothing to update') end
566
+
567
+ -- Prepare repositories and specifications
568
+ H.ensure_git_exec()
569
+ H.plugs_ensure_origin_source(plugs)
570
+
571
+ -- Preprocess before downloading
572
+ H.plugs_infer_head(plugs)
573
+ H.plugs_ensure_target_refs(plugs)
574
+ H.plugs_infer_commit(plugs, 'monitor', 'monitor_from')
575
+
576
+ -- Download data if asked
577
+ if not opts.offline then H.plugs_download_updates(plugs) end
578
+
579
+ -- Process data for update
580
+ H.plugs_infer_commit(plugs, 'checkout', 'checkout_to')
581
+ H.plugs_infer_commit(plugs, 'monitor', 'monitor_to')
582
+ H.plugs_infer_log(plugs, 'head', 'checkout_to', 'checkout_log')
583
+ H.plugs_infer_log(plugs, 'monitor_from', 'monitor_to', 'monitor_log')
584
+
585
+ -- Checkout if asked (before feedback to include possible checkout errors)
586
+ if opts.force then H.plugs_checkout(plugs) end
587
+
588
+ -- Make feedback
589
+ local lines = H.update_compute_feedback_lines(plugs)
590
+ local feedback = opts.force and H.update_feedback_log or H.update_feedback_confirm
591
+ feedback(lines)
592
+
593
+ -- Show job warnings and errors
594
+ H.plugs_show_job_notifications(plugs, 'update')
595
+ end
596
+
597
+ --- Clean plugins
598
+ ---
599
+ --- - Compute absent plugins: not registered in current session
600
+ --- (see |MiniDeps.get_session()|) but present on disk in dedicated "pack/deps"
601
+ --- package (inside `config.path.package`).
602
+ --- - If cleaning is forced, delete all absent plugins from disk.
603
+ --- Otherwise show confirmation buffer with instructions on how to proceed.
604
+ ---
605
+ ---@param opts table|nil Options. Possible fields:
606
+ --- - <force> `(boolean)` - whether to force delete without confirmation.
607
+ --- Default: `false`.
608
+ MiniDeps.clean = function(opts)
609
+ opts = vim.tbl_deep_extend('force', { force = false }, opts or {})
610
+
611
+ -- Compute path candidates to delete
612
+ local is_in_session = {}
613
+ for _, s in ipairs(MiniDeps.get_session()) do
614
+ is_in_session[s.path] = true
615
+ end
616
+
617
+ local is_absent_plugin = function(x) return vim.fn.isdirectory(x) == 1 and not is_in_session[x] end
618
+ local absent_paths = vim.tbl_filter(is_absent_plugin, H.get_all_plugin_paths())
619
+
620
+ -- Clean
621
+ if #absent_paths == 0 then return H.notify('Nothing to clean') end
622
+ local clean_fun = opts.force and H.clean_delete or H.clean_confirm
623
+ clean_fun(absent_paths)
624
+ end
625
+
626
+ --- Compute snapshot
627
+ ---
628
+ ---@return table A snapshot table: plugin names as keys and state as values.
629
+ --- All plugins in current session are processed.
630
+ MiniDeps.snap_get = function()
631
+ local plugs = H.plugs_from_names()
632
+ H.ensure_git_exec()
633
+ H.plugs_infer_head(plugs)
634
+ H.plugs_show_job_notifications(plugs, 'computing snapshot')
635
+
636
+ local snap = {}
637
+ for _, p in ipairs(plugs) do
638
+ if p.head ~= '' then snap[p.name] = p.head end
639
+ end
640
+ return snap
641
+ end
642
+
643
+ --- Apply snapshot
644
+ ---
645
+ --- Notes:
646
+ --- - Checking out states from snapshot does not update session plugin spec
647
+ --- (`checkout` field in particular). Among others, it means that next call
648
+ --- to |MiniDeps.update()| might override the result of this function.
649
+ --- To make changes permanent, set `checkout` spec field to state from snapshot.
650
+ ---
651
+ ---@param snap table A snapshot table: plugin names as keys and state as values.
652
+ --- Only plugins in current session are processed.
653
+ MiniDeps.snap_set = function(snap)
654
+ if type(snap) ~= 'table' then H.error('Snapshot should be a table.') end
655
+
656
+ -- Construct current session plugin data with `checkout` from snapshot
657
+ for k, v in pairs(snap) do
658
+ if not (type(k) == 'string' and type(v) == 'string') then snap[k] = nil end
659
+ end
660
+ local plugs = H.plugs_from_names(vim.tbl_keys(snap))
661
+ for _, p in ipairs(plugs) do
662
+ p.checkout = snap[p.name]
663
+ end
664
+
665
+ -- Checkout
666
+ H.ensure_git_exec()
667
+ H.plugs_checkout(plugs)
668
+ H.plugs_show_job_notifications(plugs, 'applying snapshot')
669
+ end
670
+
671
+ --- Save snapshot
672
+ ---
673
+ ---@param path string|nil A valid path on disk where to write snapshot computed
674
+ --- with |MiniDeps.snap_get()|.
675
+ --- Default: `config.path.snapshot`.
676
+ MiniDeps.snap_save = function(path)
677
+ path = path or H.full_path(H.get_config().path.snapshot)
678
+ if type(path) ~= 'string' then H.error('`path` should be string.') end
679
+
680
+ -- Compute snapshot
681
+ local snap = MiniDeps.snap_get()
682
+
683
+ -- Write snapshot
684
+ local lines = vim.split(vim.inspect(snap), '\n')
685
+ lines[1] = 'return ' .. lines[1]
686
+ vim.fn.mkdir(vim.fn.fnamemodify(path, ':h'), 'p')
687
+ vim.fn.writefile(lines, path)
688
+
689
+ H.notify('Created snapshot at ' .. vim.inspect(path))
690
+ end
691
+
692
+ --- Load snapshot file
693
+ ---
694
+ --- Notes from |MiniDeps.snap_set()| also apply here.
695
+ ---
696
+ ---@param path string|nil A valid path on disk from where to read snapshot.
697
+ --- Default: `config.path.snapshot`.
698
+ MiniDeps.snap_load = function(path)
699
+ path = path or H.full_path(H.get_config().path.snapshot)
700
+ if vim.fn.filereadable(path) ~= 1 then H.error('`path` should be path to a readable file.') end
701
+
702
+ local ok, snap = pcall(dofile, H.full_path(path))
703
+ if not (ok and type(snap) == 'table') then H.error('`path` is not a path to proper snapshot.') end
704
+
705
+ MiniDeps.snap_set(snap)
706
+ end
707
+
708
+ --- Get session
709
+ ---
710
+ --- Plugin is registered in current session if it either:
711
+ --- - Was added with |MiniDeps.add()| (preserving order of calls).
712
+ --- - Is a "start" plugin and present in |'runtimepath'|.
713
+ ---
714
+ ---@return table Array with specifications of all plugins registered in
715
+ --- current session.
716
+ MiniDeps.get_session = function()
717
+ -- Normalize `H.session` allowing specs for same plugin
718
+ local res, plugin_ids = {}, {}
719
+ local add_spec = function(spec)
720
+ local id = plugin_ids[spec.path] or (#res + 1)
721
+ -- Treat `depends` differently as it is an array and direct merge is bad
722
+ -- Also: https://github.com/neovim/neovim/pull/15094#discussion_r671663938
723
+ local depends = vim.deepcopy((res[id] or {}).depends or {})
724
+ vim.list_extend(depends, spec.depends or {})
725
+ res[id] = vim.tbl_deep_extend('force', res[id] or {}, spec)
726
+ res[id].depends = depends
727
+
728
+ plugin_ids[spec.path] = id
729
+ end
730
+ vim.tbl_map(add_spec, H.session)
731
+ H.session = res
732
+
733
+ -- Add 'start/' plugins that are in 'rtp'. NOTE: not whole session concept is
734
+ -- built around presence in 'rtp' to 100% ensure to preserve the order in
735
+ -- which user called `add()`.
736
+ local start_path = H.full_path(H.get_package_path() .. '/pack/deps/start')
737
+ local pattern = string.format('^%s/([^/]+)$', vim.pesc(start_path))
738
+ for _, runtime_path in ipairs(vim.api.nvim_list_runtime_paths()) do
739
+ -- Make sure plugin path is normalized (matters on Windows)
740
+ local path = H.full_path(runtime_path)
741
+ local name = string.match(path, pattern)
742
+ if name ~= nil then add_spec({ path = path, name = name, hooks = {}, depends = {} }) end
743
+ end
744
+
745
+ -- Return copy to not allow modification in place
746
+ return vim.deepcopy(res)
747
+ end
748
+
749
+ --- Execute function now
750
+ ---
751
+ --- Safely execute function immediately. Errors are shown with |vim.notify()|
752
+ --- later, after all queued functions (including with |MiniDeps.later()|)
753
+ --- are executed, thus not blocking execution of next code in file.
754
+ ---
755
+ --- Assumed to be used as a first step during two-stage config execution to
756
+ --- load plugins immediately during startup. See |MiniDeps-overview|.
757
+ ---
758
+ ---@param f function Callable to execute.
759
+ MiniDeps.now = function(f)
760
+ local ok, err = pcall(f)
761
+ if not ok then table.insert(H.cache.exec_errors, err) end
762
+ H.schedule_finish()
763
+ end
764
+
765
+ --- Execute function later
766
+ ---
767
+ --- Queue function to be safely executed later without blocking execution of
768
+ --- next code in file. All queued functions are guaranteed to be executed in
769
+ --- order they were added.
770
+ --- Errors are shown with |vim.notify()| after all queued functions are executed.
771
+ ---
772
+ --- Assumed to be used as a second step during two-stage config execution to
773
+ --- load plugins "lazily" after startup. See |MiniDeps-overview|.
774
+ ---
775
+ ---@param f function Callable to execute.
776
+ MiniDeps.later = function(f)
777
+ table.insert(H.cache.later_callback_queue, f)
778
+ H.schedule_finish()
779
+ end
780
+
781
+ -- Helper data ================================================================
782
+ -- Module default config
783
+ H.default_config = MiniDeps.config
784
+
785
+ -- Array of plugin specs
786
+ H.session = {}
787
+
788
+ -- Various cache
789
+ H.cache = {
790
+ -- Whether finish of `now()` or `later()` is already scheduled
791
+ finish_is_scheduled = false,
792
+
793
+ -- Callback queue for `later()`
794
+ later_callback_queue = {},
795
+
796
+ -- Errors during execution of `now()` or `later()`
797
+ exec_errors = {},
798
+
799
+ -- Git version
800
+ git_version = nil,
801
+ }
802
+
803
+ -- Helper functionality =======================================================
804
+ -- Settings -------------------------------------------------------------------
805
+ H.setup_config = function(config)
806
+ H.check_type('config', config, 'table', true)
807
+ config = vim.tbl_deep_extend('force', vim.deepcopy(H.default_config), config or {})
808
+
809
+ H.check_type('job', config.job, 'table')
810
+ H.check_type('job.n_threads', config.job.n_threads, 'number', true)
811
+ H.check_type('job.timeout', config.job.timeout, 'number')
812
+
813
+ H.check_type('path', config.path, 'table')
814
+ H.check_type('path.package', config.path.package, 'string')
815
+ H.check_type('path.snapshot', config.path.snapshot, 'string')
816
+ H.check_type('path.log', config.path.log, 'string')
817
+
818
+ H.check_type('silent', config.silent, 'boolean')
819
+
820
+ return config
821
+ end
822
+
823
+ H.apply_config = function(config)
824
+ MiniDeps.config = config
825
+
826
+ -- Reset current session to allow resourcing script with `setup()` call
827
+ H.session = {}
828
+
829
+ -- Add target package path to 'packpath'
830
+ local pack_path = H.full_path(config.path.package)
831
+ vim.cmd('set packpath^=' .. vim.fn.fnameescape(pack_path))
832
+ end
833
+
834
+ H.get_config = function(config)
835
+ return vim.tbl_deep_extend('force', MiniDeps.config, vim.b.minideps_config or {}, config or {})
836
+ end
837
+
838
+ H.create_autocommands = function()
839
+ local gr = vim.api.nvim_create_augroup('MiniDeps', {})
840
+ vim.api.nvim_create_autocmd('ColorScheme', { group = gr, callback = H.create_default_hl, desc = 'Ensure colors' })
841
+ end
842
+
843
+ --stylua: ignore
844
+ H.create_default_hl = function()
845
+ local hi = function(name, opts)
846
+ opts.default = true
847
+ vim.api.nvim_set_hl(0, name, opts)
848
+ end
849
+
850
+ local has_core_diff_hl = vim.fn.has('nvim-0.10') == 1
851
+ hi('MiniDepsChangeAdded', { link = has_core_diff_hl and 'Added' or 'diffAdded' })
852
+ hi('MiniDepsChangeRemoved', { link = has_core_diff_hl and 'Removed' or 'diffRemoved' })
853
+ hi('MiniDepsHint', { link = 'DiagnosticHint' })
854
+ hi('MiniDepsInfo', { link = 'DiagnosticInfo' })
855
+ hi('MiniDepsMsgBreaking', { link = 'DiagnosticWarn' })
856
+ hi('MiniDepsPlaceholder', { link = 'Comment' })
857
+ hi('MiniDepsTitle', { link = 'Title' })
858
+ hi('MiniDepsTitleError', { link = 'DiffDelete' })
859
+ hi('MiniDepsTitleSame', { link = 'DiffText' })
860
+ hi('MiniDepsTitleUpdate', { link = 'DiffAdd' })
861
+ end
862
+
863
+ H.create_user_commands = function()
864
+ -- Do not create commands immediately to increase startup time
865
+ local new_cmd = vim.schedule_wrap(vim.api.nvim_create_user_command)
866
+
867
+ local complete_session_names = function(arg, _, _)
868
+ local session_names = vim.tbl_map(function(s) return s.name end, MiniDeps.get_session())
869
+ return vim.tbl_filter(function(n) return vim.startswith(n, arg) end, session_names)
870
+ end
871
+ local complete_disk_names = function(arg, _, _)
872
+ local disk_names = vim.tbl_map(function(p) return vim.fn.fnamemodify(p, ':t') end, H.get_all_plugin_paths())
873
+ return vim.tbl_filter(function(n) return vim.startswith(n, arg) end, disk_names)
874
+ end
875
+
876
+ local add = function(input) MiniDeps.add(input.fargs[1]) end
877
+ new_cmd('DepsAdd', add, { nargs = '+', complete = complete_disk_names, desc = 'Add plugin to session' })
878
+
879
+ local make_update_cmd = function(name, offline, desc)
880
+ local callback = function(input)
881
+ local names
882
+ if #input.fargs > 0 then names = input.fargs end
883
+ MiniDeps.update(names, { force = input.bang, offline = offline })
884
+ end
885
+ local opts = { bang = true, complete = complete_session_names, nargs = '*', desc = desc }
886
+ new_cmd(name, callback, opts)
887
+ end
888
+ make_update_cmd('DepsUpdate', false, 'Update plugins')
889
+ make_update_cmd('DepsUpdateOffline', true, 'Update plugins without downloading from source')
890
+
891
+ local show_log = function()
892
+ H.edit(H.get_config().path.log)
893
+ H.update_add_syntax()
894
+ vim.cmd([[syntax match MiniDepsTitle "^\(==========\).*\1$"]])
895
+ end
896
+ new_cmd('DepsShowLog', show_log, { desc = 'Show log' })
897
+
898
+ local clean = function(input) MiniDeps.clean({ force = input.bang }) end
899
+ new_cmd('DepsClean', clean, { bang = true, desc = 'Delete unused plugins' })
900
+
901
+ local snap_save = function(input) MiniDeps.snap_save(input.fargs[1]) end
902
+ new_cmd('DepsSnapSave', snap_save, { nargs = '*', complete = 'file', desc = 'Save plugin snapshot' })
903
+
904
+ local snap_load = function(input) MiniDeps.snap_load(input.fargs[1]) end
905
+ new_cmd('DepsSnapLoad', snap_load, { nargs = '*', complete = 'file', desc = 'Load plugin snapshot' })
906
+ end
907
+
908
+ -- Git commands ---------------------------------------------------------------
909
+ H.git_cmd = function(cmd_name, ...)
910
+ local args = H.git_args[cmd_name](...)
911
+ if args == nil then return {} end
912
+
913
+ -- Use '-c gc.auto=0' to disable `stderr` "Auto packing..." messages
914
+ return { 'git', '-c', 'gc.auto=0', unpack(args) }
915
+ end
916
+
917
+ --stylua: ignore
918
+ H.git_args = {
919
+ version = function()
920
+ return { 'version' }
921
+ end,
922
+ clone = function(source, path)
923
+ local res = {
924
+ 'clone', '--quiet', '--filter=blob:none',
925
+ '--recurse-submodules', '--also-filter-submodules', '--origin', 'origin',
926
+ source, path,
927
+ }
928
+ -- Use `--also-filter-submodules` only with appropriate version
929
+ if not (H.cache.git_version.major >= 2 and H.cache.git_version.minor >= 36) then
930
+ table.remove(res, 5)
931
+ end
932
+ return res
933
+ end,
934
+ stash = function(timestamp)
935
+ return { 'stash', '--quiet', '--message', '(mini.deps) ' .. timestamp .. ' Stash before checkout' }
936
+ end,
937
+ checkout = function(target)
938
+ return { 'checkout', '--quiet', target }
939
+ end,
940
+ -- Using '--tags --force' means conflicting tags will be synced with remote
941
+ fetch = function()
942
+ return { 'fetch', '--quiet', '--tags', '--force', '--recurse-submodules=yes', 'origin' }
943
+ end,
944
+ set_origin = function(source)
945
+ return { 'remote', 'set-url', 'origin', source }
946
+ end,
947
+ get_origin = function()
948
+ return { 'remote', 'get-url', 'origin' }
949
+ end,
950
+ get_default_origin_branch = function()
951
+ return { 'rev-parse', '--abbrev-ref', 'origin/HEAD' }
952
+ end,
953
+ is_origin_branch = function(name)
954
+ -- Returns branch's name if it is present
955
+ return { 'branch', '--list', '--all', '--format=%(refname:short)', 'origin/' .. name }
956
+ end,
957
+ -- Using `rev-list -1` shows a commit of revision, while `rev-parse` shows
958
+ -- hash of revision. Those are different for annotated tags.
959
+ get_hash = function(rev)
960
+ return { 'rev-list', '-1', rev }
961
+ end,
962
+ log = function(from, to)
963
+ if from == nil or to == nil or from == to then return nil end
964
+ -- `--topo-order` makes showing divergent branches nicer
965
+ -- `--decorate-refs` shows only tags near commits (not `origin/main`, etc.)
966
+ --stylua: ignore
967
+ return {
968
+ 'log', '--pretty=format:%m %h | %ai | %an%d%n %s%n', '--topo-order', '--decorate-refs=refs/tags',
969
+ from .. '...' .. to,
970
+ }
971
+ end,
972
+ }
973
+
974
+ H.ensure_git_exec = function()
975
+ if H.cache.git_version ~= nil then return end
976
+ local jobs = { H.cli_new_job(H.git_cmd('version'), vim.fn.getcwd()) }
977
+ H.cli_run(jobs)
978
+ if #jobs[1].err > 0 then H.error('Could not find executable `git` CLI tool') end
979
+ local major, minor = string.match(H.cli_stream_tostring(jobs[1].out), '(%d+)%.(%d+)')
980
+ H.cache.git_version = { major = tonumber(major), minor = tonumber(minor) }
981
+ end
982
+
983
+ -- Plugin specification -------------------------------------------------------
984
+ H.expand_spec = function(target, spec)
985
+ -- Prepare
986
+ if type(spec) == 'string' then
987
+ local field = string.find(spec, '/') ~= nil and 'source' or 'name'
988
+ spec = { [field] = spec }
989
+ end
990
+ if type(spec) ~= 'table' then H.error('Plugin spec should be table.') end
991
+
992
+ local has_min_fields = type(spec.source) == 'string' or type(spec.name) == 'string'
993
+ if not has_min_fields then H.error('Plugin spec should have proper `source` or `name`.') end
994
+
995
+ -- Normalize
996
+ spec = vim.deepcopy(spec)
997
+
998
+ if spec.source and type(spec.source) ~= 'string' then H.error('`source` in plugin spec should be string.') end
999
+ local is_user_repo = type(spec.source) == 'string' and spec.source:find('^[%w-]+/[%w-_.]+$') ~= nil
1000
+ if is_user_repo then spec.source = 'https://github.com/' .. spec.source end
1001
+
1002
+ spec.name = spec.name or vim.fn.fnamemodify(spec.source, ':t')
1003
+ if type(spec.name) ~= 'string' then H.error('`name` in plugin spec should be string.') end
1004
+ if string.find(spec.name, '/') ~= nil then H.error('`name` in plugin spec should not contain "/".') end
1005
+ if spec.name == '' then H.error('`name` in plugin spec should not be empty.') end
1006
+
1007
+ if spec.checkout and type(spec.checkout) ~= 'string' then H.error('`checkout` in plugin spec should be string.') end
1008
+ if spec.monitor and type(spec.monitor) ~= 'string' then H.error('`monitor` in plugin spec should be string.') end
1009
+
1010
+ spec.hooks = vim.deepcopy(spec.hooks) or {}
1011
+ if type(spec.hooks) ~= 'table' then H.error('`hooks` in plugin spec should be table.') end
1012
+ local hook_names = { 'pre_install', 'post_install', 'pre_checkout', 'post_checkout' }
1013
+ for _, hook_name in ipairs(hook_names) do
1014
+ local is_not_hook = spec.hooks[hook_name] and not vim.is_callable(spec.hooks[hook_name])
1015
+ if is_not_hook then H.error('`hooks.' .. hook_name .. '` in plugin spec should be callable.') end
1016
+ end
1017
+
1018
+ -- Expand dependencies recursively before adding current spec to target
1019
+ spec.depends = vim.deepcopy(spec.depends) or {}
1020
+ if not H.islist(spec.depends) then H.error('`depends` in plugin spec should be array.') end
1021
+ for _, dep_spec in ipairs(spec.depends) do
1022
+ H.expand_spec(target, dep_spec)
1023
+ end
1024
+
1025
+ table.insert(target, spec)
1026
+ end
1027
+
1028
+ -- Plugin operations ----------------------------------------------------------
1029
+ H.plugs_exec_hooks = function(plugs, name)
1030
+ for _, p in ipairs(plugs) do
1031
+ local has_error = p.job and #p.job.err > 0
1032
+ local should_execute = vim.is_callable(p.hooks[name]) and not has_error
1033
+ if should_execute then
1034
+ local ok, err = pcall(p.hooks[name], { path = p.path, source = p.source, name = p.name })
1035
+ if not ok then
1036
+ local msg = string.format('Error executing %s hook in `%s`:\n%s', name, p.name, err)
1037
+ H.notify(msg, 'ERROR')
1038
+ end
1039
+ end
1040
+ end
1041
+ end
1042
+
1043
+ H.plugs_install = function(plugs)
1044
+ -- Clone
1045
+ local prepare = function(p)
1046
+ if p.source == nil and #p.job.err == 0 then p.job.err = { 'SPECIFICATION HAS NO `source` TO INSTALL PLUGIN.' } end
1047
+ p.job.command = H.git_cmd('clone', p.source or '', p.path)
1048
+ p.job.exit_msg = string.format('Installed `%s`', p.name)
1049
+ end
1050
+ H.plugs_run_jobs(plugs, prepare)
1051
+
1052
+ -- Checkout
1053
+ vim.tbl_map(function(p) p.job.cwd = p.path end, plugs)
1054
+ H.plugs_checkout(plugs, { exec_hooks = false, all_helptags = true })
1055
+
1056
+ -- Show warnings and errors
1057
+ H.plugs_show_job_notifications(plugs, 'installing plugin')
1058
+ end
1059
+
1060
+ H.plugs_download_updates = function(plugs)
1061
+ -- Show actual target number of plugins attempted to fetch
1062
+ local n_noerror = 0
1063
+ for _, p in ipairs(plugs) do
1064
+ if #p.job.err == 0 then n_noerror = n_noerror + 1 end
1065
+ end
1066
+ if n_noerror == 0 then return end
1067
+ H.notify('Downloading ' .. n_noerror .. ' update' .. (n_noerror > 1 and 's' or ''))
1068
+
1069
+ local prepare = function(p)
1070
+ p.job.command = H.git_cmd('fetch')
1071
+ p.job.exit_msg = string.format('Downloaded update for `%s`', p.name)
1072
+ end
1073
+ H.plugs_run_jobs(plugs, prepare)
1074
+ end
1075
+
1076
+ H.plugs_checkout = function(plugs, opts)
1077
+ opts = vim.tbl_deep_extend('force', { exec_hooks = true, all_helptags = false }, opts or {})
1078
+
1079
+ H.plugs_infer_head(plugs)
1080
+ H.plugs_ensure_target_refs(plugs)
1081
+ H.plugs_infer_commit(plugs, 'checkout', 'checkout_to')
1082
+
1083
+ -- Operate only on plugins that actually need checkout
1084
+ local checkout_plugs = vim.tbl_filter(function(p) return p.head ~= p.checkout_to end, plugs)
1085
+
1086
+ -- Stash changes
1087
+ local stash_command = H.git_cmd('stash', H.get_timestamp())
1088
+ local prepare = function(p) p.job.command = stash_command end
1089
+ H.plugs_run_jobs(checkout_plugs, prepare)
1090
+
1091
+ -- Execute pre hooks
1092
+ if opts.exec_hooks then H.plugs_exec_hooks(checkout_plugs, 'pre_checkout') end
1093
+
1094
+ -- Checkout
1095
+ prepare = function(p)
1096
+ p.job.command = H.git_cmd('checkout', p.checkout_to)
1097
+ p.job.exit_msg = string.format('Checked out `%s` in `%s`', p.checkout, p.name)
1098
+ end
1099
+ H.plugs_run_jobs(checkout_plugs, prepare)
1100
+
1101
+ -- Execute post hooks
1102
+ if opts.exec_hooks then H.plugs_exec_hooks(checkout_plugs, 'post_checkout') end
1103
+
1104
+ -- (Re)Generate help tags according to the current help files
1105
+ local help_plugs = opts.all_helptags and plugs or checkout_plugs
1106
+ for _, p in ipairs(help_plugs) do
1107
+ local doc_dir = p.path .. '/doc'
1108
+ -- Completely redo tags
1109
+ vim.fn.delete(doc_dir .. '/tags')
1110
+ local has_help_files = vim.fn.glob(doc_dir .. '/**') ~= ''
1111
+ if has_help_files then pcall(vim.cmd, 'helptags ' .. vim.fn.fnameescape(doc_dir)) end
1112
+ end
1113
+ end
1114
+
1115
+ -- Plugin operation helpers ---------------------------------------------------
1116
+ H.plugs_from_names = function(names)
1117
+ if names and not H.islist(names) then H.error('`names` should be array.') end
1118
+ for _, name in ipairs(names or {}) do
1119
+ if type(name) ~= 'string' then H.error('`names` should contain only strings.') end
1120
+ end
1121
+
1122
+ local res = {}
1123
+ for _, spec in ipairs(MiniDeps.get_session()) do
1124
+ if names == nil or vim.tbl_contains(names, spec.name) then
1125
+ spec.job = H.cli_new_job({}, spec.path)
1126
+ table.insert(res, spec)
1127
+ end
1128
+ end
1129
+
1130
+ return res
1131
+ end
1132
+
1133
+ H.plugs_run_jobs = function(plugs, prepare, process)
1134
+ if vim.is_callable(prepare) then vim.tbl_map(prepare, plugs) end
1135
+
1136
+ H.cli_run(vim.tbl_map(function(p) return p.job end, plugs))
1137
+
1138
+ if vim.is_callable(process) then vim.tbl_map(process, plugs) end
1139
+
1140
+ -- Clean jobs. Preserve errors for jobs to be properly reusable.
1141
+ for _, p in ipairs(plugs) do
1142
+ p.job.command, p.job.exit_msg, p.job.out = {}, nil, {}
1143
+ end
1144
+ end
1145
+
1146
+ H.plugs_show_job_notifications = function(plugs, action_name)
1147
+ for _, p in ipairs(plugs) do
1148
+ local warn = H.cli_stream_tostring(p.job.warn)
1149
+ if warn ~= '' then
1150
+ local msg = string.format('Warnings in `%s` during %s\n%s', p.name, action_name, warn)
1151
+ H.notify(msg, 'WARN')
1152
+ end
1153
+ local err = H.cli_stream_tostring(p.job.err)
1154
+ if err ~= '' then
1155
+ local msg = string.format('Error in `%s` during %s\n%s', p.name, action_name, err)
1156
+ H.notify(msg, 'ERROR')
1157
+ end
1158
+ end
1159
+ end
1160
+
1161
+ H.plugs_ensure_origin_source = function(plugs)
1162
+ local prepare = function(p) p.job.command = p.source and H.git_cmd('set_origin', p.source) or H.git_cmd('get_origin') end
1163
+ local process = function(p) p.source = p.source or H.cli_stream_tostring(p.job.out) end
1164
+ H.plugs_run_jobs(plugs, prepare, process)
1165
+ end
1166
+
1167
+ H.plugs_ensure_target_refs = function(plugs)
1168
+ local prepare = function(p)
1169
+ local needs_infer = p.checkout == nil or p.monitor == nil
1170
+ p.job.command = needs_infer and H.git_cmd('get_default_origin_branch') or {}
1171
+ end
1172
+ local process = function(p)
1173
+ local def_branch = H.cli_stream_tostring(p.job.out):gsub('^origin/', '')
1174
+ p.checkout = p.checkout or def_branch
1175
+ p.monitor = p.monitor or def_branch
1176
+ p.has_monitor = p.checkout ~= p.monitor
1177
+ end
1178
+ H.plugs_run_jobs(plugs, prepare, process)
1179
+ end
1180
+
1181
+ H.plugs_infer_head = function(plugs)
1182
+ local prepare = function(p) p.job.command = p.head == nil and H.git_cmd('get_hash', 'HEAD') or {} end
1183
+ local process = function(p) p.head = p.head or H.cli_stream_tostring(p.job.out) end
1184
+ H.plugs_run_jobs(plugs, prepare, process)
1185
+ end
1186
+
1187
+ H.plugs_infer_commit = function(plugs, field_ref, field_out)
1188
+ -- Determine if reference points to an origin branch (to avoid error later)
1189
+ local prepare = function(p)
1190
+ -- Don't recompute commit if it is already computed
1191
+ -- Don't compute commit for 'monitor' if it won't be used
1192
+ p.should_infer = p[field_out] == nil and (field_ref ~= 'monitor' or p.has_monitor)
1193
+ p.job.command = p.should_infer and H.git_cmd('is_origin_branch', p[field_ref]) or {}
1194
+ end
1195
+ local process = function(p) p.is_ref_origin_branch = H.cli_stream_tostring(p.job.out):find('%S') ~= nil end
1196
+ H.plugs_run_jobs(plugs, prepare, process)
1197
+
1198
+ -- Infer commit depending on whether it points to origin branch
1199
+ prepare = function(p)
1200
+ -- Force `checkout = 'HEAD'` to always point to current commit to freeze
1201
+ -- updates. This is needed because `origin/HEAD` is also present.
1202
+ local is_from_origin = p.is_ref_origin_branch and p[field_ref] ~= 'HEAD'
1203
+ local ref = (is_from_origin and 'origin/' or '') .. p[field_ref]
1204
+ p.job.command = p.should_infer and H.git_cmd('get_hash', ref) or {}
1205
+ end
1206
+ process = function(p)
1207
+ if p.should_infer then p[field_out] = H.cli_stream_tostring(p.job.out) end
1208
+ p.is_ref_origin_branch, p.should_infer = nil, nil
1209
+ end
1210
+ H.plugs_run_jobs(plugs, prepare, process)
1211
+ end
1212
+
1213
+ H.plugs_infer_log = function(plugs, field_from, field_to, field_out)
1214
+ local prepare = function(p) p.job.command = H.git_cmd('log', p[field_from], p[field_to]) end
1215
+ local process = function(p) p[field_out] = H.cli_stream_tostring(p.job.out) end
1216
+ H.plugs_run_jobs(plugs, prepare, process)
1217
+ end
1218
+
1219
+ -- File system ----------------------------------------------------------------
1220
+ H.get_plugin_path = function(name)
1221
+ local package_path = H.get_package_path()
1222
+
1223
+ -- First check for the most common case of name present in 'pack/deps/opt'
1224
+ local opt_path = string.format('%s/pack/deps/opt/%s', package_path, name)
1225
+ if vim.loop.fs_stat(opt_path) ~= nil then return opt_path, true end
1226
+
1227
+ -- Allow processing 'pack/deps/start'
1228
+ local start_path = string.format('%s/pack/deps/start/%s', package_path, name)
1229
+ if vim.loop.fs_stat(start_path) ~= nil then return start_path, true end
1230
+
1231
+ -- Use 'opt' directory by default
1232
+ return opt_path, false
1233
+ end
1234
+
1235
+ H.get_all_plugin_paths = function()
1236
+ local deps_path, res = H.get_package_path() .. '/pack/deps', {}
1237
+ vim.list_extend(res, H.readdir(deps_path .. '/opt'))
1238
+ vim.list_extend(res, H.readdir(deps_path .. '/start'))
1239
+ return res
1240
+ end
1241
+
1242
+ H.get_package_path = function() return H.full_path(H.get_config().path.package) end
1243
+
1244
+ -- Clean ----------------------------------------------------------------------
1245
+ H.clean_confirm = function(paths)
1246
+ -- Compute lines
1247
+ local lines = {
1248
+ 'This is a confirmation report before a clean.',
1249
+ '',
1250
+ 'Lines `- <plugin>` show plugins to be deleted from disk.',
1251
+ 'Remove line to not delete that plugin.',
1252
+ '',
1253
+ 'To finish clean, write this buffer (for example, with `:write` command).',
1254
+ 'To cancel clean, close this window (for example, with `:close` command).',
1255
+ '',
1256
+ }
1257
+ local n_header = #lines - 1
1258
+ for _, p in ipairs(paths) do
1259
+ table.insert(lines, string.format('- %s (%s)', vim.fn.fnamemodify(p, ':t'), p))
1260
+ end
1261
+
1262
+ -- Show report in new buffer in separate tabpage
1263
+ local finish_clean = function(buf_id)
1264
+ -- Compute plugin paths to update
1265
+ local paths_to_delete = {}
1266
+ for _, l in ipairs(vim.api.nvim_buf_get_lines(buf_id, 0, -1, false)) do
1267
+ local cur_path = string.match(l, '^%- .* %((.*)%)$')
1268
+ if cur_path ~= nil then table.insert(paths_to_delete, cur_path) end
1269
+ end
1270
+
1271
+ if #paths_to_delete == 0 then return H.notify('Nothing to delete') end
1272
+ H.clean_delete(paths_to_delete)
1273
+ end
1274
+ H.show_confirm_buf(lines, { name = 'confirm-clean', exec_on_write = finish_clean })
1275
+
1276
+ -- Define basic highlighting
1277
+ vim.cmd('syntax region MiniDepsHint start="^\\%1l" end="\\%' .. n_header .. 'l$"')
1278
+
1279
+ -- Define conceal to show only name with whole path when cursor is on it
1280
+ vim.cmd('syntax conceal on')
1281
+ vim.cmd([[syntax match MiniDepsInfo "\s\+(.\{-})$"]])
1282
+ vim.cmd('syntax conceal off')
1283
+ vim.cmd('setlocal conceallevel=3')
1284
+ end
1285
+
1286
+ H.clean_delete = function(paths)
1287
+ local n_to_delete = #paths
1288
+ for i, p in ipairs(paths) do
1289
+ vim.fn.delete(p, 'rf')
1290
+ local msg = string.format('(%d/%d) Deleted `%s` from disk', i, n_to_delete, vim.fn.fnamemodify(p, ':t'))
1291
+ H.notify(msg)
1292
+ end
1293
+ end
1294
+
1295
+ -- Update ---------------------------------------------------------------------
1296
+ H.update_compute_feedback_lines = function(plugs)
1297
+ -- Construct lines with metadata for later sort
1298
+ local plug_data = {}
1299
+ for i, p in ipairs(plugs) do
1300
+ local lines = H.update_compute_report_single(p)
1301
+ --stylua: ignore
1302
+ plug_data[i] = {
1303
+ lines = lines, has_error = p.has_error, has_updates = p.has_updates, has_monitor = p.has_monitor, name = p.name, index = i
1304
+ }
1305
+ end
1306
+
1307
+ -- Sort to put first ones with errors, then with updates, then rest
1308
+ local compare = function(a, b)
1309
+ if a.has_error and not b.has_error then return true end
1310
+ if not a.has_error and b.has_error then return false end
1311
+ if a.has_updates and not b.has_updates then return true end
1312
+ if not a.has_updates and b.has_updates then return false end
1313
+ if a.has_monitor and not b.has_monitor then return true end
1314
+ if not a.has_monitor and b.has_monitor then return false end
1315
+ if a.name == 'mini.nvim' then return true end
1316
+ if b.name == 'mini.nvim' then return false end
1317
+ return a.index < b.index
1318
+ end
1319
+ table.sort(plug_data, compare)
1320
+
1321
+ local plug_lines = vim.tbl_map(function(x) return x.lines end, plug_data)
1322
+ return vim.split(table.concat(plug_lines, '\n\n'), '\n')
1323
+ end
1324
+
1325
+ H.update_compute_report_single = function(p)
1326
+ p.has_error, p.has_updates = #p.job.err > 0, p.head ~= p.checkout_to
1327
+
1328
+ local err = H.cli_stream_tostring(p.job.err)
1329
+ if err ~= '' then return string.format('!!! %s !!!\n\n%s', p.name, err) end
1330
+
1331
+ -- Compute title surrounding based on whether plugin needs an update
1332
+ local surrounding = p.has_updates and '+++' or '---'
1333
+ local parts = { string.format('%s %s %s\n', surrounding, p.name, surrounding) }
1334
+
1335
+ if p.head == p.checkout_to then
1336
+ table.insert(parts, 'Path: ' .. p.path .. '\n')
1337
+ table.insert(parts, 'Source: ' .. (p.source or '<None>') .. '\n')
1338
+ table.insert(parts, string.format('State: %s (%s)', p.checkout_to, p.checkout))
1339
+ else
1340
+ table.insert(parts, 'Path: ' .. p.path .. '\n')
1341
+ table.insert(parts, 'Source: ' .. (p.source or '<None>') .. '\n')
1342
+ table.insert(parts, 'State before: ' .. p.head .. '\n')
1343
+ table.insert(parts, string.format('State after: %s (%s)', p.checkout_to, p.checkout))
1344
+ end
1345
+
1346
+ -- Show pending updates only if they are present
1347
+ if p.has_updates then
1348
+ table.insert(parts, string.format('\n\nPending updates from `%s`:\n', p.checkout))
1349
+ table.insert(parts, p.checkout_log)
1350
+ end
1351
+
1352
+ -- Show monitor updates only if user asked for them
1353
+ if p.has_monitor then
1354
+ table.insert(parts, string.format('\n\nMonitor updates from `%s`:\n', p.monitor))
1355
+ table.insert(parts, p.monitor_log ~= '' and p.monitor_log or '<Nothing>')
1356
+ end
1357
+
1358
+ return table.concat(parts, '')
1359
+ end
1360
+
1361
+ H.update_feedback_confirm = function(lines)
1362
+ -- Add helper header
1363
+ local report = {
1364
+ 'This is a confirmation report before an update.',
1365
+ '',
1366
+ 'Line `+++ <plugin_name> +++` means plugin will be updated.',
1367
+ 'See update details below it.',
1368
+ 'Changes starting with ">"/"<" will be added/removed.',
1369
+ 'Remove the line to not update that plugin.',
1370
+ '',
1371
+ 'Line `--- <plugin_name> ---` means plugin has nothing to update.',
1372
+ '',
1373
+ "Line `!!! <plugin_name> !!!` means plugin had an error and won't be updated.",
1374
+ 'See error details below it.',
1375
+ '',
1376
+ 'Use regular fold keys (`zM`, `zR`, etc.) to manage shorter view.',
1377
+ 'To finish update, write this buffer (for example, with `:write` command).',
1378
+ 'To cancel update, close this window (for example, with `:close` command).',
1379
+ '',
1380
+ }
1381
+ local n_header = #report - 1
1382
+ vim.list_extend(report, lines)
1383
+
1384
+ -- Show report in new buffer in separate tabpage
1385
+ local finish_update = function(buf_id)
1386
+ -- Compute plugin names to update
1387
+ local names = {}
1388
+ for _, l in ipairs(vim.api.nvim_buf_get_lines(buf_id, 0, -1, false)) do
1389
+ local cur_name = string.match(l, '^%+%+%+ (.*) %+%+%+$')
1390
+ if cur_name ~= nil then table.insert(names, cur_name) end
1391
+ end
1392
+
1393
+ -- Update and delete buffer (in that order, to show that update is done)
1394
+ MiniDeps.update(names, { force = true, offline = true })
1395
+ end
1396
+
1397
+ H.show_confirm_buf(report, { name = 'confirm-update', exec_on_write = finish_update, setup_folds = true })
1398
+
1399
+ -- Define basic highlighting
1400
+ vim.cmd('syntax region MiniDepsHint start="^\\%1l" end="\\%' .. n_header .. 'l$"')
1401
+ H.update_add_syntax()
1402
+ end
1403
+
1404
+ H.update_add_syntax = function()
1405
+ vim.cmd([[
1406
+ syntax match MiniDepsTitleError "^!!! .\+ !!!$"
1407
+ syntax match MiniDepsTitleUpdate "^+++ .\+ +++$"
1408
+ syntax match MiniDepsTitleSame "^--- .\+ ---$"
1409
+ syntax match MiniDepsInfo "^Path: \+\zs[^ ]\+"
1410
+ syntax match MiniDepsInfo "^Source: \+\zs[^ ]\+"
1411
+ syntax match MiniDepsInfo "^State[^:]*: \+\zs[^ ]\+\ze"
1412
+ syntax match MiniDepsHint "\(^State.\+\)\@<=(.\+)$"
1413
+ syntax match MiniDepsChangeAdded "^> .*$"
1414
+ syntax match MiniDepsChangeRemoved "^< .*$"
1415
+ syntax match MiniDepsMsgBreaking "^ \S\+!: .*$"
1416
+ syntax match MiniDepsPlaceholder "^<.*>$"
1417
+ ]])
1418
+ end
1419
+
1420
+ H.update_feedback_log = function(lines)
1421
+ local title = string.format('========== Update %s ==========', H.get_timestamp())
1422
+ table.insert(lines, 1, title)
1423
+ table.insert(lines, '')
1424
+
1425
+ local log_path = H.get_config().path.log
1426
+ vim.fn.mkdir(vim.fn.fnamemodify(log_path, ':h'), 'p')
1427
+ vim.fn.writefile(lines, log_path, 'a')
1428
+ end
1429
+
1430
+ -- Confirm --------------------------------------------------------------------
1431
+ H.show_confirm_buf = function(lines, opts)
1432
+ -- Show buffer
1433
+ local buf_id = vim.api.nvim_create_buf(true, true)
1434
+ H.set_buf_name(buf_id, opts.name)
1435
+ vim.api.nvim_buf_set_lines(buf_id, 0, -1, false, lines)
1436
+ vim.cmd('tab sbuffer ' .. buf_id)
1437
+ local tab_num, win_id = vim.api.nvim_tabpage_get_number(0), vim.api.nvim_get_current_win()
1438
+
1439
+ local delete_buffer = vim.schedule_wrap(function()
1440
+ pcall(vim.api.nvim_buf_delete, buf_id, { force = true })
1441
+ pcall(function() vim.cmd('tabclose ' .. tab_num) end)
1442
+ vim.cmd('redraw')
1443
+ end)
1444
+
1445
+ -- Define folding
1446
+ local is_title = function(l) return l:find('^%-%-%-') or l:find('^%+%+%+') or l:find('^%!%!%!') end
1447
+ --stylua: ignore
1448
+ MiniDeps._confirm_foldexpr = function(lnum)
1449
+ if lnum == 1 then return 0 end
1450
+ if is_title(vim.fn.getline(lnum - 1)) then return 1 end
1451
+ if is_title(vim.fn.getline(lnum + 1)) then return 0 end
1452
+ return '='
1453
+ end
1454
+
1455
+ -- Possibly set up folding. Use `:setlocal` for these options to not be
1456
+ -- inherited if some other buffer is opened in the same window.
1457
+ if opts.setup_folds then
1458
+ vim.cmd('setlocal foldenable foldmethod=expr foldlevel=999')
1459
+ vim.cmd('setlocal foldexpr=v:lua.MiniDeps._confirm_foldexpr(v:lnum)')
1460
+ end
1461
+
1462
+ -- Define action on accepting confirm
1463
+ local finish = function()
1464
+ MiniDeps._confirm_foldexpr = nil
1465
+ opts.exec_on_write(buf_id)
1466
+ delete_buffer()
1467
+ end
1468
+ -- - Use `nested` to allow other events (`WinEnter` for 'mini.statusline')
1469
+ vim.api.nvim_create_autocmd('BufWriteCmd', { buffer = buf_id, nested = true, callback = finish })
1470
+
1471
+ -- Define action to cancel confirm
1472
+ local cancel_au_id
1473
+ local on_cancel = function(data)
1474
+ MiniDeps._confirm_foldexpr = nil
1475
+ if tonumber(data.match) ~= win_id then return end
1476
+ pcall(vim.api.nvim_del_autocmd, cancel_au_id)
1477
+ delete_buffer()
1478
+ end
1479
+ cancel_au_id = vim.api.nvim_create_autocmd('WinClosed', { nested = true, callback = on_cancel })
1480
+
1481
+ -- Set buffer-local options last (so that user autocmmands could override)
1482
+ vim.bo.buftype, vim.bo.filetype, vim.bo.modified = 'acwrite', 'minideps-confirm', false
1483
+ end
1484
+
1485
+ -- CLI ------------------------------------------------------------------------
1486
+ H.cli_run = function(jobs)
1487
+ local config_job = H.get_config().job
1488
+ local n_threads = math.max(config_job.n_threads or H.get_n_threads(), 1)
1489
+
1490
+ -- Use only actually runnable jobs
1491
+ local should_run = function(job)
1492
+ -- Run only if there is command to run and previous runs of same reusable
1493
+ -- job did not result into error
1494
+ return type(job.command) == 'table' and #job.command > 0 and #job.err == 0
1495
+ end
1496
+ jobs = vim.tbl_filter(should_run, jobs)
1497
+
1498
+ local n_total, id_started, n_finished = #jobs, 0, 0
1499
+ if n_total == 0 then return end
1500
+
1501
+ local run_next
1502
+ run_next = function()
1503
+ if n_total <= id_started then return end
1504
+ id_started = id_started + 1
1505
+
1506
+ local job = jobs[id_started]
1507
+ local command, cwd, exit_msg = job.command or {}, job.cwd, job.exit_msg
1508
+
1509
+ -- Prepare data for `vim.loop.spawn`
1510
+ local executable, args = command[1], vim.list_slice(command, 2, #command)
1511
+ local process, stdout, stderr = nil, vim.loop.new_pipe(), vim.loop.new_pipe()
1512
+
1513
+ -- - Unset special `GIT_xxx` variables that can affect `git` commands
1514
+ local env_map = vim.fn.environ()
1515
+ env_map.GIT_DIR, env_map.GIT_WORK_TREE = nil, nil
1516
+ local env = {}
1517
+ for k, v in pairs(env_map) do
1518
+ table.insert(env, k .. '=' .. tostring(v))
1519
+ end
1520
+
1521
+ local spawn_opts = { args = args, cwd = cwd, env = env, stdio = { nil, stdout, stderr } }
1522
+
1523
+ local on_exit = function(code)
1524
+ -- Process only not already closing job
1525
+ if process:is_closing() then return end
1526
+ process:close()
1527
+
1528
+ -- Process exit code: if 0 treat `stderr` as warning; error otherwise
1529
+ if code == 0 then
1530
+ vim.list_extend(job.warn, job.err)
1531
+ -- NOTE: This is valid as only jobs with `err = {}` are filtered to run
1532
+ job.err = {}
1533
+ else
1534
+ table.insert(job.err, 1, 'ERROR CODE ' .. code .. '\n')
1535
+ end
1536
+
1537
+ -- Finalize job
1538
+ n_finished = n_finished + 1
1539
+ if type(exit_msg) == 'string' and #job.err == 0 then
1540
+ H.notify(string.format('(%d/%d) %s', n_finished, n_total, exit_msg))
1541
+ end
1542
+
1543
+ -- Start next parallel job
1544
+ run_next()
1545
+ end
1546
+
1547
+ process = vim.loop.spawn(executable, spawn_opts, on_exit)
1548
+ H.cli_read_stream(stdout, job.out)
1549
+ H.cli_read_stream(stderr, job.err)
1550
+ vim.defer_fn(function()
1551
+ if not process:is_active() then return end
1552
+ table.insert(job.err, 'PROCESS REACHED TIMEOUT.')
1553
+ on_exit(1)
1554
+ end, config_job.timeout)
1555
+ end
1556
+
1557
+ for _ = 1, n_threads do
1558
+ run_next()
1559
+ end
1560
+
1561
+ vim.wait(config_job.timeout * n_total, function() return n_total <= n_finished end, 1)
1562
+ end
1563
+
1564
+ H.cli_read_stream = function(stream, feed)
1565
+ local callback = function(err, data)
1566
+ if err then return table.insert(feed, 1, 'ERROR: ' .. err) end
1567
+ if data ~= nil then return table.insert(feed, data) end
1568
+ stream:close()
1569
+ end
1570
+ stream:read_start(callback)
1571
+ end
1572
+
1573
+ H.cli_stream_tostring = function(stream) return (table.concat(stream):gsub('\n+$', '')) end
1574
+
1575
+ H.cli_new_job = function(command, cwd, exit_msg)
1576
+ return { command = command, cwd = cwd, exit_msg = exit_msg, out = {}, warn = {}, err = {} }
1577
+ end
1578
+
1579
+ -- Two-stage execution --------------------------------------------------------
1580
+ H.schedule_finish = function()
1581
+ if H.cache.finish_is_scheduled then return end
1582
+ vim.schedule(H.finish)
1583
+ H.cache.finish_is_scheduled = true
1584
+ end
1585
+
1586
+ H.finish = function()
1587
+ local timer, step_delay = vim.loop.new_timer(), 1
1588
+ local f = nil
1589
+ f = vim.schedule_wrap(function()
1590
+ local callback = H.cache.later_callback_queue[1]
1591
+ if callback == nil then
1592
+ H.cache.finish_is_scheduled, H.cache.later_callback_queue = false, {}
1593
+ H.report_errors()
1594
+ return
1595
+ end
1596
+
1597
+ table.remove(H.cache.later_callback_queue, 1)
1598
+ MiniDeps.now(callback)
1599
+ timer:start(step_delay, 0, f)
1600
+ end)
1601
+ timer:start(step_delay, 0, f)
1602
+ end
1603
+
1604
+ H.report_errors = function()
1605
+ if #H.cache.exec_errors == 0 then return end
1606
+ local error_lines = table.concat(H.cache.exec_errors, '\n\n')
1607
+ H.cache.exec_errors = {}
1608
+ H.notify('There were errors during two-stage execution:\n\n' .. error_lines, 'ERROR')
1609
+ end
1610
+
1611
+ -- Utilities ------------------------------------------------------------------
1612
+ H.error = function(msg) error('(mini.deps) ' .. msg, 0) end
1613
+
1614
+ H.check_type = function(name, val, ref, allow_nil)
1615
+ if type(val) == ref or (ref == 'callable' and vim.is_callable(val)) or (allow_nil and val == nil) then return end
1616
+ H.error(string.format('`%s` should be %s, not %s', name, ref, type(val)))
1617
+ end
1618
+
1619
+ H.set_buf_name = function(buf_id, name) vim.api.nvim_buf_set_name(buf_id, 'minideps://' .. buf_id .. '/' .. name) end
1620
+
1621
+ H.notify = vim.schedule_wrap(function(msg, level)
1622
+ level = level or 'INFO'
1623
+ if H.get_config().silent and level ~= 'ERROR' and level ~= 'WARN' then return end
1624
+ if type(msg) == 'table' then msg = table.concat(msg, '\n') end
1625
+ vim.notify(string.format('(mini.deps) %s', msg), vim.log.levels[level])
1626
+ vim.cmd('redraw')
1627
+ end)
1628
+
1629
+ H.edit = function(path, win_id)
1630
+ if type(path) ~= 'string' then return end
1631
+ local b = vim.api.nvim_win_get_buf(win_id or 0)
1632
+ local try_mimic_buf_reuse = (vim.fn.bufname(b) == '' and vim.bo[b].buftype ~= 'quickfix' and not vim.bo[b].modified)
1633
+ and (#vim.fn.win_findbuf(b) == 1 and vim.deep_equal(vim.fn.getbufline(b, 1, '$'), { '' }))
1634
+ local buf_id = vim.fn.bufadd(vim.fn.fnamemodify(path, ':.'))
1635
+ -- Showing in window also loads. Use `pcall` to not error with swap messages.
1636
+ pcall(vim.api.nvim_win_set_buf, win_id or 0, buf_id)
1637
+ vim.bo[buf_id].buflisted = true
1638
+ if try_mimic_buf_reuse then pcall(vim.api.nvim_buf_delete, b, { unload = false }) end
1639
+ return buf_id
1640
+ end
1641
+
1642
+ H.get_timestamp = function() return vim.fn.strftime('%Y-%m-%d %H:%M:%S') end
1643
+
1644
+ H.get_n_threads = function() return math.floor(0.8 * #(vim.loop.cpu_info() or {})) end
1645
+
1646
+ H.full_path = function(path) return (vim.fn.fnamemodify(path, ':p'):gsub('\\', '/'):gsub('/+', '/'):gsub('(.)/$', '%1')) end
1647
+
1648
+ H.readdir = function(path)
1649
+ if vim.fn.isdirectory(path) ~= 1 then return {} end
1650
+ return vim.tbl_map(function(x) return path .. '/' .. x end, vim.fn.readdir(path))
1651
+ end
1652
+
1653
+ H.source = function(path)
1654
+ pcall(function() vim.cmd('source ' .. vim.fn.fnameescape(path)) end)
1655
+ end
1656
+
1657
+ -- TODO: Remove after compatibility with Neovim=0.9 is dropped
1658
+ H.islist = vim.fn.has('nvim-0.10') == 1 and vim.islist or vim.tbl_islist
1659
+
1660
+ return MiniDeps