chore: format runtime with stylua

This commit is contained in:
Christian Clason
2022-05-09 11:23:51 +02:00
parent 676e9e9334
commit aefdc6783c
37 changed files with 4198 additions and 3569 deletions

View File

@@ -7,23 +7,23 @@ if vim.g.do_filetype_lua ~= 1 then
return return
end end
vim.api.nvim_create_augroup("filetypedetect", {clear = false}) vim.api.nvim_create_augroup('filetypedetect', { clear = false })
vim.api.nvim_create_autocmd({"BufRead", "BufNewFile"}, { vim.api.nvim_create_autocmd({ 'BufRead', 'BufNewFile' }, {
group = "filetypedetect", group = 'filetypedetect',
callback = function() callback = function()
vim.filetype.match(vim.fn.expand("<afile>")) vim.filetype.match(vim.fn.expand('<afile>'))
end, end,
}) })
-- These *must* be sourced after the autocommand above is created -- These *must* be sourced after the autocommand above is created
if not vim.g.did_load_ftdetect then if not vim.g.did_load_ftdetect then
vim.cmd [[ vim.cmd([[
augroup filetypedetect augroup filetypedetect
runtime! ftdetect/*.vim runtime! ftdetect/*.vim
runtime! ftdetect/*.lua runtime! ftdetect/*.lua
augroup END augroup END
]] ]])
end end
-- Set a marker so that the ftdetect scripts are not sourced a second time by filetype.vim -- Set a marker so that the ftdetect scripts are not sourced a second time by filetype.vim
@@ -31,17 +31,17 @@ vim.g.did_load_ftdetect = 1
-- If filetype.vim is disabled, set up the autocmd to use scripts.vim -- If filetype.vim is disabled, set up the autocmd to use scripts.vim
if vim.g.did_load_filetypes then if vim.g.did_load_filetypes then
vim.api.nvim_create_autocmd({"BufRead", "BufNewFile"}, { vim.api.nvim_create_autocmd({ 'BufRead', 'BufNewFile' }, {
group = "filetypedetect", group = 'filetypedetect',
command = "if !did_filetype() && expand('<amatch>') !~ g:ft_ignore_pat | runtime! scripts.vim | endif", command = "if !did_filetype() && expand('<amatch>') !~ g:ft_ignore_pat | runtime! scripts.vim | endif",
}) })
vim.api.nvim_create_autocmd("StdinReadPost", { vim.api.nvim_create_autocmd('StdinReadPost', {
group = "filetypedetect", group = 'filetypedetect',
command = "if !did_filetype() | runtime! scripts.vim | endif", command = 'if !did_filetype() | runtime! scripts.vim | endif',
}) })
end end
if not vim.g.ft_ignore_pat then if not vim.g.ft_ignore_pat then
vim.g.ft_ignore_pat = "\\.\\(Z\\|gz\\|bz2\\|zip\\|tgz\\)$" vim.g.ft_ignore_pat = '\\.\\(Z\\|gz\\|bz2\\|zip\\|tgz\\)$'
end end

View File

@@ -3,4 +3,4 @@
-- Last Change: 2022 Mar 29 -- Last Change: 2022 Mar 29
-- it's a lisp! -- it's a lisp!
vim.cmd [[ runtime! ftplugin/lisp.vim ]] vim.cmd([[ runtime! ftplugin/lisp.vim ]])

View File

@@ -3,4 +3,4 @@
-- Last Change: 2022 Mar 29 -- Last Change: 2022 Mar 29
-- it's a lisp! -- it's a lisp!
vim.cmd [[ runtime! indent/lisp.vim ]] vim.cmd([[ runtime! indent/lisp.vim ]])

View File

@@ -8,7 +8,7 @@ local function highlight_line(line, linenr)
local overstrike, escape = false, false local overstrike, escape = false, false
local hls = {} -- Store highlight groups as { attr, start, final } local hls = {} -- Store highlight groups as { attr, start, final }
local NONE, BOLD, UNDERLINE, ITALIC = 0, 1, 2, 3 local NONE, BOLD, UNDERLINE, ITALIC = 0, 1, 2, 3
local hl_groups = {[BOLD]="manBold", [UNDERLINE]="manUnderline", [ITALIC]="manItalic"} local hl_groups = { [BOLD] = 'manBold', [UNDERLINE] = 'manUnderline', [ITALIC] = 'manItalic' }
local attr = NONE local attr = NONE
local byte = 0 -- byte offset local byte = 0 -- byte offset
@@ -47,7 +47,7 @@ local function highlight_line(line, linenr)
end end
if continue_hl then if continue_hl then
hls[#hls + 1] = {attr=attr, start=byte, final=-1} hls[#hls + 1] = { attr = attr, start = byte, final = -1 }
else else
if attr == NONE then if attr == NONE then
for a, _ in pairs(hl_groups) do for a, _ in pairs(hl_groups) do
@@ -63,7 +63,7 @@ local function highlight_line(line, linenr)
-- can be represented in one byte. Any code point above that is represented by -- can be represented in one byte. Any code point above that is represented by
-- a leading byte (0xc0 and above) and continuation bytes (0x80 to 0xbf, or -- a leading byte (0xc0 and above) and continuation bytes (0x80 to 0xbf, or
-- decimal 128 to 191). -- decimal 128 to 191).
for char in line:gmatch("[^\128-\191][\128-\191]*") do for char in line:gmatch('[^\128-\191][\128-\191]*') do
if overstrike then if overstrike then
local last_hl = hls[#hls] local last_hl = hls[#hls]
if char == prev_char then if char == prev_char then
@@ -93,7 +93,7 @@ local function highlight_line(line, linenr)
if last_hl and last_hl.attr == attr and last_hl.final == byte then if last_hl and last_hl.attr == attr and last_hl.final == byte then
last_hl.final = byte + #char last_hl.final = byte + #char
else else
hls[#hls + 1] = {attr=attr, start=byte, final=byte + #char} hls[#hls + 1] = { attr = attr, start = byte, final = byte + #char }
end end
overstrike = false overstrike = false
@@ -106,25 +106,25 @@ local function highlight_line(line, linenr)
-- We only want to match against SGR sequences, which consist of ESC -- We only want to match against SGR sequences, which consist of ESC
-- followed by '[', then a series of parameter and intermediate bytes in -- followed by '[', then a series of parameter and intermediate bytes in
-- the range 0x20 - 0x3f, then 'm'. (See ECMA-48, sections 5.4 & 8.3.117) -- the range 0x20 - 0x3f, then 'm'. (See ECMA-48, sections 5.4 & 8.3.117)
local sgr = prev_char:match("^%[([\032-\063]*)m$") local sgr = prev_char:match('^%[([\032-\063]*)m$')
-- Ignore escape sequences with : characters, as specified by ITU's T.416 -- Ignore escape sequences with : characters, as specified by ITU's T.416
-- Open Document Architecture and interchange format. -- Open Document Architecture and interchange format.
if sgr and not string.find(sgr, ":") then if sgr and not string.find(sgr, ':') then
local match local match
while sgr and #sgr > 0 do while sgr and #sgr > 0 do
-- Match against SGR parameters, which may be separated by ';' -- Match against SGR parameters, which may be separated by ';'
match, sgr = sgr:match("^(%d*);?(.*)") match, sgr = sgr:match('^(%d*);?(.*)')
add_attr_hl(match + 0) -- coerce to number add_attr_hl(match + 0) -- coerce to number
end end
escape = false escape = false
elseif not prev_char:match("^%[[\032-\063]*$") then elseif not prev_char:match('^%[[\032-\063]*$') then
-- Stop looking if this isn't a partial CSI sequence -- Stop looking if this isn't a partial CSI sequence
escape = false escape = false
end end
elseif char == "\027" then elseif char == '\027' then
escape = true escape = true
prev_char = '' prev_char = ''
elseif char == "\b" then elseif char == '\b' then
overstrike = true overstrike = true
prev_char = chars[#chars] prev_char = chars[#chars]
byte = byte - #prev_char byte = byte - #prev_char
@@ -143,7 +143,7 @@ local function highlight_line(line, linenr)
hl_groups[hl.attr], hl_groups[hl.attr],
linenr - 1, linenr - 1,
hl.start, hl.start,
hl.final hl.final,
} }
end end
end end
@@ -152,8 +152,8 @@ local function highlight_line(line, linenr)
end end
local function highlight_man_page() local function highlight_man_page()
local mod = vim.api.nvim_buf_get_option(0, "modifiable") local mod = vim.api.nvim_buf_get_option(0, 'modifiable')
vim.api.nvim_buf_set_option(0, "modifiable", true) vim.api.nvim_buf_set_option(0, 'modifiable', true)
local lines = vim.api.nvim_buf_get_lines(0, 0, -1, false) local lines = vim.api.nvim_buf_get_lines(0, 0, -1, false)
for i, line in ipairs(lines) do for i, line in ipairs(lines) do
@@ -166,7 +166,7 @@ local function highlight_man_page()
end end
buf_hls = {} buf_hls = {}
vim.api.nvim_buf_set_option(0, "modifiable", mod) vim.api.nvim_buf_set_option(0, 'modifiable', mod)
end end
return { highlight_man_page = highlight_man_page } return { highlight_man_page = highlight_man_page }

View File

@@ -5,13 +5,17 @@ local F = {}
---@param a ---@param a
---@param b ---@param b
function F.if_nil(a, b) function F.if_nil(a, b)
if a == nil then return b end if a == nil then
return b
end
return a return a
end end
-- Use in combination with pcall -- Use in combination with pcall
function F.ok_or_nil(status, ...) function F.ok_or_nil(status, ...)
if not status then return end if not status then
return
end
return ... return ...
end end
@@ -29,7 +33,7 @@ end
--- like {...} except preserve the length explicitly --- like {...} except preserve the length explicitly
function F.pack_len(...) function F.pack_len(...)
return {n=select('#', ...), ...} return { n = select('#', ...), ... }
end end
--- like unpack() but use the length set by F.pack_len if present --- like unpack() but use the length set by F.pack_len if present

View File

@@ -40,26 +40,28 @@ local vim = assert(vim)
-- These are for loading runtime modules lazily since they aren't available in -- These are for loading runtime modules lazily since they aren't available in
-- the nvim binary as specified in executor.c -- the nvim binary as specified in executor.c
for k,v in pairs { for k, v in pairs({
treesitter=true; treesitter = true,
filetype = true; filetype = true,
F=true; F = true,
lsp=true; lsp = true,
highlight=true; highlight = true,
diagnostic=true; diagnostic = true,
keymap=true; keymap = true,
ui=true; ui = true,
} do vim._submodules[k] = v end }) do
vim._submodules[k] = v
end
vim.log = { vim.log = {
levels = { levels = {
TRACE = 0; TRACE = 0,
DEBUG = 1; DEBUG = 1,
INFO = 2; INFO = 2,
WARN = 3; WARN = 3,
ERROR = 4; ERROR = 4,
OFF = 5; OFF = 5,
} },
} }
-- Internal-only until comments in #8107 are addressed. -- Internal-only until comments in #8107 are addressed.
@@ -77,14 +79,14 @@ function vim._os_proc_info(pid)
if pid == nil or pid <= 0 or type(pid) ~= 'number' then if pid == nil or pid <= 0 or type(pid) ~= 'number' then
error('invalid pid') error('invalid pid')
end end
local cmd = { 'ps', '-p', pid, '-o', 'comm=', } local cmd = { 'ps', '-p', pid, '-o', 'comm=' }
local err, name = vim._system(cmd) local err, name = vim._system(cmd)
if 1 == err and vim.trim(name) == '' then if 1 == err and vim.trim(name) == '' then
return {} -- Process not found. return {} -- Process not found.
elseif 0 ~= err then elseif 0 ~= err then
error('command failed: '..vim.fn.string(cmd)) error('command failed: ' .. vim.fn.string(cmd))
end end
local _, ppid = vim._system({ 'ps', '-p', pid, '-o', 'ppid=', }) local _, ppid = vim._system({ 'ps', '-p', pid, '-o', 'ppid=' })
-- Remove trailing whitespace. -- Remove trailing whitespace.
name = vim.trim(name):gsub('^.*/', '') name = vim.trim(name):gsub('^.*/', '')
ppid = tonumber(ppid) or -1 ppid = tonumber(ppid) or -1
@@ -101,12 +103,12 @@ function vim._os_proc_children(ppid)
if ppid == nil or ppid <= 0 or type(ppid) ~= 'number' then if ppid == nil or ppid <= 0 or type(ppid) ~= 'number' then
error('invalid ppid') error('invalid ppid')
end end
local cmd = { 'pgrep', '-P', ppid, } local cmd = { 'pgrep', '-P', ppid }
local err, rv = vim._system(cmd) local err, rv = vim._system(cmd)
if 1 == err and vim.trim(rv) == '' then if 1 == err and vim.trim(rv) == '' then
return {} -- Process not found. return {} -- Process not found.
elseif 0 ~= err then elseif 0 ~= err then
error('command failed: '..vim.fn.string(cmd)) error('command failed: ' .. vim.fn.string(cmd))
end end
local children = {} local children = {}
for s in rv:gmatch('%S+') do for s in rv:gmatch('%S+') do
@@ -164,7 +166,7 @@ do
tdots, tick, got_line1, undo_started, trailing_nl = now, 0, false, false, false tdots, tick, got_line1, undo_started, trailing_nl = now, 0, false, false, false
end end
if #lines == 0 then if #lines == 0 then
lines = {''} lines = { '' }
end end
if #lines == 1 and lines[1] == '' and not is_last_chunk then if #lines == 1 and lines[1] == '' and not is_last_chunk then
-- An empty chunk can cause some edge cases in streamed pasting, -- An empty chunk can cause some edge cases in streamed pasting,
@@ -197,12 +199,12 @@ do
nchars = nchars + line:len() nchars = nchars + line:len()
end end
local row, col = unpack(vim.api.nvim_win_get_cursor(0)) local row, col = unpack(vim.api.nvim_win_get_cursor(0))
local bufline = vim.api.nvim_buf_get_lines(0, row-1, row, true)[1] local bufline = vim.api.nvim_buf_get_lines(0, row - 1, row, true)[1]
local firstline = lines[1] local firstline = lines[1]
firstline = bufline:sub(1, col)..firstline firstline = bufline:sub(1, col) .. firstline
lines[1] = firstline lines[1] = firstline
lines[#lines] = lines[#lines]..bufline:sub(col + nchars + 1, bufline:len()) lines[#lines] = lines[#lines] .. bufline:sub(col + nchars + 1, bufline:len())
vim.api.nvim_buf_set_lines(0, row-1, row, false, lines) vim.api.nvim_buf_set_lines(0, row - 1, row, false, lines)
elseif mode:find('^[nvV\22sS\19]') then -- Normal or Visual or Select mode elseif mode:find('^[nvV\22sS\19]') then -- Normal or Visual or Select mode
if mode:find('^n') then -- Normal mode if mode:find('^n') then -- Normal mode
-- When there was a trailing new line in the previous chunk, -- When there was a trailing new line in the previous chunk,
@@ -216,7 +218,7 @@ do
if mode:find('^[VS]') then -- linewise if mode:find('^[VS]') then -- linewise
if cursor_pos[2] < del_start[2] then -- replacing lines at eof if cursor_pos[2] < del_start[2] then -- replacing lines at eof
-- create a new line -- create a new line
vim.api.nvim_put({''}, 'l', true, true) vim.api.nvim_put({ '' }, 'l', true, true)
end end
vim.api.nvim_put(lines, 'c', false, false) vim.api.nvim_put(lines, 'c', false, false)
else else
@@ -240,7 +242,7 @@ do
vim.api.nvim_command(('echo "%s"'):format(dots)) vim.api.nvim_command(('echo "%s"'):format(dots))
end end
if is_last_chunk then if is_last_chunk then
vim.api.nvim_command('redraw'..(tick > 1 and '|echo ""' or '')) vim.api.nvim_command('redraw' .. (tick > 1 and '|echo ""' or ''))
end end
return true -- Paste will not continue if not returning `true`. return true -- Paste will not continue if not returning `true`.
end end
@@ -252,10 +254,12 @@ end
---@see |vim.schedule()| ---@see |vim.schedule()|
---@see |vim.in_fast_event()| ---@see |vim.in_fast_event()|
function vim.schedule_wrap(cb) function vim.schedule_wrap(cb)
return (function (...) return function(...)
local args = vim.F.pack_len(...) local args = vim.F.pack_len(...)
vim.schedule(function() cb(vim.F.unpack_len(args)) end) vim.schedule(function()
cb(vim.F.unpack_len(args))
end) end)
end
end end
-- vim.fn.{func}(...) -- vim.fn.{func}(...)
@@ -264,7 +268,7 @@ vim.fn = setmetatable({}, {
local _fn local _fn
if vim.api[key] ~= nil then if vim.api[key] ~= nil then
_fn = function() _fn = function()
error(string.format("Tried to call API function with vim.fn: use vim.api.%s instead", key)) error(string.format('Tried to call API function with vim.fn: use vim.api.%s instead', key))
end end
else else
_fn = function(...) _fn = function(...)
@@ -273,7 +277,7 @@ vim.fn = setmetatable({}, {
end end
t[key] = _fn t[key] = _fn
return _fn return _fn
end end,
}) })
vim.funcref = function(viml_func_name) vim.funcref = function(viml_func_name)
@@ -291,9 +295,9 @@ do
--@private --@private
local function make_dict_accessor(scope, handle) local function make_dict_accessor(scope, handle)
validate { validate({
scope = {scope, 's'}; scope = { scope, 's' },
} })
local mt = {} local mt = {}
function mt:__newindex(k, v) function mt:__newindex(k, v)
return vim._setvar(scope, handle or 0, k, v) return vim._setvar(scope, handle or 0, k, v)
@@ -355,10 +359,10 @@ function vim.region(bufnr, pos1, pos2, regtype, inclusive)
c2 = vim.str_byteindex(bufline, c2) c2 = vim.str_byteindex(bufline, c2)
end end
else else
c1 = (l == pos1[1]) and (pos1[2]) or 0 c1 = (l == pos1[1]) and pos1[2] or 0
c2 = (l == pos2[1]) and (pos2[2] + (inclusive and 1 or 0)) or -1 c2 = (l == pos2[1]) and (pos2[2] + (inclusive and 1 or 0)) or -1
end end
table.insert(region, l, {c1, c2}) table.insert(region, l, { c1, c2 })
end end
return region return region
end end
@@ -372,19 +376,22 @@ end
---@param timeout Number of milliseconds to wait before calling `fn` ---@param timeout Number of milliseconds to wait before calling `fn`
---@return timer luv timer object ---@return timer luv timer object
function vim.defer_fn(fn, timeout) function vim.defer_fn(fn, timeout)
vim.validate { fn = { fn, 'c', true}; } vim.validate({ fn = { fn, 'c', true } })
local timer = vim.loop.new_timer() local timer = vim.loop.new_timer()
timer:start(timeout, 0, vim.schedule_wrap(function() timer:start(
timeout,
0,
vim.schedule_wrap(function()
timer:stop() timer:stop()
timer:close() timer:close()
fn() fn()
end)) end)
)
return timer return timer
end end
--- Display a notification to the user. --- Display a notification to the user.
--- ---
--- This function can be overridden by plugins to display notifications using a --- This function can be overridden by plugins to display notifications using a
@@ -398,9 +405,9 @@ function vim.notify(msg, level, opts) -- luacheck: no unused args
if level == vim.log.levels.ERROR then if level == vim.log.levels.ERROR then
vim.api.nvim_err_writeln(msg) vim.api.nvim_err_writeln(msg)
elseif level == vim.log.levels.WARN then elseif level == vim.log.levels.WARN then
vim.api.nvim_echo({{msg, 'WarningMsg'}}, true, {}) vim.api.nvim_echo({ { msg, 'WarningMsg' } }, true, {})
else else
vim.api.nvim_echo({{msg}}, true, {}) vim.api.nvim_echo({ { msg } }, true, {})
end end
end end
@@ -453,10 +460,10 @@ function vim.on_key(fn, ns_id)
return #on_key_cbs return #on_key_cbs
end end
vim.validate { vim.validate({
fn = { fn, 'c', true}, fn = { fn, 'c', true },
ns_id = { ns_id, 'n', true } ns_id = { ns_id, 'n', true },
} })
if ns_id == nil or ns_id == 0 then if ns_id == nil or ns_id == 0 then
ns_id = vim.api.nvim_create_namespace('') ns_id = vim.api.nvim_create_namespace('')
@@ -481,10 +488,13 @@ function vim._on_key(char)
end end
if failed_ns_ids[1] then if failed_ns_ids[1] then
error(string.format( error(
string.format(
"Error executing 'on_key' with ns_ids '%s'\n Messages: %s", "Error executing 'on_key' with ns_ids '%s'\n Messages: %s",
table.concat(failed_ns_ids, ", "), table.concat(failed_ns_ids, ', '),
table.concat(failed_messages, "\n"))) table.concat(failed_messages, '\n')
)
)
end end
end end
@@ -511,8 +521,10 @@ function vim._expand_pat(pat, env)
-- Probably just need to do a smarter match than just `:match` -- Probably just need to do a smarter match than just `:match`
-- Get the last part of the pattern -- Get the last part of the pattern
local last_part = pat:match("[%w.:_%[%]'\"]+$") local last_part = pat:match('[%w.:_%[%]\'"]+$')
if not last_part then return {}, 0 end if not last_part then
return {}, 0
end
local parts, search_index = vim._expand_pat_get_parts(last_part) local parts, search_index = vim._expand_pat_get_parts(last_part)
@@ -529,7 +541,7 @@ function vim._expand_pat(pat, env)
-- Normally, we just have a string -- Normally, we just have a string
-- Just attempt to get the string directly from the environment -- Just attempt to get the string directly from the environment
if type(part) == "string" then if type(part) == 'string' then
key = part key = part
else else
-- However, sometimes you want to use a variable, and complete on it -- However, sometimes you want to use a variable, and complete on it
@@ -554,7 +566,7 @@ function vim._expand_pat(pat, env)
local field = rawget(final_env, key) local field = rawget(final_env, key)
if field == nil then if field == nil then
local mt = getmetatable(final_env) local mt = getmetatable(final_env)
if mt and type(mt.__index) == "table" then if mt and type(mt.__index) == 'table' then
field = rawget(mt.__index, key) field = rawget(mt.__index, key)
elseif final_env == vim and vim._submodules[key] then elseif final_env == vim and vim._submodules[key] then
field = vim[key] field = vim[key]
@@ -570,18 +582,18 @@ function vim._expand_pat(pat, env)
local keys = {} local keys = {}
---@private ---@private
local function insert_keys(obj) local function insert_keys(obj)
for k,_ in pairs(obj) do for k, _ in pairs(obj) do
if type(k) == "string" and string.sub(k,1,string.len(match_part)) == match_part then if type(k) == 'string' and string.sub(k, 1, string.len(match_part)) == match_part then
table.insert(keys,k) table.insert(keys, k)
end end
end end
end end
if type(final_env) == "table" then if type(final_env) == 'table' then
insert_keys(final_env) insert_keys(final_env)
end end
local mt = getmetatable(final_env) local mt = getmetatable(final_env)
if mt and type(mt.__index) == "table" then if mt and type(mt.__index) == 'table' then
insert_keys(mt.__index) insert_keys(mt.__index)
end end
if final_env == vim then if final_env == vim then
@@ -602,12 +614,12 @@ vim._expand_pat_get_parts = function(lua_string)
for idx = 1, #lua_string do for idx = 1, #lua_string do
local s = lua_string:sub(idx, idx) local s = lua_string:sub(idx, idx)
if not in_brackets and (s == "." or s == ":") then if not in_brackets and (s == '.' or s == ':') then
table.insert(parts, accumulator) table.insert(parts, accumulator)
accumulator = '' accumulator = ''
search_index = idx + 1 search_index = idx + 1
elseif s == "[" then elseif s == '[' then
in_brackets = true in_brackets = true
table.insert(parts, accumulator) table.insert(parts, accumulator)
@@ -619,7 +631,7 @@ vim._expand_pat_get_parts = function(lua_string)
in_brackets = false in_brackets = false
search_index = idx + 1 search_index = idx + 1
if string_char == "VAR" then if string_char == 'VAR' then
table.insert(parts, { accumulator }) table.insert(parts, { accumulator })
accumulator = '' accumulator = ''
@@ -631,7 +643,7 @@ vim._expand_pat_get_parts = function(lua_string)
if s == '"' or s == "'" then if s == '"' or s == "'" then
string_char = s string_char = s
elseif s ~= ' ' then elseif s ~= ' ' then
string_char = "VAR" string_char = 'VAR'
accumulator = s accumulator = s
end end
elseif string_char then elseif string_char then
@@ -649,7 +661,9 @@ vim._expand_pat_get_parts = function(lua_string)
end end
end end
parts = vim.tbl_filter(function(val) return #val > 0 end, parts) parts = vim.tbl_filter(function(val)
return #val > 0
end, parts)
return parts, search_index return parts, search_index
end end
@@ -677,20 +691,20 @@ function vim._cs_remote(rcid, server_addr, connect_error, args)
local function connection_failure_errmsg(consequence) local function connection_failure_errmsg(consequence)
local explanation local explanation
if server_addr == '' then if server_addr == '' then
explanation = "No server specified with --server" explanation = 'No server specified with --server'
else else
explanation = "Failed to connect to '" .. server_addr .. "'" explanation = "Failed to connect to '" .. server_addr .. "'"
if connect_error ~= "" then if connect_error ~= '' then
explanation = explanation .. ": " .. connect_error explanation = explanation .. ': ' .. connect_error
end end
end end
return "E247: " .. explanation .. ". " .. consequence return 'E247: ' .. explanation .. '. ' .. consequence
end end
local f_silent = false local f_silent = false
local f_tab = false local f_tab = false
local subcmd = string.sub(args[1],10) local subcmd = string.sub(args[1], 10)
if subcmd == 'tab' then if subcmd == 'tab' then
f_tab = true f_tab = true
elseif subcmd == 'silent' then elseif subcmd == 'silent' then
@@ -713,16 +727,18 @@ function vim._cs_remote(rcid, server_addr, connect_error, args)
print(vim.fn.rpcrequest(rcid, 'nvim_eval', args[2])) print(vim.fn.rpcrequest(rcid, 'nvim_eval', args[2]))
return { should_exit = true, tabbed = false } return { should_exit = true, tabbed = false }
elseif subcmd ~= '' then elseif subcmd ~= '' then
return { errmsg='Unknown option argument: ' .. args[1] } return { errmsg = 'Unknown option argument: ' .. args[1] }
end end
if rcid == 0 then if rcid == 0 then
if not f_silent then if not f_silent then
vim.notify(connection_failure_errmsg("Editing locally"), vim.log.levels.WARN) vim.notify(connection_failure_errmsg('Editing locally'), vim.log.levels.WARN)
end end
else else
local command = {} local command = {}
if f_tab then table.insert(command, 'tab') end if f_tab then
table.insert(command, 'tab')
end
table.insert(command, 'drop') table.insert(command, 'drop')
for i = 2, #args do for i = 2, #args do
table.insert(command, vim.fn.fnameescape(args[i])) table.insert(command, vim.fn.fnameescape(args[i]))
@@ -746,7 +762,7 @@ end
--- from. Defaults to "Nvim". --- from. Defaults to "Nvim".
function vim.deprecate(name, alternative, version, plugin) function vim.deprecate(name, alternative, version, plugin)
local message = name .. ' is deprecated' local message = name .. ' is deprecated'
plugin = plugin or "Nvim" plugin = plugin or 'Nvim'
message = alternative and (message .. ', use ' .. alternative .. ' instead.') or message message = alternative and (message .. ', use ' .. alternative .. ' instead.') or message
message = message .. ' See :h deprecated\nThis function will be removed in ' .. plugin .. ' version ' .. version message = message .. ' See :h deprecated\nThis function will be removed in ' .. plugin .. ' version ' .. version
vim.notify_once(message, vim.log.levels.WARN) vim.notify_once(message, vim.log.levels.WARN)

View File

@@ -3,7 +3,7 @@ local vim = assert(vim)
local pathtrails = {} local pathtrails = {}
vim._so_trails = {} vim._so_trails = {}
for s in (package.cpath..';'):gmatch('[^;]*;') do for s in (package.cpath .. ';'):gmatch('[^;]*;') do
s = s:sub(1, -2) -- Strip trailing semicolon s = s:sub(1, -2) -- Strip trailing semicolon
-- Find out path patterns. pathtrail should contain something like -- Find out path patterns. pathtrail should contain something like
-- /?.so, \?.dll. This allows not to bother determining what correct -- /?.so, \?.dll. This allows not to bother determining what correct
@@ -17,29 +17,29 @@ end
function vim._load_package(name) function vim._load_package(name)
local basename = name:gsub('%.', '/') local basename = name:gsub('%.', '/')
local paths = {"lua/"..basename..".lua", "lua/"..basename.."/init.lua"} local paths = { 'lua/' .. basename .. '.lua', 'lua/' .. basename .. '/init.lua' }
local found = vim.api.nvim__get_runtime(paths, false, {is_lua=true}) local found = vim.api.nvim__get_runtime(paths, false, { is_lua = true })
if #found > 0 then if #found > 0 then
local f, err = loadfile(found[1]) local f, err = loadfile(found[1])
return f or error(err) return f or error(err)
end end
local so_paths = {} local so_paths = {}
for _,trail in ipairs(vim._so_trails) do for _, trail in ipairs(vim._so_trails) do
local path = "lua"..trail:gsub('?', basename) -- so_trails contains a leading slash local path = 'lua' .. trail:gsub('?', basename) -- so_trails contains a leading slash
table.insert(so_paths, path) table.insert(so_paths, path)
end end
found = vim.api.nvim__get_runtime(so_paths, false, {is_lua=true}) found = vim.api.nvim__get_runtime(so_paths, false, { is_lua = true })
if #found > 0 then if #found > 0 then
-- Making function name in Lua 5.1 (see src/loadlib.c:mkfuncname) is -- Making function name in Lua 5.1 (see src/loadlib.c:mkfuncname) is
-- a) strip prefix up to and including the first dash, if any -- a) strip prefix up to and including the first dash, if any
-- b) replace all dots by underscores -- b) replace all dots by underscores
-- c) prepend "luaopen_" -- c) prepend "luaopen_"
-- So "foo-bar.baz" should result in "luaopen_bar_baz" -- So "foo-bar.baz" should result in "luaopen_bar_baz"
local dash = name:find("-", 1, true) local dash = name:find('-', 1, true)
local modname = dash and name:sub(dash + 1) or name local modname = dash and name:sub(dash + 1) or name
local f, err = package.loadlib(found[1], "luaopen_"..modname:gsub("%.", "_")) local f, err = package.loadlib(found[1], 'luaopen_' .. modname:gsub('%.', '_'))
return f or error(err) return f or error(err)
end end
return nil return nil
@@ -49,15 +49,15 @@ end
table.insert(package.loaders, 2, vim._load_package) table.insert(package.loaders, 2, vim._load_package)
-- builtin functions which always should be available -- builtin functions which always should be available
require'vim.shared' require('vim.shared')
vim._submodules = {inspect=true} vim._submodules = { inspect = true }
-- These are for loading runtime modules in the vim namespace lazily. -- These are for loading runtime modules in the vim namespace lazily.
setmetatable(vim, { setmetatable(vim, {
__index = function(t, key) __index = function(t, key)
if vim._submodules[key] then if vim._submodules[key] then
t[key] = require('vim.'..key) t[key] = require('vim.' .. key)
return t[key] return t[key]
elseif vim.startswith(key, 'uri_') then elseif vim.startswith(key, 'uri_') then
local val = require('vim.uri')[key] local val = require('vim.uri')[key]
@@ -67,7 +67,7 @@ setmetatable(vim, {
return t[key] return t[key]
end end
end end
end end,
}) })
--- <Docs described in |vim.empty_dict()| > --- <Docs described in |vim.empty_dict()| >
@@ -79,5 +79,5 @@ end
-- only on main thread: functions for interacting with editor state -- only on main thread: functions for interacting with editor state
if not vim.is_thread() then if not vim.is_thread() then
require'vim._editor' require('vim._editor')
end end

View File

@@ -13,7 +13,9 @@ local SET_TYPES = setmetatable({
local options_info = {} local options_info = {}
for _, v in pairs(a.nvim_get_all_options_info()) do for _, v in pairs(a.nvim_get_all_options_info()) do
options_info[v.name] = v options_info[v.name] = v
if v.shortname ~= "" then options_info[v.shortname] = v end if v.shortname ~= '' then
options_info[v.shortname] = v
end
end end
local get_scoped_options = function(scope) local get_scoped_options = function(scope)
@@ -27,19 +29,21 @@ local get_scoped_options = function(scope)
return result return result
end end
local buf_options = get_scoped_options("buf") local buf_options = get_scoped_options('buf')
local glb_options = get_scoped_options("global") local glb_options = get_scoped_options('global')
local win_options = get_scoped_options("win") local win_options = get_scoped_options('win')
local function make_meta_accessor(get, set, del, validator) local function make_meta_accessor(get, set, del, validator)
validator = validator or function() return true end validator = validator or function()
return true
end
validate { validate({
get = {get, 'f'}; get = { get, 'f' },
set = {set, 'f'}; set = { set, 'f' },
del = {del, 'f', true}; del = { del, 'f', true },
validator = {validator, 'f'}; validator = { validator, 'f' },
} })
local mt = {} local mt = {}
function mt:__newindex(k, v) function mt:__newindex(k, v)
@@ -73,7 +77,7 @@ end, vim.fn.setenv)
do -- buffer option accessor do -- buffer option accessor
local function new_buf_opt_accessor(bufnr) local function new_buf_opt_accessor(bufnr)
local function get(k) local function get(k)
if bufnr == nil and type(k) == "number" then if bufnr == nil and type(k) == 'number' then
return new_buf_opt_accessor(k) return new_buf_opt_accessor(k)
end end
@@ -103,7 +107,7 @@ end
do -- window option accessor do -- window option accessor
local function new_win_opt_accessor(winnr) local function new_win_opt_accessor(winnr)
local function get(k) local function get(k)
if winnr == nil and type(k) == "number" then if winnr == nil and type(k) == 'number' then
return new_win_opt_accessor(k) return new_win_opt_accessor(k)
end end
return a.nvim_win_get_option(winnr or 0, k) return a.nvim_win_get_option(winnr or 0, k)
@@ -131,17 +135,19 @@ end
-- vim global option -- vim global option
-- this ONLY sets the global option. like `setglobal` -- this ONLY sets the global option. like `setglobal`
vim.go = make_meta_accessor( vim.go = make_meta_accessor(function(k)
function(k) return a.nvim_get_option_value(k, {scope = "global"}) end, return a.nvim_get_option_value(k, { scope = 'global' })
function(k, v) return a.nvim_set_option_value(k, v, {scope = "global"}) end end, function(k, v)
) return a.nvim_set_option_value(k, v, { scope = 'global' })
end)
-- vim `set` style options. -- vim `set` style options.
-- it has no additional metamethod magic. -- it has no additional metamethod magic.
vim.o = make_meta_accessor( vim.o = make_meta_accessor(function(k)
function(k) return a.nvim_get_option_value(k, {}) end, return a.nvim_get_option_value(k, {})
function(k, v) return a.nvim_set_option_value(k, v, {}) end end, function(k, v)
) return a.nvim_set_option_value(k, v, {})
end)
---@brief [[ ---@brief [[
--- vim.opt, vim.opt_local and vim.opt_global implementation --- vim.opt, vim.opt_local and vim.opt_global implementation
@@ -154,7 +160,9 @@ vim.o = make_meta_accessor(
--- Preserves the order and does not mutate the original list --- Preserves the order and does not mutate the original list
local remove_duplicate_values = function(t) local remove_duplicate_values = function(t)
local result, seen = {}, {} local result, seen = {}, {}
if type(t) == "function" then error(debug.traceback("asdf")) end if type(t) == 'function' then
error(debug.traceback('asdf'))
end
for _, v in ipairs(t) do for _, v in ipairs(t) do
if not seen[v] then if not seen[v] then
table.insert(result, v) table.insert(result, v)
@@ -184,24 +192,28 @@ local OptionTypes = setmetatable({
MAP = 4, MAP = 4,
SET = 5, SET = 5,
}, { }, {
__index = function(_, k) error("Not a valid OptionType: " .. k) end, __index = function(_, k)
__newindex = function(_, k) error("Cannot set a new OptionType: " .. k) end, error('Not a valid OptionType: ' .. k)
end,
__newindex = function(_, k)
error('Cannot set a new OptionType: ' .. k)
end,
}) })
--- Convert a vimoption_T style dictionary to the correct OptionType associated with it. --- Convert a vimoption_T style dictionary to the correct OptionType associated with it.
---@return OptionType ---@return OptionType
local get_option_type = function(name, info) local get_option_type = function(name, info)
if info.type == "boolean" then if info.type == 'boolean' then
return OptionTypes.BOOLEAN return OptionTypes.BOOLEAN
elseif info.type == "number" then elseif info.type == 'number' then
return OptionTypes.NUMBER return OptionTypes.NUMBER
elseif info.type == "string" then elseif info.type == 'string' then
if not info.commalist and not info.flaglist then if not info.commalist and not info.flaglist then
return OptionTypes.STRING return OptionTypes.STRING
end end
if key_value_options[name] then if key_value_options[name] then
assert(info.commalist, "Must be a comma list to use key:value style") assert(info.commalist, 'Must be a comma list to use key:value style')
return OptionTypes.MAP return OptionTypes.MAP
end end
@@ -211,13 +223,12 @@ local get_option_type = function(name, info)
return OptionTypes.ARRAY return OptionTypes.ARRAY
end end
error("Fallthrough in OptionTypes") error('Fallthrough in OptionTypes')
else else
error("Not a known info.type:" .. info.type) error('Not a known info.type:' .. info.type)
end end
end end
-- Check whether the OptionTypes is allowed for vim.opt -- Check whether the OptionTypes is allowed for vim.opt
-- If it does not match, throw an error which indicates which option causes the error. -- If it does not match, throw an error which indicates which option causes the error.
local function assert_valid_value(name, value, types) local function assert_valid_value(name, value, types)
@@ -228,16 +239,18 @@ local function assert_valid_value(name, value, types)
end end
end end
error(string.format("Invalid option type '%s' for '%s', should be %s", type_of_value, name, table.concat(types, " or "))) error(
string.format("Invalid option type '%s' for '%s', should be %s", type_of_value, name, table.concat(types, ' or '))
)
end end
local valid_types = { local valid_types = {
[OptionTypes.BOOLEAN] = { "boolean" }, [OptionTypes.BOOLEAN] = { 'boolean' },
[OptionTypes.NUMBER] = { "number" }, [OptionTypes.NUMBER] = { 'number' },
[OptionTypes.STRING] = { "string" }, [OptionTypes.STRING] = { 'string' },
[OptionTypes.SET] = { "string", "table" }, [OptionTypes.SET] = { 'string', 'table' },
[OptionTypes.ARRAY] = { "string", "table" }, [OptionTypes.ARRAY] = { 'string', 'table' },
[OptionTypes.MAP] = { "string", "table" }, [OptionTypes.MAP] = { 'string', 'table' },
} }
--- Convert a lua value to a vimoption_T value --- Convert a lua value to a vimoption_T value
@@ -245,12 +258,20 @@ local convert_value_to_vim = (function()
-- Map of functions to take a Lua style value and convert to vimoption_T style value. -- Map of functions to take a Lua style value and convert to vimoption_T style value.
-- Each function takes (info, lua_value) -> vim_value -- Each function takes (info, lua_value) -> vim_value
local to_vim_value = { local to_vim_value = {
[OptionTypes.BOOLEAN] = function(_, value) return value end, [OptionTypes.BOOLEAN] = function(_, value)
[OptionTypes.NUMBER] = function(_, value) return value end, return value
[OptionTypes.STRING] = function(_, value) return value end, end,
[OptionTypes.NUMBER] = function(_, value)
return value
end,
[OptionTypes.STRING] = function(_, value)
return value
end,
[OptionTypes.SET] = function(info, value) [OptionTypes.SET] = function(info, value)
if type(value) == "string" then return value end if type(value) == 'string' then
return value
end
if info.flaglist and info.commalist then if info.flaglist and info.commalist then
local keys = {} local keys = {}
@@ -261,7 +282,7 @@ local convert_value_to_vim = (function()
end end
table.sort(keys) table.sort(keys)
return table.concat(keys, ",") return table.concat(keys, ',')
else else
local result = '' local result = ''
for k, v in pairs(value) do for k, v in pairs(value) do
@@ -275,23 +296,27 @@ local convert_value_to_vim = (function()
end, end,
[OptionTypes.ARRAY] = function(info, value) [OptionTypes.ARRAY] = function(info, value)
if type(value) == "string" then return value end if type(value) == 'string' then
return value
end
if not info.allows_duplicates then if not info.allows_duplicates then
value = remove_duplicate_values(value) value = remove_duplicate_values(value)
end end
return table.concat(value, ",") return table.concat(value, ',')
end, end,
[OptionTypes.MAP] = function(_, value) [OptionTypes.MAP] = function(_, value)
if type(value) == "string" then return value end if type(value) == 'string' then
return value
end
local result = {} local result = {}
for opt_key, opt_value in pairs(value) do for opt_key, opt_value in pairs(value) do
table.insert(result, string.format("%s:%s", opt_key, opt_value)) table.insert(result, string.format('%s:%s', opt_key, opt_value))
end end
table.sort(result) table.sort(result)
return table.concat(result, ",") return table.concat(result, ',')
end, end,
} }
@@ -312,12 +337,18 @@ local convert_value_to_lua = (function()
-- Map of OptionType to functions that take vimoption_T values and convert to lua values. -- Map of OptionType to functions that take vimoption_T values and convert to lua values.
-- Each function takes (info, vim_value) -> lua_value -- Each function takes (info, vim_value) -> lua_value
local to_lua_value = { local to_lua_value = {
[OptionTypes.BOOLEAN] = function(_, value) return value end, [OptionTypes.BOOLEAN] = function(_, value)
[OptionTypes.NUMBER] = function(_, value) return value end, return value
[OptionTypes.STRING] = function(_, value) return value end, end,
[OptionTypes.NUMBER] = function(_, value)
return value
end,
[OptionTypes.STRING] = function(_, value)
return value
end,
[OptionTypes.ARRAY] = function(info, value) [OptionTypes.ARRAY] = function(info, value)
if type(value) == "table" then if type(value) == 'table' then
if not info.allows_duplicates then if not info.allows_duplicates then
value = remove_duplicate_values(value) value = remove_duplicate_values(value)
end end
@@ -332,41 +363,43 @@ local convert_value_to_lua = (function()
end end
-- Handles unescaped commas in a list. -- Handles unescaped commas in a list.
if string.find(value, ",,,") then if string.find(value, ',,,') then
local comma_split = vim.split(value, ",,,") local comma_split = vim.split(value, ',,,')
local left = comma_split[1] local left = comma_split[1]
local right = comma_split[2] local right = comma_split[2]
local result = {} local result = {}
vim.list_extend(result, vim.split(left, ",")) vim.list_extend(result, vim.split(left, ','))
table.insert(result, ",") table.insert(result, ',')
vim.list_extend(result, vim.split(right, ",")) vim.list_extend(result, vim.split(right, ','))
table.sort(result) table.sort(result)
return result return result
end end
if string.find(value, ",^,,", 1, true) then if string.find(value, ',^,,', 1, true) then
local comma_split = vim.split(value, ",^,,", true) local comma_split = vim.split(value, ',^,,', true)
local left = comma_split[1] local left = comma_split[1]
local right = comma_split[2] local right = comma_split[2]
local result = {} local result = {}
vim.list_extend(result, vim.split(left, ",")) vim.list_extend(result, vim.split(left, ','))
table.insert(result, "^,") table.insert(result, '^,')
vim.list_extend(result, vim.split(right, ",")) vim.list_extend(result, vim.split(right, ','))
table.sort(result) table.sort(result)
return result return result
end end
return vim.split(value, ",") return vim.split(value, ',')
end, end,
[OptionTypes.SET] = function(info, value) [OptionTypes.SET] = function(info, value)
if type(value) == "table" then return value end if type(value) == 'table' then
return value
end
-- Empty strings mean that there is nothing there, -- Empty strings mean that there is nothing there,
-- so empty table should be returned. -- so empty table should be returned.
@@ -374,10 +407,10 @@ local convert_value_to_lua = (function()
return {} return {}
end end
assert(info.flaglist, "That is the only one I know how to handle") assert(info.flaglist, 'That is the only one I know how to handle')
if info.flaglist and info.commalist then if info.flaglist and info.commalist then
local split_value = vim.split(value, ",") local split_value = vim.split(value, ',')
local result = {} local result = {}
for _, v in ipairs(split_value) do for _, v in ipairs(split_value) do
result[v] = true result[v] = true
@@ -395,15 +428,17 @@ local convert_value_to_lua = (function()
end, end,
[OptionTypes.MAP] = function(info, raw_value) [OptionTypes.MAP] = function(info, raw_value)
if type(raw_value) == "table" then return raw_value end if type(raw_value) == 'table' then
return raw_value
end
assert(info.commalist, "Only commas are supported currently") assert(info.commalist, 'Only commas are supported currently')
local result = {} local result = {}
local comma_split = vim.split(raw_value, ",") local comma_split = vim.split(raw_value, ',')
for _, key_value_str in ipairs(comma_split) do for _, key_value_str in ipairs(comma_split) do
local key, value = unpack(vim.split(key_value_str, ":")) local key, value = unpack(vim.split(key_value_str, ':'))
key = vim.trim(key) key = vim.trim(key)
result[key] = value result[key] = value
@@ -443,17 +478,21 @@ local prepend_value = (function()
end, end,
[OptionTypes.MAP] = function(left, right) [OptionTypes.MAP] = function(left, right)
return vim.tbl_extend("force", left, right) return vim.tbl_extend('force', left, right)
end, end,
[OptionTypes.SET] = function(left, right) [OptionTypes.SET] = function(left, right)
return vim.tbl_extend("force", left, right) return vim.tbl_extend('force', left, right)
end, end,
} }
return function(name, info, current, new) return function(name, info, current, new)
return value_mutator( return value_mutator(
name, info, convert_value_to_lua(name, info, current), convert_value_to_lua(name, info, new), methods name,
info,
convert_value_to_lua(name, info, current),
convert_value_to_lua(name, info, new),
methods
) )
end end
end)() end)()
@@ -478,17 +517,21 @@ local add_value = (function()
end, end,
[OptionTypes.MAP] = function(left, right) [OptionTypes.MAP] = function(left, right)
return vim.tbl_extend("force", left, right) return vim.tbl_extend('force', left, right)
end, end,
[OptionTypes.SET] = function(left, right) [OptionTypes.SET] = function(left, right)
return vim.tbl_extend("force", left, right) return vim.tbl_extend('force', left, right)
end, end,
} }
return function(name, info, current, new) return function(name, info, current, new)
return value_mutator( return value_mutator(
name, info, convert_value_to_lua(name, info, current), convert_value_to_lua(name, info, new), methods name,
info,
convert_value_to_lua(name, info, current),
convert_value_to_lua(name, info, new),
methods
) )
end end
end)() end)()
@@ -518,11 +561,11 @@ local remove_value = (function()
end, end,
[OptionTypes.STRING] = function() [OptionTypes.STRING] = function()
error("Subtraction not supported for strings.") error('Subtraction not supported for strings.')
end, end,
[OptionTypes.ARRAY] = function(left, right) [OptionTypes.ARRAY] = function(left, right)
if type(right) == "string" then if type(right) == 'string' then
remove_one_item(left, right) remove_one_item(left, right)
else else
for _, v in ipairs(right) do for _, v in ipairs(right) do
@@ -534,7 +577,7 @@ local remove_value = (function()
end, end,
[OptionTypes.MAP] = function(left, right) [OptionTypes.MAP] = function(left, right)
if type(right) == "string" then if type(right) == 'string' then
left[right] = nil left[right] = nil
else else
for _, v in ipairs(right) do for _, v in ipairs(right) do
@@ -546,7 +589,7 @@ local remove_value = (function()
end, end,
[OptionTypes.SET] = function(left, right) [OptionTypes.SET] = function(left, right)
if type(right) == "string" then if type(right) == 'string' then
left[right] = nil left[right] = nil
else else
for _, v in ipairs(right) do for _, v in ipairs(right) do
@@ -567,9 +610,9 @@ local create_option_metatable = function(set_type)
local set_mt, option_mt local set_mt, option_mt
local make_option = function(name, value) local make_option = function(name, value)
local info = assert(options_info[name], "Not a valid option name: " .. name) local info = assert(options_info[name], 'Not a valid option name: ' .. name)
if type(value) == "table" and getmetatable(value) == option_mt then if type(value) == 'table' and getmetatable(value) == option_mt then
assert(name == value._name, "must be the same value, otherwise that's weird.") assert(name == value._name, "must be the same value, otherwise that's weird.")
value = value._value value = value._value
@@ -584,9 +627,9 @@ local create_option_metatable = function(set_type)
local scope local scope
if set_type == SET_TYPES.GLOBAL then if set_type == SET_TYPES.GLOBAL then
scope = "global" scope = 'global'
elseif set_type == SET_TYPES.LOCAL then elseif set_type == SET_TYPES.LOCAL then
scope = "local" scope = 'local'
end end
option_mt = { option_mt = {
@@ -594,7 +637,7 @@ local create_option_metatable = function(set_type)
-- opt[my_option] = value -- opt[my_option] = value
_set = function(self) _set = function(self)
local value = convert_value_to_vim(self._name, self._info, self._value) local value = convert_value_to_vim(self._name, self._info, self._value)
a.nvim_set_option_value(self._name, value, {scope = scope}) a.nvim_set_option_value(self._name, value, { scope = scope })
return self return self
end, end,
@@ -625,13 +668,13 @@ local create_option_metatable = function(set_type)
__sub = function(self, right) __sub = function(self, right)
return make_option(self._name, remove_value(self._name, self._info, self._value, right)) return make_option(self._name, remove_value(self._name, self._info, self._value, right))
end end,
} }
option_mt.__index = option_mt option_mt.__index = option_mt
set_mt = { set_mt = {
__index = function(_, k) __index = function(_, k)
return make_option(k, a.nvim_get_option_value(k, {scope = scope})) return make_option(k, a.nvim_get_option_value(k, { scope = scope }))
end, end,
__newindex = function(_, k, v) __newindex = function(_, k, v)

View File

@@ -7,6 +7,6 @@
local lua_version = _VERSION:sub(-3) local lua_version = _VERSION:sub(-3)
if lua_version >= "5.2" then if lua_version >= '5.2' then
unpack = table.unpack -- luacheck: ignore 121 143 unpack = table.unpack -- luacheck: ignore 121 143
end end

File diff suppressed because it is too large Load Diff

File diff suppressed because it is too large Load Diff

View File

@@ -9,7 +9,7 @@ local function getlines(bufnr, start_lnum, end_lnum, opts)
local lines = vim.api.nvim_buf_get_lines(bufnr, start_lnum - 1, end_lnum, false) local lines = vim.api.nvim_buf_get_lines(bufnr, start_lnum - 1, end_lnum, false)
opts = opts or {} opts = opts or {}
return opts.concat and (table.concat(lines) or "") or lines return opts.concat and (table.concat(lines) or '') or lines
end end
---@private ---@private
@@ -35,23 +35,23 @@ function M.bindzone(path, bufnr) end
function M.btm(bufnr) function M.btm(bufnr)
if vim.g.dosbatch_syntax_for_btm and vim.g.dosbatch_syntax_for_btm ~= 0 then if vim.g.dosbatch_syntax_for_btm and vim.g.dosbatch_syntax_for_btm ~= 0 then
vim.bo[bufnr].filetype = "dosbatch" vim.bo[bufnr].filetype = 'dosbatch'
else else
vim.bo[bufnr].filetype = "btm" vim.bo[bufnr].filetype = 'btm'
end end
end end
-- Returns true if file content looks like RAPID -- Returns true if file content looks like RAPID
local function is_rapid(bufnr, extension) local function is_rapid(bufnr, extension)
if extension == "cfg" then if extension == 'cfg' then
local line = getlines(bufnr, 1):lower() local line = getlines(bufnr, 1):lower()
return findany(line, { "eio:cfg", "mmc:cfg", "moc:cfg", "proc:cfg", "sio:cfg", "sys:cfg" }) return findany(line, { 'eio:cfg', 'mmc:cfg', 'moc:cfg', 'proc:cfg', 'sio:cfg', 'sys:cfg' })
end end
local first = "^%s*module%s+%S+%s*" local first = '^%s*module%s+%S+%s*'
-- Called from mod, prg or sys functions -- Called from mod, prg or sys functions
for _, line in ipairs(getlines(bufnr, 1, -1)) do for _, line in ipairs(getlines(bufnr, 1, -1)) do
if not line:find("^%s*$") then if not line:find('^%s*$') then
return findany(line:lower(), { "^%s*%%%%%%", first .. "(", first .. "$" }) return findany(line:lower(), { '^%s*%%%%%%', first .. '(', first .. '$' })
end end
end end
-- Only found blank lines -- Only found blank lines
@@ -61,10 +61,10 @@ end
function M.cfg(bufnr) function M.cfg(bufnr)
if vim.g.filetype_cfg then if vim.g.filetype_cfg then
vim.bo[bufnr].filetype = vim.g.filetype_cfg vim.bo[bufnr].filetype = vim.g.filetype_cfg
elseif is_rapid(bufnr, "cfg") then elseif is_rapid(bufnr, 'cfg') then
vim.bo[bufnr].filetype = "rapid" vim.bo[bufnr].filetype = 'rapid'
else else
vim.bo[bufnr].filetype = "cfg" vim.bo[bufnr].filetype = 'cfg'
end end
end end
@@ -85,23 +85,23 @@ function M.e(path, bufnr) end
-- If not found, assume SGML. -- If not found, assume SGML.
function M.ent(bufnr) function M.ent(bufnr)
for _, line in ipairs(getlines(bufnr, 1, 5)) do for _, line in ipairs(getlines(bufnr, 1, 5)) do
if line:find("^%s*[#{]") then if line:find('^%s*[#{]') then
vim.bo[bufnr].filetype = "cl" vim.bo[bufnr].filetype = 'cl'
return return
elseif not line:find("^%s*$") then elseif not line:find('^%s*$') then
-- Not a blank line, not a comment, and not a block start, -- Not a blank line, not a comment, and not a block start,
-- so doesn't look like valid cl code. -- so doesn't look like valid cl code.
break break
end end
end end
vim.bo[bufnr].filetype = "dtd" vim.bo[bufnr].filetype = 'dtd'
end end
function M.euphoria(bufnr) function M.euphoria(bufnr)
if vim.g.filetype_euphoria then if vim.g.filetype_euphoria then
vim.bo[bufnr].filetype = vim.g.filetype_euphoria vim.bo[bufnr].filetype = vim.g.filetype_euphoria
else else
vim.bo[bufnr].filetype = "euphoria3" vim.bo[bufnr].filetype = 'euphoria3'
end end
end end
@@ -111,12 +111,12 @@ function M.ex(bufnr)
else else
for _, line in ipairs(getlines(bufnr, 1, 100)) do for _, line in ipairs(getlines(bufnr, 1, 100)) do
-- TODO: in the Vim regex, \> is used to match the end of the word, can this be omitted? -- TODO: in the Vim regex, \> is used to match the end of the word, can this be omitted?
if findany(line, { "^%-%-", "^ifdef", "^include" }) then if findany(line, { '^%-%-', '^ifdef', '^include' }) then
vim.bo[bufnr].filetype = "euphoria3" vim.bo[bufnr].filetype = 'euphoria3'
return return
end end
end end
vim.bo[bufnr].filetype = "elixir" vim.bo[bufnr].filetype = 'elixir'
end end
end end
@@ -126,10 +126,10 @@ end
function M.foam(bufnr) function M.foam(bufnr)
local foam_file = false local foam_file = false
for _, line in ipairs(getlines(bufnr, 1, 15)) do for _, line in ipairs(getlines(bufnr, 1, 15)) do
if line:find("^FoamFile") then if line:find('^FoamFile') then
foam_file = true foam_file = true
elseif foam_file and line:find("^%s*object") then elseif foam_file and line:find('^%s*object') then
vim.bo[bufnr].filetype = "foam" vim.bo[bufnr].filetype = 'foam'
return return
end end
end end
@@ -141,10 +141,10 @@ function M.frm(bufnr)
else else
-- Always ignore case -- Always ignore case
local lines = getlines(bufnr, 1, 5, { concat = true }):lower() local lines = getlines(bufnr, 1, 5, { concat = true }):lower()
if findany(lines, { "vb_name", "begin vb%.form", "begin vb%.mdiform" }) then if findany(lines, { 'vb_name', 'begin vb%.form', 'begin vb%.mdiform' }) then
vim.bo[bufnr].filetype = "vb" vim.bo[bufnr].filetype = 'vb'
else else
vim.bo[bufnr].filetype = "form" vim.bo[bufnr].filetype = 'form'
end end
end end
end end
@@ -153,21 +153,21 @@ function M.fs(path, bufnr) end
function M.header(bufnr) function M.header(bufnr)
for _, line in ipairs(getlines(bufnr, 1, 200)) do for _, line in ipairs(getlines(bufnr, 1, 200)) do
if findany(line, { "^@interface", "^@end", "^@class" }) then if findany(line, { '^@interface', '^@end', '^@class' }) then
if vim.g.c_syntax_for_h then if vim.g.c_syntax_for_h then
vim.bo[bufnr].filetype = "objc" vim.bo[bufnr].filetype = 'objc'
else else
vim.bo[bufnr].filetype = "objcpp" vim.bo[bufnr].filetype = 'objcpp'
end end
return return
end end
end end
if vim.g.c_syntax_for_h then if vim.g.c_syntax_for_h then
vim.bo[bufnr].filetype = "c" vim.bo[bufnr].filetype = 'c'
elseif vim.g.ch_syntax_for_h then elseif vim.g.ch_syntax_for_h then
vim.bo[bufnr].filetype = "ch" vim.bo[bufnr].filetype = 'ch'
else else
vim.bo[bufnr].filetype = "cpp" vim.bo[bufnr].filetype = 'cpp'
end end
end end
@@ -176,22 +176,22 @@ function M.idl(bufnr)
-- Always ignore case -- Always ignore case
line = line:lower() line = line:lower()
if findany(line, { '^%s*import%s+"unknwn"%.idl', '^%s*import%s+"objidl"%.idl' }) then if findany(line, { '^%s*import%s+"unknwn"%.idl', '^%s*import%s+"objidl"%.idl' }) then
vim.bo[bufnr].filetype = "msidl" vim.bo[bufnr].filetype = 'msidl'
return return
end end
end end
vim.bo[bufnr].filetype = "idl" vim.bo[bufnr].filetype = 'idl'
end end
function M.inc(path, bufnr) end function M.inc(path, bufnr) end
function M.inp(bufnr) function M.inp(bufnr)
if getlines(bufnr, 1):find("^%*") then if getlines(bufnr, 1):find('^%*') then
vim.bo[bufnr].filetype = "abaqus" vim.bo[bufnr].filetype = 'abaqus'
else else
for _, line in ipairs(getlines(bufnr, 1, 500)) do for _, line in ipairs(getlines(bufnr, 1, 500)) do
if line:lower():find("^header surface data") then if line:lower():find('^header surface data') then
vim.bo[bufnr].filetype = "trasys" vim.bo[bufnr].filetype = 'trasys'
return return
end end
end end
@@ -208,32 +208,32 @@ function M.m(path, bufnr) end
-- MS message text files use ';', Sendmail files use '#' or 'dnl' -- MS message text files use ';', Sendmail files use '#' or 'dnl'
function M.mc(bufnr) function M.mc(bufnr)
for _, line in ipairs(getlines(bufnr, 1, 20)) do for _, line in ipairs(getlines(bufnr, 1, 20)) do
if findany(line:lower(), { "^%s*#", "^%s*dnl" }) then if findany(line:lower(), { '^%s*#', '^%s*dnl' }) then
-- Sendmail .mc file -- Sendmail .mc file
vim.bo[bufnr].filetype = "m4" vim.bo[bufnr].filetype = 'm4'
return return
elseif line:find("^%s*;") then elseif line:find('^%s*;') then
vim.bo[bufnr].filetype = "msmessages" vim.bo[bufnr].filetype = 'msmessages'
return return
end end
end end
-- Default: Sendmail .mc file -- Default: Sendmail .mc file
vim.bo[bufnr].filetype = "m4" vim.bo[bufnr].filetype = 'm4'
end end
function M.mm(path, bufnr) end function M.mm(path, bufnr) end
function M.mms(bufnr) function M.mms(bufnr)
for _, line in ipairs(getlines(bufnr, 1, 20)) do for _, line in ipairs(getlines(bufnr, 1, 20)) do
if findany(line, { "^%s*%%", "^%s*//", "^%*" }) then if findany(line, { '^%s*%%', '^%s*//', '^%*' }) then
vim.bo[bufnr].filetype = "mmix" vim.bo[bufnr].filetype = 'mmix'
return return
elseif line:find("^%s*#") then elseif line:find('^%s*#') then
vim.bo[bufnr].filetype = "make" vim.bo[bufnr].filetype = 'make'
return return
end end
end end
vim.bo[bufnr].filetype = "mmix" vim.bo[bufnr].filetype = 'mmix'
end end
function M.mod(path, bufnr) end function M.mod(path, bufnr) end
@@ -242,8 +242,8 @@ function M.mod(path, bufnr) end
-- that case it is probably an nroff file: 'filetype' is set and 1 is returned. -- that case it is probably an nroff file: 'filetype' is set and 1 is returned.
function M.nroff(bufnr) function M.nroff(bufnr)
for _, line in ipairs(getlines(bufnr, 1, 5)) do for _, line in ipairs(getlines(bufnr, 1, 5)) do
if line:find("^%.") then if line:find('^%.') then
vim.bo[bufnr].filetype = "nroff" vim.bo[bufnr].filetype = 'nroff'
return 1 return 1
end end
end end
@@ -264,10 +264,10 @@ function M.progress_cweb(bufnr)
if vim.g.filetype_w then if vim.g.filetype_w then
vim.bo[bufnr].filetype = vim.g.filetype_w vim.bo[bufnr].filetype = vim.g.filetype_w
else else
if getlines(bufnr, 1):find("^&ANALYZE") or getlines(bufnr, 3):find("^&GLOBAL%-DEFINE") then if getlines(bufnr, 1):find('^&ANALYZE') or getlines(bufnr, 3):find('^&GLOBAL%-DEFINE') then
vim.bo[bufnr].filetype = "progress" vim.bo[bufnr].filetype = 'progress'
else else
vim.bo[bufnr].filetype = "cweb" vim.bo[bufnr].filetype = 'cweb'
end end
end end
end end
@@ -280,20 +280,20 @@ function M.r(bufnr)
local lines = getlines(bufnr, 1, 50) local lines = getlines(bufnr, 1, 50)
-- TODO: \< / \> which match the beginning / end of a word -- TODO: \< / \> which match the beginning / end of a word
-- Rebol is easy to recognize, check for that first -- Rebol is easy to recognize, check for that first
if table.concat(lines):lower():find("rebol") then if table.concat(lines):lower():find('rebol') then
vim.bo[bufnr].filetype = "rebol" vim.bo[bufnr].filetype = 'rebol'
return return
end end
for _, line in ipairs(lines) do for _, line in ipairs(lines) do
-- R has # comments -- R has # comments
if line:find("^%s*#") then if line:find('^%s*#') then
vim.bo[bufnr].filetype = "r" vim.bo[bufnr].filetype = 'r'
return return
end end
-- Rexx has /* comments */ -- Rexx has /* comments */
if line:find("^%s*/%*") then if line:find('^%s*/%*') then
vim.bo[bufnr].filetype = "rexx" vim.bo[bufnr].filetype = 'rexx'
return return
end end
end end
@@ -303,14 +303,14 @@ function M.r(bufnr)
vim.bo[bufnr].filetype = vim.g.filetype_r vim.bo[bufnr].filetype = vim.g.filetype_r
else else
-- Rexx used to be the default, but R appears to be much more popular. -- Rexx used to be the default, but R appears to be much more popular.
vim.bo[bufnr].filetype = "r" vim.bo[bufnr].filetype = 'r'
end end
end end
function M.redif(bufnr) function M.redif(bufnr)
for _, line in ipairs(getlines(bufnr, 1, 5)) do for _, line in ipairs(getlines(bufnr, 1, 5)) do
if line:lower():find("^template%-type:") then if line:lower():find('^template%-type:') then
vim.bo[bufnr].filetype = "redif" vim.bo[bufnr].filetype = 'redif'
end end
end end
end end
@@ -321,24 +321,29 @@ function M.rules(path, bufnr) end
-- detection between scala and SuperCollider -- detection between scala and SuperCollider
function M.sc(bufnr) function M.sc(bufnr)
for _, line in ipairs(getlines(bufnr, 1, 25)) do for _, line in ipairs(getlines(bufnr, 1, 25)) do
if findany(line, { "[A-Za-z0-9]*%s:%s[A-Za-z0-9]", "var%s<", "classvar%s<", "%^this.*", "|%w*|", "%+%s%w*%s{", "%*ar%s" }) then if
vim.bo[bufnr].filetype = "supercollider" findany(
line,
{ '[A-Za-z0-9]*%s:%s[A-Za-z0-9]', 'var%s<', 'classvar%s<', '%^this.*', '|%w*|', '%+%s%w*%s{', '%*ar%s' }
)
then
vim.bo[bufnr].filetype = 'supercollider'
return return
end end
end end
vim.bo[bufnr].filetype = "scala" vim.bo[bufnr].filetype = 'scala'
end end
-- This function checks the first line of file extension "scd" to resolve -- This function checks the first line of file extension "scd" to resolve
-- detection between scdoc and SuperCollider -- detection between scdoc and SuperCollider
function M.scd(bufnr) function M.scd(bufnr)
local first = "^%S+%(%d[0-9A-Za-z]*%)" local first = '^%S+%(%d[0-9A-Za-z]*%)'
local opt = [[%s+"[^"]*"]] local opt = [[%s+"[^"]*"]]
local line = getlines(bufnr, 1) local line = getlines(bufnr, 1)
if findany(line, { first .. "$", first .. opt .. "$", first .. opt .. opt .. "$" }) then if findany(line, { first .. '$', first .. opt .. '$', first .. opt .. opt .. '$' }) then
vim.bo[bufnr].filetype = "scdoc" vim.bo[bufnr].filetype = 'scdoc'
else else
vim.bo[bufnr].filetype = "supercollider" vim.bo[bufnr].filetype = 'supercollider'
end end
end end
@@ -350,7 +355,7 @@ function M.sql(bufnr)
if vim.g.filetype_sql then if vim.g.filetype_sql then
vim.bo[bufnr].filetype = vim.g.filetype_sql vim.bo[bufnr].filetype = vim.g.filetype_sql
else else
vim.bo[bufnr].filetype = "sql" vim.bo[bufnr].filetype = 'sql'
end end
end end
@@ -365,46 +370,46 @@ function M.tf(bufnr)
for _, line in ipairs(getlines(bufnr, 1, -1)) do for _, line in ipairs(getlines(bufnr, 1, -1)) do
-- Assume terraform file on a non-empty line (not whitespace-only) -- Assume terraform file on a non-empty line (not whitespace-only)
-- and when the first non-whitespace character is not a ; or / -- and when the first non-whitespace character is not a ; or /
if not line:find("^%s*$") and not line:find("^%s*[;/]") then if not line:find('^%s*$') and not line:find('^%s*[;/]') then
vim.bo[bufnr].filetype = "terraform" vim.bo[bufnr].filetype = 'terraform'
return return
end end
end end
vim.bo[bufnr].filetype = "tf" vim.bo[bufnr].filetype = 'tf'
end end
function M.xml(bufnr) function M.xml(bufnr)
for _, line in ipairs(getlines(bufnr, 1, 100)) do for _, line in ipairs(getlines(bufnr, 1, 100)) do
line = line:lower() line = line:lower()
local is_docbook4 = line:find("<!doctype.*docbook") local is_docbook4 = line:find('<!doctype.*docbook')
local is_docbook5 = line:find([[ xmlns="http://docbook.org/ns/docbook"]]) local is_docbook5 = line:find([[ xmlns="http://docbook.org/ns/docbook"]])
if is_docbook4 or is_docbook5 then if is_docbook4 or is_docbook5 then
vim.b[bufnr].docbk_type = "xml" vim.b[bufnr].docbk_type = 'xml'
vim.b[bufnr].docbk_ver = is_docbook4 and 4 or 5 vim.b[bufnr].docbk_ver = is_docbook4 and 4 or 5
vim.bo[bufnr].filetype = "docbk" vim.bo[bufnr].filetype = 'docbk'
return return
end end
if line:find([[xmlns:xbl="http://www.mozilla.org/xbl"]]) then if line:find([[xmlns:xbl="http://www.mozilla.org/xbl"]]) then
vim.bo[bufnr].filetype = "xbl" vim.bo[bufnr].filetype = 'xbl'
return return
end end
end end
vim.bo[bufnr].filetype = "xml" vim.bo[bufnr].filetype = 'xml'
end end
function M.y(bufnr) function M.y(bufnr)
for _, line in ipairs(getlines(bufnr, 1, 100)) do for _, line in ipairs(getlines(bufnr, 1, 100)) do
if line:find("^%s*%%") then if line:find('^%s*%%') then
vim.bo[bufnr].filetype = "yacc" vim.bo[bufnr].filetype = 'yacc'
return return
end end
-- TODO: in the Vim regex, \> is used to match the end of the word after "class", -- TODO: in the Vim regex, \> is used to match the end of the word after "class",
-- can this be omitted? -- can this be omitted?
if findany(line, { "^%s*#", "^%class", "^%s*#%s*include" }) then if findany(line, { '^%s*#', '^%class', '^%s*#%s*include' }) then
vim.bo[bufnr].filetype = "racc" vim.bo[bufnr].filetype = 'racc'
end end
end end
vim.bo[bufnr].filetype = "yacc" vim.bo[bufnr].filetype = 'yacc'
end end
-- luacheck: pop -- luacheck: pop

View File

@@ -14,14 +14,14 @@ function M.create(higroup, hi_info, default)
local options = {} local options = {}
-- TODO: Add validation -- TODO: Add validation
for k, v in pairs(hi_info) do for k, v in pairs(hi_info) do
table.insert(options, string.format("%s=%s", k, v)) table.insert(options, string.format('%s=%s', k, v))
end end
vim.cmd(string.format([[highlight %s %s %s]], default and "default" or "", higroup, table.concat(options, " "))) vim.cmd(string.format([[highlight %s %s %s]], default and 'default' or '', higroup, table.concat(options, ' ')))
end end
---@private ---@private
function M.link(higroup, link_to, force) function M.link(higroup, link_to, force)
vim.cmd(string.format([[highlight%s link %s %s]], force and "!" or " default", higroup, link_to)) vim.cmd(string.format([[highlight%s link %s %s]], force and '!' or ' default', higroup, link_to))
end end
--- Highlight range between two positions --- Highlight range between two positions
@@ -37,7 +37,7 @@ end
-- - priority number indicating priority of highlight (default priorities.user) -- - priority number indicating priority of highlight (default priorities.user)
function M.range(bufnr, ns, higroup, start, finish, opts) function M.range(bufnr, ns, higroup, start, finish, opts)
opts = opts or {} opts = opts or {}
local regtype = opts.regtype or "v" local regtype = opts.regtype or 'v'
local inclusive = opts.inclusive or false local inclusive = opts.inclusive or false
local priority = opts.priority or M.priorities.user local priority = opts.priority or M.priorities.user
@@ -63,7 +63,7 @@ function M.range(bufnr, ns, higroup, start, finish, opts)
end end
end end
local yank_ns = api.nvim_create_namespace("hlyank") local yank_ns = api.nvim_create_namespace('hlyank')
--- Highlight the yanked region --- Highlight the yanked region
--- ---
--- use from init.vim via --- use from init.vim via
@@ -87,10 +87,10 @@ function M.on_yank(opts)
if t == nil then if t == nil then
return true return true
else else
return type(t) == "table" return type(t) == 'table'
end end
end, end,
"a table or nil to configure options (see `:h highlight.on_yank`)", 'a table or nil to configure options (see `:h highlight.on_yank`)',
}, },
}) })
opts = opts or {} opts = opts or {}
@@ -98,17 +98,17 @@ function M.on_yank(opts)
local on_macro = opts.on_macro or false local on_macro = opts.on_macro or false
local on_visual = (opts.on_visual ~= false) local on_visual = (opts.on_visual ~= false)
if not on_macro and vim.fn.reg_executing() ~= "" then if not on_macro and vim.fn.reg_executing() ~= '' then
return return
end end
if event.operator ~= "y" or event.regtype == "" then if event.operator ~= 'y' or event.regtype == '' then
return return
end end
if not on_visual and event.visual then if not on_visual and event.visual then
return return
end end
local higroup = opts.higroup or "IncSearch" local higroup = opts.higroup or 'IncSearch'
local timeout = opts.timeout or 150 local timeout = opts.timeout or 150
local bufnr = api.nvim_get_current_buf() local bufnr = api.nvim_get_current_buf()

View File

@@ -1,7 +1,7 @@
local inspect = { local inspect = {
_VERSION = "inspect.lua 3.1.0", _VERSION = 'inspect.lua 3.1.0',
_URL = "http://github.com/kikito/inspect.lua", _URL = 'http://github.com/kikito/inspect.lua',
_DESCRIPTION = "human-readable representations of tables", _DESCRIPTION = 'human-readable representations of tables',
_LICENSE = [[ _LICENSE = [[
MIT LICENSE MIT LICENSE
@@ -30,12 +30,12 @@ local inspect = {
inspect.KEY = setmetatable({}, { inspect.KEY = setmetatable({}, {
__tostring = function() __tostring = function()
return "inspect.KEY" return 'inspect.KEY'
end, end,
}) })
inspect.METATABLE = setmetatable({}, { inspect.METATABLE = setmetatable({}, {
__tostring = function() __tostring = function()
return "inspect.METATABLE" return 'inspect.METATABLE'
end, end,
}) })
@@ -61,52 +61,52 @@ end
-- \a => '\\a', \0 => '\\0', 31 => '\31' -- \a => '\\a', \0 => '\\0', 31 => '\31'
local shortControlCharEscapes = { local shortControlCharEscapes = {
["\a"] = "\\a", ['\a'] = '\\a',
["\b"] = "\\b", ['\b'] = '\\b',
["\f"] = "\\f", ['\f'] = '\\f',
["\n"] = "\\n", ['\n'] = '\\n',
["\r"] = "\\r", ['\r'] = '\\r',
["\t"] = "\\t", ['\t'] = '\\t',
["\v"] = "\\v", ['\v'] = '\\v',
["\127"] = "\\127", ['\127'] = '\\127',
} }
local longControlCharEscapes = { ["\127"] = "\127" } local longControlCharEscapes = { ['\127'] = '\127' }
for i = 0, 31 do for i = 0, 31 do
local ch = char(i) local ch = char(i)
if not shortControlCharEscapes[ch] then if not shortControlCharEscapes[ch] then
shortControlCharEscapes[ch] = "\\" .. i shortControlCharEscapes[ch] = '\\' .. i
longControlCharEscapes[ch] = fmt("\\%03d", i) longControlCharEscapes[ch] = fmt('\\%03d', i)
end end
end end
local function escape(str) local function escape(str)
return (gsub(gsub(gsub(str, "\\", "\\\\"), "(%c)%f[0-9]", longControlCharEscapes), "%c", shortControlCharEscapes)) return (gsub(gsub(gsub(str, '\\', '\\\\'), '(%c)%f[0-9]', longControlCharEscapes), '%c', shortControlCharEscapes))
end end
local function isIdentifier(str) local function isIdentifier(str)
return type(str) == "string" and not not str:match("^[_%a][_%a%d]*$") return type(str) == 'string' and not not str:match('^[_%a][_%a%d]*$')
end end
local flr = math.floor local flr = math.floor
local function isSequenceKey(k, sequenceLength) local function isSequenceKey(k, sequenceLength)
return type(k) == "number" and flr(k) == k and 1 <= k and k <= sequenceLength return type(k) == 'number' and flr(k) == k and 1 <= k and k <= sequenceLength
end end
local defaultTypeOrders = { local defaultTypeOrders = {
["number"] = 1, ['number'] = 1,
["boolean"] = 2, ['boolean'] = 2,
["string"] = 3, ['string'] = 3,
["table"] = 4, ['table'] = 4,
["function"] = 5, ['function'] = 5,
["userdata"] = 6, ['userdata'] = 6,
["thread"] = 7, ['thread'] = 7,
} }
local function sortKeys(a, b) local function sortKeys(a, b)
local ta, tb = type(a), type(b) local ta, tb = type(a), type(b)
-- strings and numbers are sorted numerically/alphabetically -- strings and numbers are sorted numerically/alphabetically
if ta == tb and (ta == "string" or ta == "number") then if ta == tb and (ta == 'string' or ta == 'number') then
return a < b return a < b
end end
@@ -137,7 +137,7 @@ local function getKeys(t)
end end
local function countCycles(x, cycles) local function countCycles(x, cycles)
if type(x) == "table" then if type(x) == 'table' then
if cycles[x] then if cycles[x] then
cycles[x] = cycles[x] + 1 cycles[x] = cycles[x] + 1
else else
@@ -173,7 +173,7 @@ local function processRecursive(process, item, path, visited)
end end
local processed = process(item, path) local processed = process(item, path)
if type(processed) == "table" then if type(processed) == 'table' then
local processedCopy = {} local processedCopy = {}
visited[item] = processedCopy visited[item] = processedCopy
local processedKey local processedKey
@@ -186,7 +186,7 @@ local function processRecursive(process, item, path, visited)
end end
local mt = processRecursive(process, getmetatable(processed), makePath(path, inspect.METATABLE), visited) local mt = processRecursive(process, getmetatable(processed), makePath(path, inspect.METATABLE), visited)
if type(mt) ~= "table" then if type(mt) ~= 'table' then
mt = nil mt = nil
end end
setmetatable(processedCopy, mt) setmetatable(processedCopy, mt)
@@ -222,27 +222,27 @@ end
function Inspector:putValue(v) function Inspector:putValue(v)
local buf = self.buf local buf = self.buf
local tv = type(v) local tv = type(v)
if tv == "string" then if tv == 'string' then
puts(buf, smartQuote(escape(v))) puts(buf, smartQuote(escape(v)))
elseif elseif
tv == "number" tv == 'number'
or tv == "boolean" or tv == 'boolean'
or tv == "nil" or tv == 'nil'
or tv == "cdata" or tv == 'cdata'
or tv == "ctype" or tv == 'ctype'
or (vim and v == vim.NIL) or (vim and v == vim.NIL)
then then
puts(buf, tostring(v)) puts(buf, tostring(v))
elseif tv == "table" and not self.ids[v] then elseif tv == 'table' and not self.ids[v] then
local t = v local t = v
if t == inspect.KEY or t == inspect.METATABLE then if t == inspect.KEY or t == inspect.METATABLE then
puts(buf, tostring(t)) puts(buf, tostring(t))
elseif self.level >= self.depth then elseif self.level >= self.depth then
puts(buf, "{...}") puts(buf, '{...}')
else else
if self.cycles[t] > 1 then if self.cycles[t] > 1 then
puts(buf, fmt("<%d>", self:getId(t))) puts(buf, fmt('<%d>', self:getId(t)))
end end
local keys, keysLen, seqLen = getKeys(t) local keys, keysLen, seqLen = getKeys(t)
@@ -253,15 +253,15 @@ function Inspector:putValue(v)
return return
end end
puts(buf, "{") puts(buf, '{')
self.level = self.level + 1 self.level = self.level + 1
for i = 1, seqLen + keysLen do for i = 1, seqLen + keysLen do
if i > 1 then if i > 1 then
puts(buf, ",") puts(buf, ',')
end end
if i <= seqLen then if i <= seqLen then
puts(buf, " ") puts(buf, ' ')
self:putValue(t[i]) self:putValue(t[i])
else else
local k = keys[i - seqLen] local k = keys[i - seqLen]
@@ -269,36 +269,36 @@ function Inspector:putValue(v)
if isIdentifier(k) then if isIdentifier(k) then
puts(buf, k) puts(buf, k)
else else
puts(buf, "[") puts(buf, '[')
self:putValue(k) self:putValue(k)
puts(buf, "]") puts(buf, ']')
end end
puts(buf, " = ") puts(buf, ' = ')
self:putValue(t[k]) self:putValue(t[k])
end end
end end
if type(mt) == "table" then if type(mt) == 'table' then
if seqLen + keysLen > 0 then if seqLen + keysLen > 0 then
puts(buf, ",") puts(buf, ',')
end end
tabify(self) tabify(self)
puts(buf, "<metatable> = ") puts(buf, '<metatable> = ')
self:putValue(mt) self:putValue(mt)
end end
self.level = self.level - 1 self.level = self.level - 1
if keysLen > 0 or type(mt) == "table" then if keysLen > 0 or type(mt) == 'table' then
tabify(self) tabify(self)
elseif seqLen > 0 then elseif seqLen > 0 then
puts(buf, " ") puts(buf, ' ')
end end
puts(buf, "}") puts(buf, '}')
end end
else else
puts(buf, fmt("<%s %d>", tv, self:getId(v))) puts(buf, fmt('<%s %d>', tv, self:getId(v)))
end end
end end
@@ -306,8 +306,8 @@ function inspect.inspect(root, options)
options = options or {} options = options or {}
local depth = options.depth or math.huge local depth = options.depth or math.huge
local newline = options.newline or "\n" local newline = options.newline or '\n'
local indent = options.indent or " " local indent = options.indent or ' '
local process = options.process local process = options.process
if process then if process then

View File

@@ -49,20 +49,20 @@ local keymap = {}
--- Default `false`. --- Default `false`.
---@see |nvim_set_keymap()| ---@see |nvim_set_keymap()|
function keymap.set(mode, lhs, rhs, opts) function keymap.set(mode, lhs, rhs, opts)
vim.validate { vim.validate({
mode = {mode, {'s', 't'}}, mode = { mode, { 's', 't' } },
lhs = {lhs, 's'}, lhs = { lhs, 's' },
rhs = {rhs, {'s', 'f'}}, rhs = { rhs, { 's', 'f' } },
opts = {opts, 't', true} opts = { opts, 't', true },
} })
opts = vim.deepcopy(opts) or {} opts = vim.deepcopy(opts) or {}
local is_rhs_luaref = type(rhs) == "function" local is_rhs_luaref = type(rhs) == 'function'
mode = type(mode) == 'string' and {mode} or mode mode = type(mode) == 'string' and { mode } or mode
if is_rhs_luaref and opts.expr then if is_rhs_luaref and opts.expr then
local user_rhs = rhs local user_rhs = rhs
rhs = function () rhs = function()
local res = user_rhs() local res = user_rhs()
if res == nil then if res == nil then
-- TODO(lewis6991): Handle this in C? -- TODO(lewis6991): Handle this in C?
@@ -118,14 +118,14 @@ end
---@see |vim.keymap.set()| ---@see |vim.keymap.set()|
--- ---
function keymap.del(modes, lhs, opts) function keymap.del(modes, lhs, opts)
vim.validate { vim.validate({
mode = {modes, {'s', 't'}}, mode = { modes, { 's', 't' } },
lhs = {lhs, 's'}, lhs = { lhs, 's' },
opts = {opts, 't', true} opts = { opts, 't', true },
} })
opts = opts or {} opts = opts or {}
modes = type(modes) == 'string' and {modes} or modes modes = type(modes) == 'string' and { modes } or modes
local buffer = false local buffer = false
if opts.buffer ~= nil then if opts.buffer ~= nil then

File diff suppressed because it is too large Load Diff

View File

@@ -41,7 +41,7 @@ P.take_until = function(targets, specials)
parsed = true, parsed = true,
value = { value = {
raw = table.concat(raw, ''), raw = table.concat(raw, ''),
esc = table.concat(esc, '') esc = table.concat(esc, ''),
}, },
pos = new_pos, pos = new_pos,
} }
@@ -248,48 +248,66 @@ S.format = P.any(
capture_index = values[3], capture_index = values[3],
}, Node) }, Node)
end), end),
P.map(P.seq(S.dollar, S.open, S.int, S.colon, S.slash, P.any( P.map(
P.token('upcase'), P.seq(
P.token('downcase'), S.dollar,
P.token('capitalize'), S.open,
P.token('camelcase'), S.int,
P.token('pascalcase') S.colon,
), S.close), function(values) 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({ return setmetatable({
type = Node.Type.FORMAT, type = Node.Type.FORMAT,
capture_index = values[3], capture_index = values[3],
modifier = values[6], modifier = values[6],
}, Node) }, Node)
end), end
P.map(P.seq(S.dollar, S.open, S.int, S.colon, P.any( ),
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.question, P.take_until({ ':' }, { '\\' }), S.colon, P.take_until({ '}' }, { '\\' })),
P.seq(S.plus, P.take_until({ '}' }, { '\\' })), P.seq(S.plus, P.take_until({ '}' }, { '\\' })),
P.seq(S.minus, P.take_until({ '}' }, { '\\' })) P.seq(S.minus, P.take_until({ '}' }, { '\\' }))
), S.close), function(values) ),
S.close
),
function(values)
return setmetatable({ return setmetatable({
type = Node.Type.FORMAT, type = Node.Type.FORMAT,
capture_index = values[3], capture_index = values[3],
if_text = values[5][2].esc, if_text = values[5][2].esc,
else_text = (values[5][4] or {}).esc, else_text = (values[5][4] or {}).esc,
}, Node) }, Node)
end) end
)
) )
S.transform = P.map(P.seq( S.transform = P.map(
P.seq(
S.slash, S.slash,
P.take_until({ '/' }, { '\\' }), P.take_until({ '/' }, { '\\' }),
S.slash, S.slash,
P.many(P.any(S.format, S.text({ '$', '/' }, { '\\' }))), P.many(P.any(S.format, S.text({ '$', '/' }, { '\\' }))),
S.slash, S.slash,
P.opt(P.pattern('[ig]+')) P.opt(P.pattern('[ig]+'))
), function(values) ),
function(values)
return setmetatable({ return setmetatable({
type = Node.Type.TRANSFORM, type = Node.Type.TRANSFORM,
pattern = values[2].raw, pattern = values[2].raw,
format = values[4], format = values[4],
option = values[6], option = values[6],
}, Node) }, Node)
end) end
)
S.tabstop = P.any( S.tabstop = P.any(
P.map(P.seq(S.dollar, S.int), function(values) P.map(P.seq(S.dollar, S.int), function(values)
@@ -314,34 +332,38 @@ S.tabstop = P.any(
) )
S.placeholder = 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) 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({ return setmetatable({
type = Node.Type.PLACEHOLDER, type = Node.Type.PLACEHOLDER,
tabstop = values[3], tabstop = values[3],
children = values[5], children = values[5],
}, Node) }, Node)
end) end
)
) )
S.choice = P.map(P.seq( S.choice = P.map(
P.seq(
S.dollar, S.dollar,
S.open, S.open,
S.int, S.int,
S.pipe, S.pipe,
P.many( P.many(P.map(P.seq(S.text({ ',', '|' }), P.opt(S.comma)), function(values)
P.map(P.seq(S.text({ ',', '|' }), P.opt(S.comma)), function(values)
return values[1].esc return values[1].esc
end) end)),
),
S.pipe, S.pipe,
S.close S.close
), function(values) ),
function(values)
return setmetatable({ return setmetatable({
type = Node.Type.CHOICE, type = Node.Type.CHOICE,
tabstop = values[3], tabstop = values[3],
items = values[5], items = values[5],
}, Node) }, Node)
end) end
)
S.variable = P.any( S.variable = P.any(
P.map(P.seq(S.dollar, S.var), function(values) P.map(P.seq(S.dollar, S.var), function(values)
@@ -363,13 +385,16 @@ S.variable = P.any(
transform = values[4], transform = values[4],
}, Node) }, Node)
end), 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) 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({ return setmetatable({
type = Node.Type.VARIABLE, type = Node.Type.VARIABLE,
name = values[3], name = values[3],
children = values[5], children = values[5],
}, Node) }, Node)
end) end
)
) )
S.snippet = P.map(P.many(P.any(S.toplevel, S.text({ '$' }, { '}', '\\' }))), function(values) S.snippet = P.map(P.many(P.any(S.toplevel, S.text({ '$' }, { '}', '\\' }))), function(values)

View File

@@ -1,7 +1,7 @@
local vim = vim local vim = vim
local validate = vim.validate local validate = vim.validate
local vfn = vim.fn local vfn = vim.fn
local util = require 'vim.lsp.util' local util = require('vim.lsp.util')
local M = {} local M = {}
@@ -9,7 +9,9 @@ local M = {}
--- Returns nil if {status} is false or nil, otherwise returns the rest of the --- Returns nil if {status} is false or nil, otherwise returns the rest of the
--- arguments. --- arguments.
local function ok_or_nil(status, ...) local function ok_or_nil(status, ...)
if not status then return end if not status then
return
end
return ... return ...
end end
@@ -39,10 +41,10 @@ end
--- ---
---@see |vim.lsp.buf_request()| ---@see |vim.lsp.buf_request()|
local function request(method, params, handler) local function request(method, params, handler)
validate { validate({
method = {method, 's'}; method = { method, 's' },
handler = {handler, 'f', true}; handler = { handler, 'f', true },
} })
return vim.lsp.buf_request(0, method, params, handler) return vim.lsp.buf_request(0, method, params, handler)
end end
@@ -51,7 +53,7 @@ end
--- ---
---@returns `true` if server responds. ---@returns `true` if server responds.
function M.server_ready() 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 end
--- Displays hover information about the symbol under the cursor in a floating --- Displays hover information about the symbol under the cursor in a floating
@@ -117,9 +119,9 @@ end
-- --
---@returns The client that the user selected or nil ---@returns The client that the user selected or nil
local function select_client(method, on_choice) local function select_client(method, on_choice)
validate { validate({
on_choice = { on_choice, 'function', false }, on_choice = { on_choice, 'function', false },
} })
local clients = vim.tbl_values(vim.lsp.buf_get_clients()) local clients = vim.tbl_values(vim.lsp.buf_get_clients())
clients = vim.tbl_filter(function(client) clients = vim.tbl_filter(function(client)
return client.supports_method(method) return client.supports_method(method)
@@ -191,24 +193,21 @@ function M.format(options)
if options.filter then if options.filter then
clients = options.filter(clients) clients = options.filter(clients)
elseif options.id then elseif options.id then
clients = vim.tbl_filter( clients = vim.tbl_filter(function(client)
function(client) return client.id == options.id end, return client.id == options.id
clients end, clients)
)
elseif options.name then elseif options.name then
clients = vim.tbl_filter( clients = vim.tbl_filter(function(client)
function(client) return client.name == options.name end, return client.name == options.name
clients end, clients)
)
end end
clients = vim.tbl_filter( clients = vim.tbl_filter(function(client)
function(client) return client.supports_method("textDocument/formatting") end, return client.supports_method('textDocument/formatting')
clients end, clients)
)
if #clients == 0 then 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 end
if options.async then if options.async then
@@ -218,7 +217,7 @@ function M.format(options)
return return
end end
local params = util.make_formatting_params(options.formatting_options) 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'] local handler = client.handlers['textDocument/formatting'] or vim.lsp.handlers['textDocument/formatting']
handler(...) handler(...)
do_format(next(clients, idx)) do_format(next(clients, idx))
@@ -229,11 +228,11 @@ function M.format(options)
local timeout_ms = options.timeout_ms or 1000 local timeout_ms = options.timeout_ms or 1000
for _, client in pairs(clients) do for _, client in pairs(clients) do
local params = util.make_formatting_params(options.formatting_options) 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 if result and result.result then
util.apply_text_edits(result.result, bufnr, client.offset_encoding) util.apply_text_edits(result.result, bufnr, client.offset_encoding)
elseif err then 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 end
end end
@@ -310,7 +309,7 @@ end
---the remaining clients in the order as they occur in the `order` list. ---the remaining clients in the order as they occur in the `order` list.
function M.formatting_seq_sync(options, timeout_ms, order) 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) 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() local bufnr = vim.api.nvim_get_current_buf()
-- sort the clients according to `order` -- sort the clients according to `order`
@@ -326,13 +325,18 @@ function M.formatting_seq_sync(options, timeout_ms, order)
-- loop through the clients and make synchronous formatting requests -- loop through the clients and make synchronous formatting requests
for _, client in pairs(clients) do 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 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 if result and result.result then
util.apply_text_edits(result.result, bufnr, client.offset_encoding) util.apply_text_edits(result.result, bufnr, client.offset_encoding)
elseif err then 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 end
end end
@@ -377,20 +381,18 @@ function M.rename(new_name, options)
if options.filter then if options.filter then
clients = options.filter(clients) clients = options.filter(clients)
elseif options.name then elseif options.name then
clients = vim.tbl_filter( clients = vim.tbl_filter(function(client)
function(client) return client.name == options.name end, return client.name == options.name
clients end, clients)
)
end end
-- Clients must at least support rename, prepareRename is optional -- Clients must at least support rename, prepareRename is optional
clients = vim.tbl_filter( clients = vim.tbl_filter(function(client)
function(client) return client.supports_method("textDocument/rename") end, return client.supports_method('textDocument/rename')
clients end, clients)
)
if #clients == 0 then 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 end
local win = vim.api.nvim_get_current_win() local win = vim.api.nvim_get_current_win()
@@ -427,7 +429,7 @@ function M.rename(new_name, options)
end, bufnr) end, bufnr)
end 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) local params = util.make_position_params(win, client.offset_encoding)
client.request('textDocument/prepareRename', params, function(err, result) client.request('textDocument/prepareRename', params, function(err, result)
if err or result == nil then if err or result == nil then
@@ -446,7 +448,7 @@ function M.rename(new_name, options)
end end
local prompt_opts = { local prompt_opts = {
prompt = "New Name: " prompt = 'New Name: ',
} }
-- result: Range | { range: Range, placeholder: string } -- result: Range | { range: Range, placeholder: string }
if result.placeholder then if result.placeholder then
@@ -466,15 +468,15 @@ function M.rename(new_name, options)
end) end)
end, bufnr) end, bufnr)
else 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 if new_name then
rename(new_name) rename(new_name)
return return
end end
local prompt_opts = { local prompt_opts = {
prompt = "New Name: ", prompt = 'New Name: ',
default = cword default = cword,
} }
vim.ui.input(prompt_opts, function(input) vim.ui.input(prompt_opts, function(input)
if not input or #input == 0 then if not input or #input == 0 then
@@ -493,10 +495,10 @@ end
---@param context (table) Context for the request ---@param context (table) Context for the request
---@see https://microsoft.github.io/language-server-protocol/specifications/specification-current/#textDocument_references ---@see https://microsoft.github.io/language-server-protocol/specifications/specification-current/#textDocument_references
function M.references(context) function M.references(context)
validate { context = { context, 't', true } } validate({ context = { context, 't', true } })
local params = util.make_position_params() local params = util.make_position_params()
params.context = context or { params.context = context or {
includeDeclaration = true; includeDeclaration = true,
} }
request('textDocument/references', params) request('textDocument/references', params)
end end
@@ -510,14 +512,16 @@ end
---@private ---@private
local function pick_call_hierarchy_item(call_hierarchy_items) 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 if #call_hierarchy_items == 1 then
return call_hierarchy_items[1] return call_hierarchy_items[1]
end end
local items = {} local items = {}
for i, item in pairs(call_hierarchy_items) do for i, item in pairs(call_hierarchy_items) do
local entry = item.detail or item.name 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 end
local choice = vim.fn.inputlist(items) local choice = vim.fn.inputlist(items)
if choice < 1 or choice > #items then if choice < 1 or choice > #items then
@@ -539,8 +543,8 @@ local function call_hierarchy(method)
if client then if client then
client.request(method, { item = call_hierarchy_item }, nil, ctx.bufnr) client.request(method, { item = call_hierarchy_item }, nil, ctx.bufnr)
else else
vim.notify(string.format( vim.notify(
'Client with id=%d disappeared during call hierarchy request', ctx.client_id), string.format('Client with id=%d disappeared during call hierarchy request', ctx.client_id),
vim.log.levels.WARN vim.log.levels.WARN
) )
end end
@@ -576,20 +580,25 @@ end
--- Add the folder at path to the workspace folders. If {path} is --- Add the folder at path to the workspace folders. If {path} is
--- not provided, the user will be prompted for a path using |input()|. --- not provided, the user will be prompted for a path using |input()|.
function M.add_workspace_folder(workspace_folder) function M.add_workspace_folder(workspace_folder)
workspace_folder = workspace_folder or npcall(vfn.input, "Workspace Folder: ", vfn.expand('%:p:h'), 'dir') workspace_folder = workspace_folder or npcall(vfn.input, 'Workspace Folder: ', vfn.expand('%:p:h'), 'dir')
vim.api.nvim_command("redraw") vim.api.nvim_command('redraw')
if not (workspace_folder and #workspace_folder > 0) then return end if not (workspace_folder and #workspace_folder > 0) then
if vim.fn.isdirectory(workspace_folder) == 0 then
print(workspace_folder, " is not a valid directory")
return return
end end
local params = util.make_workspace_params({{uri = vim.uri_from_fname(workspace_folder); name = workspace_folder}}, {{}}) if vim.fn.isdirectory(workspace_folder) == 0 then
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 } },
{ {} }
)
for _, client in pairs(vim.lsp.buf_get_clients()) do for _, client in pairs(vim.lsp.buf_get_clients()) do
local found = false local found = false
for _, folder in pairs(client.workspace_folders or {}) do for _, folder in pairs(client.workspace_folders or {}) do
if folder.name == workspace_folder then if folder.name == workspace_folder then
found = true found = true
print(workspace_folder, "is already part of this workspace") print(workspace_folder, 'is already part of this workspace')
break break
end end
end end
@@ -607,10 +616,15 @@ end
--- {path} is not provided, the user will be prompted for --- {path} is not provided, the user will be prompted for
--- a path using |input()|. --- a path using |input()|.
function M.remove_workspace_folder(workspace_folder) function M.remove_workspace_folder(workspace_folder)
workspace_folder = workspace_folder or npcall(vfn.input, "Workspace Folder: ", vfn.expand('%:p:h')) workspace_folder = workspace_folder or npcall(vfn.input, 'Workspace Folder: ', vfn.expand('%:p:h'))
vim.api.nvim_command("redraw") vim.api.nvim_command('redraw')
if not (workspace_folder and #workspace_folder > 0) then return end if not (workspace_folder and #workspace_folder > 0) then
local params = util.make_workspace_params({{}}, {{uri = vim.uri_from_fname(workspace_folder); name = workspace_folder}}) 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 _, client in pairs(vim.lsp.buf_get_clients()) do
for idx, folder in pairs(client.workspace_folders) do for idx, folder in pairs(client.workspace_folders) do
if folder.name == workspace_folder then if folder.name == workspace_folder then
@@ -620,7 +634,7 @@ function M.remove_workspace_folder(workspace_folder)
end end
end end
end end
print(workspace_folder, "is not currently part of the workspace") print(workspace_folder, 'is not currently part of the workspace')
end end
--- Lists all symbols in the current workspace in the quickfix window. --- Lists all symbols in the current workspace in the quickfix window.
@@ -631,11 +645,11 @@ end
--- ---
---@param query (string, optional) ---@param query (string, optional)
function M.workspace_symbol(query) function M.workspace_symbol(query)
query = query or npcall(vfn.input, "Query: ") query = query or npcall(vfn.input, 'Query: ')
if query == nil then if query == nil then
return return
end end
local params = {query = query} local params = { query = query }
request('workspace/symbol', params) request('workspace/symbol', params)
end end
@@ -665,7 +679,6 @@ function M.clear_references()
util.buf_clear_references() util.buf_clear_references()
end end
---@private ---@private
-- --
--- This is not public because the main extension point is --- This is not public because the main extension point is
@@ -728,10 +741,11 @@ local function on_code_action_results(results, ctx, options)
-- --
local client = vim.lsp.get_client_by_id(action_tuple[1]) local client = vim.lsp.get_client_by_id(action_tuple[1])
local action = action_tuple[2] local action = action_tuple[2]
if not action.edit if
not action.edit
and client and client
and vim.tbl_get(client.server_capabilities, "codeActionProvider", "resolveProvider") then and vim.tbl_get(client.server_capabilities, 'codeActionProvider', 'resolveProvider')
then
client.request('codeAction/resolve', action, function(err, resolved_action) client.request('codeAction/resolve', action, function(err, resolved_action)
if err then if err then
vim.notify(err.code .. ': ' .. err.message, vim.log.levels.ERROR) vim.notify(err.code .. ': ' .. err.message, vim.log.levels.ERROR)
@@ -761,7 +775,6 @@ local function on_code_action_results(results, ctx, options)
}, on_user_choice) }, on_user_choice)
end end
--- Requests code actions from all clients and calls the handler exactly once --- Requests code actions from all clients and calls the handler exactly once
--- with all aggregated results --- with all aggregated results
---@private ---@private
@@ -769,7 +782,7 @@ local function code_action_request(params, options)
local bufnr = vim.api.nvim_get_current_buf() local bufnr = vim.api.nvim_get_current_buf()
local method = 'textDocument/codeAction' local method = 'textDocument/codeAction'
vim.lsp.buf_request_all(bufnr, method, params, function(results) 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) on_code_action_results(results, ctx, options)
end) end)
end end
@@ -794,7 +807,7 @@ end
--- (after filtering), the action is applied without user query. --- (after filtering), the action is applied without user query.
---@see https://microsoft.github.io/language-server-protocol/specifications/specification-current/#textDocument_codeAction ---@see https://microsoft.github.io/language-server-protocol/specifications/specification-current/#textDocument_codeAction
function M.code_action(options) function M.code_action(options)
validate { options = { options, 't', true } } validate({ options = { options, 't', true } })
options = options or {} options = options or {}
-- Detect old API call code_action(context) which should now be -- Detect old API call code_action(context) which should now be
-- code_action({ context = context} ) -- code_action({ context = context} )
@@ -826,7 +839,7 @@ end
---@param end_pos ({number, number}, optional) mark-indexed position. ---@param end_pos ({number, number}, optional) mark-indexed position.
---Defaults to the end of the last visual selection. ---Defaults to the end of the last visual selection.
function M.range_code_action(context, start_pos, end_pos) function M.range_code_action(context, start_pos, end_pos)
validate { context = { context, 't', true } } validate({ context = { context, 't', true } })
context = context or {} context = context or {}
if not context.diagnostics then if not context.diagnostics then
context.diagnostics = vim.lsp.diagnostic.get_line_diagnostics() context.diagnostics = vim.lsp.diagnostic.get_line_diagnostics()
@@ -841,16 +854,16 @@ end
---@param command_params table A valid `ExecuteCommandParams` object ---@param command_params table A valid `ExecuteCommandParams` object
---@see https://microsoft.github.io/language-server-protocol/specifications/specification-current/#workspace_executeCommand ---@see https://microsoft.github.io/language-server-protocol/specifications/specification-current/#workspace_executeCommand
function M.execute_command(command_params) function M.execute_command(command_params)
validate { validate({
command = { command_params.command, 's' }, command = { command_params.command, 's' },
arguments = { command_params.arguments, 't', true } arguments = { command_params.arguments, 't', true },
} })
command_params = { command_params = {
command=command_params.command, command = command_params.command,
arguments=command_params.arguments, arguments = command_params.arguments,
workDoneToken=command_params.workDoneToken, workDoneToken = command_params.workDoneToken,
} }
request('workspace/executeCommand', command_params ) request('workspace/executeCommand', command_params)
end end
return M return M

View File

@@ -12,7 +12,7 @@ local lens_cache_by_buf = setmetatable({}, {
__index = function(t, b) __index = function(t, b)
local key = b > 0 and b or api.nvim_get_current_buf() local key = b > 0 and b or api.nvim_get_current_buf()
return rawget(t, key) return rawget(t, key)
end end,
}) })
local namespaces = setmetatable({}, { local namespaces = setmetatable({}, {
@@ -20,13 +20,12 @@ local namespaces = setmetatable({}, {
local value = api.nvim_create_namespace('vim_lsp_codelens:' .. key) local value = api.nvim_create_namespace('vim_lsp_codelens:' .. key)
rawset(t, key, value) rawset(t, key, value)
return value return value
end; end,
}) })
---@private ---@private
M.__namespaces = namespaces M.__namespaces = namespaces
---@private ---@private
local function execute_lens(lens, bufnr, client_id) local function execute_lens(lens, bufnr, client_id)
local line = lens.range.start.line local line = lens.range.start.line
@@ -44,9 +43,13 @@ local function execute_lens(lens, bufnr, client_id)
local command_provider = client.server_capabilities.executeCommandProvider local command_provider = client.server_capabilities.executeCommandProvider
local commands = type(command_provider) == 'table' and command_provider.commands or {} local commands = type(command_provider) == 'table' and command_provider.commands or {}
if not vim.tbl_contains(commands, command.command) then if not vim.tbl_contains(commands, command.command) then
vim.notify(string.format( vim.notify(
"Language server does not support command `%s`. This command may require a client extension.", command.command), string.format(
vim.log.levels.WARN) 'Language server does not support command `%s`. This command may require a client extension.',
command.command
),
vim.log.levels.WARN
)
return return
end end
client.request('workspace/executeCommand', command, function(...) client.request('workspace/executeCommand', command, function(...)
@@ -56,14 +59,15 @@ local function execute_lens(lens, bufnr, client_id)
end, bufnr) end, bufnr)
end end
--- Return all lenses for the given buffer --- Return all lenses for the given buffer
--- ---
---@param bufnr number Buffer number. 0 can be used for the current buffer. ---@param bufnr number Buffer number. 0 can be used for the current buffer.
---@return table (`CodeLens[]`) ---@return table (`CodeLens[]`)
function M.get(bufnr) function M.get(bufnr)
local lenses_by_client = lens_cache_by_buf[bufnr or 0] 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 = {} local lenses = {}
for _, client_lenses in pairs(lenses_by_client) do for _, client_lenses in pairs(lenses_by_client) do
vim.list_extend(lenses, client_lenses) vim.list_extend(lenses, client_lenses)
@@ -71,7 +75,6 @@ function M.get(bufnr)
return lenses return lenses
end end
--- Run the code lens in the current line --- Run the code lens in the current line
--- ---
function M.run() function M.run()
@@ -82,7 +85,7 @@ function M.run()
for client, lenses in pairs(lenses_by_client) do for client, lenses in pairs(lenses_by_client) do
for _, lens in pairs(lenses) do for _, lens in pairs(lenses) do
if lens.range.start.line == (line - 1) then 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 end
end end
@@ -105,7 +108,6 @@ function M.run()
end end
end end
--- Display the lenses using virtual text --- Display the lenses using virtual text
--- ---
---@param lenses table of lenses to display (`CodeLens[] | null`) ---@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 local num_line_lenses = #line_lenses
for j, lens in ipairs(line_lenses) do for j, lens in ipairs(line_lenses) do
local text = lens.command and lens.command.title or 'Unresolved lens ...' 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 if j < num_line_lenses then
table.insert(chunks, {' | ', 'LspCodeLensSeparator' }) table.insert(chunks, { ' | ', 'LspCodeLensSeparator' })
end end
end end
if #chunks > 0 then if #chunks > 0 then
api.nvim_buf_set_extmark(bufnr, ns, i, 0, { virt_text = chunks, api.nvim_buf_set_extmark(bufnr, ns, i, 0, {
hl_mode="combine" }) virt_text = chunks,
hl_mode = 'combine',
})
end end
end end
end end
--- Store lenses for a specific buffer and client --- Store lenses for a specific buffer and client
--- ---
---@param lenses table of lenses to store (`CodeLens[] | null`) ---@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 lens_cache_by_buf[bufnr] = lenses_by_client
local ns = namespaces[client_id] local ns = namespaces[client_id]
api.nvim_buf_attach(bufnr, false, { 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) on_lines = function(_, b, _, first_lnum, last_lnum)
api.nvim_buf_clear_namespace(b, ns, first_lnum, last_lnum) api.nvim_buf_clear_namespace(b, ns, first_lnum, last_lnum)
end end,
}) })
end end
lenses_by_client[client_id] = lenses lenses_by_client[client_id] = lenses
end end
---@private ---@private
local function resolve_lenses(lenses, bufnr, client_id, callback) local function resolve_lenses(lenses, bufnr, client_id, callback)
lenses = lenses or {} lenses = lenses or {}
@@ -201,8 +205,7 @@ local function resolve_lenses(lenses, bufnr, client_id, callback)
ns, ns,
lens.range.start.line, lens.range.start.line,
0, 0,
{ virt_text = {{ lens.command.title, 'LspCodeLens' }}, { virt_text = { { lens.command.title, 'LspCodeLens' } }, hl_mode = 'combine' }
hl_mode="combine" }
) )
end end
countdown() countdown()
@@ -211,13 +214,12 @@ local function resolve_lenses(lenses, bufnr, client_id, callback)
end end
end end
--- |lsp-handler| for the method `textDocument/codeLens` --- |lsp-handler| for the method `textDocument/codeLens`
--- ---
function M.on_codelens(err, result, ctx, _) function M.on_codelens(err, result, ctx, _)
if err then if err then
active_refreshes[ctx.bufnr] = nil active_refreshes[ctx.bufnr] = nil
local _ = log.error() and log.error("codelens", err) local _ = log.error() and log.error('codelens', err)
return return
end end
@@ -232,7 +234,6 @@ function M.on_codelens(err, result, ctx, _)
end) end)
end end
--- Refresh the codelens for the current buffer --- Refresh the codelens for the current buffer
--- ---
--- It is recommended to trigger this using an autocmd or via keymap. --- It is recommended to trigger this using an autocmd or via keymap.
@@ -243,7 +244,7 @@ end
--- ---
function M.refresh() function M.refresh()
local params = { local params = {
textDocument = util.make_text_document_params() textDocument = util.make_text_document_params(),
} }
local bufnr = api.nvim_get_current_buf() local bufnr = api.nvim_get_current_buf()
if active_refreshes[bufnr] then if active_refreshes[bufnr] then
@@ -253,5 +254,4 @@ function M.refresh()
vim.lsp.buf_request(0, 'textDocument/codeLens', params, M.on_codelens) vim.lsp.buf_request(0, 'textDocument/codeLens', params, M.on_codelens)
end end
return M return M

View File

@@ -50,12 +50,12 @@ end
---@private ---@private
local function line_byte_from_position(lines, lnum, col, offset_encoding) 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 return col
end end
local line = lines[lnum + 1] 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 if ok then
return result return result
end end
@@ -75,7 +75,7 @@ local function get_buf_lines(bufnr)
return return
end end
local content = f:read("*a") local content = f:read('*a')
if not content then if not content then
-- Some LSP servers report diagnostics at a directory level, in which case -- Some LSP servers report diagnostics at a directory level, in which case
-- io.read() returns nil -- io.read() returns nil
@@ -83,7 +83,7 @@ local function get_buf_lines(bufnr)
return return
end end
local lines = vim.split(content, "\n") local lines = vim.split(content, '\n')
f:close() f:close()
return lines return lines
end end
@@ -92,10 +92,10 @@ end
local function diagnostic_lsp_to_vim(diagnostics, bufnr, client_id) local function diagnostic_lsp_to_vim(diagnostics, bufnr, client_id)
local buf_lines = get_buf_lines(bufnr) local buf_lines = get_buf_lines(bufnr)
local client = vim.lsp.get_client_by_id(client_id) 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) return vim.tbl_map(function(diagnostic)
local start = diagnostic.range.start local start = diagnostic.range.start
local _end = diagnostic.range["end"] local _end = diagnostic.range['end']
return { return {
lnum = start.line, lnum = start.line,
col = line_byte_from_position(buf_lines, start.line, start.character, offset_encoding), col = line_byte_from_position(buf_lines, start.line, start.character, offset_encoding),
@@ -122,14 +122,14 @@ end
---@private ---@private
local function diagnostic_vim_to_lsp(diagnostics) local function diagnostic_vim_to_lsp(diagnostics)
return vim.tbl_map(function(diagnostic) 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 -- "keep" the below fields over any duplicate fields in diagnostic.user_data.lsp
range = { range = {
start = { start = {
line = diagnostic.lnum, line = diagnostic.lnum,
character = diagnostic.col, character = diagnostic.col,
}, },
["end"] = { ['end'] = {
line = diagnostic.end_lnum, line = diagnostic.end_lnum,
character = diagnostic.end_col, character = diagnostic.end_col,
}, },
@@ -148,10 +148,10 @@ local _client_namespaces = {}
--- ---
---@param client_id number The id of the LSP client ---@param client_id number The id of the LSP client
function M.get_namespace(client_id) 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 if not _client_namespaces[client_id] then
local client = vim.lsp.get_client_by_id(client_id) 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) _client_namespaces[client_id] = vim.api.nvim_create_namespace(name)
end end
return _client_namespaces[client_id] return _client_namespaces[client_id]
@@ -203,7 +203,7 @@ function M.on_publish_diagnostics(_, result, ctx, config)
for _, opt in pairs(config) do for _, opt in pairs(config) do
if type(opt) == 'table' then if type(opt) == 'table' then
if not opt.severity and opt.severity_limit 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 end
end end
@@ -240,7 +240,6 @@ end
-- Deprecated Functions {{{ -- Deprecated Functions {{{
--- Save diagnostics to the current buffer. --- Save diagnostics to the current buffer.
--- ---
---@deprecated Prefer |vim.diagnostic.set()| ---@deprecated Prefer |vim.diagnostic.set()|
@@ -251,7 +250,7 @@ end
---@param client_id number ---@param client_id number
---@private ---@private
function M.save(diagnostics, bufnr, client_id) 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) local namespace = M.get_namespace(client_id)
vim.diagnostic.set(namespace, bufnr, diagnostic_lsp_to_vim(diagnostics, bufnr, client_id)) vim.diagnostic.set(namespace, bufnr, diagnostic_lsp_to_vim(diagnostics, bufnr, client_id))
end end
@@ -265,14 +264,14 @@ end
--- If nil, diagnostics of all clients are included. --- If nil, diagnostics of all clients are included.
---@return table with diagnostics grouped by bufnr (bufnr: Diagnostic[]) ---@return table with diagnostics grouped by bufnr (bufnr: Diagnostic[])
function M.get_all(client_id) 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 result = {}
local namespace local namespace
if client_id then if client_id then
namespace = M.get_namespace(client_id) namespace = M.get_namespace(client_id)
end end
for _, bufnr in ipairs(vim.api.nvim_list_bufs()) do 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 result[bufnr] = diagnostics
end end
return result return result
@@ -287,8 +286,10 @@ end
--- Else, return just the diagnostics associated with the client_id. --- Else, return just the diagnostics associated with the client_id.
---@param predicate function|nil Optional function for filtering diagnostics ---@param predicate function|nil Optional function for filtering diagnostics
function M.get(bufnr, client_id, predicate) function M.get(bufnr, client_id, predicate)
vim.deprecate('vim.lsp.diagnostic.get', 'vim.diagnostic.get', '0.8' ) vim.deprecate('vim.lsp.diagnostic.get', 'vim.diagnostic.get', '0.8')
predicate = predicate or function() return true end predicate = predicate or function()
return true
end
if client_id == nil then if client_id == nil then
local all_diagnostics = {} local all_diagnostics = {}
vim.lsp.for_each_buffer_client(bufnr, function(_, iter_client_id, _) vim.lsp.for_each_buffer_client(bufnr, function(_, iter_client_id, _)
@@ -301,7 +302,7 @@ function M.get(bufnr, client_id, predicate)
end end
local namespace = M.get_namespace(client_id) 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 end
--- Get the diagnostics by line --- Get the diagnostics by line
@@ -325,7 +326,7 @@ function M.get_line_diagnostics(bufnr, line_nr, opts, client_id)
if opts.severity then if opts.severity then
opts.severity = severity_lsp_to_vim(opts.severity) opts.severity = severity_lsp_to_vim(opts.severity)
elseif opts.severity_limit then 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
if client_id then if client_id then
@@ -349,7 +350,7 @@ end
---@param severity DiagnosticSeverity ---@param severity DiagnosticSeverity
---@param client_id number the client id ---@param client_id number the client id
function M.get_count(bufnr, severity, 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) severity = severity_lsp_to_vim(severity)
local opts = { severity = severity } local opts = { severity = severity }
if client_id ~= nil then if client_id ~= nil then
@@ -366,15 +367,15 @@ end
---@param opts table See |vim.lsp.diagnostic.goto_next()| ---@param opts table See |vim.lsp.diagnostic.goto_next()|
---@return table Previous diagnostic ---@return table Previous diagnostic
function M.get_prev(opts) 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 then
if opts.severity then if opts.severity then
opts.severity = severity_lsp_to_vim(opts.severity) opts.severity = severity_lsp_to_vim(opts.severity)
elseif opts.severity_limit then 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
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 end
--- Return the pos, {row, col}, for the prev diagnostic in the current buffer. --- 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()| ---@param opts table See |vim.lsp.diagnostic.goto_next()|
---@return table Previous diagnostic position ---@return table Previous diagnostic position
function M.get_prev_pos(opts) 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 then
if opts.severity then if opts.severity then
opts.severity = severity_lsp_to_vim(opts.severity) opts.severity = severity_lsp_to_vim(opts.severity)
elseif opts.severity_limit then 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
end end
return vim.diagnostic.get_prev_pos(opts) return vim.diagnostic.get_prev_pos(opts)
@@ -401,12 +402,12 @@ end
--- ---
---@param opts table See |vim.lsp.diagnostic.goto_next()| ---@param opts table See |vim.lsp.diagnostic.goto_next()|
function M.goto_prev(opts) 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 then
if opts.severity then if opts.severity then
opts.severity = severity_lsp_to_vim(opts.severity) opts.severity = severity_lsp_to_vim(opts.severity)
elseif opts.severity_limit then 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
end end
return vim.diagnostic.goto_prev(opts) return vim.diagnostic.goto_prev(opts)
@@ -419,15 +420,15 @@ end
---@param opts table See |vim.lsp.diagnostic.goto_next()| ---@param opts table See |vim.lsp.diagnostic.goto_next()|
---@return table Next diagnostic ---@return table Next diagnostic
function M.get_next(opts) 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 then
if opts.severity then if opts.severity then
opts.severity = severity_lsp_to_vim(opts.severity) opts.severity = severity_lsp_to_vim(opts.severity)
elseif opts.severity_limit then 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
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 end
--- Return the pos, {row, col}, for the next diagnostic in the current buffer. --- 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()| ---@param opts table See |vim.lsp.diagnostic.goto_next()|
---@return table Next diagnostic position ---@return table Next diagnostic position
function M.get_next_pos(opts) 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 then
if opts.severity then if opts.severity then
opts.severity = severity_lsp_to_vim(opts.severity) opts.severity = severity_lsp_to_vim(opts.severity)
elseif opts.severity_limit then 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
end end
return vim.diagnostic.get_next_pos(opts) return vim.diagnostic.get_next_pos(opts)
@@ -452,12 +453,12 @@ end
--- ---
---@deprecated Prefer |vim.diagnostic.goto_next()| ---@deprecated Prefer |vim.diagnostic.goto_next()|
function M.goto_next(opts) 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 then
if opts.severity then if opts.severity then
opts.severity = severity_lsp_to_vim(opts.severity) opts.severity = severity_lsp_to_vim(opts.severity)
elseif opts.severity_limit then 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
end end
return vim.diagnostic.goto_next(opts) return vim.diagnostic.goto_next(opts)
@@ -476,10 +477,10 @@ end
--- - severity_limit (DiagnosticSeverity): --- - severity_limit (DiagnosticSeverity):
--- - Limit severity of diagnostics found. E.g. "Warning" means { "Error", "Warning" } will be valid. --- - Limit severity of diagnostics found. E.g. "Warning" means { "Error", "Warning" } will be valid.
function M.set_signs(diagnostics, bufnr, client_id, _, opts) 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) local namespace = M.get_namespace(client_id)
if opts and not opts.severity and opts.severity_limit then 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 end
vim.diagnostic._set_signs(namespace, bufnr, diagnostic_lsp_to_vim(diagnostics, bufnr, client_id), opts) vim.diagnostic._set_signs(namespace, bufnr, diagnostic_lsp_to_vim(diagnostics, bufnr, client_id), opts)
@@ -497,10 +498,10 @@ end
--- - severity_limit (DiagnosticSeverity): --- - severity_limit (DiagnosticSeverity):
--- - Limit severity of diagnostics found. E.g. "Warning" means { "Error", "Warning" } will be valid. --- - Limit severity of diagnostics found. E.g. "Warning" means { "Error", "Warning" } will be valid.
function M.set_underline(diagnostics, bufnr, client_id, _, opts) 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) local namespace = M.get_namespace(client_id)
if opts and not opts.severity and opts.severity_limit then 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 end
return vim.diagnostic._set_underline(namespace, bufnr, diagnostic_lsp_to_vim(diagnostics, bufnr, client_id), opts) return vim.diagnostic._set_underline(namespace, bufnr, diagnostic_lsp_to_vim(diagnostics, bufnr, client_id), opts)
end end
@@ -519,10 +520,10 @@ end
--- - severity_limit (DiagnosticSeverity): --- - severity_limit (DiagnosticSeverity):
--- - Limit severity of diagnostics found. E.g. "Warning" means { "Error", "Warning" } will be valid. --- - Limit severity of diagnostics found. E.g. "Warning" means { "Error", "Warning" } will be valid.
function M.set_virtual_text(diagnostics, bufnr, client_id, _, opts) 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) local namespace = M.get_namespace(client_id)
if opts and not opts.severity and opts.severity_limit then 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 end
return vim.diagnostic._set_virtual_text(namespace, bufnr, diagnostic_lsp_to_vim(diagnostics, bufnr, client_id), opts) return vim.diagnostic._set_virtual_text(namespace, bufnr, diagnostic_lsp_to_vim(diagnostics, bufnr, client_id), opts)
end end
@@ -538,7 +539,7 @@ end
---@return an array of [text, hl_group] arrays. This can be passed directly to ---@return an array of [text, hl_group] arrays. This can be passed directly to
--- the {virt_text} option of |nvim_buf_set_extmark()|. --- the {virt_text} option of |nvim_buf_set_extmark()|.
function M.get_virtual_text_chunks_for_line(bufnr, _, line_diags, opts) 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) return vim.diagnostic._get_virt_text_chunks(diagnostic_lsp_to_vim(line_diags, bufnr), opts)
end end
@@ -556,14 +557,14 @@ end
---@param position table|nil The (0,0)-indexed position ---@param position table|nil The (0,0)-indexed position
---@return table {popup_bufnr, win_id} ---@return table {popup_bufnr, win_id}
function M.show_position_diagnostics(opts, buf_nr, position) 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 = opts or {}
opts.scope = "cursor" opts.scope = 'cursor'
opts.pos = position opts.pos = position
if opts.severity then if opts.severity then
opts.severity = severity_lsp_to_vim(opts.severity) opts.severity = severity_lsp_to_vim(opts.severity)
elseif opts.severity_limit then 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.open_float(buf_nr, opts) return vim.diagnostic.open_float(buf_nr, opts)
end end
@@ -580,9 +581,9 @@ end
---@param client_id number|nil the client id ---@param client_id number|nil the client id
---@return table {popup_bufnr, win_id} ---@return table {popup_bufnr, win_id}
function M.show_line_diagnostics(opts, buf_nr, line_nr, client_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 = opts or {}
opts.scope = "line" opts.scope = 'line'
opts.pos = line_nr opts.pos = line_nr
if client_id then if client_id then
opts.namespace = M.get_namespace(client_id) opts.namespace = M.get_namespace(client_id)
@@ -604,7 +605,7 @@ end
--- client. The default is to redraw diagnostics for all attached --- client. The default is to redraw diagnostics for all attached
--- clients. --- clients.
function M.redraw(bufnr, client_id) 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) bufnr = get_bufnr(bufnr)
if not client_id then if not client_id then
return vim.lsp.for_each_buffer_client(bufnr, function(client) return vim.lsp.for_each_buffer_client(bufnr, function(client)
@@ -632,12 +633,12 @@ end
--- - {workspace}: (boolean, default true) --- - {workspace}: (boolean, default true)
--- - Set the list with workspace diagnostics --- - Set the list with workspace diagnostics
function M.set_qflist(opts) 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 {} opts = opts or {}
if opts.severity then if opts.severity then
opts.severity = severity_lsp_to_vim(opts.severity) opts.severity = severity_lsp_to_vim(opts.severity)
elseif opts.severity_limit then 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
if opts.client_id then if opts.client_id then
opts.client_id = nil opts.client_id = nil
@@ -664,12 +665,12 @@ end
--- - {workspace}: (boolean, default false) --- - {workspace}: (boolean, default false)
--- - Set the list with workspace diagnostics --- - Set the list with workspace diagnostics
function M.set_loclist(opts) 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 {} opts = opts or {}
if opts.severity then if opts.severity then
opts.severity = severity_lsp_to_vim(opts.severity) opts.severity = severity_lsp_to_vim(opts.severity)
elseif opts.severity_limit then 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
if opts.client_id then if opts.client_id then
opts.client_id = nil opts.client_id = nil
@@ -692,7 +693,7 @@ end
-- send diagnostic information and the client will still process it. The -- send diagnostic information and the client will still process it. The
-- diagnostics are simply not displayed to the user. -- diagnostics are simply not displayed to the user.
function M.disable(bufnr, client_id) 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 if not client_id then
return vim.lsp.for_each_buffer_client(bufnr, function(client) return vim.lsp.for_each_buffer_client(bufnr, function(client)
M.disable(bufnr, client.id) M.disable(bufnr, client.id)
@@ -713,7 +714,7 @@ end
--- client. The default is to enable diagnostics for all attached --- client. The default is to enable diagnostics for all attached
--- clients. --- clients.
function M.enable(bufnr, client_id) 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 if not client_id then
return vim.lsp.for_each_buffer_client(bufnr, function(client) return vim.lsp.for_each_buffer_client(bufnr, function(client)
M.enable(bufnr, client.id) M.enable(bufnr, client.id)

View File

@@ -1,6 +1,6 @@
local log = require 'vim.lsp.log' local log = require('vim.lsp.log')
local protocol = require 'vim.lsp.protocol' local protocol = require('vim.lsp.protocol')
local util = require 'vim.lsp.util' local util = require('vim.lsp.util')
local vim = vim local vim = vim
local api = vim.api local api = vim.api
@@ -12,8 +12,8 @@ local M = {}
--- Writes to error buffer. --- Writes to error buffer.
---@param ... (table of strings) Will be concatenated before being written ---@param ... (table of strings) Will be concatenated before being written
local function err_message(...) local function err_message(...)
vim.notify(table.concat(vim.tbl_flatten{...}), vim.log.levels.ERROR) vim.notify(table.concat(vim.tbl_flatten({ ... })), vim.log.levels.ERROR)
api.nvim_command("redraw") api.nvim_command('redraw')
end end
--see: https://microsoft.github.io/language-server-protocol/specifications/specification-current/#workspace_executeCommand --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 function progress_handler(_, result, ctx, _)
local client_id = ctx.client_id local client_id = ctx.client_id
local client = vim.lsp.get_client_by_id(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 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 return vim.NIL
end end
local val = result.value -- unspecified yet local val = result.value -- unspecified yet
local token = result.token -- string or number 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 then
if val.kind == 'begin' then if val.kind == 'begin' then
client.messages.progress[token] = { client.messages.progress[token] = {
@@ -42,11 +44,11 @@ local function progress_handler(_, result, ctx, _)
percentage = val.percentage, percentage = val.percentage,
} }
elseif val.kind == 'report' then elseif val.kind == 'report' then
client.messages.progress[token].message = val.message; client.messages.progress[token].message = val.message
client.messages.progress[token].percentage = val.percentage; client.messages.progress[token].percentage = val.percentage
elseif val.kind == 'end' then elseif val.kind == 'end' then
if client.messages.progress[token] == nil 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 else
client.messages.progress[token].message = val.message client.messages.progress[token].message = val.message
client.messages.progress[token].done = true client.messages.progress[token].done = true
@@ -57,7 +59,7 @@ local function progress_handler(_, result, ctx, _)
client.messages.progress[token].done = true client.messages.progress[token].done = true
end end
vim.api.nvim_command("doautocmd <nomodeline> User LspProgressUpdate") vim.api.nvim_command('doautocmd <nomodeline> User LspProgressUpdate')
end end
--see: https://microsoft.github.io/language-server-protocol/specifications/specification-current/#progress --see: https://microsoft.github.io/language-server-protocol/specifications/specification-current/#progress
@@ -68,9 +70,9 @@ M['window/workDoneProgress/create'] = function(_, result, ctx)
local client_id = ctx.client_id local client_id = ctx.client_id
local client = vim.lsp.get_client_by_id(client_id) local client = vim.lsp.get_client_by_id(client_id)
local token = result.token -- string or number local token = result.token -- string or number
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 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 return vim.NIL
end end
client.messages.progress[token] = {} client.messages.progress[token] = {}
@@ -79,14 +81,13 @@ end
--see: https://microsoft.github.io/language-server-protocol/specifications/specification-current/#window_showMessageRequest --see: https://microsoft.github.io/language-server-protocol/specifications/specification-current/#window_showMessageRequest
M['window/showMessageRequest'] = function(_, result) M['window/showMessageRequest'] = function(_, result)
local actions = result.actions local actions = result.actions
print(result.message) print(result.message)
local option_strings = {result.message, "\nRequest Actions:"} local option_strings = { result.message, '\nRequest Actions:' }
for i, action in ipairs(actions) do for i, action in ipairs(actions) do
local title = action.title:gsub('\r\n', '\\r\\n') local title = action.title:gsub('\r\n', '\\r\\n')
title = title:gsub('\n', '\\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 end
-- window/showMessageRequest can return either MessageActionItem[] or null. -- window/showMessageRequest can return either MessageActionItem[] or null.
@@ -101,11 +102,11 @@ end
--see: https://microsoft.github.io/language-server-protocol/specifications/specification-current/#client_registerCapability --see: https://microsoft.github.io/language-server-protocol/specifications/specification-current/#client_registerCapability
M['client/registerCapability'] = function(_, _, ctx) M['client/registerCapability'] = function(_, _, ctx)
local client_id = ctx.client_id local client_id = ctx.client_id
local warning_tpl = "The language server %s triggers a registerCapability ".. local warning_tpl = 'The language server %s triggers a registerCapability '
"handler despite dynamicRegistration set to false. ".. .. 'handler despite dynamicRegistration set to false. '
"Report upstream, this warning is harmless" .. 'Report upstream, this warning is harmless'
local client = vim.lsp.get_client_by_id(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)
local warning = string.format(warning_tpl, client_name) local warning = string.format(warning_tpl, client_name)
log.warn(warning) log.warn(warning)
return vim.NIL return vim.NIL
@@ -113,17 +114,19 @@ end
--see: https://microsoft.github.io/language-server-protocol/specifications/specification-current/#workspace_applyEdit --see: https://microsoft.github.io/language-server-protocol/specifications/specification-current/#workspace_applyEdit
M['workspace/applyEdit'] = function(_, workspace_edit, ctx) 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? -- TODO(ashkan) Do something more with label?
local client_id = ctx.client_id local client_id = ctx.client_id
local client = vim.lsp.get_client_by_id(client_id) local client = vim.lsp.get_client_by_id(client_id)
if workspace_edit.label then if workspace_edit.label then
print("Workspace edit", workspace_edit.label) print('Workspace edit', workspace_edit.label)
end end
local status, result = pcall(util.apply_workspace_edit, workspace_edit.edit, client.offset_encoding) local status, result = pcall(util.apply_workspace_edit, workspace_edit.edit, client.offset_encoding)
return { return {
applied = status; applied = status,
failureReason = result; failureReason = result,
} }
end end
@@ -132,7 +135,7 @@ M['workspace/configuration'] = function(_, result, ctx)
local client_id = ctx.client_id local client_id = ctx.client_id
local client = vim.lsp.get_client_by_id(client_id) local client = vim.lsp.get_client_by_id(client_id)
if not client then 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 return
end end
if not result.items then if not result.items then
@@ -158,7 +161,7 @@ M['workspace/workspaceFolders'] = function(_, _, ctx)
local client_id = ctx.client_id local client_id = ctx.client_id
local client = vim.lsp.get_client_by_id(client_id) local client = vim.lsp.get_client_by_id(client_id)
if not client then 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 return
end end
return client.workspace_folders or vim.NIL return client.workspace_folders or vim.NIL
@@ -172,7 +175,6 @@ M['textDocument/codeLens'] = function(...)
return require('vim.lsp.codelens').on_codelens(...) return require('vim.lsp.codelens').on_codelens(...)
end end
--see: https://microsoft.github.io/language-server-protocol/specifications/specification-current/#textDocument_references --see: https://microsoft.github.io/language-server-protocol/specifications/specification-current/#textDocument_references
M['textDocument/references'] = function(_, result, ctx, config) M['textDocument/references'] = function(_, result, ctx, config)
if not result or vim.tbl_isempty(result) then if not result or vim.tbl_isempty(result) then
@@ -182,23 +184,22 @@ M['textDocument/references'] = function(_, result, ctx, config)
config = config or {} config = config or {}
if config.loclist then if config.loclist then
vim.fn.setloclist(0, {}, ' ', { vim.fn.setloclist(0, {}, ' ', {
title = 'References'; title = 'References',
items = util.locations_to_items(result, client.offset_encoding); items = util.locations_to_items(result, client.offset_encoding),
context = ctx; context = ctx,
}) })
api.nvim_command("lopen") api.nvim_command('lopen')
else else
vim.fn.setqflist({}, ' ', { vim.fn.setqflist({}, ' ', {
title = 'References'; title = 'References',
items = util.locations_to_items(result, client.offset_encoding); items = util.locations_to_items(result, client.offset_encoding),
context = ctx; context = ctx,
}) })
api.nvim_command("botright copen") api.nvim_command('botright copen')
end end
end end
end end
---@private ---@private
--- Return a function that converts LSP responses to list items and opens the list --- 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 {} config = config or {}
if config.loclist then if config.loclist then
vim.fn.setloclist(0, {}, ' ', { vim.fn.setloclist(0, {}, ' ', {
title = title_fn(ctx); title = title_fn(ctx),
items = map_result(result, ctx.bufnr); items = map_result(result, ctx.bufnr),
context = ctx; context = ctx,
}) })
api.nvim_command("lopen") api.nvim_command('lopen')
else else
vim.fn.setqflist({}, ' ', { vim.fn.setqflist({}, ' ', {
title = title_fn(ctx); title = title_fn(ctx),
items = map_result(result, ctx.bufnr); items = map_result(result, ctx.bufnr),
context = ctx; context = ctx,
}) })
api.nvim_command("botright copen") api.nvim_command('botright copen')
end end
end end
end end
end end
--see: https://microsoft.github.io/language-server-protocol/specifications/specification-current/#textDocument_documentSymbol --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) 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) return string.format('Symbols in %s', fname)
end) end)
@@ -249,36 +249,44 @@ end)
--see: https://microsoft.github.io/language-server-protocol/specifications/specification-current/#textDocument_rename --see: https://microsoft.github.io/language-server-protocol/specifications/specification-current/#textDocument_rename
M['textDocument/rename'] = function(_, result, ctx, _) 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) local client = vim.lsp.get_client_by_id(ctx.client_id)
util.apply_workspace_edit(result, client.offset_encoding) util.apply_workspace_edit(result, client.offset_encoding)
end end
--see: https://microsoft.github.io/language-server-protocol/specifications/specification-current/#textDocument_rangeFormatting --see: https://microsoft.github.io/language-server-protocol/specifications/specification-current/#textDocument_rangeFormatting
M['textDocument/rangeFormatting'] = function(_, result, ctx, _) 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) local client = vim.lsp.get_client_by_id(ctx.client_id)
util.apply_text_edits(result, ctx.bufnr, client.offset_encoding) util.apply_text_edits(result, ctx.bufnr, client.offset_encoding)
end end
--see: https://microsoft.github.io/language-server-protocol/specifications/specification-current/#textDocument_formatting --see: https://microsoft.github.io/language-server-protocol/specifications/specification-current/#textDocument_formatting
M['textDocument/formatting'] = function(_, result, ctx, _) 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) local client = vim.lsp.get_client_by_id(ctx.client_id)
util.apply_text_edits(result, ctx.bufnr, client.offset_encoding) util.apply_text_edits(result, ctx.bufnr, client.offset_encoding)
end end
--see: https://microsoft.github.io/language-server-protocol/specifications/specification-current/#textDocument_completion --see: https://microsoft.github.io/language-server-protocol/specifications/specification-current/#textDocument_completion
M['textDocument/completion'] = function(_, result, _, _) 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 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 = assert(api.nvim_buf_get_lines(0, row - 1, row, false)[1])
local line_to_cursor = line:sub(col+1) local line_to_cursor = line:sub(col + 1)
local textMatch = vim.fn.match(line_to_cursor, '\\k*$') 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) 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 end
--- |lsp-handler| for the method "textDocument/hover" --- |lsp-handler| for the method "textDocument/hover"
@@ -307,7 +315,7 @@ function M.hover(_, result, ctx, config)
vim.notify('No information available') vim.notify('No information available')
return return
end end
return util.open_floating_preview(markdown_lines, "markdown", config) return util.open_floating_preview(markdown_lines, 'markdown', config)
end end
--see: https://microsoft.github.io/language-server-protocol/specifications/specification-current/#textDocument_hover --see: https://microsoft.github.io/language-server-protocol/specifications/specification-current/#textDocument_hover
@@ -335,9 +343,9 @@ local function location_handler(_, result, ctx, _)
if #result > 1 then if #result > 1 then
vim.fn.setqflist({}, ' ', { vim.fn.setqflist({}, ' ', {
title = 'LSP locations', 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 end
else else
util.jump_to_location(result, client.offset_encoding) util.jump_to_location(result, client.offset_encoding)
@@ -379,7 +387,7 @@ function M.signature_help(_, result, ctx, config)
return return
end end
local client = vim.lsp.get_client_by_id(ctx.client_id) 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 ft = api.nvim_buf_get_option(ctx.bufnr, 'filetype')
local lines, hl = util.convert_signature_help_to_markdown_lines(result, ft, triggers) local lines, hl = util.convert_signature_help_to_markdown_lines(result, ft, triggers)
lines = util.trim_empty_lines(lines) lines = util.trim_empty_lines(lines)
@@ -389,9 +397,9 @@ function M.signature_help(_, result, ctx, config)
end end
return return
end end
local fbuf, fwin = util.open_floating_preview(lines, "markdown", config) local fbuf, fwin = util.open_floating_preview(lines, 'markdown', config)
if hl then 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 end
return fbuf, fwin return fbuf, fwin
end end
@@ -401,10 +409,14 @@ M['textDocument/signatureHelp'] = M.signature_help
--see: https://microsoft.github.io/language-server-protocol/specifications/specification-current/#textDocument_documentHighlight --see: https://microsoft.github.io/language-server-protocol/specifications/specification-current/#textDocument_documentHighlight
M['textDocument/documentHighlight'] = function(_, result, ctx, _) 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_id = ctx.client_id
local client = vim.lsp.get_client_by_id(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) util.buf_highlight_references(ctx.bufnr, result, client.offset_encoding)
end end
@@ -417,7 +429,9 @@ end
---@returns `CallHierarchyOutgoingCall[]` if {direction} is `"to"`, ---@returns `CallHierarchyOutgoingCall[]` if {direction} is `"to"`,
local make_call_hierarchy_handler = function(direction) local make_call_hierarchy_handler = function(direction)
return function(_, result) return function(_, result)
if not result then return end if not result then
return
end
local items = {} local items = {}
for _, call_hierarchy_call in pairs(result) do for _, call_hierarchy_call in pairs(result) do
local call_hierarchy_item = call_hierarchy_call[direction] local call_hierarchy_item = call_hierarchy_call[direction]
@@ -430,8 +444,8 @@ local make_call_hierarchy_handler = function(direction)
}) })
end end
end end
vim.fn.setqflist({}, ' ', {title = 'LSP call hierarchy', items = items}) vim.fn.setqflist({}, ' ', { title = 'LSP call hierarchy', items = items })
api.nvim_command("botright copen") api.nvim_command('botright copen')
end end
end end
@@ -447,9 +461,9 @@ M['window/logMessage'] = function(_, result, ctx, _)
local message = result.message local message = result.message
local client_id = ctx.client_id local client_id = ctx.client_id
local client = vim.lsp.get_client_by_id(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 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 end
if message_type == protocol.MessageType.Error then if message_type == protocol.MessageType.Error then
log.error(message) log.error(message)
@@ -469,15 +483,15 @@ M['window/showMessage'] = function(_, result, ctx, _)
local message = result.message local message = result.message
local client_id = ctx.client_id local client_id = ctx.client_id
local client = vim.lsp.get_client_by_id(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 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 end
if message_type == protocol.MessageType.Error then if message_type == protocol.MessageType.Error then
err_message("LSP[", client_name, "] ", message) err_message('LSP[', client_name, '] ', message)
else else
local message_type_name = protocol.MessageType[message_type] 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 end
return result return result
end end
@@ -485,8 +499,12 @@ end
-- Add boilerplate error validation and logging for all of these. -- Add boilerplate error validation and logging for all of these.
for k, fn in pairs(M) do for k, fn in pairs(M) do
M[k] = function(err, result, ctx, config) M[k] = function(err, result, ctx, config)
local _ = log.trace() and log.trace('default_handler', ctx.method, { local _ = log.trace()
err = err, result = result, ctx=vim.inspect(ctx), config = config and log.trace('default_handler', ctx.method, {
err = err,
result = result,
ctx = vim.inspect(ctx),
config = config,
}) })
if err then if err then
@@ -499,7 +517,7 @@ for k, fn in pairs(M) do
-- Per LSP, don't show ContentModified error to the user. -- Per LSP, don't show ContentModified error to the user.
if err.code ~= protocol.ErrorCodes.ContentModified then if err.code ~= protocol.ErrorCodes.ContentModified then
local client = vim.lsp.get_client_by_id(ctx.client_id) 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) err_message(client_name .. ': ' .. tostring(err.code) .. ': ' .. err.message)
end end

View File

@@ -8,20 +8,19 @@ function M.check()
local log = require('vim.lsp.log') local log = require('vim.lsp.log')
local current_log_level = log.get_level() local current_log_level = log.get_level()
local log_level_string = log.levels[current_log_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 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 end
local log_path = vim.lsp.get_log_path() 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_size = vim.loop.fs_stat(log_path).size
local report_fn = (log_size / 1000000 > 100 and report_warn or report_info) 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 end
return M return M

View File

@@ -14,21 +14,23 @@ log.levels = vim.deepcopy(vim.log.levels)
-- Default log level is warn. -- Default log level is warn.
local current_log_level = log.levels.WARN local current_log_level = log.levels.WARN
local log_date_format = "%F %H:%M:%S" local log_date_format = '%F %H:%M:%S'
local format_func = function(arg) return vim.inspect(arg, {newline=''}) end local format_func = function(arg)
return vim.inspect(arg, { newline = '' })
end
do 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 ---@private
local function path_join(...) local function path_join(...)
return table.concat(vim.tbl_flatten{...}, path_sep) return table.concat(vim.tbl_flatten({ ... }), path_sep)
end end
local logfilename = path_join(vim.fn.stdpath('cache'), 'lsp.log') local logfilename = path_join(vim.fn.stdpath('cache'), 'lsp.log')
-- TODO: Ideally the directory should be created in open_logfile(), right -- TODO: Ideally the directory should be created in open_logfile(), right
-- before opening the log file, but open_logfile() can be called from libuv -- before opening the log file, but open_logfile() can be called from libuv
-- callbacks, where using fn.mkdir() is not allowed. -- callbacks, where using fn.mkdir() is not allowed.
vim.fn.mkdir(vim.fn.stdpath('cache'), "p") vim.fn.mkdir(vim.fn.stdpath('cache'), 'p')
--- Returns the log filename. --- Returns the log filename.
---@returns (string) log filename ---@returns (string) log filename
@@ -41,28 +43,28 @@ do
--- Opens log file. Returns true if file is open, false on error --- Opens log file. Returns true if file is open, false on error
local function open_logfile() local function open_logfile()
-- Try to open file only once -- Try to open file only once
if logfile then return true end if logfile then
if openerr then return false end 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 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) vim.notify(err_msg, vim.log.levels.ERROR)
return false return false
end end
local log_info = vim.loop.fs_stat(logfilename) local log_info = vim.loop.fs_stat(logfilename)
if log_info and log_info.size > 1e9 then if log_info and log_info.size > 1e9 then
local warn_msg = string.format( local warn_msg = string.format('LSP client log is large (%d MB): %s', log_info.size / (1000 * 1000), logfilename)
"LSP client log is large (%d MB): %s",
log_info.size / (1000 * 1000),
logfilename
)
vim.notify(warn_msg) vim.notify(warn_msg)
end end
-- Start message for logging -- 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 return true
end end
@@ -83,24 +85,36 @@ do
-- ``` -- ```
-- --
-- This way you can avoid string allocations if the log level isn't high enough. -- 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(...) log[level:lower()] = function(...)
local argc = select("#", ...) local argc = select('#', ...)
if levelnr < current_log_level then return false end if levelnr < current_log_level then
if argc == 0 then return true end return false
if not open_logfile() then return false end end
local info = debug.getinfo(2, "Sl") if argc == 0 then
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) 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 } local parts = { header }
for i = 1, argc do for i = 1, argc do
local arg = select(i, ...) local arg = select(i, ...)
if arg == nil then if arg == nil then
table.insert(parts, "nil") table.insert(parts, 'nil')
else else
table.insert(parts, format_func(arg)) table.insert(parts, format_func(arg))
end end
end end
logfile:write(table.concat(parts, '\t'), "\n") logfile:write(table.concat(parts, '\t'), '\n')
logfile:flush() logfile:flush()
end end
end end
@@ -115,10 +129,10 @@ vim.tbl_add_reverse_lookup(log.levels)
---@param level (string or number) One of `vim.lsp.log.levels` ---@param level (string or number) One of `vim.lsp.log.levels`
function log.set_level(level) function log.set_level(level)
if type(level) == 'string' then 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 else
assert(type(level) == 'number', "level must be a number or string") assert(type(level) == 'number', 'level must be a number or string')
assert(log.levels[level], string.format("Invalid log level: %d", level)) assert(log.levels[level], string.format('Invalid log level: %d', level))
current_log_level = level current_log_level = level
end end
end end
@@ -132,7 +146,7 @@ end
--- Sets formatting function used to format logs --- Sets formatting function used to format logs
---@param handle function function to apply to logging arguments, pass vim.inspect for multi-line formatting ---@param handle function function to apply to logging arguments, pass vim.inspect for multi-line formatting
function log.set_format_func(handle) 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 format_func = handle
end end

View File

@@ -23,150 +23,150 @@ end
local constants = { local constants = {
DiagnosticSeverity = { DiagnosticSeverity = {
-- Reports an error. -- Reports an error.
Error = 1; Error = 1,
-- Reports a warning. -- Reports a warning.
Warning = 2; Warning = 2,
-- Reports an information. -- Reports an information.
Information = 3; Information = 3,
-- Reports a hint. -- Reports a hint.
Hint = 4; Hint = 4,
}; },
DiagnosticTag = { DiagnosticTag = {
-- Unused or unnecessary code -- Unused or unnecessary code
Unnecessary = 1; Unnecessary = 1,
-- Deprecated or obsolete code -- Deprecated or obsolete code
Deprecated = 2; Deprecated = 2,
}; },
MessageType = { MessageType = {
-- An error message. -- An error message.
Error = 1; Error = 1,
-- A warning message. -- A warning message.
Warning = 2; Warning = 2,
-- An information message. -- An information message.
Info = 3; Info = 3,
-- A log message. -- A log message.
Log = 4; Log = 4,
}; },
-- The file event type. -- The file event type.
FileChangeType = { FileChangeType = {
-- The file got created. -- The file got created.
Created = 1; Created = 1,
-- The file got changed. -- The file got changed.
Changed = 2; Changed = 2,
-- The file got deleted. -- The file got deleted.
Deleted = 3; Deleted = 3,
}; },
-- The kind of a completion entry. -- The kind of a completion entry.
CompletionItemKind = { CompletionItemKind = {
Text = 1; Text = 1,
Method = 2; Method = 2,
Function = 3; Function = 3,
Constructor = 4; Constructor = 4,
Field = 5; Field = 5,
Variable = 6; Variable = 6,
Class = 7; Class = 7,
Interface = 8; Interface = 8,
Module = 9; Module = 9,
Property = 10; Property = 10,
Unit = 11; Unit = 11,
Value = 12; Value = 12,
Enum = 13; Enum = 13,
Keyword = 14; Keyword = 14,
Snippet = 15; Snippet = 15,
Color = 16; Color = 16,
File = 17; File = 17,
Reference = 18; Reference = 18,
Folder = 19; Folder = 19,
EnumMember = 20; EnumMember = 20,
Constant = 21; Constant = 21,
Struct = 22; Struct = 22,
Event = 23; Event = 23,
Operator = 24; Operator = 24,
TypeParameter = 25; TypeParameter = 25,
}; },
-- How a completion was triggered -- How a completion was triggered
CompletionTriggerKind = { CompletionTriggerKind = {
-- Completion was triggered by typing an identifier (24x7 code -- Completion was triggered by typing an identifier (24x7 code
-- complete), manual invocation (e.g Ctrl+Space) or via API. -- complete), manual invocation (e.g Ctrl+Space) or via API.
Invoked = 1; Invoked = 1,
-- Completion was triggered by a trigger character specified by -- Completion was triggered by a trigger character specified by
-- the `triggerCharacters` properties of the `CompletionRegistrationOptions`. -- the `triggerCharacters` properties of the `CompletionRegistrationOptions`.
TriggerCharacter = 2; TriggerCharacter = 2,
-- Completion was re-triggered as the current completion list is incomplete. -- Completion was re-triggered as the current completion list is incomplete.
TriggerForIncompleteCompletions = 3; TriggerForIncompleteCompletions = 3,
}; },
-- A document highlight kind. -- A document highlight kind.
DocumentHighlightKind = { DocumentHighlightKind = {
-- A textual occurrence. -- A textual occurrence.
Text = 1; Text = 1,
-- Read-access of a symbol, like reading a variable. -- Read-access of a symbol, like reading a variable.
Read = 2; Read = 2,
-- Write-access of a symbol, like writing to a variable. -- Write-access of a symbol, like writing to a variable.
Write = 3; Write = 3,
}; },
-- A symbol kind. -- A symbol kind.
SymbolKind = { SymbolKind = {
File = 1; File = 1,
Module = 2; Module = 2,
Namespace = 3; Namespace = 3,
Package = 4; Package = 4,
Class = 5; Class = 5,
Method = 6; Method = 6,
Property = 7; Property = 7,
Field = 8; Field = 8,
Constructor = 9; Constructor = 9,
Enum = 10; Enum = 10,
Interface = 11; Interface = 11,
Function = 12; Function = 12,
Variable = 13; Variable = 13,
Constant = 14; Constant = 14,
String = 15; String = 15,
Number = 16; Number = 16,
Boolean = 17; Boolean = 17,
Array = 18; Array = 18,
Object = 19; Object = 19,
Key = 20; Key = 20,
Null = 21; Null = 21,
EnumMember = 22; EnumMember = 22,
Struct = 23; Struct = 23,
Event = 24; Event = 24,
Operator = 25; Operator = 25,
TypeParameter = 26; TypeParameter = 26,
}; },
-- Represents reasons why a text document is saved. -- Represents reasons why a text document is saved.
TextDocumentSaveReason = { TextDocumentSaveReason = {
-- Manually triggered, e.g. by the user pressing save, by starting debugging, -- Manually triggered, e.g. by the user pressing save, by starting debugging,
-- or by an API call. -- or by an API call.
Manual = 1; Manual = 1,
-- Automatic after a delay. -- Automatic after a delay.
AfterDelay = 2; AfterDelay = 2,
-- When the editor lost focus. -- When the editor lost focus.
FocusOut = 3; FocusOut = 3,
}; },
ErrorCodes = { ErrorCodes = {
-- Defined by JSON RPC -- Defined by JSON RPC
ParseError = -32700; ParseError = -32700,
InvalidRequest = -32600; InvalidRequest = -32600,
MethodNotFound = -32601; MethodNotFound = -32601,
InvalidParams = -32602; InvalidParams = -32602,
InternalError = -32603; InternalError = -32603,
serverErrorStart = -32099; serverErrorStart = -32099,
serverErrorEnd = -32000; serverErrorEnd = -32000,
ServerNotInitialized = -32002; ServerNotInitialized = -32002,
UnknownErrorCode = -32001; UnknownErrorCode = -32001,
-- Defined by the protocol. -- Defined by the protocol.
RequestCancelled = -32800; RequestCancelled = -32800,
ContentModified = -32801; ContentModified = -32801,
}; },
-- Describes the content type that a client supports in various -- Describes the content type that a client supports in various
-- result literals like `Hover`, `ParameterInfo` or `CompletionItem`. -- result literals like `Hover`, `ParameterInfo` or `CompletionItem`.
@@ -175,88 +175,88 @@ local constants = {
-- are reserved for internal usage. -- are reserved for internal usage.
MarkupKind = { MarkupKind = {
-- Plain text is supported as a content format -- Plain text is supported as a content format
PlainText = 'plaintext'; PlainText = 'plaintext',
-- Markdown is supported as a content format -- Markdown is supported as a content format
Markdown = 'markdown'; Markdown = 'markdown',
}; },
ResourceOperationKind = { ResourceOperationKind = {
-- Supports creating new files and folders. -- Supports creating new files and folders.
Create = 'create'; Create = 'create',
-- Supports renaming existing files and folders. -- Supports renaming existing files and folders.
Rename = 'rename'; Rename = 'rename',
-- Supports deleting existing files and folders. -- Supports deleting existing files and folders.
Delete = 'delete'; Delete = 'delete',
}; },
FailureHandlingKind = { FailureHandlingKind = {
-- Applying the workspace change is simply aborted if one of the changes provided -- Applying the workspace change is simply aborted if one of the changes provided
-- fails. All operations executed before the failing operation stay executed. -- fails. All operations executed before the failing operation stay executed.
Abort = 'abort'; Abort = 'abort',
-- All operations are executed transactionally. That means they either all -- All operations are executed transactionally. That means they either all
-- succeed or no changes at all are applied to the workspace. -- 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 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 -- If resource changes (create, rename or delete file) are part of the change the failure
-- handling strategy is abort. -- handling strategy is abort.
TextOnlyTransactional = 'textOnlyTransactional'; TextOnlyTransactional = 'textOnlyTransactional',
-- The client tries to undo the operations already executed. But there is no -- The client tries to undo the operations already executed. But there is no
-- guarantee that this succeeds. -- guarantee that this succeeds.
Undo = 'undo'; Undo = 'undo',
}; },
-- Known error codes for an `InitializeError`; -- Known error codes for an `InitializeError`;
InitializeError = { InitializeError = {
-- If the protocol version provided by the client can't be handled by the server. -- 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 -- @deprecated This initialize error got replaced by client capabilities. There is
-- no version handshake in version 3.0x -- no version handshake in version 3.0x
unknownProtocolVersion = 1; unknownProtocolVersion = 1,
}; },
-- Defines how the host (editor) should sync document changes to the language server. -- Defines how the host (editor) should sync document changes to the language server.
TextDocumentSyncKind = { TextDocumentSyncKind = {
-- Documents should not be synced at all. -- Documents should not be synced at all.
None = 0; None = 0,
-- Documents are synced by always sending the full content -- Documents are synced by always sending the full content
-- of the document. -- of the document.
Full = 1; Full = 1,
-- Documents are synced by sending the full content on open. -- Documents are synced by sending the full content on open.
-- After that only incremental updates to the document are -- After that only incremental updates to the document are
-- send. -- send.
Incremental = 2; Incremental = 2,
}; },
WatchKind = { WatchKind = {
-- Interested in create events. -- Interested in create events.
Create = 1; Create = 1,
-- Interested in change events -- Interested in change events
Change = 2; Change = 2,
-- Interested in delete events -- Interested in delete events
Delete = 4; Delete = 4,
}; },
-- Defines whether the insert text in a completion item should be interpreted as -- Defines whether the insert text in a completion item should be interpreted as
-- plain text or a snippet. -- plain text or a snippet.
InsertTextFormat = { InsertTextFormat = {
-- The primary text to be inserted is treated as a plain string. -- 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. -- The primary text to be inserted is treated as a snippet.
-- --
-- A snippet can define tab stops and placeholders with `$1`, `$2` -- A snippet can define tab stops and placeholders with `$1`, `$2`
-- and `${3:foo};`. `$0` defines the final tab stop, it defaults to -- and `${3:foo};`. `$0` defines the final tab stop, it defaults to
-- the end of the snippet. Placeholders with equal identifiers are linked, -- the end of the snippet. Placeholders with equal identifiers are linked,
-- that is typing in one will update others too. -- that is typing in one will update others too.
Snippet = 2; Snippet = 2,
}; },
-- A set of predefined code action kinds -- A set of predefined code action kinds
CodeActionKind = { CodeActionKind = {
-- Empty kind. -- Empty kind.
Empty = ''; Empty = '',
-- Base kind for quickfix actions -- Base kind for quickfix actions
QuickFix = 'quickfix'; QuickFix = 'quickfix',
-- Base kind for refactoring actions -- Base kind for refactoring actions
Refactor = 'refactor'; Refactor = 'refactor',
-- Base kind for refactoring extraction actions -- Base kind for refactoring extraction actions
-- --
-- Example extract actions: -- Example extract actions:
@@ -266,7 +266,7 @@ local constants = {
-- - Extract variable -- - Extract variable
-- - Extract interface from class -- - Extract interface from class
-- - ... -- - ...
RefactorExtract = 'refactor.extract'; RefactorExtract = 'refactor.extract',
-- Base kind for refactoring inline actions -- Base kind for refactoring inline actions
-- --
-- Example inline actions: -- Example inline actions:
@@ -275,7 +275,7 @@ local constants = {
-- - Inline variable -- - Inline variable
-- - Inline constant -- - Inline constant
-- - ... -- - ...
RefactorInline = 'refactor.inline'; RefactorInline = 'refactor.inline',
-- Base kind for refactoring rewrite actions -- Base kind for refactoring rewrite actions
-- --
-- Example rewrite actions: -- Example rewrite actions:
@@ -286,14 +286,14 @@ local constants = {
-- - Make method static -- - Make method static
-- - Move method to base class -- - Move method to base class
-- - ... -- - ...
RefactorRewrite = 'refactor.rewrite'; RefactorRewrite = 'refactor.rewrite',
-- Base kind for source actions -- Base kind for source actions
-- --
-- Source code actions apply to the entire file. -- Source code actions apply to the entire file.
Source = 'source'; Source = 'source',
-- Base kind for an organize imports source action -- Base kind for an organize imports source action
SourceOrganizeImports = 'source.organizeImports'; SourceOrganizeImports = 'source.organizeImports',
}; },
} }
for k, v in pairs(constants) do for k, v in pairs(constants) do
@@ -620,19 +620,19 @@ function protocol.make_client_capabilities()
return { return {
textDocument = { textDocument = {
synchronization = { synchronization = {
dynamicRegistration = false; dynamicRegistration = false,
-- TODO(ashkan) Send textDocument/willSave before saving (BufWritePre) -- TODO(ashkan) Send textDocument/willSave before saving (BufWritePre)
willSave = false; willSave = false,
-- TODO(ashkan) Implement textDocument/willSaveWaitUntil -- TODO(ashkan) Implement textDocument/willSaveWaitUntil
willSaveWaitUntil = false; willSaveWaitUntil = false,
-- Send textDocument/didSave after saving (BufWritePost) -- Send textDocument/didSave after saving (BufWritePost)
didSave = true; didSave = true,
}; },
codeAction = { codeAction = {
dynamicRegistration = false; dynamicRegistration = false,
codeActionLiteralSupport = { codeActionLiteralSupport = {
codeActionKind = { codeActionKind = {
@@ -640,138 +640,146 @@ function protocol.make_client_capabilities()
local res = vim.tbl_values(protocol.CodeActionKind) local res = vim.tbl_values(protocol.CodeActionKind)
table.sort(res) table.sort(res)
return res return res
end)(); end)(),
}; },
}; },
isPreferredSupport = true; isPreferredSupport = true,
dataSupport = true; dataSupport = true,
resolveSupport = { resolveSupport = {
properties = { 'edit', } properties = { 'edit' },
}; },
}; },
completion = { completion = {
dynamicRegistration = false; dynamicRegistration = false,
completionItem = { completionItem = {
-- Until we can actually expand snippet, move cursor and allow for true snippet experience, -- Until we can actually expand snippet, move cursor and allow for true snippet experience,
-- this should be disabled out of the box. -- this should be disabled out of the box.
-- However, users can turn this back on if they have a snippet plugin. -- However, users can turn this back on if they have a snippet plugin.
snippetSupport = false; snippetSupport = false,
commitCharactersSupport = false; commitCharactersSupport = false,
preselectSupport = false; preselectSupport = false,
deprecatedSupport = false; deprecatedSupport = false,
documentationFormat = { protocol.MarkupKind.Markdown; protocol.MarkupKind.PlainText }; documentationFormat = { protocol.MarkupKind.Markdown, protocol.MarkupKind.PlainText },
}; },
completionItemKind = { completionItemKind = {
valueSet = (function() valueSet = (function()
local res = {} local res = {}
for k in ipairs(protocol.CompletionItemKind) do 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 end
return res return res
end)(); end)(),
}; },
-- TODO(tjdevries): Implement this -- TODO(tjdevries): Implement this
contextSupport = false; contextSupport = false,
}; },
declaration = { declaration = {
linkSupport = true; linkSupport = true,
}; },
definition = { definition = {
linkSupport = true; linkSupport = true,
}; },
implementation = { implementation = {
linkSupport = true; linkSupport = true,
}; },
typeDefinition = { typeDefinition = {
linkSupport = true; linkSupport = true,
}; },
hover = { hover = {
dynamicRegistration = false; dynamicRegistration = false,
contentFormat = { protocol.MarkupKind.Markdown; protocol.MarkupKind.PlainText }; contentFormat = { protocol.MarkupKind.Markdown, protocol.MarkupKind.PlainText },
}; },
signatureHelp = { signatureHelp = {
dynamicRegistration = false; dynamicRegistration = false,
signatureInformation = { signatureInformation = {
activeParameterSupport = true; activeParameterSupport = true,
documentationFormat = { protocol.MarkupKind.Markdown; protocol.MarkupKind.PlainText }; documentationFormat = { protocol.MarkupKind.Markdown, protocol.MarkupKind.PlainText },
parameterInformation = { parameterInformation = {
labelOffsetSupport = true; labelOffsetSupport = true,
}; },
}; },
}; },
references = { references = {
dynamicRegistration = false; dynamicRegistration = false,
}; },
documentHighlight = { documentHighlight = {
dynamicRegistration = false dynamicRegistration = false,
}; },
documentSymbol = { documentSymbol = {
dynamicRegistration = false; dynamicRegistration = false,
symbolKind = { symbolKind = {
valueSet = (function() valueSet = (function()
local res = {} local res = {}
for k in ipairs(protocol.SymbolKind) do 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 end
return res return res
end)(); end)(),
}; },
hierarchicalDocumentSymbolSupport = true; hierarchicalDocumentSymbolSupport = true,
}; },
rename = { rename = {
dynamicRegistration = false; dynamicRegistration = false,
prepareSupport = true; prepareSupport = true,
}; },
publishDiagnostics = { publishDiagnostics = {
relatedInformation = true; relatedInformation = true,
tagSupport = { tagSupport = {
valueSet = (function() valueSet = (function()
local res = {} local res = {}
for k in ipairs(protocol.DiagnosticTag) do 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 end
return res return res
end)(); end)(),
}; },
}; },
}; },
workspace = { workspace = {
symbol = { symbol = {
dynamicRegistration = false; dynamicRegistration = false,
symbolKind = { symbolKind = {
valueSet = (function() valueSet = (function()
local res = {} local res = {}
for k in ipairs(protocol.SymbolKind) do 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 end
return res return res
end)(); end)(),
}; },
hierarchicalWorkspaceSymbolSupport = true; hierarchicalWorkspaceSymbolSupport = true,
}; },
workspaceFolders = true; workspaceFolders = true,
applyEdit = true; applyEdit = true,
workspaceEdit = { workspaceEdit = {
resourceOperations = {'rename', 'create', 'delete',}, resourceOperations = { 'rename', 'create', 'delete' },
}; },
}; },
callHierarchy = { callHierarchy = {
dynamicRegistration = false; dynamicRegistration = false,
}; },
experimental = nil; experimental = nil,
window = { window = {
workDoneProgress = true; workDoneProgress = true,
showMessage = { showMessage = {
messageActionItem = { messageActionItem = {
additionalPropertiesSupport = false; additionalPropertiesSupport = false,
}; },
}; },
showDocument = { showDocument = {
support = false; support = false,
}; },
}; },
} }
end end
@@ -791,12 +799,12 @@ function protocol.resolve_capabilities(server_capabilities)
willSaveWaitUntil = false, willSaveWaitUntil = false,
save = { save = {
includeText = false, includeText = false,
} },
} }
elseif type(textDocumentSync) == 'number' then elseif type(textDocumentSync) == 'number' then
-- Backwards compatibility -- Backwards compatibility
if not TextDocumentSyncKind[textDocumentSync] then if not TextDocumentSyncKind[textDocumentSync] then
return nil, "Invalid server TextDocumentSyncKind for textDocumentSync" return nil, 'Invalid server TextDocumentSyncKind for textDocumentSync'
end end
server_capabilities.textDocumentSync = { server_capabilities.textDocumentSync = {
openClose = true, openClose = true,
@@ -805,10 +813,10 @@ function protocol.resolve_capabilities(server_capabilities)
willSaveWaitUntil = false, willSaveWaitUntil = false,
save = { save = {
includeText = false, includeText = false,
} },
} }
elseif type(textDocumentSync) ~= 'table' then 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 end
return server_capabilities return server_capabilities
end end
@@ -827,39 +835,41 @@ function protocol._resolve_capabilities_compat(server_capabilities)
if textDocumentSync == nil then if textDocumentSync == nil then
-- Defaults if omitted. -- Defaults if omitted.
text_document_sync_properties = { text_document_sync_properties = {
text_document_open_close = false; text_document_open_close = false,
text_document_did_change = TextDocumentSyncKind.None; text_document_did_change = TextDocumentSyncKind.None,
-- text_document_did_change = false; -- text_document_did_change = false;
text_document_will_save = false; text_document_will_save = false,
text_document_will_save_wait_until = false; text_document_will_save_wait_until = false,
text_document_save = false; text_document_save = false,
text_document_save_include_text = false; text_document_save_include_text = false,
} }
elseif type(textDocumentSync) == 'number' then elseif type(textDocumentSync) == 'number' then
-- Backwards compatibility -- Backwards compatibility
if not TextDocumentSyncKind[textDocumentSync] then if not TextDocumentSyncKind[textDocumentSync] then
return nil, "Invalid server TextDocumentSyncKind for textDocumentSync" return nil, 'Invalid server TextDocumentSyncKind for textDocumentSync'
end end
text_document_sync_properties = { text_document_sync_properties = {
text_document_open_close = true; text_document_open_close = true,
text_document_did_change = textDocumentSync; text_document_did_change = textDocumentSync,
text_document_will_save = false; text_document_will_save = false,
text_document_will_save_wait_until = false; text_document_will_save_wait_until = false,
text_document_save = true; text_document_save = true,
text_document_save_include_text = false; text_document_save_include_text = false,
} }
elseif type(textDocumentSync) == 'table' then elseif type(textDocumentSync) == 'table' then
text_document_sync_properties = { text_document_sync_properties = {
text_document_open_close = if_nil(textDocumentSync.openClose, false); text_document_open_close = if_nil(textDocumentSync.openClose, false),
text_document_did_change = if_nil(textDocumentSync.change, TextDocumentSyncKind.None); text_document_did_change = if_nil(textDocumentSync.change, TextDocumentSyncKind.None),
text_document_will_save = if_nil(textDocumentSync.willSave, false); text_document_will_save = if_nil(textDocumentSync.willSave, false),
text_document_will_save_wait_until = if_nil(textDocumentSync.willSaveWaitUntil, false); text_document_will_save_wait_until = if_nil(textDocumentSync.willSaveWaitUntil, false),
text_document_save = if_nil(textDocumentSync.save, false); text_document_save = if_nil(textDocumentSync.save, false),
text_document_save_include_text = if_nil(type(textDocumentSync.save) == 'table' text_document_save_include_text = if_nil(
and textDocumentSync.save.includeText, false); type(textDocumentSync.save) == 'table' and textDocumentSync.save.includeText,
false
),
} }
else 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
end end
general_properties.completion = server_capabilities.completionProvider ~= nil 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 = true
general_properties.code_lens_resolve = server_capabilities.codeLensProvider.resolveProvider or false general_properties.code_lens_resolve = server_capabilities.codeLensProvider.resolveProvider or false
else else
error("The server sent invalid codeLensProvider") error('The server sent invalid codeLensProvider')
end end
if server_capabilities.codeActionProvider == nil then if server_capabilities.codeActionProvider == nil then
general_properties.code_action = false general_properties.code_action = false
elseif type(server_capabilities.codeActionProvider) == 'boolean' elseif
or type(server_capabilities.codeActionProvider) == 'table' then type(server_capabilities.codeActionProvider) == 'boolean'
or type(server_capabilities.codeActionProvider) == 'table'
then
general_properties.code_action = server_capabilities.codeActionProvider general_properties.code_action = server_capabilities.codeActionProvider
else else
error("The server sent invalid codeActionProvider") error('The server sent invalid codeActionProvider')
end end
if server_capabilities.declarationProvider == nil then if server_capabilities.declarationProvider == nil then
@@ -908,7 +920,7 @@ function protocol._resolve_capabilities_compat(server_capabilities)
elseif type(server_capabilities.declarationProvider) == 'table' then elseif type(server_capabilities.declarationProvider) == 'table' then
general_properties.declaration = server_capabilities.declarationProvider general_properties.declaration = server_capabilities.declarationProvider
else else
error("The server sent invalid declarationProvider") error('The server sent invalid declarationProvider')
end end
if server_capabilities.typeDefinitionProvider == nil then if server_capabilities.typeDefinitionProvider == nil then
@@ -918,7 +930,7 @@ function protocol._resolve_capabilities_compat(server_capabilities)
elseif type(server_capabilities.typeDefinitionProvider) == 'table' then elseif type(server_capabilities.typeDefinitionProvider) == 'table' then
general_properties.type_definition = server_capabilities.typeDefinitionProvider general_properties.type_definition = server_capabilities.typeDefinitionProvider
else else
error("The server sent invalid typeDefinitionProvider") error('The server sent invalid typeDefinitionProvider')
end end
if server_capabilities.implementationProvider == nil then if server_capabilities.implementationProvider == nil then
@@ -928,7 +940,7 @@ function protocol._resolve_capabilities_compat(server_capabilities)
elseif type(server_capabilities.implementationProvider) == 'table' then elseif type(server_capabilities.implementationProvider) == 'table' then
general_properties.implementation = server_capabilities.implementationProvider general_properties.implementation = server_capabilities.implementationProvider
else else
error("The server sent invalid implementationProvider") error('The server sent invalid implementationProvider')
end end
local workspace = server_capabilities.workspace local workspace = server_capabilities.workspace
@@ -937,43 +949,43 @@ function protocol._resolve_capabilities_compat(server_capabilities)
-- Defaults if omitted. -- Defaults if omitted.
workspace_properties = { workspace_properties = {
workspace_folder_properties = { workspace_folder_properties = {
supported = false; supported = false,
changeNotifications=false; changeNotifications = false,
} },
} }
elseif type(workspace.workspaceFolders) == 'table' then elseif type(workspace.workspaceFolders) == 'table' then
workspace_properties = { workspace_properties = {
workspace_folder_properties = { workspace_folder_properties = {
supported = if_nil(workspace.workspaceFolders.supported, false); supported = if_nil(workspace.workspaceFolders.supported, false),
changeNotifications = if_nil(workspace.workspaceFolders.changeNotifications, false); changeNotifications = if_nil(workspace.workspaceFolders.changeNotifications, false),
},
}
} }
else else
error("The server sent invalid workspace") error('The server sent invalid workspace')
end end
local signature_help_properties local signature_help_properties
if server_capabilities.signatureHelpProvider == nil then if server_capabilities.signatureHelpProvider == nil then
signature_help_properties = { signature_help_properties = {
signature_help = false; signature_help = false,
signature_help_trigger_characters = {}; signature_help_trigger_characters = {},
} }
elseif type(server_capabilities.signatureHelpProvider) == 'table' then elseif type(server_capabilities.signatureHelpProvider) == 'table' then
signature_help_properties = { signature_help_properties = {
signature_help = true; signature_help = true,
-- The characters that trigger signature help automatically. -- 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 else
error("The server sent invalid signatureHelpProvider") error('The server sent invalid signatureHelpProvider')
end end
local capabilities = vim.tbl_extend("error" local capabilities = vim.tbl_extend(
, text_document_sync_properties 'error',
, signature_help_properties text_document_sync_properties,
, workspace_properties signature_help_properties,
, general_properties workspace_properties,
general_properties
) )
return capabilities return capabilities

View File

@@ -32,9 +32,9 @@ local function env_merge(env)
-- Merge. -- Merge.
env = vim.tbl_extend('force', vim.fn.environ(), env) env = vim.tbl_extend('force', vim.fn.environ(), env)
local final_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') assert(type(k) == 'string', 'env must be a dict')
table.insert(final_env, k..'='..tostring(v)) table.insert(final_env, k .. '=' .. tostring(v))
end end
return final_env return final_env
end end
@@ -45,10 +45,12 @@ end
---@param encoded_message (string) ---@param encoded_message (string)
---@returns (table) table containing encoded message and `Content-Length` attribute ---@returns (table) table containing encoded message and `Content-Length` attribute
local function format_message_with_content_length(encoded_message) local function format_message_with_content_length(encoded_message)
return table.concat { return table.concat({
'Content-Length: '; tostring(#encoded_message); '\r\n\r\n'; 'Content-Length: ',
encoded_message; tostring(#encoded_message),
} '\r\n\r\n',
encoded_message,
})
end end
---@private ---@private
@@ -65,23 +67,25 @@ local function parse_headers(header)
if line == '' then if line == '' then
break break
end end
local key, value = line:match("^%s*(%S+)%s*:%s*(.+)%s*$") local key, value = line:match('^%s*(%S+)%s*:%s*(.+)%s*$')
if key then if key then
key = key:lower():gsub('%-', '_') key = key:lower():gsub('%-', '_')
headers[key] = value headers[key] = value
else else
local _ = log.error() and log.error("invalid header line %q", line) local _ = log.error() and log.error('invalid header line %q', line)
error(string.format("invalid header line %q", line)) error(string.format('invalid header line %q', line))
end end
end end
headers.content_length = tonumber(headers.content_length) 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 return headers
end end
-- This is the start of any possible header patterns. The gsub converts it to a -- This is the start of any possible header patterns. The gsub converts it to a
-- case insensitive pattern. -- 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 ---@private
--- The actual workhorse. --- The actual workhorse.
@@ -100,17 +104,16 @@ local function request_parser_loop()
-- be searching for. -- be searching for.
-- TODO(ashkan) I'd like to remove this, but it seems permanent :( -- TODO(ashkan) I'd like to remove this, but it seems permanent :(
local buffer_start = buffer:find(header_start_pattern) 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 local content_length = headers.content_length
-- Use table instead of just string to buffer the message. It prevents -- Use table instead of just string to buffer the message. It prevents
-- a ton of strings allocating. -- a ton of strings allocating.
-- ref. http://www.lua.org/pil/11.6.html -- 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] local body_length = #body_chunks[1]
-- Keep waiting for data until we have enough. -- Keep waiting for data until we have enough.
while body_length < content_length do while body_length < content_length do
local chunk = coroutine.yield() local chunk = coroutine.yield() or error('Expected more data for the body. The server may have died.') -- TODO hmm.
or error("Expected more data for the body. The server may have died.") -- TODO hmm.
table.insert(body_chunks, chunk) table.insert(body_chunks, chunk)
body_length = body_length + #chunk body_length = body_length + #chunk
end end
@@ -123,25 +126,24 @@ local function request_parser_loop()
end end
local body = table.concat(body_chunks) local body = table.concat(body_chunks)
-- Yield our data. -- Yield our data.
buffer = rest..(coroutine.yield(headers, body) buffer = rest
or error("Expected more data for the body. The server may have died.")) -- TODO hmm. .. (coroutine.yield(headers, body) or error('Expected more data for the body. The server may have died.')) -- TODO hmm.
else else
-- Get more data since we don't have enough. -- Get more data since we don't have enough.
buffer = buffer..(coroutine.yield() buffer = buffer .. (coroutine.yield() or error('Expected more data for the header. The server may have died.')) -- TODO hmm.
or error("Expected more data for the header. The server may have died.")) -- TODO hmm.
end end
end end
end end
--- Mapping of error codes used by the client --- Mapping of error codes used by the client
local client_errors = { local client_errors = {
INVALID_SERVER_MESSAGE = 1; INVALID_SERVER_MESSAGE = 1,
INVALID_SERVER_JSON = 2; INVALID_SERVER_JSON = 2,
NO_RESULT_CALLBACK_FOUND = 3; NO_RESULT_CALLBACK_FOUND = 3,
READ_ERROR = 4; READ_ERROR = 4,
NOTIFICATION_HANDLER_ERROR = 5; NOTIFICATION_HANDLER_ERROR = 5,
SERVER_REQUEST_HANDLER_ERROR = 6; SERVER_REQUEST_HANDLER_ERROR = 6,
SERVER_RESULT_CALLBACK_ERROR = 7; SERVER_RESULT_CALLBACK_ERROR = 7,
} }
client_errors = vim.tbl_add_reverse_lookup(client_errors) 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 ---@param err (table) The error object
---@returns (string) The formatted error message ---@returns (string) The formatted error message
local function format_rpc_error(err) local function format_rpc_error(err)
validate { validate({
err = { err, 't' }; err = { err, 't' },
} })
-- There is ErrorCodes in the LSP specification, -- There is ErrorCodes in the LSP specification,
-- but in ResponseError.code it is not used and the actual type is number. -- but in ResponseError.code it is not used and the actual type is number.
local code local code
if protocol.ErrorCodes[err.code] then 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 else
code = string.format("code_name = unknown, code = %s,", err.code) code = string.format('code_name = unknown, code = %s,', err.code)
end end
local message_parts = {"RPC[Error]", code} local message_parts = { 'RPC[Error]', code }
if err.message then if err.message then
table.insert(message_parts, "message =") table.insert(message_parts, 'message =')
table.insert(message_parts, string.format("%q", err.message)) table.insert(message_parts, string.format('%q', err.message))
end end
if err.data then if err.data then
table.insert(message_parts, "data =") table.insert(message_parts, 'data =')
table.insert(message_parts, vim.inspect(err.data)) table.insert(message_parts, vim.inspect(err.data))
end end
return table.concat(message_parts, ' ') 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)? -- TODO should this error or just pick a sane error (like InternalError)?
local code_name = assert(protocol.ErrorCodes[code], 'Invalid RPC error code') local code_name = assert(protocol.ErrorCodes[code], 'Invalid RPC error code')
return setmetatable({ return setmetatable({
code = code; code = code,
message = message or code_name; message = message or code_name,
data = data; data = data,
}, { }, {
__tostring = format_rpc_error; __tostring = format_rpc_error,
}) })
end end
@@ -220,7 +222,7 @@ end
---@param signal (number): Number describing the signal used to terminate (if ---@param signal (number): Number describing the signal used to terminate (if
---any) ---any)
function default_dispatchers.on_exit(code, signal) 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 end
---@private ---@private
--- Default dispatcher for client errors. --- Default dispatcher for client errors.
@@ -258,15 +260,15 @@ end
--- - {handle} A handle for low-level interaction with the LSP server process --- - {handle} A handle for low-level interaction with the LSP server process
--- |vim.loop|. --- |vim.loop|.
local function start(cmd, cmd_args, dispatchers, extra_spawn_params) 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}) local _ = log.info() and log.info('Starting RPC client', { cmd = cmd, args = cmd_args, extra = extra_spawn_params })
validate { validate({
cmd = { cmd, 's' }; cmd = { cmd, 's' },
cmd_args = { cmd_args, 't' }; cmd_args = { cmd_args, 't' },
dispatchers = { dispatchers, 't', true }; dispatchers = { dispatchers, 't', true },
} })
if extra_spawn_params and extra_spawn_params.cwd then 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 end
if dispatchers then if dispatchers then
local user_dispatchers = dispatchers 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] local user_dispatcher = user_dispatchers[dispatch_name]
if user_dispatcher then if user_dispatcher then
if type(user_dispatcher) ~= 'function' 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 end
-- server_request is wrapped elsewhere. -- server_request is wrapped elsewhere.
if not (dispatch_name == 'server_request' if
or dispatch_name == 'on_exit') -- TODO this blocks the loop exiting for some reason. not (dispatch_name == 'server_request' or dispatch_name == 'on_exit') -- TODO this blocks the loop exiting for some reason.
then then
user_dispatcher = schedule_wrap(user_dispatcher) user_dispatcher = schedule_wrap(user_dispatcher)
end end
@@ -317,9 +319,9 @@ local function start(cmd, cmd_args, dispatchers, extra_spawn_params)
dispatchers.on_exit(code, signal) dispatchers.on_exit(code, signal)
end end
local spawn_params = { local spawn_params = {
args = cmd_args; args = cmd_args,
stdio = {stdin, stdout, stderr}; stdio = { stdin, stdout, stderr },
detached = true; detached = true,
} }
if extra_spawn_params then if extra_spawn_params then
spawn_params.cwd = extra_spawn_params.cwd spawn_params.cwd = extra_spawn_params.cwd
@@ -330,11 +332,11 @@ local function start(cmd, cmd_args, dispatchers, extra_spawn_params)
end end
handle, pid = uv.spawn(cmd, spawn_params, onexit) handle, pid = uv.spawn(cmd, spawn_params, onexit)
if handle == nil then if handle == nil then
local msg = string.format("Spawning language server with cmd: `%s` failed", cmd) local msg = string.format('Spawning language server with cmd: `%s` failed', cmd)
if string.match(pid, "ENOENT") then if string.match(pid, 'ENOENT') then
msg = msg .. ". The language server is either not installed, missing from PATH, or not executable." msg = msg .. '. The language server is either not installed, missing from PATH, or not executable.'
else else
msg = msg .. string.format(" with error message: %s", pid) msg = msg .. string.format(' with error message: %s', pid)
end end
vim.notify(msg, vim.log.levels.WARN) vim.notify(msg, vim.log.levels.WARN)
return return
@@ -348,8 +350,10 @@ local function start(cmd, cmd_args, dispatchers, extra_spawn_params)
---@param payload table ---@param payload table
---@returns true if the payload could be scheduled, false if the main event-loop is in the process of closing. ---@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 function encode_and_send(payload)
local _ = log.debug() and log.debug("rpc.send", payload) local _ = log.debug() and log.debug('rpc.send', payload)
if handle == nil or handle:is_closing() then return false end if handle == nil or handle:is_closing() then
return false
end
local encoded = vim.json.encode(payload) local encoded = vim.json.encode(payload)
stdin:write(format_message_with_content_length(encoded)) stdin:write(format_message_with_content_length(encoded))
return true 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 ---@param params (table): Parameters for the invoked LSP method
---@returns (bool) `true` if notification could be sent, `false` if not ---@returns (bool) `true` if notification could be sent, `false` if not
local function notify(method, params) local function notify(method, params)
return encode_and_send { return encode_and_send({
jsonrpc = "2.0"; jsonrpc = '2.0',
method = method; method = method,
params = params; params = params,
} })
end end
---@private ---@private
--- sends an error object to the remote LSP process. --- sends an error object to the remote LSP process.
local function send_response(request_id, err, result) local function send_response(request_id, err, result)
return encode_and_send { return encode_and_send({
id = request_id; id = request_id,
jsonrpc = "2.0"; jsonrpc = '2.0',
error = err; error = err,
result = result; result = result,
} })
end end
-- FIXME: DOC: Should be placed on the RPC client object returned by -- 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 ---@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 ---@returns (bool, number) `(true, message_id)` if request could be sent, `false` if not
local function request(method, params, callback, notify_reply_callback) local function request(method, params, callback, notify_reply_callback)
validate { validate({
callback = { callback, 'f' }; callback = { callback, 'f' },
notify_reply_callback = { notify_reply_callback, 'f', true }; notify_reply_callback = { notify_reply_callback, 'f', true },
} })
message_index = message_index + 1 message_index = message_index + 1
local message_id = message_index local message_id = message_index
local result = encode_and_send { local result = encode_and_send({
id = message_id; id = message_id,
jsonrpc = "2.0"; jsonrpc = '2.0',
method = method; method = method,
params = params; params = params,
} })
if result then if result then
if message_callbacks then if message_callbacks then
message_callbacks[message_id] = schedule_wrap(callback) 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) stderr:read_start(function(_err, chunk)
if chunk then 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
end) end)
@@ -455,7 +459,7 @@ local function start(cmd, cmd_args, dispatchers, extra_spawn_params)
on_error(client_errors.INVALID_SERVER_JSON, decoded) on_error(client_errors.INVALID_SERVER_JSON, decoded)
return return
end 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 if type(decoded.method) == 'string' and decoded.id then
local err local err
@@ -463,17 +467,30 @@ local function start(cmd, cmd_args, dispatchers, extra_spawn_params)
-- we can still use the result. -- we can still use the result.
schedule(function() schedule(function()
local status, result local status, result
status, result, err = try_call(client_errors.SERVER_REQUEST_HANDLER_ERROR, status, result, err = try_call(
dispatchers.server_request, decoded.method, decoded.params) client_errors.SERVER_REQUEST_HANDLER_ERROR,
local _ = log.debug() and log.debug("server_request: callback result", { status = status, result = result, err = err }) 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 status then
if not (result or err) then if not (result or err) then
-- TODO this can be a problem if `null` is sent for result. needs vim.NIL -- 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 end
if err then if err then
assert(type(err) == 'table', "err must be a table. 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.") 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 err.message = err.message or code_name
end end
else else
@@ -485,16 +502,15 @@ local function start(cmd, cmd_args, dispatchers, extra_spawn_params)
end) 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 elseif decoded.id and (decoded.result ~= vim.NIL or decoded.error ~= vim.NIL) then
-- We sent a number, so we expect a number. -- We sent a number, so we expect a number.
local result_id = tonumber(decoded.id) local result_id = tonumber(decoded.id)
-- Notify the user that a response was received for the request -- Notify the user that a response was received for the request
local notify_reply_callback = notify_reply_callbacks and notify_reply_callbacks[result_id] local notify_reply_callback = notify_reply_callbacks and notify_reply_callbacks[result_id]
if notify_reply_callback then if notify_reply_callback then
validate { validate({
notify_reply_callback = { notify_reply_callback, 'f' }; notify_reply_callback = { notify_reply_callback, 'f' },
} })
notify_reply_callback(result_id) notify_reply_callback(result_id)
notify_reply_callbacks[result_id] = nil notify_reply_callbacks[result_id] = nil
end end
@@ -503,7 +519,7 @@ local function start(cmd, cmd_args, dispatchers, extra_spawn_params)
if decoded.error then if decoded.error then
local mute_error = false local mute_error = false
if decoded.error.code == protocol.ErrorCodes.RequestCancelled then 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 mute_error = true
end end
@@ -523,24 +539,22 @@ local function start(cmd, cmd_args, dispatchers, extra_spawn_params)
local callback = message_callbacks and message_callbacks[result_id] local callback = message_callbacks and message_callbacks[result_id]
if callback then if callback then
message_callbacks[result_id] = nil message_callbacks[result_id] = nil
validate { validate({
callback = { callback, 'f' }; callback = { callback, 'f' },
} })
if decoded.error then if decoded.error then
decoded.error = setmetatable(decoded.error, { decoded.error = setmetatable(decoded.error, {
__tostring = format_rpc_error; __tostring = format_rpc_error,
}) })
end end
try_call(client_errors.SERVER_RESULT_CALLBACK_ERROR, try_call(client_errors.SERVER_RESULT_CALLBACK_ERROR, callback, decoded.error, decoded.result)
callback, decoded.error, decoded.result)
else else
on_error(client_errors.NO_RESULT_CALLBACK_FOUND, decoded) 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 end
elseif type(decoded.method) == 'string' then elseif type(decoded.method) == 'string' then
-- Notification -- Notification
try_call(client_errors.NOTIFICATION_HANDLER_ERROR, try_call(client_errors.NOTIFICATION_HANDLER_ERROR, dispatchers.notification, decoded.method, decoded.params)
dispatchers.notification, decoded.method, decoded.params)
else else
-- Invalid server message -- Invalid server message
on_error(client_errors.INVALID_SERVER_MESSAGE, decoded) on_error(client_errors.INVALID_SERVER_MESSAGE, decoded)
@@ -556,7 +570,9 @@ local function start(cmd, cmd_args, dispatchers, extra_spawn_params)
return return
end end
-- This should signal that we are done reading from the client. -- 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 -- Flush anything in the parser by looping until we don't get a result
-- anymore. -- anymore.
while true do while true do
@@ -574,17 +590,17 @@ local function start(cmd, cmd_args, dispatchers, extra_spawn_params)
end) end)
return { return {
pid = pid; pid = pid,
handle = handle; handle = handle,
request = request; request = request,
notify = notify notify = notify,
} }
end end
return { return {
start = start; start = start,
rpc_response_error = rpc_response_error; rpc_response_error = rpc_response_error,
format_rpc_error = format_rpc_error; format_rpc_error = format_rpc_error,
client_errors = client_errors; client_errors = client_errors,
} }
-- vim:sw=2 ts=2 et -- vim:sw=2 ts=2 et

View File

@@ -175,7 +175,7 @@ local function compute_start_range(prev_lines, curr_lines, firstline, lastline,
end end
-- Convert byte to codepoint if applicable -- 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 byte_idx = start_byte_idx
char_idx = 1 char_idx = 1
elseif start_byte_idx == #prev_line + 1 then elseif start_byte_idx == #prev_line + 1 then
@@ -203,14 +203,30 @@ end
---@param new_lastline integer ---@param new_lastline integer
---@param offset_encoding string ---@param offset_encoding string
---@returns (int, int) end_line_idx and end_col_idx of range ---@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. -- If firstline == new_lastline, the first change occurred on a line that was deleted.
-- In this case, the last_byte... -- In this case, the last_byte...
if firstline == new_lastline then 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 end
if firstline == lastline then 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 end
-- Compare on last line, at minimum will be the start range -- Compare on last line, at minimum will be the start range
local start_line_idx = start_range.line_idx 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 end
for idx = 0, max_length do for idx = 0, max_length do
byte_offset = idx byte_offset = idx
if if str_byte(prev_line, prev_line_length - byte_offset) ~= str_byte(curr_line, curr_line_length - byte_offset) then
str_byte(prev_line, prev_line_length - byte_offset) ~= str_byte(curr_line, curr_line_length - byte_offset)
then
break break
end end
end end
@@ -282,13 +296,12 @@ end
---@returns string text extracted from defined region ---@returns string text extracted from defined region
local function extract_text(lines, start_range, end_range, line_ending) local function extract_text(lines, start_range, end_range, line_ending)
if not lines[start_range.line_idx] then if not lines[start_range.line_idx] then
return "" return ''
end end
-- Trivial case: start and end range are the same line, directly grab changed text -- Trivial case: start and end range are the same line, directly grab changed text
if start_range.line_idx == end_range.line_idx then if start_range.line_idx == end_range.line_idx then
-- string.sub is inclusive, end_range is not -- 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) return string.sub(lines[start_range.line_idx], start_range.byte_idx, end_range.byte_idx - 1)
else else
-- Handle deletion case -- Handle deletion case
-- Collect the changed portion of the first changed line -- 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. -- 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)) table.insert(result, string.sub(lines[end_range.line_idx], 1, end_range.byte_idx - 1))
else else
table.insert(result, "") table.insert(result, '')
end end
-- Add line ending between all lines -- Add line ending between all lines

File diff suppressed because it is too large Load Diff

View File

@@ -24,7 +24,9 @@ vim.deepcopy = (function()
local deepcopy_funcs = { local deepcopy_funcs = {
table = function(orig, cache) table = function(orig, cache)
if cache[orig] then return cache[orig] end if cache[orig] then
return cache[orig]
end
local copy = {} local copy = {}
cache[orig] = copy cache[orig] = copy
@@ -46,7 +48,7 @@ vim.deepcopy = (function()
if f then if f then
return f(orig, cache or {}) return f(orig, cache or {})
else else
error("Cannot deepcopy object of type "..type(orig)) error('Cannot deepcopy object of type ' .. type(orig))
end end
end end
end)() end)()
@@ -62,14 +64,14 @@ end)()
---@param plain If `true` use `sep` literally (passed to String.find) ---@param plain If `true` use `sep` literally (passed to String.find)
---@returns Iterator over the split components ---@returns Iterator over the split components
function vim.gsplit(s, sep, plain) function vim.gsplit(s, sep, plain)
vim.validate{s={s,'s'},sep={sep,'s'},plain={plain,'b',true}} vim.validate({ s = { s, 's' }, sep = { sep, 's' }, plain = { plain, 'b', true } })
local start = 1 local start = 1
local done = false local done = false
local function _pass(i, j, ...) local function _pass(i, j, ...)
if i then if i then
assert(j+1 > start, "Infinite loop detected") assert(j + 1 > start, 'Infinite loop detected')
local seg = s:sub(start, i - 1) local seg = s:sub(start, i - 1)
start = j + 1 start = j + 1
return seg, ... return seg, ...
@@ -87,7 +89,7 @@ function vim.gsplit(s, sep, plain)
if start == #s then if start == #s then
done = true done = true
end end
return _pass(start+1, start) return _pass(start + 1, start)
end end
return _pass(s:find(sep, start, plain)) return _pass(s:find(sep, start, plain))
end end
@@ -119,7 +121,7 @@ function vim.split(s, sep, kwargs)
-- Support old signature for backward compatibility -- Support old signature for backward compatibility
plain = kwargs plain = kwargs
else else
vim.validate { kwargs = {kwargs, 't', true} } vim.validate({ kwargs = { kwargs, 't', true } })
kwargs = kwargs or {} kwargs = kwargs or {}
plain = kwargs.plain plain = kwargs.plain
trimempty = kwargs.trimempty trimempty = kwargs.trimempty
@@ -128,7 +130,7 @@ function vim.split(s, sep, kwargs)
local t = {} local t = {}
local skip = trimempty local skip = trimempty
for c in vim.gsplit(s, sep, plain) do for c in vim.gsplit(s, sep, plain) do
if c ~= "" then if c ~= '' then
skip = false skip = false
end end
@@ -139,7 +141,7 @@ function vim.split(s, sep, kwargs)
if trimempty then if trimempty then
for i = #t, 1, -1 do for i = #t, 1, -1 do
if t[i] ~= "" then if t[i] ~= '' then
break break
end end
table.remove(t, i) table.remove(t, i)
@@ -157,7 +159,7 @@ end
---@param t Table ---@param t Table
---@returns list of keys ---@returns list of keys
function vim.tbl_keys(t) function vim.tbl_keys(t)
assert(type(t) == 'table', string.format("Expected table, got %s", type(t))) assert(type(t) == 'table', string.format('Expected table, got %s', type(t)))
local keys = {} local keys = {}
for k, _ in pairs(t) do for k, _ in pairs(t) do
@@ -172,7 +174,7 @@ end
---@param t Table ---@param t Table
---@returns list of values ---@returns list of values
function vim.tbl_values(t) function vim.tbl_values(t)
assert(type(t) == 'table', string.format("Expected table, got %s", type(t))) assert(type(t) == 'table', string.format('Expected table, got %s', type(t)))
local values = {} local values = {}
for _, v in pairs(t) do for _, v in pairs(t) do
@@ -186,7 +188,7 @@ end
---@param func function or callable table ---@param func function or callable table
---@param t table ---@param t table
function vim.tbl_map(func, t) function vim.tbl_map(func, t)
vim.validate{func={func,'c'},t={t,'t'}} vim.validate({ func = { func, 'c' }, t = { t, 't' } })
local rettab = {} local rettab = {}
for k, v in pairs(t) do for k, v in pairs(t) do
@@ -200,7 +202,7 @@ end
---@param func function or callable table ---@param func function or callable table
---@param t table ---@param t table
function vim.tbl_filter(func, t) function vim.tbl_filter(func, t)
vim.validate{func={func,'c'},t={t,'t'}} vim.validate({ func = { func, 'c' }, t = { t, 't' } })
local rettab = {} local rettab = {}
for _, entry in pairs(t) do for _, entry in pairs(t) do
@@ -217,9 +219,9 @@ end
---@param value Value to compare ---@param value Value to compare
---@returns true if `t` contains `value` ---@returns true if `t` contains `value`
function vim.tbl_contains(t, value) function vim.tbl_contains(t, value)
vim.validate{t={t,'t'}} vim.validate({ t = { t, 't' } })
for _,v in ipairs(t) do for _, v in ipairs(t) do
if v == value then if v == value then
return true return true
end end
@@ -233,23 +235,23 @@ end
--- ---
---@param t Table to check ---@param t Table to check
function vim.tbl_isempty(t) function vim.tbl_isempty(t)
assert(type(t) == 'table', string.format("Expected table, got %s", type(t))) assert(type(t) == 'table', string.format('Expected table, got %s', type(t)))
return next(t) == nil return next(t) == nil
end end
--- we only merge empty tables or tables that are not a list --- we only merge empty tables or tables that are not a list
---@private ---@private
local function can_merge(v) local function can_merge(v)
return type(v) == "table" and (vim.tbl_isempty(v) or not vim.tbl_islist(v)) return type(v) == 'table' and (vim.tbl_isempty(v) or not vim.tbl_islist(v))
end end
local function tbl_extend(behavior, deep_extend, ...) local function tbl_extend(behavior, deep_extend, ...)
if (behavior ~= 'error' and behavior ~= 'keep' and behavior ~= 'force') then if behavior ~= 'error' and behavior ~= 'keep' and behavior ~= 'force' then
error('invalid "behavior": '..tostring(behavior)) error('invalid "behavior": ' .. tostring(behavior))
end end
if select('#', ...) < 2 then if select('#', ...) < 2 then
error('wrong number of arguments (given '..tostring(1 + select('#', ...))..', expected at least 3)') error('wrong number of arguments (given ' .. tostring(1 + select('#', ...)) .. ', expected at least 3)')
end end
local ret = {} local ret = {}
@@ -259,14 +261,14 @@ local function tbl_extend(behavior, deep_extend, ...)
for i = 1, select('#', ...) do for i = 1, select('#', ...) do
local tbl = select(i, ...) local tbl = select(i, ...)
vim.validate{["after the second argument"] = {tbl,'t'}} vim.validate({ ['after the second argument'] = { tbl, 't' } })
if tbl then if tbl then
for k, v in pairs(tbl) do for k, v in pairs(tbl) do
if deep_extend and can_merge(v) and can_merge(ret[k]) then if deep_extend and can_merge(v) and can_merge(ret[k]) then
ret[k] = tbl_extend(behavior, true, ret[k], v) ret[k] = tbl_extend(behavior, true, ret[k], v)
elseif behavior ~= 'force' and ret[k] ~= nil then elseif behavior ~= 'force' and ret[k] ~= nil then
if behavior == 'error' then if behavior == 'error' then
error('key found in more than one map: '..k) error('key found in more than one map: ' .. k)
end -- Else behavior is "keep". end -- Else behavior is "keep".
else else
ret[k] = v ret[k] = v
@@ -311,8 +313,12 @@ end
---@param b second value ---@param b second value
---@returns `true` if values are equals, else `false`. ---@returns `true` if values are equals, else `false`.
function vim.deep_equal(a, b) function vim.deep_equal(a, b)
if a == b then return true end if a == b then
if type(a) ~= type(b) then return false end return true
end
if type(a) ~= type(b) then
return false
end
if type(a) == 'table' then if type(a) == 'table' then
for k, v in pairs(a) do for k, v in pairs(a) do
if not vim.deep_equal(v, b[k]) then if not vim.deep_equal(v, b[k]) then
@@ -340,7 +346,13 @@ function vim.tbl_add_reverse_lookup(o)
for _, k in ipairs(keys) do for _, k in ipairs(keys) do
local v = o[k] local v = o[k]
if o[v] then if o[v] then
error(string.format("The reverse lookup found an existing value for %q while processing key %q", tostring(v), tostring(k))) error(
string.format(
'The reverse lookup found an existing value for %q while processing key %q',
tostring(v),
tostring(k)
)
)
end end
o[v] = k o[v] = k
end end
@@ -361,7 +373,7 @@ end
--- ---
---@returns nested value indexed by key if it exists, else nil ---@returns nested value indexed by key if it exists, else nil
function vim.tbl_get(o, ...) function vim.tbl_get(o, ...)
local keys = {...} local keys = { ... }
if #keys == 0 then if #keys == 0 then
return return
end end
@@ -389,12 +401,12 @@ end
---@param finish Final index on src. defaults to #src ---@param finish Final index on src. defaults to #src
---@returns dst ---@returns dst
function vim.list_extend(dst, src, start, finish) function vim.list_extend(dst, src, start, finish)
vim.validate { vim.validate({
dst = {dst, 't'}; dst = { dst, 't' },
src = {src, 't'}; src = { src, 't' },
start = {start, 'n', true}; start = { start, 'n', true },
finish = {finish, 'n', true}; finish = { finish, 'n', true },
} })
for i = start or 1, finish or #src do for i = start or 1, finish or #src do
table.insert(dst, src[i]) table.insert(dst, src[i])
end end
@@ -414,7 +426,7 @@ function vim.tbl_flatten(t)
local n = #_t local n = #_t
for i = 1, n do for i = 1, n do
local v = _t[i] local v = _t[i]
if type(v) == "table" then if type(v) == 'table' then
_tbl_flatten(v) _tbl_flatten(v)
elseif v then elseif v then
table.insert(result, v) table.insert(result, v)
@@ -441,7 +453,7 @@ function vim.tbl_islist(t)
local count = 0 local count = 0
for k, _ in pairs(t) do for k, _ in pairs(t) do
if type(k) == "number" then if type(k) == 'number' then
count = count + 1 count = count + 1
else else
return false return false
@@ -471,10 +483,12 @@ end
---@param t Table ---@param t Table
---@returns Number that is the number of the value in table ---@returns Number that is the number of the value in table
function vim.tbl_count(t) function vim.tbl_count(t)
vim.validate{t={t,'t'}} vim.validate({ t = { t, 't' } })
local count = 0 local count = 0
for _ in pairs(t) do count = count + 1 end for _ in pairs(t) do
count = count + 1
end
return count return count
end end
@@ -487,7 +501,7 @@ end
function vim.list_slice(list, start, finish) function vim.list_slice(list, start, finish)
local new_list = {} local new_list = {}
for i = start or 1, finish or #list do for i = start or 1, finish or #list do
new_list[#new_list+1] = list[i] new_list[#new_list + 1] = list[i]
end end
return new_list return new_list
end end
@@ -498,7 +512,7 @@ end
---@param s String to trim ---@param s String to trim
---@returns String with whitespace removed from its beginning and end ---@returns String with whitespace removed from its beginning and end
function vim.trim(s) function vim.trim(s)
vim.validate{s={s,'s'}} vim.validate({ s = { s, 's' } })
return s:match('^%s*(.*%S)') or '' return s:match('^%s*(.*%S)') or ''
end end
@@ -508,7 +522,7 @@ end
---@param s String to escape ---@param s String to escape
---@returns %-escaped pattern string ---@returns %-escaped pattern string
function vim.pesc(s) function vim.pesc(s)
vim.validate{s={s,'s'}} vim.validate({ s = { s, 's' } })
return s:gsub('[%(%)%.%%%+%-%*%?%[%]%^%$]', '%%%1') return s:gsub('[%(%)%.%%%+%-%*%?%[%]%^%$]', '%%%1')
end end
@@ -518,7 +532,7 @@ end
---@param prefix (string) a prefix ---@param prefix (string) a prefix
---@return (boolean) true if `prefix` is a prefix of s ---@return (boolean) true if `prefix` is a prefix of s
function vim.startswith(s, prefix) function vim.startswith(s, prefix)
vim.validate { s = {s, 's'}; prefix = {prefix, 's'}; } vim.validate({ s = { s, 's' }, prefix = { prefix, 's' } })
return s:sub(1, #prefix) == prefix return s:sub(1, #prefix) == prefix
end end
@@ -528,7 +542,7 @@ end
---@param suffix (string) a suffix ---@param suffix (string) a suffix
---@return (boolean) true if `suffix` is a suffix of s ---@return (boolean) true if `suffix` is a suffix of s
function vim.endswith(s, suffix) function vim.endswith(s, suffix)
vim.validate { s = {s, 's'}; suffix = {suffix, 's'}; } vim.validate({ s = { s, 's' }, suffix = { suffix, 's' } })
return #suffix == 0 or s:sub(-#suffix) == suffix return #suffix == 0 or s:sub(-#suffix) == suffix
end end
@@ -586,12 +600,18 @@ function vim.validate(opt) end -- luacheck: no unused
do do
local type_names = { local type_names = {
['table'] = 'table', t = 'table', ['table'] = 'table',
['string'] = 'string', s = 'string', t = 'table',
['number'] = 'number', n = 'number', ['string'] = 'string',
['boolean'] = 'boolean', b = 'boolean', s = 'string',
['function'] = 'function', f = 'function', ['number'] = 'number',
['callable'] = 'callable', c = 'callable', n = 'number',
['boolean'] = 'boolean',
b = 'boolean',
['function'] = 'function',
f = 'function',
['callable'] = 'callable',
c = 'callable',
['nil'] = 'nil', ['nil'] = 'nil',
['thread'] = 'thread', ['thread'] = 'thread',
['userdata'] = 'userdata', ['userdata'] = 'userdata',
@@ -617,16 +637,16 @@ do
local optional = (true == spec[3]) local optional = (true == spec[3])
if type(types) == 'string' then if type(types) == 'string' then
types = {types} types = { types }
end end
if vim.is_callable(types) then if vim.is_callable(types) then
-- Check user-provided validation function. -- Check user-provided validation function.
local valid, optional_message = types(val) local valid, optional_message = types(val)
if not valid then if not valid then
local error_message = string.format("%s: expected %s, got %s", param_name, (spec[3] or '?'), tostring(val)) local error_message = string.format('%s: expected %s, got %s', param_name, (spec[3] or '?'), tostring(val))
if optional_message ~= nil then if optional_message ~= nil then
error_message = error_message .. string.format(". Info: %s", optional_message) error_message = error_message .. string.format('. Info: %s', optional_message)
end end
return false, error_message return false, error_message
@@ -646,10 +666,10 @@ do
end end
end end
if not success then if not success then
return false, string.format("%s: expected %s, got %s", param_name, table.concat(types, '|'), type(val)) return false, string.format('%s: expected %s, got %s', param_name, table.concat(types, '|'), type(val))
end end
else else
return false, string.format("invalid type name: %s", tostring(types)) return false, string.format('invalid type name: %s', tostring(types))
end end
end end
@@ -668,9 +688,13 @@ end
---@param f Any object ---@param f Any object
---@return true if `f` is callable, else false ---@return true if `f` is callable, else false
function vim.is_callable(f) function vim.is_callable(f)
if type(f) == 'function' then return true end if type(f) == 'function' then
return true
end
local m = getmetatable(f) local m = getmetatable(f)
if m == nil then return false end if m == nil then
return false
end
return type(m.__call) == 'function' return type(m.__call) == 'function'
end end

View File

@@ -1,32 +1,32 @@
local a = vim.api local a = vim.api
local query = require'vim.treesitter.query' local query = require('vim.treesitter.query')
local language = require'vim.treesitter.language' local language = require('vim.treesitter.language')
local LanguageTree = require'vim.treesitter.languagetree' local LanguageTree = require('vim.treesitter.languagetree')
-- TODO(bfredl): currently we retain parsers for the lifetime of the buffer. -- TODO(bfredl): currently we retain parsers for the lifetime of the buffer.
-- Consider use weak references to release parser if all plugins are done with -- Consider use weak references to release parser if all plugins are done with
-- it. -- it.
local parsers = {} local parsers = {}
local M = vim.tbl_extend("error", query, language) local M = vim.tbl_extend('error', query, language)
M.language_version = vim._ts_get_language_version() M.language_version = vim._ts_get_language_version()
M.minimum_language_version = vim._ts_get_minimum_language_version() M.minimum_language_version = vim._ts_get_minimum_language_version()
setmetatable(M, { setmetatable(M, {
__index = function (t, k) __index = function(t, k)
if k == "highlighter" then if k == 'highlighter' then
t[k] = require'vim.treesitter.highlighter' t[k] = require('vim.treesitter.highlighter')
return t[k] return t[k]
elseif k == "language" then elseif k == 'language' then
t[k] = require"vim.treesitter.language" t[k] = require('vim.treesitter.language')
return t[k] return t[k]
elseif k == "query" then elseif k == 'query' then
t[k] = require"vim.treesitter.query" t[k] = require('vim.treesitter.query')
return t[k] return t[k]
end end
end end,
}) })
--- Creates a new parser. --- Creates a new parser.
--- ---
@@ -63,7 +63,11 @@ function M._create_parser(bufnr, lang, opts)
self:_on_reload(...) self:_on_reload(...)
end end
a.nvim_buf_attach(self:source(), false, {on_bytes=bytes_cb, on_detach=detach_cb, on_reload=reload_cb, preview=true}) a.nvim_buf_attach(
self:source(),
false,
{ on_bytes = bytes_cb, on_detach = detach_cb, on_reload = reload_cb, preview = true }
)
self:parse() self:parse()
@@ -87,7 +91,7 @@ function M.get_parser(bufnr, lang, opts)
bufnr = a.nvim_get_current_buf() bufnr = a.nvim_get_current_buf()
end end
if lang == nil then if lang == nil then
lang = a.nvim_buf_get_option(bufnr, "filetype") lang = a.nvim_buf_get_option(bufnr, 'filetype')
end end
if parsers[bufnr] == nil or parsers[bufnr]:lang() ~= lang then if parsers[bufnr] == nil or parsers[bufnr]:lang() ~= lang then
@@ -105,10 +109,10 @@ end
---@param lang The language of this string ---@param lang The language of this string
---@param opts Options to pass to the created language tree ---@param opts Options to pass to the created language tree
function M.get_string_parser(str, lang, opts) function M.get_string_parser(str, lang, opts)
vim.validate { vim.validate({
str = { str, 'string' }, str = { str, 'string' },
lang = { lang, 'string' } lang = { lang, 'string' },
} })
language.require_language(lang) language.require_language(lang)
return LanguageTree.new(str, lang, opts) return LanguageTree.new(str, lang, opts)

View File

@@ -15,24 +15,22 @@ function M.check()
local report_error = vim.fn['health#report_error'] local report_error = vim.fn['health#report_error']
local parsers = M.list_parsers() local parsers = M.list_parsers()
report_info(string.format("Runtime ABI version : %d", ts.language_version)) report_info(string.format('Runtime ABI version : %d', ts.language_version))
for _, parser in pairs(parsers) do for _, parser in pairs(parsers) do
local parsername = vim.fn.fnamemodify(parser, ":t:r") local parsername = vim.fn.fnamemodify(parser, ':t:r')
local is_loadable, ret = pcall(ts.language.require_language, parsername) local is_loadable, ret = pcall(ts.language.require_language, parsername)
if not is_loadable then if not is_loadable then
report_error(string.format("Impossible to load parser for %s: %s", parsername, ret)) report_error(string.format('Impossible to load parser for %s: %s', parsername, ret))
elseif ret then elseif ret then
local lang = ts.language.inspect_language(parsername) local lang = ts.language.inspect_language(parsername)
report_ok(string.format("Loaded parser for %s: ABI version %d", report_ok(string.format('Loaded parser for %s: ABI version %d', parsername, lang._abi_version))
parsername, lang._abi_version))
else else
report_error(string.format("Unable to load parser for %s", parsername)) report_error(string.format('Unable to load parser for %s', parsername))
end end
end end
end end
return M return M

View File

@@ -1,5 +1,5 @@
local a = vim.api local a = vim.api
local query = require"vim.treesitter.query" local query = require('vim.treesitter.query')
-- support reload for quick experimentation -- support reload for quick experimentation
local TSHighlighter = rawget(vim.treesitter, 'TSHighlighter') or {} local TSHighlighter = rawget(vim.treesitter, 'TSHighlighter') or {}
@@ -10,13 +10,13 @@ TSHighlighter.active = TSHighlighter.active or {}
local TSHighlighterQuery = {} local TSHighlighterQuery = {}
TSHighlighterQuery.__index = TSHighlighterQuery TSHighlighterQuery.__index = TSHighlighterQuery
local ns = a.nvim_create_namespace("treesitter/highlighter") local ns = a.nvim_create_namespace('treesitter/highlighter')
local _default_highlights = {} local _default_highlights = {}
local _link_default_highlight_once = function(from, to) local _link_default_highlight_once = function(from, to)
if not _default_highlights[from] then if not _default_highlights[from] then
_default_highlights[from] = true _default_highlights[from] = true
vim.cmd(string.format("highlight default link %s %s", from, to)) vim.cmd(string.format('highlight default link %s %s', from, to))
end end
return from return from
@@ -31,65 +31,65 @@ local subcapture_fallback = {
shortened = shortened:match('(.*)%.') shortened = shortened:match('(.*)%.')
rtn = shortened and rawget(self, shortened) rtn = shortened and rawget(self, shortened)
end end
rawset(self, capture, rtn or "__notfound") rawset(self, capture, rtn or '__notfound')
return rtn return rtn
end end,
} }
TSHighlighter.hl_map = setmetatable({ TSHighlighter.hl_map = setmetatable({
["error"] = "Error", ['error'] = 'Error',
["text.underline"] = "Underlined", ['text.underline'] = 'Underlined',
["todo"] = "Todo", ['todo'] = 'Todo',
["debug"] = "Debug", ['debug'] = 'Debug',
-- Miscs -- Miscs
["comment"] = "Comment", ['comment'] = 'Comment',
["punctuation.delimiter"] = "Delimiter", ['punctuation.delimiter'] = 'Delimiter',
["punctuation.bracket"] = "Delimiter", ['punctuation.bracket'] = 'Delimiter',
["punctuation.special"] = "Delimiter", ['punctuation.special'] = 'Delimiter',
-- Constants -- Constants
["constant"] = "Constant", ['constant'] = 'Constant',
["constant.builtin"] = "Special", ['constant.builtin'] = 'Special',
["constant.macro"] = "Define", ['constant.macro'] = 'Define',
["define"] = "Define", ['define'] = 'Define',
["macro"] = "Macro", ['macro'] = 'Macro',
["string"] = "String", ['string'] = 'String',
["string.regex"] = "String", ['string.regex'] = 'String',
["string.escape"] = "SpecialChar", ['string.escape'] = 'SpecialChar',
["character"] = "Character", ['character'] = 'Character',
["character.special"] = "SpecialChar", ['character.special'] = 'SpecialChar',
["number"] = "Number", ['number'] = 'Number',
["boolean"] = "Boolean", ['boolean'] = 'Boolean',
["float"] = "Float", ['float'] = 'Float',
-- Functions -- Functions
["function"] = "Function", ['function'] = 'Function',
["function.special"] = "Function", ['function.special'] = 'Function',
["function.builtin"] = "Special", ['function.builtin'] = 'Special',
["function.macro"] = "Macro", ['function.macro'] = 'Macro',
["parameter"] = "Identifier", ['parameter'] = 'Identifier',
["method"] = "Function", ['method'] = 'Function',
["field"] = "Identifier", ['field'] = 'Identifier',
["property"] = "Identifier", ['property'] = 'Identifier',
["constructor"] = "Special", ['constructor'] = 'Special',
-- Keywords -- Keywords
["conditional"] = "Conditional", ['conditional'] = 'Conditional',
["repeat"] = "Repeat", ['repeat'] = 'Repeat',
["label"] = "Label", ['label'] = 'Label',
["operator"] = "Operator", ['operator'] = 'Operator',
["keyword"] = "Keyword", ['keyword'] = 'Keyword',
["exception"] = "Exception", ['exception'] = 'Exception',
["type"] = "Type", ['type'] = 'Type',
["type.builtin"] = "Type", ['type.builtin'] = 'Type',
["type.qualifier"] = "Type", ['type.qualifier'] = 'Type',
["type.definition"] = "Typedef", ['type.definition'] = 'Typedef',
["storageclass"] = "StorageClass", ['storageclass'] = 'StorageClass',
["structure"] = "Structure", ['structure'] = 'Structure',
["include"] = "Include", ['include'] = 'Include',
["preproc"] = "PreProc", ['preproc'] = 'PreProc',
}, subcapture_fallback) }, subcapture_fallback)
---@private ---@private
@@ -113,13 +113,13 @@ function TSHighlighterQuery.new(lang, query_string)
rawset(table, capture, id) rawset(table, capture, id)
return id return id
end end,
}) })
if query_string then if query_string then
self._query = query.parse_query(lang, query_string) self._query = query.parse_query(lang, query_string)
else else
self._query = query.get_query(lang, "highlights") self._query = query.get_query(lang, 'highlights')
end end
return self return self
@@ -152,17 +152,23 @@ end
function TSHighlighter.new(tree, opts) function TSHighlighter.new(tree, opts)
local self = setmetatable({}, TSHighlighter) local self = setmetatable({}, TSHighlighter)
if type(tree:source()) ~= "number" then if type(tree:source()) ~= 'number' then
error("TSHighlighter can not be used with a string parser source.") error('TSHighlighter can not be used with a string parser source.')
end end
opts = opts or {} opts = opts or {}
self.tree = tree self.tree = tree
tree:register_cbs { tree:register_cbs({
on_changedtree = function(...) self:on_changedtree(...) end; on_changedtree = function(...)
on_bytes = function(...) self:on_bytes(...) end; self:on_changedtree(...)
on_detach = function(...) self:on_detach(...) end; end,
} on_bytes = function(...)
self:on_bytes(...)
end,
on_detach = function(...)
self:on_detach(...)
end,
})
self.bufnr = tree:source() self.bufnr = tree:source()
self.edit_count = 0 self.edit_count = 0
@@ -181,7 +187,7 @@ function TSHighlighter.new(tree, opts)
end end
end end
a.nvim_buf_set_option(self.bufnr, "syntax", "") a.nvim_buf_set_option(self.bufnr, 'syntax', '')
TSHighlighter.active[self.bufnr] = self TSHighlighter.active[self.bufnr] = self
@@ -190,7 +196,7 @@ function TSHighlighter.new(tree, opts)
-- syntax FileType autocmds. Later on we should integrate with the -- syntax FileType autocmds. Later on we should integrate with the
-- `:syntax` and `set syntax=...` machinery properly. -- `:syntax` and `set syntax=...` machinery properly.
if vim.g.syntax_on ~= 1 then if vim.g.syntax_on ~= 1 then
vim.api.nvim_command("runtime! syntax/synload.vim") vim.api.nvim_command('runtime! syntax/synload.vim')
end end
self.tree:parse() self.tree:parse()
@@ -210,7 +216,7 @@ function TSHighlighter:get_highlight_state(tstree)
if not self._highlight_states[tstree] then if not self._highlight_states[tstree] then
self._highlight_states[tstree] = { self._highlight_states[tstree] = {
next_row = 0, next_row = 0,
iter = nil iter = nil,
} }
end end
@@ -235,7 +241,7 @@ end
---@private ---@private
function TSHighlighter:on_changedtree(changes) function TSHighlighter:on_changedtree(changes)
for _, ch in ipairs(changes or {}) do for _, ch in ipairs(changes or {}) do
a.nvim__buf_redraw_range(self.bufnr, ch[1], ch[3]+1) a.nvim__buf_redraw_range(self.bufnr, ch[1], ch[3] + 1)
end end
end end
@@ -253,19 +259,25 @@ end
---@private ---@private
local function on_line_impl(self, buf, line) local function on_line_impl(self, buf, line)
self.tree:for_each_tree(function(tstree, tree) self.tree:for_each_tree(function(tstree, tree)
if not tstree then return end if not tstree then
return
end
local root_node = tstree:root() local root_node = tstree:root()
local root_start_row, _, root_end_row, _ = root_node:range() local root_start_row, _, root_end_row, _ = root_node:range()
-- Only worry about trees within the line range -- Only worry about trees within the line range
if root_start_row > line or root_end_row < line then return end if root_start_row > line or root_end_row < line then
return
end
local state = self:get_highlight_state(tstree) local state = self:get_highlight_state(tstree)
local highlighter_query = self:get_query(tree:lang()) local highlighter_query = self:get_query(tree:lang())
-- Some injected languages may not have highlight queries. -- Some injected languages may not have highlight queries.
if not highlighter_query:query() then return end if not highlighter_query:query() then
return
end
if state.iter == nil then if state.iter == nil then
state.iter = highlighter_query:query():iter_captures(root_node, self.bufnr, line, root_end_row + 1) state.iter = highlighter_query:query():iter_captures(root_node, self.bufnr, line, root_end_row + 1)
@@ -274,14 +286,17 @@ local function on_line_impl(self, buf, line)
while line >= state.next_row do while line >= state.next_row do
local capture, node, metadata = state.iter() local capture, node, metadata = state.iter()
if capture == nil then break end if capture == nil then
break
end
local start_row, start_col, end_row, end_col = node:range() local start_row, start_col, end_row, end_col = node:range()
local hl = highlighter_query.hl_cache[capture] local hl = highlighter_query.hl_cache[capture]
if hl and end_row >= line then if hl and end_row >= line then
a.nvim_buf_set_extmark(buf, ns, start_row, start_col, a.nvim_buf_set_extmark(buf, ns, start_row, start_col, {
{ end_line = end_row, end_col = end_col, end_line = end_row,
end_col = end_col,
hl_group = hl, hl_group = hl,
ephemeral = true, ephemeral = true,
priority = tonumber(metadata.priority) or 100, -- Low but leaves room below priority = tonumber(metadata.priority) or 100, -- Low but leaves room below
@@ -298,7 +313,9 @@ end
---@private ---@private
function TSHighlighter._on_line(_, _win, buf, line, _) function TSHighlighter._on_line(_, _win, buf, line, _)
local self = TSHighlighter.active[buf] local self = TSHighlighter.active[buf]
if not self then return end if not self then
return
end
on_line_impl(self, buf, line) on_line_impl(self, buf, line)
end end
@@ -324,9 +341,9 @@ function TSHighlighter._on_win(_, _win, buf, _topline)
end end
a.nvim_set_decoration_provider(ns, { a.nvim_set_decoration_provider(ns, {
on_buf = TSHighlighter._on_buf; on_buf = TSHighlighter._on_buf,
on_win = TSHighlighter._on_win; on_win = TSHighlighter._on_win,
on_line = TSHighlighter._on_line; on_line = TSHighlighter._on_line,
}) })
return TSHighlighter return TSHighlighter

View File

@@ -22,13 +22,15 @@ function M.require_language(lang, path, silent)
end end
-- TODO(bfredl): help tag? -- TODO(bfredl): help tag?
error("no parser for '"..lang.."' language, see :help treesitter-parsers") error("no parser for '" .. lang .. "' language, see :help treesitter-parsers")
end end
path = paths[1] path = paths[1]
end end
if silent then if silent then
return pcall(function() vim._ts_add_language(path, lang) end) return pcall(function()
vim._ts_add_language(path, lang)
end)
else else
vim._ts_add_language(path, lang) vim._ts_add_language(path, lang)
end end

View File

@@ -1,6 +1,6 @@
local a = vim.api local a = vim.api
local query = require'vim.treesitter.query' local query = require('vim.treesitter.query')
local language = require'vim.treesitter.language' local language = require('vim.treesitter.language')
local LanguageTree = {} local LanguageTree = {}
LanguageTree.__index = LanguageTree LanguageTree.__index = LanguageTree
@@ -32,9 +32,10 @@ function LanguageTree.new(source, lang, opts)
_regions = {}, _regions = {},
_trees = {}, _trees = {},
_opts = opts, _opts = opts,
_injection_query = injections[lang] _injection_query = injections[lang] and query.parse_query(lang, injections[lang]) or query.get_query(
and query.parse_query(lang, injections[lang]) lang,
or query.get_query(lang, "injections"), 'injections'
),
_valid = false, _valid = false,
_parser = vim._create_ts_parser(lang), _parser = vim._create_ts_parser(lang),
_callbacks = { _callbacks = {
@@ -42,11 +43,10 @@ function LanguageTree.new(source, lang, opts)
bytes = {}, bytes = {},
detach = {}, detach = {},
child_added = {}, child_added = {},
child_removed = {} child_removed = {},
}, },
}, LanguageTree) }, LanguageTree)
return self return self
end end
@@ -264,11 +264,11 @@ end
---@param regions A list of regions this tree should manage and parse. ---@param regions A list of regions this tree should manage and parse.
function LanguageTree:set_included_regions(regions) function LanguageTree:set_included_regions(regions)
-- TODO(vigoux): I don't think string parsers are useful for now -- TODO(vigoux): I don't think string parsers are useful for now
if type(self._source) == "number" then if type(self._source) == 'number' then
-- Transform the tables from 4 element long to 6 element long (with byte offset) -- Transform the tables from 4 element long to 6 element long (with byte offset)
for _, region in ipairs(regions) do for _, region in ipairs(regions) do
for i, range in ipairs(region) do for i, range in ipairs(region) do
if type(range) == "table" and #range == 4 then if type(range) == 'table' and #range == 4 then
local start_row, start_col, end_row, end_col = unpack(range) local start_row, start_col, end_row, end_col = unpack(range)
-- Easy case, this is a buffer parser -- Easy case, this is a buffer parser
-- TODO(vigoux): proper byte computation here, and account for EOL ? -- TODO(vigoux): proper byte computation here, and account for EOL ?
@@ -303,7 +303,9 @@ end
--- instead of using the entire nodes range. --- instead of using the entire nodes range.
---@private ---@private
function LanguageTree:_get_injections() function LanguageTree:_get_injections()
if not self._injection_query then return {} end if not self._injection_query then
return {}
end
local injections = {} local injections = {}
@@ -311,7 +313,9 @@ function LanguageTree:_get_injections()
local root_node = tree:root() local root_node = tree:root()
local start_line, _, end_line, _ = root_node:range() local start_line, _, end_line, _ = root_node:range()
for pattern, match, metadata in self._injection_query:iter_matches(root_node, self._source, start_line, end_line+1) do for pattern, match, metadata in
self._injection_query:iter_matches(root_node, self._source, start_line, end_line + 1)
do
local lang = nil local lang = nil
local ranges = {} local ranges = {}
local combined = metadata.combined local combined = metadata.combined
@@ -322,8 +326,8 @@ function LanguageTree:_get_injections()
local content = metadata.content local content = metadata.content
-- Allow for captured nodes to be used -- Allow for captured nodes to be used
if type(content) == "number" then if type(content) == 'number' then
content = {match[content]} content = { match[content] }
end end
if content then if content then
@@ -342,15 +346,15 @@ function LanguageTree:_get_injections()
local name = self._injection_query.captures[id] local name = self._injection_query.captures[id]
-- Lang should override any other language tag -- Lang should override any other language tag
if name == "language" and not lang then if name == 'language' and not lang then
lang = query.get_node_text(node, self._source) lang = query.get_node_text(node, self._source)
elseif name == "combined" then elseif name == 'combined' then
combined = true combined = true
elseif name == "content" and #ranges == 0 then elseif name == 'content' and #ranges == 0 then
table.insert(ranges, node) table.insert(ranges, node)
-- Ignore any tags that start with "_" -- Ignore any tags that start with "_"
-- Allows for other tags to be used in matches -- Allows for other tags to be used in matches
elseif string.sub(name, 1, 1) ~= "_" then elseif string.sub(name, 1, 1) ~= '_' then
if not lang then if not lang then
lang = name lang = name
end end
@@ -414,10 +418,19 @@ function LanguageTree:_do_callback(cb_name, ...)
end end
---@private ---@private
function LanguageTree:_on_bytes(bufnr, changed_tick, function LanguageTree:_on_bytes(
start_row, start_col, start_byte, bufnr,
old_row, old_col, old_byte, changed_tick,
new_row, new_col, new_byte) start_row,
start_col,
start_byte,
old_row,
old_col,
old_byte,
new_row,
new_col,
new_byte
)
self:invalidate() self:invalidate()
local old_end_col = old_col + ((old_row == 0) and start_col or 0) local old_end_col = old_col + ((old_row == 0) and start_col or 0)
@@ -426,16 +439,33 @@ function LanguageTree:_on_bytes(bufnr, changed_tick,
-- Edit all trees recursively, together BEFORE emitting a bytes callback. -- Edit all trees recursively, together BEFORE emitting a bytes callback.
-- In most cases this callback should only be called from the root tree. -- In most cases this callback should only be called from the root tree.
self:for_each_tree(function(tree) self:for_each_tree(function(tree)
tree:edit(start_byte,start_byte+old_byte,start_byte+new_byte, tree:edit(
start_row, start_col, start_byte,
start_row+old_row, old_end_col, start_byte + old_byte,
start_row+new_row, new_end_col) start_byte + new_byte,
start_row,
start_col,
start_row + old_row,
old_end_col,
start_row + new_row,
new_end_col
)
end) end)
self:_do_callback('bytes', bufnr, changed_tick, self:_do_callback(
start_row, start_col, start_byte, 'bytes',
old_row, old_col, old_byte, bufnr,
new_row, new_col, new_byte) changed_tick,
start_row,
start_col,
start_byte,
old_row,
old_col,
old_byte,
new_row,
new_col,
new_byte
)
end end
---@private ---@private
@@ -443,7 +473,6 @@ function LanguageTree:_on_reload()
self:invalidate(true) self:invalidate(true)
end end
---@private ---@private
function LanguageTree:_on_detach(...) function LanguageTree:_on_detach(...)
self:invalidate(true) self:invalidate(true)
@@ -459,7 +488,9 @@ end
--- - `on_child_added` : emitted when a child is added to the tree. --- - `on_child_added` : emitted when a child is added to the tree.
--- - `on_child_removed` : emitted when a child is removed from the tree. --- - `on_child_removed` : emitted when a child is removed from the tree.
function LanguageTree:register_cbs(cbs) function LanguageTree:register_cbs(cbs)
if not cbs then return end if not cbs then
return
end
if cbs.on_changedtree then if cbs.on_changedtree then
table.insert(self._callbacks.changedtree, cbs.on_changedtree) table.insert(self._callbacks.changedtree, cbs.on_changedtree)

View File

@@ -1,5 +1,5 @@
local a = vim.api local a = vim.api
local language = require'vim.treesitter.language' local language = require('vim.treesitter.language')
-- query: pattern matching on trees -- query: pattern matching on trees
-- predicate matching is implemented in lua -- predicate matching is implemented in lua
@@ -43,7 +43,9 @@ function M.get_query_files(lang, query_name, is_included)
local query_path = string.format('queries/%s/%s.scm', lang, query_name) local query_path = string.format('queries/%s/%s.scm', lang, query_name)
local lang_files = dedupe_files(a.nvim_get_runtime_file(query_path, true)) local lang_files = dedupe_files(a.nvim_get_runtime_file(query_path, true))
if #lang_files == 0 then return {} end if #lang_files == 0 then
return {}
end
local base_langs = {} local base_langs = {}
@@ -52,7 +54,7 @@ function M.get_query_files(lang, query_name, is_included)
-- ;+ inherits: ({language},)*{language} -- ;+ inherits: ({language},)*{language}
-- --
-- {language} ::= {lang} | ({lang}) -- {language} ::= {lang} | ({lang})
local MODELINE_FORMAT = "^;+%s*inherits%s*:?%s*([a-z_,()]+)%s*$" local MODELINE_FORMAT = '^;+%s*inherits%s*:?%s*([a-z_,()]+)%s*$'
for _, file in ipairs(lang_files) do for _, file in ipairs(lang_files) do
local modeline = safe_read(file, '*l') local modeline = safe_read(file, '*l')
@@ -62,7 +64,7 @@ function M.get_query_files(lang, query_name, is_included)
if langlist then if langlist then
for _, incllang in ipairs(vim.split(langlist, ',', true)) do for _, incllang in ipairs(vim.split(langlist, ',', true)) do
local is_optional = incllang:match("%(.*%)") local is_optional = incllang:match('%(.*%)')
if is_optional then if is_optional then
if not is_included then if not is_included then
@@ -90,7 +92,7 @@ end
local function read_query_files(filenames) local function read_query_files(filenames)
local contents = {} local contents = {}
for _,filename in ipairs(filenames) do for _, filename in ipairs(filenames) do
table.insert(contents, safe_read(filename, '*a')) table.insert(contents, safe_read(filename, '*a'))
end end
@@ -142,7 +144,7 @@ local query_cache = setmetatable({}, {
__index = function(tbl, key) __index = function(tbl, key)
rawset(tbl, key, {}) rawset(tbl, key, {})
return rawget(tbl, key) return rawget(tbl, key)
end end,
}) })
--- Parse {query} as a string. (If the query is in a file, the caller --- Parse {query} as a string. (If the query is in a file, the caller
@@ -185,7 +187,7 @@ function M.get_node_text(node, source)
local start_row, start_col, start_byte = node:start() local start_row, start_col, start_byte = node:start()
local end_row, end_col, end_byte = node:end_() local end_row, end_col, end_byte = node:end_()
if type(source) == "number" then if type(source) == 'number' then
local lines local lines
local eof_row = a.nvim_buf_line_count(source) local eof_row = a.nvim_buf_line_count(source)
if start_row >= eof_row then if start_row >= eof_row then
@@ -201,28 +203,28 @@ function M.get_node_text(node, source)
if #lines > 0 then if #lines > 0 then
if #lines == 1 then if #lines == 1 then
lines[1] = string.sub(lines[1], start_col+1, end_col) lines[1] = string.sub(lines[1], start_col + 1, end_col)
else else
lines[1] = string.sub(lines[1], start_col+1) lines[1] = string.sub(lines[1], start_col + 1)
lines[#lines] = string.sub(lines[#lines], 1, end_col) lines[#lines] = string.sub(lines[#lines], 1, end_col)
end end
end end
return table.concat(lines, "\n") return table.concat(lines, '\n')
elseif type(source) == "string" then elseif type(source) == 'string' then
return source:sub(start_byte+1, end_byte) return source:sub(start_byte + 1, end_byte)
end end
end end
-- Predicate handler receive the following arguments -- Predicate handler receive the following arguments
-- (match, pattern, bufnr, predicate) -- (match, pattern, bufnr, predicate)
local predicate_handlers = { local predicate_handlers = {
["eq?"] = function(match, _, source, predicate) ['eq?'] = function(match, _, source, predicate)
local node = match[predicate[2]] local node = match[predicate[2]]
local node_text = M.get_node_text(node, source) local node_text = M.get_node_text(node, source)
local str local str
if type(predicate[3]) == "string" then if type(predicate[3]) == 'string' then
-- (#eq? @aa "foo") -- (#eq? @aa "foo")
str = predicate[3] str = predicate[3]
else else
@@ -237,20 +239,20 @@ local predicate_handlers = {
return true return true
end, end,
["lua-match?"] = function(match, _, source, predicate) ['lua-match?'] = function(match, _, source, predicate)
local node = match[predicate[2]] local node = match[predicate[2]]
local regex = predicate[3] local regex = predicate[3]
return string.find(M.get_node_text(node, source), regex) return string.find(M.get_node_text(node, source), regex)
end, end,
["match?"] = (function() ['match?'] = (function()
local magic_prefixes = {['\\v']=true, ['\\m']=true, ['\\M']=true, ['\\V']=true} local magic_prefixes = { ['\\v'] = true, ['\\m'] = true, ['\\M'] = true, ['\\V'] = true }
---@private ---@private
local function check_magic(str) local function check_magic(str)
if string.len(str) < 2 or magic_prefixes[string.sub(str,1,2)] then if string.len(str) < 2 or magic_prefixes[string.sub(str, 1, 2)] then
return str return str
end end
return '\\v'..str return '\\v' .. str
end end
local compiled_vim_regexes = setmetatable({}, { local compiled_vim_regexes = setmetatable({}, {
@@ -258,7 +260,7 @@ local predicate_handlers = {
local res = vim.regex(check_magic(pattern)) local res = vim.regex(check_magic(pattern))
rawset(t, pattern, res) rawset(t, pattern, res)
return res return res
end end,
}) })
return function(match, _, source, pred) return function(match, _, source, pred)
@@ -268,11 +270,11 @@ local predicate_handlers = {
end end
end)(), end)(),
["contains?"] = function(match, _, source, predicate) ['contains?'] = function(match, _, source, predicate)
local node = match[predicate[2]] local node = match[predicate[2]]
local node_text = M.get_node_text(node, source) local node_text = M.get_node_text(node, source)
for i=3,#predicate do for i = 3, #predicate do
if string.find(node_text, predicate[i], 1, true) then if string.find(node_text, predicate[i], 1, true) then
return true return true
end end
@@ -281,19 +283,19 @@ local predicate_handlers = {
return false return false
end, end,
["any-of?"] = function(match, _, source, predicate) ['any-of?'] = function(match, _, source, predicate)
local node = match[predicate[2]] local node = match[predicate[2]]
local node_text = M.get_node_text(node, source) local node_text = M.get_node_text(node, source)
-- Since 'predicate' will not be used by callers of this function, use it -- Since 'predicate' will not be used by callers of this function, use it
-- to store a string set built from the list of words to check against. -- to store a string set built from the list of words to check against.
local string_set = predicate["string_set"] local string_set = predicate['string_set']
if not string_set then if not string_set then
string_set = {} string_set = {}
for i=3,#predicate do for i = 3, #predicate do
string_set[predicate[i]] = true string_set[predicate[i]] = true
end end
predicate["string_set"] = string_set predicate['string_set'] = string_set
end end
return string_set[node_text] return string_set[node_text]
@@ -301,15 +303,14 @@ local predicate_handlers = {
} }
-- As we provide lua-match? also expose vim-match? -- As we provide lua-match? also expose vim-match?
predicate_handlers["vim-match?"] = predicate_handlers["match?"] predicate_handlers['vim-match?'] = predicate_handlers['match?']
-- Directives store metadata or perform side effects against a match. -- Directives store metadata or perform side effects against a match.
-- Directives should always end with a `!`. -- Directives should always end with a `!`.
-- Directive handler receive the following arguments -- Directive handler receive the following arguments
-- (match, pattern, bufnr, predicate, metadata) -- (match, pattern, bufnr, predicate, metadata)
local directive_handlers = { local directive_handlers = {
["set!"] = function(_, _, _, pred, metadata) ['set!'] = function(_, _, _, pred, metadata)
if #pred == 4 then if #pred == 4 then
-- (#set! @capture "key" "value") -- (#set! @capture "key" "value")
local capture = pred[2] local capture = pred[2]
@@ -324,9 +325,9 @@ local directive_handlers = {
end, end,
-- Shifts the range of a node. -- Shifts the range of a node.
-- Example: (#offset! @_node 0 1 0 -1) -- Example: (#offset! @_node 0 1 0 -1)
["offset!"] = function(match, _, _, pred, metadata) ['offset!'] = function(match, _, _, pred, metadata)
local offset_node = match[pred[2]] local offset_node = match[pred[2]]
local range = {offset_node:range()} local range = { offset_node:range() }
local start_row_offset = pred[3] or 0 local start_row_offset = pred[3] or 0
local start_col_offset = pred[4] or 0 local start_col_offset = pred[4] or 0
local end_row_offset = pred[5] or 0 local end_row_offset = pred[5] or 0
@@ -339,9 +340,9 @@ local directive_handlers = {
-- If this produces an invalid range, we just skip it. -- If this produces an invalid range, we just skip it.
if range[1] < range[3] or (range[1] == range[3] and range[2] <= range[4]) then if range[1] < range[3] or (range[1] == range[3] and range[2] <= range[4]) then
metadata.content = {range} metadata.content = { range }
end
end end
end,
} }
--- Adds a new predicate to be used in queries --- Adds a new predicate to be used in queries
@@ -351,7 +352,7 @@ local directive_handlers = {
--- signature will be (match, pattern, bufnr, predicate) --- signature will be (match, pattern, bufnr, predicate)
function M.add_predicate(name, handler, force) function M.add_predicate(name, handler, force)
if predicate_handlers[name] and not force then if predicate_handlers[name] and not force then
error(string.format("Overriding %s", name)) error(string.format('Overriding %s', name))
end end
predicate_handlers[name] = handler predicate_handlers[name] = handler
@@ -364,7 +365,7 @@ end
--- signature will be (match, pattern, bufnr, predicate) --- signature will be (match, pattern, bufnr, predicate)
function M.add_directive(name, handler, force) function M.add_directive(name, handler, force)
if directive_handlers[name] and not force then if directive_handlers[name] and not force then
error(string.format("Overriding %s", name)) error(string.format('Overriding %s', name))
end end
directive_handlers[name] = handler directive_handlers[name] = handler
@@ -387,7 +388,7 @@ end
---@private ---@private
local function is_directive(name) local function is_directive(name)
return string.sub(name, -1) == "!" return string.sub(name, -1) == '!'
end end
---@private ---@private
@@ -404,7 +405,7 @@ function Query:match_preds(match, pattern, source)
-- Skip over directives... they will get processed after all the predicates. -- Skip over directives... they will get processed after all the predicates.
if not is_directive(pred[1]) then if not is_directive(pred[1]) then
if string.sub(pred[1], 1, 4) == "not-" then if string.sub(pred[1], 1, 4) == 'not-' then
pred_name = string.sub(pred[1], 5) pred_name = string.sub(pred[1], 5)
is_not = true is_not = true
else else
@@ -415,7 +416,7 @@ function Query:match_preds(match, pattern, source)
local handler = predicate_handlers[pred_name] local handler = predicate_handlers[pred_name]
if not handler then if not handler then
error(string.format("No handler for %s", pred[1])) error(string.format('No handler for %s', pred[1]))
return false return false
end end
@@ -438,7 +439,7 @@ function Query:apply_directives(match, pattern, source, metadata)
local handler = directive_handlers[pred[1]] local handler = directive_handlers[pred[1]]
if not handler then if not handler then
error(string.format("No handler for %s", pred[1])) error(string.format('No handler for %s', pred[1]))
return return
end end
@@ -447,7 +448,6 @@ function Query:apply_directives(match, pattern, source, metadata)
end end
end end
--- Returns the start and stop value if set else the node's range. --- Returns the start and stop value if set else the node's range.
-- When the node's range is used, the stop is incremented by 1 -- When the node's range is used, the stop is incremented by 1
-- to make the search inclusive. -- to make the search inclusive.
@@ -492,7 +492,7 @@ end
---@returns The matching capture id ---@returns The matching capture id
---@returns The captured node ---@returns The captured node
function Query:iter_captures(node, source, start, stop) function Query:iter_captures(node, source, start, stop)
if type(source) == "number" and source == 0 then if type(source) == 'number' and source == 0 then
source = vim.api.nvim_get_current_buf() source = vim.api.nvim_get_current_buf()
end end
@@ -549,7 +549,7 @@ end
---@returns The matching pattern id ---@returns The matching pattern id
---@returns The matching match ---@returns The matching match
function Query:iter_matches(node, source, start, stop) function Query:iter_matches(node, source, start, stop)
if type(source) == "number" and source == 0 then if type(source) == 'number' and source == 0 then
source = vim.api.nvim_get_current_buf() source = vim.api.nvim_get_current_buf()
end end

View File

@@ -37,12 +37,12 @@ local M = {}
--- </pre> --- </pre>
function M.select(items, opts, on_choice) function M.select(items, opts, on_choice)
vim.validate { vim.validate({
items = { items, 'table', false }, items = { items, 'table', false },
on_choice = { on_choice, 'function', false }, on_choice = { on_choice, 'function', false },
} })
opts = opts or {} opts = opts or {}
local choices = {opts.prompt or 'Select one of:'} local choices = { opts.prompt or 'Select one of:' }
local format_item = opts.format_item or tostring local format_item = opts.format_item or tostring
for i, item in pairs(items) do for i, item in pairs(items) do
table.insert(choices, string.format('%d: %s', i, format_item(item))) table.insert(choices, string.format('%d: %s', i, format_item(item)))
@@ -83,9 +83,9 @@ end
--- end) --- end)
--- </pre> --- </pre>
function M.input(opts, on_confirm) function M.input(opts, on_confirm)
vim.validate { vim.validate({
on_confirm = { on_confirm, 'function', false }, on_confirm = { on_confirm, 'function', false },
} })
opts = opts or {} opts = opts or {}
local input = vim.fn.input(opts) local input = vim.fn.input(opts)

View File

@@ -3,7 +3,6 @@
-- https://tools.ietf.org/html/rfc2732 -- https://tools.ietf.org/html/rfc2732
-- https://tools.ietf.org/html/rfc2396 -- https://tools.ietf.org/html/rfc2396
local uri_decode local uri_decode
do do
local schar = string.char local schar = string.char
@@ -14,7 +13,7 @@ do
return schar(tonumber(hex, 16)) return schar(tonumber(hex, 16))
end end
uri_decode = function(str) uri_decode = function(str)
return str:gsub("%%([a-fA-F0-9][a-fA-F0-9])", hex_to_char) return str:gsub('%%([a-fA-F0-9][a-fA-F0-9])', hex_to_char)
end end
end end
@@ -23,33 +22,36 @@ do
local PATTERNS = { local PATTERNS = {
--- RFC 2396 --- RFC 2396
-- https://tools.ietf.org/html/rfc2396#section-2.2 -- https://tools.ietf.org/html/rfc2396#section-2.2
rfc2396 = "^A-Za-z0-9%-_.!~*'()"; rfc2396 = "^A-Za-z0-9%-_.!~*'()",
--- RFC 2732 --- RFC 2732
-- https://tools.ietf.org/html/rfc2732 -- https://tools.ietf.org/html/rfc2732
rfc2732 = "^A-Za-z0-9%-_.!~*'()[]"; rfc2732 = "^A-Za-z0-9%-_.!~*'()[]",
--- RFC 3986 --- RFC 3986
-- https://tools.ietf.org/html/rfc3986#section-2.2 -- https://tools.ietf.org/html/rfc3986#section-2.2
rfc3986 = "^A-Za-z0-9%-._~!$&'()*+,;=:@/"; rfc3986 = "^A-Za-z0-9%-._~!$&'()*+,;=:@/",
} }
local sbyte, tohex = string.byte local sbyte, tohex = string.byte
if jit then if jit then
tohex = require'bit'.tohex tohex = require('bit').tohex
else else
tohex = function(b) return string.format("%02x", b) end tohex = function(b)
return string.format('%02x', b)
end
end end
---@private ---@private
local function percent_encode_char(char) local function percent_encode_char(char)
return "%"..tohex(sbyte(char), 2) return '%' .. tohex(sbyte(char), 2)
end end
uri_encode = function(text, rfc) uri_encode = function(text, rfc)
if not text then return end if not text then
return
end
local pattern = PATTERNS[rfc] or PATTERNS.rfc3986 local pattern = PATTERNS[rfc] or PATTERNS.rfc3986
return text:gsub("(["..pattern.."])", percent_encode_char) return text:gsub('([' .. pattern .. '])', percent_encode_char)
end end
end end
---@private ---@private
local function is_windows_file_uri(uri) local function is_windows_file_uri(uri)
return uri:match('^file:/+[a-zA-Z]:') ~= nil return uri:match('^file:/+[a-zA-Z]:') ~= nil
@@ -59,16 +61,16 @@ end
---@param path string Path to file ---@param path string Path to file
---@return string URI ---@return string URI
local function uri_from_fname(path) local function uri_from_fname(path)
local volume_path, fname = path:match("^([a-zA-Z]:)(.*)") local volume_path, fname = path:match('^([a-zA-Z]:)(.*)')
local is_windows = volume_path ~= nil local is_windows = volume_path ~= nil
if is_windows then if is_windows then
path = volume_path..uri_encode(fname:gsub("\\", "/")) path = volume_path .. uri_encode(fname:gsub('\\', '/'))
else else
path = uri_encode(path) path = uri_encode(path)
end end
local uri_parts = {"file://"} local uri_parts = { 'file://' }
if is_windows then if is_windows then
table.insert(uri_parts, "/") table.insert(uri_parts, '/')
end end
table.insert(uri_parts, path) table.insert(uri_parts, path)
return table.concat(uri_parts) return table.concat(uri_parts)
@@ -82,11 +84,11 @@ local WINDOWS_URI_SCHEME_PATTERN = '^([a-zA-Z]+[a-zA-Z0-9.+-]*):[a-zA-Z]:.*'
---@return string URI ---@return string URI
local function uri_from_bufnr(bufnr) local function uri_from_bufnr(bufnr)
local fname = vim.api.nvim_buf_get_name(bufnr) local fname = vim.api.nvim_buf_get_name(bufnr)
local volume_path = fname:match("^([a-zA-Z]:).*") local volume_path = fname:match('^([a-zA-Z]:).*')
local is_windows = volume_path ~= nil local is_windows = volume_path ~= nil
local scheme local scheme
if is_windows then if is_windows then
fname = fname:gsub("\\", "/") fname = fname:gsub('\\', '/')
scheme = fname:match(WINDOWS_URI_SCHEME_PATTERN) scheme = fname:match(WINDOWS_URI_SCHEME_PATTERN)
else else
scheme = fname:match(URI_SCHEME_PATTERN) scheme = fname:match(URI_SCHEME_PATTERN)

View File

@@ -3,4 +3,4 @@
-- Last Change: 2022 Apr 13 -- Last Change: 2022 Apr 13
-- it's a lisp! -- it's a lisp!
vim.cmd [[ runtime! syntax/lisp.vim ]] vim.cmd([[ runtime! syntax/lisp.vim ]])