nix-community / nixvim

Configure Neovim with Nix! [maintainers=@GaetanLepage, @traxys, @mattsturgeon, @khaneliman]
https://nix-community.github.io/nixvim
MIT License
1.7k stars 262 forks source link

[BUG] Spellchecking requires the "site" dir to be on the runtimepath #2297

Closed carlhammann closed 3 weeks ago

carlhammann commented 3 weeks ago
Field Description
Plugin treesitter, spell
Nixpkgs unstable

Description

The presence of treesitter seems to affect German (non-English in general?) spell checking: When the plugin is absent, the spell checker fails to find (or download) word lists.

In detail: Setting opts.spell = true and opts.spelllang = ["de"] in an otherwise empty configuration, I expect German spell checking to be enabled everywhere. However, when I open any file, I get, in order, the following messages (and spell checking does not work):

spellfile#LoadFile(): No (writable) spell directory found.
Press ENTER or type command to continue
Failed to create: /home/carl/.local/share/nvim/site/spell
Press ENTER or type command to continue
Warning: Cannot find word list "de.utf-8.spl" or "de.ascii.spl"
Press ENTER or type command to continue

Now, the funny bit: If I set plugins.treesitter.enable = true, the problem disappears.

I haven't investigated this further. Maybe this has something to do with the fact that treesitter may depend on files outside the nix store (grammars installed with TSInstall), and therefore "opens the door", which was forgotten for spelling, because of the prevalence of English-only configs?

Minimal, Reproducible Example (MRE)

Using the following flake:

{
  inputs.nixvim.url = "github:nix-community/nixvim";
  outputs = {nixvim, ...}: let
    system = "x86_64-linux";
  in {
    packages.${system} = with nixvim.legacyPackages.${system}; {
      bad = makeNixvim {
        opts.spell = true;
        opts.spelllang = ["de"];
      };
      good = makeNixvim {
        opts.spell = true;
        opts.spelllang = ["de"];
        plugins.treesitter.enable = true;
        plugins.treesitter.grammarPackages = []; # just to save some bandwidth; doesn't change the outcome
      };
    };
  };
}
flake.lock ```json { "nodes": { "devshell": { "inputs": { "nixpkgs": [ "nixvim", "nixpkgs" ] }, "locked": { "lastModified": 1722113426, "narHash": "sha256-Yo/3loq572A8Su6aY5GP56knpuKYRvM2a1meP9oJZCw=", "owner": "numtide", "repo": "devshell", "rev": "67cce7359e4cd3c45296fb4aaf6a19e2a9c757ae", "type": "github" }, "original": { "owner": "numtide", "repo": "devshell", "type": "github" } }, "flake-compat": { "locked": { "lastModified": 1696426674, "narHash": "sha256-kvjfFW7WAETZlt09AgDn1MrtKzP7t90Vf7vypd3OL1U=", "rev": "0f9255e01c2351cc7d116c072cb317785dd33b33", "revCount": 57, "type": "tarball", "url": "https://api.flakehub.com/f/pinned/edolstra/flake-compat/1.0.1/018afb31-abd1-7bff-a5e4-cff7e18efb7a/source.tar.gz" }, "original": { "type": "tarball", "url": "https://flakehub.com/f/edolstra/flake-compat/1.tar.gz" } }, "flake-parts": { "inputs": { "nixpkgs-lib": [ "nixvim", "nixpkgs" ] }, "locked": { "lastModified": 1726153070, "narHash": "sha256-HO4zgY0ekfwO5bX0QH/3kJ/h4KvUDFZg8YpkNwIbg1U=", "owner": "hercules-ci", "repo": "flake-parts", "rev": "bcef6817a8b2aa20a5a6dbb19b43e63c5bf8619a", "type": "github" }, "original": { "owner": "hercules-ci", "repo": "flake-parts", "type": "github" } }, "flake-utils": { "inputs": { "systems": "systems" }, "locked": { "lastModified": 1710146030, "narHash": "sha256-SZ5L6eA7HJ/nmkzGG7/ISclqe6oZdOZTNoesiInkXPQ=", "owner": "numtide", "repo": "flake-utils", "rev": "b1d9ab70662946ef0850d488da1c9019f3a9752a", "type": "github" }, "original": { "owner": "numtide", "repo": "flake-utils", "type": "github" } }, "git-hooks": { "inputs": { "flake-compat": [ "nixvim", "flake-compat" ], "gitignore": "gitignore", "nixpkgs": [ "nixvim", "nixpkgs" ], "nixpkgs-stable": [ "nixvim", "nixpkgs" ] }, "locked": { "lastModified": 1726745158, "narHash": "sha256-D5AegvGoEjt4rkKedmxlSEmC+nNLMBPWFxvmYnVLhjk=", "owner": "cachix", "repo": "git-hooks.nix", "rev": "4e743a6920eab45e8ba0fbe49dc459f1423a4b74", "type": "github" }, "original": { "owner": "cachix", "repo": "git-hooks.nix", "type": "github" } }, "gitignore": { "inputs": { "nixpkgs": [ "nixvim", "git-hooks", "nixpkgs" ] }, "locked": { "lastModified": 1709087332, "narHash": "sha256-HG2cCnktfHsKV0s4XW83gU3F57gaTljL9KNSuG6bnQs=", "owner": "hercules-ci", "repo": "gitignore.nix", "rev": "637db329424fd7e46cf4185293b9cc8c88c95394", "type": "github" }, "original": { "owner": "hercules-ci", "repo": "gitignore.nix", "type": "github" } }, "home-manager": { "inputs": { "nixpkgs": [ "nixvim", "nixpkgs" ] }, "locked": { "lastModified": 1726902823, "narHash": "sha256-Gkc7pwTVLKj4HSvRt8tXNvosl8RS9hrBAEhOjAE0Tt4=", "owner": "nix-community", "repo": "home-manager", "rev": "14929f7089268481d86b83ed31ffd88713dcd415", "type": "github" }, "original": { "owner": "nix-community", "repo": "home-manager", "type": "github" } }, "nix-darwin": { "inputs": { "nixpkgs": [ "nixvim", "nixpkgs" ] }, "locked": { "lastModified": 1726742753, "narHash": "sha256-QclpWrIFIg/yvWRiOUaMp1WR+TGUE9tb7RE31xHlxWc=", "owner": "lnl7", "repo": "nix-darwin", "rev": "c03f85fa42d68d1056ca1740f3113b04f3addff2", "type": "github" }, "original": { "owner": "lnl7", "repo": "nix-darwin", "type": "github" } }, "nixpkgs": { "locked": { "lastModified": 1726755586, "narHash": "sha256-PmUr/2GQGvFTIJ6/Tvsins7Q43KTMvMFhvG6oaYK+Wk=", "owner": "NixOS", "repo": "nixpkgs", "rev": "c04d5652cfa9742b1d519688f65d1bbccea9eb7e", "type": "github" }, "original": { "owner": "NixOS", "ref": "nixos-unstable", "repo": "nixpkgs", "type": "github" } }, "nixvim": { "inputs": { "devshell": "devshell", "flake-compat": "flake-compat", "flake-parts": "flake-parts", "git-hooks": "git-hooks", "home-manager": "home-manager", "nix-darwin": "nix-darwin", "nixpkgs": "nixpkgs", "nuschtosSearch": "nuschtosSearch", "treefmt-nix": "treefmt-nix" }, "locked": { "lastModified": 1727050833, "narHash": "sha256-uq8pP+WsJsZ3QA+GoSftUsAvhx3btBOCWAqKQSFcL1M=", "owner": "nix-community", "repo": "nixvim", "rev": "a9345dcfc31519734361fecd246d32164feafbca", "type": "github" }, "original": { "owner": "nix-community", "repo": "nixvim", "type": "github" } }, "nuschtosSearch": { "inputs": { "flake-utils": "flake-utils", "nixpkgs": [ "nixvim", "nixpkgs" ] }, "locked": { "lastModified": 1726816132, "narHash": "sha256-AbB0lgc0IbzLIxj1O3cosiMNAVQak4KJtvq9q8MjHhs=", "owner": "NuschtOS", "repo": "search", "rev": "7733a39a1321057172d87e6251ded7cdeb67171e", "type": "github" }, "original": { "owner": "NuschtOS", "repo": "search", "type": "github" } }, "root": { "inputs": { "nixvim": "nixvim" } }, "systems": { "locked": { "lastModified": 1681028828, "narHash": "sha256-Vy1rq5AaRuLzOxct8nz4T6wlgyUR7zLU309k9mBC768=", "owner": "nix-systems", "repo": "default", "rev": "da67096a3b9bf56a91d16901293e51ba5b49a27e", "type": "github" }, "original": { "owner": "nix-systems", "repo": "default", "type": "github" } }, "treefmt-nix": { "inputs": { "nixpkgs": [ "nixvim", "nixpkgs" ] }, "locked": { "lastModified": 1726734507, "narHash": "sha256-VUH5O5AcOSxb0uL/m34dDkxFKP6WLQ6y4I1B4+N3L2w=", "owner": "numtide", "repo": "treefmt-nix", "rev": "ee41a466c2255a3abe6bc50fc6be927cdee57a9f", "type": "github" }, "original": { "owner": "numtide", "repo": "treefmt-nix", "type": "github" } } }, "root": "root", "version": 7 } ```
carlhammann commented 3 weeks ago

It's indeed as I conjectured above: The spell directory lives under ~/.local/share/nvim/site/, which is normally removed from the runtimepath, but treesitter adds it back. The latter fact is also documented here.

I don't know what the solution™ is; for me, setting

extraConfigLua = ''vim.opt.runtimepath:prepend(vim.fs.joinpath(vim.fn.stdpath("data"), "site"))'';

breaks nothing.

What's the rationale behind removing the [standard user data directory](https://neovim.io/doc/user/builtin.html#stdpath()) from the runtimepath, isn't the stuff in there volatile anyway? A superficial grep finds only examples of plugins using stdpath("data") for database-like things.

MattSturgeon commented 3 weeks ago

cc @GaetanLepage this is one of the edge-cases I was worried our current "pure rtp when wrapRC is enabled" implementation would lead to.

Do you recall if we had any real-world issues with impurities coming from the "site" directory or was that just included in the rtp-purification on the assumption it'd be needed?

Personally I think we should:

  1. de-couple purifying the rtp from wrapping the init file
  2. not remove "site" even when purifying (at least by default)
traxys commented 3 weeks ago

I think most issues we had was superfluous init.lua files

GaetanLepage commented 3 weeks ago

Yes, I also faced issues with spellfiles on my end... I am OK with a splitting as you suggest. The purity claim was a bit ambitious as I was not doing anything exhaustive to prevent file creation in the user home directory.

MattSturgeon commented 3 weeks ago

Hm, maybe we can have something like:

isolatedRuntime = mkOption {
  type = types.bool;
  default = config.wrapRc;
  defaultText = literalExpression "config.wrapRc";
  description = ''
    Whether to isolate vim's runtimepath by removing impure directories.

    Recommended when using `wrapRc`.

    The following directories will be removed:
    - ~/.config/nvim
    - ~/.config/nvim/after

    > [!NOTE]
    > ~/.local/share/nvim/site will **not** be removed for compatibility reasons
  '';
}