Skip to content

Commit

Permalink
fix(neotest): use all LSP clients to search for test positions (#209)
Browse files Browse the repository at this point in the history
  • Loading branch information
mrcjkb authored Feb 6, 2024
1 parent b42e081 commit 8940ef5
Show file tree
Hide file tree
Showing 4 changed files with 112 additions and 60 deletions.
4 changes: 4 additions & 0 deletions .github/workflows/review-checklist.yml
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,10 @@ on:
pull_request_target:
types: [opened, review_requested]

concurrency:
group: ${{ github.ref }}
cancel-in-progress: false

jobs:
review-checklist:
name: Review Checklist
Expand Down
10 changes: 10 additions & 0 deletions lua/rustaceanvim/compat.lua
Original file line number Diff line number Diff line change
@@ -1,12 +1,22 @@
---@diagnostic disable: deprecated, duplicate-doc-field, duplicate-doc-alias
---@mod rustaceanvim.compat Functions for backward compatibility with older Neovim versions
--- and with compatibility type annotations to make the type checker
--- happy for both stable and nightly neovim versions.

local M = {}

M.joinpath = vim.fs.joinpath or function(...)
return (table.concat({ ... }, '/'):gsub('//+', '/'))
end

---@class vim.lsp.get_clients.filter
---@field id integer|nil Match clients by id
---@field bufnr integer|nil match clients attached to the given buffer
---@field name string|nil match clients by name
---@field method string|nil match client by supported method name

---@alias vim.lsp.get_active_clients.filter vim.lsp.get_clients.filter

M.get_clients = vim.lsp.get_clients or vim.lsp.get_active_clients

M.uv = vim.uv or vim.loop
Expand Down
80 changes: 32 additions & 48 deletions lua/rustaceanvim/neotest/init.lua
Original file line number Diff line number Diff line change
Expand Up @@ -30,29 +30,17 @@
local lib = require('neotest.lib')
local nio = require('nio')
local trans = require('rustaceanvim.neotest.trans')
local cargo = require('rustaceanvim.cargo')

---@type neotest.Adapter
local NeotestAdapter = { name = 'rustaceanvim' }

---@param file_name string
---@return string | nil
NeotestAdapter.root = function(file_name)
local cargo = require('rustaceanvim.cargo')
return cargo.get_root_dir(file_name)
end

---@param name string
---@return integer
local function find_buffer_by_name(name)
for _, bufnr in ipairs(vim.api.nvim_list_bufs()) do
local buf_name = vim.api.nvim_buf_get_name(bufnr)
if buf_name == name then
return bufnr
end
end
return -1
end

---@param file_path string
---@return boolean
NeotestAdapter.is_test_file = function(file_path)
Expand All @@ -67,26 +55,17 @@ end
NeotestAdapter.discover_positions = function(file_path)
---@type rustaceanvim.neotest.Position[]
local positions = {}
local bufnr = find_buffer_by_name(file_path)
if bufnr == -1 then
---@diagnostic disable-next-line: missing-parameter
return lib.positions.parse_tree(positions)
end
local rust_analyzer = require('rustaceanvim.rust_analyzer')
if #rust_analyzer.get_active_rustaceanvim_clients(bufnr) == 0 then
---@diagnostic disable-next-line: missing-parameter
return lib.positions.parse_tree(positions)
end
local params = {
textDocument = vim.lsp.util.make_text_document_params(bufnr),
position = nil, -- get em all
}
local future = nio.control.future()
rust_analyzer.buf_request(bufnr, 'experimental/runnables', params, function(_, runnables)
future.set(runnables)
rust_analyzer.file_request(file_path, 'experimental/runnables', nil, function(err, runnables)
if err then
future.set_error(err)
else
future.set(runnables)
end
end)
local runnables = future:wait()
if type(runnables) ~= 'table' or #runnables == 0 then
local ok, runnables = pcall(future.wait)
if not ok or type(runnables) ~= 'table' or #runnables == 0 then
---@diagnostic disable-next-line: missing-parameter
return lib.positions.parse_tree(positions)
end
Expand Down Expand Up @@ -139,24 +118,29 @@ NeotestAdapter.discover_positions = function(file_path)
end
end
end
local file_pos = {
id = file_path,
name = file_path,
type = 'file',
path = file_path,
range = { 0, 0, max_end_row, 0 },
runnable = namespace_count > 1 and crate_runnable or namespace_runnable,
}
table.insert(sorted_positions, 1, file_pos)
local crate_pos = {
id = 'rustaceanvim:' .. crate_runnable.args.workspaceRoot,
name = 'suite',
type = 'dir',
path = crate_runnable.args.workspaceRoot,
range = { 0, 0, 0, 0 },
runnable = crate_runnable,
}
table.insert(sorted_positions, 1, crate_pos)
if namespace_runnable then
local file_pos = {
id = file_path,
name = file_path,
type = 'file',
path = file_path,
range = { 0, 0, max_end_row, 0 },
runnable = namespace_runnable,
}
table.insert(sorted_positions, 1, file_pos)
end
if crate_runnable and #sorted_positions > 0 then
-- Only insert a crate runnable position if there exist positions
local crate_pos = {
id = 'rustaceanvim:' .. crate_runnable.args.workspaceRoot,
name = 'suite',
type = 'dir',
path = crate_runnable.args.workspaceRoot,
range = { 0, 0, 0, 0 },
runnable = crate_runnable,
}
table.insert(sorted_positions, 1, crate_pos)
end
---@diagnostic disable-next-line: missing-parameter
return lib.positions.parse_tree(sorted_positions)
end
Expand Down
78 changes: 66 additions & 12 deletions lua/rustaceanvim/rust_analyzer.lua
Original file line number Diff line number Diff line change
Expand Up @@ -5,10 +5,14 @@ local compat = require('rustaceanvim.compat')
---@class RustAnalyzerClientAdapter
local M = {}

---@param bufnr? number
---@param bufnr number | nil 0 for the current buffer, `nil` for no buffer filter
---@param filter? vim.lsp.get_clients.filter
---@return lsp.Client[]
M.get_active_rustaceanvim_clients = function(bufnr)
local filter = { name = 'rust-analyzer' }
M.get_active_rustaceanvim_clients = function(bufnr, filter)
---@type vim.lsp.get_clients.filter
filter = vim.tbl_deep_extend('force', filter or {}, {
name = 'rust-analyzer',
})
if bufnr then
filter.bufnr = bufnr
end
Expand All @@ -25,11 +29,9 @@ M.buf_request = function(bufnr, method, params, handler)
bufnr = vim.api.nvim_get_current_buf()
end
local client_found = false
for _, client in ipairs(M.get_active_rustaceanvim_clients()) do
if client.supports_method(method, { bufnr = bufnr }) then
client.request(method, params, handler, bufnr)
client_found = true
end
for _, client in ipairs(M.get_active_rustaceanvim_clients(bufnr, { method = method })) do
client.request(method, params, handler, bufnr)
client_found = true
end
if not client_found then
local error_msg = 'No rust-analyzer client for ' .. method .. ' attched to buffer ' .. bufnr
Expand All @@ -47,16 +49,68 @@ M.buf_request = function(bufnr, method, params, handler)
end
end

----@param name string
----@return integer
local function find_buffer_by_name(name)
for _, bufnr in ipairs(vim.api.nvim_list_bufs()) do
local buf_name = vim.api.nvim_buf_get_name(bufnr)
if buf_name == name then
return bufnr
end
end
return 0
end

---@param file_path string Search for clients with a root_dir matching this file path
---@param method string LSP method name
---@param params table|nil Parameters to send to the server
M.notify = function(method, params)
---@param handler? lsp.Handler See |lsp-handler|
--- If nil, follows resolution strategy defined in |lsp-handler-configuration|
M.file_request = function(file_path, method, params, handler)
local client_found = false
for _, client in ipairs(M.get_active_rustaceanvim_clients()) do
if client.supports_method(method) then
client.notify(method, params)
for _, client in ipairs(M.get_active_rustaceanvim_clients(nil, { method = method })) do
local root_dir = client.config.root_dir
if root_dir and vim.startswith(file_path, root_dir) then
local bufnr = find_buffer_by_name(file_path)
if not params then
params = {
textDocument = {
uri = vim.uri_from_fname(file_path),
},
position = nil,
}
end
client.request(method, params, handler, bufnr)
client_found = true
if bufnr == -1 then
return
end
end
end
if not client_found then
local error_msg = 'No rust-analyzer client for ' .. method .. ' and file ' .. file_path
if handler then
---@type lsp.HandlerContext
local ctx = {
bufnr = -1,
client_id = -1,
method = method,
}
handler({ code = -1, message = error_msg }, nil, ctx)
else
vim.notify(error_msg, vim.log.levels.ERROR)
end
end
end

---@param method string LSP method name
---@param params table|nil Parameters to send to the server
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)
client_found = true
end
if not client_found then
vim.notify('No rust-analyzer client found for method: ' .. method, vim.log.levels.ERROR)
end
Expand Down

0 comments on commit 8940ef5

Please sign in to comment.