bug: No autoload on require when username is lua
Did you check docs and existing issues?
- [x] I have read all the lazy.nvim docs
- [x] I have updated the plugin to the latest version before submitting this issue
- [x] I have searched the existing issues of lazy.nvim
- [x] I have searched the existing issues of plugins related to this issue
Neovim version (nvim -v)
0.11.0
Operating system/version
Arch linux
Describe the bug
https://github.com/user-attachments/assets/a8d0f2e6-b4ca-49cf-8648-00457c737fb1
When the username is "lua", lazy.nvim silently fails to autoload a plugin onrequire
Steps To Reproduce
Setup:
sudo useradd -m -c "Lua test user" lua
sudo su lua
cd
mkdir -p .config/nvim
# ... cp the supplied repro...
nvim init.lua
# ... wait for the installation to complete ...
nvim init.lua
Reproduce:
Type: :lua require("lualine") and enter
Teardown:
exit
sudo userdel lua
rm -rf /home/lua
Expected Behavior
Normally, given the supplied repro, lualine would appear after typing :lua require("lualine") and enter.
Repro
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 = {
{ "nvim-lualine/lualine.nvim", lazy = true, opts = {} },
},
})
See failed to run config for lualine.nvim on starting NeoVim in LazyVim, and perhaps this comment in trouble.nvim.
I unfortunately had to close PR #1982, as it broke existing behavior...
Hey @abeldekat I thought of something but not sure at all if it works. Could you try something along these lines
diff --git a/lua/lazy/core/loader.lua b/lua/lazy/core/loader.lua
index 1501efd..bdcbc38 100644
--- a/lua/lazy/core/loader.lua
+++ b/lua/lazy/core/loader.lua
@@ -529,7 +529,7 @@ function M.colorscheme(name)
end
function M.auto_load(modname, modpath)
- local plugin = Plugin.find(modpath, { fast = not M.did_handlers })
+ local plugin = Plugin.find(modpath, modname, { fast = not M.did_handlers })
if plugin then
plugin._.rtp_loaded = true
-- don't load if:
diff --git a/lua/lazy/core/plugin.lua b/lua/lazy/core/plugin.lua
index 37d1a8f..2981785 100644
--- a/lua/lazy/core/plugin.lua
+++ b/lua/lazy/core/plugin.lua
@@ -384,13 +384,16 @@ end
-- Finds the plugin that has this path
---@param path string
+---@param modname? string
---@param opts? {fast?:boolean}
-function M.find(path, opts)
+function M.find(path, modname, opts)
if not Config.spec then
return
end
opts = opts or {}
- local lua = path:find("/lua/", 1, true)
+ local topmod = Util.topmod(modname)
+ local modlocation = path:find(topmod, 1, true)
+ local lua = path:find("/lua/", modlocation + 1, true)
if lua then
local name = path:sub(1, lua - 1)
local slash = name:reverse():find("/", 1, true)
So, basically change the M.find signature to also accept modname since we can already pass it from M.auto_load into it and then just find the topmod location and search for the first /lua/ after that.
Please keep in mind I might just be spewing total non-sense.
PS: I didn't write the extra conditional check for if modname exists or not, but I hope you get the gist. But like I said maybe it's just non-sense.
PS2: I made wrong assumption that topmod would be equivalent to plugin.name, but that's not true. So, maybe we would like to find the first /lua/ in reverse after topmod maybe? That should be the top lua directory inside the plugins.
Hi @dpetka2001, thanks for the suggestions!
I created the PR under the incorrect assumption that the text "lua" would by convention only be used in the root level of a plugin. Unfortunately I do not know the code well enough to provide or comment on alternatives.
Hi @abeldekat would you be willing to test the following patch
diff --git a/lua/lazy/core/loader.lua b/lua/lazy/core/loader.lua
index 1501efd..bdcbc38 100644
--- a/lua/lazy/core/loader.lua
+++ b/lua/lazy/core/loader.lua
@@ -529,7 +529,7 @@ function M.colorscheme(name)
end
function M.auto_load(modname, modpath)
- local plugin = Plugin.find(modpath, { fast = not M.did_handlers })
+ local plugin = Plugin.find(modpath, modname, { fast = not M.did_handlers })
if plugin then
plugin._.rtp_loaded = true
-- don't load if:
diff --git a/lua/lazy/core/plugin.lua b/lua/lazy/core/plugin.lua
index 37d1a8f..0ac2bbf 100644
--- a/lua/lazy/core/plugin.lua
+++ b/lua/lazy/core/plugin.lua
@@ -384,23 +384,44 @@ end
-- Finds the plugin that has this path
---@param path string
+---@param modname? string
---@param opts? {fast?:boolean}
-function M.find(path, opts)
+function M.find(path, modname, opts)
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]
+ if modname then
+ local topmod = Util.topmod(modname)
+ local reversed = path:reverse()
+ local topreversed = topmod:reverse()
+ local topreversed_loc = reversed:find(topreversed, 1, true)
+ local rev_start, rev_end = reversed:find("/aul/", topreversed_loc + 1, true)
+ if rev_start then
+ local rev_next_slash = reversed:find("/", rev_end + 1, true)
+ if rev_next_slash then
+ local name = path:sub(#path - rev_next_slash +2, #path - rev_end)
+ if name then
+ if opts.fast then
+ return Config.spec.meta.plugins[name]
+ end
+ return Config.spec.plugins[name]
+ end
+ end
+ end
+ else
+ 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 Config.spec.plugins[name]
end
end
end
I think it might work, and thus still isn't good enough...)
It's a tough issue to fix in my opinion. I gathered some of my thoughts, which of cause still suffer from a lack of deep inside knowledge...
Markers in the code: 1 loader 2 autoload 3 find
A plugin on disk has the following path: Outside/N/"lua"/Inner, where:
- Outside: The outside path, like
/home/user/.local/share/nvim/lazy - N: The name of the plugin
- "lua":
Neovimspecific entrypoint - Inside: The organization on the inside, like
blink/cmp/fuzzy/lua/match_indices.lua
The existing code for "find" implicitly assumes that there is no "lua" folder in Outside. As a consequence, no assumptions are needed on the organization in Inside. The code can discard that tail when trying to find the name of the plugin.
My PR, and your code as well, now does make assumptions on Inside, because unfortunately, a user can be named "lua" in Outside. Other situations I can think of:
- Perhaps
NVIM_APPNAMEcould also be "lua" - The user overrides directory locations via the XDG spec.
- Plugins can be configured to be located in a user defined directory.
When an assumption on Inside does not hold true that might lead to bugs that are very difficult to solve.
In your code for example, the topmod for blink.cmp becomes blink. The algorithm only works because there is no other nested "blink" in Inside...
A good fix would eliminate assumptions. As suggested by @Phanen:
local lua = path:trim_rtp():find("/lua/", 1, true)
A solution will need to be performant.
Closing the issue as a "known limitation" is also a possibility perhaps.
Thank you for your insight. Indeed if there's another blink nested in the modname, then it would not actually find the correct location of the directory top module. It also might or not find the top /lua/ directory depending if there's another /lua/ directory before the nested blink module (if there's no /lua/ directory before the nested blink module then it would correctly find the Neovim /lua/ entry point).
But I agree that we make too many assumptions and a better solution should be in place if at all. This all started from a rather niche case of the username being lua, so it wouldn't surprise me if Folke just dismissed it as a known limitation as you say.
This issue is stale because it has been open 30 days with no activity. Remove stale label or comment or this will be closed in 7 days.
Not stale
This issue is stale because it has been open 30 days with no activity. Remove stale label or comment or this will be closed in 7 days.
Nah
This issue is stale because it has been open 30 days with no activity. Remove stale label or comment or this will be closed in 7 days.
.
This issue is stale because it has been open 30 days with no activity. Remove stale label or comment or this will be closed in 7 days.
.
This issue is stale because it has been open 30 days with no activity. Remove stale label or comment or this will be closed in 7 days.
.
This issue is stale because it has been open 30 days with no activity. Remove stale label or comment or this will be closed in 7 days.
.
This issue is stale because it has been open 30 days with no activity. Remove stale label or comment or this will be closed in 7 days.
.