diff options
Diffstat (limited to 'runtime/lua')
-rw-r--r-- | runtime/lua/health.lua | 23 | ||||
-rw-r--r-- | runtime/lua/vim/diagnostic.lua | 24 | ||||
-rw-r--r-- | runtime/lua/vim/lsp.lua | 50 | ||||
-rw-r--r-- | runtime/lua/vim/lsp/health.lua | 2 | ||||
-rw-r--r-- | runtime/lua/vim/lsp/rpc.lua | 43 | ||||
-rw-r--r-- | runtime/lua/vim/lsp/util.lua | 1 | ||||
-rw-r--r-- | runtime/lua/vim/shared.lua | 46 | ||||
-rw-r--r-- | runtime/lua/vim/treesitter/health.lua | 2 |
8 files changed, 135 insertions, 56 deletions
diff --git a/runtime/lua/health.lua b/runtime/lua/health.lua new file mode 100644 index 0000000000..142a353bf2 --- /dev/null +++ b/runtime/lua/health.lua @@ -0,0 +1,23 @@ +local M = {} + +function M.report_start(msg) + vim.fn['health#report_start'](msg) +end + +function M.report_info(msg) + vim.fn['health#report_info'](msg) +end + +function M.report_ok(msg) + vim.fn['health#report_ok'](msg) +end + +function M.report_warn(msg, ...) + vim.fn['health#report_warn'](msg, ...) +end + +function M.report_error(msg, ...) + vim.fn['health#report_error'](msg, ...) +end + +return M diff --git a/runtime/lua/vim/diagnostic.lua b/runtime/lua/vim/diagnostic.lua index c7c8c1878e..e8aba6b7a3 100644 --- a/runtime/lua/vim/diagnostic.lua +++ b/runtime/lua/vim/diagnostic.lua @@ -27,7 +27,10 @@ local global_diagnostic_options = { ---@private local function to_severity(severity) - return type(severity) == 'string' and M.severity[string.upper(severity)] or severity + if type(severity) == 'string' then + return assert(M.severity[string.upper(severity)], string.format("Invalid severity: %s", severity)) + end + return severity end ---@private @@ -125,9 +128,7 @@ local function get_namespace(ns) end end - if not name then - return vim.notify("namespace does not exist or is anonymous", vim.log.levels.ERROR) - end + assert(name, "namespace does not exist or is anonymous") all_namespaces[ns] = { name = name, @@ -398,6 +399,17 @@ local function show_diagnostics(opts, diagnostics) diagnostics = prefix_source(opts.source, diagnostics) end + -- Use global setting for severity_sort since 'show_diagnostics' is namespace + -- independent + local severity_sort = global_diagnostic_options.severity_sort + if severity_sort then + if type(severity_sort) == "table" and severity_sort.reverse then + table.sort(diagnostics, function(a, b) return a.severity > b.severity end) + else + table.sort(diagnostics, function(a, b) return a.severity < b.severity end) + end + end + for i, diagnostic in ipairs(diagnostics) do local prefix = string.format("%d. ", i) local hiname = floating_highlight_map[diagnostic.severity] @@ -510,6 +522,9 @@ local function diagnostic_move_pos(opts, pos) return end + -- Save position in the window's jumplist + vim.api.nvim_win_call(win_id, function() vim.cmd("normal! m'") end) + vim.api.nvim_win_set_cursor(win_id, {pos[1] + 1, pos[2]}) if enable_popup then @@ -1140,7 +1155,6 @@ function M.show_position_diagnostics(opts, bufnr, position) local diagnostics = M.get(bufnr, opts) clamp_line_numbers(bufnr, diagnostics) local position_diagnostics = vim.tbl_filter(match_position_predicate, diagnostics) - table.sort(position_diagnostics, function(a, b) return a.severity < b.severity end) return show_diagnostics(opts, position_diagnostics) end diff --git a/runtime/lua/vim/lsp.lua b/runtime/lua/vim/lsp.lua index c7a88a0993..a9e27cf6ac 100644 --- a/runtime/lua/vim/lsp.lua +++ b/runtime/lua/vim/lsp.lua @@ -41,6 +41,7 @@ lsp._request_name_to_capability = { ['textDocument/documentSymbol'] = 'document_symbol'; ['textDocument/prepareCallHierarchy'] = 'call_hierarchy'; ['textDocument/rename'] = 'rename'; + ['textDocument/prepareRename'] = 'rename'; ['textDocument/codeAction'] = 'code_action'; ['textDocument/codeLens'] = 'code_lens'; ['codeLens/resolve'] = 'code_lens_resolve'; @@ -1383,6 +1384,29 @@ function lsp.buf_notify(bufnr, method, params) return resp end + +---@private +local function adjust_start_col(lnum, line, items, encoding) + local min_start_char = nil + for _, item in pairs(items) do + if item.textEdit and item.textEdit.range.start.line == lnum - 1 then + if min_start_char and min_start_char ~= item.textEdit.range.start.character then + return nil + end + min_start_char = item.textEdit.range.start.character + end + end + if min_start_char then + if encoding == 'utf-8' then + return min_start_char + else + return vim.str_byteindex(line, min_start_char, encoding == 'utf-16') + end + else + return nil + end +end + --- Implements 'omnifunc' compatible LSP completion. --- ---@see |complete-functions| @@ -1418,17 +1442,37 @@ function lsp.omnifunc(findstart, base) -- Get the start position of the current keyword local textMatch = vim.fn.match(line_to_cursor, '\\k*$') - local prefix = line_to_cursor:sub(textMatch+1) local params = util.make_position_params() local items = {} - lsp.buf_request(bufnr, 'textDocument/completion', params, function(err, result) + lsp.buf_request(bufnr, 'textDocument/completion', params, function(err, result, ctx) if err or not result or vim.fn.mode() ~= "i" then return end + + -- Completion response items may be relative to a position different than `textMatch`. + -- Concrete example, with sumneko/lua-language-server: + -- + -- require('plenary.asy| + -- ▲ ▲ ▲ + -- │ │ └── cursor_pos: 20 + -- │ └────── textMatch: 17 + -- └────────────── textEdit.range.start.character: 9 + -- .newText = 'plenary.async' + -- ^^^ + -- prefix (We'd remove everything not starting with `asy`, + -- so we'd eliminate the `plenary.async` result + -- + -- `adjust_start_col` is used to prefer the language server boundary. + -- + local client = lsp.get_client_by_id(ctx.client_id) + local encoding = client and client.offset_encoding or 'utf-16' + local candidates = util.extract_completion_items(result) + local startbyte = adjust_start_col(pos[1], line, candidates, encoding) or textMatch + local prefix = line:sub(startbyte + 1, pos[2]) local matches = util.text_document_completion_list_to_complete_items(result, prefix) -- TODO(ashkan): is this the best way to do this? vim.list_extend(items, matches) - vim.fn.complete(textMatch+1, items) + vim.fn.complete(startbyte + 1, items) end) -- Return -2 to signal that we should continue completion so that we can diff --git a/runtime/lua/vim/lsp/health.lua b/runtime/lua/vim/lsp/health.lua index 855679a2df..ed3eea59df 100644 --- a/runtime/lua/vim/lsp/health.lua +++ b/runtime/lua/vim/lsp/health.lua @@ -1,7 +1,7 @@ local M = {} --- Performs a healthcheck for LSP -function M.check_health() +function M.check() local report_info = vim.fn['health#report_info'] local report_warn = vim.fn['health#report_warn'] diff --git a/runtime/lua/vim/lsp/rpc.lua b/runtime/lua/vim/lsp/rpc.lua index 255eb65dfe..d9a684a738 100644 --- a/runtime/lua/vim/lsp/rpc.lua +++ b/runtime/lua/vim/lsp/rpc.lua @@ -13,36 +13,6 @@ local function is_dir(filename) return stat and stat.type == 'directory' or false end -local NIL = vim.NIL - ----@private -local recursive_convert_NIL -recursive_convert_NIL = function(v, tbl_processed) - if v == NIL then - return nil - elseif not tbl_processed[v] and type(v) == 'table' then - tbl_processed[v] = true - local inside_list = vim.tbl_islist(v) - return vim.tbl_map(function(x) - if not inside_list or (inside_list and type(x) == "table") then - return recursive_convert_NIL(x, tbl_processed) - else - return x - end - end, v) - end - - return v -end - ----@private ---- Returns its argument, but converts `vim.NIL` to Lua `nil`. ----@param v (any) Argument ----@returns (any) -local function convert_NIL(v) - return recursive_convert_NIL(v, {}) -end - ---@private --- Merges current process env with the given env and returns the result as --- a list of "k=v" strings. @@ -457,7 +427,7 @@ local function start(cmd, cmd_args, dispatchers, extra_spawn_params) ---@private local function handle_body(body) - local ok, decoded = pcall(vim.json.decode, body) + local ok, decoded = pcall(vim.json.decode, body, { luanil = { object = true } }) if not ok then on_error(client_errors.INVALID_SERVER_JSON, decoded) return @@ -466,8 +436,6 @@ local function start(cmd, cmd_args, dispatchers, extra_spawn_params) if type(decoded.method) == 'string' and decoded.id then local err - -- Server Request - decoded.params = convert_NIL(decoded.params) -- Schedule here so that the users functions don't trigger an error and -- we can still use the result. schedule(function() @@ -494,22 +462,16 @@ local function start(cmd, cmd_args, dispatchers, extra_spawn_params) end) -- This works because we are expecting vim.NIL here elseif decoded.id and (decoded.result ~= vim.NIL or decoded.error ~= vim.NIL) then - -- Server Result - decoded.error = convert_NIL(decoded.error) - decoded.result = convert_NIL(decoded.result) -- We sent a number, so we expect a number. local result_id = tonumber(decoded.id) - -- Do not surface RequestCancelled or ContentModified to users, it is RPC-internal. + -- Do not surface RequestCancelled to users, it is RPC-internal. if decoded.error then local mute_error = false if decoded.error.code == protocol.ErrorCodes.RequestCancelled then local _ = log.debug() and log.debug("Received cancellation ack", decoded) mute_error = true - elseif decoded.error.code == protocol.ErrorCodes.ContentModified then - local _ = log.debug() and log.debug("Received content modified ack", decoded) - mute_error = true end if mute_error then @@ -544,7 +506,6 @@ local function start(cmd, cmd_args, dispatchers, extra_spawn_params) end elseif type(decoded.method) == 'string' then -- Notification - decoded.params = convert_NIL(decoded.params) try_call(client_errors.NOTIFICATION_HANDLER_ERROR, dispatchers.notification, decoded.method, decoded.params) else diff --git a/runtime/lua/vim/lsp/util.lua b/runtime/lua/vim/lsp/util.lua index fca956fb57..3751f94caf 100644 --- a/runtime/lua/vim/lsp/util.lua +++ b/runtime/lua/vim/lsp/util.lua @@ -193,6 +193,7 @@ function M.get_progress_messages() title = ctx.title or "empty title", message = ctx.message, percentage = ctx.percentage, + done = ctx.done, progress = true, } table.insert(new_messages, new_report) diff --git a/runtime/lua/vim/shared.lua b/runtime/lua/vim/shared.lua index 18c1e21049..b57b7ad4ad 100644 --- a/runtime/lua/vim/shared.lua +++ b/runtime/lua/vim/shared.lua @@ -98,17 +98,53 @@ end --- <pre> --- split(":aa::b:", ":") --> {'','aa','','b',''} --- split("axaby", "ab?") --> {'','x','y'} ---- split(x*yz*o, "*", true) --> {'x','yz','o'} +--- split("x*yz*o", "*", {plain=true}) --> {'x','yz','o'} +--- split("|x|y|z|", "|", {trimempty=true}) --> {'x', 'y', 'z'} --- </pre> --- +--- ---@see |vim.gsplit()| --- ---@param s String to split ---@param sep Separator string or pattern ----@param plain If `true` use `sep` literally (passed to String.find) +---@param kwargs Keyword arguments: +--- - plain: (boolean) If `true` use `sep` literally (passed to string.find) +--- - trimempty: (boolean) If `true` remove empty items from the front +--- and back of the list ---@returns List-like table of the split components. -function vim.split(s,sep,plain) - local t={} for c in vim.gsplit(s, sep, plain) do table.insert(t,c) end +function vim.split(s, sep, kwargs) + local plain + local trimempty = false + if type(kwargs) == 'boolean' then + -- Support old signature for backward compatibility + plain = kwargs + else + vim.validate { kwargs = {kwargs, 't', true} } + kwargs = kwargs or {} + plain = kwargs.plain + trimempty = kwargs.trimempty + end + + local t = {} + local skip = trimempty + for c in vim.gsplit(s, sep, plain) do + if c ~= "" then + skip = false + end + + if not skip then + table.insert(t, c) + end + end + + if trimempty then + for i = #t, 1, -1 do + if t[i] ~= "" then + break + end + table.remove(t, i) + end + end + return t end diff --git a/runtime/lua/vim/treesitter/health.lua b/runtime/lua/vim/treesitter/health.lua index e031ba1bd6..53ccc6e88d 100644 --- a/runtime/lua/vim/treesitter/health.lua +++ b/runtime/lua/vim/treesitter/health.lua @@ -9,7 +9,7 @@ function M.list_parsers() end --- Performs a healthcheck for treesitter integration -function M.check_health() +function M.check() local report_info = vim.fn['health#report_info'] local report_ok = vim.fn['health#report_ok'] local report_error = vim.fn['health#report_error'] |