aboutsummaryrefslogtreecommitdiff
path: root/runtime/lua/vim/lsp/util.lua
diff options
context:
space:
mode:
authorJosh Rahm <rahm@google.com>2022-10-11 19:00:52 +0000
committerJosh Rahm <rahm@google.com>2022-10-11 19:00:52 +0000
commit21e2e46242033c7aaa6ccfb23e256680816c063c (patch)
treef089522cfb145d6e9c8a86a01d8e454ce5501e20 /runtime/lua/vim/lsp/util.lua
parent179d3ed87b17988f5fe00d8b99f2611a28212be7 (diff)
parent760b399f6c0c6470daa0663752bd22886997f9e6 (diff)
downloadrneovim-21e2e46242033c7aaa6ccfb23e256680816c063c.tar.gz
rneovim-21e2e46242033c7aaa6ccfb23e256680816c063c.tar.bz2
rneovim-21e2e46242033c7aaa6ccfb23e256680816c063c.zip
Merge remote-tracking branch 'upstream/master' into floattitlefloattitle
Diffstat (limited to 'runtime/lua/vim/lsp/util.lua')
-rw-r--r--runtime/lua/vim/lsp/util.lua174
1 files changed, 116 insertions, 58 deletions
diff --git a/runtime/lua/vim/lsp/util.lua b/runtime/lua/vim/lsp/util.lua
index 283099bbcf..b0f9c1660e 100644
--- a/runtime/lua/vim/lsp/util.lua
+++ b/runtime/lua/vim/lsp/util.lua
@@ -1,6 +1,5 @@
local protocol = require('vim.lsp.protocol')
local snippet = require('vim.lsp._snippet')
-local vim = vim
local validate = vim.validate
local api = vim.api
local list_extend = vim.list_extend
@@ -110,6 +109,15 @@ local function split_lines(value)
return split(value, '\n', true)
end
+---@private
+local function create_window_without_focus()
+ local prev = vim.api.nvim_get_current_win()
+ vim.cmd.new()
+ local new = vim.api.nvim_get_current_win()
+ vim.api.nvim_set_current_win(prev)
+ return new
+end
+
--- Convert byte index to `encoding` index.
--- Convenience wrapper around vim.str_utfindex
---@param line string line to be indexed
@@ -459,35 +467,52 @@ function M.apply_text_edits(text_edits, bufnr, offset_encoding)
text = split(text_edit.newText, '\n', true),
}
- -- Some LSP servers may return +1 range of the buffer content but nvim_buf_set_text can't accept it so we should fix it here.
local max = api.nvim_buf_line_count(bufnr)
- if max <= e.start_row or max <= e.end_row then
- local len = #(get_line(bufnr, max - 1) or '')
- if max <= e.start_row then
- e.start_row = max - 1
- e.start_col = len
- table.insert(e.text, 1, '')
- end
+ -- If the whole edit is after the lines in the buffer we can simply add the new text to the end
+ -- of the buffer.
+ if max <= e.start_row then
+ api.nvim_buf_set_lines(bufnr, max, max, false, e.text)
+ else
+ local last_line_len = #(get_line(bufnr, math.min(e.end_row, max - 1)) or '')
+ -- Some LSP servers may return +1 range of the buffer content but nvim_buf_set_text can't
+ -- accept it so we should fix it here.
if max <= e.end_row then
e.end_row = max - 1
- e.end_col = len
+ e.end_col = last_line_len
+ has_eol_text_edit = true
+ else
+ -- If the replacement is over the end of a line (i.e. e.end_col is out of bounds and the
+ -- replacement text ends with a newline We can likely assume that the replacement is assumed
+ -- to be meant to replace the newline with another newline and we need to make sure this
+ -- doesn't add an extra empty line. E.g. when the last line to be replaced contains a '\r'
+ -- in the file some servers (clangd on windows) will include that character in the line
+ -- while nvim_buf_set_text doesn't count it as part of the line.
+ if
+ e.end_col > last_line_len
+ and #text_edit.newText > 0
+ and string.sub(text_edit.newText, -1) == '\n'
+ then
+ table.remove(e.text, #e.text)
+ end
end
- has_eol_text_edit = true
- end
- api.nvim_buf_set_text(bufnr, e.start_row, e.start_col, e.end_row, e.end_col, e.text)
-
- -- Fix cursor position.
- local row_count = (e.end_row - e.start_row) + 1
- if e.end_row < cursor.row then
- cursor.row = cursor.row + (#e.text - row_count)
- is_cursor_fixed = true
- elseif e.end_row == cursor.row and e.end_col <= cursor.col then
- cursor.row = cursor.row + (#e.text - row_count)
- cursor.col = #e.text[#e.text] + (cursor.col - e.end_col)
- if #e.text == 1 then
- cursor.col = cursor.col + e.start_col
+ -- Make sure we don't go out of bounds for e.end_col
+ e.end_col = math.min(last_line_len, e.end_col)
+
+ api.nvim_buf_set_text(bufnr, e.start_row, e.start_col, e.end_row, e.end_col, e.text)
+
+ -- Fix cursor position.
+ local row_count = (e.end_row - e.start_row) + 1
+ if e.end_row < cursor.row then
+ cursor.row = cursor.row + (#e.text - row_count)
+ is_cursor_fixed = true
+ elseif e.end_row == cursor.row and e.end_col <= cursor.col then
+ cursor.row = cursor.row + (#e.text - row_count)
+ cursor.col = #e.text[#e.text] + (cursor.col - e.end_col)
+ if #e.text == 1 then
+ cursor.col = cursor.col + e.start_col
+ end
+ is_cursor_fixed = true
end
- is_cursor_fixed = true
end
end
@@ -755,8 +780,11 @@ local function create_file(change)
-- from spec: Overwrite wins over `ignoreIfExists`
local fname = vim.uri_to_fname(change.uri)
if not opts.ignoreIfExists or opts.overwrite then
+ vim.fn.mkdir(vim.fs.dirname(fname), 'p')
local file = io.open(fname, 'w')
- file:close()
+ if file then
+ file:close()
+ end
end
vim.fn.bufadd(fname)
end
@@ -887,8 +915,8 @@ function M.convert_signature_help_to_markdown_lines(signature_help, ft, triggers
return
end
--The active signature. If omitted or the value lies outside the range of
- --`signatures` the value defaults to zero or is ignored if `signatures.length
- --=== 0`. Whenever possible implementors should make an active decision about
+ --`signatures` the value defaults to zero or is ignored if `signatures.length == 0`.
+ --Whenever possible implementors should make an active decision about
--the active signature and shouldn't rely on a default value.
local contents = {}
local active_hl
@@ -1036,50 +1064,80 @@ function M.make_floating_popup_options(width, height, opts)
}
end
---- Jumps to a location.
+--- Shows document and optionally jumps to the location.
---
---@param location table (`Location`|`LocationLink`)
----@param offset_encoding string utf-8|utf-16|utf-32 (required)
----@param reuse_win boolean Jump to existing window if buffer is already opened.
----@returns `true` if the jump succeeded
-function M.jump_to_location(location, offset_encoding, reuse_win)
+---@param offset_encoding "utf-8" | "utf-16" | "utf-32"
+---@param opts table options
+--- - reuse_win (boolean) Jump to existing window if buffer is already open.
+--- - focus (boolean) Whether to focus/jump to location if possible. Defaults to true.
+---@return boolean `true` if succeeded
+function M.show_document(location, offset_encoding, opts)
-- location may be Location or LocationLink
local uri = location.uri or location.targetUri
if uri == nil then
- return
+ return false
end
if offset_encoding == nil then
- vim.notify_once(
- 'jump_to_location must be called with valid offset encoding',
- vim.log.levels.WARN
- )
+ vim.notify_once('show_document must be called with valid offset encoding', vim.log.levels.WARN)
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')
+ opts = opts or {}
+ local focus = vim.F.if_nil(opts.focus, true)
+ if focus then
+ -- Save position in jumplist
+ vim.cmd("normal! m'")
- --- Jump to new location (adjusting for UTF-16 encoding of characters)
- local win = reuse_win and bufwinid(bufnr)
- if win then
+ -- 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')
+ end
+
+ local win = opts.reuse_win and bufwinid(bufnr)
+ or focus and api.nvim_get_current_win()
+ or create_window_without_focus()
+
+ api.nvim_buf_set_option(bufnr, 'buflisted', true)
+ api.nvim_win_set_buf(win, bufnr)
+ if focus then
api.nvim_set_current_win(win)
- else
- api.nvim_buf_set_option(bufnr, 'buflisted', true)
- api.nvim_set_current_buf(bufnr)
end
+
+ -- location may be Location or LocationLink
local range = location.range or location.targetSelectionRange
- local row = range.start.line
- local col = get_line_byte_from_position(bufnr, range.start, offset_encoding)
- api.nvim_win_set_cursor(0, { row + 1, col })
- -- Open folds under the cursor
- vim.cmd('normal! zv')
+ if range then
+ --- Jump to new location (adjusting for encoding of characters)
+ local row = range.start.line
+ local col = get_line_byte_from_position(bufnr, range.start, offset_encoding)
+ api.nvim_win_set_cursor(win, { row + 1, col })
+ api.nvim_win_call(win, function()
+ -- Open folds under the cursor
+ vim.cmd('normal! zv')
+ end)
+ end
+
return true
end
+--- Jumps to a location.
+---
+---@param location table (`Location`|`LocationLink`)
+---@param offset_encoding "utf-8" | "utf-16" | "utf-32"
+---@param reuse_win boolean Jump to existing window if buffer is already open.
+---@return boolean `true` if the jump succeeded
+function M.jump_to_location(location, offset_encoding, reuse_win)
+ if offset_encoding == nil then
+ vim.notify_once(
+ 'jump_to_location must be called with valid offset encoding',
+ vim.log.levels.WARN
+ )
+ end
+
+ return M.show_document(location, offset_encoding, { reuse_win = reuse_win, focus = true })
+end
+
--- Previews a location in a floating window
---
--- behavior depends on type of location:
@@ -1484,7 +1542,7 @@ end
---
---@param contents table of lines to show in window
---@param syntax string of syntax to set for opened buffer
----@param opts table with optional fields (additional keys are passed on to |vim.api.nvim_open_win()|)
+---@param opts table with optional fields (additional keys are passed on to |nvim_open_win()|)
--- - height: (number) height of floating window
--- - width: (number) width of floating window
--- - wrap: (boolean, default true) wrap long lines
@@ -1799,7 +1857,7 @@ end
--- CAUTION: Modifies the input in-place!
---
---@param lines (table) list of lines
----@returns (string) filetype or 'markdown' if it was unchanged.
+---@returns (string) filetype or "markdown" if it was unchanged.
function M.try_trim_markdown_code_blocks(lines)
local language_id = lines[1]:match('^```(.*)')
if language_id then
@@ -1972,7 +2030,7 @@ function M.make_workspace_params(added, removed)
end
--- Returns indentation size.
---
----@see |shiftwidth|
+---@see 'shiftwidth'
---@param bufnr (number|nil): Buffer handle, defaults to current
---@returns (number) indentation size
function M.get_effective_tabstop(bufnr)