diff --git a/.github/DISCUSSION_TEMPLATE/q-a.yml b/.github/DISCUSSION_TEMPLATE/q-a.yml new file mode 100644 index 00000000..b4c81c4f --- /dev/null +++ b/.github/DISCUSSION_TEMPLATE/q-a.yml @@ -0,0 +1,104 @@ +--- +labels: [Question] +body: + - type: markdown + attributes: + value: | + Thanks for your interest in rustaceanvim 🦀 + Please follow these instructions, fill every question, and do every step. 🙏 + - type: checkboxes + id: checks + attributes: + label: Checked other resources + description: Please confirm and check all the following options. + options: + - label: This is not a bug report or feature request (Please use [issues](https://github.com/mrcjkb/rustaceanvim/issues/new/choose) for those). + required: true + - label: This is a question about rustaceanvim, not rust-analyzer or Neovim. + required: true + - label: This is [not a question about autocompletion](https://github.com/mrcjkb/rustaceanvim?tab=readme-ov-file#im-having-issues-with-autocompletion). + required: true + - label: I have read the README. + required: true + - label: I have read the [FAQ](https://github.com/mrcjkb/rustaceanvim?tab=readme-ov-file#left_speech_bubble-faq). + required: true + - label: I have searched for a solution using `:h rustaceanvim`. + required: true + - label: I have searched [existing discussions](https://github.com/mrcjkb/rustaceanvim/discussions). + required: true + - label: I have searched the existing issues/discussions of other plugins related to this question (if applicable). + required: false + - label: I have searched the [existing rust-analyzer discussions](https://github.com/rust-lang/rust-analyzer/discussions) (if applicable). + required: false + - label: I have added a very descriptive title to this question. + required: true + - type: textarea + id: description + attributes: + label: Description + description: | + What is the problem, question, or error? + + Write a short description explaining what you are doing, what you expect to happen, and what is currently happening. + placeholder: | + * I'm trying to use the `rustaceanvim` plugin to do X. + * I expect to see Y. + * Instead, it does Z. + validations: + required: true + - type: textarea + id: example + attributes: + label: Example Code + description: | + Please add a self-contained, [minimal, reproducible, example](https://stackoverflow.com/help/minimal-reproducible-example) with your use case. + You can use [the minimal config as a template](https://github.com/mrcjkb/rustaceanvim/blob/master/troubleshooting/minimal.lua). + + If a maintainer can copy it, run it, and see it right away, there's a much higher chance that you'll be able to get help. + + > [!IMPORTANT] + > + > - Reduce your code to the minimum required to reproduce the issue if possible. This makes it much easier for others to help you. + > - Avoid screenshots when possible, as they are hard to read and (more importantly) don't allow others to copy-and-paste your code. + + placeholder: | + vim.env.LAZY_STDPATH = '.repro' + load(vim.fn.system('curl -s https://raw.githubusercontent.com/folke/lazy.nvim/main/bootstrap.lua'))() + + require('lazy.minit').repro { + spec = { + { + 'mrcjkb/rustaceanvim', + version = '^5', + init = function() + -- Configure rustaceanvim here + vim.g.rustaceanvim = {} + end, + lazy = false, + }, + }, + } + -- do anything else you need to do to reproduce the issue + render: lua + validations: + required: true + - type: input + attributes: + label: "Neovim version (nvim -v)" + placeholder: "v0.10.1" + validations: + required: true + - type: input + attributes: + label: "Operating system/version" + placeholder: "NixOS 24.05" + validations: + required: true + - type: textarea + attributes: + label: "Output of :checkhealth rustaceanvim" + render: "console" + placeholder: | + Please paste the output here, as it may contain important information for debugging. + validations: + required: true diff --git a/.github/ISSUE_TEMPLATE/bug_report.yml b/.github/ISSUE_TEMPLATE/bug_report.yml index a859de02..b31dd462 100644 --- a/.github/ISSUE_TEMPLATE/bug_report.yml +++ b/.github/ISSUE_TEMPLATE/bug_report.yml @@ -22,16 +22,20 @@ body: required: false - label: I can reproduce the issue with stable Neovim (not a distribution or nightly) and the [minimal config](https://github.com/mrcjkb/rustaceanvim/blob/master/troubleshooting/minimal.lua). required: true + - label: This is an issue about rustaceanvim, not rust-analyzer or Neovim. + required: true + - label: This is [not an issue with autocompletion](https://github.com/mrcjkb/rustaceanvim?tab=readme-ov-file#im-having-issues-with-autocompletion). + required: true - type: input attributes: label: "Neovim version (nvim -v)" - placeholder: "v0.9.5" + placeholder: "v0.10.1" validations: required: true - type: input attributes: label: "Operating system/version" - placeholder: "NixOS 23.11" + placeholder: "NixOS 24.05" validations: required: true - type: textarea @@ -53,6 +57,7 @@ body: > - To rule out another plugin causing the issue, please start with a [minimal config](https://github.com/mrcjkb/rustaceanvim/blob/master/troubleshooting/minimal.lua). > - Please refer to the readme's [troubleshooting guide](https://github.com/mrcjkb/rustaceanvim?tab=readme-ov-file#stethoscope-troubleshooting). > - Please provide exact (repeatable) steps, including project initialization (if applicable). + > - Avoid screenshots when possible, as they are hard to read and (more importantly) don't allow others to copy-and-paste your code. placeholder: | mkdir -p /tmp/minimal NVIM_DATA_MINIMAL=/tmp/minimal NVIM_APP_NAME="nvim-minimal" -u minimal.lua @@ -78,6 +83,12 @@ body: label: "The minimal config used to reproduce this issue." description: | Run with `nvim -u minimal.lua` + + > [!IMPORTANT] + > + > - Reduce your code to the minimum required to reproduce. + > - Avoid screenshots when possible, as they are hard to read and (more importantly) don't allow others to copy-and-paste your code. + placeholder: | vim.env.LAZY_STDPATH = '.repro' load(vim.fn.system('curl -s https://raw.githubusercontent.com/folke/lazy.nvim/main/bootstrap.lua'))() @@ -86,7 +97,7 @@ body: spec = { { 'mrcjkb/rustaceanvim', - version = '^4', + version = '^5', init = function() -- Configure rustaceanvim here vim.g.rustaceanvim = {} diff --git a/.github/workflows/release.yml b/.github/workflows/luarocks.yml similarity index 55% rename from .github/workflows/release.yml rename to .github/workflows/luarocks.yml index fddca06d..46a87fa9 100644 --- a/.github/workflows/release.yml +++ b/.github/workflows/luarocks.yml @@ -1,8 +1,12 @@ -name: "Release" +--- +name: "Push to LuaRocks" on: push: tags: - "*" + release: + types: + - created pull_request: jobs: @@ -12,23 +16,17 @@ jobs: steps: - name: Checkout uses: actions/checkout@v4 - - name: Fail if changelog entry does not exist - if: startsWith(github.ref, 'refs/tags/') - run: grep -q "${{ github.ref_name }}" CHANGELOG.md + with: + fetch-depth: 0 # Required to count the commits + - name: Get Version + run: echo "LUAROCKS_VERSION=$(git describe --abbrev=0 --tags)" >> $GITHUB_ENV - name: LuaRocks Upload - uses: nvim-neorocks/luarocks-tag-release@v5 + uses: nvim-neorocks/luarocks-tag-release@v7 env: LUAROCKS_API_KEY: ${{ secrets.LUAROCKS_API_KEY }} with: - test_interpreters: "" + version: ${{ env.LUAROCKS_VERSION }} detailed_description: | This plugin automatically configures the rust-analyzer builtin LSP client and integrates with other Rust tools. See the README's #features section for more info. - - name: GitHub Release - if: startsWith(github.ref, 'refs/tags/') - uses: ncipollo/release-action@v1 - with: - bodyFile: "CHANGELOG.md" - allowUpdates: true - diff --git a/.github/workflows/release-please.yml b/.github/workflows/release-please.yml new file mode 100644 index 00000000..8f2d6016 --- /dev/null +++ b/.github/workflows/release-please.yml @@ -0,0 +1,22 @@ +--- +permissions: + contents: write + pull-requests: write + +name: Release Please + +on: + workflow_dispatch: + push: + branches: + - master + +jobs: + release: + name: release + runs-on: ubuntu-latest + steps: + - uses: googleapis/release-please-action@v4 + with: + release-type: simple + token: ${{ secrets.PAT }} diff --git a/.github/workflows/review-checklist.yml b/.github/workflows/review-checklist.yml index 8d31faf5..ee2e20ff 100644 --- a/.github/workflows/review-checklist.yml +++ b/.github/workflows/review-checklist.yml @@ -49,6 +49,5 @@ jobs: - [ ] Tests have been added. - [ ] Tested manually (Steps to reproduce in PR description). - [ ] Updated documentation. - - [ ] Updated [CHANGELOG.md](https://github.com/mrcjkb/rustaceanvim/blob/master/CHANGELOG.md) `, }) diff --git a/.gitignore b/.gitignore index 34a72b84..1b988b9f 100644 --- a/.gitignore +++ b/.gitignore @@ -1,4 +1,5 @@ -result/ +result +result-* .direnv .luarc.json .repro diff --git a/.neoconf.json b/.neoconf.json deleted file mode 100644 index f42d8477..00000000 --- a/.neoconf.json +++ /dev/null @@ -1,19 +0,0 @@ -{ - "neodev": { - "library": { - "enabled": true, - "plugins": [ - "nvim-dap", - "toggleterm.nvim", - "neotest", - ] - } - }, - "neoconf": { - "plugins": { - "lua_ls": { - "enabled": true - } - } - }, -} diff --git a/CHANGELOG.md b/CHANGELOG.md index 49f53cd0..7b4e26a6 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,10 +1,163 @@ - + # Changelog All notable changes to this project will be documented in this file. +This project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0.html). -The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.0.0/), -and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0.html). +## [5.16.0](https://github.com/mrcjkb/rustaceanvim/compare/v5.15.4...v5.16.0) (2024-12-02) + + +### Features + +* **lsp:** info log when starting client in standalone mode ([0362314](https://github.com/mrcjkb/rustaceanvim/commit/03623143c2cd9fa54c9769702c458b087c5b9863)) + + +### Bug Fixes + +* **lsp:** fall back to default offset encoding if not set ([4ac7a3c](https://github.com/mrcjkb/rustaceanvim/commit/4ac7a3c6cca9e393229651cc90733afbdc7c6395)) + +## [5.15.4](https://github.com/mrcjkb/rustaceanvim/compare/v5.15.3...v5.15.4) (2024-11-29) + + +### Bug Fixes + +* **lsp/nightly:** avoid deprecations with no alternative in stable (╯°□°)╯︵ ┻━┻ ([#587](https://github.com/mrcjkb/rustaceanvim/issues/587)) ([f116a55](https://github.com/mrcjkb/rustaceanvim/commit/f116a555d3d30d2aabf74f1e5f1c1b2b377e6516)) +* **lsp:** remove info notification when switching target architecture ([7a565dc](https://github.com/mrcjkb/rustaceanvim/commit/7a565dce677278a83419e01ecd630d135590dfe2)) + +## [5.15.3](https://github.com/mrcjkb/rustaceanvim/compare/v5.15.2...v5.15.3) (2024-11-27) + + +### Bug Fixes + +* **lsp:** schedule notification if lsp restart times out ([1f97e08](https://github.com/mrcjkb/rustaceanvim/commit/1f97e08765a6149f87abe3aad6c8d03d9884a628)) + +## [5.15.2](https://github.com/mrcjkb/rustaceanvim/compare/v5.15.1...v5.15.2) (2024-11-25) + + +### Bug Fixes + +* various `:RustAnalyzer target` regressions ([#591](https://github.com/mrcjkb/rustaceanvim/issues/591)) ([4f62c30](https://github.com/mrcjkb/rustaceanvim/commit/4f62c30d80a52ea41a4c0d1f12195aa01c89c2eb)) + +## [5.15.1](https://github.com/mrcjkb/rustaceanvim/compare/v5.15.0...v5.15.1) (2024-11-25) + + +### Bug Fixes + +* expose `target` scommand in `:RustAnalyzer` command completion ([#589](https://github.com/mrcjkb/rustaceanvim/issues/589)) ([b4e35d5](https://github.com/mrcjkb/rustaceanvim/commit/b4e35d5b18b77f0304c2949941ad75644cb9ce23)) + +## [5.15.0](https://github.com/mrcjkb/rustaceanvim/compare/v5.14.1...v5.15.0) (2024-11-18) + + +### Features + +* **lsp:** pass `client_id` to `on_initialized` ([#584](https://github.com/mrcjkb/rustaceanvim/issues/584)) ([900c6c5](https://github.com/mrcjkb/rustaceanvim/commit/900c6c5214b0fcec9a71309d7b05186bbaa3fa48)) + +## [5.14.1](https://github.com/mrcjkb/rustaceanvim/compare/v5.14.0...v5.14.1) (2024-11-12) + + +### Bug Fixes + +* compatibility with nvim-nightly ([0b40190](https://github.com/mrcjkb/rustaceanvim/commit/0b401909394d15898e4a8fcf959f74cbcba5d3ca)) + +## [5.14.0](https://github.com/mrcjkb/rustaceanvim/compare/v5.13.2...v5.14.0) (2024-11-10) + + +### Features + +* **lsp:** allow overriding server command via `rust-analzyer.server.path` setting ([#567](https://github.com/mrcjkb/rustaceanvim/issues/567)) ([7a8665b](https://github.com/mrcjkb/rustaceanvim/commit/7a8665bdf891ec00277704fd4a5b719587ca9082)) + +## [5.13.2](https://github.com/mrcjkb/rustaceanvim/compare/v5.13.1...v5.13.2) (2024-11-09) + + +### Bug Fixes + +* work around bug in Nushell on Windows ([#564](https://github.com/mrcjkb/rustaceanvim/issues/564)) ([59f15ef](https://github.com/mrcjkb/rustaceanvim/commit/59f15efe7fcc6be5de57319764911849597f92a3)) + +## [5.13.1](https://github.com/mrcjkb/rustaceanvim/compare/v5.13.0...v5.13.1) (2024-10-28) + + +### Performance Improvements + +* optimize target_arch switching ([#548](https://github.com/mrcjkb/rustaceanvim/issues/548)) ([6c4c8d8](https://github.com/mrcjkb/rustaceanvim/commit/6c4c8d82db26b9deab655ca4f75f526652a0de8a)) + +## [5.13.0](https://github.com/mrcjkb/rustaceanvim/compare/v5.12.0...v5.13.0) (2024-10-17) + + +### Features + +* **lsp:** target architecture switching command for `RustAnalyzer` ([#541](https://github.com/mrcjkb/rustaceanvim/issues/541)) ([95715b2](https://github.com/mrcjkb/rustaceanvim/commit/95715b28c87b4cb3a8a38e063e2aa5cd3a8024d7)) + + +### Bug Fixes + +* remove corrupt file that breaks git clone on windows ([ccff140](https://github.com/mrcjkb/rustaceanvim/commit/ccff14065096c8978c431944f0f0db16db952c7b)) + +## [5.12.0](https://github.com/mrcjkb/rustaceanvim/compare/v5.11.0...v5.12.0) (2024-10-15) + + +### Features + +* **config:** health check reports for .vscode/settings.json ([#539](https://github.com/mrcjkb/rustaceanvim/issues/539)) ([cb31013](https://github.com/mrcjkb/rustaceanvim/commit/cb31013a983faec6339d3bf6aad782da8fc7e111)) + +## [5.11.0](https://github.com/mrcjkb/rustaceanvim/compare/v5.10.1...v5.11.0) (2024-10-01) + + +### Features + +* **lsp:** preserve cursor position for move_item command ([#532](https://github.com/mrcjkb/rustaceanvim/issues/532)) ([a07bb0d](https://github.com/mrcjkb/rustaceanvim/commit/a07bb0d256d1f9693ae8fb96dbcc5350b18f2978)) + +## [5.10.1](https://github.com/mrcjkb/rustaceanvim/compare/v5.10.0...v5.10.1) (2024-09-27) + + +### Bug Fixes + +* **windows:** remove empty file causing git clone to fail ([b7c8171](https://github.com/mrcjkb/rustaceanvim/commit/b7c8171b1a496e20a2906bf74d1a260f802932d3)) + +## [5.10.0](https://github.com/mrcjkb/rustaceanvim/compare/v5.9.0...v5.10.0) (2024-09-27) + + +### Features + +* add hint on how to configure/disable server status notifications ([711e25f](https://github.com/mrcjkb/rustaceanvim/commit/711e25fe11b6e72fbeda52d9d81b85a5aa3a81ab)) + +## [5.9.0](https://github.com/mrcjkb/rustaceanvim/compare/v5.8.1...v5.9.0) (2024-09-23) + + +### Features + +* **lsp:** only notify on server status error by default ([#519](https://github.com/mrcjkb/rustaceanvim/issues/519)) ([360ac5c](https://github.com/mrcjkb/rustaceanvim/commit/360ac5c80f299f282cbb1967bbfe5aa1e1c6e66e)) + +## [5.8.1](https://github.com/mrcjkb/rustaceanvim/compare/v5.8.0...v5.8.1) (2024-09-21) + + +### Bug Fixes + +* **lsp:** update deprecated API calls ([#514](https://github.com/mrcjkb/rustaceanvim/issues/514)) ([9a36905](https://github.com/mrcjkb/rustaceanvim/commit/9a369055aebd0411a11600f7cfd5c9b39c751eaa)) + +## [5.8.0](https://github.com/mrcjkb/rustaceanvim/compare/5.7.0...v5.8.0) (2024-09-20) + + +### Features + +* **lsp:** `` mapping for hover actions ([#510](https://github.com/mrcjkb/rustaceanvim/issues/510)) ([b52bbc4](https://github.com/mrcjkb/rustaceanvim/commit/b52bbc4bb0e50bf7da65b2695e1d602344877858)) + + +### Bug Fixes + +* remove luajit requirement ([#512](https://github.com/mrcjkb/rustaceanvim/issues/512)) ([9db87de](https://github.com/mrcjkb/rustaceanvim/commit/9db87deb7b00d64466b56afff645756530db1c03)) + +## [5.7.0] + +### Added + +- LSP: More information in unhealthy rust-analyzer notifications. + Thanks [@edevil](https://github.com/edevil)! + +## [5.6.0] - 2024-09-18 + +### Added + +- LSP: Notify if rust-analyzer is not healthy. ## Unreleased diff --git a/README.md b/README.md index 3f125923..235ab875 100644 --- a/README.md +++ b/README.md @@ -150,6 +150,14 @@ vim.keymap.set( end, { silent = true, buffer = bufnr } ) +vim.keymap.set( + "n", + "K", -- Override Neovim's built-in hover keymap with rustaceanvim's hover actions + function() + vim.cmd.RustLsp({'hover', 'actions'}) + end, + { silent = true, buffer = bufnr } +) ``` >[!TIP] @@ -403,6 +411,18 @@ vim.keymap.set( By default, this plugin replaces Neovim's built-in hover handler with hover actions, so you can also use `vim.lsp.buf.hover()`. + You can invoke a hover action by switching to the hover window and entering `` + on the respective line, or with a keymap for the `RustHoverAction` mapping, + which accepts a `` prefix as the (1-based) index of the hover action to invoke. + + For example, if you set the following keymap: + + ```lua + vim.keymap.set('n', 'a', 'RustHoverAction') + ``` + + you can invoke the third hover action with `3a`. + ![](https://github.com/mrcjkb/rustaceanvim/assets/12857160/c7b6c730-4439-47b0-9a75-7ea4e6831f7a) @@ -806,6 +826,7 @@ the `server.default_settings` will be used. [^2]: See [this example](https://github.com/rust-analyzer/rust-project.json-example/blob/master/.vscode/settings.json) and the rust-analyzer [configuration manual](https://rust-analyzer.github.io/manual.html#configuration). + Note that JSON5 is currently not supported by Neovim. Another option is to use `:h exrc`. @@ -878,6 +899,12 @@ See [`:h rustaceanvim.mason`](./doc/mason.txt) for details about troubleshooting mason.nvim and nvim-lspconfig issues, or configuring rustaceanvim to use a rust-analyzer installation that is managed by mason.nvim. +#### I am not seeing diagnostics in a standalone file + +rust-analyzer has limited support for standalone files. +Many diagnostics come from Cargo. If you're not in a Cargo project, +you won't see any Cargo diagnostics. + ## :link: Related Projects - [`rouge8/neotest-rust`](https://github.com/rouge8/neotest-rust) diff --git a/doc/mason.txt b/doc/mason.txt index a30f1510..a2c2359a 100644 --- a/doc/mason.txt +++ b/doc/mason.txt @@ -14,11 +14,15 @@ the `server.cmd` setting (see |rustaceanvim.config| and |RustaceanLspClientOpts| server = { cmd = function() local mason_registry = require('mason-registry') - local ra_binary = mason_registry.is_installed('rust-analyzer') - -- This may need to be tweaked, depending on the operating system. - and mason_registry.get_package('rust-analyzer'):get_install_path() .. "/rust-analyzer" - or "rust-analyzer" - return { ra_binary } -- You can add args to the list, such as '--log-file' + if mason_registry.is_installed('rust-analyzer') then + -- This may need to be tweaked depending on the operating system. + local ra = mason_registry.get_package('rust-analyzer') + local ra_filename = ra:get_receipt():get().links.bin['rust-analyzer'] + return { ('%s/%s'):format(ra:get_install_path(), ra_filename or 'rust-analyzer') } + else + -- global installation + return { 'rust-analyzer' } + end end, }, } diff --git a/doc/rustaceanvim.txt b/doc/rustaceanvim.txt index facf0e45..690e2419 100644 --- a/doc/rustaceanvim.txt +++ b/doc/rustaceanvim.txt @@ -24,6 +24,10 @@ Commands: ':RustAnalyzer stop' - Stop the LSP client. ':RustAnalyzer restart' - Restart the LSP client. ':RustAnalyzer reloadSettings' - Reload settings for the LSP client. + ':RustAnalyzer target ' - Set the target architecture for the LSP client. + The ':RustAnalyzer target' command can take a valid rustc target, + such as 'wasm32-unknown-unknown', or it can be left empty to set the LSP client + to use the default target architecture for the operating system. The ':RustLsp[!]' command is available after the LSP client has initialized. It accepts the following subcommands: @@ -152,35 +156,35 @@ rustaceanvim.tools.Opts *rustaceanvim.tools.Opts* Fields: ~ {executor?} (rustaceanvim.Executor|rustaceanvim.executor_alias) - The executor to use for runnables/debuggables + The executor to use for runnables/debuggables {test_executor?} (rustaceanvim.Executor|rustaceanvim.test_executor_alias) - The executor to use for runnables that are tests / testables + The executor to use for runnables that are tests / testables {crate_test_executor?} (rustaceanvim.Executor|rustaceanvim.test_executor_alias) - The executor to use for runnables that are crate test suites (--all-targets) + The executor to use for runnables that are crate test suites (--all-targets) {cargo_override?} (string) - Set this to override the 'cargo' command for runnables, debuggables (etc., e.g. to 'cross'). - If set, this takes precedence over 'enable_nextest'. + Set this to override the 'cargo' command for runnables, debuggables (etc., e.g. to 'cross'). + If set, this takes precedence over 'enable_nextest'. {enable_nextest?} (boolean) - Whether to enable nextest. If enabled, `cargo test` commands will be transformed to `cargo nextest run` commands. - Defaults to `true` if cargo-nextest is detected. Ignored if `cargo_override` is set. + Whether to enable nextest. If enabled, `cargo test` commands will be transformed to `cargo nextest run` commands. + Defaults to `true` if cargo-nextest is detected. Ignored if `cargo_override` is set. {enable_clippy?} (boolean) - Whether to enable clippy checks on save if a clippy installation is detected. - Default: `true` - {on_initialized?} (fun(health:rustaceanvim.RAInitializedStatus)) - Function that is invoked when the LSP server has finished initializing + Whether to enable clippy checks on save if a clippy installation is detected. + Default: `true` + {on_initialized?} (fun(health:rustaceanvim.RAInitializedStatus,client_id:integer)) + Function that is invoked when the LSP server has finished initializing {reload_workspace_from_cargo_toml?} (boolean) - Automatically call `RustReloadWorkspace` when writing to a Cargo.toml file - {hover_actions?} (rustaceanvim.hover-actions.Opts) Options for hover actions - {code_actions?} (rustaceanvim.code-action.Opts) Options for code actions + Automatically call `RustReloadWorkspace` when writing to a Cargo.toml file + {hover_actions?} (rustaceanvim.hover-actions.Opts) Options for hover actions + {code_actions?} (rustaceanvim.code-action.Opts) Options for code actions {float_win_config?} (rustaceanvim.FloatWinConfig) - Options applied to floating windows. - See |api-win_config|. + Options applied to floating windows. + See |api-win_config|. {create_graph?} (rustaceanvim.crate-graph.Opts) - Options for showing the crate graph based on graphviz and the dot + Options for showing the crate graph based on graphviz and the dot {open_url?} (fun(url:string):nil) - If set, overrides how to open URLs + If set, overrides how to open URLs {rustc?} (rustaceanvim.rustc.Opts) - Options for `rustc` + Options for `rustc` rustaceanvim.Executor *rustaceanvim.Executor* @@ -262,7 +266,7 @@ rustaceanvim.crate-graph.Opts *rustaceanvim.crate-graph.Opts* {enabled_graphviz_backends?} (string[]) Override the enabled graphviz backends list, used for input validation and autocompletion. {pipe?} (string) - Overide the pipe symbol in the shell command. + Override the pipe symbol in the shell command. Useful if using a shell that is not supported by this plugin. @@ -299,18 +303,16 @@ rustaceanvim.lsp.ClientOpts *rustaceanvim.lsp.ClientOpts* {load_vscode_settings?} (boolean) Whether to search (upward from the buffer) for rust-analyzer settings in .vscode/settings json. If found, loaded settings will override configured options. - Default: true - - See: ~ - |vim.lsp.ClientConfig| + Default: `true` + {status_notify_level?} (rustaceanvim.server.status_notify_level) + Server status warning level to notify at. + Default: 'error' -rustaceanvim.dap.Opts *rustaceanvim.dap.Opts* +rustaceanvim.server.status_notify_level*rustaceanvim.server.status_notify_level* - Fields: ~ - {autoload_configurations} (boolean) - Whether to autoload nvim-dap configurations when rust-analyzer has attached? - Default: `true`. + Type: ~ + "error"|"warning"|rustaceanvim.disable rustaceanvim.disable *rustaceanvim.disable* @@ -319,6 +321,30 @@ rustaceanvim.disable *rustaceanvim.disable* false +rustaceanvim.dap.Opts *rustaceanvim.dap.Opts* + + Fields: ~ + {autoload_configurations?} (boolean) + Whether to autoload nvim-dap configurations when rust-analyzer has attached? + Default: `true` + {adapter?} (rustaceanvim.dap.executable.Config|rustaceanvim.dap.server.Config|rustaceanvim.disable|fun():rustaceanvim.dap.executable.Config|rustaceanvim.dap.server.Config|rustaceanvim.disable) + Defaults to creating the `rt_lldb` adapter, which is a |rustaceanvim.dap.server.Config| + if `codelldb` is detected, and a |rustaceanvim.dap.executable.Config|` if `lldb` is detected. + Set to `false` to disable. + {configuration?} (rustaceanvim.dap.client.Config|rustaceanvim.disable|fun():rustaceanvim.dap.client.Config|rustaceanvim.disable) + Dap client configuration. Defaults to a function that looks for a `launch.json` file + or returns a |rustaceanvim.dap.executable.Config| that launches the `rt_lldb` adapter. + Set to `false` to disable. + {add_dynamic_library_paths?} (boolean|fun():boolean) + Accommodate dynamically-linked targets by passing library paths to lldb. + Default: `true`. + {auto_generate_source_map?} (fun():boolean|boolean) + Whether to auto-generate a source map for the standard library. + {load_rust_types?} (fun():boolean|boolean) + Whether to get Rust types via initCommands (rustlib/etc/lldb_commands, lldb only). + Default: `true`. + + rustaceanvim.dap.Command *rustaceanvim.dap.Command* Type: ~ @@ -366,7 +392,7 @@ rustaceanvim.dap.adapter.types.server *rustaceanvim.dap.adapter.types.server* *rustaceanvim.dap.client.Config* -rustaceanvim.dap.client.Config : Configuration +rustaceanvim.dap.client.Config : dap.Configuration Fields: ~ {type} (string) The dap adapter to use @@ -423,8 +449,9 @@ LSP configuration utility *rustaceanvim.config.server* rustaceanvim.LoadRASettingsOpts *rustaceanvim.LoadRASettingsOpts* Fields: ~ - {settings_file_pattern} (string|nil) File name or pattern to search for. Defaults to 'rust-analyzer.json' - {default_settings} (table|nil) Default settings to merge the loaded settings into + {settings_file_pattern} (string|nil) + (deprecated) File name or pattern to search for. Defaults to 'rust-analyzer.json' + {default_settings} (table|nil) Default settings to merge the loaded settings into. *server.load_rust_analyzer_settings* diff --git a/flake.lock b/flake.lock index 5dd678bb..c989f8d1 100644 --- a/flake.lock +++ b/flake.lock @@ -19,11 +19,11 @@ "flake-compat_2": { "flake": false, "locked": { - "lastModified": 1696426674, - "narHash": "sha256-kvjfFW7WAETZlt09AgDn1MrtKzP7t90Vf7vypd3OL1U=", + "lastModified": 1732722421, + "narHash": "sha256-HRJ/18p+WoXpWJkcdsk9St5ZiukCqSDgbOGFa8Okehg=", "owner": "edolstra", "repo": "flake-compat", - "rev": "0f9255e01c2351cc7d116c072cb317785dd33b33", + "rev": "9ed2ac151eada2306ca8c418ebd97807bb08f6ac", "type": "github" }, "original": { @@ -49,6 +49,22 @@ } }, "flake-compat_4": { + "flake": false, + "locked": { + "lastModified": 1732722421, + "narHash": "sha256-HRJ/18p+WoXpWJkcdsk9St5ZiukCqSDgbOGFa8Okehg=", + "owner": "edolstra", + "repo": "flake-compat", + "rev": "9ed2ac151eada2306ca8c418ebd97807bb08f6ac", + "type": "github" + }, + "original": { + "owner": "edolstra", + "repo": "flake-compat", + "type": "github" + } + }, + "flake-compat_5": { "flake": false, "locked": { "lastModified": 1696426674, @@ -64,7 +80,7 @@ "type": "github" } }, - "flake-compat_5": { + "flake-compat_6": { "flake": false, "locked": { "lastModified": 1696426674, @@ -80,7 +96,7 @@ "type": "github" } }, - "flake-compat_6": { + "flake-compat_7": { "flake": false, "locked": { "lastModified": 1696426674, @@ -101,11 +117,11 @@ "nixpkgs-lib": "nixpkgs-lib" }, "locked": { - "lastModified": 1725234343, - "narHash": "sha256-+ebgonl3NbiKD2UD0x4BszCZQ6sTfL4xioaM49o5B3Y=", + "lastModified": 1730504689, + "narHash": "sha256-hgmguH29K2fvs9szpq2r3pz2/8cJd2LPS+b4tfNFCwE=", "owner": "hercules-ci", "repo": "flake-parts", - "rev": "567b938d64d4b4112ee253b9274472dc3a346eb6", + "rev": "506278e768c2a08bec68eb62932193e341f55c90", "type": "github" }, "original": { @@ -137,11 +153,11 @@ "nixpkgs-lib": "nixpkgs-lib_3" }, "locked": { - "lastModified": 1725234343, - "narHash": "sha256-+ebgonl3NbiKD2UD0x4BszCZQ6sTfL4xioaM49o5B3Y=", + "lastModified": 1730504689, + "narHash": "sha256-hgmguH29K2fvs9szpq2r3pz2/8cJd2LPS+b4tfNFCwE=", "owner": "hercules-ci", "repo": "flake-parts", - "rev": "567b938d64d4b4112ee253b9274472dc3a346eb6", + "rev": "506278e768c2a08bec68eb62932193e341f55c90", "type": "github" }, "original": { @@ -159,11 +175,11 @@ ] }, "locked": { - "lastModified": 1725234343, - "narHash": "sha256-+ebgonl3NbiKD2UD0x4BszCZQ6sTfL4xioaM49o5B3Y=", + "lastModified": 1730504689, + "narHash": "sha256-hgmguH29K2fvs9szpq2r3pz2/8cJd2LPS+b4tfNFCwE=", "owner": "hercules-ci", "repo": "flake-parts", - "rev": "567b938d64d4b4112ee253b9274472dc3a346eb6", + "rev": "506278e768c2a08bec68eb62932193e341f55c90", "type": "github" }, "original": { @@ -199,11 +215,11 @@ "nixpkgs-lib": "nixpkgs-lib_4" }, "locked": { - "lastModified": 1696343447, - "narHash": "sha256-B2xAZKLkkeRFG5XcHHSXXcP7To9Xzr59KXeZiRf4vdQ=", + "lastModified": 1725234343, + "narHash": "sha256-+ebgonl3NbiKD2UD0x4BszCZQ6sTfL4xioaM49o5B3Y=", "owner": "hercules-ci", "repo": "flake-parts", - "rev": "c9afaba3dfa4085dbd2ccb38dfade5141e33d9d4", + "rev": "567b938d64d4b4112ee253b9274472dc3a346eb6", "type": "github" }, "original": { @@ -265,11 +281,11 @@ "nixpkgs-stable": "nixpkgs-stable_2" }, "locked": { - "lastModified": 1725513492, - "narHash": "sha256-tyMUA6NgJSvvQuzB7A1Sf8+0XCHyfSPRx/b00o6K0uo=", + "lastModified": 1732021966, + "narHash": "sha256-mnTbjpdqF0luOkou8ZFi2asa1N3AA2CchR/RqCNmsGE=", "owner": "cachix", "repo": "git-hooks.nix", - "rev": "7570de7b9b504cfe92025dd1be797bf546f66528", + "rev": "3308484d1a443fc5bc92012435d79e80458fe43c", "type": "github" }, "original": { @@ -294,11 +310,11 @@ ] }, "locked": { - "lastModified": 1725513492, - "narHash": "sha256-tyMUA6NgJSvvQuzB7A1Sf8+0XCHyfSPRx/b00o6K0uo=", + "lastModified": 1732021966, + "narHash": "sha256-mnTbjpdqF0luOkou8ZFi2asa1N3AA2CchR/RqCNmsGE=", "owner": "cachix", "repo": "git-hooks.nix", - "rev": "7570de7b9b504cfe92025dd1be797bf546f66528", + "rev": "3308484d1a443fc5bc92012435d79e80458fe43c", "type": "github" }, "original": { @@ -307,6 +323,31 @@ "type": "github" } }, + "git-hooks_4": { + "inputs": { + "flake-compat": "flake-compat_7", + "gitignore": "gitignore_5", + "nixpkgs": [ + "vimcats", + "nixpkgs" + ], + "nixpkgs-stable": "nixpkgs-stable_4" + }, + "locked": { + "lastModified": 1725438226, + "narHash": "sha256-lL4hQ+g2qiZ02WfidLkrujaT23c6E2Wm7S0ZQhSB8Jk=", + "owner": "mrcjkb", + "repo": "git-hooks.nix", + "rev": "ec0f4d97f48a1f32bd87804e2390f03999790ec0", + "type": "github" + }, + "original": { + "owner": "mrcjkb", + "ref": "clippy", + "repo": "git-hooks.nix", + "type": "github" + } + }, "gitignore": { "inputs": { "nixpkgs": [ @@ -395,6 +436,28 @@ "type": "github" } }, + "gitignore_5": { + "inputs": { + "nixpkgs": [ + "vimcats", + "git-hooks", + "nixpkgs" + ] + }, + "locked": { + "lastModified": 1709087332, + "narHash": "sha256-HG2cCnktfHsKV0s4XW83gU3F57gaTljL9KNSuG6bnQs=", + "owner": "hercules-ci", + "repo": "gitignore.nix", + "rev": "637db329424fd7e46cf4185293b9cc8c88c95394", + "type": "github" + }, + "original": { + "owner": "hercules-ci", + "repo": "gitignore.nix", + "type": "github" + } + }, "hercules-ci-effects": { "inputs": { "flake-parts": "flake-parts_5", @@ -405,11 +468,11 @@ ] }, "locked": { - "lastModified": 1724947644, - "narHash": "sha256-MHHrHasTngp7EYQOObHJ1a/IsRF+wodHqOckhH6uZbk=", + "lastModified": 1730903510, + "narHash": "sha256-mnynlrPeiW0nUQ8KGZHb3WyxAxA3Ye/BH8gMjdoKP6E=", "owner": "hercules-ci", "repo": "hercules-ci-effects", - "rev": "dba4367b9a9d9615456c430a6d6af716f6e84cef", + "rev": "b89ac4d66d618b915b1f0a408e2775fe3821d141", "type": "github" }, "original": { @@ -443,11 +506,11 @@ "nixpkgs": "nixpkgs_4" }, "locked": { - "lastModified": 1725686700, - "narHash": "sha256-wCjTrQhEcsU3Gb4wUzKZgJzD/Ydm+cwU+wzRUAhwua0=", + "lastModified": 1732944430, + "narHash": "sha256-RxJyXmeLMxwlkkDynaHOy/rrYNuIK61R/nbzoUH2xlM=", "owner": "nvim-neorocks", "repo": "neorocks", - "rev": "b4ecf2fc5d258e2d8754d573785209098877fa93", + "rev": "f7fe2c43b5bf0c8840963d96dda1fe65e963ade8", "type": "github" }, "original": { @@ -466,11 +529,11 @@ "nixpkgs": "nixpkgs_3" }, "locked": { - "lastModified": 1725621813, - "narHash": "sha256-PULZxwVju7sX7YTDiu2Gr15htFEEoMJ95ZZ6FGj+qa0=", + "lastModified": 1732925246, + "narHash": "sha256-MjqyzOEa6s9OGO8SMh5C2kqo57+iSNsgDbzPBbpi4QU=", "owner": "nix-community", "repo": "neovim-nightly-overlay", - "rev": "f751e2e0c00a9393410361b205f42658611ae89b", + "rev": "7461a0b228bb48bb02af086f8b9ee9a83583120b", "type": "github" }, "original": { @@ -482,11 +545,11 @@ "neovim-src": { "flake": false, "locked": { - "lastModified": 1725578611, - "narHash": "sha256-sRezYqvmEjS/TIumXiwIN+ttuEPhNNtDnUY7wCpSCAs=", + "lastModified": 1732903628, + "narHash": "sha256-JF8zmoLdqmbKCSS5Smf/Yj0jEl5f+qKhSubhPo/BvUM=", "owner": "neovim", "repo": "neovim", - "rev": "9570ad24f52442d75d677412cfe2fa83a7ff7a88", + "rev": "2833925cfc688786759d6a980a1ad62b62d20570", "type": "github" }, "original": { @@ -513,14 +576,14 @@ }, "nixpkgs-lib": { "locked": { - "lastModified": 1725233747, - "narHash": "sha256-Ss8QWLXdr2JCBPcYChJhz4xJm+h/xjl4G0c0XlP6a74=", + "lastModified": 1730504152, + "narHash": "sha256-lXvH/vOfb4aGYyvFmZK/HlsNsr/0CVWlwYvo2rxJk3s=", "type": "tarball", - "url": "https://github.com/NixOS/nixpkgs/archive/356624c12086a18f2ea2825fed34523d60ccc4e3.tar.gz" + "url": "https://github.com/NixOS/nixpkgs/archive/cc2f28000298e1269cea6612cd06ec9979dd5d7f.tar.gz" }, "original": { "type": "tarball", - "url": "https://github.com/NixOS/nixpkgs/archive/356624c12086a18f2ea2825fed34523d60ccc4e3.tar.gz" + "url": "https://github.com/NixOS/nixpkgs/archive/cc2f28000298e1269cea6612cd06ec9979dd5d7f.tar.gz" } }, "nixpkgs-lib_2": { @@ -536,6 +599,18 @@ } }, "nixpkgs-lib_3": { + "locked": { + "lastModified": 1730504152, + "narHash": "sha256-lXvH/vOfb4aGYyvFmZK/HlsNsr/0CVWlwYvo2rxJk3s=", + "type": "tarball", + "url": "https://github.com/NixOS/nixpkgs/archive/cc2f28000298e1269cea6612cd06ec9979dd5d7f.tar.gz" + }, + "original": { + "type": "tarball", + "url": "https://github.com/NixOS/nixpkgs/archive/cc2f28000298e1269cea6612cd06ec9979dd5d7f.tar.gz" + } + }, + "nixpkgs-lib_4": { "locked": { "lastModified": 1725233747, "narHash": "sha256-Ss8QWLXdr2JCBPcYChJhz4xJm+h/xjl4G0c0XlP6a74=", @@ -547,31 +622,29 @@ "url": "https://github.com/NixOS/nixpkgs/archive/356624c12086a18f2ea2825fed34523d60ccc4e3.tar.gz" } }, - "nixpkgs-lib_4": { + "nixpkgs-stable": { "locked": { - "dir": "lib", - "lastModified": 1696019113, - "narHash": "sha256-X3+DKYWJm93DRSdC5M6K5hLqzSya9BjibtBsuARoPco=", + "lastModified": 1720386169, + "narHash": "sha256-NGKVY4PjzwAa4upkGtAMz1npHGoRzWotlSnVlqI40mo=", "owner": "NixOS", "repo": "nixpkgs", - "rev": "f5892ddac112a1e9b3612c39af1b72987ee5783a", + "rev": "194846768975b7ad2c4988bdb82572c00222c0d7", "type": "github" }, "original": { - "dir": "lib", "owner": "NixOS", - "ref": "nixos-unstable", + "ref": "nixos-24.05", "repo": "nixpkgs", "type": "github" } }, - "nixpkgs-stable": { + "nixpkgs-stable_2": { "locked": { - "lastModified": 1720386169, - "narHash": "sha256-NGKVY4PjzwAa4upkGtAMz1npHGoRzWotlSnVlqI40mo=", + "lastModified": 1730741070, + "narHash": "sha256-edm8WG19kWozJ/GqyYx2VjW99EdhjKwbY3ZwdlPAAlo=", "owner": "NixOS", "repo": "nixpkgs", - "rev": "194846768975b7ad2c4988bdb82572c00222c0d7", + "rev": "d063c1dd113c91ab27959ba540c0d9753409edf3", "type": "github" }, "original": { @@ -581,13 +654,13 @@ "type": "github" } }, - "nixpkgs-stable_2": { + "nixpkgs-stable_3": { "locked": { - "lastModified": 1720386169, - "narHash": "sha256-NGKVY4PjzwAa4upkGtAMz1npHGoRzWotlSnVlqI40mo=", + "lastModified": 1730741070, + "narHash": "sha256-edm8WG19kWozJ/GqyYx2VjW99EdhjKwbY3ZwdlPAAlo=", "owner": "NixOS", "repo": "nixpkgs", - "rev": "194846768975b7ad2c4988bdb82572c00222c0d7", + "rev": "d063c1dd113c91ab27959ba540c0d9753409edf3", "type": "github" }, "original": { @@ -597,7 +670,7 @@ "type": "github" } }, - "nixpkgs-stable_3": { + "nixpkgs-stable_4": { "locked": { "lastModified": 1720386169, "narHash": "sha256-NGKVY4PjzwAa4upkGtAMz1npHGoRzWotlSnVlqI40mo=", @@ -615,11 +688,11 @@ }, "nixpkgs_2": { "locked": { - "lastModified": 1719082008, - "narHash": "sha256-jHJSUH619zBQ6WdC21fFAlDxHErKVDJ5fpN0Hgx4sjs=", + "lastModified": 1730768919, + "narHash": "sha256-8AKquNnnSaJRXZxc5YmF/WfmxiHX6MMZZasRP6RRQkE=", "owner": "NixOS", "repo": "nixpkgs", - "rev": "9693852a2070b398ee123a329e68f0dab5526681", + "rev": "a04d33c0c3f1a59a2c1cb0c6e34cd24500e5a1dc", "type": "github" }, "original": { @@ -631,11 +704,11 @@ }, "nixpkgs_3": { "locked": { - "lastModified": 1725503534, - "narHash": "sha256-hd1eRyPtTkRnAPYpnEfzugQwAifVYMmOR3z+MTTSw+g=", + "lastModified": 1732617236, + "narHash": "sha256-PYkz6U0bSEaEB1al7O1XsqVNeSNS+s3NVclJw7YC43w=", "owner": "NixOS", "repo": "nixpkgs", - "rev": "fcb54ddcc974cff59bdfb7c1ac9e080299763d2d", + "rev": "af51545ec9a44eadf3fe3547610a5cdd882bc34e", "type": "github" }, "original": { @@ -647,11 +720,11 @@ }, "nixpkgs_4": { "locked": { - "lastModified": 1725534445, - "narHash": "sha256-Yd0FK9SkWy+ZPuNqUgmVPXokxDgMJoGuNpMEtkfcf84=", + "lastModified": 1732780316, + "narHash": "sha256-NskLIz0ue4Uqbza+1+8UGHuPVr8DrUiLfZu5VS4VQxw=", "owner": "nixos", "repo": "nixpkgs", - "rev": "9bb1e7571aadf31ddb4af77fc64b2d59580f9a39", + "rev": "226216574ada4c3ecefcbbec41f39ce4655f78ef", "type": "github" }, "original": { @@ -663,11 +736,11 @@ }, "nixpkgs_5": { "locked": { - "lastModified": 1725534445, - "narHash": "sha256-Yd0FK9SkWy+ZPuNqUgmVPXokxDgMJoGuNpMEtkfcf84=", + "lastModified": 1732937961, + "narHash": "sha256-B5pYT+IVaqcrfOekkwKvx/iToDnuQWzc2oyDxzzBDc4=", "owner": "nixos", "repo": "nixpkgs", - "rev": "9bb1e7571aadf31ddb4af77fc64b2d59580f9a39", + "rev": "4703b8d2c708e13a8cab03d865f90973536dcdf5", "type": "github" }, "original": { @@ -679,11 +752,11 @@ }, "nixpkgs_6": { "locked": { - "lastModified": 1719082008, - "narHash": "sha256-jHJSUH619zBQ6WdC21fFAlDxHErKVDJ5fpN0Hgx4sjs=", + "lastModified": 1730768919, + "narHash": "sha256-8AKquNnnSaJRXZxc5YmF/WfmxiHX6MMZZasRP6RRQkE=", "owner": "NixOS", "repo": "nixpkgs", - "rev": "9693852a2070b398ee123a329e68f0dab5526681", + "rev": "a04d33c0c3f1a59a2c1cb0c6e34cd24500e5a1dc", "type": "github" }, "original": { @@ -695,11 +768,11 @@ }, "nixpkgs_7": { "locked": { - "lastModified": 1697379843, - "narHash": "sha256-RcnGuJgC2K/UpTy+d32piEoBXq2M+nVFzM3ah/ZdJzg=", + "lastModified": 1725910328, + "narHash": "sha256-n9pCtzGZ0httmTwMuEbi5E78UQ4ZbQMr1pzi5N0LAG8=", "owner": "NixOS", "repo": "nixpkgs", - "rev": "12bdeb01ff9e2d3917e6a44037ed7df6e6c3df9d", + "rev": "5775c2583f1801df7b790bf7f7d710a19bac66f4", "type": "github" }, "original": { @@ -717,11 +790,11 @@ "nixpkgs-stable": "nixpkgs-stable_3" }, "locked": { - "lastModified": 1725513492, - "narHash": "sha256-tyMUA6NgJSvvQuzB7A1Sf8+0XCHyfSPRx/b00o6K0uo=", + "lastModified": 1732021966, + "narHash": "sha256-mnTbjpdqF0luOkou8ZFi2asa1N3AA2CchR/RqCNmsGE=", "owner": "cachix", "repo": "pre-commit-hooks.nix", - "rev": "7570de7b9b504cfe92025dd1be797bf546f66528", + "rev": "3308484d1a443fc5bc92012435d79e80458fe43c", "type": "github" }, "original": { @@ -743,14 +816,15 @@ "vimcats": { "inputs": { "flake-parts": "flake-parts_6", + "git-hooks": "git-hooks_4", "nixpkgs": "nixpkgs_7" }, "locked": { - "lastModified": 1725500280, - "narHash": "sha256-Gk3wy8OvvW26bd+wVRjuHPch6DJOXJjW2TK7Jbe0hrw=", + "lastModified": 1726045206, + "narHash": "sha256-/ekQF2pE/juZ7AVge9bqodQLx4n0uaV4j2taD5U78R0=", "owner": "mrcjkb", "repo": "vimcats", - "rev": "eeba18f1205388f403b1b3cc5a71747e0ca0a2ba", + "rev": "fd213ad22ffbfff87899c872752a1c92814c93fa", "type": "github" }, "original": { diff --git a/ftplugin/rust.lua b/ftplugin/rust.lua index ecf7852d..5b8a92dc 100644 --- a/ftplugin/rust.lua +++ b/ftplugin/rust.lua @@ -1,5 +1,6 @@ ---@type rustaceanvim.Config local config = require('rustaceanvim.config.internal') +local compat = require('rustaceanvim.compat') if not vim.g.loaded_rustaceanvim then require('rustaceanvim.config.check').check_for_lspconfig_conflict(vim.schedule_wrap(function(warn) @@ -22,7 +23,7 @@ if not vim.g.loaded_rustaceanvim then vim.lsp.commands['rust-analyzer.gotoLocation'] = function(command, ctx) local client = vim.lsp.get_client_by_id(ctx.client_id) if client then - vim.lsp.util.jump_to_location(command.arguments[1], client.offset_encoding) + compat.show_document(command.arguments[1], client.offset_encoding or 'utf-8') end end @@ -37,6 +38,7 @@ if not vim.g.loaded_rustaceanvim then local cached_commands = require('rustaceanvim.cached_commands') cached_commands.set_last_debuggable(args) local rt_dap = require('rustaceanvim.dap') + ---@diagnostic disable-next-line: invisible rt_dap.start(args) end diff --git a/lua/rustaceanvim/cached_commands.lua b/lua/rustaceanvim/cached_commands.lua index 79121676..f9f54518 100644 --- a/lua/rustaceanvim/cached_commands.lua +++ b/lua/rustaceanvim/cached_commands.lua @@ -43,6 +43,7 @@ M.execute_last_debuggable = function(executableArgsOverride) args.executableArgs = executableArgsOverride end local rt_dap = require('rustaceanvim.dap') + ---@diagnostic disable-next-line: invisible rt_dap.start(args) else local debuggables = require('rustaceanvim.commands.debuggables') diff --git a/lua/rustaceanvim/commands/code_action_group.lua b/lua/rustaceanvim/commands/code_action_group.lua index 85180923..6603958c 100644 --- a/lua/rustaceanvim/commands/code_action_group.lua +++ b/lua/rustaceanvim/commands/code_action_group.lua @@ -1,5 +1,6 @@ local ui = require('rustaceanvim.ui') local config = require('rustaceanvim.config.internal') +local compat = require('rustaceanvim.compat') local M = {} ---@class rustaceanvim.RACodeAction @@ -15,11 +16,11 @@ local M = {} ---@field arguments? any[] ---@param action rustaceanvim.RACodeAction | rustaceanvim.RACommand ----@param client lsp.Client +---@param client vim.lsp.Client ---@param ctx table function M.apply_action(action, client, ctx) if action.edit then - vim.lsp.util.apply_workspace_edit(action.edit, client.offset_encoding) + vim.lsp.util.apply_workspace_edit(action.edit, client.offset_encoding or 'utf-8') end if action.command then local command = type(action.command) == 'table' and action.command or action @@ -47,7 +48,7 @@ function M.on_user_choice(action_tuple, ctx) return end if not action.edit and type(code_action_provider) == 'table' and code_action_provider.resolveProvider then - client.request('codeAction/resolve', action, function(err, resolved_action) + compat.client_request(client, 'codeAction/resolve', action, function(err, resolved_action) ---@cast resolved_action rustaceanvim.RACodeAction|rustaceanvim.RACommand if err then vim.notify(err.code .. ': ' .. err.message, vim.log.levels.ERROR) @@ -376,8 +377,14 @@ M.state = { M.code_action_group = function() local context = {} - context.diagnostics = vim.lsp.diagnostic.get_line_diagnostics() - local params = vim.lsp.util.make_range_params() + context.diagnostics = require('rustaceanvim.compat').get_line_diagnostics() + local ra = require('rustaceanvim.rust_analyzer') + local clients = ra.get_active_rustaceanvim_clients(0) + if #clients == 0 then + return + end + local params = vim.lsp.util.make_range_params(0, clients[1].offset_encoding or 'utf-8') + ---@diagnostic disable-next-line: inject-field params.context = context vim.lsp.buf_request_all(0, 'textDocument/codeAction', params, function(results) diff --git a/lua/rustaceanvim/commands/debuggables.lua b/lua/rustaceanvim/commands/debuggables.lua index acb7b893..4d6c6b58 100644 --- a/lua/rustaceanvim/commands/debuggables.lua +++ b/lua/rustaceanvim/commands/debuggables.lua @@ -86,6 +86,7 @@ local function dap_run(args) local rt_dap = require('rustaceanvim.dap') local ok, dap = pcall(require, 'dap') if ok then + ---@diagnostic disable-next-line: invisible rt_dap.start(args, true, dap.run) local cached_commands = require('rustaceanvim.cached_commands') cached_commands.set_last_debuggable(args) @@ -131,6 +132,7 @@ local function add_debuggables_to_nvim_dap(debuggables) if not debuggable then return end + ---@diagnostic disable-next-line: invisible rt_dap.start(debuggable.args, false, function(configuration) local name = 'Cargo: ' .. build_label(debuggable.args) if not _dap_configuration_added[name] then diff --git a/lua/rustaceanvim/commands/expand_macro.lua b/lua/rustaceanvim/commands/expand_macro.lua index 26c21d67..cbb7536a 100644 --- a/lua/rustaceanvim/commands/expand_macro.lua +++ b/lua/rustaceanvim/commands/expand_macro.lua @@ -2,11 +2,6 @@ local ui = require('rustaceanvim.ui') local M = {} ----@return lsp_position_params -local function get_params() - return vim.lsp.util.make_position_params() -end - ---@type integer | nil local latest_buf_id = nil @@ -70,11 +65,15 @@ local function handler(_, result) ui.resize(true, '-25') end -local rl = require('rustaceanvim.rust_analyzer') - --- Sends the request to rust-analyzer to expand the macro under the cursor function M.expand_macro() - rl.buf_request(0, 'rust-analyzer/expandMacro', get_params(), handler) + local ra = require('rustaceanvim.rust_analyzer') + local clients = ra.get_active_rustaceanvim_clients(0) + if #clients == 0 then + return + end + local params = vim.lsp.util.make_position_params(0, clients[1].offset_encoding or 'utf-8') + ra.buf_request(0, 'rust-analyzer/expandMacro', params, handler) end return M.expand_macro diff --git a/lua/rustaceanvim/commands/external_docs.lua b/lua/rustaceanvim/commands/external_docs.lua index 0f644a61..41a43404 100644 --- a/lua/rustaceanvim/commands/external_docs.lua +++ b/lua/rustaceanvim/commands/external_docs.lua @@ -1,14 +1,22 @@ local M = {} -local rl = require('rustaceanvim.rust_analyzer') - function M.open_external_docs() - rl.buf_request(0, 'experimental/externalDocs', vim.lsp.util.make_position_params(), function(_, url) - if url then - local config = require('rustaceanvim.config.internal') - config.tools.open_url(url) + local ra = require('rustaceanvim.rust_analyzer') + local clients = ra.get_active_rustaceanvim_clients(0) + if #clients == 0 then + return + end + ra.buf_request( + 0, + 'experimental/externalDocs', + vim.lsp.util.make_position_params(0, clients[1].offset_encoding or 'utf-8'), + function(_, url) + if url then + local config = require('rustaceanvim.config.internal') + config.tools.open_url(url) + end end - end) + ) end return M.open_external_docs diff --git a/lua/rustaceanvim/commands/hover_range.lua b/lua/rustaceanvim/commands/hover_range.lua index 75d8a876..7554035b 100644 --- a/lua/rustaceanvim/commands/hover_range.lua +++ b/lua/rustaceanvim/commands/hover_range.lua @@ -42,18 +42,17 @@ local function get_visual_selected_range() return make_lsp_position(row1, math.min(col1, col2), row1, math.max(col1, col2)) end ----@return lsp_range_params -local function get_opts() - local params = vim.lsp.util.make_range_params() +function M.hover_range() + local ra = require('rustaceanvim.rust_analyzer') + local clients = ra.get_active_rustaceanvim_clients(0) + if #clients == 0 then + return + end + local params = vim.lsp.util.make_range_params(0, clients[1].offset_encoding or 'utf-8') + ---@diagnostic disable-next-line: inject-field params.position = get_visual_selected_range() params.range = nil - return params -end - -local rl = require('rustaceanvim.rust_analyzer') - -function M.hover_range() - rl.buf_request(0, 'textDocument/hover', get_opts()) + ra.buf_request(0, 'textDocument/hover', params) end return M.hover_range diff --git a/lua/rustaceanvim/commands/init.lua b/lua/rustaceanvim/commands/init.lua index 7ec07ecb..93788964 100644 --- a/lua/rustaceanvim/commands/init.lua +++ b/lua/rustaceanvim/commands/init.lua @@ -159,9 +159,13 @@ local rustlsp_command_tbl = { }, joinLines = { impl = function(_, opts) + local cmds = require('rustaceanvim.commands.join_lines') ---@cast opts vim.api.keyset.user_command - local visual_mode = opts.range and opts.range ~= 0 or false - require('rustaceanvim.commands.join_lines')(visual_mode) + if opts.range and opts.range ~= 0 then + cmds.join_lines_visual() + else + cmds.join_lines() + end end, }, moveItem = { @@ -171,9 +175,9 @@ local rustlsp_command_tbl = { return end if args[1] == 'down' then - require('rustaceanvim.commands.move_item')() + require('rustaceanvim.commands.move_item')('Down') elseif args[1] == 'up' then - require('rustaceanvim.commands.move_item')(true) + require('rustaceanvim.commands.move_item')('Up') else vim.notify( 'moveItem: unexpected argument: ' .. vim.inspect(args) .. " expected 'up' or 'down'", @@ -203,9 +207,13 @@ local rustlsp_command_tbl = { ssr = { impl = function(args, opts) ---@cast opts vim.api.keyset.user_command - local visual_mode = opts.range and opts.range > 0 or false local query = args and #args > 0 and table.concat(args, ' ') or nil - require('rustaceanvim.commands.ssr')(query, visual_mode) + local cmds = require('rustaceanvim.commands.ssr') + if opts.range and opts.range > 0 then + cmds.ssr_visual(query) + else + cmds.ssr(query) + end end, }, reloadWorkspace = { diff --git a/lua/rustaceanvim/commands/join_lines.lua b/lua/rustaceanvim/commands/join_lines.lua index 89f42181..6cb8ab4c 100644 --- a/lua/rustaceanvim/commands/join_lines.lua +++ b/lua/rustaceanvim/commands/join_lines.lua @@ -1,30 +1,42 @@ local M = {} ----@alias lsp_join_lines_params { textDocument: lsp_text_document, ranges: lsp_range[] } - ----@param visual_mode boolean ----@return lsp_join_lines_params -local function get_params(visual_mode) - local params = visual_mode and vim.lsp.util.make_given_range_params() or vim.lsp.util.make_range_params() +---@param params { textDocument: lsp_text_document, range: lsp_range } +---@return { textDocument: lsp_text_document, ranges: lsp_range[] } +local function modify_params(params) local range = params.range - params.range = nil + ---@diagnostic disable-next-line: inject-field params.ranges = { range } - + ---@diagnostic disable-next-line: return-type-mismatch return params end local function handler(_, result, ctx) - vim.lsp.util.apply_text_edits(result, ctx.bufnr, vim.lsp.get_client_by_id(ctx.client_id).offset_encoding) + vim.lsp.util.apply_text_edits(result, ctx.bufnr, vim.lsp.get_client_by_id(ctx.client_id).offset_encoding or 'utf-8') end -local rl = require('rustaceanvim.rust_analyzer') +local ra = require('rustaceanvim.rust_analyzer') + +--- Sends the request to rust-analyzer to get the TextEdits to join the lines +--- under the cursor and applies them (for use in visual mode) +function M.join_lines_visual() + local clients = ra.get_active_rustaceanvim_clients(0) + if #clients == 0 then + return + end + local params = modify_params(vim.lsp.util.make_given_range_params(nil, nil, 0, clients[1].offset_encoding or 'utf-8')) + ra.buf_request(0, 'experimental/joinLines', params, handler) +end --- Sends the request to rust-analyzer to get the TextEdits to join the lines --- under the cursor and applies them ----@param visual_mode boolean -function M.join_lines(visual_mode) - rl.buf_request(0, 'experimental/joinLines', get_params(visual_mode), handler) +function M.join_lines() + local clients = ra.get_active_rustaceanvim_clients(0) + if #clients == 0 then + return + end + local params = modify_params(vim.lsp.util.make_range_params(0, clients[1].offset_encoding or 'utf-8')) + ra.buf_request(0, 'experimental/joinLines', params, handler) end -return M.join_lines +return M diff --git a/lua/rustaceanvim/commands/move_item.lua b/lua/rustaceanvim/commands/move_item.lua index 524ef8e5..8a44931d 100644 --- a/lua/rustaceanvim/commands/move_item.lua +++ b/lua/rustaceanvim/commands/move_item.lua @@ -2,31 +2,65 @@ local M = {} ---@alias lsp_move_items_params { textDocument: lsp_text_document, range: lsp_range, direction: 'Up' | 'Down' } ----@param up boolean ----@return lsp_move_items_params -local function get_params(up) - local direction = up and 'Up' or 'Down' - local params = vim.lsp.util.make_range_params() - params.direction = direction +---@param prev_text_edit rustaceanvim.lsp.TextEdit +---@param text_edit rustaceanvim.lsp.TextEdit +local function text_edit_line_range_diff(prev_text_edit, text_edit) + return math.max(0, text_edit.range.start.line - prev_text_edit.range['end'].line - 1) + - (prev_text_edit.range.start.line == text_edit.range.start.line and 1 or 0) +end - return params +---@param text_edits rustaceanvim.lsp.TextEdit[] +local function extract_cursor_position(text_edits) + local cursor = { text_edits[1].range.start.line } + local prev_text_edit + for _, text_edit in ipairs(text_edits) do + if text_edit.newText and text_edit.insertTextFormat == 2 and not cursor[2] then + cursor[1] = cursor[1] + (prev_text_edit and text_edit_line_range_diff(prev_text_edit, text_edit) or 0) + local snippet_pos_start = string.find(text_edit.newText, '%$0') + local lines = vim.split(string.sub(text_edit.newText, 1, snippet_pos_start), '\n') + local line_count = #lines + cursor[1] = cursor[1] + line_count + if snippet_pos_start then + local start_offset = line_count == 1 and text_edit.range.start.character or 0 + local last_line_length = #lines[line_count] - 1 + cursor[2] = start_offset + last_line_length + end + end + prev_text_edit = text_edit + end + return cursor end -- move it baby -local function handler(_, result, ctx) - if result == nil or #result == 0 then +---@param text_edits? rustaceanvim.lsp.TextEdit[] +---@param ctx lsp.HandlerContext +local function handler(_, text_edits, ctx) + if text_edits == nil or #text_edits == 0 then return end + local cursor = extract_cursor_position(text_edits) local overrides = require('rustaceanvim.overrides') - overrides.snippet_text_edits_to_text_edits(result) - vim.lsp.util.apply_text_edits(result, ctx.bufnr, vim.lsp.get_client_by_id(ctx.client_id).offset_encoding) + overrides.snippet_text_edits_to_text_edits(text_edits) + vim.lsp.util.apply_text_edits( + text_edits, + ctx.bufnr, + vim.lsp.get_client_by_id(ctx.client_id).offset_encoding or 'utf-8' + ) + vim.api.nvim_win_set_cursor(0, cursor) end -local rl = require('rustaceanvim.rust_analyzer') - -- Sends the request to rust-analyzer to move the item and handle the response -function M.move_item(up) - rl.buf_request(0, 'experimental/moveItem', get_params(up or false), handler) +---@param direction 'Up' | 'Down' +function M.move_item(direction) + local ra = require('rustaceanvim.rust_analyzer') + local clients = ra.get_active_rustaceanvim_clients(0) + if #clients == 0 then + return + end + local params = vim.lsp.util.make_range_params(0, clients[1].offset_encoding or 'utf-8') + ---@diagnostic disable-next-line: inject-field + params.direction = direction + ra.buf_request(0, 'experimental/moveItem', params, handler) end return M.move_item diff --git a/lua/rustaceanvim/commands/open_cargo_toml.lua b/lua/rustaceanvim/commands/open_cargo_toml.lua index 20b0cff2..817fd878 100644 --- a/lua/rustaceanvim/commands/open_cargo_toml.lua +++ b/lua/rustaceanvim/commands/open_cargo_toml.lua @@ -1,5 +1,7 @@ local M = {} +local compat = require('rustaceanvim.compat') + local function get_params() return { textDocument = vim.lsp.util.make_text_document_params(0), @@ -13,7 +15,7 @@ local function handler(_, result, ctx) local client = vim.lsp.get_client_by_id(ctx.client_id) if client then - vim.lsp.util.jump_to_location(result, client.offset_encoding) + compat.show_document(result, client.offset_encoding or 'utf-8') end end diff --git a/lua/rustaceanvim/commands/parent_module.lua b/lua/rustaceanvim/commands/parent_module.lua index 616da5f6..56127253 100644 --- a/lua/rustaceanvim/commands/parent_module.lua +++ b/lua/rustaceanvim/commands/parent_module.lua @@ -1,10 +1,6 @@ local M = {} -local rl = require('rustaceanvim.rust_analyzer') - -local function get_params() - return vim.lsp.util.make_position_params(0, nil) -end +local compat = require('rustaceanvim.compat') local function handler(_, result, ctx) if result == nil or vim.tbl_isempty(result) then @@ -20,13 +16,19 @@ local function handler(_, result, ctx) local client = vim.lsp.get_client_by_id(ctx.client_id) if client then - vim.lsp.util.jump_to_location(location, client.offset_encoding) + compat.show_document(location, client.offset_encoding or 'utf-8') end end --- Sends the request to rust-analyzer to get the parent modules location and open it function M.parent_module() - rl.buf_request(0, 'experimental/parentModule', get_params(), handler) + local ra = require('rustaceanvim.rust_analyzer') + local clients = ra.get_active_rustaceanvim_clients(0) + if #clients == 0 then + return + end + local params = vim.lsp.util.make_position_params(0, clients[1].offset_encoding or 'utf-8') + ra.buf_request(0, 'experimental/parentModule', params, handler) end return M.parent_module diff --git a/lua/rustaceanvim/commands/rustc_unpretty.lua b/lua/rustaceanvim/commands/rustc_unpretty.lua index 1858937f..73b51c9e 100644 --- a/lua/rustaceanvim/commands/rustc_unpretty.lua +++ b/lua/rustaceanvim/commands/rustc_unpretty.lua @@ -51,6 +51,7 @@ local function get_vim_range(range, buf) -- Use the value of the last col of the previous row instead. erow = erow - 1 if not buf or buf == 0 then + ---@diagnostic disable-next-line: assign-type-mismatch ecol = vim.fn.col { erow, '$' } - 1 else ecol = #vim.api.nvim_buf_get_lines(buf, erow - 1, erow, false)[1] diff --git a/lua/rustaceanvim/commands/ssr.lua b/lua/rustaceanvim/commands/ssr.lua index 66b7026e..4eee1085 100644 --- a/lua/rustaceanvim/commands/ssr.lua +++ b/lua/rustaceanvim/commands/ssr.lua @@ -1,14 +1,14 @@ local M = {} ----@param query string ----@param visual_mode boolean -local function get_opts(query, visual_mode) - local opts = vim.lsp.util.make_position_params() - local range = (visual_mode and vim.lsp.util.make_given_range_params() or vim.lsp.util.make_range_params()).range - opts.query = query - opts.parseOnly = false - opts.selections = { range } - return opts +local ra = require('rustaceanvim.rust_analyzer') + +---@params table +---@query string +---@param range table +local function modify_params(params, query, range) + params.query = query + params.parseOnly = false + params.selections = { range } end local function handler(err, result, ctx) @@ -19,23 +19,40 @@ local function handler(err, result, ctx) local client = vim.lsp.get_client_by_id(ctx.client_id) if client then - vim.lsp.util.apply_workspace_edit(result, client.offset_encoding) + vim.lsp.util.apply_workspace_edit(result, client.offset_encoding or 'utf-8') end end -local rl = require('rustaceanvim.rust_analyzer') - ----@param query string | nil ----@param visual_mode boolean -function M.ssr(query, visual_mode) +---@param query? string +---@param make_range_params fun(bufnr: integer, offset_encoding: string):{ range: table } +local function ssr(query, make_range_params) + local clients = ra.get_active_rustaceanvim_clients(0) + if #clients == 0 then + return + end + local params = vim.lsp.util.make_position_params(0, clients[1].offset_encoding or 'utf-8') + local range = make_range_params(0, clients[1].offset_encoding or 'utf-8').range if not query then vim.ui.input({ prompt = 'Enter query: ' }, function(input) query = input end) end + modify_params(params, query, range) if query then - rl.buf_request(0, 'experimental/ssr', get_opts(query, visual_mode), handler) + ra.buf_request(0, 'experimental/ssr', params, handler) end end -return M.ssr +---@param query? string +M.ssr = function(query) + ssr(query, vim.lsp.util.make_range_params) +end + +---@param query string | nil +function M.ssr_visual(query) + ssr(query, function(winnr, offset_encoding) + return vim.lsp.util.make_given_range_params(nil, nil, winnr, offset_encoding or 'utf-8') + end) +end + +return M diff --git a/lua/rustaceanvim/commands/syntax_tree.lua b/lua/rustaceanvim/commands/syntax_tree.lua index ba89594d..51a21754 100644 --- a/lua/rustaceanvim/commands/syntax_tree.lua +++ b/lua/rustaceanvim/commands/syntax_tree.lua @@ -2,11 +2,6 @@ local ui = require('rustaceanvim.ui') local M = {} ----@return lsp_range_params -local function get_params() - return vim.lsp.util.make_range_params() -end - ---@type integer | nil local latest_buf_id = nil @@ -29,9 +24,14 @@ local function handler(_, result) ui.resize(true, '-25') end -local rl = require('rustaceanvim.rust_analyzer') function M.syntax_tree() - rl.buf_request(0, 'rust-analyzer/syntaxTree', get_params(), handler) + local ra = require('rustaceanvim.rust_analyzer') + local clients = ra.get_active_rustaceanvim_clients(0) + if #clients == 0 then + return + end + local params = vim.lsp.util.make_range_params(0, clients[1].offset_encoding or 'utf-8') + ra.buf_request(0, 'rust-analyzer/syntaxTree', params, handler) end return M.syntax_tree diff --git a/lua/rustaceanvim/commands/view_ir.lua b/lua/rustaceanvim/commands/view_ir.lua index 617db33f..4f588a90 100644 --- a/lua/rustaceanvim/commands/view_ir.lua +++ b/lua/rustaceanvim/commands/view_ir.lua @@ -41,7 +41,12 @@ end ---@param level rustaceanvim.ir.level function M.viewIR(level) - local position_params = vim.lsp.util.make_position_params(0, nil) + local ra = require('rustaceanvim.rust_analyzer') + local clients = ra.get_active_rustaceanvim_clients(0) + if #clients == 0 then + return + end + local position_params = vim.lsp.util.make_position_params(0, clients[1].offset_encoding or 'utf-8') rl.buf_request(0, 'rust-analyzer/view' .. level, position_params, function(...) return handler(level, ...) end) diff --git a/lua/rustaceanvim/compat.lua b/lua/rustaceanvim/compat.lua new file mode 100644 index 00000000..e4a57d28 --- /dev/null +++ b/lua/rustaceanvim/compat.lua @@ -0,0 +1,85 @@ +---@diagnostic disable: deprecated, duplicate-doc-alias + +---@mod rustaceanvim.compat compativility layer for +---API calls that are deprecated or removed in nvim nightly + +local compat = {} + +---@return lsp.Diagnostic[] +function compat.get_line_diagnostics() + if vim.lsp.diagnostic.from then + local opts = { + lnum = vim.api.nvim_win_get_cursor(0)[1] - 1, + } + return vim.lsp.diagnostic.from(vim.diagnostic.get(0, opts)) + end + ---@diagnostic disable-next-line: deprecated + return vim.lsp.diagnostic.get_line_diagnostics() +end + +---@param location lsp.Location|lsp.LocationLink +---@param offset_encoding 'utf-8'|'utf-16'|'utf-32'? +---@return boolean `true` if the jump succeeded +function compat.show_document(location, offset_encoding) + local show_document = vim.lsp.show_document + if not show_document then + return vim.lsp.util.jump_to_location(location, offset_encoding or 'utf-8') + end + return show_document(location, offset_encoding or 'utf-8', { focus = true }) +end + +--- @param client vim.lsp.Client +--- @param method string LSP method name. +--- @param params? table LSP request params. +--- @param handler? lsp.Handler Response |lsp-handler| for this method. +--- @param bufnr? integer Buffer handle. 0 for current (default). +--- @return boolean status indicates whether the request was successful. +--- If it is `false`, then it will always be `false` (the client has shutdown). +--- @return integer? request_id Can be used with |Client:cancel_request()|. +--- `nil` is request failed. +--- to cancel the-request. +function compat.client_request(client, method, params, handler, bufnr) + local info = debug.getinfo(client.request, 'u') + if info.nparams > 0 then + ---@diagnostic disable-next-line: param-type-mismatch + return client:request(method, params, handler, bufnr) + else + ---@diagnostic disable-next-line: param-type-mismatch + return client.request(method, params, handler, bufnr) + end +end + +--- @param client vim.lsp.Client +--- @param method string LSP method name. +--- @param params table? LSP request params. +--- @return boolean status indicating if the notification was successful. +--- If it is false, then the client has shutdown. +function compat.client_notify(client, method, params) + -- Nothing brings me more joy than updating Neovim nightly + -- and discovering that my perfectly functioning plugin has been obliterated because + -- a feature was deprecated without an alternative in stable. + -- Truly, it's the chaos I live for. (╯°□°)╯︵ ┻━┻ + local info = debug.getinfo(client.notify, 'u') + if info.nparams > 0 then + ---@diagnostic disable-next-line: param-type-mismatch + return client:notify(method, params) + else + ---@diagnostic disable-next-line: param-type-mismatch + return client.notify(method, params) + end +end + +---@param client vim.lsp.Client +---@return boolean +function compat.client_is_stopped(client) + local info = debug.getinfo(client.is_stopped, 'u') + if info.nparams > 0 then + ---@diagnostic disable-next-line: param-type-mismatch + return client:is_stopped() + else + ---@diagnostic disable-next-line: missing-parameter + return client.is_stopped() + end +end + +return compat diff --git a/lua/rustaceanvim/config/init.lua b/lua/rustaceanvim/config/init.lua index 48f5d79f..252306be 100644 --- a/lua/rustaceanvim/config/init.lua +++ b/lua/rustaceanvim/config/init.lua @@ -78,7 +78,7 @@ vim.g.rustaceanvim = vim.g.rustaceanvim ---@field enable_clippy? boolean --- ---Function that is invoked when the LSP server has finished initializing ----@field on_initialized? fun(health:rustaceanvim.RAInitializedStatus) +---@field on_initialized? fun(health:rustaceanvim.RAInitializedStatus, client_id:integer) --- ---Automatically call `RustReloadWorkspace` when writing to a Cargo.toml file ---@field reload_workspace_from_cargo_toml? boolean @@ -150,7 +150,7 @@ vim.g.rustaceanvim = vim.g.rustaceanvim ---Override the enabled graphviz backends list, used for input validation and autocompletion. ---@field enabled_graphviz_backends? string[] --- ----Overide the pipe symbol in the shell command. +---Override the pipe symbol in the shell command. ---Useful if using a shell that is not supported by this plugin. ---@field pipe? string @@ -189,19 +189,28 @@ vim.g.rustaceanvim = vim.g.rustaceanvim --- ---Whether to search (upward from the buffer) for rust-analyzer settings in .vscode/settings json. ---If found, loaded settings will override configured options. ----Default: true +---Default: `true` ---@field load_vscode_settings? boolean +--- +---Server status warning level to notify at. +---Default: 'error' +---@field status_notify_level? rustaceanvim.server.status_notify_level +--- ---@see vim.lsp.ClientConfig +---@alias rustaceanvim.server.status_notify_level 'error' | 'warning' | rustaceanvim.disable + +---@alias rustaceanvim.disable false + ---@class rustaceanvim.dap.Opts --- ---Whether to autoload nvim-dap configurations when rust-analyzer has attached? ----Default: `true`. ----@field autoload_configurations boolean +---Default: `true` +---@field autoload_configurations? boolean --- ---Defaults to creating the `rt_lldb` adapter, which is a |rustaceanvim.dap.server.Config| ---if `codelldb` is detected, and a |rustaceanvim.dap.executable.Config|` if `lldb` is detected. --- Set to `false` to disable. +---Set to `false` to disable. ---@field adapter? rustaceanvim.dap.executable.Config | rustaceanvim.dap.server.Config | rustaceanvim.disable | fun():(rustaceanvim.dap.executable.Config | rustaceanvim.dap.server.Config | rustaceanvim.disable) --- ---Dap client configuration. Defaults to a function that looks for a `launch.json` file @@ -220,8 +229,6 @@ vim.g.rustaceanvim = vim.g.rustaceanvim ---Default: `true`. ---@field load_rust_types? fun():boolean | boolean ----@alias rustaceanvim.disable false - ---@alias rustaceanvim.dap.Command string ---@class rustaceanvim.dap.executable.Config @@ -246,7 +253,7 @@ vim.g.rustaceanvim = vim.g.rustaceanvim ---@alias rustaceanvim.dap.adapter.types.executable "executable" ---@alias rustaceanvim.dap.adapter.types.server "server" ----@class rustaceanvim.dap.client.Config: Configuration +---@class rustaceanvim.dap.client.Config: dap.Configuration ---@field type string The dap adapter to use ---@field name string ---@field request rustaceanvim.dap.config.requests.launch | rustaceanvim.dap.config.requests.attach | rustaceanvim.dap.config.requests.custom The type of dap session diff --git a/lua/rustaceanvim/config/internal.lua b/lua/rustaceanvim/config/internal.lua index 198e3bef..68852f93 100644 --- a/lua/rustaceanvim/config/internal.lua +++ b/lua/rustaceanvim/config/internal.lua @@ -10,6 +10,7 @@ local RustaceanConfig ---@class rustaceanvim.internal.RAInitializedStatus : rustaceanvim.RAInitializedStatus ---@field health rustaceanvim.lsp_server_health_status ---@field quiescent boolean inactive? +---@field message string | nil --- ---@param dap_adapter rustaceanvim.dap.executable.Config | rustaceanvim.dap.server.Config | rustaceanvim.disable ---@return boolean @@ -43,6 +44,7 @@ local function load_dap_configuration(type) stopOnEntry = false, } if type == 'lldb' then + ---@diagnostic disable-next-line: inject-field dap_config.runInTerminal = true end ---@diagnostic disable-next-line: different-requires @@ -104,7 +106,7 @@ local RustaceanDefaultConfig = { --- callback to execute once rust-analyzer is done initializing the workspace --- The callback receives one parameter indicating the `health` of the server: "ok" | "warning" | "error" - ---@type fun(health:rustaceanvim.RAInitializedStatus) | nil + ---@type fun(health:rustaceanvim.RAInitializedStatus, client_id:integer) | nil on_initialized = nil, --- automatically call RustReloadWorkspace when writing to a Cargo.toml file. @@ -294,6 +296,8 @@ local RustaceanDefaultConfig = { }, ---@type boolean Whether to search (upward from the buffer) for rust-analyzer settings in .vscode/settings json. load_vscode_settings = true, + ---@type rustaceanvim.server.status_notify_level + status_notify_level = 'error', }, --- debugging stuff diff --git a/lua/rustaceanvim/config/json.lua b/lua/rustaceanvim/config/json.lua index 351e409f..e5423435 100644 --- a/lua/rustaceanvim/config/json.lua +++ b/lua/rustaceanvim/config/json.lua @@ -1,10 +1,37 @@ local M = {} -local function tbl_set(tbl, keys, value) +local warnings = {} +local errors = {} +local is_json_config_loaded = false + +---@param warning string +local function add_warning(warning) + table.insert(warnings, warning) +end + +---@param err string +local function add_error(err) + table.insert(errors, err) +end + +---@param field_name string | nil +---@param tbl unknown +---@param keys string[] +---@param value unknown +local function tbl_set(field_name, tbl, keys, value) local next = table.remove(keys, 1) + if type(tbl) ~= 'table' then + add_warning(([[ +Ignored field '%s' of invalid type '%s': %s +Please refer to the rust-analyzer documentation at +https://rust-analyzer.github.io/manual.html#%s +]]):format(field_name, type(value), vim.inspect(value), field_name)) + return + end if #keys > 0 then tbl[next] = tbl[next] or {} - tbl_set(tbl[next], keys, value) + field_name = (field_name and field_name .. '.' or '') .. next + tbl_set(field_name, tbl[next], keys, value) else tbl[next] = value end @@ -15,14 +42,17 @@ end ---@param json_value unknown local function override_tbl_values(tbl, json_key, json_value) local keys = vim.split(json_key, '%.') - tbl_set(tbl, keys, json_value) + tbl_set(nil, tbl, keys, json_value) end ---@param json_content string ---@return table function M.silent_decode(json_content) + warnings = {} + errors = {} local ok, json_tbl = pcall(vim.json.decode, json_content) if not ok or type(json_tbl) ~= 'table' then + add_error(('Failed to decode json: %s'):format(json_tbl or 'unknown error')) return {} end return json_tbl @@ -32,6 +62,7 @@ end ---@param json_tbl { [string]: unknown } ---@param key_predicate? fun(string): boolean function M.override_with_json_keys(tbl, json_tbl, key_predicate) + is_json_config_loaded = true for json_key, value in pairs(json_tbl) do if not key_predicate or key_predicate(json_key) then override_tbl_values(tbl, json_key, value) @@ -47,4 +78,16 @@ function M.override_with_rust_analyzer_json_keys(tbl, json_tbl) end) end +function M.is_json_config_loaded() + return is_json_config_loaded +end + +function M.get_warnings() + return warnings +end + +function M.get_errors() + return errors +end + return M diff --git a/lua/rustaceanvim/dap.lua b/lua/rustaceanvim/dap.lua index 38673813..a6d8436f 100644 --- a/lua/rustaceanvim/dap.lua +++ b/lua/rustaceanvim/dap.lua @@ -310,32 +310,34 @@ function M.start(args, verbose, callback, on_error) end vim.schedule(function() local executables = {} - for value in output:gmatch('([^\n]*)\n?') do - local is_json, artifact = pcall(vim.fn.json_decode, value) - if not is_json then - goto loop_end - end - - -- only process artifact if it's valid json object and it is a compiler artifact - if type(artifact) ~= 'table' or artifact.reason ~= 'compiler-artifact' then - goto loop_end - end - - local is_binary = vim.list_contains(artifact.target.crate_types, 'bin') - local is_build_script = vim.list_contains(artifact.target.kind, 'custom-build') - local is_test = ((artifact.profile.test == true) and (artifact.executable ~= nil)) - or vim.list_contains(artifact.target.kind, 'test') - -- only add executable to the list if we want a binary debug and it is a binary - -- or if we want a test debug and it is a test - if - (cargo_args[1] == 'build' and is_binary and not is_build_script) - or (cargo_args[1] == 'test' and is_test) - then - table.insert(executables, artifact.executable) - end - - ::loop_end:: - end + vim + .iter(output:gmatch('([^\n]*)\n?')) + ---@param value string + :map(function(value) + local is_json, artifact = pcall(vim.fn.json_decode, value) + ---@diagnostic disable-next-line: redundant-return-value + return is_json, artifact + end) + ---@param is_json boolean + :filter(function(is_json, artifact) + -- only process artifact if it's valid json object and it is a compiler artifact + return is_json and type(artifact) == 'table' and artifact.reason == 'compiler-artifact' + end) + ---@param artifact table + :each(function(_, artifact) + local is_binary = vim.list_contains(artifact.target.crate_types, 'bin') + local is_build_script = vim.list_contains(artifact.target.kind, 'custom-build') + local is_test = ((artifact.profile.test == true) and (artifact.executable ~= nil)) + or vim.list_contains(artifact.target.kind, 'test') + -- only add executable to the list if we want a binary debug and it is a binary + -- or if we want a test debug and it is a test + if + (cargo_args[1] == 'build' and is_binary and not is_build_script) + or (cargo_args[1] == 'test' and is_test) + then + table.insert(executables, artifact.executable) + end + end) -- only 1 executable is allowed for debugging - error out if zero or many were found if #executables <= 0 then on_error('No compilation artifacts found.') diff --git a/lua/rustaceanvim/executors/meta.lua b/lua/rustaceanvim/executors/_meta.lua similarity index 100% rename from lua/rustaceanvim/executors/meta.lua rename to lua/rustaceanvim/executors/_meta.lua diff --git a/lua/rustaceanvim/executors/quickfix.lua b/lua/rustaceanvim/executors/quickfix.lua index 16b8de84..f416bfcf 100644 --- a/lua/rustaceanvim/executors/quickfix.lua +++ b/lua/rustaceanvim/executors/quickfix.lua @@ -10,7 +10,7 @@ end ---@param lines string[] local function append_qf(lines) - vim.fn.setqflist({}, 'a', { lines = { lines } }) + vim.fn.setqflist({}, 'a', { lines = lines }) scroll_qf() end diff --git a/lua/rustaceanvim/health.lua b/lua/rustaceanvim/health.lua index 584f0a45..851b66a4 100644 --- a/lua/rustaceanvim/health.lua +++ b/lua/rustaceanvim/health.lua @@ -2,15 +2,7 @@ local health = {} -local h = vim.health or require('health') ----@diagnostic disable-next-line: deprecated -local start = h.start or h.report_start ----@diagnostic disable-next-line: deprecated -local ok = h.ok or h.report_ok ----@diagnostic disable-next-line: deprecated -local error = h.error or h.report_error ----@diagnostic disable-next-line: deprecated -local warn = h.warn or h.report_warn +local h = vim.health ---@class rustaceanvim.LuaDependency ---@field module string The name of a module @@ -43,11 +35,11 @@ local lua_dependencies = { ---@param dep rustaceanvim.LuaDependency local function check_lua_dependency(dep) if pcall(require, dep.module) then - ok(dep.url .. ' installed.') + h.ok(dep.url .. ' installed.') return end if dep.optional() then - warn(('%s not installed. %s %s'):format(dep.module, dep.info, dep.url)) + h.warn(('%s not installed. %s %s'):format(dep.module, dep.info, dep.url)) else error(('Lua dependency %s not found: %s'):format(dep.module, dep.url)) end @@ -88,14 +80,14 @@ local function check_external_dependency(dep) local mb_version_len = version_or_err and (mb_version_newline_idx and mb_version_newline_idx - 1 or version_or_err:len()) version_or_err = version_or_err and version_or_err:sub(0, mb_version_len) or '(unknown version)' - ok(('%s: found %s'):format(dep.name, version_or_err)) + h.ok(('%s: found %s'):format(dep.name, version_or_err)) if dep.extra_checks_if_installed then dep.extra_checks_if_installed(binary) end return end if dep.optional() then - warn(([[ + h.warn(([[ %s: not found. Install %s for extended capabilities. %s @@ -114,13 +106,13 @@ end ---@param config rustaceanvim.Config local function check_config(config) - start('Checking config') + h.start('Checking config') if vim.g.rustaceanvim and not config.was_g_rustaceanvim_sourced then error('vim.g.rustaceanvim is set, but it was sourced after rustaceanvim was initialized.') end local valid, err = require('rustaceanvim.config.check').validate(config) if valid then - ok('No errors found in config.') + h.ok('No errors found in config.') else error(err or '' .. vim.g.rustaceanvim and '' or ' This looks like a plugin bug!') end @@ -136,23 +128,42 @@ local function is_dap_enabled() end local function check_for_conflicts() - start('Checking for conflicting plugins') + h.start('Checking for conflicting plugins') require('rustaceanvim.config.check').check_for_lspconfig_conflict(error) if package.loaded['rustaceanvim.neotest'] ~= nil and package.loaded['neotest-rust'] ~= nil then error('rustaceanvim.neotest and neotest-rust are both loaded. This is likely a conflict.') return end - ok('No conflicting plugins detected.') + h.ok('No conflicting plugins detected.') end local function check_tree_sitter() - start('Checking for tree-sitter parser') + h.start('Checking for tree-sitter parser') local has_tree_sitter_rust_parser = #vim.api.nvim_get_runtime_file('parser/rust.so', true) > 0 or #vim.api.nvim_get_runtime_file('parser/rust.dll', true) > 0 if has_tree_sitter_rust_parser then - ok('tree-sitter parser for Rust detected.') + h.ok('tree-sitter parser for Rust detected.') else - warn("No tree-sitter parser for Rust detected. Required by 'Rustc unpretty' command.") + h.warn("No tree-sitter parser for Rust detected. Required by 'Rustc unpretty' command.") + end +end + +local function check_json_config() + local json = require('rustaceanvim.config.json') + if json.is_json_config_loaded() then + local errors = json.get_errors() + if #errors > 0 then + h.warn('.vscode/settings.json failed to load.') + vim.iter(errors):each(h.error) + return + end + local warnings = json.get_warnings() + if #warnings == 0 then + h.ok('.vscode/settings.json loaded without errors.') + else + h.warn('.vscode/settings.json loaded with warnings.') + vim.iter(warnings):each(h.warn) + end end end @@ -160,12 +171,12 @@ function health.check() local types = require('rustaceanvim.types.internal') local config = require('rustaceanvim.config.internal') - start('Checking for Lua dependencies') + h.start('Checking for Lua dependencies') for _, dep in ipairs(lua_dependencies) do check_lua_dependency(dep) end - start('Checking external dependencies') + h.start('Checking external dependencies') local adapter = types.evaluate(config.dap.adapter) ---@cast adapter rustaceanvim.dap.executable.Config | rustaceanvim.dap.server.Config | boolean @@ -207,7 +218,7 @@ function health.check() extra_checks_if_not_installed = function() local bin = get_rust_analyzer_binary() if vim.fn.executable(bin) == 1 then - warn("rust-analyzer wrapper detected. Run 'rustup component add rust-analyzer' to install rust-analyzer.") + h.warn("rust-analyzer wrapper detected. Run 'rustup component add rust-analyzer' to install rust-analyzer.") end end, }, @@ -281,7 +292,7 @@ function health.check() }) end if adapter == false and is_dap_enabled() then - warn('No debug adapter detected. Make sure either lldb or codelldb is available on the path.') + h.warn('No debug adapter detected. Make sure either lldb or codelldb is available on the path.') end for _, dep in ipairs(external_dependencies) do check_external_dependency(dep) @@ -289,6 +300,7 @@ function health.check() check_config(config) check_for_conflicts() check_tree_sitter() + check_json_config() end return health diff --git a/lua/rustaceanvim/hover_actions.lua b/lua/rustaceanvim/hover_actions.lua index cfcd0e70..9967df7c 100644 --- a/lua/rustaceanvim/hover_actions.lua +++ b/lua/rustaceanvim/hover_actions.lua @@ -3,10 +3,6 @@ local lsp_util = vim.lsp.util local M = {} -local function get_params() - return lsp_util.make_position_params(0, nil) -end - ---@class rustaceanvim.hover_actions.State local _state = { ---@type integer @@ -30,10 +26,8 @@ end -- run the command under the cursor, if the thing under the cursor is not the -- command then do nothing ---@param ctx table -local function run_command(ctx) - local winnr = vim.api.nvim_get_current_win() - local line = vim.api.nvim_win_get_cursor(winnr)[1] - +---@param line integer +local function run_command(ctx, line) if line > #_state.commands then return end @@ -61,6 +55,7 @@ local function parse_commands() return prompt end +---@param ctx lsp.HandlerContext function M.handler(_, result, ctx) if not (result and result.contents) then -- return { 'No information available' } @@ -132,15 +127,24 @@ function M.handler(_, result, ctx) -- run the command under the cursor vim.keymap.set('n', '', function() - run_command(ctx) + local line = vim.api.nvim_win_get_cursor(winnr)[1] + run_command(ctx, line) end, { buffer = bufnr, noremap = true, silent = true }) + vim.keymap.set('n', 'RustHoverAction', function() + local line = math.max(vim.v.count, 1) + run_command(ctx, line) + end, { buffer = vim.api.nvim_get_current_buf(), noremap = true, silent = true }) end -local rl = require('rustaceanvim.rust_analyzer') - --- Sends the request to rust-analyzer to get hover actions and handle it function M.hover_actions() - rl.buf_request(0, 'textDocument/hover', get_params(), M.handler) + local ra = require('rustaceanvim.rust_analyzer') + local clients = ra.get_active_rustaceanvim_clients(0) + if #clients == 0 then + return + end + local params = lsp_util.make_position_params(0, clients[1].offset_encoding or 'utf-8') + ra.buf_request(0, 'textDocument/hover', params, M.handler) end return M diff --git a/lua/rustaceanvim/init.lua b/lua/rustaceanvim/init.lua index 22050a46..bf7ee927 100644 --- a/lua/rustaceanvim/init.lua +++ b/lua/rustaceanvim/init.lua @@ -16,6 +16,11 @@ --- ':RustAnalyzer stop' - Stop the LSP client. --- ':RustAnalyzer restart' - Restart the LSP client. --- ':RustAnalyzer reloadSettings' - Reload settings for the LSP client. +--- ':RustAnalyzer target ' - Set the target architecture for the LSP client. + +--- The ':RustAnalyzer target' command can take a valid rustc target, +--- such as 'wasm32-unknown-unknown', or it can be left empty to set the LSP client +--- to use the default target architecture for the operating system. --- ---The ':RustLsp[!]' command is available after the LSP client has initialized. ---It accepts the following subcommands: diff --git a/lua/rustaceanvim/lsp/_meta.lua b/lua/rustaceanvim/lsp/_meta.lua new file mode 100644 index 00000000..4bce41a2 --- /dev/null +++ b/lua/rustaceanvim/lsp/_meta.lua @@ -0,0 +1,6 @@ +error('Cannot import a meta module') + +---@class rustaceanvim.lsp.TextEdit +---@field range lsp.Range +---@field newText? string +---@field insertTextFormat? integer diff --git a/lua/rustaceanvim/lsp.lua b/lua/rustaceanvim/lsp/init.lua similarity index 72% rename from lua/rustaceanvim/lsp.lua rename to lua/rustaceanvim/lsp/init.lua index 4770d451..ab1b207d 100644 --- a/lua/rustaceanvim/lsp.lua +++ b/lua/rustaceanvim/lsp/init.lua @@ -6,6 +6,8 @@ local rust_analyzer = require('rustaceanvim.rust_analyzer') local server_status = require('rustaceanvim.server_status') local cargo = require('rustaceanvim.cargo') local os = require('rustaceanvim.os') +local rustc = require('rustaceanvim.rustc') +local compat = require('rustaceanvim.compat') local function override_apply_text_edits() local old_func = vim.lsp.util.apply_text_edits @@ -13,11 +15,11 @@ local function override_apply_text_edits() vim.lsp.util.apply_text_edits = function(edits, bufnr, offset_encoding) local overrides = require('rustaceanvim.overrides') overrides.snippet_text_edits_to_text_edits(edits) - old_func(edits, bufnr, offset_encoding) + old_func(edits, bufnr, offset_encoding or 'utf-8') end end ----@param client lsp.Client +---@param client vim.lsp.Client ---@param root_dir string ---@return boolean local function is_in_workspace(client, root_dir) @@ -93,6 +95,54 @@ local function configure_file_watcher(server_cfg) end end +---LSP restart internal implementations +---@param bufnr? number The buffer number, defaults to the current buffer +---@param filter? rustaceanvim.lsp.get_clients.Filter +---@param callback? fun(client: vim.lsp.Client) Optional callback to run for each client before restarting. +---@return number|nil client_id +local function restart(bufnr, filter, callback) + bufnr = bufnr or vim.api.nvim_get_current_buf() + local clients = M.stop(bufnr, filter) + local timer, _, _ = vim.uv.new_timer() + if not timer then + vim.schedule(function() + vim.notify('rustaceanvim.lsp: Failed to initialise timer for LSP client restart.', vim.log.levels.ERROR) + end) + return + end + local max_attempts = 50 + local attempts_to_live = max_attempts + local stopped_client_count = 0 + timer:start(200, 100, function() + for _, client in ipairs(clients) do + if compat.client_is_stopped(client) then + stopped_client_count = stopped_client_count + 1 + vim.schedule(function() + -- Execute the callback, if provided, for additional actions before restarting + if callback then + callback(client) + end + M.start(bufnr) + end) + end + end + if stopped_client_count >= #clients then + timer:stop() + attempts_to_live = 0 + elseif attempts_to_live <= 0 then + vim.schedule(function() + vim.notify( + ('rustaceanvim.lsp: Could not restart all LSP clients after %d attempts.'):format(max_attempts), + vim.log.levels.ERROR + ) + end) + timer:stop() + attempts_to_live = 0 + end + attempts_to_live = attempts_to_live - 1 + end) +end + ---@class rustaceanvim.lsp.StartConfig: rustaceanvim.lsp.ClientConfig ---@field root_dir string | nil ---@field init_options? table @@ -117,7 +167,14 @@ M.start = function(bufnr) local lsp_start_config = vim.tbl_deep_extend('force', {}, client_config) local root_dir = cargo.get_config_root_dir(client_config, bufname) if not root_dir then - --- No project root found. Start in detached/standalone mode. + vim.notify( + [[ +rustaceanvim: +No project root found. +Starting rust-analyzer client in detached/standalone mode (with reduced functionality). +]], + vim.log.levels.INFO + ) root_dir = vim.fs.dirname(bufname) lsp_start_config.init_options = { detachedFiles = { bufname } } end @@ -147,7 +204,7 @@ M.start = function(bufnr) removed = {}, }, } - client.rpc.notify('workspace/didChangeWorkspaceFolders', params) + compat.client_notify(client, 'workspace/didChangeWorkspaceFolders', params) if not client.workspace_folders then client.workspace_folders = {} end @@ -158,8 +215,23 @@ M.start = function(bufnr) end local rust_analyzer_cmd = types.evaluate(client_config.cmd) - if #rust_analyzer_cmd == 0 or vim.fn.executable(rust_analyzer_cmd[1]) ~= 1 then - vim.notify('rust-analyzer binary not found.', vim.log.levels.ERROR) + -- special case: rust-analyzer has a `rust-analyzer.server.path` config option + -- that allows you to override the path via .vscode/settings.json + local server_path = vim.tbl_get(lsp_start_config.settings, 'rust-analyzer', 'server', 'path') + if type(server_path) == 'string' then + rust_analyzer_cmd[1] = server_path + -- + end + if #rust_analyzer_cmd == 0 then + vim.schedule(function() + vim.notify('rust-analyzer command is not set!', vim.log.levels.ERROR) + end) + return + end + if vim.fn.executable(rust_analyzer_cmd[1]) ~= 1 then + vim.schedule(function() + vim.notify(('%s is not executable'):format(rust_analyzer_cmd[1]), vim.log.levels.ERROR) + end) return end ---@cast rust_analyzer_cmd string[] @@ -214,18 +286,19 @@ end ---Stop the LSP client. ---@param bufnr? number The buffer number, defaults to the current buffer ----@return table[] clients A list of clients that will be stopped -M.stop = function(bufnr) +---@param filter? rustaceanvim.lsp.get_clients.Filter +---@return vim.lsp.Client[] clients A list of clients that will be stopped +M.stop = function(bufnr, filter) bufnr = bufnr or vim.api.nvim_get_current_buf() - local clients = rust_analyzer.get_active_rustaceanvim_clients(bufnr) + local clients = rust_analyzer.get_active_rustaceanvim_clients(bufnr, filter) vim.lsp.stop_client(clients) if type(clients) == 'table' then - ---@cast clients lsp.Client[] + ---@cast clients vim.lsp.Client[] for _, client in ipairs(clients) do server_status.reset_client_state(client.id) end else - ---@cast clients lsp.Client + ---@cast clients vim.lsp.Client server_status.reset_client_state(clients.id) end return clients @@ -233,63 +306,60 @@ end ---Reload settings for the LSP client. ---@param bufnr? number The buffer number, defaults to the current buffer ----@return table[] clients A list of clients that will be have their settings reloaded +---@return vim.lsp.Client[] clients A list of clients that will be have their settings reloaded M.reload_settings = function(bufnr) bufnr = bufnr or vim.api.nvim_get_current_buf() local clients = rust_analyzer.get_active_rustaceanvim_clients(bufnr) - ---@cast clients lsp.Client[] + ---@cast clients vim.lsp.Client[] for _, client in ipairs(clients) do local settings = get_start_settings(vim.api.nvim_buf_get_name(bufnr), client.config.root_dir, config.server) ---@diagnostic disable-next-line: inject-field client.settings = settings - client.notify('workspace/didChangeConfiguration', { + compat.client_notify(client, 'workspace/didChangeConfiguration', { settings = client.settings, }) end return clients end ----Restart the LSP client. ----Fails silently if the buffer's filetype is not one of the filetypes specified in the config. ----@param bufnr? number The buffer number (optional), defaults to the current buffer ----@return number|nil client_id The LSP client ID after restart -M.restart = function(bufnr) - bufnr = bufnr or vim.api.nvim_get_current_buf() - local clients = M.stop(bufnr) - local timer, _, _ = vim.uv.new_timer() - if not timer then - -- TODO: Log error when logging is implemented - return - end - local attempts_to_live = 50 - local stopped_client_count = 0 - timer:start(200, 100, function() - for _, client in ipairs(clients) do - if client:is_stopped() then - stopped_client_count = stopped_client_count + 1 +---Updates the target architecture setting for the LSP client associated with the given buffer. +---@param bufnr? number The buffer number, defaults to the current buffer +---@param target? string Cargo target triple (e.g., 'x86_64-unknown-linux-gnu') to set +M.set_target_arch = function(bufnr, target) + target = target or rustc.DEFAULT_RUSTC_TARGET + ---@param client vim.lsp.Client + restart(bufnr, { exclude_rustc_target = target }, function(client) + rustc.with_rustc_target_architectures(function(rustc_targets) + if rustc_targets[target] then + local ra = client.config.settings['rust-analyzer'] + ra.cargo = ra.cargo or {} + ra.cargo.target = target + compat.client_notify(client, 'workspace/didChangeConfiguration', { settings = client.config.settings }) + return + else vim.schedule(function() - M.start(bufnr) + vim.notify('Invalid target architecture provided: ' .. tostring(target), vim.log.levels.ERROR) end) + return end - end - if stopped_client_count >= #clients then - timer:stop() - attempts_to_live = 0 - elseif attempts_to_live <= 0 then - vim.notify('rustaceanvim.lsp: Could not restart all LSP clients.', vim.log.levels.ERROR) - timer:stop() - attempts_to_live = 0 - end - attempts_to_live = attempts_to_live - 1 + end) end) end +---Restart the LSP client. +---Fails silently if the buffer's filetype is not one of the filetypes specified in the config. +---@return number|nil client_id The LSP client ID after restart +M.restart = function() + return restart() +end + ---@enum RustAnalyzerCmd local RustAnalyzerCmd = { start = 'start', stop = 'stop', restart = 'restart', reload_settings = 'reloadSettings', + target = 'target', } local function rust_analyzer_cmd(opts) @@ -304,16 +374,19 @@ local function rust_analyzer_cmd(opts) M.restart() elseif cmd == RustAnalyzerCmd.reload_settings then M.reload_settings() + elseif cmd == RustAnalyzerCmd.target then + local target_arch = fargs[2] + M.set_target_arch(nil, target_arch) end end vim.api.nvim_create_user_command('RustAnalyzer', rust_analyzer_cmd, { nargs = '+', - desc = 'Starts or stops the rust-analyzer LSP client', + desc = 'Starts, stops the rust-analyzer LSP client or changes the target', complete = function(arg_lead, cmdline, _) local clients = rust_analyzer.get_active_rustaceanvim_clients() ---@type RustAnalyzerCmd[] - local commands = #clients == 0 and { 'start' } or { 'stop', 'restart', 'reloadSettings' } + local commands = #clients == 0 and { 'start' } or { 'stop', 'restart', 'reloadSettings', 'target' } if cmdline:match('^RustAnalyzer%s+%w*$') then return vim.tbl_filter(function(command) return command:find(arg_lead) ~= nil diff --git a/lua/rustaceanvim/neotest/init.lua b/lua/rustaceanvim/neotest/init.lua index 2465d177..08b506b7 100644 --- a/lua/rustaceanvim/neotest/init.lua +++ b/lua/rustaceanvim/neotest/init.lua @@ -273,6 +273,7 @@ function NeotestAdapter.build_spec(run_args) local dap = require('rustaceanvim.dap') overrides.sanitize_command_for_debugging(runnable.args.cargoArgs) local future = nio.control.future() + ---@diagnostic disable-next-line: invisible dap.start(runnable.args, false, function(strategy) future.set(strategy) end, function(err) @@ -297,7 +298,6 @@ function NeotestAdapter.build_spec(run_args) local is_cargo_test = args[1] == 'test' local insert_pos = is_cargo_test and 2 or 3 table.insert(args, insert_pos, '--no-fail-fast') - table.insert(args, insert_pos, '--color=never') if is_cargo_test then -- cargo test needs to pass --color=never to the test runner too table.insert(args, '--color=never') diff --git a/lua/rustaceanvim/overrides.lua b/lua/rustaceanvim/overrides.lua index 6bcff94c..fb0317d1 100644 --- a/lua/rustaceanvim/overrides.lua +++ b/lua/rustaceanvim/overrides.lua @@ -21,12 +21,12 @@ local function parse_snippet(input) return ok and tostring(parsed) or parse_snippet_fallback(input) end ----@param spe? table -function M.snippet_text_edits_to_text_edits(spe) - if type(spe) ~= 'table' then +---@param text_edits? rustaceanvim.lsp.TextEdit[] +function M.snippet_text_edits_to_text_edits(text_edits) + if type(text_edits) ~= 'table' then return end - for _, value in ipairs(spe) do + for _, value in ipairs(text_edits) do if value.newText and value.insertTextFormat then value.newText = parse_snippet(value.newText) end diff --git a/lua/rustaceanvim/runnables.lua b/lua/rustaceanvim/runnables.lua index f5b89c24..8cc0faf7 100644 --- a/lua/rustaceanvim/runnables.lua +++ b/lua/rustaceanvim/runnables.lua @@ -185,8 +185,13 @@ end ---@param runnables rustaceanvim.RARunnable ---@return integer | nil choice function M.get_runnable_at_cursor_position(runnables) + local ra = require('rustaceanvim.rust_analyzer') + local clients = ra.get_active_rustaceanvim_clients(0) + if #clients == 0 then + return + end ---@type lsp.Position - local position = vim.lsp.util.make_position_params().position + local position = vim.lsp.util.make_position_params(0, clients[1].offset_encoding or 'utf-8').position ---@type integer|nil, integer|nil local choice, fallback for idx, runnable in ipairs(runnables) do diff --git a/lua/rustaceanvim/rust_analyzer.lua b/lua/rustaceanvim/rust_analyzer.lua index 47a82897..3851bac2 100644 --- a/lua/rustaceanvim/rust_analyzer.lua +++ b/lua/rustaceanvim/rust_analyzer.lua @@ -1,22 +1,52 @@ ---@mod rustaceanvim.rust_analyzer Functions for interacting with rust-analyzer local os = require('rustaceanvim.os') +local rustc = require('rustaceanvim.rustc') +local compat = require('rustaceanvim.compat') ---@class rustaceanvim.rust-analyzer.ClientAdapter local M = {} +M.load_os_rustc_target = function() + vim.system({ 'rustc', '-Vv' }, { text = true }, function(result) + if result.code == 0 then + for line in result.stdout:gmatch('[^\r\n]+') do + local host = line:match('^host:%s*(.+)$') + if host then + M.os_rustc_target = host + break + end + end + end + end) +end + +---@class rustaceanvim.lsp.get_clients.Filter: vim.lsp.get_clients.Filter +---@field exclude_rustc_target? string Cargo target triple (e.g., 'x86_64-unknown-linux-gnu') to filter rust-analyzer clients + ---@param bufnr number | nil 0 for the current buffer, `nil` for no buffer filter ----@param filter? vim.lsp.get_clients.Filter +---@param filter? rustaceanvim.lsp.get_clients.Filter ---@return vim.lsp.Client[] M.get_active_rustaceanvim_clients = function(bufnr, filter) ---@type vim.lsp.get_clients.Filter - filter = vim.tbl_deep_extend('force', filter or {}, { + local client_filter = vim.tbl_deep_extend('force', filter or {}, { name = 'rust-analyzer', }) if bufnr then - filter.bufnr = bufnr + client_filter.bufnr = bufnr + end + local clients = vim.lsp.get_clients(client_filter) + if filter and filter.exclude_rustc_target then + clients = vim.tbl_filter(function(client) + local cargo_target = vim.tbl_get(client, 'config', 'settings', 'rust-analyzer', 'cargo', 'target') + if filter.exclude_rustc_target == rustc.DEFAULT_RUSTC_TARGET and cargo_target == nil then + return false + end + return cargo_target ~= filter.exclude_rustc_target + end, clients) end - return vim.lsp.get_clients(filter) + + return clients end ---@param method string LSP method name @@ -31,7 +61,7 @@ M.any_buf_request = function(method, params, handler) end -- No buffer found. Try any client. for _, client in ipairs(M.get_active_rustaceanvim_clients(nil, { method = method })) do - client.request(method, params, handler, 0) + compat.client_request(client, method, params, handler, 0) end end @@ -47,7 +77,7 @@ M.buf_request = function(bufnr, method, params, handler) end local client_found = false for _, client in ipairs(M.get_active_rustaceanvim_clients(bufnr, { method = method })) do - client.request(method, params, handler, bufnr) + compat.client_request(client, method, params, handler, 0) client_found = true end return client_found @@ -55,7 +85,7 @@ end ---@param file_path string Search for clients with a root_dir matching this file path ---@param method string LSP method name ----@return lsp.Client|nil +---@return vim.lsp.Client|nil M.get_client_for_file = function(file_path, method) for _, client in ipairs(M.get_active_rustaceanvim_clients(nil, { method = method })) do local root_dir = client.config.root_dir @@ -70,7 +100,7 @@ end M.notify = function(method, params) local client_found = false for _, client in ipairs(M.get_active_rustaceanvim_clients(0, { method = method })) do - client.notify(method, params) + compat.client_notify(client, method, params) client_found = true end if not client_found then diff --git a/lua/rustaceanvim/rustc.lua b/lua/rustaceanvim/rustc.lua new file mode 100644 index 00000000..f9c43e3f --- /dev/null +++ b/lua/rustaceanvim/rustc.lua @@ -0,0 +1,39 @@ +local M = {} + +--- Default target value for rustc when no specific target is provided. +--- Used as a fallback to let rustc determine the appropriate target based on the OS. +M.DEFAULT_RUSTC_TARGET = 'OS' + +---Local rustc targets cache +local rustc_targets_cache = nil + +---Handles retrieving rustc target architectures and running the passed in callback +---to perform certain actions using the retrieved targets. +---@param callback fun(targets: string[]) +M.with_rustc_target_architectures = function(callback) + if rustc_targets_cache then + return callback(rustc_targets_cache) + end + vim.system( + { 'rustc', '--print', 'target-list' }, + { text = true }, + ---@param result vim.SystemCompleted + function(result) + if result.code ~= 0 then + error('Failed to retrieve rustc targets: ' .. result.stderr) + end + rustc_targets_cache = vim.iter(result.stdout:gmatch('[^\r\n]+')):fold( + {}, + ---@param acc table + ---@param target string + function(acc, target) + acc[target] = true + return acc + end + ) + return callback(rustc_targets_cache) + end + ) +end + +return M diff --git a/lua/rustaceanvim/server_status.lua b/lua/rustaceanvim/server_status.lua index 21390451..dbe59db8 100644 --- a/lua/rustaceanvim/server_status.lua +++ b/lua/rustaceanvim/server_status.lua @@ -5,10 +5,42 @@ local M = {} ---@type { [integer]: boolean } local _ran_once = {} +---@param health rustaceanvim.lsp_server_health_status +---@return boolean +local function is_notify_enabled_for(health) + if health and health == 'ok' then + return false + end + local notify_level = config.server.status_notify_level + if not notify_level then + return false + end + if notify_level == 'error' then + return health == 'error' + end + return true +end + ---@param result rustaceanvim.internal.RAInitializedStatus +---@param ctx lsp.HandlerContext function M.handler(_, result, ctx, _) -- quiescent means the full set of results is ready. - if not result.quiescent or _ran_once[ctx.client_id] then + if not result or not result.quiescent then + return + end + -- notify of LSP errors/warnings + if is_notify_enabled_for(result.health) then + local message = ([[ +rust-analyzer health status is [%s]: +%s +Run ':RustLsp logFile' for details. +To configure or disable rust-analyzer server status notifications, +see ':h rustaceanvim.lsp.ClientOpts'. +]]):format(result.health, result.message or '[unknown error]') + vim.notify(message, vim.log.levels.WARN) + end + -- deduplicate messages. + if _ran_once[ctx.client_id] then return end -- rust-analyzer may provide incomplete/empty inlay hints by the time Neovim @@ -26,7 +58,7 @@ function M.handler(_, result, ctx, _) end -- Load user on_initialized if config.tools.on_initialized then - config.tools.on_initialized(result) + config.tools.on_initialized(result, ctx.client_id) end if config.dap.autoload_configurations then require('rustaceanvim.commands.debuggables').add_dap_debuggables() diff --git a/lua/rustaceanvim/shell.lua b/lua/rustaceanvim/shell.lua index 559284ab..cdd84dd3 100644 --- a/lua/rustaceanvim/shell.lua +++ b/lua/rustaceanvim/shell.lua @@ -44,7 +44,7 @@ end ---@param path string ---@return string function M.make_cd_command(path) - return ('cd "%s"'):format(path) + return ("cd '%s'"):format(path) end ---@param command string diff --git a/nix/plugin-overlay.nix b/nix/plugin-overlay.nix index cc94f66d..4a87f7af 100755 --- a/nix/plugin-overlay.nix +++ b/nix/plugin-overlay.nix @@ -20,21 +20,25 @@ lua5_1 = prev.lua5_1.override { packageOverrides = rustaceanvim-luaPackage-override; }; + luajit = prev.luajit.override { + packageOverrides = rustaceanvim-luaPackage-override; + }; lua51Packages = final.lua5_1.pkgs; + luajitPackages = final.luajit.pkgs; in { inherit lua5_1 lua51Packages + luajit + luajitPackages ; vimPlugins = prev.vimPlugins // { rustaceanvim = final.neovimUtils.buildNeovimPlugin { - pname = name; - version = "scm-1"; - src = self; + luaAttr = final.luajitPackages.rustaceanvim; }; }; diff --git a/spec/json_spec.lua b/spec/json_spec.lua index 838d75ae..446b6122 100644 --- a/spec/json_spec.lua +++ b/spec/json_spec.lua @@ -42,4 +42,47 @@ describe('Decode rust-analyzer settings from json', function() }, }, tbl) end) + it('persists warnings on invalid config', function() + local invalid_json_content = [[ +{ + "rust-analyzer.checkOnSave.overrideCommand": [ + "cargo", + "check", + "-p", + "service_b", + "--message-format=json" + ], + "rust-analyzer.foo.enable": true, + "rust-analyzer.foo.bar.enable": true, + "rust-analyzer.foo.bar.baz.bat": "something deeply nested", + "some-other-key.foo.bar.baz.bat": "should not be included" +} +]] + local tbl = { + ['rust-analyzer'] = { + checkOnSave = true, + }, + } + local json_tbl = json.silent_decode(invalid_json_content) + json.override_with_rust_analyzer_json_keys(tbl, json_tbl) + assert.same({ + [[ +Ignored field 'rust-analyzer.checkOnSave' of invalid type 'table': { "cargo", "check", "-p", "service_b", "--message-format=json" } +Please refer to the rust-analyzer documentation at +https://rust-analyzer.github.io/manual.html#rust-analyzer.checkOnSave +]], + }, json.get_warnings()) + end) + it('persists warnings about config parse errors', function() + local unsupported_json_content = [[ +{ + // This is a json5 comment + "rust-analyzer.foo.enable": true, +} +]] + json.silent_decode(unsupported_json_content) + assert.same({ + 'Failed to decode json: Expected object key string but found invalid token at character 5', + }, json.get_errors()) + end) end) diff --git a/spec/lsp_spec.lua b/spec/lsp_spec.lua index 7c73d492..ea02f115 100644 --- a/spec/lsp_spec.lua +++ b/spec/lsp_spec.lua @@ -1,8 +1,13 @@ --- load RustAnalyzer command -require('rustaceanvim.lsp') - local stub = require('luassert.stub') describe('LSP client API', function() + vim.g.rustaceanvim = { + server = { + -- Prevent start in detached mode + root_dir = vim.fn.tempname(), + }, + } + -- load RustAnalyzer command + require('rustaceanvim.lsp') local RustaceanConfig = require('rustaceanvim.config.internal') local Types = require('rustaceanvim.types.internal') local ra_bin = Types.evaluate(RustaceanConfig.server.cmd)[1]