From e8b3bb6c819317a03d48cff4e367f978ce86dc4c Mon Sep 17 00:00:00 2001 From: Marc Jakobi Date: Mon, 23 Oct 2023 00:15:41 +0200 Subject: [PATCH] fix(lsp): compatibility with Neovim 0.9 --- CHANGELOG.md | 2 +- lua/rustaceanvim/compat.lua | 12 ++++++++++++ lua/rustaceanvim/lsp.lua | 23 ++++++----------------- lua/rustaceanvim/rust_analyzer.lua | 18 ++++++++++++++++-- nix/ci-overlay.nix | 5 ++++- spec/lsp_spec.lua | 13 +++++++++++-- 6 files changed, 50 insertions(+), 23 deletions(-) create mode 100644 lua/rustaceanvim/compat.lua diff --git a/CHANGELOG.md b/CHANGELOG.md index 1d234fd9..144cc985 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -10,7 +10,7 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0 ### Fixed - Add support for `workspace/didChangeWorkspaceFolders` to prevent more than one rust-analyzer server from spawning per Neovim instance [#7](https://github.com/mrcjkb/rustaceanvim/issues/7). -- Implement `vim.fs.joinpath` for Neovim 0.9 compatibility. +- Neovim 0.9 compatibility [#9](https://github.com/mrcjkb/rustaceanvim/issues/9). ## [3.0.0] - 2023-10-22 diff --git a/lua/rustaceanvim/compat.lua b/lua/rustaceanvim/compat.lua new file mode 100644 index 00000000..ac00b207 --- /dev/null +++ b/lua/rustaceanvim/compat.lua @@ -0,0 +1,12 @@ +---@diagnostic disable: deprecated +---@mod rustaceanvim.compat Functions for backward compatibility with older Neovim versions + +local M = {} + +M.joinpath = vim.fs.joinpath or function(...) + return (table.concat({ ... }, '/'):gsub('//+', '/')) +end + +M.get_clients = vim.lsp.get_clients or vim.lsp.get_active_clients + +return M diff --git a/lua/rustaceanvim/lsp.lua b/lua/rustaceanvim/lsp.lua index f1e97aee..a45f8cb4 100644 --- a/lua/rustaceanvim/lsp.lua +++ b/lua/rustaceanvim/lsp.lua @@ -2,10 +2,9 @@ local M = {} ---@type RustaceanConfig local config = require('rustaceanvim.config.internal') local methods = require('vim.lsp.protocol').Methods - -local joinpath = vim.fs.joinpath or function(...) - return (table.concat({ ... }, '/'):gsub('//+', '/')) -end +local compat = require('rustaceanvim.compat') +local rust_analyzer = require('rustaceanvim.rust_analyzer') +local joinpath = compat.joinpath local function override_apply_text_edits() local old_func = vim.lsp.util.apply_text_edits @@ -26,7 +25,7 @@ local function is_library(fname) for _, item in ipairs { toolchains, registry } do if fname:sub(1, #item) == item then - local clients = vim.lsp.get_clients { name = 'rust-analyzer' } + local clients = rust_analyzer.get_active_rustaceanvim_clients() return clients[#clients].config.root_dir end end @@ -75,16 +74,6 @@ local function get_root_dir(fname) })[1]) end ----@param bufnr? number ----@return lsp.Client[] -local function get_active_rustaceanvim_clients(bufnr) - local filter = { name = 'rust-analyzer' } - if bufnr then - filter.bufnr = bufnr - end - return vim.lsp.get_clients(filter) -end - local function is_in_workspace(client, root_dir) if not client.workspace_folders then return false @@ -109,7 +98,7 @@ M.start = function() lsp_start_opts.root_dir = root_dir -- Check if a client is already running and add the workspace folder if necessary. - for _, client in pairs(get_active_rustaceanvim_clients()) do + for _, client in pairs(rust_analyzer.get_active_rustaceanvim_clients()) do if not is_in_workspace(client, root_dir) then local workspace_folder = { uri = vim.uri_from_fname(root_dir), name = root_dir } local params = { @@ -220,7 +209,7 @@ end ---@return table[] clients A list of clients that will be stopped M.stop = function() local bufnr = vim.api.nvim_get_current_buf() - local clients = get_active_rustaceanvim_clients(bufnr) + local clients = rust_analyzer.get_active_rustaceanvim_clients(bufnr) vim.lsp.stop_client(clients) return clients end diff --git a/lua/rustaceanvim/rust_analyzer.lua b/lua/rustaceanvim/rust_analyzer.lua index 181b1c2f..c0198cfc 100644 --- a/lua/rustaceanvim/rust_analyzer.lua +++ b/lua/rustaceanvim/rust_analyzer.lua @@ -1,6 +1,20 @@ +---@mod rustaceanvim.rust_analyzer Functions for interacting with rust-analyzer + +local compat = require('rustaceanvim.compat') + ---@class RustAnalyzerClientAdapter local M = {} +---@param bufnr? number +---@return lsp.Client[] +M.get_active_rustaceanvim_clients = function(bufnr) + local filter = { name = 'rust-analyzer' } + if bufnr then + filter.bufnr = bufnr + end + return compat.get_clients(filter) +end + ---@param bufnr integer Buffer handle, or 0 for current. ---@param method string LSP method name ---@param params table|nil Parameters to send to the server @@ -10,7 +24,7 @@ M.buf_request = function(bufnr, method, params, handler) if bufnr == nil or bufnr == 0 then bufnr = vim.api.nvim_get_current_buf() end - for _, client in ipairs(vim.lsp.get_clients { name = 'rust-analyzer' }) do + for _, client in ipairs(M.get_active_rustaceanvim_clients()) do if client.supports_method(method, { bufnr = bufnr }) then client.request(method, params, handler, bufnr) end @@ -20,7 +34,7 @@ end ---@param method string LSP method name ---@param params table|nil Parameters to send to the server M.notify = function(method, params) - for _, client in ipairs(vim.lsp.get_clients { name = 'rust-analyzer' }) do + for _, client in ipairs(M.get_active_rustaceanvim_clients()) do if client.supports_method(method) then client.notify(method, params) end diff --git a/nix/ci-overlay.nix b/nix/ci-overlay.nix index 33e7dbf3..aea1e787 100755 --- a/nix/ci-overlay.nix +++ b/nix/ci-overlay.nix @@ -33,7 +33,10 @@ neovim = nvim-wrapped; # luaPackages = ps: with ps; []; - # extraPackages = []; + extraPackages = with final; [ + rust-analyzer + cargo + ]; preCheck = '' export HOME=$(realpath .) diff --git a/spec/lsp_spec.lua b/spec/lsp_spec.lua index 65b6b6c4..2ecbfd69 100644 --- a/spec/lsp_spec.lua +++ b/spec/lsp_spec.lua @@ -1,11 +1,20 @@ +local uv = vim.uv or vim.loop +-- load RustAnalyzer command +require('rustaceanvim.lsp') + +local stub = require('luassert.stub') describe('LSP client API', function() local RustaceanConfig = require('rustaceanvim.config.internal') local Types = require('rustaceanvim.types.internal') local ra_bin = Types.evaluate(RustaceanConfig.server.cmd)[1] if vim.fn.executable(ra_bin) ~= 0 then it('Can spin up rust-analyzer.', function() - --- TODO: Figure out how to add tests for this - print('TODO') + local lsp_start = stub(vim.lsp, 'start') + vim.cmd.e('test.rs') + vim.cmd.RustAnalyzer('start') + -- TODO: Use something less flaky, e.g. a timeout + uv.sleep(5000) + assert.stub(lsp_start).was_called() end) end end)