diff --git a/README.md b/README.md index 068d704..b6e18fb 100644 --- a/README.md +++ b/README.md @@ -198,7 +198,8 @@ projectSettings = { ### Language packages (module defaults) -Add Python/R/Julia libraries that get appended to the module defaults. The built-in defaults are: +Add Python/R/Julia libraries. These are appended to the built-in defaults, which +always apply (see `settings.langPackageDefaults` in `modules/module/settings/lang-packages.nix`): - Python: `duckdb`, `polars` - R: `arrow`, `broom`, `data_table`, `janitor`, `styler` @@ -214,7 +215,8 @@ projectSettings = { }; ``` -Use `lib.mkForce` to replace rather than append: +Use `lib.mkForce` to replace other `lang_packages` assignments; the built-in defaults still apply. +To exclude defaults, override `catPkgs.` instead: ```nix projectSettings = { @@ -337,5 +339,5 @@ cats.nix ───────────────► config.cats │ - **Overlays** (`overlays/`) inject dependency package sets into nixpkgs (`rpkgs`, `baseRPackages`, `basePythonPackages`, plugins). Top-level `rWrapper` and `quarto` are compatibility conveniences, but `pkgs.rpkgs` is the canonical R surface for downstream configuration. - **cat-packages.nix** is the single source of truth for per-category packages. Each list is gated by its cat toggle. - **deps.nix** wires `catPkgs` into the wrapper's runtime PATH. -- **settings.lang_packages** holds the shared defaults used by local outputs and downstream module consumers. +- **settings.lang_packages** holds project-specific language libraries appended to the built-in `settings.langPackageDefaults`, used by local outputs and downstream module consumers. - **flake.nix** exports `lib.eval`, `lib.mkWrapper`, `lib.devShellPackages`, and `lib.shellHook` as the canonical downstream helpers, and builds `packages.default` and `devShells.default` from the same module config. diff --git a/flake.lock b/flake.lock index 57e58b8..0f12e11 100644 --- a/flake.lock +++ b/flake.lock @@ -22,11 +22,11 @@ }, "nixpkgs": { "locked": { - "lastModified": 1786384358, - "narHash": "sha256-RzPPiWeUtuvymnpuEWsdtzli5w4kjZs49FqEs3/1u+I=", + "lastModified": 1787498568, + "narHash": "sha256-9i/VTdusq/+NM/tz+J1Re+ojkMB8MBf0QshnYfzHz30=", "owner": "nixos", "repo": "nixpkgs", - "rev": "2fcb964de67fcf60b43471c55d5d99e61a9ccb5a", + "rev": "56c02bc00adcf003215cc4bd996d6efaf4cff188", "type": "github" }, "original": { @@ -36,22 +36,6 @@ "type": "github" } }, - "plugins-bloocky": { - "flake": false, - "locked": { - "lastModified": 1786453931, - "narHash": "sha256-OpVm/ubj6rxjGFkjluQLhys/qGWqmN3E7dUMiFUYAZw=", - "owner": "atiladefreitas", - "repo": "bloocky", - "rev": "64a6de6560c40b3351d29545528ddaed98cd9984", - "type": "github" - }, - "original": { - "owner": "atiladefreitas", - "repo": "bloocky", - "type": "github" - } - }, "plugins-cmp-pandoc-references": { "flake": false, "locked": { @@ -68,22 +52,6 @@ "type": "github" } }, - "plugins-dooing": { - "flake": false, - "locked": { - "lastModified": 1786125118, - "narHash": "sha256-Iil8eCCz61T1Hz1y2cij71Hti+QGXbjH49eGCPMqTbA=", - "owner": "atiladefreitas", - "repo": "dooing", - "rev": "2871aaf400c49187aa4216e7cf7abfa37cd32840", - "type": "github" - }, - "original": { - "owner": "atiladefreitas", - "repo": "dooing", - "type": "github" - } - }, "r-nvim-nix": { "inputs": { "nixpkgs": [ @@ -108,11 +76,11 @@ }, "rixpkgs": { "locked": { - "lastModified": 1786421894, - "narHash": "sha256-BQLZ0kJYdZ3FmC2Vq7PJFmx4wnyifoCdz+5CIkdbDxI=", + "lastModified": 1787585894, + "narHash": "sha256-tp/kigWpoz0DUyDmbqVHLjZova3n98vn7R7HTRtjeWg=", "owner": "dwinkler1", "repo": "rixpkgs", - "rev": "9518d16a41413cf08eecde557b337b87f1422e91", + "rev": "5a76f5802a7728c62feff1399c7302b53276ed8f", "type": "github" }, "original": { @@ -143,9 +111,7 @@ "inputs": { "fran": "fran", "nixpkgs": "nixpkgs", - "plugins-bloocky": "plugins-bloocky", "plugins-cmp-pandoc-references": "plugins-cmp-pandoc-references", - "plugins-dooing": "plugins-dooing", "r-nvim-nix": "r-nvim-nix", "rixpkgs": "rixpkgs", "wrappers": "wrappers" @@ -158,11 +124,11 @@ ] }, "locked": { - "lastModified": 1782135443, - "narHash": "sha256-vAmbArdCyjqpVW+37aCy/PMBOLIqukUXLQuEKLwUhA4=", + "lastModified": 1787579952, + "narHash": "sha256-Dn6IPaLyltx8hBTyC4hgNWDb7T7WHRYUG6wWh6KBHXA=", "owner": "BirdeeHub", "repo": "nix-wrapper-modules", - "rev": "6e7f66fa2cdf4d63162580b438f7fcf87c28a46f", + "rev": "b56e973dd2bd62244b44d6542fb52182bc3a3cda", "type": "github" }, "original": { diff --git a/flake.nix b/flake.nix index 3d3543e..b92139f 100644 --- a/flake.nix +++ b/flake.nix @@ -29,16 +29,6 @@ url = "github:jmbuhr/cmp-pandoc-references"; flake = false; }; - - "plugins-bloocky" = { - url = "github:atiladefreitas/bloocky"; - flake = false; - }; - - "plugins-dooing" = { - url = "github:atiladefreitas/dooing"; - flake = false; - }; }; outputs = { @@ -216,10 +206,10 @@ (builtins.length (self.lib.devShellPackages defaultConfig) > 0) ]; overrideAssertions = [ - (!(overrideConfig.cats.r or false)) + (overrideConfig.cats.r or false) (builtins.length overrideNix == 1) ((builtins.head overrideNix) == "alejandra") - (builtins.match ".*R RHOME.*" overrideShellHook == null) + (builtins.match ".*R RHOME.*" overrideShellHook != null) ]; in pkgs.runCommand "check-downstream-overrides" { @@ -241,6 +231,36 @@ touch $out ''; + # Downstream modules assign settings.lang_packages. plainly. The + # module system replaces same-option defaults, so built-in defaults must + # live in langPackageDefaults and be composed at the consumption sites. + # This check fails if someone moves defaults back into lang_packages: + # then a downstream assignment would silently drop lintr etc. + append-semantics = let + downstreamCfg = (self.lib.eval { + inherit pkgs; + modules = [ + { + cats.r = true; + settings.lang_packages.r = [ pkgs.rpkgs.rPackages.fixest ]; + } + ]; + }).config; + rWrapperPkg = builtins.head downstreamCfg.catPkgs.r; + in + pkgs.runCommand "check-lang-packages-append" { } '' + script=$(readlink -f "${rWrapperPkg}/bin/R") + grep -q "r-lintr-" "$script" || { + echo "default R packages were dropped by a downstream lang_packages assignment" >&2 + exit 1 + } + grep -q "r-fixest-" "$script" || { + echo "downstream lang_packages.r additions were not appended" >&2 + exit 1 + } + echo "lang_packages append semantics OK" > $out + ''; + smoke-test = pkgs.runCommand "smoke-test" {} '' # The Nix build sandbox has a read-only HOME; point XDG dirs at a # writable location so vim.lsp/shaDa can write state headlessly. diff --git a/modules/module/settings/cat-packages.nix b/modules/module/settings/cat-packages.nix index dbdba3f..a2a1400 100644 --- a/modules/module/settings/cat-packages.nix +++ b/modules/module/settings/cat-packages.nix @@ -9,14 +9,17 @@ let # NOTE: Package list expressions are lazily evaluated, and derivations are # not built until needed, so keep side-effecting expressions out of these # lists. - maybe = cat: pkgsList: - lib.optionals (config.cats.${cat} or false) pkgsList; - rPackages = (pkgs.baseRPackages or [ ]) ++ config.settings.lang_packages.r; + maybe = cat: pkgsList: lib.optionals (config.cats.${cat} or false) pkgsList; + rPackages = + (pkgs.baseRPackages or [ ]) + ++ config.settings.langPackageDefaults.r + ++ config.settings.lang_packages.r; rWrapperPackages = rPackages; quartoPkg = - if config.cats.r or false - then pkgs.rpkgs.quarto.override { extraRPackages = rPackages; } - else pkgs.quarto; + if config.cats.r or false then + pkgs.rpkgs.quarto.override { extraRPackages = rPackages; } + else + pkgs.quarto; in { options.catPkgs = lib.mkOption { @@ -25,83 +28,107 @@ in }; config.catPkgs = { - always = maybe "always" (with pkgs; [ - ripgrep - ]); + always = maybe "always" ( + with pkgs; + [ + ripgrep + ] + ); clickhouse = maybe "clickhouse" (with pkgs; [ clickhouse-lts ]); - external = maybe "external" (with pkgs; [ - nodejs - perl - ruby - shfmt - sqlfluff - tree-sitter - ]); + external = maybe "external" ( + with pkgs; + [ + nodejs + perl + ruby + shfmt + sqlfluff + tree-sitter + ] + ); julia = maybe "julia" [ - (pkgs.julia-bin.withPackages config.settings.lang_packages.julia) + (pkgs.julia-bin.withPackages ( + config.settings.langPackageDefaults.julia ++ config.settings.lang_packages.julia + )) ]; lua = maybe "lua" (with pkgs; [ lua-language-server ]); - markdown = maybe "markdown" (with pkgs; [ - python3Packages.pylatexenc - quartoPkg - zk - marksman - texlab - imagemagick - harper - ]); + markdown = maybe "markdown" ( + with pkgs; + [ + python3Packages.pylatexenc + quartoPkg + zk + marksman + texlab + imagemagick + harper + ] + ); - nix = maybe "nix" (with pkgs; [ - alejandra - nix-doc - nixd - ]); + nix = maybe "nix" ( + with pkgs; + [ + alejandra + nix-doc + nixd + ] + ); - optional = maybe "optional" (with pkgs; [ - bat - broot - devenv - dust - fd - fzf - gawk - gh - git - hunspell - hunspellDicts.de-at - hunspellDicts.en-us - ispell - jq - just - lazygit - man - ncdu - pigz - poppler - ripgrep - tokei - wget - yq - ]); + optional = maybe "optional" ( + with pkgs; + [ + bat + broot + devenv + dust + fd + fzf + gawk + gh + git + hunspell + hunspellDicts.de-at + hunspellDicts.en-us + ispell + jq + just + lazygit + man + ncdu + pigz + poppler + ripgrep + tokei + wget + yq + ] + ); - python = maybe "python" (let - python_packages_fn = - if pkgs ? basePythonPackages - then ps: pkgs.basePythonPackages ps ++ config.settings.lang_packages.python - else _: config.settings.lang_packages.python; - python_with_packages = pkgs.python3.withPackages python_packages_fn; - in - with pkgs; [ + python = maybe "python" ( + let + python_packages_fn = + if pkgs ? basePythonPackages then + ps: + pkgs.basePythonPackages ps + ++ config.settings.langPackageDefaults.python + ++ config.settings.lang_packages.python + else + _: config.settings.langPackageDefaults.python ++ config.settings.lang_packages.python; + python_with_packages = pkgs.python3.withPackages python_packages_fn; + in + with pkgs; + [ python_with_packages ruff basedpyright uv - ]); + ] + ); r = maybe "r" [ (pkgs.rpkgs.rWrapper.override { packages = rWrapperPackages; }) diff --git a/modules/module/settings/core.nix b/modules/module/settings/core.nix index bebc487..52c9413 100644 --- a/modules/module/settings/core.nix +++ b/modules/module/settings/core.nix @@ -8,7 +8,7 @@ config.settings.config_directory = ../../..; # Default colorscheme and background - config.settings.colorscheme = lib.mkDefault "cyberdream"; + config.settings.colorscheme = lib.mkDefault "kanagawa"; config.settings.background = lib.mkDefault "dark"; # Enable RC wrapping (allows neovim to find the config) diff --git a/modules/module/settings/env.nix b/modules/module/settings/env.nix index 75ac273..0575cf0 100644 --- a/modules/module/settings/env.nix +++ b/modules/module/settings/env.nix @@ -14,6 +14,15 @@ # R.nvim v1.x owns its cache and temporary directories and exports # RNVIM_COMPLDIR/RNVIM_TMPDIR during setup. Do not inject literal `$PWD` # values into the wrapper environment. + # + # ponytail: with R_LIBS_USER unset-or-empty, loading compiled namespaces + # (S7 via btw) segfaults on macOS for bare-terminal launches outside the + # devShell hook. Any non-empty value avoids it; this store path exists, + # contains no libraries, and stays inert. The devShell hook overrides it + # with $PWD/.r-libs. Upgrade path: teach wlib to emit expandable defaults. + (lib.mkIf (config.cats.r or false) { + R_LIBS_USER = "${pkgs.rpkgs.rWrapper}"; + }) ]; # Environment variables with defaults (can be overridden by user) diff --git a/modules/module/settings/hosts.nix b/modules/module/settings/hosts.nix index 2db1515..4638e5e 100644 --- a/modules/module/settings/hosts.nix +++ b/modules/module/settings/hosts.nix @@ -5,7 +5,10 @@ ... }: let - rPackages = (pkgs.baseRPackages or [ ]) ++ config.settings.lang_packages.r; + rPackages = + (pkgs.baseRPackages or [ ]) + ++ config.settings.langPackageDefaults.r + ++ config.settings.lang_packages.r; rWrapperPkg = pkgs.rpkgs.rWrapper.override { packages = rPackages ++ [pkgs.nvimcom]; }; in { diff --git a/modules/module/settings/lang-packages.nix b/modules/module/settings/lang-packages.nix index 3be7a9a..c172f8c 100644 --- a/modules/module/settings/lang-packages.nix +++ b/modules/module/settings/lang-packages.nix @@ -5,53 +5,75 @@ ... }: { - options.settings.lang_packages = lib.mkOption { - type = lib.types.submodule { - options = { - python = lib.mkOption { - type = lib.types.listOf lib.types.package; - default = [ ]; - description = "Additional Python-related packages appended to the python spec (overlay defaults remain)."; - }; - r = lib.mkOption { - type = lib.types.listOf lib.types.package; - default = [ ]; - description = "Additional R-related packages appended to the r spec (overlay defaults remain)."; - }; - julia = lib.mkOption { - type = lib.types.listOf lib.types.str; - default = [ ]; - description = "Additional Julia packages (names) passed to julia-bin.withPackages."; + options.settings = { + # Built-in language libraries composed into every language spec. + # Consumers extend via settings.lang_packages, which is APPENDED to these. + langPackageDefaults = lib.mkOption { + type = lib.types.submodule { + options = { + python = lib.mkOption { + type = lib.types.listOf lib.types.package; + default = [ ]; + }; + r = lib.mkOption { + type = lib.types.listOf lib.types.package; + default = [ ]; + }; + julia = lib.mkOption { + type = lib.types.listOf lib.types.str; + default = [ ]; + }; }; }; + default = { }; + }; + + lang_packages = lib.mkOption { + type = lib.types.submodule { + options = { + python = lib.mkOption { + type = lib.types.listOf lib.types.package; + default = [ ]; + description = "Additional Python-related packages appended to the python spec (overlay defaults remain)."; + }; + r = lib.mkOption { + type = lib.types.listOf lib.types.package; + default = [ ]; + description = "Additional R-related packages appended to the r spec (overlay defaults remain)."; + }; + julia = lib.mkOption { + type = lib.types.listOf lib.types.str; + default = [ ]; + description = "Additional Julia packages (names) passed to julia-bin.withPackages."; + }; + }; + }; + default = { }; + description = '' + Project-specific language libraries. Appended to settings.langPackageDefaults + in each language spec's runtime packages. + ''; }; - default = { }; - description = '' - Language-specific package defaults and downstream overrides appended to each - language spec's runtime packages. - ''; }; - config.settings.lang_packages = { - python = lib.mkDefault (with pkgs.python3Packages; [ + config.settings.langPackageDefaults = { + python = with pkgs.python3Packages; [ duckdb polars - ]); - r = lib.mkDefault ( - (with pkgs.rpkgs.rPackages; [ - arrow - broom - data_table - janitor - styler - # vscDebugger is not on CRAN/Bioconductor, so it is not available in - # pkgs.rpkgs.rPackages. Install it manually in your R library if you - # want to use the nvim-dap R adapter (see plugin/26_dap.lua). - # vscDebugger - lintr - ]) - ); - julia = lib.mkDefault [ + ]; + r = with pkgs.rpkgs.rPackages; [ + arrow + broom + data_table + janitor + styler + # vscDebugger is not on CRAN/Bioconductor, so it is not available in + # pkgs.rpkgs.rPackages. Install it manually in your R library if you + # want to use the nvim-dap R adapter (see plugin/26_dap.lua). + # vscDebugger + lintr + ]; + julia = [ "DataFramesMeta" "QuackIO" ]; diff --git a/modules/module/specs/plugins.nix b/modules/module/specs/plugins.nix index babb1ba..34ac406 100644 --- a/modules/module/specs/plugins.nix +++ b/modules/module/specs/plugins.nix @@ -80,8 +80,6 @@ in { plenary-nvim vimtex neogit - config.nvim-lib.neovimPlugins.bloocky - config.nvim-lib.neovimPlugins.dooing { data = mini-nvim; pname = "mini.nvim";