diff --git a/.github/dependabot.yml b/.github/dependabot.yml index 162fed05..8ca4ed01 100644 --- a/.github/dependabot.yml +++ b/.github/dependabot.yml @@ -1,11 +1,7 @@ version: 2 updates: - package-ecosystem: github-actions + open-pull-requests-limit: 15 directory: "/" schedule: interval: daily - open-pull-requests-limit: 15 - reviewers: - - NotAShelf - assignees: - - NotAShelf diff --git a/.github/typos.toml b/.github/typos.toml index 26be7d9b..25d5c0e1 100644 --- a/.github/typos.toml +++ b/.github/typos.toml @@ -6,6 +6,9 @@ default.extend-ignore-words-re = [ "annote", "viw", "typ", + "edn", + "esy", "BA", # somehow "BANanaD3V" is valid, but BA is not... + "Emac" ] diff --git a/.github/workflows/update.yml b/.github/workflows/update.yml new file mode 100644 index 00000000..10bf2089 --- /dev/null +++ b/.github/workflows/update.yml @@ -0,0 +1,91 @@ +name: Weekly Dependency Updates +on: + workflow_dispatch: + schedule: + # 8 PM UTC every Friday + - cron: '0 20 * * 5' +jobs: + update-dependencies: + runs-on: ubuntu-latest + steps: + - name: Checkout repository + uses: actions/checkout@v5 + + - name: "Install Nix" + uses: cachix/install-nix-action@v31.8.2 + + - name: Set up Git + run: | + git config user.name "GitHub Actions Bot" + git config user.email "actions@github.com" + + - name: Create branch for updates + run: | + DATE=$(date +%Y-%m-%d) + BRANCH_NAME="update/dependencies-$DATE" + git checkout -b $BRANCH_NAME + echo "BRANCH_NAME=$BRANCH_NAME" >> $GITHUB_ENV + + - name: Update npins + run: nix run nixpkgs#npins update + + # Only update Nixpkgs. mnw might break on update, better to track it manually to avoid + # unexpected breakage. + - name: Update nixpkgs + run: nix flake update nixpkgs + + - name: Check for changes + id: check_changes + run: | + if git diff --quiet; then + echo "No changes detected" + echo "changes_detected=false" >> "$GITHUB_OUTPUT" + exit 0 + else + echo "Changes detected" + echo "changes_detected=true" >> "$GITHUB_OUTPUT" + fi + + # FIXME: Worth adding additional checks for, e.g., fragile plugins + # or modules + # nix build .#checks.. + # We'll probably want to handle this with machine tests + - name: Verify changes + if: steps.check_changes.outputs.changes_detected == 'true' + run: | + # Run verification tests to ensure updates don't break anything + nix flake check + + + - name: Set date variable + run: echo "DATE=$(date +%Y-%m-%d)" >> "$GITHUB_ENV" + + - name: Commit and push changes + if: steps.check_changes.outputs.changes_detected == 'true' + run: | + git add . + git commit -m "pins: bump all plugins (${{ env.DATE }})" + git push -u origin $BRANCH_NAME + + - name: Create Pull Request + if: steps.check_changes.outputs.changes_detected == 'true' + uses: peter-evans/create-pull-request@v7 + with: + branch: ${{ env.BRANCH_NAME }} + base: main + labels: dependencies,automated pr + token: ${{ secrets.GITHUB_TOKEN }} + commit-message: "npins: bump all plugins (${{ env.DATE }})" + title: "Weekly Dependency Updates: ${{ env.DATE }}" + body: | + > [!NOTE] + > This PR was automatically generated by the Weekly Dependency Updates workflow. Please wait + > for all CI steps to complete, and test any major changes personally. + + Updates Performed: + + - Updated dependencies using `npins update` + - Updated nixpkgs using `nix flake update nixpkgs` + + If the verification steps have passed, updates should be safe to merge. For failing CI steps + submit a Pull Request targetting ${{ env.BRANCH_NAME }} diff --git a/configuration.nix b/configuration.nix index 68776638..cc314288 100644 --- a/configuration.nix +++ b/configuration.nix @@ -31,6 +31,7 @@ isMaximal: { lspSignature.enable = !isMaximal; # conflicts with blink in maximal otter-nvim.enable = isMaximal; nvim-docs-view.enable = isMaximal; + harper-ls.enable = isMaximal; }; debugger = { @@ -56,6 +57,7 @@ isMaximal: { clang.enable = isMaximal; css.enable = isMaximal; html.enable = isMaximal; + json.enable = isMaximal; sql.enable = isMaximal; java.enable = isMaximal; kotlin.enable = isMaximal; @@ -67,7 +69,7 @@ isMaximal: { typst.enable = isMaximal; rust = { enable = isMaximal; - crates.enable = isMaximal; + extensions.crates-nvim.enable = isMaximal; }; # Language modules that are not as common. @@ -84,8 +86,11 @@ isMaximal: { ocaml.enable = false; elixir.enable = false; haskell.enable = false; + hcl.enable = false; ruby.enable = false; fsharp.enable = false; + just.enable = false; + qml.enable = false; tailwind.enable = false; svelte.enable = false; @@ -188,6 +193,7 @@ isMaximal: { vim-wakatime.enable = false; diffview-nvim.enable = true; yanky-nvim.enable = false; + qmk-nvim.enable = false; # requires hardware specific options icon-picker.enable = isMaximal; surround.enable = isMaximal; leetcode-nvim.enable = isMaximal; diff --git a/docs/manual/configuring/languages.md b/docs/manual/configuring/languages.md index 252163fb..bd2e487d 100644 --- a/docs/manual/configuring/languages.md +++ b/docs/manual/configuring/languages.md @@ -1,10 +1,10 @@ # Language Support {#ch-languages} Language specific support means there is a combination of language specific -plugins, `treesitter` support, `nvim-lspconfig` language servers, and `null-ls` -integration. This gets you capabilities ranging from autocompletion to -formatting to diagnostics. The following languages have sections under the -`vim.languages` attribute. +plugins, `treesitter` support, `nvim-lspconfig` language servers, `conform-nvim` +formatters, and `nvim-lint` linter integration. This gets you capabilities +ranging from autocompletion to formatting to diagnostics. The following +languages have sections under the `vim.languages` attribute. - Rust: [vim.languages.rust.enable](#opt-vim.languages.rust.enable) - Nix: [vim.languages.nix.enable](#opt-vim.languages.nix.enable) @@ -20,6 +20,35 @@ formatting to diagnostics. The following languages have sections under the - Lua: [vim.languages.lua.enable](#opt-vim.languages.lua.enable) - PHP: [vim.languages.php.enable](#opt-vim.languages.php.enable) - F#: [vim.languages.fsharp.enable](#opt-vim.languages.fsharp.enable) +- Assembly: [vim.languages.assembly.enable](#opt-vim.languages.assembly.enable) +- Astro: [vim.languages.astro.enable](#opt-vim.languages.astro.enable) +- Bash: [vim.languages.bash.enable](#opt-vim.languages.bash.enable) +- Clang: [vim.languages.clang.enable](#opt-vim.languages.clang.enable) +- Clojure: [vim.languages.clojure.enable](#opt-vim.languages.clojure.enable) +- C#: [vim.languages.csharp.enable](#opt-vim.languages.csharp.enable) +- CSS: [vim.languages.css.enable](#opt-vim.languages.css.enable) +- CUE: [vim.languages.cue.enable](#opt-vim.languages.cue.enable) +- Elixir: [vim.languages.elixir.enable](#opt-vim.languages.elixir.enable) +- Gleam: [vim.languages.gleam.enable](#opt-vim.languages.gleam.enable) +- HCL: [vim.languages.hcl.enable](#opt-vim.languages.hcl.enable) +- Helm: [vim.languages.helm.enable](#opt-vim.languages.helm.enable) +- Julia: [vim.languages.julia.enable](#opt-vim.languages.julia.enable) +- Kotlin: [vim.languages.kotlin.enable](#opt-vim.languages.kotlin.enable) +- Nim: [vim.languages.nim.enable](#opt-vim.languages.nim.enable) +- Nu: [vim.languages.nu.enable](#opt-vim.languages.nu.enable) +- OCaml: [vim.languages.ocaml.enable](#opt-vim.languages.ocaml.enable) +- Odin: [vim.languages.odin.enable](#opt-vim.languages.odin.enable) +- R: [vim.languages.r.enable](#opt-vim.languages.r.enable) +- Ruby: [vim.languages.ruby.enable](#opt-vim.languages.ruby.enable) +- Scala: [vim.languages.scala.enable](#opt-vim.languages.scala.enable) +- Svelte: [vim.languages.svelte.enable](#opt-vim.languages.svelte.enable) +- Tailwind: [vim.languages.tailwind.enable](#opt-vim.languages.tailwind.enable) +- Terraform: + [vim.languages.terraform.enable](#opt-vim.languages.terraform.enable) +- Typst: [vim.languages.typst.enable](#opt-vim.languages.typst.enable) +- Vala: [vim.languages.vala.enable](#opt-vim.languages.vala.enable) +- WGSL: [vim.languages.wgsl.enable](#opt-vim.languages.wgsl.enable) +- YAML: [vim.languages.yaml.enable](#opt-vim.languages.yaml.enable) Adding support for more languages, and improving support for existing ones are great places where you can contribute with a PR. diff --git a/docs/release-notes/rl-0.7.md b/docs/release-notes/rl-0.7.md index 83b4efd7..dca9319a 100644 --- a/docs/release-notes/rl-0.7.md +++ b/docs/release-notes/rl-0.7.md @@ -170,7 +170,7 @@ The changes are, in no particular order: - Add [ocaml-lsp] support -- Fix `Emac` typo +- Fix misspelled "Emacs" - Add [new-file-template.nvim] to automatically fill new file contents using templates diff --git a/docs/release-notes/rl-0.8.md b/docs/release-notes/rl-0.8.md index 36acad6a..c7d8b3d7 100644 --- a/docs/release-notes/rl-0.8.md +++ b/docs/release-notes/rl-0.8.md @@ -28,6 +28,10 @@ align with the "hunks" themed mapping and avoid conflict with the new [neogit] group. +- LSP keybinds and related plugin integrations are now attached in an LspAttach + autocmd event. If you were calling `default_on_attach()` in your LSP setup you + can remove them now. + [NotAShelf](https://github.com/notashelf): [typst-preview.nvim]: https://github.com/chomosuke/typst-preview.nvim @@ -68,7 +72,6 @@ - Add [](#opt-vim.lsp.lightbulb.autocmd.enable) for manually managing the previously managed lightbulb autocommand. - - A warning will occur if [](#opt-vim.lsp.lightbulb.autocmd.enable) and `vim.lsp.lightbulb.setupOpts.autocmd.enabled` are both set at the same time. Pick only one. @@ -111,6 +114,14 @@ - Add [hunk.nvim], Neovim plugin & tool for splitting diffs in Neovim. Available as `vim.git.hunk-nvim` +- Move `crates.nvim` into `languages.rust.extensions and support` `setupOpts` + for the plugin. Deprecates the top level "crates" option in `languages.rust`. + +[sjcobb2022](https://github.com/sjcobb2022): + +- Migrate all current lsp configurations to `vim.lsp.server` and remove internal + dependency on `nvim-lspconfig` + [amadaluzia](https://github.com/amadaluzia): [haskell-tools.nvim]: https://github.com/MrcJkb/haskell-tools.nvim @@ -130,6 +141,11 @@ - Moved code setting `additionalRuntimePaths` and `enableLuaLoader` out of `luaConfigPre`'s default to prevent being overridden - Use conform over custom autocmds for LSP format on save +- Move LSP keybinds and other related plugin integrations into an LspAttach + event. +- Allow multiple formatters in language modules. +- Fixed `prettier` in astro and svelte, and removed `prettierd` due to high + complexity that would be needed to support it. [diniamo](https://github.com/diniamo): @@ -284,6 +300,13 @@ - Fix [blink.cmp] breaking when built-in sources were modified. - Fix [conform.nvim] not allowing disabling formatting on and after save. Use `null` value to disable them if conform is enabled. +- Add [markdown-oxide](https://github.com/Feel-ix-343/markdown-oxide) option to + markdown language module. +- Fix Helm-YAML language module integration. YAML diagnostics will now remain in + `helmfile`s when both are enabled. +- Fix YAML language module not activating LSP keybinds if the Helm language + module was also enabled. +- Fix `json` language module (default) language server not activating. [TheColorman](https://github.com/TheColorman): @@ -319,6 +342,7 @@ - Add global function `nvf_lint` under `vim.diagnostics.nvim-lint.lint_function`. - Deprecate `vim.scrollOffset` in favor of `vim.options.scrolloff`. +- Fix `svelte-language-server` not reloading .js/.ts files on change. [Sc3l3t0n](https://github.com/Sc3l3t0n): @@ -389,9 +413,12 @@ [poz](https://poz.pet): [everforest]: https://github.com/sainnhe/everforest +[oil]: https://github.com/stevearc/oil.nvim +[oil-git-status]: https://github.com/refractalize/oil-git-status.nvim - Fix gitsigns null-ls issue. - Add [everforest] theme support. +- Add [oil-git-status] support to [oil] module. [Haskex](https://github.com/haskex): @@ -479,13 +506,8 @@ - fix broken `neorg` grammars - remove obsolete warning in the `otter` module - -[Cool-Game-Dev](https://github.com/Cool-Game-Dev): - -[nvim-biscuits]: https://github.com/code-biscuits/nvim-biscuits - -- Add [nvim-biscuits] to show block context. Available at - `vim.utility.nvim-biscuits`. +- add mainProgram attribute to vala language server wrapper +- fix `crates-nvim`'s completions by using the in-program lsp [JManch](https://github.com/JManch): @@ -493,6 +515,56 @@ `autocomplete.nvim-cmp.enable` was disabled and `autocomplete.nvim-cmp.sources` had not been modified. +[Poseidon](https://github.com/poseidon-rises): + +[nvim-biscuits]: https://github.com/code-biscuits/nvim-biscuits +[just-lsp]: https://github.com/terror/just-lsp +[roslyn-ls]: https://github.com/dotnet/vscode-csharp +[jsonls]: https://github.com/microsoft/vscode/tree/1.101.2/extensions/json-language-features/server +[jsonfmt]: https://github.com/caarlos0/jsonfmt +[superhtml]: https://github.com/kristoff-it/superhtml +[htmlHINT]: https://github.com/htmlhint/HTMLHint +[qmk-nvim]: https://github.com/codethread/qmk.nvim +[qmlls]: https://doc.qt.io/qt-6/qtqml-tooling-qmlls.html +[qmlformat]: https://doc.qt.io/qt-6/qtqml-tooling-qmlformat.html + +- Add [nvim-biscuits] support under `vim.utility.nvim-biscuits`. +- Add just support under `vim.languages.just` using [just-lsp]. +- Add [roslyn-ls] to the `vim.languages.csharp` module. +- Add JSON support under `vim.languages.json` using [jsonls] and [jsonfmt]. +- Add advanced HTML support under `vim.languages.html` using [superhtml] and + [htmlHINT]. +- Add QMK support under `vim.utility.qmk-nvim` via [qmk-nvim]. +- Add QML support under `vim.languages.qml` using [qmlls] and [qmlformat]. + +[Morsicus](https://github.com/Morsicus): + +- Add [EEx Treesitter Grammar](https://github.com/connorlay/tree-sitter-eex) for + Elixir +- Add + [HEEx Treesitter Grammar](https://github.com/phoenixframework/tree-sitter-heex) + for Elixir + +[diced](https://github.com/diced): + +- Fixed `typescript` treesitter grammar not being included by default. + +[valterschutz](https://github.com/valterschutz): + +[ruff]: (https://github.com/astral-sh/ruff) + +- Add [ruff-fix] as a formatter option in `vim.languages.python.format.type`. + +[gmvar](https://github.com/gmvar): + +[harper-ls]: https://github.com/Automattic/harper + +- Add [harper-ls] to the `vim.lsp` module. + +[derethil](https://github.com/derethil): + +- Fix `vim.lazy.plugins..enabled` Lua evaluation. + [Jules](https://github.com/jules-sommer): [nvim-highlight-colors]: https://github.com/brenoprata10/nvim-highlight-colors @@ -517,3 +589,21 @@ [smoka7/hop.nvim]: https://github.com/smoka7/hop.nvim - Migrate [phaazon/hop.nvim] to [smoka7/hop.nvim] + [typst-concealer]. [simon-wg](https://github.com/simon-wg): + +- Update `python` language module to use correct lsp binary. +- Fix `python` pyright and basedpyright language servers not using default on + attach behavior. + +[critical](https://github.com/critical): + +[mellow.nvim]: https://github.com/mellow-theme/mellow.nvim + +- Add [mellow.nvim] plugin for vim and lualine theme support + +[valyntyler](https://github.com/valyntyler): + +[emmet-ls]: https://github.com/aca/emmet-ls + +- Enable `languages.ts.format` for `.js` files +- Add [emmet-ls] to `html.lsp.servers` diff --git a/flake.lock b/flake.lock index 5a357a75..9e8b3eb5 100644 --- a/flake.lock +++ b/flake.lock @@ -53,11 +53,11 @@ }, "nixpkgs": { "locked": { - "lastModified": 1761880412, - "narHash": "sha256-QoJjGd4NstnyOG4mm4KXF+weBzA2AH/7gn1Pmpfcb0A=", + "lastModified": 1764081664, + "narHash": "sha256-sUoHmPr/EwXzRMpv1u/kH+dXuvJEyyF2Q7muE+t0EU4=", "owner": "nixos", "repo": "nixpkgs", - "rev": "a7fc11be66bdfb5cdde611ee5ce381c183da8386", + "rev": "dc205f7b4fdb04c8b7877b43edb7b73be7730081", "type": "github" }, "original": { diff --git a/flake/develop.nix b/flake/develop.nix index aae9d92f..71c13688 100644 --- a/flake/develop.nix +++ b/flake/develop.nix @@ -3,7 +3,6 @@ pkgs, config, self', - inputs', ... }: { devShells = { diff --git a/flake/pkgs/by-name/prettier-plugin-astro/package.nix b/flake/pkgs/by-name/prettier-plugin-astro/package.nix new file mode 100644 index 00000000..d893da7c --- /dev/null +++ b/flake/pkgs/by-name/prettier-plugin-astro/package.nix @@ -0,0 +1,48 @@ +{ + stdenv, + fetchFromGitHub, + nodejs, + pnpm_9, + pins, +}: let + pin = pins.prettier-plugin-astro; +in + stdenv.mkDerivation (finalAttrs: { + pname = "prettier-plugin-astro"; + version = pin.version or pin.revision; + + src = fetchFromGitHub { + inherit (pin.repository) owner repo; + rev = finalAttrs.version; + sha256 = pin.hash; + }; + + pnpmDeps = pnpm_9.fetchDeps { + inherit (finalAttrs) pname src; + fetcherVersion = 2; + hash = "sha256-K7pIWLkIIbUKDIcysfEtcf/eVMX9ZgyFHdqcuycHCNE="; + }; + + nativeBuildInputs = [ + nodejs + pnpm_9.configHook + ]; + + buildPhase = '' + runHook preBuild + + pnpm run build + + runHook postBuild + ''; + + installPhase = '' + runHook preInstall + + # mkdir -p $out/dist + cp -r dist/ $out + cp -r node_modules $out + + runHook postInstall + ''; + }) diff --git a/flake/pkgs/by-name/prettier-plugin-svelte/package.nix b/flake/pkgs/by-name/prettier-plugin-svelte/package.nix new file mode 100644 index 00000000..540ff2b7 --- /dev/null +++ b/flake/pkgs/by-name/prettier-plugin-svelte/package.nix @@ -0,0 +1,19 @@ +{ + buildNpmPackage, + fetchFromGitHub, + pins, +}: let + pin = pins.prettier-plugin-svelte; +in + buildNpmPackage (finalAttrs: { + pname = "prettier-plugin-svelte"; + version = pin.version or pin.revision; + + src = fetchFromGitHub { + inherit (pin.repository) owner repo; + rev = finalAttrs.version; + sha256 = pin.hash; + }; + + npmDepsHash = "sha256-D+gDdKiIG38jV+M/BqTKf0yYj1KXpbIodtQFdzocpn8="; + }) diff --git a/lib/dag.nix b/lib/dag.nix index cbf52083..db5d53b7 100644 --- a/lib/dag.nix +++ b/lib/dag.nix @@ -8,7 +8,7 @@ # - the addition of the function `entryBefore` indicating a "wanted # by" relationship. {lib}: let - inherit (builtins) isAttrs attrValues attrNames elem all head tail length toJSON isString; + inherit (builtins) isAttrs attrValues attrNames elem all head tail length toJSON isString removeAttrs; inherit (lib.attrsets) filterAttrs mapAttrs; inherit (lib.lists) toposort; inherit (lib.nvim.dag) empty isEntry entryBetween entryAfter entriesBetween entryAnywhere topoSort; @@ -169,10 +169,11 @@ in { else value) dag; sortedDag = topoSort finalDag; + loopDetail = map (loops: removeAttrs loops ["data"]) sortedDag.loops; result = if sortedDag ? result then mapResult sortedDag.result - else abort ("Dependency cycle in ${name}: " + toJSON sortedDag); + else abort ("Dependency cycle in ${name}: " + toJSON loopDetail); in result; } diff --git a/lib/languages.nix b/lib/languages.nix index c4074144..899d9ea8 100644 --- a/lib/languages.nix +++ b/lib/languages.nix @@ -1,7 +1,7 @@ {lib}: let inherit (builtins) isString getAttr; inherit (lib.options) mkOption; - inherit (lib.types) listOf bool str submodule attrsOf anything either nullOr; + inherit (lib.types) listOf bool str submodule attrsOf anything either nullOr uniq; inherit (lib.nvim.attrsets) mapListToAttrs; inherit (lib.nvim.types) luaInline; in { @@ -46,7 +46,7 @@ in { capabilities = mkOption { type = nullOr (either luaInline (attrsOf anything)); default = null; - description = "LSP capabilitiess to pass to lspconfig"; + description = "LSP capabilities to pass to LSP server configuration"; }; on_attach = mkOption { @@ -58,11 +58,11 @@ in { filetypes = mkOption { type = nullOr (listOf str); default = null; - description = "Filetypes to auto-attach LSP in"; + description = "Filetypes to auto-attach LSP server in"; }; cmd = mkOption { - type = nullOr (listOf str); + type = nullOr (either luaInline (uniq (listOf str))); default = null; description = "Command used to start the LSP server"; }; diff --git a/lib/lua.nix b/lib/lua.nix index 4106a4c2..bf879031 100644 --- a/lib/lua.nix +++ b/lib/lua.nix @@ -1,92 +1,52 @@ # Helpers for converting values to lua {lib}: let - inherit (builtins) hasAttr head throw typeOf isList isAttrs isBool isInt isString isPath isFloat toJSON; - inherit (lib.attrsets) mapAttrsToList filterAttrs; - inherit (lib.strings) concatStringsSep concatMapStringsSep stringToCharacters; - inherit (lib.trivial) boolToString warn; -in rec { - # Convert a null value to lua's nil - nullString = value: - if value == null - then "nil" - else "'${value}'"; - - # convert an expression to lua - expToLua = exp: - if isList exp - then listToLuaTable exp # if list, convert to lua table - else if isAttrs exp - then attrsetToLuaTable exp # if attrs, convert to table - else if isBool exp - then boolToString exp # if bool, convert to string - else if isInt exp - then toString exp # if int, convert to string - else if exp == null - then "nil" - else (toJSON exp); # otherwise jsonify the value and print as is - - # convert list to a lua table - listToLuaTable = list: - "{ " + (concatStringsSep ", " (map expToLua list)) + " }"; - - # convert attrset to a lua table - attrsetToLuaTable = attrset: - "{ " - + ( - concatStringsSep ", " - ( - mapAttrsToList ( - name: value: - name - + " = " - + (expToLua value) - ) - attrset - ) - ) - + " }"; - # Convert a list of lua expressions to a lua table. The difference to listToLuaTable is that the elements here are expected to be lua expressions already, whereas listToLuaTable converts from nix types to lua first - luaTable = items: ''{${concatStringsSep "," items}}''; - isLuaInline = object: (object._type or null) == "lua-inline"; toLuaObject = args: - if isAttrs args - then - if isLuaInline args - then args.expr - else if hasAttr "__empty" args - then - warn '' - Using `__empty` to define an empty lua table is deprecated. Use an empty attrset instead. - '' "{ }" - else - "{" - + (concatStringsSep "," - (mapAttrsToList - (n: v: - if head (stringToCharacters n) == "@" - then toLuaObject v - else "[${toLuaObject n}] = " + (toLuaObject v)) - (filterAttrs - (_: v: v != null) - args))) - + "}" - else if isList args - then "{" + concatMapStringsSep "," toLuaObject args + "}" - else if isString args - then - # This should be enough! - toJSON args - else if isPath args - then toJSON (toString args) - else if isBool args - then "${boolToString args}" - else if isFloat args - then "${toString args}" - else if isInt args - then "${toString args}" - else if (args == null) - then "nil" - else throw "could not convert object of type `${typeOf args}` to lua object"; -} + { + int = toString args; + float = toString args; + + # escapes \ and quotes + string = builtins.toJSON args; + path = builtins.toJSON args; + + bool = lib.boolToString args; + null = "nil"; + + list = "{${lib.concatMapStringsSep ",\n" toLuaObject args}}"; + + set = + if lib.isDerivation args + then ''"${args}"'' + else if isLuaInline args + then args.expr + else "{${ + lib.pipe args [ + (lib.filterAttrs (_: v: v != null)) + (builtins.mapAttrs ( + n: v: + if lib.hasPrefix "@" n + then toLuaObject v + else "[${toLuaObject n}] = ${toLuaObject v}" + )) + builtins.attrValues + (lib.concatStringsSep ",\n") + ] + }}"; + } + .${ + builtins.typeOf args + } + or (builtins.throw "Could not convert object of type `${builtins.typeOf args}` to lua object"); +in + { + inherit isLuaInline toLuaObject; + luaTable = x: (toLuaObject (map lib.mkLuaInline x)); + } + // lib.genAttrs [ + "nullString" + "expToLua" + "listToLuaTable" + "attrsetToLuaTable" + ] (name: lib.warn "${name} is deprecated use toLuaObject instead" toLuaObject) diff --git a/lib/types/custom.nix b/lib/types/custom.nix index 8e8be8a2..f6905b02 100644 --- a/lib/types/custom.nix +++ b/lib/types/custom.nix @@ -1,7 +1,10 @@ {lib}: let + inherit (builtins) toJSON; inherit (lib.options) mergeEqualOption; + inherit (lib.lists) singleton; inherit (lib.strings) isString stringLength match; - inherit (lib.types) listOf mkOptionType; + inherit (lib.types) listOf mkOptionType coercedTo; + inherit (lib.trivial) warn; in { mergelessListOf = elemType: mkOptionType { @@ -25,4 +28,15 @@ in { description = "RGB color in hex format"; check = v: isString v && (match "#?[0-9a-fA-F]{6}" v) != null; }; + + # no compound types please + deprecatedSingleOrListOf = option: t: + coercedTo + t + (x: + warn '' + ${option} no longer accepts non-list values, use [${toJSON x}] instead + '' + (singleton x)) + (listOf t); } diff --git a/lib/types/default.nix b/lib/types/default.nix index 044a8221..66adfbbc 100644 --- a/lib/types/default.nix +++ b/lib/types/default.nix @@ -10,5 +10,5 @@ in { inherit (typesDag) dagOf; inherit (typesPlugin) pluginsOpt extraPluginType mkPluginSetupOption luaInline pluginType borderType; inherit (typesLanguage) diagnostics mkGrammarOption; - inherit (customTypes) char hexColor mergelessListOf; + inherit (customTypes) char hexColor mergelessListOf deprecatedSingleOrListOf; } diff --git a/modules/extra/deprecations.nix b/modules/extra/deprecations.nix index 0a5733ab..7012feac 100644 --- a/modules/extra/deprecations.nix +++ b/modules/extra/deprecations.nix @@ -1,7 +1,9 @@ {lib, ...}: let - inherit (lib.modules) mkRemovedOptionModule mkRenamedOptionModule; + inherit (builtins) head; + inherit (lib.modules) mkRemovedOptionModule mkRenamedOptionModule doRename; inherit (lib.lists) concatLists; inherit (lib.nvim.config) batchRenameOptions; + inherit (lib.trivial) warn; renamedVimOpts = batchRenameOptions ["vim"] ["vim" "options"] { # 2024-12-01 @@ -20,6 +22,31 @@ # 2025-02-07 scrollOffset = "scrolloff"; }; + + mkRemovedLspOpt = lang: (mkRemovedOptionModule ["vim" "languages" lang "lsp" "opts"] '' + `vim.languages.${lang}.lsp.opts` is now moved to `vim.lsp.servers..init_options` + ''); + + mkRemovedLspPackage = lang: (mkRemovedOptionModule ["vim" "languages" lang "lsp" "package"] '' + `vim.languages.${lang}.lsp.package` is now moved to `vim.lsp.servers..cmd` + ''); + + mkRenamedLspServer = lang: + doRename + { + from = ["vim" "languages" lang "lsp" "server"]; + to = ["vim" "languages" lang "lsp" "servers"]; + visible = false; + warn = true; + use = x: + warn + "Obsolete option `vim.languages.${lang}.lsp.server` used, use `vim.languages.${lang}.lsp.servers` instead." + (head x); + }; + + mkRemovedFormatPackage = lang: (mkRemovedOptionModule ["vim" "languages" lang "format" "package"] '' + `vim.languages.${lang}.format.package` is removed, please use `vim.formatter.conform-nvim.formatters..command` instead. + ''); in { imports = concatLists [ [ @@ -120,8 +147,165 @@ in { in 'vim.clipboard.registers'. Please see the documentation for the new module for more details, or open an issue if you are confused. '') + + # 2025-07-12 + (mkRenamedLspServer "assembly") + + (mkRenamedLspServer "astro") + (mkRemovedLspPackage "astro") + + (mkRenamedLspServer "bash") + (mkRemovedLspPackage "bash") + + (mkRemovedLspOpt "clang") + (mkRemovedLspPackage "clang") + (mkRenamedLspServer "clang") + + (mkRemovedLspPackage "clojure") + + (mkRenamedLspServer "csharp") + (mkRemovedLspPackage "csharp") + + (mkRenamedLspServer "css") + (mkRemovedLspPackage "css") + + (mkRemovedLspPackage "cue") + + (mkRenamedLspServer "dart") + (mkRemovedLspPackage "dart") + (mkRemovedLspOpt "dart") + + (mkRenamedLspServer "elixir") + (mkRemovedLspPackage "elixir") + + (mkRenamedLspServer "fsharp") + (mkRemovedLspPackage "fsharp") + + (mkRenamedLspServer "gleam") + (mkRemovedLspPackage "gleam") + + (mkRenamedLspServer "go") + (mkRemovedLspPackage "go") + + (mkRemovedLspPackage "haskell") + + (mkRemovedLspPackage "hcl") + + (mkRenamedLspServer "helm") + (mkRemovedLspPackage "helm") + + (mkRemovedLspPackage "java") + + (mkRenamedLspServer "julia") + (mkRemovedLspPackage "julia") + + (mkRemovedLspPackage "kotlin") + + (mkRemovedLspPackage "lua") + + (mkRenamedLspServer "markdown") + (mkRemovedLspPackage "markdown") + + (mkRenamedLspServer "nim") + (mkRemovedLspPackage "nim") + + (mkRenamedLspServer "nix") + (mkRemovedLspPackage "nix") + (mkRemovedOptionModule ["vim" "languages" "nix" "lsp" "options"] '' + `vim.languages.nix.lsp.options` has been moved to `vim.lsp.servers..init_options`. + '') + + (mkRenamedLspServer "nu") + (mkRemovedLspPackage "nu") + + (mkRenamedLspServer "ocaml") + (mkRemovedLspPackage "ocaml") + + (mkRenamedLspServer "odin") + (mkRemovedLspPackage "odin") + + (mkRenamedLspServer "php") + (mkRemovedLspPackage "php") + + (mkRenamedLspServer "python") + (mkRemovedLspPackage "python") + + (mkRenamedLspServer "r") + (mkRemovedLspPackage "r") + + (mkRenamedLspServer "ruby") + (mkRemovedLspPackage "ruby") + + (mkRenamedLspServer "sql") + (mkRemovedLspPackage "sql") + + (mkRenamedLspServer "svelte") + (mkRemovedLspPackage "svelte") + + (mkRenamedLspServer "tailwind") + (mkRemovedLspPackage "tailwind") + + (mkRemovedLspPackage "terraform") + + (mkRenamedLspServer "ts") + (mkRemovedLspPackage "ts") + + (mkRenamedLspServer "typst") + (mkRemovedLspPackage "typst") + + (mkRenamedLspServer "vala") + (mkRemovedLspPackage "vala") + + (mkRenamedLspServer "wgsl") + (mkRemovedLspPackage "wgsl") + + (mkRenamedLspServer "yaml") + (mkRemovedLspPackage "yaml") + + (mkRenamedLspServer "zig") + (mkRemovedLspPackage "zig") + + # 2025-10-22 + (mkRenamedOptionModule ["vim" "languages" "rust" "crates" "enable"] ["vim" "languages" "rust" "extensions" "crates-nvim" "enable"]) + (mkRemovedOptionModule ["vim" "languages" "rust" "crates" "codeActions"] '' + 'vim.languages.rust.crates' option has been moved to 'vim.languages.rust.extensions.crates-nvim' in full and the + codeActions option has been removed. To set up code actions again, you may use the the new 'setupOpts' option + located under 'vim.languages.rust.extensions.crates-nvim'. Refer to crates.nvim documentation for setup steps: + + + '') + + (mkRemovedOptionModule ["vim" "language" "astro" "format"] '' + This option has been removed due to being broken for a long time. + '') + (mkRemovedOptionModule ["vim" "language" "svelte" "format"] '' + This option has been removed due to being broken for a long time. + '') ] + (map mkRemovedFormatPackage [ + "bash" + "css" + "elixir" + "fsharp" + "go" + "hcl" + "html" + "json" + "lua" + "markdown" + "nim" + "nix" + "ocaml" + "python" + "qml" + "r" + "ruby" + "rust" + "sql" + "ts" + "typst" + ]) # Migrated via batchRenameOptions. Further batch renames must be below this line. renamedVimOpts ]; diff --git a/modules/neovim/init/autocmds.nix b/modules/neovim/init/autocmds.nix index 07656134..579a211e 100644 --- a/modules/neovim/init/autocmds.nix +++ b/modules/neovim/init/autocmds.nix @@ -9,7 +9,7 @@ inherit (lib.types) nullOr submodule listOf str bool; inherit (lib.nvim.types) luaInline; inherit (lib.nvim.lua) toLuaObject; - inherit (lib.nvim.dag) entryAfter; + inherit (lib.nvim.dag) entryAfter entryBetween; autocommandType = submodule { options = { @@ -144,7 +144,7 @@ in { enabledAutogroups = filter (au: au.enable) cfg.augroups; in { luaConfigRC = { - augroups = entryAfter ["pluginConfigs"] (optionalString (enabledAutogroups != []) '' + augroups = entryBetween ["autocmds"] ["pluginConfigs"] (optionalString (enabledAutogroups != []) '' local nvf_autogroups = {} for _, group in ipairs(${toLuaObject enabledAutogroups}) do if group.name then diff --git a/modules/neovim/init/default.nix b/modules/neovim/init/default.nix index 30a481a1..3f195085 100644 --- a/modules/neovim/init/default.nix +++ b/modules/neovim/init/default.nix @@ -8,5 +8,6 @@ ./highlight.nix ./lsp.nix ./spellcheck.nix + ./util.nix ]; } diff --git a/modules/neovim/init/lsp.nix b/modules/neovim/init/lsp.nix index b89c3fde..b17c5554 100644 --- a/modules/neovim/init/lsp.nix +++ b/modules/neovim/init/lsp.nix @@ -16,6 +16,7 @@ cfg = config.vim.lsp; + # TODO: lspConfigurations filter on enabledServers instead of cfg.servers? lspConfigurations = mapAttrsToList ( name: value: '' @@ -76,7 +77,6 @@ in { { vim.lsp.servers."*" = { capabilities = mkDefault (mkLuaInline "capabilities"); - on_attach = mkDefault (mkLuaInline "default_on_attach"); }; } diff --git a/modules/neovim/init/spellcheck.nix b/modules/neovim/init/spellcheck.nix index 5c593a65..9f635bf5 100644 --- a/modules/neovim/init/spellcheck.nix +++ b/modules/neovim/init/spellcheck.nix @@ -9,7 +9,7 @@ inherit (lib.strings) concatLines concatStringsSep optionalString; inherit (lib.attrsets) mapAttrsToList; inherit (lib.types) listOf str attrsOf; - inherit (lib.nvim.lua) listToLuaTable; + inherit (lib.nvim.lua) toLuaObject; inherit (lib.nvim.dag) entryAfter; cfg = config.vim.spellcheck; @@ -152,7 +152,7 @@ in { vim.api.nvim_create_augroup("nvf_autocmds", {clear = false}) vim.api.nvim_create_autocmd({ "FileType" }, { group = "nvf_autocmds", - pattern = ${listToLuaTable cfg.ignoredFiletypes}, + pattern = ${toLuaObject cfg.ignoredFiletypes}, callback = function() vim.opt_local.spell = false end, diff --git a/modules/neovim/init/util.nix b/modules/neovim/init/util.nix new file mode 100644 index 00000000..d151e53a --- /dev/null +++ b/modules/neovim/init/util.nix @@ -0,0 +1,177 @@ +{ + config, + lib, + ... +}: let + inherit (lib.modules) mkIf mkMerge; + inherit (lib.nvim.dag) entryBefore; + + cfg = config.vim.lsp; +in { + config = mkMerge [ + (mkIf (cfg.servers != {}) { + vim.luaConfigRC.lsp-util = + entryBefore ["lsp-servers"] + /* + lua + */ + '' + -- Port of nvim-lspconfig util + local util = { path = {} } + + util.default_config = { + log_level = vim.lsp.protocol.MessageType.Warning, + message_level = vim.lsp.protocol.MessageType.Warning, + settings = vim.empty_dict(), + init_options = vim.empty_dict(), + handlers = {}, + autostart = true, + capabilities = vim.lsp.protocol.make_client_capabilities(), + } + + -- global on_setup hook + util.on_setup = nil + + do + local validate = vim.validate + local api = vim.api + local lsp = vim.lsp + local nvim_eleven = vim.fn.has 'nvim-0.11' == 1 + + local iswin = vim.uv.os_uname().version:match 'Windows' + + local function escape_wildcards(path) + return path:gsub('([%[%]%?%*])', '\\%1') + end + + local function is_fs_root(path) + if iswin then + return path:match '^%a:$' + else + return path == '/' + end + end + + local function traverse_parents(path, cb) + path = vim.uv.fs_realpath(path) + local dir = path + -- Just in case our algo is buggy, don't infinite loop. + for _ = 1, 100 do + dir = vim.fs.dirname(dir) + if not dir then + return + end + -- If we can't ascend further, then stop looking. + if cb(dir, path) then + return dir, path + end + if is_fs_root(dir) then + break + end + end + end + + util.root_pattern = function(...) + local patterns = util.tbl_flatten { ... } + return function(startpath) + startpath = util.strip_archive_subpath(startpath) + for _, pattern in ipairs(patterns) do + local match = util.search_ancestors(startpath, function(path) + for _, p in ipairs(vim.fn.glob(table.concat({ escape_wildcards(path), pattern }, '/'), true, true)) do + if vim.uv.fs_stat(p) then + return path + end + end + end) + + if match ~= nil then + return match + end + end + end + end + + util.root_markers_with_field = function(root_files, new_names, field, fname) + local path = vim.fn.fnamemodify(fname, ':h') + local found = vim.fs.find(new_names, { path = path, upward = true }) + + for _, f in ipairs(found or {}) do + -- Match the given `field`. + for line in io.lines(f) do + if line:find(field) then + root_files[#root_files + 1] = vim.fs.basename(f) + break + end + end + end + + return root_files + end + + util.insert_package_json = function(root_files, field, fname) + return util.root_markers_with_field(root_files, { 'package.json', 'package.json5' }, field, fname) + end + + util.strip_archive_subpath = function(path) + -- Matches regex from zip.vim / tar.vim + path = vim.fn.substitute(path, 'zipfile://\\(.\\{-}\\)::[^\\\\].*$', '\\1', ''') + path = vim.fn.substitute(path, 'tarfile:\\(.\\{-}\\)::.*$', '\\1', ''') + return path + end + + util.get_typescript_server_path = function(root_dir) + local project_roots = vim.fs.find('node_modules', { path = root_dir, upward = true, limit = math.huge }) + for _, project_root in ipairs(project_roots) do + local typescript_path = project_root .. '/typescript' + local stat = vim.loop.fs_stat(typescript_path) + if stat and stat.type == 'directory' then + return typescript_path .. '/lib' + end + end + return ''' + end + + util.search_ancestors = function(startpath, func) + if nvim_eleven then + validate('func', func, 'function') + end + if func(startpath) then + return startpath + end + local guard = 100 + for path in vim.fs.parents(startpath) do + -- Prevent infinite recursion if our algorithm breaks + guard = guard - 1 + if guard == 0 then + return + end + + if func(path) then + return path + end + end + end + + util.path.is_descendant = function(root, path) + if not path then + return false + end + + local function cb(dir, _) + return dir == root + end + + local dir, _ = traverse_parents(path, cb) + + return dir == root + end + + util.tbl_flatten = function(t) + --- @diagnostic disable-next-line:deprecated + return nvim_eleven and vim.iter(t):flatten(math.huge):totable() or vim.tbl_flatten(t) + end + end + ''; + }) + ]; +} diff --git a/modules/plugins/assistant/chatgpt/config.nix b/modules/plugins/assistant/chatgpt/config.nix index b1066e5a..bf4d9972 100644 --- a/modules/plugins/assistant/chatgpt/config.nix +++ b/modules/plugins/assistant/chatgpt/config.nix @@ -1,6 +1,7 @@ { config, lib, + options, ... }: let inherit (lib.modules) mkIf mkMerge; @@ -10,8 +11,7 @@ cfg = config.vim.assistant.chatgpt; - self = import ./chatgpt.nix {inherit lib;}; - mappingDefinitions = self.options.vim.assistant.chatgpt.mappings; + mappingDefinitions = options.vim.assistant.chatgpt.mappings; mappings = addDescriptionsToMappings cfg.mappings mappingDefinitions; maps = mkMerge [ (mkSetBinding mappings.editWithInstructions "ChatGPTEditWithInstruction") diff --git a/modules/plugins/formatter/conform-nvim/conform-nvim.nix b/modules/plugins/formatter/conform-nvim/conform-nvim.nix index cfe89bf3..4e27f9f0 100644 --- a/modules/plugins/formatter/conform-nvim/conform-nvim.nix +++ b/modules/plugins/formatter/conform-nvim/conform-nvim.nix @@ -1,13 +1,58 @@ {lib, ...}: let inherit (lib.generators) mkLuaInline; inherit (lib.options) mkOption mkEnableOption literalMD; - inherit (lib.types) attrs either nullOr; + inherit (lib.types) attrs either nullOr listOf submodule str; inherit (lib.nvim.lua) toLuaObject; inherit (lib.nvim.types) luaInline mkPluginSetupOption; + + formattersType = submodule { + freeformType = attrs; + options = { + command = mkOption { + type = nullOr (either str luaInline); + default = null; + description = "The command to run."; + }; + + args = mkOption { + type = nullOr (either (listOf str) luaInline); + default = null; + description = '' + A list of strings, or a lua function that returns a list of strings. + + Return a single string instead of a list to run the command in a + shell. + ''; + }; + + prepend_args = mkOption { + type = nullOr (either (listOf str) luaInline); + default = null; + description = '' + When inherit = true, add additional arguments to the beginning of + args. Can also be a function, like args. + ''; + }; + + append_args = mkOption { + type = nullOr (either (listOf str) luaInline); + default = null; + description = '' + When inherit = true, add additional arguments to the end of args. + Can also be a function, like args. + ''; + }; + }; + }; in { options.vim.formatter.conform-nvim = { enable = mkEnableOption "lightweight yet powerful formatter plugin for Neovim [conform-nvim]"; setupOpts = mkPluginSetupOption "conform.nvim" { + formatters = mkOption { + type = formattersType; + default = {}; + description = "Custom formatters and overrides for built-in formatters."; + }; formatters_by_ft = mkOption { type = attrs; default = {}; diff --git a/modules/plugins/git/git-conflict/config.nix b/modules/plugins/git/git-conflict/config.nix index bc9905d6..9d99aacc 100644 --- a/modules/plugins/git/git-conflict/config.nix +++ b/modules/plugins/git/git-conflict/config.nix @@ -1,6 +1,7 @@ { config, lib, + options, ... }: let inherit (lib.modules) mkIf mkMerge; @@ -10,8 +11,7 @@ cfg = config.vim.git.git-conflict; - self = import ./git-conflict.nix {inherit lib config;}; - gcMappingDefinitions = self.options.vim.git.git-conflict.mappings; + gcMappingDefinitions = options.vim.git.git-conflict.mappings; gcMappings = addDescriptionsToMappings cfg.mappings gcMappingDefinitions; in { diff --git a/modules/plugins/git/gitsigns/config.nix b/modules/plugins/git/gitsigns/config.nix index e89eebcf..e2d31df5 100644 --- a/modules/plugins/git/gitsigns/config.nix +++ b/modules/plugins/git/gitsigns/config.nix @@ -1,6 +1,7 @@ { config, lib, + options, ... }: let inherit (builtins) toJSON; @@ -12,8 +13,7 @@ cfg = config.vim.git.gitsigns; - self = import ./gitsigns.nix {inherit lib config;}; - gsMappingDefinitions = self.options.vim.git.gitsigns.mappings; + gsMappingDefinitions = options.vim.git.gitsigns.mappings; gsMappings = addDescriptionsToMappings cfg.mappings gsMappingDefinitions; in { diff --git a/modules/plugins/languages/asm.nix b/modules/plugins/languages/asm.nix index 9fe58a76..4fb36f4c 100644 --- a/modules/plugins/languages/asm.nix +++ b/modules/plugins/languages/asm.nix @@ -4,12 +4,24 @@ lib, ... }: let + inherit (builtins) attrNames; inherit (lib.options) mkEnableOption mkOption; inherit (lib.modules) mkIf mkMerge; - inherit (lib.types) package; - inherit (lib.nvim.types) mkGrammarOption; + inherit (lib.types) enum; + inherit (lib.nvim.types) mkGrammarOption deprecatedSingleOrListOf; + inherit (lib.meta) getExe; + inherit (lib.nvim.attrsets) mapListToAttrs; cfg = config.vim.languages.assembly; + defaultServers = ["asm-lsp"]; + servers = { + asm-lsp = { + enable = true; + cmd = [(getExe pkgs.asm-lsp)]; + filetypes = ["asm" "vmasm"]; + root_markers = [".asm-lsp.toml" ".git"]; + }; + }; in { options.vim.languages.assembly = { enable = mkEnableOption "Assembly support"; @@ -20,12 +32,11 @@ in { }; lsp = { - enable = mkEnableOption "Assembly LSP support (asm-lsp)" // {default = config.vim.lsp.enable;}; - - package = mkOption { - type = package; - default = pkgs.asm-lsp; - description = "asm-lsp package"; + enable = mkEnableOption "Assembly LSP support" // {default = config.vim.lsp.enable;}; + servers = mkOption { + type = deprecatedSingleOrListOf "vim.language.asm.lsp.servers" (enum (attrNames servers)); + default = defaultServers; + description = "Assembly LSP server to use"; }; }; }; @@ -36,14 +47,12 @@ in { }) (mkIf cfg.lsp.enable { - vim.lsp.lspconfig.enable = true; - vim.lsp.lspconfig.sources.asm-lsp = '' - lspconfig.asm_lsp.setup { - capabilities = capabilities, - on_attach = default_on_attach, - cmd = {"${cfg.lsp.package}/bin/asm-lsp"}, - } - ''; + vim.lsp.servers = + mapListToAttrs (n: { + name = n; + value = servers.${n}; + }) + cfg.lsp.servers; }) ]); } diff --git a/modules/plugins/languages/astro.nix b/modules/plugins/languages/astro.nix index 21b6e5a3..2cf6ee04 100644 --- a/modules/plugins/languages/astro.nix +++ b/modules/plugins/languages/astro.nix @@ -1,4 +1,5 @@ { + self, config, pkgs, lib, @@ -7,49 +8,54 @@ inherit (builtins) attrNames; inherit (lib.options) mkEnableOption mkOption; inherit (lib.modules) mkIf mkMerge; - inherit (lib.lists) isList; inherit (lib.meta) getExe; - inherit (lib.types) enum either listOf package str; - inherit (lib.nvim.lua) expToLua; - inherit (lib.nvim.types) mkGrammarOption diagnostics; + inherit (lib.types) enum coercedTo; + inherit (lib.nvim.attrsets) mapListToAttrs; + inherit (lib.nvim.types) mkGrammarOption diagnostics deprecatedSingleOrListOf; + inherit (lib.generators) mkLuaInline; cfg = config.vim.languages.astro; - defaultServer = "astro"; + defaultServers = ["astro"]; servers = { astro = { - package = pkgs.astro-language-server; - lspConfig = '' - lspconfig.astro.setup { - capabilities = capabilities, - on_attach = attach_keymaps, - cmd = ${ - if isList cfg.lsp.package - then expToLua cfg.lsp.package - else ''{"${cfg.lsp.package}/bin/astro-ls", "--stdio"}'' - } - } - ''; + enable = true; + cmd = [(getExe pkgs.astro-language-server) "--stdio"]; + filetypes = ["astro"]; + root_markers = ["package.json" "tsconfig.json" "jsconfig.json" ".git"]; + init_options = { + typescript = {}; + }; + before_init = + mkLuaInline + /* + lua + */ + '' + function(_, config) + if config.init_options and config.init_options.typescript and not config.init_options.typescript.tsdk then + config.init_options.typescript.tsdk = util.get_typescript_server_path(config.root_dir) + end + end + ''; }; }; - # TODO: specify packages - defaultFormat = "prettier"; - formats = { + defaultFormat = ["prettier"]; + formats = let + parser = "${self.packages.${pkgs.stdenv.hostPlatform.system}.prettier-plugin-astro}/index.js"; + in { prettier = { - package = pkgs.prettier; - }; - - prettierd = { - package = pkgs.prettierd; + command = getExe pkgs.prettier; + options.ft_parsers.astro = "astro"; + prepend_args = ["--plugin=${parser}"]; }; biome = { - package = pkgs.biome; + command = getExe pkgs.biome; }; }; - # TODO: specify packages defaultDiagnosticsProvider = ["eslint_d"]; diagnosticsProviders = { eslint_d = let @@ -69,6 +75,15 @@ }; }; }; + + formatType = + deprecatedSingleOrListOf + "vim.languages.astro.format.type" + (coercedTo (enum ["prettierd"]) (_: + lib.warn + "vim.languages.astro.format.type: prettierd is deprecated, use prettier instead" + "prettier") + (enum (attrNames formats))); in { options.vim.languages.astro = { enable = mkEnableOption "Astro language support"; @@ -81,34 +96,20 @@ in { lsp = { enable = mkEnableOption "Astro LSP support" // {default = config.vim.lsp.enable;}; - - server = mkOption { - type = enum (attrNames servers); - default = defaultServer; + servers = mkOption { + type = deprecatedSingleOrListOf "vim.language.astro.lsp.servers" (enum (attrNames servers)); + default = defaultServers; description = "Astro LSP server to use"; }; - - package = mkOption { - type = either package (listOf str); - default = servers.${cfg.lsp.server}.package; - example = ''[lib.getExe pkgs.astro-language-server "--minify" "--stdio"]''; - description = "Astro LSP server package, or the command to run as a list of strings"; - }; }; format = { enable = mkEnableOption "Astro formatting" // {default = config.vim.languages.enableFormat;}; type = mkOption { - description = "Astro formatter to use"; - type = enum (attrNames formats); + type = formatType; default = defaultFormat; - }; - - package = mkOption { - description = "Astro formatter package"; - type = package; - default = formats.${cfg.format.type}.package; + description = "Astro formatter to use"; }; }; @@ -130,16 +131,25 @@ in { }) (mkIf cfg.lsp.enable { - vim.lsp.lspconfig.enable = true; - vim.lsp.lspconfig.sources.astro-lsp = servers.${cfg.lsp.server}.lspConfig; + vim.lsp.servers = + mapListToAttrs (n: { + name = n; + value = servers.${n}; + }) + cfg.lsp.servers; }) (mkIf cfg.format.enable { vim.formatter.conform-nvim = { enable = true; - setupOpts.formatters_by_ft.astro = [cfg.format.type]; - setupOpts.formatters.${cfg.format.type} = { - command = getExe cfg.format.package; + setupOpts = { + formatters_by_ft.astro = cfg.format.type; + formatters = + mapListToAttrs (name: { + inherit name; + value = formats.${name}; + }) + cfg.format.type; }; }; }) diff --git a/modules/plugins/languages/bash.nix b/modules/plugins/languages/bash.nix index 9a5e3e13..467f3f63 100644 --- a/modules/plugins/languages/bash.nix +++ b/modules/plugins/languages/bash.nix @@ -5,38 +5,35 @@ ... }: let inherit (builtins) attrNames; - inherit (lib.options) mkOption mkEnableOption literalExpression; + inherit (lib.options) mkOption mkEnableOption; inherit (lib.meta) getExe; inherit (lib.modules) mkIf mkMerge; - inherit (lib.lists) isList; - inherit (lib.types) enum either package listOf str bool; - inherit (lib.nvim.types) diagnostics mkGrammarOption; - inherit (lib.nvim.lua) expToLua; + inherit (lib.types) enum bool; + inherit (lib.generators) mkLuaInline; + inherit (lib.nvim.types) diagnostics mkGrammarOption deprecatedSingleOrListOf; + inherit (lib.nvim.attrsets) mapListToAttrs; cfg = config.vim.languages.bash; - defaultServer = "bash-ls"; + defaultServers = ["bash-ls"]; servers = { bash-ls = { - package = pkgs.bash-language-server; - lspConfig = '' - lspconfig.bashls.setup{ - capabilities = capabilities; - on_attach = default_on_attach; - cmd = ${ - if isList cfg.lsp.package - then expToLua cfg.lsp.package - else ''{"${cfg.lsp.package}/bin/bash-language-server", "start"}'' + enable = true; + cmd = [(getExe pkgs.bash-language-server) "start"]; + filetypes = ["bash" "sh"]; + root_markers = [".git"]; + settings = { + basheIde = { + globPattern = mkLuaInline "vim.env.GLOB_PATTERN or '*@(.sh|.inc|.bash|.command)'"; }; - } - ''; + }; }; }; - defaultFormat = "shfmt"; + defaultFormat = ["shfmt"]; formats = { shfmt = { - package = pkgs.shfmt; + command = getExe pkgs.shfmt; }; }; @@ -56,38 +53,24 @@ in { }; lsp = { - enable = mkEnableOption "Enable Bash LSP support" // {default = config.vim.lsp.enable;}; - - server = mkOption { + enable = mkEnableOption "Bash LSP support" // {default = config.vim.lsp.enable;}; + servers = mkOption { + type = deprecatedSingleOrListOf "vim.language.bash.lsp.servers" (enum (attrNames servers)); + default = defaultServers; description = "Bash LSP server to use"; - type = enum (attrNames servers); - default = defaultServer; - }; - - package = mkOption { - description = "bash-language-server package, or the command to run as a list of strings"; - example = literalExpression ''[lib.getExe pkgs.bash-language-server "start"]''; - type = either package (listOf str); - default = pkgs.bash-language-server; }; }; format = { enable = mkOption { - description = "Enable Bash formatting"; type = bool; default = config.vim.languages.enableFormat; + description = "Enable Bash formatting"; }; type = mkOption { - description = "Bash formatter to use"; - type = enum (attrNames formats); + type = deprecatedSingleOrListOf "vim.language.bash.format.type" (enum (attrNames formats)); default = defaultFormat; - }; - - package = mkOption { - description = "Bash formatter package"; - type = package; - default = formats.${cfg.format.type}.package; + description = "Bash formatter to use"; }; }; @@ -108,16 +91,25 @@ in { }) (mkIf cfg.lsp.enable { - vim.lsp.lspconfig.enable = true; - vim.lsp.lspconfig.sources.bash-lsp = servers.${cfg.lsp.server}.lspConfig; + vim.lsp.servers = + mapListToAttrs (n: { + name = n; + value = servers.${n}; + }) + cfg.lsp.servers; }) (mkIf cfg.format.enable { vim.formatter.conform-nvim = { enable = true; - setupOpts.formatters_by_ft.sh = [cfg.format.type]; - setupOpts.formatters.${cfg.format.type} = { - command = getExe cfg.format.package; + setupOpts = { + formatters_by_ft.sh = cfg.format.type; + formatters = + mapListToAttrs (name: { + inherit name; + value = formats.${name}; + }) + cfg.format.type; }; }; }) diff --git a/modules/plugins/languages/clang.nix b/modules/plugins/languages/clang.nix index 2db178e9..cfc86131 100644 --- a/modules/plugins/languages/clang.nix +++ b/modules/plugins/languages/clang.nix @@ -5,47 +5,139 @@ ... }: let inherit (builtins) attrNames; - inherit (lib.lists) isList; - inherit (lib.strings) optionalString; inherit (lib.options) mkEnableOption mkOption; - inherit (lib.types) bool enum package either listOf str nullOr; + inherit (lib.types) bool enum package; + inherit (lib.meta) getExe; inherit (lib.modules) mkIf mkMerge; - inherit (lib.nvim.lua) expToLua; - inherit (lib.nvim.types) mkGrammarOption; + inherit (lib.generators) mkLuaInline; + inherit (lib.nvim.types) mkGrammarOption deprecatedSingleOrListOf; + inherit (lib.nvim.attrsets) mapListToAttrs; inherit (lib.nvim.dag) entryAfter; - packageToCmd = package: defaultCmd: - if isList cfg.lsp.package - then expToLua cfg.lsp.package - else ''{ "${cfg.lsp.package}/bin/${defaultCmd}" }''; - cfg = config.vim.languages.clang; - defaultServer = "clangd"; + defaultServers = ["clangd"]; servers = { ccls = { - package = pkgs.ccls; - lspConfig = '' - lspconfig.ccls.setup{ - capabilities = capabilities; - on_attach=default_on_attach; - cmd = ${packageToCmd cfg.lsp.package "ccls"}; - ${optionalString (cfg.lsp.opts != null) "init_options = ${cfg.lsp.opts}"} - } + cmd = [(getExe pkgs.ccls)]; + filetypes = ["c" "cpp" "objc" "objcpp" "cuda"]; + offset_encoding = "utf-32"; + root_markers = ["compile_commands.json" ".ccls" ".git"]; + workspace_required = true; + on_attach = mkLuaInline '' + function(client, bufnr) + local function switch_source_header(bufnr) + local method_name = "textDocument/switchSourceHeader" + local params = vim.lsp.util.make_text_document_params(bufnr) + client:request(method_name, params, function(err, result) + if err then + error(tostring(err)) + end + if not result then + vim.notify('corresponding file cannot be determined') + return + end + vim.cmd.edit(vim.uri_to_fname(result)) + end, bufnr) + end + + vim.api.nvim_buf_create_user_command( + bufnr, + "LspCclsSwitchSourceHeader", + function(arg) + switch_source_header(client, 0) + end, + {desc = "Switch between source/header"} + ) + end ''; }; + clangd = { - package = pkgs.clang-tools; - lspConfig = '' - local clangd_cap = capabilities - -- use same offsetEncoding as null-ls - clangd_cap.offsetEncoding = {"utf-16"} - lspconfig.clangd.setup{ - capabilities = clangd_cap; - on_attach=default_on_attach; - cmd = ${packageToCmd cfg.lsp.package "clangd"}; - ${optionalString (cfg.lsp.opts != null) "init_options = ${cfg.lsp.opts}"} - } + cmd = ["${pkgs.clang-tools}/bin/clangd"]; + filetypes = ["c" "cpp" "objc" "objcpp" "cuda" "proto"]; + root_markers = [ + ".clangd" + ".clang-tidy" + ".clang-format" + "compile_commands.json" + "compile_flags.txt" + "configure.ac" + ".git" + ]; + capabilities = { + textDocument = { + completion = { + editsNearCursor = true; + }; + }; + offsetEncoding = ["utf-8" "utf-16"]; + }; + on_attach = mkLuaInline '' + function(client, bufnr) + local function switch_source_header(bufnr) + local method_name = "textDocument/switchSourceHeader" + local client = vim.lsp.get_clients({ bufnr = bufnr, name = "clangd", })[1] + if not client then + return vim.notify(('method %s is not supported by any servers active on the current buffer'):format(method_name)) + end + local params = vim.lsp.util.make_text_document_params(bufnr) + client.request(method_name, params, function(err, result) + if err then + error(tostring(err)) + end + if not result then + vim.notify('corresponding file cannot be determined') + return + end + vim.cmd.edit(vim.uri_to_fname(result)) + end, bufnr) + end + + local function symbol_info() + local bufnr = vim.api.nvim_get_current_buf() + local clangd_client = vim.lsp.get_clients({ bufnr = bufnr, name = "clangd" })[1] + if not clangd_client or not clangd_client.supports_method 'textDocument/symbolInfo' then + return vim.notify('Clangd client not found', vim.log.levels.ERROR) + end + local win = vim.api.nvim_get_current_win() + local params = vim.lsp.util.make_position_params(win, clangd_client.offset_encoding) + clangd_client:request('textDocument/symbolInfo', params, function(err, res) + if err or #res == 0 then + -- Clangd always returns an error, there is not reason to parse it + return + end + local container = string.format('container: %s', res[1].containerName) ---@type string + local name = string.format('name: %s', res[1].name) ---@type string + vim.lsp.util.open_floating_preview({ name, container }, "", { + height = 2, + width = math.max(string.len(name), string.len(container)), + focusable = false, + focus = false, + border = 'single', + title = 'Symbol Info', + }) + end, bufnr) + end + + vim.api.nvim_buf_create_user_command( + bufnr, + "ClangdSwitchSourceHeader", + function(arg) + switch_source_header(0) + end, + {desc = "Switch between source/header"} + ) + + vim.api.nvim_buf_create_user_command( + bufnr, + "ClangdShowSymbolInfo", + function(arg) + symbol_info() + end, + {desc = "Show symbol info"} + ) + end ''; }; }; @@ -100,23 +192,10 @@ in { lsp = { enable = mkEnableOption "clang LSP support" // {default = config.vim.lsp.enable;}; - server = mkOption { + servers = mkOption { description = "The clang LSP server to use"; - type = enum (attrNames servers); - default = defaultServer; - }; - - package = mkOption { - description = "clang LSP server package, or the command to run as a list of strings"; - example = ''[lib.getExe pkgs.jdt-language-server " - data " " ~/.cache/jdtls/workspace "]''; - type = either package (listOf str); - default = servers.${cfg.lsp.server}.package; - }; - - opts = mkOption { - description = "Options to pass to clang LSP server"; - type = nullOr str; - default = null; + type = deprecatedSingleOrListOf "vim.language.clang.lsp.servers" (enum (attrNames servers)); + default = defaultServers; }; }; @@ -150,9 +229,12 @@ in { }) (mkIf cfg.lsp.enable { - vim.lsp.lspconfig.enable = true; - - vim.lsp.lspconfig.sources.clang-lsp = servers.${cfg.lsp.server}.lspConfig; + vim.lsp.servers = + mapListToAttrs (name: { + inherit name; + value = servers.${name}; + }) + cfg.lsp.servers; }) (mkIf cfg.dap.enable { diff --git a/modules/plugins/languages/clojure.nix b/modules/plugins/languages/clojure.nix index 0b932708..53c7fbbe 100644 --- a/modules/plugins/languages/clojure.nix +++ b/modules/plugins/languages/clojure.nix @@ -4,15 +4,25 @@ lib, ... }: let + inherit (builtins) attrNames; inherit (lib.options) mkEnableOption mkOption; inherit (lib.modules) mkIf mkMerge; inherit (lib.meta) getExe; - inherit (lib.lists) isList; - inherit (lib.types) either listOf package str; + inherit (lib.types) enum listOf; inherit (lib.nvim.types) mkGrammarOption; - inherit (lib.nvim.lua) expToLua; + inherit (lib.nvim.attrsets) mapListToAttrs; cfg = config.vim.languages.clojure; + + defaultServers = ["clojure-lsp"]; + servers = { + clojure-lsp = { + enable = true; + cmd = [(getExe pkgs.clojure-lsp)]; + filetypes = ["clojure" "edn"]; + root_markers = ["project.clj" "deps.edn" "build.boot" "shadow-cljs.edn" ".git" "bb.edn"]; + }; + }; in { options.vim.languages.clojure = { enable = mkEnableOption "Clojure language support"; @@ -24,28 +34,22 @@ in { lsp = { enable = mkEnableOption "Clojure LSP support" // {default = config.vim.lsp.enable;}; - package = mkOption { - type = either package (listOf str); - default = pkgs.clojure-lsp; - description = "Clojure LSP"; + servers = mkOption { + type = listOf (enum (attrNames servers)); + default = defaultServers; + description = "Clojure LSP server to use"; }; }; }; config = mkIf cfg.enable (mkMerge [ (mkIf cfg.lsp.enable { - vim.lsp.lspconfig.enable = true; - vim.lsp.lspconfig.sources.clojure-lsp = '' - lspconfig.clojure_lsp.setup { - capabilities = capabilities; - on_attach = default_on_attach; - cmd = ${ - if isList cfg.lsp.package - then expToLua cfg.lsp.package - else ''{"${getExe cfg.lsp.package}"}'' - }; - } - ''; + vim.lsp.servers = + mapListToAttrs (n: { + name = n; + value = servers.${n}; + }) + cfg.lsp.servers; }) (mkIf cfg.treesitter.enable { diff --git a/modules/plugins/languages/csharp.nix b/modules/plugins/languages/csharp.nix index 49ae4985..20e5bd72 100644 --- a/modules/plugins/languages/csharp.nix +++ b/modules/plugins/languages/csharp.nix @@ -5,14 +5,16 @@ options, ... }: let - inherit (builtins) attrNames; + inherit (builtins) attrNames concatMap; inherit (lib.options) mkEnableOption mkOption; - inherit (lib.types) either listOf package str enum; + inherit (lib.types) enum; inherit (lib.modules) mkIf mkMerge; - inherit (lib.lists) isList; + inherit (lib.meta) getExe; + inherit (lib.generators) mkLuaInline; inherit (lib.strings) optionalString; - inherit (lib.nvim.types) mkGrammarOption; - inherit (lib.nvim.lua) expToLua; + inherit (lib.nvim.types) mkGrammarOption deprecatedSingleOrListOf; + inherit (lib.nvim.lua) toLuaObject; + inherit (lib.nvim.attrsets) mapListToAttrs; lspKeyConfig = config.vim.lsp.mappings; lspKeyOptions = options.vim.lsp.mappings; @@ -25,58 +27,147 @@ # Omnisharp doesn't have colors in popup docs for some reason, and I've also # seen mentions of it being way slower, so until someone finds missing # functionality, this will be the default. - defaultServer = "csharp_ls"; + defaultServers = ["csharp_ls"]; servers = { omnisharp = { - package = pkgs.omnisharp-roslyn; - internalFormatter = true; - lspConfig = '' - lspconfig.omnisharp.setup { - capabilities = capabilities, - on_attach = function(client, bufnr) - default_on_attach(client, bufnr) - - local oe = require("omnisharp_extended") - ${mkLspBinding "goToDefinition" "oe.lsp_definition"} - ${mkLspBinding "goToType" "oe.lsp_type_definition"} - ${mkLspBinding "listReferences" "oe.lsp_references"} - ${mkLspBinding "listImplementations" "oe.lsp_implementation"} - end, - cmd = ${ - if isList cfg.lsp.package - then expToLua cfg.lsp.package - else "{'${cfg.lsp.package}/bin/OmniSharp'}" - } + cmd = mkLuaInline '' + { + ${toLuaObject (getExe pkgs.omnisharp-roslyn)}, + '-z', -- https://github.com/OmniSharp/omnisharp-vscode/pull/4300 + '--hostPID', + tostring(vim.fn.getpid()), + 'DotNet:enablePackageRestore=false', + '--encoding', + 'utf-8', + '--languageserver', } ''; + filetypes = ["cs" "vb"]; + root_dir = mkLuaInline '' + function(bufnr, on_dir) + local function find_root_pattern(fname, lua_pattern) + return vim.fs.root(0, function(name, path) + return name:match(lua_pattern) + end) + end + + local fname = vim.api.nvim_buf_get_name(bufnr) + on_dir(find_root_pattern(fname, "%.sln$") or find_root_pattern(fname, "%.csproj$")) + end + ''; + init_options = {}; + capabilities = { + workspace = { + workspaceFolders = false; # https://github.com/OmniSharp/omnisharp-roslyn/issues/909 + }; + }; + on_attach = mkLuaInline '' + function(client, bufnr) + local oe = require("omnisharp_extended") + ${mkLspBinding "goToDefinition" "oe.lsp_definition"} + ${mkLspBinding "goToType" "oe.lsp_type_definition"} + ${mkLspBinding "listReferences" "oe.lsp_references"} + ${mkLspBinding "listImplementations" "oe.lsp_implementation"} + end + ''; + settings = { + FormattingOptions = { + # Enables support for reading code style, naming convention and analyzer + # settings from .editorconfig. + EnableEditorConfigSupport = true; + # Specifies whether 'using' directives should be grouped and sorted during + # document formatting. + OrganizeImports = null; + }; + MsBuild = { + # If true, MSBuild project system will only load projects for files that + # were opened in the editor. This setting is useful for big C# codebases + # and allows for faster initialization of code navigation features only + # for projects that are relevant to code that is being edited. With this + # setting enabled OmniSharp may load fewer projects and may thus display + # incomplete reference lists for symbols. + LoadProjectsOnDemand = null; + }; + RoslynExtensionsOptions = { + # Enables support for roslyn analyzers, code fixes and rulesets. + EnableAnalyzersSupport = null; + # Enables support for showing unimported types and unimported extension + # methods in completion lists. When committed, the appropriate using + # directive will be added at the top of the current file. This option can + # have a negative impact on initial completion responsiveness; + # particularly for the first few completion sessions after opening a + # solution. + EnableImportCompletion = null; + # Only run analyzers against open files when 'enableRoslynAnalyzers' is + # true + AnalyzeOpenDocumentsOnly = null; + # Enables the possibility to see the code in external nuget dependencies + EnableDecompilationSupport = null; + }; + RenameOptions = { + RenameInComments = null; + RenameOverloads = null; + RenameInStrings = null; + }; + Sdk = { + # Specifies whether to include preview versions of the .NET SDK when + # determining which version to use for project loading. + IncludePrereleases = true; + }; + }; }; csharp_ls = { - package = pkgs.csharp-ls; - internalFormatter = true; - lspConfig = '' - local extended_handler = require("csharpls_extended").handler + cmd = [(lib.getExe pkgs.csharp-ls)]; + filetypes = ["cs"]; + root_dir = mkLuaInline '' + function(bufnr, on_dir) + local function find_root_pattern(fname, lua_pattern) + return vim.fs.root(0, function(name, path) + return name:match(lua_pattern) + end) + end - lspconfig.csharp_ls.setup { - capabilities = capabilities, - on_attach = default_on_attach, - handlers = { - ["textDocument/definition"] = extended_handler, - ["textDocument/typeDefinition"] = extended_handler - }, - cmd = ${ - if isList cfg.lsp.package - then expToLua cfg.lsp.package - else "{'${cfg.lsp.package}/bin/csharp-ls'}" - } + local fname = vim.api.nvim_buf_get_name(bufnr) + on_dir(find_root_pattern(fname, "%.sln$") or find_root_pattern(fname, "%.csproj$")) + end + ''; + init_options = { + AutomaticWorkspaceInit = true; + }; + }; + + roslyn_ls = { + cmd = mkLuaInline '' + { + ${toLuaObject (getExe pkgs.roslyn-ls)}, + '--logLevel=Warning', + '--extensionLogDirectory=' .. vim.fs.dirname(vim.lsp.get_log_path()), + '--stdio', } ''; + + filetypes = ["cs"]; + root_dir = mkLuaInline '' + function(bufnr, on_dir) + local function find_root_pattern(fname, lua_pattern) + return vim.fs.root(0, function(name, path) + return name:match(lua_pattern) + end) + end + + local fname = vim.api.nvim_buf_get_name(bufnr) + on_dir(find_root_pattern(fname, "%.sln$") or find_root_pattern(fname, "%.csproj$")) + end + ''; + init_options = {}; }; }; extraServerPlugins = { omnisharp = ["omnisharp-extended-lsp-nvim"]; csharp_ls = ["csharpls-extended-lsp-nvim"]; + roslyn_ls = []; }; cfg = config.vim.languages.csharp; @@ -92,16 +183,10 @@ in { lsp = { enable = mkEnableOption "C# LSP support" // {default = config.vim.lsp.enable;}; - server = mkOption { + servers = mkOption { description = "C# LSP server to use"; - type = enum (attrNames servers); - default = defaultServer; - }; - - package = mkOption { - description = "C# LSP server package, or the command to run as a list of strings"; - type = either package (listOf str); - default = servers.${cfg.lsp.server}.package; + type = deprecatedSingleOrListOf "vim.language.csharp.lsp.servers" (enum (attrNames servers)); + default = defaultServers; }; }; }; @@ -114,9 +199,13 @@ in { }) (mkIf cfg.lsp.enable { - vim.startPlugins = extraServerPlugins.${cfg.lsp.server} or []; - vim.lsp.lspconfig.enable = true; - vim.lsp.lspconfig.sources.csharp-lsp = servers.${cfg.lsp.server}.lspConfig; + vim.startPlugins = concatMap (server: extraServerPlugins.${server}) cfg.lsp.servers; + vim.lsp.servers = + mapListToAttrs (name: { + inherit name; + value = servers.${name}; + }) + cfg.lsp.servers; }) ]); } diff --git a/modules/plugins/languages/css.nix b/modules/plugins/languages/css.nix index 1075bbc7..28e94151 100644 --- a/modules/plugins/languages/css.nix +++ b/modules/plugins/languages/css.nix @@ -8,65 +8,40 @@ inherit (lib.options) mkEnableOption mkOption; inherit (lib.meta) getExe; inherit (lib.modules) mkIf mkMerge; - inherit (lib.lists) isList; - inherit (lib.types) enum either listOf package str; - inherit (lib.nvim.types) mkGrammarOption; - inherit (lib.nvim.lua) expToLua; + inherit (lib.types) enum; + inherit (lib.nvim.types) mkGrammarOption deprecatedSingleOrListOf; + inherit (lib.nvim.attrsets) mapListToAttrs; cfg = config.vim.languages.css; - defaultServer = "vscode-langservers-extracted"; + defaultServer = ["cssls"]; servers = { - vscode-langservers-extracted = { - package = pkgs.vscode-langservers-extracted; - lspConfig = '' - -- enable (broadcasting) snippet capability for completion - -- see - local css_capabilities = vim.lsp.protocol.make_client_capabilities() - css_capabilities.textDocument.completion.completionItem.snippetSupport = true - - -- cssls setup - lspconfig.cssls.setup { - capabilities = css_capabilities; - on_attach = default_on_attach; - cmd = ${ - if isList cfg.lsp.package - then expToLua cfg.lsp.package - else ''{"${cfg.lsp.package}/bin/vscode-css-language-server", "--stdio"}'' - } - } - ''; + cssls = { + cmd = ["${pkgs.vscode-langservers-extracted}/bin/vscode-css-language-server" "--stdio"]; + filetypes = ["css" "scss" "less"]; + # needed to enable formatting + init_options = {provideFormatter = true;}; + root_markers = [".git" "package.json"]; + settings = { + css.validate = true; + scss.validate = true; + less.validate = true; + }; }; }; - defaultFormat = "prettier"; + defaultFormat = ["prettier"]; formats = { prettier = { - package = pkgs.prettier; + command = getExe pkgs.prettier; }; prettierd = { - package = pkgs.prettierd; - nullConfig = '' - table.insert( - ls_sources, - null_ls.builtins.formatting.prettier.with({ - command = "${cfg.format.package}/bin/prettierd", - }) - ) - ''; + command = getExe pkgs.prettierd; }; biome = { - package = pkgs.biome; - nullConfig = '' - table.insert( - ls_sources, - null_ls.builtins.formatting.biome.with({ - command = "${cfg.format.package}/bin/biome", - }) - ) - ''; + command = getExe pkgs.biome; }; }; in { @@ -82,17 +57,10 @@ in { lsp = { enable = mkEnableOption "CSS LSP support" // {default = config.vim.lsp.enable;}; - server = mkOption { - description = "CSS LSP server to use"; - type = enum (attrNames servers); + servers = mkOption { + type = deprecatedSingleOrListOf "vim.language.css.lsp.servers" (enum (attrNames servers)); default = defaultServer; - }; - - package = mkOption { - description = "CSS LSP server package, or the command to run as a list of strings"; - example = ''[lib.getExe pkgs.jdt-language-server " - data " " ~/.cache/jdtls/workspace "]''; - type = either package (listOf str); - default = servers.${cfg.lsp.server}.package; + description = "CSS LSP server to use"; }; }; @@ -101,15 +69,9 @@ in { type = mkOption { description = "CSS formatter to use"; - type = enum (attrNames formats); + type = deprecatedSingleOrListOf "vim.language.css.format.type" (enum (attrNames formats)); default = defaultFormat; }; - - package = mkOption { - description = "CSS formatter package"; - type = package; - default = formats.${cfg.format.type}.package; - }; }; }; @@ -120,16 +82,25 @@ in { }) (mkIf cfg.lsp.enable { - vim.lsp.lspconfig.enable = true; - vim.lsp.lspconfig.sources.css-lsp = servers.${cfg.lsp.server}.lspConfig; + vim.lsp.servers = + mapListToAttrs (name: { + inherit name; + value = servers.${name}; + }) + cfg.lsp.servers; }) (mkIf cfg.format.enable { vim.formatter.conform-nvim = { enable = true; - setupOpts.formatters_by_ft.css = [cfg.format.type]; - setupOpts.formatters.${cfg.format.type} = { - command = getExe cfg.format.package; + setupOpts = { + formatters_by_ft.css = cfg.format.type; + formatters = + mapListToAttrs (name: { + inherit name; + value = formats.${name}; + }) + cfg.format.type; }; }; }) diff --git a/modules/plugins/languages/cue.nix b/modules/plugins/languages/cue.nix index bd446cbf..a22ff91c 100644 --- a/modules/plugins/languages/cue.nix +++ b/modules/plugins/languages/cue.nix @@ -4,11 +4,17 @@ lib, ... }: let - inherit (lib.options) mkEnableOption mkOption; + inherit (lib.options) mkEnableOption; + inherit (lib.meta) getExe; inherit (lib.modules) mkIf mkMerge; - inherit (lib.types) package; inherit (lib.nvim.types) mkGrammarOption; + lspOptions = { + cmd = [(getExe pkgs.cue) "lsp"]; + filetypes = ["cue"]; + root_markers = ["cue.mod" ".git"]; + }; + cfg = config.vim.languages.cue; in { options.vim.languages.cue = { @@ -22,12 +28,6 @@ in { lsp = { enable = mkEnableOption "CUE LSP support" // {default = config.vim.lsp.enable;}; - - package = mkOption { - type = package; - default = pkgs.cue; - description = "cue lsp implementation"; - }; }; }; @@ -38,14 +38,7 @@ in { }) (mkIf cfg.lsp.enable { - vim.lsp.lspconfig.enable = true; - vim.lsp.lspconfig.sources.cue-lsp = '' - lspconfig.cue.setup { - capabilities = capabilities, - on_attach = default_on_attach, - cmd = {"${cfg.lsp.package}/bin/cue", "lsp"}, - } - ''; + vim.lsp.servers.cue = lspOptions; }) ]); } diff --git a/modules/plugins/languages/dart.nix b/modules/plugins/languages/dart.nix index c2a1810d..291999c2 100644 --- a/modules/plugins/languages/dart.nix +++ b/modules/plugins/languages/dart.nix @@ -6,34 +6,38 @@ }: let inherit (builtins) attrNames; inherit (lib.modules) mkIf mkMerge; + inherit (lib.meta) getExe; inherit (lib.trivial) boolToString; - inherit (lib.lists) isList; inherit (lib.options) mkEnableOption mkOption; - inherit (lib.types) enum either listOf package nullOr str bool; + inherit (lib.types) enum package nullOr str bool; inherit (lib.strings) optionalString; - inherit (lib.nvim.lua) expToLua; - inherit (lib.nvim.types) mkGrammarOption; + inherit (lib.nvim.types) mkGrammarOption deprecatedSingleOrListOf; inherit (lib.nvim.dag) entryAfter; + inherit (lib.nvim.attrsets) mapListToAttrs; cfg = config.vim.languages.dart; ftcfg = cfg.flutter-tools; - defaultServer = "dart"; + defaultServers = ["dart"]; servers = { dart = { - package = pkgs.dart; - lspConfig = '' - lspconfig.dartls.setup{ - capabilities = capabilities; - on_attach=default_on_attach; - cmd = ${ - if isList cfg.lsp.package - then expToLua cfg.lsp.package - else ''{"${cfg.lsp.package}/bin/dart", "language-server", "--protocol=lsp"}'' + enable = true; + cmd = [(getExe pkgs.dart) "language-server" "--protocol=lsp"]; + filetypes = ["dart"]; + root_markers = ["pubspec.yaml"]; + init_options = { + onlyAnalyzeProjectsWithOpenFiles = true; + suggestFromUnimportedLibraries = true; + closingLabels = true; + outline = true; + flutterOutline = true; + }; + settings = { + dart = { + completeFunctionCalls = true; + showTodos = true; }; - ${optionalString (cfg.lsp.opts != null) "init_options = ${cfg.lsp.dartOpts}"} - } - ''; + }; }; }; in { @@ -46,23 +50,11 @@ in { }; lsp = { - enable = mkEnableOption "Dart LSP support"; - server = mkOption { - description = "The Dart LSP server to use"; - type = enum (attrNames servers); - default = defaultServer; - }; - package = mkOption { - type = either package (listOf str); - default = servers.${cfg.lsp.server}.package; - example = ''[lib.getExe pkgs.jdt-language-server "-data" "~/.cache/jdtls/workspace"]''; - description = "Dart LSP server package, or the command to run as a list of strings"; - }; - - opts = mkOption { - type = nullOr str; - default = null; - description = "Options to pass to Dart LSP server"; + enable = mkEnableOption "Dart LSP support" // {default = config.vim.lsp.enable;}; + servers = mkOption { + type = deprecatedSingleOrListOf "vim.language.dart.lsp.servers" (enum (attrNames servers)); + default = defaultServers; + description = "Dart LSP server to use"; }; }; @@ -131,19 +123,23 @@ in { }; }; - config.vim = mkIf cfg.enable (mkMerge [ + config = mkIf cfg.enable (mkMerge [ (mkIf cfg.treesitter.enable { - treesitter.enable = true; - treesitter.grammars = [cfg.treesitter.package]; + vim.treesitter.enable = true; + vim.treesitter.grammars = [cfg.treesitter.package]; }) (mkIf cfg.lsp.enable { - lsp.lspconfig.enable = true; - lsp.lspconfig.sources.dart-lsp = servers.${cfg.lsp.server}.lspConfig; + vim.lsp.servers = + mapListToAttrs (n: { + name = n; + value = servers.${n}; + }) + cfg.lsp.servers; }) (mkIf ftcfg.enable { - startPlugins = [ + vim.startPlugins = [ ( if ftcfg.enableNoResolvePatch then "flutter-tools-patched" @@ -152,7 +148,7 @@ in { "plenary-nvim" ]; - pluginRC.flutter-tools = entryAfter ["lsp-setup"] '' + vim.pluginRC.flutter-tools = entryAfter ["lsp-servers"] '' require('flutter-tools').setup { ${optionalString (ftcfg.flutterPackage != null) "flutter_path = \"${ftcfg.flutterPackage}/bin/flutter\","} lsp = { @@ -165,7 +161,6 @@ in { }, capabilities = capabilities, - on_attach = default_on_attach; }, ${optionalString cfg.dap.enable '' debugger = { diff --git a/modules/plugins/languages/default.nix b/modules/plugins/languages/default.nix index 961d7cc5..fd45758f 100644 --- a/modules/plugins/languages/default.nix +++ b/modules/plugins/languages/default.nix @@ -21,6 +21,7 @@ in { ./html.nix ./haskell.nix ./java.nix + ./json.nix ./lua.nix ./markdown.nix ./nim.nix @@ -29,6 +30,7 @@ in { ./ocaml.nix ./php.nix ./python.nix + ./qml.nix ./r.nix ./rust.nix ./scala.nix @@ -46,6 +48,7 @@ in { ./wgsl.nix ./yaml.nix ./ruby.nix + ./just.nix # This is now a hard deprecation. (mkRenamedOptionModule ["vim" "languages" "enableLSP"] ["vim" "lsp" "enable"]) diff --git a/modules/plugins/languages/elixir.nix b/modules/plugins/languages/elixir.nix index 241efd8e..0aa32453 100644 --- a/modules/plugins/languages/elixir.nix +++ b/modules/plugins/languages/elixir.nix @@ -7,40 +7,43 @@ inherit (builtins) attrNames; inherit (lib.options) mkEnableOption mkOption; inherit (lib.modules) mkIf mkMerge; - inherit (lib.lists) isList; - inherit (lib.types) enum either listOf package str; - inherit (lib.nvim.types) mkGrammarOption; - inherit (lib.nvim.lua) expToLua; + inherit (lib.meta) getExe; + inherit (lib.types) enum; + inherit (lib.generators) mkLuaInline; + inherit (lib.nvim.types) mkGrammarOption deprecatedSingleOrListOf; inherit (lib.nvim.dag) entryAnywhere; + inherit (lib.nvim.attrsets) mapListToAttrs; cfg = config.vim.languages.elixir; - defaultServer = "elixirls"; + defaultServers = ["elixirls"]; servers = { elixirls = { - package = pkgs.elixir-ls; - lspConfig = '' - -- elixirls setup - lspconfig.elixirls.setup { - capabilities = capabilities, - on_attach = default_on_attach, - cmd = ${ - if isList cfg.lsp.package - then expToLua cfg.lsp.package - else ''{"${cfg.lsp.package}/bin/elixir-ls"}'' - } - } - ''; + enable = true; + cmd = [(getExe pkgs.elixir-ls)]; + filetypes = ["elixir" "eelixir" "heex" "surface"]; + root_dir = + mkLuaInline + /* + lua + */ + '' + function(bufnr, on_dir) + local fname = vim.api.nvim_buf_get_name(bufnr) + local matches = vim.fs.find({ 'mix.exs' }, { upward = true, limit = 2, path = fname }) + local child_or_root_path, maybe_umbrella_path = unpack(matches) + local root_dir = vim.fs.dirname(maybe_umbrella_path or child_or_root_path) + + on_dir(root_dir) + end + ''; }; }; - defaultFormat = "mix"; + defaultFormat = ["mix"]; formats = { mix = { - package = pkgs.elixir; - config = { - command = "${cfg.format.package}/bin/mix"; - }; + command = "${pkgs.elixir}/bin/mix"; }; }; in { @@ -50,22 +53,16 @@ in { treesitter = { enable = mkEnableOption "Elixir treesitter" // {default = config.vim.languages.enableTreesitter;}; package = mkGrammarOption pkgs "elixir"; + heexPackage = mkGrammarOption pkgs "heex"; + eexPackage = mkGrammarOption pkgs "eex"; }; lsp = { enable = mkEnableOption "Elixir LSP support" // {default = config.vim.lsp.enable;}; - - server = mkOption { + servers = mkOption { + type = deprecatedSingleOrListOf "vim.language.elixir.lsp.servers" (enum (attrNames servers)); + default = defaultServers; description = "Elixir LSP server to use"; - type = enum (attrNames servers); - default = defaultServer; - }; - - package = mkOption { - description = "Elixir LSP server package, or the command to run as a list of strings"; - example = ''[lib.getExe pkgs.jdt-language-server " - data " " ~/.cache/jdtls/workspace "]''; - type = either package (listOf str); - default = servers.${cfg.lsp.server}.package; }; }; @@ -73,15 +70,9 @@ in { enable = mkEnableOption "Elixir formatting" // {default = config.vim.languages.enableFormat;}; type = mkOption { - description = "Elixir formatter to use"; - type = enum (attrNames formats); + type = deprecatedSingleOrListOf "vim.language.elixir.format.type" (enum (attrNames formats)); default = defaultFormat; - }; - - package = mkOption { - description = "Elixir formatter package"; - type = package; - default = formats.${cfg.format.type}.package; + description = "Elixir formatter to use"; }; }; @@ -93,20 +84,34 @@ in { config = mkIf cfg.enable (mkMerge [ (mkIf cfg.treesitter.enable { vim.treesitter.enable = true; - vim.treesitter.grammars = [cfg.treesitter.package]; + vim.treesitter.grammars = [ + cfg.treesitter.package + cfg.treesitter.heexPackage + cfg.treesitter.eexPackage + ]; }) (mkIf cfg.lsp.enable { - vim.lsp.lspconfig.enable = true; - vim.lsp.lspconfig.sources.elixir-lsp = servers.${cfg.lsp.server}.lspConfig; + vim.lsp.servers = + mapListToAttrs (n: { + name = n; + value = servers.${n}; + }) + cfg.lsp.servers; }) (mkIf cfg.format.enable { vim.formatter.conform-nvim = { enable = true; - setupOpts.formatters_by_ft.elixir = [cfg.format.type]; - setupOpts.formatters.${cfg.format.type} = - formats.${cfg.format.type}.config; + setupOpts = { + formatters_by_ft.elixir = cfg.format.type; + formatters = + mapListToAttrs (name: { + inherit name; + value = formats.${name}; + }) + cfg.format.type; + }; }; }) diff --git a/modules/plugins/languages/fsharp.nix b/modules/plugins/languages/fsharp.nix index 6ed0ccab..19703022 100644 --- a/modules/plugins/languages/fsharp.nix +++ b/modules/plugins/languages/fsharp.nix @@ -6,36 +6,55 @@ }: let inherit (builtins) attrNames; inherit (lib.options) mkEnableOption mkOption; - inherit (lib.types) either listOf package str enum; + inherit (lib.types) enum; inherit (lib.meta) getExe; inherit (lib.modules) mkIf mkMerge; - inherit (lib.lists) isList; - inherit (lib.nvim.types) mkGrammarOption; - inherit (lib.nvim.lua) expToLua; + inherit (lib.generators) mkLuaInline; + inherit (lib.nvim.types) mkGrammarOption deprecatedSingleOrListOf; + inherit (lib.nvim.attrsets) mapListToAttrs; - defaultServer = "fsautocomplete"; + defaultServer = ["fsautocomplete"]; servers = { fsautocomplete = { - package = pkgs.fsautocomplete; - internalFormatter = false; - lspConfig = '' - lspconfig.fsautocomplete.setup { - capabilities = capabilities; - on_attach = default_on_attach; - cmd = ${ - if isList cfg.lsp.package - then expToLua cfg.lsp.package - else "{'${cfg.lsp.package}/bin/fsautocomplete'}" - }, - } + cmd = [(getExe pkgs.fsautocomplete) "--adaptive-lsp-server-enabled"]; + filetypes = ["fsharp"]; + root_dir = mkLuaInline '' + function(bufnr, on_dir) + on_dir(vim.fs.root(bufnr, function(name, path) + return name == ".git" or name:match("%.sln$") or name:match("%.fsproj$") + end)) + end ''; + init_options = { + AutomaticWorkspaceInit = true; + }; + settings = { + FSharp = { + keywordsAutocomplete = true; + ExternalAutocomplete = false; + Linter = true; + UnionCaseStubGeneration = true; + UnionCaseStubGenerationBody = ''failwith "Not Implemented"''; + RecordStubGeneration = true; + RecordStubGenerationBody = ''failwith "Not Implemented"''; + InterfaceStubGeneration = true; + InterfaceStubGenerationObjectIdentifier = "this"; + InterfaceStubGenerationMethodBody = ''failwith "Not Implemented"''; + UnusedOpensAnalyzer = true; + UnusedDeclarationsAnalyzer = true; + UseSdkScripts = true; + SimplifyNameAnalyzer = true; + ResolveNamespaces = true; + EnableReferenceCodeLens = true; + }; + }; }; }; - defaultFormat = "fantomas"; + defaultFormat = ["fantomas"]; formats = { fantomas = { - package = pkgs.fantomas; + command = getExe pkgs.fantomas; }; }; @@ -52,33 +71,20 @@ in { lsp = { enable = mkEnableOption "F# LSP support" // {default = config.vim.lsp.enable;}; - server = mkOption { - type = enum (attrNames servers); + servers = mkOption { + type = deprecatedSingleOrListOf "vim.language.fsharp.lsp.servers" (enum (attrNames servers)); default = defaultServer; description = "F# LSP server to use"; }; - - package = mkOption { - type = either package (listOf str); - default = servers.${cfg.lsp.server}.package; - example = ''[lib.getExe pkgs.fsautocomplete "--state-directory" "~/.cache/fsautocomplete"]''; - description = "F# LSP server package, or the command to run as a list of strings"; - }; }; format = { enable = mkEnableOption "F# formatting" // {default = config.vim.languages.enableFormat;}; type = mkOption { - type = enum (attrNames formats); + type = deprecatedSingleOrListOf "vim.language.fsharp.format.type" (enum (attrNames formats)); default = defaultFormat; description = "F# formatter to use"; }; - - package = mkOption { - type = package; - default = formats.${cfg.format.type}.package; - description = "F# formatter package"; - }; }; }; }; @@ -90,16 +96,25 @@ in { }) (mkIf cfg.lsp.enable { - vim.lsp.lspconfig.enable = true; - vim.lsp.lspconfig.sources.fsharp-lsp = servers.${cfg.lsp.server}.lspConfig; + vim.lsp.servers = + mapListToAttrs (name: { + inherit name; + value = servers.${name}; + }) + cfg.lsp.servers; }) (mkIf cfg.format.enable { vim.formatter.conform-nvim = { enable = true; - setupOpts.formatters_by_ft.fsharp = [cfg.format.type]; - setupOpts.formatters.${cfg.format.type} = { - command = getExe cfg.format.package; + setupOpts = { + formatters_by_ft.fsharp = cfg.format.type; + formatters = + mapListToAttrs (name: { + inherit name; + value = formats.${name}; + }) + cfg.format.type; }; }; }) diff --git a/modules/plugins/languages/gleam.nix b/modules/plugins/languages/gleam.nix index 272ef51e..349d9ac1 100644 --- a/modules/plugins/languages/gleam.nix +++ b/modules/plugins/languages/gleam.nix @@ -7,28 +7,20 @@ inherit (builtins) attrNames; inherit (lib.options) mkEnableOption mkOption; inherit (lib.modules) mkIf mkMerge; - inherit (lib.lists) isList; - inherit (lib.types) enum either listOf package str; - inherit (lib.nvim.lua) expToLua; - inherit (lib.nvim.types) mkGrammarOption; + inherit (lib.meta) getExe; + inherit (lib.types) enum; + inherit (lib.nvim.types) mkGrammarOption deprecatedSingleOrListOf; + inherit (lib.nvim.attrsets) mapListToAttrs; cfg = config.vim.languages.gleam; - defaultServer = "gleam"; + defaultServers = ["gleam"]; servers = { gleam = { - package = pkgs.gleam; - lspConfig = '' - lspconfig.gleam.setup{ - capabilities = capabilities, - on_attach = default_on_attach, - cmd = ${ - if isList cfg.lsp.package - then expToLua cfg.lsp.package - else ''{"${cfg.lsp.package}/bin/gleam", "lsp"}'' - } - } - ''; + enable = true; + cmd = [(getExe pkgs.gleam) "lsp"]; + filetypes = ["gleam"]; + root_markers = ["gleam.toml" ".git"]; }; }; in { @@ -42,18 +34,11 @@ in { lsp = { enable = mkEnableOption "Gleam LSP support" // {default = config.vim.lsp.enable;}; - - server = mkOption { - type = enum (attrNames servers); - default = defaultServer; + servers = mkOption { + type = deprecatedSingleOrListOf "vim.language.gleam.lsp.servers" (enum (attrNames servers)); + default = defaultServers; description = "Gleam LSP server to use"; }; - - package = mkOption { - type = either package (listOf str); - default = servers.${cfg.lsp.server}.package; - description = "Gleam LSP server package, or the command to run as a list of strings"; - }; }; }; @@ -64,8 +49,12 @@ in { }) (mkIf cfg.lsp.enable { - vim.lsp.lspconfig.enable = true; - vim.lsp.lspconfig.sources.gleam-lsp = servers.${cfg.lsp.server}.lspConfig; + vim.lsp.servers = + mapListToAttrs (n: { + name = n; + value = servers.${n}; + }) + cfg.lsp.servers; }) ]); } diff --git a/modules/plugins/languages/go.nix b/modules/plugins/languages/go.nix index bab0ff4a..c51077f9 100644 --- a/modules/plugins/languages/go.nix +++ b/modules/plugins/languages/go.nix @@ -8,45 +8,67 @@ inherit (lib.options) mkEnableOption mkOption literalMD; inherit (lib.modules) mkIf mkMerge; inherit (lib.meta) getExe; - inherit (lib.lists) isList; - inherit (lib.types) bool enum either listOf package str; - inherit (lib.nvim.types) mkGrammarOption; - inherit (lib.nvim.lua) expToLua; + inherit (lib.generators) mkLuaInline; + inherit (lib.types) bool enum package; + inherit (lib.nvim.types) mkGrammarOption deprecatedSingleOrListOf; inherit (lib.nvim.dag) entryAfter; + inherit (lib.nvim.attrsets) mapListToAttrs; cfg = config.vim.languages.go; - defaultServer = "gopls"; + defaultServers = ["gopls"]; servers = { gopls = { - package = pkgs.gopls; - lspConfig = '' - lspconfig.gopls.setup { - capabilities = capabilities; - on_attach = default_on_attach; - cmd = ${ - if isList cfg.lsp.package - then expToLua cfg.lsp.package - else ''{"${cfg.lsp.package}/bin/gopls", "serve"}'' - }, - } + cmd = [(getExe pkgs.gopls)]; + filetypes = ["go" "gomod" "gowork" "gotmpl"]; + root_dir = mkLuaInline '' + function(bufnr, on_dir) + local fname = vim.api.nvim_buf_get_name(bufnr) + + local function get_root(fname) + if _G.nvf_gopls_mod_cache and fname:sub(1, #_G.nvf_gopls_mod_cache) == _G.nvf_gopls_mod_cache then + local clients = vim.lsp.get_clients { name = 'gopls' } + if #clients > 0 then + return clients[#clients].config.root_dir + end + end + return vim.fs.root(fname, 'go.work') or vim.fs.root(fname, 'go.mod') or vim.fs.root(fname, '.git') + end + + -- see: https://github.com/neovim/nvim-lspconfig/issues/804 + if _G.nvf_gopls_mod_cache then + on_dir(get_root(fname)) + return + end + local cmd = { 'go', 'env', 'GOMODCACHE' } + local ok, err = pcall(vim.system, cmd, { text = true }, function(output) + if output.code == 0 then + if output.stdout then + _G.nvf_gopls_mod_cache = vim.trim(output.stdout) + end + on_dir(get_root(fname)) + else + vim.schedule(function() + vim.notify(('[gopls] cmd failed with code %d: %s\n%s'):format(output.code, cmd, output.stderr)) + end) + end + end) + if not ok then vim.notify(('[gopls] cmd failed: %s\n%s'):format(cmd, err)) end + end ''; }; }; - defaultFormat = "gofmt"; + defaultFormat = ["gofmt"]; formats = { gofmt = { - package = pkgs.go; - config.command = "${cfg.format.package}/bin/gofmt"; + command = "${pkgs.go}/bin/gofmt"; }; gofumpt = { - package = pkgs.gofumpt; - config.command = getExe cfg.format.package; + command = getExe pkgs.gofumpt; }; golines = { - package = pkgs.golines; - config.command = "${cfg.format.package}/bin/golines"; + command = "${pkgs.golines}/bin/golines"; }; }; @@ -69,17 +91,10 @@ in { lsp = { enable = mkEnableOption "Go LSP support" // {default = config.vim.lsp.enable;}; - server = mkOption { + servers = mkOption { + type = deprecatedSingleOrListOf "vim.language.go.lsp.servers" (enum (attrNames servers)); + default = defaultServers; description = "Go LSP server to use"; - type = enum (attrNames servers); - default = defaultServer; - }; - - package = mkOption { - description = "Go LSP server package, or the command to run as a list of strings"; - example = ''[lib.getExe pkgs.jdt-language-server " - data " " ~/.cache/jdtls/workspace "]''; - type = either package (listOf str); - default = servers.${cfg.lsp.server}.package; }; }; @@ -95,15 +110,9 @@ in { type = mkOption { description = "Go formatter to use"; - type = enum (attrNames formats); + type = deprecatedSingleOrListOf "vim.language.go.format.type" (enum (attrNames formats)); default = defaultFormat; }; - - package = mkOption { - description = "Go formatter package"; - type = package; - default = formats.${cfg.format.type}.package; - }; }; dap = { @@ -134,15 +143,26 @@ in { }) (mkIf cfg.lsp.enable { - vim.lsp.lspconfig.enable = true; - vim.lsp.lspconfig.sources.go-lsp = servers.${cfg.lsp.server}.lspConfig; + vim.lsp.servers = + mapListToAttrs (name: { + inherit name; + value = servers.${name}; + }) + cfg.lsp.servers; }) (mkIf cfg.format.enable { vim.formatter.conform-nvim = { enable = true; - setupOpts.formatters_by_ft.go = [cfg.format.type]; - setupOpts.formatters.${cfg.format.type} = formats.${cfg.format.type}.config; + setupOpts = { + formatters_by_ft.go = cfg.format.type; + formatters = + mapListToAttrs (name: { + inherit name; + value = formats.${name}; + }) + cfg.format.type; + }; }; }) diff --git a/modules/plugins/languages/haskell.nix b/modules/plugins/languages/haskell.nix index f50c9f09..ac18fe55 100644 --- a/modules/plugins/languages/haskell.nix +++ b/modules/plugins/languages/haskell.nix @@ -4,17 +4,64 @@ pkgs, ... }: let - inherit (builtins) isList; - inherit (lib.types) either package listOf str; + inherit (builtins) isList attrNames; + inherit (lib.types) either package enum listOf str; inherit (lib.options) mkEnableOption mkOption; inherit (lib.strings) optionalString; inherit (lib.modules) mkIf mkMerge; inherit (lib.nvim.types) mkGrammarOption; inherit (lib.nvim.dag) entryAfter; - inherit (lib.nvim.lua) expToLua; + inherit (lib.nvim.lua) toLuaObject; + inherit (lib.meta) getExe'; + inherit (lib.generators) mkLuaInline; inherit (pkgs) haskellPackages; cfg = config.vim.languages.haskell; + + defaultServers = ["hls"]; + servers = { + hls = { + enable = false; + cmd = [(getExe' pkgs.haskellPackages.haskell-language-server "haskell-language-server-wrapper") "--lsp"]; + filetypes = ["haskell" "lhaskell"]; + on_attach = + mkLuaInline + /* + lua + */ + '' + function(client, bufnr) + local ht = require("haskell-tools") + local opts = { noremap = true, silent = true, buffer = bufnr } + vim.keymap.set('n', 'cl', vim.lsp.codelens.run, opts) + vim.keymap.set('n', 'hs', ht.hoogle.hoogle_signature, opts) + vim.keymap.set('n', 'ea', ht.lsp.buf_eval_all, opts) + vim.keymap.set('n', 'rr', ht.repl.toggle, opts) + vim.keymap.set('n', 'rf', function() + ht.repl.toggle(vim.api.nvim_buf_get_name(0)) + end, opts) + vim.keymap.set('n', 'rq', ht.repl.quit, opts) + end + ''; + root_dir = + mkLuaInline + /* + lua + */ + '' + function(bufnr, on_dir) + local fname = vim.api.nvim_buf_get_name(bufnr) + on_dir(util.root_pattern('hie.yaml', 'stack.yaml', 'cabal.project', '*.cabal', 'package.yaml')(fname)) + end + ''; + settings = { + haskell = { + formattingProvider = "ormolu"; + cabalFormattingProvider = "cabalfmt"; + }; + }; + }; + }; in { options.vim.languages.haskell = { enable = mkEnableOption "Haskell support"; @@ -25,21 +72,20 @@ in { }; lsp = { - enable = mkEnableOption "LSP support for Haskell" // {default = config.vim.lsp.enable;}; - package = mkOption { - description = "Haskell LSP package or command to run the Haskell LSP"; - example = ''[ (lib.getExe pkgs.haskellPackages.haskell-language-server) "--debug" ]''; - default = haskellPackages.haskell-language-server; - type = either package (listOf str); + enable = mkEnableOption "Haskell LSP support" // {default = config.vim.lsp.enable;}; + servers = mkOption { + type = listOf (enum (attrNames servers)); + default = defaultServers; + description = "Haskell LSP server to use"; }; }; dap = { enable = mkEnableOption "DAP support for Haskell" // {default = config.vim.languages.enableDAP;}; package = mkOption { - description = "Haskell DAP package or command to run the Haskell DAP"; default = haskellPackages.haskell-debug-adapter; type = either package (listOf str); + description = "Haskell DAP package or command to run the Haskell DAP"; }; }; }; @@ -57,7 +103,7 @@ in { startPlugins = ["haskell-tools-nvim"]; luaConfigRC.haskell-tools-nvim = entryAfter - ["lsp-setup"] + ["lsp-servers"] '' vim.g.haskell_tools = { ${optionalString cfg.lsp.enable '' @@ -67,31 +113,13 @@ in { enable = true, }, }, - hls = { - cmd = ${ - if isList cfg.lsp.package - then expToLua cfg.lsp.package - else ''{"${cfg.lsp.package}/bin/haskell-language-server-wrapper", "--lsp"}'' - }, - on_attach = function(client, bufnr, ht) - default_on_attach(client, bufnr, ht) - local opts = { noremap = true, silent = true, buffer = bufnr } - vim.keymap.set('n', 'cl', vim.lsp.codelens.run, opts) - vim.keymap.set('n', 'hs', ht.hoogle.hoogle_signature, opts) - vim.keymap.set('n', 'ea', ht.lsp.buf_eval_all, opts) - vim.keymap.set('n', 'rr', ht.repl.toggle, opts) - vim.keymap.set('n', 'rf', function() - ht.repl.toggle(vim.api.nvim_buf_get_name(0)) - end, opts) - vim.keymap.set('n', 'rq', ht.repl.quit, opts) - end, - }, + hls = ${toLuaObject servers.hls}, ''} ${optionalString cfg.dap.enable '' dap = { cmd = ${ if isList cfg.dap.package - then expToLua cfg.dap.package + then toLuaObject cfg.dap.package else ''{"${cfg.dap.package}/bin/haskell-debug-adapter"}'' }, }, diff --git a/modules/plugins/languages/hcl.nix b/modules/plugins/languages/hcl.nix index e702170c..e32719b9 100644 --- a/modules/plugins/languages/hcl.nix +++ b/modules/plugins/languages/hcl.nix @@ -8,29 +8,26 @@ inherit (lib.options) mkEnableOption mkOption; inherit (lib.meta) getExe; inherit (lib.modules) mkIf mkMerge; - inherit (lib.types) package bool enum; - inherit (lib.nvim.types) mkGrammarOption; + inherit (lib.types) bool enum listOf; + inherit (lib.nvim.types) mkGrammarOption deprecatedSingleOrListOf; + inherit (lib.nvim.attrsets) mapListToAttrs; cfg = config.vim.languages.hcl; - defaultServer = "terraform-ls"; + defaultServers = ["terraform-ls"]; servers = { terraform-ls = { - package = pkgs.terraform-ls; - lspConfig = '' - lspconfig.terraformls.setup { - capabilities = capabilities, - on_attach=default_on_attach, - cmd = {"${lib.getExe cfg.lsp.package}", "serve"}, - } - ''; + enable = true; + cmd = [(getExe pkgs.terraform-ls) "serve"]; + filetypes = ["terraform" "terraform-vars"]; + root_markers = [".terraform" ".git"]; }; }; - defaultFormat = "hclfmt"; + defaultFormat = ["hclfmt"]; formats = { hclfmt = { - package = pkgs.hclfmt; + command = getExe pkgs.hclfmt; }; }; in { @@ -43,12 +40,11 @@ in { }; lsp = { - enable = mkEnableOption "HCL LSP support (terraform-ls)" // {default = config.vim.lsp.enable;}; - # TODO: (maybe, is it better?) it would be cooler to use vscode-extensions.hashicorp.hcl probably, shouldn't be too hard - package = mkOption { - type = package; - default = servers.${defaultServer}.package; - description = "HCL language server package (terraform-ls)"; + enable = mkEnableOption "HCL LSP support" // {default = config.vim.lsp.enable;}; + servers = mkOption { + type = listOf (enum (attrNames servers)); + default = defaultServers; + description = "HCL LSP server to use"; }; }; @@ -59,15 +55,10 @@ in { description = "Enable HCL formatting"; }; type = mkOption { - type = enum (attrNames formats); + type = deprecatedSingleOrListOf "vim.language.hcl.format.type" (enum (attrNames formats)); default = defaultFormat; description = "HCL formatter to use"; }; - package = mkOption { - type = package; - default = formats.${cfg.format.type}.package; - description = "HCL formatter package"; - }; }; }; @@ -96,18 +87,25 @@ in { }) (mkIf cfg.lsp.enable { - vim.lsp.lspconfig.enable = true; - vim.lsp.lspconfig.sources = lib.optionalAttrs (! config.vim.languages.terraform.lsp.enable) { - terraform-ls = servers.${cfg.lsp.server}.lspConfig; - }; + vim.lsp.servers = + mapListToAttrs (n: { + name = n; + value = servers.${n}; + }) + cfg.lsp.servers; }) (mkIf cfg.format.enable { vim.formatter.conform-nvim = { enable = true; - setupOpts.formatters_by_ft.hcl = [cfg.format.type]; - setupOpts.formatters.${cfg.format.type} = { - command = getExe cfg.format.package; + setupOpts = { + formatters_by_ft.hcl = cfg.format.type; + formatters = + mapListToAttrs (name: { + inherit name; + value = formats.${name}; + }) + cfg.format.type; }; }; }) diff --git a/modules/plugins/languages/helm.nix b/modules/plugins/languages/helm.nix index ffe115c9..18b09722 100644 --- a/modules/plugins/languages/helm.nix +++ b/modules/plugins/languages/helm.nix @@ -4,44 +4,39 @@ lib, ... }: let - inherit (builtins) attrNames; + inherit (builtins) attrNames head; inherit (lib.options) mkEnableOption mkOption; - inherit (lib.modules) mkIf mkMerge; - inherit (lib.lists) isList; - inherit (lib.types) enum either listOf package str; - inherit (lib.nvim.types) mkGrammarOption; - inherit (lib.nvim.lua) expToLua; + inherit (lib.modules) mkDefault mkIf mkMerge; + inherit (lib.meta) getExe; + inherit (lib.types) enum; + inherit (lib.nvim.types) mkGrammarOption deprecatedSingleOrListOf; + inherit (lib.nvim.attrsets) mapListToAttrs; cfg = config.vim.languages.helm; yamlCfg = config.vim.languages.yaml; - helmCmd = - if isList cfg.lsp.package - then cfg.lsp.package - else ["${cfg.lsp.package}/bin/helm_ls" "serve"]; - yamlCmd = - if isList yamlCfg.lsp.package - then builtins.elemAt yamlCfg.lsp.package 0 - else "${yamlCfg.lsp.package}/bin/yaml-language-server"; - - defaultServer = "helm-ls"; + defaultServers = ["helm-ls"]; servers = { helm-ls = { - package = pkgs.helm-ls; - lspConfig = '' - lspconfig.helm_ls.setup { - capabilities = capabilities, - on_attach = default_on_attach, - cmd = ${expToLua helmCmd}, - settings = { - ['helm-ls'] = { - yamlls = { - path = "${yamlCmd}" - } - } - } - } - ''; + enable = true; + cmd = [(getExe pkgs.helm-ls) "serve"]; + filetypes = ["helm" "yaml.helm-values"]; + root_markers = ["Chart.yaml"]; + capabilities = { + didChangeWatchedFiles = { + dynamicRegistration = true; + }; + }; + settings = mkIf (yamlCfg.enable && yamlCfg.lsp.enable) { + helm-ls = { + yamlls = { + # Without this being enabled, the YAML language module will look broken in helmfiles + # if both modules are enabled at once. + enabled = mkDefault yamlCfg.lsp.enable; + path = head config.vim.lsp.servers.${head yamlCfg.lsp.servers}.cmd; + }; + }; + }; }; }; in { @@ -55,17 +50,10 @@ in { lsp = { enable = mkEnableOption "Helm LSP support" // {default = config.vim.lsp.enable;}; - - server = mkOption { + servers = mkOption { + type = deprecatedSingleOrListOf "vim.language.helm.lsp.servers" (enum (attrNames servers)); + default = defaultServers; description = "Helm LSP server to use"; - type = enum (attrNames servers); - default = defaultServer; - }; - - package = mkOption { - description = "Helm LSP server package"; - type = either package (listOf str); - default = servers.${cfg.lsp.server}.package; }; }; }; @@ -77,8 +65,12 @@ in { }) (mkIf cfg.lsp.enable { - vim.lsp.lspconfig.enable = true; - vim.lsp.lspconfig.sources.helm-lsp = servers.${cfg.lsp.server}.lspConfig; + vim.lsp.servers = + mapListToAttrs (n: { + name = n; + value = servers.${n}; + }) + cfg.lsp.servers; }) { diff --git a/modules/plugins/languages/html.nix b/modules/plugins/languages/html.nix index 0bef2767..85918ad2 100644 --- a/modules/plugins/languages/html.nix +++ b/modules/plugins/languages/html.nix @@ -4,14 +4,46 @@ lib, ... }: let + inherit (builtins) attrNames; + inherit (lib.meta) getExe; inherit (lib.options) mkEnableOption mkOption; inherit (lib.modules) mkIf mkMerge; - inherit (lib.types) bool; + inherit (lib.types) bool enum; inherit (lib.lists) optional; - inherit (lib.nvim.types) mkGrammarOption; + inherit (lib.nvim.types) mkGrammarOption diagnostics deprecatedSingleOrListOf; inherit (lib.nvim.dag) entryAnywhere; + inherit (lib.nvim.attrsets) mapListToAttrs; cfg = config.vim.languages.html; + + defaultServers = ["superhtml"]; + servers = { + superhtml = { + cmd = [(getExe pkgs.superhtml) "lsp"]; + filetypes = ["html" "shtml" "htm"]; + root_markers = ["index.html" ".git"]; + }; + emmet-ls = { + cmd = [(getExe pkgs.emmet-ls) "--stdio"]; + filetypes = ["html" "shtml" "htm"]; + root_markers = ["index.html" ".git"]; + }; + }; + + defaultFormat = ["superhtml"]; + formats = { + superhtml = { + command = "${pkgs.superhtml}/bin/superhtml"; + args = ["fmt" "-"]; + }; + }; + + defaultDiagnosticsProvider = ["htmlhint"]; + diagnosticsProviders = { + htmlhint = { + config.cmd = getExe pkgs.htmlhint; + }; + }; in { options.vim.languages.html = { enable = mkEnableOption "HTML language support"; @@ -19,9 +51,38 @@ in { enable = mkEnableOption "HTML treesitter support" // {default = config.vim.languages.enableTreesitter;}; package = mkGrammarOption pkgs "html"; autotagHtml = mkOption { - description = "Enable autoclose/autorename of html tags (nvim-ts-autotag)"; type = bool; default = true; + description = "Enable autoclose/autorename of html tags (nvim-ts-autotag)"; + }; + }; + + lsp = { + enable = mkEnableOption "HTML LSP support" // {default = config.vim.lsp.enable;}; + servers = mkOption { + type = deprecatedSingleOrListOf "vim.language.html.lsp.servers" (enum (attrNames servers)); + default = defaultServers; + description = "HTML LSP server to use"; + }; + }; + + format = { + enable = mkEnableOption "HTML formatting" // {default = config.vim.languages.enableFormat;}; + + type = mkOption { + type = deprecatedSingleOrListOf "vim.language.html.format.type" (enum (attrNames formats)); + default = defaultFormat; + description = "HTML formatter to use"; + }; + }; + + extraDiagnostics = { + enable = mkEnableOption "extra HTML diagnostics" // {default = config.vim.languages.enableExtraDiagnostics;}; + + types = diagnostics { + langDesc = "HTML"; + inherit diagnosticsProviders; + inherit defaultDiagnosticsProvider; }; }; }; @@ -41,5 +102,40 @@ in { ''); }; }) + + (mkIf cfg.lsp.enable { + vim.lsp.servers = + mapListToAttrs (n: { + name = n; + value = servers.${n}; + }) + cfg.lsp.servers; + }) + + (mkIf (cfg.format.enable && !cfg.lsp.enable) { + vim.formatter.conform-nvim = { + enable = true; + setupOpts = { + formatters_by_ft.html = cfg.format.type; + formatters = + mapListToAttrs (name: { + inherit name; + value = formats.${name}; + }) + cfg.format.type; + }; + }; + }) + + (mkIf cfg.extraDiagnostics.enable { + vim.diagnostics.nvim-lint = { + enable = true; + linters_by_ft.html = cfg.extraDiagnostics.types; + linters = mkMerge (map (name: { + ${name} = diagnosticsProviders.${name}.config; + }) + cfg.extraDiagnostics.types); + }; + }) ]); } diff --git a/modules/plugins/languages/java.nix b/modules/plugins/languages/java.nix index 2e26feea..82fae184 100644 --- a/modules/plugins/languages/java.nix +++ b/modules/plugins/languages/java.nix @@ -7,12 +7,59 @@ inherit (lib.options) mkEnableOption mkOption; inherit (lib.modules) mkIf mkMerge; inherit (lib.meta) getExe; - inherit (lib.lists) isList; - inherit (lib.types) either listOf package str; + inherit (builtins) attrNames; + inherit (lib.types) listOf enum; inherit (lib.nvim.types) mkGrammarOption; - inherit (lib.nvim.lua) expToLua; + inherit (lib.nvim.attrsets) mapListToAttrs; + inherit (lib.nvim.dag) entryBefore; + inherit (lib.generators) mkLuaInline; cfg = config.vim.languages.java; + + defaultServers = ["jdtls"]; + servers = { + jdtls = { + enable = true; + cmd = + mkLuaInline + /* + lua + */ + '' + { + '${getExe pkgs.jdt-language-server}', + '-configuration', + get_jdtls_config_dir(), + '-data', + get_jdtls_workspace_dir(), + get_jdtls_jvm_args(), + } + ''; + filetypes = ["java"]; + root_markers = [ + # Multi-module projects + ".git" + "build.gradle" + "build.gradle.kts" + # Single-module projects + "build.xml" # Ant + "pom.xml" # Maven + "settings.gradle" # Gradle + "settings.gradle.kts" # Gradle + ]; + init_options = { + workspace = mkLuaInline "get_jdtls_workspace_dir()"; + jvm_args = {}; + os_config = mkLuaInline "nil"; + }; + handlers = { + "textDocument/codeAction" = mkLuaInline "jdtls_on_textdocument_codeaction"; + "textDocument/rename" = mkLuaInline "jdtls_on_textdocument_rename"; + "workspace/applyEdit" = mkLuaInline "jdtls_on_workspace_applyedit"; + "language/status" = mkLuaInline "vim.schedule_wrap(jdtls_on_language_status)"; + }; + }; + }; in { options.vim.languages.java = { enable = mkEnableOption "Java language support"; @@ -23,30 +70,107 @@ in { }; lsp = { - enable = mkEnableOption "Java LSP support (java-language-server)" // {default = config.vim.lsp.enable;}; - package = mkOption { - description = "java language server package, or the command to run as a list of strings"; - example = ''[lib.getExe pkgs.jdt-language-server "-data" "~/.cache/jdtls/workspace"]''; - type = either package (listOf str); - default = pkgs.jdt-language-server; + enable = mkEnableOption "Java LSP support" // {default = config.vim.lsp.enable;}; + servers = mkOption { + type = listOf (enum (attrNames servers)); + default = defaultServers; + description = "Java LSP server to use"; }; }; }; config = mkIf cfg.enable (mkMerge [ (mkIf cfg.lsp.enable { - vim.lsp.lspconfig.enable = true; - vim.lsp.lspconfig.sources.jdtls = '' - lspconfig.jdtls.setup { - capabilities = capabilities, - on_attach = default_on_attach, - cmd = ${ - if isList cfg.lsp.package - then expToLua cfg.lsp.package - else ''{"${getExe cfg.lsp.package}", "-data", vim.fn.stdpath("cache").."/jdtls/workspace"}'' - }, - } - ''; + vim.luaConfigRC.jdtls-util = + entryBefore ["lsp-servers"] + /* + lua + */ + '' + local jdtls_handlers = require 'vim.lsp.handlers' + + local jdtls_env = { + HOME = vim.uv.os_homedir(), + XDG_CACHE_HOME = os.getenv 'XDG_CACHE_HOME', + JDTLS_JVM_ARGS = os.getenv 'JDTLS_JVM_ARGS', + } + + local function get_cache_dir() + return jdtls_env.XDG_CACHE_HOME and jdtls_env.XDG_CACHE_HOME or jdtls_env.HOME .. '/.cache' + end + + local function get_jdtls_cache_dir() + return get_cache_dir() .. '/jdtls' + end + + local function get_jdtls_config_dir() + return get_jdtls_cache_dir() .. '/config' + end + + local function get_jdtls_workspace_dir() + return get_jdtls_cache_dir() .. '/workspace' + end + + local function get_jdtls_jvm_args() + local args = {} + for a in string.gmatch((jdtls_env.JDTLS_JVM_ARGS or '''), '%S+') do + local arg = string.format('--jvm-arg=%s', a) + table.insert(args, arg) + end + return unpack(args) + end + + -- TextDocument version is reported as 0, override with nil so that + -- the client doesn't think the document is newer and refuses to update + -- See: https://github.com/eclipse/eclipse.jdt.ls/issues/1695 + local function jdtls_fix_zero_version(workspace_edit) + if workspace_edit and workspace_edit.documentChanges then + for _, change in pairs(workspace_edit.documentChanges) do + local text_document = change.textDocument + if text_document and text_document.version and text_document.version == 0 then + text_document.version = nil + end + end + end + return workspace_edit + end + + local function jdtls_on_textdocument_codeaction(err, actions, ctx) + for _, action in ipairs(actions) do + -- TODO: (steelsojka) Handle more than one edit? + if action.command == 'java.apply.workspaceEdit' then -- 'action' is Command in java format + action.edit = jdtls_fix_zero_version(action.edit or action.arguments[1]) + elseif type(action.command) == 'table' and action.command.command == 'java.apply.workspaceEdit' then -- 'action' is CodeAction in java format + action.edit = jdtls_fix_zero_version(action.edit or action.command.arguments[1]) + end + end + + jdtls_handlers[ctx.method](err, actions, ctx) + end + + local function jdtls_on_textdocument_rename(err, workspace_edit, ctx) + jdtls_handlers[ctx.method](err, jdtls_fix_zero_version(workspace_edit), ctx) + end + + local function jdtls_on_workspace_applyedit(err, workspace_edit, ctx) + jdtls_handlers[ctx.method](err, jdtls_fix_zero_version(workspace_edit), ctx) + end + + -- Non-standard notification that can be used to display progress + local function jdtls_on_language_status(_, result) + local command = vim.api.nvim_command + command 'echohl ModeMsg' + command(string.format('echo "%s"', result.message)) + command 'echohl None' + end + ''; + + vim.lsp.servers = + mapListToAttrs (n: { + name = n; + value = servers.${n}; + }) + cfg.lsp.servers; }) (mkIf cfg.treesitter.enable { diff --git a/modules/plugins/languages/json.nix b/modules/plugins/languages/json.nix new file mode 100644 index 00000000..c928d3c5 --- /dev/null +++ b/modules/plugins/languages/json.nix @@ -0,0 +1,96 @@ +{ + config, + pkgs, + lib, + ... +}: let + inherit (builtins) attrNames; + inherit (lib.options) mkOption mkEnableOption; + inherit (lib.meta) getExe' getExe; + inherit (lib.modules) mkIf mkMerge; + inherit (lib.types) enum; + inherit (lib.nvim.types) mkGrammarOption deprecatedSingleOrListOf; + inherit (lib.nvim.attrsets) mapListToAttrs; + + cfg = config.vim.languages.json; + + defaultServers = ["jsonls"]; + servers = { + jsonls = { + cmd = [(getExe' pkgs.vscode-langservers-extracted "vscode-json-language-server") "--stdio"]; + filetypes = ["json" "jsonc"]; + init_options = {provideFormatter = true;}; + root_markers = [".git"]; + }; + }; + + defaultFormat = ["jsonfmt"]; + + formats = { + jsonfmt = { + command = getExe pkgs.jsonfmt; + args = ["-w" "-"]; + }; + }; +in { + options.vim.languages.json = { + enable = mkEnableOption "JSON language support"; + + treesitter = { + enable = mkEnableOption "JSON treesitter" // {default = config.vim.languages.enableTreesitter;}; + + package = mkGrammarOption pkgs "json"; + }; + + lsp = { + enable = mkEnableOption "JSON LSP support" // {default = config.vim.lsp.enable;}; + + servers = mkOption { + type = deprecatedSingleOrListOf "vim.language.json.lsp.servers" (enum (attrNames servers)); + default = defaultServers; + description = "JSON LSP server to use"; + }; + }; + + format = { + enable = mkEnableOption "JSON formatting" // {default = config.vim.languages.enableFormat;}; + + type = mkOption { + description = "JSON formatter to use"; + type = deprecatedSingleOrListOf "vim.language.json.format.type" (enum (attrNames formats)); + default = defaultFormat; + }; + }; + }; + + config = mkIf cfg.enable (mkMerge [ + (mkIf cfg.treesitter.enable { + vim.treesitter.enable = true; + vim.treesitter.grammars = [cfg.treesitter.package]; + }) + + (mkIf cfg.lsp.enable { + vim.lsp.servers = + mapListToAttrs (name: { + inherit name; + value = servers.${name}; + }) + cfg.lsp.servers; + }) + + (mkIf cfg.format.enable { + vim.formatter.conform-nvim = { + enable = true; + setupOpts = { + formatters_by_ft.json = cfg.format.type; + formatters = + mapListToAttrs (name: { + inherit name; + value = formats.${name}; + }) + cfg.format.type; + }; + }; + }) + ]); +} diff --git a/modules/plugins/languages/julia.nix b/modules/plugins/languages/julia.nix index 8c48b070..577798c7 100644 --- a/modules/plugins/languages/julia.nix +++ b/modules/plugins/languages/julia.nix @@ -4,63 +4,82 @@ config, ... }: let - inherit (builtins) attrNames isList; + inherit (builtins) attrNames; inherit (lib.options) mkEnableOption mkOption; - inherit (lib.types) either listOf package str enum bool nullOr; + inherit (lib.types) enum; inherit (lib.modules) mkIf mkMerge; - inherit (lib.strings) optionalString; - inherit (lib.nvim.types) mkGrammarOption; - inherit (lib.nvim.lua) expToLua; + inherit (lib.meta) getExe; + inherit (lib.nvim.types) mkGrammarOption deprecatedSingleOrListOf; + inherit (lib.generators) mkLuaInline; + inherit (lib.nvim.attrsets) mapListToAttrs; + inherit (lib.nvim.dag) entryBefore; - defaultServer = "julials"; + defaultServers = ["julials"]; servers = { julials = { - package = pkgs.julia.withPackages ["LanguageServer"]; - internalFormatter = true; - lspConfig = '' - lspconfig.julials.setup { - capabilities = capabilities, - on_attach = default_on_attach, - cmd = ${ - if isList cfg.lsp.package - then expToLua cfg.lsp.package - else '' - { - "${optionalString (cfg.lsp.package != null) "${cfg.lsp.package}/bin/"}julia", - "--startup-file=no", - "--history-file=no", - "--eval", - [[ - using LanguageServer - - depot_path = get(ENV, "JULIA_DEPOT_PATH", "") - project_path = let - dirname(something( - ## 1. Finds an explicitly set project (JULIA_PROJECT) - Base.load_path_expand(( - p = get(ENV, "JULIA_PROJECT", nothing); - p === nothing ? nothing : isempty(p) ? nothing : p - )), - ## 2. Look for a Project.toml file in the current working directory, - ## or parent directories, with $HOME as an upper boundary - Base.current_project(), - ## 3. First entry in the load path - get(Base.load_path(), 1, nothing), - ## 4. Fallback to default global environment, - ## this is more or less unreachable - Base.load_path_expand("@v#.#"), - )) - end - @info "Running language server" VERSION pwd() project_path depot_path - server = LanguageServer.LanguageServerInstance(stdin, stdout, project_path, depot_path) - server.runlinter = true - run(server) - ]] - } - '' - } - } - ''; + enable = true; + cmd = + mkLuaInline + /* + lua + */ + '' + { + '${getExe (pkgs.julia.withPackages ["LanguageServer"])}', + '--startup-file=no', + '--history-file=no', + '-e', + [[ + # Load LanguageServer.jl: attempt to load from ~/.julia/environments/nvim-lspconfig + # with the regular load path as a fallback + ls_install_path = joinpath( + get(DEPOT_PATH, 1, joinpath(homedir(), ".julia")), + "environments", "nvim-lspconfig" + ) + pushfirst!(LOAD_PATH, ls_install_path) + using LanguageServer + popfirst!(LOAD_PATH) + depot_path = get(ENV, "JULIA_DEPOT_PATH", "") + project_path = let + dirname(something( + ## 1. Finds an explicitly set project (JULIA_PROJECT) + Base.load_path_expand(( + p = get(ENV, "JULIA_PROJECT", nothing); + p === nothing ? nothing : isempty(p) ? nothing : p + )), + ## 2. Look for a Project.toml file in the current working directory, + ## or parent directories, with $HOME as an upper boundary + Base.current_project(), + ## 3. First entry in the load path + get(Base.load_path(), 1, nothing), + ## 4. Fallback to default global environment, + ## this is more or less unreachable + Base.load_path_expand("@v#.#"), + )) + end + @info "Running language server" VERSION pwd() project_path depot_path + server = LanguageServer.LanguageServerInstance(stdin, stdout, project_path, depot_path) + server.runlinter = true + run(server) + ]], + } + ''; + filetypes = ["julia"]; + root_markers = ["Project.toml" "JuliaProject.toml"]; + on_attach = + mkLuaInline + /* + lua + */ + '' + function(_, bufnr) + vim.api.nvim_buf_create_user_command(bufnr, 'LspJuliaActivateEnv', activate_julia_env, { + desc = 'Activate a Julia environment', + nargs = '?', + complete = 'file', + }) + end + ''; }; }; @@ -76,38 +95,26 @@ in { }; lsp = { - enable = mkOption { - type = bool; - default = config.vim.lsp.enable; + enable = mkEnableOption "Julia LSP support" // {default = config.vim.lsp.enable;}; + servers = mkOption { + type = deprecatedSingleOrListOf "vim.language.julia.lsp.servers" (enum (attrNames servers)); + default = defaultServers; description = '' - Whether to enable Julia LSP support. + Julia LSP Server to Use ::: {.note} The entirety of Julia is bundled with nvf, if you enable this option, since there is no way to provide only the LSP server. If you want to avoid that, you have to change - [](#opt-vim.languages.julia.lsp.package) to use the Julia binary - in {env}`PATH` (set it to `null`), and add the `LanguageServer` package to - Julia in your devshells. + [vim.lsp.servers.julials.cmd](#opt-vim.lsp.servers._name_.cmd) to use + the Julia binary in {env}`PATH`, and add the `LanguageServer` + package to Julia in your devshells. + + Check the source file of this option for the full `cmd`. ::: ''; }; - - server = mkOption { - type = enum (attrNames servers); - default = defaultServer; - description = "Julia LSP server to use"; - }; - - package = mkOption { - description = '' - Julia LSP server package, `null` to use the Julia binary in {env}`PATH`, or - the command to run as a list of strings. - ''; - type = nullOr (either package (listOf str)); - default = servers.${cfg.lsp.server}.package; - }; }; }; }; @@ -119,8 +126,70 @@ in { }) (mkIf cfg.lsp.enable { - vim.lsp.lspconfig.enable = true; - vim.lsp.lspconfig.sources.julia-lsp = servers.${cfg.lsp.server}.lspConfig; + vim.luaConfigRC.julia-util = + entryBefore ["lsp-servers"] + /* + lua + */ + '' + local function activate_julia_env(path) + assert(vim.fn.has 'nvim-0.10' == 1, 'requires Nvim 0.10 or newer') + local bufnr = vim.api.nvim_get_current_buf() + local julials_clients = vim.lsp.get_clients { bufnr = bufnr, name = 'julials' } + assert( + #julials_clients > 0, + 'method julia/activateenvironment is not supported by any servers active on the current buffer' + ) + local function _activate_env(environment) + if environment then + for _, julials_client in ipairs(julials_clients) do + julials_client:notify('julia/activateenvironment', { envPath = environment }) + end + vim.notify('Julia environment activated: \n`' .. environment .. '`', vim.log.levels.INFO) + end + end + if path then + path = vim.fs.normalize(vim.fn.fnamemodify(vim.fn.expand(path), ':p')) + local found_env = false + for _, project_file in ipairs(root_files) do + local file = vim.uv.fs_stat(vim.fs.joinpath(path, project_file)) + if file and file.type then + found_env = true + break + end + end + if not found_env then + vim.notify('Path is not a julia environment: \n`' .. path .. '`', vim.log.levels.WARN) + return + end + _activate_env(path) + else + local depot_paths = vim.env.JULIA_DEPOT_PATH + and vim.split(vim.env.JULIA_DEPOT_PATH, vim.fn.has 'win32' == 1 and ';' or ':') + or { vim.fn.expand '~/.julia' } + local environments = {} + vim.list_extend(environments, vim.fs.find(root_files, { type = 'file', upward = true, limit = math.huge })) + for _, depot_path in ipairs(depot_paths) do + local depot_env = vim.fs.joinpath(vim.fs.normalize(depot_path), 'environments') + vim.list_extend( + environments, + vim.fs.find(function(name, env_path) + return vim.tbl_contains(root_files, name) and string.sub(env_path, #depot_env + 1):match '^/[^/]*$' + end, { path = depot_env, type = 'file', limit = math.huge }) + ) + end + environments = vim.tbl_map(vim.fs.dirname, environments) + vim.ui.select(environments, { prompt = 'Select a Julia environment' }, _activate_env) + end + end + ''; + + vim.lsp.servers = + mapListToAttrs (n: { + name = n; + value = servers.${n}; + }) + cfg.lsp.servers; }) ]); } diff --git a/modules/plugins/languages/just.nix b/modules/plugins/languages/just.nix new file mode 100644 index 00000000..7645f255 --- /dev/null +++ b/modules/plugins/languages/just.nix @@ -0,0 +1,64 @@ +{ + config, + lib, + pkgs, + ... +}: let + inherit (builtins) attrNames; + inherit (lib.options) mkEnableOption mkOption; + inherit (lib.modules) mkIf mkMerge; + inherit (lib.meta) getExe; + inherit (lib.types) enum listOf; + inherit (lib.nvim.attrsets) mapListToAttrs; + inherit (lib.nvim.types) mkGrammarOption; + + cfg = config.vim.languages.just; + + defaultServers = ["just-lsp"]; + servers = { + just-lsp = { + enable = true; + cmd = [(getExe pkgs.just-lsp)]; + filetypes = ["just"]; + root_markers = [".git" "justfile"]; + }; + }; +in { + options.vim.languages.just = { + enable = mkEnableOption "Just support"; + + treesitter = { + enable = + mkEnableOption "Just treesitter" // {default = config.vim.languages.enableTreesitter;}; + package = mkGrammarOption pkgs "just"; + }; + + lsp = { + enable = + mkEnableOption "Just LSP support" // {default = config.vim.lsp.enable;}; + servers = mkOption { + type = listOf (enum (attrNames servers)); + default = defaultServers; + description = "Just LSP server to use"; + }; + }; + }; + + config = mkIf cfg.enable (mkMerge [ + (mkIf cfg.treesitter.enable { + vim.treesitter = { + enable = true; + grammars = [cfg.treesitter.package]; + }; + }) + + (mkIf cfg.lsp.enable { + vim.lsp.servers = + mapListToAttrs (n: { + name = n; + value = servers.${n}; + }) + cfg.lsp.servers; + }) + ]); +} diff --git a/modules/plugins/languages/kotlin.nix b/modules/plugins/languages/kotlin.nix index 1118afdf..f316c4cb 100644 --- a/modules/plugins/languages/kotlin.nix +++ b/modules/plugins/languages/kotlin.nix @@ -4,16 +4,47 @@ lib, ... }: let - inherit (lib.options) mkEnableOption mkOption literalExpression; + inherit (lib.options) mkEnableOption mkOption; inherit (lib.modules) mkIf mkMerge; - inherit (lib.meta) getExe; - inherit (lib.types) either package listOf str; + inherit (lib.meta) getExe' getExe; + inherit (builtins) attrNames; + inherit (lib.types) enum listOf; inherit (lib.nvim.types) mkGrammarOption diagnostics; - inherit (lib.lists) isList; - inherit (lib.nvim.lua) expToLua; + inherit (lib.generators) mkLuaInline; + inherit (lib.nvim.attrsets) mapListToAttrs; cfg = config.vim.languages.kotlin; + defaultServers = ["kotlin-language-server"]; + servers = { + kotlin-language-server = { + enable = true; + cmd = [(getExe' pkgs.kotlin-language-server "kotlin-language-server")]; + filetypes = ["kotlin"]; + root_markers = [ + "settings.gradle" # Gradle (multi-project) + "settings.gradle.kts" # Gradle (multi-project) + "build.xml" # Ant + "pom.xml" # Maven + "build.gradle" # Gradle + "build.gradle.kts" # gradle + ]; + init_options = { + storagePath = mkLuaInline " + vim.fs.root(vim.fn.expand '%:p:h', + { + 'settings.gradle', -- Gradle (multi-project) + 'settings.gradle.kts', -- Gradle (multi-project) + 'build.xml', -- Ant + 'pom.xml', -- Maven + 'build.gradle', -- Gradle + 'build.gradle.kts', -- Gradle + } + )"; + }; + }; + }; + defaultDiagnosticsProvider = ["ktlint"]; diagnosticsProviders = { ktlint = { @@ -31,22 +62,10 @@ in { lsp = { enable = mkEnableOption "Kotlin LSP support" // {default = config.vim.lsp.enable;}; - - package = mkOption { - description = "kotlin_language_server package with Kotlin runtime"; - type = either package (listOf str); - example = literalExpression '' - pkgs.symlinkJoin { - name = "kotlin-language-server-wrapped"; - paths = [pkgs.kotlin-language-server]; - nativeBuildInputs = [pkgs.makeBinaryWrapper]; - postBuild = ''' - wrapProgram $out/bin/kotlin-language-server \ - --prefix PATH : ''${pkgs.kotlin}/bin - '''; - }; - ''; - default = pkgs.kotlin-language-server; + servers = mkOption { + type = listOf (enum (attrNames servers)); + default = defaultServers; + description = "Kotlin LSP server to use"; }; }; @@ -78,23 +97,12 @@ in { }) (mkIf cfg.lsp.enable { - vim.lsp.lspconfig.enable = true; - vim.lsp.lspconfig.sources.kotlin_language_server = '' - lspconfig.kotlin_language_server.setup { - capabilities = capabilities, - root_dir = lspconfig.util.root_pattern("main.kt", ".git"), - on_attach=default_on_attach, - init_options = { - -- speeds up the startup time for the LSP - storagePath = vim.fn.stdpath('state') .. '/kotlin', - }, - cmd = ${ - if isList cfg.lsp.package - then expToLua cfg.lsp.package - else ''{"${cfg.lsp.package}/bin/kotlin-language-server"}'' - }, - } - ''; + vim.lsp.servers = + mapListToAttrs (n: { + name = n; + value = servers.${n}; + }) + cfg.lsp.servers; }) ]); } diff --git a/modules/plugins/languages/lua.nix b/modules/plugins/languages/lua.nix index 9f56cbea..276c9a6a 100644 --- a/modules/plugins/languages/lua.nix +++ b/modules/plugins/languages/lua.nix @@ -8,17 +8,36 @@ inherit (lib.options) mkEnableOption mkOption; inherit (lib.modules) mkIf mkMerge; inherit (lib.meta) getExe; - inherit (lib.lists) isList; - inherit (lib.types) bool either enum listOf package str; - inherit (lib.nvim.types) diagnostics mkGrammarOption; - inherit (lib.nvim.lua) expToLua; + inherit (lib.types) bool enum listOf; + inherit (lib.nvim.types) diagnostics mkGrammarOption deprecatedSingleOrListOf; inherit (lib.nvim.dag) entryBefore; + inherit (lib.nvim.attrsets) mapListToAttrs; cfg = config.vim.languages.lua; - defaultFormat = "stylua"; + + defaultServers = ["lua-language-server"]; + servers = { + lua-language-server = { + enable = true; + cmd = [(getExe pkgs.lua-language-server)]; + filetypes = ["lua"]; + root_markers = [ + ".luarc.json" + ".luarc.jsonc" + ".luacheckrc" + ".stylua.toml" + "stylua.toml" + "selene.toml" + "selene.yml" + ".git" + ]; + }; + }; + + defaultFormat = ["stylua"]; formats = { stylua = { - package = pkgs.stylua; + command = getExe pkgs.stylua; }; }; @@ -43,12 +62,11 @@ in { }; lsp = { - enable = mkEnableOption "Lua LSP support via LuaLS" // {default = config.vim.lsp.enable;}; - - package = mkOption { - description = "LuaLS package, or the command to run as a list of strings"; - type = either package (listOf str); - default = pkgs.lua-language-server; + enable = mkEnableOption "Lua LSP support" // {default = config.vim.lsp.enable;}; + servers = mkOption { + type = listOf (enum (attrNames servers)); + default = defaultServers; + description = "Lua LSP server to use"; }; lazydev.enable = mkEnableOption "lazydev.nvim integration, useful for neovim plugin developers"; @@ -61,16 +79,10 @@ in { description = "Enable Lua formatting"; }; type = mkOption { - type = enum (attrNames formats); + type = deprecatedSingleOrListOf "vim.language.lua.format.type" (enum (attrNames formats)); default = defaultFormat; description = "Lua formatter to use"; }; - - package = mkOption { - type = package; - default = formats.${cfg.format.type}.package; - description = "Lua formatter package"; - }; }; extraDiagnostics = { @@ -91,23 +103,17 @@ in { (mkIf cfg.enable (mkMerge [ (mkIf cfg.lsp.enable { - vim.lsp.lspconfig.enable = true; - vim.lsp.lspconfig.sources.lua-lsp = '' - lspconfig.lua_ls.setup { - capabilities = capabilities; - on_attach = default_on_attach; - cmd = ${ - if isList cfg.lsp.package - then expToLua cfg.lsp.package - else ''{"${getExe cfg.lsp.package}"}'' - }; - } - ''; + vim.lsp.servers = + mapListToAttrs (n: { + name = n; + value = servers.${n}; + }) + cfg.lsp.servers; }) (mkIf cfg.lsp.lazydev.enable { vim.startPlugins = ["lazydev-nvim"]; - vim.pluginRC.lazydev = entryBefore ["lua-lsp"] '' + vim.pluginRC.lazydev = entryBefore ["lsp-servers"] '' require("lazydev").setup({ enabled = function(root_dir) return not vim.uv.fs_stat(root_dir .. "/.luarc.json") @@ -120,9 +126,14 @@ in { (mkIf cfg.format.enable { vim.formatter.conform-nvim = { enable = true; - setupOpts.formatters_by_ft.lua = [cfg.format.type]; - setupOpts.formatters.${cfg.format.type} = { - command = getExe cfg.format.package; + setupOpts = { + formatters_by_ft.lua = cfg.format.type; + formatters = + mapListToAttrs (name: { + inherit name; + value = formats.${name}; + }) + cfg.format.type; }; }; }) diff --git a/modules/plugins/languages/markdown.nix b/modules/plugins/languages/markdown.nix index 23848835..59615cec 100644 --- a/modules/plugins/languages/markdown.nix +++ b/modules/plugins/languages/markdown.nix @@ -8,42 +8,42 @@ inherit (lib.meta) getExe; inherit (lib.modules) mkIf mkMerge; inherit (lib.options) mkEnableOption mkOption; - inherit (lib.lists) isList; - inherit (lib.types) bool enum either package listOf str nullOr; - inherit (lib.nvim.lua) expToLua toLuaObject; - inherit (lib.nvim.types) diagnostics mkGrammarOption mkPluginSetupOption; + inherit (lib.types) bool enum listOf str nullOr; + inherit (lib.nvim.lua) toLuaObject; + inherit (lib.nvim.types) diagnostics mkGrammarOption mkPluginSetupOption deprecatedSingleOrListOf; inherit (lib.nvim.dag) entryAnywhere; + inherit (lib.nvim.attrsets) mapListToAttrs; + inherit (lib.trivial) warn; cfg = config.vim.languages.markdown; - defaultServer = "marksman"; + defaultServers = ["marksman"]; servers = { marksman = { - package = pkgs.marksman; - lspConfig = '' - lspconfig.marksman.setup{ - capabilities = capabilities; - on_attach = default_on_attach; - cmd = ${ - if isList cfg.lsp.package - then expToLua cfg.lsp.package - else ''{"${cfg.lsp.package}/bin/marksman", "server"}'' - }, - } - ''; + enable = true; + cmd = [(getExe pkgs.marksman) "server"]; + filetypes = ["markdown" "markdown.mdx"]; + root_markers = [".marksman.toml" ".git"]; + }; + + markdown-oxide = { + enable = true; + cmd = [(getExe pkgs.markdown-oxide)]; + filetypes = ["markdown"]; + root_markers = [".git" ".obsidian" ".moxide.toml"]; }; }; - defaultFormat = "deno_fmt"; + defaultFormat = ["deno_fmt"]; formats = { # for backwards compatibility denofmt = { - package = pkgs.deno; + command = getExe pkgs.deno; }; deno_fmt = { - package = pkgs.deno; + command = getExe pkgs.deno; }; prettierd = { - package = pkgs.prettierd; + command = getExe pkgs.prettierd; }; }; defaultDiagnosticsProvider = ["markdownlint-cli2"]; @@ -67,19 +67,12 @@ in { }; lsp = { - enable = mkEnableOption "Enable Markdown LSP support" // {default = config.vim.lsp.enable;}; + enable = mkEnableOption "Markdown LSP support" // {default = config.vim.lsp.enable;}; - server = mkOption { - type = enum (attrNames servers); - default = defaultServer; + servers = mkOption { description = "Markdown LSP server to use"; - }; - - package = mkOption { - type = either package (listOf str); - default = servers.${cfg.lsp.server}.package; - example = ''[lib.getExe pkgs.jdt-language-server " - data " " ~/.cache/jdtls/workspace "]''; - description = "Markdown LSP server package, or the command to run as a list of strings"; + type = deprecatedSingleOrListOf "vim.language.markdown.lsp.servers" (enum (attrNames servers)); + default = defaultServers; }; }; @@ -87,17 +80,11 @@ in { enable = mkEnableOption "Markdown formatting" // {default = config.vim.languages.enableFormat;}; type = mkOption { - type = enum (attrNames formats); + type = deprecatedSingleOrListOf "vim.language.markdown.format.type" (enum (attrNames formats)); default = defaultFormat; description = "Markdown formatter to use. `denofmt` is deprecated and currently aliased to deno_fmt."; }; - package = mkOption { - type = package; - default = formats.${cfg.format.type}.package; - description = "Markdown formatter package"; - }; - extraFiletypes = mkOption { type = listOf str; default = []; @@ -161,20 +148,34 @@ in { }) (mkIf cfg.lsp.enable { - vim.lsp.lspconfig.enable = true; - vim.lsp.lspconfig.sources.markdown-lsp = servers.${cfg.lsp.server}.lspConfig; + vim.lsp.servers = + mapListToAttrs (n: { + name = n; + value = servers.${n}; + }) + cfg.lsp.servers; }) (mkIf cfg.format.enable { vim.formatter.conform-nvim = { enable = true; - setupOpts.formatters_by_ft.markdown = [cfg.format.type]; - setupOpts.formatters.${ - if cfg.format.type == "denofmt" - then "deno_fmt" - else cfg.format.type - } = { - command = getExe cfg.format.package; + setupOpts = { + formatters_by_ft.markdown = cfg.format.type; + formatters = let + names = map (name: + if name == "denofmt" + then + warn '' + vim.languages.markdown.format.type: "denofmt" is renamed to "deno_fmt". + '' "deno_fmt" + else name) + cfg.format.type; + in + mapListToAttrs (name: { + inherit name; + value = formats.${name}; + }) + names; }; }; }) diff --git a/modules/plugins/languages/nim.nix b/modules/plugins/languages/nim.nix index 6e5885e2..faf71baa 100644 --- a/modules/plugins/languages/nim.nix +++ b/modules/plugins/languages/nim.nix @@ -6,41 +6,41 @@ }: let inherit (builtins) attrNames; inherit (lib.options) mkEnableOption mkOption; + inherit (lib.meta) getExe'; inherit (lib.modules) mkIf mkMerge; - inherit (lib.lists) isList; - inherit (lib.types) enum either listOf package str; - inherit (lib.nvim.types) mkGrammarOption; - inherit (lib.nvim.lua) expToLua; + inherit (lib.types) enum; + inherit (lib.nvim.types) mkGrammarOption deprecatedSingleOrListOf; + inherit (lib.generators) mkLuaInline; + inherit (lib.nvim.attrsets) mapListToAttrs; cfg = config.vim.languages.nim; - defaultServer = "nimlsp"; + defaultServers = ["nimlsp"]; servers = { nimlsp = { - package = pkgs.nimlsp; - lspConfig = '' - lspconfig.nimls.setup{ - capabilities = capabilities; - on_attach = default_on_attach; - cmd = ${ - if isList cfg.lsp.package - then expToLua cfg.lsp.package - else '' - {"${cfg.lsp.package}/bin/nimlsp"} - '' - }; - } - ''; + enable = true; + cmd = [(getExe' pkgs.nimlsp "nimlsp")]; + filetypes = ["nim"]; + root_dir = + mkLuaInline + /* + lua + */ + '' + function(bufnr, on_dir) + local fname = vim.api.nvim_buf_get_name(bufnr) + on_dir( + util.root_pattern '*.nimble'(fname) or vim.fs.dirname(vim.fs.find('.git', { path = fname, upward = true })[1]) + ) + end + ''; }; }; - defaultFormat = "nimpretty"; + defaultFormat = ["nimpretty"]; formats = { nimpretty = { - package = pkgs.nim; - config = { - command = "${cfg.format.package}/bin/nimpretty"; - }; + command = "${pkgs.nim}/bin/nimpretty"; }; }; in { @@ -54,32 +54,20 @@ in { lsp = { enable = mkEnableOption "Nim LSP support" // {default = config.vim.lsp.enable;}; - server = mkOption { - description = "Nim LSP server to use"; - type = str; - default = defaultServer; - }; - package = mkOption { - description = "Nim LSP server package, or the command to run as a list of strings"; - example = ''[lib.getExe pkgs.nimlsp]''; - type = either package (listOf str); - default = servers.${cfg.lsp.server}.package; + servers = mkOption { + type = deprecatedSingleOrListOf "vim.language.nim.lsp.servers" (enum (attrNames servers)); + default = defaultServers; + description = "Nim LSP server to use"; }; }; format = { enable = mkEnableOption "Nim formatting" // {default = config.vim.languages.enableFormat;}; type = mkOption { - description = "Nim formatter to use"; - type = enum (attrNames formats); + type = deprecatedSingleOrListOf "vim.language.nim.format.type" (enum (attrNames formats)); default = defaultFormat; - }; - - package = mkOption { - description = "Nim formatter package"; - type = package; - default = formats.${cfg.format.type}.package; + description = "Nim formatter to use"; }; }; }; @@ -100,15 +88,26 @@ in { }) (mkIf cfg.lsp.enable { - vim.lsp.lspconfig.enable = true; - vim.lsp.lspconfig.sources.nim-lsp = servers.${cfg.lsp.server}.lspConfig; + vim.lsp.servers = + mapListToAttrs (n: { + name = n; + value = servers.${n}; + }) + cfg.lsp.servers; }) (mkIf cfg.format.enable { vim.formatter.conform-nvim = { enable = true; - setupOpts.formatters_by_ft.nim = [cfg.format.type]; - setupOpts.formatters.${cfg.format.type} = formats.${cfg.format.type}.config; + setupOpts = { + formatters_by_ft.nim = cfg.format.type; + formatters = + mapListToAttrs (name: { + inherit name; + value = formats.${name}; + }) + cfg.format.type; + }; }; }) ]); diff --git a/modules/plugins/languages/nix.nix b/modules/plugins/languages/nix.nix index 7d126e15..f03806f9 100644 --- a/modules/plugins/languages/nix.nix +++ b/modules/plugins/languages/nix.nix @@ -9,93 +9,37 @@ inherit (lib.meta) getExe; inherit (lib.options) mkEnableOption mkOption; inherit (lib.modules) mkIf mkMerge; - inherit (lib.lists) isList; - inherit (lib.strings) optionalString; - inherit (lib.types) anything attrsOf enum either listOf nullOr package str; - inherit (lib.nvim.types) mkGrammarOption diagnostics; - inherit (lib.nvim.lua) expToLua toLuaObject; + inherit (lib.types) enum; + inherit (lib.nvim.types) mkGrammarOption diagnostics deprecatedSingleOrListOf; + inherit (lib.nvim.attrsets) mapListToAttrs; cfg = config.vim.languages.nix; - useFormat = "on_attach = default_on_attach"; - noFormat = "on_attach = attach_keymaps"; - - defaultServer = "nil"; - packageToCmd = package: defaultCmd: - if isList package - then expToLua package - else ''{"${package}/bin/${defaultCmd}"}''; + defaultServers = ["nil"]; servers = { nil = { - package = pkgs.nil; - internalFormatter = true; - lspConfig = '' - lspconfig.nil_ls.setup{ - capabilities = capabilities, - ${ - if cfg.format.enable - then useFormat - else noFormat - }, - cmd = ${packageToCmd cfg.lsp.package "nil"}, - ${optionalString cfg.format.enable '' - settings = { - ["nil"] = { - ${optionalString (cfg.format.type == "alejandra") - '' - formatting = { - command = {"${cfg.format.package}/bin/alejandra", "--quiet"}, - }, - ''} - ${optionalString (cfg.format.type == "nixfmt") - '' - formatting = { - command = {"${cfg.format.package}/bin/nixfmt"}, - }, - ''} - }, - }, - ''} - } - ''; + enable = true; + cmd = [(getExe pkgs.nil)]; + filetypes = ["nix"]; + root_markers = [".git" "flake.nix"]; }; - nixd = let - settings.nixd = { - inherit (cfg.lsp) options; - formatting.command = - if !cfg.format.enable - then null - else if cfg.format.type == "alejandra" - then ["${cfg.format.package}/bin/alejandra" "--quiet"] - else ["${cfg.format.package}/bin/nixfmt"]; - }; - in { - package = pkgs.nixd; - internalFormatter = true; - lspConfig = '' - lspconfig.nixd.setup{ - capabilities = capabilities, - ${ - if cfg.format.enable - then useFormat - else noFormat - }, - cmd = ${packageToCmd cfg.lsp.package "nixd"}, - settings = ${toLuaObject settings}, - } - ''; + nixd = { + enable = true; + cmd = [(getExe pkgs.nixd)]; + filetypes = ["nix"]; + root_markers = [".git" "flake.nix"]; }; }; - defaultFormat = "alejandra"; + defaultFormat = ["alejandra"]; formats = { alejandra = { - package = pkgs.alejandra; + command = getExe pkgs.alejandra; }; nixfmt = { - package = pkgs.nixfmt-rfc-style; + command = getExe pkgs.nixfmt-rfc-style; }; }; @@ -136,23 +80,10 @@ in { lsp = { enable = mkEnableOption "Nix LSP support" // {default = config.vim.lsp.enable;}; - server = mkOption { + servers = mkOption { + type = deprecatedSingleOrListOf "vim.language.nix.lsp.servers" (enum (attrNames servers)); + default = defaultServers; description = "Nix LSP server to use"; - type = enum (attrNames servers); - default = defaultServer; - }; - - package = mkOption { - description = "Nix LSP server package, or the command to run as a list of strings"; - example = ''[lib.getExe pkgs.jdt-language-server "-data" "~/.cache/jdtls/workspace"]''; - type = either package (listOf str); - default = servers.${cfg.lsp.server}.package; - }; - - options = mkOption { - type = nullOr (attrsOf anything); - default = null; - description = "Options to pass to nixd LSP server"; }; }; @@ -161,15 +92,9 @@ in { type = mkOption { description = "Nix formatter to use"; - type = enum (attrNames formats); + type = deprecatedSingleOrListOf "vim.language.nix.format.type" (enum (attrNames formats)); default = defaultFormat; }; - - package = mkOption { - description = "Nix formatter package"; - type = package; - default = formats.${cfg.format.type}.package; - }; }; extraDiagnostics = { @@ -193,13 +118,6 @@ in { ${concatStringsSep ", " (attrNames formats)} ''; } - { - assertion = cfg.lsp.server != "rnix"; - message = '' - rnix-lsp has been archived upstream. Please use one of the following available language servers: - ${concatStringsSep ", " (attrNames servers)} - ''; - } ]; } @@ -209,16 +127,25 @@ in { }) (mkIf cfg.lsp.enable { - vim.lsp.lspconfig.enable = true; - vim.lsp.lspconfig.sources.nix-lsp = servers.${cfg.lsp.server}.lspConfig; + vim.lsp.servers = + mapListToAttrs (n: { + name = n; + value = servers.${n}; + }) + cfg.lsp.servers; }) - (mkIf (cfg.format.enable && (!cfg.lsp.enable || !servers.${cfg.lsp.server}.internalFormatter)) { + (mkIf cfg.format.enable { vim.formatter.conform-nvim = { enable = true; - setupOpts.formatters_by_ft.nix = [cfg.format.type]; - setupOpts.formatters.${cfg.format.type} = { - command = getExe cfg.format.package; + setupOpts = { + formatters_by_ft.nix = cfg.format.type; + formatters = + mapListToAttrs (name: { + inherit name; + value = formats.${name}; + }) + cfg.format.type; }; }; }) diff --git a/modules/plugins/languages/nu.nix b/modules/plugins/languages/nu.nix index bd007fc8..7bbe1d78 100644 --- a/modules/plugins/languages/nu.nix +++ b/modules/plugins/languages/nu.nix @@ -5,27 +5,30 @@ ... }: let inherit (lib.options) mkEnableOption mkOption; - inherit (lib.types) str either package listOf; + inherit (lib.types) enum; inherit (lib.modules) mkIf mkMerge; - inherit (lib.nvim.lua) expToLua; - inherit (lib.nvim.types) mkGrammarOption; - inherit (builtins) isList; + inherit (lib.nvim.types) mkGrammarOption deprecatedSingleOrListOf; + inherit (lib.meta) getExe; + inherit (lib.generators) mkLuaInline; + inherit (lib.nvim.attrsets) mapListToAttrs; + inherit (builtins) attrNames; - defaultServer = "nushell"; + defaultServers = ["nushell"]; servers = { nushell = { - package = pkgs.nushell; - lspConfig = '' - lspconfig.nushell.setup{ - capabilities = capabilities, - on_attach = default_on_attach, - cmd = ${ - if isList cfg.lsp.package - then expToLua cfg.lsp.package - else ''{"${cfg.lsp.package}/bin/nu", "--no-config-file", "--lsp"}'' - } - } - ''; + enable = true; + cmd = [(getExe pkgs.nushell) "--no-config-file" "--lsp"]; + filetypes = ["nu"]; + root_dir = + mkLuaInline + /* + lua + */ + '' + function(bufnr, on_dir) + on_dir(vim.fs.root(bufnr, { '.git' }) or vim.fs.dirname(vim.api.nvim_buf_get_name(bufnr))) + end + ''; }; }; @@ -41,17 +44,11 @@ in { lsp = { enable = mkEnableOption "Nu LSP support" // {default = config.vim.lsp.enable;}; - server = mkOption { - type = str; - default = defaultServer; - description = "Nu LSP server to use"; - }; - package = mkOption { - type = either package (listOf str); - default = servers.${cfg.lsp.server}.package; - example = ''[(lib.getExe pkgs.nushell) "--lsp"]''; - description = "Nu LSP server package, or the command to run as a list of strings"; + servers = mkOption { + type = deprecatedSingleOrListOf "vim.language.nu.lsp.servers" (enum (attrNames servers)); + default = defaultServers; + description = "Nu LSP server to use"; }; }; }; @@ -63,8 +60,12 @@ in { }) (mkIf cfg.lsp.enable { - vim.lsp.lspconfig.enable = true; - vim.lsp.lspconfig.sources.nu-lsp = servers.${cfg.lsp.server}.lspConfig; + vim.lsp.servers = + mapListToAttrs (n: { + name = n; + value = servers.${n}; + }) + cfg.lsp.servers; }) ]); } diff --git a/modules/plugins/languages/ocaml.nix b/modules/plugins/languages/ocaml.nix index 995ca04d..2ff5bfce 100644 --- a/modules/plugins/languages/ocaml.nix +++ b/modules/plugins/languages/ocaml.nix @@ -8,35 +8,57 @@ inherit (lib.options) mkEnableOption mkOption; inherit (lib.modules) mkIf mkMerge; inherit (lib.meta) getExe; - inherit (lib.lists) isList; - inherit (lib.types) either enum listOf package str; - inherit (lib.nvim.types) mkGrammarOption; - inherit (lib.nvim.lua) expToLua; + inherit (lib.types) enum; + inherit (lib.nvim.types) mkGrammarOption deprecatedSingleOrListOf; + inherit (lib.generators) mkLuaInline; + inherit (lib.nvim.attrsets) mapListToAttrs; cfg = config.vim.languages.ocaml; - defaultServer = "ocaml-lsp"; + defaultServers = ["ocaml-lsp"]; servers = { ocaml-lsp = { - package = pkgs.ocamlPackages.ocaml-lsp; - lspConfig = '' - lspconfig.ocamllsp.setup { - capabilities = capabilities, - on_attach = default_on_attach, - cmd = ${ - if isList cfg.lsp.package - then expToLua cfg.lsp.package - else ''{"${getExe cfg.lsp.package}"}'' - }; - } - ''; + enable = true; + cmd = [(getExe pkgs.ocamlPackages.ocaml-lsp)]; + filetypes = ["ocaml" "menhir" "ocamlinterface" "ocamllex" "reason" "dune"]; + root_dir = + mkLuaInline + /* + lua + */ + '' + function(bufnr, on_dir) + local fname = vim.api.nvim_buf_get_name(bufnr) + on_dir(util.root_pattern('*.opam', 'esy.json', 'package.json', '.git', 'dune-project', 'dune-workspace')(fname)) + end + ''; + get_language_id = + mkLuaInline + /* + lua + */ + '' + function(_, ftype) + local language_id_of = { + menhir = 'ocaml.menhir', + ocaml = 'ocaml', + ocamlinterface = 'ocaml.interface', + ocamllex = 'ocaml.ocamllex', + reason = 'reason', + dune = 'dune', + } + + return language_id_of[ftype] + + end + ''; }; }; - defaultFormat = "ocamlformat"; + defaultFormat = ["ocamlformat"]; formats = { ocamlformat = { - package = pkgs.ocamlPackages.ocamlformat; + command = getExe pkgs.ocamlPackages.ocamlformat; }; }; in { @@ -49,38 +71,33 @@ in { }; lsp = { - enable = mkEnableOption "OCaml LSP support (ocaml-lsp)" // {default = config.vim.lsp.enable;}; - server = mkOption { - description = "OCaml LSP server to user"; - type = enum (attrNames servers); - default = defaultServer; - }; - package = mkOption { - description = "OCaml language server package, or the command to run as a list of strings"; - type = either package (listOf str); - default = servers.${cfg.lsp.server}.package; + enable = mkEnableOption "OCaml LSP support" // {default = config.vim.lsp.enable;}; + + servers = mkOption { + type = deprecatedSingleOrListOf "vim.language.ocaml.lsp.servers" (enum (attrNames servers)); + default = defaultServers; + description = "OCaml LSP server to use"; }; }; format = { enable = mkEnableOption "OCaml formatting support (ocamlformat)" // {default = config.vim.languages.enableFormat;}; type = mkOption { - description = "OCaml formatter to use"; - type = enum (attrNames formats); + type = deprecatedSingleOrListOf "vim.language.ocaml.format.type" (enum (attrNames formats)); default = defaultFormat; - }; - package = mkOption { - description = "OCaml formatter package"; - type = package; - default = formats.${cfg.format.type}.package; + description = "OCaml formatter to use"; }; }; }; config = mkIf cfg.enable (mkMerge [ (mkIf cfg.lsp.enable { - vim.lsp.lspconfig.enable = true; - vim.lsp.lspconfig.sources.ocaml-lsp = servers.${cfg.lsp.server}.lspConfig; + vim.lsp.servers = + mapListToAttrs (n: { + name = n; + value = servers.${n}; + }) + cfg.lsp.servers; }) (mkIf cfg.treesitter.enable { @@ -91,9 +108,14 @@ in { (mkIf cfg.format.enable { vim.formatter.conform-nvim = { enable = true; - setupOpts.formatters_by_ft.ocaml = [cfg.format.type]; - setupOpts.formatters.${cfg.format.type} = { - command = getExe cfg.format.package; + setupOpts = { + formatters_by_ft.ocaml = cfg.format.type; + formatters = + mapListToAttrs (name: { + inherit name; + value = formats.${name}; + }) + cfg.format.type; }; }; }) diff --git a/modules/plugins/languages/odin.nix b/modules/plugins/languages/odin.nix index 6d20351c..fa2254c5 100644 --- a/modules/plugins/languages/odin.nix +++ b/modules/plugins/languages/odin.nix @@ -7,26 +7,28 @@ inherit (builtins) attrNames; inherit (lib.options) mkEnableOption mkOption; inherit (lib.modules) mkIf mkMerge; - inherit (lib.lists) isList; - inherit (lib.types) either listOf package str enum; - inherit (lib.nvim.lua) expToLua; - inherit (lib.nvim.types) mkGrammarOption; + inherit (lib.types) enum; + inherit (lib.meta) getExe; + inherit (lib.nvim.types) mkGrammarOption deprecatedSingleOrListOf; + inherit (lib.generators) mkLuaInline; + inherit (lib.nvim.attrsets) mapListToAttrs; - defaultServer = "ols"; + defaultServers = ["ols"]; servers = { ols = { - package = pkgs.ols; - lspConfig = '' - lspconfig.ols.setup { - capabilities = capabilities, - on_attach = default_on_attach, - cmd = ${ - if isList cfg.lsp.package - then expToLua cfg.lsp.package - else "{'${cfg.lsp.package}/bin/ols'}" - } - } - ''; + enable = true; + cmd = [(getExe pkgs.ols)]; + filetypes = ["odin"]; + root_dir = + mkLuaInline + /* + lua + */ + '' + function(bufnr, on_dir) + local fname = vim.api.nvim_buf_get_name(bufnr) + on_dir(util.root_pattern('ols.json', '.git', '*.odin')(fname)) + end''; }; }; @@ -43,17 +45,11 @@ in { lsp = { enable = mkEnableOption "Odin LSP support" // {default = config.vim.lsp.enable;}; - server = mkOption { - type = enum (attrNames servers); - default = defaultServer; + servers = mkOption { + type = deprecatedSingleOrListOf "vim.language.odin.lsp.servers" (enum (attrNames servers)); + default = defaultServers; description = "Odin LSP server to use"; }; - - package = mkOption { - description = "Ols package, or the command to run as a list of strings"; - type = either package (listOf str); - default = pkgs.ols; - }; }; }; @@ -64,8 +60,12 @@ in { }) (mkIf cfg.lsp.enable { - vim.lsp.lspconfig.enable = true; - vim.lsp.lspconfig.sources.odin-lsp = servers.${cfg.lsp.server}.lspConfig; + vim.lsp.servers = + mapListToAttrs (n: { + name = n; + value = servers.${n}; + }) + cfg.lsp.servers; }) ]); } diff --git a/modules/plugins/languages/php.nix b/modules/plugins/languages/php.nix index 61ff0425..193c0a1f 100644 --- a/modules/plugins/languages/php.nix +++ b/modules/plugins/languages/php.nix @@ -8,81 +8,60 @@ inherit (lib.options) mkEnableOption mkOption; inherit (lib.meta) getExe; inherit (lib.modules) mkIf mkMerge; - inherit (lib.lists) isList; - inherit (lib.types) enum either listOf package str; - inherit (lib.nvim.types) mkGrammarOption; - inherit (lib.nvim.lua) expToLua; + inherit (lib.types) enum; + inherit (lib.nvim.types) mkGrammarOption deprecatedSingleOrListOf; + inherit (lib.nvim.attrsets) mapListToAttrs; + inherit (lib.generators) mkLuaInline; cfg = config.vim.languages.php; - defaultServer = "phpactor"; + defaultServers = ["phpactor"]; servers = { phpactor = { - package = pkgs.phpactor; - lspConfig = '' - lspconfig.phpactor.setup{ - capabilities = capabilities, - on_attach = default_on_attach, - cmd = ${ - if isList cfg.lsp.package - then expToLua cfg.lsp.package - else '' - { - "${getExe cfg.lsp.package}", - "language-server" - }, - '' - } - } - ''; + enable = true; + cmd = [(getExe pkgs.phpactor) "language-server"]; + filetypes = ["php"]; + root_markers = [".git" "composer.json" ".phpactor.json" ".phpactor.yml"]; + workspace_required = true; }; phan = { - package = pkgs.php81Packages.phan; - lspConfig = '' - lspconfig.phan.setup{ - capabilities = capabilities, - on_attach = default_on_attach, - cmd = ${ - if isList cfg.lsp.package - then expToLua cfg.lsp.package - else '' - { - "${getExe cfg.lsp.package}", - "-m", - "json", - "--no-color", - "--no-progress-bar", - "-x", - "-u", - "-S", - "--language-server-on-stdin", - "--allow-polyfill-parser" - }, - '' - } - } - ''; + enable = true; + cmd = [ + (getExe pkgs.php81Packages.phan) + "-m" + "json" + "--no-color" + "--no-progress-bar" + "-x" + "-u" + "-S" + "--language-server-on-stdin" + "--allow-polyfill-parser" + ]; + filetypes = ["php"]; + root_dir = + mkLuaInline + /* + lua + */ + '' + function(bufnr, on_dir) + local fname = vim.api.nvim_buf_get_name(bufnr) + local cwd = assert(vim.uv.cwd()) + local root = vim.fs.root(fname, { 'composer.json', '.git' }) + + -- prefer cwd if root is a descendant + on_dir(root and vim.fs.relpath(cwd, root) and cwd) + end + ''; }; intelephense = { - package = pkgs.intelephense; - lspConfig = '' - lspconfig.intelephense.setup{ - capabilities = capabilities, - on_attach = default_on_attach, - cmd = ${ - if isList cfg.lsp.package - then expToLua cfg.lsp.package - else '' - { - "${getExe cfg.lsp.package}", - "--stdio" - }, - '' - } - } - ''; + enable = true; + cmd = [(getExe pkgs.intelephense) "--stdio"]; + filetypes = ["php"]; + root_markers = ["composer.json" ".git"]; }; }; in { @@ -97,17 +76,10 @@ in { lsp = { enable = mkEnableOption "PHP LSP support" // {default = config.vim.lsp.enable;}; - server = mkOption { + servers = mkOption { + type = deprecatedSingleOrListOf "vim.language.php.lsp.servers" (enum (attrNames servers)); + default = defaultServers; description = "PHP LSP server to use"; - type = enum (attrNames servers); - default = defaultServer; - }; - - package = mkOption { - description = "PHP LSP server package, or the command to run as a list of strings"; - example = ''[lib.getExe pkgs.jdt-language-server " - data " " ~/.cache/jdtls/workspace "]''; - type = either package (listOf str); - default = servers.${cfg.lsp.server}.package; }; }; }; @@ -117,11 +89,14 @@ in { vim.treesitter.enable = true; vim.treesitter.grammars = [cfg.treesitter.package]; }) + (mkIf cfg.lsp.enable { - vim.lsp.lspconfig = { - enable = true; - sources.php-lsp = servers.${cfg.lsp.server}.lspConfig; - }; + vim.lsp.servers = + mapListToAttrs (n: { + name = n; + value = servers.${n}; + }) + cfg.lsp.servers; }) ]); } diff --git a/modules/plugins/languages/python.nix b/modules/plugins/languages/python.nix index bec7ec8b..b1cbe224 100644 --- a/modules/plugins/languages/python.nix +++ b/modules/plugins/languages/python.nix @@ -6,88 +6,152 @@ }: let inherit (builtins) attrNames; inherit (lib.options) mkEnableOption mkOption literalExpression; - inherit (lib.meta) getExe; + inherit (lib.lists) flatten; + inherit (lib.meta) getExe getExe'; inherit (lib.modules) mkIf mkMerge; - inherit (lib.lists) isList; - inherit (lib.types) enum either listOf package str bool; - inherit (lib.nvim.lua) expToLua; + inherit (lib.types) enum package bool; + inherit (lib.nvim.attrsets) mapListToAttrs; + inherit (lib.nvim.types) deprecatedSingleOrListOf; + inherit (lib.generators) mkLuaInline; + inherit (lib.nvim.dag) entryBefore; + inherit (lib.trivial) warn; cfg = config.vim.languages.python; - defaultServer = "basedpyright"; + defaultServers = ["basedpyright"]; servers = { pyright = { - package = pkgs.pyright; - lspConfig = '' - lspconfig.pyright.setup{ - capabilities = capabilities; - on_attach = default_on_attach; - cmd = ${ - if isList cfg.lsp.package - then expToLua cfg.lsp.package - else ''{"${cfg.lsp.package}/bin/pyright-langserver", "--stdio"}'' - } - } + enable = true; + cmd = [(getExe' pkgs.pyright "pyright-langserver") "--stdio"]; + filetypes = ["python"]; + root_markers = [ + "pyproject.toml" + "setup.py" + "setup.cfg" + "requirements.txt" + "Pipfile" + "pyrightconfig.json" + ".git" + ]; + settings = { + python = { + analysis = { + autoSearchPaths = true; + useLibraryCodeForTypes = true; + diagnosticMode = "openFilesOnly"; + }; + }; + }; + on_attach = mkLuaInline '' + function(client, bufnr) + vim.api.nvim_buf_create_user_command(bufnr, 'LspPyrightOrganizeImports', function() + local params = { + command = 'pyright.organizeimports', + arguments = { vim.uri_from_bufnr(bufnr) }, + } + + -- Using client.request() directly because "pyright.organizeimports" is private + -- (not advertised via capabilities), which client:exec_cmd() refuses to call. + -- https://github.com/neovim/neovim/blob/c333d64663d3b6e0dd9aa440e433d346af4a3d81/runtime/lua/vim/lsp/client.lua#L1024-L1030 + client.request('workspace/executeCommand', params, nil, bufnr) + end, { + desc = 'Organize Imports', + }) + vim.api.nvim_buf_create_user_command(bufnr, 'LspPyrightSetPythonPath', set_python_path, { + desc = 'Reconfigure basedpyright with the provided python path', + nargs = 1, + complete = 'file', + }) + end ''; }; basedpyright = { - package = pkgs.basedpyright; - lspConfig = '' - lspconfig.basedpyright.setup{ - capabilities = capabilities; - on_attach = default_on_attach; - cmd = ${ - if isList cfg.lsp.package - then expToLua cfg.lsp.package - else ''{"${cfg.lsp.package}/bin/basedpyright-langserver", "--stdio"}'' - } - } + enable = true; + cmd = [(getExe' pkgs.basedpyright "basedpyright-langserver") "--stdio"]; + filetypes = ["python"]; + root_markers = [ + "pyproject.toml" + "setup.py" + "setup.cfg" + "requirements.txt" + "Pipfile" + "pyrightconfig.json" + ".git" + ]; + settings = { + basedpyright = { + analysis = { + autoSearchPaths = true; + useLibraryCodeForTypes = true; + diagnosticMode = "openFilesOnly"; + }; + }; + }; + on_attach = mkLuaInline '' + function(client, bufnr) + vim.api.nvim_buf_create_user_command(bufnr, 'LspPyrightOrganizeImports', function() + local params = { + command = 'basedpyright.organizeimports', + arguments = { vim.uri_from_bufnr(bufnr) }, + } + + -- Using client.request() directly because "basedpyright.organizeimports" is private + -- (not advertised via capabilities), which client:exec_cmd() refuses to call. + -- https://github.com/neovim/neovim/blob/c333d64663d3b6e0dd9aa440e433d346af4a3d81/runtime/lua/vim/lsp/client.lua#L1024-L1030 + client.request('workspace/executeCommand', params, nil, bufnr) + end, { + desc = 'Organize Imports', + }) + + vim.api.nvim_buf_create_user_command(bufnr, 'LspPyrightSetPythonPath', set_python_path, { + desc = 'Reconfigure basedpyright with the provided python path', + nargs = 1, + complete = 'file', + }) + end ''; }; python-lsp-server = { - package = pkgs.python3Packages.python-lsp-server; - lspConfig = '' - lspconfig.pylsp.setup{ - capabilities = capabilities; - on_attach = default_on_attach; - cmd = ${ - if isList cfg.lsp.package - then expToLua cfg.lsp.package - else ''{"${cfg.lsp.package}/bin/pylsp"}'' - } - } - ''; + enable = true; + cmd = [(getExe pkgs.python3Packages.python-lsp-server)]; + filetypes = ["python"]; + root_markers = [ + "pyproject.toml" + "setup.py" + "setup.cfg" + "requirements.txt" + "Pipfile" + ".git" + ]; }; }; - defaultFormat = "black"; + defaultFormat = ["black"]; formats = { black = { - package = pkgs.black; + command = getExe pkgs.black; }; isort = { - package = pkgs.isort; + command = getExe pkgs.isort; }; - black-and-isort = { - package = pkgs.writeShellApplication { - name = "black"; - runtimeInputs = [pkgs.black pkgs.isort]; - text = '' - black --quiet - "$@" | isort --profile black - - ''; - }; - }; + # dummy option for backwards compat + black-and-isort = {}; ruff = { + command = getExe pkgs.ruff; + args = ["format" "-"]; + }; + + ruff-check = { package = pkgs.writeShellApplication { - name = "ruff"; + name = "ruff-check"; runtimeInputs = [pkgs.ruff]; text = '' - ruff format - + ruff check --fix --exit-zero - ''; }; }; @@ -171,17 +235,10 @@ in { lsp = { enable = mkEnableOption "Python LSP support" // {default = config.vim.lsp.enable;}; - server = mkOption { + servers = mkOption { + type = deprecatedSingleOrListOf "vim.language.python.lsp.servers" (enum (attrNames servers)); + default = defaultServers; description = "Python LSP server to use"; - type = enum (attrNames servers); - default = defaultServer; - }; - - package = mkOption { - description = "python LSP server package, or the command to run as a list of strings"; - example = ''[lib.getExe pkgs.jdt-language-server "-data" "~/.cache/jdtls/workspace"]''; - type = either package (listOf str); - default = servers.${cfg.lsp.server}.package; }; }; @@ -189,30 +246,24 @@ in { enable = mkEnableOption "Python formatting" // {default = config.vim.languages.enableFormat;}; type = mkOption { - description = "Python formatter to use"; - type = enum (attrNames formats); + type = deprecatedSingleOrListOf "vim.language.python.format.type" (enum (attrNames formats)); default = defaultFormat; - }; - - package = mkOption { - description = "Python formatter package"; - type = package; - default = formats.${cfg.format.type}.package; + description = "Python formatters to use"; }; }; # TODO this implementation is very bare bones, I don't know enough python to implement everything dap = { enable = mkOption { - description = "Enable Python Debug Adapter"; type = bool; default = config.vim.languages.enableDAP; + description = "Enable Python Debug Adapter"; }; debugger = mkOption { - description = "Python debugger to use"; type = enum (attrNames debuggers); default = defaultDebugger; + description = "Python debugger to use"; }; package = mkOption { @@ -234,26 +285,59 @@ in { }) (mkIf cfg.lsp.enable { - vim.lsp.lspconfig.enable = true; - vim.lsp.lspconfig.sources.python-lsp = servers.${cfg.lsp.server}.lspConfig; + vim.luaConfigRC.python-util = + entryBefore ["lsp-servers"] + /* + lua + */ + '' + local function set_python_path(server_name, command) + local path = command.args + local clients = vim.lsp.get_clients { + bufnr = vim.api.nvim_get_current_buf(), + name = server_name, + } + for _, client in ipairs(clients) do + if client.settings then + client.settings.python = vim.tbl_deep_extend('force', client.settings.python or {}, { pythonPath = path }) + else + client.config.settings = vim.tbl_deep_extend('force', client.config.settings, { python = { pythonPath = path } }) + end + client:notify('workspace/didChangeConfiguration', { settings = nil }) + end + end + ''; + + vim.lsp.servers = + mapListToAttrs (n: { + name = n; + value = servers.${n}; + }) + cfg.lsp.servers; }) (mkIf cfg.format.enable { - vim.formatter.conform-nvim = { + vim.formatter.conform-nvim = let + names = flatten (map (type: + if type == "black-and-isort" + then + warn '' + vim.languages.python.format.type: "black-and-isort" is deprecated, + use `["black" "isort"]` instead. + '' ["black" "isort"] + else type) + cfg.format.type); + in { enable = true; - # HACK: I'm planning to remove these soon so I just took the easiest way out - setupOpts.formatters_by_ft.python = - if cfg.format.type == "black-and-isort" - then ["black"] - else [cfg.format.type]; - setupOpts.formatters = - if (cfg.format.type == "black-and-isort") - then { - black.command = "${cfg.format.package}/bin/black"; - } - else { - ${cfg.format.type}.command = getExe cfg.format.package; - }; + setupOpts = { + formatters_by_ft.python = names; + formatters = + mapListToAttrs (name: { + inherit name; + value = formats.${name}; + }) + names; + }; }; }) diff --git a/modules/plugins/languages/qml.nix b/modules/plugins/languages/qml.nix new file mode 100644 index 00000000..e8f40654 --- /dev/null +++ b/modules/plugins/languages/qml.nix @@ -0,0 +1,95 @@ +{ + config, + pkgs, + lib, + ... +}: let + inherit (builtins) attrNames; + inherit (lib.meta) getExe'; + inherit (lib.options) mkEnableOption mkOption; + inherit (lib.modules) mkIf mkMerge; + inherit (lib.types) enum; + inherit (lib.nvim.types) mkGrammarOption deprecatedSingleOrListOf; + inherit (lib.nvim.attrsets) mapListToAttrs; + + cfg = config.vim.languages.qml; + + qmlPackage = pkgs.kdePackages.qtdeclarative; + + defaultServers = ["qmlls"]; + servers = { + qmlls = { + cmd = [(getExe' qmlPackage "qmlls")]; + filetypes = ["qml" "qmljs"]; + rootmarkers = [".git"]; + }; + }; + + defaultFormat = ["qmlformat"]; + formats = { + qmlformat = { + command = "${qmlPackage}/bin/qmlformat"; + args = ["-i" "$FILENAME"]; + stdin = false; + }; + }; +in { + options.vim.languages.qml = { + enable = mkEnableOption "QML language support"; + treesitter = { + enable = mkEnableOption "QML treesitter support" // {default = config.vim.languages.enableTreesitter;}; + package = mkGrammarOption pkgs "qmljs"; + }; + + lsp = { + enable = mkEnableOption "QML LSP support" // {default = config.vim.lsp.enable;}; + servers = mkOption { + type = deprecatedSingleOrListOf "vim.language.qml.lsp.servers" (enum (attrNames servers)); + default = defaultServers; + description = "QML LSP server to use"; + }; + }; + + format = { + enable = mkEnableOption "QML formatting" // {default = config.vim.languages.enableFormat;}; + + type = mkOption { + type = deprecatedSingleOrListOf "vim.language.qml.format.type" (enum (attrNames formats)); + default = defaultFormat; + description = "QML formatter to use"; + }; + }; + }; + + config = mkIf cfg.enable (mkMerge [ + (mkIf cfg.treesitter.enable { + vim.treesitter = { + enable = true; + grammars = [cfg.treesitter.package]; + }; + }) + (mkIf cfg.lsp.enable { + vim.lsp.servers = + mapListToAttrs (n: { + name = n; + value = servers.${n}; + }) + cfg.lsp.servers; + }) + + (mkIf (cfg.format.enable && !cfg.lsp.enable) { + vim.formatter.conform-nvim = { + enable = true; + setupOpts = { + formatters_by_ft.qml = cfg.format.type; + formatters = + mapListToAttrs (name: { + inherit name; + value = formats.${name}; + }) + cfg.format.type; + }; + }; + }) + ]); +} diff --git a/modules/plugins/languages/r.nix b/modules/plugins/languages/r.nix index 894c63f6..d9f6e9f0 100644 --- a/modules/plugins/languages/r.nix +++ b/modules/plugins/languages/r.nix @@ -5,12 +5,13 @@ ... }: let inherit (builtins) attrNames; - inherit (lib.options) mkEnableOption mkOption literalExpression; + inherit (lib.options) mkEnableOption mkOption; inherit (lib.modules) mkIf mkMerge; - inherit (lib.lists) isList; - inherit (lib.types) enum either listOf package str; - inherit (lib.nvim.lua) expToLua; - inherit (lib.nvim.types) mkGrammarOption; + inherit (lib.types) enum; + inherit (lib.meta) getExe; + inherit (lib.nvim.types) mkGrammarOption deprecatedSingleOrListOf; + inherit (lib.nvim.attrsets) mapListToAttrs; + inherit (lib.generators) mkLuaInline; cfg = config.vim.languages.r; @@ -18,54 +19,44 @@ packages = [pkgs.rPackages.languageserver]; }; - defaultFormat = "format_r"; + defaultFormat = ["format_r"]; formats = { styler = { - package = pkgs.rWrapper.override { - packages = [pkgs.rPackages.styler]; - }; - config = { - command = "${cfg.format.package}/bin/R"; - }; + command = let + pkg = pkgs.rWrapper.override {packages = [pkgs.rPackages.styler];}; + in "${pkg}/bin/R"; }; format_r = { - package = pkgs.rWrapper.override { - packages = [pkgs.rPackages.formatR]; - }; - config = { - command = "${cfg.format.package}/bin/R"; - stdin = true; - args = [ - "--slave" - "--no-restore" - "--no-save" - "-s" - "-e" - ''formatR::tidy_source(source="stdin")'' - ]; - # TODO: range_args seem to be possible - # https://github.com/nvimtools/none-ls.nvim/blob/main/lua/null-ls/builtins/formatting/format_r.lua - }; + command = let + pkg = pkgs.rWrapper.override { + packages = [pkgs.rPackages.formatR]; + }; + in "${pkg}/bin/R"; + stdin = true; + args = [ + "--slave" + "--no-restore" + "--no-save" + "-s" + "-e" + ''formatR::tidy_source(source="stdin")'' + ]; + # TODO: range_args seem to be possible + # https://github.com/nvimtools/none-ls.nvim/blob/main/lua/null-ls/builtins/formatting/format_r.lua }; }; - defaultServer = "r_language_server"; + defaultServers = ["r_language_server"]; servers = { r_language_server = { - package = pkgs.writeShellScriptBin "r_lsp" '' - ${r-with-languageserver}/bin/R --slave -e "languageserver::run()" - ''; - lspConfig = '' - lspconfig.r_language_server.setup{ - capabilities = capabilities; - on_attach = default_on_attach; - cmd = ${ - if isList cfg.lsp.package - then expToLua cfg.lsp.package - else ''{"${lib.getExe cfg.lsp.package}"}'' - } - } + enable = true; + cmd = [(getExe r-with-languageserver) "--no-echo" "-e" "languageserver::run()"]; + filetypes = ["r" "rmd" "quarto"]; + root_dir = mkLuaInline '' + function(bufnr, on_dir) + on_dir(vim.fs.root(bufnr, '.git') or vim.uv.os_homedir()) + end ''; }; }; @@ -81,17 +72,10 @@ in { lsp = { enable = mkEnableOption "R LSP support" // {default = config.vim.lsp.enable;}; - server = mkOption { + servers = mkOption { + type = deprecatedSingleOrListOf "vim.language.r.lsp.servers" (enum (attrNames servers)); + default = defaultServers; description = "R LSP server to use"; - type = enum (attrNames servers); - default = defaultServer; - }; - - package = mkOption { - description = "R LSP server package, or the command to run as a list of strings"; - example = literalExpression "[ (lib.getExe pkgs.jdt-language-server) \"-data\" \"~/.cache/jdtls/workspace\" ]"; - type = either package (listOf str); - default = servers.${cfg.lsp.server}.package; }; }; @@ -99,16 +83,10 @@ in { enable = mkEnableOption "R formatting" // {default = config.vim.languages.enableFormat;}; type = mkOption { - type = enum (attrNames formats); + type = deprecatedSingleOrListOf "vim.language.r.format.type" (enum (attrNames formats)); default = defaultFormat; description = "R formatter to use"; }; - - package = mkOption { - type = package; - default = formats.${cfg.format.type}.package; - description = "R formatter package"; - }; }; }; @@ -121,14 +99,25 @@ in { (mkIf cfg.format.enable { vim.formatter.conform-nvim = { enable = true; - setupOpts.formatters_by_ft.r = [cfg.format.type]; - setupOpts.formatters.${cfg.format.type} = formats.${cfg.format.type}.config; + setupOpts = { + formatters_by_ft.r = cfg.format.type; + formatters = + mapListToAttrs (name: { + inherit name; + value = formats.${name}; + }) + cfg.format.type; + }; }; }) (mkIf cfg.lsp.enable { - vim.lsp.lspconfig.enable = true; - vim.lsp.lspconfig.sources.r-lsp = servers.${cfg.lsp.server}.lspConfig; + vim.lsp.servers = + mapListToAttrs (n: { + name = n; + value = servers.${n}; + }) + cfg.lsp.servers; }) ]); } diff --git a/modules/plugins/languages/ruby.nix b/modules/plugins/languages/ruby.nix index 1f3ce82f..14896f01 100644 --- a/modules/plugins/languages/ruby.nix +++ b/modules/plugins/languages/ruby.nix @@ -8,55 +8,51 @@ inherit (lib.options) mkEnableOption mkOption; inherit (lib.meta) getExe; inherit (lib.modules) mkIf mkMerge; - inherit (lib.nvim.types) mkGrammarOption diagnostics; - inherit (lib.nvim.lua) expToLua; - inherit (lib.lists) isList; - inherit (lib.types) either listOf package str enum; + inherit (lib.nvim.types) mkGrammarOption diagnostics deprecatedSingleOrListOf; + inherit (lib.types) enum; + inherit (lib.nvim.attrsets) mapListToAttrs; cfg = config.vim.languages.ruby; - defaultServer = "rubyserver"; + defaultServers = ["solargraph"]; servers = { - rubyserver = { - package = pkgs.rubyPackages.solargraph; - lspConfig = '' - lspconfig.solargraph.setup { - capabilities = capabilities, - on_attach = attach_keymaps, - flags = { - debounce_text_changes = 150, - }, - cmd = ${ - if isList cfg.lsp.package - then expToLua cfg.lsp.package - else ''{ "${cfg.lsp.package}/bin/solargraph", "stdio" }'' - } - } - ''; + ruby_lsp = { + enable = true; + cmd = [(getExe pkgs.ruby-lsp)]; + filetypes = ["ruby" "eruby"]; + root_markers = ["Gemfile" ".git"]; + init_options = { + formatter = "auto"; + }; }; - rubylsp = { - package = pkgs.ruby-lsp; - lspConfig = '' - lspconfig.ruby_lsp.setup { - capabilities = capabilities, - on_attach = default_on_attach, - cmd = ${ - if isList cfg.lsp.package - then expToLua cfg.lsp.package - else ''{ "${cfg.lsp.package}/bin/ruby-lsp" }'' - } - } - ''; + + solargraph = { + enable = true; + cmd = [(getExe pkgs.rubyPackages.solargraph) "stdio"]; + filetypes = ["ruby"]; + root_markers = ["Gemfile" ".git"]; + settings = { + solargraph = { + diagnostics = true; + }; + }; + + flags = { + debounce_text_changes = 150; + }; + + init_options = { + formatting = true; + }; }; }; # testing - defaultFormat = "rubocop"; + defaultFormat = ["rubocop"]; formats = { rubocop = { - # TODO: is this right? - package = pkgs.rubyPackages.rubocop; + command = getExe pkgs.rubyPackages.rubocop; }; }; @@ -64,7 +60,6 @@ diagnosticsProviders = { rubocop = { package = pkgs.rubyPackages.rubocop; - config.command = getExe cfg.format.package; }; }; in { @@ -79,33 +74,21 @@ in { lsp = { enable = mkEnableOption "Ruby LSP support" // {default = config.vim.lsp.enable;}; - server = mkOption { - type = enum (attrNames servers); - default = defaultServer; + servers = mkOption { + type = deprecatedSingleOrListOf "vim.language.ruby.lsp.servers" (enum (attrNames servers)); + default = defaultServers; description = "Ruby LSP server to use"; }; - - package = mkOption { - type = either package (listOf str); - default = servers.${cfg.lsp.server}.package; - description = "Ruby LSP server package, or the command to run as a list of strings"; - }; }; format = { enable = mkEnableOption "Ruby formatter support" // {default = config.vim.languages.enableFormat;}; type = mkOption { - type = enum (attrNames formats); + type = deprecatedSingleOrListOf "vim.language.ruby.format.type" (enum (attrNames formats)); default = defaultFormat; description = "Ruby formatter to use"; }; - - package = mkOption { - type = package; - default = formats.${cfg.format.type}.package; - description = "Ruby formatter package"; - }; }; extraDiagnostics = { @@ -128,16 +111,25 @@ in { }) (mkIf cfg.lsp.enable { - vim.lsp.lspconfig.enable = true; - vim.lsp.lspconfig.sources.ruby-lsp = servers.${cfg.lsp.server}.lspConfig; + vim.lsp.servers = + mapListToAttrs (n: { + name = n; + value = servers.${n}; + }) + cfg.lsp.servers; }) (mkIf cfg.format.enable { vim.formatter.conform-nvim = { enable = true; - setupOpts.formatters_by_ft.ruby = [cfg.format.type]; - setupOpts.formatters.${cfg.format.type} = { - command = getExe cfg.format.package; + setupOpts = { + formatters_by_ft.ruby = cfg.format.type; + formatters = + mapListToAttrs (name: { + inherit name; + value = formats.${name}; + }) + cfg.format.type; }; }; }) diff --git a/modules/plugins/languages/rust.nix b/modules/plugins/languages/rust.nix index 09c54ae6..fea75305 100644 --- a/modules/plugins/languages/rust.nix +++ b/modules/plugins/languages/rust.nix @@ -4,24 +4,24 @@ lib, ... }: let - inherit (builtins) attrNames; inherit (lib.meta) getExe; inherit (lib.modules) mkIf mkMerge; inherit (lib.options) mkOption mkEnableOption literalMD; inherit (lib.strings) optionalString; - inherit (lib.trivial) boolToString; inherit (lib.lists) isList; - inherit (lib.types) bool package str listOf either enum; - inherit (lib.nvim.types) mkGrammarOption; - inherit (lib.nvim.lua) expToLua; + inherit (lib.attrsets) attrNames; + inherit (lib.types) bool package str listOf either enum int; + inherit (lib.nvim.lua) expToLua toLuaObject; + inherit (lib.nvim.attrsets) mapListToAttrs; + inherit (lib.nvim.types) mkGrammarOption mkPluginSetupOption deprecatedSingleOrListOf; inherit (lib.nvim.dag) entryAfter entryAnywhere; cfg = config.vim.languages.rust; - defaultFormat = "rustfmt"; + defaultFormat = ["rustfmt"]; formats = { rustfmt = { - package = pkgs.rustfmt; + command = getExe pkgs.rustfmt; }; }; in { @@ -33,15 +33,6 @@ in { package = mkGrammarOption pkgs "rust"; }; - crates = { - enable = mkEnableOption "crates-nvim, tools for managing dependencies"; - codeActions = mkOption { - description = "Enable code actions through null-ls"; - type = bool; - default = true; - }; - }; - lsp = { enable = mkEnableOption "Rust LSP support (rust-analyzer with extra tools)" // {default = config.vim.lsp.enable;}; package = mkOption { @@ -79,15 +70,9 @@ in { type = mkOption { description = "Rust formatter to use"; - type = enum (attrNames formats); + type = deprecatedSingleOrListOf "vim.language.rust.format.type" (enum (attrNames formats)); default = defaultFormat; }; - - package = mkOption { - description = "Rust formatter package"; - type = package; - default = formats.${cfg.format.type}.package; - }; }; dap = { @@ -103,25 +88,39 @@ in { default = pkgs.lldb; }; }; + + extensions = { + crates-nvim = { + enable = mkEnableOption "crates.io dependency management [crates-nvim]"; + + setupOpts = mkPluginSetupOption "crates-nvim" { + lsp = { + enabled = mkEnableOption "crates.nvim's in-process language server" // {default = cfg.extensions.crates-nvim.enable;}; + actions = mkEnableOption "actions for crates-nvim's in-process language server" // {default = cfg.extensions.crates-nvim.enable;}; + completion = mkEnableOption "completion for crates-nvim's in-process language server" // {default = cfg.extensions.crates-nvim.enable;}; + hover = mkEnableOption "hover actions for crates-nvim's in-process language server" // {default = cfg.extensions.crates-nvim.enable;}; + }; + completion = { + crates = { + enabled = mkEnableOption "completion for crates-nvim's in-process language server" // {default = cfg.extensions.crates-nvim.enable;}; + max_results = mkOption { + description = "The maximum number of search results to display"; + type = int; + default = 8; + }; + min_chars = mkOption { + description = "The minimum number of characters to type before completions begin appearing"; + type = int; + default = 3; + }; + }; + }; + }; + }; + }; }; config = mkIf cfg.enable (mkMerge [ - (mkIf cfg.crates.enable { - vim = { - startPlugins = ["crates-nvim"]; - lsp.null-ls.enable = mkIf cfg.crates.codeActions true; - autocomplete.nvim-cmp.sources = {crates = "[Crates]";}; - pluginRC.rust-crates = entryAnywhere '' - require('crates').setup { - null_ls = { - enabled = ${boolToString cfg.crates.codeActions}, - name = "crates.nvim", - } - } - ''; - }; - }) - (mkIf cfg.treesitter.enable { vim.treesitter.enable = true; vim.treesitter.grammars = [cfg.treesitter.package]; @@ -130,9 +129,14 @@ in { (mkIf cfg.format.enable { vim.formatter.conform-nvim = { enable = true; - setupOpts.formatters_by_ft.rust = [cfg.format.type]; - setupOpts.formatters.${cfg.format.type} = { - command = getExe cfg.format.package; + setupOpts = { + formatters_by_ft.rust = cfg.format.type; + formatters = + mapListToAttrs (name: { + inherit name; + value = formats.${name}; + }) + cfg.format.type; }; }; }) @@ -140,7 +144,6 @@ in { (mkIf (cfg.lsp.enable || cfg.dap.enable) { vim = { startPlugins = ["rustaceanvim"]; - pluginRC.rustaceanvim = entryAfter ["lsp-setup"] '' vim.g.rustaceanvim = { ${optionalString cfg.lsp.enable '' @@ -199,5 +202,16 @@ in { ''; }; }) + + (mkIf cfg.extensions.crates-nvim.enable { + vim = mkMerge [ + { + startPlugins = ["crates-nvim"]; + pluginRC.rust-crates = entryAnywhere '' + require("crates").setup(${toLuaObject cfg.extensions.crates-nvim.setupOpts}) + ''; + } + ]; + }) ]); } diff --git a/modules/plugins/languages/sql.nix b/modules/plugins/languages/sql.nix index add46c15..e3e686ec 100644 --- a/modules/plugins/languages/sql.nix +++ b/modules/plugins/languages/sql.nix @@ -8,43 +8,36 @@ inherit (lib.options) mkEnableOption mkOption; inherit (lib.meta) getExe; inherit (lib.modules) mkIf mkMerge; - inherit (lib.lists) isList; - inherit (lib.types) enum either listOf package str; - inherit (lib.nvim.lua) expToLua; - inherit (lib.nvim.types) diagnostics; + inherit (lib.types) enum package str; + inherit (lib.nvim.types) diagnostics deprecatedSingleOrListOf; + inherit (lib.nvim.attrsets) mapListToAttrs; + inherit (lib.generators) mkLuaInline; cfg = config.vim.languages.sql; sqlfluffDefault = pkgs.sqlfluff; - defaultServer = "sqls"; + defaultServers = ["sqls"]; servers = { sqls = { - package = pkgs.sqls; - lspConfig = '' - lspconfig.sqls.setup { - on_attach = function(client) - client.server_capabilities.execute_command = true - on_attach_keymaps(client, bufnr) - require'sqls'.setup{} - end, - cmd = ${ - if isList cfg.lsp.package - then expToLua cfg.lsp.package - else ''{ "${cfg.lsp.package}/bin/sqls", "-config", string.format("%s/config.yml", vim.fn.getcwd()) }'' - } - } + enable = true; + cmd = [(getExe pkgs.sqls)]; + filetypes = ["sql" "mysql"]; + root_markers = ["config.yml"]; + settings = {}; + on_attach = mkLuaInline '' + function(client, bufnr) + client.server_capabilities.execute_command = true + require'sqls'.setup{} + end ''; }; }; - defaultFormat = "sqlfluff"; + defaultFormat = ["sqlfluff"]; formats = { sqlfluff = { - package = sqlfluffDefault; - config = { - command = getExe cfg.format.package; - append_args = ["--dialect=${cfg.dialect}"]; - }; + command = getExe sqlfluffDefault; + append_args = ["--dialect=${cfg.dialect}"]; }; }; @@ -63,35 +56,28 @@ in { enable = mkEnableOption "SQL language support"; dialect = mkOption { - description = "SQL dialect for sqlfluff (if used)"; type = str; default = "ansi"; + description = "SQL dialect for sqlfluff (if used)"; }; treesitter = { enable = mkEnableOption "SQL treesitter" // {default = config.vim.languages.enableTreesitter;}; package = mkOption { - description = "SQL treesitter grammar to use"; type = package; default = pkgs.vimPlugins.nvim-treesitter.builtGrammars.sql; + description = "SQL treesitter grammar to use"; }; }; lsp = { enable = mkEnableOption "SQL LSP support" // {default = config.vim.lsp.enable;}; - server = mkOption { + servers = mkOption { + type = deprecatedSingleOrListOf "vim.language.sql.lsp.servers" (enum (attrNames servers)); + default = defaultServers; description = "SQL LSP server to use"; - type = enum (attrNames servers); - default = defaultServer; - }; - - package = mkOption { - description = "SQL LSP server package, or the command to run as a list of strings"; - example = ''[lib.getExe pkgs.jdt-language-server "-data" "~/.cache/jdtls/workspace"]''; - type = either package (listOf str); - default = servers.${cfg.lsp.server}.package; }; }; @@ -99,15 +85,9 @@ in { enable = mkEnableOption "SQL formatting" // {default = config.vim.languages.enableFormat;}; type = mkOption { - description = "SQL formatter to use"; - type = enum (attrNames formats); + type = deprecatedSingleOrListOf "vim.language.sql.format.type" (enum (attrNames formats)); default = defaultFormat; - }; - - package = mkOption { - description = "SQL formatter package"; - type = package; - default = formats.${cfg.format.type}.package; + description = "SQL formatter to use"; }; }; @@ -132,18 +112,27 @@ in { vim = { startPlugins = ["sqls-nvim"]; - lsp.lspconfig = { - enable = true; - sources.sql-lsp = servers.${cfg.lsp.server}.lspConfig; - }; + lsp.servers = + mapListToAttrs (n: { + name = n; + value = servers.${n}; + }) + cfg.lsp.servers; }; }) (mkIf cfg.format.enable { vim.formatter.conform-nvim = { enable = true; - setupOpts.formatters_by_ft.sql = [cfg.format.type]; - setupOpts.formatters.${cfg.format.type} = formats.${cfg.format.type}.config; + setupOpts = { + formatters_by_ft.sql = cfg.format.type; + formatters = + mapListToAttrs (name: { + inherit name; + value = formats.${name}; + }) + cfg.format.type; + }; }; }) diff --git a/modules/plugins/languages/svelte.nix b/modules/plugins/languages/svelte.nix index 4cc9ffe9..bfe793a0 100644 --- a/modules/plugins/languages/svelte.nix +++ b/modules/plugins/languages/svelte.nix @@ -1,4 +1,5 @@ { + self, config, pkgs, lib, @@ -7,41 +8,65 @@ inherit (builtins) attrNames; inherit (lib.options) mkEnableOption mkOption; inherit (lib.modules) mkIf mkMerge; - inherit (lib.lists) isList; inherit (lib.meta) getExe; - inherit (lib.types) enum either listOf package str; - inherit (lib.nvim.lua) expToLua; - inherit (lib.nvim.types) mkGrammarOption diagnostics; + inherit (lib.types) enum package coercedTo; + inherit (lib.nvim.types) mkGrammarOption diagnostics deprecatedSingleOrListOf; + inherit (lib.nvim.attrsets) mapListToAttrs; + inherit (lib.generators) mkLuaInline; cfg = config.vim.languages.svelte; - defaultServer = "svelte"; + defaultServers = ["svelte"]; servers = { svelte = { - package = pkgs.svelte-language-server; - lspConfig = '' - lspconfig.svelte.setup { - capabilities = capabilities; - on_attach = attach_keymaps, - cmd = ${ - if isList cfg.lsp.package - then expToLua cfg.lsp.package - else ''{"${cfg.lsp.package}/bin/svelteserver", "--stdio"}'' - } - } + enable = true; + cmd = [(getExe pkgs.svelte-language-server) "--stdio"]; + filetypes = ["svelte"]; + root_dir = mkLuaInline '' + function(bufnr, on_dir) + local root_files = { 'package.json', '.git' } + local fname = vim.api.nvim_buf_get_name(bufnr) + -- Svelte LSP only supports file:// schema. https://github.com/sveltejs/language-tools/issues/2777 + if vim.uv.fs_stat(fname) ~= nil then + on_dir(vim.fs.dirname(vim.fs.find(root_files, { path = fname, upward = true })[1])) + end + end + ''; + on_attach = mkLuaInline '' + function(client, bufnr) + vim.api.nvim_create_autocmd('BufWritePost', { + pattern = { '*.js', '*.ts' }, + group = vim.api.nvim_create_augroup('svelte_js_ts_file_watch', {}), + callback = function(ctx) + -- internal API to sync changes that have not yet been saved to the file system + client:notify('$/onDidChangeTsOrJsFile', { uri = ctx.match }) + end, + }) + + vim.api.nvim_buf_create_user_command(bufnr, 'LspMigrateToSvelte5', function() + client:exec_cmd({ + command = 'migrate_to_svelte_5', + arguments = { vim.uri_from_bufnr(bufnr) }, + }) + end, { desc = 'Migrate Component to Svelte 5 Syntax' }) + end ''; }; }; - # TODO: specify packages - defaultFormat = "prettier"; - formats = { + defaultFormat = ["prettier"]; + formats = let + prettierPlugin = self.packages.${pkgs.stdenv.system}.prettier-plugin-svelte; + prettierPluginPath = "${prettierPlugin}/lib/node_modules/prettier-plugin-svelte/plugin.js"; + in { prettier = { - package = pkgs.prettier; + command = getExe pkgs.nodePackages.prettier; + options.ft_parsers.svelte = "svelte"; + prepend_args = ["--plugin=${prettierPluginPath}"]; }; biome = { - package = pkgs.biome; + command = getExe pkgs.biome; }; }; @@ -65,6 +90,15 @@ }; }; }; + + formatType = + deprecatedSingleOrListOf + "vim.languages.svelte.format.type" + (coercedTo (enum ["prettierd"]) (_: + lib.warn + "vim.languages.svelte.format.type: prettierd is deprecated, use prettier instead" + "prettier") + (enum (attrNames formats))); in { options.vim.languages.svelte = { enable = mkEnableOption "Svelte language support"; @@ -78,17 +112,10 @@ in { lsp = { enable = mkEnableOption "Svelte LSP support" // {default = config.vim.lsp.enable;}; - server = mkOption { + servers = mkOption { + type = deprecatedSingleOrListOf "vim.language.svelte.lsp.servers" (enum (attrNames servers)); + default = defaultServers; description = "Svelte LSP server to use"; - type = enum (attrNames servers); - default = defaultServer; - }; - - package = mkOption { - description = "Svelte LSP server package, or the command to run as a list of strings"; - example = ''[lib.getExe pkgs.jdt-language-server "-data" "~/.cache/jdtls/workspace"]''; - type = either package (listOf str); - default = servers.${cfg.lsp.server}.package; }; }; @@ -96,15 +123,9 @@ in { enable = mkEnableOption "Svelte formatting" // {default = config.vim.languages.enableFormat;}; type = mkOption { - description = "Svelte formatter to use"; - type = enum (attrNames formats); + type = formatType; default = defaultFormat; - }; - - package = mkOption { - description = "Svelte formatter package"; - type = package; - default = formats.${cfg.format.type}.package; + description = "Svelte formatter to use"; }; }; @@ -126,16 +147,25 @@ in { }) (mkIf cfg.lsp.enable { - vim.lsp.lspconfig.enable = true; - vim.lsp.lspconfig.sources.svelte-lsp = servers.${cfg.lsp.server}.lspConfig; + vim.lsp.servers = + mapListToAttrs (n: { + name = n; + value = servers.${n}; + }) + cfg.lsp.servers; }) (mkIf cfg.format.enable { vim.formatter.conform-nvim = { enable = true; - setupOpts.formatters_by_ft.svelte = [cfg.format.type]; - setupOpts.formatters.${cfg.format.type} = { - command = getExe cfg.format.package; + setupOpts = { + formatters_by_ft.svelte = cfg.format.type; + formatters = + mapListToAttrs (name: { + inherit name; + value = formats.${name}; + }) + cfg.format.type; }; }; }) diff --git a/modules/plugins/languages/tailwind.nix b/modules/plugins/languages/tailwind.nix index fbe707ba..35449237 100644 --- a/modules/plugins/languages/tailwind.nix +++ b/modules/plugins/languages/tailwind.nix @@ -7,26 +7,142 @@ inherit (builtins) attrNames; inherit (lib.options) mkEnableOption mkOption; inherit (lib.modules) mkIf mkMerge; - inherit (lib.lists) isList; - inherit (lib.types) enum either listOf package str; - inherit (lib.nvim.lua) expToLua; + inherit (lib.meta) getExe; + inherit (lib.types) enum; + inherit (lib.nvim.attrsets) mapListToAttrs; + inherit (lib.nvim.types) deprecatedSingleOrListOf; + inherit (lib.generators) mkLuaInline; cfg = config.vim.languages.tailwind; - defaultServer = "tailwindcss-language-server"; + defaultServers = ["tailwindcss"]; servers = { - tailwindcss-language-server = { - package = pkgs.tailwindcss-language-server; - lspConfig = '' - lspconfig.tailwindcss.setup { - capabilities = capabilities; - on_attach = default_on_attach; - cmd = ${ - if isList cfg.lsp.package - then expToLua cfg.lsp.package - else ''{"${cfg.lsp.package}/bin/tailwindcss-language-server", "--stdio"}'' - } - } + tailwindcss = { + enable = true; + cmd = [(getExe pkgs.tailwindcss-language-server) "--stdio"]; + filetypes = [ + # html + "aspnetcorerazor" + "astro" + "astro-markdown" + "blade" + "clojure" + "django-html" + "htmldjango" + "edge" + "eelixir" + "elixir" + "ejs" + "erb" + "eruby" + "gohtml" + "gohtmltmpl" + "haml" + "handlebars" + "hbs" + "html" + "htmlangular" + "html-eex" + "heex" + "jade" + "leaf" + "liquid" + "markdown" + "mdx" + "mustache" + "njk" + "nunjucks" + "php" + "razor" + "slim" + "twig" + # css + "css" + "less" + "postcss" + "sass" + "scss" + "stylus" + "sugarss" + # js + "javascript" + "javascriptreact" + "reason" + "rescript" + "typescript" + "typescriptreact" + # mixed + "vue" + "svelte" + "templ" + ]; + settings = { + tailwindCSS = { + validate = true; + lint = { + cssConflict = "warning"; + invalidApply = "error"; + invalidScreen = "error"; + invalidVariant = "error"; + invalidConfigPath = "error"; + invalidTailwindDirective = "error"; + recommendedVariantOrder = "warning"; + }; + classAttributes = [ + "class" + "className" + "class:list" + "classList" + "ngClass" + ]; + includeLanguages = { + eelixir = "html-eex"; + elixir = "phoenix-heex"; + eruby = "erb"; + heex = "phoenix-heex"; + htmlangular = "html"; + templ = "html"; + }; + }; + }; + before_init = mkLuaInline '' + function(_, config) + if not config.settings then + config.settings = {} + end + if not config.settings.editor then + config.settings.editor = {} + end + if not config.settings.editor.tabSize then + config.settings.editor.tabSize = vim.lsp.util.get_effective_tabstop() + end + end + ''; + workspace_required = true; + root_dir = mkLuaInline '' + function(bufnr, on_dir) + local root_files = { + -- Generic + 'tailwind.config.js', + 'tailwind.config.cjs', + 'tailwind.config.mjs', + 'tailwind.config.ts', + 'postcss.config.js', + 'postcss.config.cjs', + 'postcss.config.mjs', + 'postcss.config.ts', + -- Django + 'theme/static_src/tailwind.config.js', + 'theme/static_src/tailwind.config.cjs', + 'theme/static_src/tailwind.config.mjs', + 'theme/static_src/tailwind.config.ts', + 'theme/static_src/postcss.config.js', + } + local fname = vim.api.nvim_buf_get_name(bufnr) + root_files = util.insert_package_json(root_files, 'tailwindcss', fname) + root_files = util.root_markers_with_field(root_files, { 'mix.lock', 'Gemfile.lock' }, 'tailwind', fname) + on_dir(vim.fs.dirname(vim.fs.find(root_files, { path = fname, upward = true })[1])) + end ''; }; }; @@ -37,25 +153,22 @@ in { lsp = { enable = mkEnableOption "Tailwindcss LSP support" // {default = config.vim.lsp.enable;}; - server = mkOption { + servers = mkOption { + type = deprecatedSingleOrListOf "vim.language.tailwind.lsp.servers" (enum (attrNames servers)); + default = defaultServers; description = "Tailwindcss LSP server to use"; - type = enum (attrNames servers); - default = defaultServer; - }; - - package = mkOption { - description = "Tailwindcss LSP server package, or the command to run as a list of strings"; - example = ''[lib.getExe pkgs.jdt-language-server " - data " " ~/.cache/jdtls/workspace "]''; - type = either package (listOf str); - default = servers.${cfg.lsp.server}.package; }; }; }; config = mkIf cfg.enable (mkMerge [ (mkIf cfg.lsp.enable { - vim.lsp.lspconfig.enable = true; - vim.lsp.lspconfig.sources.tailwindcss-lsp = servers.${cfg.lsp.server}.lspConfig; + vim.lsp.servers = + mapListToAttrs (n: { + name = n; + value = servers.${n}; + }) + cfg.lsp.servers; }) ]); } diff --git a/modules/plugins/languages/terraform.nix b/modules/plugins/languages/terraform.nix index 095da072..5f3952df 100644 --- a/modules/plugins/languages/terraform.nix +++ b/modules/plugins/languages/terraform.nix @@ -4,12 +4,25 @@ lib, ... }: let + inherit (builtins) attrNames; inherit (lib.options) mkEnableOption mkOption; inherit (lib.modules) mkIf mkMerge; - inherit (lib.types) package; + inherit (lib.meta) getExe; + inherit (lib.types) enum listOf; inherit (lib.nvim.types) mkGrammarOption; + inherit (lib.nvim.attrsets) mapListToAttrs; cfg = config.vim.languages.terraform; + + defaultServers = ["terraformls"]; + servers = { + terraformls = { + enable = true; + cmd = [(getExe pkgs.terraform-ls) "serve"]; + filetypes = ["terraform" "terraform-vars"]; + root_markers = [".terraform" ".git"]; + }; + }; in { options.vim.languages.terraform = { enable = mkEnableOption "Terraform/HCL support"; @@ -22,10 +35,10 @@ in { lsp = { enable = mkEnableOption "Terraform LSP support (terraform-ls)" // {default = config.vim.lsp.enable;}; - package = mkOption { - description = "terraform-ls package"; - type = package; - default = pkgs.terraform-ls; + servers = mkOption { + type = listOf (enum (attrNames servers)); + default = defaultServers; + description = "Terraform LSP server to use"; }; }; }; @@ -36,14 +49,12 @@ in { }) (mkIf cfg.lsp.enable { - vim.lsp.lspconfig.enable = true; - vim.lsp.lspconfig.sources.terraform-ls = '' - lspconfig.terraformls.setup { - capabilities = capabilities, - on_attach=default_on_attach, - cmd = {"${cfg.lsp.package}/bin/terraform-ls", "serve"}, - } - ''; + vim.lsp.servers = + mapListToAttrs (n: { + name = n; + value = servers.${n}; + }) + cfg.lsp.servers; }) ]); } diff --git a/modules/plugins/languages/ts.nix b/modules/plugins/languages/ts.nix index 6064da98..e5dc8d13 100644 --- a/modules/plugins/languages/ts.nix +++ b/modules/plugins/languages/ts.nix @@ -4,86 +4,187 @@ lib, ... }: let - inherit (builtins) attrNames; + inherit (builtins) attrNames elem; inherit (lib.options) mkEnableOption mkOption; inherit (lib.modules) mkIf mkMerge; - inherit (lib.lists) isList; inherit (lib.meta) getExe; - inherit (lib.types) enum either listOf package str bool; - inherit (lib.nvim.lua) expToLua toLuaObject; - inherit (lib.nvim.types) mkGrammarOption diagnostics mkPluginSetupOption; - inherit (lib.nvim.dag) entryAnywhere; + inherit (lib.types) enum package bool; + inherit (lib.generators) mkLuaInline; + inherit (lib.nvim.attrsets) mapListToAttrs; + inherit (lib.nvim.lua) toLuaObject; + inherit (lib.nvim.types) mkGrammarOption diagnostics mkPluginSetupOption deprecatedSingleOrListOf; + inherit (lib.nvim.dag) entryAnywhere entryBefore; cfg = config.vim.languages.ts; - defaultServer = "ts_ls"; - servers = { + defaultServers = ["ts_ls"]; + servers = let ts_ls = { - package = pkgs.typescript-language-server; - lspConfig = '' - lspconfig.ts_ls.setup { - capabilities = capabilities, - on_attach = function(client, bufnr) - attach_keymaps(client, bufnr); - client.server_capabilities.documentFormattingProvider = false; - end, - cmd = ${ - if isList cfg.lsp.package - then expToLua cfg.lsp.package - else ''{"${cfg.lsp.package}/bin/typescript-language-server", "--stdio"}'' - } - } + cmd = [(getExe pkgs.typescript-language-server) "--stdio"]; + init_options = {hostInfo = "neovim";}; + filetypes = [ + "javascript" + "javascriptreact" + "javascript.jsx" + "typescript" + "typescriptreact" + "typescript.tsx" + ]; + root_markers = ["tsconfig.json" "jsconfig.json" "package.json" ".git"]; + handlers = { + # handle rename request for certain code actions like extracting functions / types + "_typescript.rename" = mkLuaInline '' + function(_, result, ctx) + local client = assert(vim.lsp.get_client_by_id(ctx.client_id)) + vim.lsp.util.show_document({ + uri = result.textDocument.uri, + range = { + start = result.position, + ['end'] = result.position, + }, + }, client.offset_encoding) + vim.lsp.buf.rename() + return vim.NIL + end + ''; + }; + on_attach = mkLuaInline '' + function(client, bufnr) + -- ts_ls provides `source.*` code actions that apply to the whole file. These only appear in + -- `vim.lsp.buf.code_action()` if specified in `context.only`. + vim.api.nvim_buf_create_user_command(0, 'LspTypescriptSourceAction', function() + local source_actions = vim.tbl_filter(function(action) + return vim.startswith(action, 'source.') + end, client.server_capabilities.codeActionProvider.codeActionKinds) + + vim.lsp.buf.code_action({ + context = { + only = source_actions, + }, + }) + end, {}) + end ''; }; - - denols = { - package = pkgs.deno; - lspConfig = '' - vim.g.markdown_fenced_languages = { "ts=typescript" } - lspconfig.denols.setup { - capabilities = capabilities; - on_attach = attach_keymaps, - cmd = ${ - if isList cfg.lsp.package - then expToLua cfg.lsp.package - else ''{"${cfg.lsp.package}/bin/deno", "lsp"}'' - } - } - ''; - }; - + in { + inherit ts_ls; # Here for backwards compatibility. Still consider tsserver a valid # configuration in the enum, but assert if it's set to *properly* # redirect the user to the correct server. - tsserver = { - package = pkgs.typescript-language-server; - lspConfig = '' - lspconfig.ts_ls.setup { - capabilities = capabilities; - on_attach = attach_keymaps, - cmd = ${ - if isList cfg.lsp.package - then expToLua cfg.lsp.package - else ''{"${cfg.lsp.package}/bin/typescript-language-server", "--stdio"}'' - } - } + tsserver = ts_ls; + + denols = { + cmd = [(getExe pkgs.deno) "lsp"]; + cmd_env = {NO_COLOR = true;}; + filetypes = [ + "javascript" + "javascriptreact" + "javascript.jsx" + "typescript" + "typescriptreact" + "typescript.tsx" + ]; + root_markers = ["deno.json" "deno.jsonc" ".git"]; + settings = { + deno = { + enable = true; + suggest = { + imports = { + hosts = { + "https://deno.land" = true; + }; + }; + }; + }; + }; + handlers = { + "textDocument/definition" = mkLuaInline "nvf_denols_handler"; + "textDocument/typeDefinition" = mkLuaInline "nvf_denols_handler"; + "textDocument/references" = mkLuaInline "nvf_denols_handler"; + }; + on_attach = mkLuaInline '' + function(client, bufnr) + vim.api.nvim_buf_create_user_command(0, 'LspDenolsCache', function() + client:exec_cmd({ + command = 'deno.cache', + arguments = { {}, vim.uri_from_bufnr(bufnr) }, + }, { bufnr = bufnr }, function(err, _result, ctx) + if err then + local uri = ctx.params.arguments[2] + vim.api.nvim_err_writeln('cache command failed for ' .. vim.uri_to_fname(uri)) + end + end) + end, { + desc = 'Cache a module and all of its dependencies.', + }) + end ''; }; }; + denols_handlers = '' + local function nvf_denols_virtual_text_document_handler(uri, res, client) + if not res then + return nil + end + + local lines = vim.split(res.result, '\n') + local bufnr = vim.uri_to_bufnr(uri) + + local current_buf = vim.api.nvim_buf_get_lines(bufnr, 0, -1, false) + if #current_buf ~= 0 then + return nil + end + + vim.api.nvim_buf_set_lines(bufnr, 0, -1, false, lines) + vim.api.nvim_set_option_value('readonly', true, { buf = bufnr }) + vim.api.nvim_set_option_value('modified', false, { buf = bufnr }) + vim.api.nvim_set_option_value('modifiable', false, { buf = bufnr }) + vim.lsp.buf_attach_client(bufnr, client.id) + end + + local function nvf_denols_virtual_text_document(uri, client) + local params = { + textDocument = { + uri = uri, + }, + } + local result = client.request_sync('deno/virtualTextDocument', params) + nvf_denols_virtual_text_document_handler(uri, result, client) + end + + local function nvf_denols_handler(err, result, ctx, config) + if not result or vim.tbl_isempty(result) then + return nil + end + + local client = vim.lsp.get_client_by_id(ctx.client_id) + for _, res in pairs(result) do + local uri = res.uri or res.targetUri + if uri:match '^deno:' then + nvf_denols_virtual_text_document(uri, client) + res['uri'] = uri + res['targetUri'] = uri + end + end + + vim.lsp.handlers[ctx.method](err, result, ctx, config) + end + ''; + # TODO: specify packages - defaultFormat = "prettier"; + defaultFormat = ["prettier"]; formats = { prettier = { - package = pkgs.prettier; + command = getExe pkgs.prettier; }; prettierd = { - package = pkgs.prettierd; + command = getExe pkgs.prettierd; }; biome = { - package = pkgs.biome; + command = getExe pkgs.biome; }; }; @@ -115,24 +216,18 @@ in { treesitter = { enable = mkEnableOption "Typescript/Javascript treesitter" // {default = config.vim.languages.enableTreesitter;}; - tsPackage = mkGrammarOption pkgs "tsx"; + tsPackage = mkGrammarOption pkgs "typescript"; + tsxPackage = mkGrammarOption pkgs "tsx"; jsPackage = mkGrammarOption pkgs "javascript"; }; lsp = { enable = mkEnableOption "Typescript/Javascript LSP support" // {default = config.vim.lsp.enable;}; - server = mkOption { + servers = mkOption { + type = deprecatedSingleOrListOf "vim.language.ts.lsp.servers" (enum (attrNames servers)); + default = defaultServers; description = "Typescript/Javascript LSP server to use"; - type = enum (attrNames servers); - default = defaultServer; - }; - - package = mkOption { - description = "Typescript/Javascript LSP server package, or the command to run as a list of strings"; - example = ''[lib.getExe pkgs.jdt-language-server "-data" "~/.cache/jdtls/workspace"]''; - type = either package (listOf str); - default = servers.${cfg.lsp.server}.package; }; }; @@ -141,15 +236,9 @@ in { type = mkOption { description = "Typescript/Javascript formatter to use"; - type = enum (attrNames formats); + type = deprecatedSingleOrListOf "vim.language.ts.format.type" (enum (attrNames formats)); default = defaultFormat; }; - - package = mkOption { - description = "Typescript/Javascript formatter package"; - type = package; - default = formats.${cfg.format.type}.package; - }; }; extraDiagnostics = { @@ -186,24 +275,43 @@ in { config = mkIf cfg.enable (mkMerge [ (mkIf cfg.treesitter.enable { vim.treesitter.enable = true; - vim.treesitter.grammars = [cfg.treesitter.tsPackage cfg.treesitter.jsPackage]; + vim.treesitter.grammars = [ + cfg.treesitter.tsPackage + cfg.treesitter.tsxPackage + cfg.treesitter.jsPackage + ]; }) (mkIf cfg.lsp.enable { - vim.lsp.lspconfig.enable = true; - vim.lsp.lspconfig.sources.ts-lsp = servers.${cfg.lsp.server}.lspConfig; + vim.lsp.servers = + mapListToAttrs (name: { + inherit name; + value = servers.${name}; + }) + cfg.lsp.servers; + }) + + (mkIf (cfg.lsp.enable && elem "denols" cfg.lsp.servers) { + vim.globals.markdown_fenced_languages = ["ts=typescript"]; + vim.luaConfigRC.denols_handlers = entryBefore ["lsp-servers"] denols_handlers; }) (mkIf cfg.format.enable { vim.formatter.conform-nvim = { enable = true; setupOpts = { - formatters_by_ft.typescript = [cfg.format.type]; - # .tsx files - formatters_by_ft.typescriptreact = [cfg.format.type]; - formatters.${cfg.format.type} = { - command = getExe cfg.format.package; + formatters_by_ft = { + typescript = cfg.format.type; + javascript = cfg.format.type; + # .tsx/.jsx files + typescriptreact = cfg.format.type; }; + setupOpts.formatters = + mapListToAttrs (name: { + inherit name; + value = formats.${name}; + }) + cfg.format.type; }; }; }) @@ -234,7 +342,7 @@ in { { assertions = [ { - assertion = cfg.lsp.enable -> cfg.lsp.server != "tsserver"; + assertion = cfg.lsp.enable -> !(elem "tsserver" cfg.lsp.servers); message = '' As of a recent lspconfig update, the `tsserver` configuration has been renamed to `ts_ls` to match upstream behaviour of `lspconfig`, and the name `tsserver` diff --git a/modules/plugins/languages/typst.nix b/modules/plugins/languages/typst.nix index 77f0e504..10783b61 100644 --- a/modules/plugins/languages/typst.nix +++ b/modules/plugins/languages/typst.nix @@ -4,64 +4,99 @@ lib, ... }: let - inherit (lib.options) mkEnableOption mkOption; + inherit (lib.options) mkOption mkEnableOption; inherit (lib.modules) mkIf mkMerge; - inherit (lib.lists) isList; - inherit (lib.types) nullOr enum either attrsOf listOf package str bool int; + inherit (lib.types) nullOr enum attrsOf listOf package str bool int; inherit (lib.attrsets) attrNames; inherit (lib.meta) getExe; - inherit (lib.nvim.binds) mkMappingOption mkKeymap; - inherit (lib.nvim.lua) expToLua toLuaObject; - inherit (lib.nvim.types) mkGrammarOption mkPluginSetupOption; + inherit (lib.nvim.types) mkGrammarOption mkPluginSetupOption deprecatedSingleOrListOf; inherit (lib.nvim.dag) entryAnywhere; + inherit (lib.nvim.lua) toLuaObject; + inherit (lib.nvim.attrsets) mapListToAttrs; + inherit (lib.nvim.binds) mkKeymap mkMappingOption; + inherit (lib.generators) mkLuaInline; cfg = config.vim.languages.typst; - defaultServer = "tinymist"; + defaultServers = ["tinymist"]; servers = { - typst-lsp = { - package = pkgs.typst-lsp; - lspConfig = '' - lspconfig.typst_lsp.setup { - capabilities = capabilities, - on_attach = function(client, bufnr) - -- Disable semantic tokens as a workaround for a semantic token error when using non-english characters - client.server_capabilities.semanticTokensProvider = nil - end, - cmd = ${ - if isList cfg.lsp.package - then expToLua cfg.lsp.package - else ''{"${cfg.lsp.package}/bin/typst-lsp"}'' - }, - } + typst_lsp = { + enable = true; + cmd = [(getExe pkgs.typst-lsp)]; + filetypes = ["typst"]; + root_markers = [".git"]; + on_attach = mkLuaInline '' + function(client, bufnr) + -- Disable semantic tokens as a workaround for a semantic token error when using non-english characters + client.server_capabilities.semanticTokensProvider = nil + end ''; }; tinymist = { - package = pkgs.tinymist; - lspConfig = '' - lspconfig.tinymist.setup { - capabilities = capabilities, - single_file_support = true, - on_attach = function(client, bufnr) - -- Disable semantic tokens as a workaround for a semantic token error when using non-english characters - client.server_capabilities.semanticTokensProvider = nil - end, - cmd = ${ - if isList cfg.lsp.package - then expToLua cfg.lsp.package - else ''{"${cfg.lsp.package}/bin/tinymist"}'' - }, - } + enable = true; + cmd = [(getExe pkgs.tinymist)]; + filetypes = ["typst"]; + root_markers = [".git"]; + on_attach = mkLuaInline '' + function(client, bufnr) + local function create_tinymist_command(command_name, client, bufnr) + local export_type = command_name:match 'tinymist%.export(%w+)' + local info_type = command_name:match 'tinymist%.(%w+)' + if info_type and info_type:match '^get' then + info_type = info_type:gsub('^get', 'Get') + end + local cmd_display = export_type or info_type + local function run_tinymist_command() + local arguments = { vim.api.nvim_buf_get_name(bufnr) } + local title_str = export_type and ('Export ' .. cmd_display) or cmd_display + local function handler(err, res) + if err then + return vim.notify(err.code .. ': ' .. err.message, vim.log.levels.ERROR) + end + vim.notify(export_type and res or vim.inspect(res), vim.log.levels.INFO) + end + if vim.fn.has 'nvim-0.11' == 1 then + return client:exec_cmd({ + title = title_str, + command = command_name, + arguments = arguments, + }, { bufnr = bufnr }, handler) + else + return vim.notify('Tinymist commands require Neovim 0.11+', vim.log.levels.WARN) + end + end + local cmd_name = export_type and ('LspTinymistExport' .. cmd_display) or ('LspTinymist' .. cmd_display) + local cmd_desc = export_type and ('Export to ' .. cmd_display) or ('Get ' .. cmd_display) + return run_tinymist_command, cmd_name, cmd_desc + end + + for _, command in ipairs { + 'tinymist.exportSvg', + 'tinymist.exportPng', + 'tinymist.exportPdf', + 'tinymist.exportMarkdown', + 'tinymist.exportText', + 'tinymist.exportQuery', + 'tinymist.exportAnsiHighlight', + 'tinymist.getServerInfo', + 'tinymist.getDocumentTrace', + 'tinymist.getWorkspaceLabels', + 'tinymist.getDocumentMetrics', + } do + local cmd_func, cmd_name, cmd_desc = create_tinymist_command(command, client, bufnr) + vim.api.nvim_buf_create_user_command(bufnr, cmd_name, cmd_func, { nargs = 0, desc = cmd_desc }) + end + end ''; }; }; - defaultFormat = "typstyle"; + defaultFormat = ["typstyle"]; formats = { # https://github.com/Enter-tainer/typstyle typstyle = { - package = pkgs.typstyle; + command = getExe pkgs.typstyle; }; }; in { @@ -76,17 +111,10 @@ in { lsp = { enable = mkEnableOption "Typst LSP support (typst-lsp)" // {default = config.vim.lsp.enable;}; - server = mkOption { + servers = mkOption { + type = deprecatedSingleOrListOf "vim.language.typst.lsp.servers" (enum (attrNames servers)); + default = defaultServers; description = "Typst LSP server to use"; - type = enum (attrNames servers); - default = defaultServer; - }; - - package = mkOption { - description = "typst-lsp package, or the command to run as a list of strings"; - example = ''[lib.getExe pkgs.jdt-language-server "-data" "~/.cache/jdtls/workspace"]''; - type = either package (listOf str); - default = servers.${cfg.lsp.server}.package; }; }; @@ -94,15 +122,9 @@ in { enable = mkEnableOption "Typst document formatting" // {default = config.vim.languages.enableFormat;}; type = mkOption { - description = "Typst formatter to use"; - type = enum (attrNames formats); + type = deprecatedSingleOrListOf "vim.language.typst.format.type" (enum (attrNames formats)); default = defaultFormat; - }; - - package = mkOption { - description = "Typst formatter package"; - type = package; - default = formats.${cfg.format.type}.package; + description = "Typst formatter to use"; }; }; @@ -129,7 +151,7 @@ in { dependencies_bin = mkOption { type = attrsOf str; default = { - "tinymist" = getExe servers.tinymist.package; + "tinymist" = getExe pkgs.tinymist; "websocat" = getExe pkgs.websocat; }; @@ -210,16 +232,25 @@ in { (mkIf cfg.format.enable { vim.formatter.conform-nvim = { enable = true; - setupOpts.formatters_by_ft.typst = [cfg.format.type]; - setupOpts.formatters.${cfg.format.type} = { - command = getExe cfg.format.package; + setupOpts = { + formatters_by_ft.typst = cfg.format.type; + formatters = + mapListToAttrs (name: { + inherit name; + value = formats.${name}; + }) + cfg.format.type; }; }; }) (mkIf cfg.lsp.enable { - vim.lsp.lspconfig.enable = true; - vim.lsp.lspconfig.sources.typst-lsp = servers.${cfg.lsp.server}.lspConfig; + vim.lsp.servers = + mapListToAttrs (n: { + name = n; + value = servers.${n}; + }) + cfg.lsp.servers; }) # Extensions diff --git a/modules/plugins/languages/vala.nix b/modules/plugins/languages/vala.nix index 220926d0..af58758d 100644 --- a/modules/plugins/languages/vala.nix +++ b/modules/plugins/languages/vala.nix @@ -7,36 +7,58 @@ inherit (builtins) attrNames; inherit (lib.options) mkEnableOption mkOption; inherit (lib.modules) mkIf mkMerge; - inherit (lib.lists) isList; - inherit (lib.types) enum either listOf package str; - inherit (lib.nvim.types) mkGrammarOption; - inherit (lib.nvim.lua) expToLua; + inherit (lib.meta) getExe; + inherit (lib.types) enum; + inherit (lib.nvim.types) mkGrammarOption deprecatedSingleOrListOf; + inherit (lib.nvim.attrsets) mapListToAttrs; + inherit (lib.generators) mkLuaInline; cfg = config.vim.languages.vala; - defaultServer = "vala_ls"; + defaultServers = ["vala_ls"]; servers = { vala_ls = { - package = pkgs.symlinkJoin { - name = "vala-language-server-wrapper"; - paths = [pkgs.vala-language-server]; - buildInputs = [pkgs.makeBinaryWrapper]; - postBuild = '' - wrapProgram $out/bin/vala-language-server \ - --prefix PATH : ${pkgs.uncrustify}/bin - ''; - }; - internalFormatter = true; - lspConfig = '' - lspconfig.vala_ls.setup { - capabilities = capabilities; - on_attach = default_on_attach; - cmd = ${ - if isList cfg.lsp.package - then expToLua cfg.lsp.package - else ''{"${cfg.lsp.package}/bin/vala-language-server"}'' - }, - } + enable = true; + cmd = [ + (getExe (pkgs.symlinkJoin { + name = "vala-language-server-wrapper"; + paths = [pkgs.vala-language-server]; + meta.mainProgram = "vala-language-server-wrapper"; + buildInputs = [pkgs.makeBinaryWrapper]; + postBuild = '' + wrapProgram $out/bin/vala-language-server \ + --prefix PATH : ${pkgs.uncrustify}/bin + ''; + })) + ]; + filetypes = ["vala" "genie"]; + root_dir = mkLuaInline '' + function(bufnr, on_dir) + local meson_matcher = function(path) + local pattern = 'meson.build' + local f = vim.fn.glob(table.concat({ path, pattern }, '/')) + if f == ''' then + return nil + end + for line in io.lines(f) do + -- skip meson comments + if not line:match '^%s*#.*' then + local str = line:gsub('%s+', ''') + if str ~= ''' then + if str:match '^project%(' then + return path + else + break + end + end + end + end + end + + local fname = vim.api.nvim_buf_get_name(bufnr) + local root = vim.iter(vim.fs.parents(fname)):find(meson_matcher) + on_dir(root or vim.fs.dirname(vim.fs.find('.git', { path = fname, upward = true })[1])) + end ''; }; }; @@ -51,16 +73,10 @@ in { lsp = { enable = mkEnableOption "Vala LSP support" // {default = config.vim.lsp.enable;}; - server = mkOption { + servers = mkOption { + type = deprecatedSingleOrListOf "vim.language.vala.lsp.servers" (enum (attrNames servers)); + default = defaultServers; description = "Vala LSP server to use"; - type = enum (attrNames servers); - default = defaultServer; - }; - - package = mkOption { - description = "Vala LSP server package, or the command to run as a list of strings"; - type = either package (listOf str); - default = servers.${cfg.lsp.server}.package; }; }; }; @@ -72,8 +88,12 @@ in { }) (mkIf cfg.lsp.enable { - vim.lsp.lspconfig.enable = true; - vim.lsp.lspconfig.sources.vala_ls = servers.${cfg.lsp.server}.lspConfig; + vim.lsp.servers = + mapListToAttrs (n: { + name = n; + value = servers.${n}; + }) + cfg.lsp.servers; }) ]); } diff --git a/modules/plugins/languages/wgsl.nix b/modules/plugins/languages/wgsl.nix index 44323092..f38cb124 100644 --- a/modules/plugins/languages/wgsl.nix +++ b/modules/plugins/languages/wgsl.nix @@ -5,31 +5,23 @@ ... }: let inherit (builtins) attrNames; - inherit (lib.lists) isList; inherit (lib.modules) mkIf mkMerge; - inherit (lib.nvim.lua) expToLua; - inherit (lib.nvim.types) mkGrammarOption; - inherit (lib.options) literalExpression mkEnableOption mkOption; - inherit (lib.types) either enum listOf package str; + inherit (lib.nvim.types) mkGrammarOption deprecatedSingleOrListOf; + inherit (lib.options) mkEnableOption mkOption; + inherit (lib.types) enum; + inherit (lib.meta) getExe; + inherit (lib.nvim.attrsets) mapListToAttrs; cfg = config.vim.languages.wgsl; - defaultServer = "wgsl-analyzer"; + defaultServers = ["wgsl-analyzer"]; servers = { wgsl-analyzer = { - package = pkgs.wgsl-analyzer; - internalFormatter = true; - lspConfig = '' - lspconfig.wgsl_analyzer.setup { - capabilities = capabilities, - on_attach = default_on_attach, - cmd = ${ - if isList cfg.lsp.package - then expToLua cfg.lsp.package - else "{'${cfg.lsp.package}/bin/wgsl-analyzer'}" - } - } - ''; + enable = true; + cmd = [(getExe pkgs.wgsl-analyzer)]; + filetypes = ["wgsl"]; + root_markers = [".git"]; + settings = {}; }; }; in { @@ -44,18 +36,11 @@ in { lsp = { enable = mkEnableOption "WGSL LSP support" // {default = config.vim.lsp.enable;}; - server = mkOption { - type = enum (attrNames servers); - default = defaultServer; + servers = mkOption { + type = deprecatedSingleOrListOf "vim.language.wgsl.lsp.servers" (enum (attrNames servers)); + default = defaultServers; description = "WGSL LSP server to use"; }; - - package = mkOption { - description = "wgsl-analyzer package, or the command to run as a list of strings"; - example = literalExpression "[(lib.getExe pkgs.wgsl-analyzer)]"; - type = either package (listOf str); - default = pkgs.wgsl-analyzer; - }; }; }; @@ -68,12 +53,12 @@ in { }) (mkIf cfg.lsp.enable { - vim = { - lsp.lspconfig = { - enable = true; - sources.wgsl_analyzer = servers.${cfg.lsp.server}.lspConfig; - }; - }; + vim.lsp.servers = + mapListToAttrs (n: { + name = n; + value = servers.${n}; + }) + cfg.lsp.servers; }) ]); } diff --git a/modules/plugins/languages/yaml.nix b/modules/plugins/languages/yaml.nix index a75a921f..2e57d8be 100644 --- a/modules/plugins/languages/yaml.nix +++ b/modules/plugins/languages/yaml.nix @@ -5,43 +5,45 @@ ... }: let inherit (builtins) attrNames; + inherit (lib.generators) mkLuaInline; inherit (lib.options) mkEnableOption mkOption; inherit (lib.modules) mkIf mkMerge; - inherit (lib.lists) isList; - inherit (lib.types) enum either listOf package str; - inherit (lib.nvim.types) mkGrammarOption; - inherit (lib.nvim.lua) expToLua; + inherit (lib.meta) getExe; + inherit (lib.types) enum; + inherit (lib.nvim.types) mkGrammarOption deprecatedSingleOrListOf; + inherit (lib.nvim.attrsets) mapListToAttrs; cfg = config.vim.languages.yaml; - onAttach = - if config.vim.languages.helm.lsp.enable - then '' - on_attach = function(client, bufnr) - local filetype = vim.bo[bufnr].filetype - if filetype == "helm" then - client.stop() + on_attach = + if config.vim.languages.helm.lsp.enable && config.vim.languages.helm.enable + then + mkLuaInline '' + function(client, bufnr) + local filetype = vim.bo[bufnr].filetype + if filetype == "helm" then + client.stop() + end end - end'' - else "on_attach = default_on_attach"; + '' + else null; - defaultServer = "yaml-language-server"; + defaultServers = ["yaml-language-server"]; servers = { yaml-language-server = { - package = pkgs.yaml-language-server; - lspConfig = '' - - - lspconfig.yamlls.setup { - capabilities = capabilities, - ${onAttach}, - cmd = ${ - if isList cfg.lsp.package - then expToLua cfg.lsp.package - else ''{"${cfg.lsp.package}/bin/yaml-language-server", "--stdio"}'' - }, - } - ''; + enable = true; + cmd = [(getExe pkgs.yaml-language-server) "--stdio"]; + filetypes = ["yaml" "yaml.docker-compose" "yaml.gitlab" "yaml.helm-values"]; + root_markers = [".git"]; + inherit on_attach; + # -- https://github.com/redhat-developer/vscode-redhat-telemetry#how-to-disable-telemetry-reporting + settings = { + redhat = { + telemetry = { + enabled = false; + }; + }; + }; }; }; in { @@ -55,18 +57,11 @@ in { }; lsp = { - enable = mkEnableOption "YAML LSP support" // {default = config.vim.lsp.enable;}; - - server = mkOption { - type = enum (attrNames servers); - default = defaultServer; - description = "YAML LSP server to use"; - }; - - package = mkOption { - type = either package (listOf str); - default = servers.${cfg.lsp.server}.package; - description = "YAML LSP server package"; + enable = mkEnableOption "Yaml LSP support" // {default = config.vim.lsp.enable;}; + servers = mkOption { + type = deprecatedSingleOrListOf "vim.language.yaml.lsp.servers" (enum (attrNames servers)); + default = defaultServers; + description = "Yaml LSP server to use"; }; }; }; @@ -78,8 +73,12 @@ in { }) (mkIf cfg.lsp.enable { - vim.lsp.lspconfig.enable = true; - vim.lsp.lspconfig.sources.yaml-lsp = servers.${cfg.lsp.server}.lspConfig; + vim.lsp.servers = + mapListToAttrs (n: { + name = n; + value = servers.${n}; + }) + cfg.lsp.servers; }) ]); } diff --git a/modules/plugins/languages/zig.nix b/modules/plugins/languages/zig.nix index 89307eab..57ce961e 100644 --- a/modules/plugins/languages/zig.nix +++ b/modules/plugins/languages/zig.nix @@ -7,29 +7,21 @@ inherit (builtins) attrNames; inherit (lib.options) mkEnableOption mkOption; inherit (lib.modules) mkIf mkMerge mkDefault; - inherit (lib.lists) isList; - inherit (lib.types) bool either listOf package str enum; - inherit (lib.nvim.lua) expToLua; - inherit (lib.nvim.types) mkGrammarOption; + inherit (lib.types) bool package enum; + inherit (lib.nvim.types) mkGrammarOption deprecatedSingleOrListOf; + inherit (lib.meta) getExe; + inherit (lib.nvim.attrsets) mapListToAttrs; cfg = config.vim.languages.zig; - defaultServer = "zls"; + defaultServers = ["zls"]; servers = { zls = { - package = pkgs.zls; - internalFormatter = true; - lspConfig = '' - lspconfig.zls.setup { - capabilities = capabilities, - on_attach = default_on_attach, - cmd = ${ - if isList cfg.lsp.package - then expToLua cfg.lsp.package - else "{'${cfg.lsp.package}/bin/zls'}" - } - } - ''; + enable = true; + cmd = [(getExe pkgs.zls)]; + filetypes = ["zig" "zir"]; + root_markers = ["zls.json" "build.zig" ".git"]; + workspace_required = false; }; }; @@ -74,17 +66,11 @@ in { lsp = { enable = mkEnableOption "Zig LSP support" // {default = config.vim.lsp.enable;}; - server = mkOption { - type = enum (attrNames servers); - default = defaultServer; + servers = mkOption { + type = deprecatedSingleOrListOf "vim.language.zig.lsp.servers" (enum (attrNames servers)); + default = defaultServers; description = "Zig LSP server to use"; }; - - package = mkOption { - description = "ZLS package, or the command to run as a list of strings"; - type = either package (listOf str); - default = pkgs.zls; - }; }; dap = { @@ -118,10 +104,12 @@ in { (mkIf cfg.lsp.enable { vim = { - lsp.lspconfig = { - enable = true; - sources.zig-lsp = servers.${cfg.lsp.server}.lspConfig; - }; + lsp.servers = + mapListToAttrs (n: { + name = n; + value = servers.${n}; + }) + cfg.lsp.servers; # nvf handles autosaving already globals.zig_fmt_autosave = mkDefault 0; diff --git a/modules/plugins/lsp/config.nix b/modules/plugins/lsp/config.nix index e7b67c8e..791b72b7 100644 --- a/modules/plugins/lsp/config.nix +++ b/modules/plugins/lsp/config.nix @@ -1,7 +1,7 @@ { config, lib, - pkgs, + options, ... }: let inherit (lib.generators) mkLuaInline; @@ -10,20 +10,21 @@ inherit (lib.strings) optionalString; inherit (lib.trivial) boolToString; inherit (lib.nvim.binds) addDescriptionsToMappings; + inherit (lib.nvim.dag) entryBefore; + inherit (lib.nvim.lua) toLuaObject; cfg = config.vim.lsp; usingNvimCmp = config.vim.autocomplete.nvim-cmp.enable; usingBlinkCmp = config.vim.autocomplete.blink-cmp.enable; - self = import ./module.nix {inherit config lib pkgs;}; conformCfg = config.vim.formatter.conform-nvim; conformFormatOnSave = conformCfg.enable && conformCfg.setupOpts.format_on_save != null; augroup = "nvf_lsp"; - mappingDefinitions = self.options.vim.lsp.mappings; + mappingDefinitions = options.vim.lsp.mappings; mappings = addDescriptionsToMappings cfg.mappings mappingDefinitions; mkBinding = binding: action: if binding.value != null - then "vim.keymap.set('n', '${binding.value}', ${action}, {buffer=bufnr, noremap=true, silent=true, desc='${binding.description}'})" + then "vim.keymap.set('n', ${toLuaObject binding.value}, ${action}, {buffer=bufnr, noremap=true, silent=true, desc=${toLuaObject binding.description}})" else ""; in { config = mkIf cfg.enable { @@ -35,20 +36,26 @@ in { augroups = [{name = augroup;}]; autocmds = - (optional cfg.inlayHints.enable { - group = augroup; - event = ["LspAttach"]; - desc = "LSP on-attach enable inlay hints autocmd"; - callback = mkLuaInline '' - function(event) - local bufnr = event.buf - local client = vim.lsp.get_client_by_id(event.data.client_id) - if client and client.supports_method(vim.lsp.protocol.Methods.textDocument_inlayHint) then - vim.lsp.inlay_hint.enable(not vim.lsp.inlay_hint.is_enabled({ bufnr = bufnr }), { bufnr = bufnr }) + [ + { + group = augroup; + event = ["LspAttach"]; + desc = "LSP on-attach add keybinds, enable inlay hints, and other plugin integrations"; + callback = mkLuaInline '' + function(event) + local bufnr = event.buf + local client = vim.lsp.get_client_by_id(event.data.client_id) + default_on_attach(client, bufnr) + + ${optionalString cfg.inlayHints.enable '' + if client and client.supports_method(vim.lsp.protocol.Methods.textDocument_inlayHint) then + vim.lsp.inlay_hint.enable(not vim.lsp.inlay_hint.is_enabled({ bufnr = bufnr }), { bufnr = bufnr }) + end + ''} end - end - ''; - }) + ''; + } + ] ++ (optional (!conformFormatOnSave) { group = augroup; event = ["BufWritePre"]; @@ -87,7 +94,7 @@ in { ''; }); - pluginRC.lsp-setup = '' + pluginRC.lsp-setup = entryBefore ["autocmds"] '' vim.g.formatsave = ${boolToString cfg.formatOnSave}; local attach_keymaps = function(client, bufnr) diff --git a/modules/plugins/lsp/default.nix b/modules/plugins/lsp/default.nix index eb694583..ed20685d 100644 --- a/modules/plugins/lsp/default.nix +++ b/modules/plugins/lsp/default.nix @@ -7,6 +7,7 @@ ./lspconfig ./lspsaga ./null-ls + ./harper-ls # lsp plugins ./lspsaga diff --git a/modules/plugins/lsp/harper-ls/config.nix b/modules/plugins/lsp/harper-ls/config.nix new file mode 100644 index 00000000..d6e27bfc --- /dev/null +++ b/modules/plugins/lsp/harper-ls/config.nix @@ -0,0 +1,19 @@ +{ + config, + lib, + pkgs, + ... +}: let + inherit (lib.modules) mkIf; + inherit (lib.meta) getExe; + + cfg = config.vim.lsp; +in { + config = mkIf (cfg.enable && cfg.harper-ls.enable) { + vim.lsp.servers.harper-ls = { + root_markers = [".git"]; + cmd = [(getExe pkgs.harper) "--stdio"]; + settings = {harper-ls = cfg.harper-ls.settings;}; + }; + }; +} diff --git a/modules/plugins/lsp/harper-ls/default.nix b/modules/plugins/lsp/harper-ls/default.nix new file mode 100644 index 00000000..9b4b3ec7 --- /dev/null +++ b/modules/plugins/lsp/harper-ls/default.nix @@ -0,0 +1,6 @@ +{ + imports = [ + ./harper-ls.nix + ./config.nix + ]; +} diff --git a/modules/plugins/lsp/harper-ls/harper-ls.nix b/modules/plugins/lsp/harper-ls/harper-ls.nix new file mode 100644 index 00000000..9fab45fe --- /dev/null +++ b/modules/plugins/lsp/harper-ls/harper-ls.nix @@ -0,0 +1,35 @@ +{lib, ...}: let + inherit (lib.options) mkOption mkEnableOption; + inherit (lib.types) anything attrsOf; +in { + options.vim.lsp.harper-ls = { + enable = mkEnableOption "Harper grammar checking LSP"; + settings = mkOption { + type = attrsOf anything; + default = {}; + example = { + userDictPath = ""; + workspaceDictPath = ""; + fileDictPath = ""; + linters = { + BoringWords = true; + PossessiveNoun = true; + SentenceCapitalization = false; + SpellCheck = false; + }; + codeActions = { + ForceStable = false; + }; + markdown = { + IgnoreLinkTitle = false; + }; + diagnosticSeverity = "hint"; + isolateEnglish = false; + dialect = "American"; + maxFileLength = 120000; + ignoredLintsPath = {}; + }; + description = "Settings to pass to harper-ls"; + }; + }; +} diff --git a/modules/plugins/lsp/nvim-docs-view/config.nix b/modules/plugins/lsp/nvim-docs-view/config.nix index 5c7ad2b7..997e9a72 100644 --- a/modules/plugins/lsp/nvim-docs-view/config.nix +++ b/modules/plugins/lsp/nvim-docs-view/config.nix @@ -1,6 +1,7 @@ { config, lib, + options, ... }: let inherit (lib.modules) mkIf mkMerge; @@ -9,9 +10,8 @@ inherit (lib.nvim.lua) toLuaObject; cfg = config.vim.lsp.nvim-docs-view; - self = import ./nvim-docs-view.nix {inherit lib;}; - mappingDefinitions = self.options.vim.lsp.nvim-docs-view.mappings; + mappingDefinitions = options.vim.lsp.nvim-docs-view.mappings; mappings = addDescriptionsToMappings cfg.mappings mappingDefinitions; in { config = mkIf cfg.enable { diff --git a/modules/plugins/lsp/otter/config.nix b/modules/plugins/lsp/otter/config.nix index 85f24c70..f3dbe4d6 100644 --- a/modules/plugins/lsp/otter/config.nix +++ b/modules/plugins/lsp/otter/config.nix @@ -1,6 +1,7 @@ { config, lib, + options, ... }: let inherit (lib.modules) mkIf mkMerge; @@ -10,8 +11,7 @@ cfg = config.vim.lsp; - self = import ./otter.nix {inherit lib;}; - mappingDefinitions = self.options.vim.lsp.otter-nvim.mappings; + mappingDefinitions = options.vim.lsp.otter-nvim.mappings; mappings = addDescriptionsToMappings cfg.otter-nvim.mappings mappingDefinitions; in { config = mkIf (cfg.enable && cfg.otter-nvim.enable) { diff --git a/modules/plugins/minimap/codewindow/config.nix b/modules/plugins/minimap/codewindow/config.nix index a853514a..8a62ee04 100644 --- a/modules/plugins/minimap/codewindow/config.nix +++ b/modules/plugins/minimap/codewindow/config.nix @@ -1,6 +1,7 @@ { config, lib, + options, ... }: let inherit (lib.modules) mkIf mkMerge; @@ -9,9 +10,7 @@ cfg = config.vim.minimap.codewindow; - self = import ./codewindow.nix {inherit lib;}; - - mappingDefinitions = self.options.vim.minimap.codewindow.mappings; + mappingDefinitions = options.vim.minimap.codewindow.mappings; mappings = addDescriptionsToMappings cfg.mappings mappingDefinitions; in { config = mkIf cfg.enable { diff --git a/modules/plugins/notes/orgmode/orgmode.nix b/modules/plugins/notes/orgmode/orgmode.nix index fe2c4be8..103a1453 100644 --- a/modules/plugins/notes/orgmode/orgmode.nix +++ b/modules/plugins/notes/orgmode/orgmode.nix @@ -5,9 +5,9 @@ ... }: let inherit (lib.modules) mkRenamedOptionModule; - inherit (lib.options) mkEnableOption mkOption; + inherit (lib.options) mkEnableOption mkOption mkPackageOption; inherit (lib.types) str listOf; - inherit (lib.nvim.types) mkGrammarOption mkPluginSetupOption; + inherit (lib.nvim.types) mkPluginSetupOption; in { imports = [ (mkRenamedOptionModule ["vim" "notes" "orgmode" "orgAgendaFiles"] ["vim" "notes" "orgmode" "setupOpts" "org_agenda_files"]) @@ -33,7 +33,9 @@ in { treesitter = { enable = mkEnableOption "Orgmode treesitter" // {default = config.vim.languages.enableTreesitter;}; - orgPackage = mkGrammarOption pkgs "org"; + orgPackage = mkPackageOption pkgs ["org-nvim treesitter"] { + default = ["tree-sitter-grammars" "tree-sitter-org-nvim"]; + }; }; }; } diff --git a/modules/plugins/notes/todo-comments/config.nix b/modules/plugins/notes/todo-comments/config.nix index b03efce4..47fa3641 100644 --- a/modules/plugins/notes/todo-comments/config.nix +++ b/modules/plugins/notes/todo-comments/config.nix @@ -1,7 +1,7 @@ { - pkgs, config, lib, + options, ... }: let inherit (lib.modules) mkMerge mkIf; @@ -9,8 +9,7 @@ inherit (lib.nvim.lua) toLuaObject; cfg = config.vim.notes.todo-comments; - self = import ./todo-comments.nix {inherit pkgs lib;}; - inherit (self.options.vim.notes.todo-comments) mappings; + inherit (options.vim.notes.todo-comments) mappings; in { config = mkIf cfg.enable { vim = { diff --git a/modules/plugins/statusline/lualine/supported_themes.nix b/modules/plugins/statusline/lualine/supported_themes.nix index 3528b24f..b7fe2de8 100644 --- a/modules/plugins/statusline/lualine/supported_themes.nix +++ b/modules/plugins/statusline/lualine/supported_themes.nix @@ -5,4 +5,5 @@ "oxocarbon" "gruvbox" "nord" + "mellow" ] diff --git a/modules/plugins/tabline/nvim-bufferline/config.nix b/modules/plugins/tabline/nvim-bufferline/config.nix index 250425d3..396e6886 100644 --- a/modules/plugins/tabline/nvim-bufferline/config.nix +++ b/modules/plugins/tabline/nvim-bufferline/config.nix @@ -1,6 +1,7 @@ { config, lib, + options, ... }: let inherit (lib.modules) mkIf mkMerge; @@ -9,8 +10,8 @@ inherit (lib.nvim.lua) toLuaObject; cfg = config.vim.tabline.nvimBufferline; - self = import ./nvim-bufferline.nix {inherit config lib;}; - inherit (self.options.vim.tabline.nvimBufferline) mappings; + + inherit (options.vim.tabline.nvimBufferline) mappings; in { config = mkIf cfg.enable { vim = { diff --git a/modules/plugins/theme/supported-themes.nix b/modules/plugins/theme/supported-themes.nix index 6419ba14..c2a5e1ca 100644 --- a/modules/plugins/theme/supported-themes.nix +++ b/modules/plugins/theme/supported-themes.nix @@ -315,4 +315,13 @@ in { styles = ["hard" "medium" "soft"]; }; + + mellow = { + setup = {transparent ? false, ...}: '' + -- Mellow configuration + vim.g.mellow_transparent = ${boolToString transparent} + + vim.cmd.colorscheme("mellow") + ''; + }; } diff --git a/modules/plugins/treesitter/config.nix b/modules/plugins/treesitter/config.nix index 14aba5e1..f7955110 100644 --- a/modules/plugins/treesitter/config.nix +++ b/modules/plugins/treesitter/config.nix @@ -1,7 +1,7 @@ { config, - pkgs, lib, + options, ... }: let inherit (lib.modules) mkIf mkMerge; @@ -12,8 +12,7 @@ cfg = config.vim.treesitter; - self = import ./treesitter.nix {inherit pkgs lib;}; - mappingDefinitions = self.options.vim.treesitter.mappings; + mappingDefinitions = options.vim.treesitter.mappings; mappings = addDescriptionsToMappings cfg.mappings mappingDefinitions; in { config = mkIf cfg.enable { diff --git a/modules/plugins/utility/default.nix b/modules/plugins/utility/default.nix index 349fb47a..2e00e270 100644 --- a/modules/plugins/utility/default.nix +++ b/modules/plugins/utility/default.nix @@ -19,6 +19,7 @@ ./oil-nvim ./outline ./preview + ./qmk-nvim ./sleuth ./smart-splits ./snacks-nvim diff --git a/modules/plugins/utility/gestures/gesture-nvim/config.nix b/modules/plugins/utility/gestures/gesture-nvim/config.nix index 9838c178..6c40e3ea 100644 --- a/modules/plugins/utility/gestures/gesture-nvim/config.nix +++ b/modules/plugins/utility/gestures/gesture-nvim/config.nix @@ -1,6 +1,7 @@ { config, lib, + options, ... }: let inherit (lib.modules) mkIf mkMerge; @@ -9,9 +10,7 @@ cfg = config.vim.gestures.gesture-nvim; - self = import ./gesture-nvim.nix {inherit lib;}; - - mappingDefinitions = self.options.vim.gestures.gesture-nvim.mappings; + mappingDefinitions = options.vim.gestures.gesture-nvim.mappings; mappings = addDescriptionsToMappings cfg.mappings mappingDefinitions; in { config = mkIf cfg.enable { diff --git a/modules/plugins/utility/motion/flash/config.nix b/modules/plugins/utility/motion/flash/config.nix index 83ddd0b9..73c94ab3 100644 --- a/modules/plugins/utility/motion/flash/config.nix +++ b/modules/plugins/utility/motion/flash/config.nix @@ -14,7 +14,7 @@ in { "flash-nvim" = { package = "flash-nvim"; setupModule = "flash"; - setupOpts = cfg.setupOpts; + inherit (cfg) setupOpts; lazy = true; diff --git a/modules/plugins/utility/motion/hop/config.nix b/modules/plugins/utility/motion/hop/config.nix index 505b9ebb..ef494309 100644 --- a/modules/plugins/utility/motion/hop/config.nix +++ b/modules/plugins/utility/motion/hop/config.nix @@ -1,6 +1,7 @@ { config, lib, + options, ... }: let inherit (lib.modules) mkIf; @@ -9,9 +10,7 @@ cfg = config.vim.utility.motion.hop; - self = import ./hop.nix {inherit lib;}; - - mappingDefinitions = self.options.vim.utility.motion.hop.mappings; + mappingDefinitions = options.vim.utility.motion.hop.mappings; mappings = addDescriptionsToMappings cfg.mappings mappingDefinitions; in { config = mkIf cfg.enable { diff --git a/modules/plugins/utility/oil-nvim/config.nix b/modules/plugins/utility/oil-nvim/config.nix index dca4de60..9344c305 100644 --- a/modules/plugins/utility/oil-nvim/config.nix +++ b/modules/plugins/utility/oil-nvim/config.nix @@ -3,18 +3,38 @@ lib, ... }: let + inherit (lib.attrsets) optionalAttrs recursiveUpdate; + inherit (lib.lists) optionals; inherit (lib.modules) mkIf; inherit (lib.nvim.lua) toLuaObject; - inherit (lib.nvim.dag) entryAnywhere; + inherit (lib.nvim.dag) entryAfter entryAnywhere; cfg = config.vim.utility.oil-nvim; in { config = mkIf cfg.enable { vim = { - startPlugins = ["oil-nvim"]; - pluginRC.oil-nvim = entryAnywhere '' - require("oil").setup(${toLuaObject cfg.setupOpts}); - ''; + startPlugins = ["oil-nvim"] ++ (optionals cfg.gitStatus.enable ["oil-git-status.nvim"]); + + pluginRC.oil-nvim = let + gitStatusDefaultOpts = { + # https://github.com/refractalize/oil-git-status.nvim?tab=readme-ov-file#configuration + win_options = { + signcolumn = "yes:2"; + }; + }; + + setupOpts = + recursiveUpdate + (optionalAttrs cfg.gitStatus.enable gitStatusDefaultOpts) + cfg.setupOpts; + in + entryAnywhere '' + require("oil").setup(${toLuaObject setupOpts}); + ''; + + pluginRC.oil-git-status-nvim = mkIf cfg.gitStatus.enable (entryAfter ["oil-nvim"] '' + require("oil-git-status").setup(${toLuaObject cfg.gitStatus.setupOpts}) + ''); }; }; } diff --git a/modules/plugins/utility/oil-nvim/oil-nvim.nix b/modules/plugins/utility/oil-nvim/oil-nvim.nix index 557f3db6..426188bc 100644 --- a/modules/plugins/utility/oil-nvim/oil-nvim.nix +++ b/modules/plugins/utility/oil-nvim/oil-nvim.nix @@ -8,5 +8,13 @@ in { ''; setupOpts = mkPluginSetupOption "oil-nvim" {}; + + gitStatus = { + enable = mkEnableOption '' + Git status on [oil-nvim] directory listings + ''; + + setupOpts = mkPluginSetupOption "oil-git-status-nvim" {}; + }; }; } diff --git a/modules/plugins/utility/preview/glow/config.nix b/modules/plugins/utility/preview/glow/config.nix index 4529a534..7297df83 100644 --- a/modules/plugins/utility/preview/glow/config.nix +++ b/modules/plugins/utility/preview/glow/config.nix @@ -2,6 +2,7 @@ pkgs, config, lib, + options, ... }: let inherit (lib.modules) mkIf mkMerge; @@ -9,10 +10,7 @@ inherit (lib.nvim.dag) entryAnywhere; cfg = config.vim.utility.preview.glow; - self = import ./glow.nix { - inherit lib config pkgs; - }; - inherit (self.options.vim.utility.preview.glow) mappings; + inherit (options.vim.utility.preview.glow) mappings; in { config = mkIf cfg.enable { vim.startPlugins = ["glow-nvim"]; diff --git a/modules/plugins/utility/qmk-nvim/config.nix b/modules/plugins/utility/qmk-nvim/config.nix new file mode 100644 index 00000000..c86a483a --- /dev/null +++ b/modules/plugins/utility/qmk-nvim/config.nix @@ -0,0 +1,36 @@ +{ + config, + lib, + ... +}: let + inherit (lib.modules) mkIf; + inherit (lib.nvim.lua) toLuaObject; + inherit (lib.nvim.dag) entryAfter; + + cfg = config.vim.utility.qmk-nvim; +in { + config = mkIf cfg.enable { + vim = { + startPlugins = ["qmk-nvim"]; + + pluginRC.qmk-nvim = entryAfter ["nvim-notify"] '' + require('qmk').setup(${toLuaObject cfg.setupOpts}) + ''; + }; + + assertions = [ + { + assertion = cfg.setupOpts.variant == "qmk" && cfg.setupOpts.comment_preview.position != "inside"; + message = "comment_preview.position can only be set to inside when using the qmk layoyt"; + } + { + assertion = cfg.setupOpts.name != null; + message = "qmk-nvim requires 'vim.utility.qmk.setupOpts.name' to be set."; + } + { + assertion = cfg.setupOpts.layout != null; + message = "qmk-nvim requires 'vim.utility.qmk.setupOpts.layout' to be set."; + } + ]; + }; +} diff --git a/modules/plugins/utility/qmk-nvim/default.nix b/modules/plugins/utility/qmk-nvim/default.nix new file mode 100644 index 00000000..4162ff2e --- /dev/null +++ b/modules/plugins/utility/qmk-nvim/default.nix @@ -0,0 +1,6 @@ +{ + imports = [ + ./config.nix + ./qmk-nvim.nix + ]; +} diff --git a/modules/plugins/utility/qmk-nvim/qmk-nvim.nix b/modules/plugins/utility/qmk-nvim/qmk-nvim.nix new file mode 100644 index 00000000..2c541e64 --- /dev/null +++ b/modules/plugins/utility/qmk-nvim/qmk-nvim.nix @@ -0,0 +1,49 @@ +{lib, ...}: let + inherit (lib.options) mkOption mkEnableOption; + inherit (lib.types) attrsOf nullOr enum lines str; + inherit (lib.nvim.types) mkPluginSetupOption; +in { + options.vim.utility.qmk-nvim = { + enable = mkEnableOption "QMK and ZMK keymaps in nvim"; + + setupOpts = mkPluginSetupOption "qmk.nvim" { + name = mkOption { + type = nullOr str; + default = null; + description = "The name of the layout"; + }; + + layout = mkOption { + type = nullOr lines; + default = null; + description = '' + The keyboard key layout + see for more details + ''; + }; + + variant = mkOption { + type = enum ["qmk" "zmk"]; + default = "qmk"; + description = "Chooses the expected hardware target"; + }; + + comment_preview = { + position = mkOption { + type = enum ["top" "bottom" "inside" "none"]; + default = "top"; + description = "Controls the position of the preview"; + }; + + keymap_overrides = mkOption { + type = attrsOf str; + default = {}; + description = '' + Key codes to text replacements + see for more details + ''; + }; + }; + }; + }; +} diff --git a/modules/wrapper/build/config.nix b/modules/wrapper/build/config.nix index a1807388..68b2c00d 100644 --- a/modules/wrapper/build/config.nix +++ b/modules/wrapper/build/config.nix @@ -22,9 +22,7 @@ passthru.vimPlugin = false; }; - # build a vim plugin with the given name and arguments - # if the plugin is nvim-treesitter, warn the user to use buildTreesitterPlug - # instead + # Build a Vim plugin with the given name and arguments. buildPlug = attrs: let pin = getPin attrs.pname; in @@ -36,6 +34,7 @@ // attrs ); + # Build a given Treesitter grammar. buildTreesitterPlug = grammars: vimPlugins.nvim-treesitter.withPlugins (_: grammars); pluginBuilders = { @@ -48,6 +47,9 @@ doCheck = false; }; + # Get plugins built from source from self.packages + # If adding a new plugin to be built from source, it must also be inherited + # here. inherit (inputs.self.packages.${pkgs.stdenv.system}) blink-cmp avante-nvim; }; @@ -71,29 +73,38 @@ # Wrap the user's desired (unwrapped) Neovim package with arguments that'll be used to # generate a wrapped Neovim package. neovim-wrapped = inputs.mnw.lib.wrap {inherit pkgs;} { + appName = "nvf"; neovim = config.vim.package; + initLua = config.vim.builtLuaConfigRC; + luaFiles = config.vim.extraLuaFiles; + + # Plugin configurations plugins = { start = buildConfigPlugins config.vim.startPlugins; opt = buildConfigPlugins config.vim.optPlugins; }; - appName = "nvf"; - extraBinPath = config.vim.extraPackages; - initLua = config.vim.builtLuaConfigRC; - luaFiles = config.vim.extraLuaFiles; + + # Providers for Neovim providers = { + ruby.enable = config.vim.withRuby; + nodeJs.enable = config.vim.withNodeJs; python3 = { enable = config.vim.withPython3; extraPackages = ps: map (flip builtins.getAttr ps) config.vim.python3Packages; }; - ruby.enable = config.vim.withRuby; - nodeJs.enable = config.vim.withNodeJs; }; + + # Aliases to link `nvim` to aliases = lib.optional config.vim.viAlias "vi" ++ lib.optional config.vim.vimAlias "vim"; + # Additional packages or Lua packages to be made available to Neovim + extraBinPath = config.vim.extraPackages; extraLuaPackages = ps: map (flip builtins.getAttr ps) config.vim.luaPackages; }; + # A store path representing the built Lua configuration. dummyInit = pkgs.writeText "nvf-init.lua" config.vim.builtLuaConfigRC; + # Additional helper scripts for printing and displaying nvf configuration # in your commandline. printConfig = pkgs.writers.writeDashBin "nvf-print-config" "cat ${dummyInit}"; @@ -106,10 +117,23 @@ paths = [neovim-wrapped printConfig printConfigPath]; postBuild = "echo Helpers added"; - # Allow evaluating config.vim, i.e., config.vim from the packages' passthru - # attribute. For example, packages.x86_64-linux.neovim.passthru.neovimConfig - # will return the configuration in full. - passthru.neovimConfig = config.vim; + passthru = { + # Allow evaluating config.vim, i.e., config.vim from the packages' passthru + # attribute. For example, packages.x86_64-linux.neovim.passthru.neovimConfig + # will return the configuration in full. + neovimConfig = config.vim; + + # Also expose the helper scripts in passthru. + nvfPrintConfig = printConfig; + nvfPrintConfigPath = printConfigPath; + + # In systems where we only have a package and no module, this can be used + # to access the built init.lua + initLua = dummyInit; + + mnwConfig = neovim-wrapped.passthru.config; + mnwConfigDir = neovim-wrapped.passthru.configDir; + }; meta = neovim-wrapped.meta diff --git a/modules/wrapper/environment/options.nix b/modules/wrapper/environment/options.nix index c401f506..d3b28b0f 100644 --- a/modules/wrapper/environment/options.nix +++ b/modules/wrapper/environment/options.nix @@ -3,7 +3,7 @@ lib, ... }: let - inherit (lib.options) mkOption mkEnableOption literalMD; + inherit (lib.options) mkOption mkEnableOption literalMD literalExpression; inherit (lib.types) package bool str listOf attrsOf; inherit (lib.nvim.types) pluginsOpt extraPluginType; in { @@ -11,6 +11,7 @@ in { package = mkOption { type = package; default = pkgs.neovim-unwrapped; + defaultText = literalExpression "pkgs.neovim-unwrapped"; description = '' The neovim package to use for the wrapper. This corresponds to the package that will be wrapped @@ -27,21 +28,20 @@ in { viAlias = mkOption { type = bool; default = true; + example = false; description = "Enable the `vi` alias for `nvim`"; }; vimAlias = mkOption { type = bool; default = true; + example = false; description = "Enable the `vim` alias for `nvim`"; }; startPlugins = pluginsOpt { default = ["plenary-nvim"]; - example = '' - [pkgs.vimPlugins.telescope-nvim] - ''; - + example = literalExpression "[pkgs.vimPlugins.telescope-nvim]"; description = '' List of plugins to load on startup. This is used internally to add plugins to Neovim's runtime. @@ -54,9 +54,7 @@ in { optPlugins = pluginsOpt { default = []; - example = '' - [pkgs.vimPlugins.vim-ghost] - ''; + example = literalExpression "[pkgs.vimPlugins.vim-ghost]"; description = '' List of plugins to optionally load on startup. @@ -108,7 +106,7 @@ in { ''; }; - # this defaults to `true` in the wrapper + # This defaults to `true` in the wrapper # and since we pass this value to the wrapper # with an inherit, it should be `true` here as well withRuby = @@ -120,14 +118,14 @@ in { }; withNodeJs = mkEnableOption '' - NodeJs support in the Neovim wrapper + NodeJS support in the Neovim wrapper ''; luaPackages = mkOption { type = listOf str; default = []; example = ''["magick" "serpent"]''; - description = "List of lua packages to install"; + description = "List of Lua packages to install"; }; withPython3 = mkEnableOption '' @@ -144,7 +142,7 @@ in { pluginOverrides = mkOption { type = attrsOf package; default = {}; - example = '' + example = literalExpression '' { lazydev-nvim = pkgs.fetchFromGitHub { owner = "folke"; diff --git a/modules/wrapper/lazy/lazy.nix b/modules/wrapper/lazy/lazy.nix index eb1f5cdf..cef54360 100644 --- a/modules/wrapper/lazy/lazy.nix +++ b/modules/wrapper/lazy/lazy.nix @@ -1,7 +1,7 @@ {lib, ...}: let inherit (lib.options) mkOption mkEnableOption; inherit (lib.types) enum listOf submodule nullOr str bool int attrsOf anything either oneOf lines; - inherit (lib.nvim.types) pluginType; + inherit (lib.nvim.types) pluginType luaInline; inherit (lib.nvim.config) mkBool; lznKeysSpec = submodule { @@ -98,7 +98,7 @@ # lz.n options enabled = mkOption { - type = nullOr (either bool str); + type = nullOr (either bool luaInline); default = null; description = "When false, or if the lua function returns false, this plugin will not be included in the spec"; }; diff --git a/npins/sources.json b/npins/sources.json index fce2fe15..20867776 100644 --- a/npins/sources.json +++ b/npins/sources.json @@ -964,6 +964,19 @@ "url": "https://github.com/OXY2DEV/markview.nvim/archive/de79a7626d54d7785436105ef72f37ee8fe8fa16.tar.gz", "hash": "032i6m9pld1zyhd7lq49dg4qh98w6vmmzqp2f46drhq0ds26hs4h" }, + "mellow": { + "type": "Git", + "repository": { + "type": "GitHub", + "owner": "mellow-theme", + "repo": "mellow.nvim" + }, + "branch": "main", + "submodules": false, + "revision": "5cd188489bcc7eb512f0a30581ad972070f8e5cd", + "url": "https://github.com/mellow-theme/mellow.nvim/archive/5cd188489bcc7eb512f0a30581ad972070f8e5cd.tar.gz", + "hash": "0hjfhwvvlc50rrxscg2lbkip7v5s13yaja0pckhhxfkbz17b5v83" + }, "mind-nvim": { "type": "Git", "repository": { @@ -2091,6 +2104,19 @@ "url": "https://github.com/epwalsh/obsidian.nvim/archive/14e0427bef6c55da0d63f9a313fd9941be3a2479.tar.gz", "hash": "15ycmhn48ryaqzch6w3w6llq2qgmjx8xwkb9dn0075z60dybpflr" }, + "oil-git-status.nvim": { + "type": "Git", + "repository": { + "type": "GitHub", + "owner": "refractalize", + "repo": "oil-git-status.nvim" + }, + "branch": "main", + "submodules": false, + "revision": "4b5cf53842c17a09420919e655a6a559da3112d7", + "url": "https://github.com/refractalize/oil-git-status.nvim/archive/4b5cf53842c17a09420919e655a6a559da3112d7.tar.gz", + "hash": "1jzw6lkvi9xxzqy8xz056xlb45byr1arklh87zmyf0nj9plm2nsp" + }, "oil-nvim": { "type": "Git", "repository": { @@ -2208,6 +2234,38 @@ "url": "https://github.com/tris203/precognition.nvim/archive/2aae2687207029b3611a0e19a289f9e1c7efbe16.tar.gz", "hash": "0b4yz5jvqd0li373067cs1n761vn9jxkhcvhsrvm9h1snqw1c6nk" }, + "prettier-plugin-astro": { + "type": "GitRelease", + "repository": { + "type": "GitHub", + "owner": "withastro", + "repo": "prettier-plugin-astro" + }, + "pre_releases": false, + "version_upper_bound": null, + "release_prefix": null, + "submodules": false, + "version": "v0.14.1", + "revision": "57234893ca374c8e401cce1f931180d314e13eac", + "url": "https://api.github.com/repos/withastro/prettier-plugin-astro/tarball/v0.14.1", + "hash": "14ffwxggcnyc947pdxgsgz1v2q76m5xmybfxg8kyla4l7phg6qsw" + }, + "prettier-plugin-svelte": { + "type": "GitRelease", + "repository": { + "type": "GitHub", + "owner": "sveltejs", + "repo": "prettier-plugin-svelte" + }, + "pre_releases": false, + "version_upper_bound": null, + "release_prefix": null, + "submodules": false, + "version": "v3.3.2", + "revision": "76c04ebfdff4306842e8ab0cd96b1c53c7041dde", + "url": "https://api.github.com/repos/sveltejs/prettier-plugin-svelte/tarball/v3.3.2", + "hash": "0g6h5lvhyms6nvk83vkd3yi8rvsz2v5g6cw03fqsv2nj45s6cf7r" + }, "project-nvim": { "type": "Git", "repository": { @@ -2234,6 +2292,19 @@ "url": "https://github.com/kevinhwang91/promise-async/archive/119e8961014c9bfaf1487bf3c2a393d254f337e2.tar.gz", "hash": "0q4a0rmy09hka6zvydzjj2gcm2j5mlbrhbxfcdjj33ngpblkmqzm" }, + "qmk-nvim": { + "type": "Git", + "repository": { + "type": "GitHub", + "owner": "codethread", + "repo": "qmk.nvim" + }, + "branch": "main", + "submodules": false, + "revision": "3c804c1480991e4837514900b22b9358cfd64fa1", + "url": "https://github.com/codethread/qmk.nvim/archive/3c804c1480991e4837514900b22b9358cfd64fa1.tar.gz", + "hash": "03fsx6qsn8b36jp5m0fkdla6gkkzdv2j18y378y3cqpjclgq995a" + }, "rainbow-delimiters-nvim": { "type": "Git", "repository": {