Files
neovim/test/functional/plugin/lsp/codelens_spec.lua

405 lines
13 KiB
Lua

local t = require('test.testutil')
local n = require('test.functional.testnvim')()
local t_lsp = require('test.functional.plugin.lsp.testutil')
local Screen = require('test.functional.ui.screen')
local dedent = t.dedent
local eq = t.eq
local api = n.api
local exec_lua = n.exec_lua
local insert = n.insert
local feed = n.feed
local clear_notrace = t_lsp.clear_notrace
local create_server_definition = t_lsp.create_server_definition
describe('vim.lsp.codelens', function()
local text = dedent([[
struct S {
a: i32,
b: String,
}
impl S {
fn new(a: i32, b: String) -> Self {
S { a, b }
}
}
fn main() {
let s = S::new(42, String::from("Hello, world!"));
println!("S.a: {}, S.b: {}", s.a, s.b);
}
]])
local grid_with_lenses = dedent([[
{1: 1 implementation} |
struct S { |
a: i32, |
b: String, |
} |
|
impl S { |
fn new(a: i32, b: String) -> Self { |
S { a, b } |
} |
} |
|
{1: ▶︎ Run } |
fn main() { |
let s = S::new(42, String::from("Hello, world!"))|
; |
println!("S.a: {}, S.b: {}", s.a, s.b); |
} |
^ |
|
]])
local grid_without_lenses = dedent([[
struct S { |
a: i32, |
b: String, |
} |
|
impl S { |
fn new(a: i32, b: String) -> Self { |
S { a, b } |
} |
} |
|
fn main() { |
let s = S::new(42, String::from("Hello, world!"))|
; |
println!("S.a: {}, S.b: {}", s.a, s.b); |
} |
^ |
{1:~ }|*2
|
]])
--- @type test.functional.ui.screen
local screen
--- @type integer
local client_id
before_each(function()
clear_notrace()
exec_lua(create_server_definition)
screen = Screen.new(nil, 20)
client_id = exec_lua(function()
_G.server = _G._create_server({
capabilities = {
codeLensProvider = {
resolveProvider = true,
},
},
handlers = {
['textDocument/codeLens'] = function(_, _, callback)
callback(nil, {
{
data = {
kind = {
impls = {
position = {
character = 7,
line = 0,
},
},
},
version = 0,
},
range = {
['end'] = {
character = 8,
line = 0,
},
start = {
character = 7,
line = 0,
},
},
},
{
command = {
arguments = {},
command = 'rust-analyzer.runSingle',
title = '▶︎ Run ',
},
range = {
['end'] = {
character = 7,
line = 11,
},
start = {
character = 3,
line = 11,
},
},
},
})
end,
['codeLens/resolve'] = function(_, _, callback)
vim.schedule(function()
callback(nil, {
command = {
arguments = {},
command = 'rust-analyzer.showReferences',
title = '1 implementation',
},
range = {
['end'] = {
character = 8,
line = 0,
},
start = {
character = 7,
line = 0,
},
},
})
end)
end,
},
})
return vim.lsp.start({ name = 'dummy', cmd = _G.server.cmd })
end)
insert(text)
exec_lua(function()
vim.lsp.codelens.enable()
end)
screen:expect({ grid = grid_with_lenses })
end)
it('clears code lenses when disabled', function()
exec_lua(function()
vim.lsp.codelens.enable(false)
end)
screen:expect({ grid = grid_without_lenses })
end)
it('clears code lenses when sole client detaches', function()
exec_lua(function()
vim.lsp.get_client_by_id(client_id):stop()
end)
screen:expect({ grid = grid_without_lenses })
end)
it('get code lenses in the current buffer', function()
local result = exec_lua(function()
vim.api.nvim_win_set_cursor(0, { 12, 3 })
return vim.lsp.codelens.get()
end)
eq({
{
client_id = 1,
lens = {
command = {
arguments = {},
command = 'rust-analyzer.showReferences',
title = '1 implementation',
},
range = {
['end'] = {
character = 8,
line = 0,
},
start = {
character = 7,
line = 0,
},
},
},
},
{
client_id = 1,
lens = {
command = {
arguments = {},
command = 'rust-analyzer.runSingle',
title = '▶︎ Run ',
},
range = {
['end'] = {
character = 7,
line = 11,
},
start = {
character = 3,
line = 11,
},
},
},
},
}, result)
end)
it('refreshes code lenses on request', function()
feed('ggdd')
screen:expect([[
{1: 1 implementation} |
^a: i32, |
b: String, |
} |
|
impl S { |
fn new(a: i32, b: String) -> Self { |
S { a, b } |
} |
} |
|
{1: ▶︎ Run } |
fn main() { |
let s = S::new(42, String::from("Hello, world!"))|
; |
println!("S.a: {}, S.b: {}", s.a, s.b); |
} |
|
{1:~ }|
|
]])
exec_lua(function()
vim.lsp.codelens.on_refresh(
nil,
nil,
{ method = 'workspace/codeLens/refresh', client_id = client_id }
)
end)
screen:expect([[
{1: 1 implementation} |
^a: i32, |
b: String, |
} |
|
impl S { |
fn new(a: i32, b: String) -> Self { |
S { a, b } |
} |
} |
|
fn main() { |
{1: ▶︎ Run } |
let s = S::new(42, String::from("Hello, world!"))|
; |
println!("S.a: {}, S.b: {}", s.a, s.b); |
} |
|
{1:~ }|
|
]])
end)
it('ignores stale codeLens/resolve responses', function()
clear_notrace()
exec_lua(create_server_definition)
insert('line1\nline2\n')
exec_lua(function()
local codelens_request_count = 0
_G.stale_resolve_sent = false
_G.server = _G._create_server({
capabilities = {
codeLensProvider = {
resolveProvider = true,
},
},
handlers = {
['textDocument/codeLens'] = function(_, _, callback)
codelens_request_count = codelens_request_count + 1
if codelens_request_count == 1 then
callback(nil, {
{
range = {
['end'] = {
character = 1,
line = 0,
},
start = {
character = 0,
line = 0,
},
},
},
})
else
callback(nil, {})
end
end,
['codeLens/resolve'] = function(_, lens, callback)
vim.defer_fn(function()
_G.stale_resolve_sent = true
callback(nil, {
command = {
arguments = {},
command = 'dummy.command',
title = 'resolved',
},
range = lens.range,
})
end, 100)
end,
},
})
local stale_client_id = vim.lsp.start({ name = 'dummy', cmd = _G.server.cmd })
vim.lsp.codelens.enable()
vim.wait(1000, function()
return #vim.lsp.codelens.get() > 0
end)
vim.api.nvim__redraw({ flush = true })
vim.lsp.codelens.on_refresh(nil, nil, {
method = 'workspace/codeLens/refresh',
client_id = stale_client_id,
})
assert(
vim.wait(1000, function()
return _G.stale_resolve_sent
end),
'timed out waiting for stale resolve response'
)
end)
eq('', api.nvim_get_vvar('errmsg'))
end)
it('clears extmarks beyond the bottom of the buffer', function()
feed('12G4dd')
screen:expect([[
{1: 1 implementation} |
struct S { |
a: i32, |
b: String, |
} |
|
impl S { |
fn new(a: i32, b: String) -> Self { |
S { a, b } |
} |
} |
|
^ |
{1:~ }|*6
4 fewer lines |
]])
end)
after_each(function()
api.nvim_exec_autocmds('VimLeavePre', { modeline = false })
end)
end)