lazy.nvim icon indicating copy to clipboard operation
lazy.nvim copied to clipboard

bug: Plugins `init` blocks executed in random order regardless of the dependencies settings

Open zdm opened this issue 9 months ago • 20 comments

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)

nvim 0.10.4

Operating system/version

windows, linux

Describe the bug

Plugins init blocks executed in random order regardless of the dependencies settings.

Steps To Reproduce

  1. Put this file to the "lua/plugins/test.lua"
return {
    {
        "vim-scripts/Marks-Browser",
        init = function ()
            vim.g.test = "test"
        end,
    },
    {
        "mattn/emmet-vim",
        dependencies = {
            "vim-scripts/Marks-Browser"
        },
        init = function ()
            vim.g.test1 = vim.g.test .. "123"
        end
    },
}
  1. Run nvim several times
  2. See, that sometimes ir throws error that vim.g.test is not defined during concatenation.

Expected Behavior

It should execute init and config code respecting order of the dependency tree.

Repro

See above.

zdm avatar Mar 25 '25 09:03 zdm

It is possible, that I am using dependencies config incorrectly. If dependency is defined as a string - is it a link to the dependency spec, which is defined somewhere else? What if I have plugin, which is a dependency for several other plugins? I want to define it spec once and use links to this spec.

zdm avatar Mar 25 '25 09:03 zdm

Probably similar to #1751. Seems like there's no way to ensure the order of the init functions.

dpetka2001 avatar Mar 25 '25 10:03 dpetka2001

Possible to implement? Topological sort can be used to build deps tree.

And do you know, how dependency is processed, if it is defined as a string? Is it treated as a link to the spec? Is is possible to use it as a link if spec for plugin with this id is defined separately?

zdm avatar Mar 25 '25 10:03 zdm

Sorry, but I'm just a simple user like you and I have limited programming knowledge and haven't dug into lazy.nvim's code. I just remembered something similar was asked in that other issue.

If you want more details, you will have to wait for maintainer's input. I'm sure he'll have better insight.

dpetka2001 avatar Mar 25 '25 10:03 dpetka2001

I'm not sure if the dependency is treated as a link. But I've seen numerous examples where the plugin spec is defined in some other place and then the plugin name is used as a dependency to ensure that it will load before the current plugin spec.

dpetka2001 avatar Mar 25 '25 11:03 dpetka2001

Yes, but for this issues we need to be 100% sure. Lets' wait until somebody from support team will answer.

zdm avatar Mar 25 '25 11:03 zdm

For an example you can see in LazyVim for Mason. Here is its plugin spec and then here it's also defined as a dependency for conform.nvim. But that only ensures the loading order of the plugins.

I believe the init functions get parsed and then loaded before any plugin specs loading, but the order is not deterministic.

dpetka2001 avatar Mar 25 '25 11:03 dpetka2001

Maybe you are right. In any case it should be executed in sorted order, otherwise what is the sense to specify dependencies ))

zdm avatar Mar 25 '25 11:03 zdm

Specifying dependencies only affects the loading order of the plugins. Not the execution of the plugins' init function. They are 2 different things to the best of my understanding.

I believe the relevant code can be found here. pairs does not guarantee a sorted order. Later on you see for the startup plugins that ipairs is used, but that is because get_start_plugins returns a sorted table.

Maybe it would be a performance hit to sort all plugin specs, thus only the startup plugins get sorted to make the performance hit as low as possible. Because there might also be plugins that won't get loaded that might have an init function as well that will get executed.

This is just my best guesstimation from reading the above code. In the end we should definitely wait for maintainer's input as he's far more familiar with lazy.nvim's internals.

dpetka2001 avatar Mar 25 '25 11:03 dpetka2001

I don't this that it affects performance. Building deps tree for 100 elements will take microseconds.

zdm avatar Mar 25 '25 11:03 zdm

It just not implemented. But it should.

zdm avatar Mar 25 '25 11:03 zdm

I think that what @dpetka2001 is saying here is correct.

In the init phase, plugins don't get loaded, so dependencies are not relevant yet.

This can also be observed in the ui, under profiling->lazy.nvim->startup. The init phase triggers even before the start phase.

An alternative for your use case that is guaranteed to work:


return {
  {
    "vim-scripts/Marks-Browser",
    init = function()
      vim.g.test = "test"
    end,
  },
  {
    "mattn/emmet-vim",
    dependencies = {
      "vim-scripts/Marks-Browser",
    },
    config = function() -- config is executed when the plugin loads
      vim.g.test1 = vim.g.test .. "123"
    end,
  },
}

abeldekat avatar Mar 26 '25 09:03 abeldekat

Yes, I already use config as temporary solution. But this is not quite correct. Config is executed after plugin is loaded, but in some cases variables should be defined before load and run plugin code.

To make it work correctly and close all potential questions in the future it is better to fix this issue.

zdm avatar Mar 26 '25 09:03 zdm

Also want to say, that this is obvious and expected behavior, that if dependencies are set, all actions should be performed in the sorted order.

zdm avatar Mar 26 '25 09:03 zdm

Also want to say, that this is obvious and expected behavior, that if dependencies are set, all actions should be performed in the sorted order.

That's up for debate I guess. The init spec does not mention any order. As during init no plugins are loaded automatically, using the output of another plugin in that init is questionable.

Out of curiosity: Do you have a specific situation where you are unable to achieve what you want? A repro would be interesting.

abeldekat avatar Mar 26 '25 09:03 abeldekat

As the current design stands, dependencies and in general plugin specs have nothing to do with init functions.

I'm not sure how big of a refactor that could entail and if maintainer was even interested in something like this, but one relatively naive solution (with my very basic knowledge and understanding and keeping in mind that I might as well be entirely wrong) would be to first execute the init functions of startup plugins, since that table is already sorted, and then execute the rest of the plugins init functions with something like vim.tbl_filter on Config.plugins to remove the already executed startup plugins. That way you could maybe give a lower priority for the plugins you want loaded afterwards and thus it will be sorted after the plugin that you want the second plugin to depend on, but still dependencies would not have any effect on the init functions. But this sorted execution of init functions would only have effect for startup plugins.

But I'm really not sure if that is a viable solution and the way it impacts the rest of the implementation, so I'm deferring everything I mentioned to maintainer when he's able to give back some input.

dpetka2001 avatar Mar 26 '25 10:03 dpetka2001

Out of curiosity: Do you have a specific situation where you are unable to achieve what you want? A repro would be interesting.

I my case I can use config. But this is a hack. I described above why. This issue - is a potential problem, because code behaves unexpectedly. I just want to make it better.

zdm avatar Mar 26 '25 10:03 zdm

Guys, usingconfigor playing with priority - all this is a hacks. We can invent many other ways to avoid this problem here. But only the right way is to sort deps and then perform all actions in the sorted order.

zdm avatar Mar 26 '25 10:03 zdm

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.

github-actions[bot] avatar Apr 26 '25 02:04 github-actions[bot]

Close?

zdm avatar Apr 26 '25 03:04 zdm

as explained init is not to be used like this

folke avatar May 12 '25 13:05 folke