Compare commits

...

401 commits

Author SHA1 Message Date
github-actions[bot]
6c3bda4aca
chore(main): release 11.17.1 (#1927)
🤖 I have created a release *beep* *boop*
---


##
[11.17.1](https://github.com/folke/lazy.nvim/compare/v11.17.0...v11.17.1)
(2025-02-25)


### Bug Fixes

* **bootstrap:** support for older Neovim versions
([1c9ba37](1c9ba37045))
* **meta:** rebuild dirty right after disable. See
[#1889](https://github.com/folke/lazy.nvim/issues/1889)
([d51cf69](d51cf69783))

---
This PR was generated with [Release
Please](https://github.com/googleapis/release-please). See
[documentation](https://github.com/googleapis/release-please#release-please).

Co-authored-by: github-actions[bot] <41898282+github-actions[bot]@users.noreply.github.com>
2025-02-25 20:19:57 +01:00
Folke Lemaitre
d51cf69783
fix(meta): rebuild dirty right after disable. See #1889 2025-02-25 20:18:25 +01:00
Folke Lemaitre
1c9ba37045
fix(bootstrap): support for older Neovim versions 2025-02-25 20:02:32 +01:00
github-actions[bot]
96a205c8ce
chore(main): release 11.17.0 (#1877)
🤖 I have created a release *beep* *boop*
---


##
[11.17.0](https://github.com/folke/lazy.nvim/compare/v11.16.2...v11.17.0)
(2025-02-24)


### Features

* **config,render:** allow customizing the debug icon
([#1863](https://github.com/folke/lazy.nvim/issues/1863))
([a9c660d](a9c660d6ef))
* **util:** pass lang to `vim.notify` so that snacks notifier can render
the ft. Closes [#1919](https://github.com/folke/lazy.nvim/issues/1919)
([c6a57a3](c6a57a3534))


### Bug Fixes

* **config:** add missing space on the default debug icon
([#1879](https://github.com/folke/lazy.nvim/issues/1879))
([4df5c4d](4df5c4d65a))
* **meta:** disable top-level specs before the rest. Closes
[#1889](https://github.com/folke/lazy.nvim/issues/1889)
([f81a3fb](f81a3fb7fe))
* **ui:** do not show virt_lines for messages
([#1904](https://github.com/folke/lazy.nvim/issues/1904))
([f15a939](f15a93907d))

---
This PR was generated with [Release
Please](https://github.com/googleapis/release-please). See
[documentation](https://github.com/googleapis/release-please#release-please).

Co-authored-by: github-actions[bot] <41898282+github-actions[bot]@users.noreply.github.com>
2025-02-25 19:14:41 +01:00
Salomon Popp
a8c6db5da7
style(types): add optional return value for LazyKeysSpec rhs (#1924)
## Description

<!-- Describe the big picture of your changes to communicate to the
maintainers
  why we should accept this pull request. -->

when `expr=true` the rhs function should return a string.

example

```lua
{
    keys = {
        {
            '<leader>j',
            function()
                return require('dial.map').inc_normal()
            end,
            expr = true,
            desc = 'Increment value',
        }
    }
}
```

## Related Issue(s)

<!--
  If this PR fixes any issues, please link to the issue here.
  - Fixes #<issue_number>
-->

## Screenshots

<!-- Add screenshots of the changes if applicable. -->
2025-02-24 07:20:35 +01:00
Folke Lemaitre
e5e9bf4821
test: fix tests 2025-02-15 23:09:58 +01:00
Folke Lemaitre
f81a3fb7fe
fix(meta): disable top-level specs before the rest. Closes #1889 2025-02-15 23:06:09 +01:00
github-actions[bot]
ac21a639c7 chore(build): auto-generate rockspec mappings 2025-02-15 07:25:07 +00:00
Folke Lemaitre
c6a57a3534
feat(util): pass lang to vim.notify so that snacks notifier can render the ft. Closes #1919 2025-02-15 08:19:49 +01:00
JINNOUCHI Yasushi
f15a93907d
fix(ui): do not show virt_lines for messages (#1904)
## Description

<!-- Describe the big picture of your changes to communicate to the
maintainers
  why we should accept this pull request. -->

https://github.com/neovim/neovim/pull/31959 has introduced virtual lines
for showing diagnostics. If this is enabled (default value), messages
from lazy.nvim, such as `update available` are shown as virtual lines in
addition to virtual texts.

## Related Issue(s)

<!--
  If this PR fixes any issues, please link to the issue here.
  - Fixes #<issue_number>
-->

## Screenshots

<!-- Add screenshots of the changes if applicable. -->

* ***before***
- <img width="592" alt="スクリーンショット 2025-01-27 16 42 27"
src="https://github.com/user-attachments/assets/6994ac84-4862-4532-81ee-80f4015181e5"
/>
* ***after***
- <img width="585" alt="スクリーンショット 2025-01-27 16 42 58"
src="https://github.com/user-attachments/assets/d19f63dc-fb6d-434f-89cf-a0bc49d31c5f"
/>
2025-02-06 07:53:12 +01:00
github-actions[bot]
7527af40dd chore(build): auto-generate rockspec mappings 2025-01-30 19:19:03 +00:00
Folke Lemaitre
5586fda88d
ci: remove debug 2025-01-30 20:14:57 +01:00
Folke Lemaitre
4f30c61b64
ci: check 2025-01-30 20:13:53 +01:00
github-actions[bot]
d8f26efd45 chore(build): auto-generate docs 2025-01-06 20:14:50 +00:00
Eduardo Bray
4df5c4d65a
fix(config): add missing space on the default debug icon (#1879)
## Description

Adds the missing space from a9c660d

## Screenshots

Current:


![imagen](https://github.com/user-attachments/assets/9a3a1a0c-43ad-49f3-8b39-b3250f53ec40)

After:


![imagen](https://github.com/user-attachments/assets/3b3d4dfd-3c03-4db9-8f61-d2bd4f9ed22d)
2025-01-06 21:14:06 +01:00
github-actions[bot]
72aa3a2624 chore(build): auto-generate docs 2025-01-05 05:41:31 +00:00
Shihua Zeng
a9c660d6ef
feat(config,render): allow customizing the debug icon (#1863)
## Description

lazy.nvim allows users to configure all icons except for the debug icon.
This PR enables user to configure the debug icon with `ui.icons.debug`

## Screenshots

Before:


![image](https://github.com/user-attachments/assets/42b02fd9-58e6-4ebc-a1a7-c5e91f07a11a)

After (with config `{ ui = { icons = { debug = ' ' } } }`):


![image](https://github.com/user-attachments/assets/3ade5392-a988-4a10-86fc-f52b41a690c5)
2025-01-05 06:40:44 +01:00
github-actions[bot]
7e6c863bc7
chore(main): release 11.16.2 (#1854)
🤖 I have created a release *beep* *boop*
---


##
[11.16.2](https://github.com/folke/lazy.nvim/compare/v11.16.1...v11.16.2)
(2024-12-13)


### Bug Fixes

* **meta:** when a plugin is both optional and disabled, then just
delete it from the list
([805b85c](805b85c2ea))

---
This PR was generated with [Release
Please](https://github.com/googleapis/release-please). See
[documentation](https://github.com/googleapis/release-please#release-please).

Co-authored-by: github-actions[bot] <41898282+github-actions[bot]@users.noreply.github.com>
2024-12-13 19:58:17 +01:00
Folke Lemaitre
805b85c2ea
fix(meta): when a plugin is both optional and disabled, then just delete it from the list 2024-12-13 19:56:52 +01:00
github-actions[bot]
7c493713bc
chore(main): release 11.16.1 (#1844)
🤖 I have created a release *beep* *boop*
---


##
[11.16.1](https://github.com/folke/lazy.nvim/compare/v11.16.0...v11.16.1)
(2024-12-09)


### Bug Fixes

* **types:** ensure all fields for `LazyPluginSpec` are optional
([#1843](https://github.com/folke/lazy.nvim/issues/1843))
([703be1d](703be1dda3)),
closes [#1842](https://github.com/folke/lazy.nvim/issues/1842)

---
This PR was generated with [Release
Please](https://github.com/googleapis/release-please). See
[documentation](https://github.com/googleapis/release-please#release-please).

Co-authored-by: github-actions[bot] <41898282+github-actions[bot]@users.noreply.github.com>
2024-12-10 20:12:21 +01:00
github-actions[bot]
b97ee167f5 chore(build): auto-generate docs 2024-12-10 05:18:15 +00:00
Daemon
703be1dda3
fix(types): ensure all fields for LazyPluginSpec are optional (#1843)
> After updating lua_ls to
[v3.13.3](https://github.com/LuaLS/lua-language-server/releases/tag/3.13.3)
noticed my plugin scripts using `@type LazyPluginSpec` now have
`missing-fields` warnings.
It seems they have changed how `missing-fields` diagnostics work with
inherited types:
7b2d58537f.

Duplicate offending fields as optional in type `LazyPluginSpec`

Closes: #1842
2024-12-09 22:40:21 +01:00
github-actions[bot]
014d1d6d78
chore(main): release 11.16.0 (#1838)
🤖 I have created a release *beep* *boop*
---


##
[11.16.0](https://github.com/folke/lazy.nvim/compare/v11.15.0...v11.16.0)
(2024-12-07)


### Features

* **plugin:** added support for virtual plugins. Closes
[#1836](https://github.com/folke/lazy.nvim/issues/1836)
([ee64abc](ee64abc76b))


### Bug Fixes

* **plugin:** don't check if dir exists for virtual plugins
([656cf43](656cf43093))
* **render:** show correct key for home. Fixes
[#1796](https://github.com/folke/lazy.nvim/issues/1796)
([b08dba8](b08dba8107))

---
This PR was generated with [Release
Please](https://github.com/googleapis/release-please). See
[documentation](https://github.com/googleapis/release-please#release-please).

Co-authored-by: github-actions[bot] <41898282+github-actions[bot]@users.noreply.github.com>
2024-12-07 21:44:18 +01:00
Folke Lemaitre
656cf43093
fix(plugin): don't check if dir exists for virtual plugins 2024-12-07 11:52:43 +01:00
Folke Lemaitre
b08dba8107
fix(render): show correct key for home. Fixes #1796 2024-12-07 07:57:03 +01:00
Folke Lemaitre
ee64abc76b
feat(plugin): added support for virtual plugins. Closes #1836 2024-12-06 20:28:53 +01:00
github-actions[bot]
3388a26417 chore(build): auto-generate rockspec mappings 2024-12-05 08:11:36 +00:00
github-actions[bot]
a44e9cd165
chore(main): release 11.15.0 (#1835)
🤖 I have created a release *beep* *boop*
---


##
[11.15.0](https://github.com/folke/lazy.nvim/compare/v11.14.2...v11.15.0)
(2024-12-05)


### Features

* **plugin:** show error for local plugins that don't exist. Fixes
[#1773](https://github.com/folke/lazy.nvim/issues/1773)
([9570a5a](9570a5ae7b))

---
This PR was generated with [Release
Please](https://github.com/googleapis/release-please). See
[documentation](https://github.com/googleapis/release-please#release-please).

Co-authored-by: github-actions[bot] <41898282+github-actions[bot]@users.noreply.github.com>
2024-12-05 09:10:16 +01:00
Folke Lemaitre
9570a5ae7b
feat(plugin): show error for local plugins that don't exist. Fixes #1773 2024-12-05 09:06:28 +01:00
github-actions[bot]
56ead98e05 chore(build): auto-generate rockspec mappings 2024-11-20 08:28:09 +00:00
Folke Lemaitre
7d0fe7615a
ci: docgen fixes 2024-11-20 09:24:28 +01:00
github-actions[bot]
8e11d208d6 chore(build): auto-generate docs 2024-11-20 08:13:59 +00:00
Folke Lemaitre
25749704e4
docs: docgen 2024-11-20 09:11:45 +01:00
Folke Lemaitre
e41dffcbaf
docs: fix hl_group docgen 2024-11-20 09:08:44 +01:00
github-actions[bot]
7967abe557
chore(main): release 11.14.2 (#1730)
🤖 I have created a release *beep* *boop*
---


##
[11.14.2](https://github.com/folke/lazy.nvim/compare/v11.14.1...v11.14.2)
(2024-11-10)


### Bug Fixes

* **bootstrap:** single forward slash. Fixes
[#1747](https://github.com/folke/lazy.nvim/issues/1747)
([aca30f6](aca30f6361))
* **completion:** check if command string is a prefix of Lazy
([#1760](https://github.com/folke/lazy.nvim/issues/1760))
([e9fd76e](e9fd76e239)),
closes [#1758](https://github.com/folke/lazy.nvim/issues/1758)
* **docs:** always update helptags for local plugins
([60cf258](60cf258a9a))
* **luarocks:** try to install from root manifest
([#1687](https://github.com/folke/lazy.nvim/issues/1687))
([591ef40](591ef40f2d))
* **rocks:** add lib64 plugin directory to package.cpath
([#1717](https://github.com/folke/lazy.nvim/issues/1717))
([80da254](80da254e64))
* **rockspec:** allow binary lua files. Fixes
[#1800](https://github.com/folke/lazy.nvim/issues/1800)
([408449a](408449a59a))

---
This PR was generated with [Release
Please](https://github.com/googleapis/release-please). See
[documentation](https://github.com/googleapis/release-please#release-please).

Co-authored-by: github-actions[bot] <41898282+github-actions[bot]@users.noreply.github.com>
2024-11-11 11:07:59 +01:00
Folke Lemaitre
60cf258a9a
fix(docs): always update helptags for local plugins 2024-11-10 07:28:51 +01:00
github-actions[bot]
b1134ab82e chore(build): auto-generate rockspec mappings 2024-11-04 10:49:21 +00:00
Folke Lemaitre
408449a59a
fix(rockspec): allow binary lua files. Fixes #1800 2024-11-04 11:46:48 +01:00
github-actions[bot]
cf8ecc2c5e chore(build): auto-generate rockspec mappings 2024-10-22 10:47:57 +00:00
Lorenzo Zabot
40dab7450e
style(typos): correct a few typos (#1776)
## Description

<!-- Describe the big picture of your changes to communicate to the
maintainers
  why we should accept this pull request. -->
This PR just fixes a few typos :)

`dont => don't`
## Related Issue(s)

<!--
  If this PR fixes any issues, please link to the issue here.
  - Fixes #<issue_number>
-->

## Screenshots

<!-- Add screenshots of the changes if applicable. -->
2024-10-22 12:43:33 +02:00
github-actions[bot]
1159bdccd8 chore(build): auto-generate rockspec mappings 2024-10-02 07:54:44 +00:00
Luna Saphie Mittelbach
e9fd76e239
fix(completion): check if command string is a prefix of Lazy (#1760)
Problem: Command completion doesn't work if the command name isn't
written in full

Solution: Use vim.startswith to check if the command is a prefix of
'Lazy'

Fixes #1758
2024-10-02 09:52:51 +02:00
github-actions[bot]
460e1cd8f2 chore(build): auto-generate rockspec mappings 2024-09-16 08:17:41 +00:00
Folke Lemaitre
aca30f6361
fix(bootstrap): single forward slash. Fixes #1747 2024-09-16 10:13:11 +02:00
github-actions[bot]
48b52b5cfc chore(build): auto-generate rockspec mappings 2024-08-31 07:03:18 +00:00
Marc Jakobi
591ef40f2d
fix(luarocks): try to install from root manifest (#1687)
## Description

When passing the `--dev` flag to `luarocks`, it will prioritise `dev`
versions when resolving dependencies (treating `dev` or `scm` as greater
than a SemVer version) if the rockspec doesn't specify an upper version
constraint (which is often the case).

Dev packages are often unstable and may cause more problems, especially
for Windows users (an example I've seen is git for windows trying and
failing to checkout submodules).

For now , a good compromise between too many retries and not retrying at
all could be to try `luarocks install` from the root manifest first, but
to keep the `--dev` flag in `luarocks make`.

If that still causes problems, it might be better to fall back to
`luarocks make` without `--dev` first, and then to try `luarocks ---dev
make` as a last resort.
In rocks.nvim, we only fall back to adding the `--dev` flag if the
install error message contains the string `"No results matching query
were found"`; assuming that stable non-dev packages shouldn't depend on
dev packages.
2024-08-31 08:59:45 +02:00
Roger Kim
80da254e64
fix(rocks): add lib64 plugin directory to package.cpath (#1717)
## Description
`package.cpath` is missing the `lib64` directory for plugins that have
luarocks dependencies.

## Context
I found this issue when I was working on my new Neovim plugin on my
Fedora 39 machine. I added the `luasockets` dependency to rockspec file
in my plugin like so:

```
rockspec_format = "3.0"
package = "typeracer.nvim"
version = "scm-1"
source = {
	url = "git+https://github.com/carbon-steel/typeracer.nvim",
}
dependencies = {
	"luasocket",
}
test_dependencies = {
	"nlua",
}
build = {
	type = "builtin",
	copy_directories = {},
}

```

I found that the dynamic libraries from the `luasockets` dependency were
installed like so:
`/home/username/.local/share/nvim/lazy-rocks/typeracer.nvim/lib64/lua/5.1/socket/core.so`.
However, the only entry related to my plugin `typeracer.nvim` was:
`/home/glyph/.local/share/nvim/lazy-rocks/typeracer.nvim/lib/lua/5.1/?.so`.
The issue is that we only have the plugin's `lib` directory in
`package.cpath` and not `lib64`.

I looked through `lazy.nvim`'s code and I think adding the `lib64`
directory should fix the issue. I don't know if we also want to worry
about `lib32` as well, but so far, this change works for me.
2024-08-31 08:58:43 +02:00
Christoph Zirkelbach
014a72b7a8
docs: update dev.path description (#1711)
## Description

In the issue (#1707) I was confused by the description of `dev.path`. I
thought functions must also return the general directory for local
plugins, but it must be the plugin directory.

## Related Issue(s)

#1707
2024-08-31 08:57:58 +02:00
github-actions[bot]
077102c5bf
chore(main): release 11.14.1 (#1680)
🤖 I have created a release *beep* *boop*
---


##
[11.14.1](https://github.com/folke/lazy.nvim/compare/v11.14.0...v11.14.1)
(2024-07-25)


### Bug Fixes

* **plugins:** "Vim:E150: Not a directory" on plugin update
([#1679](https://github.com/folke/lazy.nvim/issues/1679))
([7108809](7108809ab1))

---
This PR was generated with [Release
Please](https://github.com/googleapis/release-please). See
[documentation](https://github.com/googleapis/release-please#release-please).

Co-authored-by: github-actions[bot] <41898282+github-actions[bot]@users.noreply.github.com>
2024-07-25 13:38:23 +02:00
Alexander Grebennik
7108809ab1
fix(plugins): "Vim:E150: Not a directory" on plugin update (#1679)
## Description

On plugins update it fails with following error for any plugin.

```
~/.local/share/nvim/lazy/lazy.nvim/manage/task/plugin.lua:95: Vim:E150: Not a directory: ~/.local/share/nvim/lazy/gitsigns.nvim/doc/
```
2024-07-25 12:53:01 +02:00
github-actions[bot]
4496b4cad6
chore(main): release 11.14.0 (#1673)
🤖 I have created a release *beep* *boop*
---


##
[11.14.0](https://github.com/folke/lazy.nvim/compare/v11.13.5...v11.14.0)
(2024-07-24)


### Features

* added `opts.git.cooldown` to allow updating plugins on slow
connections. Fixes
[#1656](https://github.com/folke/lazy.nvim/issues/1656)
([d5686ef](d5686efbd0))
* **plugin:** improve error handling and show better error message
([c02268a](c02268ac6e))


### Bug Fixes

* **plugin:** make .lazy.lua work again
([b4a5a12](b4a5a1209e))

---
This PR was generated with [Release
Please](https://github.com/googleapis/release-please). See
[documentation](https://github.com/googleapis/release-please#release-please).

Co-authored-by: github-actions[bot] <41898282+github-actions[bot]@users.noreply.github.com>
2024-07-24 07:38:49 +02:00
Folke Lemaitre
b4a5a1209e
fix(plugin): make .lazy.lua work again 2024-07-24 07:23:36 +02:00
Folke Lemaitre
c02268ac6e
feat(plugin): improve error handling and show better error message 2024-07-23 17:24:33 +02:00
Folke Lemaitre
d5686efbd0
feat: added opts.git.cooldown to allow updating plugins on slow connections. Fixes #1656 2024-07-23 13:31:25 +02:00
github-actions[bot]
839f9e78e7
chore(main): release 11.13.5 (#1672)
🤖 I have created a release *beep* *boop*
---


##
[11.13.5](https://github.com/folke/lazy.nvim/compare/v11.13.4...v11.13.5)
(2024-07-22)


### Bug Fixes

* **health:** dont use vim.fn.system to get cmd versions
([7d29719](7d29719ade))

---
This PR was generated with [Release
Please](https://github.com/googleapis/release-please). See
[documentation](https://github.com/googleapis/release-please#release-please).

Co-authored-by: github-actions[bot] <41898282+github-actions[bot]@users.noreply.github.com>
2024-07-22 14:41:58 +02:00
Folke Lemaitre
7d29719ade
fix(health): dont use vim.fn.system to get cmd versions 2024-07-22 14:38:00 +02:00
Folke Lemaitre
cc028e77eb
ci: update 2024-07-22 14:24:40 +02:00
github-actions[bot]
16a5c46aa3
chore(main): release 11.13.4 (#1670)
🤖 I have created a release *beep* *boop*
---


##
[11.13.4](https://github.com/folke/lazy.nvim/compare/v11.13.3...v11.13.4)
(2024-07-22)


### Bug Fixes

* **loader:** add plugins whose rtp got loaded early to start plugins
([34b0126](34b0126e5b))
* **loader:** explicitely set package.loaded.modname to nil to prevent
recursive loading errors
([12f2c74](12f2c74244))

---
This PR was generated with [Release
Please](https://github.com/googleapis/release-please). See
[documentation](https://github.com/googleapis/release-please#release-please).

Co-authored-by: github-actions[bot] <41898282+github-actions[bot]@users.noreply.github.com>
2024-07-22 09:47:04 +02:00
Folke Lemaitre
12f2c74244
fix(loader): explicitely set package.loaded.modname to nil to prevent recursive loading errors 2024-07-22 09:45:03 +02:00
Folke Lemaitre
34b0126e5b
fix(loader): add plugins whose rtp got loaded early to start plugins 2024-07-22 09:45:03 +02:00
github-actions[bot]
a09c876f6e
chore(main): release 11.13.3 (#1669)
🤖 I have created a release *beep* *boop*
---


##
[11.13.3](https://github.com/folke/lazy.nvim/compare/v11.13.2...v11.13.3)
(2024-07-21)


### Reverts

* fix(loader): add auto loaded module to package.loaded early to prevent
require loops
([a692bf8](a692bf8688))

---
This PR was generated with [Release
Please](https://github.com/googleapis/release-please). See
[documentation](https://github.com/googleapis/release-please#release-please).

Co-authored-by: github-actions[bot] <41898282+github-actions[bot]@users.noreply.github.com>
2024-07-21 17:34:22 +02:00
Folke Lemaitre
a692bf8688
revert: fix(loader): add auto loaded module to package.loaded early to prevent require loops
This reverts commit 18d1c1b47e.
2024-07-21 17:32:50 +02:00
github-actions[bot]
8bef0742a2
chore(main): release 11.13.2 (#1668)
🤖 I have created a release *beep* *boop*
---


##
[11.13.2](https://github.com/folke/lazy.nvim/compare/v11.13.1...v11.13.2)
(2024-07-21)


### Bug Fixes

* **loader:** add auto loaded module to package.loaded early to prevent
require loops
([18d1c1b](18d1c1b47e))

---
This PR was generated with [Release
Please](https://github.com/googleapis/release-please). See
[documentation](https://github.com/googleapis/release-please#release-please).

Co-authored-by: github-actions[bot] <41898282+github-actions[bot]@users.noreply.github.com>
2024-07-21 15:53:27 +02:00
Folke Lemaitre
18d1c1b47e
fix(loader): add auto loaded module to package.loaded early to prevent require loops 2024-07-21 12:41:54 +02:00
github-actions[bot]
9a374a0fb4
chore(main): release 11.13.1 (#1664)
🤖 I have created a release *beep* *boop*
---


##
[11.13.1](https://github.com/folke/lazy.nvim/compare/v11.13.0...v11.13.1)
(2024-07-19)


### Bug Fixes

* **build:** only load the plugin before build for `:` build commands
([5bdb12a](5bdb12a038))

---
This PR was generated with [Release
Please](https://github.com/googleapis/release-please). See
[documentation](https://github.com/googleapis/release-please#release-please).

Co-authored-by: github-actions[bot] <41898282+github-actions[bot]@users.noreply.github.com>
2024-07-19 11:50:14 +02:00
Folke Lemaitre
5bdb12a038
fix(build): only load the plugin before build for : build commands 2024-07-19 08:57:37 +02:00
github-actions[bot]
c92c6b5fd2 chore(build): auto-generate docs 2024-07-18 13:57:58 +00:00
Gert Burger
124b864233
docs(commands): fix command ordering for sync (#1661)
## Description

After a discussion on Slack we noticed that the description of the sync
command is a bit vague. Some people, including myself, assumed
`clean`/`install`/`update` referred to build steps being performed per
plugin.

Another person mentioned they thought it referred to the Lazy commands,
which does make more sense.
They also noticed that the order of the commands do not match the source
code.

So this PR corrects the order, assuming it was meant to be ordered, and
mentions that those are commands for clarity.
2024-07-18 15:57:12 +02:00
github-actions[bot]
8f62257511
chore(main): release 11.13.0 (#1660)
🤖 I have created a release *beep* *boop*
---


##
[11.13.0](https://github.com/folke/lazy.nvim/compare/v11.12.0...v11.13.0)
(2024-07-17)


### Features

* **ui:** added mapping descriptions
([6ca90a2](6ca90a2120))

---
This PR was generated with [Release
Please](https://github.com/googleapis/release-please). See
[documentation](https://github.com/googleapis/release-please#release-please).

Co-authored-by: github-actions[bot] <41898282+github-actions[bot]@users.noreply.github.com>
2024-07-18 10:47:30 +02:00
Folke Lemaitre
6ca90a2120
feat(ui): added mapping descriptions 2024-07-18 00:40:42 +02:00
github-actions[bot]
5473e3d77c
chore(main): release 11.12.0 (#1655)
🤖 I have created a release *beep* *boop*
---


##
[11.12.0](https://github.com/folke/lazy.nvim/compare/v11.11.1...v11.12.0)
(2024-07-16)


### Features

* **git:** added git network throttle to limit network related git ops
per interval. Closes
[#1635](https://github.com/folke/lazy.nvim/issues/1635)
([d731a6b](d731a6b005))

---
This PR was generated with [Release
Please](https://github.com/googleapis/release-please). See
[documentation](https://github.com/googleapis/release-please#release-please).

Co-authored-by: github-actions[bot] <41898282+github-actions[bot]@users.noreply.github.com>
2024-07-17 12:54:53 +02:00
Folke Lemaitre
d731a6b005
feat(git): added git network throttle to limit network related git ops per interval. Closes #1635 2024-07-16 16:50:31 +02:00
github-actions[bot]
9d445ebbd8
chore(update): update repository (#1653)
Automated changes by
[create-pull-request](https://github.com/peter-evans/create-pull-request)
GitHub action

Co-authored-by: folke <292349+folke@users.noreply.github.com>
2024-07-15 17:09:32 +02:00
github-actions[bot]
315191aa9e
chore(update): update repository (#1651)
Automated changes by
[create-pull-request](https://github.com/peter-evans/create-pull-request)
GitHub action

Co-authored-by: folke <292349+folke@users.noreply.github.com>
2024-07-15 11:19:51 +02:00
github-actions[bot]
b02c9eae6a
chore(main): release 11.11.1 (#1643)
🤖 I have created a release *beep* *boop*
---


##
[11.11.1](https://github.com/folke/lazy.nvim/compare/v11.11.0...v11.11.1)
(2024-07-13)


### Bug Fixes

* **config:** check for lib64. Fixes
[#1343](https://github.com/folke/lazy.nvim/issues/1343)
([93499c5](93499c5deb))
* **lockfile:** ensure newline at EOF for lockfile
([#1639](https://github.com/folke/lazy.nvim/issues/1639))
([7ed9f71](7ed9f7173c))

---
This PR was generated with [Release
Please](https://github.com/googleapis/release-please). See
[documentation](https://github.com/googleapis/release-please#release-please).

Co-authored-by: github-actions[bot] <41898282+github-actions[bot]@users.noreply.github.com>
2024-07-13 22:50:19 +02:00
github-actions[bot]
6e66f8e655
chore(update): update repository (#1648)
Automated changes by
[create-pull-request](https://github.com/peter-evans/create-pull-request)
GitHub action

Co-authored-by: folke <292349+folke@users.noreply.github.com>
2024-07-13 19:29:13 +02:00
Folke Lemaitre
93499c5deb
fix(config): check for lib64. Fixes #1343 2024-07-13 18:07:56 +02:00
github-actions[bot]
788feaf10e
chore(update): update repository (#1644)
Automated changes by
[create-pull-request](https://github.com/peter-evans/create-pull-request)
GitHub action

Co-authored-by: folke <292349+folke@users.noreply.github.com>
2024-07-13 16:16:17 +02:00
github-actions[bot]
58c6bc4ab2
chore(update): update repository (#1638)
Automated changes by
[create-pull-request](https://github.com/peter-evans/create-pull-request)
GitHub action

Co-authored-by: folke <292349+folke@users.noreply.github.com>
2024-07-13 10:07:17 +02:00
Ethan Wu
7ed9f7173c
fix(lockfile): ensure newline at EOF for lockfile (#1639)
## Description

The lockfile currently does not end with a newline at EOF. Text files
should [end with a newline](https://unix.stackexchange.com/a/18789).
This also lets you manually edit the lockfile in vim without 'fixeol'
creating a spurious change for the added newline.

This change however will create a change in users' lockfiles adding a
newline upon updating, but since the lockfile would be changing anyways
to update lazy.nvim itself, this is likely acceptable.

## Related Issue(s)

*none*

## Screenshots

*N/A*
2024-07-13 09:51:44 +02:00
Folke Lemaitre
54f70c757c
ci: add luassert to minitest for now 2024-07-13 09:45:01 +02:00
github-actions[bot]
070418dca1
chore(main): release 11.11.0 (#1634)
🤖 I have created a release *beep* *boop*
---


##
[11.11.0](https://github.com/folke/lazy.nvim/compare/v11.10.4...v11.11.0)
(2024-07-11)


### Features

* add plugin name to handlers.managed
([17473db](17473db1d7))


### Bug Fixes

* **minit:** add tests to package.path when running busted (helpers.lua
etc)
([fadebdc](fadebdc76b))
* **util:** strip `-lua` in normname
([54b003c](54b003c650))

---
This PR was generated with [Release
Please](https://github.com/googleapis/release-please). See
[documentation](https://github.com/googleapis/release-please#release-please).

Co-authored-by: github-actions[bot] <41898282+github-actions[bot]@users.noreply.github.com>
2024-07-13 09:25:55 +02:00
Folke Lemaitre
1d451b4c2c
ci: use mini.test instead of busted 2024-07-11 22:32:11 +02:00
Folke Lemaitre
17473db1d7
feat: add plugin name to handlers.managed 2024-07-11 22:04:06 +02:00
Folke Lemaitre
54b003c650
fix(util): strip -lua in normname 2024-07-11 22:03:53 +02:00
github-actions[bot]
d1de92dffa chore(build): auto-generate docs 2024-07-09 15:26:28 +00:00
Folke Lemaitre
2cb8af1eb1
ci: skip docs on main 2024-07-09 17:25:41 +02:00
folke
159036c576 chore(build): auto-generate docs 2024-07-09 13:03:11 +00:00
Folke Lemaitre
fadebdc76b
fix(minit): add tests to package.path when running busted (helpers.lua etc) 2024-07-09 15:02:18 +02:00
github-actions[bot]
1870238cf9 chore(build): auto-generate docs 2024-07-08 05:53:40 +00:00
github-actions[bot]
f918318d21
chore(main): release 11.10.4 (#1628)
🤖 I have created a release *beep* *boop*
---


##
[11.10.4](https://github.com/folke/lazy.nvim/compare/v11.10.3...v11.10.4)
(2024-07-08)


### Bug Fixes

* **rocks:** try building anyway even when prerequisits have not been
met. (will likely fail)
([f0324de](f0324defdd))
* **ui:** don't treat suspended as headless. Closes
[#1626](https://github.com/folke/lazy.nvim/issues/1626)
([2dfccd7](2dfccd7b94))

---
This PR was generated with [Release
Please](https://github.com/googleapis/release-please). See
[documentation](https://github.com/googleapis/release-please#release-please).

Co-authored-by: github-actions[bot] <41898282+github-actions[bot]@users.noreply.github.com>
2024-07-08 07:52:50 +02:00
folke
a4c473cc2d chore(build): auto-generate docs 2024-07-08 05:46:33 +00:00
Folke Lemaitre
f0324defdd
fix(rocks): try building anyway even when prerequisits have not been met. (will likely fail) 2024-07-08 07:45:45 +02:00
github-actions[bot]
0002bfbd9f chore(build): auto-generate docs 2024-07-08 05:28:57 +00:00
Folke Lemaitre
2dfccd7b94
fix(ui): don't treat suspended as headless. Closes #1626 2024-07-08 07:28:02 +02:00
folke
44cd12fa27 chore(build): auto-generate docs 2024-07-07 19:26:10 +00:00
github-actions[bot]
a6daaf68a2
chore(main): release 11.10.3 (#1625)
🤖 I have created a release *beep* *boop*
---


##
[11.10.3](https://github.com/folke/lazy.nvim/compare/v11.10.2...v11.10.3)
(2024-07-07)


### Bug Fixes

* **git:** local plugin fixes
([#1624](https://github.com/folke/lazy.nvim/issues/1624))
([72c0dc9](72c0dc9462))

---
This PR was generated with [Release
Please](https://github.com/googleapis/release-please). See
[documentation](https://github.com/googleapis/release-please#release-please).

Co-authored-by: github-actions[bot] <41898282+github-actions[bot]@users.noreply.github.com>
2024-07-07 21:25:12 +02:00
github-actions[bot]
436d09af7d chore(build): auto-generate docs 2024-07-07 15:15:37 +00:00
github-actions[bot]
93c9a3f872 chore(build): auto-generate docs 2024-07-07 15:14:44 +00:00
Andre Toerien
72c0dc9462
fix(git): local plugin fixes (#1624)
## Description

As I described in
https://github.com/folke/lazy.nvim/pull/1512#issuecomment-2212474372,
this makes it so that local plugins will only show as needing updates if
the local branch is behind the upstream branch. This is done by checking
the output of the `git log` command, and only setting `plugin._.updates`
if the output is not empty.

This seems to solve my issue where local plugins with unpushed changes
always show as needing updates, but if there's a easier/better way of
doing it then please feel free to edit/close this. Or if you don't agree
that the current behaviour is a bug, then that's also fine - it's not a
big deal and I can easily just ignore the "updates available" notice.

I also came across a minor issue where the plugin diff view (press `d`)
compares the wrong commits for local plugins, because
[lua/lazy/view/init.lua](c771cf4928/lua/lazy/view/init.lua (L268))
always uses `get_target`. I fixed this by moving `get_local_target` into
`get_target` - I think this is simpler and more straightforward than the
alternative of adding a ternary everywhere `get_target` is called.

This second bugfix is a very small change, so I've just included it
here, but I'm happy to make a second PR if you'd like.

## Related Issue(s)

Related PR: #1512
2024-07-07 17:13:49 +02:00
folke
c771cf4928 chore(build): auto-generate docs 2024-07-07 06:46:59 +00:00
github-actions[bot]
25026d2382 chore(build): auto-generate docs 2024-07-07 06:45:25 +00:00
folke
083f3dfb5e chore(build): auto-generate docs 2024-07-07 06:45:23 +00:00
github-actions[bot]
89b264ac1d
chore(main): release 11.10.2 (#1621)
🤖 I have created a release *beep* *boop*
---


##
[11.10.2](https://github.com/folke/lazy.nvim/compare/v11.10.1...v11.10.2)
(2024-07-07)


### Bug Fixes

* **git:** only check for new commits for local plugins. Closes
[#1512](https://github.com/folke/lazy.nvim/issues/1512)
([81d2bff](81d2bfffdc))

---
This PR was generated with [Release
Please](https://github.com/googleapis/release-please). See
[documentation](https://github.com/googleapis/release-please#release-please).

Co-authored-by: github-actions[bot] <41898282+github-actions[bot]@users.noreply.github.com>
2024-07-07 08:44:37 +02:00
github-actions[bot]
23aeb224ed chore(build): auto-generate docs 2024-07-07 06:43:02 +00:00
Folke Lemaitre
81d2bfffdc
fix(git): only check for new commits for local plugins. Closes #1512 2024-07-07 08:42:21 +02:00
folke
933f0b596c chore(build): auto-generate docs 2024-07-06 21:46:26 +00:00
Folke Lemaitre
55b46b3993
ci: update 2024-07-06 23:45:30 +02:00
Folke Lemaitre
23ea80b6a3
ci: update 2024-07-06 23:19:48 +02:00
Folke Lemaitre
e6035dc59b
ci: update 2024-07-06 23:18:28 +02:00
github-actions[bot]
0ff7e83c17 chore(build): auto-generate docs 2024-07-06 16:05:52 +00:00
folke
1e7745a4a0 chore(build): auto-generate docs 2024-07-06 16:05:47 +00:00
github-actions[bot]
49a35d3c8c
chore(update): update repository (#1618)
Automated changes by
[create-pull-request](https://github.com/peter-evans/create-pull-request)
GitHub action

Co-authored-by: folke <292349+folke@users.noreply.github.com>
2024-07-06 18:05:10 +02:00
github-actions[bot]
16ccd54360 chore(build): auto-generate docs 2024-07-06 13:01:02 +00:00
github-actions[bot]
c060de160a
chore(update): update repository (#1616)
Automated changes by
[create-pull-request](https://github.com/peter-evans/create-pull-request)
GitHub action

Co-authored-by: folke <292349+folke@users.noreply.github.com>
2024-07-06 15:00:16 +02:00
folke
e3154ff0b7 chore(build): auto-generate docs 2024-07-06 09:48:26 +00:00
Folke Lemaitre
c3a9cec06b
ci: update 2024-07-06 11:45:24 +02:00
github-actions[bot]
d901d2166f chore(build): auto-generate docs 2024-07-05 17:03:38 +00:00
folke
94b6b67031 chore(build): auto-generate docs 2024-07-05 17:03:29 +00:00
Folke Lemaitre
538f060e42
ci: update 2024-07-05 19:02:48 +02:00
github-actions[bot]
894cd193e9 chore(build): auto-generate docs 2024-07-05 14:13:57 +00:00
folke
11e802dbaa chore(build): auto-generate docs 2024-07-05 14:13:53 +00:00
github-actions[bot]
6ca23c15f6
chore(main): release 11.10.1 (#1612)
Co-authored-by: github-actions[bot] <41898282+github-actions[bot]@users.noreply.github.com>
2024-07-05 16:13:11 +02:00
github-actions[bot]
40e08f2b8a chore(build): auto-generate docs 2024-07-05 14:05:18 +00:00
Folke Lemaitre
d0c00e697a
ci: remove tests dep 2024-07-05 16:04:29 +02:00
folke
61c7156b57 chore(build): auto-generate docs 2024-07-05 14:04:23 +00:00
Folke Lemaitre
6186b3de3e
ci: add generated files to .styluaignore 2024-07-05 16:03:09 +02:00
Folke Lemaitre
53661bb38c
ci: update 2024-07-05 16:03:09 +02:00
github-actions[bot]
a1d23e80ba chore(build): auto-generate docs 2024-07-05 07:31:34 +00:00
Folke Lemaitre
baac551777
fix(lockfile): keep cond=false and enabed=false in lockfile. Fixes #1535. Fixes #1606 2024-07-05 09:01:04 +02:00
github-actions[bot]
407e65c792
chore(main): release 11.10.0 (#1609)
Co-authored-by: github-actions[bot] <41898282+github-actions[bot]@users.noreply.github.com>
2024-07-04 21:01:57 +02:00
Folke Lemaitre
851b12034d
ci: use main for bootstrap 2024-07-04 18:05:36 +02:00
Folke Lemaitre
1225f1dc60
ci: dont enable local specs for minit 2024-07-04 18:00:59 +02:00
Folke Lemaitre
a17ad27435
build: better minit 2024-07-04 17:53:45 +02:00
Folke Lemaitre
923e1aa7a4
fix(plugin): local spec name 2024-07-04 16:16:39 +02:00
Folke Lemaitre
6fdd904ee4
fix(config): determine headless only during startup. Fixes #1608 2024-07-04 15:19:41 +02:00
Folke Lemaitre
0f2786bcc9
feat(profiling): merge VeryLazy stats and show startuptime in profile view 2024-07-04 15:17:10 +02:00
github-actions[bot]
cea5920abb
chore(main): release 11.9.2 (#1595)
Co-authored-by: github-actions[bot] <41898282+github-actions[bot]@users.noreply.github.com>
2024-07-03 12:11:21 +02:00
github-actions[bot]
36c85945ee chore(build): auto-generate docs 2024-07-03 08:20:37 +00:00
Folke Lemaitre
d0921f5b9b
fix(health): check for errors when executing commands. Closes #1599 2024-07-02 13:43:03 +02:00
Folke Lemaitre
a9d7ade203
perf(plugin): minor optim to resolve imports a bit faster 2024-07-01 07:07:49 +02:00
Folke Lemaitre
1fad61712b
fix(async): make asyncs abortable 2024-06-30 13:35:14 +02:00
github-actions[bot]
c882227f1f
chore(main): release 11.9.1 (#1592)
Co-authored-by: github-actions[bot] <41898282+github-actions[bot]@users.noreply.github.com>
2024-06-30 12:47:41 +02:00
Folke Lemaitre
2f4ac035bc
perf: suspend when tasks are active 2024-06-30 09:13:04 +02:00
Folke Lemaitre
c7ed87f9ca
perf: automatically suspend the scheduler when all threads are waiting (#1591)
* perf: automatically suspend the scheduler when all threads are waiting

* ci: fix ci

* test: cleanup
2024-06-30 08:48:03 +02:00
github-actions[bot]
0507e19289
chore(main): release 11.9.0 (#1587)
Co-authored-by: github-actions[bot] <41898282+github-actions[bot]@users.noreply.github.com>
2024-06-29 17:01:41 +02:00
Folke Lemaitre
5e3c112cb3
feat(ui): use [[ & ]] to navigate between plugins. Fixes #1463 2024-06-29 14:23:31 +02:00
Folke Lemaitre
3772914075
fix(ui): when closing details, jump to plugin header. Closes #1338 2024-06-29 14:23:31 +02:00
github-actions[bot]
5bddef2415
chore(main): release 11.8.2 (#1586)
Co-authored-by: github-actions[bot] <41898282+github-actions[bot]@users.noreply.github.com>
2024-06-29 13:59:37 +02:00
Folke Lemaitre
5d334b9f57
fix(ui): save/restore view right before/after rendering 2024-06-29 13:58:13 +02:00
Folke Lemaitre
a75d950b8f
fix(process): deal with process errors 2024-06-29 13:52:50 +02:00
Folke Lemaitre
332a7ff9b3
ci: auto-commit-action v5 2024-06-29 12:06:54 +02:00
Folke Lemaitre
440999fc5a
ci: fix 2024-06-29 12:03:42 +02:00
Folke Lemaitre
07ccb49ace
ci: set env for docs 2024-06-29 12:01:52 +02:00
Folke Lemaitre
09f69bae4b
ci: dispatch docs gen from main 2024-06-29 12:00:15 +02:00
Folke Lemaitre
9ab3061690
perf(rocks): vim.fn.executable is slow on WSL2, so only check for luarocks when needed. Closes #1585 2024-06-29 11:44:31 +02:00
github-actions[bot]
8dd947fccd
chore(main): release 11.8.1 (#1584)
Co-authored-by: github-actions[bot] <41898282+github-actions[bot]@users.noreply.github.com>
2024-06-29 10:37:55 +02:00
Folke Lemaitre
3513227a9a
fix(async): remove debug assert 2024-06-29 10:36:37 +02:00
github-actions[bot]
00c23e72a3
chore(main): release 11.8.0 (#1583)
Co-authored-by: github-actions[bot] <41898282+github-actions[bot]@users.noreply.github.com>
2024-06-29 10:20:23 +02:00
Folke Lemaitre
695a05872a
feat(plugin): allow loading specs without pkg 2024-06-29 10:18:33 +02:00
github-actions[bot]
7af8a317e2 chore(build): auto-generate docs 2024-06-29 06:29:46 +00:00
github-actions[bot]
cece2a9b4a
chore(main): release 11.7.0 (#1582)
Co-authored-by: github-actions[bot] <41898282+github-actions[bot]@users.noreply.github.com>
2024-06-29 08:13:24 +02:00
Folke Lemaitre
88f4d13e5f
feat(minit): fallback to habamax when no colorscheme set 2024-06-29 08:11:42 +02:00
Folke Lemaitre
3078688263
ci(minit): LAZY_STDPATH 2024-06-29 08:03:37 +02:00
Folke Lemaitre
cdfea60506
build(bootstrap): added support for custom lazypath 2024-06-29 08:03:06 +02:00
Folke Lemaitre
0e106c085c
ci(minit): added repro 2024-06-29 07:46:41 +02:00
Folke Lemaitre
b1821ca2fa
ci: tests using minit 2024-06-29 07:26:55 +02:00
Folke Lemaitre
f47ab692f1
ci: allow to run busted script with nvim -u to inspect env 2024-06-29 07:26:55 +02:00
github-actions[bot]
cfdfb786b1
chore(main): release 11.6.0 (#1579)
Co-authored-by: github-actions[bot] <41898282+github-actions[bot]@users.noreply.github.com>
2024-06-29 07:17:01 +02:00
Folke Lemaitre
c93eb359a3
ci: minit (minimal init) 2024-06-29 07:15:22 +02:00
Folke Lemaitre
ba1a9c5392
ci: bootstrap script 2024-06-29 07:13:37 +02:00
github-actions[bot]
a1fffe18f9 chore(build): auto-generate docs 2024-06-28 18:22:55 +00:00
Folke Lemaitre
9cf745939d
feat(task): build procs can now yield a LazyMsg for more control 2024-06-28 20:17:35 +02:00
github-actions[bot]
ec95702ae6
chore(main): release 11.5.2 (#1577)
Co-authored-by: github-actions[bot] <41898282+github-actions[bot]@users.noreply.github.com>
2024-06-28 18:33:22 +02:00
Folke Lemaitre
2a6a2dce1b
fix(git): tagrefs 2024-06-28 18:31:12 +02:00
github-actions[bot]
a617d9f47b
chore(main): release 11.5.1 (#1573)
Co-authored-by: github-actions[bot] <41898282+github-actions[bot]@users.noreply.github.com>
2024-06-28 17:47:04 +02:00
Folke Lemaitre
f85575ab23
perf: use timer instead of check for async executor 2024-06-28 17:44:43 +02:00
Folke Lemaitre
ab46edbd47
perf: async render 2024-06-28 17:44:21 +02:00
Folke Lemaitre
a36ebd2a75
refactor: async processes 2024-06-28 16:08:26 +02:00
Folke Lemaitre
4319846b8c
fix(rocks): lua-5.1. Closes #1575 2024-06-28 16:07:49 +02:00
Folke Lemaitre
60fe75c88d
fix(task): run on_exit async. See #1569 2024-06-28 00:35:41 +02:00
Folke Lemaitre
461552474c
refactor: cleanup 2024-06-28 00:35:40 +02:00
github-actions[bot]
37c7163f8d
chore(main): release 11.5.0 (#1565)
Co-authored-by: github-actions[bot] <41898282+github-actions[bot]@users.noreply.github.com>
2024-06-27 17:34:05 +02:00
Folke Lemaitre
68cee30cdb
perf: prevent active waiting in coroutines. suspend/resume instead 2024-06-27 13:06:39 +02:00
Folke Lemaitre
79bcc02d17
ci: make simple test script 2024-06-27 12:48:50 +02:00
Folke Lemaitre
e3e431480d
feat(ui): remap gx -> K. Fixes #1561 2024-06-27 12:38:47 +02:00
Folke Lemaitre
c0fd59b020
feat(health): show steps to get luarocks working. See #1570 2024-06-27 12:31:58 +02:00
Folke Lemaitre
82276321f5
fix(rocks): if installing with luarocks (binaries) fails, then build from source. Fixes #1563 2024-06-27 11:33:11 +02:00
Folke Lemaitre
e02c5b1b57
fix(runner): only check for errors when a task is no longer running 2024-06-27 11:32:30 +02:00
Folke Lemaitre
a0391c3e21
fix(manage): dont skip install for plugins that need a build, but dont have an url (like local plugins). Fixes #1563 2024-06-27 11:32:00 +02:00
Folke Lemaitre
53f314d9e6
feat(ui): show indication of plugins that need build. See #1563 2024-06-27 11:31:20 +02:00
Folke Lemaitre
2e1167df4a
feat: added localleader-i to inspect a plugin 2024-06-27 11:30:29 +02:00
Folke Lemaitre
9c8e7a4840
feat(health): show user's lazy.nvim version in checkhealth 2024-06-27 10:44:35 +02:00
Folke Lemaitre
6a423278a1
fix(meta): resolve deps from meta instead of fragments. Fixes #1566 2024-06-27 10:21:54 +02:00
Folke Lemaitre
24a86d5ca4
fix(pkg): only show pkg changed when effectively changing a pkg file. Fixes #1567 2024-06-27 09:48:37 +02:00
Folke Lemaitre
591ded8309
feat(ui): keep cursor position when rendering view 2024-06-26 22:45:21 +02:00
Folke Lemaitre
249902ab31
fix(ui): diagnostics without status 2024-06-26 22:44:57 +02:00
Folke Lemaitre
a0a51c06c2
feat: added opts.headless to control ansi output when running headless 2024-06-26 21:38:28 +02:00
Folke Lemaitre
93b3a77286
fix(runner): wait_step 2024-06-26 20:17:32 +02:00
Folke Lemaitre
66a4170f0e
fix(runner): properly do concurrency 2024-06-26 19:58:45 +02:00
Folke Lemaitre
97f4df0824
fix(runner): only use Config.plugins when updated. Fixes #1560 2024-06-26 19:39:28 +02:00
Folke Lemaitre
804cae0a65
refactor: hererocks check 2024-06-26 19:39:28 +02:00
github-actions[bot]
20af3fcc4e
chore(main): release 11.4.2 (#1558)
Co-authored-by: github-actions[bot] <41898282+github-actions[bot]@users.noreply.github.com>
2024-06-26 18:47:38 +02:00
Folke Lemaitre
56075b57c4
fix(runner): bring concurrency back 2024-06-26 18:45:40 +02:00
Folke Lemaitre
206d208018
test: fix tests 2024-06-26 18:42:52 +02:00
Folke Lemaitre
6c7ef7e27a
refactor: logging 2024-06-26 18:31:31 +02:00
Folke Lemaitre
765773a176
refactor: use new async code for runner and simplify task class 2024-06-26 17:06:56 +02:00
Folke Lemaitre
768de1ebf6
refactor: move scheduler to async 2024-06-26 15:11:31 +02:00
Folke Lemaitre
0eb46e7816
ci: use utfTerminal output for busted 2024-06-26 14:35:29 +02:00
Folke Lemaitre
9c1dd5a090
test: buse busted for running tasks 2024-06-26 14:30:55 +02:00
Folke Lemaitre
bbe136bda6
test: fix tests 2024-06-26 14:30:38 +02:00
Folke Lemaitre
0614ca6ca6
perf: tasks are now fully async 2024-06-26 14:28:53 +02:00
Folke Lemaitre
36952153ec
perf(util): improve impl of throttle 2024-06-26 14:28:22 +02:00
Folke Lemaitre
64fd346728
test: cleanup 2024-06-26 11:25:04 +02:00
Folke Lemaitre
707e2e923b
test: fix init test 2024-06-26 11:11:11 +02:00
Folke Lemaitre
90e14d1585
refactor(rocks): is_simple_build 2024-06-26 11:11:00 +02:00
Folke Lemaitre
2aa8e061f2
fix(config): dont start checker/change_detection when running headless 2024-06-26 11:10:43 +02:00
Folke Lemaitre
aa1c9572aa
fix(rocks): build.type instead of build.build_type 2024-06-26 08:50:06 +02:00
Folke Lemaitre
473361139c
fix(rockspec): dont lazy-load rock deps 2024-06-26 08:49:42 +02:00
Folke Lemaitre
d63e80bae9
test: remove debug output 2024-06-26 07:18:07 +02:00
Folke Lemaitre
e79805d706
fix(ui): don't show output when it's the same as error 2024-06-26 07:15:12 +02:00
Folke Lemaitre
28e435b7f3
fix(git): fetch commit from origin or local to check if branch was changed. See #1549 2024-06-26 07:15:12 +02:00
github-actions[bot]
6d60dc3c05 chore(build): auto-generate rockspec mappings 2024-06-26 04:22:43 +00:00
Folke Lemaitre
67e1e8e6a3
ci: added neo to rockspec patterns 2024-06-26 06:22:23 +02:00
github-actions[bot]
69041bccb7
chore(main): release 11.4.1 (#1555)
Co-authored-by: github-actions[bot] <41898282+github-actions[bot]@users.noreply.github.com>
2024-06-25 22:09:17 +02:00
Folke Lemaitre
3f7368c3ac
ci: use 5.1 manifest 2024-06-25 21:53:56 +02:00
Folke Lemaitre
0d9fd636be
fix(health): show what plugins need luarocks and if none, use warnings instead of errors. See #1551 2024-06-25 21:15:51 +02:00
github-actions[bot]
9ac375653b
chore(main): release 11.4.0 (#1554)
Co-authored-by: github-actions[bot] <41898282+github-actions[bot]@users.noreply.github.com>
2024-06-25 21:05:40 +02:00
Folke Lemaitre
6b8bf58ebf
feat(rocks): simple rockspecs are now fully resolved by lazy without luarocks. See #1548 2024-06-25 20:53:42 +02:00
Folke Lemaitre
be74a8a535
feat(pkg): utils to get rock to url mappings 2024-06-25 20:39:12 +02:00
Folke Lemaitre
25981e1f39
fix(meta): only tag new top-level pkg fragment as optional 2024-06-25 20:39:12 +02:00
github-actions[bot]
aff7ee8e89 chore(build): auto-generate rockspec mappings 2024-06-25 18:35:51 +00:00
Folke Lemaitre
aff65371fc
ci: add cmp to generated luarock mappings 2024-06-25 20:35:27 +02:00
github-actions[bot]
33be7ac317 chore(build): auto-generate rockspec mappings 2024-06-25 16:13:21 +00:00
Folke Lemaitre
7cda552c1c
ci: more rockspec patterns 2024-06-25 18:12:39 +02:00
Folke Lemaitre
49c0b86a6f
ci: move to _generated.lua 2024-06-25 18:07:28 +02:00
github-actions[bot]
786a3febc0 chore(build): auto-generate rockspec mappings 2024-06-25 15:48:19 +00:00
Folke Lemaitre
88911547e7
ci: fix some github urls 2024-06-25 17:48:04 +02:00
Folke Lemaitre
8abfed457c
test: fix tests 2024-06-25 17:42:48 +02:00
github-actions[bot]
bc7bfb5d36 chore(build): auto-generate rockspec mappings 2024-06-25 15:42:29 +00:00
Folke Lemaitre
b6eba0d026
ci: auto-get rockspec mappings for rock name => github short url or url 2024-06-25 17:41:16 +02:00
github-actions[bot]
2ca68f9979
chore(main): release 11.3.0 (#1543)
Co-authored-by: github-actions[bot] <41898282+github-actions[bot]@users.noreply.github.com>
2024-06-25 16:21:15 +02:00
Folke Lemaitre
24c832213c
fix(meta): no need to check for old_dir when frags were not built yet. Fixes #1550 2024-06-25 15:51:40 +02:00
Folke Lemaitre
4ca3e9aa51
fix(rocks): windows 2024-06-25 15:44:13 +02:00
Folke Lemaitre
7d3f69104f
fix(rocks): better errors / warnings when something goes wrong with luarocks 2024-06-25 13:23:25 +02:00
github-actions[bot]
9005e8ede7 chore(build): auto-generate docs 2024-06-25 06:48:06 +00:00
Folke Lemaitre
45cd8d3f0f
fix(rocks): hererocks paths on windows 2024-06-25 06:40:50 +02:00
github-actions[bot]
0a5839ceea chore(build): auto-generate docs 2024-06-25 05:58:24 +00:00
Folke Lemaitre
d87da76679
feat(rocks): use hererocks to install luarocks when luarocks is not found 2024-06-25 07:55:30 +02:00
Folke Lemaitre
dea1f687fe
fix(fragments): check for empty plugin names 2024-06-25 00:31:34 +02:00
github-actions[bot]
378bfb4656
chore(main): release 11.2.1 (#1542)
Co-authored-by: github-actions[bot] <41898282+github-actions[bot]@users.noreply.github.com>
2024-06-24 23:44:20 +02:00
Folke Lemaitre
62a47b921f
fix(loader): no need to check plugin.dir in auto_load 2024-06-24 23:42:53 +02:00
github-actions[bot]
9bcbbc17a7
chore(main): release 11.2.0 (#1541)
Co-authored-by: github-actions[bot] <41898282+github-actions[bot]@users.noreply.github.com>
2024-06-24 21:59:16 +02:00
Folke Lemaitre
a089d43dba
feat: rewrite some known plugins to lazy specs instead of luarocks (plenary.nvim). Closes #1540 2024-06-24 21:57:34 +02:00
Folke Lemaitre
1446f6cfbb
perf: minimize meta rebuild when loading specs 2024-06-24 21:56:43 +02:00
github-actions[bot]
972baa615b chore(build): auto-generate docs 2024-06-24 18:23:56 +00:00
Folke Lemaitre
0081c95aee
style: allow false for build 2024-06-24 20:23:03 +02:00
github-actions[bot]
b6b0c4c15c
chore(main): release 11.1.0 (#1539)
Co-authored-by: github-actions[bot] <41898282+github-actions[bot]@users.noreply.github.com>
2024-06-24 20:06:31 +02:00
Folke Lemaitre
ae4881d36e
fix(health): only check for luarocks when luarocks is enabled. 2024-06-24 20:03:00 +02:00
Folke Lemaitre
77edda11bf
test: fixes 2024-06-24 19:55:11 +02:00
github-actions[bot]
bd397ff1e3 chore(build): auto-generate docs 2024-06-24 17:45:13 +00:00
Folke Lemaitre
e3ee51b668
fix(health): show missing luarocks as warning 2024-06-24 19:44:22 +02:00
Folke Lemaitre
07c067a1a8
feat: make it easier to disable luarocks 2024-06-24 19:44:07 +02:00
Folke Lemaitre
105d4805ad
fix(runner): sync package specs after installing and before building 2024-06-24 19:38:33 +02:00
Folke Lemaitre
656d3d1f5b
feat: show rockspec deps in plugin details 2024-06-24 18:02:54 +02:00
Folke Lemaitre
0f45c0d062
fix(health): added luarocks check to health 2024-06-24 17:50:29 +02:00
github-actions[bot]
79c2efc8d8 chore(build): auto-generate docs 2024-06-24 15:49:09 +00:00
Folke Lemaitre
79afa96b90
style: remove pkg.versions 2024-06-24 17:47:01 +02:00
github-actions[bot]
cd3581efd1 chore(build): auto-generate docs 2024-06-24 15:42:27 +00:00
github-actions[bot]
7f52977c1d
chore(main): release 11.0.1 (#1538)
Co-authored-by: github-actions[bot] <41898282+github-actions[bot]@users.noreply.github.com>
2024-06-24 17:39:01 +02:00
Folke Lemaitre
146de4e801
fix(rocks): dont trigger rebuild for luarocks when build is overriden 2024-06-24 17:36:59 +02:00
github-actions[bot]
c33b9fbf8d
chore(main): release 11.0.0 (#1537)
Co-authored-by: github-actions[bot] <41898282+github-actions[bot]@users.noreply.github.com>
2024-06-24 17:16:50 +02:00
github-actions[bot]
eb26e95deb chore(build): auto-generate docs 2024-06-24 17:15:10 +02:00
Folke Lemaitre
9a6c219826 fix(rocks): only build rockspec when it has deps or an advanced build step 2024-06-24 17:15:10 +02:00
Folke Lemaitre
dbffad6f44 fix(fragments): prevent adding the same spec instance more than once 2024-06-24 17:15:10 +02:00
github-actions[bot]
fd04bc62f9 chore(build): auto-generate docs 2024-06-24 17:15:10 +02:00
Folke Lemaitre
b73c57ed9e fix(luarocks): cleanup luarocks when deleting a plugin 2024-06-24 17:15:10 +02:00
Folke Lemaitre
368747bc9a feat(build): build files and functions are now async. use coroutine.yield to interrupt and report progress 2024-06-24 17:15:10 +02:00
Folke Lemaitre
fcfd54835d feat: spec.rocks is no longer needed & added support for installing any luarock 2024-06-24 17:15:10 +02:00
github-actions[bot]
b3ee5b96f2 chore(build): auto-generate docs 2024-06-24 17:15:10 +02:00
github-actions[bot]
d4c07d062d chore(build): auto-generate docs 2024-06-24 17:15:10 +02:00
Folke Lemaitre
d498f81b8c style: wording 2024-06-24 17:15:10 +02:00
github-actions[bot]
23c2851265 chore(build): auto-generate docs 2024-06-24 17:15:10 +02:00
Folke Lemaitre
502600d3e6 docs: fix default lazy-loading comment 2024-06-24 17:15:10 +02:00
Folke Lemaitre
7b6ddbfc13 fix(pkg): automatically update pkgs when editing a pkg file 2024-06-24 17:15:10 +02:00
Folke Lemaitre
3515cb518f fix(pkg): make sure state dir exists 2024-06-24 17:15:10 +02:00
Folke Lemaitre
fd8229d6e3 fix(pkg): versioning and reload specs when pkg-cache is dirty 2024-06-24 17:15:10 +02:00
Folke Lemaitre
183f59e2e8 feat!: new docs for v11.0 2024-06-24 17:15:10 +02:00
Folke Lemaitre
4326d4b487 fix(pkg): correctly pre-load package specs and remove them when needed during resolve 2024-06-24 17:15:10 +02:00
Folke Lemaitre
ee2ca39f67 feat(meta): check for dir changes for plugins already added to the rtp 2024-06-24 17:15:10 +02:00
Folke Lemaitre
05e31ad059 docs: added comment on package source order 2024-06-24 17:15:10 +02:00
Folke Lemaitre
d2bea9eefd docs: added doc comments 2024-06-24 17:15:10 +02:00
Folke Lemaitre
c1912e2348 feat(pkg): import package specs in the scope of the plugin 2024-06-24 17:15:10 +02:00
Folke Lemaitre
75ffe56f70 feat: rewrite of spec resolving 2024-06-24 17:15:10 +02:00
Folke Lemaitre
8d35e60eeb test: fix spec tests 2024-06-24 17:15:10 +02:00
Folke Lemaitre
3be55a4615 feat: added support for plugin packages by lazy, rockspec and packspec 2024-06-24 17:15:10 +02:00
Folke Lemaitre
f1ba2e3d05 feat: luarocks support 2024-06-24 17:15:10 +02:00
Folke Lemaitre
8eba74c3fc feat: packspec 2024-06-24 17:15:10 +02:00
github-actions[bot]
4ea9fe0600
chore(main): release 10.24.3 (#1529)
Co-authored-by: github-actions[bot] <41898282+github-actions[bot]@users.noreply.github.com>
2024-06-23 07:38:56 +02:00
github-actions[bot]
849890c2c4 chore(build): auto-generate vimdoc 2024-06-23 05:00:24 +00:00
Ben Elan
6994ee3751
ci(release): fix version bumping in extra-file (#1534)
* ci(release): fix version bumping in extra-file

Release Please's doc is either incorrect or they have a bug, but this
resolves the issue.

* ci(release): use manifest file
2024-06-23 06:59:54 +02:00
Folke Lemaitre
bc62078366
ci: remove deploy 2024-06-18 23:48:55 +02:00
Folke Lemaitre
acc0449d83
ci: docs deploy 2024-06-18 23:37:01 +02:00
github-actions[bot]
07269d494e chore(build): auto-generate vimdoc 2024-06-18 21:22:17 +00:00
Folke Lemaitre
1325119e85
ci: docs 2024-06-18 23:21:42 +02:00
github-actions[bot]
cd6a829343 chore(build): auto-generate vimdoc 2024-06-17 22:36:50 +00:00
Folke Lemaitre
f4d57485b0
refactor: reloader 2024-06-18 00:36:11 +02:00
Folke Lemaitre
025520d083
fix(util): dump 2024-06-18 00:35:56 +02:00
github-actions[bot]
c501b429cf
chore(main): release 10.24.2 (#1526)
Co-authored-by: github-actions[bot] <41898282+github-actions[bot]@users.noreply.github.com>
2024-06-16 16:25:23 +02:00
Folke Lemaitre
b4316da731
fix(plugin): rebuild optional when needed and remove frags from parent deps. Fixes #1402 2024-06-16 15:52:33 +02:00
github-actions[bot]
38d6b74b63
chore(main): release 10.24.1 (#1525)
Co-authored-by: github-actions[bot] <41898282+github-actions[bot]@users.noreply.github.com>
2024-06-16 07:15:01 +02:00
github-actions[bot]
5da0245f05 chore(build): auto-generate vimdoc 2024-06-16 05:10:11 +00:00
Folke Lemaitre
be5dfba542
fix(plugin): better way of dealing with local specs. Fixes #1524 2024-06-16 07:09:33 +02:00
github-actions[bot]
4c6479e98a
chore(main): release 10.24.0 (#1522)
Co-authored-by: github-actions[bot] <41898282+github-actions[bot]@users.noreply.github.com>
2024-06-15 09:54:45 +02:00
Folke Lemaitre
e2e10d9cbe
feat: find local_spec in parent directories as well. Closes #1519 2024-06-15 09:28:58 +02:00
github-actions[bot]
6944b105cb chore(build): auto-generate vimdoc 2024-06-15 07:12:11 +00:00
Folke Lemaitre
067fd41933
fix(plugin): check optional plugins again after resolving enabled. Fixes #1402 2024-06-15 09:11:36 +02:00
Folke Lemaitre
eb4957442e
ci: update release please. Fixes #1504 2024-06-07 16:31:18 +02:00
dependabot[bot]
db5c465509
build(deps): bump actions/checkout from 3 to 4 (#1508)
Bumps [actions/checkout](https://github.com/actions/checkout) from 3 to 4.
- [Release notes](https://github.com/actions/checkout/releases)
- [Changelog](https://github.com/actions/checkout/blob/main/CHANGELOG.md)
- [Commits](https://github.com/actions/checkout/compare/v3...v4)

---
updated-dependencies:
- dependency-name: actions/checkout
  dependency-type: direct:production
  update-type: version-update:semver-major
...

Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2024-06-07 16:28:52 +02:00
dependabot[bot]
0aac19ccc7
build(deps): bump stefanzweifel/git-auto-commit-action from 4 to 5 (#1506)
Bumps [stefanzweifel/git-auto-commit-action](https://github.com/stefanzweifel/git-auto-commit-action) from 4 to 5.
- [Release notes](https://github.com/stefanzweifel/git-auto-commit-action/releases)
- [Changelog](https://github.com/stefanzweifel/git-auto-commit-action/blob/master/CHANGELOG.md)
- [Commits](https://github.com/stefanzweifel/git-auto-commit-action/compare/v4...v5)

---
updated-dependencies:
- dependency-name: stefanzweifel/git-auto-commit-action
  dependency-type: direct:production
  update-type: version-update:semver-major
...

Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2024-06-07 16:28:42 +02:00
Folke Lemaitre
938e195108
ci: Update dependabot.yml 2024-06-07 16:26:38 +02:00
Folke Lemaitre
034b03c803
ci: Create dependabot.yml 2024-06-07 16:23:47 +02:00
github-actions[bot]
fafe1f7c64
chore(main): release 10.23.0 (#1502)
Co-authored-by: github-actions[bot] <41898282+github-actions[bot]@users.noreply.github.com>
2024-06-07 10:54:20 +02:00
github-actions[bot]
e0bc9a07e4 chore(build): auto-generate vimdoc 2024-06-07 07:03:28 +00:00
Folke Lemaitre
1f7b720cff
feat(plugin): opts_extend can be a list of dotted keys that will be extended instead of merged 2024-06-07 09:02:52 +02:00
Folke Lemaitre
89ddc59d19
Revert "feat(util): opts merging now supports lists extending by tagging a table with __extend = true. Use with care"
This reverts commit 74fd3611f2.
2024-06-06 23:27:29 +02:00
Folke Lemaitre
74fd3611f2
feat(util): opts merging now supports lists extending by tagging a table with __extend = true. Use with care 2024-06-06 23:23:52 +02:00
github-actions[bot]
70f2c090d3
chore(main): release 10.22.2 (#1500)
Co-authored-by: github-actions[bot] <41898282+github-actions[bot]@users.noreply.github.com>
2024-06-06 12:47:53 +02:00
Folke Lemaitre
ff90417808
fix(keys): buffer-local nop mappings 2024-06-06 10:15:35 +02:00
github-actions[bot]
0c1ec520af chore(build): auto-generate vimdoc 2024-06-06 07:29:39 +00:00
Folke Lemaitre
3e4c795cec
fix(keys): never lazy-load <nop> or empty rhs keymaps 2024-06-06 09:28:35 +02:00
github-actions[bot]
0fc34a0cf5 chore(build): auto-generate vimdoc 2024-06-04 04:58:50 +00:00
Vlad
784a726f2e
style: Set vim.opt.rtp type to vim.Option (#1498)
## Details

There is an issue in the `neodev` repo that mentions this:
[#193](https://github.com/folke/neodev.nvim/issues/193)

I think the problem comes from a combination of 2 things:

1. As mentioned in the [Reddit post](https://www.reddit.com/r/neovim/comments/1cvrilk/diagnosticwarning_after_upgrade_to_neovim_010/):
   `Nvim never had Lua type annotations for vim.opt`.
2. Setting `vim.opt.rtp` anywhere in the config will cause Lua-LS to infer the
   type for `vim.opt.rtp`. While users are unlikely to do this it does happen
   in `lazy.nvim`, in places shown in this PR.
2024-06-04 06:58:19 +02:00
github-actions[bot]
ebbf84eb23 chore(build): auto-generate vimdoc 2024-06-03 07:19:16 +00:00
Zhizhen He
f39c79fcb1
style: fix some typo (#1496) 2024-06-03 09:18:43 +02:00
github-actions[bot]
b0ba3f9399
chore(main): release 10.22.1 (#1495)
Co-authored-by: github-actions[bot] <41898282+github-actions[bot]@users.noreply.github.com>
2024-06-02 14:53:20 +02:00
Folke Lemaitre
9242edb739
fix: force new release 2024-06-02 14:51:46 +02:00
github-actions[bot]
1418f30806 chore(build): auto-generate vimdoc 2024-06-02 12:49:18 +00:00
Folke Lemaitre
58536d4c81
Revert "feat: set vim.env.LAZY to lazy root"
This reverts commit 6a141a6dbb.
2024-06-02 14:48:42 +02:00
github-actions[bot]
ad30030b6a
chore(main): release 10.22.0 (#1493)
Co-authored-by: github-actions[bot] <41898282+github-actions[bot]@users.noreply.github.com>
2024-06-01 18:57:42 +02:00
github-actions[bot]
a3edeae34a chore(build): auto-generate vimdoc 2024-06-01 16:21:15 +00:00
Folke Lemaitre
6a141a6dbb
feat: set vim.env.LAZY to lazy root 2024-06-01 18:20:39 +02:00
github-actions[bot]
eab487c252
chore(main): release 10.21.2 (#1492)
Co-authored-by: github-actions[bot] <41898282+github-actions[bot]@users.noreply.github.com>
2024-05-31 20:41:13 +02:00
Anshuman Medhi
b77aaa08cb
fix(ui): deduplicate plugins when selecting multiple (#1491) 2024-05-31 16:28:33 +02:00
github-actions[bot]
4a8f813d81
chore(main): release 10.21.1 (#1490)
Co-authored-by: github-actions[bot] <41898282+github-actions[bot]@users.noreply.github.com>
2024-05-31 14:14:25 +02:00
github-actions[bot]
52244a0c1d chore(build): auto-generate vimdoc 2024-05-31 11:56:30 +00:00
Folke Lemaitre
917dfbe2a9
fix(view): backward compat for older Neovim versions. Fixes #1489 2024-05-31 13:55:54 +02:00
github-actions[bot]
24fa2a9708
chore(main): release 10.21.0 (#1477)
Co-authored-by: github-actions[bot] <41898282+github-actions[bot]@users.noreply.github.com>
2024-05-26 16:59:48 +02:00
github-actions[bot]
a55d275eca chore(build): auto-generate vimdoc 2024-05-26 14:44:42 +00:00
Folke Lemaitre
9dde1f1bce
feat: added support for local spec files .lazy.lua 2024-05-26 16:43:52 +02:00
Christian Clason
ea7b9c3c3f
fix(render): disable underline for diagnostics (#1478)
Problem: On current Nvim nightlies, a regression in a
`vim.highlight.range()` refactor makes underlines for diagnostics at end
of line extend into the next line, leading to visual artifacts in the
update view.

Solution: Suppress underlines for diagnostics, as they are not wanted
anyway.
2024-05-26 15:21:58 +02:00
github-actions[bot]
98210e2f82 chore(build): auto-generate vimdoc 2024-05-26 08:40:48 +00:00
Anshuman Medhi
7667a73dee
feat: single-plugin keys in the lazy view in visual mode (#1476)
Applies to all plugins contained in the range
2024-05-26 10:40:08 +02:00
github-actions[bot]
8f19915175
chore(main): release 10.20.5 (#1445)
Co-authored-by: github-actions[bot] <41898282+github-actions[bot]@users.noreply.github.com>
2024-05-22 00:10:58 +02:00
github-actions[bot]
8411fe9467 chore(build): auto-generate vimdoc 2024-05-21 20:11:25 +00:00
Folke Lemaitre
82cf974e09
fix(keys): properly deal with ft list for keys. Fixes #1448 2024-05-21 22:10:49 +02:00
github-actions[bot]
9895337d1f chore(build): auto-generate vimdoc 2024-05-20 19:15:40 +00:00
Folke Lemaitre
39de11a2fa
fix(keys): properly re-create buffer-local mappings. Fixes #1448 2024-05-20 21:15:04 +02:00
github-actions[bot]
0de782a6b0 chore(build): auto-generate vimdoc 2024-05-19 15:30:17 +00:00
Folke Lemaitre
56a34a825b
fix(help): get rid of any tbl_flatten or iter flatten code 2024-05-19 17:29:43 +02:00
Folke Lemaitre
5d29ffeaa0
style: favorite icon 2024-05-18 13:23:55 +02:00
github-actions[bot]
c717ab88ff chore(build): auto-generate vimdoc 2024-05-18 08:14:46 +00:00
Kevin Traver
2e04a0c02c
fix(checker): ignore dev plugins (#1384) 2024-05-18 10:14:12 +02:00
github-actions[bot]
05240b4154 chore(build): auto-generate vimdoc 2024-05-16 19:45:23 +00:00
Sebastian Lyng Johansen
d039aecddb
fix: use vim.iter():flatten():totable() over vim.tbl_flatten (#1454) 2024-05-16 21:44:51 +02:00
github-actions[bot]
e44636a433 chore(build): auto-generate vimdoc 2024-05-13 06:35:16 +00:00
Folke Lemaitre
d2a4ce22dc
fix(git): force autocrlf=false. Fixes #1055 2024-05-13 08:34:39 +02:00
github-actions[bot]
758bb5de98
chore(main): release 10.20.4 (#1400)
Co-authored-by: github-actions[bot] <41898282+github-actions[bot]@users.noreply.github.com>
2024-05-12 10:15:14 +02:00
github-actions[bot]
1651030404 chore(build): auto-generate vimdoc 2024-05-12 07:52:52 +00:00
Markus Koller
bb0179139a
docs: clarify default config implementation (#1407) 2024-05-12 09:52:21 +02:00
Tristan Knight
2fcbcaf07a
fix(reload): strings in lua reload (#1439) 2024-05-12 09:50:06 +02:00
github-actions[bot]
76d321008f chore(build): auto-generate vimdoc 2024-05-12 07:44:06 +00:00
Folke Lemaitre
40845063a2
fix(ui): hover now opens repo url when no diff with main. Fixes #1430 2024-05-12 09:43:34 +02:00
github-actions[bot]
d3974346b6 chore(build): auto-generate vimdoc 2024-05-04 08:01:54 +00:00
Iordanis Petkakis
24234f47a2
fix(ui): add conditional nvim_get_hl_by_name for Neovim 0.8.0 (#1429) 2024-05-04 10:01:20 +02:00
github-actions[bot]
3f13f08043 chore(build): auto-generate vimdoc 2024-04-22 08:34:07 +00:00
Folke Lemaitre
481aed70cc
fix(heath): vim.uv. Fixes #1412 2024-04-22 10:33:32 +02:00
Folke Lemaitre
31ddbea7c1
fix(ui): set backdrop filetype to lazy_backdrop. Fixes #1399 2024-03-29 20:28:47 +01:00
github-actions[bot]
7a2617575a chore(build): auto-generate vimdoc 2024-03-29 07:19:12 +00:00
github-actions[bot]
bef521ac89
chore(main): release 10.20.3 (#1394)
Co-authored-by: github-actions[bot] <41898282+github-actions[bot]@users.noreply.github.com>
2024-03-29 08:18:38 +01:00
Folke Lemaitre
0ccf031227
fix(ui): disable backdrop when Neovim is transparent 2024-03-28 14:52:07 +01:00
github-actions[bot]
ba58b87ed9 chore(build): auto-generate vimdoc 2024-03-28 11:16:10 +00:00
Folke Lemaitre
f61ca6ec70
docs: fix commands table. Fixes #1393 2024-03-28 12:15:31 +01:00
Folke Lemaitre
e753eb6025
ci: better docgen 2024-03-27 22:59:00 +01:00
github-actions[bot]
e888d5b64c chore(build): auto-generate vimdoc 2024-03-27 08:23:55 +00:00
Folke Lemaitre
a836600573
docs: make bootstrap work on stable and nightly. Fixes #1391 2024-03-27 09:23:19 +01:00
github-actions[bot]
65887ea871
chore(main): release 10.20.2 (#1388)
Co-authored-by: github-actions[bot] <41898282+github-actions[bot]@users.noreply.github.com>
2024-03-27 08:55:21 +01:00
github-actions[bot]
b38b2257b6 chore(build): auto-generate vimdoc 2024-03-27 07:49:31 +00:00
Folke Lemaitre
eefb8974d6
fix(ui): special handling for floats closed before VimEnter. Seems that WinClosed events dont execute before that. Fixes #1390 2024-03-27 08:48:55 +01:00
Folke Lemaitre
d37a76b871
fix(ui): only enable backdrop when guicolors is set. Fixes #1387 2024-03-26 22:57:58 +01:00
github-actions[bot]
68941b7b13
chore(main): release 10.20.1 (#1386)
Co-authored-by: github-actions[bot] <41898282+github-actions[bot]@users.noreply.github.com>
2024-03-26 20:32:47 +01:00
Folke Lemaitre
79e2e85934
fix(ui): properly cleanup on :quit. Fixes #1385 2024-03-26 20:31:15 +01:00
github-actions[bot]
107719d31e
chore(main): release 10.20.0 (#1382)
Co-authored-by: github-actions[bot] <41898282+github-actions[bot]@users.noreply.github.com>
2024-03-26 19:54:49 +01:00
github-actions[bot]
6749a259c1 chore(build): auto-generate vimdoc 2024-03-26 18:52:53 +00:00
Folke Lemaitre
a6b74f30d5
feat(ui): backdrop for the lazy floating window. Can be disabled with opts.ui.backdrop 2024-03-26 19:52:19 +01:00
github-actions[bot]
08954f723b chore(build): auto-generate vimdoc 2024-03-24 10:30:32 +00:00
Folke Lemaitre
eade87fb83
fix(types): fixed type for version. Fixes #1381 2024-03-24 11:30:00 +01:00
github-actions[bot]
af6afefbb4
chore(main): release 10.19.0 (#1377)
Co-authored-by: github-actions[bot] <41898282+github-actions[bot]@users.noreply.github.com>
2024-03-22 23:39:43 +01:00
Folke Lemaitre
66466a2594
feat(util): option to force system app for util.open 2024-03-22 23:35:22 +01:00
github-actions[bot]
8134f2ac04
chore(main): release 10.18.3 (#1376)
Co-authored-by: github-actions[bot] <41898282+github-actions[bot]@users.noreply.github.com>
2024-03-22 22:51:11 +01:00
Riley Bruins
9131ea4c4a
fix(cache): vim.loop fallback (#1375) 2024-03-22 22:42:17 +01:00
87 changed files with 124485 additions and 2753 deletions

11
.busted Normal file
View file

@ -0,0 +1,11 @@
return {
_all = {
coverage = false,
},
default = {
verbose = true,
},
tests = {
verbose = true,
},
}

7
.editorconfig Normal file
View file

@ -0,0 +1,7 @@
root = true
[*]
insert_final_newline = true
indent_style = space
indent_size = 2
charset = utf-8

3
.github/.release-please-manifest.json vendored Normal file
View file

@ -0,0 +1,3 @@
{
".": "11.17.1"
}

View file

@ -6,7 +6,10 @@ body:
- type: markdown - type: markdown
attributes: attributes:
value: | value: |
**Before** reporting an issue, make sure to read the [documentation](https://github.com/folke/lazy.nvim) and search [existing issues](https://github.com/folke/lazy.nvim/issues). Usage questions such as ***"How do I...?"*** belong in [Discussions](https://github.com/folke/lazy.nvim/discussions) and will be closed. **Before** reporting an issue, make sure to read the [documentation](https://github.com/folke/lazy.nvim)
and search [existing issues](https://github.com/folke/lazy.nvim/issues).
Usage questions such as ***"How do I...?"*** belong in [Discussions](https://github.com/folke/lazy.nvim/discussions) and will be closed.
- type: checkboxes - type: checkboxes
attributes: attributes:
label: Did you check docs and existing issues? label: Did you check docs and existing issues?
@ -14,6 +17,8 @@ body:
options: options:
- label: I have read all the lazy.nvim docs - label: I have read all the lazy.nvim docs
required: true required: true
- label: I have updated the plugin to the latest version before submitting this issue
required: true
- label: I have searched the existing issues of lazy.nvim - label: I have searched the existing issues of lazy.nvim
required: true required: true
- label: I have searched the existing issues of plugins related to this issue - label: I have searched the existing issues of plugins related to this issue
@ -57,32 +62,14 @@ body:
label: Repro label: Repro
description: Minimal `init.lua` to reproduce this issue. Save as `repro.lua` and run with `nvim -u repro.lua` description: Minimal `init.lua` to reproduce this issue. Save as `repro.lua` and run with `nvim -u repro.lua`
value: | value: |
-- DO NOT change the paths and don't remove the colorscheme vim.env.LAZY_STDPATH = ".repro"
local root = vim.fn.fnamemodify("./.repro", ":p") load(vim.fn.system("curl -s https://raw.githubusercontent.com/folke/lazy.nvim/main/bootstrap.lua"))()
-- set stdpaths to use .repro require("lazy.minit").repro({
for _, name in ipairs({ "config", "data", "state", "cache" }) do spec = {
vim.env[("XDG_%s_HOME"):format(name:upper())] = root .. "/" .. name -- add any other plugins here
end },
-- bootstrap lazy
local lazypath = root .. "/plugins/lazy.nvim"
if not vim.loop.fs_stat(lazypath) then
vim.fn.system({ "git", "clone", "--filter=blob:none", "https://github.com/folke/lazy.nvim.git", lazypath, })
end
vim.opt.runtimepath:prepend(lazypath)
-- install plugins
local plugins = {
"folke/tokyonight.nvim",
-- add any other plugins here
}
require("lazy").setup(plugins, {
root = root .. "/plugins",
}) })
render: lua
vim.cmd.colorscheme("tokyonight")
-- add anything else here
render: Lua
validations: validations:
required: false required: false

5
.github/ISSUE_TEMPLATE/config.yml vendored Normal file
View file

@ -0,0 +1,5 @@
blank_issues_enabled: false
contact_links:
- name: Ask a question
url: https://github.com/folke/lazy.nvim/discussions
about: Use Github discussions instead

16
.github/PULL_REQUEST_TEMPLATE.md vendored Normal file
View file

@ -0,0 +1,16 @@
## Description
<!-- Describe the big picture of your changes to communicate to the maintainers
why we should accept this pull request. -->
## Related Issue(s)
<!--
If this PR fixes any issues, please link to the issue here.
- Fixes #<issue_number>
-->
## Screenshots
<!-- Add screenshots of the changes if applicable. -->

6
.github/dependabot.yml vendored Normal file
View file

@ -0,0 +1,6 @@
version: 2
updates:
- package-ecosystem: "github-actions"
directory: "/"
schedule:
interval: "weekly"

9
.github/release-please-config.json vendored Normal file
View file

@ -0,0 +1,9 @@
{
"$schema": "https://raw.githubusercontent.com/googleapis/release-please/main/schemas/config.json",
"packages": {
".": {
"release-type": "simple",
"extra-files": ["lua/lazy/core/config.lua"]
}
}
}

View file

@ -1,74 +1,15 @@
name: CI name: CI
on: on:
push: push:
branches: [main, master]
pull_request: pull_request:
jobs: jobs:
tests: ci:
strategy: uses: folke/github/.github/workflows/ci.yml@main
matrix: secrets: inherit
# os: [ubuntu-latest, windows-latest] with:
os: [ubuntu-latest] plugin: lazy.nvim
runs-on: ${{ matrix.os }} repo: folke/lazy.nvim
steps: docs: false
- uses: actions/checkout@v3
- name: Install Neovim
shell: bash
run: |
mkdir -p /tmp/nvim
wget -q https://github.com/neovim/neovim/releases/download/nightly/nvim.appimage -O /tmp/nvim/nvim.appimage
cd /tmp/nvim
chmod a+x ./nvim.appimage
./nvim.appimage --appimage-extract
echo "/tmp/nvim/squashfs-root/usr/bin/" >> $GITHUB_PATH
- name: Run Tests
run: |
nvim --version
[ ! -d tests ] && exit 0
nvim --headless -u tests/init.lua -c "PlenaryBustedDirectory tests/ {minimal_init = 'tests/init.lua', sequential = true}"
docs:
runs-on: ubuntu-latest
needs: tests
if: ${{ github.ref == 'refs/heads/main' }}
steps:
- uses: actions/checkout@v3
- name: panvimdoc
uses: kdheepak/panvimdoc@main
with:
vimdoc: lazy.nvim
version: "Neovim >= 0.8.0"
demojify: true
treesitter: true
- name: Push changes
uses: stefanzweifel/git-auto-commit-action@v4
with:
commit_message: "chore(build): auto-generate vimdoc"
commit_user_name: "github-actions[bot]"
commit_user_email: "github-actions[bot]@users.noreply.github.com"
commit_author: "github-actions[bot] <github-actions[bot]@users.noreply.github.com>"
release:
name: release
if: ${{ github.ref == 'refs/heads/main' }}
needs:
- docs
- tests
runs-on: ubuntu-latest
steps:
- uses: google-github-actions/release-please-action@v3
id: release
with:
release-type: simple
package-name: lazy.nvim
extra-files: |
lua/lazy/core/config.lua
- uses: actions/checkout@v3
- name: tag stable versions
if: ${{ steps.release.outputs.release_created }}
run: |
git config user.name github-actions[bot]
git config user.email github-actions[bot]@users.noreply.github.com
git remote add gh-token "https://${{ secrets.GITHUB_TOKEN }}@github.com/google-github-actions/release-please-action.git"
git tag -d stable || true
git push origin :stable || true
git tag -a stable -m "Last Stable Release"
git push origin stable

30
.github/workflows/community.yml vendored Normal file
View file

@ -0,0 +1,30 @@
name: Community
on:
push:
branches:
- main
jobs:
community:
runs-on: ubuntu-latest
if: ${{ github.ref == 'refs/heads/main' && github.repository_owner == 'folke' }}
steps:
- uses: actions/checkout@v4
- uses: folke/github/neovim@main
- name: Rockspec Build
id: rockspec-build
uses: actions/cache@v4
with:
path: build
key: rockspec-build
- name: Generate Rockspec
if: steps.rockspec-build.cache-hit != 'true'
run: |
nvim -l lua/lazy/build.lua
- name: Push changes
uses: stefanzweifel/git-auto-commit-action@v5
with:
commit_message: "chore(build): auto-generate rockspec mappings"
commit_user_name: "github-actions[bot]"
commit_user_email: "github-actions[bot]@users.noreply.github.com"
commit_author: "github-actions[bot] <github-actions[bot]@users.noreply.github.com>"

19
.github/workflows/docs.yml vendored Normal file
View file

@ -0,0 +1,19 @@
name: Docs
on:
push:
branches:
- main
jobs:
docs:
runs-on: ubuntu-latest
if: ${{ github.ref == 'refs/heads/main' && github.repository_owner == 'folke' }}
env:
GH_TOKEN: ${{ github.token }}
steps:
- uses: actions/checkout@v4
with:
ref: docs
- name: Generate Docs
shell: bash
run: gh workflow run "Deploy to Github Pages" --ref docs

8
.github/workflows/labeler.yml vendored Normal file
View file

@ -0,0 +1,8 @@
name: "PR Labeler"
on:
- pull_request_target
jobs:
labeler:
uses: folke/github/.github/workflows/labeler.yml@main
secrets: inherit

18
.github/workflows/pr.yml vendored Normal file
View file

@ -0,0 +1,18 @@
name: PR Title
on:
pull_request_target:
types:
- opened
- edited
- synchronize
- reopened
- ready_for_review
permissions:
pull-requests: read
jobs:
pr-title:
uses: folke/github/.github/workflows/pr.yml@main
secrets: inherit

11
.github/workflows/stale.yml vendored Normal file
View file

@ -0,0 +1,11 @@
name: Stale Issues & PRs
on:
schedule:
- cron: "30 1 * * *"
jobs:
stale:
if: contains(fromJSON('["folke", "LazyVim"]'), github.repository_owner)
uses: folke/github/.github/workflows/stale.yml@main
secrets: inherit

13
.github/workflows/update.yml vendored Normal file
View file

@ -0,0 +1,13 @@
name: Update Repo
on:
workflow_dispatch:
schedule:
# Run every hour
- cron: "0 * * * *"
jobs:
update:
if: contains(fromJSON('["folke", "LazyVim"]'), github.repository_owner)
uses: folke/github/.github/workflows/update.yml@main
secrets: inherit

15
.gitignore vendored
View file

@ -1,8 +1,9 @@
tt.*
.tests
doc/tags
debug
.repro
foo.*
*.log *.log
data /.repro
/.tests
/build
/debug
/doc/tags
foo.*
node_modules
tt.*

1
.styluaignore Normal file
View file

@ -0,0 +1 @@
lua/lazy/community/_generated.lua

View file

@ -1,5 +1,620 @@
# Changelog # Changelog
## [11.17.1](https://github.com/folke/lazy.nvim/compare/v11.17.0...v11.17.1) (2025-02-25)
### Bug Fixes
* **bootstrap:** support for older Neovim versions ([1c9ba37](https://github.com/folke/lazy.nvim/commit/1c9ba3704564a2e34a22191bb89678680ffeb245))
* **meta:** rebuild dirty right after disable. See [#1889](https://github.com/folke/lazy.nvim/issues/1889) ([d51cf69](https://github.com/folke/lazy.nvim/commit/d51cf6978321d659e68a8bc38ee806bd2517a196))
## [11.17.0](https://github.com/folke/lazy.nvim/compare/v11.16.2...v11.17.0) (2025-02-24)
### Features
* **config,render:** allow customizing the debug icon ([#1863](https://github.com/folke/lazy.nvim/issues/1863)) ([a9c660d](https://github.com/folke/lazy.nvim/commit/a9c660d6ef1b396869d3d951760aa7a3dbfe575f))
* **util:** pass lang to `vim.notify` so that snacks notifier can render the ft. Closes [#1919](https://github.com/folke/lazy.nvim/issues/1919) ([c6a57a3](https://github.com/folke/lazy.nvim/commit/c6a57a3534d3494bcc5ff9b0586e141bdb0280eb))
### Bug Fixes
* **config:** add missing space on the default debug icon ([#1879](https://github.com/folke/lazy.nvim/issues/1879)) ([4df5c4d](https://github.com/folke/lazy.nvim/commit/4df5c4d65a3bbf801edd9ec55fb1ae55cfa72dd0))
* **meta:** disable top-level specs before the rest. Closes [#1889](https://github.com/folke/lazy.nvim/issues/1889) ([f81a3fb](https://github.com/folke/lazy.nvim/commit/f81a3fb7feaf460ec7c8c983682b4a693b18fdd4))
* **ui:** do not show virt_lines for messages ([#1904](https://github.com/folke/lazy.nvim/issues/1904)) ([f15a939](https://github.com/folke/lazy.nvim/commit/f15a93907ddad3d9139aea465ae18336d87f5ce6))
## [11.16.2](https://github.com/folke/lazy.nvim/compare/v11.16.1...v11.16.2) (2024-12-13)
### Bug Fixes
* **meta:** when a plugin is both optional and disabled, then just delete it from the list ([805b85c](https://github.com/folke/lazy.nvim/commit/805b85c2ea3bd6f9506ef22cbd6e3a39172b5b08))
## [11.16.1](https://github.com/folke/lazy.nvim/compare/v11.16.0...v11.16.1) (2024-12-09)
### Bug Fixes
* **types:** ensure all fields for `LazyPluginSpec` are optional ([#1843](https://github.com/folke/lazy.nvim/issues/1843)) ([703be1d](https://github.com/folke/lazy.nvim/commit/703be1dda35e142e76e94e7503cf67d6b98a1d35)), closes [#1842](https://github.com/folke/lazy.nvim/issues/1842)
## [11.16.0](https://github.com/folke/lazy.nvim/compare/v11.15.0...v11.16.0) (2024-12-07)
### Features
* **plugin:** added support for virtual plugins. Closes [#1836](https://github.com/folke/lazy.nvim/issues/1836) ([ee64abc](https://github.com/folke/lazy.nvim/commit/ee64abc76be2b237b95d241a924b0323005b868a))
### Bug Fixes
* **plugin:** don't check if dir exists for virtual plugins ([656cf43](https://github.com/folke/lazy.nvim/commit/656cf4309396b7b8b62984e923bf8d8a0013f7d7))
* **render:** show correct key for home. Fixes [#1796](https://github.com/folke/lazy.nvim/issues/1796) ([b08dba8](https://github.com/folke/lazy.nvim/commit/b08dba8107b5bdaaa007f18cf6c0cc0e0fd576aa))
## [11.15.0](https://github.com/folke/lazy.nvim/compare/v11.14.2...v11.15.0) (2024-12-05)
### Features
* **plugin:** show error for local plugins that don't exist. Fixes [#1773](https://github.com/folke/lazy.nvim/issues/1773) ([9570a5a](https://github.com/folke/lazy.nvim/commit/9570a5ae7b17dcde4718c7458fd986c10f015a99))
## [11.14.2](https://github.com/folke/lazy.nvim/compare/v11.14.1...v11.14.2) (2024-11-10)
### Bug Fixes
* **bootstrap:** single forward slash. Fixes [#1747](https://github.com/folke/lazy.nvim/issues/1747) ([aca30f6](https://github.com/folke/lazy.nvim/commit/aca30f63619a7492ecdea8833a065cf83c80f764))
* **completion:** check if command string is a prefix of Lazy ([#1760](https://github.com/folke/lazy.nvim/issues/1760)) ([e9fd76e](https://github.com/folke/lazy.nvim/commit/e9fd76e239cc18da289f9a3f80f35fa16b003175)), closes [#1758](https://github.com/folke/lazy.nvim/issues/1758)
* **docs:** always update helptags for local plugins ([60cf258](https://github.com/folke/lazy.nvim/commit/60cf258a9ae7fffe04bb31141141a91845158dcc))
* **luarocks:** try to install from root manifest ([#1687](https://github.com/folke/lazy.nvim/issues/1687)) ([591ef40](https://github.com/folke/lazy.nvim/commit/591ef40f2da3a26fbcc0466988cd6fe45ca68cae))
* **rocks:** add lib64 plugin directory to package.cpath ([#1717](https://github.com/folke/lazy.nvim/issues/1717)) ([80da254](https://github.com/folke/lazy.nvim/commit/80da254e645f579c28394ee0f08f75a9c9481744))
* **rockspec:** allow binary lua files. Fixes [#1800](https://github.com/folke/lazy.nvim/issues/1800) ([408449a](https://github.com/folke/lazy.nvim/commit/408449a59adb8c2a31c32fff606676b32ce4552a))
## [11.14.1](https://github.com/folke/lazy.nvim/compare/v11.14.0...v11.14.1) (2024-07-25)
### Bug Fixes
* **plugins:** "Vim:E150: Not a directory" on plugin update ([#1679](https://github.com/folke/lazy.nvim/issues/1679)) ([7108809](https://github.com/folke/lazy.nvim/commit/7108809ab18dc1b1e6f402b29e2e1d35a5d311d5))
## [11.14.0](https://github.com/folke/lazy.nvim/compare/v11.13.5...v11.14.0) (2024-07-24)
### Features
* added `opts.git.cooldown` to allow updating plugins on slow connections. Fixes [#1656](https://github.com/folke/lazy.nvim/issues/1656) ([d5686ef](https://github.com/folke/lazy.nvim/commit/d5686efbd00942b3e38de7c08b8df69d961b02f0))
* **plugin:** improve error handling and show better error message ([c02268a](https://github.com/folke/lazy.nvim/commit/c02268ac6e6aab92249d020d75efc588bd9d24fa))
### Bug Fixes
* **plugin:** make .lazy.lua work again ([b4a5a12](https://github.com/folke/lazy.nvim/commit/b4a5a1209e4c64fa67aedf721a383541a64056d1))
## [11.13.5](https://github.com/folke/lazy.nvim/compare/v11.13.4...v11.13.5) (2024-07-22)
### Bug Fixes
* **health:** dont use vim.fn.system to get cmd versions ([7d29719](https://github.com/folke/lazy.nvim/commit/7d29719ade6f5a269e3b7d08b246641b5b079aaa))
## [11.13.4](https://github.com/folke/lazy.nvim/compare/v11.13.3...v11.13.4) (2024-07-22)
### Bug Fixes
* **loader:** add plugins whose rtp got loaded early to start plugins ([34b0126](https://github.com/folke/lazy.nvim/commit/34b0126e5b3966f1dbe148d6f8450213115e76b2))
* **loader:** explicitely set package.loaded.modname to nil to prevent recursive loading errors ([12f2c74](https://github.com/folke/lazy.nvim/commit/12f2c74244cc768d97c83972aa63722389b5d96d))
## [11.13.3](https://github.com/folke/lazy.nvim/compare/v11.13.2...v11.13.3) (2024-07-21)
### Reverts
* fix(loader): add auto loaded module to package.loaded early to prevent require loops ([a692bf8](https://github.com/folke/lazy.nvim/commit/a692bf86883457f45fe3f773bfc8bc4d9e4b070c))
## [11.13.2](https://github.com/folke/lazy.nvim/compare/v11.13.1...v11.13.2) (2024-07-21)
### Bug Fixes
* **loader:** add auto loaded module to package.loaded early to prevent require loops ([18d1c1b](https://github.com/folke/lazy.nvim/commit/18d1c1b47e175cd58dc12bf4792ef4e9a50505fa))
## [11.13.1](https://github.com/folke/lazy.nvim/compare/v11.13.0...v11.13.1) (2024-07-19)
### Bug Fixes
* **build:** only load the plugin before build for `:` build commands ([5bdb12a](https://github.com/folke/lazy.nvim/commit/5bdb12a038e5a72cc793f38893f1a9c9fb741759))
## [11.13.0](https://github.com/folke/lazy.nvim/compare/v11.12.0...v11.13.0) (2024-07-17)
### Features
* **ui:** added mapping descriptions ([6ca90a2](https://github.com/folke/lazy.nvim/commit/6ca90a21202808796418e46d3cebfbb5a44e54a2))
## [11.12.0](https://github.com/folke/lazy.nvim/compare/v11.11.1...v11.12.0) (2024-07-16)
### Features
* **git:** added git network throttle to limit network related git ops per interval. Closes [#1635](https://github.com/folke/lazy.nvim/issues/1635) ([d731a6b](https://github.com/folke/lazy.nvim/commit/d731a6b005fd239e85e555bd57362382f6c1e461))
## [11.11.1](https://github.com/folke/lazy.nvim/compare/v11.11.0...v11.11.1) (2024-07-13)
### Bug Fixes
* **config:** check for lib64. Fixes [#1343](https://github.com/folke/lazy.nvim/issues/1343) ([93499c5](https://github.com/folke/lazy.nvim/commit/93499c5deb37641c6cf71528a93f101d186b409f))
* **lockfile:** ensure newline at EOF for lockfile ([#1639](https://github.com/folke/lazy.nvim/issues/1639)) ([7ed9f71](https://github.com/folke/lazy.nvim/commit/7ed9f7173cdec71a057053d7e6efc20c2c230b95))
## [11.11.0](https://github.com/folke/lazy.nvim/compare/v11.10.4...v11.11.0) (2024-07-11)
### Features
* add plugin name to handlers.managed ([17473db](https://github.com/folke/lazy.nvim/commit/17473db1d79ea30e06126834be7fd95ca511557b))
### Bug Fixes
* **minit:** add tests to package.path when running busted (helpers.lua etc) ([fadebdc](https://github.com/folke/lazy.nvim/commit/fadebdc76b71a1d3658a88a025c6c8fb4749e0f8))
* **util:** strip `-lua` in normname ([54b003c](https://github.com/folke/lazy.nvim/commit/54b003c650f07b771e61566f7be2629beb2b781f))
## [11.10.4](https://github.com/folke/lazy.nvim/compare/v11.10.3...v11.10.4) (2024-07-08)
### Bug Fixes
* **rocks:** try building anyway even when prerequisits have not been met. (will likely fail) ([f0324de](https://github.com/folke/lazy.nvim/commit/f0324defdd43be8aa14aaf3a794ff3d5581f36ba))
* **ui:** don't treat suspended as headless. Closes [#1626](https://github.com/folke/lazy.nvim/issues/1626) ([2dfccd7](https://github.com/folke/lazy.nvim/commit/2dfccd7b948beb26d8bcff7f9113a3a5c85cbc4a))
## [11.10.3](https://github.com/folke/lazy.nvim/compare/v11.10.2...v11.10.3) (2024-07-07)
### Bug Fixes
* **git:** local plugin fixes ([#1624](https://github.com/folke/lazy.nvim/issues/1624)) ([72c0dc9](https://github.com/folke/lazy.nvim/commit/72c0dc9462ab3bf1a68198afabc1eb4e2940d299))
## [11.10.2](https://github.com/folke/lazy.nvim/compare/v11.10.1...v11.10.2) (2024-07-07)
### Bug Fixes
* **git:** only check for new commits for local plugins. Closes [#1512](https://github.com/folke/lazy.nvim/issues/1512) ([81d2bff](https://github.com/folke/lazy.nvim/commit/81d2bfffdc8c84a40d25cae7fd4800178c19a138))
## [11.10.1](https://github.com/folke/lazy.nvim/compare/v11.10.0...v11.10.1) (2024-07-05)
### Bug Fixes
* **lockfile:** keep cond=false and enabed=false in lockfile. Fixes [#1535](https://github.com/folke/lazy.nvim/issues/1535). Fixes [#1606](https://github.com/folke/lazy.nvim/issues/1606) ([baac551](https://github.com/folke/lazy.nvim/commit/baac5517770abd6eee63d11cf4791ef5bf5702e8))
## [11.10.0](https://github.com/folke/lazy.nvim/compare/v11.9.2...v11.10.0) (2024-07-04)
### Features
* **profiling:** merge VeryLazy stats and show startuptime in profile view ([0f2786b](https://github.com/folke/lazy.nvim/commit/0f2786bcc91347188627534471ee75c3f6f16b2d))
### Bug Fixes
* **config:** determine headless only during startup. Fixes [#1608](https://github.com/folke/lazy.nvim/issues/1608) ([6fdd904](https://github.com/folke/lazy.nvim/commit/6fdd904ee45b66d933c5d2f72bcec337e13744f8))
* **plugin:** local spec name ([923e1aa](https://github.com/folke/lazy.nvim/commit/923e1aa7a49d945afa4c03da4f8ff052cd6d14a6))
## [11.9.2](https://github.com/folke/lazy.nvim/compare/v11.9.1...v11.9.2) (2024-07-02)
### Bug Fixes
* **async:** make asyncs abortable ([1fad617](https://github.com/folke/lazy.nvim/commit/1fad61712bd3937dda925775a7736b8efbcbf1a7))
* **health:** check for errors when executing commands. Closes [#1599](https://github.com/folke/lazy.nvim/issues/1599) ([d0921f5](https://github.com/folke/lazy.nvim/commit/d0921f5b9b3d2c5e09618da55a018228edcc4d16))
### Performance Improvements
* **plugin:** minor optim to resolve imports a bit faster ([a9d7ade](https://github.com/folke/lazy.nvim/commit/a9d7ade203b3f3ee3058c082c62afdf8e4bcb416))
## [11.9.1](https://github.com/folke/lazy.nvim/compare/v11.9.0...v11.9.1) (2024-06-30)
### Performance Improvements
* automatically suspend the scheduler when all threads are waiting ([#1591](https://github.com/folke/lazy.nvim/issues/1591)) ([c7ed87f](https://github.com/folke/lazy.nvim/commit/c7ed87f9ca03ea412134d6a6ea55b43232eb6b0c))
* suspend when tasks are active ([2f4ac03](https://github.com/folke/lazy.nvim/commit/2f4ac035bcc66292250de7134d73007b147f64e8))
## [11.9.0](https://github.com/folke/lazy.nvim/compare/v11.8.2...v11.9.0) (2024-06-29)
### Features
* **ui:** use [[ & ]] to navigate between plugins. Fixes [#1463](https://github.com/folke/lazy.nvim/issues/1463) ([5e3c112](https://github.com/folke/lazy.nvim/commit/5e3c112cb32c9cb6e8622aab4446358e039def7c))
### Bug Fixes
* **ui:** when closing details, jump to plugin header. Closes [#1338](https://github.com/folke/lazy.nvim/issues/1338) ([3772914](https://github.com/folke/lazy.nvim/commit/37729140751577e87318c137d90d0e6bb00ceff1))
## [11.8.2](https://github.com/folke/lazy.nvim/compare/v11.8.1...v11.8.2) (2024-06-29)
### Bug Fixes
* **process:** deal with process errors ([a75d950](https://github.com/folke/lazy.nvim/commit/a75d950b8f356733ad2d20c4bdb794179e6d4ff1))
* **ui:** save/restore view right before/after rendering ([5d334b9](https://github.com/folke/lazy.nvim/commit/5d334b9f579aacd09603dd9e19b6730fbfcf4c72))
### Performance Improvements
* **rocks:** `vim.fn.executable` is slow on WSL2, so only check for `luarocks` when needed. Closes [#1585](https://github.com/folke/lazy.nvim/issues/1585) ([9ab3061](https://github.com/folke/lazy.nvim/commit/9ab306169060eeab7ebca00653318683e72ab62d))
## [11.8.1](https://github.com/folke/lazy.nvim/compare/v11.8.0...v11.8.1) (2024-06-29)
### Bug Fixes
* **async:** remove debug assert ([3513227](https://github.com/folke/lazy.nvim/commit/3513227a9a41c8e6366e1719f4cefbe891ca73d2))
## [11.8.0](https://github.com/folke/lazy.nvim/compare/v11.7.0...v11.8.0) (2024-06-29)
### Features
* **plugin:** allow loading specs without pkg ([695a058](https://github.com/folke/lazy.nvim/commit/695a05872a5b44e366e5532eb2fe38a64fae8357))
## [11.7.0](https://github.com/folke/lazy.nvim/compare/v11.6.0...v11.7.0) (2024-06-29)
### Features
* **minit:** fallback to habamax when no colorscheme set ([88f4d13](https://github.com/folke/lazy.nvim/commit/88f4d13e5f489eb30959db03a94ebfa10a78b47f))
## [11.6.0](https://github.com/folke/lazy.nvim/compare/v11.5.2...v11.6.0) (2024-06-29)
### Features
* **task:** build procs can now yield a LazyMsg for more control ([9cf7459](https://github.com/folke/lazy.nvim/commit/9cf745939d792204a18d7ad10a54d22386ececf3))
## [11.5.2](https://github.com/folke/lazy.nvim/compare/v11.5.1...v11.5.2) (2024-06-28)
### Bug Fixes
* **git:** tagrefs ([2a6a2dc](https://github.com/folke/lazy.nvim/commit/2a6a2dce1b14f35e7eb7cbe8f25202ed83cba697))
## [11.5.1](https://github.com/folke/lazy.nvim/compare/v11.5.0...v11.5.1) (2024-06-28)
### Bug Fixes
* **rocks:** lua-5.1. Closes [#1575](https://github.com/folke/lazy.nvim/issues/1575) ([4319846](https://github.com/folke/lazy.nvim/commit/4319846b8c8a05975c4139b0bc9f7e6e7a9e6e21))
* **task:** run on_exit async. See [#1569](https://github.com/folke/lazy.nvim/issues/1569) ([60fe75c](https://github.com/folke/lazy.nvim/commit/60fe75c88db22025989600bb53dba247654d9ed5))
### Performance Improvements
* async render ([ab46edb](https://github.com/folke/lazy.nvim/commit/ab46edbd47fa9f380db65dbf0a7c35d18d810b19))
* use timer instead of check for async executor ([f85575a](https://github.com/folke/lazy.nvim/commit/f85575ab23c81eb897fb2cb1240a0fa1cb41f7f4))
## [11.5.0](https://github.com/folke/lazy.nvim/compare/v11.4.2...v11.5.0) (2024-06-27)
### Features
* added `opts.headless` to control ansi output when running headless ([a0a51c0](https://github.com/folke/lazy.nvim/commit/a0a51c06c2fcddda925667142516c89777eb0c8e))
* added localleader-i to inspect a plugin ([2e1167d](https://github.com/folke/lazy.nvim/commit/2e1167df4ab055e8327317ac38210b111cbaec83))
* **health:** show steps to get luarocks working. See [#1570](https://github.com/folke/lazy.nvim/issues/1570) ([c0fd59b](https://github.com/folke/lazy.nvim/commit/c0fd59b020dc4efb91b226b0bbc4a22f28c12321))
* **health:** show user's lazy.nvim version in checkhealth ([9c8e7a4](https://github.com/folke/lazy.nvim/commit/9c8e7a48406109458370f3b52f6f058943db40f4))
* **ui:** keep cursor position when rendering view ([591ded8](https://github.com/folke/lazy.nvim/commit/591ded8309e45806ae3fb58b7b68fe58785a3ada))
* **ui:** remap gx -&gt; K. Fixes [#1561](https://github.com/folke/lazy.nvim/issues/1561) ([e3e4314](https://github.com/folke/lazy.nvim/commit/e3e431480d6c9ab460cf08b1e35311c2ab2c05c4))
* **ui:** show indication of plugins that need build. See [#1563](https://github.com/folke/lazy.nvim/issues/1563) ([53f314d](https://github.com/folke/lazy.nvim/commit/53f314d9e6ef594677acdf5f038a4a042a7f3e38))
### Bug Fixes
* **manage:** dont skip install for plugins that need a build, but dont have an url (like local plugins). Fixes [#1563](https://github.com/folke/lazy.nvim/issues/1563) ([a0391c3](https://github.com/folke/lazy.nvim/commit/a0391c3e21e063df9dee70f17ae7891497cdcec9))
* **meta:** resolve deps from meta instead of fragments. Fixes [#1566](https://github.com/folke/lazy.nvim/issues/1566) ([6a42327](https://github.com/folke/lazy.nvim/commit/6a423278a10ff7b1a76795275111d01632851c48))
* **pkg:** only show pkg changed when effectively changing a pkg file. Fixes [#1567](https://github.com/folke/lazy.nvim/issues/1567) ([24a86d5](https://github.com/folke/lazy.nvim/commit/24a86d5ca4652a77f0f2c78dd7c693a3c369ab68))
* **rocks:** if installing with luarocks (binaries) fails, then build from source. Fixes [#1563](https://github.com/folke/lazy.nvim/issues/1563) ([8227632](https://github.com/folke/lazy.nvim/commit/82276321f5132c680a852bec0bb9b55694ab2a21))
* **runner:** only check for errors when a task is no longer running ([e02c5b1](https://github.com/folke/lazy.nvim/commit/e02c5b1b5787210dfbf89681d94e7861b30aa139))
* **runner:** only use Config.plugins when updated. Fixes [#1560](https://github.com/folke/lazy.nvim/issues/1560) ([97f4df0](https://github.com/folke/lazy.nvim/commit/97f4df0824da13b2b0d065f0dc43c49862581a01))
* **runner:** properly do concurrency ([66a4170](https://github.com/folke/lazy.nvim/commit/66a4170f0e9ab238972f73a268582cf65026a017))
* **runner:** wait_step ([93b3a77](https://github.com/folke/lazy.nvim/commit/93b3a77286c4212850e21a6b3e31d328b5a86df4))
* **ui:** diagnostics without status ([249902a](https://github.com/folke/lazy.nvim/commit/249902ab3194226efec0dbc3e000e758c43b4714))
### Performance Improvements
* prevent active waiting in coroutines. suspend/resume instead ([68cee30](https://github.com/folke/lazy.nvim/commit/68cee30cdb1f7a29d10b44b00506aafa092b6cee))
## [11.4.2](https://github.com/folke/lazy.nvim/compare/v11.4.1...v11.4.2) (2024-06-26)
### Bug Fixes
* **config:** dont start checker/change_detection when running headless ([2aa8e06](https://github.com/folke/lazy.nvim/commit/2aa8e061f22579b0cabc74f05a90f7344d92195c))
* **git:** fetch commit from origin or local to check if branch was changed. See [#1549](https://github.com/folke/lazy.nvim/issues/1549) ([28e435b](https://github.com/folke/lazy.nvim/commit/28e435b7f34eecd8b90bc87ac71c70b79fcb03b3))
* **rocks:** build.type instead of build.build_type ([aa1c957](https://github.com/folke/lazy.nvim/commit/aa1c9572aa1916e582f9b9c3d43e272b4f23b326))
* **rockspec:** dont lazy-load rock deps ([4733611](https://github.com/folke/lazy.nvim/commit/473361139cc05936cd5afb08ab68e5bee1ebb5b3))
* **runner:** bring concurrency back ([56075b5](https://github.com/folke/lazy.nvim/commit/56075b57c421fc5e751c1da7a7f1bf18ec1499a7))
* **ui:** don't show output when it's the same as error ([e79805d](https://github.com/folke/lazy.nvim/commit/e79805d706f815a62467260cb307844c368c3dba))
### Performance Improvements
* tasks are now fully async ([0614ca6](https://github.com/folke/lazy.nvim/commit/0614ca6ca629704cb1846c0d6f9a250b526900b9))
* **util:** improve impl of throttle ([3695215](https://github.com/folke/lazy.nvim/commit/36952153ecb5b196c74e2d9a28eb0e01a9eb02fe))
## [11.4.1](https://github.com/folke/lazy.nvim/compare/v11.4.0...v11.4.1) (2024-06-25)
### Bug Fixes
* **health:** show what plugins need luarocks and if none, use warnings instead of errors. See [#1551](https://github.com/folke/lazy.nvim/issues/1551) ([0d9fd63](https://github.com/folke/lazy.nvim/commit/0d9fd636beb9e3783edcdba2b31932280bdc05f7))
## [11.4.0](https://github.com/folke/lazy.nvim/compare/v11.3.0...v11.4.0) (2024-06-25)
### Features
* **pkg:** utils to get rock to url mappings ([be74a8a](https://github.com/folke/lazy.nvim/commit/be74a8a535fea6a480143fb52b4d6958d9e2da94))
* **rocks:** simple rockspecs are now fully resolved by lazy without luarocks. See [#1548](https://github.com/folke/lazy.nvim/issues/1548) ([6b8bf58](https://github.com/folke/lazy.nvim/commit/6b8bf58ebf9114f8f31fb78cbf057e452cb0e540))
### Bug Fixes
* **meta:** only tag new top-level pkg fragment as optional ([25981e1](https://github.com/folke/lazy.nvim/commit/25981e1f3927ee0b22aefea122ebac1cddafdca6))
## [11.3.0](https://github.com/folke/lazy.nvim/compare/v11.2.1...v11.3.0) (2024-06-25)
### Features
* **rocks:** use hererocks to install luarocks when luarocks is not found ([d87da76](https://github.com/folke/lazy.nvim/commit/d87da7667939deff2ed8b5a3c198d9ea2e03fee4))
### Bug Fixes
* **fragments:** check for empty plugin names ([dea1f68](https://github.com/folke/lazy.nvim/commit/dea1f687fe6e15eb3098557a69d44231ebcb6cf5))
* **meta:** no need to check for old_dir when frags were not built yet. Fixes [#1550](https://github.com/folke/lazy.nvim/issues/1550) ([24c8322](https://github.com/folke/lazy.nvim/commit/24c832213c505a0d7ca021c0e14bba43e0fef75c))
* **rocks:** better errors / warnings when something goes wrong with luarocks ([7d3f691](https://github.com/folke/lazy.nvim/commit/7d3f69104fb39d3e6e12f808204b3a7b38f86916))
* **rocks:** hererocks paths on windows ([45cd8d3](https://github.com/folke/lazy.nvim/commit/45cd8d3f0fab197e6e0391cffa38879bdda4c2cd))
* **rocks:** windows ([4ca3e9a](https://github.com/folke/lazy.nvim/commit/4ca3e9aa51c03dda73b40ec9901deac5d4f11c69))
## [11.2.1](https://github.com/folke/lazy.nvim/compare/v11.2.0...v11.2.1) (2024-06-24)
### Bug Fixes
* **loader:** no need to check plugin.dir in auto_load ([62a47b9](https://github.com/folke/lazy.nvim/commit/62a47b921fbffb3c1c8088a731029ae234f98851))
## [11.2.0](https://github.com/folke/lazy.nvim/compare/v11.1.0...v11.2.0) (2024-06-24)
### Features
* rewrite some known plugins to lazy specs instead of luarocks (plenary.nvim). Closes [#1540](https://github.com/folke/lazy.nvim/issues/1540) ([a089d43](https://github.com/folke/lazy.nvim/commit/a089d43dba7438532c56e1c582c5974713bd48f8))
### Performance Improvements
* minimize meta rebuild when loading specs ([1446f6c](https://github.com/folke/lazy.nvim/commit/1446f6cfbb4ca0a7ee0baf3acc86ab5e4be5ab22))
## [11.1.0](https://github.com/folke/lazy.nvim/compare/v11.0.1...v11.1.0) (2024-06-24)
### Features
* make it easier to disable luarocks ([07c067a](https://github.com/folke/lazy.nvim/commit/07c067a1a82bb0988179e1887bba9df4721b3ea7))
* show rockspec deps in plugin details ([656d3d1](https://github.com/folke/lazy.nvim/commit/656d3d1f5b5910e50af3d67286999ff7088ebfb6))
### Bug Fixes
* **health:** added luarocks check to health ([0f45c0d](https://github.com/folke/lazy.nvim/commit/0f45c0d0623b4850716898a5e399c844466690f6))
* **health:** only check for luarocks when luarocks is enabled. ([ae4881d](https://github.com/folke/lazy.nvim/commit/ae4881d36e7f589124f8eb7febfc6a8b58f8e027))
* **health:** show missing luarocks as warning ([e3ee51b](https://github.com/folke/lazy.nvim/commit/e3ee51b6680a116649da56f6c651d53c3e47be4e))
* **runner:** sync package specs after installing and before building ([105d480](https://github.com/folke/lazy.nvim/commit/105d4805ad58875d0b0fafe1185679539b8bc69a))
## [11.0.1](https://github.com/folke/lazy.nvim/compare/v11.0.0...v11.0.1) (2024-06-24)
### Bug Fixes
* **rocks:** dont trigger rebuild for luarocks when build is overriden ([146de4e](https://github.com/folke/lazy.nvim/commit/146de4e801f9169e79052a51365eaae789094611))
## [11.0.0](https://github.com/folke/lazy.nvim/compare/v10.24.3...v11.0.0) (2024-06-24)
### ⚠ BREAKING CHANGES
* new docs for v11.0
### Features
* added support for plugin packages by lazy, rockspec and packspec ([3be55a4](https://github.com/folke/lazy.nvim/commit/3be55a46158cde17e2b853e531d260f3738a5346))
* **build:** build files and functions are now async. use coroutine.yield to interrupt and report progress ([368747b](https://github.com/folke/lazy.nvim/commit/368747bc9a314b4f0745547ebdcc3fbc4d100c0a))
* luarocks support ([f1ba2e3](https://github.com/folke/lazy.nvim/commit/f1ba2e3d057ae5c03d04134a9d538d0b2251f13b))
* **meta:** check for dir changes for plugins already added to the rtp ([ee2ca39](https://github.com/folke/lazy.nvim/commit/ee2ca39f672a2d6f4cbb683b525e6b3d91f3fc0c))
* new docs for v11.0 ([183f59e](https://github.com/folke/lazy.nvim/commit/183f59e2e85dea0c38ed7d16c7c7e543c0b739c7))
* packspec ([8eba74c](https://github.com/folke/lazy.nvim/commit/8eba74c3fc41e1a364225f744022f8b3ff11d796))
* **pkg:** import package specs in the scope of the plugin ([c1912e2](https://github.com/folke/lazy.nvim/commit/c1912e23481ba72a8d8f7a5c736f5e2547e6853e))
* rewrite of spec resolving ([75ffe56](https://github.com/folke/lazy.nvim/commit/75ffe56f70faac43f077796b91178d2f1419f8ce))
* spec.rocks is no longer needed & added support for installing any luarock ([fcfd548](https://github.com/folke/lazy.nvim/commit/fcfd54835da5af64c6046060f4db62c4626d209c))
### Bug Fixes
* **fragments:** prevent adding the same spec instance more than once ([dbffad6](https://github.com/folke/lazy.nvim/commit/dbffad6f44674a3c1b23c649a0abab299d7349d8))
* **luarocks:** cleanup luarocks when deleting a plugin ([b73c57e](https://github.com/folke/lazy.nvim/commit/b73c57ed9ec8e63bbb867d21a3f3a865224b25d4))
* **pkg:** automatically update pkgs when editing a pkg file ([7b6ddbf](https://github.com/folke/lazy.nvim/commit/7b6ddbfc137ad5d8b178a3bbf5a1338630f30625))
* **pkg:** correctly pre-load package specs and remove them when needed during resolve ([4326d4b](https://github.com/folke/lazy.nvim/commit/4326d4b487d4facc19b375ca30cd633cf56d88ed))
* **pkg:** make sure state dir exists ([3515cb5](https://github.com/folke/lazy.nvim/commit/3515cb518f61c02b41cd3a8d8135c9a5862a982f))
* **pkg:** versioning and reload specs when pkg-cache is dirty ([fd8229d](https://github.com/folke/lazy.nvim/commit/fd8229d6e312e83d6bafda256adf0e650b13ca01))
* **rocks:** only build rockspec when it has deps or an advanced build step ([9a6c219](https://github.com/folke/lazy.nvim/commit/9a6c21982638b6e2ea498514baee9186c0e60d82))
## [10.24.3](https://github.com/folke/lazy.nvim/compare/v10.24.2...v10.24.3) (2024-06-23)
### Bug Fixes
* **util:** dump ([025520d](https://github.com/folke/lazy.nvim/commit/025520d083c61baa7cd1f45807f5fe1ac9fbb50d))
## [10.24.2](https://github.com/folke/lazy.nvim/compare/v10.24.1...v10.24.2) (2024-06-16)
### Bug Fixes
* **plugin:** rebuild optional when needed and remove frags from parent deps. Fixes [#1402](https://github.com/folke/lazy.nvim/issues/1402) ([b4316da](https://github.com/folke/lazy.nvim/commit/b4316da7310682144c279c5f0451e59ee5f6c9d1))
## [10.24.1](https://github.com/folke/lazy.nvim/compare/v10.24.0...v10.24.1) (2024-06-16)
### Bug Fixes
* **plugin:** better way of dealing with local specs. Fixes [#1524](https://github.com/folke/lazy.nvim/issues/1524) ([be5dfba](https://github.com/folke/lazy.nvim/commit/be5dfba54216ccb80959df24d48540f07ee127a3))
## [10.24.0](https://github.com/folke/lazy.nvim/compare/v10.23.0...v10.24.0) (2024-06-15)
### Features
* find local_spec in parent directories as well. Closes [#1519](https://github.com/folke/lazy.nvim/issues/1519) ([e2e10d9](https://github.com/folke/lazy.nvim/commit/e2e10d9cbe133265ccdcc44cafa7c10773d96837))
### Bug Fixes
* **plugin:** check optional plugins again after resolving enabled. Fixes [#1402](https://github.com/folke/lazy.nvim/issues/1402) ([067fd41](https://github.com/folke/lazy.nvim/commit/067fd41933c9f59eb3445eb942052c651a4c9a62))
## [10.23.0](https://github.com/folke/lazy.nvim/compare/v10.22.2...v10.23.0) (2024-06-07)
### Features
* **plugin:** `opts_extend` can be a list of dotted keys that will be extended instead of merged ([1f7b720](https://github.com/folke/lazy.nvim/commit/1f7b720cffa6d8f00ebb040bc60e8e056e0a6002))
* **util:** opts merging now supports lists extending by tagging a table with __extend = true. Use with care ([74fd361](https://github.com/folke/lazy.nvim/commit/74fd3611f291a2506c5534109689bb7b028f0566))
## [10.22.2](https://github.com/folke/lazy.nvim/compare/v10.22.1...v10.22.2) (2024-06-06)
### Bug Fixes
* **keys:** buffer-local nop mappings ([ff90417](https://github.com/folke/lazy.nvim/commit/ff904178089582f90fdc625493f3d3bddbefd6ea))
* **keys:** never lazy-load `&lt;nop&gt;` or empty rhs keymaps ([3e4c795](https://github.com/folke/lazy.nvim/commit/3e4c795cec32481bc6d0b30c05125fdf7ef2d412))
## [10.22.1](https://github.com/folke/lazy.nvim/compare/v10.22.0...v10.22.1) (2024-06-02)
### Bug Fixes
* force new release ([9242edb](https://github.com/folke/lazy.nvim/commit/9242edb73939e7508dbd827e9c013579391f0668))
## [10.22.0](https://github.com/folke/lazy.nvim/compare/v10.21.2...v10.22.0) (2024-06-01)
### Features
* set `vim.env.LAZY` to lazy root ([6a141a6](https://github.com/folke/lazy.nvim/commit/6a141a6dbb6f6b5495ef6716c0dce898546d7b2c))
## [10.21.2](https://github.com/folke/lazy.nvim/compare/v10.21.1...v10.21.2) (2024-05-31)
### Bug Fixes
* **ui:** deduplicate plugins when selecting multiple ([#1491](https://github.com/folke/lazy.nvim/issues/1491)) ([b77aaa0](https://github.com/folke/lazy.nvim/commit/b77aaa08cb5b178ed8662765caa41c70ff254a4c))
## [10.21.1](https://github.com/folke/lazy.nvim/compare/v10.21.0...v10.21.1) (2024-05-31)
### Bug Fixes
* **view:** backward compat for older Neovim versions. Fixes [#1489](https://github.com/folke/lazy.nvim/issues/1489) ([917dfbe](https://github.com/folke/lazy.nvim/commit/917dfbe2a9b606443639d1e809f2e4561a6dd654))
## [10.21.0](https://github.com/folke/lazy.nvim/compare/v10.20.5...v10.21.0) (2024-05-26)
### Features
* added support for local spec files `.lazy.lua` ([9dde1f1](https://github.com/folke/lazy.nvim/commit/9dde1f1bce44a8fd8cb885b5a8e8d47d8fd7b8c1))
* single-plugin keys in the lazy view in visual mode ([#1476](https://github.com/folke/lazy.nvim/issues/1476)) ([7667a73](https://github.com/folke/lazy.nvim/commit/7667a73dee381c5fb7d538f6152aeb591e3f0372))
### Bug Fixes
* **render:** disable underline for diagnostics ([#1478](https://github.com/folke/lazy.nvim/issues/1478)) ([ea7b9c3](https://github.com/folke/lazy.nvim/commit/ea7b9c3c3fd9026e1a5ae27950585df9a42ccd5b))
## [10.20.5](https://github.com/folke/lazy.nvim/compare/v10.20.4...v10.20.5) (2024-05-21)
### Bug Fixes
* **checker:** ignore dev plugins ([#1384](https://github.com/folke/lazy.nvim/issues/1384)) ([2e04a0c](https://github.com/folke/lazy.nvim/commit/2e04a0c02c17facd3772c382099215acbe72535b))
* **git:** force `autocrlf=false`. Fixes [#1055](https://github.com/folke/lazy.nvim/issues/1055) ([d2a4ce2](https://github.com/folke/lazy.nvim/commit/d2a4ce22dc02aa08c176cd7692b5b0ed74e4722b))
* **help:** get rid of any tbl_flatten or iter flatten code ([56a34a8](https://github.com/folke/lazy.nvim/commit/56a34a825b55e0e30cd9df0e055e428a13afd4aa))
* **keys:** properly deal with ft list for keys. Fixes [#1448](https://github.com/folke/lazy.nvim/issues/1448) ([82cf974](https://github.com/folke/lazy.nvim/commit/82cf974e0939b3440c4470cbcd8e7869abfe480b))
* **keys:** properly re-create buffer-local mappings. Fixes [#1448](https://github.com/folke/lazy.nvim/issues/1448) ([39de11a](https://github.com/folke/lazy.nvim/commit/39de11a2fa7f4b91556631c49a673bf3e48bcc16))
* use vim.iter():flatten():totable() over vim.tbl_flatten ([#1454](https://github.com/folke/lazy.nvim/issues/1454)) ([d039aec](https://github.com/folke/lazy.nvim/commit/d039aecddb414c2df9d295e9182ed217196a2c1c))
## [10.20.4](https://github.com/folke/lazy.nvim/compare/v10.20.3...v10.20.4) (2024-05-12)
### Bug Fixes
* **heath:** vim.uv. Fixes [#1412](https://github.com/folke/lazy.nvim/issues/1412) ([481aed7](https://github.com/folke/lazy.nvim/commit/481aed70cc4d8e8a38463fd16edf81a23c153247))
* **reload:** strings in lua reload ([#1439](https://github.com/folke/lazy.nvim/issues/1439)) ([2fcbcaf](https://github.com/folke/lazy.nvim/commit/2fcbcaf07ab79594f2ba25ebf6f4c47e250c33be))
* **ui:** add conditional `nvim_get_hl_by_name` for Neovim 0.8.0 ([#1429](https://github.com/folke/lazy.nvim/issues/1429)) ([24234f4](https://github.com/folke/lazy.nvim/commit/24234f47a21ca690de829ea1159b553a733f3968))
* **ui:** hover now opens repo url when no diff with main. Fixes [#1430](https://github.com/folke/lazy.nvim/issues/1430) ([4084506](https://github.com/folke/lazy.nvim/commit/40845063a2586b725d84d44e41fe2c8737751a30))
* **ui:** set backdrop filetype to `lazy_backdrop`. Fixes [#1399](https://github.com/folke/lazy.nvim/issues/1399) ([31ddbea](https://github.com/folke/lazy.nvim/commit/31ddbea7c10b6920c9077b66c97951ca8682d5c8))
## [10.20.3](https://github.com/folke/lazy.nvim/compare/v10.20.2...v10.20.3) (2024-03-28)
### Bug Fixes
* **ui:** disable backdrop when Neovim is transparent ([0ccf031](https://github.com/folke/lazy.nvim/commit/0ccf0312270d2d976ec551a9034bf05720f2486b))
## [10.20.2](https://github.com/folke/lazy.nvim/compare/v10.20.1...v10.20.2) (2024-03-27)
### Bug Fixes
* **ui:** only enable backdrop when guicolors is set. Fixes [#1387](https://github.com/folke/lazy.nvim/issues/1387) ([d37a76b](https://github.com/folke/lazy.nvim/commit/d37a76b87137c777f3d778ed03729d7f332a85f0))
* **ui:** special handling for floats closed before VimEnter. Seems that WinClosed events dont execute before that. Fixes [#1390](https://github.com/folke/lazy.nvim/issues/1390) ([eefb897](https://github.com/folke/lazy.nvim/commit/eefb8974d6a092da6e1631855e4288499b651fdd))
## [10.20.1](https://github.com/folke/lazy.nvim/compare/v10.20.0...v10.20.1) (2024-03-26)
### Bug Fixes
* **ui:** properly cleanup on `:quit`. Fixes [#1385](https://github.com/folke/lazy.nvim/issues/1385) ([79e2e85](https://github.com/folke/lazy.nvim/commit/79e2e8593410f199b85f5d61a83704a16169282f))
## [10.20.0](https://github.com/folke/lazy.nvim/compare/v10.19.0...v10.20.0) (2024-03-26)
### Features
* **ui:** backdrop for the lazy floating window. Can be disabled with `opts.ui.backdrop` ([a6b74f3](https://github.com/folke/lazy.nvim/commit/a6b74f30d5aab79a40d932f449c0aa5d4a0c6934))
### Bug Fixes
* **types:** fixed type for `version`. Fixes [#1381](https://github.com/folke/lazy.nvim/issues/1381) ([eade87f](https://github.com/folke/lazy.nvim/commit/eade87fb837d6cdeef94587ce5e8c8dfb9f88920))
## [10.19.0](https://github.com/folke/lazy.nvim/compare/v10.18.3...v10.19.0) (2024-03-22)
### Features
* **util:** option to force system app for util.open ([66466a2](https://github.com/folke/lazy.nvim/commit/66466a2594ab0c446193772a68c236c7e4e02ade))
## [10.18.3](https://github.com/folke/lazy.nvim/compare/v10.18.2...v10.18.3) (2024-03-22)
### Bug Fixes
* **cache:** vim.loop fallback ([#1375](https://github.com/folke/lazy.nvim/issues/1375)) ([9131ea4](https://github.com/folke/lazy.nvim/commit/9131ea4c4ae59e347716659088a76d9b9ce3b2f5))
## [10.18.2](https://github.com/folke/lazy.nvim/compare/v10.18.1...v10.18.2) (2024-03-22) ## [10.18.2](https://github.com/folke/lazy.nvim/compare/v10.18.1...v10.18.2) (2024-03-22)

843
README.md
View file

@ -1,4 +1,36 @@
# 💤 lazy.nvim <h4 align="center">
<a href="https://lazy.folke.io/installation">Install</a>
·
<a href="https://lazy.folke.io/configuration">Configure</a>
·
<a href="https://lazy.folke.io">Docs</a>
</h4>
<div align="center"><p>
<a href="https://github.com/folke/lazy.nvim/releases/latest">
<img alt="Latest release" src="https://img.shields.io/github/v/release/folke/lazy.nvim?style=for-the-badge&logo=starship&color=C9CBFF&logoColor=D9E0EE&labelColor=302D41&include_prerelease&sort=semver" />
</a>
<a href="https://github.com/folke/lazy.nvim/pulse">
<img alt="Last commit" src="https://img.shields.io/github/last-commit/folke/lazy.nvim?style=for-the-badge&logo=starship&color=8bd5ca&logoColor=D9E0EE&labelColor=302D41"/>
</a>
<a href="https://github.com/folke/lazy.nvim/blob/main/LICENSE">
<img alt="License" src="https://img.shields.io/github/license/folke/lazy.nvim?style=for-the-badge&logo=starship&color=ee999f&logoColor=D9E0EE&labelColor=302D41" />
</a>
<a href="https://github.com/folke/lazy.nvim/stargazers">
<img alt="Stars" src="https://img.shields.io/github/stars/folke/lazy.nvim?style=for-the-badge&logo=starship&color=c69ff5&logoColor=D9E0EE&labelColor=302D41" />
</a>
<a href="https://github.com/folke/lazy.nvim/issues">
<img alt="Issues" src="https://img.shields.io/github/issues/folke/lazy.nvim?style=for-the-badge&logo=bilibili&color=F5E0DC&logoColor=D9E0EE&labelColor=302D41" />
</a>
<a href="https://github.com/folke/lazy.nvim">
<img alt="Repo Size" src="https://img.shields.io/github/repo-size/folke/lazy.nvim?color=%23DDB6F2&label=SIZE&logo=codesandbox&style=for-the-badge&logoColor=D9E0EE&labelColor=302D41" />
</a>
<a href="https://twitter.com/intent/follow?screen_name=folke">
<img alt="follow on Twitter" src="https://img.shields.io/twitter/follow/folke?style=for-the-badge&logo=twitter&color=8aadf3&logoColor=D9E0EE&labelColor=302D41" />
</a>
</div>
**lazy.nvim** is a modern plugin manager for Neovim. **lazy.nvim** is a modern plugin manager for Neovim.
@ -29,810 +61,9 @@
- Neovim >= **0.8.0** (needs to be built with **LuaJIT**) - Neovim >= **0.8.0** (needs to be built with **LuaJIT**)
- Git >= **2.19.0** (for partial clones support) - Git >= **2.19.0** (for partial clones support)
- a [Nerd Font](https://www.nerdfonts.com/) **_(optional)_** - a [Nerd Font](https://www.nerdfonts.com/) **_(optional)_**
- [luarocks](https://luarocks.org/) to install rockspecs.
You can remove `rockspec` from `opts.pkg.sources` to disable this feature.
## 📦 Installation ## 🚀 Getting Started
You can add the following Lua code to your `init.lua` to bootstrap **lazy.nvim**: Check the [documentation website](https://lazy.folke.io/) for more information.
<!-- bootstrap:start -->
```lua
local lazypath = vim.fn.stdpath("data") .. "/lazy/lazy.nvim"
if not vim.loop.fs_stat(lazypath) then
vim.fn.system({
"git",
"clone",
"--filter=blob:none",
"https://github.com/folke/lazy.nvim.git",
"--branch=stable", -- latest stable release
lazypath,
})
end
vim.opt.rtp:prepend(lazypath)
```
<!-- bootstrap:end -->
Next step is to add **lazy.nvim** below the code added in the prior step in `init.lua`:
```lua
require("lazy").setup(plugins, opts)
```
- **plugins**: this should be a `table` or a `string`
- `table`: a list with your [Plugin Spec](#-plugin-spec)
- `string`: a Lua module name that contains your [Plugin Spec](#-plugin-spec). See [Structuring Your Plugins](#-structuring-your-plugins)
- **opts**: see [Configuration](#%EF%B8%8F-configuration) **_(optional)_**
```lua
-- Example using a list of specs with the default options
vim.g.mapleader = " " -- Make sure to set `mapleader` before lazy so your mappings are correct
vim.g.maplocalleader = "\\" -- Same for `maplocalleader`
require("lazy").setup({
"folke/which-key.nvim",
{ "folke/neoconf.nvim", cmd = "Neoconf" },
"folke/neodev.nvim",
})
```
It is recommended to run `:checkhealth lazy` after installation.
## 🔌 Plugin Spec
| Property | Type | Description |
| ---------------- | ----------------------------------------------------------------------------------------------------------------------------------- | ---------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
| **[1]** | `string?` | Short plugin url. Will be expanded using `config.git.url_format` |
| **dir** | `string?` | A directory pointing to a local plugin |
| **url** | `string?` | A custom git url where the plugin is hosted |
| **name** | `string?` | A custom name for the plugin used for the local plugin directory and as the display name |
| **dev** | `boolean?` | When `true`, a local plugin directory will be used instead. See `config.dev` |
| **lazy** | `boolean?` | When `true`, the plugin will only be loaded when needed. Lazy-loaded plugins are automatically loaded when their Lua modules are `required`, or when one of the lazy-loading handlers triggers |
| **enabled** | `boolean?` or `fun():boolean` | When `false`, or if the `function` returns false, then this plugin will not be included in the spec |
| **cond** | `boolean?` or `fun(LazyPlugin):boolean` | When `false`, or if the `function` returns false, then this plugin will not be loaded. Useful to disable some plugins in vscode, or firenvim for example. |
| **dependencies** | `LazySpec[]` | A list of plugin names or plugin specs that should be loaded when the plugin loads. Dependencies are always lazy-loaded unless specified otherwise. When specifying a name, make sure the plugin spec has been defined somewhere else. |
| **init** | `fun(LazyPlugin)` | `init` functions are always executed during startup |
| **opts** | `table` or `fun(LazyPlugin, opts:table)` | `opts` should be a table (will be merged with parent specs), return a table (replaces parent specs) or should change a table. The table will be passed to the `Plugin.config()` function. Setting this value will imply `Plugin.config()` |
| **config** | `fun(LazyPlugin, opts:table)` or `true` | `config` is executed when the plugin loads. The default implementation will automatically run `require(MAIN).setup(opts)`. Lazy uses several heuristics to determine the plugin's `MAIN` module automatically based on the plugin's **name**. See also `opts`. To use the default implementation without `opts` set `config` to `true`. |
| **main** | `string?` | You can specify the `main` module to use for `config()` and `opts()`, in case it can not be determined automatically. See `config()` |
| **build** | `fun(LazyPlugin)` or `string` or a list of build commands | `build` is executed when a plugin is installed or updated. Before running `build`, a plugin is first loaded. If it's a string it will be ran as a shell command. When prefixed with `:` it is a Neovim command. You can also specify a list to executed multiple build commands. Some plugins provide their own `build.lua` which is automatically used by lazy. So no need to specify a build step for those plugins. |
| **branch** | `string?` | Branch of the repository |
| **tag** | `string?` | Tag of the repository |
| **commit** | `string?` | Commit of the repository |
| **version** | `string?` or `false` to override the default | Version to use from the repository. Full [Semver](https://devhints.io/semver) ranges are supported |
| **pin** | `boolean?` | When `true`, this plugin will not be included in updates |
| **submodules** | `boolean?` | When false, git submodules will not be fetched. Defaults to `true` |
| **event** | `string?` or `string[]` or `fun(self:LazyPlugin, event:string[]):string[]` or `{event:string[]\|string, pattern?:string[]\|string}` | Lazy-load on event. Events can be specified as `BufEnter` or with a pattern like `BufEnter *.lua` |
| **cmd** | `string?` or `string[]` or `fun(self:LazyPlugin, cmd:string[]):string[]` | Lazy-load on command |
| **ft** | `string?` or `string[]` or `fun(self:LazyPlugin, ft:string[]):string[]` | Lazy-load on filetype |
| **keys** | `string?` or `string[]` or `LazyKeysSpec[]` or `fun(self:LazyPlugin, keys:string[]):(string \| LazyKeysSpec)[]` | Lazy-load on key mapping |
| **module** | `false?` | Do not automatically load this Lua module when it's required somewhere |
| **priority** | `number?` | Only useful for **start** plugins (`lazy=false`) to force loading certain plugins first. Default priority is `50`. It's recommended to set this to a high number for colorschemes. |
| **optional** | `boolean?` | When a spec is tagged optional, it will only be included in the final spec, when the same plugin has been specified at least once somewhere else without `optional`. This is mainly useful for Neovim distros, to allow setting options on plugins that may/may not be part of the user's plugins |
### Lazy Loading
**lazy.nvim** automagically lazy-loads Lua modules, so it is not needed to
specify `module=...` everywhere in your plugin specification. This means that if
you have a plugin `A` that is lazy-loaded and a plugin `B` that requires a
module of plugin `A`, then plugin `A` will be loaded on demand as expected.
If you don't want this behavior for a certain plugin, you can specify that with `module=false`.
You can then manually load the plugin with `:Lazy load foobar.nvim`.
You can configure **lazy.nvim** to lazy-load all plugins by default with `config.defaults.lazy = true`.
Additionally, you can also lazy-load on **events**, **commands**,
**file types** and **key mappings**.
Plugins will be lazy-loaded when one of the following is `true`:
- The plugin only exists as a dependency in your spec
- It has an `event`, `cmd`, `ft` or `keys` key
- `config.defaults.lazy == true`
#### 🌈 Colorschemes
Colorscheme plugins can be configured with `lazy=true`. The plugin will automagically load
when doing `colorscheme foobar`.
> **NOTE:** since **start** plugins can possibly change existing highlight groups,
> it's important to make sure that your main **colorscheme** is loaded first.
> To ensure this you can use the `priority=1000` field. **_(see the examples)_**
#### ⌨️ Lazy Key Mappings
The `keys` property can be a `string` or `string[]` for simple normal-mode mappings, or it
can be a `LazyKeysSpec` table with the following key-value pairs:
- **[1]**: (`string`) lhs **_(required)_**
- **[2]**: (`string|fun()`) rhs **_(optional)_**
- **mode**: (`string|string[]`) mode **_(optional, defaults to `"n"`)_**
- **ft**: (`string|string[]`) `filetype` for buffer-local keymaps **_(optional)_**
- any other option valid for `vim.keymap.set`
Key mappings will load the plugin the first time they get executed.
When `[2]` is `nil`, then the real mapping has to be created by the `config()` function.
```lua
-- Example for neo-tree.nvim
{
"nvim-neo-tree/neo-tree.nvim",
keys = {
{ "<leader>ft", "<cmd>Neotree toggle<cr>", desc = "NeoTree" },
},
config = function()
require("neo-tree").setup()
end,
}
```
### Versioning
If you want to install a specific revision of a plugin, you can use `commit`,
`tag`, `branch`, `version`.
The `version` property supports [Semver](https://semver.org/) ranges.
<details>
<summary>Click to see some examples</summary>
- `*`: latest stable version (this excludes pre-release versions)
- `1.2.x`: any version that starts with `1.2`, such as `1.2.0`, `1.2.3`, etc.
- `^1.2.3`: any version that is compatible with `1.2.3`, such as `1.3.0`, `1.4.5`, etc., but not `2.0.0`.
- `~1.2.3`: any version that is compatible with `1.2.3`, such as `1.2.4`, `1.2.5`, but not `1.3.0`.
- `>1.2.3`: any version that is greater than `1.2.3`, such as `1.3.0`, `1.4.5`, etc.
- `>=1.2.3`: any version that is greater than or equal to `1.2.3`, such as `1.2.3`, `1.3.0`, `1.4.5`, etc.
- `<1.2.3`: any version that is less than `1.2.3`, such as `1.1.0`, `1.0.5`, etc.
- `<=1.2.3`: any version that is less than or equal to `1.2.3`, such as `1.2.3`, `1.1.0`, `1.0.5`, etc
</details>
You can set `config.defaults.version = "*"` to install the latest stable
version of plugins that support Semver.
### Examples
<!-- spec:start -->
```lua
return {
-- the colorscheme should be available when starting Neovim
{
"folke/tokyonight.nvim",
lazy = false, -- make sure we load this during startup if it is your main colorscheme
priority = 1000, -- make sure to load this before all the other start plugins
config = function()
-- load the colorscheme here
vim.cmd([[colorscheme tokyonight]])
end,
},
-- I have a separate config.mappings file where I require which-key.
-- With lazy the plugin will be automatically loaded when it is required somewhere
{ "folke/which-key.nvim", lazy = true },
{
"nvim-neorg/neorg",
-- lazy-load on filetype
ft = "norg",
-- options for neorg. This will automatically call `require("neorg").setup(opts)`
opts = {
load = {
["core.defaults"] = {},
},
},
},
{
"dstein64/vim-startuptime",
-- lazy-load on a command
cmd = "StartupTime",
-- init is called during startup. Configuration for vim plugins typically should be set in an init function
init = function()
vim.g.startuptime_tries = 10
end,
},
{
"hrsh7th/nvim-cmp",
-- load cmp on InsertEnter
event = "InsertEnter",
-- these dependencies will only be loaded when cmp loads
-- dependencies are always lazy-loaded unless specified otherwise
dependencies = {
"hrsh7th/cmp-nvim-lsp",
"hrsh7th/cmp-buffer",
},
config = function()
-- ...
end,
},
-- if some code requires a module from an unloaded plugin, it will be automatically loaded.
-- So for api plugins like devicons, we can always set lazy=true
{ "nvim-tree/nvim-web-devicons", lazy = true },
-- you can use the VeryLazy event for things that can
-- load later and are not important for the initial UI
{ "stevearc/dressing.nvim", event = "VeryLazy" },
{
"Wansmer/treesj",
keys = {
{ "J", "<cmd>TSJToggle<cr>", desc = "Join Toggle" },
},
opts = { use_default_keymaps = false, max_join_length = 150 },
},
{
"monaqa/dial.nvim",
-- lazy-load on keys
-- mode is `n` by default. For more advanced options, check the section on key mappings
keys = { "<C-a>", { "<C-x>", mode = "n" } },
},
-- local plugins need to be explicitly configured with dir
{ dir = "~/projects/secret.nvim" },
-- you can use a custom url to fetch a plugin
{ url = "git@github.com:folke/noice.nvim.git" },
-- local plugins can also be configure with the dev option.
-- This will use {config.dev.path}/noice.nvim/ instead of fetching it from Github
-- With the dev option, you can easily switch between the local and installed version of a plugin
{ "folke/noice.nvim", dev = true },
}
```
<!-- spec:end -->
## ⚙️ Configuration
**lazy.nvim** comes with the following defaults:
<!-- config:start -->
```lua
{
root = vim.fn.stdpath("data") .. "/lazy", -- directory where plugins will be installed
defaults = {
lazy = false, -- should plugins be lazy-loaded?
version = nil,
-- default `cond` you can use to globally disable a lot of plugins
-- when running inside vscode for example
cond = nil, ---@type boolean|fun(self:LazyPlugin):boolean|nil
-- version = "*", -- enable this to try installing the latest stable versions of plugins
},
-- leave nil when passing the spec as the first argument to setup()
spec = nil, ---@type LazySpec
lockfile = vim.fn.stdpath("config") .. "/lazy-lock.json", -- lockfile generated after running update.
concurrency = jit.os:find("Windows") and (vim.loop.available_parallelism() * 2) or nil, ---@type number limit the maximum amount of concurrent tasks
git = {
-- defaults for the `Lazy log` command
-- log = { "-10" }, -- show the last 10 commits
log = { "-8" }, -- show commits from the last 3 days
timeout = 120, -- kill processes that take more than 2 minutes
url_format = "https://github.com/%s.git",
-- lazy.nvim requires git >=2.19.0. If you really want to use lazy with an older version,
-- then set the below to false. This should work, but is NOT supported and will
-- increase downloads a lot.
filter = true,
},
dev = {
---@type string | fun(plugin: LazyPlugin): string directory where you store your local plugin projects
path = "~/projects",
---@type string[] plugins that match these patterns will use your local versions instead of being fetched from GitHub
patterns = {}, -- For example {"folke"}
fallback = false, -- Fallback to git when local plugin doesn't exist
},
install = {
-- install missing plugins on startup. This doesn't increase startup time.
missing = true,
-- try to load one of these colorschemes when starting an installation during startup
colorscheme = { "habamax" },
},
ui = {
-- a number <1 is a percentage., >1 is a fixed size
size = { width = 0.8, height = 0.8 },
wrap = true, -- wrap the lines in the ui
-- The border to use for the UI window. Accepts same border values as |nvim_open_win()|.
border = "none",
title = nil, ---@type string only works when border is not "none"
title_pos = "center", ---@type "center" | "left" | "right"
-- Show pills on top of the Lazy window
pills = true, ---@type boolean
icons = {
cmd = " ",
config = "",
event = "",
ft = " ",
init = " ",
import = " ",
keys = " ",
lazy = "󰒲 ",
loaded = "●",
not_loaded = "○",
plugin = " ",
runtime = " ",
require = "󰢱 ",
source = " ",
start = "",
task = "✔ ",
list = {
"●",
"➜",
"★",
"",
},
},
-- leave nil, to automatically select a browser depending on your OS.
-- If you want to use a specific browser, you can define it here
browser = nil, ---@type string?
throttle = 20, -- how frequently should the ui process render events
custom_keys = {
-- You can define custom key maps here. If present, the description will
-- be shown in the help menu.
-- To disable one of the defaults, set it to false.
["<localleader>l"] = {
function(plugin)
require("lazy.util").float_term({ "lazygit", "log" }, {
cwd = plugin.dir,
})
end,
desc = "Open lazygit log",
},
["<localleader>t"] = {
function(plugin)
require("lazy.util").float_term(nil, {
cwd = plugin.dir,
})
end,
desc = "Open terminal in plugin dir",
},
},
},
diff = {
-- diff command <d> can be one of:
-- * browser: opens the github compare view. Note that this is always mapped to <K> as well,
-- so you can have a different command for diff <d>
-- * git: will run git diff and open a buffer with filetype git
-- * terminal_git: will open a pseudo terminal with git diff
-- * diffview.nvim: will open Diffview to show the diff
cmd = "git",
},
checker = {
-- automatically check for plugin updates
enabled = false,
concurrency = nil, ---@type number? set to 1 to check for updates very slowly
notify = true, -- get a notification when new updates are found
frequency = 3600, -- check for updates every hour
check_pinned = false, -- check for pinned packages that can't be updated
},
change_detection = {
-- automatically check for config file changes and reload the ui
enabled = true,
notify = true, -- get a notification when changes are found
},
performance = {
cache = {
enabled = true,
},
reset_packpath = true, -- reset the package path to improve startup time
rtp = {
reset = true, -- reset the runtime path to $VIMRUNTIME and your config directory
---@type string[]
paths = {}, -- add any custom paths here that you want to includes in the rtp
---@type string[] list any plugins you want to disable here
disabled_plugins = {
-- "gzip",
-- "matchit",
-- "matchparen",
-- "netrwPlugin",
-- "tarPlugin",
-- "tohtml",
-- "tutor",
-- "zipPlugin",
},
},
},
-- lazy can generate helptags from the headings in markdown readme files,
-- so :help works even for plugins that don't have vim docs.
-- when the readme opens with :help it will be correctly displayed as markdown
readme = {
enabled = true,
root = vim.fn.stdpath("state") .. "/lazy/readme",
files = { "README.md", "lua/**/README.md" },
-- only generate markdown helptags for plugins that dont have docs
skip_if_doc_exists = true,
},
state = vim.fn.stdpath("state") .. "/lazy/state.json", -- state info for checker and other things
build = {
-- Plugins can provide a `build.lua` file that will be executed when the plugin is installed
-- or updated. When the plugin spec also has a `build` command, the plugin's `build.lua` not be
-- executed. In this case, a warning message will be shown.
warn_on_override = true,
},
-- Enable profiling of lazy.nvim. This will add some overhead,
-- so only enable this when you are debugging lazy.nvim
profiling = {
-- Enables extra stats on the debug tab related to the loader cache.
-- Additionally gathers stats about all package.loaders
loader = false,
-- Track each new require in the Lazy profiling tab
require = false,
},
}
```
<!-- config:end -->
<details>
<summary>If you don't want to use a Nerd Font, you can replace the icons with Unicode symbols.</summary>
```lua
{
ui = {
icons = {
cmd = "⌘",
config = "🛠",
event = "📅",
ft = "📂",
init = "⚙",
keys = "🗝",
plugin = "🔌",
runtime = "💻",
require = "🌙",
source = "📄",
start = "🚀",
task = "📌",
lazy = "💤 ",
},
},
}
```
</details>
## 🚀 Usage
Plugins are managed with the `:Lazy` command.
Open the help with `<?>` to see all the key mappings.
You can press `<CR>` on a plugin to show its details. Most properties
can be hovered with `<K>` to open links, help files, readmes,
git commits and git issues.
Lazy can automatically check for updates in the background. This feature
can be enabled with `config.checker.enabled = true`.
Any operation can be started from the UI, with a sub command or an API function:
<!-- commands:start -->
| Command | Lua | Description |
| ------------------------- | -------------------------------- | ---------------------------------------------------------------------------------------------------------------------------------------------------- |
| `:Lazy build {plugins}` | `require("lazy").build(opts)` | Rebuild a plugin |
| `:Lazy check [plugins]` | `require("lazy").check(opts?)` | Check for updates and show the log (git fetch) |
| `:Lazy clean [plugins]` | `require("lazy").clean(opts?)` | Clean plugins that are no longer needed |
| `:Lazy clear` | `require("lazy").clear()` | Clear finished tasks |
| `:Lazy debug` | `require("lazy").debug()` | Show debug information |
| `:Lazy health` | `require("lazy").health()` | Run `:checkhealth lazy` |
| `:Lazy help` | `require("lazy").help()` | Toggle this help page |
| `:Lazy home` | `require("lazy").home()` | Go back to plugin list |
| `:Lazy install [plugins]` | `require("lazy").install(opts?)` | Install missing plugins |
| `:Lazy load {plugins}` | `require("lazy").load(opts)` | Load a plugin that has not been loaded yet. Similar to `:packadd`. Like `:Lazy load foo.nvim`. Use `:Lazy! load` to skip `cond` checks. |
| `:Lazy log [plugins]` | `require("lazy").log(opts?)` | Show recent updates |
| `:Lazy profile` | `require("lazy").profile()` | Show detailed profiling |
| `:Lazy reload {plugins}` | `require("lazy").reload(opts)` | Reload a plugin (experimental!!) |
| `:Lazy restore [plugins]` | `require("lazy").restore(opts?)` | Updates all plugins to the state in the lockfile. For a single plugin: restore it to the state in the lockfile or to a given commit under the cursor |
| `:Lazy sync [plugins]` | `require("lazy").sync(opts?)` | Run install, clean and update |
| `:Lazy update [plugins]` | `require("lazy").update(opts?)` | Update plugins. This will also update the lockfile |
<!-- commands:end -->
Any command can have a **bang** to make the command wait till it finished. For example,
if you want to sync lazy from the cmdline, you can use:
```shell
nvim --headless "+Lazy! sync" +qa
```
`opts` is a table with the following key-values:
- **wait**: when true, then the call will wait till the operation completed
- **show**: when false, the UI will not be shown
- **plugins**: a list of plugin names to run the operation on
- **concurrency**: limit the `number` of concurrently running tasks
Stats API (`require("lazy").stats()`):
<!-- stats:start -->
```lua
{
-- startuptime in milliseconds till UIEnter
startuptime = 0,
-- when true, startuptime is the accurate cputime for the Neovim process. (Linux & Macos)
-- this is more accurate than `nvim --startuptime`, and as such will be slightly higher
-- when false, startuptime is calculated based on a delta with a timestamp when lazy started.
real_cputime = false,
count = 0, -- total number of plugins
loaded = 0, -- number of loaded plugins
---@type table<string, number>
times = {},
}
```
<!-- stats:end -->
**lazy.nvim** provides a statusline component that you can use to show the number of pending updates.
Make sure to enable `config.checker.enabled = true` to make this work.
<details>
<summary>Example of configuring <a href="https://github.com/nvim-lualine/lualine.nvim">lualine.nvim</a></summary>
```lua
require("lualine").setup({
sections = {
lualine_x = {
{
require("lazy.status").updates,
cond = require("lazy.status").has_updates,
color = { fg = "#ff9e64" },
},
},
},
})
```
</details>
### 📆 User Events
The following user events will be triggered:
- **LazyDone**: when lazy has finished starting up and loaded your config
- **LazySync**: after running sync
- **LazyInstall**: after an install
- **LazyUpdate**: after an update
- **LazyClean**: after a clean
- **LazyCheck**: after checking for updates
- **LazyLog**: after running log
- **LazyLoad**: after loading a plugin. The `data` attribute will contain the plugin name.
- **LazySyncPre**: before running sync
- **LazyInstallPre**: before an install
- **LazyUpdatePre**: before an update
- **LazyCleanPre**: before a clean
- **LazyCheckPre**: before checking for updates
- **LazyLogPre**: before running log
- **LazyReload**: triggered by change detection after reloading plugin specs
- **VeryLazy**: triggered after `LazyDone` and processing `VimEnter` auto commands
- **LazyVimStarted**: triggered after `UIEnter` when `require("lazy").stats().startuptime` has been calculated.
Useful to update the startuptime on your dashboard.
## 🔒 Lockfile `lazy-lock.json`
After every **update**, the local lockfile is updated with the installed revisions.
It is recommended to have this file under version control.
If you use your Neovim config on multiple machines, using the lockfile, you can
ensure that the same version of every plugin is installed.
If you are on another machine, you can do `:Lazy restore`, to update all your plugins to
the version from the lockfile.
## ⚡ Performance
Great care has been taken to make the startup code (`lazy.core`) as efficient as possible.
During startup, all Lua files used before `VimEnter` or `BufReadPre` are byte-compiled and cached,
similar to what [impatient.nvim](https://github.com/lewis6991/impatient.nvim) does.
My config for example loads in about `11ms` with `93` plugins. I do a lot of lazy-loading though :)
**lazy.nvim** comes with an advanced profiler `:Lazy profile` to help you improve performance.
The profiling view shows you why and how long it took to load your plugins.
![image](https://user-images.githubusercontent.com/292349/208301766-5c400561-83c3-4811-9667-1ec4bb3c43b8.png)
## 🐛 Debug
See an overview of active lazy-loading handlers and what's in the module cache.
![image](https://user-images.githubusercontent.com/292349/208301790-7eedbfa5-d202-4e70-852e-de68aa47233b.png)
## ▶️ Startup Sequence
**lazy.nvim** does **NOT** use Neovim packages and even disables plugin loading
completely (`vim.go.loadplugins = false`). It takes over the complete
startup sequence for more flexibility and better performance.
In practice this means that step 10 of [Neovim Initialization](https://neovim.io/doc/user/starting.html#initialization) is done by Lazy:
1. All the plugins' `init()` functions are executed
2. All plugins with `lazy=false` are loaded. This includes sourcing `/plugin` and `/ftdetect` files. (`/after` will not be sourced yet)
3. All files from `/plugin` and `/ftdetect` directories in your rtp are sourced (excluding `/after`)
4. All `/after/plugin` files are sourced (this includes `/after` from plugins)
Files from runtime directories are always sourced in alphabetical order.
## 📂 Structuring Your Plugins
Some users may want to split their plugin specs in multiple files.
Instead of passing a spec table to `setup()`, you can use a Lua module.
The specs from the **module** and any top-level **sub-modules** will be merged together in the final spec,
so it is not needed to add `require` calls in your main plugin file to the other files.
The benefits of using this approach:
- Simple to **add** new plugin specs. Just create a new file in your plugins module.
- Allows for **caching** of all your plugin specs. This becomes important if you have a lot of smaller plugin specs.
- Spec changes will automatically be **reloaded** when they're updated, so the `:Lazy` UI is always up to date.
Example:
- `~/.config/nvim/init.lua`
```lua
require("lazy").setup("plugins")
```
- `~/.config/nvim/lua/plugins.lua` or `~/.config/nvim/lua/plugins/init.lua` **_(this file is optional)_**
```lua
return {
"folke/neodev.nvim",
"folke/which-key.nvim",
{ "folke/neoconf.nvim", cmd = "Neoconf" },
}
```
- Any lua file in `~/.config/nvim/lua/plugins/*.lua` will be automatically merged in the main plugin spec
For a real-life example, you can check [LazyVim](https://github.com/LazyVim/LazyVim) and more specifically:
- [lazyvim.plugins](https://github.com/LazyVim/LazyVim/tree/main/lua/lazyvim/plugins) contains all the plugin specs that will be loaded
### ↩️ Importing Specs, `config` & `opts`
As part of a spec, you can add `import` statements to import additional plugin modules.
Both of the `setup()` calls are equivalent:
```lua
require("lazy").setup("plugins")
-- Same as:
require("lazy").setup({{import = "plugins"}})
```
To import multiple modules from a plugin, add additional specs for each import.
For example, to import LazyVim core plugins and an optional plugin:
```lua
require("lazy").setup({
spec = {
{ "LazyVim/LazyVim", import = "lazyvim.plugins" },
{ import = "lazyvim.plugins.extras.coding.copilot" },
}
})
```
When you import specs, you can override them by simply adding a spec for the same plugin to your local
specs, adding any keys you want to override / merge.
`opts`, `dependencies`, `cmd`, `event`, `ft` and `keys` are always merged with the parent spec.
Any other property will override the property from the parent spec.
## 📦 Migration Guide
### [packer.nvim](https://github.com/wbthomason/packer.nvim)
- `setup` ➡️ `init`
- `requires` ➡️ `dependencies`
- `as` ➡️ `name`
- `opt` ➡️ `lazy`
- `run` ➡️ `build`
- `lock` ➡️ `pin`
- `disable=true` ➡️ `enabled = false`
- `tag='*'` ➡️ `version="*"`
- `after` is **_not needed_** for most use-cases. Use `dependencies` otherwise.
- `wants` is **_not needed_** for most use-cases. Use `dependencies` otherwise.
- `config` don't support string type, use `fun(LazyPlugin)` instead.
- `module` is auto-loaded. No need to specify
- `keys` spec is [different](#%EF%B8%8F-lazy-key-mappings)
- `rtp` can be accomplished with:
```lua
config = function(plugin)
vim.opt.rtp:append(plugin.dir .. "/custom-rtp")
end
```
With packer `wants`, `requires` and `after` can be used to manage dependencies.
With lazy, this isn't needed for most of the Lua dependencies. They can be installed just like normal plugins
(even with `lazy=true`) and will be loaded when other plugins need them.
The `dependencies` key can be used to group those required plugins with the one that requires them.
The plugins which are added as `dependencies` will always be lazy-loaded and loaded when the plugin is loaded.
### [paq-nvim](https://github.com/savq/paq-nvim)
- `as` ➡️ `name`
- `opt` ➡️ `lazy`
- `run` ➡️ `build`
## ❌ Uninstalling
To uninstall **lazy.nvim**, you need to remove the following files and directories:
- **data**: `~/.local/share/nvim/lazy`
- **state**: `~/.local/state/nvim/lazy`
- **lockfile**: `~/.config/nvim/lazy-lock.json`
> Paths can differ if you changed `XDG` environment variables.
## 🌈 Highlight Groups
<details>
<summary>Click to see all highlight groups</summary>
<!-- colors:start -->
| Highlight Group | Default Group | Description |
| --------------------- | -------------------------- | --------------------------------------------------- |
| **LazyButton** | **_CursorLine_** | |
| **LazyButtonActive** | **_Visual_** | |
| **LazyComment** | **_Comment_** | |
| **LazyCommit** | **_@variable.builtin_** | commit ref |
| **LazyCommitIssue** | **_Number_** | |
| **LazyCommitScope** | **_Italic_** | conventional commit scope |
| **LazyCommitType** | **_Title_** | conventional commit type |
| **LazyDimmed** | **_Conceal_** | property |
| **LazyDir** | **_@markup.link_** | directory |
| **LazyH1** | **_IncSearch_** | home button |
| **LazyH2** | **_Bold_** | titles |
| **LazyLocal** | **_Constant_** | |
| **LazyNoCond** | **_DiagnosticWarn_** | unloaded icon for a plugin where `cond()` was false |
| **LazyNormal** | **_NormalFloat_** | |
| **LazyProgressDone** | **_Constant_** | progress bar done |
| **LazyProgressTodo** | **_LineNr_** | progress bar todo |
| **LazyProp** | **_Conceal_** | property |
| **LazyReasonCmd** | **_Operator_** | |
| **LazyReasonEvent** | **_Constant_** | |
| **LazyReasonFt** | **_Character_** | |
| **LazyReasonImport** | **_Identifier_** | |
| **LazyReasonKeys** | **_Statement_** | |
| **LazyReasonPlugin** | **_Special_** | |
| **LazyReasonRequire** | **_@variable.parameter_** | |
| **LazyReasonRuntime** | **_@macro_** | |
| **LazyReasonSource** | **_Character_** | |
| **LazyReasonStart** | **_@variable.member_** | |
| **LazySpecial** | **_@punctuation.special_** | |
| **LazyTaskError** | **_ErrorMsg_** | task errors |
| **LazyTaskOutput** | **_MsgArea_** | task output |
| **LazyUrl** | **_@markup.link_** | url |
| **LazyValue** | **_@string_** | value of a property |
<!-- colors:end -->
</details>
## 📚 Plugin Authors
If your plugin needs a build step, you can create a file `build.lua` or `build/init.lua`
in the root of your repo. This file will be loaded when the plugin is installed or updated.
This makes it easier for users, as they no longer need to specify a `build` command.
## 📦 Other Neovim Plugin Managers in Lua
- [pckr.nvim](https://github.com/lewis6991/pckr.nvim)
- [packer.nvim](https://github.com/wbthomason/packer.nvim)
- [paq-nvim](https://github.com/savq/paq-nvim)
- [neopm](https://github.com/ii14/neopm)
- [dep](https://github.com/chiyadev/dep)
- [optpack.nvim](https://github.com/notomo/optpack.nvim)
- [pact.nvim](https://github.com/rktjmp/pact.nvim)

View file

@ -51,7 +51,7 @@
- [ ] add support to specify `engines`, `os` and `cpu` like in `package.json` - [ ] add support to specify `engines`, `os` and `cpu` like in `package.json`
- [ ] semver merging. Should check if two or more semver ranges are compatible and calculate the union range - [ ] semver merging. Should check if two or more semver ranges are compatible and calculate the union range
- default semver merging strategy: if no version matches all, then use highest version? - default semver merging strategy: if no version matches all, then use the highest version?
- [ ] package meta index (package.lua cache for all packages) - [ ] package meta index (package.lua cache for all packages)
- [x] document highlight groups - [x] document highlight groups
@ -65,7 +65,7 @@
Maybe a quick, "for example, if you have a lua file Maybe a quick, "for example, if you have a lua file
`~/.config/nvim/lua/config/plugins.lua` that returns a table" or something it'd `~/.config/nvim/lua/config/plugins.lua` that returns a table" or something it'd
remove most question marks I think. remove most question marks I think.
- [x] When autoinstalling the plugins the cursor isn't focused on the floating - [x] When auto-installing the plugins the cursor isn't focused on the floating
window, but on the non-floating window in the background. window, but on the non-floating window in the background.
- [x] Doing `:Lazy clean` doesn't show which plugins were removed. - [x] Doing `:Lazy clean` doesn't show which plugins were removed.
- [x] Shouldn't the "Versioning" section be in the "Lockfile" chapter? - [x] Shouldn't the "Versioning" section be in the "Lockfile" chapter?

51
bootstrap.lua Normal file
View file

@ -0,0 +1,51 @@
-- Lazy Bootstrapper
-- Usage:
-- ```lua
-- load(vim.fn.system("curl -s https://raw.githubusercontent.com/folke/lazy.nvim/main/bootstrap.lua"))()
-- ```
local M = {}
function M.setup()
local uv = vim.uv or vim.loop
if vim.env.LAZY_STDPATH then
local root = vim.fn.fnamemodify(vim.env.LAZY_STDPATH, ":p"):gsub("[\\/]$", "")
for _, name in ipairs({ "config", "data", "state", "cache" }) do
vim.env[("XDG_%s_HOME"):format(name:upper())] = root .. "/" .. name
end
end
if vim.env.LAZY_PATH and not uv.fs_stat(vim.env.LAZY_PATH) then
vim.env.LAZY_PATH = nil
end
local lazypath = vim.env.LAZY_PATH or vim.fn.stdpath("data") .. "/lazy/lazy.nvim"
if not vim.env.LAZY_PATH and not uv.fs_stat(lazypath) then
vim.api.nvim_echo({
{
"Cloning lazy.nvim\n\n",
"DiagnosticInfo",
},
}, true, {})
local lazyrepo = "https://github.com/folke/lazy.nvim.git"
local ok, out = pcall(vim.fn.system, {
"git",
"clone",
"--filter=blob:none",
lazyrepo,
lazypath,
})
if not ok or vim.v.shell_error ~= 0 then
vim.api.nvim_echo({
{ "Failed to clone lazy.nvim\n", "ErrorMsg" },
{ vim.trim(out or ""), "WarningMsg" },
{ "\nPress any key to exit...", "MoreMsg" },
}, true, {})
vim.fn.getchar()
os.exit(1)
end
end
vim.opt.rtp:prepend(lazypath)
end
M.setup()
return M

0
doc/.keep Normal file
View file

File diff suppressed because it is too large Load diff

222
lua/lazy/async.lua Normal file
View file

@ -0,0 +1,222 @@
local Util = require("lazy.core.util")
local M = {}
---@type Async[]
M._active = {}
---@type Async[]
M._suspended = {}
M._executor = assert(vim.loop.new_check())
M.BUDGET = 10
---@type table<thread, Async>
M._threads = setmetatable({}, { __mode = "k" })
---@alias AsyncEvent "done" | "error" | "yield" | "ok"
---@class Async
---@field _co thread
---@field _fn fun()
---@field _suspended? boolean
---@field _on table<AsyncEvent, fun(res:any, async:Async)[]>
local Async = {}
---@param fn async fun()
---@return Async
function Async.new(fn)
local self = setmetatable({}, { __index = Async })
return self:init(fn)
end
---@param fn async fun()
---@return Async
function Async:init(fn)
self._fn = fn
self._on = {}
self._co = coroutine.create(function()
local ok, err = pcall(self._fn)
if not ok then
self:_emit("error", err)
end
self:_emit("done")
end)
M._threads[self._co] = self
return M.add(self)
end
---@param event AsyncEvent
---@param cb async fun(res:any, async:Async)
function Async:on(event, cb)
self._on[event] = self._on[event] or {}
table.insert(self._on[event], cb)
return self
end
---@private
---@param event AsyncEvent
---@param res any
function Async:_emit(event, res)
for _, cb in ipairs(self._on[event] or {}) do
cb(res, self)
end
end
function Async:running()
return coroutine.status(self._co) ~= "dead"
end
---@async
function Async:sleep(ms)
vim.defer_fn(function()
self:resume()
end, ms)
self:suspend()
end
---@async
---@param yield? boolean
function Async:suspend(yield)
self._suspended = true
if coroutine.running() == self._co and yield ~= false then
M.yield()
end
end
function Async:resume()
self._suspended = false
M._run()
end
---@async
---@param yield? boolean
function Async:wake(yield)
local async = M.running()
assert(async, "Not in an async context")
self:on("done", function()
async:resume()
end)
async:suspend(yield)
end
---@async
function Async:wait()
if coroutine.running() == self._co then
error("Cannot wait on self")
end
local async = M.running()
if async then
self:wake()
else
while self:running() do
vim.wait(10)
end
end
return self
end
function Async:step()
if self._suspended then
return true
end
local status = coroutine.status(self._co)
if status == "suspended" then
local ok, res = coroutine.resume(self._co)
if not ok then
error(res)
elseif res then
self:_emit("yield", res)
end
end
return self:running()
end
function M.abort()
for _, async in ipairs(M._active) do
coroutine.resume(async._co, "abort")
end
end
function M.yield()
if coroutine.yield() == "abort" then
error("aborted", 2)
end
end
function M.step()
local start = vim.uv.hrtime()
for _ = 1, #M._active do
if Util.exiting() or vim.uv.hrtime() - start > M.BUDGET * 1e6 then
break
end
local state = table.remove(M._active, 1)
if state:step() then
if state._suspended then
table.insert(M._suspended, state)
else
table.insert(M._active, state)
end
end
end
for _ = 1, #M._suspended do
local state = table.remove(M._suspended, 1)
table.insert(state._suspended and M._suspended or M._active, state)
end
-- M.debug()
if #M._active == 0 or Util.exiting() then
return M._executor:stop()
end
end
function M.debug()
local lines = {
"- active: " .. #M._active,
"- suspended: " .. #M._suspended,
}
for _, async in ipairs(M._active) do
local info = debug.getinfo(async._fn)
local file = vim.fn.fnamemodify(info.short_src:sub(1), ":~:.")
table.insert(lines, ("%s:%d"):format(file, info.linedefined))
if #lines > 10 then
break
end
end
local msg = table.concat(lines, "\n")
M._notif = vim.notify(msg, nil, { replace = M._notif })
end
---@param async Async
function M.add(async)
table.insert(M._active, async)
M._run()
return async
end
function M._run()
if not Util.exiting() and not M._executor:is_active() then
M._executor:start(vim.schedule_wrap(M.step))
end
end
function M.running()
local co = coroutine.running()
if co then
return M._threads[co]
end
end
---@async
---@param ms number
function M.sleep(ms)
local async = M.running()
assert(async, "Not in an async context")
async:sleep(ms)
end
M.Async = Async
M.new = Async.new
return M

100
lua/lazy/build.lua Normal file
View file

@ -0,0 +1,100 @@
vim.opt.rtp:append(".")
local Rocks = require("lazy.pkg.rockspec")
local Semver = require("lazy.manage.semver")
local Util = require("lazy.util")
local M = {}
M.patterns = { "nvim", "treesitter", "tree-sitter", "cmp", "neo" }
function M.fetch(url, file, prefix)
if not vim.uv.fs_stat(file) then
print((prefix or "") .. "Fetching " .. url .. " to " .. file .. "\n")
vim.cmd.redraw()
local out = vim.fn.system("wget " .. url .. " -O " .. file)
if vim.v.shell_error ~= 0 then
pcall(vim.uv.fs_unlink, file)
error("Failed to fetch " .. url .. ":\n" .. out)
end
end
end
---@return RockManifest?
function M.fetch_manifest()
local manifest_file = "build/manifest.lua"
M.fetch("https://luarocks.org/manifest-5.1", manifest_file)
return Rocks.parse(manifest_file)
end
function M.fetch_rockspec(name, version, prefix)
version = version or "scm-1"
local url = "https://luarocks.org/" .. name .. "-" .. version .. ".rockspec"
M.fetch(url, "build/" .. name .. ".rockspec", prefix)
end
function M.build()
vim.fn.mkdir("build", "p")
local manifest = M.fetch_manifest() or {}
---@type {name:string, version:string, url:string}[]
local nvim_rocks = {}
for rock, vv in pairs(manifest.repository or {}) do
local matches = false
for _, pattern in ipairs(M.patterns) do
if rock:find(pattern, 1, true) then
matches = true
break
end
end
if matches then
local versions = vim.tbl_map(Semver.version, vim.tbl_keys(vv))
versions = vim.tbl_filter(function(v)
return not not v
end, versions)
local last = Semver.last(versions) or next(vv)
last = type(last) == "table" and last.input or last
table.insert(nvim_rocks, { name = rock, version = last })
end
end
table.sort(nvim_rocks, function(a, b)
return a.name < b.name
end)
for r, rock in ipairs(nvim_rocks) do
local progress = string.format("[%d/%d] ", r, #nvim_rocks)
local ok, err = pcall(M.fetch_rockspec, rock.name, rock.version, progress)
if not ok then
err = vim.trim("Error: " .. err)
local lines = vim.split(err, "\n")
lines = vim.tbl_map(function(line)
return " " .. line
end, lines)
print(table.concat(lines, "\n") .. "\n")
end
end
for _, rock in ipairs(nvim_rocks) do
local rockspec = Rocks.rockspec("build/" .. rock.name .. ".rockspec")
if rockspec then
local url = rockspec.source and rockspec.source.url
-- parse github short url
if url and url:find("://github.com/") then
url = url:gsub("^.*://github.com/", "")
local parts = vim.split(url, "/")
url = parts[1] .. "/" .. parts[2]
url = url:gsub("%.git$", "")
end
if url then
rock.url = url
print(rock.name .. " " .. url)
else
print("Error: " .. rock.name .. " missing source url\n\n")
print(vim.inspect(rockspec) .. "\n")
end
end
end
Util.write_file("lua/lazy/community/_generated.lua", "return \n" .. vim.inspect(nvim_rocks))
end
M.build()
return M

File diff suppressed because it is too large Load diff

View file

@ -0,0 +1,28 @@
local M = {}
---@type table<string, string>
local mapping = nil
local function load()
if not mapping then
mapping = {}
---@type {name:string, url:string, version:string}[]
local gen = require("lazy.community._generated")
for _, rock in ipairs(gen) do
mapping[rock.name] = rock.url
end
end
return mapping
end
---@param rock string
---@return string?
function M.get_url(rock)
return load()[rock]
end
function M.get_spec(name)
return require("lazy.community.specs")[name]
end
return M

View file

@ -0,0 +1,7 @@
---@type table<string, LazySpec>
return {
["plenary.nvim"] = {
"nvim-lua/plenary.nvim",
lazy = true,
},
}

View file

@ -1,4 +1,4 @@
local uv = vim.uv local uv = vim.uv or vim.loop
local M = {} local M = {}
@ -8,7 +8,7 @@ local M = {}
---@class ModuleFindOpts ---@class ModuleFindOpts
---@field all? boolean Search for all matches (defaults to `false`) ---@field all? boolean Search for all matches (defaults to `false`)
---@field rtp? boolean Search for modname in the runtime path (defaults to `true`) ---@field rtp? boolean Search for modname in the runtime path (defaults to `true`)
---@field patterns? string[] Paterns to use (defaults to `{"/init.lua", ".lua"}`) ---@field patterns? string[] Patterns to use (defaults to `{"/init.lua", ".lua"}`)
---@field paths? string[] Extra paths to search for modname ---@field paths? string[] Extra paths to search for modname
---@class ModuleInfo ---@class ModuleInfo
@ -51,7 +51,7 @@ end
---@private ---@private
function Loader.normalize(path) function Loader.normalize(path)
if path:sub(1, 1) == "~" then if path:sub(1, 1) == "~" then
local home = vim.uv.os_homedir() or "~" local home = uv.os_homedir() or "~"
if home:sub(-1) == "\\" or home:sub(-1) == "/" then if home:sub(-1) == "\\" or home:sub(-1) == "/" then
home = home:sub(1, -2) home = home:sub(1, -2)
end end
@ -442,9 +442,9 @@ function Loader.lsmod(path)
if not Loader._indexed[path] then if not Loader._indexed[path] then
local start = uv.hrtime() local start = uv.hrtime()
Loader._indexed[path] = {} Loader._indexed[path] = {}
local handle = vim.uv.fs_scandir(path .. "/lua") local handle = uv.fs_scandir(path .. "/lua")
while handle do while handle do
local name, t = vim.uv.fs_scandir_next(handle) local name, t = uv.fs_scandir_next(handle)
if not name then if not name then
break break
end end
@ -474,13 +474,13 @@ function Loader.lsmod(path)
return Loader._indexed[path] return Loader._indexed[path]
end end
--- Debug function that wrapps all loaders and tracks stats --- Debug function that wraps all loaders and tracks stats
---@private ---@private
function M._profile_loaders() function M._profile_loaders()
for l, loader in pairs(package.loaders) do for l, loader in pairs(package.loaders) do
local loc = debug.getinfo(loader, "Sn").source:sub(2) local loc = debug.getinfo(loader, "Sn").source:sub(2)
package.loaders[l] = function(modname) package.loaders[l] = function(modname)
local start = vim.uv.hrtime() local start = uv.hrtime()
local ret = loader(modname) local ret = loader(modname)
Loader.track("loader " .. l .. ": " .. loc, start) Loader.track("loader " .. l .. ": " .. loc, start)
Loader.track("loader_all", start) Loader.track("loader_all", start)

View file

@ -7,17 +7,23 @@ local M = {}
M.defaults = { M.defaults = {
root = vim.fn.stdpath("data") .. "/lazy", -- directory where plugins will be installed root = vim.fn.stdpath("data") .. "/lazy", -- directory where plugins will be installed
defaults = { defaults = {
-- Set this to `true` to have all your plugins lazy-loaded by default.
-- Only do this if you know what you are doing, as it can lead to unexpected behavior.
lazy = false, -- should plugins be lazy-loaded? lazy = false, -- should plugins be lazy-loaded?
version = nil, -- It's recommended to leave version=false for now, since a lot the plugin that support versioning,
-- have outdated releases, which may break your Neovim install.
version = nil, -- always use the latest git commit
-- version = "*", -- try installing the latest stable version for plugins that support semver
-- default `cond` you can use to globally disable a lot of plugins -- default `cond` you can use to globally disable a lot of plugins
-- when running inside vscode for example -- when running inside vscode for example
cond = nil, ---@type boolean|fun(self:LazyPlugin):boolean|nil cond = nil, ---@type boolean|fun(self:LazyPlugin):boolean|nil
-- version = "*", -- enable this to try installing the latest stable versions of plugins
}, },
-- leave nil when passing the spec as the first argument to setup() -- leave nil when passing the spec as the first argument to setup()
spec = nil, ---@type LazySpec spec = nil, ---@type LazySpec
local_spec = true, -- load project specific .lazy.lua spec files. They will be added at the end of the spec.
lockfile = vim.fn.stdpath("config") .. "/lazy-lock.json", -- lockfile generated after running update. lockfile = vim.fn.stdpath("config") .. "/lazy-lock.json", -- lockfile generated after running update.
concurrency = jit.os:find("Windows") and (vim.uv.available_parallelism() * 2) or nil, ---@type number limit the maximum amount of concurrent tasks ---@type number? limit the maximum amount of concurrent tasks
concurrency = jit.os:find("Windows") and (vim.uv.available_parallelism() * 2) or nil,
git = { git = {
-- defaults for the `Lazy log` command -- defaults for the `Lazy log` command
-- log = { "--since=3 days ago" }, -- show commits from the last 3 days -- log = { "--since=3 days ago" }, -- show commits from the last 3 days
@ -28,9 +34,42 @@ M.defaults = {
-- then set the below to false. This should work, but is NOT supported and will -- then set the below to false. This should work, but is NOT supported and will
-- increase downloads a lot. -- increase downloads a lot.
filter = true, filter = true,
-- rate of network related git operations (clone, fetch, checkout)
throttle = {
enabled = false, -- not enabled by default
-- max 2 ops every 5 seconds
rate = 2,
duration = 5 * 1000, -- in ms
},
-- Time in seconds to wait before running fetch again for a plugin.
-- Repeated update/check operations will not run again until this
-- cooldown period has passed.
cooldown = 0,
},
pkg = {
enabled = true,
cache = vim.fn.stdpath("state") .. "/lazy/pkg-cache.lua",
-- the first package source that is found for a plugin will be used.
sources = {
"lazy",
"rockspec", -- will only be used when rocks.enabled is true
"packspec",
},
},
rocks = {
enabled = true,
root = vim.fn.stdpath("data") .. "/lazy-rocks",
server = "https://nvim-neorocks.github.io/rocks-binaries/",
-- use hererocks to install luarocks?
-- set to `nil` to use hererocks when luarocks is not found
-- set to `true` to always use hererocks
-- set to `false` to always use luarocks
hererocks = nil,
}, },
dev = { dev = {
---@type string | fun(plugin: LazyPlugin): string directory where you store your local plugin projects -- Directory where you store your local plugin projects. If a function is used,
-- the plugin directory (e.g. `~/projects/plugin-name`) must be returned.
---@type string | fun(plugin: LazyPlugin): string
path = "~/projects", path = "~/projects",
---@type string[] plugins that match these patterns will use your local versions instead of being fetched from GitHub ---@type string[] plugins that match these patterns will use your local versions instead of being fetched from GitHub
patterns = {}, -- For example {"folke"} patterns = {}, -- For example {"folke"}
@ -48,6 +87,8 @@ M.defaults = {
wrap = true, -- wrap the lines in the ui wrap = true, -- wrap the lines in the ui
-- The border to use for the UI window. Accepts same border values as |nvim_open_win()|. -- The border to use for the UI window. Accepts same border values as |nvim_open_win()|.
border = "none", border = "none",
-- The backdrop opacity. 0 is fully opaque, 100 is fully transparent.
backdrop = 60,
title = nil, ---@type string only works when border is not "none" title = nil, ---@type string only works when border is not "none"
title_pos = "center", ---@type "center" | "left" | "right" title_pos = "center", ---@type "center" | "left" | "right"
-- Show pills on top of the Lazy window -- Show pills on top of the Lazy window
@ -55,7 +96,9 @@ M.defaults = {
icons = { icons = {
cmd = "", cmd = "",
config = "", config = "",
debug = "",
event = "", event = "",
favorite = "",
ft = "", ft = "",
init = "", init = "",
import = "", import = "",
@ -79,7 +122,7 @@ M.defaults = {
-- leave nil, to automatically select a browser depending on your OS. -- leave nil, to automatically select a browser depending on your OS.
-- If you want to use a specific browser, you can define it here -- If you want to use a specific browser, you can define it here
browser = nil, ---@type string? browser = nil, ---@type string?
throttle = 20, -- how frequently should the ui process render events throttle = 1000 / 30, -- how frequently should the ui process render events
custom_keys = { custom_keys = {
-- You can define custom key maps here. If present, the description will -- You can define custom key maps here. If present, the description will
-- be shown in the help menu. -- be shown in the help menu.
@ -94,6 +137,16 @@ M.defaults = {
desc = "Open lazygit log", desc = "Open lazygit log",
}, },
["<localleader>i"] = {
function(plugin)
Util.notify(vim.inspect(plugin), {
title = "Inspect " .. plugin.name,
lang = "lua",
})
end,
desc = "Inspect Plugin",
},
["<localleader>t"] = { ["<localleader>t"] = {
function(plugin) function(plugin)
require("lazy.util").float_term(nil, { require("lazy.util").float_term(nil, {
@ -104,6 +157,17 @@ M.defaults = {
}, },
}, },
}, },
-- Output options for headless mode
headless = {
-- show the output from process commands like git
process = true,
-- show log messages
log = true,
-- show task start/end
task = true,
-- use ansi colors
colors = true,
},
diff = { diff = {
-- diff command <d> can be one of: -- diff command <d> can be one of:
-- * browser: opens the github compare view. Note that this is always mapped to <K> as well, -- * browser: opens the github compare view. Note that this is always mapped to <K> as well,
@ -155,16 +219,10 @@ M.defaults = {
enabled = true, enabled = true,
root = vim.fn.stdpath("state") .. "/lazy/readme", root = vim.fn.stdpath("state") .. "/lazy/readme",
files = { "README.md", "lua/**/README.md" }, files = { "README.md", "lua/**/README.md" },
-- only generate markdown helptags for plugins that dont have docs -- only generate markdown helptags for plugins that don't have docs
skip_if_doc_exists = true, skip_if_doc_exists = true,
}, },
state = vim.fn.stdpath("state") .. "/lazy/state.json", -- state info for checker and other things state = vim.fn.stdpath("state") .. "/lazy/state.json", -- state info for checker and other things
build = {
-- Plugins can provide a `build.lua` file that will be executed when the plugin is installed
-- or updated. When the plugin spec also has a `build` command, the plugin's `build.lua` not be
-- executed. In this case, a warning message will be shown.
warn_on_override = true,
},
-- Enable profiling of lazy.nvim. This will add some overhead, -- Enable profiling of lazy.nvim. This will add some overhead,
-- so only enable this when you are debugging lazy.nvim -- so only enable this when you are debugging lazy.nvim
profiling = { profiling = {
@ -177,7 +235,14 @@ M.defaults = {
debug = false, debug = false,
} }
M.version = "10.18.2" -- x-release-please-version function M.hererocks()
if M.options.rocks.hererocks == nil then
M.options.rocks.hererocks = vim.fn.executable("luarocks") == 0
end
return M.options.rocks.hererocks
end
M.version = "11.17.1" -- x-release-please-version
M.ns = vim.api.nvim_create_namespace("lazy") M.ns = vim.api.nvim_create_namespace("lazy")
@ -202,8 +267,10 @@ M.mapleader = nil
---@type string ---@type string
M.maplocalleader = nil M.maplocalleader = nil
M.suspended = false
function M.headless() function M.headless()
return #vim.api.nvim_list_uis() == 0 return not M.suspended and #vim.api.nvim_list_uis() == 0
end end
---@param opts? LazyConfig ---@param opts? LazyConfig
@ -215,6 +282,7 @@ function M.setup(opts)
end end
table.insert(M.options.install.colorscheme, "habamax") table.insert(M.options.install.colorscheme, "habamax")
-- root
M.options.root = Util.norm(M.options.root) M.options.root = Util.norm(M.options.root)
if type(M.options.dev.path) == "string" then if type(M.options.dev.path) == "string" then
M.options.dev.path = Util.norm(M.options.dev.path) M.options.dev.path = Util.norm(M.options.dev.path)
@ -230,13 +298,17 @@ function M.setup(opts)
M.me = debug.getinfo(1, "S").source:sub(2) M.me = debug.getinfo(1, "S").source:sub(2)
M.me = Util.norm(vim.fn.fnamemodify(M.me, ":p:h:h:h:h")) M.me = Util.norm(vim.fn.fnamemodify(M.me, ":p:h:h:h:h"))
local lib = vim.fn.fnamemodify(vim.v.progpath, ":p:h:h") .. "/lib"
lib = vim.uv.fs_stat(lib .. "64") and (lib .. "64") or lib
lib = lib .. "/nvim"
if M.options.performance.rtp.reset then if M.options.performance.rtp.reset then
---@type vim.Option
vim.opt.rtp = { vim.opt.rtp = {
vim.fn.stdpath("config"), vim.fn.stdpath("config"),
vim.fn.stdpath("data") .. "/site", vim.fn.stdpath("data") .. "/site",
M.me, M.me,
vim.env.VIMRUNTIME, vim.env.VIMRUNTIME,
vim.fn.fnamemodify(vim.v.progpath, ":p:h:h") .. "/lib/nvim", lib,
vim.fn.stdpath("config") .. "/after", vim.fn.stdpath("config") .. "/after",
} }
end end
@ -250,10 +322,6 @@ function M.setup(opts)
M.mapleader = vim.g.mapleader M.mapleader = vim.g.mapleader
M.maplocalleader = vim.g.maplocalleader M.maplocalleader = vim.g.maplocalleader
if M.headless() then
require("lazy.view.commands").setup()
end
vim.api.nvim_create_autocmd("UIEnter", { vim.api.nvim_create_autocmd("UIEnter", {
once = true, once = true,
callback = function() callback = function()
@ -261,21 +329,42 @@ function M.setup(opts)
end, end,
}) })
vim.api.nvim_create_autocmd("User", { if M.headless() then
pattern = "VeryLazy", require("lazy.view.commands").setup()
once = true, else
callback = function() vim.api.nvim_create_autocmd("User", {
require("lazy.view.commands").setup() pattern = "VeryLazy",
if M.options.change_detection.enabled then once = true,
require("lazy.manage.reloader").enable() callback = function()
end require("lazy.view.commands").setup()
if M.options.checker.enabled then if M.options.change_detection.enabled then
vim.defer_fn(function() require("lazy.manage.reloader").enable()
require("lazy.manage.checker").start() end
end, 10) if M.options.checker.enabled then
end vim.defer_fn(function()
end, require("lazy.manage.checker").start()
}) end, 10)
end
-- useful for plugin developers when making changes to a packspec file
vim.api.nvim_create_autocmd("BufWritePost", {
pattern = { "lazy.lua", "pkg.json", "*.rockspec" },
callback = function()
local plugin = require("lazy.core.plugin").find(vim.uv.cwd() .. "/lua/")
if plugin then
require("lazy").pkg({ plugins = { plugin } })
end
end,
})
vim.api.nvim_create_autocmd({ "VimSuspend", "VimResume" }, {
callback = function(ev)
M.suspended = ev.event == "VimSuspend"
end,
})
end,
})
end
Util.very_lazy() Util.very_lazy()
end end

174
lua/lazy/core/fragments.lua Normal file
View file

@ -0,0 +1,174 @@
local Config = require("lazy.core.config")
local Util = require("lazy.core.util")
--- This class is used to manage the fragments of a plugin spec.
--- It keeps track of the fragments and their relations to other fragments.
--- A fragment can be a dependency (dependencies) or a child (specs) of another fragment.
---@class LazyFragments
---@field fragments table<number, LazyFragment>
---@field frag_stack number[]
---@field dep_stack number[]
---@field dirty table<number, boolean>
---@field plugins table<LazyPlugin, number>
---@field spec LazySpecLoader
local M = {}
M._fid = 0
local function next_id()
M._fid = M._fid + 1
return M._fid
end
---@param spec LazySpecLoader
---@return LazyFragments
function M.new(spec)
local self = setmetatable({}, { __index = M })
self.fragments = {}
self.frag_stack = {}
self.dep_stack = {}
self.spec = spec
self.dirty = {}
self.plugins = {}
return self
end
---@param id number
function M:get(id)
return self.fragments[id]
end
--- Remove a fragment and all its children.
--- This will also remove the fragment from its parent's children list.
---@param id number
function M:del(id)
-- del fragment
local fragment = self.fragments[id]
if not fragment then
return
end
self.dirty[id] = true
-- remove from parent
local pid = fragment.pid
if pid then
local parent = self.fragments[pid]
if parent.frags then
---@param fid number
parent.frags = Util.filter(function(fid)
return fid ~= id
end, parent.frags)
end
if parent.deps then
---@param fid number
parent.deps = Util.filter(function(fid)
return fid ~= id
end, parent.deps)
end
self.dirty[pid] = true
end
-- remove children
if fragment.frags then
for _, fid in ipairs(fragment.frags) do
self:del(fid)
end
end
self.fragments[id] = nil
end
--- Add a fragment to the fragments list.
--- This also resolves its name, url, dir, dependencies and child specs.
---@param plugin LazyPluginSpec
function M:add(plugin)
if self.plugins[plugin] then
return self.fragments[self.plugins[plugin]]
end
local id = next_id()
setmetatable(plugin, nil)
self.plugins[plugin] = id
local pid = self.frag_stack[#self.frag_stack]
---@type LazyFragment
local fragment = {
id = id,
pid = pid,
name = plugin.name,
url = plugin.url,
dir = plugin.dir,
spec = plugin --[[@as LazyPlugin]],
}
-- short url / ref
if plugin[1] then
local slash = plugin[1]:find("/", 1, true)
if slash then
local prefix = plugin[1]:sub(1, 4)
if prefix == "http" or prefix == "git@" then
fragment.url = fragment.url or plugin[1]
else
fragment.name = fragment.name or plugin[1]:sub(slash + 1)
fragment.url = fragment.url or Config.options.git.url_format:format(plugin[1])
end
else
fragment.name = fragment.name or plugin[1]
end
end
-- name
fragment.name = fragment.name
or fragment.url and self.spec.get_name(fragment.url)
or fragment.dir and self.spec.get_name(fragment.dir)
if not fragment.name or fragment.name == "" then
return self.spec:error("Invalid plugin spec " .. vim.inspect(plugin))
end
if type(plugin.config) == "table" then
self.spec:warn(
"{" .. fragment.name .. "}: setting a table to `Plugin.config` is deprecated. Please use `Plugin.opts` instead"
)
---@diagnostic disable-next-line: assign-type-mismatch
plugin.opts = plugin.config
plugin.config = nil
end
self.fragments[id] = fragment
-- add to parent
if pid then
local parent = self.fragments[pid]
parent.frags = parent.frags or {}
table.insert(parent.frags, id)
end
-- add to parent's deps
local did = self.dep_stack[#self.dep_stack]
if did and did == pid then
fragment.dep = true
local parent = self.fragments[did]
parent.deps = parent.deps or {}
table.insert(parent.deps, id)
end
table.insert(self.frag_stack, id)
-- dependencies
if plugin.dependencies then
table.insert(self.dep_stack, id)
self.spec:normalize(plugin.dependencies)
table.remove(self.dep_stack)
end
-- child specs
if plugin.specs then
self.spec:normalize(plugin.specs)
end
table.remove(self.frag_stack)
return fragment
end
return M

View file

@ -75,7 +75,9 @@ function M:_add(event)
end end
-- HACK: work-around for https://github.com/neovim/neovim/issues/25526 -- HACK: work-around for https://github.com/neovim/neovim/issues/25526
done = true done = true
Util.track({ [self.type] = event.id }) if event.id ~= "VeryLazy" then
Util.track({ [self.type] = event.id })
end
local state = M.get_state(ev.event, ev.buf, ev.data) local state = M.get_state(ev.event, ev.buf, ev.data)
@ -86,7 +88,9 @@ function M:_add(event)
for _, s in ipairs(state) do for _, s in ipairs(state) do
M.trigger(s) M.trigger(s)
end end
Util.track() if event.id ~= "VeryLazy" then
Util.track()
end
end, end,
}) })
end end

View file

@ -5,7 +5,7 @@ local Util = require("lazy.core.util")
---@field type LazyHandlerTypes ---@field type LazyHandlerTypes
---@field extends? LazyHandler ---@field extends? LazyHandler
---@field active table<string,table<string,string>> ---@field active table<string,table<string,string>>
---@field managed table<string,string> ---@field managed table<string,string> mapping handler keys to plugin names
---@field super LazyHandler ---@field super LazyHandler
local M = {} local M = {}
@ -114,7 +114,7 @@ function M:add(plugin)
if not self.active[key] then if not self.active[key] then
self.active[key] = {} self.active[key] = {}
self:_add(value) self:_add(value)
self.managed[key] = key self.managed[key] = plugin.name
end end
self.active[key][plugin.name] = plugin.name self.active[key][plugin.name] = plugin.name
end end

View file

@ -11,7 +11,7 @@ local Util = require("lazy.core.util")
---@class LazyKeysSpec: LazyKeysBase ---@class LazyKeysSpec: LazyKeysBase
---@field [1] string lhs ---@field [1] string lhs
---@field [2]? string|fun()|false rhs ---@field [2]? string|fun():string?|false rhs
---@field mode? string|string[] ---@field mode? string|string[]
---@class LazyKeys: LazyKeysBase ---@class LazyKeys: LazyKeysBase
@ -102,6 +102,11 @@ function M.opts(keys)
return opts return opts
end end
---@param keys LazyKeys
function M.is_nop(keys)
return type(keys.rhs) == "string" and (keys.rhs == "" or keys.rhs:lower() == "<nop>")
end
---@param keys LazyKeys ---@param keys LazyKeys
function M:_add(keys) function M:_add(keys)
local lhs = keys.lhs local lhs = keys.lhs
@ -109,6 +114,10 @@ function M:_add(keys)
---@param buf? number ---@param buf? number
local function add(buf) local function add(buf)
if M.is_nop(keys) then
return self:_set(keys, buf)
end
vim.keymap.set(keys.mode, lhs, function() vim.keymap.set(keys.mode, lhs, function()
local plugins = self.active[keys.id] local plugins = self.active[keys.id]
@ -123,11 +132,6 @@ function M:_add(keys)
Util.track() Util.track()
end end
-- Create the real buffer-local mapping
if keys.ft then
self:_set(keys, buf)
end
if keys.mode:sub(-1) == "a" then if keys.mode:sub(-1) == "a" then
lhs = lhs .. "<C-]>" lhs = lhs .. "<C-]>"
end end
@ -148,7 +152,7 @@ function M:_add(keys)
vim.api.nvim_create_autocmd("FileType", { vim.api.nvim_create_autocmd("FileType", {
pattern = keys.ft, pattern = keys.ft,
callback = function(event) callback = function(event)
if self.active[keys.id] then if self.active[keys.id] and not M.is_nop(keys) then
add(event.buf) add(event.buf)
else else
-- Only create the mapping if its managed by lazy -- Only create the mapping if its managed by lazy
@ -162,19 +166,24 @@ function M:_add(keys)
end end
end end
-- Delete a mapping and create the real global -- Delete a mapping and create the real global/buffer-local
-- mapping when needed -- mapping when needed
---@param keys LazyKeys ---@param keys LazyKeys
function M:_del(keys) function M:_del(keys)
pcall(vim.keymap.del, keys.mode, keys.lhs, { -- bufs will be all buffers of the filetype for a buffer-local mapping
-- NOTE: for buffer-local mappings, we only delete the mapping for the current buffer -- OR `false` for a global mapping
-- So the mapping could still exist in other buffers local bufs = { false }
buffer = keys.ft and true or nil,
}) if keys.ft then
-- make sure to create global mappings when needed local ft = type(keys.ft) == "string" and { keys.ft } or keys.ft --[[@as string[] ]]
-- buffer-local mappings are managed by lazy bufs = vim.tbl_filter(function(buf)
if not keys.ft then return vim.tbl_contains(ft, vim.bo[buf].filetype)
self:_set(keys) end, vim.api.nvim_list_bufs())
end
for _, buf in ipairs(bufs) do
pcall(vim.keymap.del, keys.mode, keys.lhs, { buffer = buf or nil })
self:_set(keys, buf or nil)
end end
end end

View file

@ -44,6 +44,7 @@ function M.setup()
while M.install_missing() do while M.install_missing() do
count = count + 1 count = count + 1
if count > 5 then if count > 5 then
Util.error("Too many rounds of missing plugins")
break break
end end
end end
@ -66,7 +67,10 @@ end
-- multiple rounds can happen when importing a spec from a missing plugin -- multiple rounds can happen when importing a spec from a missing plugin
function M.install_missing() function M.install_missing()
for _, plugin in pairs(Config.plugins) do for _, plugin in pairs(Config.plugins) do
if not (plugin._.installed or Plugin.has_errors(plugin)) then local installed = plugin._.installed
local has_errors = Plugin.has_errors(plugin)
if not has_errors and not (installed and not plugin._.build) then
for _, colorscheme in ipairs(Config.options.install.colorscheme) do for _, colorscheme in ipairs(Config.options.install.colorscheme) do
if colorscheme == "default" then if colorscheme == "default" then
break break
@ -100,7 +104,7 @@ function M.startup()
M.source(vim.env.VIMRUNTIME .. "/filetype.lua") M.source(vim.env.VIMRUNTIME .. "/filetype.lua")
-- backup original rtp -- backup original rtp
local rtp = vim.opt.rtp:get() local rtp = vim.opt.rtp:get() --[[@as string[] ]]
-- 1. run plugin init -- 1. run plugin init
Util.track({ start = "init" }) Util.track({ start = "init" })
@ -131,7 +135,7 @@ function M.startup()
if not path:find("after/?$") then if not path:find("after/?$") then
-- these paths don't will already have their ftdetect ran, -- these paths don't will already have their ftdetect ran,
-- by sourcing filetype.lua above, so skip them -- by sourcing filetype.lua above, so skip them
M.did_ftdetect[path] = true M.did_ftdetect[path] = path
M.packadd(path) M.packadd(path)
end end
end end
@ -139,7 +143,9 @@ function M.startup()
-- 4. load after plugins -- 4. load after plugins
Util.track({ start = "after" }) Util.track({ start = "after" })
for _, path in ipairs(vim.opt.rtp:get()) do for _, path in
ipairs(vim.opt.rtp:get() --[[@as string[] ]])
do
if path:find("after/?$") then if path:find("after/?$") then
M.source_runtime(path, "plugin") M.source_runtime(path, "plugin")
end end
@ -155,7 +161,7 @@ function M.get_start_plugins()
---@type LazyPlugin[] ---@type LazyPlugin[]
local start = {} local start = {}
for _, plugin in pairs(Config.plugins) do for _, plugin in pairs(Config.plugins) do
if plugin.lazy == false and not plugin._.loaded then if not plugin._.loaded and (plugin._.rtp_loaded or plugin.lazy == false) then
start[#start + 1] = plugin start[#start + 1] = plugin
end end
end end
@ -335,7 +341,13 @@ function M._load(plugin, reason, opts)
Util.track({ plugin = plugin.name, start = reason.start }) Util.track({ plugin = plugin.name, start = reason.start })
Handler.disable(plugin) Handler.disable(plugin)
M.add_to_rtp(plugin) if not plugin.virtual then
M.add_to_rtp(plugin)
end
if plugin._.pkg and plugin._.pkg.source == "rockspec" then
M.add_to_luapath(plugin)
end
if plugin.dependencies then if plugin.dependencies then
Util.try(function() Util.try(function()
@ -343,7 +355,9 @@ function M._load(plugin, reason, opts)
end, "Failed to load deps for " .. plugin.name) end, "Failed to load deps for " .. plugin.name)
end end
M.packadd(plugin.dir) if not plugin.virtual then
M.packadd(plugin.dir)
end
if plugin.config or plugin.opts then if plugin.config or plugin.opts then
M.config(plugin) M.config(plugin)
end end
@ -454,10 +468,8 @@ function M.add_to_rtp(plugin)
local rtp = vim.api.nvim_get_runtime_file("", true) local rtp = vim.api.nvim_get_runtime_file("", true)
local idx_dir, idx_after local idx_dir, idx_after
local is_win = jit.os:find("Windows")
for i, path in ipairs(rtp) do for i, path in ipairs(rtp) do
if is_win then if Util.is_win then
path = Util.norm(path) path = Util.norm(path)
end end
if path == Config.me then if path == Config.me then
@ -476,9 +488,22 @@ function M.add_to_rtp(plugin)
table.insert(rtp, idx_after or (#rtp + 1), after) table.insert(rtp, idx_after or (#rtp + 1), after)
end end
---@type vim.Option
vim.opt.rtp = rtp vim.opt.rtp = rtp
end end
---@param plugin LazyPlugin
function M.add_to_luapath(plugin)
local root = Config.options.rocks.root .. "/" .. plugin.name
local path = root .. "/share/lua/5.1"
local cpath = root .. "/lib/lua/5.1"
local cpath2 = root .. "/lib64/lua/5.1"
package.path = package.path .. ";" .. path .. "/?.lua;" .. path .. "/?/init.lua;"
package.cpath = package.cpath .. ";" .. cpath .. "/?." .. (jit.os:find("Windows") and "dll" or "so") .. ";"
package.cpath = package.cpath .. ";" .. cpath2 .. "/?." .. (jit.os:find("Windows") and "dll" or "so") .. ";"
end
function M.source(path) function M.source(path)
Util.track({ runtime = path }) Util.track({ runtime = path })
Util.try(function() Util.try(function()
@ -504,8 +529,8 @@ function M.colorscheme(name)
end end
function M.auto_load(modname, modpath) function M.auto_load(modname, modpath)
local plugin = Plugin.find(modpath) local plugin = Plugin.find(modpath, { fast = not M.did_handlers })
if plugin and modpath:find(plugin.dir, 1, true) == 1 then if plugin then
plugin._.rtp_loaded = true plugin._.rtp_loaded = true
-- don't load if: -- don't load if:
-- * handlers haven't been setup yet -- * handlers haven't been setup yet
@ -525,9 +550,17 @@ end
---@param modname string ---@param modname string
function M.loader(modname) function M.loader(modname)
local paths = Util.get_unloaded_rtp(modname) local paths, cached = Util.get_unloaded_rtp(modname, { cache = true })
local ret = Cache.find(modname, { rtp = false, paths = paths })[1] local ret = Cache.find(modname, { rtp = false, paths = paths })[1]
if not ret and cached then
paths = Util.get_unloaded_rtp(modname)
ret = Cache.find(modname, { rtp = false, paths = paths })[1]
end
if ret then if ret then
-- explicitly set to nil to prevent loading errors
package.loaded[modname] = nil
M.auto_load(modname, ret.modpath) M.auto_load(modname, ret.modpath)
local mod = package.loaded[modname] local mod = package.loaded[modname]
if type(mod) == "table" then if type(mod) == "table" then

360
lua/lazy/core/meta.lua Normal file
View file

@ -0,0 +1,360 @@
local Config = require("lazy.core.config")
local Pkg = require("lazy.pkg")
local Util = require("lazy.core.util")
--- This class is used to manage the plugins.
--- A plugin is a collection of fragments that are related to each other.
---@class LazyMeta
---@field plugins table<string, LazyPlugin>
---@field str_to_meta table<string, LazyPlugin>
---@field frag_to_meta table<number, LazyPlugin>
---@field dirty table<string, boolean>
---@field spec LazySpecLoader
---@field fragments LazyFragments
---@field pkgs table<string, number>
local M = {}
---@param spec LazySpecLoader
---@return LazyMeta
function M.new(spec)
local self = setmetatable({}, { __index = M })
self.spec = spec
self.fragments = require("lazy.core.fragments").new(spec)
self.plugins = {}
self.frag_to_meta = {}
self.str_to_meta = {}
self.dirty = {}
self.pkgs = {}
return self
end
-- import package specs
function M:load_pkgs()
if not Config.options.pkg.enabled then
return
end
for _, pkg in ipairs(Pkg.get()) do
local last_id = self.fragments._fid
local meta, fragment = self:add(pkg.spec)
if meta and fragment then
meta._.pkg = pkg
-- tag all top-level package fragments that were added as optional
for _, fid in ipairs(meta._.frags) do
if fid > last_id then
local frag = self.fragments:get(fid)
frag.spec.optional = true
end
end
-- keep track of the top-level package fragment
self.pkgs[pkg.dir] = fragment.id
end
end
end
--- Remove a plugin and all its fragments.
---@param name string
function M:del(name)
local meta = self.plugins[name]
if not meta then
return
end
for _, fid in ipairs(meta._.frags or {}) do
self.fragments:del(fid)
end
self.plugins[name] = nil
end
--- Add a fragment to a plugin.
--- This will create a new plugin if it does not exist.
--- It also keeps track of renames.
---@param plugin LazyPluginSpec
function M:add(plugin)
local fragment = self.fragments:add(plugin)
if not fragment then
return
end
local meta = self.plugins[fragment.name]
or fragment.url and self.str_to_meta[fragment.url]
or fragment.dir and self.str_to_meta[fragment.dir]
if not meta then
meta = { name = fragment.name, _ = { frags = {} } }
local url, dir = fragment.url, fragment.dir
-- add to index
if url then
self.str_to_meta[url] = meta
end
if dir then
self.str_to_meta[dir] = meta
end
end
table.insert(meta._.frags, fragment.id)
if meta._ and meta._.rtp_loaded and meta.dir then
local old_dir = meta.dir
self:_rebuild(meta.name)
local new_dir = meta.dir
if old_dir ~= new_dir then
local msg = "Plugin `" .. meta.name .. "` changed `dir`:\n- from: `" .. old_dir .. "`\n- to: `" .. new_dir .. "`"
msg = msg .. "\n\nThis plugin was already partially loaded, so things may break.\nPlease fix your config."
self.spec:error(msg)
end
end
if plugin.name then
-- handle renames
if meta.name ~= plugin.name then
self.plugins[meta.name] = nil
meta.name = plugin.name
end
end
self.plugins[meta.name] = meta
self.frag_to_meta[fragment.id] = meta
self.dirty[meta.name] = true
return meta, fragment
end
--- Rebuild all plugins based on dirty fragments,
--- or dirty plugins. Will remove plugins that no longer have fragments.
function M:rebuild()
local frag_count = vim.tbl_count(self.fragments.dirty)
local plugin_count = vim.tbl_count(self.dirty)
if frag_count == 0 and plugin_count == 0 then
return
end
if Config.options.debug then
Util.track("rebuild plugins frags=" .. frag_count .. " plugins=" .. plugin_count)
end
for fid in pairs(self.fragments.dirty) do
local meta = self.frag_to_meta[fid]
if meta then
if self.fragments:get(fid) then
-- fragment still exists, so mark plugin as dirty
self.dirty[meta.name] = true
else
-- fragment was deleted, so remove it from plugin
self.frag_to_meta[fid] = nil
---@param f number
meta._.frags = Util.filter(function(f)
return f ~= fid
end, meta._.frags)
-- if no fragments left, delete plugin
if #meta._.frags == 0 then
self:del(meta.name)
else
self.dirty[meta.name] = true
end
end
end
end
self.fragments.dirty = {}
for n, _ in pairs(self.dirty) do
self:_rebuild(n)
end
if Config.options.debug then
Util.track()
end
end
--- Rebuild a single plugin.
--- This will resolve the plugin based on its fragments using metatables.
--- This also resolves dependencies, dep, optional, dir, dev, and url.
---@param name string
function M:_rebuild(name)
if not self.dirty[name] then
return
end
self.dirty[name] = nil
local plugin = self.plugins[name]
if not plugin or #plugin._.frags == 0 then
self.plugins[name] = nil
return
end
setmetatable(plugin, nil)
plugin.dependencies = {}
local super = nil
plugin.url = nil
plugin._.dep = true
plugin._.top = true
plugin.optional = true
assert(#plugin._.frags > 0, "no fragments found for plugin " .. name)
---@type table<number, boolean>
local added = {}
for _, fid in ipairs(plugin._.frags) do
if not added[fid] then
added[fid] = true
local fragment = self.fragments:get(fid)
assert(fragment, "fragment " .. fid .. " not found, for plugin " .. name)
---@diagnostic disable-next-line: no-unknown
super = setmetatable(fragment.spec, super and { __index = super } or nil)
plugin._.dep = plugin._.dep and fragment.dep
plugin.optional = plugin.optional and (rawget(fragment.spec, "optional") == true)
plugin.url = fragment.url or plugin.url
plugin._.top = plugin._.top and fragment.pid == nil
-- dependencies
for _, dep in ipairs(fragment.deps or {}) do
local dep_meta = self.frag_to_meta[dep]
if dep_meta then
table.insert(plugin.dependencies, dep_meta.name)
end
end
end
end
super = super or {}
-- dir / dev
plugin.dev = super.dev
plugin.dir = super.dir
if plugin.dir then
plugin.dir = Util.norm(plugin.dir)
elseif super.virtual then
plugin.dir = Util.norm("/dev/null/" .. plugin.name)
else
if plugin.dev == nil and plugin.url then
for _, pattern in ipairs(Config.options.dev.patterns) do
if plugin.url:find(pattern, 1, true) then
plugin.dev = true
break
end
end
end
if plugin.dev == true then
local dev_dir = type(Config.options.dev.path) == "string" and Config.options.dev.path .. "/" .. plugin.name
or Util.norm(Config.options.dev.path(plugin))
if not Config.options.dev.fallback or vim.fn.isdirectory(dev_dir) == 1 then
plugin.dir = dev_dir
else
plugin.dev = false
end
end
plugin.dir = plugin.dir or Config.options.root .. "/" .. plugin.name
end
-- dependencies
if #plugin.dependencies == 0 and not super.dependencies then
plugin.dependencies = nil
end
-- optional
if not plugin.optional and not super.optional then
plugin.optional = nil
end
setmetatable(plugin, { __index = super })
return plugin
end
--- Disable a plugin.
---@param plugin LazyPlugin
function M:disable(plugin)
plugin._.kind = "disabled"
self:del(plugin.name)
self.spec.disabled[plugin.name] = plugin
end
--- Check if a plugin should be disabled, but ignore uninstalling it.
function M:fix_cond()
for _, plugin in pairs(self.plugins) do
local cond = plugin.cond
if cond == nil then
cond = Config.options.defaults.cond
end
if cond == false or (type(cond) == "function" and not cond(plugin)) then
plugin._.cond = false
local stack = { plugin }
while #stack > 0 do
local p = table.remove(stack) --[[@as LazyPlugin]]
if not self.spec.ignore_installed[p.name] then
for _, dep in ipairs(p.dependencies or {}) do
table.insert(stack, self.plugins[dep])
end
self.spec.ignore_installed[p.name] = true
end
end
plugin.enabled = false
end
end
end
--- Removes plugins for which all its fragments are optional.
function M:fix_optional()
if self.spec.optional then
return 0
end
local changes = 0
for _, plugin in pairs(self.plugins) do
if plugin.optional then
changes = changes + 1
self:del(plugin.name)
end
end
self:rebuild()
return changes
end
--- Removes plugins that are disabled.
function M:fix_disabled()
local changes = 0
local function check(top)
for _, plugin in pairs(self.plugins) do
if (plugin._.top or false) == top then
if plugin.enabled == false or (type(plugin.enabled) == "function" and not plugin.enabled()) then
changes = changes + 1
if plugin.optional then
self:del(plugin.name)
else
self:disable(plugin)
end
self:rebuild()
end
end
end
end
-- disable top-level plugins first, since they may have non-top-level frags
-- that disable other plugins
check(true)
-- then disable non-top-level plugins
check(false)
return changes
end
--- Removes package fragments for plugins that no longer use the same directory.
function M:fix_pkgs()
for dir, fid in pairs(self.pkgs) do
local plugin = self.frag_to_meta[fid]
plugin = plugin and self.plugins[plugin.name]
if plugin then
-- check if plugin is still in the same directory
if plugin.dir ~= dir then
self.fragments:del(fid)
end
end
end
self:rebuild()
end
--- Resolve all plugins, based on cond, enabled and optional.
function M:resolve()
Util.track("resolve plugins")
self:rebuild()
self:fix_pkgs()
self:fix_cond()
-- selene: allow(empty_loop)
while self:fix_disabled() + self:fix_optional() > 0 do
end
Util.track()
end
return M

View file

@ -1,5 +1,6 @@
local Config = require("lazy.core.config") local Config = require("lazy.core.config")
local Handler = require("lazy.core.handler") local Meta = require("lazy.core.meta")
local Pkg = require("lazy.pkg")
local Util = require("lazy.core.util") local Util = require("lazy.core.util")
---@class LazyCorePlugin ---@class LazyCorePlugin
@ -7,10 +8,9 @@ local M = {}
M.loading = false M.loading = false
---@class LazySpecLoader ---@class LazySpecLoader
---@field meta LazyMeta
---@field plugins table<string, LazyPlugin> ---@field plugins table<string, LazyPlugin>
---@field fragments table<number, LazyPlugin>
---@field disabled table<string, LazyPlugin> ---@field disabled table<string, LazyPlugin>
---@field dirty table<string, true>
---@field ignore_installed table<string, true> ---@field ignore_installed table<string, true>
---@field modules string[] ---@field modules string[]
---@field notifs {msg:string, level:number, file?:string}[] ---@field notifs {msg:string, level:number, file?:string}[]
@ -18,33 +18,44 @@ M.loading = false
---@field optional? boolean ---@field optional? boolean
local Spec = {} local Spec = {}
M.Spec = Spec M.Spec = Spec
M.last_fid = 0 M.LOCAL_SPEC = ".lazy.lua"
M.fid_stack = {} ---@type number[]
---@param spec? LazySpec ---@param spec? LazySpec
---@param opts? {optional?:boolean} ---@param opts? {optional?:boolean, pkg?:boolean}
function Spec.new(spec, opts) function Spec.new(spec, opts)
local self = setmetatable({}, { __index = Spec }) local self = setmetatable({}, Spec)
self.plugins = {} self.meta = Meta.new(self)
self.fragments = {}
self.disabled = {} self.disabled = {}
self.modules = {} self.modules = {}
self.dirty = {}
self.notifs = {} self.notifs = {}
self.ignore_installed = {} self.ignore_installed = {}
self.optional = opts and opts.optional self.optional = opts and opts.optional
if not (opts and opts.pkg == false) then
self.meta:load_pkgs()
end
if spec then if spec then
self:parse(spec) self:parse(spec)
end end
return self return self
end end
function Spec:__index(key)
if Spec[key] then
return Spec[key]
end
if key == "plugins" then
self.meta:rebuild()
return self.meta.plugins
end
end
function Spec:parse(spec) function Spec:parse(spec)
self:normalize(spec) self:normalize(spec)
self:fix_disabled() self.meta:resolve()
end end
-- PERF: optimized code to get package name without using lua patterns -- PERF: optimized code to get package name without using lua patterns
---@return string
function Spec.get_name(pkg) function Spec.get_name(pkg)
local name = pkg:sub(-4) == ".git" and pkg:sub(1, -5) or pkg local name = pkg:sub(-4) == ".git" and pkg:sub(1, -5) or pkg
name = name:sub(-1) == "/" and name:sub(1, -2) or name name = name:sub(-1) == "/" and name:sub(1, -2) or name
@ -52,120 +63,6 @@ function Spec.get_name(pkg)
return slash and name:sub(#name - slash + 2) or pkg:gsub("%W+", "_") return slash and name:sub(#name - slash + 2) or pkg:gsub("%W+", "_")
end end
---@param plugin LazyPlugin
---@param results? string[]
function Spec:add(plugin, results)
-- check if we already processed this spec. Can happen when a user uses the same instance of a spec in multiple specs
-- see https://github.com/folke/lazy.nvim/issues/45
if rawget(plugin, "_") then
if results then
table.insert(results, plugin.name)
end
return plugin
end
local is_ref = plugin[1] and not plugin[1]:find("/", 1, true)
if not plugin.url and not is_ref and plugin[1] then
local prefix = plugin[1]:sub(1, 4)
if prefix == "http" or prefix == "git@" then
plugin.url = plugin[1]
else
plugin.url = Config.options.git.url_format:format(plugin[1])
end
end
---@type string?
local dir
if plugin.dir then
dir = Util.norm(plugin.dir)
-- local plugin
plugin.name = plugin.name or Spec.get_name(plugin.dir)
elseif plugin.url then
plugin.name = plugin.name or Spec.get_name(plugin.url)
-- check for dev plugins
if plugin.dev == nil then
for _, pattern in ipairs(Config.options.dev.patterns) do
if plugin.url:find(pattern, 1, true) then
plugin.dev = true
break
end
end
end
elseif is_ref then
plugin.name = plugin[1]
else
self:error("Invalid plugin spec " .. vim.inspect(plugin))
return
end
if not plugin.name or plugin.name == "" then
self:error("Plugin spec " .. vim.inspect(plugin) .. " has no name")
return
end
-- dev plugins
if plugin.dev then
local dir_dev
if type(Config.options.dev.path) == "string" then
dir_dev = Config.options.dev.path .. "/" .. plugin.name
else
dir_dev = Util.norm(Config.options.dev.path(plugin))
end
if not Config.options.dev.fallback or vim.fn.isdirectory(dir_dev) == 1 then
dir = dir_dev
end
elseif plugin.dev == false then
-- explicitely select the default path
dir = Config.options.root .. "/" .. plugin.name
end
if type(plugin.config) == "table" then
self:warn(
"{" .. plugin.name .. "}: setting a table to `Plugin.config` is deprecated. Please use `Plugin.opts` instead"
)
---@diagnostic disable-next-line: assign-type-mismatch
plugin.opts = plugin.config
plugin.config = nil
end
local fpid = M.fid_stack[#M.fid_stack]
M.last_fid = M.last_fid + 1
plugin._ = {
dir = dir,
fid = M.last_fid,
fpid = fpid,
dep = fpid ~= nil,
module = self.importing,
}
self.fragments[plugin._.fid] = plugin
-- remote plugin
plugin.dir = plugin._.dir or (plugin.name and (Config.options.root .. "/" .. plugin.name)) or nil
if fpid then
local parent = self.fragments[fpid]
parent._.fdeps = parent._.fdeps or {}
table.insert(parent._.fdeps, plugin._.fid)
end
if plugin.dependencies then
table.insert(M.fid_stack, plugin._.fid)
plugin.dependencies = self:normalize(plugin.dependencies, {})
table.remove(M.fid_stack)
end
if self.plugins[plugin.name] then
plugin = self:merge(self.plugins[plugin.name], plugin)
end
self.plugins[plugin.name] = plugin
if results then
table.insert(results, plugin.name)
end
return plugin
end
function Spec:error(msg) function Spec:error(msg)
self:log(msg, vim.log.levels.ERROR) self:log(msg, vim.log.levels.ERROR)
end end
@ -174,155 +71,6 @@ function Spec:warn(msg)
self:log(msg, vim.log.levels.WARN) self:log(msg, vim.log.levels.WARN)
end end
--- Rebuilds a plugin spec excluding any removed fragments
---@param name? string
function Spec:rebuild(name)
if not name then
for n, _ in pairs(self.dirty) do
self:rebuild(n)
end
self.dirty = {}
end
local plugin = self.plugins[name]
if not plugin then
return
end
local fragments = {} ---@type LazyPlugin[]
repeat
local super = plugin._.super
if self.fragments[plugin._.fid] then
plugin._.dep = plugin._.fpid ~= nil
plugin._.super = nil
if plugin._.fdeps then
plugin.dependencies = {}
for _, cid in ipairs(plugin._.fdeps) do
if self.fragments[cid] then
table.insert(plugin.dependencies, self.fragments[cid].name)
end
end
end
setmetatable(plugin, nil)
table.insert(fragments, 1, plugin)
end
plugin = super
until not plugin
if #fragments == 0 then
self.plugins[name] = nil
return
end
plugin = fragments[1]
for i = 2, #fragments do
plugin = self:merge(plugin, fragments[i])
end
self.plugins[name] = plugin
end
--- Recursively removes all fragments from a plugin spec or a given fragment
---@param id string|number Plugin name or fragment id
---@param opts {self: boolean}
function Spec:remove_fragments(id, opts)
local fids = {} ---@type number[]
if type(id) == "number" then
fids[1] = id
else
local plugin = self.plugins[id]
repeat
fids[#fids + 1] = plugin._.fid
plugin = plugin._.super
until not plugin
end
for _, fid in ipairs(fids) do
local fragment = self.fragments[fid]
if fragment then
for _, cid in ipairs(fragment._.fdeps or {}) do
self:remove_fragments(cid, { self = true })
end
if opts.self then
self.fragments[fid] = nil
end
self.dirty[fragment.name] = true
end
end
end
function Spec:fix_cond()
for _, plugin in pairs(self.plugins) do
local cond = plugin.cond
if cond == nil then
cond = Config.options.defaults.cond
end
if cond == false or (type(cond) == "function" and not cond(plugin)) then
plugin._.cond = false
local stack = { plugin }
while #stack > 0 do
local p = table.remove(stack)
if not self.ignore_installed[p.name] then
for _, dep in ipairs(p.dependencies or {}) do
table.insert(stack, self.plugins[dep])
end
self.ignore_installed[p.name] = true
end
end
plugin.enabled = false
end
end
end
function Spec:fix_optional()
if not self.optional then
---@param plugin LazyPlugin
local function all_optional(plugin)
return (not plugin) or (rawget(plugin, "optional") and all_optional(plugin._.super))
end
-- handle optional plugins
for _, plugin in pairs(self.plugins) do
if plugin.optional and all_optional(plugin) then
-- remove all optional fragments
self:remove_fragments(plugin.name, { self = true })
self.plugins[plugin.name] = nil
end
end
end
end
function Spec:fix_disabled()
for _, plugin in pairs(self.plugins) do
if not plugin.name or not plugin.dir then
self:error("Plugin spec for **" .. plugin.name .. "** not found.\n```lua\n" .. vim.inspect(plugin) .. "\n```")
self.plugins[plugin.name] = nil
end
end
self:fix_optional()
self:rebuild()
self:fix_cond()
self:rebuild()
self.dirty = {}
for _, plugin in pairs(self.plugins) do
local disabled = plugin.enabled == false or (type(plugin.enabled) == "function" and not plugin.enabled())
if disabled then
plugin._.kind = "disabled"
-- remove all child fragments
self:remove_fragments(plugin.name, { self = false })
self.plugins[plugin.name] = nil
self.disabled[plugin.name] = plugin
end
end
-- rebuild any plugin specs that were modified
self:rebuild()
end
---@param msg string ---@param msg string
---@param level number ---@param level number
function Spec:log(msg, level) function Spec:log(msg, level)
@ -342,29 +90,21 @@ function Spec:report(level)
end end
---@param spec LazySpec|LazySpecImport ---@param spec LazySpec|LazySpecImport
---@param results? string[] function Spec:normalize(spec)
function Spec:normalize(spec, results)
if type(spec) == "string" then if type(spec) == "string" then
if not spec:find("/", 1, true) then self.meta:add({ spec })
-- spec is a plugin name
if results then
table.insert(results, spec)
end
else
self:add({ spec }, results)
end
elseif #spec > 1 or Util.is_list(spec) then elseif #spec > 1 or Util.is_list(spec) then
---@cast spec LazySpec[] ---@cast spec LazySpec[]
for _, s in ipairs(spec) do for _, s in ipairs(spec) do
self:normalize(s, results) self:normalize(s)
end end
elseif spec[1] or spec.dir or spec.url then elseif spec[1] or spec.dir or spec.url then
---@cast spec LazyPlugin ---@cast spec LazyPluginSpec
local plugin = self:add(spec, results) self.meta:add(spec)
---@diagnostic disable-next-line: cast-type-mismatch ---@diagnostic disable-next-line: cast-type-mismatch
---@cast plugin LazySpecImport ---@cast spec LazySpecImport
if plugin and plugin.import then if spec and spec.import then
self:import(plugin) self:import(spec)
end end
elseif spec.import then elseif spec.import then
---@cast spec LazySpecImport ---@cast spec LazySpecImport
@ -372,7 +112,6 @@ function Spec:normalize(spec, results)
else else
self:error("Invalid plugin spec " .. vim.inspect(spec)) self:error("Invalid plugin spec " .. vim.inspect(spec))
end end
return results
end end
---@param spec LazySpecImport ---@param spec LazySpecImport
@ -380,10 +119,18 @@ function Spec:import(spec)
if spec.import == "lazy" then if spec.import == "lazy" then
return self:error("You can't name your plugins module `lazy`.") return self:error("You can't name your plugins module `lazy`.")
end end
if type(spec.import) ~= "string" then if type(spec.import) == "function" then
if not spec.name then
return self:error("Invalid import spec. Missing name: " .. vim.inspect(spec))
end
elseif type(spec.import) ~= "string" then
return self:error("Invalid import spec. `import` should be a string: " .. vim.inspect(spec)) return self:error("Invalid import spec. `import` should be a string: " .. vim.inspect(spec))
end end
if vim.tbl_contains(self.modules, spec.import) then
local import_name = spec.name or spec.import
---@cast import_name string
if vim.tbl_contains(self.modules, import_name) then
return return
end end
if spec.cond == false or (type(spec.cond) == "function" and not spec.cond()) then if spec.cond == false or (type(spec.cond) == "function" and not spec.cond()) then
@ -393,28 +140,49 @@ function Spec:import(spec)
return return
end end
self.modules[#self.modules + 1] = spec.import self.modules[#self.modules + 1] = import_name
local import = spec.import
local imported = 0 local imported = 0
---@type string[] ---@type {modname: string, load: fun():(LazyPluginSpec?, string?)}[]
local modnames = {} local modspecs = {}
Util.lsmod(spec.import, function(modname)
modnames[#modnames + 1] = modname
end)
table.sort(modnames)
for _, modname in ipairs(modnames) do if type(import) == "string" then
Util.lsmod(import, function(modname, modpath)
modspecs[#modspecs + 1] = {
modname = modname,
load = function()
local mod, err = loadfile(modpath)
if mod then
return mod()
else
return nil, err
end
end,
}
end)
table.sort(modspecs, function(a, b)
return a.modname < b.modname
end)
else
modspecs = { { modname = import_name, load = spec.import } }
end
for _, modspec in ipairs(modspecs) do
imported = imported + 1 imported = imported + 1
local modname = modspec.modname
Util.track({ import = modname }) Util.track({ import = modname })
self.importing = modname self.importing = modname
-- unload the module so we get a clean slate -- unload the module so we get a clean slate
---@diagnostic disable-next-line: no-unknown ---@diagnostic disable-next-line: no-unknown
package.loaded[modname] = nil package.loaded[modname] = nil
Util.try(function() Util.try(function()
local mod = require(modname) local mod, err = modspec.load()
if type(mod) ~= "table" then if err then
self.importing = nil self:error("Failed to load `" .. modname .. "`:\n" .. err)
elseif type(mod) ~= "table" then
return self:error( return self:error(
"Invalid spec module: `" "Invalid spec module: `"
.. modname .. modname
@ -422,59 +190,23 @@ function Spec:import(spec)
.. type(mod) .. type(mod)
.. "` was returned instead" .. "` was returned instead"
) )
else
self:normalize(mod)
end end
self:normalize(mod)
self.importing = nil
Util.track()
end, { end, {
msg = "Failed to load `" .. modname .. "`", msg = "Failed to load `" .. modname .. "`",
on_error = function(msg) on_error = function(msg)
self:error(msg) self:error(msg)
self.importing = nil
Util.track()
end, end,
}) })
self.importing = nil
Util.track()
end end
if imported == 0 then if imported == 0 then
self:error("No specs found for module " .. spec.import) self:error("No specs found for module " .. vim.inspect(spec.import))
end end
end end
---@param old LazyPlugin
---@param new LazyPlugin
---@return LazyPlugin
function Spec:merge(old, new)
new._.dep = old._.dep and new._.dep
if new.url and old.url and new.url ~= old.url then
self:warn("Two plugins with the same name and different url:\n" .. vim.inspect({ old = old, new = new }))
end
if new.dependencies and old.dependencies then
Util.extend(new.dependencies, old.dependencies)
end
local new_dir = new._.dir or old._.dir or (new.name and (Config.options.root .. "/" .. new.name)) or nil
if new_dir ~= old.dir then
local msg = "Plugin `" .. new.name .. "` changed `dir`:\n- from: `" .. old.dir .. "`\n- to: `" .. new_dir .. "`"
if new._.rtp_loaded or old._.rtp_loaded then
msg = msg
.. "\n\nThis plugin was already partially loaded, so we did not change it's `dir`.\nPlease fix your config."
self:error(msg)
new_dir = old.dir
else
self:warn(msg)
end
end
new.dir = new_dir
new._.rtp_loaded = new._.rtp_loaded or old._.rtp_loaded
new._.super = old
setmetatable(new, { __index = old })
return new
end
function M.update_state() function M.update_state()
---@type string[] ---@type string[]
local cloning = {} local cloning = {}
@ -505,12 +237,15 @@ function M.update_state()
or plugin.cmd or plugin.cmd
plugin.lazy = lazy and true or false plugin.lazy = lazy and true or false
end end
if plugin.dir:find(Config.options.root, 1, true) == 1 then if plugin.virtual then
plugin._.is_local = true
plugin._.installed = true -- local plugins are managed by the user
elseif plugin.dir:find(Config.options.root, 1, true) == 1 then
plugin._.installed = installed[plugin.name] ~= nil plugin._.installed = installed[plugin.name] ~= nil
installed[plugin.name] = nil installed[plugin.name] = nil
else else
plugin._.is_local = true plugin._.is_local = true
plugin._.installed = true -- local plugins are managed by the user plugin._.installed = vim.fn.isdirectory(plugin.dir) == 1
end end
end end
@ -518,6 +253,8 @@ function M.update_state()
installed[name] = nil installed[name] = nil
end end
M.update_rocks_state()
Config.to_clean = {} Config.to_clean = {}
for pack, dir_type in pairs(installed) do for pack, dir_type in pairs(installed) do
table.insert(Config.to_clean, { table.insert(Config.to_clean, {
@ -533,12 +270,65 @@ function M.update_state()
end end
end end
function M.update_rocks_state()
local root = Config.options.rocks.root
---@type table<string,string>
local installed = {}
Util.ls(root, function(_, name, type)
if type == "directory" then
installed[name] = name
end
end)
for _, plugin in pairs(Config.plugins) do
if plugin.build == "rockspec" or plugin.name == "hererocks" then
plugin._.build = not installed[plugin.name]
end
end
end
---@return LazySpecImport?
function M.find_local_spec()
if not Config.options.local_spec then
return
end
local path = vim.uv.cwd()
while path and path ~= "" do
local file = path .. "/" .. M.LOCAL_SPEC
if vim.fn.filereadable(file) == 1 then
return {
name = vim.fn.fnamemodify(file, ":~:."),
import = function()
local data = vim.secure.read(file)
if data then
return loadstring(data, M.LOCAL_SPEC)()
end
return {}
end,
}
end
local p = vim.fn.fnamemodify(path, ":h")
if p == path then
break
end
path = p
end
end
function M.load() function M.load()
M.loading = true M.loading = true
-- load specs -- load specs
Util.track("spec") Util.track("spec")
Config.spec = Spec.new() Config.spec = Spec.new()
Config.spec:parse({ vim.deepcopy(Config.options.spec), { "folke/lazy.nvim" } })
local specs = {
---@diagnostic disable-next-line: param-type-mismatch
vim.deepcopy(Config.options.spec),
}
specs[#specs + 1] = M.find_local_spec()
specs[#specs + 1] = { "folke/lazy.nvim" }
Config.spec:parse(specs)
-- override some lazy props -- override some lazy props
local lazy = Config.spec.plugins["lazy.nvim"] local lazy = Config.spec.plugins["lazy.nvim"]
@ -551,16 +341,30 @@ function M.load()
lazy._.loaded = {} lazy._.loaded = {}
end end
-- add hererocks when enabled and needed
for _, plugin in pairs(Config.spec.plugins) do
if plugin.build == "rockspec" then
if Config.hererocks() then
Config.spec.meta:add({
"luarocks/hererocks",
build = "rockspec",
lazy = true,
})
end
break
end
end
local existing = Config.plugins local existing = Config.plugins
Config.plugins = Config.spec.plugins Config.plugins = Config.spec.plugins
-- copy state. This wont do anything during startup -- copy state. This wont do anything during startup
for name, plugin in pairs(existing) do for name, plugin in pairs(existing) do
if Config.plugins[name] then if Config.plugins[name] then
local dep = Config.plugins[name]._.dep local new_state = Config.plugins[name]._
local super = Config.plugins[name]._.super
Config.plugins[name]._ = plugin._ Config.plugins[name]._ = plugin._
Config.plugins[name]._.dep = dep Config.plugins[name]._.dep = new_state.dep
Config.plugins[name]._.super = super Config.plugins[name]._.frags = new_state.frags
Config.plugins[name]._.pkg = new_state.pkg
end end
end end
Util.track() Util.track()
@ -568,23 +372,36 @@ function M.load()
Util.track("state") Util.track("state")
M.update_state() M.update_state()
Util.track() Util.track()
if Config.options.pkg.enabled and Pkg.dirty then
Pkg.update()
return M.load()
end
M.loading = false M.loading = false
vim.api.nvim_exec_autocmds("User", { pattern = "LazyPlugins", modeline = false }) vim.api.nvim_exec_autocmds("User", { pattern = "LazyPlugins", modeline = false })
end end
-- Finds the plugin that has this path -- Finds the plugin that has this path
---@param path string ---@param path string
function M.find(path) ---@param opts? {fast?:boolean}
function M.find(path, opts)
if not Config.spec then if not Config.spec then
return return
end end
opts = opts or {}
local lua = path:find("/lua/", 1, true) local lua = path:find("/lua/", 1, true)
if lua then if lua then
local name = path:sub(1, lua - 1) local name = path:sub(1, lua - 1)
local slash = name:reverse():find("/", 1, true) local slash = name:reverse():find("/", 1, true)
if slash then if slash then
name = name:sub(#name - slash + 2) name = name:sub(#name - slash + 2)
return name and Config.plugins[name] or Config.spec.plugins[name] or nil if name then
if opts.fast then
return Config.spec.meta.plugins[name]
end
return Config.spec.plugins[name]
end
end end
end end
end end
@ -592,7 +409,7 @@ end
---@param plugin LazyPlugin ---@param plugin LazyPlugin
function M.has_errors(plugin) function M.has_errors(plugin)
for _, task in ipairs(plugin._.tasks or {}) do for _, task in ipairs(plugin._.tasks or {}) do
if task.error then if task:has_errors() then
return true return true
end end
end end
@ -627,8 +444,9 @@ function M._values(root, plugin, prop, is_list)
if not plugin[prop] then if not plugin[prop] then
return {} return {}
end end
local super = getmetatable(plugin)
---@type table ---@type table
local ret = plugin._.super and M._values(root, plugin._.super, prop, is_list) or {} local ret = super and M._values(root, super.__index, prop, is_list) or {}
local values = rawget(plugin, prop) local values = rawget(plugin, prop)
if not values then if not values then
@ -639,7 +457,28 @@ function M._values(root, plugin, prop, is_list)
end end
values = type(values) == "table" and values or { values } values = type(values) == "table" and values or { values }
return is_list and Util.extend(ret, values) or Util.merge(ret, values) if is_list then
return Util.extend(ret, values)
else
---@type {path:string[], list:any[]}[]
local lists = {}
---@diagnostic disable-next-line: no-unknown
for _, key in ipairs(plugin[prop .. "_extend"] or {}) do
local path = vim.split(key, ".", { plain = true })
local r = Util.key_get(ret, path)
local v = Util.key_get(values, path)
if type(r) == "table" and type(v) == "table" then
lists[key] = { path = path, list = {} }
vim.list_extend(lists[key].list, r)
vim.list_extend(lists[key].list, v)
end
end
local t = Util.merge(ret, values)
for _, list in pairs(lists) do
Util.key_set(t, list.path, list.list)
end
return t
end
end end
return M return M

View file

@ -5,6 +5,7 @@ local M = {}
---@type LazyProfile[] ---@type LazyProfile[]
M._profiles = { { name = "lazy" } } M._profiles = { { name = "lazy" } }
M.is_win = jit.os:find("Windows")
---@param data (string|{[string]:string})? ---@param data (string|{[string]:string})?
---@param time number? ---@param time number?
@ -28,6 +29,24 @@ function M.track(data, time)
end end
end end
function M.exiting()
return vim.v.exiting ~= vim.NIL
end
---@generic T
---@param list T[]
---@param fn fun(v: T):boolean?
---@return T[]
function M.filter(fn, list)
local ret = {}
for _, v in ipairs(list) do
if fn(v) then
table.insert(ret, v)
end
end
return ret
end
---@generic F: fun() ---@generic F: fun()
---@param data (string|{[string]:string})? ---@param data (string|{[string]:string})?
---@param fn F ---@param fn F
@ -47,7 +66,7 @@ end
---@param name string ---@param name string
---@return string ---@return string
function M.normname(name) function M.normname(name)
local ret = name:lower():gsub("^n?vim%-", ""):gsub("%.n?vim$", ""):gsub("%.lua", ""):gsub("[^a-z]+", "") local ret = name:lower():gsub("^n?vim%-", ""):gsub("%.n?vim$", ""):gsub("[%.%-]lua", ""):gsub("[^a-z]+", "")
return ret return ret
end end
@ -92,7 +111,10 @@ function M.pretty_trace(opts)
return #trace > 0 and ("\n\n# stacktrace:\n" .. table.concat(trace, "\n")) or "" return #trace > 0 and ("\n\n# stacktrace:\n" .. table.concat(trace, "\n")) or ""
end end
---@generic R
---@param fn fun():R?
---@param opts? string|{msg:string, on_error:fun(msg)} ---@param opts? string|{msg:string, on_error:fun(msg)}
---@return R
function M.try(fn, opts) function M.try(fn, opts)
opts = type(opts) == "string" and { msg = opts } or opts or {} opts = type(opts) == "string" and { msg = opts } or opts or {}
local msg = opts.msg local msg = opts.msg
@ -149,7 +171,9 @@ function M.very_lazy()
return return
end end
vim.g.did_very_lazy = true vim.g.did_very_lazy = true
M.track({ event = "VeryLazy" })
vim.api.nvim_exec_autocmds("User", { pattern = "VeryLazy", modeline = false }) vim.api.nvim_exec_autocmds("User", { pattern = "VeryLazy", modeline = false })
M.track()
end) end)
end end
@ -221,18 +245,33 @@ function M.walkmods(root, fn, modname)
end end
---@param modname string ---@param modname string
function M.get_unloaded_rtp(modname) ---@return string
modname = modname:gsub("/", ".") function M.topmod(modname)
local idx = modname:find(".", 1, true) return modname:match("^[^./]+") or modname
local topmod = idx and modname:sub(1, idx - 1) or modname end
topmod = M.normname(topmod)
---@type table<string, string[]>
M.unloaded_cache = {}
---@param modname string
---@param opts? {cache?:boolean}
function M.get_unloaded_rtp(modname, opts)
opts = opts or {}
local topmod = M.topmod(modname)
if opts.cache and M.unloaded_cache[topmod] then
return M.unloaded_cache[topmod], true
end
local norm = M.normname(topmod)
---@type string[]
local rtp = {} local rtp = {}
local Config = require("lazy.core.config") local Config = require("lazy.core.config")
if Config.spec then if Config.spec then
for _, plugin in pairs(Config.spec.plugins) do for _, plugin in pairs(Config.spec.plugins) do
if not (plugin._.loaded or plugin.module == false) then if not (plugin._.loaded or plugin.module == false or plugin.virtual) then
if topmod == M.normname(plugin.name) then if norm == M.normname(plugin.name) then
table.insert(rtp, 1, plugin.dir) table.insert(rtp, 1, plugin.dir)
else else
table.insert(rtp, plugin.dir) table.insert(rtp, plugin.dir)
@ -240,31 +279,45 @@ function M.get_unloaded_rtp(modname)
end end
end end
end end
return rtp M.unloaded_cache[topmod] = rtp
return rtp, false
end end
function M.find_root(modname) function M.find_root(modname)
local paths, cached = M.get_unloaded_rtp(modname, { cache = true })
local ret = require("lazy.core.cache").find(modname, { local ret = require("lazy.core.cache").find(modname, {
rtp = true, rtp = true,
paths = M.get_unloaded_rtp(modname), paths = paths,
patterns = { "", ".lua" }, patterns = { ".lua", "" },
})[1] })[1]
if not ret and cached then
paths = M.get_unloaded_rtp(modname)
ret = require("lazy.core.cache").find(modname, {
rtp = false,
paths = paths,
patterns = { ".lua", "" },
})[1]
end
if ret then if ret then
local root = ret.modpath:gsub("/init%.lua$", ""):gsub("%.lua$", "") return ret.modpath:gsub("%.lua$", ""), ret.modpath
return root
end end
end end
---@param modname string ---@param modname string
---@param fn fun(modname:string, modpath:string) ---@param fn fun(modname:string, modpath:string)
function M.lsmod(modname, fn) function M.lsmod(modname, fn)
local root = M.find_root(modname) local root, match = M.find_root(modname)
if not root then if not root then
return return
end end
if vim.uv.fs_stat(root .. ".lua") then if match:sub(-4) == ".lua" then
fn(modname, root .. ".lua") fn(modname, match)
if not vim.uv.fs_stat(root) then
return
end
end end
M.ls(root, function(path, name, type) M.ls(root, function(path, name, type)
@ -321,6 +374,7 @@ function M.notify(msg, opts)
local lang = opts.lang or "markdown" local lang = opts.lang or "markdown"
local n = opts.once and vim.notify_once or vim.notify local n = opts.once and vim.notify_once or vim.notify
n(msg, opts.level or vim.log.levels.INFO, { n(msg, opts.level or vim.log.levels.INFO, {
ft = lang,
on_open = function(win) on_open = function(win)
local ok = pcall(function() local ok = pcall(function()
vim.treesitter.language.add("markdown") vim.treesitter.language.add("markdown")
@ -428,4 +482,35 @@ function M.lazy_require(module)
}) })
end end
---@param t table
---@param key string|string[]
---@return any
function M.key_get(t, key)
local path = type(key) == "table" and key or vim.split(key, ".", true)
local value = t
for _, k in ipairs(path) do
if type(value) ~= "table" then
return value
end
value = value[k]
end
return value
end
---@param t table
---@param key string|string[]
---@param value any
function M.key_set(t, key, value)
local path = type(key) == "table" and key or vim.split(key, ".", true)
local last = t
for i = 1, #path - 1 do
local k = path[i]
if type(last[k]) ~= "table" then
last[k] = {}
end
last = last[k]
end
last[path[#path]] = value
end
return M return M

View file

@ -71,7 +71,7 @@ function M.commands()
modes.load.opts = true modes.load.opts = true
local lines = { local lines = {
{ "Command", "Lua", "Description" }, { "Command", "Lua", "Description" },
{ "---", "---", "---", "---" }, { "---", "---", "---" },
} }
Util.foreach(modes, function(name, mode) Util.foreach(modes, function(name, mode)
if commands[name] then if commands[name] then
@ -109,9 +109,15 @@ function M.table(lines)
return table.concat(ret, "\n") return table.concat(ret, "\n")
end end
---@param opts? {name?:string, path?:string, modname?:string}
---@return ReadmeBlock ---@return ReadmeBlock
function M.colors() function M.colors(opts)
local str = M.extract("lua/lazy/view/colors.lua", "\nM%.colors = ({.-\n})") opts = vim.tbl_extend("force", {
name = "Lazy",
path = "lua/lazy/view/colors.lua",
modname = "lazy.view.colors",
}, opts or {})
local str = M.extract(opts.path, "\nM%.colors = ({.-\n})")
---@type table<string,string> ---@type table<string,string>
local comments = {} local comments = {}
for _, line in ipairs(vim.split(str, "\n")) do for _, line in ipairs(vim.split(str, "\n")) do
@ -124,8 +130,9 @@ function M.colors()
{ "Highlight Group", "Default Group", "Description" }, { "Highlight Group", "Default Group", "Description" },
{ "---", "---", "---" }, { "---", "---", "---" },
} }
Util.foreach(require("lazy.view.colors").colors, function(group, link) Util.foreach(require(opts.modname).colors, function(group, link)
lines[#lines + 1] = { "**Lazy" .. group .. "**", "***" .. link .. "***", comments[group] or "" } link = type(link) == "table" and "`" .. vim.inspect(link):gsub("%s+", " ") .. "`" or "***" .. link .. "***"
lines[#lines + 1] = { "**" .. opts.name .. group .. "**", link, comments[group] or "" }
end) end)
return { content = M.table(lines) } return { content = M.table(lines) }
end end

View file

@ -80,8 +80,8 @@ return {
-- you can use a custom url to fetch a plugin -- you can use a custom url to fetch a plugin
{ url = "git@github.com:folke/noice.nvim.git" }, { url = "git@github.com:folke/noice.nvim.git" },
-- local plugins can also be configure with the dev option. -- local plugins can also be configured with the dev option.
-- This will use {config.dev.path}/noice.nvim/ instead of fetching it from Github -- This will use {config.dev.path}/noice.nvim/ instead of fetching it from GitHub
-- With the dev option, you can easily switch between the local and installed version of a plugin -- With the dev option, you can easily switch between the local and installed version of a plugin
{ "folke/noice.nvim", dev = true }, { "folke/noice.nvim", dev = true },
} }

View file

@ -1,4 +1,6 @@
local Config = require("lazy.core.config") local Config = require("lazy.core.config")
local Process = require("lazy.manage.process")
local uv = vim.uv or vim.loop
local M = {} local M = {}
@ -7,15 +9,67 @@ local start = vim.health.start or vim.health.report_start
local ok = vim.health.ok or vim.health.report_ok local ok = vim.health.ok or vim.health.report_ok
local warn = vim.health.warn or vim.health.report_warn local warn = vim.health.warn or vim.health.report_warn
local error = vim.health.error or vim.health.report_error local error = vim.health.error or vim.health.report_error
local info = vim.health.info or vim.health.report_info
---@class LazyHealth
---@field error? fun(msg:string)
---@field warn? fun(msg:string)
---@field ok? fun(msg:string)
---@class LazyHealthHave : LazyHealth
---@field version? string
---@field version_pattern? string
---@field optional? boolean
---@param cmd string|string[]
---@param opts? LazyHealthHave
function M.have(cmd, opts)
opts = vim.tbl_extend("force", {
error = error,
warn = warn,
ok = ok,
version = "--version",
}, opts or {})
cmd = type(cmd) == "table" and cmd or { cmd }
---@cast cmd string[]
---@type string?
local found
for _, c in ipairs(cmd) do
if vim.fn.executable(c) == 1 then
local out, exit_code = Process.exec({ c, opts.version })
if exit_code ~= 0 then
opts.error(("failed to get version of {%s}\n%s"):format(c, table.concat(out, "\n")))
else
local version = vim.trim(out[1] or "")
version = version:gsub("^%s*" .. vim.pesc(c) .. "%s*", "")
if opts.version_pattern and not version:find(opts.version_pattern, 1, true) then
opts.warn(("`%s` version `%s` needed, but found `%s`"):format(c, opts.version_pattern, version))
else
found = ("{%s} `%s`"):format(c, version)
break
end
end
end
end
if found then
opts.ok(found)
return true
else
(opts.optional and opts.warn or opts.error)(
("{%s} %snot installed"):format(
table.concat(cmd, "} or {"),
opts.version_pattern and "version `" .. opts.version_pattern .. "` " or ""
)
)
end
end
function M.check() function M.check()
start("lazy.nvim") start("lazy.nvim")
info("{lazy.nvim} version `" .. Config.version .. "`")
if vim.fn.executable("git") == 1 then M.have("git")
ok("Git installed")
else
error("Git not installed?")
end
local sites = vim.opt.packpath:get() local sites = vim.opt.packpath:get()
local default_site = vim.fn.stdpath("data") .. "/site" local default_site = vim.fn.stdpath("data") .. "/site"
@ -26,7 +80,7 @@ function M.check()
local existing = false local existing = false
for _, site in pairs(sites) do for _, site in pairs(sites) do
for _, packs in ipairs(vim.fn.expand(site .. "/pack/*", false, true)) do for _, packs in ipairs(vim.fn.expand(site .. "/pack/*", false, true)) do
if not packs:find("[/\\]dist$") and vim.uv.fs_stat(packs) then if not packs:find("[/\\]dist$") and uv.fs_stat(packs) then
existing = true existing = true
warn("found existing packages at `" .. packs .. "`") warn("found existing packages at `" .. packs .. "`")
end end
@ -36,7 +90,7 @@ function M.check()
ok("no existing packages found by other package managers") ok("no existing packages found by other package managers")
end end
for _, name in ipairs({ "packer", "plugged", "paq" }) do for _, name in ipairs({ "packer", "plugged", "paq", "pckr", "mini.deps" }) do
for _, path in ipairs(vim.opt.rtp:get()) do for _, path in ipairs(vim.opt.rtp:get()) do
if path:find(name, 1, true) then if path:find(name, 1, true) then
error("Found paths on the rtp from another plugin manager `" .. name .. "`") error("Found paths on the rtp from another plugin manager `" .. name .. "`")
@ -46,7 +100,7 @@ function M.check()
end end
local packer_compiled = vim.fn.stdpath("config") .. "/plugin/packer_compiled.lua" local packer_compiled = vim.fn.stdpath("config") .. "/plugin/packer_compiled.lua"
if vim.uv.fs_stat(packer_compiled) then if uv.fs_stat(packer_compiled) then
error("please remove the file `" .. packer_compiled .. "`") error("please remove the file `" .. packer_compiled .. "`")
else else
ok("packer_compiled.lua not found") ok("packer_compiled.lua not found")
@ -58,7 +112,6 @@ function M.check()
else else
for _, plugin in pairs(spec.plugins) do for _, plugin in pairs(spec.plugins) do
M.check_valid(plugin) M.check_valid(plugin)
M.check_override(plugin)
end end
if #spec.notifs > 0 then if #spec.notifs > 0 then
error("Issues were reported when loading your specs:") error("Issues were reported when loading your specs:")
@ -74,6 +127,47 @@ function M.check()
end end
end end
end end
start("luarocks")
if Config.options.rocks.enabled then
if Config.hererocks() then
info("checking `hererocks` installation")
else
info("checking `luarocks` installation")
end
local need_luarocks = {}
for _, plugin in pairs(spec.plugins) do
if plugin.build == "rockspec" then
table.insert(need_luarocks, plugin.name)
end
end
if #need_luarocks == 0 then
ok("no plugins require `luarocks`, so you can ignore any warnings below")
else
local lines = vim.tbl_map(function(name)
return " * `" .. name .. "`"
end, need_luarocks)
info("you have some plugins that require `luarocks`:\n" .. table.concat(lines, "\n"))
end
local ok = require("lazy.pkg.rockspec").check({
error = #need_luarocks > 0 and error or warn,
warn = warn,
ok = ok,
})
if not ok then
warn(table.concat({
"Lazy won't be able to install plugins that require `luarocks`.",
"Here's what you can do:",
" - fix your `luarocks` installation",
Config.hererocks() and " - disable *hererocks* with `opts.rocks.hererocks = false`"
or " - enable `hererocks` with `opts.rocks.hererocks = true`",
" - disable `luarocks` support completely with `opts.rocks.enabled = false`",
}, "\n"))
end
else
ok("luarocks disabled")
end
end end
---@param plugin LazyPlugin ---@param plugin LazyPlugin
@ -87,23 +181,6 @@ function M.check_valid(plugin)
end end
end end
---@param plugin LazyPlugin
function M.check_override(plugin)
if not plugin._.super then
return
end
local Handler = require("lazy.core.handler")
local skip = { "dependencies", "_", "opts", 1 }
vim.list_extend(skip, vim.tbl_values(Handler.types))
for key, value in pairs(plugin._.super) do
if not vim.tbl_contains(skip, key) and plugin[key] and plugin[key] ~= value then
warn("{" .. plugin.name .. "}: overriding <" .. key .. ">")
end
end
end
M.valid = { M.valid = {
1, 1,
"_", "_",

View file

@ -8,10 +8,11 @@ function M.index(plugin)
return {} return {}
end end
---@param file string local files = {}
local files = vim.tbl_flatten(vim.tbl_map(function(file)
return vim.fn.expand(plugin.dir .. "/" .. file, false, true) for _, file in ipairs(Config.options.readme.files) do
end, Config.options.readme.files)) vim.list_extend(files, vim.fn.expand(plugin.dir .. "/" .. file, false, true))
end
---@type table<string,{file:string, tag:string, line:string}> ---@type table<string,{file:string, tag:string, line:string}>
local tags = {} local tags = {}

View file

@ -122,7 +122,7 @@ end
function M.bootstrap() function M.bootstrap()
local lazypath = vim.fn.stdpath("data") .. "/lazy/lazy.nvim" local lazypath = vim.fn.stdpath("data") .. "/lazy/lazy.nvim"
if not vim.uv.fs_stat(lazypath) then if not (vim.uv or vim.loop).fs_stat(lazypath) then
vim.fn.system({ vim.fn.system({
"git", "git",
"clone", "clone",

View file

@ -35,7 +35,9 @@ end
function M.fast_check(opts) function M.fast_check(opts)
opts = opts or {} opts = opts or {}
for _, plugin in pairs(Config.plugins) do for _, plugin in pairs(Config.plugins) do
if not plugin.pin and plugin._.installed then -- don't check local plugins here, since we mark them as needing updates
-- only if local is behind upstream (if the git log task gives no output)
if plugin._.installed and not (plugin.pin or plugin._.is_local) then
plugin._.updates = nil plugin._.updates = nil
local info = Git.info(plugin.dir) local info = Git.info(plugin.dir)
local ok, target = pcall(Git.get_target, plugin) local ok, target = pcall(Git.get_target, plugin)

View file

@ -116,6 +116,12 @@ end
---@param plugin LazyPlugin ---@param plugin LazyPlugin
---@return GitInfo? ---@return GitInfo?
function M.get_target(plugin) function M.get_target(plugin)
if plugin._.is_local then
local info = M.info(plugin.dir)
local branch = assert(info and info.branch or M.get_branch(plugin))
return { branch = branch, commit = M.get_commit(plugin.dir, branch, true) }
end
local branch = assert(M.get_branch(plugin)) local branch = assert(M.get_branch(plugin))
if plugin.commit then if plugin.commit then
@ -144,7 +150,6 @@ function M.get_target(plugin)
} }
end end
end end
---@diagnostic disable-next-line: return-type-mismatch
return { branch = branch, commit = M.get_commit(plugin.dir, branch, true) } return { branch = branch, commit = M.get_commit(plugin.dir, branch, true) }
end end
@ -184,7 +189,12 @@ function M.get_tag_refs(repo, tagref)
tagref = tagref or "--tags" tagref = tagref or "--tags"
---@type table<string,string> ---@type table<string,string>
local tags = {} local tags = {}
local lines = Process.exec({ "git", "show-ref", "-d", tagref }, { cwd = repo }) local ok, lines = pcall(function()
return Process.exec({ "git", "show-ref", "-d", tagref }, { cwd = repo })
end)
if not ok then
return {}
end
for _, line in ipairs(lines) do for _, line in ipairs(lines) do
local ref, tag = line:match("^(%w+) refs/tags/([^%^]+)%^?{?}?$") local ref, tag = line:match("^(%w+) refs/tags/([^%^]+)%^?{?}?$")
if ref then if ref then

View file

@ -80,14 +80,23 @@ function M.install(opts)
opts = M.opts(opts, { mode = "install" }) opts = M.opts(opts, { mode = "install" })
return M.run({ return M.run({
pipeline = { pipeline = {
"plugin.exists",
"git.clone", "git.clone",
{ "git.checkout", lockfile = opts.lockfile }, { "git.checkout", lockfile = opts.lockfile },
"plugin.docs", "plugin.docs",
"wait", {
"wait",
---@param runner Runner
sync = function(runner)
require("lazy.pkg").update()
Plugin.load()
runner:update()
end,
},
"plugin.build", "plugin.build",
}, },
plugins = function(plugin) plugins = function(plugin)
return plugin.url and not plugin._.installed return not (plugin._.installed and not plugin._.build)
end, end,
}, opts):wait(function() }, opts):wait(function()
require("lazy.manage.lock").update() require("lazy.manage.lock").update()
@ -100,13 +109,22 @@ function M.update(opts)
opts = M.opts(opts, { mode = "update" }) opts = M.opts(opts, { mode = "update" })
return M.run({ return M.run({
pipeline = { pipeline = {
"plugin.exists",
"git.origin", "git.origin",
"git.branch", "git.branch",
"git.fetch", "git.fetch",
"git.status", "git.status",
{ "git.checkout", lockfile = opts.lockfile }, { "git.checkout", lockfile = opts.lockfile },
"plugin.docs", "plugin.docs",
"wait", {
"wait",
---@param runner Runner
sync = function(runner)
require("lazy.pkg").update()
Plugin.load()
runner:update()
end,
},
"plugin.build", "plugin.build",
{ "git.log", updated = true }, { "git.log", updated = true },
}, },
@ -131,6 +149,7 @@ function M.check(opts)
opts = opts or {} opts = opts or {}
return M.run({ return M.run({
pipeline = { pipeline = {
"plugin.exists",
{ "git.origin", check = true }, { "git.origin", check = true },
"git.fetch", "git.fetch",
"git.status", "git.status",
@ -220,7 +239,7 @@ function M.clear(plugins)
if plugin._.tasks then if plugin._.tasks then
---@param task LazyTask ---@param task LazyTask
plugin._.tasks = vim.tbl_filter(function(task) plugin._.tasks = vim.tbl_filter(function(task)
return task:is_running() return task:running() or task:has_errors()
end, plugin._.tasks) end, plugin._.tasks)
end end
end end

View file

@ -3,50 +3,53 @@ local Git = require("lazy.manage.git")
local M = {} local M = {}
---@type table<string, {commit:string, branch:string}> ---@alias LazyLockfile table<string, {commit:string, branch:string}>
---@type LazyLockfile
M.lock = {} M.lock = {}
M._loaded = false M._loaded = false
function M.update() function M.update()
M.load()
vim.fn.mkdir(vim.fn.fnamemodify(Config.options.lockfile, ":p:h"), "p") vim.fn.mkdir(vim.fn.fnamemodify(Config.options.lockfile, ":p:h"), "p")
local f = assert(io.open(Config.options.lockfile, "wb")) local f = assert(io.open(Config.options.lockfile, "wb"))
f:write("{\n") f:write("{\n")
M.lock = {}
---@param plugin LazyPlugin -- keep disabled and cond plugins
local plugins = vim.tbl_filter(function(plugin) for name in pairs(M.lock) do
return not plugin._.is_local and plugin._.installed if not (Config.spec.disabled[name] or Config.spec.ignore_installed[name]) then
end, Config.plugins) M.lock[name] = nil
end
end
for _, plugin in pairs(Config.plugins) do
if not plugin._.is_local and plugin._.installed then
local info = assert(Git.info(plugin.dir))
M.lock[plugin.name] = {
branch = info.branch or assert(Git.get_branch(plugin)),
commit = assert(info.commit, "commit is nil"),
}
end
end
---@param plugin LazyPlugin
---@type string[] ---@type string[]
local names = vim.tbl_map(function(plugin) local names = vim.tbl_keys(M.lock)
return plugin.name
end, plugins)
table.sort(names) table.sort(names)
for n, name in ipairs(names) do for n, name in ipairs(names) do
local plugin = Config.plugins[name] local info = M.lock[name]
if not plugin._.is_local and plugin._.installed then f:write(([[ %q: { "branch": %q, "commit": %q }]]):format(name, info.branch, info.commit))
local info = assert(Git.info(plugin.dir)) if n ~= #names then
if not info.branch then f:write(",\n")
info.branch = assert(Git.get_branch(plugin))
end
info.commit = info.commit
-- f:write(([[ [%q] = { branch = %q, commit = %q },]]):format(name, info.branch, info.commit) .. "\n")
f:write(([[ %q: { "branch": %q, "commit": %q }]]):format(name, info.branch, info.commit))
if n ~= #names then
f:write(",\n")
end
---@diagnostic disable-next-line: assign-type-mismatch
M.lock[plugin.name] = info
end end
end end
f:write("\n}") f:write("\n}\n")
f:close() f:close()
end end
function M.load() function M.load()
if M._loaded then
return
end
M.lock = {} M.lock = {}
M._loaded = true M._loaded = true
local f = io.open(Config.options.lockfile, "r") local f = io.open(Config.options.lockfile, "r")
@ -64,9 +67,7 @@ end
---@param plugin LazyPlugin ---@param plugin LazyPlugin
---@return {commit:string, branch:string} ---@return {commit:string, branch:string}
function M.get(plugin) function M.get(plugin)
if not M._loaded then M.load()
M.load()
end
return M.lock[plugin.name] return M.lock[plugin.name]
end end

View file

@ -1,66 +1,133 @@
local Async = require("lazy.async")
local Config = require("lazy.core.config") local Config = require("lazy.core.config")
local M = {}
---@type table<uv_process_t, true>
M.running = {}
M.signals = {
"HUP",
"INT",
"QUIT",
"ILL",
"TRAP",
"ABRT",
"BUS",
"FPE",
"KILL",
"USR1",
"SEGV",
"USR2",
"PIPE",
"ALRM",
"TERM",
"CHLD",
"CONT",
"STOP",
"TSTP",
"TTIN",
"TTOU",
"URG",
"XCPU",
"XFSZ",
"VTALRM",
"PROF",
"WINCH",
"IO",
"PWR",
"EMT",
"SYS",
"INFO",
}
---@diagnostic disable-next-line: no-unknown ---@diagnostic disable-next-line: no-unknown
local uv = vim.uv local uv = vim.uv
---@class ProcessOpts ---@class ProcessOpts
---@field args string[] ---@field args string[]
---@field cwd? string ---@field cwd? string
---@field on_line? fun(string) ---@field on_line? fun(line:string)
---@field on_exit? fun(ok:boolean, output:string) ---@field on_exit? fun(ok:boolean, output:string)
---@field on_data? fun(data:string, is_stderr?:boolean)
---@field timeout? number ---@field timeout? number
---@field env? table<string,string> ---@field env? table<string,string>
---@param opts? ProcessOpts local M = {}
---@param cmd string
function M.spawn(cmd, opts)
opts = opts or {}
opts.timeout = opts.timeout or (Config.options.git and Config.options.git.timeout * 1000)
---@type table<uv_process_t, LazyProcess>
M.running = setmetatable({}, { __mode = "k" })
---@class LazyProcess: Async
---@field handle? uv_process_t
---@field pid? number
---@field cmd string
---@field opts ProcessOpts
---@field timeout? uv_timer_t
---@field timedout? boolean
---@field data string
---@field check? uv_check_t
---@field code? number
---@field signal? number
local Process = setmetatable({}, { __index = Async.Async })
---@param cmd string|string[]
---@param opts? ProcessOpts
function Process.new(cmd, opts)
local self = setmetatable({}, { __index = Process })
---@async
Process.init(self, function()
self:_run()
end)
opts = opts or {}
opts.args = opts.args or {}
if type(cmd) == "table" then
self.cmd = cmd[1]
vim.list_extend(opts.args, vim.list_slice(cmd, 2))
else
self.cmd = cmd
end
opts.timeout = opts.timeout or (Config.options.git and Config.options.git.timeout * 1000)
-- make sure the cwd is valid
if not opts.cwd and type(uv.cwd()) ~= "string" then
opts.cwd = uv.os_homedir()
end
opts.on_line = opts.on_line and vim.schedule_wrap(opts.on_line) or nil
opts.on_data = opts.on_data and vim.schedule_wrap(opts.on_data) or nil
self.data = ""
self.opts = opts
self.code = 1
self.signal = 0
return self
end
---@async
function Process:_run()
self:guard()
local stdout = assert(uv.new_pipe())
local stderr = assert(uv.new_pipe())
self.handle = uv.spawn(self.cmd, {
stdio = { nil, stdout, stderr },
args = self.opts.args,
cwd = self.opts.cwd,
env = self:env(),
}, function(code, signal)
self.code = code
self.signal = signal
if self.timeout then
self.timeout:stop()
end
self.handle:close()
stdout:close()
stderr:close()
self:resume()
end)
if self.handle then
M.running[self.handle] = self
stdout:read_start(function(err, data)
self:on_data(err, data)
end)
stderr:read_start(function(err, data)
self:on_data(err, data, true)
end)
self:suspend()
while not (self.handle:is_closing() and stdout:is_closing() and stderr:is_closing()) do
Async.yield()
end
else
self.data = "Failed to spawn process " .. self.cmd .. " " .. vim.inspect(self.opts)
end
self:on_exit()
end
function Process:on_exit()
self.data = self.data:gsub("[^\r\n]+\r", "")
if self.timedout then
self.data = self.data .. "\n" .. "Process was killed because it reached the timeout"
elseif self.signal ~= 0 then
self.data = self.data .. "\n" .. "Process was killed with SIG" .. M.signals[self.signal]:upper()
end
if self.opts.on_exit then
self.opts.on_exit(self.code == 0 and self.signal == 0, self.data)
end
end
function Process:guard()
if self.opts.timeout then
self.timeout = assert(uv.new_timer())
self.timeout:start(self.opts.timeout, 0, function()
self.timedout = true
self:kill()
end)
end
end
function Process:env()
---@type table<string, string> ---@type table<string, string>
local env = vim.tbl_extend("force", { local env = vim.tbl_extend("force", {
GIT_SSH_COMMAND = "ssh -oBatchMode=yes", GIT_SSH_COMMAND = "ssh -oBatchMode=yes",
}, uv.os_environ(), opts.env or {}) }, uv.os_environ(), self.opts.env or {})
env.GIT_DIR = nil env.GIT_DIR = nil
env.GIT_WORK_TREE = nil env.GIT_WORK_TREE = nil
env.GIT_TERMINAL_PROMPT = "0" env.GIT_TERMINAL_PROMPT = "0"
@ -71,128 +138,102 @@ function M.spawn(cmd, opts)
for k, v in pairs(env) do for k, v in pairs(env) do
env_flat[#env_flat + 1] = k .. "=" .. v env_flat[#env_flat + 1] = k .. "=" .. v
end end
return env_flat
local stdout = assert(uv.new_pipe())
local stderr = assert(uv.new_pipe())
local output = ""
---@type uv_process_t?
local handle = nil
---@type uv_timer_t
local timeout
local killed = false
if opts.timeout then
timeout = assert(uv.new_timer())
timeout:start(opts.timeout, 0, function()
if M.kill(handle) then
killed = true
end
end)
end
-- make sure the cwd is valid
if not opts.cwd and type(uv.cwd()) ~= "string" then
opts.cwd = uv.os_homedir()
end
handle = uv.spawn(cmd, {
stdio = { nil, stdout, stderr },
args = opts.args,
cwd = opts.cwd,
env = env_flat,
}, function(exit_code, signal)
---@cast handle uv_process_t
M.running[handle] = nil
if timeout then
timeout:stop()
timeout:close()
end
handle:close()
stdout:close()
stderr:close()
local check = assert(uv.new_check())
check:start(function()
if not stdout:is_closing() or not stderr:is_closing() then
return
end
check:stop()
if opts.on_exit then
output = output:gsub("[^\r\n]+\r", "")
if killed then
output = output .. "\n" .. "Process was killed because it reached the timeout"
elseif signal ~= 0 then
output = output .. "\n" .. "Process was killed with SIG" .. M.signals[signal]
end
vim.schedule(function()
opts.on_exit(exit_code == 0 and signal == 0, output)
end)
end
end)
end)
if not handle then
if opts.on_exit then
opts.on_exit(false, "Failed to spawn process " .. cmd .. " " .. vim.inspect(opts))
end
return
end
M.running[handle] = true
---@param data? string
local function on_output(err, data)
assert(not err, err)
if data then
output = output .. data:gsub("\r\n", "\n")
local lines = vim.split(vim.trim(output:gsub("\r$", "")):gsub("[^\n\r]+\r", ""), "\n")
if opts.on_line then
vim.schedule(function()
opts.on_line(lines[#lines])
end)
end
end
end
uv.read_start(stdout, on_output)
uv.read_start(stderr, on_output)
return handle
end end
function M.kill(handle) ---@param signals uv.aliases.signals|uv.aliases.signals[]|nil
if handle and not handle:is_closing() then function Process:kill(signals)
M.running[handle] = nil if not self.handle or self.handle:is_closing() then
uv.process_kill(handle, "sigint") return
return true
end end
signals = signals or { "sigterm", "sigkill" }
signals = type(signals) == "table" and signals or { signals }
---@cast signals uv.aliases.signals[]
local timer = assert(uv.new_timer())
timer:start(0, 1000, function()
if self.handle and not self.handle:is_closing() and #signals > 0 then
self.handle:kill(table.remove(signals, 1))
else
timer:stop()
end
end)
end
---@param err? string
---@param data? string
---@param is_stderr? boolean
function Process:on_data(err, data, is_stderr)
assert(not err, err)
if not data then
return
end
if self.opts.on_data then
self.opts.on_data(data, is_stderr)
end
self.data = self.data .. data:gsub("\r\n", "\n")
local lines = vim.split(vim.trim(self.data:gsub("\r$", "")):gsub("[^\n\r]+\r", ""), "\n")
if self.opts.on_line then
self.opts.on_line(lines[#lines])
end
end
M.signals = {
"hup",
"int",
"quit",
"ill",
"trap",
"abrt",
"bus",
"fpe",
"kill",
"usr1",
"segv",
"usr2",
"pipe",
"alrm",
"term",
"chld",
"cont",
"stop",
"tstp",
"ttin",
"ttou",
"urg",
"xcpu",
"xfsz",
"vtalrm",
"prof",
"winch",
"io",
"pwr",
"emt",
"sys",
"info",
}
---@param cmd string|string[]
---@param opts? ProcessOpts
function M.spawn(cmd, opts)
return Process.new(cmd, opts)
end end
function M.abort() function M.abort()
for handle in pairs(M.running) do for _, proc in pairs(M.running) do
M.kill(handle) proc:kill()
end end
end end
---@param cmd string[] ---@async
---@param opts? {cwd:string, env:table} ---@param cmd string|string[]
---@param opts? ProcessOpts
function M.exec(cmd, opts) function M.exec(cmd, opts)
opts = opts or {} opts = opts or {}
---@type string[] local proc = M.spawn(cmd, opts)
local lines proc:wait()
local job = vim.fn.jobstart(cmd, { return vim.split(proc.data, "\n"), proc.code
cwd = opts.cwd,
pty = false,
env = opts.env,
stdout_buffered = true,
on_stdout = function(_, _lines)
lines = _lines
end,
})
vim.fn.jobwait({ job })
return lines
end end
return M return M

View file

@ -84,19 +84,24 @@ function M.check(start)
end end
if not (start or #changes == 0) then if not (start or #changes == 0) then
vim.schedule(function() M.reload(changes)
if Config.options.change_detection.notify and not Config.headless() then
local lines = { "# Config Change Detected. Reloading...", "" }
for _, change in ipairs(changes) do
table.insert(lines, "- **" .. change.what .. "**: `" .. vim.fn.fnamemodify(change.file, ":p:~:.") .. "`")
end
Util.warn(lines)
end
Plugin.load()
vim.api.nvim_exec_autocmds("User", { pattern = "LazyRender", modeline = false })
vim.api.nvim_exec_autocmds("User", { pattern = "LazyReload", modeline = false })
end)
end end
end end
---@param {file:string, what:string}[]
function M.reload(changes)
vim.schedule(function()
if Config.options.change_detection.notify and not Config.headless() then
local lines = { "# Config Change Detected. Reloading...", "" }
for _, change in ipairs(changes) do
table.insert(lines, "- **" .. change.what .. "**: `" .. vim.fn.fnamemodify(change.file, ":p:~:.") .. "`")
end
Util.warn(lines)
end
Plugin.load()
vim.api.nvim_exec_autocmds("User", { pattern = "LazyRender", modeline = false })
vim.api.nvim_exec_autocmds("User", { pattern = "LazyReload", modeline = false })
end)
end
return M return M

View file

@ -1,21 +1,23 @@
local Async = require("lazy.async")
local Config = require("lazy.core.config") local Config = require("lazy.core.config")
local Task = require("lazy.manage.task") local Task = require("lazy.manage.task")
local Util = require("lazy.util")
---@class RunnerOpts ---@class RunnerOpts
---@field pipeline (string|{[1]:string, [string]:any})[] ---@field pipeline (string|{[1]:string, [string]:any})[]
---@field plugins? LazyPlugin[]|fun(plugin:LazyPlugin):any? ---@field plugins? LazyPlugin[]|fun(plugin:LazyPlugin):any?
---@field concurrency? number ---@field concurrency? number
---@alias PipelineStep {task:string, opts?:TaskOptions} ---@class RunnerTask
---@alias LazyRunnerTask {co:thread, status: {task?:LazyTask, waiting?:boolean}, plugin: LazyPlugin} ---@field task? LazyTask
---@field step number
---@alias PipelineStep {task:string, opts?:TaskOptions }
---@class Runner ---@class Runner
---@field _plugins LazyPlugin[] ---@field _plugins table<string,LazyPlugin>
---@field _running LazyRunnerTask[]
---@field _pipeline PipelineStep[] ---@field _pipeline PipelineStep[]
---@field _on_done fun()[]
---@field _opts RunnerOpts ---@field _opts RunnerOpts
---@field _running? Async
local Runner = {} local Runner = {}
---@param opts RunnerOpts ---@param opts RunnerOpts
@ -24,16 +26,17 @@ function Runner.new(opts)
self._opts = opts or {} self._opts = opts or {}
local plugins = self._opts.plugins local plugins = self._opts.plugins
---@type LazyPlugin[]
local pp = {}
if type(plugins) == "function" then if type(plugins) == "function" then
self._plugins = vim.tbl_filter(plugins, Config.plugins) pp = vim.tbl_filter(plugins, Config.plugins)
else else
self._plugins = plugins or Config.plugins pp = plugins or Config.plugins
end
self._plugins = {}
for _, plugin in ipairs(pp) do
self._plugins[plugin.name] = plugin
end end
table.sort(self._plugins, function(a, b)
return a.name < b.name
end)
self._running = {}
self._on_done = {}
---@param step string|(TaskOptions|{[1]:string}) ---@param step string|(TaskOptions|{[1]:string})
self._pipeline = vim.tbl_map(function(step) self._pipeline = vim.tbl_map(function(step)
@ -43,130 +46,152 @@ function Runner.new(opts)
return self return self
end end
---@param entry LazyRunnerTask function Runner:plugin(name)
function Runner:_resume(entry) return self._plugins[name]
if entry.status.task and not entry.status.task:is_done() then
return true
end
local ok, status = coroutine.resume(entry.co)
if not ok then
Util.error("Could not resume a task\n" .. status)
end
entry.status = ok and status
return entry.status ~= nil
end end
function Runner:resume(waiting) --- Update plugins
if waiting then function Runner:update()
for _, entry in ipairs(self._running) do for name in pairs(self._plugins) do
if entry.status then self._plugins[name] = Config.plugins[name] or self._plugins[name]
if entry.status.waiting then
entry.status.waiting = false
entry.plugin._.working = true
end
end
end
end end
local running = 0
for _, entry in ipairs(self._running) do
if entry.status then
if not entry.status.waiting and self:_resume(entry) then
running = running + 1
if self._opts.concurrency and running >= self._opts.concurrency then
break
end
end
end
end
return running > 0 or (not waiting and self:resume(true))
end end
function Runner:start() function Runner:start()
for _, plugin in pairs(self._plugins) do ---@async
local co = coroutine.create(self.run_pipeline) self._running = Async.new(function()
local ok, err = coroutine.resume(co, self, plugin) self:_start()
if ok then
table.insert(self._running, { co = co, status = {}, plugin = plugin })
else
Util.error("Could not start tasks for " .. plugin.name .. "\n" .. err)
end
end
local check = vim.uv.new_check()
check:start(function()
if self:resume() then
return
end
check:stop()
self._running = {}
for _, cb in ipairs(self._on_done) do
vim.schedule(cb)
end
self._on_done = {}
end) end)
end end
---@async ---@async
---@param plugin LazyPlugin function Runner:_start()
function Runner:run_pipeline(plugin) ---@type string[]
plugin._.working = true local names = vim.tbl_keys(self._plugins)
coroutine.yield() table.sort(names)
for _, step in ipairs(self._pipeline) do
if step.task == "wait" then ---@type table<string,RunnerTask>
plugin._.working = false local state = {}
coroutine.yield({ waiting = true })
plugin._.working = true local active = 1
else local waiting = 0
local task = self:queue(plugin, step.task, step.opts) ---@type number?
if task then local wait_step = nil
coroutine.yield({ task = task })
assert(task:is_done()) ---@async
if task.error then ---@param resume? boolean
local function continue(resume)
active = 0
waiting = 0
wait_step = nil
local next = {} ---@type string[]
-- check running tasks
for _, name in ipairs(names) do
state[name] = state[name] or { step = 0 }
local s = state[name]
local is_running = s.task and s.task:running()
local step = self._pipeline[s.step]
if is_running then
-- still running
active = active + 1
-- selene:allow(empty_if)
elseif s.task and s.task:has_errors() then
-- don't continue tasks if there are errors
elseif step and step.task == "wait" and not resume then
-- waiting for sync
waiting = waiting + 1
wait_step = s.step
else
next[#next + 1] = name
end
end
-- schedule next tasks
for _, name in ipairs(next) do
if self._opts.concurrency and active >= self._opts.concurrency then
break
end
local s = state[name]
local plugin = self:plugin(name)
while s.step <= #self._pipeline do
if s.step == #self._pipeline then
-- done
s.task = nil
plugin._.working = false plugin._.working = false
return break
elseif s.step < #self._pipeline then
-- next
s.step = s.step + 1
local step = self._pipeline[s.step]
if step.task == "wait" then
plugin._.working = false
waiting = waiting + 1
wait_step = s.step
break
else
s.task = self:queue(plugin, step)
plugin._.working = true
if s.task then
active = active + 1
s.task:wake(false)
break
end
end
end end
end end
end end
end end
plugin._.working = false
while active > 0 do
continue()
if active == 0 and waiting > 0 then
local sync = self._pipeline[wait_step]
if sync and sync.opts and type(sync.opts.sync) == "function" then
sync.opts.sync(self)
end
continue(true)
end
if active > 0 then
self._running:suspend()
end
end
end end
---@param plugin LazyPlugin ---@param plugin LazyPlugin
---@param task_name string ---@param step PipelineStep
---@param opts? TaskOptions
---@return LazyTask? ---@return LazyTask?
function Runner:queue(plugin, task_name, opts) function Runner:queue(plugin, step)
assert(self._running) assert(self._running and self._running:running(), "Runner is not running")
local def = vim.split(task_name, ".", { plain = true }) local def = vim.split(step.task, ".", { plain = true })
---@type LazyTaskDef ---@type LazyTaskDef
local task_def = require("lazy.manage.task." .. def[1])[def[2]] local task_def = require("lazy.manage.task." .. def[1])[def[2]]
assert(task_def) assert(task_def, "Task not found: " .. step.task)
opts = opts or {} local opts = step.opts or {}
if not (task_def.skip and task_def.skip(plugin, opts)) then if not (task_def.skip and task_def.skip(plugin, opts)) then
local task = Task.new(plugin, def[2], task_def.run, opts) return Task.new(plugin, def[2], task_def.run, opts)
task:start()
return task
end end
end end
function Runner:is_running()
return self._running and self._running:running()
end
-- Execute the callback async when done. -- Execute the callback async when done.
-- When no callback is specified, this will wait sync -- When no callback is specified, this will wait sync
---@param cb? fun() ---@param cb? fun()
function Runner:wait(cb) function Runner:wait(cb)
if #self._running == 0 then if not self:is_running() then
if cb then if cb then
cb() cb()
end end
return self return self
end end
if cb then if cb then
table.insert(self._on_done, cb) self._running:on("done", cb)
else else
-- sync wait self._running:wait()
while #self._running > 0 do
vim.wait(10)
end
end end
return self return self
end end

View file

@ -9,6 +9,7 @@ local M = {}
---@field patch number ---@field patch number
---@field prerelease? string ---@field prerelease? string
---@field build? string ---@field build? string
---@field input? string
local Semver = {} local Semver = {}
Semver.__index = Semver Semver.__index = Semver
@ -90,6 +91,7 @@ function M.version(version)
patch = patch == "" and 0 or tonumber(patch), patch = patch == "" and 0 or tonumber(patch),
prerelease = prerelease ~= "" and prerelease or nil, prerelease = prerelease ~= "" and prerelease or nil,
build = build ~= "" and build or nil, build = build ~= "" and build or nil,
input = version,
}, Semver) }, Semver)
end end
end end

View file

@ -4,25 +4,39 @@ local Util = require("lazy.util")
---@type table<string, LazyTaskDef> ---@type table<string, LazyTaskDef>
local M = {} local M = {}
local function rm(dir)
local stat = vim.uv.fs_lstat(dir)
assert(stat and stat.type == "directory", dir .. " should be a directory!")
Util.walk(dir, function(path, _, type)
if type == "directory" then
vim.uv.fs_rmdir(path)
else
vim.uv.fs_unlink(path)
end
end)
vim.uv.fs_rmdir(dir)
end
M.clean = { M.clean = {
skip = function(plugin) skip = function(plugin)
return plugin._.is_local return plugin._.is_local
end, end,
run = function(self) ---@param opts? {rocks_only?:boolean}
run = function(self, opts)
opts = opts or {}
local dir = self.plugin.dir:gsub("/+$", "") local dir = self.plugin.dir:gsub("/+$", "")
assert(dir:find(Config.options.root, 1, true) == 1, self.plugin.dir .. " should be under packpath!") assert(dir:find(Config.options.root, 1, true) == 1, self.plugin.dir .. " should be under packpath!")
local stat = vim.uv.fs_lstat(dir) local rock_root = Config.options.rocks.root .. "/" .. self.plugin.name
assert(stat and stat.type == "directory", self.plugin.dir .. " should be a directory!") if vim.uv.fs_stat(rock_root) then
rm(rock_root)
end
Util.walk(dir, function(path, _, type) if opts.rocks_only then
if type == "directory" then return
vim.uv.fs_rmdir(path) end
else
vim.uv.fs_unlink(path) rm(dir)
end
end)
vim.uv.fs_rmdir(dir)
self.plugin._.installed = false self.plugin._.installed = false
end, end,

View file

@ -1,8 +1,54 @@
local Async = require("lazy.async")
local Config = require("lazy.core.config") local Config = require("lazy.core.config")
local Git = require("lazy.manage.git") local Git = require("lazy.manage.git")
local Lock = require("lazy.manage.lock") local Lock = require("lazy.manage.lock")
local Util = require("lazy.util") local Util = require("lazy.util")
local throttle = {}
throttle.running = 0
throttle.waiting = {} ---@type Async[]
throttle.timer = vim.uv.new_timer()
function throttle.next()
throttle.running = 0
while #throttle.waiting > 0 and throttle.running < Config.options.git.throttle.rate do
---@type Async
local task = table.remove(throttle.waiting, 1)
task:resume()
throttle.running = throttle.running + 1
end
if throttle.running == 0 then
throttle.timer:stop()
end
end
function throttle.wait()
if not Config.options.git.throttle.enabled then
return
end
if not throttle.timer:is_active() then
throttle.timer:start(0, Config.options.git.throttle.duration, vim.schedule_wrap(throttle.next))
end
local running = Async.running()
if throttle.running < Config.options.git.throttle.rate then
throttle.running = throttle.running + 1
else
table.insert(throttle.waiting, running)
coroutine.yield("waiting")
running:suspend()
coroutine.yield("")
end
end
---@param plugin LazyPlugin
local function cooldown(plugin)
if not plugin._.last_check then
return false
end
local delta = (vim.uv.now() - plugin._.last_check) / 1000
return delta < Config.options.git.cooldown
end
---@type table<string, LazyTaskDef> ---@type table<string, LazyTaskDef>
local M = {} local M = {}
@ -18,8 +64,10 @@ M.log = {
local stat = vim.uv.fs_stat(plugin.dir .. "/.git") local stat = vim.uv.fs_stat(plugin.dir .. "/.git")
return not (stat and stat.type == "directory") return not (stat and stat.type == "directory")
end, end,
---@async
---@param opts {args?: string[], updated?:boolean, check?:boolean} ---@param opts {args?: string[], updated?:boolean, check?:boolean}
run = function(self, opts) run = function(self, opts)
-- self:spawn({ "sleep", "5" })
local args = { local args = {
"log", "log",
"--pretty=format:%h %s (%cr)", "--pretty=format:%h %s (%cr)",
@ -30,11 +78,13 @@ M.log = {
"--no-show-signature", "--no-show-signature",
} }
local info, target
if opts.updated then if opts.updated then
table.insert(args, self.plugin._.updated.from .. ".." .. (self.plugin._.updated.to or "HEAD")) table.insert(args, self.plugin._.updated.from .. ".." .. (self.plugin._.updated.to or "HEAD"))
elseif opts.check then elseif opts.check then
local info = assert(Git.info(self.plugin.dir)) info = assert(Git.info(self.plugin.dir))
local target = assert(Git.get_target(self.plugin)) target = assert(Git.get_target(self.plugin))
if not target.commit then if not target.commit then
for k, v in pairs(target) do for k, v in pairs(target) do
error(k .. " '" .. v .. "' not found") error(k .. " '" .. v .. "' not found")
@ -42,15 +92,17 @@ M.log = {
error("no target commit found") error("no target commit found")
end end
assert(target.commit, self.plugin.name .. " " .. target.branch) assert(target.commit, self.plugin.name .. " " .. target.branch)
if Git.eq(info, target) then if not self.plugin._.is_local then
if Config.options.checker.check_pinned then if Git.eq(info, target) then
local last_commit = Git.get_commit(self.plugin.dir, target.branch, true) if Config.options.checker.check_pinned then
if not Git.eq(info, { commit = last_commit }) then local last_commit = Git.get_commit(self.plugin.dir, target.branch, true)
self.plugin._.outdated = true if not Git.eq(info, { commit = last_commit }) then
self.plugin._.outdated = true
end
end end
else
self.plugin._.updates = { from = info, to = target }
end end
else
self.plugin._.updates = { from = info, to = target }
end end
table.insert(args, info.commit .. ".." .. target.commit) table.insert(args, info.commit .. ".." .. target.commit)
else else
@ -61,6 +113,14 @@ M.log = {
args = args, args = args,
cwd = self.plugin.dir, cwd = self.plugin.dir,
}) })
-- for local plugins, mark as needing updates only if local is
-- behind upstream, i.e. if git log gave no output
if opts.check and self.plugin._.is_local then
if not vim.tbl_isempty(self:get_log()) then
self.plugin._.updates = { from = info, to = target }
end
end
end, end,
} }
@ -68,7 +128,9 @@ M.clone = {
skip = function(plugin) skip = function(plugin)
return plugin._.installed or plugin._.is_local return plugin._.installed or plugin._.is_local
end, end,
---@async
run = function(self) run = function(self)
throttle.wait()
local args = { local args = {
"clone", "clone",
self.plugin.url, self.plugin.url,
@ -84,6 +146,12 @@ M.clone = {
args[#args + 1] = "--origin=origin" args[#args + 1] = "--origin=origin"
-- If git config --global core.autocrlf is true on a Unix/Linux system, then the git clone
-- process will lead to files with CRLF endings. Vi / vim / neovim cannot handle this.
-- Force git to clone with core.autocrlf=false.
args[#args + 1] = "-c"
args[#args + 1] = "core.autocrlf=false"
args[#args + 1] = "--progress" args[#args + 1] = "--progress"
if self.plugin.branch then if self.plugin.branch then
@ -121,8 +189,9 @@ M.branch = {
return true return true
end end
local branch = assert(Git.get_branch(plugin)) local branch = assert(Git.get_branch(plugin))
return Git.get_commit(plugin.dir, branch) return Git.get_commit(plugin.dir, branch, true)
end, end,
---@async
run = function(self) run = function(self)
local args = { local args = {
"remote", "remote",
@ -148,14 +217,17 @@ M.origin = {
local origin = Git.get_origin(plugin.dir) local origin = Git.get_origin(plugin.dir)
return origin == plugin.url return origin == plugin.url
end, end,
---@async
---@param opts {check?:boolean} ---@param opts {check?:boolean}
run = function(self, opts) run = function(self, opts)
if opts.check then if opts.check then
local origin = Git.get_origin(self.plugin.dir) local origin = Git.get_origin(self.plugin.dir)
self.error = "Origin has changed:\n" self:error({
self.error = self.error .. " * old: " .. origin .. "\n" "Origin has changed:",
self.error = self.error .. " * new: " .. self.plugin.url .. "\n" " * old: " .. origin,
self.error = self.error .. "Please run update to fix" " * new: " .. self.plugin.url,
"Please run update to fix",
})
return return
end end
require("lazy.manage.task.fs").clean.run(self, opts) require("lazy.manage.task.fs").clean.run(self, opts)
@ -167,6 +239,7 @@ M.status = {
skip = function(plugin) skip = function(plugin)
return not plugin._.installed or plugin._.is_local return not plugin._.installed or plugin._.is_local
end, end,
---@async
run = function(self) run = function(self)
self:spawn("git", { self:spawn("git", {
args = { "ls-files", "-d", "-m" }, args = { "ls-files", "-d", "-m" },
@ -174,6 +247,7 @@ M.status = {
on_exit = function(ok, output) on_exit = function(ok, output)
if ok then if ok then
local lines = vim.split(output, "\n") local lines = vim.split(output, "\n")
---@type string[]
lines = vim.tbl_filter(function(line) lines = vim.tbl_filter(function(line)
-- Fix doc/tags being marked as modified -- Fix doc/tags being marked as modified
if line:gsub("[\\/]", "/") == "doc/tags" then if line:gsub("[\\/]", "/") == "doc/tags" then
@ -184,12 +258,13 @@ M.status = {
return line ~= "" return line ~= ""
end, lines) end, lines)
if #lines > 0 then if #lines > 0 then
self.error = "You have local changes in `" .. self.plugin.dir .. "`:\n" local msg = { "You have local changes in `" .. self.plugin.dir .. "`:" }
for _, line in ipairs(lines) do for _, line in ipairs(lines) do
self.error = self.error .. " * " .. line .. "\n" msg[#msg + 1] = " * " .. line
end end
self.error = self.error .. "Please remove them to update.\n" msg[#msg + 1] = "Please remove them to update."
self.error = self.error .. "You can also press `x` to remove the plugin and then `I` to install it again." msg[#msg + 1] = "You can also press `x` to remove the plugin and then `I` to install it again."
self:error(msg)
end end
end end
end, end,
@ -200,10 +275,12 @@ M.status = {
-- fetches all needed origin branches -- fetches all needed origin branches
M.fetch = { M.fetch = {
skip = function(plugin) skip = function(plugin)
return not plugin._.installed or plugin._.is_local return not plugin._.installed or plugin._.is_local or cooldown(plugin)
end, end,
---@async
run = function(self) run = function(self)
throttle.wait()
local args = { local args = {
"fetch", "fetch",
"--recurse-submodules", "--recurse-submodules",
@ -219,6 +296,11 @@ M.fetch = {
self:spawn("git", { self:spawn("git", {
args = args, args = args,
cwd = self.plugin.dir, cwd = self.plugin.dir,
on_exit = function(ok)
if ok then
self.plugin._.last_check = vim.uv.now()
end
end,
}) })
end, end,
} }
@ -230,8 +312,10 @@ M.checkout = {
return not plugin._.installed or plugin._.is_local return not plugin._.installed or plugin._.is_local
end, end,
---@async
---@param opts {lockfile?:boolean} ---@param opts {lockfile?:boolean}
run = function(self, opts) run = function(self, opts)
throttle.wait()
local info = assert(Git.info(self.plugin.dir)) local info = assert(Git.info(self.plugin.dir))
local target = assert(Git.get_target(self.plugin)) local target = assert(Git.get_target(self.plugin))
@ -251,7 +335,7 @@ M.checkout = {
end end
end end
-- dont run checkout if target is already reached. -- don't run checkout if target is already reached.
-- unless we just cloned, since then we won't have any data yet -- unless we just cloned, since then we won't have any data yet
if Git.eq(info, target) and info.branch == target.branch then if Git.eq(info, target) and info.branch == target.branch then
self.plugin._.updated = { self.plugin._.updated = {

View file

@ -1,23 +1,29 @@
local Async = require("lazy.async")
local Config = require("lazy.core.config")
local Process = require("lazy.manage.process") local Process = require("lazy.manage.process")
local Terminal = require("lazy.terminal")
local colors = Config.options.headless.colors
---@class LazyTaskDef ---@class LazyTaskDef
---@field skip? fun(plugin:LazyPlugin, opts?:TaskOptions):any? ---@field skip? fun(plugin:LazyPlugin, opts?:TaskOptions):any?
---@field run fun(task:LazyTask, opts:TaskOptions) ---@field run async fun(task:LazyTask, opts:TaskOptions)
---@alias LazyTaskState fun():boolean? ---@alias LazyTaskFn async fun(task:LazyTask, opts:TaskOptions)
---@class LazyTask ---@class LazyMsg
---@field msg string
---@field level? number
---@class LazyTask: Async
---@field plugin LazyPlugin ---@field plugin LazyPlugin
---@field name string ---@field name string
---@field output string ---@field private _log LazyMsg[]
---@field status string ---@field private _started number
---@field error? string
---@field private _task fun(task:LazyTask)
---@field private _running LazyPluginState[]
---@field private _started? number
---@field private _ended? number ---@field private _ended? number
---@field private _opts TaskOptions ---@field private _opts TaskOptions
local Task = {} ---@field private _level number
local Task = setmetatable({}, { __index = Async.Async })
---@class TaskOptions: {[string]:any} ---@class TaskOptions: {[string]:any}
---@field on_done? fun(task:LazyTask) ---@field on_done? fun(task:LazyTask)
@ -25,146 +31,210 @@ local Task = {}
---@param plugin LazyPlugin ---@param plugin LazyPlugin
---@param name string ---@param name string
---@param opts? TaskOptions ---@param opts? TaskOptions
---@param task fun(task:LazyTask) ---@param task LazyTaskFn
function Task.new(plugin, name, task, opts) function Task.new(plugin, name, task, opts)
local self = setmetatable({}, { local self = setmetatable({}, { __index = Task })
__index = Task, ---@async
}) Task.init(self, function()
self:_run(task)
end)
self:set_level()
self._opts = opts or {} self._opts = opts or {}
self._running = {} self._log = {}
self._task = task
self._started = nil
self.plugin = plugin self.plugin = plugin
self.name = name self.name = name
self.output = "" self._started = vim.uv.hrtime()
self.status = ""
plugin._.tasks = plugin._.tasks or {}
---@param other LazyTask ---@param other LazyTask
plugin._.tasks = vim.tbl_filter(function(other) plugin._.tasks = vim.tbl_filter(function(other)
return other.name ~= name or other:is_running() return other.name ~= name or other:running()
end, plugin._.tasks) end, plugin._.tasks or {})
table.insert(plugin._.tasks, self) table.insert(plugin._.tasks, self)
self:render()
return self return self
end end
function Task:has_started() ---@param level? number
return self._started ~= nil ---@return LazyMsg[]
function Task:get_log(level)
level = level or vim.log.levels.DEBUG
return vim.tbl_filter(function(msg)
return msg.level >= level
end, self._log)
end end
function Task:is_done() ---@param level? number
return self:has_started() and not self:is_running() function Task:output(level)
return table.concat(
---@param m LazyMsg
vim.tbl_map(function(m)
return m.msg
end, self:get_log(level)),
"\n"
)
end end
function Task:is_running() function Task:status()
return self:has_started() and self._ended == nil local ret = self._log[#self._log]
local msg = ret and vim.trim(ret.msg) or ""
return msg ~= "" and msg or nil
end end
function Task:start() function Task:has_errors()
if vim.in_fast_event() then return self._level >= vim.log.levels.ERROR
return vim.schedule(function() end
self:start()
function Task:has_warnings()
return self._level >= vim.log.levels.WARN
end
---@param level? number
function Task:set_level(level)
self._level = level or vim.log.levels.TRACE
end
---@async
---@param task LazyTaskFn
function Task:_run(task)
if Config.headless() and Config.options.headless.task then
self:log("Running task " .. self.name, vim.log.levels.INFO)
end
self
:on("done", function()
self:_done()
end) end)
:on("error", function(err)
self:error(err)
end)
:on("yield", function(msg)
self:log(msg)
end)
task(self, self._opts)
end
---@param msg string|string[]|LazyMsg
---@param level? number
function Task:log(msg, level)
if type(msg) == "table" and msg.msg then
level = msg.level or level
msg = msg.msg
end end
self._started = vim.uv.hrtime() level = level or vim.log.levels.DEBUG
---@type boolean, string|any self._level = math.max(self._level or 0, level or 0)
local ok, err = pcall(self._task, self, self._opts) msg = type(msg) == "table" and table.concat(msg, "\n") or msg
if not ok then ---@cast msg string
self.error = err or "failed" table.insert(self._log, { msg = msg, level = level })
self:render()
if Config.headless() then
self:headless()
end end
self:_check() end
function Task:render()
vim.schedule(function()
vim.api.nvim_exec_autocmds("User", { pattern = "LazyRender", modeline = false })
end)
end
function Task:headless()
if not Config.options.headless.log then
return
end
local msg = self._log[#self._log]
if not msg or msg.level == vim.log.levels.TRACE then
return
end
local map = {
[vim.log.levels.ERROR] = Terminal.red,
[vim.log.levels.WARN] = Terminal.yellow,
[vim.log.levels.INFO] = Terminal.blue,
}
local color = Config.options.headless.colors and map[msg.level]
io.write(Terminal.prefix(color and color(msg.msg) or msg.msg, self:prefix()))
io.write("\n")
end
---@param msg string|string[]
function Task:error(msg)
self:log(msg, vim.log.levels.ERROR)
end
---@param msg string|string[]
function Task:warn(msg)
self:log(msg, vim.log.levels.WARN)
end end
---@private ---@private
function Task:_check() function Task:_done()
for _, state in ipairs(self._running) do if Config.headless() and Config.options.headless.task then
if state() then local ms = math.floor(self:time() + 0.5)
return self:log("Finished task " .. self.name .. " in " .. ms .. "ms", vim.log.levels.INFO)
end
end end
self._ended = vim.uv.hrtime() self._ended = vim.uv.hrtime()
if self._opts.on_done then if self._opts.on_done then
self._opts.on_done(self) self._opts.on_done(self)
end end
vim.api.nvim_exec_autocmds("User", { pattern = "LazyRender", modeline = false }) self:render()
vim.api.nvim_exec_autocmds("User", { vim.schedule(function()
pattern = "LazyPlugin" .. self.name:sub(1, 1):upper() .. self.name:sub(2), vim.api.nvim_exec_autocmds("User", {
data = { plugin = self.plugin.name }, pattern = "LazyPlugin" .. self.name:sub(1, 1):upper() .. self.name:sub(2),
}) data = { plugin = self.plugin.name },
})
end)
end end
function Task:time() function Task:time()
if not self:has_started() then return ((self._ended or vim.uv.hrtime()) - self._started) / 1e6
return 0
end
if not self:is_done() then
return (vim.uv.hrtime() - self._started) / 1e6
end
return (self._ended - self._started) / 1e6
end
---@param fn fun()
function Task:schedule(fn)
local done = false
table.insert(self._running, function()
return not done
end)
vim.schedule(function()
---@type boolean, string|any
local ok, err = pcall(fn)
if not ok then
self.error = err or "failed"
end
done = true
self:_check()
end)
end end
---@async
---@param cmd string ---@param cmd string
---@param opts? ProcessOpts ---@param opts? ProcessOpts
function Task:spawn(cmd, opts) function Task:spawn(cmd, opts)
opts = opts or {} opts = opts or {}
local on_line = opts.on_line local on_line = opts.on_line
local on_exit = opts.on_exit
local headless = Config.headless() and Config.options.headless.process
function opts.on_line(line) function opts.on_line(line)
self.status = line if not headless then
return self:log(line, vim.log.levels.TRACE)
end
if on_line then if on_line then
pcall(on_line, line) pcall(on_line, line)
end end
vim.api.nvim_exec_autocmds("User", { pattern = "LazyRender", modeline = false })
end end
---@param output string if headless then
function opts.on_exit(ok, output) opts.on_data = function(data)
self.output = self.output .. output -- prefix with plugin name
if not ok then io.write(Terminal.prefix(data, self:prefix()))
self.error = self.error and (self.error .. "\n" .. output) or output
end end
if on_exit then
pcall(on_exit, ok, output)
end
self:_check()
end end
local proc = Process.spawn(cmd, opts) local proc = Process.spawn(cmd, opts)
table.insert(self._running, function() proc:wait()
return proc and not proc:is_closing()
end)
end
---@param tasks (LazyTask?)[] local ok = proc.code == 0 and proc.signal == 0
function Task.all_done(tasks) if not headless then
for _, task in ipairs(tasks) do local msg = vim.trim(proc.data)
if task and not task:is_done() then if #msg > 0 then
return false self:log(vim.trim(proc.data), ok and vim.log.levels.DEBUG or vim.log.levels.ERROR)
end end
end end
return true
if opts.on_exit then
pcall(opts.on_exit, ok, proc.data)
end
return ok
end end
function Task:wait() function Task:prefix()
while self:is_running() do local plugin = "[" .. self.plugin.name .. "] "
vim.wait(10) local task = string.rep(" ", 20 - #(self.name .. self.plugin.name)) .. self.name
end
return colors and Terminal.magenta(plugin) .. Terminal.cyan(task) .. Terminal.bright_black(" | ")
or plugin .. " " .. task .. " | "
end end
return Task return Task

View file

@ -1,5 +1,5 @@
local Config = require("lazy.core.config")
local Loader = require("lazy.core.loader") local Loader = require("lazy.core.loader")
local Rocks = require("lazy.pkg.rockspec")
local Util = require("lazy.util") local Util = require("lazy.util")
---@type table<string, LazyTaskDef> ---@type table<string, LazyTaskDef>
@ -8,26 +8,49 @@ local M = {}
---@param plugin LazyPlugin ---@param plugin LazyPlugin
local function get_build_file(plugin) local function get_build_file(plugin)
for _, path in ipairs({ "build.lua", "build/init.lua" }) do for _, path in ipairs({ "build.lua", "build/init.lua" }) do
path = plugin.dir .. "/" .. path if Util.file_exists(plugin.dir .. "/" .. path) then
if Util.file_exists(path) then
return path return path
end end
end end
end end
local B = {}
---@param task LazyTask
---@param build string
function B.cmd(task, build)
if task.plugin.build ~= "rockspec" then
Loader.load(task.plugin, { task = "build" })
end
local cmd = vim.api.nvim_parse_cmd(build:sub(2), {}) --[[@as vim.api.keyset.cmd]]
task:log(vim.api.nvim_cmd(cmd, { output = true }))
end
---@async
---@param task LazyTask
---@param build string
function B.shell(task, build)
local shell = vim.env.SHELL or vim.o.shell
local shell_args = shell:find("cmd.exe", 1, true) and "/c" or "-c"
task:spawn(shell, {
args = { shell_args, build },
cwd = task.plugin.dir,
})
end
M.build = { M.build = {
---@param opts? {force:boolean} ---@param opts? {force:boolean}
skip = function(plugin, opts) skip = function(plugin, opts)
if opts and opts.force then if opts and opts.force then
return false return false
end end
return not (plugin._.dirty and (plugin.build or get_build_file(plugin))) return not ((plugin._.dirty or plugin._.build) and (plugin.build or get_build_file(plugin)))
end, end,
---@async
run = function(self) run = function(self)
vim.cmd([[silent! runtime plugin/rplugin.vim]]) vim.cmd([[silent! runtime plugin/rplugin.vim]])
Loader.load(self.plugin, { task = "build" })
local builders = self.plugin.build local builders = self.plugin.build
-- Skip if `build` is set to `false` -- Skip if `build` is set to `false`
@ -35,39 +58,27 @@ M.build = {
return return
end end
local build_file = get_build_file(self.plugin) builders = builders or get_build_file(self.plugin)
if build_file then
if builders then
if Config.options.build.warn_on_override then
Util.warn(
("Plugin **%s** provides its own build script, but you also defined a `build` command.\nThe `build.lua` file will not be used"):format(
self.plugin.name
)
)
end
else
builders = function()
Loader.source(build_file)
end
end
end
if builders then if builders then
builders = type(builders) == "table" and builders or { builders } builders = type(builders) == "table" and builders or { builders }
---@cast builders (string|fun(LazyPlugin))[] ---@cast builders (string|fun(LazyPlugin))[]
for _, build in ipairs(builders) do for _, build in ipairs(builders) do
if type(build) == "string" and build:sub(1, 1) == ":" then if type(build) == "function" then
local cmd = vim.api.nvim_parse_cmd(build:sub(2), {})
self.output = vim.api.nvim_cmd(cmd, { output = true })
elseif type(build) == "function" then
build(self.plugin) build(self.plugin)
elseif build == "rockspec" then
Rocks.build(self)
elseif build:sub(1, 1) == ":" then
B.cmd(self, build)
elseif build:match("%.lua$") then
local file = self.plugin.dir .. "/" .. build
local chunk, err = loadfile(file)
if not chunk or err then
error(err)
end
chunk()
else else
local shell = vim.env.SHELL or vim.o.shell B.shell(self, build)
local shell_args = shell:find("cmd.exe", 1, true) and "/c" or "-c"
self:spawn(shell, {
args = { shell_args, build },
cwd = self.plugin.dir,
})
end end
end end
end end
@ -76,12 +87,23 @@ M.build = {
M.docs = { M.docs = {
skip = function(plugin) skip = function(plugin)
return not plugin._.dirty return not plugin._.is_local and not plugin._.dirty
end, end,
run = function(self) run = function(self)
local docs = self.plugin.dir .. "/doc/" local docs = self.plugin.dir .. "/doc"
if Util.file_exists(docs) then if Util.file_exists(docs) then
self.output = vim.api.nvim_cmd({ cmd = "helptags", args = { docs } }, { output = true }) self:log(vim.api.nvim_cmd({ cmd = "helptags", args = { docs } }, { output = true }))
end
end,
}
M.exists = {
skip = function(plugin)
return not plugin._.is_local or plugin.virtual
end,
run = function(self)
if not Util.file_exists(self.plugin.dir) then
self:error("Local plugin does not exist at `" .. self.plugin.dir .. "`")
end end
end, end,
} }

214
lua/lazy/minit.lua Normal file
View file

@ -0,0 +1,214 @@
---@diagnostic disable: inject-field
local islist = vim.islist or vim.tbl_islist
local M = {}
---@param opts LazyConfig
---@return LazySpec[]
local function get_spec(opts)
local ret = opts.spec or {}
return ret and type(ret) == "table" and islist(ret) and ret or { ret }
end
---@param defaults LazyConfig
---@param opts LazyConfig
function M.extend(defaults, opts)
local spec = {}
vim.list_extend(spec, get_spec(defaults))
vim.list_extend(spec, get_spec(opts))
return vim.tbl_deep_extend("force", defaults, opts, { spec = spec })
end
---@param opts LazyConfig
function M.setup(opts)
opts = M.extend({
local_spec = false,
change_detection = { enabled = false },
dev = {
patterns = vim.env.LAZY_DEV and vim.split(vim.env.LAZY_DEV, ",") or nil,
},
}, opts)
local args = {}
local is_busted = false
local is_minitest = false
for _, a in ipairs(_G.arg) do
if a == "--busted" then
is_busted = true
elseif a == "--minitest" then
is_minitest = true
else
table.insert(args, a)
end
end
_G.arg = args
if is_busted then
opts = M.busted.setup(opts)
elseif is_minitest then
opts = M.minitest.setup(opts)
end
-- set stdpaths to use .tests
if vim.env.LAZY_STDPATH then
local root = vim.fn.fnamemodify(vim.env.LAZY_STDPATH, ":p")
for _, name in ipairs({ "config", "data", "state", "cache" }) do
vim.env[("XDG_%s_HOME"):format(name:upper())] = root .. "/" .. name
end
end
vim.o.loadplugins = true
require("lazy").setup(opts)
if vim.g.colors_name == nil then
vim.cmd("colorscheme habamax")
end
require("lazy").update():wait()
if vim.bo.filetype == "lazy" then
local errors = false
for _, plugin in pairs(require("lazy.core.config").spec.plugins) do
errors = errors or require("lazy.core.plugin").has_errors(plugin)
end
if not errors then
vim.cmd.close()
end
end
if is_busted then
M.busted.run()
elseif is_minitest then
M.minitest.run()
end
end
function M.repro(opts)
opts = M.extend({
spec = {
{
"folke/tokyonight.nvim",
priority = 1000,
lazy = false,
config = function()
require("tokyonight").setup({ style = "moon" })
require("tokyonight").load()
end,
},
},
install = { colorscheme = { "tokyonight" } },
}, opts)
M.setup(opts)
end
M.minitest = {}
function M.minitest.run()
local Config = require("lazy.core.config")
-- disable termnial output for the tests
Config.options.headless = {}
if not require("lazy.core.config").headless() then
return vim.notify("busted can only run in headless mode. Please run with `nvim -l`", vim.log.levels.WARN)
end
package.path = package.path .. ";" .. vim.uv.cwd() .. "/tests/?.lua"
local Test = require("mini.test")
local expect = Test.expect
local _assert = assert
local Assert = {
__call = function(_, ...)
return _assert(...)
end,
same = expect.equality,
equal = expect.equality,
are = {
equal = expect.equality,
},
is_not = {
same = expect.no_equality,
},
is_not_nil = function(a)
return expect.no_equality(nil, a)
end,
is_true = function(a)
return expect.equality(true, a)
end,
is_false = function(a)
return expect.equality(false, a)
end,
}
Assert.__index = Assert
assert = setmetatable({}, Assert)
assert = require("luassert")
require("mini.test").run()
end
---@param opts LazyConfig
function M.minitest.setup(opts)
return M.extend({
spec = {
"lunarmodules/luassert",
{
"echasnovski/mini.test",
opts = {
collect = {
find_files = function()
return vim.fn.globpath("tests", "**/*_spec.lua", true, true)
end,
},
-- script_path = "tests/minit.lua",
},
},
{ dir = vim.uv.cwd() },
},
rocks = { hererocks = true },
}, opts)
end
M.busted = {}
function M.busted.run()
local Config = require("lazy.core.config")
-- disable termnial output for the tests
Config.options.headless = {}
if not require("lazy.core.config").headless() then
return vim.notify("busted can only run in headless mode. Please run with `nvim -l`", vim.log.levels.WARN)
end
package.path = package.path .. ";" .. vim.uv.cwd() .. "/tests/?.lua"
-- run busted
return pcall(require("busted.runner"), {
standalone = false,
}) or os.exit(1)
end
---@param opts LazyConfig
function M.busted.setup(opts)
local args = table.concat(_G.arg, " ")
local json = args:find("--output[ =]json")
return M.extend({
spec = {
"lunarmodules/busted",
{ dir = vim.uv.cwd() },
},
headless = {
process = not json,
log = not json,
task = not json,
},
rocks = { hererocks = true },
}, opts)
end
---@param opts LazyConfig
function M.busted.init(opts)
opts = M.busted.setup(opts)
M.setup(opts)
M.busted.run()
end
setmetatable(M.busted, {
__call = function(_, opts)
M.busted.init(opts)
end,
})
return M

137
lua/lazy/pkg/init.lua Normal file
View file

@ -0,0 +1,137 @@
local Config = require("lazy.core.config")
local Util = require("lazy.core.util")
local M = {}
M.VERSION = 12
M.dirty = false
---@class LazyPkg
---@field name string
---@field dir string
---@field source "lazy" | "packspec" | "rockspec"
---@field file string
---@field spec LazyPluginSpec
---@class LazyPkgSpec
---@field file string
---@field source? string
---@field spec? LazySpec
---@field code? string
---@class LazyPkgSource
---@field name string
---@field get fun(plugin:LazyPlugin):LazyPkgSpec?
---@class LazyPkgCache
---@field pkgs LazyPkg[]
---@field version number
---@type LazyPkg[]?
M.cache = nil
function M.update()
---@type LazyPkgSource[]
local sources = {}
for _, s in ipairs(Config.options.pkg.sources) do
if s ~= "rockspec" or Config.options.rocks.enabled then
sources[#sources + 1] = {
name = s,
get = require("lazy.pkg." .. s).get,
}
end
end
---@type LazyPkgCache
local ret = {
version = M.VERSION,
pkgs = {},
}
for _, plugin in pairs(Config.plugins) do
if plugin._.installed then
for _, source in ipairs(sources) do
local spec = source.get(plugin)
if spec then
---@type LazyPkg
local pkg = {
name = plugin.name,
dir = plugin.dir,
source = spec.source or source.name,
file = spec.file,
spec = spec.spec or {},
}
if type(spec.code) == "string" then
pkg.spec = { _raw = spec.code }
end
table.insert(ret.pkgs, pkg)
break
end
end
end
end
table.sort(ret.pkgs, function(a, b)
return a.name < b.name
end)
local U = require("lazy.util")
local code = "return " .. U.dump(ret)
vim.fn.mkdir(vim.fn.fnamemodify(Config.options.pkg.cache, ":h"), "p")
U.write_file(Config.options.pkg.cache, code)
M.dirty = false
M.cache = nil
end
local function _load()
Util.track("pkg")
M.cache = nil
if vim.uv.fs_stat(Config.options.pkg.cache) then
Util.try(function()
local chunk, err = loadfile(Config.options.pkg.cache)
if not chunk then
error(err)
end
---@type LazyPkgCache?
local ret = chunk()
if ret and ret.version == M.VERSION then
M.cache = {}
for _, pkg in ipairs(ret.pkgs) do
if type(pkg.spec) == "function" then
pkg.spec = pkg.spec()
end
-- wrap in the scope of the plugin
pkg.spec = { pkg.name, specs = pkg.spec }
end
M.cache = ret.pkgs
end
end, "Error loading pkg:")
end
if rawget(M, "cache") then
M.dirty = false
else
M.cache = {}
M.dirty = true
end
Util.track()
end
---@param dir string
---@return LazyPkg?
---@overload fun():LazyPkg[]
function M.get(dir)
if dir then
for _, pkg in ipairs(M.cache) do
if pkg.dir == dir then
return pkg
end
end
return
end
return M.cache
end
return setmetatable(M, {
__index = function(_, key)
if key == "cache" then
_load()
return M.cache
end
end,
})

29
lua/lazy/pkg/lazy.lua Normal file
View file

@ -0,0 +1,29 @@
local Util = require("lazy.util")
local M = {}
M.lazy_file = "lazy.lua"
---@param plugin LazyPlugin
---@return LazyPkg?
function M.get(plugin)
local file = Util.norm(plugin.dir .. "/" .. M.lazy_file)
if Util.file_exists(file) then
---@type fun(): LazySpec
local chunk = Util.try(function()
local ret, err = loadfile(file)
return err and error(err) or ret
end, "`" .. M.lazy_file .. "` for **" .. plugin.name .. "** has errors:")
if not chunk then
Util.error("Invalid `" .. M.lazy_file .. "` for **" .. plugin.name .. "**")
return
end
return {
source = "lazy",
file = M.lazy_file,
code = "function()\n" .. Util.read_file(file) .. "\nend",
}
end
end
return M

54
lua/lazy/pkg/packspec.lua Normal file
View file

@ -0,0 +1,54 @@
local Util = require("lazy.util")
---@class PackSpec
---@field dependencies? table<string, string>
---@field lazy? LazyPluginSpec
---
local M = {}
M.pkg_file = "pkg.json"
---@param plugin LazyPlugin
---@return LazyPkg?
function M.get(plugin)
local file = Util.norm(plugin.dir .. "/" .. M.pkg_file)
if not Util.file_exists(file) then
return
end
---@type PackSpec
local pkg = Util.try(function()
return vim.json.decode(Util.read_file(file))
end, "`" .. M.pkg_file .. "` for **" .. plugin.name .. "** has errors:")
if not pkg then
return
end
---@type LazySpec
local ret = {}
if pkg.dependencies then
for url, version in pairs(pkg.dependencies) do
-- HACK: Add `.git` to github urls
if url:find("github") and not url:match("%.git$") then
url = url .. ".git"
end
ret[#ret + 1] = { url = url, version = version }
end
end
local p = pkg.lazy
if p then
p.url = p.url or plugin.url
p.dir = p.dir or plugin.dir
ret[#ret + 1] = p
end
if pkg.lazy then
ret[#ret + 1] = pkg.lazy
end
return {
source = "lazy",
file = M.pkg_file,
spec = ret,
}
end
return M

343
lua/lazy/pkg/rockspec.lua Normal file
View file

@ -0,0 +1,343 @@
--# selene:allow(incorrect_standard_library_use)
local Community = require("lazy.community")
local Config = require("lazy.core.config")
local Health = require("lazy.health")
local Util = require("lazy.util")
---@class RockSpec
---@field rockspec_format string
---@field package string
---@field version string
---@field dependencies string[]
---@field build? {type?: string, modules?: any[]}
---@field source? {url?: string}
---@class RockManifest
---@field repository table<string, table<string,any>>
local M = {}
M.skip = { "lua" }
M.rewrites = {
["plenary.nvim"] = { "nvim-lua/plenary.nvim", lazy = true },
}
M.python = { "python3", "python" }
---@class HereRocks
M.hererocks = {}
---@param task LazyTask
function M.hererocks.build(task)
local root = Config.options.rocks.root .. "/hererocks"
---@param p string
local python = vim.tbl_filter(function(p)
return vim.fn.executable(p) == 1
end, M.python)[1]
task:spawn(python, {
args = {
"hererocks.py",
"--verbose",
"-l",
"5.1",
"-r",
"latest",
root,
},
cwd = task.plugin.dir,
})
end
---@param bin string
function M.hererocks.bin(bin)
local hererocks = Config.options.rocks.root .. "/hererocks/bin"
return Util.norm(hererocks .. "/" .. bin)
end
-- check if hererocks is building
---@return boolean?
function M.hererocks.building()
return vim.tbl_get(Config.plugins.hererocks or {}, "_", "build")
end
---@param opts? LazyHealth
function M.check(opts)
opts = vim.tbl_extend("force", {
error = Util.error,
warn = Util.warn,
ok = function() end,
}, opts or {})
local ok = false
if Config.hererocks() then
if M.hererocks.building() then
ok = true
else
ok = Health.have(M.python, opts)
ok = Health.have(M.hererocks.bin("luarocks")) and ok
Health.have(
M.hererocks.bin("lua"),
vim.tbl_extend("force", opts, {
version = "-v",
version_pattern = "5.1",
})
)
end
else
ok = Health.have("luarocks", opts)
Health.have(
{ "lua5.1", "lua", "lua-5.1" },
vim.tbl_extend("force", opts, {
version = "-v",
version_pattern = "5.1",
})
)
end
return ok
end
---@async
---@param task LazyTask
function M.build(task)
M.check({
error = function(msg)
task:error(msg:gsub("[{}]", "`"))
end,
warn = function(msg)
task:warn(msg)
end,
ok = function(msg) end,
})
if task:has_warnings() then
task:log({
"",
"This plugin requires `luarocks`. Try one of the following:",
" - fix your `luarocks` installation",
Config.hererocks() and " - disable *hererocks* with `opts.rocks.hererocks = false`"
or " - enable `hererocks` with `opts.rocks.hererocks = true`",
" - disable `luarocks` support completely with `opts.rocks.enabled = false`",
})
task:warn("\nWill try building anyway, but will likely fail...")
task:warn("\n" .. string.rep("-", 80) .. "\n")
task:set_level(vim.log.levels.WARN)
end
if task.plugin.name == "hererocks" then
return M.hererocks.build(task)
end
local env = {}
local luarocks = "luarocks"
if Config.hererocks() then
-- hererocks is still building, so skip for now
-- a new build will happen in the next round
if M.hererocks.building() then
return
end
local sep = Util.is_win and ";" or ":"
local hererocks = Config.options.rocks.root .. "/hererocks/bin"
if Util.is_win then
hererocks = hererocks:gsub("/", "\\")
end
local path = vim.split(vim.env.PATH, sep)
table.insert(path, 1, hererocks)
env = {
PATH = table.concat(path, sep),
}
if Util.is_win then
luarocks = luarocks .. ".bat"
end
end
local pkg = task.plugin._.pkg
assert(pkg, "missing rockspec pkg for " .. task.plugin.name .. "\nThis shouldn't happen, please report.")
local rockspec = M.rockspec(task.plugin.dir .. "/" .. pkg.file) or {}
assert(
rockspec.package,
"missing rockspec package name for " .. task.plugin.name .. "\nThis shouldn't happen, please report."
)
local root = Config.options.rocks.root .. "/" .. task.plugin.name
local ok = task:spawn(luarocks, {
args = {
"--tree",
root,
"--server",
Config.options.rocks.server,
"--lua-version",
"5.1",
"install", -- use install so that we can make use of pre-built rocks
"--force-fast",
"--deps-mode",
"one",
rockspec.package,
},
cwd = task.plugin.dir,
env = env,
})
if ok then
return
end
task:warn("Failed installing " .. rockspec.package .. " with `luarocks`.")
task:warn("\n" .. string.rep("-", 80) .. "\n")
task:warn("Trying to build from source.")
-- install failed, so try building from source
task:set_level() -- reset level
ok = task:spawn(luarocks, {
args = {
"--tree",
root,
"--dev",
"--lua-version",
"5.1",
"make",
"--force-fast",
"--deps-mode",
"one",
},
cwd = task.plugin.dir,
env = env,
})
if not ok then
require("lazy.manage.task.fs").clean.run(task, { rocks_only = true })
end
end
---@param rockspec RockSpec
function M.is_simple_build(rockspec)
local type = vim.tbl_get(rockspec, "build", "type")
return type == nil or type == "none" or (type == "builtin" and not rockspec.build.modules)
end
---@param file string
---@return table?
function M.parse(file)
local ret = {}
local ok = pcall(function()
loadfile(file, nil, ret)()
end) and ret or nil
return ok and ret or nil
end
---@param plugin LazyPlugin
function M.deps(plugin)
local root = Config.options.rocks.root .. "/" .. plugin.name
---@type RockManifest?
local manifest = M.parse(root .. "/lib/luarocks/rocks-5.1/manifest")
return manifest and vim.tbl_keys(manifest.repository or {})
end
---@param file string
---@return RockSpec?
function M.rockspec(file)
return M.parse(file)
end
---@param plugin LazyPlugin
function M.find_rockspec(plugin)
local rockspec_file ---@type string?
Util.ls(plugin.dir, function(path, name, t)
if t == "file" then
for _, suffix in ipairs({ "scm", "git", "dev" }) do
suffix = suffix .. "-1.rockspec"
if name:sub(-#suffix) == suffix then
rockspec_file = path
return false
end
end
end
end)
return rockspec_file
end
---@param plugin LazyPlugin
---@return LazyPkgSpec?
function M.get(plugin)
if Community.get_spec(plugin.name) then
return {
file = "community",
source = "lazy",
spec = Community.get_spec(plugin.name),
}
end
local rockspec_file = M.find_rockspec(plugin)
local rockspec = rockspec_file and M.rockspec(rockspec_file)
if not rockspec then
return
end
local has_lua = not not vim.uv.fs_stat(plugin.dir .. "/lua")
---@type LazyPluginSpec
local specs = {}
---@param dep string
local rocks = vim.tbl_filter(function(dep)
local name = dep:gsub("%s.*", "")
local url = Community.get_url(name)
local spec = Community.get_spec(name)
if spec then
-- community spec
table.insert(specs, spec)
return false
elseif url then
-- Neovim plugin rock
table.insert(specs, { url })
return false
end
return not vim.tbl_contains(M.skip, name)
end, rockspec.dependencies or {})
local use =
-- package without a /lua directory
not has_lua
-- has dependencies that are not skipped,
-- not in community specs,
-- and don't have a rockspec mapping
or #rocks > 0
-- has a complex build process
or not M.is_simple_build(rockspec)
if not use then
-- community specs only
return #specs > 0
and {
file = vim.fn.fnamemodify(rockspec_file, ":t"),
spec = {
plugin.name,
specs = specs,
build = false,
},
}
or nil
end
local lazy = nil
if not has_lua then
lazy = false
end
return {
file = vim.fn.fnamemodify(rockspec_file, ":t"),
spec = {
plugin.name,
build = "rockspec",
lazy = lazy,
},
}
end
return M

View file

@ -6,7 +6,7 @@ local M = {}
M._stats = { M._stats = {
-- startuptime in milliseconds till UIEnter -- startuptime in milliseconds till UIEnter
startuptime = 0, startuptime = 0,
-- when true, startuptime is the accurate cputime for the Neovim process. (Linux & Macos) -- when true, startuptime is the accurate cputime for the Neovim process. (Linux & macOS)
-- this is more accurate than `nvim --startuptime`, and as such will be slightly higher -- this is more accurate than `nvim --startuptime`, and as such will be slightly higher
-- when false, startuptime is calculated based on a delta with a timestamp when lazy started. -- when false, startuptime is calculated based on a delta with a timestamp when lazy started.
real_cputime = false, real_cputime = false,
@ -21,6 +21,7 @@ M.C = nil
function M.on_ui_enter() function M.on_ui_enter()
M._stats.startuptime = M.track("UIEnter") M._stats.startuptime = M.track("UIEnter")
require("lazy.core.util").track({ start = "startuptime" }, M._stats.startuptime * 1e6)
vim.api.nvim_exec_autocmds("User", { pattern = "LazyVimStarted", modeline = false }) vim.api.nvim_exec_autocmds("User", { pattern = "LazyVimStarted", modeline = false })
end end

71
lua/lazy/terminal.lua Normal file
View file

@ -0,0 +1,71 @@
---@class Ansi: table<string, fun(string):string>
local M = {}
M.colors = {
reset = "\27[0m",
black = "\27[30m",
red = "\27[31m",
green = "\27[32m",
yellow = "\27[33m",
blue = "\27[34m",
magenta = "\27[35m",
cyan = "\27[36m",
white = "\27[37m",
bright_black = "\27[90m",
bright_red = "\27[91m",
bright_green = "\27[92m",
bright_yellow = "\27[93m",
bright_blue = "\27[94m",
bright_magenta = "\27[95m",
bright_cyan = "\27[96m",
bright_white = "\27[97m",
}
function M.color(text, color)
return M.colors[color] .. text .. M.colors.reset
end
-- stylua: ignore start
function M.black(text) return M.color(text, "black") end
function M.red(text) return M.color(text, "red") end
function M.green(text) return M.color(text, "green") end
function M.yellow(text) return M.color(text, "yellow") end
function M.blue(text) return M.color(text, "blue") end
function M.magenta(text) return M.color(text, "magenta") end
function M.cyan(text) return M.color(text, "cyan") end
function M.white(text) return M.color(text, "white") end
function M.bright_black(text) return M.color(text, "bright_black") end
function M.bright_red(text) return M.color(text, "bright_red") end
function M.bright_green(text) return M.color(text, "bright_green") end
function M.bright_yellow(text) return M.color(text, "bright_yellow") end
function M.bright_blue(text) return M.color(text, "bright_blue") end
function M.bright_magenta(text) return M.color(text, "bright_magenta") end
function M.bright_cyan(text) return M.color(text, "bright_cyan") end
function M.bright_white(text) return M.color(text, "bright_white") end
-- stylua: ignore end
---@param data string
---@param prefix string
function M.prefix(data, prefix)
-- Normalize Windows-style newlines to simple newlines
data = data:gsub("\r\n", "\n")
-- Handle prefix for the first line, if data starts immediately
data = prefix .. data
-- Prefix new lines ensuring not to double prefix if a line starts with \r
data = data:gsub("(\n)([^\r])", "%1" .. prefix .. "%2")
-- Handle carriage returns properly to avoid double prefixing
-- Replace any \r not followed by \n with \r, then add a prefix only if the following character isn't the start of our prefix
data = data:gsub("\r([^\n])", function(nextChar)
if nextChar:sub(1, #prefix) == prefix then
return "\r" .. nextChar
else
return "\r" .. prefix .. nextChar
end
end)
return data
end
return M

View file

@ -2,28 +2,28 @@
---@alias LazyPluginKind "normal"|"clean"|"disabled" ---@alias LazyPluginKind "normal"|"clean"|"disabled"
---@class LazyPluginState ---@class LazyPluginState
---@field fid number id of the plugin spec fragment
---@field fpid? number parent id of the plugin spec fragment
---@field fdeps? number[] children ids of the fragment
---@field loaded? {[string]:string}|{time:number}
---@field installed? boolean
---@field tasks? LazyTask[]
---@field working? boolean
---@field dirty? boolean
---@field updated? {from:string, to:string}
---@field is_local? boolean
---@field updates? {from:GitInfo, to:GitInfo}
---@field cloned? boolean
---@field outdated? boolean
---@field kind? LazyPluginKind
---@field dep? boolean True if this plugin is only in the spec as a dependency
---@field cond? boolean
---@field super? LazyPlugin
---@field module? string
---@field dir? string Explicit dir or dev set for this plugin
---@field rtp_loaded? boolean
---@field handlers? LazyPluginHandlers
---@field cache? table<string,any> ---@field cache? table<string,any>
---@field cloned? boolean
---@field cond? boolean
---@field dep? boolean True if this plugin is only in the spec as a dependency
---@field dir? string Explicit dir or dev set for this plugin
---@field dirty? boolean
---@field build? boolean
---@field frags? number[]
---@field top? boolean
---@field handlers? LazyPluginHandlers
---@field installed? boolean
---@field is_local? boolean
---@field kind? LazyPluginKind
---@field loaded? {[string]:string}|{time:number}
---@field outdated? boolean
---@field rtp_loaded? boolean
---@field tasks? LazyTask[]
---@field updated? {from:string, to:string}
---@field updates? {from:GitInfo, to:GitInfo}
---@field last_check? number
---@field working? boolean
---@field pkg? LazyPkg
---@alias PluginOpts table|fun(self:LazyPlugin, opts:table):table? ---@alias PluginOpts table|fun(self:LazyPlugin, opts:table):table?
@ -31,7 +31,7 @@
---@field init? fun(self:LazyPlugin) Will always be run ---@field init? fun(self:LazyPlugin) Will always be run
---@field deactivate? fun(self:LazyPlugin) Unload/Stop a plugin ---@field deactivate? fun(self:LazyPlugin) Unload/Stop a plugin
---@field config? fun(self:LazyPlugin, opts:table)|true Will be executed when loading the plugin ---@field config? fun(self:LazyPlugin, opts:table)|true Will be executed when loading the plugin
---@field build? string|fun(self:LazyPlugin)|(string|fun(self:LazyPlugin))[] ---@field build? false|string|async fun(self:LazyPlugin)|(string|async fun(self:LazyPlugin))[]
---@field opts? PluginOpts ---@field opts? PluginOpts
---@class LazyPluginHandlers ---@class LazyPluginHandlers
@ -44,7 +44,7 @@
---@field branch? string ---@field branch? string
---@field tag? string ---@field tag? string
---@field commit? string ---@field commit? string
---@field version? string ---@field version? string|boolean
---@field pin? boolean ---@field pin? boolean
---@field submodules? boolean Defaults to true ---@field submodules? boolean Defaults to true
@ -60,9 +60,12 @@
---@field lazy? boolean ---@field lazy? boolean
---@field priority? number Only useful for lazy=false plugins to force loading certain plugins first. Default priority is 50 ---@field priority? number Only useful for lazy=false plugins to force loading certain plugins first. Default priority is 50
---@field dev? boolean If set, then link to the respective folder under your ~/projects ---@field dev? boolean If set, then link to the respective folder under your ~/projects
---@field rocks? string[]
---@field virtual? boolean virtual plugins won't be installed or added to the rtp.
---@class LazyPlugin: LazyPluginBase,LazyPluginHandlers,LazyPluginHooks,LazyPluginRef ---@class LazyPlugin: LazyPluginBase,LazyPluginHandlers,LazyPluginHooks,LazyPluginRef
---@field dependencies? string[] ---@field dependencies? string[]
---@field specs? string|string[]|LazyPluginSpec[]
---@field _ LazyPluginState ---@field _ LazyPluginState
---@class LazyPluginSpecHandlers ---@class LazyPluginSpecHandlers
@ -73,11 +76,27 @@
---@field module? false ---@field module? false
---@class LazyPluginSpec: LazyPluginBase,LazyPluginSpecHandlers,LazyPluginHooks,LazyPluginRef ---@class LazyPluginSpec: LazyPluginBase,LazyPluginSpecHandlers,LazyPluginHooks,LazyPluginRef
---@field name? string display name and name used for plugin config files
---@field dir? string
---@field dependencies? string|string[]|LazyPluginSpec[] ---@field dependencies? string|string[]|LazyPluginSpec[]
---@field specs? string|string[]|LazyPluginSpec[]
---@alias LazySpec string|LazyPluginSpec|LazySpecImport|LazySpec[] ---@alias LazySpec string|LazyPluginSpec|LazySpecImport|LazySpec[]
---@class LazySpecImport ---@class LazySpecImport
---@field import string spec module to import ---@field import string|(fun():LazyPluginSpec) spec module to import
---@field name? string
---@field enabled? boolean|(fun():boolean) ---@field enabled? boolean|(fun():boolean)
---@field cond? boolean|(fun():boolean) ---@field cond? boolean|(fun():boolean)
---@class LazyFragment
---@field id number
---@field pkg? boolean
---@field pid? number
---@field deps? number[]
---@field frags? number[]
---@field dep? boolean
---@field name string
---@field url? string
---@field dir? string
---@field spec LazyPlugin

View file

@ -19,13 +19,15 @@ function M.wo(win, k, v)
end end
end end
function M.open(uri) ---@param opts? {system?:boolean}
if M.file_exists(uri) then function M.open(uri, opts)
opts = opts or {}
if not opts.system and M.file_exists(uri) then
return M.float({ style = "", file = uri }) return M.float({ style = "", file = uri })
end end
local Config = require("lazy.core.config") local Config = require("lazy.core.config")
local cmd local cmd
if Config.options.ui.browser then if not opts.system and Config.options.ui.browser then
cmd = { Config.options.ui.browser, uri } cmd = { Config.options.ui.browser, uri }
elseif vim.fn.has("win32") == 1 then elseif vim.fn.has("win32") == 1 then
cmd = { "explorer", uri } cmd = { "explorer", uri }
@ -71,31 +73,57 @@ end
---@param fn F ---@param fn F
---@return F ---@return F
function M.throttle(ms, fn) function M.throttle(ms, fn)
local timer = vim.uv.new_timer() ---@type Async
local running = false local async
local first = true local pending = false
return function(...) return function()
local args = { ... } if async and async:running() then
local wrapped = function() pending = true
fn(unpack(args)) return
end end
if not running then ---@async
if first then async = require("lazy.async").new(function()
wrapped() repeat
first = false pending = false
end fn()
async:sleep(ms)
timer:start(ms, 0, function() until not pending
running = false end)
vim.schedule(wrapped)
end)
running = true
end
end end
end end
--- Creates a weak reference to an object.
--- Calling the returned function will return the object if it has not been garbage collected.
---@generic T: table
---@param obj T
---@return T|fun():T?
function M.weak(obj)
local weak = { _obj = obj }
---@return table<any, any>
local function get()
local ret = rawget(weak, "_obj")
return ret == nil and error("Object has been garbage collected", 2) or ret
end
local mt = {
__mode = "v",
__call = function(t)
return rawget(t, "_obj")
end,
__index = function(_, k)
return get()[k]
end,
__newindex = function(_, k, v)
get()[k] = v
end,
__pairs = function()
return pairs(get())
end,
}
return setmetatable(weak, mt)
end
---@class LazyCmdOptions: LazyFloatOptions ---@class LazyCmdOptions: LazyFloatOptions
---@field cwd? string ---@field cwd? string
---@field env? table<string,string> ---@field env? table<string,string>
@ -134,12 +162,21 @@ end
---@param opts? LazyCmdOptions|{filetype?:string} ---@param opts? LazyCmdOptions|{filetype?:string}
function M.float_cmd(cmd, opts) function M.float_cmd(cmd, opts)
opts = opts or {} opts = opts or {}
local Process = require("lazy.manage.process")
local lines, code = Process.exec(cmd, { cwd = opts.cwd })
if code ~= 0 then
M.error({
"`" .. table.concat(cmd, " ") .. "`",
"",
"## Error",
table.concat(lines, "\n"),
}, { title = "Command Failed (" .. code .. ")" })
return
end
local float = M.float(opts) local float = M.float(opts)
if opts.filetype then if opts.filetype then
vim.bo[float.buf].filetype = opts.filetype vim.bo[float.buf].filetype = opts.filetype
end end
local Process = require("lazy.manage.process")
local lines = Process.exec(cmd, { cwd = opts.cwd })
vim.api.nvim_buf_set_lines(float.buf, 0, -1, false, lines) vim.api.nvim_buf_set_lines(float.buf, 0, -1, false, lines)
vim.bo[float.buf].modifiable = false vim.bo[float.buf].modifiable = false
return float return float
@ -205,20 +242,25 @@ function M._dump(value, result)
table.insert(result, tostring(value)) table.insert(result, tostring(value))
elseif t == "string" then elseif t == "string" then
table.insert(result, ("%q"):format(value)) table.insert(result, ("%q"):format(value))
elseif t == "table" and value._raw then
table.insert(result, value._raw)
elseif t == "table" then elseif t == "table" then
table.insert(result, "{") table.insert(result, "{")
local i = 1 for _, v in ipairs(value) do
---@diagnostic disable-next-line: no-unknown
for k, v in pairs(value) do
if k == i then
elseif type(k) == "string" then
table.insert(result, ("[%q]="):format(k))
else
table.insert(result, k .. "=")
end
M._dump(v, result) M._dump(v, result)
table.insert(result, ",") table.insert(result, ",")
i = i + 1 end
---@diagnostic disable-next-line: no-unknown
for k, v in pairs(value) do
if type(k) == "string" then
if k:match("^[a-zA-Z]+$") then
table.insert(result, ("%s="):format(k))
else
table.insert(result, ("[%q]="):format(k))
end
M._dump(v, result)
table.insert(result, ",")
end
end end
table.insert(result, "}") table.insert(result, "}")
else else

View file

@ -30,19 +30,22 @@ M.colors = {
Button = "CursorLine", Button = "CursorLine",
ButtonActive = "Visual", ButtonActive = "Visual",
TaskOutput = "MsgArea", -- task output TaskOutput = "MsgArea", -- task output
TaskError = "ErrorMsg", -- task errors Error = "DiagnosticError", -- task errors
Warning = "DiagnosticWarn", -- task errors
Info = "DiagnosticInfo", -- task errors
Dir = "@markup.link", -- directory Dir = "@markup.link", -- directory
Url = "@markup.link", -- url Url = "@markup.link", -- url
Bold = { bold = true },
Italic = { italic = true },
} }
M.did_setup = false M.did_setup = false
function M.set_hl() function M.set_hl()
for hl_group, link in pairs(M.colors) do for hl_group, link in pairs(M.colors) do
vim.api.nvim_set_hl(0, "Lazy" .. hl_group, { local hl = type(link) == "table" and link or { link = link }
link = link, hl.default = true
default = true, vim.api.nvim_set_hl(0, "Lazy" .. hl_group, hl)
})
end end
end end
@ -54,6 +57,11 @@ function M.setup()
M.did_setup = true M.did_setup = true
M.set_hl() M.set_hl()
vim.api.nvim_create_autocmd("VimEnter", {
callback = function()
M.set_hl()
end,
})
vim.api.nvim_create_autocmd("ColorScheme", { vim.api.nvim_create_autocmd("ColorScheme", {
callback = function() callback = function()
M.set_hl() M.set_hl()

View file

@ -34,6 +34,21 @@ M.commands = {
health = function() health = function()
vim.cmd.checkhealth("lazy") vim.cmd.checkhealth("lazy")
end, end,
---@param opts ManagerOpts
pkg = function(opts)
local Pkg = require("lazy.pkg")
Pkg.update()
require("lazy.manage.reloader").reload({
{
file = "pkg",
what = "changed",
},
})
for _, plugin in pairs(opts and opts.plugins or {}) do
local spec = Pkg.get(plugin.dir)
Util.info(vim.inspect(spec), { lang = "lua", title = plugin.name })
end
end,
home = function() home = function()
View.show("home") View.show("home")
end, end,
@ -56,6 +71,9 @@ M.commands = {
end, end,
reload = function(opts) reload = function(opts)
for _, plugin in pairs(opts.plugins) do for _, plugin in pairs(opts.plugins) do
if type(plugin) == "string" then
plugin = Config.plugins[plugin]
end
Util.warn("Reloading **" .. plugin.name .. "**") Util.warn("Reloading **" .. plugin.name .. "**")
require("lazy.core.loader").reload(plugin) require("lazy.core.loader").reload(plugin)
end end
@ -71,7 +89,7 @@ M.commands = {
} }
function M.complete(cmd, prefix) function M.complete(cmd, prefix)
if not (ViewConfig.commands[cmd] or {}).plugins then if not (ViewConfig.commands[cmd] or {}).plugins and cmd ~= "pkg" then
return return
end end
---@type string[] ---@type string[]
@ -128,7 +146,7 @@ end
---@return string, string[] ---@return string, string[]
function M.parse(args) function M.parse(args)
local parts = vim.split(vim.trim(args), "%s+") local parts = vim.split(vim.trim(args), "%s+")
if parts[1]:find("Lazy") then if vim.startswith("Lazy", parts[1]) then
table.remove(parts, 1) table.remove(parts, 1)
end end
if args:sub(-1) == " " then if args:sub(-1) == " " then

View file

@ -34,6 +34,8 @@ M.keys = {
profile_sort = "<C-s>", profile_sort = "<C-s>",
profile_filter = "<C-f>", profile_filter = "<C-f>",
abort = "<C-c>", abort = "<C-c>",
next = "]]",
prev = "[[",
} }
---@type table<string,LazyViewCommand> ---@type table<string,LazyViewCommand>

View file

@ -15,12 +15,16 @@ local ViewConfig = require("lazy.view.config")
---@field persistent? boolean ---@field persistent? boolean
---@field ft? string ---@field ft? string
---@field noautocmd? boolean ---@field noautocmd? boolean
---@field backdrop? float
---@class LazyFloat ---@class LazyFloat
---@field buf number ---@field buf number
---@field win number ---@field win number
---@field opts LazyFloatOptions ---@field opts LazyFloatOptions
---@field win_opts LazyWinOpts ---@field win_opts LazyWinOpts
---@field backdrop_buf number
---@field backdrop_win number
---@field id number
---@overload fun(opts?:LazyFloatOptions):LazyFloat ---@overload fun(opts?:LazyFloatOptions):LazyFloat
local M = {} local M = {}
@ -30,6 +34,12 @@ setmetatable(M, {
end, end,
}) })
local _id = 0
local function next_id()
_id = _id + 1
return _id
end
---@param opts? LazyFloatOptions ---@param opts? LazyFloatOptions
function M.new(opts) function M.new(opts)
local self = setmetatable({}, { __index = M }) local self = setmetatable({}, { __index = M })
@ -39,10 +49,12 @@ end
---@param opts? LazyFloatOptions ---@param opts? LazyFloatOptions
function M:init(opts) function M:init(opts)
require("lazy.view.colors").setup() require("lazy.view.colors").setup()
self.id = next_id()
self.opts = vim.tbl_deep_extend("force", { self.opts = vim.tbl_deep_extend("force", {
size = Config.options.ui.size, size = Config.options.ui.size,
style = "minimal", style = "minimal",
border = Config.options.ui.border or "none", border = Config.options.ui.border or "none",
backdrop = Config.options.ui.backdrop or 60,
zindex = 50, zindex = 50,
}, opts or {}) }, opts or {})
@ -61,8 +73,13 @@ function M:init(opts)
title_pos = self.opts.title and self.opts.title_pos or nil, title_pos = self.opts.title and self.opts.title_pos or nil,
} }
self:mount() self:mount()
self:on_key(ViewConfig.keys.close, self.close) self:on("VimEnter", function()
self:on({ "BufDelete", "BufHidden" }, self.close, { once = true }) vim.schedule(function()
if not self:win_valid() then
self:close()
end
end)
end, { buffer = false })
return self return self
end end
@ -114,9 +131,43 @@ function M:mount()
self.buf = vim.api.nvim_create_buf(false, true) self.buf = vim.api.nvim_create_buf(false, true)
end end
local normal, has_bg
if vim.fn.has("nvim-0.9.0") == 0 then
normal = vim.api.nvim_get_hl_by_name("Normal", true)
has_bg = normal and normal.background ~= nil
else
normal = vim.api.nvim_get_hl(0, { name = "Normal" })
has_bg = normal and normal.bg ~= nil
end
if has_bg and self.opts.backdrop and self.opts.backdrop < 100 and vim.o.termguicolors then
self.backdrop_buf = vim.api.nvim_create_buf(false, true)
self.backdrop_win = vim.api.nvim_open_win(self.backdrop_buf, false, {
relative = "editor",
width = vim.o.columns,
height = vim.o.lines,
row = 0,
col = 0,
style = "minimal",
focusable = false,
zindex = self.opts.zindex - 1,
})
vim.api.nvim_set_hl(0, "LazyBackdrop", { bg = "#000000", default = true })
Util.wo(self.backdrop_win, "winhighlight", "Normal:LazyBackdrop")
Util.wo(self.backdrop_win, "winblend", self.opts.backdrop)
vim.bo[self.backdrop_buf].buftype = "nofile"
vim.bo[self.backdrop_buf].filetype = "lazy_backdrop"
end
self:layout() self:layout()
self.win = vim.api.nvim_open_win(self.buf, true, self.win_opts) self.win = vim.api.nvim_open_win(self.buf, true, self.win_opts)
self:on("WinClosed", function()
self:close()
self:augroup(true)
end, { win = true })
self:focus() self:focus()
self:on_key(ViewConfig.keys.close, self.close, "Close")
self:on({ "BufDelete", "BufHidden" }, self.close)
if vim.bo[self.buf].buftype == "" then if vim.bo[self.buf].buftype == "" then
vim.bo[self.buf].buftype = "nofile" vim.bo[self.buf].buftype = "nofile"
@ -149,40 +200,60 @@ function M:mount()
end end
config.style = self.opts.style ~= "" and self.opts.style or nil config.style = self.opts.style ~= "" and self.opts.style or nil
vim.api.nvim_win_set_config(self.win, config) vim.api.nvim_win_set_config(self.win, config)
if self.backdrop_win and vim.api.nvim_win_is_valid(self.backdrop_win) then
vim.api.nvim_win_set_config(self.backdrop_win, {
width = vim.o.columns,
height = vim.o.lines,
})
end
opts() opts()
vim.api.nvim_exec_autocmds("User", { pattern = "LazyFloatResized", modeline = false }) vim.api.nvim_exec_autocmds("User", { pattern = "LazyFloatResized", modeline = false })
end, end,
}) })
end end
---@param clear? boolean
function M:augroup(clear)
return vim.api.nvim_create_augroup("trouble.window." .. self.id, { clear = clear == true })
end
---@param events string|string[] ---@param events string|string[]
---@param fn fun(self?):boolean? ---@param fn fun(self:LazyFloat, event:{buf:number}):boolean?
---@param opts? table ---@param opts? vim.api.keyset.create_autocmd | {buffer: false, win?:boolean}
function M:on(events, fn, opts) function M:on(events, fn, opts)
if type(events) == "string" then opts = opts or {}
events = { events } if opts.win then
opts.pattern = self.win .. ""
opts.win = nil
elseif opts.buffer == nil then
opts.buffer = self.buf
elseif opts.buffer == false then
opts.buffer = nil
end end
for _, e in ipairs(events) do if opts.pattern then
local event, pattern = e:match("(%w+) (%w+)") opts.buffer = nil
event = event or e
vim.api.nvim_create_autocmd(
event,
vim.tbl_extend("force", {
pattern = pattern,
buffer = (not pattern) and self.buf or nil,
callback = function()
return fn(self)
end,
}, opts or {})
)
end end
local _self = Util.weak(self)
opts.callback = function(e)
local this = _self()
if not this then
-- delete the autocmd
return true
end
return fn(this, e)
end
opts.group = self:augroup()
vim.api.nvim_create_autocmd(events, opts)
end end
---@param key string ---@param key string
---@param fn fun(self?) ---@param fn fun(self?)
---@param desc? string ---@param desc? string
function M:on_key(key, fn, desc) ---@param mode? string[]
vim.keymap.set("n", key, function() function M:on_key(key, fn, desc, mode)
vim.keymap.set(mode or "n", key, function()
fn(self) fn(self)
end, { end, {
nowait = true, nowait = true,
@ -193,6 +264,7 @@ end
---@param opts? {wipe:boolean} ---@param opts? {wipe:boolean}
function M:close(opts) function M:close(opts)
self:augroup(true)
local buf = self.buf local buf = self.buf
local win = self.win local win = self.win
local wipe = opts and opts.wipe local wipe = opts and opts.wipe
@ -204,7 +276,18 @@ function M:close(opts)
if wipe then if wipe then
self.buf = nil self.buf = nil
end end
local backdrop_buf = self.backdrop_buf
local backdrop_win = self.backdrop_win
self.backdrop_buf = nil
self.backdrop_win = nil
vim.schedule(function() vim.schedule(function()
if backdrop_win and vim.api.nvim_win_is_valid(backdrop_win) then
vim.api.nvim_win_close(backdrop_win, true)
end
if backdrop_buf and vim.api.nvim_buf_is_valid(backdrop_buf) then
vim.api.nvim_buf_delete(backdrop_buf, { force = true })
end
if win and vim.api.nvim_win_is_valid(win) then if win and vim.api.nvim_win_is_valid(win) then
vim.api.nvim_win_close(win, true) vim.api.nvim_win_close(win, true)
end end
@ -212,6 +295,7 @@ function M:close(opts)
vim.diagnostic.reset(Config.ns, buf) vim.diagnostic.reset(Config.ns, buf)
vim.api.nvim_buf_delete(buf, { force = true }) vim.api.nvim_buf_delete(buf, { force = true })
end end
vim.cmd.redraw()
end) end)
end end

View file

@ -53,7 +53,7 @@ function M.create()
Float.init(self, { Float.init(self, {
title = Config.options.ui.title, title = Config.options.ui.title,
title_pos = Config.options.ui.title_pos, title_pos = Config.options.ui.title_pos,
noautocmd = true, noautocmd = false,
}) })
if Config.options.ui.wrap then if Config.options.ui.wrap then
@ -67,19 +67,27 @@ function M.create()
self.state = vim.deepcopy(default_state) self.state = vim.deepcopy(default_state)
self.render = Render.new(self) self.render = Render.new(self)
self.update = Util.throttle(Config.options.ui.throttle, self.update) local update = self.update
self.update = Util.throttle(Config.options.ui.throttle, function()
self:on({ "User LazyRender", "User LazyFloatResized" }, function() update(self)
if not (self.buf and vim.api.nvim_buf_is_valid(self.buf)) then
return true
end
self:update()
end) end)
for _, pattern in ipairs({ "LazyRender", "LazyFloatResized" }) do
self:on({ "User" }, function()
if not (self.buf and vim.api.nvim_buf_is_valid(self.buf)) then
return true
end
self:update()
end, { pattern = pattern })
end
vim.keymap.set("n", ViewConfig.keys.abort, function() vim.keymap.set("n", ViewConfig.keys.abort, function()
require("lazy.manage.process").abort() require("lazy.manage.process").abort()
require("lazy.async").abort()
return ViewConfig.keys.abort return ViewConfig.keys.abort
end, { silent = true, buffer = self.buf, expr = true }) end, { silent = true, buffer = self.buf, expr = true, desc = "Abort" })
vim.keymap.set("n", "gx", "K", { buffer = self.buf, remap = true })
-- plugin details -- plugin details
self:on_key(ViewConfig.keys.details, function() self:on_key(ViewConfig.keys.details, function()
@ -89,17 +97,49 @@ function M.create()
name = plugin.name, name = plugin.name,
kind = plugin._.kind, kind = plugin._.kind,
} }
self.state.plugin = not vim.deep_equal(self.state.plugin, selected) and selected or nil
local open = not vim.deep_equal(self.state.plugin, selected)
if not open then
local row = self.render:get_row(selected)
if row then
vim.api.nvim_win_set_cursor(self.view.win, { row, 8 })
end
end
self.state.plugin = open and selected or nil
self:update() self:update()
end end
end) end, "Details")
self:on_key(ViewConfig.keys.next, function()
local cursor = vim.api.nvim_win_get_cursor(self.view.win)
for l = 1, #self.render.locations, 1 do
local loc = self.render.locations[l]
if loc.from > cursor[1] then
vim.api.nvim_win_set_cursor(self.view.win, { loc.from, 8 })
return
end
end
end, "Next Plugin")
self:on_key(ViewConfig.keys.prev, function()
local cursor = vim.api.nvim_win_get_cursor(self.view.win)
for l = #self.render.locations, 1, -1 do
local loc = self.render.locations[l]
if loc.from < cursor[1] then
vim.api.nvim_win_set_cursor(self.view.win, { loc.from, 8 })
return
end
end
end, "Prev Plugin")
self:on_key(ViewConfig.keys.profile_sort, function() self:on_key(ViewConfig.keys.profile_sort, function()
if self.state.mode == "profile" then if self.state.mode == "profile" then
self.state.profile.sort_time_taken = not self.state.profile.sort_time_taken self.state.profile.sort_time_taken = not self.state.profile.sort_time_taken
self:update() self:update()
end end
end) end, "Sort Profile")
self:on_key(ViewConfig.keys.profile_filter, function() self:on_key(ViewConfig.keys.profile_filter, function()
if self.state.mode == "profile" then if self.state.mode == "profile" then
@ -119,17 +159,18 @@ function M.create()
end end
end) end)
end end
end) end, "Filter Profile")
for lhs, rhs in pairs(Config.options.ui.custom_keys) do for lhs, rhs in pairs(Config.options.ui.custom_keys) do
if rhs then if rhs then
local handler = type(rhs) == "table" and rhs[1] or rhs local handler = type(rhs) == "table" and rhs[1] or rhs
local desc = type(rhs) == "table" and rhs.desc or nil
self:on_key(lhs, function() self:on_key(lhs, function()
local plugin = self.render:get_plugin() local plugin = self.render:get_plugin()
if plugin then if plugin then
handler(plugin) handler(plugin)
end end
end) end, desc)
end end
end end
@ -140,9 +181,7 @@ end
function M:update() function M:update()
if self.buf and vim.api.nvim_buf_is_valid(self.buf) then if self.buf and vim.api.nvim_buf_is_valid(self.buf) then
vim.bo[self.buf].modifiable = true
self.render:update() self.render:update()
vim.bo[self.buf].modifiable = false
vim.cmd.redraw() vim.cmd.redraw()
end end
end end
@ -181,17 +220,17 @@ function M:setup_patterns()
["(https?://%S+)"] = function(url) ["(https?://%S+)"] = function(url)
Util.open(url) Util.open(url)
end, end,
}, self.hover) }, self.hover, "Hover")
self:on_pattern(ViewConfig.keys.diff, { self:on_pattern(ViewConfig.keys.diff, {
[commit_pattern] = function(hash) [commit_pattern] = function(hash)
self:diff({ commit = hash }) self:diff({ commit = hash })
end, end,
}, self.diff) }, self.diff, "Diff")
self:on_pattern(ViewConfig.commands.restore.key_plugin, { self:on_pattern(ViewConfig.commands.restore.key_plugin, {
[commit_pattern] = function(hash) [commit_pattern] = function(hash)
self:restore({ commit = hash }) self:restore({ commit = hash })
end, end,
}, self.restore) }, self.restore, "Restore")
end end
---@param opts? {commit:string} ---@param opts? {commit:string}
@ -209,13 +248,13 @@ function M:restore(opts)
end end
function M:hover() function M:hover()
if self:diff({ browser = true }) then if self:diff({ browser = true, hover = true }) then
return return
end end
self:open_url("") self:open_url("")
end end
---@param opts? {commit?:string, browser:boolean} ---@param opts? {commit?:string, browser:boolean, hover:boolean}
function M:diff(opts) function M:diff(opts)
opts = opts or {} opts = opts or {}
local plugin = self.render:get_plugin() local plugin = self.render:get_plugin()
@ -229,6 +268,9 @@ function M:diff(opts)
local info = assert(Git.info(plugin.dir)) local info = assert(Git.info(plugin.dir))
local target = assert(Git.get_target(plugin)) local target = assert(Git.get_target(plugin))
diff = { from = info.commit, to = target.commit } diff = { from = info.commit, to = target.commit }
if opts.hover and diff.from == diff.to then
return
end
end end
if not diff then if not diff then
@ -253,7 +295,8 @@ end
---@param key string ---@param key string
---@param patterns table<string, fun(str:string)> ---@param patterns table<string, fun(str:string)>
---@param fallback? fun(self) ---@param fallback? fun(self)
function M:on_pattern(key, patterns, fallback) ---@param desc? string
function M:on_pattern(key, patterns, fallback, desc)
self:on_key(key, function() self:on_key(key, function()
local line = vim.api.nvim_get_current_line() local line = vim.api.nvim_get_current_line()
local pos = vim.api.nvim_win_get_cursor(0) local pos = vim.api.nvim_win_get_cursor(0)
@ -275,7 +318,7 @@ function M:on_pattern(key, patterns, fallback)
if fallback then if fallback then
fallback(self) fallback(self)
end end
end) end, desc)
end end
function M:setup_modes() function M:setup_modes()
@ -292,11 +335,28 @@ function M:setup_modes()
end end
if m.key_plugin and name ~= "restore" then if m.key_plugin and name ~= "restore" then
self:on_key(m.key_plugin, function() self:on_key(m.key_plugin, function()
local plugin = self.render:get_plugin() local esc = vim.api.nvim_replace_termcodes("<esc>", true, true, true)
if plugin then vim.api.nvim_feedkeys(esc, "n", false)
Commands.cmd(name, { plugins = { plugin } }) local plugins = {}
if vim.api.nvim_get_mode().mode:lower() == "v" then
local f, t = vim.fn.line("."), vim.fn.line("v")
if f > t then
f, t = t, f
end
for i = f, t do
local plugin = self.render:get_plugin(i)
if plugin then
plugins[plugin.name] = plugin
end
end
plugins = vim.tbl_values(plugins)
else
plugins[1] = self.render:get_plugin()
end end
end, m.desc_plugin) if #plugins > 0 then
Commands.cmd(name, { plugins = plugins })
end
end, m.desc_plugin, { "n", "x" })
end end
end end
end end

View file

@ -51,7 +51,7 @@ function M:update()
if plugin._.tasks then if plugin._.tasks then
for _, task in ipairs(plugin._.tasks) do for _, task in ipairs(plugin._.tasks) do
self.progress.total = self.progress.total + 1 self.progress.total = self.progress.total + 1
if not task:is_running() then if not task:running() then
self.progress.done = self.progress.done + 1 self.progress.done = self.progress.done + 1
end end
end end
@ -74,7 +74,17 @@ function M:update()
end end
self:trim() self:trim()
vim.bo[self.view.buf].modifiable = true
local view = vim.api.nvim_win_call(self.view.win, vim.fn.winsaveview)
self:render(self.view.buf) self:render(self.view.buf)
vim.api.nvim_win_call(self.view.win, function()
vim.fn.winrestview(view)
end)
vim.bo[self.view.buf].modifiable = false
vim.diagnostic.set( vim.diagnostic.set(
Config.ns, Config.ns,
self.view.buf, self.view.buf,
@ -84,7 +94,7 @@ function M:update()
diag.lnum = diag.row - 1 diag.lnum = diag.row - 1
return diag return diag
end, self._diagnostics), end, self._diagnostics),
{ signs = false, virtual_text = true } { signs = false, virtual_text = true, underline = false, virtual_lines = false }
) )
end end
@ -112,6 +122,15 @@ function M:get_plugin(row)
end end
end end
---@param selected {name:string, kind?: LazyPluginKind}
function M:get_row(selected)
for _, loc in ipairs(self.locations) do
if loc.kind == selected.kind and loc.name == selected.name then
return loc.from
end
end
end
function M:title() function M:title()
self:nl() self:nl()
local modes = vim.tbl_filter(function(c) local modes = vim.tbl_filter(function(c)
@ -125,8 +144,6 @@ function M:title()
if mode.name == "home" then if mode.name == "home" then
if self.view.state.mode == "home" then if self.view.state.mode == "home" then
title = " lazy.nvim " .. Config.options.ui.icons.lazy title = " lazy.nvim " .. Config.options.ui.icons.lazy
else
title = " lazy.nvim (H) "
end end
end end
@ -182,7 +199,15 @@ function M:help()
:nl() :nl()
self:append("or the plugin was just updated. Otherwise the plugin webpage will open."):nl():nl() self:append("or the plugin was just updated. Otherwise the plugin webpage will open."):nl():nl()
self:append("Use "):append("<d>", "LazySpecial"):append(" on a commit or plugin to open the diff view"):nl() self:append("Use "):append("<d>", "LazySpecial"):append(" on a commit or plugin to open the diff view"):nl():nl()
self
:append("Use ")
:append("<]]>", "LazySpecial")
:append(" and ")
:append("<[[>", "LazySpecial")
:append(" to navigate between plugins")
:nl()
:nl()
self:nl() self:nl()
self:append("Keyboard Shortcuts", "LazyH2"):nl() self:append("Keyboard Shortcuts", "LazyH2"):nl()
@ -354,7 +379,37 @@ end
---@param plugin LazyPlugin ---@param plugin LazyPlugin
function M:diagnostics(plugin) function M:diagnostics(plugin)
if plugin._.updated then local skip = false
for _, task in ipairs(plugin._.tasks or {}) do
if task:running() then
self:diagnostic({
severity = vim.diagnostic.severity.WARN,
message = task.name .. (task:status() and (": " .. task:status()) or ""),
})
skip = true
elseif task:has_errors() then
self:diagnostic({
message = task.name .. " failed",
severity = vim.diagnostic.severity.ERROR,
})
skip = true
elseif task:has_warnings() then
self:diagnostic({
message = task.name .. " warning",
severity = vim.diagnostic.severity.WARN,
})
skip = true
end
end
if skip then
return
end
if plugin._.build then
self:diagnostic({
message = "needs build",
severity = vim.diagnostic.severity.WARN,
})
elseif plugin._.updated then
if plugin._.updated.from == plugin._.updated.to then if plugin._.updated.from == plugin._.updated.to then
self:diagnostic({ self:diagnostic({
message = "already up to date", message = "already up to date",
@ -383,19 +438,6 @@ function M:diagnostics(plugin)
}) })
end end
end end
for _, task in ipairs(plugin._.tasks or {}) do
if task:is_running() then
self:diagnostic({
severity = vim.diagnostic.severity.WARN,
message = task.name .. (task.status == "" and "" or (": " .. task.status)),
})
elseif task.error then
self:diagnostic({
message = task.name .. " failed",
severity = vim.diagnostic.severity.ERROR,
})
end
end
end end
---@param plugin LazyPlugin ---@param plugin LazyPlugin
@ -434,6 +476,22 @@ function M:plugin(plugin)
{ name = plugin.name, from = plugin_start, to = self:row() - 1, kind = plugin._.kind } { name = plugin.name, from = plugin_start, to = self:row() - 1, kind = plugin._.kind }
end end
---@param str string
---@param hl? string|Extmark
---@param opts? {indent?: number, prefix?: string, wrap?: boolean}
function M:markdown(str, hl, opts)
local lines = vim.split(str, "\n")
for _, line in ipairs(lines) do
self:append(line, hl, opts):highlight({
["`.-`"] = "@markup.raw.markdown_inline",
["%*.-%*"] = "LazyItalic",
["%*%*.-%*%*"] = "LazyBold",
["^%s*-"] = "Special",
})
self:nl()
end
end
---@param plugin LazyPlugin ---@param plugin LazyPlugin
function M:tasks(plugin) function M:tasks(plugin)
for _, task in ipairs(plugin._.tasks or {}) do for _, task in ipairs(plugin._.tasks or {}) do
@ -442,46 +500,56 @@ function M:tasks(plugin)
self:append(" " .. math.floor((task:time()) * 100) / 100 .. "ms", "Bold") self:append(" " .. math.floor((task:time()) * 100) / 100 .. "ms", "Bold")
self:nl() self:nl()
end end
if task.error then
self:append(vim.trim(task.error), "LazyTaskError", { indent = 6 }) if not task:has_warnings() and task.name == "log" then
self:nl()
elseif task.name == "log" then
self:log(task) self:log(task)
elseif self.view:is_selected(plugin) and task.output ~= "" and task.output ~= task.error then else
self:append(vim.trim(task.output), "LazyTaskOutput", { indent = 6 }) local hls = {
self:nl() [vim.log.levels.ERROR] = "LazyError",
[vim.log.levels.WARN] = "LazyWarning",
[vim.log.levels.INFO] = "LazyInfo",
}
for _, msg in ipairs(task:get_log()) do
if task:has_warnings() or self.view:is_selected(plugin) then
self:markdown(msg.msg, hls[msg.level] or "LazyTaskOutput", { indent = 6 })
end
end
end end
end end
end end
---@param task LazyTask ---@param task LazyTask
function M:log(task) function M:log(task)
local log = vim.trim(task.output) local log = vim.trim(task:output())
if log ~= "" then if log ~= "" then
local lines = vim.split(log, "\n") local lines = vim.split(log, "\n")
for _, line in ipairs(lines) do for _, line in ipairs(lines) do
local ref, msg, time = line:match("^(%w+) (.*) (%(.*%))$") local ref, msg, time = line:match("^(%w+) (.*) (%(.*%))$")
if msg:find("^%S+!:") then if msg then
self:diagnostic({ message = "Breaking Changes", severity = vim.diagnostic.severity.WARN }) if msg:find("^%S+!:") then
end self:diagnostic({ message = "Breaking Changes", severity = vim.diagnostic.severity.WARN })
self:append(ref:sub(1, 7) .. " ", "LazyCommit", { indent = 6 })
local dimmed = false
for _, dim in ipairs(ViewConfig.dimmed_commits) do
if msg:find("^" .. dim) then
dimmed = true
end end
self:append(ref:sub(1, 7) .. " ", "LazyCommit", { indent = 6 })
local dimmed = false
for _, dim in ipairs(ViewConfig.dimmed_commits) do
if msg:find("^" .. dim) then
dimmed = true
end
end
self:append(vim.trim(msg), dimmed and "LazyDimmed" or nil):highlight({
["#%d+"] = "LazyCommitIssue",
["^%S+:"] = dimmed and "Bold" or "LazyCommitType",
["^%S+(%(.*%))!?:"] = "LazyCommitScope",
["`.-`"] = "@markup.raw.markdown_inline",
["%*.-%*"] = "Italic",
["%*%*.-%*%*"] = "Bold",
})
self:append(" " .. time, "LazyComment")
self:nl()
-- else
-- self:append(line, "LazyTaskOutput", { indent = 6 }):nl()
end end
self:append(vim.trim(msg), dimmed and "LazyDimmed" or nil):highlight({
["#%d+"] = "LazyCommitIssue",
["^%S+:"] = dimmed and "Bold" or "LazyCommitType",
["^%S+(%(.*%))!?:"] = "LazyCommitScope",
["`.-`"] = "@markup.raw.markdown_inline",
["%*.-%*"] = "Italic",
["%*%*.-%*%*"] = "Bold",
})
self:append(" " .. time, "LazyComment")
self:nl()
end end
self:nl() self:nl()
end end
@ -511,6 +579,11 @@ function M:details(plugin)
table.insert(props, { "commit", git.commit:sub(1, 7), "LazyCommit" }) table.insert(props, { "commit", git.commit:sub(1, 7), "LazyCommit" })
end end
end end
local rocks = require("lazy.pkg.rockspec").deps(plugin)
if rocks then
table.insert(props, { "rocks", vim.inspect(rocks) })
end
if Util.file_exists(plugin.dir .. "/README.md") then if Util.file_exists(plugin.dir .. "/README.md") then
table.insert(props, { "readme", "README.md" }) table.insert(props, { "readme", "README.md" })
end end
@ -686,7 +759,7 @@ function M:debug()
---@type string[] ---@type string[]
plugins = vim.tbl_values(plugins) plugins = vim.tbl_values(plugins)
table.sort(plugins) table.sort(plugins)
self:append("", "LazySpecial", { indent = 2 }) self:append(Config.options.ui.icons.debug, "LazySpecial", { indent = 2 })
if handler_type == "keys" then if handler_type == "keys" then
for k, v in pairs(Config.plugins[plugins[1]]._.handlers.keys) do for k, v in pairs(Config.plugins[plugins[1]]._.handlers.keys) do
if k == value then if k == value then

View file

@ -17,7 +17,7 @@ return {
{ {
filter = function(plugin) filter = function(plugin)
return has_task(plugin, function(task) return has_task(plugin, function(task)
return task.error ~= nil return task:has_errors()
end) end)
end, end,
title = "Failed", title = "Failed",
@ -28,19 +28,24 @@ return {
return true return true
end end
return has_task(plugin, function(task) return has_task(plugin, function(task)
return task:is_running() return task:running()
end) end)
end, end,
title = "Working", title = "Working",
}, },
{
filter = function(plugin)
return plugin._.build
end,
title = "Build",
},
{ {
filter = function(plugin) filter = function(plugin)
return has_task(plugin, function(task) return has_task(plugin, function(task)
if task.name ~= "log" then if task.name ~= "log" then
return return
end end
local lines = vim.split(task.output, "\n") for _, line in ipairs(vim.split(task:output(), "\n")) do
for _, line in ipairs(lines) do
if line:find("^%w+ %S+!:") then if line:find("^%w+ %S+!:") then
return true return true
end end
@ -71,7 +76,7 @@ return {
{ {
filter = function(plugin) filter = function(plugin)
return has_task(plugin, function(task) return has_task(plugin, function(task)
return task.name == "log" and vim.trim(task.output) ~= "" return task.name == "log" and vim.trim(task:output()) ~= ""
end) end)
end, end,
title = "Log", title = "Log",
@ -89,7 +94,7 @@ return {
title = "Not Installed", title = "Not Installed",
}, },
{ {
filter = function (plugin) filter = function(plugin)
return plugin._.outdated return plugin._.outdated
end, end,
title = "Outdated", title = "Outdated",

View file

@ -11,9 +11,7 @@ local Util = require("lazy.util")
local Text = {} local Text = {}
function Text.new() function Text.new()
local self = setmetatable({}, { local self = setmetatable({}, { __index = Text })
__index = Text,
})
self._lines = {} self._lines = {}
return self return self

116407
manifest Normal file

File diff suppressed because it is too large Load diff

3
scripts/test Executable file
View file

@ -0,0 +1,3 @@
#!/bin/env bash
nvim -l tests/minit.lua --minitest

View file

@ -1 +1,4 @@
std="vim" std="vim"
[lints]
mixed_table="allow"

View file

@ -1,39 +0,0 @@
local Git = require("lazy.manage.git")
describe("lazy", function()
before_each(function()
vim.g.lazy_did_setup = false
vim.go.loadplugins = true
for modname in pairs(package.loaded) do
if modname:find("lazy") == 1 then
package.loaded[modname] = nil
end
end
end)
local root = ".tests/data/nvim/lazy"
it("installs plugins", function()
local Lazy = require("lazy")
local Config = require("lazy.core.config")
local neodev = false
Lazy.setup({
{
"folke/neodev.nvim",
config = function()
neodev = true
end,
},
"folke/paint.nvim",
}, { install_missing = true, defaults = { lazy = true } })
assert(3 == vim.tbl_count(Config.plugins))
assert(vim.uv.fs_stat(root .. "/paint.nvim/README.md"))
assert(vim.uv.fs_stat(root .. "/neodev.nvim/README.md"))
assert(not neodev)
assert(Config.plugins["neodev.nvim"]._.installed)
assert(not Config.plugins["neodev.nvim"]._.is_local)
assert.equal("https://github.com/folke/neodev.nvim.git", Git.get_origin(Config.plugins["neodev.nvim"].dir))
assert.equal("https://github.com/folke/paint.nvim.git", Git.get_origin(Config.plugins["paint.nvim"].dir))
end)
end)

View file

@ -1,7 +1,10 @@
local Util = require("lazy.core.util")
describe("init", function() describe("init", function()
it("has correct environment for tests", function() it("has correct environment for tests", function()
for _, path in ipairs({ "config", "data", "cache", "state" }) do for _, name in ipairs({ "config", "data", "cache", "state" }) do
assert(vim.fn.stdpath(path):find(".tests/" .. path)) local path = Util.norm(vim.fn.stdpath(name) --[[@as string]])
assert(path:find(".tests/" .. name, 1, true), path .. " not in .tests")
end end
end) end)
end) end)

View file

@ -2,23 +2,32 @@ local Config = require("lazy.core.config")
local Handler = require("lazy.core.handler") local Handler = require("lazy.core.handler")
local Plugin = require("lazy.core.plugin") local Plugin = require("lazy.core.plugin")
local assert = require("luassert") local function inspect(obj)
return vim.inspect(obj):gsub("%s+", " ")
end
Config.setup() ---@param plugin LazyPlugin
local function resolve(plugin)
local meta = getmetatable(plugin)
local ret = meta and type(meta.__index) == "table" and resolve(meta.__index) or {}
for k, v in pairs(plugin) do
ret[k] = v
end
return ret
end
---@param plugins LazyPlugin[]|LazyPlugin ---@param plugins LazyPlugin[]
local function clean(plugins) local function clean(plugins)
local p = plugins return vim.tbl_map(function(plugin)
plugins = type(plugins) == "table" and plugins or { plugins } plugin = resolve(plugin)
for _, plugin in pairs(plugins) do plugin[1] = nil
plugin._.fid = nil plugin._.frags = nil
plugin._.fpid = nil
plugin._.fdeps = nil
if plugin._.dep == false then if plugin._.dep == false then
plugin._.dep = nil plugin._.dep = nil
end end
end plugin._.top = nil
return p return plugin
end, plugins)
end end
describe("plugin spec url/name", function() describe("plugin spec url/name", function()
@ -28,8 +37,16 @@ describe("plugin spec url/name", function()
{ { "foo/bar" }, { [1] = "foo/bar", name = "bar", url = "https://github.com/foo/bar.git" } }, { { "foo/bar" }, { [1] = "foo/bar", name = "bar", url = "https://github.com/foo/bar.git" } },
{ { "https://foo.bar" }, { [1] = "https://foo.bar", name = "foo.bar", url = "https://foo.bar" } }, { { "https://foo.bar" }, { [1] = "https://foo.bar", name = "foo.bar", url = "https://foo.bar" } },
{ { "foo/bar", name = "foobar" }, { [1] = "foo/bar", name = "foobar", url = "https://github.com/foo/bar.git" } }, { { "foo/bar", name = "foobar" }, { [1] = "foo/bar", name = "foobar", url = "https://github.com/foo/bar.git" } },
{ { "foo/bar", url = "123" }, { [1] = "foo/bar", name = "123", url = "123" } }, { { "foo/bar", url = "123" }, { [1] = "foo/bar", name = "bar", url = "123" } },
{ { url = "https://foobar" }, { name = "foobar", url = "https://foobar" } }, { { url = "https://foobar" }, { name = "foobar", url = "https://foobar" } },
{
{ { url = "https://foo", name = "foobar" }, { url = "https://foo" } },
{ name = "foobar", url = "https://foo" },
},
{
{ { url = "https://foo" }, { url = "https://foo", name = "foobar" } },
{ name = "foobar", url = "https://foo" },
},
{ { url = "ssh://foobar" }, { name = "foobar", url = "ssh://foobar" } }, { { url = "ssh://foobar" }, { name = "foobar", url = "ssh://foobar" } },
{ "foo/bar", { [1] = "foo/bar", name = "bar", url = "https://github.com/foo/bar.git" } }, { "foo/bar", { [1] = "foo/bar", name = "bar", url = "https://github.com/foo/bar.git" } },
{ { { { "foo/bar" } } }, { [1] = "foo/bar", name = "bar", url = "https://github.com/foo/bar.git" } }, { { { { "foo/bar" } } }, { [1] = "foo/bar", name = "bar", url = "https://github.com/foo/bar.git" } },
@ -37,15 +54,23 @@ describe("plugin spec url/name", function()
for _, test in ipairs(tests) do for _, test in ipairs(tests) do
test[2]._ = {} test[2]._ = {}
it("parses " .. vim.inspect(test[1]):gsub("%s+", " "), function() it("parses " .. inspect(test[1]), function()
if not test[2].dir then if not test[2].dir then
test[2].dir = Config.options.root .. "/" .. test[2].name test[2].dir = Config.options.root .. "/" .. test[2].name
end end
local spec = Plugin.Spec.new(test[1]) local spec = Plugin.Spec.new(test[1])
local plugins = vim.tbl_values(spec.plugins) local all = vim.deepcopy(spec.plugins)
plugins[1]._ = {} local plugins = vim.tbl_values(all)
assert(#spec.notifs == 0) plugins = vim.tbl_map(function(plugin)
assert.equal(1, #plugins) plugin._ = {}
return plugin
end, plugins)
local notifs = vim.tbl_filter(function(notif)
return notif.level > 3
end, spec.notifs)
assert(#notifs == 0, vim.inspect(spec.notifs))
assert.equal(1, #plugins, vim.inspect(all))
plugins[1]._.super = nil
assert.same(test[2], plugins[1]) assert.same(test[2], plugins[1])
end) end)
end end
@ -78,7 +103,40 @@ describe("plugin spec dir", function()
for _, test in ipairs(tests) do for _, test in ipairs(tests) do
local dir = vim.fn.expand(test[1]) local dir = vim.fn.expand(test[1])
local input = vim.list_slice(test, 2) local input = vim.list_slice(test, 2)
it("parses dir " .. vim.inspect(input):gsub("%s+", " "), function() it("parses dir " .. inspect(input), function()
local spec = Plugin.Spec.new(input)
local plugins = vim.tbl_values(spec.plugins)
assert(spec:report() == 0)
assert.equal(1, #plugins)
assert.same(dir, plugins[1].dir)
end)
end
end)
describe("plugin dev", function()
local tests = {
{
{ "lewis6991/gitsigns.nvim", opts = {}, dev = true },
{ "lewis6991/gitsigns.nvim" },
},
{
{ "lewis6991/gitsigns.nvim", opts = {}, dev = true },
{ "gitsigns.nvim" },
},
{
{ "lewis6991/gitsigns.nvim", opts = {} },
{ "lewis6991/gitsigns.nvim", dev = true },
},
{
{ "lewis6991/gitsigns.nvim", opts = {} },
{ "gitsigns.nvim", dev = true },
},
}
for _, test in ipairs(tests) do
local dir = vim.fn.expand("~/projects/gitsigns.nvim")
local input = test
it("parses dir " .. inspect(input), function()
local spec = Plugin.Spec.new(input) local spec = Plugin.Spec.new(input)
local plugins = vim.tbl_values(spec.plugins) local plugins = vim.tbl_values(spec.plugins)
assert(spec:report() == 0) assert(spec:report() == 0)
@ -114,16 +172,14 @@ describe("plugin spec opt", function()
for _, plugin in pairs(spec.plugins) do for _, plugin in pairs(spec.plugins) do
plugin.dir = nil plugin.dir = nil
end end
assert.same(clean(spec.plugins), { assert.same({
bar = { bar = {
"foo/bar",
_ = {}, _ = {},
dependencies = { "dep1", "dep2" }, dependencies = { "dep1", "dep2" },
name = "bar", name = "bar",
url = "https://github.com/foo/bar.git", url = "https://github.com/foo/bar.git",
}, },
dep1 = { dep1 = {
"foo/dep1",
_ = { _ = {
dep = true, dep = true,
}, },
@ -131,14 +187,13 @@ describe("plugin spec opt", function()
url = "https://github.com/foo/dep1.git", url = "https://github.com/foo/dep1.git",
}, },
dep2 = { dep2 = {
"foo/dep2",
_ = { _ = {
dep = true, dep = true,
}, },
name = "dep2", name = "dep2",
url = "https://github.com/foo/dep2.git", url = "https://github.com/foo/dep2.git",
}, },
}) }, clean(spec.plugins))
end end
end) end)
@ -146,13 +201,13 @@ describe("plugin spec opt", function()
before_each(function() before_each(function()
Handler.init() Handler.init()
end) end)
it("handles dep names", function() Config.options.defaults.lazy = false
Config.options.defaults.lazy = false local tests = {
local tests = { { { "foo/bar", dependencies = { { "dep1" }, "foo/dep2" } }, "foo/dep1" },
{ { "foo/bar", dependencies = { { "dep1" }, "foo/dep2" } }, "foo/dep1" }, { "foo/dep1", { "foo/bar", dependencies = { { "dep1" }, "foo/dep2" } } },
{ "foo/dep1", { "foo/bar", dependencies = { { "dep1" }, "foo/dep2" } } }, }
} for _, test in ipairs(tests) do
for _, test in ipairs(tests) do it("handles dep names " .. inspect(test), function()
local spec = Plugin.Spec.new(vim.deepcopy(test)) local spec = Plugin.Spec.new(vim.deepcopy(test))
assert(#spec.notifs == 0) assert(#spec.notifs == 0)
Config.plugins = spec.plugins Config.plugins = spec.plugins
@ -161,31 +216,74 @@ describe("plugin spec opt", function()
for _, plugin in pairs(spec.plugins) do for _, plugin in pairs(spec.plugins) do
plugin.dir = nil plugin.dir = nil
end end
assert.same(clean(spec.plugins), { assert.same({
bar = { bar = {
"foo/bar",
_ = {}, _ = {},
dependencies = { "dep1", "dep2" }, dependencies = { "dep1", "dep2" },
name = "bar", name = "bar",
url = "https://github.com/foo/bar.git", url = "https://github.com/foo/bar.git",
}, },
dep1 = { dep1 = {
"foo/dep1",
_ = {}, _ = {},
name = "dep1", name = "dep1",
url = "https://github.com/foo/dep1.git", url = "https://github.com/foo/dep1.git",
}, },
dep2 = { dep2 = {
"foo/dep2",
_ = { _ = {
dep = true, dep = true,
}, },
name = "dep2", name = "dep2",
url = "https://github.com/foo/dep2.git", url = "https://github.com/foo/dep2.git",
}, },
}) }, clean(spec.plugins))
end end)
end) end
Config.options.defaults.lazy = false
local tests = {
{
{ "foo/baz", name = "bar" },
{ "foo/fee", dependencies = { "foo/baz" } },
},
{
{ "foo/fee", dependencies = { "foo/baz" } },
{ "foo/baz", name = "bar" },
},
-- {
-- { "foo/baz", name = "bar" },
-- { "foo/fee", dependencies = { "baz" } },
-- },
{
{ "foo/baz", name = "bar" },
{ "foo/fee", dependencies = { "bar" } },
},
}
for _, test in ipairs(tests) do
it("handles dep names " .. inspect(test), function()
local spec = Plugin.Spec.new(vim.deepcopy(test))
assert(#spec.notifs == 0)
Config.plugins = spec.plugins
Plugin.update_state()
spec = Plugin.Spec.new(test)
spec.meta:rebuild()
for _, plugin in pairs(spec.plugins) do
plugin.dir = nil
end
assert.same({
bar = {
_ = {},
name = "bar",
url = "https://github.com/foo/baz.git",
},
fee = {
_ = {},
name = "fee",
url = "https://github.com/foo/fee.git",
dependencies = { "bar" },
},
}, clean(spec.plugins))
end)
end
it("handles opt from dep", function() it("handles opt from dep", function()
Config.options.defaults.lazy = false Config.options.defaults.lazy = false
@ -242,7 +340,7 @@ describe("plugin spec opt", function()
assert(#spec.notifs == 0) assert(#spec.notifs == 0)
assert(vim.tbl_count(spec.plugins) == 1) assert(vim.tbl_count(spec.plugins) == 1)
Handler.resolve(spec.plugins.bar) Handler.resolve(spec.plugins.bar)
vim.print(spec.plugins.bar._.handlers) -- vim.print(spec.plugins.bar._.handlers)
local events = vim.tbl_keys(spec.plugins.bar._.handlers.event or {}) local events = vim.tbl_keys(spec.plugins.bar._.handlers.event or {})
assert(type(events) == "table") assert(type(events) == "table")
assert(#events == 2) assert(#events == 2)
@ -357,45 +455,45 @@ describe("plugin spec opt", function()
end) end)
describe("plugin opts", function() describe("plugin opts", function()
it("correctly parses opts", function() ---@type {spec:LazySpec, opts:table}[]
---@type {spec:LazySpec, opts:table}[] local tests = {
local tests = { {
{ spec = { { "foo/foo", opts = { a = 1, b = 1 } }, { "foo/foo", opts = { a = 2 } } },
spec = { { "foo/foo", opts = { a = 1, b = 1 } }, { "foo/foo", opts = { a = 2 } } }, opts = { a = 2, b = 1 },
opts = { a = 2, b = 1 }, },
}, {
{ spec = { { "foo/foo", config = { a = 1, b = 1 } }, { "foo/foo", opts = { a = 2 } } },
spec = { { "foo/foo", config = { a = 1, b = 1 } }, { "foo/foo", opts = { a = 2 } } }, opts = { a = 2, b = 1 },
opts = { a = 2, b = 1 }, },
}, {
{ spec = { { "foo/foo", opts = { a = 1, b = 1 } }, { "foo/foo", config = { a = 2 } } },
spec = { { "foo/foo", opts = { a = 1, b = 1 } }, { "foo/foo", config = { a = 2 } } }, opts = { a = 2, b = 1 },
opts = { a = 2, b = 1 }, },
}, {
{ spec = { { "foo/foo", config = { a = 1, b = 1 } }, { "foo/foo", config = { a = 2 } } },
spec = { { "foo/foo", config = { a = 1, b = 1 } }, { "foo/foo", config = { a = 2 } } }, opts = { a = 2, b = 1 },
opts = { a = 2, b = 1 }, },
}, {
{ spec = { { "foo/foo", config = { a = 1, b = 1 } }, { "foo/foo", config = { a = vim.NIL } } },
spec = { { "foo/foo", config = { a = 1, b = 1 } }, { "foo/foo", config = { a = vim.NIL } } }, opts = { b = 1 },
opts = { b = 1 }, },
}, {
{ spec = { { "foo/foo", config = { a = 1, b = 1 } }, { "foo/foo" } },
spec = { { "foo/foo", config = { a = 1, b = 1 } }, { "foo/foo" } }, opts = { a = 1, b = 1 },
opts = { a = 1, b = 1 }, },
}, {
{ spec = { { "foo/foo" }, { "foo/foo" } },
spec = { { "foo/foo" }, { "foo/foo" } }, opts = {},
opts = {}, },
}, }
}
for _, test in ipairs(tests) do for _, test in ipairs(tests) do
it("correctly parses opts for " .. inspect(test.spec), function()
local spec = Plugin.Spec.new(test.spec) local spec = Plugin.Spec.new(test.spec)
assert(spec.plugins.foo) assert(spec.plugins.foo)
assert.same(test.opts, Plugin.values(spec.plugins.foo, "opts")) assert.same(test.opts, Plugin.values(spec.plugins.foo, "opts"))
end end)
end) end
end) end)
describe("plugin spec", function() describe("plugin spec", function()

View file

@ -5,6 +5,7 @@ local Util = require("lazy.util")
describe("util", function() describe("util", function()
local rtp = vim.opt.rtp:get() local rtp = vim.opt.rtp:get()
before_each(function() before_each(function()
---@type vim.Option
vim.opt.rtp = rtp vim.opt.rtp = rtp
for k, v in pairs(package.loaded) do for k, v in pairs(package.loaded) do
if k:find("^foobar") then if k:find("^foobar") then

View file

@ -1,36 +0,0 @@
local M = {}
function M.root(root)
local f = debug.getinfo(1, "S").source:sub(2)
return vim.fn.fnamemodify(f, ":p:h:h") .. "/" .. (root or "")
end
---@param plugin string
function M.load(plugin)
local name = plugin:match(".*/(.*)")
local package_root = M.root(".tests/site/pack/deps/start/")
if not vim.uv.fs_stat(package_root .. name) then
print("Installing " .. plugin)
vim.fn.mkdir(package_root, "p")
vim.fn.system({
"git",
"clone",
"--depth=1",
"https://github.com/" .. plugin .. ".git",
package_root .. "/" .. name,
})
end
end
function M.setup()
vim.cmd([[set runtimepath=$VIMRUNTIME]])
vim.opt.runtimepath:append(M.root())
vim.opt.packpath = { M.root(".tests/site") }
M.load("nvim-lua/plenary.nvim")
vim.env.XDG_CONFIG_HOME = M.root(".tests/config")
vim.env.XDG_DATA_HOME = M.root(".tests/data")
vim.env.XDG_STATE_HOME = M.root(".tests/state")
vim.env.XDG_CACHE_HOME = M.root(".tests/cache")
end
M.setup()

View file

@ -0,0 +1,19 @@
local Async = require("lazy.async")
local Process = require("lazy.manage.process")
describe("process", function()
it("runs sync", function()
local lines = Process.exec({ "echo", "-n", "hello" })
assert.are.same({ "hello" }, lines)
end)
it("runs sync from async context", function()
local lines ---@type string[]
local async = Async.new(function()
lines = Process.exec({ "echo", "-n", "hello" })
end)
async:wait()
assert.are.same({ "hello" }, lines)
end)
end)

View file

@ -1,3 +1,4 @@
local Async = require("lazy.async")
local Runner = require("lazy.manage.runner") local Runner = require("lazy.manage.runner")
describe("runner", function() describe("runner", function()
@ -30,11 +31,11 @@ describe("runner", function()
end, end,
} }
package.loaded["lazy.manage.task.test"]["async" .. i] = { package.loaded["lazy.manage.task.test"]["async" .. i] = {
---@async
---@param task LazyTask ---@param task LazyTask
run = function(task) run = function(task)
task:schedule(function() Async.yield()
table.insert(runs, { plugin = task.plugin.name, task = task.name }) table.insert(runs, { plugin = task.plugin.name, task = task.name })
end)
end, end,
} }
end end
@ -64,7 +65,7 @@ describe("runner", function()
local runner = Runner.new({ plugins = plugins, pipeline = { "test.test1", "test.skip", "test.test2" } }) local runner = Runner.new({ plugins = plugins, pipeline = { "test.test1", "test.skip", "test.test2" } })
runner:start() runner:start()
runner:wait() runner:wait()
assert.equal(4, #runs) assert.equal(4, #runs, runs)
end) end)
it("handles opts", function() it("handles opts", function()

View file

@ -14,6 +14,7 @@ describe("semver version", function()
["1.2.3+build"] = { major = 1, minor = 2, patch = 3, build = "build" }, ["1.2.3+build"] = { major = 1, minor = 2, patch = 3, build = "build" },
} }
for input, output in pairs(tests) do for input, output in pairs(tests) do
output.input = input
it("correctly parses " .. input, function() it("correctly parses " .. input, function()
assert.same(output, v(input)) assert.same(output, v(input))
end) end)

View file

@ -1,96 +1,82 @@
--# selene:allow(incorrect_standard_library_use) --# selene:allow(incorrect_standard_library_use)
local Async = require("lazy.async")
local Task = require("lazy.manage.task") local Task = require("lazy.manage.task")
describe("task", function() describe("task", function()
local plugin = { name = "test", _ = {} } local plugin = { name = "test", _ = {} }
local done = false ---@type {done?:boolean, error:string?}
---@type string? local task_result = {}
local error
local opts = { local opts = {
---@param task LazyTask
on_done = function(task) on_done = function(task)
done = true task_result = { done = true, error = task.error }
error = task.error
end, end,
} }
before_each(function() before_each(function()
done = false task_result = {}
error = nil
end) end)
it("simple function", function() it("simple function", function()
local task = Task.new(plugin, "test", function() end, opts) local task = Task.new(plugin, "test", function() end, opts)
assert(not task:has_started()) assert(task:running())
assert(not task:is_running()) task:wait()
task:start() assert(not task:running())
assert(not task:is_running()) assert(task_result.done)
assert(task:is_done())
assert(done)
end) end)
it("detects errors", function() it("detects errors", function()
local task = Task.new(plugin, "test", function() local task = Task.new(plugin, "test", function()
error("test") error("test")
end, opts) end, opts)
assert(not task:has_started()) assert(task:running())
assert(not task:is_running()) task:wait()
task:start() assert(not task:running())
assert(task:is_done()) assert(task_result.done)
assert(not task:is_running()) assert(task_result.error)
assert(done) assert(task:has_errors() and task:output(vim.log.levels.ERROR):find("test"))
assert(error)
assert(task.error and task.error:find("test"))
end) end)
it("schedule", function() it("async", function()
local running = false local running = true
local task = Task.new(plugin, "test", function(task) ---@async
running = true local task = Task.new(plugin, "test", function()
task:schedule(function() Async.yield()
running = false running = false
end)
end, opts) end, opts)
assert(not task:is_running()) assert(task:running())
assert(not task:has_started())
task:start()
assert(running) assert(running)
assert(#task._running == 1) assert(task:running())
assert(task:is_running())
assert(not task:is_done())
task:wait() task:wait()
assert(task:is_done()) assert(not running)
assert(not task:is_running()) assert(not task:running())
assert(done) assert(task_result.done)
assert(not task.error) assert(not task:has_errors())
end) end)
it("spawn errors", function() it("spawn errors", function()
local task = Task.new(plugin, "test", function(task) local task = Task.new(plugin, "spawn_errors", function(task)
task:spawn("foobar") task:spawn("foobar")
end, opts) end, opts)
assert(not task:is_running()) assert(task:running())
task:start() task:wait()
assert(not task:is_running()) assert(not task:running())
assert(done) assert(task_result.done)
assert(task.error and task.error:find("Failed to spawn")) assert(task:has_errors() and task:output(vim.log.levels.ERROR):find("Failed to spawn"), task:output())
end) end)
it("spawn", function() it("spawn", function()
local task = Task.new(plugin, "test", function(task) local task = Task.new(plugin, "test", function(task)
task:spawn("echo", { args = { "foo" } }) task:spawn("echo", { args = { "foo" } })
end, opts) end, opts)
assert(not task:is_running()) assert(task:running())
assert(not task:has_started()) assert(task:running())
task:start()
assert(task:has_started())
assert(task:is_running())
task:wait() task:wait()
assert(task:is_done()) assert.same(task:output(), "foo")
assert.same(task.output, "foo\n") assert(task_result.done)
assert(done) assert(not task:has_errors())
assert(not task.error)
end) end)
it("spawn 2x", function() it("spawn 2x", function()
@ -98,12 +84,11 @@ describe("task", function()
task:spawn("echo", { args = { "foo" } }) task:spawn("echo", { args = { "foo" } })
task:spawn("echo", { args = { "bar" } }) task:spawn("echo", { args = { "bar" } })
end, opts) end, opts)
assert(not task:is_running()) assert(task:running())
task:start() assert(task:running())
assert(task:is_running())
task:wait() task:wait()
assert(task.output == "foo\nbar\n" or task.output == "bar\nfoo\n") assert(task:output() == "foo\nbar" or task:output() == "bar\nfoo", task:output())
assert(done) assert(task_result.done)
assert(not task.error) assert(not task:has_errors())
end) end)
end) end)

12
tests/minit.lua Executable file
View file

@ -0,0 +1,12 @@
#!/usr/bin/env -S nvim -l
vim.env.LAZY_STDPATH = ".tests"
vim.opt.rtp:prepend(".")
-- Setup lazy.nvim
require("lazy.minit").setup({
spec = {
{ dir = vim.uv.cwd() },
},
})

View file

@ -1,3 +0,0 @@
#!/bin/sh
nvim --headless -u tests/init.lua -c "PlenaryBustedDirectory tests {minimal_init = 'tests//init.lua', sequential = true}"

View file

@ -8,42 +8,14 @@ any = true
[jit] [jit]
any = true any = true
[[describe.args]] [assert]
type = "string"
[[describe.args]]
type = "function"
[[it.args]]
type = "string"
[[it.args]]
type = "function"
[[before_each.args]]
type = "function"
[[after_each.args]]
type = "function"
[assert.is_not]
any = true any = true
[[assert.equals.args]] [describe]
type = "any" any = true
[[assert.equals.args]]
type = "any"
[[assert.equals.args]]
type = "any"
required = false
[[assert.same.args]] [it]
type = "any" any = true
[[assert.same.args]]
type = "any"
[[assert.truthy.args]] [before_each.args]
type = "any" any = true
[[assert.spy.args]]
type = "any"
[[assert.stub.args]]
type = "any"