Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

[Feature] Lazy loading #421

Open
GaetanLepage opened this issue Jun 11, 2023 · 50 comments
Open

[Feature] Lazy loading #421

GaetanLepage opened this issue Jun 11, 2023 · 50 comments
Assignees
Labels
enhancement New feature or request

Comments

@GaetanLepage
Copy link
Collaborator

GaetanLepage commented Jun 11, 2023

-->Summary: https://notes.glepage.com/rA_fyfKESvC9OxGGNHOYXw#Lazy-Loading <--

Currently, nixvim relies on the neovim built-in way of loading plugins.
This is implemented by nixpkgs neovim's module. It has the advantage of existing and leveraging the collection of plugins packaged in nixpkgs.

With that said, the very popular lazy.nvim plugin manager proposes a more performant way of loading plugins.
Imitating their lazy loading process could be a very valuable addition to nixvim.
I would guess that this feature should be optional so that conventional loading would stay the default.

As of today, no-one has started to work on this issue.
Feel free to go and make a PR, even if the work is in draft-stage. I will personally be glad to review and discuss a proposed implementation.

@pta2002
Copy link
Collaborator

pta2002 commented Jun 11, 2023

A (relatively) easy way to do this, compared to lazy.nvim's approach of basically redoing the whole lazy-loading machinery in neovim, is to make use of optional plugin loading - this is supported, I don't really remember how, but it's a matter of checking how wrapNeovim works.

This way, we can :packadd the packages only when they're needed - AFAIK this is basically how plugin lazy-loading has been done in the past.

Second, probably easier, option - the vast majority of the plugins we support are lua-based plugins. The majority of the loading time there is spent on the setup function. What if we just avoid calling that function until it's needed?

Either way, I will be looking into how lazy-loading in neovim has been traditionally done, since I don't have much experience with it, and will try to come up with some way it could work.

@pta2002
Copy link
Collaborator

pta2002 commented Jun 11, 2023

Also, another feature lazy.nvim supports is bytecode pre-compilation. This is actually totally something we could do! Basically just need to wrap the plugin's derivations to add that. Something to look into, but I'd say this is better off as a separate feature.

@pta2002
Copy link
Collaborator

pta2002 commented Jun 11, 2023

Some references:

  • Examples of lazyloading (Neovim discourse) - Basically, create some convenience functions for delaying lazy-loading, and then bind them that way. We have all the machinery required right now - we can create autocommands, rebind commands and rebind keys. With this, we can just shim a plugin's commands and :packadd the plugin when they're called, or wait for an autocommand to load them.

@GaetanLepage GaetanLepage pinned this issue Jun 15, 2023
@GaetanLepage
Copy link
Collaborator Author

Either way, I will be looking into how lazy-loading in neovim has been traditionally done, since I don't have much experience with it, and will try to come up with some way it could work.

Great ! Thanks and good luck :)
Feel free to open a PR as soon as you have a draft so that I can help and provide feedback :)

@GaetanLepage GaetanLepage added the enhancement New feature or request label Jun 22, 2023
@stasjok
Copy link

stasjok commented Jul 23, 2023

Yesterday I found out about nixvim. I'm already using home-manager. I'm not sure I would migrate though. I do some customization for neovim loading to greatly improve startup time and I'm not sure I can insert nixvim in that process. But the project looks interesting. Still I wanted to comment about lazy loading. What I think is most important for startup time are:

  1. Reducing vim.api.nvim_list_runtime_paths(). It's an actual search path where neovim will look for files. More plugins, longer the list. What is even worse in nix ecosystem is that every tree-sitter parser is actually a separate plugin. So when you are installing all parsers, your list_runtime_paths contains hundreds of entries. It's very bad for startup time. Just merging all parsers to a single plugin with symlinkJoin can greatly reduce startup time. But I went even further. I merged all plugins into one plugin pack with buildEnv. So my list_runtime_paths contains only 6 entries (.config/nvim, vim-pack-dir, vim-pack-dir/pack/myNeovimPackages/start/plugin-pack, nvim/runtime, vim-pack-dir/pack/myNeovimPackages/start/plugin-pack/after, .config/nvim/after).
    vim.loader can alleviate a long search path, because it caches directories, so nvim will not search in very directory. But vim.loader is only for lua files. Neovim also scan all directories for plugins, ftplugins, syntax and so on. So reducing list_runtime_paths improves things every time you load a buffer (because of autocommands scanning for ftplugin, syntax, indent).
    Overall reducing vim.api.nvim_list_runtime_paths() gave me the biggest impact. But unfortunately it's not an universal solution, because of file collisions. I'm currently removing help tags in every plugin and generating a new one for the entire pack. Also I'm limiting linked directories with pathsToLink.

  2. Byte-compiling every lua file. With nix we have a build step, so it's better to do a compilation beforehand, and not cache during startup. I compile every lua file: every file in every plugin, every file in my .conf/nvim directory, init.lua, and nvim runtime directory. I have a build hook that does that. As a result I have a fast startup time even on first startup and I don't pollute .cache/nvim with thousands of compiled lua files (with nix every new configuration has different path, so there are a lot of old files there). I don't use vim.loader at all. With both 1 and 2 I have even faster startup time than vim.loader. There is one caveat: lua-language-server can't parse compiled files. As a workaround I save a list of directories with original files in json and load it to lua_ls config.

  3. Reduce a number of required lua files. Usually it means not using heavy functions as callbacks for mappings. I mean do something like callback = function() require("plugin.functions").do_something() end. But you don't need to do it if require("plugin").setup() already pulling all relevant files, you won't improve speed in that case, rather the opposite (every time you use that binding).

  4. I don't do it personally, but it's possible to defer plugin setup on some event (like mapping or command). packadd will increase vim.api.nvim_list_runtime_paths() for my case, so I never use optional plugins.

So even not doing any lazy-loading I have a decent startup time. Without all these optimizations vim.loader greatly improves things. But 1) improves it even further. Maybe some of it can be implemented in nixvim. For example merging treesitter parsers in one plugin (minus 200 to vim.api.nvim_list_runtime_paths()) and byte-compiling init.lua (vim.loader doesn't compile it, because it's enabled after).

@GaetanLepage
Copy link
Collaborator Author

Thank you so much @stasjok for this great insight of your config !
This will surely help once we will tackle the performance issue seriously.

@koalagang
Copy link

I'm not using nixvim either because I need the lazy-loading but I'd like to chip in an idea. If you're willing to outsource some of the code, I'd suggest using lazy.nvim seeing as it's pretty good at lazy-loading + it would reduce the maintenance burden for developing nixvim. Its variety of options and features could come in handy. It even has async execution and automatic caching and bytecode compilation of Lua modules for improved performance. You could provide options to configure it in nix and then translate that to lua like you do with the rest of nixvim. Lazy's dir option could be used to specify the path to the plugin in the nix store. For plugins not available in the nix repos, you could generate packages (see how they do it in the nix repos) or even just let lazy.nvim handle it internally (using git) if you don't mind installing stuff outside of the nix store.

I've written some examples below to illustrate the idea.
The nix file:

{
  plugins.lazy = {
    enable = true;

    # install any plugin available in the nix repos
    nix = {

      # this installs vimPlugins.neorg
      neorg = {
        build = ":Neorg sync-parsers";
        dependencies = [
          # these are all from the nix repos too
          # they load when Neorg loads
          plenary-nvim
          nvim-treesitter
          neorg-telescope = {
            dependencies = [ telescope-nvim ];
            config = ''
              -- this is lua code
              -- it does not get loaded until neorg-telescope is loaded
              vim.wo.foldlevel = 99
            '';
          };
        ];
        cmd = "Neorg";
        ft = "norg";
        keys = "<c-t>n";
        # sometimes we like to keep the config in a different file
        # so source it from elsewhere
        config = import ./neorg.nix;
      };

      nvim-colorizer-lua = {
        cmd = "ColorizerToggle"; # only load colorizer when I use :ColorizerToggle
        config = ''
          -- more lua code
          vim.g.termguicolors = true,
          require("colorizer").setup()
        '';
      };
    };

    # obscure plugin that isn't available in the nix repos
    git."Vonr/align.nvim" = {
        branch = "v2";
        # only load align.nvim when the user presses <leader>a in visual mode
        keys = [
          { "<leader>a"; mode = "v"; };
        ];
        config = ''
          vim.keymap.set(
              'v',
              '<leader>a',
              function()
                  require'align'.align_to_string({
                      preview = true,
                  })
              end,
              { noremap = true, silent = true }
          )
        '';
      };
  };
}

This would install pkgs.vimPlugins.lazy-nvim, pkgs.vimPlugins.neorg, pkgs.vimPlugins.nvim-colorizer-lua, pkgs.vimPlugins.plenary-nvim, pkgs.vimPlugins.nvim-treesitter, pkgs.vimPlugins.neorg-telescope and pkgs.vimPlugins.telescope-nvim.
It would also generate a lua file that looks something like this:

-- add lazy.nvim to the runtime path
vim.opt.rtp:prepend("/nix/store/rjkwbzdmdfp9gfrvi0rch5g5illqwbgf-vimplugin-lazy.nvim-2023-08-26")

require("lazy").setup({
  {
      name = "neorg",
      dir = "/nix/store/iwxg2w4blbphyk7yvf7drqkmf8qfyv4x-vimplugin-neorg-2023-09-15",
      build = ":Neorg sync-parsers",
      dependencies = {
          { name = "plenary.nvim", dir = "/nix/store/4f17ss3v7lv580hmca8pc2q3zn33lqk4-lua5.1-plenary.nvim-2023-09-12" },
          { name = "nvim-treesitter", dir = "/nix/store/vp0lhrdzvknz7rb8pc6ndvv28cgsfy14-vimplugin-nvim-treesitter-2023-09-16" },
          {
              name = "telescope.nvim",
              dir = "/nix/store/1ldgn56d6sgmk42n6dna3b91w3si3sn7-lua5.1-telescope.nvim-2023-09-16",
              dependencies = { name = "neorg-telescope", dir = "/nix/store/rvg763iaqbx3czb52ghrsxhidvqqgwbk-vimplugin-neorg-telescope-2023-08-06" },
              config = function()
                -- this is lua code
                -- it does not get loaded until neorg-telescope is loaded
                vim.wo.foldlevel = 99
              end,
          },
      },
      cmd = "Neorg",
      ft = "norg",
      keys = "<c-t>n",
      config = function()
        require("path.to.neorg.config")
      end,
  },

  {
      name = "nvim-colorizer.lua",
      dir = "/nix/store/03k72h7x4m543z7vrqjwbvqjx6j789qg-vimplugin-nvim-colorizer.lua-2023-02-27",
      cmd = "ColorizerToggle",
      config = function()
        -- more lua code
        vim.g.termguicolors = true,
        require("colorizer").setup()
      end,
  },

  {
    "Vonr/align.nvim",
    branch = "v2",
    keys = {{ "<leader>a", mode = "v" }},
    config = function()
        vim.keymap.set(
            'v',
            '<leader>a',
            function()
                require'align'.align_to_string({
                    preview = true,
                })
            end,
            { noremap = true, silent = true }
        )
    end,
  },
})

Note that you shouldn't try to lazy-load lazy.nvim or the colourscheme. These should be loaded on startup.
As a related sidenote, you can install stuff with lazy.nvim without lazy-loading them by using the lazy = false option. If you choose to load the colourscheme plugin using lazy, it's also good practise to use priority = 1000 to make sure it loads before all other plugins.

Cut me some slack if any of the nix or lua code above has mistakes. I'm pretty new to both languages and have so far not used them for anything besides configuring NixOS/home-manager and Neovim.

@koalagang
Copy link

Oh btw, I came across another project called nixneovim, which seems to have pretty much the same goal as you. I don't believe they do lazy-loading but maybe some of their stuff would interest you, such as their auto-updated plugins repo.

@GaetanLepage
Copy link
Collaborator Author

Thanks for the link @koalagang !
I knew that this project existed (it was forked from nixvim a while ago). However, I wasn't aware that they had their own repo.
We used to have an infrastructure to package plugins ourselves within nixvim but we have removed it at some point.
Indeed, we now prefer the approach of upstreaming all missing plugins to nixpkgs. This makes the user experience a smoother.
The only downside is that the users of the stable branch might not get the new/updated plugins. I am not aware of any backporting of the plugins by nixpkgs maintainers... (@teto am I right here ?).
Anyway, we don't backport our updates to the nixvim stable branch.

@teto
Copy link

teto commented Nov 30, 2023

I am not aware of any backporting of the plugins by nixpkgs maintainers... (@teto am I right here ?).

you are right, no backporting. It's not critical software + we usually dont run the tests (when there are tests !) so backporting would be tricky. Plus the tooling is not great.

auto-updated plugins repo.

I am glad to learn about this, I've wanted to see this (not use it though) so that users can contribute to the nixpkgs plugin updater. Haven't seen much contribution from the usual contributors sadly.

@traxys
Copy link
Member

traxys commented Nov 30, 2023

@teto if you are interested I have a different approach for my plugin management:

@teto
Copy link

teto commented Nov 30, 2023

plugins repo uses their own updater so I better understand why they dont contribute to upstream. Would be interesting to compare the features of both and see if we can merge both approaches.

@traxys > I've seen that approach before. it's LGTM and to some extent, I wish we would leverage nix flake archive in the upstream updates like it's done for treesitter grammars already. Configuring neovim via nix is a bit tedious for most things so I use it only when the trade off is positive (plugins with complex dependencies and that) so I maintain a hybrid config with lazy to install hte other half of my plugins.

@koalagang
Copy link

@GaetanLepage I definitely agree that upstreaming plugins is ideal but surely users should have access to a way of installing plugins that haven't been upstreamed? If one wants a plugin immediately and said plugin is not in the nix repos then what would one do? Of course, you should then ask for this to be added and upstreamed so that all users can benefit but I don't think users should be forced to do this, especially considering that, like you rightfully said, those not on the unstable channel would have to wait for a whole new release to install those plugins. There should be some way of grabbing it straight from a git repo just like traditional vim plugin managers do. I think perhaps pkgs.fetchFromGitHub like how zsh's home-manager module does it could work well, though having to specify the revision and sha256 might be annoying if you regularly update your plugins (which I assume most Neovim users do).

Anyway, this might be diverging slightly from the topic of how Nixvim is going to handle lazy-loading.

@musjj
Copy link

musjj commented Nov 30, 2023

though having to specify the revision and sha256 might be annoying if you regularly update your plugins

If you're okay with having flakes as a requirement, you can use builtins.fetchTree instead. It allows you to fetch repos with only the commit hash.

But I do like the idea of generating lazy.nvim configuration though. Might be worth exploring.

@koalagang
Copy link

koalagang commented Nov 30, 2023

@musjj Haha I just discovered like 15 minutes after posting my comment that this is what flakes do 😅
I have my entire NixOS/home-manager setup inside a flake so no worries about adding that as a dependency for me personally, though perhaps others might be hesitant.

But yeah, again I like the idea of using lazy.nvim. It should possible be to do it with home-manager rather than with nixvim, which I am considering. If, instead of using programs.neovim.plugins, you use programs.neovim.extraLuaConfig then you could pass in a lua config that uses lazy.nvim but inside the dir option use ${pkgs.vimPlugins.yourplugin}.

So for example:

{ pkgs, ... }:

{
  programs.neovim.extraLuaConfig = ''
    -- this is lua code
    vim.opt.rtp:prepend(${pkgs.vimPlugins.lazy-nvim})
    require("lazy").setup({
        {
            name = "nvim-colorizer.lua",
            dir = "${pkgs.nvim-colorizer-lua}",
            cmd = "ColorizerToggle",
            config = function()
                vim.g.termguicolors = true,
                require("colorizer").setup()
            end,
        },
    })
 '';
}

Note that name is optional. It's just nice to have because otherwise the Lazy UI will show /nix/store/03k72h7x4m543z7vrqjwbvqjx6j789qg-vimplugin-nvim-colorizer.lua-2023-02-27 as the plugin's name.

The only difference between this and my suggestion earlier was basically that nixvim could do this but allowing the user to write it directly in nix rather than embedding lua code into a nix file.

P.s. I've never done this nor have I seen it being done. It's just something that, logically, I think would work.

@trueNAHO
Copy link
Contributor

There should be some way of grabbing it straight from a git repo just like traditional vim plugin managers do. I think perhaps pkgs.fetchFromGitHub like how zsh's home-manager module does it could work well, though having to specify the revision and sha256 might be annoying if you regularly update your plugins (which I assume most Neovim users do).

https://github.com/NixOS/nixpkgs/blob/76f642f87ff9b752ec652efc75234cf2fdc446aa/doc/languages-frameworks/vim.section.md?plain=1#L129-L167

@trueNAHO
Copy link
Contributor

If, instead of using programs.neovim.plugins, you use programs.neovim.extraLuaConfig then you could pass in a lua config that uses lazy.nvim but inside the dir option use ${pkgs.vimPlugins.yourplugin}.

P.s. I've never done this nor have I seen it being done. It's just something that, logically, I think would work.

Are plugin versions locked with lazy.nvim like with pure Nix?

@bmanuel
Copy link

bmanuel commented Nov 30, 2023

lazy.nvim has a json lockfile that stores the branch and commit for each dependency that it pulls via git. I'm not sure how it handles local directory dependencies.

@koalagang
Copy link

@trueNAHO

As @bmanuel rightfully said, if pulling via git then it will use its own lockfile implementation (lazy-lock.json). If you're pulling the plugins via nix as I demonstrated above, it will save this to your flake.lock if you're a flakes user. If you're using nix for installing the plugins but but you're not using a flake then there will be no lockfile.

@GaetanLepage
Copy link
Collaborator Author

@koalagang I agree, the goal is not to force the user to upstream packages.
Right now there exists two ways of using plugins in nixvim that are not packages in nixpkgs:

  • Inline buildPluginFromNix in your config to create a package for the missing plugins
  • Use the supported plugins.packer plugin manager to manage your plugins the traditional way (like you would do on another distro)

@trueNAHO
Copy link
Contributor

trueNAHO commented Dec 2, 2023

if pulling via git then it will use its own lockfile implementation (lazy-lock.json). If you're pulling the plugins via nix as I demonstrated above, it will save this to your flake.lock if you're a flakes user. If you're using nix for installing the plugins but but you're not using a flake then there will be no lockfile.

This sounds like we could encapsulate the entire lazy.nvim plugin as a Nixvim module without any reproducibility issues.

@MattSturgeon
Copy link
Collaborator

A lazy.nvim module sounds great, although I still think the original proposal of doing as much as possible in nix (at build time) is the ideal.

That said, perfection is the enemy of good...

@pta2002
Copy link
Collaborator

pta2002 commented Dec 4, 2023

Just popping back in here to add a datapoint for lazy.nvim:

We used to have a module for a popular nvim package manager (packer? I forget...). It's what was used before to install packages outside of Nix, but had some issues.

Either way, I'm not particularly opposed to having lazy.nvim as a module, but IMO that's orthogonal to this issue. We have the opportunity to have this done reproducibly at build time, and IMO we should try to do that.

I don't really see a reason not to have both.

@trueNAHO
Copy link
Contributor

trueNAHO commented Dec 4, 2023

I still think the original proposal of doing as much as possible in nix (at build time) is the ideal.

Indeed! Otherwise, IIRC lazy.nvim downloads and installs unavailable plugins only when launching nvim, which is opposed to Nix' build time. Personally, I prefer a fully reproducible all-inclusive Nix installation over a redeployable Nix installation that partially relies on bootstrapping.

I don't really see a reason not to have both.

We could trigger lazy.nvim's installation command during Nix' build time to ensure plugins are downloaded and installed at build time.

@pta2002
Copy link
Collaborator

pta2002 commented Dec 6, 2023

We could trigger lazy.nvim's installation command during Nix' build time to ensure plugins are downloaded and installed at build time.

Not sure that's easily done given Nix's sandboxing. However we can definitely look at how lazy.nvim works under the hood, and even try to use some of its code.

@musjj
Copy link

musjj commented Dec 6, 2023

@trueNAHO

Indeed! Otherwise, IIRC lazy.nvim downloads and installs unavailable plugins only when launching nvim, which is opposed to Nix' build time. Personally, I prefer a fully reproducible all-inclusive Nix installation over a redeployable Nix installation that partially relies on bootstrapping.

The original proposal was to use a nix config (with builtins.fetchTree, etc.) which is then converted into a lazy.nvim config. The plugins will be retrieved by nix itself, so no run-time bootstrap will be required.

Though I do agree that a pure nix solution would be the most ideal.

@koalagang
Copy link

koalagang commented Dec 6, 2023

@pta2002

I'd like to clarify the idea more explicitly. The proposal with lazy.nvim was not for it to install plugins (although that certainly could be exposed as an option for those who want it). Plugins can be installed by nix at build time (including lazy.nvim itself using pkgs.vimPlugins.lazy-nvim) without the help of lazy. So you might then ask "well what's the point in a plugin manager if you're not using it to install plugins?". The reason to do this is that lazy.nvim does more than just installing plugins. It allows the user to configure Neovim to only load plugins into memory after particular conditions are met, e.g. load Neorg when opening .norg files; load hop.nvim when you press the f key; load oil.nvim when you use the :Oil command; etc.

Additionally, lazy.nvim's config option allows you to pass in configurations specific to the plugins and these configurations will only be loaded when the plugins are loaded. We could actually do this in a nice nix way by having the nixvim plugin options people use placed into this at build time.

For example, if the user sets plugins.nvim-colorizer.fileTypes to css and javascript then the following would be generated:

{
    name = "nvim-colorizer",
    -- path to plugin in the nix store (installed at build time)
    dir = "/nix/store/03k72h7x4m543z7vrqjwbvqjx6j789qg-vimplugin-nvim-colorizer.lua-2023-02-27",
    config = function()
      -- config generated by nixvim's nvim-colorizer module
      require 'colorizer'.setup {
        'css';
        'javascript';
      }
    end,
},

As you can see, we're installing the plugin using nix and then just pointing lazy.nvim to the location of the plugin in the nix store so that it can handle the lazy-loading and we're generating the plugin's lua configuration at build time. If the user, for instance, wants to lazy-load it with the :ColorizerToggle command, we could expose lazy's cmd option to nix. Perhaps the user can set plugins.nvim-colorizer.cmd = "ColorizerToggle" and then this will be added in the appropriate part of the generated lua file.

I'm not sure if there's a more pure nix way of doing it since you'll have to generate the lua code anyway as Neovim doesn't natively understand nix but I agree with everyone that we should use as much nix as possible.

@pta2002
Copy link
Collaborator

pta2002 commented Dec 6, 2023

I'm not sure if there's a more pure nix way of doing it since you'll have to generate the lua code anyway as Neovim doesn't natively understand nix but I agree with everyone that we should use as much nix as possible.

I'm not really sure there's any need for that in that case. Seems like we're just using lazy.nvim as a library at this point, which I have no real objection to, though in that case it might be better to fork it ourselves and create a more special-purpose thing.

Still though, as was said before, perfect is the enemy of good. If we go with this approach I don't really have any objections, especially considering it can be spun off later fairly transparently to the end-user.

IMO the course of action for this would therefore be:

  1. Create a lazy.nvim module.
  2. Refactor plugins to define a plugin's configuration next to its requirement (see: Allow defining setup options in extraPlugins (WIP) #365)
  3. Add an experimental flag to load plugins via lazy.nvim
  4. After a while, make that the default
  5. Eventually, extract the required code out of lazy.nvim

CC: @nix-community/nixvim. If everyone agrees, I can create a project board and the required issues.

@MattSturgeon
Copy link
Collaborator

Perhaps we could have a "loading method" option which is an enumeration of:

  • default - use vim's default plugin loading
  • lualoader - replaces the existing lualoader.enable option
  • lazy.nvim - use lazy.nvim to load plugins
  • lazy.nixvim (placeholder name) - use custom lazy loading, mostly defined at build time
  • OTHER - any other loading system that comes into favour in the future course be supported without introducing a new toggle

This approach means we have a central way of selecting how plugins are loaded, seeing as (I assume) each approach will conflict the others.

Alternatively, these could each be separate enable options, and we could panic if more than one is enabled.

The proposed cmd plugin option could be documented as "only support when loading.method is set to lazy.nvim or lazy.nixvim". A trace warning could be printed if used without a supported loading method.

Having a centralised way of specifying the loading backend means supporting one method (lazy.nvim) does not block later supporting another.

@pta2002
Copy link
Collaborator

pta2002 commented Dec 6, 2023

I agree with the enum, IMO it's the best option. Removes a lot of need for assertions.

@koalagang
Copy link

koalagang commented Dec 7, 2023

@pta2002

I'm not really sure there's any need for that in that case. Seems like we're just using lazy.nvim as a library at this point, which I have no real objection to, though in that case it might be better to fork it ourselves and create a more special-purpose thing.

Entirely possible, though as you stated further down in the comment, we should probably wait until later before forking. I'd suggest taking a look at lazy's plugin spec for ideas. The most important options for lazy-loading imho are cond, dependencies, config, event, cmd, ft and keys.

From lazy.nvim's README:

[Dependenices are a] list of plugin names or plugin specs that should be loaded when the plugin loads. Dependencies are always lazy-loaded unless specified otherwise. When specifying a name, make sure the plugin spec has been defined somewhere else.

Note that, traditionally, the user has to manually mark plugins as dependencies but you could perhaps make nixvim aware of what plugins are dependencies of other plugins. I would suggest also making it possible to manually mark dependencies though as this can be useful in some situations where a plugin isn't necessarily an actual dependency of another. For example, in my config I have oil.nvim marked as a dependency of telescope-zoxide so that oil loads when I load telescope-zoxide. You could argue that this is a bit of a hack, though, and that this should be handled differently.

Useful lazy.nvim options that are unrelated to lazy-loading but more so about how to install plugins include build, branch, tag, commit and version, though I'm not sure how this would be handled in a nix fashion. I don't believe nixvim could make use of these lazy.nvim options if we're installing plugins with nix but nix has its own options for these, doesn't it? (Again, I'm quite new to nix yet so excuse my ignorance.) It is important to implement these somehow, though. build is important for build options, though this is usually something like compiling treesitter parsers or installing a nodejs package, which can all just be installed by nix at build time. However, it should perhaps be made possible to use even for packages not available in the nix repos, such as for installing telescope-fzf-native's C implementation of the fzf algorithm. branch, tag and version are important for choosing particular versions of a plugin; for example, telescope's main branch is used for development and is, thus not as stable so it's recommended to use the 0.1.x branch.

Edit: come to think of it, maybe these options are handled by upstream, at least branch, tag, commit and version.

@MattSturgeon

This approach means we have a central way of selecting how plugins are loaded, seeing as (I assume) each approach will conflict the others.

Yup. It would also modularise and future-proof the project more because you can really easily just keep adding more methods if you want to / if particular methods are requested.

Also, remember that, regardless of what method the user goes for, you should set the colourscheme plugin to be the first plugin (other than the plugin manager itself) to load to avoid race conditions where some plugins are loaded before the colourscheme, causing them not to have the right colours. With lazy.nvim this can be done by setting priority = 1000 for the colourscheme plugin.

@trueNAHO
Copy link
Contributor

trueNAHO commented Dec 8, 2023

I'm not really sure there's any need for that in that case. Seems like we're just using lazy.nvim as a library at this point, which I have no real objection to, though in that case it might be better to fork it ourselves and create a more special-purpose thing.

Entirely possible, though as you stated further down in the comment, we should probably wait until later before forking.

Keeping a special-purpose lazy.nixvim (name subject to change) fork introduces an additional layer of maintainance. Unless this adds tremendous value, we might want to avoid forking it.

Alternatively, we could try to merge our required features into lazy.nvim. Although, it seems doubtful whether lazy.nvim should support alternative frameworks in the first place.

@MattSturgeon
Copy link
Collaborator

Keeping a special-purpose lazy.nixvim (name subject to change) fork introduces an additional layer of maintainance. Unless this adds tremendous value, we might want to avoid forking it.

That item in the enum wasn't supposed to hint at a fork (poor naming on my part).

Rather it was supposed to represent the original proposal of this issue; lazy loading without using a plugin management plugin, or in other words a solution implemented at build time.

As has been discussed above, this should be possible with minimal lua code. It'll just take a fair bit more time & effort than a solution that utilizes lazy.nvim

lazy.nvim [has] options that are unrelated to lazy-loading but more so about how to install plugins, [...] though I'm not sure how this would be handled in a nix fashion.

All installation should be handled by nix, not at runtime. Therefore these options wouldn't be used. Instead the dir option would be used to tell lazy.nvim where nix installed the plugin. As per this comment and this one.

@koalagang
Copy link

Rather it was supposed to represent the original proposal of this issue; lazy loading without using a plugin management plugin, or in other words a solution implemented at build time.

But what do you mean by lazy-loading at build time? You need a plugin of some sorts to listen for input (such as commands, keys, etc.) whilst the user is using Neovim. That's not something you can do at build time. Do you mean like generating autocmds?

lazy.nvim [has] options that are unrelated to lazy-loading but more so about how to install plugins, [...] though I'm not sure how this would be handled in a nix fashion.

All installation should be handled by nix, not at runtime. Therefore these options wouldn't be used. Instead the dir option would be used to tell lazy.nvim where nix installed the plugin. As per this comment and this one.

Yes, I agree that it should be handled by nix. The point I was making was not that we should use those lazy.nvim options but I was just suggesting that perhaps the user being able to choose, for example what branch they want, could perhaps be a good thing. This would likely require them to use buildPluginFromNix though if they wanted to change it, as upstream nixpkgs chooses the branch.

Anyway, regardless of which route nixvim ultimately takes, it might be best to start with a lazy.nvim module that provides all of its options and then aim to develop an in-house method later down the line if it's deemed necessary.

@pta2002
Copy link
Collaborator

pta2002 commented Dec 9, 2023

Seeing how this is going, I'm going ahead and creating an issue for lazy.nvim to be added as a module.

Even if we end up going with another route, it's still a nice to have.

@koalagang
Copy link

@pta2002 I take it that all new discussion regarding lazy.nvim should happen in #797 now but this issue will remain open for nixvim's in-house lazy-loading implementation then?

@pta2002
Copy link
Collaborator

pta2002 commented Dec 9, 2023

Yes!

@koalagang
Copy link

Btw, in regards to lazy.nixvim (I'm assuming that's the placeholder name we're going with for nixvim's in-house lazy-loading) do you believe it would make sense to have some sane defaults? I'm thinking perhaps there could be some option like lazy.saneDefaults for this which is false by default so that users don't experience unexpected behaviour but it could help for those who aren't experienced with configuring lazy-loading or can't be bothered to configure it (which there seems to be a lot of judging by how many dotfiles I've seen which load all their plugins on startup). For example, setting Neorg to load on .norg filetypes or when using :Neorg, as well as loading nvim-cmp on the InsertEnter event (when you enter insert mode) seems sensible to me.

I know this feature isn't exactly the next step in the plan but I figured I may as well post it here now since I just had the thought and perhaps you could look back on the issue later down the line.

@pta2002
Copy link
Collaborator

pta2002 commented Dec 9, 2023

When it comes to the sane defaults, yeah, not really oposed to that. Honestly I think some plugins already sort of do that, with an option to disable it per-plugin, so maybe we could do it that way. Don't know, though.

@GaetanLepage
Copy link
Collaborator Author

I have just came across this: https://github.com/b-src/lazy-nix-helper.nvim
I haven't really looked at what it does though.

@MattSturgeon
Copy link
Collaborator

MattSturgeon commented Jan 13, 2024

I have just came across this: https://github.com/b-src/lazy-nix-helper.nvim I haven't really looked at what it does though.

It is a neovim plugin that provides a function to search the /nix/store for a specified neovim plugin.

Essentially you set dir = require("lazy-nix-helper").get_plugin_path("my-cool-plugins-dep") in your lazy.nvim config and get_plugin_path will try and find my-cool-plugins-dep in the nix store.

If get_plugin_path can't find my-cool-plugins-dep, it'll return nil and lazy.nvim will install the plugin itself.

If my-cool-plugins-dep was found, then get_plugin_path returns the path where it is installed. lazy.nvim will use that instead of installing the plugin itself.

All this happens at runtime (in lua), so it isn't the approach nixvim would take. It is useful for people who want to maintain a neovim config that can be used with or without a nix installation.

@GaetanLepage
Copy link
Collaborator Author

Ok, I see. Thank you for the explanation.
Not really what we look for, because we would know in advance the paths to the different plugins.

@ck3mp3r
Copy link
Contributor

ck3mp3r commented Feb 23, 2024

I've been experimenting after finding this: https://github.com/azuwis/lazyvim-nixvim/blob/master/flake.nix.
It uses the full blown lazy distro though, which isn't what I am interested in. I've gotten a PoC working that loads the plugins via lazy, also added the ability to add a setup config that can be passed on, but, I cannot use any of the prebuilt configuration options as they need the plugin activated, which then uses the default plugin loading mechanism, which defeats the purpose... who'd be willing to walk me through the setup of this project so I can determine where the best place would be to add a configurable hook that allows overriding the default loading mechanism?

@ck3mp3r
Copy link
Contributor

ck3mp3r commented Feb 24, 2024

Have done some more digging through the code base, I'm assuming this is where we could hook into ensuring only the plugin-manager is passed on. Obviously we'd have to then still use the list to ensure the configurations are applied however the chosen plugin-manager needs them...

@GaetanLepage
Copy link
Collaborator Author

GaetanLepage commented Mar 26, 2024

Recently we introduced new helpers to create plugin modules (mkVimPlugin / mkNeovimPlugin).
Although it was not the primary intent, they will surely make it easier to draft an implementation of lazy loading.
Indeed, it now gets quite easy to isolate the configuration of each plugin and to add new options to all plugins directly (lazyLoad.enable, lazyLoad.event...).

@pkulak
Copy link

pkulak commented Apr 14, 2024

So... it looks like to use the new lazy module we ("we" being me, someone just using nixvim who doesn't really know what's going on) have to re-do how we setup plugins so they can be loaded with lazy? Is that something I should be doing, or is it going to change soon and we're in kindof an interim state at the moment?

@MattSturgeon
Copy link
Collaborator

MattSturgeon commented Apr 14, 2024

to use the new lazy module we have to re-do how we setup plugins?

Currently, yes.

Is that something I should be doing,

If you want to try out lazy loading now, yes. If you don't like re-doing your config, no.

or is it going to change soon and we're in kindof an interim state at the moment?

The plan is to add a lazy options set to all the plugin modules. When that'll happen is up in the air. Currently nobody has enough time/interest, but hopefully soon someone will.

@Eveeifyeve
Copy link

to use the new lazy module we have to re-do how we setup plugins?

Currently, yes.

Is that something I should be doing,

If you want to try out lazy loading now, yes. If you don't like re-doing your config, no.

or is it going to change soon and we're in kindof an interim state at the moment?

The plan is to add a lazy options set to all the plugin modules. When that'll happen is up in the air. Currently nobody has enough time/interest, but hopefully soon someone will.

I had an idea what if lazy.nvim had an option that affects all plugins to lazy load the option could be programs.lazynvim.loadlazy

@GaetanLepage
Copy link
Collaborator Author

This library could be useful: https://github.com/nvim-neorocks/lz.n

@hbjydev
Copy link
Contributor

hbjydev commented Jun 19, 2024

I had an idea what if lazy.nvim had an option that affects all plugins to lazy load the option could be programs.lazynvim.loadlazy

I was just thinking this actually; if I could just enable it then set lazy loading options based on filetype or something and then just enable plugins.lazy and have all the plugins automatically use lazy.nvim, that'd be awesome.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
enhancement New feature or request
Projects
None yet
Development

No branches or pull requests