aboutsummaryrefslogtreecommitdiff
path: root/runtime/lua/vim/lsp
diff options
context:
space:
mode:
Diffstat (limited to 'runtime/lua/vim/lsp')
-rw-r--r--runtime/lua/vim/lsp/_snippet.lua167
-rw-r--r--runtime/lua/vim/lsp/buf.lua240
-rw-r--r--runtime/lua/vim/lsp/codelens.lua54
-rw-r--r--runtime/lua/vim/lsp/diagnostic.lua107
-rw-r--r--runtime/lua/vim/lsp/handlers.lua190
-rw-r--r--runtime/lua/vim/lsp/health.lua12
-rw-r--r--runtime/lua/vim/lsp/log.lua72
-rw-r--r--runtime/lua/vim/lsp/protocol.lua542
-rw-r--r--runtime/lua/vim/lsp/rpc.lua240
-rw-r--r--runtime/lua/vim/lsp/sync.lua43
-rw-r--r--runtime/lua/vim/lsp/util.lua631
11 files changed, 1272 insertions, 1026 deletions
diff --git a/runtime/lua/vim/lsp/_snippet.lua b/runtime/lua/vim/lsp/_snippet.lua
index 0140b0aee3..28064f36e9 100644
--- a/runtime/lua/vim/lsp/_snippet.lua
+++ b/runtime/lua/vim/lsp/_snippet.lua
@@ -41,7 +41,7 @@ P.take_until = function(targets, specials)
parsed = true,
value = {
raw = table.concat(raw, ''),
- esc = table.concat(esc, '')
+ esc = table.concat(esc, ''),
},
pos = new_pos,
}
@@ -248,49 +248,67 @@ S.format = P.any(
capture_index = values[3],
}, Node)
end),
- P.map(P.seq(S.dollar, S.open, S.int, S.colon, S.slash, P.any(
- P.token('upcase'),
- P.token('downcase'),
- P.token('capitalize'),
- P.token('camelcase'),
- P.token('pascalcase')
- ), S.close), function(values)
- return setmetatable({
- type = Node.Type.FORMAT,
- capture_index = values[3],
- modifier = values[6],
- }, Node)
- end),
- P.map(P.seq(S.dollar, S.open, S.int, S.colon, P.any(
- P.seq(S.question, P.take_until({ ':' }, { '\\' }), S.colon, P.take_until({ '}' }, { '\\' })),
- P.seq(S.plus, P.take_until({ '}' }, { '\\' })),
- P.seq(S.minus, P.take_until({ '}' }, { '\\' }))
- ), S.close), function(values)
+ P.map(
+ P.seq(
+ S.dollar,
+ S.open,
+ S.int,
+ S.colon,
+ S.slash,
+ P.any(P.token('upcase'), P.token('downcase'), P.token('capitalize'), P.token('camelcase'), P.token('pascalcase')),
+ S.close
+ ),
+ function(values)
+ return setmetatable({
+ type = Node.Type.FORMAT,
+ capture_index = values[3],
+ modifier = values[6],
+ }, Node)
+ end
+ ),
+ P.map(
+ P.seq(
+ S.dollar,
+ S.open,
+ S.int,
+ S.colon,
+ P.any(
+ P.seq(S.question, P.take_until({ ':' }, { '\\' }), S.colon, P.take_until({ '}' }, { '\\' })),
+ P.seq(S.plus, P.take_until({ '}' }, { '\\' })),
+ P.seq(S.minus, P.take_until({ '}' }, { '\\' }))
+ ),
+ S.close
+ ),
+ function(values)
+ return setmetatable({
+ type = Node.Type.FORMAT,
+ capture_index = values[3],
+ if_text = values[5][2].esc,
+ else_text = (values[5][4] or {}).esc,
+ }, Node)
+ end
+ )
+)
+
+S.transform = P.map(
+ P.seq(
+ S.slash,
+ P.take_until({ '/' }, { '\\' }),
+ S.slash,
+ P.many(P.any(S.format, S.text({ '$', '/' }, { '\\' }))),
+ S.slash,
+ P.opt(P.pattern('[ig]+'))
+ ),
+ function(values)
return setmetatable({
- type = Node.Type.FORMAT,
- capture_index = values[3],
- if_text = values[5][2].esc,
- else_text = (values[5][4] or {}).esc,
+ type = Node.Type.TRANSFORM,
+ pattern = values[2].raw,
+ format = values[4],
+ option = values[6],
}, Node)
- end)
+ end
)
-S.transform = P.map(P.seq(
- S.slash,
- P.take_until({ '/' }, { '\\' }),
- S.slash,
- P.many(P.any(S.format, S.text({ '$', '/' }, { '\\' }))),
- S.slash,
- P.opt(P.pattern('[ig]+'))
-), function(values)
- return setmetatable({
- type = Node.Type.TRANSFORM,
- pattern = values[2].raw,
- format = values[4],
- option = values[6],
- }, Node)
-end)
-
S.tabstop = P.any(
P.map(P.seq(S.dollar, S.int), function(values)
return setmetatable({
@@ -314,34 +332,38 @@ S.tabstop = P.any(
)
S.placeholder = P.any(
- P.map(P.seq(S.dollar, S.open, S.int, S.colon, P.many(P.any(S.toplevel, S.text({ '$', '}' }, { '\\' }))), S.close), function(values)
- return setmetatable({
- type = Node.Type.PLACEHOLDER,
- tabstop = values[3],
- children = values[5],
- }, Node)
- end)
+ P.map(
+ P.seq(S.dollar, S.open, S.int, S.colon, P.many(P.any(S.toplevel, S.text({ '$', '}' }, { '\\' }))), S.close),
+ function(values)
+ return setmetatable({
+ type = Node.Type.PLACEHOLDER,
+ tabstop = values[3],
+ children = values[5],
+ }, Node)
+ end
+ )
)
-S.choice = P.map(P.seq(
- S.dollar,
- S.open,
- S.int,
- S.pipe,
- P.many(
- P.map(P.seq(S.text({ ',', '|' }), P.opt(S.comma)), function(values)
+S.choice = P.map(
+ P.seq(
+ S.dollar,
+ S.open,
+ S.int,
+ S.pipe,
+ P.many(P.map(P.seq(S.text({ ',', '|' }), P.opt(S.comma)), function(values)
return values[1].esc
- end)
+ end)),
+ S.pipe,
+ S.close
),
- S.pipe,
- S.close
-), function(values)
- return setmetatable({
- type = Node.Type.CHOICE,
- tabstop = values[3],
- items = values[5],
- }, Node)
-end)
+ function(values)
+ return setmetatable({
+ type = Node.Type.CHOICE,
+ tabstop = values[3],
+ items = values[5],
+ }, Node)
+ end
+)
S.variable = P.any(
P.map(P.seq(S.dollar, S.var), function(values)
@@ -363,13 +385,16 @@ S.variable = P.any(
transform = values[4],
}, Node)
end),
- P.map(P.seq(S.dollar, S.open, S.var, S.colon, P.many(P.any(S.toplevel, S.text({ '$', '}' }, { '\\' }))), S.close), function(values)
- return setmetatable({
- type = Node.Type.VARIABLE,
- name = values[3],
- children = values[5],
- }, Node)
- end)
+ P.map(
+ P.seq(S.dollar, S.open, S.var, S.colon, P.many(P.any(S.toplevel, S.text({ '$', '}' }, { '\\' }))), S.close),
+ function(values)
+ return setmetatable({
+ type = Node.Type.VARIABLE,
+ name = values[3],
+ children = values[5],
+ }, Node)
+ end
+ )
)
S.snippet = P.map(P.many(P.any(S.toplevel, S.text({ '$' }, { '}', '\\' }))), function(values)
diff --git a/runtime/lua/vim/lsp/buf.lua b/runtime/lua/vim/lsp/buf.lua
index 6666b3c044..80350bcb71 100644
--- a/runtime/lua/vim/lsp/buf.lua
+++ b/runtime/lua/vim/lsp/buf.lua
@@ -1,7 +1,7 @@
local vim = vim
local validate = vim.validate
local vfn = vim.fn
-local util = require 'vim.lsp.util'
+local util = require('vim.lsp.util')
local M = {}
@@ -9,7 +9,9 @@ local M = {}
--- Returns nil if {status} is false or nil, otherwise returns the rest of the
--- arguments.
local function ok_or_nil(status, ...)
- if not status then return end
+ if not status then
+ return
+ end
return ...
end
@@ -39,10 +41,10 @@ end
---
---@see |vim.lsp.buf_request()|
local function request(method, params, handler)
- validate {
- method = {method, 's'};
- handler = {handler, 'f', true};
- }
+ validate({
+ method = { method, 's' },
+ handler = { handler, 'f', true },
+ })
return vim.lsp.buf_request(0, method, params, handler)
end
@@ -51,7 +53,7 @@ end
---
---@returns `true` if server responds.
function M.server_ready()
- return not not vim.lsp.buf_notify(0, "window/progress", {})
+ return not not vim.lsp.buf_notify(0, 'window/progress', {})
end
--- Displays hover information about the symbol under the cursor in a floating
@@ -61,26 +63,45 @@ function M.hover()
request('textDocument/hover', params)
end
+---@private
+local function request_with_options(name, params, options)
+ local req_handler
+ if options then
+ req_handler = function(err, result, ctx, config)
+ local client = vim.lsp.get_client_by_id(ctx.client_id)
+ local handler = client.handlers[name] or vim.lsp.handlers[name]
+ handler(err, result, ctx, vim.tbl_extend('force', config or {}, options))
+ end
+ end
+ request(name, params, req_handler)
+end
+
--- Jumps to the declaration of the symbol under the cursor.
---@note Many servers do not implement this method. Generally, see |vim.lsp.buf.definition()| instead.
---
-function M.declaration()
+---@param options table|nil additional options
+--- - reuse_win: (boolean) Jump to existing window if buffer is already open.
+function M.declaration(options)
local params = util.make_position_params()
- request('textDocument/declaration', params)
+ request_with_options('textDocument/declaration', params, options)
end
--- Jumps to the definition of the symbol under the cursor.
---
-function M.definition()
+---@param options table|nil additional options
+--- - reuse_win: (boolean) Jump to existing window if buffer is already open.
+function M.definition(options)
local params = util.make_position_params()
- request('textDocument/definition', params)
+ request_with_options('textDocument/definition', params, options)
end
--- Jumps to the definition of the type of the symbol under the cursor.
---
-function M.type_definition()
+---@param options table|nil additional options
+--- - reuse_win: (boolean) Jump to existing window if buffer is already open.
+function M.type_definition(options)
local params = util.make_position_params()
- request('textDocument/typeDefinition', params)
+ request_with_options('textDocument/typeDefinition', params, options)
end
--- Lists all the implementations for the symbol under the cursor in the
@@ -117,9 +138,9 @@ end
--
---@returns The client that the user selected or nil
local function select_client(method, on_choice)
- validate {
+ validate({
on_choice = { on_choice, 'function', false },
- }
+ })
local clients = vim.tbl_values(vim.lsp.buf_get_clients())
clients = vim.tbl_filter(function(client)
return client.supports_method(method)
@@ -191,24 +212,21 @@ function M.format(options)
if options.filter then
clients = options.filter(clients)
elseif options.id then
- clients = vim.tbl_filter(
- function(client) return client.id == options.id end,
- clients
- )
+ clients = vim.tbl_filter(function(client)
+ return client.id == options.id
+ end, clients)
elseif options.name then
- clients = vim.tbl_filter(
- function(client) return client.name == options.name end,
- clients
- )
+ clients = vim.tbl_filter(function(client)
+ return client.name == options.name
+ end, clients)
end
- clients = vim.tbl_filter(
- function(client) return client.supports_method("textDocument/formatting") end,
- clients
- )
+ clients = vim.tbl_filter(function(client)
+ return client.supports_method('textDocument/formatting')
+ end, clients)
if #clients == 0 then
- vim.notify("[LSP] Format request failed, no matching language servers.")
+ vim.notify('[LSP] Format request failed, no matching language servers.')
end
if options.async then
@@ -218,7 +236,7 @@ function M.format(options)
return
end
local params = util.make_formatting_params(options.formatting_options)
- client.request("textDocument/formatting", params, function(...)
+ client.request('textDocument/formatting', params, function(...)
local handler = client.handlers['textDocument/formatting'] or vim.lsp.handlers['textDocument/formatting']
handler(...)
do_format(next(clients, idx))
@@ -229,11 +247,11 @@ function M.format(options)
local timeout_ms = options.timeout_ms or 1000
for _, client in pairs(clients) do
local params = util.make_formatting_params(options.formatting_options)
- local result, err = client.request_sync("textDocument/formatting", params, timeout_ms, bufnr)
+ local result, err = client.request_sync('textDocument/formatting', params, timeout_ms, bufnr)
if result and result.result then
util.apply_text_edits(result.result, bufnr, client.offset_encoding)
elseif err then
- vim.notify(string.format("[LSP][%s] %s", client.name, err), vim.log.levels.WARN)
+ vim.notify(string.format('[LSP][%s] %s', client.name, err), vim.log.levels.WARN)
end
end
end
@@ -310,7 +328,7 @@ end
---the remaining clients in the order as they occur in the `order` list.
function M.formatting_seq_sync(options, timeout_ms, order)
vim.notify_once('vim.lsp.buf.formatting_seq_sync is deprecated. Use vim.lsp.buf.format instead', vim.log.levels.WARN)
- local clients = vim.tbl_values(vim.lsp.buf_get_clients());
+ local clients = vim.tbl_values(vim.lsp.buf_get_clients())
local bufnr = vim.api.nvim_get_current_buf()
-- sort the clients according to `order`
@@ -326,13 +344,18 @@ function M.formatting_seq_sync(options, timeout_ms, order)
-- loop through the clients and make synchronous formatting requests
for _, client in pairs(clients) do
- if vim.tbl_get(client.server_capabilities, "documentFormattingProvider") then
+ if vim.tbl_get(client.server_capabilities, 'documentFormattingProvider') then
local params = util.make_formatting_params(options)
- local result, err = client.request_sync("textDocument/formatting", params, timeout_ms, vim.api.nvim_get_current_buf())
+ local result, err = client.request_sync(
+ 'textDocument/formatting',
+ params,
+ timeout_ms,
+ vim.api.nvim_get_current_buf()
+ )
if result and result.result then
util.apply_text_edits(result.result, bufnr, client.offset_encoding)
elseif err then
- vim.notify(string.format("vim.lsp.buf.formatting_seq_sync: (%s) %s", client.name, err), vim.log.levels.WARN)
+ vim.notify(string.format('vim.lsp.buf.formatting_seq_sync: (%s) %s', client.name, err), vim.log.levels.WARN)
end
end
end
@@ -377,20 +400,18 @@ function M.rename(new_name, options)
if options.filter then
clients = options.filter(clients)
elseif options.name then
- clients = vim.tbl_filter(
- function(client) return client.name == options.name end,
- clients
- )
+ clients = vim.tbl_filter(function(client)
+ return client.name == options.name
+ end, clients)
end
-- Clients must at least support rename, prepareRename is optional
- clients = vim.tbl_filter(
- function(client) return client.supports_method("textDocument/rename") end,
- clients
- )
+ clients = vim.tbl_filter(function(client)
+ return client.supports_method('textDocument/rename')
+ end, clients)
if #clients == 0 then
- vim.notify("[LSP] Rename, no matching language servers with rename capability.")
+ vim.notify('[LSP] Rename, no matching language servers with rename capability.')
end
local win = vim.api.nvim_get_current_win()
@@ -427,7 +448,7 @@ function M.rename(new_name, options)
end, bufnr)
end
- if client.supports_method("textDocument/prepareRename") then
+ if client.supports_method('textDocument/prepareRename') then
local params = util.make_position_params(win, client.offset_encoding)
client.request('textDocument/prepareRename', params, function(err, result)
if err or result == nil then
@@ -446,7 +467,7 @@ function M.rename(new_name, options)
end
local prompt_opts = {
- prompt = "New Name: "
+ prompt = 'New Name: ',
}
-- result: Range | { range: Range, placeholder: string }
if result.placeholder then
@@ -466,15 +487,15 @@ function M.rename(new_name, options)
end)
end, bufnr)
else
- assert(client.supports_method("textDocument/rename"), 'Client must support textDocument/rename')
+ assert(client.supports_method('textDocument/rename'), 'Client must support textDocument/rename')
if new_name then
rename(new_name)
return
end
local prompt_opts = {
- prompt = "New Name: ",
- default = cword
+ prompt = 'New Name: ',
+ default = cword,
}
vim.ui.input(prompt_opts, function(input)
if not input or #input == 0 then
@@ -493,10 +514,10 @@ end
---@param context (table) Context for the request
---@see https://microsoft.github.io/language-server-protocol/specifications/specification-current/#textDocument_references
function M.references(context)
- validate { context = { context, 't', true } }
+ validate({ context = { context, 't', true } })
local params = util.make_position_params()
params.context = context or {
- includeDeclaration = true;
+ includeDeclaration = true,
}
request('textDocument/references', params)
end
@@ -510,14 +531,16 @@ end
---@private
local function pick_call_hierarchy_item(call_hierarchy_items)
- if not call_hierarchy_items then return end
+ if not call_hierarchy_items then
+ return
+ end
if #call_hierarchy_items == 1 then
return call_hierarchy_items[1]
end
local items = {}
for i, item in pairs(call_hierarchy_items) do
local entry = item.detail or item.name
- table.insert(items, string.format("%d. %s", i, entry))
+ table.insert(items, string.format('%d. %s', i, entry))
end
local choice = vim.fn.inputlist(items)
if choice < 1 or choice > #items then
@@ -539,8 +562,8 @@ local function call_hierarchy(method)
if client then
client.request(method, { item = call_hierarchy_item }, nil, ctx.bufnr)
else
- vim.notify(string.format(
- 'Client with id=%d disappeared during call hierarchy request', ctx.client_id),
+ vim.notify(
+ string.format('Client with id=%d disappeared during call hierarchy request', ctx.client_id),
vim.log.levels.WARN
)
end
@@ -576,20 +599,25 @@ end
--- Add the folder at path to the workspace folders. If {path} is
--- not provided, the user will be prompted for a path using |input()|.
function M.add_workspace_folder(workspace_folder)
- workspace_folder = workspace_folder or npcall(vfn.input, "Workspace Folder: ", vfn.expand('%:p:h'), 'dir')
- vim.api.nvim_command("redraw")
- if not (workspace_folder and #workspace_folder > 0) then return end
+ workspace_folder = workspace_folder or npcall(vfn.input, 'Workspace Folder: ', vfn.expand('%:p:h'), 'dir')
+ vim.api.nvim_command('redraw')
+ if not (workspace_folder and #workspace_folder > 0) then
+ return
+ end
if vim.fn.isdirectory(workspace_folder) == 0 then
- print(workspace_folder, " is not a valid directory")
+ print(workspace_folder, ' is not a valid directory')
return
end
- local params = util.make_workspace_params({{uri = vim.uri_from_fname(workspace_folder); name = workspace_folder}}, {{}})
+ local params = util.make_workspace_params(
+ { { uri = vim.uri_from_fname(workspace_folder), name = workspace_folder } },
+ { {} }
+ )
for _, client in pairs(vim.lsp.buf_get_clients()) do
local found = false
for _, folder in pairs(client.workspace_folders or {}) do
if folder.name == workspace_folder then
found = true
- print(workspace_folder, "is already part of this workspace")
+ print(workspace_folder, 'is already part of this workspace')
break
end
end
@@ -607,10 +635,15 @@ end
--- {path} is not provided, the user will be prompted for
--- a path using |input()|.
function M.remove_workspace_folder(workspace_folder)
- workspace_folder = workspace_folder or npcall(vfn.input, "Workspace Folder: ", vfn.expand('%:p:h'))
- vim.api.nvim_command("redraw")
- if not (workspace_folder and #workspace_folder > 0) then return end
- local params = util.make_workspace_params({{}}, {{uri = vim.uri_from_fname(workspace_folder); name = workspace_folder}})
+ workspace_folder = workspace_folder or npcall(vfn.input, 'Workspace Folder: ', vfn.expand('%:p:h'))
+ vim.api.nvim_command('redraw')
+ if not (workspace_folder and #workspace_folder > 0) then
+ return
+ end
+ local params = util.make_workspace_params(
+ { {} },
+ { { uri = vim.uri_from_fname(workspace_folder), name = workspace_folder } }
+ )
for _, client in pairs(vim.lsp.buf_get_clients()) do
for idx, folder in pairs(client.workspace_folders) do
if folder.name == workspace_folder then
@@ -620,7 +653,7 @@ function M.remove_workspace_folder(workspace_folder)
end
end
end
- print(workspace_folder, "is not currently part of the workspace")
+ print(workspace_folder, 'is not currently part of the workspace')
end
--- Lists all symbols in the current workspace in the quickfix window.
@@ -631,11 +664,11 @@ end
---
---@param query (string, optional)
function M.workspace_symbol(query)
- query = query or npcall(vfn.input, "Query: ")
+ query = query or npcall(vfn.input, 'Query: ')
if query == nil then
return
end
- local params = {query = query}
+ local params = { query = query }
request('workspace/symbol', params)
end
@@ -665,7 +698,6 @@ function M.clear_references()
util.buf_clear_references()
end
-
---@private
--
--- This is not public because the main extension point is
@@ -678,10 +710,38 @@ end
--- `codeAction/resolve`
local function on_code_action_results(results, ctx, options)
local action_tuples = {}
- local filter = options and options.filter
+
+ ---@private
+ local function action_filter(a)
+ -- filter by specified action kind
+ if options and options.context and options.context.only then
+ if not a.kind then
+ return false
+ end
+ local found = false
+ for _, o in ipairs(options.context.only) do
+ -- action kinds are hierachical with . as a separator: when requesting only
+ -- 'quickfix' this filter allows both 'quickfix' and 'quickfix.foo', for example
+ if a.kind:find('^' .. o .. '$') or a.kind:find('^' .. o .. '%.') then
+ found = true
+ break
+ end
+ end
+ if not found then
+ return false
+ end
+ end
+ -- filter by user function
+ if options and options.filter and not options.filter(a) then
+ return false
+ end
+ -- no filter removed this action
+ return true
+ end
+
for client_id, result in pairs(results) do
for _, action in pairs(result.result or {}) do
- if not filter or filter(action) then
+ if action_filter(action) then
table.insert(action_tuples, { client_id, action })
end
end
@@ -728,10 +788,11 @@ local function on_code_action_results(results, ctx, options)
--
local client = vim.lsp.get_client_by_id(action_tuple[1])
local action = action_tuple[2]
- if not action.edit
- and client
- and vim.tbl_get(client.server_capabilities, "codeActionProvider", "resolveProvider") then
-
+ if
+ not action.edit
+ and client
+ and vim.tbl_get(client.server_capabilities, 'codeActionProvider', 'resolveProvider')
+ then
client.request('codeAction/resolve', action, function(err, resolved_action)
if err then
vim.notify(err.code .. ': ' .. err.message, vim.log.levels.ERROR)
@@ -761,7 +822,6 @@ local function on_code_action_results(results, ctx, options)
}, on_user_choice)
end
-
--- Requests code actions from all clients and calls the handler exactly once
--- with all aggregated results
---@private
@@ -769,7 +829,7 @@ local function code_action_request(params, options)
local bufnr = vim.api.nvim_get_current_buf()
local method = 'textDocument/codeAction'
vim.lsp.buf_request_all(bufnr, method, params, function(results)
- local ctx = { bufnr = bufnr, method = method, params = params}
+ local ctx = { bufnr = bufnr, method = method, params = params }
on_code_action_results(results, ctx, options)
end)
end
@@ -783,8 +843,8 @@ end
--- - diagnostics (table|nil):
--- LSP `Diagnostic[]`. Inferred from the current
--- position if not provided.
---- - only (string|nil):
---- LSP `CodeActionKind` used to filter the code actions.
+--- - only (table|nil):
+--- List of LSP `CodeActionKind`s used to filter the code actions.
--- Most language servers support values like `refactor`
--- or `quickfix`.
--- - filter (function|nil):
@@ -794,7 +854,7 @@ end
--- (after filtering), the action is applied without user query.
---@see https://microsoft.github.io/language-server-protocol/specifications/specification-current/#textDocument_codeAction
function M.code_action(options)
- validate { options = { options, 't', true } }
+ validate({ options = { options, 't', true } })
options = options or {}
-- Detect old API call code_action(context) which should now be
-- code_action({ context = context} )
@@ -817,8 +877,8 @@ end
--- - diagnostics: (table|nil)
--- LSP `Diagnostic[]`. Inferred from the current
--- position if not provided.
---- - only: (string|nil)
---- LSP `CodeActionKind` used to filter the code actions.
+--- - only: (table|nil)
+--- List of LSP `CodeActionKind`s used to filter the code actions.
--- Most language servers support values like `refactor`
--- or `quickfix`.
---@param start_pos ({number, number}, optional) mark-indexed position.
@@ -826,7 +886,7 @@ end
---@param end_pos ({number, number}, optional) mark-indexed position.
---Defaults to the end of the last visual selection.
function M.range_code_action(context, start_pos, end_pos)
- validate { context = { context, 't', true } }
+ validate({ context = { context, 't', true } })
context = context or {}
if not context.diagnostics then
context.diagnostics = vim.lsp.diagnostic.get_line_diagnostics()
@@ -841,16 +901,16 @@ end
---@param command_params table A valid `ExecuteCommandParams` object
---@see https://microsoft.github.io/language-server-protocol/specifications/specification-current/#workspace_executeCommand
function M.execute_command(command_params)
- validate {
+ validate({
command = { command_params.command, 's' },
- arguments = { command_params.arguments, 't', true }
- }
+ arguments = { command_params.arguments, 't', true },
+ })
command_params = {
- command=command_params.command,
- arguments=command_params.arguments,
- workDoneToken=command_params.workDoneToken,
+ command = command_params.command,
+ arguments = command_params.arguments,
+ workDoneToken = command_params.workDoneToken,
}
- request('workspace/executeCommand', command_params )
+ request('workspace/executeCommand', command_params)
end
return M
diff --git a/runtime/lua/vim/lsp/codelens.lua b/runtime/lua/vim/lsp/codelens.lua
index 99695d2ed1..ec0aede2d3 100644
--- a/runtime/lua/vim/lsp/codelens.lua
+++ b/runtime/lua/vim/lsp/codelens.lua
@@ -12,7 +12,7 @@ local lens_cache_by_buf = setmetatable({}, {
__index = function(t, b)
local key = b > 0 and b or api.nvim_get_current_buf()
return rawget(t, key)
- end
+ end,
})
local namespaces = setmetatable({}, {
@@ -20,13 +20,12 @@ local namespaces = setmetatable({}, {
local value = api.nvim_create_namespace('vim_lsp_codelens:' .. key)
rawset(t, key, value)
return value
- end;
+ end,
})
---@private
M.__namespaces = namespaces
-
---@private
local function execute_lens(lens, bufnr, client_id)
local line = lens.range.start.line
@@ -44,10 +43,14 @@ local function execute_lens(lens, bufnr, client_id)
local command_provider = client.server_capabilities.executeCommandProvider
local commands = type(command_provider) == 'table' and command_provider.commands or {}
if not vim.tbl_contains(commands, command.command) then
- vim.notify(string.format(
- "Language server does not support command `%s`. This command may require a client extension.", command.command),
- vim.log.levels.WARN)
- return
+ vim.notify(
+ string.format(
+ 'Language server does not support command `%s`. This command may require a client extension.',
+ command.command
+ ),
+ vim.log.levels.WARN
+ )
+ return
end
client.request('workspace/executeCommand', command, function(...)
local result = vim.lsp.handlers['workspace/executeCommand'](...)
@@ -56,14 +59,15 @@ local function execute_lens(lens, bufnr, client_id)
end, bufnr)
end
-
--- Return all lenses for the given buffer
---
---@param bufnr number Buffer number. 0 can be used for the current buffer.
---@return table (`CodeLens[]`)
function M.get(bufnr)
local lenses_by_client = lens_cache_by_buf[bufnr or 0]
- if not lenses_by_client then return {} end
+ if not lenses_by_client then
+ return {}
+ end
local lenses = {}
for _, client_lenses in pairs(lenses_by_client) do
vim.list_extend(lenses, client_lenses)
@@ -71,7 +75,6 @@ function M.get(bufnr)
return lenses
end
-
--- Run the code lens in the current line
---
function M.run()
@@ -82,7 +85,7 @@ function M.run()
for client, lenses in pairs(lenses_by_client) do
for _, lens in pairs(lenses) do
if lens.range.start.line == (line - 1) then
- table.insert(options, {client=client, lens=lens})
+ table.insert(options, { client = client, lens = lens })
end
end
end
@@ -105,7 +108,6 @@ function M.run()
end
end
-
--- Display the lenses using virtual text
---
---@param lenses table of lenses to display (`CodeLens[] | null`)
@@ -133,19 +135,20 @@ function M.display(lenses, bufnr, client_id)
local num_line_lenses = #line_lenses
for j, lens in ipairs(line_lenses) do
local text = lens.command and lens.command.title or 'Unresolved lens ...'
- table.insert(chunks, {text, 'LspCodeLens' })
+ table.insert(chunks, { text, 'LspCodeLens' })
if j < num_line_lenses then
- table.insert(chunks, {' | ', 'LspCodeLensSeparator' })
+ table.insert(chunks, { ' | ', 'LspCodeLensSeparator' })
end
end
if #chunks > 0 then
- api.nvim_buf_set_extmark(bufnr, ns, i, 0, { virt_text = chunks,
- hl_mode="combine" })
+ api.nvim_buf_set_extmark(bufnr, ns, i, 0, {
+ virt_text = chunks,
+ hl_mode = 'combine',
+ })
end
end
end
-
--- Store lenses for a specific buffer and client
---
---@param lenses table of lenses to store (`CodeLens[] | null`)
@@ -158,16 +161,17 @@ function M.save(lenses, bufnr, client_id)
lens_cache_by_buf[bufnr] = lenses_by_client
local ns = namespaces[client_id]
api.nvim_buf_attach(bufnr, false, {
- on_detach = function(b) lens_cache_by_buf[b] = nil end,
+ on_detach = function(b)
+ lens_cache_by_buf[b] = nil
+ end,
on_lines = function(_, b, _, first_lnum, last_lnum)
api.nvim_buf_clear_namespace(b, ns, first_lnum, last_lnum)
- end
+ end,
})
end
lenses_by_client[client_id] = lenses
end
-
---@private
local function resolve_lenses(lenses, bufnr, client_id, callback)
lenses = lenses or {}
@@ -201,8 +205,7 @@ local function resolve_lenses(lenses, bufnr, client_id, callback)
ns,
lens.range.start.line,
0,
- { virt_text = {{ lens.command.title, 'LspCodeLens' }},
- hl_mode="combine" }
+ { virt_text = { { lens.command.title, 'LspCodeLens' } }, hl_mode = 'combine' }
)
end
countdown()
@@ -211,13 +214,12 @@ local function resolve_lenses(lenses, bufnr, client_id, callback)
end
end
-
--- |lsp-handler| for the method `textDocument/codeLens`
---
function M.on_codelens(err, result, ctx, _)
if err then
active_refreshes[ctx.bufnr] = nil
- local _ = log.error() and log.error("codelens", err)
+ local _ = log.error() and log.error('codelens', err)
return
end
@@ -232,7 +234,6 @@ function M.on_codelens(err, result, ctx, _)
end)
end
-
--- Refresh the codelens for the current buffer
---
--- It is recommended to trigger this using an autocmd or via keymap.
@@ -243,7 +244,7 @@ end
---
function M.refresh()
local params = {
- textDocument = util.make_text_document_params()
+ textDocument = util.make_text_document_params(),
}
local bufnr = api.nvim_get_current_buf()
if active_refreshes[bufnr] then
@@ -253,5 +254,4 @@ function M.refresh()
vim.lsp.buf_request(0, 'textDocument/codeLens', params, M.on_codelens)
end
-
return M
diff --git a/runtime/lua/vim/lsp/diagnostic.lua b/runtime/lua/vim/lsp/diagnostic.lua
index 28a236cc7e..126be2a0ad 100644
--- a/runtime/lua/vim/lsp/diagnostic.lua
+++ b/runtime/lua/vim/lsp/diagnostic.lua
@@ -50,12 +50,12 @@ end
---@private
local function line_byte_from_position(lines, lnum, col, offset_encoding)
- if not lines or offset_encoding == "utf-8" then
+ if not lines or offset_encoding == 'utf-8' then
return col
end
local line = lines[lnum + 1]
- local ok, result = pcall(vim.str_byteindex, line, col, offset_encoding == "utf-16")
+ local ok, result = pcall(vim.str_byteindex, line, col, offset_encoding == 'utf-16')
if ok then
return result
end
@@ -75,7 +75,7 @@ local function get_buf_lines(bufnr)
return
end
- local content = f:read("*a")
+ local content = f:read('*a')
if not content then
-- Some LSP servers report diagnostics at a directory level, in which case
-- io.read() returns nil
@@ -83,7 +83,7 @@ local function get_buf_lines(bufnr)
return
end
- local lines = vim.split(content, "\n")
+ local lines = vim.split(content, '\n')
f:close()
return lines
end
@@ -92,10 +92,10 @@ end
local function diagnostic_lsp_to_vim(diagnostics, bufnr, client_id)
local buf_lines = get_buf_lines(bufnr)
local client = vim.lsp.get_client_by_id(client_id)
- local offset_encoding = client and client.offset_encoding or "utf-16"
+ local offset_encoding = client and client.offset_encoding or 'utf-16'
return vim.tbl_map(function(diagnostic)
local start = diagnostic.range.start
- local _end = diagnostic.range["end"]
+ local _end = diagnostic.range['end']
return {
lnum = start.line,
col = line_byte_from_position(buf_lines, start.line, start.character, offset_encoding),
@@ -122,14 +122,14 @@ end
---@private
local function diagnostic_vim_to_lsp(diagnostics)
return vim.tbl_map(function(diagnostic)
- return vim.tbl_extend("keep", {
+ return vim.tbl_extend('keep', {
-- "keep" the below fields over any duplicate fields in diagnostic.user_data.lsp
range = {
start = {
line = diagnostic.lnum,
character = diagnostic.col,
},
- ["end"] = {
+ ['end'] = {
line = diagnostic.end_lnum,
character = diagnostic.end_col,
},
@@ -148,10 +148,10 @@ local _client_namespaces = {}
---
---@param client_id number The id of the LSP client
function M.get_namespace(client_id)
- vim.validate { client_id = { client_id, 'n' } }
+ vim.validate({ client_id = { client_id, 'n' } })
if not _client_namespaces[client_id] then
local client = vim.lsp.get_client_by_id(client_id)
- local name = string.format("vim.lsp.%s.%d", client and client.name or "unknown", client_id)
+ local name = string.format('vim.lsp.%s.%d', client and client.name or 'unknown', client_id)
_client_namespaces[client_id] = vim.api.nvim_create_namespace(name)
end
return _client_namespaces[client_id]
@@ -203,7 +203,7 @@ function M.on_publish_diagnostics(_, result, ctx, config)
for _, opt in pairs(config) do
if type(opt) == 'table' then
if not opt.severity and opt.severity_limit then
- opt.severity = {min=severity_lsp_to_vim(opt.severity_limit)}
+ opt.severity = { min = severity_lsp_to_vim(opt.severity_limit) }
end
end
end
@@ -240,7 +240,6 @@ end
-- Deprecated Functions {{{
-
--- Save diagnostics to the current buffer.
---
---@deprecated Prefer |vim.diagnostic.set()|
@@ -251,7 +250,7 @@ end
---@param client_id number
---@private
function M.save(diagnostics, bufnr, client_id)
- vim.deprecate('vim.lsp.diagnostic.save', 'vim.diagnostic.set', '0.8' )
+ vim.deprecate('vim.lsp.diagnostic.save', 'vim.diagnostic.set', '0.8')
local namespace = M.get_namespace(client_id)
vim.diagnostic.set(namespace, bufnr, diagnostic_lsp_to_vim(diagnostics, bufnr, client_id))
end
@@ -265,14 +264,14 @@ end
--- If nil, diagnostics of all clients are included.
---@return table with diagnostics grouped by bufnr (bufnr: Diagnostic[])
function M.get_all(client_id)
- vim.deprecate('vim.lsp.diagnostic.get_all', 'vim.diagnostic.get', '0.8' )
+ vim.deprecate('vim.lsp.diagnostic.get_all', 'vim.diagnostic.get', '0.8')
local result = {}
local namespace
if client_id then
namespace = M.get_namespace(client_id)
end
for _, bufnr in ipairs(vim.api.nvim_list_bufs()) do
- local diagnostics = diagnostic_vim_to_lsp(vim.diagnostic.get(bufnr, {namespace = namespace}))
+ local diagnostics = diagnostic_vim_to_lsp(vim.diagnostic.get(bufnr, { namespace = namespace }))
result[bufnr] = diagnostics
end
return result
@@ -287,8 +286,10 @@ end
--- Else, return just the diagnostics associated with the client_id.
---@param predicate function|nil Optional function for filtering diagnostics
function M.get(bufnr, client_id, predicate)
- vim.deprecate('vim.lsp.diagnostic.get', 'vim.diagnostic.get', '0.8' )
- predicate = predicate or function() return true end
+ vim.deprecate('vim.lsp.diagnostic.get', 'vim.diagnostic.get', '0.8')
+ predicate = predicate or function()
+ return true
+ end
if client_id == nil then
local all_diagnostics = {}
vim.lsp.for_each_buffer_client(bufnr, function(_, iter_client_id, _)
@@ -301,7 +302,7 @@ function M.get(bufnr, client_id, predicate)
end
local namespace = M.get_namespace(client_id)
- return diagnostic_vim_to_lsp(vim.tbl_filter(predicate, vim.diagnostic.get(bufnr, {namespace=namespace})))
+ return diagnostic_vim_to_lsp(vim.tbl_filter(predicate, vim.diagnostic.get(bufnr, { namespace = namespace })))
end
--- Get the diagnostics by line
@@ -325,7 +326,7 @@ function M.get_line_diagnostics(bufnr, line_nr, opts, client_id)
if opts.severity then
opts.severity = severity_lsp_to_vim(opts.severity)
elseif opts.severity_limit then
- opts.severity = {min=severity_lsp_to_vim(opts.severity_limit)}
+ opts.severity = { min = severity_lsp_to_vim(opts.severity_limit) }
end
if client_id then
@@ -349,7 +350,7 @@ end
---@param severity DiagnosticSeverity
---@param client_id number the client id
function M.get_count(bufnr, severity, client_id)
- vim.deprecate('vim.lsp.diagnostic.get_count', 'vim.diagnostic.get', '0.8' )
+ vim.deprecate('vim.lsp.diagnostic.get_count', 'vim.diagnostic.get', '0.8')
severity = severity_lsp_to_vim(severity)
local opts = { severity = severity }
if client_id ~= nil then
@@ -366,15 +367,15 @@ end
---@param opts table See |vim.lsp.diagnostic.goto_next()|
---@return table Previous diagnostic
function M.get_prev(opts)
- vim.deprecate('vim.lsp.diagnostic.get_prev', 'vim.diagnostic.get_prev', '0.8' )
+ vim.deprecate('vim.lsp.diagnostic.get_prev', 'vim.diagnostic.get_prev', '0.8')
if opts then
if opts.severity then
opts.severity = severity_lsp_to_vim(opts.severity)
elseif opts.severity_limit then
- opts.severity = {min=severity_lsp_to_vim(opts.severity_limit)}
+ opts.severity = { min = severity_lsp_to_vim(opts.severity_limit) }
end
end
- return diagnostic_vim_to_lsp({vim.diagnostic.get_prev(opts)})[1]
+ return diagnostic_vim_to_lsp({ vim.diagnostic.get_prev(opts) })[1]
end
--- Return the pos, {row, col}, for the prev diagnostic in the current buffer.
@@ -384,12 +385,12 @@ end
---@param opts table See |vim.lsp.diagnostic.goto_next()|
---@return table Previous diagnostic position
function M.get_prev_pos(opts)
- vim.deprecate('vim.lsp.diagnostic.get_prev_pos', 'vim.diagnostic.get_prev_pos', '0.8' )
+ vim.deprecate('vim.lsp.diagnostic.get_prev_pos', 'vim.diagnostic.get_prev_pos', '0.8')
if opts then
if opts.severity then
opts.severity = severity_lsp_to_vim(opts.severity)
elseif opts.severity_limit then
- opts.severity = {min=severity_lsp_to_vim(opts.severity_limit)}
+ opts.severity = { min = severity_lsp_to_vim(opts.severity_limit) }
end
end
return vim.diagnostic.get_prev_pos(opts)
@@ -401,12 +402,12 @@ end
---
---@param opts table See |vim.lsp.diagnostic.goto_next()|
function M.goto_prev(opts)
- vim.deprecate('vim.lsp.diagnostic.goto_prev', 'vim.diagnostic.goto_prev', '0.8' )
+ vim.deprecate('vim.lsp.diagnostic.goto_prev', 'vim.diagnostic.goto_prev', '0.8')
if opts then
if opts.severity then
opts.severity = severity_lsp_to_vim(opts.severity)
elseif opts.severity_limit then
- opts.severity = {min=severity_lsp_to_vim(opts.severity_limit)}
+ opts.severity = { min = severity_lsp_to_vim(opts.severity_limit) }
end
end
return vim.diagnostic.goto_prev(opts)
@@ -419,15 +420,15 @@ end
---@param opts table See |vim.lsp.diagnostic.goto_next()|
---@return table Next diagnostic
function M.get_next(opts)
- vim.deprecate('vim.lsp.diagnostic.get_next', 'vim.diagnostic.get_next', '0.8' )
+ vim.deprecate('vim.lsp.diagnostic.get_next', 'vim.diagnostic.get_next', '0.8')
if opts then
if opts.severity then
opts.severity = severity_lsp_to_vim(opts.severity)
elseif opts.severity_limit then
- opts.severity = {min=severity_lsp_to_vim(opts.severity_limit)}
+ opts.severity = { min = severity_lsp_to_vim(opts.severity_limit) }
end
end
- return diagnostic_vim_to_lsp({vim.diagnostic.get_next(opts)})[1]
+ return diagnostic_vim_to_lsp({ vim.diagnostic.get_next(opts) })[1]
end
--- Return the pos, {row, col}, for the next diagnostic in the current buffer.
@@ -437,12 +438,12 @@ end
---@param opts table See |vim.lsp.diagnostic.goto_next()|
---@return table Next diagnostic position
function M.get_next_pos(opts)
- vim.deprecate('vim.lsp.diagnostic.get_next_pos', 'vim.diagnostic.get_next_pos', '0.8' )
+ vim.deprecate('vim.lsp.diagnostic.get_next_pos', 'vim.diagnostic.get_next_pos', '0.8')
if opts then
if opts.severity then
opts.severity = severity_lsp_to_vim(opts.severity)
elseif opts.severity_limit then
- opts.severity = {min=severity_lsp_to_vim(opts.severity_limit)}
+ opts.severity = { min = severity_lsp_to_vim(opts.severity_limit) }
end
end
return vim.diagnostic.get_next_pos(opts)
@@ -452,12 +453,12 @@ end
---
---@deprecated Prefer |vim.diagnostic.goto_next()|
function M.goto_next(opts)
- vim.deprecate('vim.lsp.diagnostic.goto_next', 'vim.diagnostic.goto_next', '0.8' )
+ vim.deprecate('vim.lsp.diagnostic.goto_next', 'vim.diagnostic.goto_next', '0.8')
if opts then
if opts.severity then
opts.severity = severity_lsp_to_vim(opts.severity)
elseif opts.severity_limit then
- opts.severity = {min=severity_lsp_to_vim(opts.severity_limit)}
+ opts.severity = { min = severity_lsp_to_vim(opts.severity_limit) }
end
end
return vim.diagnostic.goto_next(opts)
@@ -476,10 +477,10 @@ end
--- - severity_limit (DiagnosticSeverity):
--- - Limit severity of diagnostics found. E.g. "Warning" means { "Error", "Warning" } will be valid.
function M.set_signs(diagnostics, bufnr, client_id, _, opts)
- vim.deprecate('vim.lsp.diagnostic.set_signs', nil , '0.8' )
+ vim.deprecate('vim.lsp.diagnostic.set_signs', nil, '0.8')
local namespace = M.get_namespace(client_id)
if opts and not opts.severity and opts.severity_limit then
- opts.severity = {min=severity_lsp_to_vim(opts.severity_limit)}
+ opts.severity = { min = severity_lsp_to_vim(opts.severity_limit) }
end
vim.diagnostic._set_signs(namespace, bufnr, diagnostic_lsp_to_vim(diagnostics, bufnr, client_id), opts)
@@ -497,10 +498,10 @@ end
--- - severity_limit (DiagnosticSeverity):
--- - Limit severity of diagnostics found. E.g. "Warning" means { "Error", "Warning" } will be valid.
function M.set_underline(diagnostics, bufnr, client_id, _, opts)
- vim.deprecate('vim.lsp.diagnostic.set_underline', nil , '0.8' )
+ vim.deprecate('vim.lsp.diagnostic.set_underline', nil, '0.8')
local namespace = M.get_namespace(client_id)
if opts and not opts.severity and opts.severity_limit then
- opts.severity = {min=severity_lsp_to_vim(opts.severity_limit)}
+ opts.severity = { min = severity_lsp_to_vim(opts.severity_limit) }
end
return vim.diagnostic._set_underline(namespace, bufnr, diagnostic_lsp_to_vim(diagnostics, bufnr, client_id), opts)
end
@@ -519,10 +520,10 @@ end
--- - severity_limit (DiagnosticSeverity):
--- - Limit severity of diagnostics found. E.g. "Warning" means { "Error", "Warning" } will be valid.
function M.set_virtual_text(diagnostics, bufnr, client_id, _, opts)
- vim.deprecate('vim.lsp.diagnostic.set_virtual_text', nil , '0.8' )
+ vim.deprecate('vim.lsp.diagnostic.set_virtual_text', nil, '0.8')
local namespace = M.get_namespace(client_id)
if opts and not opts.severity and opts.severity_limit then
- opts.severity = {min=severity_lsp_to_vim(opts.severity_limit)}
+ opts.severity = { min = severity_lsp_to_vim(opts.severity_limit) }
end
return vim.diagnostic._set_virtual_text(namespace, bufnr, diagnostic_lsp_to_vim(diagnostics, bufnr, client_id), opts)
end
@@ -538,7 +539,7 @@ end
---@return an array of [text, hl_group] arrays. This can be passed directly to
--- the {virt_text} option of |nvim_buf_set_extmark()|.
function M.get_virtual_text_chunks_for_line(bufnr, _, line_diags, opts)
- vim.deprecate('vim.lsp.diagnostic.get_virtual_text_chunks_for_line', nil, '0.8' )
+ vim.deprecate('vim.lsp.diagnostic.get_virtual_text_chunks_for_line', nil, '0.8')
return vim.diagnostic._get_virt_text_chunks(diagnostic_lsp_to_vim(line_diags, bufnr), opts)
end
@@ -556,14 +557,14 @@ end
---@param position table|nil The (0,0)-indexed position
---@return table {popup_bufnr, win_id}
function M.show_position_diagnostics(opts, buf_nr, position)
- vim.deprecate('vim.lsp.diagnostic.show_position_diagnostics', 'vim.diagnostic.open_float', '0.8' )
+ vim.deprecate('vim.lsp.diagnostic.show_position_diagnostics', 'vim.diagnostic.open_float', '0.8')
opts = opts or {}
- opts.scope = "cursor"
+ opts.scope = 'cursor'
opts.pos = position
if opts.severity then
opts.severity = severity_lsp_to_vim(opts.severity)
elseif opts.severity_limit then
- opts.severity = {min=severity_lsp_to_vim(opts.severity_limit)}
+ opts.severity = { min = severity_lsp_to_vim(opts.severity_limit) }
end
return vim.diagnostic.open_float(buf_nr, opts)
end
@@ -580,9 +581,9 @@ end
---@param client_id number|nil the client id
---@return table {popup_bufnr, win_id}
function M.show_line_diagnostics(opts, buf_nr, line_nr, client_id)
- vim.deprecate('vim.lsp.diagnostic.show_line_diagnostics', 'vim.diagnostic.open_float', '0.8' )
+ vim.deprecate('vim.lsp.diagnostic.show_line_diagnostics', 'vim.diagnostic.open_float', '0.8')
opts = opts or {}
- opts.scope = "line"
+ opts.scope = 'line'
opts.pos = line_nr
if client_id then
opts.namespace = M.get_namespace(client_id)
@@ -604,7 +605,7 @@ end
--- client. The default is to redraw diagnostics for all attached
--- clients.
function M.redraw(bufnr, client_id)
- vim.deprecate('vim.lsp.diagnostic.redraw', 'vim.diagnostic.show', '0.8' )
+ vim.deprecate('vim.lsp.diagnostic.redraw', 'vim.diagnostic.show', '0.8')
bufnr = get_bufnr(bufnr)
if not client_id then
return vim.lsp.for_each_buffer_client(bufnr, function(client)
@@ -632,12 +633,12 @@ end
--- - {workspace}: (boolean, default true)
--- - Set the list with workspace diagnostics
function M.set_qflist(opts)
- vim.deprecate('vim.lsp.diagnostic.set_qflist', 'vim.diagnostic.setqflist', '0.8' )
+ vim.deprecate('vim.lsp.diagnostic.set_qflist', 'vim.diagnostic.setqflist', '0.8')
opts = opts or {}
if opts.severity then
opts.severity = severity_lsp_to_vim(opts.severity)
elseif opts.severity_limit then
- opts.severity = {min=severity_lsp_to_vim(opts.severity_limit)}
+ opts.severity = { min = severity_lsp_to_vim(opts.severity_limit) }
end
if opts.client_id then
opts.client_id = nil
@@ -664,12 +665,12 @@ end
--- - {workspace}: (boolean, default false)
--- - Set the list with workspace diagnostics
function M.set_loclist(opts)
- vim.deprecate('vim.lsp.diagnostic.set_loclist', 'vim.diagnostic.setloclist', '0.8' )
+ vim.deprecate('vim.lsp.diagnostic.set_loclist', 'vim.diagnostic.setloclist', '0.8')
opts = opts or {}
if opts.severity then
opts.severity = severity_lsp_to_vim(opts.severity)
elseif opts.severity_limit then
- opts.severity = {min=severity_lsp_to_vim(opts.severity_limit)}
+ opts.severity = { min = severity_lsp_to_vim(opts.severity_limit) }
end
if opts.client_id then
opts.client_id = nil
@@ -692,7 +693,7 @@ end
-- send diagnostic information and the client will still process it. The
-- diagnostics are simply not displayed to the user.
function M.disable(bufnr, client_id)
- vim.deprecate('vim.lsp.diagnostic.disable', 'vim.diagnostic.disable', '0.8' )
+ vim.deprecate('vim.lsp.diagnostic.disable', 'vim.diagnostic.disable', '0.8')
if not client_id then
return vim.lsp.for_each_buffer_client(bufnr, function(client)
M.disable(bufnr, client.id)
@@ -713,7 +714,7 @@ end
--- client. The default is to enable diagnostics for all attached
--- clients.
function M.enable(bufnr, client_id)
- vim.deprecate('vim.lsp.diagnostic.enable', 'vim.diagnostic.enable', '0.8' )
+ vim.deprecate('vim.lsp.diagnostic.enable', 'vim.diagnostic.enable', '0.8')
if not client_id then
return vim.lsp.for_each_buffer_client(bufnr, function(client)
M.enable(bufnr, client.id)
diff --git a/runtime/lua/vim/lsp/handlers.lua b/runtime/lua/vim/lsp/handlers.lua
index 5c80ed0d10..61cc89dcac 100644
--- a/runtime/lua/vim/lsp/handlers.lua
+++ b/runtime/lua/vim/lsp/handlers.lua
@@ -1,6 +1,6 @@
-local log = require 'vim.lsp.log'
-local protocol = require 'vim.lsp.protocol'
-local util = require 'vim.lsp.util'
+local log = require('vim.lsp.log')
+local protocol = require('vim.lsp.protocol')
+local util = require('vim.lsp.util')
local vim = vim
local api = vim.api
@@ -12,8 +12,8 @@ local M = {}
--- Writes to error buffer.
---@param ... (table of strings) Will be concatenated before being written
local function err_message(...)
- vim.notify(table.concat(vim.tbl_flatten{...}), vim.log.levels.ERROR)
- api.nvim_command("redraw")
+ vim.notify(table.concat(vim.tbl_flatten({ ... })), vim.log.levels.ERROR)
+ api.nvim_command('redraw')
end
--see: https://microsoft.github.io/language-server-protocol/specifications/specification-current/#workspace_executeCommand
@@ -25,15 +25,17 @@ end
local function progress_handler(_, result, ctx, _)
local client_id = ctx.client_id
local client = vim.lsp.get_client_by_id(client_id)
- local client_name = client and client.name or string.format("id=%d", client_id)
+ local client_name = client and client.name or string.format('id=%d', client_id)
if not client then
- err_message("LSP[", client_name, "] client has shut down during progress update")
+ err_message('LSP[', client_name, '] client has shut down during progress update')
return vim.NIL
end
- local val = result.value -- unspecified yet
- local token = result.token -- string or number
+ local val = result.value -- unspecified yet
+ local token = result.token -- string or number
- if type(val) ~= 'table' then val = { content=val } end
+ if type(val) ~= 'table' then
+ val = { content = val }
+ end
if val.kind then
if val.kind == 'begin' then
client.messages.progress[token] = {
@@ -42,11 +44,11 @@ local function progress_handler(_, result, ctx, _)
percentage = val.percentage,
}
elseif val.kind == 'report' then
- client.messages.progress[token].message = val.message;
- client.messages.progress[token].percentage = val.percentage;
+ client.messages.progress[token].message = val.message
+ client.messages.progress[token].percentage = val.percentage
elseif val.kind == 'end' then
if client.messages.progress[token] == nil then
- err_message("LSP[", client_name, "] received `end` message with no corresponding `begin`")
+ err_message('LSP[', client_name, '] received `end` message with no corresponding `begin`')
else
client.messages.progress[token].message = val.message
client.messages.progress[token].done = true
@@ -57,20 +59,20 @@ local function progress_handler(_, result, ctx, _)
client.messages.progress[token].done = true
end
- vim.api.nvim_command("doautocmd <nomodeline> User LspProgressUpdate")
+ vim.api.nvim_command('doautocmd <nomodeline> User LspProgressUpdate')
end
--see: https://microsoft.github.io/language-server-protocol/specifications/specification-current/#progress
M['$/progress'] = progress_handler
--see: https://microsoft.github.io/language-server-protocol/specifications/specification-current/#window_workDoneProgress_create
-M['window/workDoneProgress/create'] = function(_, result, ctx)
+M['window/workDoneProgress/create'] = function(_, result, ctx)
local client_id = ctx.client_id
local client = vim.lsp.get_client_by_id(client_id)
- local token = result.token -- string or number
- local client_name = client and client.name or string.format("id=%d", client_id)
+ local token = result.token -- string or number
+ local client_name = client and client.name or string.format('id=%d', client_id)
if not client then
- err_message("LSP[", client_name, "] client has shut down while creating progress report")
+ err_message('LSP[', client_name, '] client has shut down while creating progress report')
return vim.NIL
end
client.messages.progress[token] = {}
@@ -79,20 +81,19 @@ end
--see: https://microsoft.github.io/language-server-protocol/specifications/specification-current/#window_showMessageRequest
M['window/showMessageRequest'] = function(_, result)
-
local actions = result.actions
print(result.message)
- local option_strings = {result.message, "\nRequest Actions:"}
+ local option_strings = { result.message, '\nRequest Actions:' }
for i, action in ipairs(actions) do
local title = action.title:gsub('\r\n', '\\r\\n')
title = title:gsub('\n', '\\n')
- table.insert(option_strings, string.format("%d. %s", i, title))
+ table.insert(option_strings, string.format('%d. %s', i, title))
end
-- window/showMessageRequest can return either MessageActionItem[] or null.
local choice = vim.fn.inputlist(option_strings)
if choice < 1 or choice > #actions then
- return vim.NIL
+ return vim.NIL
else
return actions[choice]
end
@@ -101,11 +102,11 @@ end
--see: https://microsoft.github.io/language-server-protocol/specifications/specification-current/#client_registerCapability
M['client/registerCapability'] = function(_, _, ctx)
local client_id = ctx.client_id
- local warning_tpl = "The language server %s triggers a registerCapability "..
- "handler despite dynamicRegistration set to false. "..
- "Report upstream, this warning is harmless"
+ local warning_tpl = 'The language server %s triggers a registerCapability '
+ .. 'handler despite dynamicRegistration set to false. '
+ .. 'Report upstream, this warning is harmless'
local client = vim.lsp.get_client_by_id(client_id)
- local client_name = client and client.name or string.format("id=%d", client_id)
+ local client_name = client and client.name or string.format('id=%d', client_id)
local warning = string.format(warning_tpl, client_name)
log.warn(warning)
return vim.NIL
@@ -113,17 +114,19 @@ end
--see: https://microsoft.github.io/language-server-protocol/specifications/specification-current/#workspace_applyEdit
M['workspace/applyEdit'] = function(_, workspace_edit, ctx)
- if not workspace_edit then return end
+ if not workspace_edit then
+ return
+ end
-- TODO(ashkan) Do something more with label?
local client_id = ctx.client_id
local client = vim.lsp.get_client_by_id(client_id)
if workspace_edit.label then
- print("Workspace edit", workspace_edit.label)
+ print('Workspace edit', workspace_edit.label)
end
local status, result = pcall(util.apply_workspace_edit, workspace_edit.edit, client.offset_encoding)
return {
- applied = status;
- failureReason = result;
+ applied = status,
+ failureReason = result,
}
end
@@ -132,7 +135,7 @@ M['workspace/configuration'] = function(_, result, ctx)
local client_id = ctx.client_id
local client = vim.lsp.get_client_by_id(client_id)
if not client then
- err_message("LSP[", client_id, "] client has shut down after sending a workspace/configuration request")
+ err_message('LSP[', client_id, '] client has shut down after sending a workspace/configuration request')
return
end
if not result.items then
@@ -158,7 +161,7 @@ M['workspace/workspaceFolders'] = function(_, _, ctx)
local client_id = ctx.client_id
local client = vim.lsp.get_client_by_id(client_id)
if not client then
- err_message("LSP[id=", client_id, "] client has shut down after sending the message")
+ err_message('LSP[id=', client_id, '] client has shut down after sending the message')
return
end
return client.workspace_folders or vim.NIL
@@ -172,7 +175,6 @@ M['textDocument/codeLens'] = function(...)
return require('vim.lsp.codelens').on_codelens(...)
end
-
--see: https://microsoft.github.io/language-server-protocol/specifications/specification-current/#textDocument_references
M['textDocument/references'] = function(_, result, ctx, config)
if not result or vim.tbl_isempty(result) then
@@ -182,23 +184,22 @@ M['textDocument/references'] = function(_, result, ctx, config)
config = config or {}
if config.loclist then
vim.fn.setloclist(0, {}, ' ', {
- title = 'References';
- items = util.locations_to_items(result, client.offset_encoding);
- context = ctx;
+ title = 'References',
+ items = util.locations_to_items(result, client.offset_encoding),
+ context = ctx,
})
- api.nvim_command("lopen")
+ api.nvim_command('lopen')
else
vim.fn.setqflist({}, ' ', {
- title = 'References';
- items = util.locations_to_items(result, client.offset_encoding);
- context = ctx;
+ title = 'References',
+ items = util.locations_to_items(result, client.offset_encoding),
+ context = ctx,
})
- api.nvim_command("botright copen")
+ api.nvim_command('botright copen')
end
end
end
-
---@private
--- Return a function that converts LSP responses to list items and opens the list
---
@@ -218,27 +219,26 @@ local function response_to_list(map_result, entity, title_fn)
config = config or {}
if config.loclist then
vim.fn.setloclist(0, {}, ' ', {
- title = title_fn(ctx);
- items = map_result(result, ctx.bufnr);
- context = ctx;
+ title = title_fn(ctx),
+ items = map_result(result, ctx.bufnr),
+ context = ctx,
})
- api.nvim_command("lopen")
+ api.nvim_command('lopen')
else
vim.fn.setqflist({}, ' ', {
- title = title_fn(ctx);
- items = map_result(result, ctx.bufnr);
- context = ctx;
+ title = title_fn(ctx),
+ items = map_result(result, ctx.bufnr),
+ context = ctx,
})
- api.nvim_command("botright copen")
+ api.nvim_command('botright copen')
end
end
end
end
-
--see: https://microsoft.github.io/language-server-protocol/specifications/specification-current/#textDocument_documentSymbol
M['textDocument/documentSymbol'] = response_to_list(util.symbols_to_items, 'document symbols', function(ctx)
- local fname = vim.fn.fnamemodify(vim.uri_to_fname(ctx.params.textDocument.uri), ":.")
+ local fname = vim.fn.fnamemodify(vim.uri_to_fname(ctx.params.textDocument.uri), ':.')
return string.format('Symbols in %s', fname)
end)
@@ -249,36 +249,44 @@ end)
--see: https://microsoft.github.io/language-server-protocol/specifications/specification-current/#textDocument_rename
M['textDocument/rename'] = function(_, result, ctx, _)
- if not result then return end
+ if not result then
+ return
+ end
local client = vim.lsp.get_client_by_id(ctx.client_id)
util.apply_workspace_edit(result, client.offset_encoding)
end
--see: https://microsoft.github.io/language-server-protocol/specifications/specification-current/#textDocument_rangeFormatting
M['textDocument/rangeFormatting'] = function(_, result, ctx, _)
- if not result then return end
+ if not result then
+ return
+ end
local client = vim.lsp.get_client_by_id(ctx.client_id)
util.apply_text_edits(result, ctx.bufnr, client.offset_encoding)
end
--see: https://microsoft.github.io/language-server-protocol/specifications/specification-current/#textDocument_formatting
M['textDocument/formatting'] = function(_, result, ctx, _)
- if not result then return end
+ if not result then
+ return
+ end
local client = vim.lsp.get_client_by_id(ctx.client_id)
util.apply_text_edits(result, ctx.bufnr, client.offset_encoding)
end
--see: https://microsoft.github.io/language-server-protocol/specifications/specification-current/#textDocument_completion
M['textDocument/completion'] = function(_, result, _, _)
- if vim.tbl_isempty(result or {}) then return end
+ if vim.tbl_isempty(result or {}) then
+ return
+ end
local row, col = unpack(api.nvim_win_get_cursor(0))
- local line = assert(api.nvim_buf_get_lines(0, row-1, row, false)[1])
- local line_to_cursor = line:sub(col+1)
+ local line = assert(api.nvim_buf_get_lines(0, row - 1, row, false)[1])
+ local line_to_cursor = line:sub(col + 1)
local textMatch = vim.fn.match(line_to_cursor, '\\k*$')
- local prefix = line_to_cursor:sub(textMatch+1)
+ local prefix = line_to_cursor:sub(textMatch + 1)
local matches = util.text_document_completion_list_to_complete_items(result, prefix)
- vim.fn.complete(textMatch+1, matches)
+ vim.fn.complete(textMatch + 1, matches)
end
--- |lsp-handler| for the method "textDocument/hover"
@@ -307,7 +315,7 @@ function M.hover(_, result, ctx, config)
vim.notify('No information available')
return
end
- return util.open_floating_preview(markdown_lines, "markdown", config)
+ return util.open_floating_preview(markdown_lines, 'markdown', config)
end
--see: https://microsoft.github.io/language-server-protocol/specifications/specification-current/#textDocument_hover
@@ -319,28 +327,30 @@ M['textDocument/hover'] = M.hover
---@param result (table) result of LSP method; a location or a list of locations.
---@param ctx (table) table containing the context of the request, including the method
---(`textDocument/definition` can return `Location` or `Location[]`
-local function location_handler(_, result, ctx, _)
+local function location_handler(_, result, ctx, config)
if result == nil or vim.tbl_isempty(result) then
local _ = log.info() and log.info(ctx.method, 'No location found')
return nil
end
local client = vim.lsp.get_client_by_id(ctx.client_id)
+ config = config or {}
+
-- textDocument/definition can return Location or Location[]
-- https://microsoft.github.io/language-server-protocol/specifications/specification-current/#textDocument_definition
if vim.tbl_islist(result) then
- util.jump_to_location(result[1], client.offset_encoding)
+ util.jump_to_location(result[1], client.offset_encoding, config.reuse_win)
if #result > 1 then
vim.fn.setqflist({}, ' ', {
title = 'LSP locations',
- items = util.locations_to_items(result, client.offset_encoding)
+ items = util.locations_to_items(result, client.offset_encoding),
})
- api.nvim_command("botright copen")
+ api.nvim_command('botright copen')
end
else
- util.jump_to_location(result, client.offset_encoding)
+ util.jump_to_location(result, client.offset_encoding, config.reuse_win)
end
end
@@ -379,7 +389,7 @@ function M.signature_help(_, result, ctx, config)
return
end
local client = vim.lsp.get_client_by_id(ctx.client_id)
- local triggers = vim.tbl_get(client.server_capabilities, "signatureHelpProvider", "triggerCharacters")
+ local triggers = vim.tbl_get(client.server_capabilities, 'signatureHelpProvider', 'triggerCharacters')
local ft = api.nvim_buf_get_option(ctx.bufnr, 'filetype')
local lines, hl = util.convert_signature_help_to_markdown_lines(result, ft, triggers)
lines = util.trim_empty_lines(lines)
@@ -389,9 +399,9 @@ function M.signature_help(_, result, ctx, config)
end
return
end
- local fbuf, fwin = util.open_floating_preview(lines, "markdown", config)
+ local fbuf, fwin = util.open_floating_preview(lines, 'markdown', config)
if hl then
- api.nvim_buf_add_highlight(fbuf, -1, "LspSignatureActiveParameter", 0, unpack(hl))
+ api.nvim_buf_add_highlight(fbuf, -1, 'LspSignatureActiveParameter', 0, unpack(hl))
end
return fbuf, fwin
end
@@ -401,10 +411,14 @@ M['textDocument/signatureHelp'] = M.signature_help
--see: https://microsoft.github.io/language-server-protocol/specifications/specification-current/#textDocument_documentHighlight
M['textDocument/documentHighlight'] = function(_, result, ctx, _)
- if not result then return end
+ if not result then
+ return
+ end
local client_id = ctx.client_id
local client = vim.lsp.get_client_by_id(client_id)
- if not client then return end
+ if not client then
+ return
+ end
util.buf_highlight_references(ctx.bufnr, result, client.offset_encoding)
end
@@ -417,7 +431,9 @@ end
---@returns `CallHierarchyOutgoingCall[]` if {direction} is `"to"`,
local make_call_hierarchy_handler = function(direction)
return function(_, result)
- if not result then return end
+ if not result then
+ return
+ end
local items = {}
for _, call_hierarchy_call in pairs(result) do
local call_hierarchy_item = call_hierarchy_call[direction]
@@ -430,8 +446,8 @@ local make_call_hierarchy_handler = function(direction)
})
end
end
- vim.fn.setqflist({}, ' ', {title = 'LSP call hierarchy', items = items})
- api.nvim_command("botright copen")
+ vim.fn.setqflist({}, ' ', { title = 'LSP call hierarchy', items = items })
+ api.nvim_command('botright copen')
end
end
@@ -447,15 +463,15 @@ M['window/logMessage'] = function(_, result, ctx, _)
local message = result.message
local client_id = ctx.client_id
local client = vim.lsp.get_client_by_id(client_id)
- local client_name = client and client.name or string.format("id=%d", client_id)
+ local client_name = client and client.name or string.format('id=%d', client_id)
if not client then
- err_message("LSP[", client_name, "] client has shut down after sending ", message)
+ err_message('LSP[', client_name, '] client has shut down after sending ', message)
end
if message_type == protocol.MessageType.Error then
log.error(message)
elseif message_type == protocol.MessageType.Warning then
log.warn(message)
- elseif message_type == protocol.MessageType.Info or message_type == protocol.MessageType.Log then
+ elseif message_type == protocol.MessageType.Info or message_type == protocol.MessageType.Log then
log.info(message)
else
log.debug(message)
@@ -469,15 +485,15 @@ M['window/showMessage'] = function(_, result, ctx, _)
local message = result.message
local client_id = ctx.client_id
local client = vim.lsp.get_client_by_id(client_id)
- local client_name = client and client.name or string.format("id=%d", client_id)
+ local client_name = client and client.name or string.format('id=%d', client_id)
if not client then
- err_message("LSP[", client_name, "] client has shut down after sending ", message)
+ err_message('LSP[', client_name, '] client has shut down after sending ', message)
end
if message_type == protocol.MessageType.Error then
- err_message("LSP[", client_name, "] ", message)
+ err_message('LSP[', client_name, '] ', message)
else
local message_type_name = protocol.MessageType[message_type]
- api.nvim_out_write(string.format("LSP[%s][%s] %s\n", client_name, message_type_name, message))
+ api.nvim_out_write(string.format('LSP[%s][%s] %s\n', client_name, message_type_name, message))
end
return result
end
@@ -485,9 +501,13 @@ end
-- Add boilerplate error validation and logging for all of these.
for k, fn in pairs(M) do
M[k] = function(err, result, ctx, config)
- local _ = log.trace() and log.trace('default_handler', ctx.method, {
- err = err, result = result, ctx=vim.inspect(ctx), config = config
- })
+ local _ = log.trace()
+ and log.trace('default_handler', ctx.method, {
+ err = err,
+ result = result,
+ ctx = vim.inspect(ctx),
+ config = config,
+ })
if err then
-- LSP spec:
@@ -499,7 +519,7 @@ for k, fn in pairs(M) do
-- Per LSP, don't show ContentModified error to the user.
if err.code ~= protocol.ErrorCodes.ContentModified then
local client = vim.lsp.get_client_by_id(ctx.client_id)
- local client_name = client and client.name or string.format("client_id=%d", ctx.client_id)
+ local client_name = client and client.name or string.format('client_id=%d', ctx.client_id)
err_message(client_name .. ': ' .. tostring(err.code) .. ': ' .. err.message)
end
diff --git a/runtime/lua/vim/lsp/health.lua b/runtime/lua/vim/lsp/health.lua
index ed3eea59df..bf8fe0932e 100644
--- a/runtime/lua/vim/lsp/health.lua
+++ b/runtime/lua/vim/lsp/health.lua
@@ -8,20 +8,20 @@ function M.check()
local log = require('vim.lsp.log')
local current_log_level = log.get_level()
local log_level_string = log.levels[current_log_level]
- report_info(string.format("LSP log level : %s", log_level_string))
+ report_info(string.format('LSP log level : %s', log_level_string))
if current_log_level < log.levels.WARN then
- report_warn(string.format("Log level %s will cause degraded performance and high disk usage", log_level_string))
+ report_warn(string.format('Log level %s will cause degraded performance and high disk usage', log_level_string))
end
local log_path = vim.lsp.get_log_path()
- report_info(string.format("Log path: %s", log_path))
+ report_info(string.format('Log path: %s', log_path))
- local log_size = vim.loop.fs_stat(log_path).size
+ local log_file = vim.loop.fs_stat(log_path)
+ local log_size = log_file and log_file.size or 0
local report_fn = (log_size / 1000000 > 100 and report_warn or report_info)
- report_fn(string.format("Log size: %d KB", log_size / 1000 ))
+ report_fn(string.format('Log size: %d KB', log_size / 1000))
end
return M
-
diff --git a/runtime/lua/vim/lsp/log.lua b/runtime/lua/vim/lsp/log.lua
index fff42fd011..29cb27d373 100644
--- a/runtime/lua/vim/lsp/log.lua
+++ b/runtime/lua/vim/lsp/log.lua
@@ -14,21 +14,23 @@ log.levels = vim.deepcopy(vim.log.levels)
-- Default log level is warn.
local current_log_level = log.levels.WARN
-local log_date_format = "%F %H:%M:%S"
-local format_func = function(arg) return vim.inspect(arg, {newline=''}) end
+local log_date_format = '%F %H:%M:%S'
+local format_func = function(arg)
+ return vim.inspect(arg, { newline = '' })
+end
do
- local path_sep = vim.loop.os_uname().version:match("Windows") and "\\" or "/"
+ local path_sep = vim.loop.os_uname().version:match('Windows') and '\\' or '/'
---@private
local function path_join(...)
- return table.concat(vim.tbl_flatten{...}, path_sep)
+ return table.concat(vim.tbl_flatten({ ... }), path_sep)
end
- local logfilename = path_join(vim.fn.stdpath('cache'), 'lsp.log')
+ local logfilename = path_join(vim.fn.stdpath('log'), 'lsp.log')
-- TODO: Ideally the directory should be created in open_logfile(), right
-- before opening the log file, but open_logfile() can be called from libuv
-- callbacks, where using fn.mkdir() is not allowed.
- vim.fn.mkdir(vim.fn.stdpath('cache'), "p")
+ vim.fn.mkdir(vim.fn.stdpath('log'), 'p')
--- Returns the log filename.
---@returns (string) log filename
@@ -41,28 +43,28 @@ do
--- Opens log file. Returns true if file is open, false on error
local function open_logfile()
-- Try to open file only once
- if logfile then return true end
- if openerr then return false end
+ if logfile then
+ return true
+ end
+ if openerr then
+ return false
+ end
- logfile, openerr = io.open(logfilename, "a+")
+ logfile, openerr = io.open(logfilename, 'a+')
if not logfile then
- local err_msg = string.format("Failed to open LSP client log file: %s", openerr)
+ local err_msg = string.format('Failed to open LSP client log file: %s', openerr)
vim.notify(err_msg, vim.log.levels.ERROR)
return false
end
local log_info = vim.loop.fs_stat(logfilename)
if log_info and log_info.size > 1e9 then
- local warn_msg = string.format(
- "LSP client log is large (%d MB): %s",
- log_info.size / (1000 * 1000),
- logfilename
- )
+ local warn_msg = string.format('LSP client log is large (%d MB): %s', log_info.size / (1000 * 1000), logfilename)
vim.notify(warn_msg)
end
-- Start message for logging
- logfile:write(string.format("[START][%s] LSP logging initiated\n", os.date(log_date_format)))
+ logfile:write(string.format('[START][%s] LSP logging initiated\n', os.date(log_date_format)))
return true
end
@@ -83,24 +85,36 @@ do
-- ```
--
-- This way you can avoid string allocations if the log level isn't high enough.
- if level ~= "OFF" then
+ if level ~= 'OFF' then
log[level:lower()] = function(...)
- local argc = select("#", ...)
- if levelnr < current_log_level then return false end
- if argc == 0 then return true end
- if not open_logfile() then return false end
- local info = debug.getinfo(2, "Sl")
- local header = string.format("[%s][%s] ...%s:%s", level, os.date(log_date_format), string.sub(info.short_src, #info.short_src - 15), info.currentline)
+ local argc = select('#', ...)
+ if levelnr < current_log_level then
+ return false
+ end
+ if argc == 0 then
+ return true
+ end
+ if not open_logfile() then
+ return false
+ end
+ local info = debug.getinfo(2, 'Sl')
+ local header = string.format(
+ '[%s][%s] ...%s:%s',
+ level,
+ os.date(log_date_format),
+ string.sub(info.short_src, #info.short_src - 15),
+ info.currentline
+ )
local parts = { header }
for i = 1, argc do
local arg = select(i, ...)
if arg == nil then
- table.insert(parts, "nil")
+ table.insert(parts, 'nil')
else
table.insert(parts, format_func(arg))
end
end
- logfile:write(table.concat(parts, '\t'), "\n")
+ logfile:write(table.concat(parts, '\t'), '\n')
logfile:flush()
end
end
@@ -115,10 +129,10 @@ vim.tbl_add_reverse_lookup(log.levels)
---@param level (string or number) One of `vim.lsp.log.levels`
function log.set_level(level)
if type(level) == 'string' then
- current_log_level = assert(log.levels[level:upper()], string.format("Invalid log level: %q", level))
+ current_log_level = assert(log.levels[level:upper()], string.format('Invalid log level: %q', level))
else
- assert(type(level) == 'number', "level must be a number or string")
- assert(log.levels[level], string.format("Invalid log level: %d", level))
+ assert(type(level) == 'number', 'level must be a number or string')
+ assert(log.levels[level], string.format('Invalid log level: %d', level))
current_log_level = level
end
end
@@ -132,7 +146,7 @@ end
--- Sets formatting function used to format logs
---@param handle function function to apply to logging arguments, pass vim.inspect for multi-line formatting
function log.set_format_func(handle)
- assert(handle == vim.inspect or type(handle) == 'function', "handle must be a function")
+ assert(handle == vim.inspect or type(handle) == 'function', 'handle must be a function')
format_func = handle
end
diff --git a/runtime/lua/vim/lsp/protocol.lua b/runtime/lua/vim/lsp/protocol.lua
index 8f50863360..6ecf7891c7 100644
--- a/runtime/lua/vim/lsp/protocol.lua
+++ b/runtime/lua/vim/lsp/protocol.lua
@@ -23,150 +23,150 @@ end
local constants = {
DiagnosticSeverity = {
-- Reports an error.
- Error = 1;
+ Error = 1,
-- Reports a warning.
- Warning = 2;
+ Warning = 2,
-- Reports an information.
- Information = 3;
+ Information = 3,
-- Reports a hint.
- Hint = 4;
- };
+ Hint = 4,
+ },
DiagnosticTag = {
-- Unused or unnecessary code
- Unnecessary = 1;
+ Unnecessary = 1,
-- Deprecated or obsolete code
- Deprecated = 2;
- };
+ Deprecated = 2,
+ },
MessageType = {
-- An error message.
- Error = 1;
+ Error = 1,
-- A warning message.
- Warning = 2;
+ Warning = 2,
-- An information message.
- Info = 3;
+ Info = 3,
-- A log message.
- Log = 4;
- };
+ Log = 4,
+ },
-- The file event type.
FileChangeType = {
-- The file got created.
- Created = 1;
+ Created = 1,
-- The file got changed.
- Changed = 2;
+ Changed = 2,
-- The file got deleted.
- Deleted = 3;
- };
+ Deleted = 3,
+ },
-- The kind of a completion entry.
CompletionItemKind = {
- Text = 1;
- Method = 2;
- Function = 3;
- Constructor = 4;
- Field = 5;
- Variable = 6;
- Class = 7;
- Interface = 8;
- Module = 9;
- Property = 10;
- Unit = 11;
- Value = 12;
- Enum = 13;
- Keyword = 14;
- Snippet = 15;
- Color = 16;
- File = 17;
- Reference = 18;
- Folder = 19;
- EnumMember = 20;
- Constant = 21;
- Struct = 22;
- Event = 23;
- Operator = 24;
- TypeParameter = 25;
- };
+ Text = 1,
+ Method = 2,
+ Function = 3,
+ Constructor = 4,
+ Field = 5,
+ Variable = 6,
+ Class = 7,
+ Interface = 8,
+ Module = 9,
+ Property = 10,
+ Unit = 11,
+ Value = 12,
+ Enum = 13,
+ Keyword = 14,
+ Snippet = 15,
+ Color = 16,
+ File = 17,
+ Reference = 18,
+ Folder = 19,
+ EnumMember = 20,
+ Constant = 21,
+ Struct = 22,
+ Event = 23,
+ Operator = 24,
+ TypeParameter = 25,
+ },
-- How a completion was triggered
CompletionTriggerKind = {
-- Completion was triggered by typing an identifier (24x7 code
-- complete), manual invocation (e.g Ctrl+Space) or via API.
- Invoked = 1;
+ Invoked = 1,
-- Completion was triggered by a trigger character specified by
-- the `triggerCharacters` properties of the `CompletionRegistrationOptions`.
- TriggerCharacter = 2;
+ TriggerCharacter = 2,
-- Completion was re-triggered as the current completion list is incomplete.
- TriggerForIncompleteCompletions = 3;
- };
+ TriggerForIncompleteCompletions = 3,
+ },
-- A document highlight kind.
DocumentHighlightKind = {
-- A textual occurrence.
- Text = 1;
+ Text = 1,
-- Read-access of a symbol, like reading a variable.
- Read = 2;
+ Read = 2,
-- Write-access of a symbol, like writing to a variable.
- Write = 3;
- };
+ Write = 3,
+ },
-- A symbol kind.
SymbolKind = {
- File = 1;
- Module = 2;
- Namespace = 3;
- Package = 4;
- Class = 5;
- Method = 6;
- Property = 7;
- Field = 8;
- Constructor = 9;
- Enum = 10;
- Interface = 11;
- Function = 12;
- Variable = 13;
- Constant = 14;
- String = 15;
- Number = 16;
- Boolean = 17;
- Array = 18;
- Object = 19;
- Key = 20;
- Null = 21;
- EnumMember = 22;
- Struct = 23;
- Event = 24;
- Operator = 25;
- TypeParameter = 26;
- };
+ File = 1,
+ Module = 2,
+ Namespace = 3,
+ Package = 4,
+ Class = 5,
+ Method = 6,
+ Property = 7,
+ Field = 8,
+ Constructor = 9,
+ Enum = 10,
+ Interface = 11,
+ Function = 12,
+ Variable = 13,
+ Constant = 14,
+ String = 15,
+ Number = 16,
+ Boolean = 17,
+ Array = 18,
+ Object = 19,
+ Key = 20,
+ Null = 21,
+ EnumMember = 22,
+ Struct = 23,
+ Event = 24,
+ Operator = 25,
+ TypeParameter = 26,
+ },
-- Represents reasons why a text document is saved.
TextDocumentSaveReason = {
-- Manually triggered, e.g. by the user pressing save, by starting debugging,
-- or by an API call.
- Manual = 1;
+ Manual = 1,
-- Automatic after a delay.
- AfterDelay = 2;
+ AfterDelay = 2,
-- When the editor lost focus.
- FocusOut = 3;
- };
+ FocusOut = 3,
+ },
ErrorCodes = {
-- Defined by JSON RPC
- ParseError = -32700;
- InvalidRequest = -32600;
- MethodNotFound = -32601;
- InvalidParams = -32602;
- InternalError = -32603;
- serverErrorStart = -32099;
- serverErrorEnd = -32000;
- ServerNotInitialized = -32002;
- UnknownErrorCode = -32001;
+ ParseError = -32700,
+ InvalidRequest = -32600,
+ MethodNotFound = -32601,
+ InvalidParams = -32602,
+ InternalError = -32603,
+ serverErrorStart = -32099,
+ serverErrorEnd = -32000,
+ ServerNotInitialized = -32002,
+ UnknownErrorCode = -32001,
-- Defined by the protocol.
- RequestCancelled = -32800;
- ContentModified = -32801;
- };
+ RequestCancelled = -32800,
+ ContentModified = -32801,
+ },
-- Describes the content type that a client supports in various
-- result literals like `Hover`, `ParameterInfo` or `CompletionItem`.
@@ -175,88 +175,88 @@ local constants = {
-- are reserved for internal usage.
MarkupKind = {
-- Plain text is supported as a content format
- PlainText = 'plaintext';
+ PlainText = 'plaintext',
-- Markdown is supported as a content format
- Markdown = 'markdown';
- };
+ Markdown = 'markdown',
+ },
ResourceOperationKind = {
-- Supports creating new files and folders.
- Create = 'create';
+ Create = 'create',
-- Supports renaming existing files and folders.
- Rename = 'rename';
+ Rename = 'rename',
-- Supports deleting existing files and folders.
- Delete = 'delete';
- };
+ Delete = 'delete',
+ },
FailureHandlingKind = {
-- Applying the workspace change is simply aborted if one of the changes provided
-- fails. All operations executed before the failing operation stay executed.
- Abort = 'abort';
+ Abort = 'abort',
-- All operations are executed transactionally. That means they either all
-- succeed or no changes at all are applied to the workspace.
- Transactional = 'transactional';
+ Transactional = 'transactional',
-- If the workspace edit contains only textual file changes they are executed transactionally.
-- If resource changes (create, rename or delete file) are part of the change the failure
-- handling strategy is abort.
- TextOnlyTransactional = 'textOnlyTransactional';
+ TextOnlyTransactional = 'textOnlyTransactional',
-- The client tries to undo the operations already executed. But there is no
-- guarantee that this succeeds.
- Undo = 'undo';
- };
+ Undo = 'undo',
+ },
-- Known error codes for an `InitializeError`;
InitializeError = {
-- If the protocol version provided by the client can't be handled by the server.
-- @deprecated This initialize error got replaced by client capabilities. There is
-- no version handshake in version 3.0x
- unknownProtocolVersion = 1;
- };
+ unknownProtocolVersion = 1,
+ },
-- Defines how the host (editor) should sync document changes to the language server.
TextDocumentSyncKind = {
-- Documents should not be synced at all.
- None = 0;
+ None = 0,
-- Documents are synced by always sending the full content
-- of the document.
- Full = 1;
+ Full = 1,
-- Documents are synced by sending the full content on open.
-- After that only incremental updates to the document are
-- send.
- Incremental = 2;
- };
+ Incremental = 2,
+ },
WatchKind = {
-- Interested in create events.
- Create = 1;
+ Create = 1,
-- Interested in change events
- Change = 2;
+ Change = 2,
-- Interested in delete events
- Delete = 4;
- };
+ Delete = 4,
+ },
-- Defines whether the insert text in a completion item should be interpreted as
-- plain text or a snippet.
InsertTextFormat = {
-- The primary text to be inserted is treated as a plain string.
- PlainText = 1;
+ PlainText = 1,
-- The primary text to be inserted is treated as a snippet.
--
-- A snippet can define tab stops and placeholders with `$1`, `$2`
-- and `${3:foo};`. `$0` defines the final tab stop, it defaults to
-- the end of the snippet. Placeholders with equal identifiers are linked,
-- that is typing in one will update others too.
- Snippet = 2;
- };
+ Snippet = 2,
+ },
-- A set of predefined code action kinds
CodeActionKind = {
-- Empty kind.
- Empty = '';
+ Empty = '',
-- Base kind for quickfix actions
- QuickFix = 'quickfix';
+ QuickFix = 'quickfix',
-- Base kind for refactoring actions
- Refactor = 'refactor';
+ Refactor = 'refactor',
-- Base kind for refactoring extraction actions
--
-- Example extract actions:
@@ -266,7 +266,7 @@ local constants = {
-- - Extract variable
-- - Extract interface from class
-- - ...
- RefactorExtract = 'refactor.extract';
+ RefactorExtract = 'refactor.extract',
-- Base kind for refactoring inline actions
--
-- Example inline actions:
@@ -275,7 +275,7 @@ local constants = {
-- - Inline variable
-- - Inline constant
-- - ...
- RefactorInline = 'refactor.inline';
+ RefactorInline = 'refactor.inline',
-- Base kind for refactoring rewrite actions
--
-- Example rewrite actions:
@@ -286,14 +286,14 @@ local constants = {
-- - Make method static
-- - Move method to base class
-- - ...
- RefactorRewrite = 'refactor.rewrite';
+ RefactorRewrite = 'refactor.rewrite',
-- Base kind for source actions
--
-- Source code actions apply to the entire file.
- Source = 'source';
+ Source = 'source',
-- Base kind for an organize imports source action
- SourceOrganizeImports = 'source.organizeImports';
- };
+ SourceOrganizeImports = 'source.organizeImports',
+ },
}
for k, v in pairs(constants) do
@@ -620,19 +620,19 @@ function protocol.make_client_capabilities()
return {
textDocument = {
synchronization = {
- dynamicRegistration = false;
+ dynamicRegistration = false,
-- TODO(ashkan) Send textDocument/willSave before saving (BufWritePre)
- willSave = false;
+ willSave = false,
-- TODO(ashkan) Implement textDocument/willSaveWaitUntil
- willSaveWaitUntil = false;
+ willSaveWaitUntil = false,
-- Send textDocument/didSave after saving (BufWritePost)
- didSave = true;
- };
+ didSave = true,
+ },
codeAction = {
- dynamicRegistration = false;
+ dynamicRegistration = false,
codeActionLiteralSupport = {
codeActionKind = {
@@ -640,138 +640,146 @@ function protocol.make_client_capabilities()
local res = vim.tbl_values(protocol.CodeActionKind)
table.sort(res)
return res
- end)();
- };
- };
- isPreferredSupport = true;
- dataSupport = true;
+ end)(),
+ },
+ },
+ isPreferredSupport = true,
+ dataSupport = true,
resolveSupport = {
- properties = { 'edit', }
- };
- };
+ properties = { 'edit' },
+ },
+ },
completion = {
- dynamicRegistration = false;
+ dynamicRegistration = false,
completionItem = {
-- Until we can actually expand snippet, move cursor and allow for true snippet experience,
-- this should be disabled out of the box.
-- However, users can turn this back on if they have a snippet plugin.
- snippetSupport = false;
+ snippetSupport = false,
- commitCharactersSupport = false;
- preselectSupport = false;
- deprecatedSupport = false;
- documentationFormat = { protocol.MarkupKind.Markdown; protocol.MarkupKind.PlainText };
- };
+ commitCharactersSupport = false,
+ preselectSupport = false,
+ deprecatedSupport = false,
+ documentationFormat = { protocol.MarkupKind.Markdown, protocol.MarkupKind.PlainText },
+ },
completionItemKind = {
valueSet = (function()
local res = {}
for k in ipairs(protocol.CompletionItemKind) do
- if type(k) == 'number' then table.insert(res, k) end
+ if type(k) == 'number' then
+ table.insert(res, k)
+ end
end
return res
- end)();
- };
+ end)(),
+ },
-- TODO(tjdevries): Implement this
- contextSupport = false;
- };
+ contextSupport = false,
+ },
declaration = {
- linkSupport = true;
- };
+ linkSupport = true,
+ },
definition = {
- linkSupport = true;
- };
+ linkSupport = true,
+ },
implementation = {
- linkSupport = true;
- };
+ linkSupport = true,
+ },
typeDefinition = {
- linkSupport = true;
- };
+ linkSupport = true,
+ },
hover = {
- dynamicRegistration = false;
- contentFormat = { protocol.MarkupKind.Markdown; protocol.MarkupKind.PlainText };
- };
+ dynamicRegistration = false,
+ contentFormat = { protocol.MarkupKind.Markdown, protocol.MarkupKind.PlainText },
+ },
signatureHelp = {
- dynamicRegistration = false;
+ dynamicRegistration = false,
signatureInformation = {
- activeParameterSupport = true;
- documentationFormat = { protocol.MarkupKind.Markdown; protocol.MarkupKind.PlainText };
+ activeParameterSupport = true,
+ documentationFormat = { protocol.MarkupKind.Markdown, protocol.MarkupKind.PlainText },
parameterInformation = {
- labelOffsetSupport = true;
- };
- };
- };
+ labelOffsetSupport = true,
+ },
+ },
+ },
references = {
- dynamicRegistration = false;
- };
+ dynamicRegistration = false,
+ },
documentHighlight = {
- dynamicRegistration = false
- };
+ dynamicRegistration = false,
+ },
documentSymbol = {
- dynamicRegistration = false;
+ dynamicRegistration = false,
symbolKind = {
valueSet = (function()
local res = {}
for k in ipairs(protocol.SymbolKind) do
- if type(k) == 'number' then table.insert(res, k) end
+ if type(k) == 'number' then
+ table.insert(res, k)
+ end
end
return res
- end)();
- };
- hierarchicalDocumentSymbolSupport = true;
- };
+ end)(),
+ },
+ hierarchicalDocumentSymbolSupport = true,
+ },
rename = {
- dynamicRegistration = false;
- prepareSupport = true;
- };
+ dynamicRegistration = false,
+ prepareSupport = true,
+ },
publishDiagnostics = {
- relatedInformation = true;
+ relatedInformation = true,
tagSupport = {
valueSet = (function()
local res = {}
for k in ipairs(protocol.DiagnosticTag) do
- if type(k) == 'number' then table.insert(res, k) end
+ if type(k) == 'number' then
+ table.insert(res, k)
+ end
end
return res
- end)();
- };
- };
- };
+ end)(),
+ },
+ },
+ },
workspace = {
symbol = {
- dynamicRegistration = false;
+ dynamicRegistration = false,
symbolKind = {
valueSet = (function()
local res = {}
for k in ipairs(protocol.SymbolKind) do
- if type(k) == 'number' then table.insert(res, k) end
+ if type(k) == 'number' then
+ table.insert(res, k)
+ end
end
return res
- end)();
- };
- hierarchicalWorkspaceSymbolSupport = true;
- };
- workspaceFolders = true;
- applyEdit = true;
+ end)(),
+ },
+ hierarchicalWorkspaceSymbolSupport = true,
+ },
+ workspaceFolders = true,
+ applyEdit = true,
workspaceEdit = {
- resourceOperations = {'rename', 'create', 'delete',},
- };
- };
+ resourceOperations = { 'rename', 'create', 'delete' },
+ },
+ },
callHierarchy = {
- dynamicRegistration = false;
- };
- experimental = nil;
+ dynamicRegistration = false,
+ },
+ experimental = nil,
window = {
- workDoneProgress = true;
+ workDoneProgress = true,
showMessage = {
messageActionItem = {
- additionalPropertiesSupport = false;
- };
- };
+ additionalPropertiesSupport = false,
+ },
+ },
showDocument = {
- support = false;
- };
- };
+ support = false,
+ },
+ },
}
end
@@ -791,12 +799,12 @@ function protocol.resolve_capabilities(server_capabilities)
willSaveWaitUntil = false,
save = {
includeText = false,
- }
+ },
}
elseif type(textDocumentSync) == 'number' then
-- Backwards compatibility
if not TextDocumentSyncKind[textDocumentSync] then
- return nil, "Invalid server TextDocumentSyncKind for textDocumentSync"
+ return nil, 'Invalid server TextDocumentSyncKind for textDocumentSync'
end
server_capabilities.textDocumentSync = {
openClose = true,
@@ -805,10 +813,10 @@ function protocol.resolve_capabilities(server_capabilities)
willSaveWaitUntil = false,
save = {
includeText = false,
- }
+ },
}
elseif type(textDocumentSync) ~= 'table' then
- return nil, string.format("Invalid type for textDocumentSync: %q", type(textDocumentSync))
+ return nil, string.format('Invalid type for textDocumentSync: %q', type(textDocumentSync))
end
return server_capabilities
end
@@ -827,39 +835,41 @@ function protocol._resolve_capabilities_compat(server_capabilities)
if textDocumentSync == nil then
-- Defaults if omitted.
text_document_sync_properties = {
- text_document_open_close = false;
- text_document_did_change = TextDocumentSyncKind.None;
--- text_document_did_change = false;
- text_document_will_save = false;
- text_document_will_save_wait_until = false;
- text_document_save = false;
- text_document_save_include_text = false;
+ text_document_open_close = false,
+ text_document_did_change = TextDocumentSyncKind.None,
+ -- text_document_did_change = false;
+ text_document_will_save = false,
+ text_document_will_save_wait_until = false,
+ text_document_save = false,
+ text_document_save_include_text = false,
}
elseif type(textDocumentSync) == 'number' then
-- Backwards compatibility
if not TextDocumentSyncKind[textDocumentSync] then
- return nil, "Invalid server TextDocumentSyncKind for textDocumentSync"
+ return nil, 'Invalid server TextDocumentSyncKind for textDocumentSync'
end
text_document_sync_properties = {
- text_document_open_close = true;
- text_document_did_change = textDocumentSync;
- text_document_will_save = false;
- text_document_will_save_wait_until = false;
- text_document_save = true;
- text_document_save_include_text = false;
+ text_document_open_close = true,
+ text_document_did_change = textDocumentSync,
+ text_document_will_save = false,
+ text_document_will_save_wait_until = false,
+ text_document_save = true,
+ text_document_save_include_text = false,
}
elseif type(textDocumentSync) == 'table' then
text_document_sync_properties = {
- text_document_open_close = if_nil(textDocumentSync.openClose, false);
- text_document_did_change = if_nil(textDocumentSync.change, TextDocumentSyncKind.None);
- text_document_will_save = if_nil(textDocumentSync.willSave, false);
- text_document_will_save_wait_until = if_nil(textDocumentSync.willSaveWaitUntil, false);
- text_document_save = if_nil(textDocumentSync.save, false);
- text_document_save_include_text = if_nil(type(textDocumentSync.save) == 'table'
- and textDocumentSync.save.includeText, false);
+ text_document_open_close = if_nil(textDocumentSync.openClose, false),
+ text_document_did_change = if_nil(textDocumentSync.change, TextDocumentSyncKind.None),
+ text_document_will_save = if_nil(textDocumentSync.willSave, false),
+ text_document_will_save_wait_until = if_nil(textDocumentSync.willSaveWaitUntil, false),
+ text_document_save = if_nil(textDocumentSync.save, false),
+ text_document_save_include_text = if_nil(
+ type(textDocumentSync.save) == 'table' and textDocumentSync.save.includeText,
+ false
+ ),
}
else
- return nil, string.format("Invalid type for textDocumentSync: %q", type(textDocumentSync))
+ return nil, string.format('Invalid type for textDocumentSync: %q', type(textDocumentSync))
end
end
general_properties.completion = server_capabilities.completionProvider ~= nil
@@ -889,16 +899,18 @@ function protocol._resolve_capabilities_compat(server_capabilities)
general_properties.code_lens = true
general_properties.code_lens_resolve = server_capabilities.codeLensProvider.resolveProvider or false
else
- error("The server sent invalid codeLensProvider")
+ error('The server sent invalid codeLensProvider')
end
if server_capabilities.codeActionProvider == nil then
general_properties.code_action = false
- elseif type(server_capabilities.codeActionProvider) == 'boolean'
- or type(server_capabilities.codeActionProvider) == 'table' then
+ elseif
+ type(server_capabilities.codeActionProvider) == 'boolean'
+ or type(server_capabilities.codeActionProvider) == 'table'
+ then
general_properties.code_action = server_capabilities.codeActionProvider
else
- error("The server sent invalid codeActionProvider")
+ error('The server sent invalid codeActionProvider')
end
if server_capabilities.declarationProvider == nil then
@@ -908,7 +920,7 @@ function protocol._resolve_capabilities_compat(server_capabilities)
elseif type(server_capabilities.declarationProvider) == 'table' then
general_properties.declaration = server_capabilities.declarationProvider
else
- error("The server sent invalid declarationProvider")
+ error('The server sent invalid declarationProvider')
end
if server_capabilities.typeDefinitionProvider == nil then
@@ -918,7 +930,7 @@ function protocol._resolve_capabilities_compat(server_capabilities)
elseif type(server_capabilities.typeDefinitionProvider) == 'table' then
general_properties.type_definition = server_capabilities.typeDefinitionProvider
else
- error("The server sent invalid typeDefinitionProvider")
+ error('The server sent invalid typeDefinitionProvider')
end
if server_capabilities.implementationProvider == nil then
@@ -928,7 +940,7 @@ function protocol._resolve_capabilities_compat(server_capabilities)
elseif type(server_capabilities.implementationProvider) == 'table' then
general_properties.implementation = server_capabilities.implementationProvider
else
- error("The server sent invalid implementationProvider")
+ error('The server sent invalid implementationProvider')
end
local workspace = server_capabilities.workspace
@@ -936,45 +948,45 @@ function protocol._resolve_capabilities_compat(server_capabilities)
if workspace == nil or workspace.workspaceFolders == nil then
-- Defaults if omitted.
workspace_properties = {
- workspace_folder_properties = {
- supported = false;
- changeNotifications=false;
- }
+ workspace_folder_properties = {
+ supported = false,
+ changeNotifications = false,
+ },
}
elseif type(workspace.workspaceFolders) == 'table' then
workspace_properties = {
workspace_folder_properties = {
- supported = if_nil(workspace.workspaceFolders.supported, false);
- changeNotifications = if_nil(workspace.workspaceFolders.changeNotifications, false);
-
- }
+ supported = if_nil(workspace.workspaceFolders.supported, false),
+ changeNotifications = if_nil(workspace.workspaceFolders.changeNotifications, false),
+ },
}
else
- error("The server sent invalid workspace")
+ error('The server sent invalid workspace')
end
local signature_help_properties
if server_capabilities.signatureHelpProvider == nil then
signature_help_properties = {
- signature_help = false;
- signature_help_trigger_characters = {};
+ signature_help = false,
+ signature_help_trigger_characters = {},
}
elseif type(server_capabilities.signatureHelpProvider) == 'table' then
signature_help_properties = {
- signature_help = true;
+ signature_help = true,
-- The characters that trigger signature help automatically.
- signature_help_trigger_characters = server_capabilities.signatureHelpProvider.triggerCharacters or {};
+ signature_help_trigger_characters = server_capabilities.signatureHelpProvider.triggerCharacters or {},
}
else
- error("The server sent invalid signatureHelpProvider")
+ error('The server sent invalid signatureHelpProvider')
end
- local capabilities = vim.tbl_extend("error"
- , text_document_sync_properties
- , signature_help_properties
- , workspace_properties
- , general_properties
- )
+ local capabilities = vim.tbl_extend(
+ 'error',
+ text_document_sync_properties,
+ signature_help_properties,
+ workspace_properties,
+ general_properties
+ )
return capabilities
end
diff --git a/runtime/lua/vim/lsp/rpc.lua b/runtime/lua/vim/lsp/rpc.lua
index be2cc58f07..2dcafc92bc 100644
--- a/runtime/lua/vim/lsp/rpc.lua
+++ b/runtime/lua/vim/lsp/rpc.lua
@@ -32,9 +32,9 @@ local function env_merge(env)
-- Merge.
env = vim.tbl_extend('force', vim.fn.environ(), env)
local final_env = {}
- for k,v in pairs(env) do
+ for k, v in pairs(env) do
assert(type(k) == 'string', 'env must be a dict')
- table.insert(final_env, k..'='..tostring(v))
+ table.insert(final_env, k .. '=' .. tostring(v))
end
return final_env
end
@@ -45,10 +45,12 @@ end
---@param encoded_message (string)
---@returns (table) table containing encoded message and `Content-Length` attribute
local function format_message_with_content_length(encoded_message)
- return table.concat {
- 'Content-Length: '; tostring(#encoded_message); '\r\n\r\n';
- encoded_message;
- }
+ return table.concat({
+ 'Content-Length: ',
+ tostring(#encoded_message),
+ '\r\n\r\n',
+ encoded_message,
+ })
end
---@private
@@ -65,23 +67,25 @@ local function parse_headers(header)
if line == '' then
break
end
- local key, value = line:match("^%s*(%S+)%s*:%s*(.+)%s*$")
+ local key, value = line:match('^%s*(%S+)%s*:%s*(.+)%s*$')
if key then
key = key:lower():gsub('%-', '_')
headers[key] = value
else
- local _ = log.error() and log.error("invalid header line %q", line)
- error(string.format("invalid header line %q", line))
+ local _ = log.error() and log.error('invalid header line %q', line)
+ error(string.format('invalid header line %q', line))
end
end
headers.content_length = tonumber(headers.content_length)
- or error(string.format("Content-Length not found in headers. %q", header))
+ or error(string.format('Content-Length not found in headers. %q', header))
return headers
end
-- This is the start of any possible header patterns. The gsub converts it to a
-- case insensitive pattern.
-local header_start_pattern = ("content"):gsub("%w", function(c) return "["..c..c:upper().."]" end)
+local header_start_pattern = ('content'):gsub('%w', function(c)
+ return '[' .. c .. c:upper() .. ']'
+end)
---@private
--- The actual workhorse.
@@ -100,17 +104,16 @@ local function request_parser_loop()
-- be searching for.
-- TODO(ashkan) I'd like to remove this, but it seems permanent :(
local buffer_start = buffer:find(header_start_pattern)
- local headers = parse_headers(buffer:sub(buffer_start, start-1))
+ local headers = parse_headers(buffer:sub(buffer_start, start - 1))
local content_length = headers.content_length
-- Use table instead of just string to buffer the message. It prevents
-- a ton of strings allocating.
-- ref. http://www.lua.org/pil/11.6.html
- local body_chunks = {buffer:sub(finish+1)}
+ local body_chunks = { buffer:sub(finish + 1) }
local body_length = #body_chunks[1]
-- Keep waiting for data until we have enough.
while body_length < content_length do
- local chunk = coroutine.yield()
- or error("Expected more data for the body. The server may have died.") -- TODO hmm.
+ local chunk = coroutine.yield() or error('Expected more data for the body. The server may have died.') -- TODO hmm.
table.insert(body_chunks, chunk)
body_length = body_length + #chunk
end
@@ -123,25 +126,24 @@ local function request_parser_loop()
end
local body = table.concat(body_chunks)
-- Yield our data.
- buffer = rest..(coroutine.yield(headers, body)
- or error("Expected more data for the body. The server may have died.")) -- TODO hmm.
+ buffer = rest
+ .. (coroutine.yield(headers, body) or error('Expected more data for the body. The server may have died.')) -- TODO hmm.
else
-- Get more data since we don't have enough.
- buffer = buffer..(coroutine.yield()
- or error("Expected more data for the header. The server may have died.")) -- TODO hmm.
+ buffer = buffer .. (coroutine.yield() or error('Expected more data for the header. The server may have died.')) -- TODO hmm.
end
end
end
--- Mapping of error codes used by the client
local client_errors = {
- INVALID_SERVER_MESSAGE = 1;
- INVALID_SERVER_JSON = 2;
- NO_RESULT_CALLBACK_FOUND = 3;
- READ_ERROR = 4;
- NOTIFICATION_HANDLER_ERROR = 5;
- SERVER_REQUEST_HANDLER_ERROR = 6;
- SERVER_RESULT_CALLBACK_ERROR = 7;
+ INVALID_SERVER_MESSAGE = 1,
+ INVALID_SERVER_JSON = 2,
+ NO_RESULT_CALLBACK_FOUND = 3,
+ READ_ERROR = 4,
+ NOTIFICATION_HANDLER_ERROR = 5,
+ SERVER_REQUEST_HANDLER_ERROR = 6,
+ SERVER_RESULT_CALLBACK_ERROR = 7,
}
client_errors = vim.tbl_add_reverse_lookup(client_errors)
@@ -151,26 +153,26 @@ client_errors = vim.tbl_add_reverse_lookup(client_errors)
---@param err (table) The error object
---@returns (string) The formatted error message
local function format_rpc_error(err)
- validate {
- err = { err, 't' };
- }
+ validate({
+ err = { err, 't' },
+ })
-- There is ErrorCodes in the LSP specification,
-- but in ResponseError.code it is not used and the actual type is number.
local code
if protocol.ErrorCodes[err.code] then
- code = string.format("code_name = %s,", protocol.ErrorCodes[err.code])
+ code = string.format('code_name = %s,', protocol.ErrorCodes[err.code])
else
- code = string.format("code_name = unknown, code = %s,", err.code)
+ code = string.format('code_name = unknown, code = %s,', err.code)
end
- local message_parts = {"RPC[Error]", code}
+ local message_parts = { 'RPC[Error]', code }
if err.message then
- table.insert(message_parts, "message =")
- table.insert(message_parts, string.format("%q", err.message))
+ table.insert(message_parts, 'message =')
+ table.insert(message_parts, string.format('%q', err.message))
end
if err.data then
- table.insert(message_parts, "data =")
+ table.insert(message_parts, 'data =')
table.insert(message_parts, vim.inspect(err.data))
end
return table.concat(message_parts, ' ')
@@ -185,11 +187,11 @@ local function rpc_response_error(code, message, data)
-- TODO should this error or just pick a sane error (like InternalError)?
local code_name = assert(protocol.ErrorCodes[code], 'Invalid RPC error code')
return setmetatable({
- code = code;
- message = message or code_name;
- data = data;
+ code = code,
+ message = message or code_name,
+ data = data,
}, {
- __tostring = format_rpc_error;
+ __tostring = format_rpc_error,
})
end
@@ -220,7 +222,7 @@ end
---@param signal (number): Number describing the signal used to terminate (if
---any)
function default_dispatchers.on_exit(code, signal)
- local _ = log.info() and log.info("client_exit", { code = code, signal = signal })
+ local _ = log.info() and log.info('client_exit', { code = code, signal = signal })
end
---@private
--- Default dispatcher for client errors.
@@ -258,15 +260,15 @@ end
--- - {handle} A handle for low-level interaction with the LSP server process
--- |vim.loop|.
local function start(cmd, cmd_args, dispatchers, extra_spawn_params)
- local _ = log.info() and log.info("Starting RPC client", {cmd = cmd, args = cmd_args, extra = extra_spawn_params})
- validate {
- cmd = { cmd, 's' };
- cmd_args = { cmd_args, 't' };
- dispatchers = { dispatchers, 't', true };
- }
+ local _ = log.info() and log.info('Starting RPC client', { cmd = cmd, args = cmd_args, extra = extra_spawn_params })
+ validate({
+ cmd = { cmd, 's' },
+ cmd_args = { cmd_args, 't' },
+ dispatchers = { dispatchers, 't', true },
+ })
if extra_spawn_params and extra_spawn_params.cwd then
- assert(is_dir(extra_spawn_params.cwd), "cwd must be a directory")
+ assert(is_dir(extra_spawn_params.cwd), 'cwd must be a directory')
end
if dispatchers then
local user_dispatchers = dispatchers
@@ -275,11 +277,11 @@ local function start(cmd, cmd_args, dispatchers, extra_spawn_params)
local user_dispatcher = user_dispatchers[dispatch_name]
if user_dispatcher then
if type(user_dispatcher) ~= 'function' then
- error(string.format("dispatcher.%s must be a function", dispatch_name))
+ error(string.format('dispatcher.%s must be a function', dispatch_name))
end
-- server_request is wrapped elsewhere.
- if not (dispatch_name == 'server_request'
- or dispatch_name == 'on_exit') -- TODO this blocks the loop exiting for some reason.
+ if
+ not (dispatch_name == 'server_request' or dispatch_name == 'on_exit') -- TODO this blocks the loop exiting for some reason.
then
user_dispatcher = schedule_wrap(user_dispatcher)
end
@@ -317,9 +319,9 @@ local function start(cmd, cmd_args, dispatchers, extra_spawn_params)
dispatchers.on_exit(code, signal)
end
local spawn_params = {
- args = cmd_args;
- stdio = {stdin, stdout, stderr};
- detached = true;
+ args = cmd_args,
+ stdio = { stdin, stdout, stderr },
+ detached = true,
}
if extra_spawn_params then
spawn_params.cwd = extra_spawn_params.cwd
@@ -330,11 +332,11 @@ local function start(cmd, cmd_args, dispatchers, extra_spawn_params)
end
handle, pid = uv.spawn(cmd, spawn_params, onexit)
if handle == nil then
- local msg = string.format("Spawning language server with cmd: `%s` failed", cmd)
- if string.match(pid, "ENOENT") then
- msg = msg .. ". The language server is either not installed, missing from PATH, or not executable."
+ local msg = string.format('Spawning language server with cmd: `%s` failed', cmd)
+ if string.match(pid, 'ENOENT') then
+ msg = msg .. '. The language server is either not installed, missing from PATH, or not executable.'
else
- msg = msg .. string.format(" with error message: %s", pid)
+ msg = msg .. string.format(' with error message: %s', pid)
end
vim.notify(msg, vim.log.levels.WARN)
return
@@ -348,8 +350,10 @@ local function start(cmd, cmd_args, dispatchers, extra_spawn_params)
---@param payload table
---@returns true if the payload could be scheduled, false if the main event-loop is in the process of closing.
local function encode_and_send(payload)
- local _ = log.debug() and log.debug("rpc.send", payload)
- if handle == nil or handle:is_closing() then return false end
+ local _ = log.debug() and log.debug('rpc.send', payload)
+ if handle == nil or handle:is_closing() then
+ return false
+ end
local encoded = vim.json.encode(payload)
stdin:write(format_message_with_content_length(encoded))
return true
@@ -363,22 +367,22 @@ local function start(cmd, cmd_args, dispatchers, extra_spawn_params)
---@param params (table): Parameters for the invoked LSP method
---@returns (bool) `true` if notification could be sent, `false` if not
local function notify(method, params)
- return encode_and_send {
- jsonrpc = "2.0";
- method = method;
- params = params;
- }
+ return encode_and_send({
+ jsonrpc = '2.0',
+ method = method,
+ params = params,
+ })
end
---@private
--- sends an error object to the remote LSP process.
local function send_response(request_id, err, result)
- return encode_and_send {
- id = request_id;
- jsonrpc = "2.0";
- error = err;
- result = result;
- }
+ return encode_and_send({
+ id = request_id,
+ jsonrpc = '2.0',
+ error = err,
+ result = result,
+ })
end
-- FIXME: DOC: Should be placed on the RPC client object returned by
@@ -392,18 +396,18 @@ local function start(cmd, cmd_args, dispatchers, extra_spawn_params)
---@param notify_reply_callback (function|nil) Callback to invoke as soon as a request is no longer pending
---@returns (bool, number) `(true, message_id)` if request could be sent, `false` if not
local function request(method, params, callback, notify_reply_callback)
- validate {
- callback = { callback, 'f' };
- notify_reply_callback = { notify_reply_callback, 'f', true };
- }
+ validate({
+ callback = { callback, 'f' },
+ notify_reply_callback = { notify_reply_callback, 'f', true },
+ })
message_index = message_index + 1
local message_id = message_index
- local result = encode_and_send {
- id = message_id;
- jsonrpc = "2.0";
- method = method;
- params = params;
- }
+ local result = encode_and_send({
+ id = message_id,
+ jsonrpc = '2.0',
+ method = method,
+ params = params,
+ })
if result then
if message_callbacks then
message_callbacks[message_id] = schedule_wrap(callback)
@@ -421,7 +425,7 @@ local function start(cmd, cmd_args, dispatchers, extra_spawn_params)
stderr:read_start(function(_err, chunk)
if chunk then
- local _ = log.error() and log.error("rpc", cmd, "stderr", chunk)
+ local _ = log.error() and log.error('rpc', cmd, 'stderr', chunk)
end
end)
@@ -455,7 +459,7 @@ local function start(cmd, cmd_args, dispatchers, extra_spawn_params)
on_error(client_errors.INVALID_SERVER_JSON, decoded)
return
end
- local _ = log.debug() and log.debug("rpc.receive", decoded)
+ local _ = log.debug() and log.debug('rpc.receive', decoded)
if type(decoded.method) == 'string' and decoded.id then
local err
@@ -463,17 +467,30 @@ local function start(cmd, cmd_args, dispatchers, extra_spawn_params)
-- we can still use the result.
schedule(function()
local status, result
- status, result, err = try_call(client_errors.SERVER_REQUEST_HANDLER_ERROR,
- dispatchers.server_request, decoded.method, decoded.params)
- local _ = log.debug() and log.debug("server_request: callback result", { status = status, result = result, err = err })
+ status, result, err = try_call(
+ client_errors.SERVER_REQUEST_HANDLER_ERROR,
+ dispatchers.server_request,
+ decoded.method,
+ decoded.params
+ )
+ local _ = log.debug()
+ and log.debug('server_request: callback result', { status = status, result = result, err = err })
if status then
if not (result or err) then
-- TODO this can be a problem if `null` is sent for result. needs vim.NIL
- error(string.format("method %q: either a result or an error must be sent to the server in response", decoded.method))
+ error(
+ string.format(
+ 'method %q: either a result or an error must be sent to the server in response',
+ decoded.method
+ )
+ )
end
if err then
- assert(type(err) == 'table', "err must be a table. Use rpc_response_error to help format errors.")
- local code_name = assert(protocol.ErrorCodes[err.code], "Errors must use protocol.ErrorCodes. Use rpc_response_error to help format errors.")
+ assert(type(err) == 'table', 'err must be a table. Use rpc_response_error to help format errors.')
+ local code_name = assert(
+ protocol.ErrorCodes[err.code],
+ 'Errors must use protocol.ErrorCodes. Use rpc_response_error to help format errors.'
+ )
err.message = err.message or code_name
end
else
@@ -483,18 +500,17 @@ local function start(cmd, cmd_args, dispatchers, extra_spawn_params)
end
send_response(decoded.id, err, result)
end)
- -- This works because we are expecting vim.NIL here
+ -- This works because we are expecting vim.NIL here
elseif decoded.id and (decoded.result ~= vim.NIL or decoded.error ~= vim.NIL) then
-
-- We sent a number, so we expect a number.
local result_id = tonumber(decoded.id)
-- Notify the user that a response was received for the request
local notify_reply_callback = notify_reply_callbacks and notify_reply_callbacks[result_id]
if notify_reply_callback then
- validate {
- notify_reply_callback = { notify_reply_callback, 'f' };
- }
+ validate({
+ notify_reply_callback = { notify_reply_callback, 'f' },
+ })
notify_reply_callback(result_id)
notify_reply_callbacks[result_id] = nil
end
@@ -503,7 +519,7 @@ local function start(cmd, cmd_args, dispatchers, extra_spawn_params)
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)
+ local _ = log.debug() and log.debug('Received cancellation ack', decoded)
mute_error = true
end
@@ -523,24 +539,22 @@ local function start(cmd, cmd_args, dispatchers, extra_spawn_params)
local callback = message_callbacks and message_callbacks[result_id]
if callback then
message_callbacks[result_id] = nil
- validate {
- callback = { callback, 'f' };
- }
+ validate({
+ callback = { callback, 'f' },
+ })
if decoded.error then
decoded.error = setmetatable(decoded.error, {
- __tostring = format_rpc_error;
+ __tostring = format_rpc_error,
})
end
- try_call(client_errors.SERVER_RESULT_CALLBACK_ERROR,
- callback, decoded.error, decoded.result)
+ try_call(client_errors.SERVER_RESULT_CALLBACK_ERROR, callback, decoded.error, decoded.result)
else
on_error(client_errors.NO_RESULT_CALLBACK_FOUND, decoded)
- local _ = log.error() and log.error("No callback found for server response id "..result_id)
+ local _ = log.error() and log.error('No callback found for server response id ' .. result_id)
end
elseif type(decoded.method) == 'string' then
-- Notification
- try_call(client_errors.NOTIFICATION_HANDLER_ERROR,
- dispatchers.notification, decoded.method, decoded.params)
+ try_call(client_errors.NOTIFICATION_HANDLER_ERROR, dispatchers.notification, decoded.method, decoded.params)
else
-- Invalid server message
on_error(client_errors.INVALID_SERVER_MESSAGE, decoded)
@@ -556,7 +570,9 @@ local function start(cmd, cmd_args, dispatchers, extra_spawn_params)
return
end
-- This should signal that we are done reading from the client.
- if not chunk then return end
+ if not chunk then
+ return
+ end
-- Flush anything in the parser by looping until we don't get a result
-- anymore.
while true do
@@ -574,17 +590,17 @@ local function start(cmd, cmd_args, dispatchers, extra_spawn_params)
end)
return {
- pid = pid;
- handle = handle;
- request = request;
- notify = notify
+ pid = pid,
+ handle = handle,
+ request = request,
+ notify = notify,
}
end
return {
- start = start;
- rpc_response_error = rpc_response_error;
- format_rpc_error = format_rpc_error;
- client_errors = client_errors;
+ start = start,
+ rpc_response_error = rpc_response_error,
+ format_rpc_error = format_rpc_error,
+ client_errors = client_errors,
}
-- vim:sw=2 ts=2 et
diff --git a/runtime/lua/vim/lsp/sync.lua b/runtime/lua/vim/lsp/sync.lua
index 9955fff3e2..73b4e0025a 100644
--- a/runtime/lua/vim/lsp/sync.lua
+++ b/runtime/lua/vim/lsp/sync.lua
@@ -79,7 +79,7 @@ local function compute_line_length(line, offset_encoding)
local length
local _
if offset_encoding == 'utf-16' then
- _, length = str_utfindex(line)
+ _, length = str_utfindex(line)
elseif offset_encoding == 'utf-32' then
length, _ = str_utfindex(line)
else
@@ -100,7 +100,7 @@ local function align_end_position(line, byte, offset_encoding)
-- If on the first byte, or an empty string: the trivial case
if byte == 1 or #line == 0 then
char = byte
- -- Called in the case of extending an empty line "" -> "a"
+ -- Called in the case of extending an empty line "" -> "a"
elseif byte == #line + 1 then
char = compute_line_length(line, offset_encoding) + 1
else
@@ -175,12 +175,12 @@ local function compute_start_range(prev_lines, curr_lines, firstline, lastline,
end
-- Convert byte to codepoint if applicable
- if start_byte_idx == 1 or (#prev_line == 0 and start_byte_idx == 1)then
+ if start_byte_idx == 1 or (#prev_line == 0 and start_byte_idx == 1) then
byte_idx = start_byte_idx
char_idx = 1
elseif start_byte_idx == #prev_line + 1 then
byte_idx = start_byte_idx
- char_idx = compute_line_length(prev_line, offset_encoding) + 1
+ char_idx = compute_line_length(prev_line, offset_encoding) + 1
else
byte_idx = start_byte_idx + str_utf_start(prev_line, start_byte_idx)
char_idx = byte_to_utf(prev_line, byte_idx, offset_encoding)
@@ -203,14 +203,30 @@ end
---@param new_lastline integer
---@param offset_encoding string
---@returns (int, int) end_line_idx and end_col_idx of range
-local function compute_end_range(prev_lines, curr_lines, start_range, firstline, lastline, new_lastline, offset_encoding)
+local function compute_end_range(
+ prev_lines,
+ curr_lines,
+ start_range,
+ firstline,
+ lastline,
+ new_lastline,
+ offset_encoding
+)
-- If firstline == new_lastline, the first change occurred on a line that was deleted.
-- In this case, the last_byte...
if firstline == new_lastline then
- return { line_idx = (lastline - new_lastline + firstline), byte_idx = 1, char_idx = 1 }, { line_idx = firstline, byte_idx = 1, char_idx = 1 }
+ return { line_idx = (lastline - new_lastline + firstline), byte_idx = 1, char_idx = 1 }, {
+ line_idx = firstline,
+ byte_idx = 1,
+ char_idx = 1,
+ }
end
if firstline == lastline then
- return { line_idx = firstline, byte_idx = 1, char_idx = 1 }, { line_idx = new_lastline - lastline + firstline, byte_idx = 1, char_idx = 1 }
+ return { line_idx = firstline, byte_idx = 1, char_idx = 1 }, {
+ line_idx = new_lastline - lastline + firstline,
+ byte_idx = 1,
+ char_idx = 1,
+ }
end
-- Compare on last line, at minimum will be the start range
local start_line_idx = start_range.line_idx
@@ -239,9 +255,7 @@ local function compute_end_range(prev_lines, curr_lines, start_range, firstline,
end
for idx = 0, max_length do
byte_offset = idx
- if
- str_byte(prev_line, prev_line_length - byte_offset) ~= str_byte(curr_line, curr_line_length - byte_offset)
- then
+ if str_byte(prev_line, prev_line_length - byte_offset) ~= str_byte(curr_line, curr_line_length - byte_offset) then
break
end
end
@@ -281,14 +295,13 @@ end
---@param end_range table new_end_range returned by last_difference
---@returns string text extracted from defined region
local function extract_text(lines, start_range, end_range, line_ending)
- if not lines[start_range.line_idx] then
- return ""
- end
+ if not lines[start_range.line_idx] then
+ return ''
+ end
-- Trivial case: start and end range are the same line, directly grab changed text
if start_range.line_idx == end_range.line_idx then
-- string.sub is inclusive, end_range is not
return string.sub(lines[start_range.line_idx], start_range.byte_idx, end_range.byte_idx - 1)
-
else
-- Handle deletion case
-- Collect the changed portion of the first changed line
@@ -303,7 +316,7 @@ local function extract_text(lines, start_range, end_range, line_ending)
-- Collect the changed portion of the last changed line.
table.insert(result, string.sub(lines[end_range.line_idx], 1, end_range.byte_idx - 1))
else
- table.insert(result, "")
+ table.insert(result, '')
end
-- Add line ending between all lines
diff --git a/runtime/lua/vim/lsp/util.lua b/runtime/lua/vim/lsp/util.lua
index bb87e8372b..0b0d48d15e 100644
--- a/runtime/lua/vim/lsp/util.lua
+++ b/runtime/lua/vim/lsp/util.lua
@@ -1,10 +1,10 @@
-local protocol = require 'vim.lsp.protocol'
-local snippet = require 'vim.lsp._snippet'
+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
-local highlight = require 'vim.highlight'
+local highlight = require('vim.highlight')
local uv = vim.loop
local npcall = vim.F.npcall
@@ -13,14 +13,14 @@ local split = vim.split
local M = {}
local default_border = {
- {"", "NormalFloat"},
- {"", "NormalFloat"},
- {"", "NormalFloat"},
- {" ", "NormalFloat"},
- {"", "NormalFloat"},
- {"", "NormalFloat"},
- {"", "NormalFloat"},
- {" ", "NormalFloat"},
+ { '', 'NormalFloat' },
+ { '', 'NormalFloat' },
+ { '', 'NormalFloat' },
+ { ' ', 'NormalFloat' },
+ { '', 'NormalFloat' },
+ { '', 'NormalFloat' },
+ { '', 'NormalFloat' },
+ { ' ', 'NormalFloat' },
}
---@private
@@ -35,43 +35,50 @@ local function get_border_size(opts)
local width = 0
if type(border) == 'string' then
- local border_size = {none = {0, 0}, single = {2, 2}, double = {2, 2}, rounded = {2, 2}, solid = {2, 2}, shadow = {1, 1}}
+ local border_size = {
+ none = { 0, 0 },
+ single = { 2, 2 },
+ double = { 2, 2 },
+ rounded = { 2, 2 },
+ solid = { 2, 2 },
+ shadow = { 1, 1 },
+ }
if border_size[border] == nil then
- error(string.format("invalid floating preview border: %s. :help vim.api.nvim_open_win()", vim.inspect(border)))
+ error(string.format('invalid floating preview border: %s. :help vim.api.nvim_open_win()', vim.inspect(border)))
end
height, width = unpack(border_size[border])
else
if 8 % #border ~= 0 then
- error(string.format("invalid floating preview border: %s. :help vim.api.nvim_open_win()", vim.inspect(border)))
+ error(string.format('invalid floating preview border: %s. :help vim.api.nvim_open_win()', vim.inspect(border)))
end
---@private
local function border_width(id)
id = (id - 1) % #border + 1
- if type(border[id]) == "table" then
+ if type(border[id]) == 'table' then
-- border specified as a table of <character, highlight group>
return vim.fn.strdisplaywidth(border[id][1])
- elseif type(border[id]) == "string" then
+ elseif type(border[id]) == 'string' then
-- border specified as a list of border characters
return vim.fn.strdisplaywidth(border[id])
end
- error(string.format("invalid floating preview border: %s. :help vim.api.nvim_open_win()", vim.inspect(border)))
+ error(string.format('invalid floating preview border: %s. :help vim.api.nvim_open_win()', vim.inspect(border)))
end
---@private
local function border_height(id)
id = (id - 1) % #border + 1
- if type(border[id]) == "table" then
+ if type(border[id]) == 'table' then
-- border specified as a table of <character, highlight group>
return #border[id][1] > 0 and 1 or 0
- elseif type(border[id]) == "string" then
+ elseif type(border[id]) == 'string' then
-- border specified as a list of border characters
return #border[id] > 0 and 1 or 0
end
- error(string.format("invalid floating preview border: %s. :help vim.api.nvim_open_win()", vim.inspect(border)))
+ error(string.format('invalid floating preview border: %s. :help vim.api.nvim_open_win()', vim.inspect(border)))
end
- height = height + border_height(2) -- top
- height = height + border_height(6) -- bottom
- width = width + border_width(4) -- right
- width = width + border_width(8) -- left
+ height = height + border_height(2) -- top
+ height = height + border_height(6) -- bottom
+ width = width + border_width(4) -- right
+ width = width + border_width(8) -- left
end
return { height = height, width = width }
@@ -89,9 +96,15 @@ end
---@param encoding string utf-8|utf-16|utf-32|nil defaults to utf-16
---@return number `encoding` index of `index` in `line`
function M._str_utfindex_enc(line, index, encoding)
- if not encoding then encoding = 'utf-16' end
+ if not encoding then
+ encoding = 'utf-16'
+ end
if encoding == 'utf-8' then
- if index then return index else return #line end
+ if index then
+ return index
+ else
+ return #line
+ end
elseif encoding == 'utf-16' then
local _, col16 = vim.str_utfindex(line, index)
return col16
@@ -99,7 +112,7 @@ function M._str_utfindex_enc(line, index, encoding)
local col32, _ = vim.str_utfindex(line, index)
return col32
else
- error("Invalid encoding: " .. vim.inspect(encoding))
+ error('Invalid encoding: ' .. vim.inspect(encoding))
end
end
@@ -111,15 +124,21 @@ end
---@param encoding string utf-8|utf-16|utf-32|nil defaults to utf-16
---@return number byte (utf-8) index of `encoding` index `index` in `line`
function M._str_byteindex_enc(line, index, encoding)
- if not encoding then encoding = 'utf-16' end
+ if not encoding then
+ encoding = 'utf-16'
+ end
if encoding == 'utf-8' then
- if index then return index else return #line end
+ if index then
+ return index
+ else
+ return #line
+ end
elseif encoding == 'utf-16' then
return vim.str_byteindex(line, index, true)
elseif encoding == 'utf-32' then
return vim.str_byteindex(line, index)
else
- error("Invalid encoding: " .. vim.inspect(encoding))
+ error('Invalid encoding: ' .. vim.inspect(encoding))
end
end
@@ -142,34 +161,38 @@ function M.set_lines(lines, A, B, new_lines)
-- specifying a line number after what we would call the last line.
local i_n = math.min(B[1] + 1, #lines)
if not (i_0 >= 1 and i_0 <= #lines + 1 and i_n >= 1 and i_n <= #lines) then
- error("Invalid range: "..vim.inspect{A = A; B = B; #lines, new_lines})
+ error('Invalid range: ' .. vim.inspect({ A = A, B = B, #lines, new_lines }))
end
- local prefix = ""
- local suffix = lines[i_n]:sub(B[2]+1)
+ local prefix = ''
+ local suffix = lines[i_n]:sub(B[2] + 1)
if A[2] > 0 then
prefix = lines[i_0]:sub(1, A[2])
end
local n = i_n - i_0 + 1
if n ~= #new_lines then
- for _ = 1, n - #new_lines do table.remove(lines, i_0) end
- for _ = 1, #new_lines - n do table.insert(lines, i_0, '') end
+ for _ = 1, n - #new_lines do
+ table.remove(lines, i_0)
+ end
+ for _ = 1, #new_lines - n do
+ table.insert(lines, i_0, '')
+ end
end
for i = 1, #new_lines do
lines[i - 1 + i_0] = new_lines[i]
end
if #suffix > 0 then
local i = i_0 + #new_lines - 1
- lines[i] = lines[i]..suffix
+ lines[i] = lines[i] .. suffix
end
if #prefix > 0 then
- lines[i_0] = prefix..lines[i_0]
+ lines[i_0] = prefix .. lines[i_0]
end
return lines
end
---@private
local function sort_by_key(fn)
- return function(a,b)
+ return function(a, b)
local ka, kb = fn(a), fn(b)
assert(#ka == #kb)
for i = 1, #ka do
@@ -191,7 +214,7 @@ end
---@param rows number[] zero-indexed line numbers
---@return table<number string> a table mapping rows to lines
local function get_lines(bufnr, rows)
- rows = type(rows) == "table" and rows or { rows }
+ rows = type(rows) == 'table' and rows or { rows }
-- This is needed for bufload and bufloaded
if bufnr == 0 then
@@ -202,7 +225,7 @@ local function get_lines(bufnr, rows)
local function buf_lines()
local lines = {}
for _, row in pairs(rows) do
- lines[row] = (vim.api.nvim_buf_get_lines(bufnr, row, row + 1, false) or { "" })[1]
+ lines[row] = (vim.api.nvim_buf_get_lines(bufnr, row, row + 1, false) or { '' })[1]
end
return lines
end
@@ -211,7 +234,7 @@ local function get_lines(bufnr, rows)
-- load the buffer if this is not a file uri
-- Custom language server protocol extensions can result in servers sending URIs with custom schemes. Plugins are able to load these via `BufReadCmd` autocmds.
- if uri:sub(1, 4) ~= "file" then
+ if uri:sub(1, 4) ~= 'file' then
vim.fn.bufload(bufnr)
return buf_lines()
end
@@ -224,8 +247,10 @@ local function get_lines(bufnr, rows)
local filename = api.nvim_buf_get_name(bufnr)
-- get the data from the file
- local fd = uv.fs_open(filename, "r", 438)
- if not fd then return "" end
+ local fd = uv.fs_open(filename, 'r', 438)
+ if not fd then
+ return ''
+ end
local stat = uv.fs_fstat(fd)
local data = uv.fs_read(fd, stat.size, 0)
uv.fs_close(fd)
@@ -242,11 +267,13 @@ local function get_lines(bufnr, rows)
local found = 0
local lnum = 0
- for line in string.gmatch(data, "([^\n]*)\n?") do
+ for line in string.gmatch(data, '([^\n]*)\n?') do
if lines[lnum] == true then
lines[lnum] = line
found = found + 1
- if found == need then break end
+ if found == need then
+ break
+ end
end
lnum = lnum + 1
end
@@ -254,13 +281,12 @@ local function get_lines(bufnr, rows)
-- change any lines we didn't find to the empty string
for i, line in pairs(lines) do
if line == true then
- lines[i] = ""
+ lines[i] = ''
end
end
return lines
end
-
---@private
--- Gets the zero-indexed line from the given buffer.
--- Works on unloaded buffers by reading the file using libuv to bypass buf reading events.
@@ -273,7 +299,6 @@ local function get_line(bufnr, row)
return get_lines(bufnr, { row })[row]
end
-
---@private
--- Position is a https://microsoft.github.io/language-server-protocol/specifications/specification-current/#position
--- Returns a zero-indexed column, since set_lines() does the conversion to
@@ -300,30 +325,27 @@ end
--- Process and return progress reports from lsp server
---@private
function M.get_progress_messages()
-
local new_messages = {}
local progress_remove = {}
for _, client in ipairs(vim.lsp.get_active_clients()) do
- local messages = client.messages
- local data = messages
- for token, ctx in pairs(data.progress) do
-
- local new_report = {
- name = data.name,
- title = ctx.title or "empty title",
- message = ctx.message,
- percentage = ctx.percentage,
- done = ctx.done,
- progress = true,
- }
- table.insert(new_messages, new_report)
+ local messages = client.messages
+ local data = messages
+ for token, ctx in pairs(data.progress) do
+ local new_report = {
+ name = data.name,
+ title = ctx.title or 'empty title',
+ message = ctx.message,
+ percentage = ctx.percentage,
+ done = ctx.done,
+ progress = true,
+ }
+ table.insert(new_messages, new_report)
- if ctx.done then
- table.insert(progress_remove, {client = client, token = token})
- end
+ if ctx.done then
+ table.insert(progress_remove, { client = client, token = token })
end
-
+ end
end
for _, item in ipairs(progress_remove) do
@@ -339,12 +361,14 @@ end
---@param offset_encoding string utf-8|utf-16|utf-32
---@see https://microsoft.github.io/language-server-protocol/specifications/specification-current/#textEdit
function M.apply_text_edits(text_edits, bufnr, offset_encoding)
- validate {
- text_edits = { text_edits, 't', false };
- bufnr = { bufnr, 'number', false };
- offset_encoding = { offset_encoding, 'string', false };
- }
- if not next(text_edits) then return end
+ validate({
+ text_edits = { text_edits, 't', false },
+ bufnr = { bufnr, 'number', false },
+ offset_encoding = { offset_encoding, 'string', false },
+ })
+ if not next(text_edits) then
+ return
+ end
if not api.nvim_buf_is_loaded(bufnr) then
vim.fn.bufload(bufnr)
end
@@ -356,7 +380,11 @@ function M.apply_text_edits(text_edits, bufnr, offset_encoding)
index = index + 1
text_edit._index = index
- if text_edit.range.start.line > text_edit.range['end'].line or text_edit.range.start.line == text_edit.range['end'].line and text_edit.range.start.character > text_edit.range['end'].character then
+ if
+ text_edit.range.start.line > text_edit.range['end'].line
+ or text_edit.range.start.line == text_edit.range['end'].line
+ and text_edit.range.start.character > text_edit.range['end'].character
+ then
local start = text_edit.range.start
text_edit.range.start = text_edit.range['end']
text_edit.range['end'] = start
@@ -406,7 +434,7 @@ function M.apply_text_edits(text_edits, bufnr, offset_encoding)
start_row = text_edit.range.start.line,
start_col = get_line_byte_from_position(bufnr, text_edit.range.start, offset_encoding),
end_row = text_edit.range['end'].line,
- end_col = get_line_byte_from_position(bufnr, text_edit.range['end'], offset_encoding),
+ end_col = get_line_byte_from_position(bufnr, text_edit.range['end'], offset_encoding),
text = vim.split(text_edit.newText, '\n', true),
}
@@ -456,7 +484,11 @@ function M.apply_text_edits(text_edits, bufnr, offset_encoding)
-- Remove final line if needed
local fix_eol = has_eol_text_edit
- fix_eol = fix_eol and (api.nvim_buf_get_option(bufnr, 'eol') or (api.nvim_buf_get_option(bufnr, 'fixeol') and not api.nvim_buf_get_option(bufnr, 'binary')))
+ fix_eol = fix_eol
+ and (
+ api.nvim_buf_get_option(bufnr, 'eol')
+ or (api.nvim_buf_get_option(bufnr, 'fixeol') and not api.nvim_buf_get_option(bufnr, 'binary'))
+ )
fix_eol = fix_eol and get_line(bufnr, max - 1) == ''
if fix_eol then
vim.api.nvim_buf_set_lines(bufnr, -2, -1, false, {})
@@ -499,7 +531,7 @@ function M.apply_text_document_edit(text_document_edit, index, offset_encoding)
local text_document = text_document_edit.textDocument
local bufnr = vim.uri_to_bufnr(text_document.uri)
if offset_encoding == nil then
- vim.notify_once("apply_text_document_edit must be called with valid offset encoding", vim.log.levels.WARN)
+ vim.notify_once('apply_text_document_edit must be called with valid offset encoding', vim.log.levels.WARN)
end
-- For lists of text document edits,
@@ -511,11 +543,16 @@ function M.apply_text_document_edit(text_document_edit, index, offset_encoding)
-- `VersionedTextDocumentIdentifier`s version may be null
-- https://microsoft.github.io/language-server-protocol/specification#versionedTextDocumentIdentifier
- if should_check_version and (text_document.version
+ if
+ should_check_version
+ and (
+ text_document.version
and text_document.version > 0
and M.buf_versions[bufnr]
- and M.buf_versions[bufnr] > text_document.version) then
- print("Buffer ", text_document.uri, " newer than edits.")
+ and M.buf_versions[bufnr] > text_document.version
+ )
+ then
+ print('Buffer ', text_document.uri, ' newer than edits.')
return
end
@@ -551,16 +588,16 @@ end
--- precedence is as follows: textEdit.newText > insertText > label
--see https://microsoft.github.io/language-server-protocol/specifications/specification-current/#textDocument_completion
local function get_completion_word(item)
- if item.textEdit ~= nil and item.textEdit.newText ~= nil and item.textEdit.newText ~= "" then
+ if item.textEdit ~= nil and item.textEdit.newText ~= nil and item.textEdit.newText ~= '' then
local insert_text_format = protocol.InsertTextFormat[item.insertTextFormat]
- if insert_text_format == "PlainText" or insert_text_format == nil then
+ if insert_text_format == 'PlainText' or insert_text_format == nil then
return item.textEdit.newText
else
return M.parse_snippet(item.textEdit.newText)
end
- elseif item.insertText ~= nil and item.insertText ~= "" then
+ elseif item.insertText ~= nil and item.insertText ~= '' then
local insert_text_format = protocol.InsertTextFormat[item.insertTextFormat]
- if insert_text_format == "PlainText" or insert_text_format == nil then
+ if insert_text_format == 'PlainText' or insert_text_format == nil then
return item.insertText
else
return M.parse_snippet(item.insertText)
@@ -588,7 +625,7 @@ end
---@returns (`vim.lsp.protocol.completionItemKind`)
---@see https://microsoft.github.io/language-server-protocol/specifications/specification-current/#textDocument_completion
function M._get_completion_item_kind_name(completion_item_kind)
- return protocol.CompletionItemKind[completion_item_kind] or "Unknown"
+ return protocol.CompletionItemKind[completion_item_kind] or 'Unknown'
end
--- Turns the result of a `textDocument/completion` request into vim-compatible
@@ -619,7 +656,7 @@ function M.text_document_completion_list_to_complete_items(result, prefix)
info = documentation
elseif type(documentation) == 'table' and type(documentation.value) == 'string' then
info = documentation.value
- -- else
+ -- else
-- TODO(ashkan) Validation handling here?
end
end
@@ -637,9 +674,9 @@ function M.text_document_completion_list_to_complete_items(result, prefix)
user_data = {
nvim = {
lsp = {
- completion_item = completion_item
- }
- }
+ completion_item = completion_item,
+ },
+ },
},
})
end
@@ -647,6 +684,15 @@ function M.text_document_completion_list_to_complete_items(result, prefix)
return matches
end
+---@private
+--- Like vim.fn.bufwinid except it works across tabpages.
+local function bufwinid(bufnr)
+ for _, win in ipairs(api.nvim_list_wins()) do
+ if api.nvim_win_get_buf(win) == bufnr then
+ return win
+ end
+ end
+end
--- Rename old_fname to new_fname
---
@@ -672,10 +718,9 @@ function M.rename(old_fname, new_fname, opts)
assert(ok, err)
local newbuf = vim.fn.bufadd(new_fname)
- for _, win in pairs(api.nvim_list_wins()) do
- if api.nvim_win_get_buf(win) == oldbuf then
- api.nvim_win_set_buf(win, newbuf)
- end
+ local win = bufwinid(oldbuf)
+ if win then
+ api.nvim_win_set_buf(win, newbuf)
end
api.nvim_buf_delete(oldbuf, { force = true })
end
@@ -700,7 +745,7 @@ local function delete_file(change)
if opts.ignoreIfNotExists and not stat then
return
end
- assert(stat, "Cannot delete not existing file or folder " .. fname)
+ assert(stat, 'Cannot delete not existing file or folder ' .. fname)
local flags
if stat and stat.type == 'directory' then
flags = opts.recursive and 'rf' or 'd'
@@ -713,7 +758,6 @@ local function delete_file(change)
api.nvim_buf_delete(bufnr, { force = true })
end
-
--- Applies a `WorkspaceEdit`.
---
---@param workspace_edit table `WorkspaceEdit`
@@ -721,22 +765,18 @@ end
--see https://microsoft.github.io/language-server-protocol/specifications/specification-current/#workspace_applyEdit
function M.apply_workspace_edit(workspace_edit, offset_encoding)
if offset_encoding == nil then
- vim.notify_once("apply_workspace_edit must be called with valid offset encoding", vim.log.levels.WARN)
+ vim.notify_once('apply_workspace_edit must be called with valid offset encoding', vim.log.levels.WARN)
end
if workspace_edit.documentChanges then
for idx, change in ipairs(workspace_edit.documentChanges) do
- if change.kind == "rename" then
- M.rename(
- vim.uri_to_fname(change.oldUri),
- vim.uri_to_fname(change.newUri),
- change.options
- )
+ if change.kind == 'rename' then
+ M.rename(vim.uri_to_fname(change.oldUri), vim.uri_to_fname(change.newUri), change.options)
elseif change.kind == 'create' then
create_file(change)
elseif change.kind == 'delete' then
delete_file(change)
elseif change.kind then
- error(string.format("Unsupported change: %q", vim.inspect(change)))
+ error(string.format('Unsupported change: %q', vim.inspect(change)))
else
M.apply_text_document_edit(change, idx, offset_encoding)
end
@@ -770,7 +810,7 @@ function M.convert_input_to_markdown_lines(input, contents)
if type(input) == 'string' then
list_extend(contents, split_lines(input))
else
- assert(type(input) == 'table', "Expected a table for Hover.contents")
+ assert(type(input) == 'table', 'Expected a table for Hover.contents')
-- MarkupContent
if input.kind then
-- The kind can be either plaintext or markdown.
@@ -779,22 +819,22 @@ function M.convert_input_to_markdown_lines(input, contents)
-- Some servers send input.value as empty, so let's ignore this :(
local value = input.value or ''
- if input.kind == "plaintext" then
+ if input.kind == 'plaintext' then
-- wrap this in a <text></text> block so that stylize_markdown
-- can properly process it as plaintext
- value = string.format("<text>\n%s\n</text>", value)
+ value = string.format('<text>\n%s\n</text>', value)
end
-- assert(type(value) == 'string')
list_extend(contents, split_lines(value))
- -- MarkupString variation 2
+ -- MarkupString variation 2
elseif input.language then
-- Some servers send input.value as empty, so let's ignore this :(
-- assert(type(input.value) == 'string')
- table.insert(contents, "```"..input.language)
+ table.insert(contents, '```' .. input.language)
list_extend(contents, split_lines(input.value or ''))
- table.insert(contents, "```")
- -- By deduction, this must be MarkedString[]
+ table.insert(contents, '```')
+ -- By deduction, this must be MarkedString[]
else
-- Use our existing logic to handle MarkedString
for _, marked_string in ipairs(input) do
@@ -838,7 +878,7 @@ function M.convert_signature_help_to_markdown_lines(signature_help, ft, triggers
local label = signature.label
if ft then
-- wrap inside a code block so stylize_markdown can render it properly
- label = ("```%s\n%s\n```"):format(ft, label)
+ label = ('```%s\n%s\n```'):format(ft, label)
end
vim.list_extend(contents, vim.split(label, '\n', true))
if signature.documentation then
@@ -846,8 +886,8 @@ function M.convert_signature_help_to_markdown_lines(signature_help, ft, triggers
end
if signature.parameters and #signature.parameters > 0 then
local active_parameter = (signature.activeParameter or signature_help.activeParameter or 0)
- if active_parameter < 0
- then active_parameter = 0
+ if active_parameter < 0 then
+ active_parameter = 0
end
-- If the activeParameter is > #parameters, then set it to the last
@@ -877,7 +917,7 @@ function M.convert_signature_help_to_markdown_lines(signature_help, ft, triggers
}
--]=]
if parameter.label then
- if type(parameter.label) == "table" then
+ if type(parameter.label) == 'table' then
active_hl = parameter.label
else
local offset = 1
@@ -890,9 +930,11 @@ function M.convert_signature_help_to_markdown_lines(signature_help, ft, triggers
end
for p, param in pairs(signature.parameters) do
offset = signature.label:find(param.label, offset, true)
- if not offset then break end
+ if not offset then
+ break
+ end
if p == active_parameter + 1 then
- active_hl = {offset - 1, offset + #parameter.label - 1}
+ active_hl = { offset - 1, offset + #parameter.label - 1 }
break
end
offset = offset + #param.label + 1
@@ -920,14 +962,14 @@ end
--- - zindex (string or table) override `zindex`, defaults to 50
---@returns (table) Options
function M.make_floating_popup_options(width, height, opts)
- validate {
- opts = { opts, 't', true };
- }
+ validate({
+ opts = { opts, 't', true },
+ })
opts = opts or {}
- validate {
- ["opts.offset_x"] = { opts.offset_x, 'n', true };
- ["opts.offset_y"] = { opts.offset_y, 'n', true };
- }
+ validate({
+ ['opts.offset_x'] = { opts.offset_x, 'n', true },
+ ['opts.offset_y'] = { opts.offset_y, 'n', true },
+ })
local anchor = ''
local row, col
@@ -936,20 +978,20 @@ function M.make_floating_popup_options(width, height, opts)
local lines_below = vim.fn.winheight(0) - lines_above
if lines_above < lines_below then
- anchor = anchor..'N'
+ anchor = anchor .. 'N'
height = math.min(lines_below, height)
row = 1
else
- anchor = anchor..'S'
+ anchor = anchor .. 'S'
height = math.min(lines_above, height)
row = 0
end
if vim.fn.wincol() + width + (opts.offset_x or 0) <= api.nvim_get_option('columns') then
- anchor = anchor..'W'
+ anchor = anchor .. 'W'
col = 0
else
- anchor = anchor..'E'
+ anchor = anchor .. 'E'
col = 1
end
@@ -971,32 +1013,40 @@ end
---
---@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)
+function M.jump_to_location(location, offset_encoding, reuse_win)
-- location may be Location or LocationLink
local uri = location.uri or location.targetUri
- if uri == nil then return end
+ if uri == nil then
+ return
+ 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('jump_to_location 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'"
+ 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 = { 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')
--- Jump to new location (adjusting for UTF-16 encoding of characters)
- api.nvim_set_current_buf(bufnr)
- api.nvim_buf_set_option(bufnr, 'buflisted', true)
+ local win = reuse_win and bufwinid(bufnr)
+ if win then
+ api.nvim_set_current_win(win)
+ else
+ api.nvim_set_current_buf(bufnr)
+ api.nvim_buf_set_option(bufnr, 'buflisted', true)
+ end
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})
+ api.nvim_win_set_cursor(0, { row + 1, col })
-- Open folds under the cursor
- vim.cmd("normal! zv")
+ vim.cmd('normal! zv')
return true
end
@@ -1011,22 +1061,24 @@ end
function M.preview_location(location, opts)
-- location may be LocationLink or Location (more useful for the former)
local uri = location.targetUri or location.uri
- if uri == nil then return end
+ if uri == nil then
+ return
+ end
local bufnr = vim.uri_to_bufnr(uri)
if not api.nvim_buf_is_loaded(bufnr) then
vim.fn.bufload(bufnr)
end
local range = location.targetRange or location.range
- local contents = api.nvim_buf_get_lines(bufnr, range.start.line, range["end"].line+1, false)
+ local contents = api.nvim_buf_get_lines(bufnr, range.start.line, range['end'].line + 1, false)
local syntax = api.nvim_buf_get_option(bufnr, 'syntax')
- if syntax == "" then
+ if syntax == '' then
-- When no syntax is set, we use filetype as fallback. This might not result
-- in a valid syntax definition. See also ft detection in stylize_markdown.
-- An empty syntax is more common now with TreeSitter, since TS disables syntax.
syntax = api.nvim_buf_get_option(bufnr, 'filetype')
end
opts = opts or {}
- opts.focus_id = "location"
+ opts.focus_id = 'location'
return M.open_floating_preview(contents, syntax, opts)
end
@@ -1047,20 +1099,20 @@ end
--- - pad_bottom number of lines to pad contents at bottom (default 0)
---@return contents table of trimmed and padded lines
function M._trim(contents, opts)
- validate {
- contents = { contents, 't' };
- opts = { opts, 't', true };
- }
+ validate({
+ contents = { contents, 't' },
+ opts = { opts, 't', true },
+ })
opts = opts or {}
contents = M.trim_empty_lines(contents)
if opts.pad_top then
for _ = 1, opts.pad_top do
- table.insert(contents, 1, "")
+ table.insert(contents, 1, '')
end
end
if opts.pad_bottom then
for _ = 1, opts.pad_bottom do
- table.insert(contents, "")
+ table.insert(contents, '')
end
end
return contents
@@ -1073,7 +1125,7 @@ end
local function get_markdown_fences()
local fences = {}
for _, fence in pairs(vim.g.markdown_fenced_languages or {}) do
- local lang, syntax = fence:match("^(.*)=(.*)$")
+ local lang, syntax = fence:match('^(.*)=(.*)$')
if lang then
fences[lang] = syntax
end
@@ -1102,28 +1154,28 @@ end
--- - separator insert separator after code block
---@returns width,height size of float
function M.stylize_markdown(bufnr, contents, opts)
- validate {
- contents = { contents, 't' };
- opts = { opts, 't', true };
- }
+ validate({
+ contents = { contents, 't' },
+ opts = { opts, 't', true },
+ })
opts = opts or {}
-- table of fence types to {ft, begin, end}
-- when ft is nil, we get the ft from the regex match
local matchers = {
- block = {nil, "```+([a-zA-Z0-9_]*)", "```+"},
- pre = {"", "<pre>", "</pre>"},
- code = {"", "<code>", "</code>"},
- text = {"text", "<text>", "</text>"},
+ block = { nil, '```+([a-zA-Z0-9_]*)', '```+' },
+ pre = { '', '<pre>', '</pre>' },
+ code = { '', '<code>', '</code>' },
+ text = { 'text', '<text>', '</text>' },
}
local match_begin = function(line)
for type, pattern in pairs(matchers) do
- local ret = line:match(string.format("^%%s*%s%%s*$", pattern[2]))
+ local ret = line:match(string.format('^%%s*%s%%s*$', pattern[2]))
if ret then
return {
type = type,
- ft = pattern[1] or ret
+ ft = pattern[1] or ret,
}
end
end
@@ -1131,7 +1183,7 @@ function M.stylize_markdown(bufnr, contents, opts)
local match_end = function(line, match)
local pattern = matchers[match.type]
- return line:match(string.format("^%%s*%s%%s*$", pattern[3]))
+ return line:match(string.format('^%%s*%s%%s*$', pattern[3]))
end
-- Clean up
@@ -1161,25 +1213,27 @@ function M.stylize_markdown(bufnr, contents, opts)
i = i + 1
end
table.insert(highlights, {
- ft = match.ft;
- start = start + 1;
- finish = #stripped;
+ ft = match.ft,
+ start = start + 1,
+ finish = #stripped,
})
-- add a separator, but not on the last line
if add_sep and i < #contents then
- table.insert(stripped, "---")
+ table.insert(stripped, '---')
markdown_lines[#stripped] = true
end
else
-- strip any empty lines or separators prior to this separator in actual markdown
- if line:match("^---+$") then
- while markdown_lines[#stripped] and (stripped[#stripped]:match("^%s*$") or stripped[#stripped]:match("^---+$")) do
+ if line:match('^---+$') then
+ while
+ markdown_lines[#stripped] and (stripped[#stripped]:match('^%s*$') or stripped[#stripped]:match('^---+$'))
+ do
markdown_lines[#stripped] = false
table.remove(stripped, #stripped)
end
end
-- add the line if its not an empty line following a separator
- if not (line:match("^%s*$") and markdown_lines[#stripped] and stripped[#stripped]:match("^---+$")) then
+ if not (line:match('^%s*$') and markdown_lines[#stripped] and stripped[#stripped]:match('^---+$')) then
table.insert(stripped, line)
markdown_lines[#stripped] = true
end
@@ -1189,13 +1243,13 @@ function M.stylize_markdown(bufnr, contents, opts)
end
-- Compute size of float needed to show (wrapped) lines
- opts.wrap_at = opts.wrap_at or (vim.wo["wrap"] and api.nvim_win_get_width(0))
+ opts.wrap_at = opts.wrap_at or (vim.wo['wrap'] and api.nvim_win_get_width(0))
local width = M._make_floating_popup_size(stripped, opts)
- local sep_line = string.rep("─", math.min(width, opts.wrap_at or width))
+ local sep_line = string.rep('─', math.min(width, opts.wrap_at or width))
for l in pairs(markdown_lines) do
- if stripped[l]:match("^---+$") then
+ if stripped[l]:match('^---+$') then
stripped[l] = sep_line
end
end
@@ -1209,24 +1263,28 @@ function M.stylize_markdown(bufnr, contents, opts)
local langs = {}
local fences = get_markdown_fences()
local function apply_syntax_to_region(ft, start, finish)
- if ft == "" then
- vim.cmd(string.format("syntax region markdownCode start=+\\%%%dl+ end=+\\%%%dl+ keepend extend", start, finish + 1))
+ if ft == '' then
+ vim.cmd(
+ string.format('syntax region markdownCode start=+\\%%%dl+ end=+\\%%%dl+ keepend extend', start, finish + 1)
+ )
return
end
ft = fences[ft] or ft
- local name = ft..idx
+ local name = ft .. idx
idx = idx + 1
- local lang = "@"..ft:upper()
+ local lang = '@' .. ft:upper()
if not langs[lang] then
-- HACK: reset current_syntax, since some syntax files like markdown won't load if it is already set
- pcall(vim.api.nvim_buf_del_var, bufnr, "current_syntax")
+ pcall(vim.api.nvim_buf_del_var, bufnr, 'current_syntax')
-- TODO(ashkan): better validation before this.
- if not pcall(vim.cmd, string.format("syntax include %s syntax/%s.vim", lang, ft)) then
+ if not pcall(vim.cmd, string.format('syntax include %s syntax/%s.vim', lang, ft)) then
return
end
langs[lang] = true
end
- vim.cmd(string.format("syntax region %s start=+\\%%%dl+ end=+\\%%%dl+ contains=%s keepend", name, start, finish + 1, lang))
+ vim.cmd(
+ string.format('syntax region %s start=+\\%%%dl+ end=+\\%%%dl+ contains=%s keepend', name, start, finish + 1, lang)
+ )
end
-- needs to run in the buffer for the regions to work
@@ -1237,13 +1295,13 @@ function M.stylize_markdown(bufnr, contents, opts)
local last = 1
for _, h in ipairs(highlights) do
if last < h.start then
- apply_syntax_to_region("lsp_markdown", last, h.start - 1)
+ apply_syntax_to_region('lsp_markdown', last, h.start - 1)
end
apply_syntax_to_region(h.ft, h.start, h.finish)
last = h.finish + 1
end
if last <= #stripped then
- apply_syntax_to_region("lsp_markdown", last, #stripped)
+ apply_syntax_to_region('lsp_markdown', last, #stripped)
end
end)
@@ -1258,23 +1316,33 @@ end
---@param bufnrs table list of buffers where the preview window will remain visible
---@see |autocmd-events|
local function close_preview_autocmd(events, winnr, bufnrs)
- local augroup = 'preview_window_'..winnr
+ local augroup = 'preview_window_' .. winnr
-- close the preview window when entered a buffer that is not
-- the floating window buffer or the buffer that spawned it
- vim.cmd(string.format([[
+ vim.cmd(string.format(
+ [[
augroup %s
autocmd!
autocmd BufEnter * lua vim.lsp.util._close_preview_window(%d, {%s})
augroup end
- ]], augroup, winnr, table.concat(bufnrs, ',')))
+ ]],
+ augroup,
+ winnr,
+ table.concat(bufnrs, ',')
+ ))
if #events > 0 then
- vim.cmd(string.format([[
+ vim.cmd(string.format(
+ [[
augroup %s
autocmd %s <buffer> lua vim.lsp.util._close_preview_window(%d)
augroup end
- ]], augroup, table.concat(events, ','), winnr))
+ ]],
+ augroup,
+ table.concat(events, ','),
+ winnr
+ ))
end
end
@@ -1290,13 +1358,17 @@ function M._close_preview_window(winnr, bufnrs)
return
end
- local augroup = 'preview_window_'..winnr
- vim.cmd(string.format([[
+ local augroup = 'preview_window_' .. winnr
+ vim.cmd(string.format(
+ [[
augroup %s
autocmd!
augroup end
augroup! %s
- ]], augroup, augroup))
+ ]],
+ augroup,
+ augroup
+ ))
pcall(vim.api.nvim_win_close, winnr, true)
end)
end
@@ -1313,10 +1385,10 @@ end
--- - max_height maximal height of floating window
---@returns width,height size of float
function M._make_floating_popup_size(contents, opts)
- validate {
- contents = { contents, 't' };
- opts = { opts, 't', true };
- }
+ validate({
+ contents = { contents, 't' },
+ opts = { opts, 't', true },
+ })
opts = opts or {}
local width = opts.width
@@ -1360,11 +1432,11 @@ function M._make_floating_popup_size(contents, opts)
if vim.tbl_isempty(line_widths) then
for _, line in ipairs(contents) do
local line_width = vim.fn.strdisplaywidth(line)
- height = height + math.ceil(line_width/wrap_at)
+ height = height + math.ceil(line_width / wrap_at)
end
else
for i = 1, #contents do
- height = height + math.max(1, math.ceil(line_widths[i]/wrap_at))
+ height = height + math.max(1, math.ceil(line_widths[i] / wrap_at))
end
end
end
@@ -1398,16 +1470,16 @@ end
---@returns bufnr,winnr buffer and window number of the newly created floating
---preview window
function M.open_floating_preview(contents, syntax, opts)
- validate {
- contents = { contents, 't' };
- syntax = { syntax, 's', true };
- opts = { opts, 't', true };
- }
+ validate({
+ contents = { contents, 't' },
+ syntax = { syntax, 's', true },
+ opts = { opts, 't', true },
+ })
opts = opts or {}
opts.wrap = opts.wrap ~= false -- wrapping by default
opts.stylize_markdown = opts.stylize_markdown ~= false
opts.focus = opts.focus ~= false
- opts.close_events = opts.close_events or {"CursorMoved", "CursorMovedI", "InsertCharPre"}
+ opts.close_events = opts.close_events or { 'CursorMoved', 'CursorMovedI', 'InsertCharPre' }
local bufnr = api.nvim_get_current_buf()
@@ -1416,7 +1488,7 @@ function M.open_floating_preview(contents, syntax, opts)
-- Go back to previous window if we are in a focusable one
local current_winnr = api.nvim_get_current_win()
if npcall(api.nvim_win_get_var, current_winnr, opts.focus_id) then
- api.nvim_command("wincmd p")
+ api.nvim_command('wincmd p')
return bufnr, current_winnr
end
do
@@ -1424,7 +1496,7 @@ function M.open_floating_preview(contents, syntax, opts)
if win and api.nvim_win_is_valid(win) and vim.fn.pumvisible() == 0 then
-- focus and return the existing buf, win
api.nvim_set_current_win(win)
- api.nvim_command("stopinsert")
+ api.nvim_command('stopinsert')
return api.nvim_win_get_buf(win), win
end
end
@@ -1432,14 +1504,13 @@ function M.open_floating_preview(contents, syntax, opts)
-- check if another floating preview already exists for this buffer
-- and close it if needed
- local existing_float = npcall(api.nvim_buf_get_var, bufnr, "lsp_floating_preview")
+ local existing_float = npcall(api.nvim_buf_get_var, bufnr, 'lsp_floating_preview')
if existing_float and api.nvim_win_is_valid(existing_float) then
api.nvim_win_close(existing_float, true)
end
local floating_bufnr = api.nvim_create_buf(false, true)
- local do_stylize = syntax == "markdown" and opts.stylize_markdown
-
+ local do_stylize = syntax == 'markdown' and opts.stylize_markdown
-- Clean up input: trim empty lines from the end, pad
contents = M._trim(contents, opts)
@@ -1475,26 +1546,32 @@ function M.open_floating_preview(contents, syntax, opts)
api.nvim_buf_set_option(floating_bufnr, 'modifiable', false)
api.nvim_buf_set_option(floating_bufnr, 'bufhidden', 'wipe')
- api.nvim_buf_set_keymap(floating_bufnr, "n", "q", "<cmd>bdelete<cr>", {silent = true, noremap = true, nowait = true})
- close_preview_autocmd(opts.close_events, floating_winnr, {floating_bufnr, bufnr})
+ api.nvim_buf_set_keymap(
+ floating_bufnr,
+ 'n',
+ 'q',
+ '<cmd>bdelete<cr>',
+ { silent = true, noremap = true, nowait = true }
+ )
+ close_preview_autocmd(opts.close_events, floating_winnr, { floating_bufnr, bufnr })
-- save focus_id
if opts.focus_id then
api.nvim_win_set_var(floating_winnr, opts.focus_id, bufnr)
end
- api.nvim_buf_set_var(bufnr, "lsp_floating_preview", floating_winnr)
+ api.nvim_buf_set_var(bufnr, 'lsp_floating_preview', floating_winnr)
return floating_bufnr, floating_winnr
end
do --[[ References ]]
- local reference_ns = api.nvim_create_namespace("vim_lsp_references")
+ local reference_ns = api.nvim_create_namespace('vim_lsp_references')
--- Removes document highlights from a buffer.
---
---@param bufnr number Buffer id
function M.buf_clear_references(bufnr)
- validate { bufnr = {bufnr, 'n', true} }
+ validate({ bufnr = { bufnr, 'n', true } })
api.nvim_buf_clear_namespace(bufnr or 0, reference_ns, 0, -1)
end
@@ -1505,35 +1582,41 @@ do --[[ References ]]
---@param offset_encoding string One of "utf-8", "utf-16", "utf-32".
---@see https://microsoft.github.io/language-server-protocol/specifications/specification-3-17/#documentHighlight
function M.buf_highlight_references(bufnr, references, offset_encoding)
- validate {
- bufnr = {bufnr, 'n', true},
- offset_encoding = { offset_encoding, 'string', false };
- }
+ validate({
+ bufnr = { bufnr, 'n', true },
+ offset_encoding = { offset_encoding, 'string', false },
+ })
for _, reference in ipairs(references) do
- local start_line, start_char = reference["range"]["start"]["line"], reference["range"]["start"]["character"]
- local end_line, end_char = reference["range"]["end"]["line"], reference["range"]["end"]["character"]
+ local start_line, start_char = reference['range']['start']['line'], reference['range']['start']['character']
+ local end_line, end_char = reference['range']['end']['line'], reference['range']['end']['character']
- local start_idx = get_line_byte_from_position(bufnr, { line = start_line, character = start_char }, offset_encoding)
+ local start_idx = get_line_byte_from_position(
+ bufnr,
+ { line = start_line, character = start_char },
+ offset_encoding
+ )
local end_idx = get_line_byte_from_position(bufnr, { line = start_line, character = end_char }, offset_encoding)
local document_highlight_kind = {
- [protocol.DocumentHighlightKind.Text] = "LspReferenceText";
- [protocol.DocumentHighlightKind.Read] = "LspReferenceRead";
- [protocol.DocumentHighlightKind.Write] = "LspReferenceWrite";
+ [protocol.DocumentHighlightKind.Text] = 'LspReferenceText',
+ [protocol.DocumentHighlightKind.Read] = 'LspReferenceRead',
+ [protocol.DocumentHighlightKind.Write] = 'LspReferenceWrite',
}
- local kind = reference["kind"] or protocol.DocumentHighlightKind.Text
- highlight.range(bufnr,
- reference_ns,
- document_highlight_kind[kind],
- { start_line, start_idx },
- { end_line, end_idx },
- { priority = vim.highlight.priorities.user })
+ local kind = reference['kind'] or protocol.DocumentHighlightKind.Text
+ highlight.range(
+ bufnr,
+ reference_ns,
+ document_highlight_kind[kind],
+ { start_line, start_idx },
+ { end_line, end_idx },
+ { priority = vim.highlight.priorities.user }
+ )
end
end
end
local position_sort = sort_by_key(function(v)
- return {v.start.line, v.start.character}
+ return { v.start.line, v.start.character }
end)
--- Returns the items with the byte position calculated correctly and in sorted
@@ -1547,7 +1630,7 @@ end)
---@returns (table) list of items
function M.locations_to_items(locations, offset_encoding)
if offset_encoding == nil then
- vim.notify_once("locations_to_items must be called with valid offset encoding", vim.log.levels.WARN)
+ vim.notify_once('locations_to_items must be called with valid offset encoding', vim.log.levels.WARN)
end
local items = {}
@@ -1556,16 +1639,15 @@ function M.locations_to_items(locations, offset_encoding)
local v = {}
rawset(t, k, v)
return v
- end;
+ end,
})
for _, d in ipairs(locations) do
-- locations may be Location or LocationLink
local uri = d.uri or d.targetUri
local range = d.range or d.targetSelectionRange
- table.insert(grouped[uri], {start = range.start})
+ table.insert(grouped[uri], { start = range.start })
end
-
local keys = vim.tbl_keys(grouped)
table.sort(keys)
-- TODO(ashkan) I wish we could do this lazily.
@@ -1588,13 +1670,13 @@ function M.locations_to_items(locations, offset_encoding)
for _, temp in ipairs(rows) do
local pos = temp.start
local row = pos.line
- local line = lines[row] or ""
+ local line = lines[row] or ''
local col = M._str_byteindex_enc(line, pos.character, offset_encoding)
table.insert(items, {
filename = filename,
lnum = row + 1,
- col = col + 1;
- text = line;
+ col = col + 1,
+ text = line,
})
end
end
@@ -1609,10 +1691,10 @@ end
---
---@param items (table) list of items
function M.set_loclist(items, win_id)
- vim.api.nvim_echo({{'vim.lsp.util.set_loclist is deprecated. See :h deprecated', 'WarningMsg'}}, true, {})
+ vim.api.nvim_echo({ { 'vim.lsp.util.set_loclist is deprecated. See :h deprecated', 'WarningMsg' } }, true, {})
vim.fn.setloclist(win_id or 0, {}, ' ', {
- title = 'Language Server';
- items = items;
+ title = 'Language Server',
+ items = items,
})
end
@@ -1623,10 +1705,10 @@ end
---
---@param items (table) list of items
function M.set_qflist(items)
- vim.api.nvim_echo({{'vim.lsp.util.set_qflist is deprecated. See :h deprecated', 'WarningMsg'}}, true, {})
+ vim.api.nvim_echo({ { 'vim.lsp.util.set_qflist is deprecated. See :h deprecated', 'WarningMsg' } }, true, {})
vim.fn.setqflist({}, ' ', {
- title = 'Language Server';
- items = items;
+ title = 'Language Server',
+ items = items,
})
end
@@ -1634,7 +1716,7 @@ end
-- the client must handle it properly even if it receives a value outside the specification.
-- https://microsoft.github.io/language-server-protocol/specifications/specification-current/#textDocument_documentSymbol
function M._get_symbol_kind_name(symbol_kind)
- return protocol.SymbolKind[symbol_kind] or "Unknown"
+ return protocol.SymbolKind[symbol_kind] or 'Unknown'
end
--- Converts symbols to quickfix list items.
@@ -1652,7 +1734,7 @@ function M.symbols_to_items(symbols, bufnr)
lnum = range.start.line + 1,
col = range.start.character + 1,
kind = kind,
- text = '['..kind..'] '..symbol.name,
+ text = '[' .. kind .. '] ' .. symbol.name,
})
elseif symbol.selectionRange then -- DocumentSymbole type
local kind = M._get_symbol_kind_name(symbol.kind)
@@ -1662,7 +1744,7 @@ function M.symbols_to_items(symbols, bufnr)
lnum = symbol.selectionRange.start.line + 1,
col = symbol.selectionRange.start.character + 1,
kind = kind,
- text = '['..kind..'] '..symbol.name
+ text = '[' .. kind .. '] ' .. symbol.name,
})
if symbol.children then
for _, v in ipairs(_symbols_to_items(symbol.children, _items, _bufnr)) do
@@ -1707,12 +1789,12 @@ end
---@param lines (table) list of lines
---@returns (string) filetype or 'markdown' if it was unchanged.
function M.try_trim_markdown_code_blocks(lines)
- local language_id = lines[1]:match("^```(.*)")
+ local language_id = lines[1]:match('^```(.*)')
if language_id then
local has_inner_code_fence = false
for i = 2, (#lines - 1) do
local line = lines[i]
- if line:sub(1,3) == '```' then
+ if line:sub(1, 3) == '```' then
has_inner_code_fence = true
break
end
@@ -1736,14 +1818,14 @@ local function make_position_param(window, offset_encoding)
local row, col = unpack(api.nvim_win_get_cursor(window))
offset_encoding = offset_encoding or M._get_offset_encoding(buf)
row = row - 1
- local line = api.nvim_buf_get_lines(buf, row, row+1, true)[1]
+ local line = api.nvim_buf_get_lines(buf, row, row + 1, true)[1]
if not line then
- return { line = 0; character = 0; }
+ return { line = 0, character = 0 }
end
col = _str_utfindex_enc(line, col, offset_encoding)
- return { line = row; character = col; }
+ return { line = row, character = col }
end
--- Creates a `TextDocumentPositionParams` object for the current buffer and cursor position.
@@ -1757,8 +1839,8 @@ function M.make_position_params(window, offset_encoding)
local buf = vim.api.nvim_win_get_buf(window)
offset_encoding = offset_encoding or M._get_offset_encoding(buf)
return {
- textDocument = M.make_text_document_params(buf);
- position = make_position_param(window, offset_encoding)
+ textDocument = M.make_text_document_params(buf),
+ position = make_position_param(window, offset_encoding),
}
end
@@ -1766,16 +1848,16 @@ end
---@param bufnr (number) buffer handle or 0 for current, defaults to current
---@returns (string) encoding first client if there is one, nil otherwise
function M._get_offset_encoding(bufnr)
- validate {
- bufnr = {bufnr, 'n', true};
- }
+ validate({
+ bufnr = { bufnr, 'n', true },
+ })
local offset_encoding
for _, client in pairs(vim.lsp.buf_get_clients(bufnr)) do
if client.offset_encoding == nil then
vim.notify_once(
- string.format("Client (id: %s) offset_encoding is nil. Do not unset offset_encoding.", client.id),
+ string.format('Client (id: %s) offset_encoding is nil. Do not unset offset_encoding.', client.id),
vim.log.levels.ERROR
)
end
@@ -1783,7 +1865,10 @@ function M._get_offset_encoding(bufnr)
if not offset_encoding then
offset_encoding = this_offset_encoding
elseif offset_encoding ~= this_offset_encoding then
- vim.notify("warning: multiple different client offset_encodings detected for buffer, this is not supported yet", vim.log.levels.WARN)
+ vim.notify(
+ 'warning: multiple different client offset_encodings detected for buffer, this is not supported yet',
+ vim.log.levels.WARN
+ )
end
end
@@ -1805,7 +1890,7 @@ function M.make_range_params(window, offset_encoding)
local position = make_position_param(window, offset_encoding)
return {
textDocument = M.make_text_document_params(buf),
- range = { start = position; ["end"] = position; }
+ range = { start = position, ['end'] = position },
}
end
@@ -1821,11 +1906,11 @@ end
---@returns { textDocument = { uri = `current_file_uri` }, range = { start =
---`start_position`, end = `end_position` } }
function M.make_given_range_params(start_pos, end_pos, bufnr, offset_encoding)
- validate {
- start_pos = {start_pos, 't', true};
- end_pos = {end_pos, 't', true};
- offset_encoding = {offset_encoding, 's', true};
- }
+ validate({
+ start_pos = { start_pos, 't', true },
+ end_pos = { end_pos, 't', true },
+ offset_encoding = { offset_encoding, 's', true },
+ })
bufnr = bufnr or vim.api.nvim_get_current_buf()
offset_encoding = offset_encoding or M._get_offset_encoding(bufnr)
local A = list_extend({}, start_pos or api.nvim_buf_get_mark(bufnr, '<'))
@@ -1835,10 +1920,10 @@ function M.make_given_range_params(start_pos, end_pos, bufnr, offset_encoding)
B[1] = B[1] - 1
-- account for offset_encoding.
if A[2] > 0 then
- A = {A[1], M.character_offset(bufnr, A[1], A[2], offset_encoding)}
+ A = { A[1], M.character_offset(bufnr, A[1], A[2], offset_encoding) }
end
if B[2] > 0 then
- B = {B[1], M.character_offset(bufnr, B[1], B[2], offset_encoding)}
+ B = { B[1], M.character_offset(bufnr, B[1], B[2], offset_encoding) }
end
-- we need to offset the end character position otherwise we loose the last
-- character of the selection, as LSP end position is exclusive
@@ -1849,9 +1934,9 @@ function M.make_given_range_params(start_pos, end_pos, bufnr, offset_encoding)
return {
textDocument = M.make_text_document_params(bufnr),
range = {
- start = {line = A[1], character = A[2]},
- ['end'] = {line = B[1], character = B[2]}
- }
+ start = { line = A[1], character = A[2] },
+ ['end'] = { line = B[1], character = B[2] },
+ },
}
end
@@ -1868,7 +1953,7 @@ end
---@param added
---@param removed
function M.make_workspace_params(added, removed)
- return { event = { added = added; removed = removed; } }
+ return { event = { added = added, removed = removed } }
end
--- Returns indentation size.
---
@@ -1876,7 +1961,7 @@ end
---@param bufnr (number|nil): Buffer handle, defaults to current
---@returns (number) indentation size
function M.get_effective_tabstop(bufnr)
- validate { bufnr = {bufnr, 'n', true} }
+ validate({ bufnr = { bufnr, 'n', true } })
local bo = bufnr and vim.bo[bufnr] or vim.bo
local sw = bo.shiftwidth
return (sw == 0 and bo.tabstop) or sw
@@ -1888,14 +1973,14 @@ end
---@returns `DocumentFormattingParams` object
---@see https://microsoft.github.io/language-server-protocol/specifications/specification-current/#textDocument_formatting
function M.make_formatting_params(options)
- validate { options = {options, 't', true} }
+ validate({ options = { options, 't', true } })
options = vim.tbl_extend('keep', options or {}, {
- tabSize = M.get_effective_tabstop();
- insertSpaces = vim.bo.expandtab;
+ tabSize = M.get_effective_tabstop(),
+ insertSpaces = vim.bo.expandtab,
})
return {
- textDocument = { uri = vim.uri_from_bufnr(0) };
- options = options;
+ textDocument = { uri = vim.uri_from_bufnr(0) },
+ options = options,
}
end
@@ -1909,7 +1994,7 @@ end
function M.character_offset(buf, row, col, offset_encoding)
local line = get_line(buf, row)
if offset_encoding == nil then
- vim.notify_once("character_offset must be called with valid offset encoding", vim.log.levels.WARN)
+ vim.notify_once('character_offset must be called with valid offset encoding', vim.log.levels.WARN)
end
-- If the col is past the EOL, use the line length.
if col > #line then