aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--runtime/lua/vim/lsp.lua33
-rw-r--r--runtime/lua/vim/lsp/diagnostic.lua12
-rw-r--r--runtime/lua/vim/lsp/util.lua42
-rw-r--r--test/functional/plugin/lsp_spec.lua125
4 files changed, 158 insertions, 54 deletions
diff --git a/runtime/lua/vim/lsp.lua b/runtime/lua/vim/lsp.lua
index e48d879495..26700288af 100644
--- a/runtime/lua/vim/lsp.lua
+++ b/runtime/lua/vim/lsp.lua
@@ -1179,38 +1179,11 @@ function lsp._vim_exit_handler()
client.stop()
end
- local function wait_async(timeout, ms, predicate, cb)
- local timer = uv.new_timer()
- local time = 0
-
- local function done(in_time)
- timer:stop()
- timer:close()
- cb(in_time)
+ if not vim.wait(500, function() return tbl_isempty(active_clients) end, 50) then
+ for _, client in pairs(active_clients) do
+ client.stop(true)
end
-
- timer:start(0, ms, function()
- if predicate() == true then
- done(true)
- return
- end
-
- if time == timeout then
- done(false)
- return
- end
-
- time = time + ms
- end)
end
-
- wait_async(500, 50, function() return tbl_isempty(active_clients) end, function(in_time)
- if not in_time then
- for _, client in pairs(active_clients) do
- client.stop(true)
- end
- end
- end)
end
nvim_command("autocmd VimLeavePre * lua vim.lsp._vim_exit_handler()")
diff --git a/runtime/lua/vim/lsp/diagnostic.lua b/runtime/lua/vim/lsp/diagnostic.lua
index e6132e78bf..6f2f846a3b 100644
--- a/runtime/lua/vim/lsp/diagnostic.lua
+++ b/runtime/lua/vim/lsp/diagnostic.lua
@@ -406,9 +406,7 @@ function M.get_line_diagnostics(bufnr, line_nr, opts, client_id)
line_diagnostics = filter_by_severity_limit(opts.severity_limit, line_diagnostics)
end
- if opts.severity_sort then
- table.sort(line_diagnostics, function(a, b) return a.severity < b.severity end)
- end
+ table.sort(line_diagnostics, function(a, b) return a.severity < b.severity end)
return line_diagnostics
end
@@ -997,6 +995,8 @@ end
--- - See |vim.lsp.diagnostic.set_signs()|
--- - update_in_insert: (default=false)
--- - Update diagnostics in InsertMode or wait until InsertLeave
+--- - severity_sort: (default=false)
+--- - Sort diagnostics (and thus signs and virtual text)
function M.on_publish_diagnostics(_, _, params, client_id, _, config)
local uri = params.uri
local bufnr = vim.uri_to_bufnr(uri)
@@ -1007,6 +1007,10 @@ function M.on_publish_diagnostics(_, _, params, client_id, _, config)
local diagnostics = params.diagnostics
+ if config and if_nil(config.severity_sort, false) then
+ table.sort(diagnostics, function(a, b) return a.severity > b.severity end)
+ end
+
-- Always save the diagnostics, even if the buf is not loaded.
-- Language servers may report compile or build errors via diagnostics
-- Users should be able to find these, even if they're in files which
@@ -1034,6 +1038,7 @@ function M.display(diagnostics, bufnr, client_id, config)
underline = true,
virtual_text = true,
update_in_insert = false,
+ severity_sort = false,
}, config)
-- TODO(tjdevries): Consider how we can make this a "standardized" kind of thing for |lsp-handlers|.
@@ -1116,7 +1121,6 @@ end
---@return table {popup_bufnr, win_id}
function M.show_line_diagnostics(opts, bufnr, line_nr, client_id)
opts = opts or {}
- opts.severity_sort = if_nil(opts.severity_sort, true)
local show_header = if_nil(opts.show_header, true)
diff --git a/runtime/lua/vim/lsp/util.lua b/runtime/lua/vim/lsp/util.lua
index 71ec85381b..92ec447b55 100644
--- a/runtime/lua/vim/lsp/util.lua
+++ b/runtime/lua/vim/lsp/util.lua
@@ -914,6 +914,23 @@ function M.make_floating_popup_options(width, height, opts)
}
end
+local function _should_add_to_tagstack(new_item)
+ local stack = vim.fn.gettagstack()
+
+ -- Check if we're at the bottom of the tagstack.
+ if stack.curidx <= 1 then return true end
+
+ local top_item = stack.items[stack.curidx-1]
+
+ -- Check if the item at the top of the tagstack is exactly the
+ -- same as the one we want to push.
+ if top_item.tagname ~= new_item.tagname then return true end
+ for i, v in ipairs(top_item.from) do
+ if v ~= new_item.from[i] then return true end
+ end
+ return false
+end
+
--- Jumps to a location.
---
--@param location (`Location`|`LocationLink`)
@@ -922,22 +939,33 @@ function M.jump_to_location(location)
-- location may be Location or LocationLink
local uri = location.uri or location.targetUri
if uri == nil then return end
- local bufnr = vim.uri_to_bufnr(uri)
- -- Save position in jumplist
- vim.cmd "normal! m'"
- -- Push a new item into tagstack
- local from = {vim.fn.bufnr('%'), vim.fn.line('.'), vim.fn.col('.'), 0}
- local items = {{tagname=vim.fn.expand('<cword>'), from=from}}
- vim.fn.settagstack(vim.fn.win_getid(), {items=items}, 't')
+ local from_bufnr = vim.fn.bufnr('%')
+ local from = {from_bufnr, vim.fn.line('.'), vim.fn.col('.'), 0}
+ local item = {tagname=vim.fn.expand('<cword>'), from=from}
--- Jump to new location (adjusting for UTF-16 encoding of characters)
+ local bufnr = vim.uri_to_bufnr(uri)
api.nvim_set_current_buf(bufnr)
api.nvim_buf_set_option(0, 'buflisted', true)
local range = location.range or location.targetSelectionRange
local row = range.start.line
local col = get_line_byte_from_position(0, range.start)
+ -- This prevents the tagstack to be filled with items that provide
+ -- no motion when CTRL-T is pressed because they're both the source
+ -- and the destination.
+ local motionless =
+ bufnr == from_bufnr and
+ row+1 == from[2] and col+1 == from[3]
+ if not motionless and _should_add_to_tagstack(item) then
+ local winid = vim.fn.win_getid()
+ local items = {item}
+ vim.fn.settagstack(winid, {items=items}, 't')
+ end
+
+ -- Jump to new location
api.nvim_win_set_cursor(0, {row + 1, col})
+
return true
end
diff --git a/test/functional/plugin/lsp_spec.lua b/test/functional/plugin/lsp_spec.lua
index 557f8a206f..6eda515fb6 100644
--- a/test/functional/plugin/lsp_spec.lua
+++ b/test/functional/plugin/lsp_spec.lua
@@ -1820,20 +1820,36 @@ describe('LSP', function()
end)
describe('lsp.util.jump_to_location', function()
- local target_bufnr
+ local default_target_bufnr
+ local default_target_uri = 'file://fake/uri'
- before_each(function()
- target_bufnr = exec_lua [[
- local bufnr = vim.uri_to_bufnr("file://fake/uri")
- local lines = {"1st line of text", "å å ɧ 汉语 ↥ 🤦 🦄"}
+ local create_buf = function(uri, lines)
+ for i, line in ipairs(lines) do
+ lines[i] = '"' .. line .. '"'
+ end
+ lines = table.concat(lines, ", ")
+
+ -- Let's set "hidden" to true in order to avoid errors when switching
+ -- between buffers in test.
+ local code = string.format([[
+ vim.api.nvim_set_option('hidden', true)
+
+ local bufnr = vim.uri_to_bufnr("%s")
+ local lines = {%s}
vim.api.nvim_buf_set_lines(bufnr, 0, 1, false, lines)
return bufnr
- ]]
+ ]], uri, lines)
+
+ return exec_lua(code)
+ end
+
+ before_each(function()
+ default_target_bufnr = create_buf(default_target_uri, {'1st line of text', 'å å ɧ 汉语 ↥ 🤦 🦄'})
end)
- local location = function(start_line, start_char, end_line, end_char)
+ local location = function(uri, start_line, start_char, end_line, end_char)
return {
- uri = "file://fake/uri",
+ uri = uri,
range = {
start = { line = start_line, character = start_char },
["end"] = { line = end_line, character = end_char },
@@ -1841,9 +1857,9 @@ describe('LSP', function()
}
end
- local jump = function(msg)
+ local jump = function(bufnr, msg)
eq(true, exec_lua('return vim.lsp.util.jump_to_location(...)', msg))
- eq(target_bufnr, exec_lua[[return vim.fn.bufnr('%')]])
+ eq(bufnr, exec_lua[[return vim.fn.bufnr('%')]])
return {
line = exec_lua[[return vim.fn.line('.')]],
col = exec_lua[[return vim.fn.col('.')]],
@@ -1851,13 +1867,13 @@ describe('LSP', function()
end
it('jumps to a Location', function()
- local pos = jump(location(0, 9, 0, 9))
+ local pos = jump(default_target_bufnr, location(default_target_uri, 0, 9, 0, 9))
eq(1, pos.line)
eq(10, pos.col)
end)
it('jumps to a LocationLink', function()
- local pos = jump({
+ local pos = jump(default_target_bufnr, {
targetUri = "file://fake/uri",
targetSelectionRange = {
start = { line = 0, character = 4 },
@@ -1873,11 +1889,94 @@ describe('LSP', function()
end)
it('jumps to the correct multibyte column', function()
- local pos = jump(location(1, 2, 1, 2))
+ local pos = jump(default_target_bufnr, location(default_target_uri, 1, 2, 1, 2))
eq(2, pos.line)
eq(4, pos.col)
eq('å', exec_lua[[return vim.fn.expand('<cword>')]])
end)
+
+ it('should not push item to tagstack if destination is the same as source', function()
+ -- Set cursor at the 2nd line, 1st character. This is the source position
+ -- for the test, and will also be the destination one, making the cursor
+ -- "motionless", thus not triggering a push to the tagstack.
+ exec_lua(string.format([[
+ vim.api.nvim_win_set_buf(0, %d)
+ vim.api.nvim_win_set_cursor(0, {2, 0})
+ ]], default_target_bufnr))
+
+ -- Jump to 'f' in 'foobar', at the 2nd line.
+ jump(default_target_bufnr, location(default_target_uri, 1, 0, 1, 0))
+
+ local stack = exec_lua[[return vim.fn.gettagstack()]]
+ eq(0, stack.length)
+ end)
+
+ it('should not push the same item from same buffer twice to tagstack', function()
+ -- Set cursor at the 2nd line, 5th character.
+ exec_lua(string.format([[
+ vim.api.nvim_win_set_buf(0, %d)
+ vim.api.nvim_win_set_cursor(0, {2, 4})
+ ]], default_target_bufnr))
+
+ local stack
+
+ -- Jump to 1st line, 1st column.
+ jump(default_target_bufnr, location(default_target_uri, 0, 0, 0, 0))
+
+ stack = exec_lua[[return vim.fn.gettagstack()]]
+ eq({default_target_bufnr, 2, 5, 0}, stack.items[1].from)
+
+ -- Go back to 5th character at 2nd line, which is currently at the top of
+ -- the tagstack.
+ exec_lua(string.format([[
+ vim.api.nvim_win_set_cursor(0, {2, 4})
+ ]], default_target_bufnr))
+
+ -- Jump again to 1st line, 1st column. Since we're jumping from the same
+ -- position we have just jumped from, this jump shouldn't be pushed to
+ -- the tagstack.
+ jump(default_target_bufnr, location(default_target_uri, 0, 0, 0, 0))
+
+ stack = exec_lua[[return vim.fn.gettagstack()]]
+ eq({default_target_bufnr, 2, 5, 0}, stack.items[1].from)
+ eq(1, stack.length)
+ end)
+
+ it('should not push the same item from another buffer twice to tagstack', function()
+ local target_uri = 'file://foo/bar'
+ local target_bufnr = create_buf(target_uri, {'this is a line', 'foobar'})
+
+ -- Set cursor at the 1st line, 3rd character of the default test buffer.
+ exec_lua(string.format([[
+ vim.api.nvim_win_set_buf(0, %d)
+ vim.api.nvim_win_set_cursor(0, {1, 2})
+ ]], default_target_bufnr))
+
+ local stack
+
+ -- Jump to 1st line, 1st column of a different buffer from the source
+ -- position.
+ jump(target_bufnr, location(target_uri, 0, 0, 0, 0))
+
+ stack = exec_lua[[return vim.fn.gettagstack()]]
+ eq({default_target_bufnr, 1, 3, 0}, stack.items[1].from)
+
+ -- Go back to 3rd character at 1st line of the default test buffer, which
+ -- is currently at the top of the tagstack.
+ exec_lua(string.format([[
+ vim.api.nvim_win_set_buf(0, %d)
+ vim.api.nvim_win_set_cursor(0, {1, 2})
+ ]], default_target_bufnr))
+
+ -- Jump again to 1st line, 1st column of the different buffer. Since
+ -- we're jumping from the same position we have just jumped from, this
+ -- jump shouldn't be pushed to the tagstack.
+ jump(target_bufnr, location(target_uri, 0, 0, 0, 0))
+
+ stack = exec_lua[[return vim.fn.gettagstack()]]
+ eq({default_target_bufnr, 1, 3, 0}, stack.items[1].from)
+ eq(1, stack.length)
+ end)
end)
describe('lsp.util._make_floating_popup_size', function()