Bundling all treesitter grammar in neovim while using lazy.nvim


{
  description = "Basic flake with home-manager for testing";

  inputs = {
    nixpkgs.url = "github:nixos/nixpkgs/?rev=9ef261221d1e72399f2036786498d78c38185c46";
    home-manager = {
      url = "github:nix-community/home-manager/?rev=34578a2fdfce4257ce5f5baf6e7efbd4e4e252b1";
      inputs.nixpkgs.follows = "nixpkgs";
    };
  };

  outputs = { self, nixpkgs, home-manager }:
    let
      system = "x86_64-linux";
      pkgs = nixpkgs.legacyPackages.${system};
    in
    {
      homeConfigurations."testuser" = home-manager.lib.homeManagerConfiguration {
        inherit pkgs;

        modules = [
          {
            home.username = "testuser";
            home.homeDirectory = "/home/testuser";
            home.stateVersion = "25.11";
            programs.home-manager.enable = true;

            programs.neovim = {
              enable = true;
              plugins = [
                pkgs.vimPlugins.nvim-treesitter.withAllGrammars
              ];
            };

          }
        ];
      };
    };
}

With the previous flake, the following line seems to make no difference in the derivation

              plugins = [
                pkgs.vimPlugins.nvim-treesitter.withAllGrammars
              ];

I tried look for the parsers and/or grammars in the result directory but it seems that only the default ones are there. I’m trying to bundle all of them but apparently it’s not working or I’m not verifying correctly.

Was anyone able to bundle treesitter in neovim without hacky ways?

I’ve used the following links as documentation:

Grammars are not listed for me in the checkheakth vim.treesitter either, but they work.

Depending on the age of the nixpkgs input, you’ll have to manually start treesitter per supported filetype and buffer.

Yes, apparently it bundles using the wrapper script parameters, not changing the file tree of the nvim derivation.

In ./result/home-path/bin/nvim:

exec "/nix/store/40v0hm4s05y3zss22jdn7y073ahdkin8-neovim-unwrapped-0.11.5/bin/nvim"  --cmd "lua vim.g.loaded_node_provider=0;vim.g.loaded_perl_provider=0;vim.g.loaded_python_provider=0;vim.g.python3_host_prog='/nix/store/0iirl9qklhxj1faw9j9mwjn2fcs2qddz-neovim-0.11.5/bin/nvim-python3';vim.g.ruby_host_prog='/nix/store/0iirl9qklhxj1faw9j9mwjn2fcs2qddz-neovim-0.11.5/bin/nvim-ruby'" --cmd "set packpath^=/nix/store/2v1q6blbrfcvqjj8p0c6kn4qs2qsfanh-vim-pack-dir" --cmd "set rtp^=/nix/store/2v1q6blbrfcvqjj8p0c6kn4qs2qsfanh-vim-pack-dir" "$@" 

this should include the grammars:

--cmd "set packpath^=/nix/store/2v1q6blbrfcvqjj8p0c6kn4qs2qsfanh-vim-pack-dir"

since:

$ tree -L 4 -l /nix/store/2v1q6blbrfcvqjj8p0c6kn4qs2qsfanh-vim-pack-dir

/nix/store/2v1q6blbrfcvqjj8p0c6kn4qs2qsfanh-vim-pack-dir/
├── nix-support/
└── pack -> /nix/store/cljbxcy9m65i9lv35hkvpfybsk2ihpqi-packdir-start/pack/
    └── myNeovimPackages/
        └── start/
            ├── nvim-treesitter -> /nix/store/079925zfwk07rk80qn8vs448167109nc-vimplugin-nvim-treesitter-2025-05-24/
            ├── vimplugin-treesitter-grammar-ada -> /nix/store/qqx5vinjy1jngspaiynsv6db0v7l62jk-vimplugin-treesitter-grammar-ada/
            ├── vimplugin-treesitter-grammar-agda -> /nix/store/lb13ifb9q9kywmfccfq742xjcwda4ngj-vimplugin-treesitter-grammar-agda/
            ├── vimplugin-treesitter-grammar-angular -> /nix/store/shwh58pdmi22mnb16l3r1lf3f0ci5hxg-vimplugin-treesitter-grammar-angular/
            ├── vimplugin-treesitter-grammar-apex -> /nix/store/kifbliphnrhs5k1y171nf4063cjgb56y-vimplugin-treesitter-grammar-apex/
            ├── vimplugin-treesitter-grammar-arduino -> /nix/store/bib15bi4pqwzsh4qax1ppnmcm3vh1bsc-vimplugin-treesitter-grammar-arduino/
            ├── vimplugin-treesitter-grammar-asm -> /nix/store/j65254dp241dyzpv4f07gbacfg3fc4d9-vimplugin-treesitter-grammar-asm/

# ...

There is one strange thing, though.

When using neovim without any user config (vim -u NONE), the output of :checkhealth vim.treesitter shows all grammars from this store path, as expected. When I load neovim with my config, the output shows as if I had only the default ones. If I comment the lazy plugin manager config (require(‘lazy’).setup(…)), I get the store paths back again, though no lazy plugin.

I guess this could be due to some lazy.nvim hackery but I’m not sure if it’s harmless with this setup.

Ok, so apparently lazy.nvim takes over the plugin loading and prevents treesitter grammars to be loaded from the wrapper cli. If you have a working setup with lazy.nvim that may be due to a previous TSInstall command. There are a bunch of issues with similar situations:

finally, the last one provided a workaround that has a performance impact:

require("lazy").setup({}, {
  performance = {
    reset_packpath = false,
  },
})

testing it with nvim --startuptime log.log did show a noticeable startup time difference, from ~80ms to ~260ms.

Depending on your neovim/nix config you can overcome this through different approaches. One I was able to test it was inserting the grammars in the config directory under parser, like:

    xdg.configFile = {

      "nvim/parser" = {
        source = let parsers = pkgs.symlinkJoin {
          name = "treesitter-parsers";
          paths = pkgs.vimPlugins.nvim-treesitter.withAllGrammars.dependencies;
        };
        in "${parsers}/parser";
      };
    }

In my case, I also mkOutOfStoreSymlink the other files/directories under .config/nvim to my lua dotfiles but that’s just my approach.