Compare commits

..

No commits in common. "main" and "v11.0.0" have entirely different histories.

76 changed files with 1088 additions and 122146 deletions

11
.busted
View file

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

View file

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

View file

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

View file

@ -6,10 +6,7 @@ body:
- type: markdown
attributes:
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
attributes:
label: Did you check docs and existing issues?
@ -17,8 +14,6 @@ body:
options:
- label: I have read all the lazy.nvim docs
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
required: true
- label: I have searched the existing issues of plugins related to this issue
@ -62,14 +57,32 @@ body:
label: Repro
description: Minimal `init.lua` to reproduce this issue. Save as `repro.lua` and run with `nvim -u repro.lua`
value: |
vim.env.LAZY_STDPATH = ".repro"
load(vim.fn.system("curl -s https://raw.githubusercontent.com/folke/lazy.nvim/main/bootstrap.lua"))()
-- DO NOT change the paths and don't remove the colorscheme
local root = vim.fn.fnamemodify("./.repro", ":p")
require("lazy.minit").repro({
spec = {
-- add any other plugins here
},
-- set stdpaths to use .repro
for _, name in ipairs({ "config", "data", "state", "cache" }) do
vim.env[("XDG_%s_HOME"):format(name:upper())] = root .. "/" .. name
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:
required: false

View file

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

View file

@ -1,16 +0,0 @@
## 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. -->

View file

@ -1,6 +1,11 @@
# To get started with Dependabot version updates, you'll need to specify which
# package ecosystems to update and where the package manifests are located.
# Please see the documentation for all configuration options:
# https://docs.github.com/code-security/dependabot/dependabot-version-updates/configuration-options-for-the-dependabot.yml-file
version: 2
updates:
- package-ecosystem: "github-actions"
directory: "/"
- package-ecosystem: "github-actions" # See documentation for possible values
directory: "/" # Location of package manifests
schedule:
interval: "weekly"

View file

@ -1,15 +1,51 @@
name: CI
on:
push:
branches: [main, master]
pull_request:
jobs:
ci:
uses: folke/github/.github/workflows/ci.yml@main
secrets: inherit
with:
plugin: lazy.nvim
repo: folke/lazy.nvim
docs: false
tests:
strategy:
matrix:
# os: [ubuntu-latest, windows-latest]
os: [ubuntu-latest]
runs-on: ${{ matrix.os }}
steps:
- uses: actions/checkout@v4
- 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}"
release:
name: release
if: ${{ github.ref == 'refs/heads/main' }}
needs:
- tests
runs-on: ubuntu-latest
steps:
- uses: googleapis/release-please-action@v4
id: release
with:
config-file: .github/release-please-config.json
manifest-file: .github/.release-please-manifest.json
- uses: actions/checkout@v4
- 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

View file

@ -1,30 +0,0 @@
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>"

View file

@ -1,19 +0,0 @@
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

View file

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

View file

@ -1,18 +0,0 @@
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

View file

@ -1,11 +0,0 @@
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

View file

@ -1,13 +0,0 @@
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,9 +1,8 @@
*.log
/.repro
/.tests
/build
/debug
/doc/tags
foo.*
node_modules
tt.*
.tests
doc/tags
debug
.repro
foo.*
*.log
data

View file

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

View file

@ -1,429 +1,5 @@
# 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)

View file

@ -61,8 +61,6 @@
- Neovim >= **0.8.0** (needs to be built with **LuaJIT**)
- Git >= **2.19.0** (for partial clones support)
- 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.
## 🚀 Getting Started

View file

@ -1,51 +0,0 @@
-- 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

View file

@ -39,7 +39,6 @@ Table of Contents *lazy.nvim-table-of-contents*
8. 🔥 Developers |lazy.nvim-🔥-developers|
- Best Practices |lazy.nvim-🔥-developers-best-practices|
- Building |lazy.nvim-🔥-developers-building|
- Minit (Minimal Init) |lazy.nvim-🔥-developers-minit-(minimal-init)|
9. Links |lazy.nvim-links|
==============================================================================
@ -60,30 +59,6 @@ Table of Contents *lazy.nvim-table-of-contents*
- **Lazy**: `lazy.lua` file
- **Rockspec**: luarocks <https://luarocks.org/> `*-scm-1.rockspec` file <https://github.com/luarocks/luarocks/wiki/Rockspec-format>
- **Packspec**: `pkg.json` (experimental, since the format <https://github.com/neovim/packspec/issues/41> is not quite there yet)
Related _lazy.nvim_ options:
>lua
{
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/",
},
}
<
- Installing neorg <https://github.com/nvim-neorg/neorg> is now as simple as:
>lua
{ "nvim-neorg/neorg", opts = {} }
<
- Packages are not limited to just Neovim plugins. You can install any
**luarocks** package, like:
>lua
@ -129,8 +104,6 @@ Table of Contents *lazy.nvim-table-of-contents*
- Neovim >= **0.8.0** (needs to be built with **LuaJIT**)
- Git >= **2.19.0** (for partial clones support)
- 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.
==============================================================================
@ -157,16 +130,7 @@ STRUCTURED SETUP *lazy.nvim-🛠️-installation-structured-setup*
local lazypath = vim.fn.stdpath("data") .. "/lazy/lazy.nvim"
if not (vim.uv or vim.loop).fs_stat(lazypath) then
local lazyrepo = "https://github.com/folke/lazy.nvim.git"
local out = vim.fn.system({ "git", "clone", "--filter=blob:none", "--branch=stable", lazyrepo, lazypath })
if vim.v.shell_error ~= 0 then
vim.api.nvim_echo({
{ "Failed to clone lazy.nvim:\n", "ErrorMsg" },
{ out, "WarningMsg" },
{ "\nPress any key to exit..." },
}, true, {})
vim.fn.getchar()
os.exit(1)
end
vim.fn.system({ "git", "clone", "--filter=blob:none", "--branch=stable", lazyrepo, lazypath })
end
vim.opt.rtp:prepend(lazypath)
@ -205,16 +169,7 @@ SINGLE FILE SETUP *lazy.nvim-🛠️-installation-single-file-setup*
local lazypath = vim.fn.stdpath("data") .. "/lazy/lazy.nvim"
if not (vim.uv or vim.loop).fs_stat(lazypath) then
local lazyrepo = "https://github.com/folke/lazy.nvim.git"
local out = vim.fn.system({ "git", "clone", "--filter=blob:none", "--branch=stable", lazyrepo, lazypath })
if vim.v.shell_error ~= 0 then
vim.api.nvim_echo({
{ "Failed to clone lazy.nvim:\n", "ErrorMsg" },
{ out, "WarningMsg" },
{ "\nPress any key to exit..." },
}, true, {})
vim.fn.getchar()
os.exit(1)
end
vim.fn.system({ "git", "clone", "--filter=blob:none", "--branch=stable", lazyrepo, lazypath })
end
vim.opt.rtp:prepend(lazypath)
@ -291,39 +246,31 @@ SPEC LOADING *lazy.nvim-🔌-plugin-spec-spec-loading*
SPEC SETUP *lazy.nvim-🔌-plugin-spec-spec-setup*
--------------------------------------------------------------------------------------------------
----------------------------------------------------------------------------------------------------
Property Type Description
---------- ----------------------------- ---------------------------------------------------------
init fun(LazyPlugin) init functions are always executed during startup. Mostly
useful for setting vim.g.* configuration used by Vim
plugins startup
---------- ----------------------------- -----------------------------------------------------------
init fun(LazyPlugin) init functions are always executed during startup
opts table or opts should be a table (will be merged with parent
fun(LazyPlugin, opts:table) 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()
opts table or opts should be a table (will be merged with parent specs),
fun(LazyPlugin, opts:table) 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) config is executed when the plugin loads. The default
or true implementation will automatically run
require(MAIN).setup(opts) if opts or config = true is
set. Lazy uses several heuristics to determine the
plugins MAIN module automatically based on the plugins
name. (opts is the recommended way to configure plugins).
require(MAIN).setup(opts) if opts or config = true is set.
Lazy uses several heuristics to determine the plugins MAIN
module automatically based on the plugins 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()
opts(), in case it can not be determined automatically. See
config()
build fun(LazyPlugin) or string or build is executed when a plugin is installed or updated.
false or a list of build See Building for more information.
commands
--------------------------------------------------------------------------------------------------
Always use `opts` instead of `config` when possible. `config` is almost never
needed.
a list of build commands See Building for more information.
----------------------------------------------------------------------------------------------------
SPEC LAZY LOADING *lazy.nvim-🔌-plugin-spec-spec-lazy-loading*
@ -540,7 +487,9 @@ function.
keys = {
{ "<leader>ft", "<cmd>Neotree toggle<cr>", desc = "NeoTree" },
},
opts = {},
config = function()
require("neo-tree").setup()
end,
}
<
@ -644,42 +593,24 @@ will be added to the plugins spec.
-- then set the below to false. This should work, but is NOT supported and will
-- increase downloads a lot.
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",
versions = true, -- Honor versions in pkg sources
-- 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
"rockspec",
"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 = {
-- 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
---@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"}
@ -706,7 +637,6 @@ will be added to the plugins spec.
icons = {
cmd = " ",
config = "",
debug = "● ",
event = " ",
favorite = " ",
ft = " ",
@ -732,7 +662,7 @@ will be added to the plugins spec.
-- 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 = 1000 / 30, -- how frequently should the ui process render events
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.
@ -747,16 +677,6 @@ will be added to the plugins spec.
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"] = {
function(plugin)
require("lazy.util").float_term(nil, {
@ -767,17 +687,6 @@ will be added to the plugins spec.
},
},
},
-- 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 command <d> can be one of:
-- * browser: opens the github compare view. Note that this is always mapped to <K> as well,
@ -829,7 +738,7 @@ will be added to the plugins spec.
enabled = true,
root = vim.fn.stdpath("state") .. "/lazy/readme",
files = { "README.md", "lua/**/README.md" },
-- only generate markdown helptags for plugins that don't have docs
-- 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
@ -875,8 +784,6 @@ If you dont want to use a Nerd Font, you can replace the icons with Unicode s
-----------------------------------------------------------------------
Highlight Group Default Group Description
----------------------- ----------------------- -----------------------
LazyBold { bold = true }
LazyButton CursorLine
LazyButtonActive Visual
@ -897,16 +804,10 @@ If you dont want to use a Nerd Font, you can replace the icons with Unicode s
LazyDir @markup.link directory
LazyError DiagnosticError task errors
LazyH1 IncSearch home button
LazyH2 Bold titles
LazyInfo DiagnosticInfo task errors
LazyItalic { italic = true }
LazyLocal Constant
LazyNoCond DiagnosticWarn unloaded icon for a
@ -943,13 +844,13 @@ If you dont want to use a Nerd Font, you can replace the icons with Unicode s
LazySpecial @punctuation.special
LazyTaskError ErrorMsg task errors
LazyTaskOutput MsgArea task output
LazyUrl @markup.link url
LazyValue @string value of a property
LazyWarning DiagnosticWarn task errors
-----------------------------------------------------------------------
==============================================================================
@ -1294,13 +1195,11 @@ BEST PRACTICES *lazy.nvim-🔥-developers-best-practices*
>lua
{ "nvim-lua/plenary.nvim", lazy = true }
<
- Always use `opts` instead of `config` when possible. `config` is almost never
needed.
- Only use `dependencies` if a plugin needs the dep to be installed **AND**
loaded. Lua plugins/libraries are automatically loaded when they are
`require()`d, so they dont need to be in `dependencies`.
- Inside a `build` function or `*.lua` build file, use
`coroutine.yield(msg:string|LazyMsg)` to show progress.
`coroutine.yield(status_msg)` to show progress.
- Dont change the `cwd` in your build function, since builds run in parallel
and changing the `cwd` will affect other builds.
@ -1319,104 +1218,10 @@ The spec **build** property can be one of the following:
- if no `build` is specified, but a `build.lua` file exists, that will be used instead.
Build functions and `*.lua` files run asynchronously in a coroutine. Use
`coroutine.yield(msg:string|LazyMsg)` to show progress.
`coroutine.yield(status_msg)` to show progress. Yielding will also schedule the
next `coroutine.resume()` to run in the next tick, so you can do long-running
tasks without blocking Neovim.
Yielding will also schedule the next `coroutine.resume()` to run in the next
tick, so you can do long-running tasks without blocking Neovim.
>lua
---@class LazyMsg
---@field msg string
---@field level? number vim.log.levels.XXX
<
Use `vim.log.levels.TRACE` to only show the message as a **status** message for
the task.
MINIT (MINIMAL INIT) *lazy.nvim-🔥-developers-minit-(minimal-init)*
**lazy.nvim** comes with some built-in functionality to help you create a
minimal init for your plugin.
I mainly use this for testing and for users to create a `repro.lua`.
When running in **headless** mode, **lazy.nvim** will log any messages to the
terminal. See `opts.headless` for more info.
**minit** will install/load all your specs and will always run an update as
well.
BOOTSTRAP ~
>lua
-- setting this env will override all XDG paths
vim.env.LAZY_STDPATH = ".tests"
-- this will install lazy in your stdpath
load(vim.fn.system("curl -s https://raw.githubusercontent.com/folke/lazy.nvim/main/bootstrap.lua"))()
<
TESTING WITH BUSTED ~
This will add `"lunarmodules/busted"`, configure `hererocks` and run `busted`.
Below is an example of how I use **minit** to run tests with busted
<https://olivinelabs.com/busted/> in **LazyVim**.
>lua
#!/usr/bin/env -S nvim -l
vim.env.LAZY_STDPATH = ".tests"
load(vim.fn.system("curl -s https://raw.githubusercontent.com/folke/lazy.nvim/main/bootstrap.lua"))()
-- Setup lazy.nvim
require("lazy.minit").busted({
spec = {
"LazyVim/starter",
"williamboman/mason-lspconfig.nvim",
"williamboman/mason.nvim",
"nvim-treesitter/nvim-treesitter",
},
})
<
To use this, you can run:
>sh
nvim -l ./tests/busted.lua tests
<
If you want to inspect the test environment, run:
>sh
nvim -u ./tests/busted.lua
<
REPRO.LUA ~
>lua
vim.env.LAZY_STDPATH = ".repro"
load(vim.fn.system("curl -s https://raw.githubusercontent.com/folke/lazy.nvim/main/bootstrap.lua"))()
require("lazy.minit").repro({
spec = {
"stevearc/conform.nvim",
"nvim-neotest/nvim-nio",
},
})
-- do anything else you need to do to reproduce the issue
<
Then run it with:
>sh
nvim -u repro.lua
<
==============================================================================
9. Links *lazy.nvim-links*

View file

@ -1,222 +0,0 @@
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

View file

@ -1,100 +0,0 @@
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

@ -1,28 +0,0 @@
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

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

View file

@ -34,42 +34,24 @@ M.defaults = {
-- then set the below to false. This should work, but is NOT supported and will
-- increase downloads a lot.
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",
versions = true, -- Honor versions in pkg sources
-- 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
"rockspec",
"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 = {
-- 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
---@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"}
@ -96,7 +78,6 @@ M.defaults = {
icons = {
cmd = "",
config = "",
debug = "",
event = "",
favorite = "",
ft = "",
@ -122,7 +103,7 @@ M.defaults = {
-- 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 = 1000 / 30, -- how frequently should the ui process render events
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.
@ -137,16 +118,6 @@ M.defaults = {
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"] = {
function(plugin)
require("lazy.util").float_term(nil, {
@ -157,17 +128,6 @@ 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 command <d> can be one of:
-- * browser: opens the github compare view. Note that this is always mapped to <K> as well,
@ -219,7 +179,7 @@ M.defaults = {
enabled = true,
root = vim.fn.stdpath("state") .. "/lazy/readme",
files = { "README.md", "lua/**/README.md" },
-- only generate markdown helptags for plugins that don't have docs
-- 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
@ -235,14 +195,7 @@ M.defaults = {
debug = false,
}
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.version = "11.0.0" -- x-release-please-version
M.ns = vim.api.nvim_create_namespace("lazy")
@ -267,10 +220,8 @@ M.mapleader = nil
---@type string
M.maplocalleader = nil
M.suspended = false
function M.headless()
return not M.suspended and #vim.api.nvim_list_uis() == 0
return #vim.api.nvim_list_uis() == 0
end
---@param opts? LazyConfig
@ -298,9 +249,6 @@ function M.setup(opts)
M.me = debug.getinfo(1, "S").source:sub(2)
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
---@type vim.Option
vim.opt.rtp = {
@ -308,7 +256,7 @@ function M.setup(opts)
vim.fn.stdpath("data") .. "/site",
M.me,
vim.env.VIMRUNTIME,
lib,
vim.fn.fnamemodify(vim.v.progpath, ":p:h:h") .. "/lib/nvim",
vim.fn.stdpath("config") .. "/after",
}
end
@ -322,6 +270,10 @@ function M.setup(opts)
M.mapleader = vim.g.mapleader
M.maplocalleader = vim.g.maplocalleader
if M.headless() then
require("lazy.view.commands").setup()
end
vim.api.nvim_create_autocmd("UIEnter", {
once = true,
callback = function()
@ -329,42 +281,33 @@ function M.setup(opts)
end,
})
if M.headless() then
require("lazy.view.commands").setup()
else
vim.api.nvim_create_autocmd("User", {
pattern = "VeryLazy",
once = true,
callback = function()
require("lazy.view.commands").setup()
if M.options.change_detection.enabled then
require("lazy.manage.reloader").enable()
end
if M.options.checker.enabled then
vim.defer_fn(function()
require("lazy.manage.checker").start()
end, 10)
end
vim.api.nvim_create_autocmd("User", {
pattern = "VeryLazy",
once = true,
callback = function()
require("lazy.view.commands").setup()
if M.options.change_detection.enabled then
require("lazy.manage.reloader").enable()
end
if M.options.checker.enabled then
vim.defer_fn(function()
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
-- 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()
require("lazy").pkg({
plugins = {
require("lazy.core.plugin").find(vim.uv.cwd() .. "/lua/"),
},
})
end,
})
end,
})
Util.very_lazy()
end

View file

@ -124,7 +124,7 @@ function M:add(plugin)
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
if not fragment.name then
return self.spec:error("Invalid plugin spec " .. vim.inspect(plugin))
end

View file

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

View file

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

View file

@ -11,7 +11,7 @@ local Util = require("lazy.core.util")
---@class LazyKeysSpec: LazyKeysBase
---@field [1] string lhs
---@field [2]? string|fun():string?|false rhs
---@field [2]? string|fun()|false rhs
---@field mode? string|string[]
---@class LazyKeys: LazyKeysBase

View file

@ -161,7 +161,7 @@ function M.get_start_plugins()
---@type LazyPlugin[]
local start = {}
for _, plugin in pairs(Config.plugins) do
if not plugin._.loaded and (plugin._.rtp_loaded or plugin.lazy == false) then
if plugin.lazy == false and not plugin._.loaded then
start[#start + 1] = plugin
end
end
@ -341,9 +341,7 @@ function M._load(plugin, reason, opts)
Util.track({ plugin = plugin.name, start = reason.start })
Handler.disable(plugin)
if not plugin.virtual then
M.add_to_rtp(plugin)
end
M.add_to_rtp(plugin)
if plugin._.pkg and plugin._.pkg.source == "rockspec" then
M.add_to_luapath(plugin)
@ -355,9 +353,7 @@ function M._load(plugin, reason, opts)
end, "Failed to load deps for " .. plugin.name)
end
if not plugin.virtual then
M.packadd(plugin.dir)
end
M.packadd(plugin.dir)
if plugin.config or plugin.opts then
M.config(plugin)
end
@ -468,8 +464,10 @@ function M.add_to_rtp(plugin)
local rtp = vim.api.nvim_get_runtime_file("", true)
local idx_dir, idx_after
local is_win = jit.os:find("Windows")
for i, path in ipairs(rtp) do
if Util.is_win then
if is_win then
path = Util.norm(path)
end
if path == Config.me then
@ -497,11 +495,8 @@ 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)
@ -529,8 +524,8 @@ function M.colorscheme(name)
end
function M.auto_load(modname, modpath)
local plugin = Plugin.find(modpath, { fast = not M.did_handlers })
if plugin then
local plugin = Plugin.find(modpath)
if plugin and modpath:find(plugin.dir, 1, true) == 1 then
plugin._.rtp_loaded = true
-- don't load if:
-- * handlers haven't been setup yet
@ -550,17 +545,9 @@ end
---@param modname string
function M.loader(modname)
local paths, cached = Util.get_unloaded_rtp(modname, { cache = true })
local paths = Util.get_unloaded_rtp(modname)
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
-- explicitly set to nil to prevent loading errors
package.loaded[modname] = nil
M.auto_load(modname, ret.modpath)
local mod = package.loaded[modname]
if type(mod) == "table" then

View file

@ -34,16 +34,13 @@ function M:load_pkgs()
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
-- tag all package fragments 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
local frag = self.fragments:get(fid)
frag.spec.optional = true
end
-- keep track of the top-level package fragment
self.pkgs[pkg.dir] = fragment.id
@ -92,7 +89,7 @@ function M:add(plugin)
table.insert(meta._.frags, fragment.id)
if meta._ and meta._.rtp_loaded and meta.dir then
if meta._ and meta._.rtp_loaded then
local old_dir = meta.dir
self:_rebuild(meta.name)
local new_dir = meta.dir
@ -120,14 +117,6 @@ 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
@ -154,9 +143,6 @@ function M:rebuild()
for n, _ in pairs(self.dirty) do
self:_rebuild(n)
end
if Config.options.debug then
Util.track()
end
end
--- Rebuild a single plugin.
@ -164,10 +150,6 @@ end
--- 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
@ -179,7 +161,6 @@ function M:_rebuild(name)
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)
@ -196,14 +177,10 @@ function M:_rebuild(name)
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
table.insert(plugin.dependencies, self.fragments:get(dep).name)
end
end
end
@ -215,8 +192,6 @@ function M:_rebuild(name)
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
@ -250,6 +225,7 @@ function M:_rebuild(name)
setmetatable(plugin, { __index = super })
self.dirty[plugin.name] = nil
return plugin
end
@ -304,26 +280,13 @@ 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
for _, plugin in pairs(self.plugins) do
if plugin.enabled == false or (type(plugin.enabled) == "function" and not plugin.enabled()) then
changes = changes + 1
self:disable(plugin)
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)
self:rebuild()
return changes
end

View file

@ -21,7 +21,7 @@ M.Spec = Spec
M.LOCAL_SPEC = ".lazy.lua"
---@param spec? LazySpec
---@param opts? {optional?:boolean, pkg?:boolean}
---@param opts? {optional?:boolean}
function Spec.new(spec, opts)
local self = setmetatable({}, Spec)
self.meta = Meta.new(self)
@ -30,9 +30,7 @@ function Spec.new(spec, opts)
self.notifs = {}
self.ignore_installed = {}
self.optional = opts and opts.optional
if not (opts and opts.pkg == false) then
self.meta:load_pkgs()
end
self.meta:load_pkgs()
if spec then
self:parse(spec)
end
@ -146,64 +144,52 @@ function Spec:import(spec)
local imported = 0
---@type {modname: string, load: fun():(LazyPluginSpec?, string?)}[]
---@type (string|(fun():LazyPluginSpec))[]
local modspecs = {}
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
Util.lsmod(import, function(modname)
modspecs[#modspecs + 1] = modname
end)
table.sort(modspecs)
else
modspecs = { { modname = import_name, load = spec.import } }
modspecs = { spec.import }
end
for _, modspec in ipairs(modspecs) do
imported = imported + 1
local modname = modspec.modname
local modname = type(modspec) == "string" and modspec or import_name
Util.track({ import = modname })
self.importing = modname
-- unload the module so we get a clean slate
---@diagnostic disable-next-line: no-unknown
package.loaded[modname] = nil
Util.try(function()
local mod, err = modspec.load()
if err then
self:error("Failed to load `" .. modname .. "`:\n" .. err)
elseif type(mod) ~= "table" then
local mod = type(modspec) == "function" and modspec() or require(modspec)
if type(mod) ~= "table" then
self.importing = nil
return self:error(
"Invalid spec module: `"
.. modname
.. "`\nExpected a `table` of specs, but a `"
.. type(mod)
.. "` was returned instead"
.. modname
.. "`\nExpected a `table` of specs, but a `"
.. type(mod)
.. "` was returned instead"
)
else
self:normalize(mod)
end
self:normalize(mod)
self.importing = nil
Util.track()
end, {
msg = "Failed to load `" .. modname .. "`",
on_error = function(msg)
self:error(msg)
self.importing = nil
Util.track()
end,
})
self.importing = nil
Util.track()
end
if imported == 0 then
self:error("No specs found for module " .. vim.inspect(spec.import))
self:error("No specs found for module " .. spec.import)
end
end
@ -230,22 +216,19 @@ function M.update_state()
plugin._ = plugin._ or {}
if plugin.lazy == nil then
local lazy = plugin._.dep
or Config.options.defaults.lazy
or plugin.event
or plugin.keys
or plugin.ft
or plugin.cmd
or Config.options.defaults.lazy
or plugin.event
or plugin.keys
or plugin.ft
or plugin.cmd
plugin.lazy = lazy and true or false
end
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
if plugin.dir:find(Config.options.root, 1, true) == 1 then
plugin._.installed = installed[plugin.name] ~= nil
installed[plugin.name] = nil
else
plugin._.is_local = true
plugin._.installed = vim.fn.isdirectory(plugin.dir) == 1
plugin._.installed = true -- local plugins are managed by the user
end
end
@ -281,19 +264,28 @@ function M.update_rocks_state()
end)
for _, plugin in pairs(Config.plugins) do
if plugin.build == "rockspec" or plugin.name == "hererocks" then
if plugin._.pkg and plugin._.pkg.source == "rockspec" then
plugin._.build = not installed[plugin.name]
end
end
end
---@param path string
function M.local_spec(path)
local file = vim.secure.read(path)
if file then
return loadstring(file)()
end
return {}
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
while path ~= "" do
local file = path .. "/" .. M.LOCAL_SPEC
if vim.fn.filereadable(file) == 1 then
return {
@ -301,7 +293,7 @@ function M.find_local_spec()
import = function()
local data = vim.secure.read(file)
if data then
return loadstring(data, M.LOCAL_SPEC)()
return loadstring(data)()
end
return {}
end,
@ -341,20 +333,6 @@ function M.load()
lazy._.loaded = {}
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
Config.plugins = Config.spec.plugins
-- copy state. This wont do anything during startup
@ -364,7 +342,6 @@ function M.load()
Config.plugins[name]._ = plugin._
Config.plugins[name]._.dep = new_state.dep
Config.plugins[name]._.frags = new_state.frags
Config.plugins[name]._.pkg = new_state.pkg
end
end
Util.track()
@ -384,24 +361,17 @@ end
-- Finds the plugin that has this path
---@param path string
---@param opts? {fast?:boolean}
function M.find(path, opts)
function M.find(path)
if not Config.spec then
return
end
opts = opts or {}
local lua = path:find("/lua/", 1, true)
if lua then
local name = path:sub(1, lua - 1)
local slash = name:reverse():find("/", 1, true)
if slash then
name = name:sub(#name - slash + 2)
if name then
if opts.fast then
return Config.spec.meta.plugins[name]
end
return Config.spec.plugins[name]
end
return name and Config.plugins[name] or Config.spec.plugins[name] or nil
end
end
end
@ -409,7 +379,7 @@ end
---@param plugin LazyPlugin
function M.has_errors(plugin)
for _, task in ipairs(plugin._.tasks or {}) do
if task:has_errors() then
if task.error then
return true
end
end

View file

@ -5,7 +5,6 @@ local M = {}
---@type LazyProfile[]
M._profiles = { { name = "lazy" } }
M.is_win = jit.os:find("Windows")
---@param data (string|{[string]:string})?
---@param time number?
@ -29,10 +28,6 @@ function M.track(data, time)
end
end
function M.exiting()
return vim.v.exiting ~= vim.NIL
end
---@generic T
---@param list T[]
---@param fn fun(v: T):boolean?
@ -66,7 +61,7 @@ end
---@param name string
---@return string
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
end
@ -171,9 +166,7 @@ function M.very_lazy()
return
end
vim.g.did_very_lazy = true
M.track({ event = "VeryLazy" })
vim.api.nvim_exec_autocmds("User", { pattern = "VeryLazy", modeline = false })
M.track()
end)
end
@ -245,33 +238,18 @@ function M.walkmods(root, fn, modname)
end
---@param modname string
---@return string
function M.topmod(modname)
return modname:match("^[^./]+") or modname
end
function M.get_unloaded_rtp(modname)
modname = modname:gsub("/", ".")
local idx = modname:find(".", 1, true)
local topmod = idx and modname:sub(1, idx - 1) or modname
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 Config = require("lazy.core.config")
if Config.spec then
for _, plugin in pairs(Config.spec.plugins) do
if not (plugin._.loaded or plugin.module == false or plugin.virtual) then
if norm == M.normname(plugin.name) then
if not (plugin._.loaded or plugin.module == false) then
if topmod == M.normname(plugin.name) then
table.insert(rtp, 1, plugin.dir)
else
table.insert(rtp, plugin.dir)
@ -279,45 +257,31 @@ function M.get_unloaded_rtp(modname, opts)
end
end
end
M.unloaded_cache[topmod] = rtp
return rtp, false
return rtp
end
function M.find_root(modname)
local paths, cached = M.get_unloaded_rtp(modname, { cache = true })
local ret = require("lazy.core.cache").find(modname, {
rtp = true,
paths = paths,
patterns = { ".lua", "" },
paths = M.get_unloaded_rtp(modname),
patterns = { "", ".lua" },
})[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
return ret.modpath:gsub("%.lua$", ""), ret.modpath
local root = ret.modpath:gsub("/init%.lua$", ""):gsub("%.lua$", "")
return root
end
end
---@param modname string
---@param fn fun(modname:string, modpath:string)
function M.lsmod(modname, fn)
local root, match = M.find_root(modname)
local root = M.find_root(modname)
if not root then
return
end
if match:sub(-4) == ".lua" then
fn(modname, match)
if not vim.uv.fs_stat(root) then
return
end
if vim.uv.fs_stat(root .. ".lua") then
fn(modname, root .. ".lua")
end
M.ls(root, function(path, name, type)
@ -374,7 +338,6 @@ function M.notify(msg, opts)
local lang = opts.lang or "markdown"
local n = opts.once and vim.notify_once or vim.notify
n(msg, opts.level or vim.log.levels.INFO, {
ft = lang,
on_open = function(win)
local ok = pcall(function()
vim.treesitter.language.add("markdown")

View file

@ -131,8 +131,7 @@ function M.colors(opts)
{ "---", "---", "---" },
}
Util.foreach(require(opts.modname).colors, function(group, link)
link = type(link) == "table" and "`" .. vim.inspect(link):gsub("%s+", " ") .. "`" or "***" .. link .. "***"
lines[#lines + 1] = { "**" .. opts.name .. group .. "**", link, comments[group] or "" }
lines[#lines + 1] = { "**" .. opts.name .. group .. "**", "***" .. link .. "***", comments[group] or "" }
end)
return { content = M.table(lines) }
end

View file

@ -1,5 +1,4 @@
local Config = require("lazy.core.config")
local Process = require("lazy.manage.process")
local uv = vim.uv or vim.loop
local M = {}
@ -9,67 +8,15 @@ local start = vim.health.start or vim.health.report_start
local ok = vim.health.ok or vim.health.report_ok
local warn = vim.health.warn or vim.health.report_warn
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()
start("lazy.nvim")
info("{lazy.nvim} version `" .. Config.version .. "`")
M.have("git")
if vim.fn.executable("git") == 1 then
ok("Git installed")
else
error("Git not installed?")
end
local sites = vim.opt.packpath:get()
local default_site = vim.fn.stdpath("data") .. "/site"
@ -90,7 +37,7 @@ function M.check()
ok("no existing packages found by other package managers")
end
for _, name in ipairs({ "packer", "plugged", "paq", "pckr", "mini.deps" }) do
for _, name in ipairs({ "packer", "plugged", "paq" }) do
for _, path in ipairs(vim.opt.rtp:get()) do
if path:find(name, 1, true) then
error("Found paths on the rtp from another plugin manager `" .. name .. "`")
@ -127,47 +74,6 @@ function M.check()
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
---@param plugin LazyPlugin

View file

@ -35,9 +35,7 @@ end
function M.fast_check(opts)
opts = opts or {}
for _, plugin in pairs(Config.plugins) do
-- 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
if not plugin.pin and not plugin.dev and plugin._.installed then
plugin._.updates = nil
local info = Git.info(plugin.dir)
local ok, target = pcall(Git.get_target, plugin)

View file

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

View file

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

View file

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

View file

@ -1,133 +1,66 @@
local Async = require("lazy.async")
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
local uv = vim.uv
---@class ProcessOpts
---@field args string[]
---@field cwd? string
---@field on_line? fun(line:string)
---@field on_line? fun(string)
---@field on_exit? fun(ok:boolean, output:string)
---@field on_data? fun(data:string, is_stderr?:boolean)
---@field timeout? number
---@field env? table<string,string>
local M = {}
---@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)
---@param cmd string
function M.spawn(cmd, opts)
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>
local env = vim.tbl_extend("force", {
GIT_SSH_COMMAND = "ssh -oBatchMode=yes",
}, uv.os_environ(), self.opts.env or {})
}, uv.os_environ(), opts.env or {})
env.GIT_DIR = nil
env.GIT_WORK_TREE = nil
env.GIT_TERMINAL_PROMPT = "0"
@ -138,102 +71,128 @@ function Process:env()
for k, v in pairs(env) do
env_flat[#env_flat + 1] = k .. "=" .. v
end
return env_flat
end
---@param signals uv.aliases.signals|uv.aliases.signals[]|nil
function Process:kill(signals)
if not self.handle or self.handle:is_closing() then
return
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
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()
-- 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
end)
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
---@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
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
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")
---@param data? string
local function on_output(err, data)
assert(not err, err)
if self.opts.on_line then
self.opts.on_line(lines[#lines])
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
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)
function M.kill(handle)
if handle and not handle:is_closing() then
M.running[handle] = nil
uv.process_kill(handle, "sigint")
return true
end
end
function M.abort()
for _, proc in pairs(M.running) do
proc:kill()
for handle in pairs(M.running) do
M.kill(handle)
end
end
---@async
---@param cmd string|string[]
---@param opts? ProcessOpts
---@param cmd string[]
---@param opts? {cwd:string, env:table}
function M.exec(cmd, opts)
opts = opts or {}
local proc = M.spawn(cmd, opts)
proc:wait()
return vim.split(proc.data, "\n"), proc.code
---@type string[]
local lines
local job = vim.fn.jobstart(cmd, {
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
return M

View file

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

View file

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

View file

@ -21,23 +21,16 @@ M.clean = {
skip = function(plugin)
return plugin._.is_local
end,
---@param opts? {rocks_only?:boolean}
run = function(self, opts)
opts = opts or {}
run = function(self)
local dir = self.plugin.dir:gsub("/+$", "")
assert(dir:find(Config.options.root, 1, true) == 1, self.plugin.dir .. " should be under packpath!")
rm(dir)
local rock_root = Config.options.rocks.root .. "/" .. self.plugin.name
if vim.uv.fs_stat(rock_root) then
rm(rock_root)
end
if opts.rocks_only then
return
end
rm(dir)
self.plugin._.installed = false
end,
}

View file

@ -1,54 +1,8 @@
local Async = require("lazy.async")
local Config = require("lazy.core.config")
local Git = require("lazy.manage.git")
local Lock = require("lazy.manage.lock")
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>
local M = {}
@ -64,10 +18,8 @@ M.log = {
local stat = vim.uv.fs_stat(plugin.dir .. "/.git")
return not (stat and stat.type == "directory")
end,
---@async
---@param opts {args?: string[], updated?:boolean, check?:boolean}
run = function(self, opts)
-- self:spawn({ "sleep", "5" })
local args = {
"log",
"--pretty=format:%h %s (%cr)",
@ -78,13 +30,11 @@ M.log = {
"--no-show-signature",
}
local info, target
if opts.updated then
table.insert(args, self.plugin._.updated.from .. ".." .. (self.plugin._.updated.to or "HEAD"))
elseif opts.check then
info = assert(Git.info(self.plugin.dir))
target = assert(Git.get_target(self.plugin))
local info = assert(Git.info(self.plugin.dir))
local target = assert(Git.get_target(self.plugin))
if not target.commit then
for k, v in pairs(target) do
error(k .. " '" .. v .. "' not found")
@ -92,17 +42,15 @@ M.log = {
error("no target commit found")
end
assert(target.commit, self.plugin.name .. " " .. target.branch)
if not self.plugin._.is_local then
if Git.eq(info, target) then
if Config.options.checker.check_pinned then
local last_commit = Git.get_commit(self.plugin.dir, target.branch, true)
if not Git.eq(info, { commit = last_commit }) then
self.plugin._.outdated = true
end
if Git.eq(info, target) then
if Config.options.checker.check_pinned then
local last_commit = Git.get_commit(self.plugin.dir, target.branch, true)
if not Git.eq(info, { commit = last_commit }) then
self.plugin._.outdated = true
end
else
self.plugin._.updates = { from = info, to = target }
end
else
self.plugin._.updates = { from = info, to = target }
end
table.insert(args, info.commit .. ".." .. target.commit)
else
@ -113,14 +61,6 @@ M.log = {
args = args,
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,
}
@ -128,9 +68,7 @@ M.clone = {
skip = function(plugin)
return plugin._.installed or plugin._.is_local
end,
---@async
run = function(self)
throttle.wait()
local args = {
"clone",
self.plugin.url,
@ -189,9 +127,8 @@ M.branch = {
return true
end
local branch = assert(Git.get_branch(plugin))
return Git.get_commit(plugin.dir, branch, true)
return Git.get_commit(plugin.dir, branch)
end,
---@async
run = function(self)
local args = {
"remote",
@ -217,17 +154,14 @@ M.origin = {
local origin = Git.get_origin(plugin.dir)
return origin == plugin.url
end,
---@async
---@param opts {check?:boolean}
run = function(self, opts)
if opts.check then
local origin = Git.get_origin(self.plugin.dir)
self:error({
"Origin has changed:",
" * old: " .. origin,
" * new: " .. self.plugin.url,
"Please run update to fix",
})
self.error = "Origin has changed:\n"
self.error = self.error .. " * old: " .. origin .. "\n"
self.error = self.error .. " * new: " .. self.plugin.url .. "\n"
self.error = self.error .. "Please run update to fix"
return
end
require("lazy.manage.task.fs").clean.run(self, opts)
@ -239,7 +173,6 @@ M.status = {
skip = function(plugin)
return not plugin._.installed or plugin._.is_local
end,
---@async
run = function(self)
self:spawn("git", {
args = { "ls-files", "-d", "-m" },
@ -247,7 +180,6 @@ M.status = {
on_exit = function(ok, output)
if ok then
local lines = vim.split(output, "\n")
---@type string[]
lines = vim.tbl_filter(function(line)
-- Fix doc/tags being marked as modified
if line:gsub("[\\/]", "/") == "doc/tags" then
@ -258,13 +190,12 @@ M.status = {
return line ~= ""
end, lines)
if #lines > 0 then
local msg = { "You have local changes in `" .. self.plugin.dir .. "`:" }
self.error = "You have local changes in `" .. self.plugin.dir .. "`:\n"
for _, line in ipairs(lines) do
msg[#msg + 1] = " * " .. line
self.error = self.error .. " * " .. line .. "\n"
end
msg[#msg + 1] = "Please remove them to update."
msg[#msg + 1] = "You can also press `x` to remove the plugin and then `I` to install it again."
self:error(msg)
self.error = self.error .. "Please remove them to update.\n"
self.error = self.error .. "You can also press `x` to remove the plugin and then `I` to install it again."
end
end
end,
@ -275,12 +206,10 @@ M.status = {
-- fetches all needed origin branches
M.fetch = {
skip = function(plugin)
return not plugin._.installed or plugin._.is_local or cooldown(plugin)
return not plugin._.installed or plugin._.is_local
end,
---@async
run = function(self)
throttle.wait()
local args = {
"fetch",
"--recurse-submodules",
@ -296,11 +225,6 @@ M.fetch = {
self:spawn("git", {
args = args,
cwd = self.plugin.dir,
on_exit = function(ok)
if ok then
self.plugin._.last_check = vim.uv.now()
end
end,
})
end,
}
@ -312,10 +236,8 @@ M.checkout = {
return not plugin._.installed or plugin._.is_local
end,
---@async
---@param opts {lockfile?:boolean}
run = function(self, opts)
throttle.wait()
local info = assert(Git.info(self.plugin.dir))
local target = assert(Git.get_target(self.plugin))
@ -335,7 +257,7 @@ M.checkout = {
end
end
-- don't run checkout if target is already reached.
-- dont run checkout if target is already reached.
-- unless we just cloned, since then we won't have any data yet
if Git.eq(info, target) and info.branch == target.branch then
self.plugin._.updated = {

View file

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

View file

@ -1,5 +1,5 @@
local Config = require("lazy.core.config")
local Loader = require("lazy.core.loader")
local Rocks = require("lazy.pkg.rockspec")
local Util = require("lazy.util")
---@type table<string, LazyTaskDef>
@ -17,16 +17,32 @@ 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 }))
function B.rockspec(task)
local root = Config.options.rocks.root .. "/" .. task.plugin.name
vim.fn.mkdir(root, "p")
task:spawn("luarocks", {
args = {
"--tree",
root,
"--server",
Config.options.rocks.server,
"--dev",
"--lua-version",
"5.1",
"make",
"--force-fast",
},
cwd = task.plugin.dir,
})
end
---@param task LazyTask
---@param build string
function B.cmd(task, build)
local cmd = vim.api.nvim_parse_cmd(build:sub(2), {}) --[[@as vim.api.keyset.cmd]]
task.output = vim.api.nvim_cmd(cmd, { output = true })
end
---@async
---@param task LazyTask
---@param build string
function B.shell(task, build)
@ -47,10 +63,13 @@ M.build = {
end
return not ((plugin._.dirty or plugin._.build) and (plugin.build or get_build_file(plugin)))
end,
---@async
run = function(self)
vim.cmd([[silent! runtime plugin/rplugin.vim]])
if self.plugin.build ~= "rockspec" then
Loader.load(self.plugin, { task = "build" })
end
local builders = self.plugin.build
-- Skip if `build` is set to `false`
@ -65,9 +84,11 @@ M.build = {
---@cast builders (string|fun(LazyPlugin))[]
for _, build in ipairs(builders) do
if type(build) == "function" then
build(self.plugin)
self:async(function()
build(self.plugin)
end)
elseif build == "rockspec" then
Rocks.build(self)
B.rockspec(self)
elseif build:sub(1, 1) == ":" then
B.cmd(self, build)
elseif build:match("%.lua$") then
@ -76,7 +97,7 @@ M.build = {
if not chunk or err then
error(err)
end
chunk()
self:async(chunk)
else
B.shell(self, build)
end
@ -87,23 +108,12 @@ M.build = {
M.docs = {
skip = function(plugin)
return not plugin._.is_local and not plugin._.dirty
return not plugin._.dirty
end,
run = function(self)
local docs = self.plugin.dir .. "/doc"
local docs = self.plugin.dir .. "/doc/"
if Util.file_exists(docs) then
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 .. "`")
self.output = vim.api.nvim_cmd({ cmd = "helptags", args = { docs } }, { output = true })
end
end,
}

View file

@ -1,214 +0,0 @@
---@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

View file

@ -1,8 +1,8 @@
local Config = require("lazy.core.config")
local Util = require("lazy.core.util")
local Util = require("lazy.util")
local M = {}
M.VERSION = 12
M.VERSION = 10
M.dirty = false
---@class LazyPkg
@ -14,7 +14,6 @@ M.dirty = false
---@class LazyPkgSpec
---@field file string
---@field source? string
---@field spec? LazySpec
---@field code? string
@ -33,12 +32,10 @@ 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
sources[#sources + 1] = {
name = s,
get = require("lazy.pkg." .. s).get,
}
end
---@type LazyPkgCache
@ -55,7 +52,7 @@ function M.update()
local pkg = {
name = plugin.name,
dir = plugin.dir,
source = spec.source or source.name,
source = source.name,
file = spec.file,
spec = spec.spec or {},
}
@ -71,10 +68,9 @@ function M.update()
table.sort(ret.pkgs, function(a, b)
return a.name < b.name
end)
local U = require("lazy.util")
local code = "return " .. U.dump(ret)
local code = "return " .. Util.dump(ret)
vim.fn.mkdir(vim.fn.fnamemodify(Config.options.pkg.cache, ":h"), "p")
U.write_file(Config.options.pkg.cache, code)
Util.write_file(Config.options.pkg.cache, code)
M.dirty = false
M.cache = nil
end

View file

@ -1,328 +1,66 @@
--# 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")
local Util = require("lazy.core.util")
local M = {}
M.dev_suffix = "-1.rockspec"
M.skip = { "lua" }
---@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
---@field build? {build_type?: string, modules?: any[]}
---@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),
}
local rockspec_file ---@type string?
Util.ls(plugin.dir, function(path, name, t)
if t == "file" and name:sub(-#M.dev_suffix) == M.dev_suffix then
rockspec_file = path
return false
end
end)
if not rockspec_file then
return
end
local rockspec_file = M.find_rockspec(plugin)
local rockspec = rockspec_file and M.rockspec(rockspec_file)
---@type RockSpec?
---@diagnostic disable-next-line: missing-fields
local rockspec = {}
local load, err = loadfile(rockspec_file, "t", rockspec)
if not load then
error(err)
end
load()
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
local use = not has_lua
or #rocks > 0
-- has a complex build process
or not M.is_simple_build(rockspec)
or (
rockspec.build
and rockspec.build.build_type
and rockspec.build.build_type ~= "none"
and not (rockspec.build.build_type == "builtin" and not rockspec.build.modules)
)
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
return
end
local lazy = nil
@ -337,7 +75,7 @@ function M.get(plugin)
build = "rockspec",
lazy = lazy,
},
}
} or nil
end
return M

View file

@ -21,7 +21,6 @@ M.C = nil
function M.on_ui_enter()
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 })
end

View file

@ -1,71 +0,0 @@
---@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

@ -10,7 +10,6 @@
---@field dirty? boolean
---@field build? boolean
---@field frags? number[]
---@field top? boolean
---@field handlers? LazyPluginHandlers
---@field installed? boolean
---@field is_local? boolean
@ -21,7 +20,6 @@
---@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
@ -31,7 +29,7 @@
---@field init? fun(self:LazyPlugin) Will always be run
---@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 build? false|string|async fun(self:LazyPlugin)|(string|async fun(self:LazyPlugin))[]
---@field build? string|async fun(self:LazyPlugin)|(string|async fun(self:LazyPlugin))[]
---@field opts? PluginOpts
---@class LazyPluginHandlers
@ -61,7 +59,6 @@
---@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 rocks? string[]
---@field virtual? boolean virtual plugins won't be installed or added to the rtp.
---@class LazyPlugin: LazyPluginBase,LazyPluginHandlers,LazyPluginHooks,LazyPluginRef
---@field dependencies? string[]
@ -76,8 +73,6 @@
---@field module? false
---@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 specs? string|string[]|LazyPluginSpec[]

View file

@ -73,24 +73,28 @@ end
---@param fn F
---@return F
function M.throttle(ms, fn)
---@type Async
local async
local pending = false
local timer = vim.uv.new_timer()
local running = false
local first = true
return function()
if async and async:running() then
pending = true
return
return function(...)
local args = { ... }
local wrapped = function()
fn(unpack(args))
end
---@async
async = require("lazy.async").new(function()
repeat
pending = false
fn()
async:sleep(ms)
if not running then
if first then
wrapped()
first = false
end
until not pending
end)
timer:start(ms, 0, function()
running = false
vim.schedule(wrapped)
end)
running = true
end
end
end
@ -162,21 +166,12 @@ end
---@param opts? LazyCmdOptions|{filetype?:string}
function M.float_cmd(cmd, opts)
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)
if opts.filetype then
vim.bo[float.buf].filetype = opts.filetype
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.bo[float.buf].modifiable = false
return float
@ -236,6 +231,18 @@ function M.markdown(msg, opts)
)
end
---@async
---@param ms number
function M.sleep(ms)
local continue = false
vim.defer_fn(function()
continue = true
end, ms)
while not continue do
coroutine.yield()
end
end
function M._dump(value, result)
local t = type(value)
if t == "number" or t == "boolean" then

View file

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

View file

@ -146,7 +146,7 @@ end
---@return string, string[]
function M.parse(args)
local parts = vim.split(vim.trim(args), "%s+")
if vim.startswith("Lazy", parts[1]) then
if parts[1]:find("Lazy") then
table.remove(parts, 1)
end
if args:sub(-1) == " " then

View file

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

View file

@ -166,7 +166,7 @@ function M:mount()
self:augroup(true)
end, { win = true })
self:focus()
self:on_key(ViewConfig.keys.close, self.close, "Close")
self:on_key(ViewConfig.keys.close, self.close)
self:on({ "BufDelete", "BufHidden" }, self.close)
if vim.bo[self.buf].buftype == "" then
@ -252,7 +252,7 @@ end
---@param fn fun(self?)
---@param desc? string
---@param mode? string[]
function M:on_key(key, fn, desc, mode)
function M:on_key(key, fn, desc,mode)
vim.keymap.set(mode or "n", key, function()
fn(self)
end, {
@ -295,7 +295,6 @@ function M:close(opts)
vim.diagnostic.reset(Config.ns, buf)
vim.api.nvim_buf_delete(buf, { force = true })
end
vim.cmd.redraw()
end)
end

View file

@ -67,10 +67,7 @@ function M.create()
self.state = vim.deepcopy(default_state)
self.render = Render.new(self)
local update = self.update
self.update = Util.throttle(Config.options.ui.throttle, function()
update(self)
end)
self.update = Util.throttle(Config.options.ui.throttle, self.update)
for _, pattern in ipairs({ "LazyRender", "LazyFloatResized" }) do
self:on({ "User" }, function()
@ -83,11 +80,8 @@ function M.create()
vim.keymap.set("n", ViewConfig.keys.abort, function()
require("lazy.manage.process").abort()
require("lazy.async").abort()
return ViewConfig.keys.abort
end, { silent = true, buffer = self.buf, expr = true, desc = "Abort" })
vim.keymap.set("n", "gx", "K", { buffer = self.buf, remap = true })
end, { silent = true, buffer = self.buf, expr = true })
-- plugin details
self:on_key(ViewConfig.keys.details, function()
@ -97,49 +91,17 @@ function M.create()
name = plugin.name,
kind = plugin._.kind,
}
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.state.plugin = not vim.deep_equal(self.state.plugin, selected) and selected or nil
self:update()
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")
end)
self:on_key(ViewConfig.keys.profile_sort, function()
if self.state.mode == "profile" then
self.state.profile.sort_time_taken = not self.state.profile.sort_time_taken
self:update()
end
end, "Sort Profile")
end)
self:on_key(ViewConfig.keys.profile_filter, function()
if self.state.mode == "profile" then
@ -159,18 +121,17 @@ function M.create()
end
end)
end
end, "Filter Profile")
end)
for lhs, rhs in pairs(Config.options.ui.custom_keys) do
if rhs then
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()
local plugin = self.render:get_plugin()
if plugin then
handler(plugin)
end
end, desc)
end)
end
end
@ -181,7 +142,9 @@ end
function M:update()
if self.buf and vim.api.nvim_buf_is_valid(self.buf) then
vim.bo[self.buf].modifiable = true
self.render:update()
vim.bo[self.buf].modifiable = false
vim.cmd.redraw()
end
end
@ -220,17 +183,17 @@ function M:setup_patterns()
["(https?://%S+)"] = function(url)
Util.open(url)
end,
}, self.hover, "Hover")
}, self.hover)
self:on_pattern(ViewConfig.keys.diff, {
[commit_pattern] = function(hash)
self:diff({ commit = hash })
end,
}, self.diff, "Diff")
}, self.diff)
self:on_pattern(ViewConfig.commands.restore.key_plugin, {
[commit_pattern] = function(hash)
self:restore({ commit = hash })
end,
}, self.restore, "Restore")
}, self.restore)
end
---@param opts? {commit:string}
@ -295,8 +258,7 @@ end
---@param key string
---@param patterns table<string, fun(str:string)>
---@param fallback? fun(self)
---@param desc? string
function M:on_pattern(key, patterns, fallback, desc)
function M:on_pattern(key, patterns, fallback)
self:on_key(key, function()
local line = vim.api.nvim_get_current_line()
local pos = vim.api.nvim_win_get_cursor(0)
@ -318,7 +280,7 @@ function M:on_pattern(key, patterns, fallback, desc)
if fallback then
fallback(self)
end
end, desc)
end)
end
function M:setup_modes()

View file

@ -51,7 +51,7 @@ function M:update()
if plugin._.tasks then
for _, task in ipairs(plugin._.tasks) do
self.progress.total = self.progress.total + 1
if not task:running() then
if not task:is_running() then
self.progress.done = self.progress.done + 1
end
end
@ -74,17 +74,7 @@ function M:update()
end
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)
vim.api.nvim_win_call(self.view.win, function()
vim.fn.winrestview(view)
end)
vim.bo[self.view.buf].modifiable = false
vim.diagnostic.set(
Config.ns,
self.view.buf,
@ -94,7 +84,7 @@ function M:update()
diag.lnum = diag.row - 1
return diag
end, self._diagnostics),
{ signs = false, virtual_text = true, underline = false, virtual_lines = false }
{ signs = false, virtual_text = true, underline = false }
)
end
@ -122,15 +112,6 @@ function M:get_plugin(row)
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()
self:nl()
local modes = vim.tbl_filter(function(c)
@ -144,6 +125,8 @@ function M:title()
if mode.name == "home" then
if self.view.state.mode == "home" then
title = " lazy.nvim " .. Config.options.ui.icons.lazy
else
title = " lazy.nvim (H) "
end
end
@ -199,15 +182,7 @@ function M:help()
: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():nl()
self
:append("Use ")
:append("<]]>", "LazySpecial")
:append(" and ")
:append("<[[>", "LazySpecial")
:append(" to navigate between plugins")
:nl()
:nl()
self:append("Use "):append("<d>", "LazySpecial"):append(" on a commit or plugin to open the diff view"):nl()
self:nl()
self:append("Keyboard Shortcuts", "LazyH2"):nl()
@ -379,37 +354,7 @@ end
---@param plugin LazyPlugin
function M:diagnostics(plugin)
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 then
if plugin._.updated.from == plugin._.updated.to then
self:diagnostic({
message = "already up to date",
@ -438,6 +383,19 @@ function M:diagnostics(plugin)
})
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
---@param plugin LazyPlugin
@ -476,22 +434,6 @@ function M:plugin(plugin)
{ name = plugin.name, from = plugin_start, to = self:row() - 1, kind = plugin._.kind }
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
function M:tasks(plugin)
for _, task in ipairs(plugin._.tasks or {}) do
@ -500,56 +442,46 @@ function M:tasks(plugin)
self:append(" " .. math.floor((task:time()) * 100) / 100 .. "ms", "Bold")
self:nl()
end
if not task:has_warnings() and task.name == "log" then
if task.error then
self:append(vim.trim(task.error), "LazyTaskError", { indent = 6 })
self:nl()
elseif task.name == "log" then
self:log(task)
else
local hls = {
[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
elseif self.view:is_selected(plugin) and task.output ~= "" and task.output ~= task.error then
self:append(vim.trim(task.output), "LazyTaskOutput", { indent = 6 })
self:nl()
end
end
end
---@param task LazyTask
function M:log(task)
local log = vim.trim(task:output())
local log = vim.trim(task.output)
if log ~= "" then
local lines = vim.split(log, "\n")
for _, line in ipairs(lines) do
local ref, msg, time = line:match("^(%w+) (.*) (%(.*%))$")
if msg then
if msg:find("^%S+!:") then
self:diagnostic({ message = "Breaking Changes", severity = vim.diagnostic.severity.WARN })
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()
if msg:find("^%S+!:") then
self:diagnostic({ message = "Breaking Changes", severity = vim.diagnostic.severity.WARN })
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()
end
self:nl()
end
@ -579,11 +511,6 @@ function M:details(plugin)
table.insert(props, { "commit", git.commit:sub(1, 7), "LazyCommit" })
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
table.insert(props, { "readme", "README.md" })
end
@ -759,7 +686,7 @@ function M:debug()
---@type string[]
plugins = vim.tbl_values(plugins)
table.sort(plugins)
self:append(Config.options.ui.icons.debug, "LazySpecial", { indent = 2 })
self:append("", "LazySpecial", { indent = 2 })
if handler_type == "keys" then
for k, v in pairs(Config.plugins[plugins[1]]._.handlers.keys) do
if k == value then

View file

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

View file

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

116407
manifest

File diff suppressed because it is too large Load diff

View file

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

39
tests/core/e2e_spec.lua Normal file
View file

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

View file

@ -2,32 +2,28 @@ local Config = require("lazy.core.config")
local Handler = require("lazy.core.handler")
local Plugin = require("lazy.core.plugin")
local assert = require("luassert")
Config.setup()
local function inspect(obj)
return vim.inspect(obj):gsub("%s+", " ")
end
---@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[]
---@param plugins LazyPlugin[]|LazyPlugin
local function clean(plugins)
return vim.tbl_map(function(plugin)
plugin = resolve(plugin)
plugin[1] = nil
local p = plugins
plugins = type(plugins) == "table" and plugins or { plugins }
for _, plugin in pairs(plugins) do
plugin._.fid = nil
plugin._.fpid = nil
plugin._.fdeps = nil
plugin._.frags = nil
if plugin._.dep == false then
plugin._.dep = nil
end
plugin._.top = nil
return plugin
end, plugins)
end
return p
end
describe("plugin spec url/name", function()
@ -174,12 +170,14 @@ describe("plugin spec opt", function()
end
assert.same({
bar = {
"foo/bar",
_ = {},
dependencies = { "dep1", "dep2" },
name = "bar",
url = "https://github.com/foo/bar.git",
},
dep1 = {
"foo/dep1",
_ = {
dep = true,
},
@ -187,6 +185,7 @@ describe("plugin spec opt", function()
url = "https://github.com/foo/dep1.git",
},
dep2 = {
"foo/dep2",
_ = {
dep = true,
},
@ -201,13 +200,13 @@ describe("plugin spec opt", function()
before_each(function()
Handler.init()
end)
Config.options.defaults.lazy = false
local tests = {
{ { "foo/bar", dependencies = { { "dep1" }, "foo/dep2" } }, "foo/dep1" },
{ "foo/dep1", { "foo/bar", dependencies = { { "dep1" }, "foo/dep2" } } },
}
for _, test in ipairs(tests) do
it("handles dep names " .. inspect(test), function()
it("handles dep names", function()
Config.options.defaults.lazy = false
local tests = {
{ { "foo/bar", dependencies = { { "dep1" }, "foo/dep2" } }, "foo/dep1" },
{ "foo/dep1", { "foo/bar", dependencies = { { "dep1" }, "foo/dep2" } } },
}
for _, test in ipairs(tests) do
local spec = Plugin.Spec.new(vim.deepcopy(test))
assert(#spec.notifs == 0)
Config.plugins = spec.plugins
@ -216,74 +215,31 @@ describe("plugin spec opt", function()
for _, plugin in pairs(spec.plugins) do
plugin.dir = nil
end
assert.same({
assert.same(clean(spec.plugins), {
bar = {
"foo/bar",
_ = {},
dependencies = { "dep1", "dep2" },
name = "bar",
url = "https://github.com/foo/bar.git",
},
dep1 = {
"foo/dep1",
_ = {},
name = "dep1",
url = "https://github.com/foo/dep1.git",
},
dep2 = {
"foo/dep2",
_ = {
dep = true,
},
name = "dep2",
url = "https://github.com/foo/dep2.git",
},
}, clean(spec.plugins))
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
})
end
end)
it("handles opt from dep", function()
Config.options.defaults.lazy = false
@ -340,7 +296,7 @@ describe("plugin spec opt", function()
assert(#spec.notifs == 0)
assert(vim.tbl_count(spec.plugins) == 1)
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 {})
assert(type(events) == "table")
assert(#events == 2)

View file

@ -1,3 +1,4 @@
---@module 'luassert'
local Keys = require("lazy.core.handler.keys")
describe("keys", function()

36
tests/init.lua Normal file
View file

@ -0,0 +1,36 @@
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

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

View file

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

View file

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

View file

@ -1,12 +0,0 @@
#!/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() },
},
})

3
tests/run Executable file
View file

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

View file

@ -8,14 +8,42 @@ any = true
[jit]
any = true
[assert]
[[describe.args]]
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
[describe]
any = true
[[assert.equals.args]]
type = "any"
[[assert.equals.args]]
type = "any"
[[assert.equals.args]]
type = "any"
required = false
[it]
any = true
[[assert.same.args]]
type = "any"
[[assert.same.args]]
type = "any"
[before_each.args]
any = true
[[assert.truthy.args]]
type = "any"
[[assert.spy.args]]
type = "any"
[[assert.stub.args]]
type = "any"