Merge pull request #18487 from clason/stylua

CI: format and lint runtime with Stylua
This commit is contained in:
Christian Clason 2022-05-11 08:54:24 +02:00 committed by GitHub
commit cb7ab98925
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
41 changed files with 4223 additions and 3571 deletions

View File

@ -86,6 +86,13 @@ jobs:
name: clint-full
run: ./ci/run_lint.sh clint-full
- if: "!cancelled()"
name: stylua
uses: JohnnyMorganz/stylua-action@1.0.0
with:
token: ${{ secrets.GITHUB_TOKEN }}
args: --check runtime/
- if: "!cancelled()"
name: lualint
run: ./ci/run_lint.sh lualint

6
.stylua.toml Normal file
View File

@ -0,0 +1,6 @@
column_width = 120
line_endings = "Unix"
indent_type = "Spaces"
indent_width = 2
quote_style = "AutoPreferSingle"
call_parentheses = "Always"

3
.styluaignore Normal file
View File

@ -0,0 +1,3 @@
/scripts
/src
/test

View File

@ -146,9 +146,16 @@ functionaltest: | nvim
functionaltest-lua: | nvim
+$(BUILD_TOOL) -C build functionaltest-lua
stylua:
stylua --check runtime/
lualint: | build/.ran-cmake deps
$(BUILD_TOOL) -C build lualint
_opt_stylua:
@command -v stylua && { $(MAKE) stylua; exit $$?; } \
|| echo "SKIP: stylua (stylua not found)"
shlint:
@shellcheck --version | head -n 2
shellcheck scripts/vim-patch.sh
@ -214,7 +221,7 @@ appimage:
appimage-%:
bash scripts/genappimage.sh $*
lint: check-single-includes clint lualint _opt_pylint _opt_shlint _opt_commitlint
lint: check-single-includes clint _opt_stylua lualint _opt_pylint _opt_shlint _opt_commitlint
# Generic pattern rules, allowing for `make build/bin/nvim` etc.
# Does not work with "Unix Makefiles".
@ -226,4 +233,4 @@ $(DEPS_BUILD_DIR)/%: phony_force
$(BUILD_TOOL) -C $(DEPS_BUILD_DIR) $(patsubst $(DEPS_BUILD_DIR)/%,%,$@)
endif
.PHONY: test lualint pylint shlint functionaltest unittest lint clint clean distclean nvim libnvim cmake deps install appimage checkprefix commitlint
.PHONY: test stylua lualint pylint shlint functionaltest unittest lint clint clean distclean nvim libnvim cmake deps install appimage checkprefix commitlint

View File

@ -7,23 +7,23 @@ if vim.g.do_filetype_lua ~= 1 then
return
end
vim.api.nvim_create_augroup("filetypedetect", {clear = false})
vim.api.nvim_create_augroup('filetypedetect', { clear = false })
vim.api.nvim_create_autocmd({"BufRead", "BufNewFile"}, {
group = "filetypedetect",
vim.api.nvim_create_autocmd({ 'BufRead', 'BufNewFile' }, {
group = 'filetypedetect',
callback = function()
vim.filetype.match(vim.fn.expand("<afile>"))
vim.filetype.match(vim.fn.expand('<afile>'))
end,
})
-- These *must* be sourced after the autocommand above is created
if not vim.g.did_load_ftdetect then
vim.cmd [[
vim.cmd([[
augroup filetypedetect
runtime! ftdetect/*.vim
runtime! ftdetect/*.lua
augroup END
]]
]])
end
-- 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 vim.g.did_load_filetypes then
vim.api.nvim_create_autocmd({"BufRead", "BufNewFile"}, {
group = "filetypedetect",
vim.api.nvim_create_autocmd({ 'BufRead', 'BufNewFile' }, {
group = 'filetypedetect',
command = "if !did_filetype() && expand('<amatch>') !~ g:ft_ignore_pat | runtime! scripts.vim | endif",
})
vim.api.nvim_create_autocmd("StdinReadPost", {
group = "filetypedetect",
command = "if !did_filetype() | runtime! scripts.vim | endif",
vim.api.nvim_create_autocmd('StdinReadPost', {
group = 'filetypedetect',
command = 'if !did_filetype() | runtime! scripts.vim | endif',
})
end
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

View File

@ -3,4 +3,4 @@
-- Last Change: 2022 Mar 29
-- 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
-- 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 hls = {} -- Store highlight groups as { attr, start, final }
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 byte = 0 -- byte offset
@ -47,7 +47,7 @@ local function highlight_line(line, linenr)
end
if continue_hl then
hls[#hls + 1] = {attr=attr, start=byte, final=-1}
hls[#hls + 1] = { attr = attr, start = byte, final = -1 }
else
if attr == NONE then
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
-- a leading byte (0xc0 and above) and continuation bytes (0x80 to 0xbf, or
-- 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
local last_hl = hls[#hls]
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
last_hl.final = byte + #char
else
hls[#hls + 1] = {attr=attr, start=byte, final=byte + #char}
hls[#hls + 1] = { attr = attr, start = byte, final = byte + #char }
end
overstrike = false
@ -106,25 +106,25 @@ local function highlight_line(line, linenr)
-- We only want to match against SGR sequences, which consist of ESC
-- 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)
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
-- Open Document Architecture and interchange format.
if sgr and not string.find(sgr, ":") then
if sgr and not string.find(sgr, ':') then
local match
while sgr and #sgr > 0 do
-- 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
end
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
escape = false
end
elseif char == "\027" then
elseif char == '\027' then
escape = true
prev_char = ''
elseif char == "\b" then
elseif char == '\b' then
overstrike = true
prev_char = chars[#chars]
byte = byte - #prev_char
@ -143,7 +143,7 @@ local function highlight_line(line, linenr)
hl_groups[hl.attr],
linenr - 1,
hl.start,
hl.final
hl.final,
}
end
end
@ -152,8 +152,8 @@ local function highlight_line(line, linenr)
end
local function highlight_man_page()
local mod = vim.api.nvim_buf_get_option(0, "modifiable")
vim.api.nvim_buf_set_option(0, "modifiable", true)
local mod = vim.api.nvim_buf_get_option(0, 'modifiable')
vim.api.nvim_buf_set_option(0, 'modifiable', true)
local lines = vim.api.nvim_buf_get_lines(0, 0, -1, false)
for i, line in ipairs(lines) do
@ -166,7 +166,7 @@ local function highlight_man_page()
end
buf_hls = {}
vim.api.nvim_buf_set_option(0, "modifiable", mod)
vim.api.nvim_buf_set_option(0, 'modifiable', mod)
end
return { highlight_man_page = highlight_man_page }

View File

@ -5,13 +5,17 @@ local F = {}
---@param a
---@param b
function F.if_nil(a, b)
if a == nil then return b end
if a == nil then
return b
end
return a
end
-- Use in combination with pcall
function F.ok_or_nil(status, ...)
if not status then return end
if not status then
return
end
return ...
end
@ -29,7 +33,7 @@ end
--- like {...} except preserve the length explicitly
function F.pack_len(...)
return {n=select('#', ...), ...}
return { n = select('#', ...), ... }
end
--- 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
-- the nvim binary as specified in executor.c
for k,v in pairs {
treesitter=true;
filetype = true;
F=true;
lsp=true;
highlight=true;
diagnostic=true;
keymap=true;
ui=true;
} do vim._submodules[k] = v end
for k, v in pairs({
treesitter = true,
filetype = true,
F = true,
lsp = true,
highlight = true,
diagnostic = true,
keymap = true,
ui = true,
}) do
vim._submodules[k] = v
end
vim.log = {
levels = {
TRACE = 0;
DEBUG = 1;
INFO = 2;
WARN = 3;
ERROR = 4;
OFF = 5;
}
TRACE = 0,
DEBUG = 1,
INFO = 2,
WARN = 3,
ERROR = 4,
OFF = 5,
},
}
-- 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
error('invalid pid')
end
local cmd = { 'ps', '-p', pid, '-o', 'comm=', }
local cmd = { 'ps', '-p', pid, '-o', 'comm=' }
local err, name = vim._system(cmd)
if 1 == err and vim.trim(name) == '' then
return {} -- Process not found.
return {} -- Process not found.
elseif 0 ~= err then
error('command failed: '..vim.fn.string(cmd))
error('command failed: ' .. vim.fn.string(cmd))
end
local _, ppid = vim._system({ 'ps', '-p', pid, '-o', 'ppid=', })
local _, ppid = vim._system({ 'ps', '-p', pid, '-o', 'ppid=' })
-- Remove trailing whitespace.
name = vim.trim(name):gsub('^.*/', '')
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
error('invalid ppid')
end
local cmd = { 'pgrep', '-P', ppid, }
local cmd = { 'pgrep', '-P', ppid }
local err, rv = vim._system(cmd)
if 1 == err and vim.trim(rv) == '' then
return {} -- Process not found.
return {} -- Process not found.
elseif 0 ~= err then
error('command failed: '..vim.fn.string(cmd))
error('command failed: ' .. vim.fn.string(cmd))
end
local children = {}
for s in rv:gmatch('%S+') do
@ -124,8 +126,8 @@ end
---
---@see https://github.com/kikito/inspect.lua
---@see https://github.com/mpeterv/vinspect
local function inspect(object, options) -- luacheck: no unused
error(object, options) -- Stub for gen_vimdoc.py
local function inspect(object, options) -- luacheck: no unused
error(object, options) -- Stub for gen_vimdoc.py
end
do
@ -160,11 +162,11 @@ do
local now = vim.loop.now()
local is_first_chunk = phase < 2
local is_last_chunk = phase == -1 or phase == 3
if is_first_chunk then -- Reset flags.
if is_first_chunk then -- Reset flags.
tdots, tick, got_line1, undo_started, trailing_nl = now, 0, false, false, false
end
if #lines == 0 then
lines = {''}
lines = { '' }
end
if #lines == 1 and lines[1] == '' and not is_last_chunk then
-- An empty chunk can cause some edge cases in streamed pasting,
@ -172,7 +174,7 @@ do
return true
end
-- Note: mode doesn't always start with "c" in cmdline mode, so use getcmdtype() instead.
if vim.fn.getcmdtype() ~= '' then -- cmdline-mode: paste only 1 line.
if vim.fn.getcmdtype() ~= '' then -- cmdline-mode: paste only 1 line.
if not got_line1 then
got_line1 = (#lines > 1)
-- Escape control characters
@ -187,9 +189,9 @@ do
if undo_started then
vim.api.nvim_command('undojoin')
end
if mode:find('^i') or mode:find('^n?t') then -- Insert mode or Terminal buffer
if mode:find('^i') or mode:find('^n?t') then -- Insert mode or Terminal buffer
vim.api.nvim_put(lines, 'c', false, true)
elseif phase < 2 and mode:find('^R') and not mode:find('^Rv') then -- Replace mode
elseif phase < 2 and mode:find('^R') and not mode:find('^Rv') then -- Replace mode
-- TODO: implement Replace mode streamed pasting
-- TODO: support Virtual Replace mode
local nchars = 0
@ -197,26 +199,26 @@ do
nchars = nchars + line:len()
end
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]
firstline = bufline:sub(1, col)..firstline
firstline = bufline:sub(1, col) .. firstline
lines[1] = firstline
lines[#lines] = lines[#lines]..bufline:sub(col + nchars + 1, bufline:len())
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
if mode:find('^n') then -- Normal mode
lines[#lines] = lines[#lines] .. bufline:sub(col + nchars + 1, bufline:len())
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
if mode:find('^n') then -- Normal mode
-- When there was a trailing new line in the previous chunk,
-- the cursor is on the first character of the next line,
-- so paste before the cursor instead of after it.
vim.api.nvim_put(lines, 'c', not trailing_nl, false)
else -- Visual or Select mode
else -- Visual or Select mode
vim.api.nvim_command([[exe "silent normal! \<Del>"]])
local del_start = vim.fn.getpos("'[")
local cursor_pos = vim.fn.getpos('.')
if mode:find('^[VS]') then -- linewise
if cursor_pos[2] < del_start[2] then -- replacing lines at eof
if mode:find('^[VS]') then -- linewise
if cursor_pos[2] < del_start[2] then -- replacing lines at eof
-- create a new line
vim.api.nvim_put({''}, 'l', true, true)
vim.api.nvim_put({ '' }, 'l', true, true)
end
vim.api.nvim_put(lines, 'c', false, false)
else
@ -227,7 +229,7 @@ do
-- put cursor at the end of the text instead of one character after it
vim.fn.setpos('.', vim.fn.getpos("']"))
trailing_nl = lines[#lines] == ''
else -- Don't know what to do in other modes
else -- Don't know what to do in other modes
return false
end
undo_started = true
@ -240,9 +242,9 @@ do
vim.api.nvim_command(('echo "%s"'):format(dots))
end
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
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.in_fast_event()|
function vim.schedule_wrap(cb)
return (function (...)
return function(...)
local args = vim.F.pack_len(...)
vim.schedule(function() cb(vim.F.unpack_len(args)) end)
end)
vim.schedule(function()
cb(vim.F.unpack_len(args))
end)
end
end
-- vim.fn.{func}(...)
@ -264,7 +268,7 @@ vim.fn = setmetatable({}, {
local _fn
if vim.api[key] ~= nil then
_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
else
_fn = function(...)
@ -273,7 +277,7 @@ vim.fn = setmetatable({}, {
end
t[key] = _fn
return _fn
end
end,
})
vim.funcref = function(viml_func_name)
@ -291,9 +295,9 @@ do
--@private
local function make_dict_accessor(scope, handle)
validate {
scope = {scope, 's'};
}
validate({
scope = { scope, 's' },
})
local mt = {}
function mt:__newindex(k, v)
return vim._setvar(scope, handle or 0, k, v)
@ -343,7 +347,7 @@ function vim.region(bufnr, pos1, pos2, regtype, inclusive)
local region = {}
for l = pos1[1], pos2[1] do
local c1, c2
if regtype:byte() == 22 then -- block selection: take width from regtype
if regtype:byte() == 22 then -- block selection: take width from regtype
c1 = pos1[2]
c2 = c1 + regtype:sub(2)
-- and adjust for non-ASCII characters
@ -355,10 +359,10 @@ function vim.region(bufnr, pos1, pos2, regtype, inclusive)
c2 = vim.str_byteindex(bufline, c2)
end
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
end
table.insert(region, l, {c1, c2})
table.insert(region, l, { c1, c2 })
end
return region
end
@ -372,19 +376,22 @@ end
---@param timeout Number of milliseconds to wait before calling `fn`
---@return timer luv timer object
function vim.defer_fn(fn, timeout)
vim.validate { fn = { fn, 'c', true}; }
vim.validate({ fn = { fn, 'c', true } })
local timer = vim.loop.new_timer()
timer:start(timeout, 0, vim.schedule_wrap(function()
timer:stop()
timer:close()
timer:start(
timeout,
0,
vim.schedule_wrap(function()
timer:stop()
timer:close()
fn()
end))
fn()
end)
)
return timer
end
--- Display a notification to the user.
---
--- 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
vim.api.nvim_err_writeln(msg)
elseif level == vim.log.levels.WARN then
vim.api.nvim_echo({{msg, 'WarningMsg'}}, true, {})
vim.api.nvim_echo({ { msg, 'WarningMsg' } }, true, {})
else
vim.api.nvim_echo({{msg}}, true, {})
vim.api.nvim_echo({ { msg } }, true, {})
end
end
@ -453,10 +460,10 @@ function vim.on_key(fn, ns_id)
return #on_key_cbs
end
vim.validate {
fn = { fn, 'c', true},
ns_id = { ns_id, 'n', true }
}
vim.validate({
fn = { fn, 'c', true },
ns_id = { ns_id, 'n', true },
})
if ns_id == nil or ns_id == 0 then
ns_id = vim.api.nvim_create_namespace('')
@ -481,10 +488,13 @@ function vim._on_key(char)
end
if failed_ns_ids[1] then
error(string.format(
"Error executing 'on_key' with ns_ids '%s'\n Messages: %s",
table.concat(failed_ns_ids, ", "),
table.concat(failed_messages, "\n")))
error(
string.format(
"Error executing 'on_key' with ns_ids '%s'\n Messages: %s",
table.concat(failed_ns_ids, ', '),
table.concat(failed_messages, '\n')
)
)
end
end
@ -511,8 +521,10 @@ function vim._expand_pat(pat, env)
-- Probably just need to do a smarter match than just `:match`
-- Get the last part of the pattern
local last_part = pat:match("[%w.:_%[%]'\"]+$")
if not last_part then return {}, 0 end
local last_part = pat:match('[%w.:_%[%]\'"]+$')
if not last_part then
return {}, 0
end
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
-- Just attempt to get the string directly from the environment
if type(part) == "string" then
if type(part) == 'string' then
key = part
else
-- 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)
if field == nil then
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)
elseif final_env == vim and vim._submodules[key] then
field = vim[key]
@ -570,18 +582,18 @@ function vim._expand_pat(pat, env)
local keys = {}
---@private
local function insert_keys(obj)
for k,_ in pairs(obj) do
if type(k) == "string" and string.sub(k,1,string.len(match_part)) == match_part then
table.insert(keys,k)
for k, _ in pairs(obj) do
if type(k) == 'string' and string.sub(k, 1, string.len(match_part)) == match_part then
table.insert(keys, k)
end
end
end
if type(final_env) == "table" then
if type(final_env) == 'table' then
insert_keys(final_env)
end
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)
end
if final_env == vim then
@ -602,12 +614,12 @@ vim._expand_pat_get_parts = function(lua_string)
for idx = 1, #lua_string do
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)
accumulator = ''
search_index = idx + 1
elseif s == "[" then
elseif s == '[' then
in_brackets = true
table.insert(parts, accumulator)
@ -619,7 +631,7 @@ vim._expand_pat_get_parts = function(lua_string)
in_brackets = false
search_index = idx + 1
if string_char == "VAR" then
if string_char == 'VAR' then
table.insert(parts, { accumulator })
accumulator = ''
@ -631,7 +643,7 @@ vim._expand_pat_get_parts = function(lua_string)
if s == '"' or s == "'" then
string_char = s
elseif s ~= ' ' then
string_char = "VAR"
string_char = 'VAR'
accumulator = s
end
elseif string_char then
@ -649,7 +661,9 @@ vim._expand_pat_get_parts = function(lua_string)
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
end
@ -677,20 +691,20 @@ function vim._cs_remote(rcid, server_addr, connect_error, args)
local function connection_failure_errmsg(consequence)
local explanation
if server_addr == '' then
explanation = "No server specified with --server"
explanation = 'No server specified with --server'
else
explanation = "Failed to connect to '" .. server_addr .. "'"
if connect_error ~= "" then
explanation = explanation .. ": " .. connect_error
if connect_error ~= '' then
explanation = explanation .. ': ' .. connect_error
end
end
return "E247: " .. explanation .. ". " .. consequence
return 'E247: ' .. explanation .. '. ' .. consequence
end
local f_silent = false
local f_tab = false
local subcmd = string.sub(args[1],10)
local subcmd = string.sub(args[1], 10)
if subcmd == 'tab' then
f_tab = true
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]))
return { should_exit = true, tabbed = false }
elseif subcmd ~= '' then
return { errmsg='Unknown option argument: ' .. args[1] }
return { errmsg = 'Unknown option argument: ' .. args[1] }
end
if rcid == 0 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
else
local command = {}
if f_tab then table.insert(command, 'tab') end
if f_tab then
table.insert(command, 'tab')
end
table.insert(command, 'drop')
for i = 2, #args do
table.insert(command, vim.fn.fnameescape(args[i]))
@ -745,11 +761,11 @@ end
---@param plugin string|nil Plugin name that the function will be removed
--- from. Defaults to "Nvim".
function vim.deprecate(name, alternative, version, plugin)
local message = name .. ' is deprecated'
plugin = plugin or "Nvim"
message = alternative and (message .. ', use ' .. alternative .. ' instead.') or message
message = message .. ' See :h deprecated\nThis function will be removed in ' .. plugin .. ' version ' .. version
vim.notify_once(message, vim.log.levels.WARN)
local message = name .. ' is deprecated'
plugin = plugin or 'Nvim'
message = alternative and (message .. ', use ' .. alternative .. ' instead.') or message
message = message .. ' See :h deprecated\nThis function will be removed in ' .. plugin .. ' version ' .. version
vim.notify_once(message, vim.log.levels.WARN)
end
require('vim._meta')

View File

@ -3,8 +3,8 @@ local vim = assert(vim)
local pathtrails = {}
vim._so_trails = {}
for s in (package.cpath..';'):gmatch('[^;]*;') do
s = s:sub(1, -2) -- Strip trailing semicolon
for s in (package.cpath .. ';'):gmatch('[^;]*;') do
s = s:sub(1, -2) -- Strip trailing semicolon
-- Find out path patterns. pathtrail should contain something like
-- /?.so, \?.dll. This allows not to bother determining what correct
-- suffixes are.
@ -17,29 +17,29 @@ end
function vim._load_package(name)
local basename = name:gsub('%.', '/')
local paths = {"lua/"..basename..".lua", "lua/"..basename.."/init.lua"}
local found = vim.api.nvim__get_runtime(paths, false, {is_lua=true})
local paths = { 'lua/' .. basename .. '.lua', 'lua/' .. basename .. '/init.lua' }
local found = vim.api.nvim__get_runtime(paths, false, { is_lua = true })
if #found > 0 then
local f, err = loadfile(found[1])
return f or error(err)
end
local so_paths = {}
for _,trail in ipairs(vim._so_trails) do
local path = "lua"..trail:gsub('?', basename) -- so_trails contains a leading slash
for _, trail in ipairs(vim._so_trails) do
local path = 'lua' .. trail:gsub('?', basename) -- so_trails contains a leading slash
table.insert(so_paths, path)
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
-- 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
-- b) replace all dots by underscores
-- c) prepend "luaopen_"
-- 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 f, err = package.loadlib(found[1], "luaopen_"..modname:gsub("%.", "_"))
local f, err = package.loadlib(found[1], 'luaopen_' .. modname:gsub('%.', '_'))
return f or error(err)
end
return nil
@ -49,15 +49,15 @@ end
table.insert(package.loaders, 2, vim._load_package)
-- 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.
setmetatable(vim, {
__index = function(t, key)
if vim._submodules[key] then
t[key] = require('vim.'..key)
t[key] = require('vim.' .. key)
return t[key]
elseif vim.startswith(key, 'uri_') then
local val = require('vim.uri')[key]
@ -67,7 +67,7 @@ setmetatable(vim, {
return t[key]
end
end
end
end,
})
--- <Docs described in |vim.empty_dict()| >
@ -79,5 +79,5 @@ end
-- only on main thread: functions for interacting with editor state
if not vim.is_thread() then
require'vim._editor'
require('vim._editor')
end

View File

@ -5,15 +5,17 @@ local a = vim.api
local validate = vim.validate
local SET_TYPES = setmetatable({
SET = 0,
LOCAL = 1,
SET = 0,
LOCAL = 1,
GLOBAL = 2,
}, { __index = error })
local options_info = {}
for _, v in pairs(a.nvim_get_all_options_info()) do
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
local get_scoped_options = function(scope)
@ -27,19 +29,21 @@ local get_scoped_options = function(scope)
return result
end
local buf_options = get_scoped_options("buf")
local glb_options = get_scoped_options("global")
local win_options = get_scoped_options("win")
local buf_options = get_scoped_options('buf')
local glb_options = get_scoped_options('global')
local win_options = get_scoped_options('win')
local function make_meta_accessor(get, set, del, validator)
validator = validator or function() return true end
validator = validator or function()
return true
end
validate {
get = {get, 'f'};
set = {set, 'f'};
del = {del, 'f', true};
validator = {validator, 'f'};
}
validate({
get = { get, 'f' },
set = { set, 'f' },
del = { del, 'f', true },
validator = { validator, 'f' },
})
local mt = {}
function mt:__newindex(k, v)
@ -73,7 +77,7 @@ end, vim.fn.setenv)
do -- buffer option accessor
local function new_buf_opt_accessor(bufnr)
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)
end
@ -103,7 +107,7 @@ end
do -- window option accessor
local function new_win_opt_accessor(winnr)
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)
end
return a.nvim_win_get_option(winnr or 0, k)
@ -131,17 +135,19 @@ end
-- vim global option
-- this ONLY sets the global option. like `setglobal`
vim.go = make_meta_accessor(
function(k) return a.nvim_get_option_value(k, {scope = "global"}) end,
function(k, v) return a.nvim_set_option_value(k, v, {scope = "global"}) end
)
vim.go = make_meta_accessor(function(k)
return a.nvim_get_option_value(k, { scope = 'global' })
end, function(k, v)
return a.nvim_set_option_value(k, v, { scope = 'global' })
end)
-- vim `set` style options.
-- it has no additional metamethod magic.
vim.o = make_meta_accessor(
function(k) return a.nvim_get_option_value(k, {}) end,
function(k, v) return a.nvim_set_option_value(k, v, {}) end
)
vim.o = make_meta_accessor(function(k)
return a.nvim_get_option_value(k, {})
end, function(k, v)
return a.nvim_set_option_value(k, v, {})
end)
---@brief [[
--- 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
local remove_duplicate_values = function(t)
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
if not seen[v] then
table.insert(result, v)
@ -171,37 +179,41 @@ end
local key_value_options = {
fillchars = true,
listchars = true,
winhl = true,
winhl = true,
}
---@class OptionTypes
--- Option Type Enum
local OptionTypes = setmetatable({
BOOLEAN = 0,
NUMBER = 1,
STRING = 2,
ARRAY = 3,
MAP = 4,
SET = 5,
NUMBER = 1,
STRING = 2,
ARRAY = 3,
MAP = 4,
SET = 5,
}, {
__index = function(_, k) error("Not a valid OptionType: " .. k) end,
__newindex = function(_, k) error("Cannot set a new OptionType: " .. k) end,
__index = function(_, k)
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.
---@return OptionType
local get_option_type = function(name, info)
if info.type == "boolean" then
if info.type == 'boolean' then
return OptionTypes.BOOLEAN
elseif info.type == "number" then
elseif info.type == 'number' then
return OptionTypes.NUMBER
elseif info.type == "string" then
elseif info.type == 'string' then
if not info.commalist and not info.flaglist then
return OptionTypes.STRING
end
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
end
@ -211,13 +223,12 @@ local get_option_type = function(name, info)
return OptionTypes.ARRAY
end
error("Fallthrough in OptionTypes")
error('Fallthrough in OptionTypes')
else
error("Not a known info.type:" .. info.type)
error('Not a known info.type:' .. info.type)
end
end
-- Check whether the OptionTypes is allowed for vim.opt
-- If it does not match, throw an error which indicates which option causes the error.
local function assert_valid_value(name, value, types)
@ -228,16 +239,18 @@ local function assert_valid_value(name, value, types)
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
local valid_types = {
[OptionTypes.BOOLEAN] = { "boolean" },
[OptionTypes.NUMBER] = { "number" },
[OptionTypes.STRING] = { "string" },
[OptionTypes.SET] = { "string", "table" },
[OptionTypes.ARRAY] = { "string", "table" },
[OptionTypes.MAP] = { "string", "table" },
[OptionTypes.BOOLEAN] = { 'boolean' },
[OptionTypes.NUMBER] = { 'number' },
[OptionTypes.STRING] = { 'string' },
[OptionTypes.SET] = { 'string', 'table' },
[OptionTypes.ARRAY] = { 'string', 'table' },
[OptionTypes.MAP] = { 'string', 'table' },
}
--- 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.
-- Each function takes (info, lua_value) -> vim_value
local to_vim_value = {
[OptionTypes.BOOLEAN] = function(_, value) return value end,
[OptionTypes.NUMBER] = function(_, value) return value end,
[OptionTypes.STRING] = function(_, value) return value end,
[OptionTypes.BOOLEAN] = function(_, value)
return value
end,
[OptionTypes.NUMBER] = function(_, value)
return value
end,
[OptionTypes.STRING] = function(_, value)
return value
end,
[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
local keys = {}
@ -261,7 +282,7 @@ local convert_value_to_vim = (function()
end
table.sort(keys)
return table.concat(keys, ",")
return table.concat(keys, ',')
else
local result = ''
for k, v in pairs(value) do
@ -275,23 +296,27 @@ local convert_value_to_vim = (function()
end,
[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
value = remove_duplicate_values(value)
end
return table.concat(value, ",")
return table.concat(value, ',')
end,
[OptionTypes.MAP] = function(_, value)
if type(value) == "string" then return value end
if type(value) == 'string' then
return value
end
local result = {}
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
table.sort(result)
return table.concat(result, ",")
return table.concat(result, ',')
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.
-- Each function takes (info, vim_value) -> lua_value
local to_lua_value = {
[OptionTypes.BOOLEAN] = function(_, value) return value end,
[OptionTypes.NUMBER] = function(_, value) return value end,
[OptionTypes.STRING] = function(_, value) return value end,
[OptionTypes.BOOLEAN] = function(_, value)
return value
end,
[OptionTypes.NUMBER] = function(_, value)
return value
end,
[OptionTypes.STRING] = function(_, value)
return value
end,
[OptionTypes.ARRAY] = function(info, value)
if type(value) == "table" then
if type(value) == 'table' then
if not info.allows_duplicates then
value = remove_duplicate_values(value)
end
@ -332,41 +363,43 @@ local convert_value_to_lua = (function()
end
-- Handles unescaped commas in a list.
if string.find(value, ",,,") then
local comma_split = vim.split(value, ",,,")
if string.find(value, ',,,') then
local comma_split = vim.split(value, ',,,')
local left = comma_split[1]
local right = comma_split[2]
local result = {}
vim.list_extend(result, vim.split(left, ","))
table.insert(result, ",")
vim.list_extend(result, vim.split(right, ","))
vim.list_extend(result, vim.split(left, ','))
table.insert(result, ',')
vim.list_extend(result, vim.split(right, ','))
table.sort(result)
return result
end
if string.find(value, ",^,,", 1, true) then
local comma_split = vim.split(value, ",^,,", true)
if string.find(value, ',^,,', 1, true) then
local comma_split = vim.split(value, ',^,,', true)
local left = comma_split[1]
local right = comma_split[2]
local result = {}
vim.list_extend(result, vim.split(left, ","))
table.insert(result, "^,")
vim.list_extend(result, vim.split(right, ","))
vim.list_extend(result, vim.split(left, ','))
table.insert(result, '^,')
vim.list_extend(result, vim.split(right, ','))
table.sort(result)
return result
end
return vim.split(value, ",")
return vim.split(value, ',')
end,
[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,
-- so empty table should be returned.
@ -374,10 +407,10 @@ local convert_value_to_lua = (function()
return {}
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
local split_value = vim.split(value, ",")
local split_value = vim.split(value, ',')
local result = {}
for _, v in ipairs(split_value) do
result[v] = true
@ -395,15 +428,17 @@ local convert_value_to_lua = (function()
end,
[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 comma_split = vim.split(raw_value, ",")
local comma_split = vim.split(raw_value, ',')
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)
result[key] = value
@ -443,17 +478,21 @@ local prepend_value = (function()
end,
[OptionTypes.MAP] = function(left, right)
return vim.tbl_extend("force", left, right)
return vim.tbl_extend('force', left, right)
end,
[OptionTypes.SET] = function(left, right)
return vim.tbl_extend("force", left, right)
return vim.tbl_extend('force', left, right)
end,
}
return function(name, info, current, new)
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)()
@ -478,17 +517,21 @@ local add_value = (function()
end,
[OptionTypes.MAP] = function(left, right)
return vim.tbl_extend("force", left, right)
return vim.tbl_extend('force', left, right)
end,
[OptionTypes.SET] = function(left, right)
return vim.tbl_extend("force", left, right)
return vim.tbl_extend('force', left, right)
end,
}
return function(name, info, current, new)
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)()
@ -518,11 +561,11 @@ local remove_value = (function()
end,
[OptionTypes.STRING] = function()
error("Subtraction not supported for strings.")
error('Subtraction not supported for strings.')
end,
[OptionTypes.ARRAY] = function(left, right)
if type(right) == "string" then
if type(right) == 'string' then
remove_one_item(left, right)
else
for _, v in ipairs(right) do
@ -534,7 +577,7 @@ local remove_value = (function()
end,
[OptionTypes.MAP] = function(left, right)
if type(right) == "string" then
if type(right) == 'string' then
left[right] = nil
else
for _, v in ipairs(right) do
@ -546,7 +589,7 @@ local remove_value = (function()
end,
[OptionTypes.SET] = function(left, right)
if type(right) == "string" then
if type(right) == 'string' then
left[right] = nil
else
for _, v in ipairs(right) do
@ -567,9 +610,9 @@ local create_option_metatable = function(set_type)
local set_mt, option_mt
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.")
value = value._value
@ -584,9 +627,9 @@ local create_option_metatable = function(set_type)
local scope
if set_type == SET_TYPES.GLOBAL then
scope = "global"
scope = 'global'
elseif set_type == SET_TYPES.LOCAL then
scope = "local"
scope = 'local'
end
option_mt = {
@ -594,7 +637,7 @@ local create_option_metatable = function(set_type)
-- opt[my_option] = value
_set = function(self)
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
end,
@ -625,13 +668,13 @@ local create_option_metatable = function(set_type)
__sub = function(self, right)
return make_option(self._name, remove_value(self._name, self._info, self._value, right))
end
end,
}
option_mt.__index = option_mt
set_mt = {
__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,
__newindex = function(_, k, v)

View File

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

View File

@ -14,14 +14,14 @@ function M.create(higroup, hi_info, default)
local options = {}
-- TODO: Add validation
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
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
---@private
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
--- Highlight range between two positions
@ -37,7 +37,7 @@ end
-- - priority number indicating priority of highlight (default priorities.user)
function M.range(bufnr, ns, higroup, start, finish, opts)
opts = opts or {}
local regtype = opts.regtype or "v"
local regtype = opts.regtype or 'v'
local inclusive = opts.inclusive or false
local priority = opts.priority or M.priorities.user
@ -63,7 +63,7 @@ function M.range(bufnr, ns, higroup, start, finish, opts)
end
end
local yank_ns = api.nvim_create_namespace("hlyank")
local yank_ns = api.nvim_create_namespace('hlyank')
--- Highlight the yanked region
---
--- use from init.vim via
@ -87,10 +87,10 @@ function M.on_yank(opts)
if t == nil then
return true
else
return type(t) == "table"
return type(t) == 'table'
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 {}
@ -98,17 +98,17 @@ function M.on_yank(opts)
local on_macro = opts.on_macro or 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
end
if event.operator ~= "y" or event.regtype == "" then
if event.operator ~= 'y' or event.regtype == '' then
return
end
if not on_visual and event.visual then
return
end
local higroup = opts.higroup or "IncSearch"
local higroup = opts.higroup or 'IncSearch'
local timeout = opts.timeout or 150
local bufnr = api.nvim_get_current_buf()

View File

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

View File

@ -49,20 +49,20 @@ local keymap = {}
--- Default `false`.
---@see |nvim_set_keymap()|
function keymap.set(mode, lhs, rhs, opts)
vim.validate {
mode = {mode, {'s', 't'}},
lhs = {lhs, 's'},
rhs = {rhs, {'s', 'f'}},
opts = {opts, 't', true}
}
vim.validate({
mode = { mode, { 's', 't' } },
lhs = { lhs, 's' },
rhs = { rhs, { 's', 'f' } },
opts = { opts, 't', true },
})
opts = vim.deepcopy(opts) or {}
local is_rhs_luaref = type(rhs) == "function"
mode = type(mode) == 'string' and {mode} or mode
local is_rhs_luaref = type(rhs) == 'function'
mode = type(mode) == 'string' and { mode } or mode
if is_rhs_luaref and opts.expr then
local user_rhs = rhs
rhs = function ()
rhs = function()
local res = user_rhs()
if res == nil then
-- TODO(lewis6991): Handle this in C?
@ -118,14 +118,14 @@ end
---@see |vim.keymap.set()|
---
function keymap.del(modes, lhs, opts)
vim.validate {
mode = {modes, {'s', 't'}},
lhs = {lhs, 's'},
opts = {opts, 't', true}
}
vim.validate({
mode = { modes, { 's', 't' } },
lhs = { lhs, 's' },
opts = { opts, 't', true },
})
opts = opts or {}
modes = type(modes) == 'string' and {modes} or modes
modes = type(modes) == 'string' and { modes } or modes
local buffer = false
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,
value = {
raw = table.concat(raw, ''),
esc = table.concat(esc, '')
esc = table.concat(esc, ''),
},
pos = new_pos,
}
@ -248,48 +248,66 @@ S.format = P.any(
capture_index = values[3],
}, Node)
end),
P.map(P.seq(S.dollar, S.open, S.int, S.colon, S.slash, P.any(
P.token('upcase'),
P.token('downcase'),
P.token('capitalize'),
P.token('camelcase'),
P.token('pascalcase')
), S.close), function(values)
return setmetatable({
type = Node.Type.FORMAT,
capture_index = values[3],
modifier = values[6],
}, Node)
end),
P.map(P.seq(S.dollar, S.open, S.int, S.colon, P.any(
P.seq(S.question, P.take_until({ ':' }, { '\\' }), S.colon, P.take_until({ '}' }, { '\\' })),
P.seq(S.plus, P.take_until({ '}' }, { '\\' })),
P.seq(S.minus, P.take_until({ '}' }, { '\\' }))
), S.close), function(values)
return setmetatable({
type = Node.Type.FORMAT,
capture_index = values[3],
if_text = values[5][2].esc,
else_text = (values[5][4] or {}).esc,
}, Node)
end)
P.map(
P.seq(
S.dollar,
S.open,
S.int,
S.colon,
S.slash,
P.any(P.token('upcase'), P.token('downcase'), P.token('capitalize'), P.token('camelcase'), P.token('pascalcase')),
S.close
),
function(values)
return setmetatable({
type = Node.Type.FORMAT,
capture_index = values[3],
modifier = values[6],
}, Node)
end
),
P.map(
P.seq(
S.dollar,
S.open,
S.int,
S.colon,
P.any(
P.seq(S.question, P.take_until({ ':' }, { '\\' }), S.colon, P.take_until({ '}' }, { '\\' })),
P.seq(S.plus, P.take_until({ '}' }, { '\\' })),
P.seq(S.minus, P.take_until({ '}' }, { '\\' }))
),
S.close
),
function(values)
return setmetatable({
type = Node.Type.FORMAT,
capture_index = values[3],
if_text = values[5][2].esc,
else_text = (values[5][4] or {}).esc,
}, Node)
end
)
)
S.transform = P.map(P.seq(
S.slash,
P.take_until({ '/' }, { '\\' }),
S.slash,
P.many(P.any(S.format, S.text({ '$', '/' }, { '\\' }))),
S.slash,
P.opt(P.pattern('[ig]+'))
), function(values)
return setmetatable({
type = Node.Type.TRANSFORM,
pattern = values[2].raw,
format = values[4],
option = values[6],
}, Node)
end)
S.transform = P.map(
P.seq(
S.slash,
P.take_until({ '/' }, { '\\' }),
S.slash,
P.many(P.any(S.format, S.text({ '$', '/' }, { '\\' }))),
S.slash,
P.opt(P.pattern('[ig]+'))
),
function(values)
return setmetatable({
type = Node.Type.TRANSFORM,
pattern = values[2].raw,
format = values[4],
option = values[6],
}, Node)
end
)
S.tabstop = P.any(
P.map(P.seq(S.dollar, S.int), function(values)
@ -314,34 +332,38 @@ S.tabstop = P.any(
)
S.placeholder = P.any(
P.map(P.seq(S.dollar, S.open, S.int, S.colon, P.many(P.any(S.toplevel, S.text({ '$', '}' }, { '\\' }))), S.close), function(values)
return setmetatable({
type = Node.Type.PLACEHOLDER,
tabstop = values[3],
children = values[5],
}, Node)
end)
P.map(
P.seq(S.dollar, S.open, S.int, S.colon, P.many(P.any(S.toplevel, S.text({ '$', '}' }, { '\\' }))), S.close),
function(values)
return setmetatable({
type = Node.Type.PLACEHOLDER,
tabstop = values[3],
children = values[5],
}, Node)
end
)
)
S.choice = P.map(P.seq(
S.dollar,
S.open,
S.int,
S.pipe,
P.many(
P.map(P.seq(S.text({ ',', '|' }), P.opt(S.comma)), function(values)
S.choice = P.map(
P.seq(
S.dollar,
S.open,
S.int,
S.pipe,
P.many(P.map(P.seq(S.text({ ',', '|' }), P.opt(S.comma)), function(values)
return values[1].esc
end)
end)),
S.pipe,
S.close
),
S.pipe,
S.close
), function(values)
return setmetatable({
type = Node.Type.CHOICE,
tabstop = values[3],
items = values[5],
}, Node)
end)
function(values)
return setmetatable({
type = Node.Type.CHOICE,
tabstop = values[3],
items = values[5],
}, Node)
end
)
S.variable = P.any(
P.map(P.seq(S.dollar, S.var), function(values)
@ -363,13 +385,16 @@ S.variable = P.any(
transform = values[4],
}, Node)
end),
P.map(P.seq(S.dollar, S.open, S.var, S.colon, P.many(P.any(S.toplevel, S.text({ '$', '}' }, { '\\' }))), S.close), function(values)
return setmetatable({
type = Node.Type.VARIABLE,
name = values[3],
children = values[5],
}, Node)
end)
P.map(
P.seq(S.dollar, S.open, S.var, S.colon, P.many(P.any(S.toplevel, S.text({ '$', '}' }, { '\\' }))), S.close),
function(values)
return setmetatable({
type = Node.Type.VARIABLE,
name = values[3],
children = values[5],
}, Node)
end
)
)
S.snippet = P.map(P.many(P.any(S.toplevel, S.text({ '$' }, { '}', '\\' }))), function(values)

View File

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

View File

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

View File

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

View File

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

View File

@ -8,20 +8,19 @@ function M.check()
local log = require('vim.lsp.log')
local current_log_level = log.get_level()
local log_level_string = log.levels[current_log_level]
report_info(string.format("LSP log level : %s", log_level_string))
report_info(string.format('LSP log level : %s', log_level_string))
if current_log_level < log.levels.WARN then
report_warn(string.format("Log level %s will cause degraded performance and high disk usage", log_level_string))
report_warn(string.format('Log level %s will cause degraded performance and high disk usage', log_level_string))
end
local log_path = vim.lsp.get_log_path()
report_info(string.format("Log path: %s", log_path))
report_info(string.format('Log path: %s', log_path))
local log_size = vim.loop.fs_stat(log_path).size
local report_fn = (log_size / 1000000 > 100 and report_warn or report_info)
report_fn(string.format("Log size: %d KB", log_size / 1000 ))
report_fn(string.format('Log size: %d KB', log_size / 1000))
end
return M

View File

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

View File

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

View File

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

View File

@ -79,7 +79,7 @@ local function compute_line_length(line, offset_encoding)
local length
local _
if offset_encoding == 'utf-16' then
_, length = str_utfindex(line)
_, length = str_utfindex(line)
elseif offset_encoding == 'utf-32' then
length, _ = str_utfindex(line)
else
@ -100,7 +100,7 @@ local function align_end_position(line, byte, offset_encoding)
-- If on the first byte, or an empty string: the trivial case
if byte == 1 or #line == 0 then
char = byte
-- Called in the case of extending an empty line "" -> "a"
-- Called in the case of extending an empty line "" -> "a"
elseif byte == #line + 1 then
char = compute_line_length(line, offset_encoding) + 1
else
@ -175,12 +175,12 @@ local function compute_start_range(prev_lines, curr_lines, firstline, lastline,
end
-- Convert byte to codepoint if applicable
if start_byte_idx == 1 or (#prev_line == 0 and start_byte_idx == 1)then
if start_byte_idx == 1 or (#prev_line == 0 and start_byte_idx == 1) then
byte_idx = start_byte_idx
char_idx = 1
elseif start_byte_idx == #prev_line + 1 then
byte_idx = start_byte_idx
char_idx = compute_line_length(prev_line, offset_encoding) + 1
char_idx = compute_line_length(prev_line, offset_encoding) + 1
else
byte_idx = start_byte_idx + str_utf_start(prev_line, start_byte_idx)
char_idx = byte_to_utf(prev_line, byte_idx, offset_encoding)
@ -203,14 +203,30 @@ end
---@param new_lastline integer
---@param offset_encoding string
---@returns (int, int) end_line_idx and end_col_idx of range
local function compute_end_range(prev_lines, curr_lines, start_range, firstline, lastline, new_lastline, offset_encoding)
local function compute_end_range(
prev_lines,
curr_lines,
start_range,
firstline,
lastline,
new_lastline,
offset_encoding
)
-- If firstline == new_lastline, the first change occurred on a line that was deleted.
-- In this case, the last_byte...
if firstline == new_lastline then
return { line_idx = (lastline - new_lastline + firstline), byte_idx = 1, char_idx = 1 }, { line_idx = firstline, byte_idx = 1, char_idx = 1 }
return { line_idx = (lastline - new_lastline + firstline), byte_idx = 1, char_idx = 1 }, {
line_idx = firstline,
byte_idx = 1,
char_idx = 1,
}
end
if firstline == lastline then
return { line_idx = firstline, byte_idx = 1, char_idx = 1 }, { line_idx = new_lastline - lastline + firstline, byte_idx = 1, char_idx = 1 }
return { line_idx = firstline, byte_idx = 1, char_idx = 1 }, {
line_idx = new_lastline - lastline + firstline,
byte_idx = 1,
char_idx = 1,
}
end
-- Compare on last line, at minimum will be the start range
local start_line_idx = start_range.line_idx
@ -239,9 +255,7 @@ local function compute_end_range(prev_lines, curr_lines, start_range, firstline,
end
for idx = 0, max_length do
byte_offset = idx
if
str_byte(prev_line, prev_line_length - byte_offset) ~= str_byte(curr_line, curr_line_length - byte_offset)
then
if str_byte(prev_line, prev_line_length - byte_offset) ~= str_byte(curr_line, curr_line_length - byte_offset) then
break
end
end
@ -281,14 +295,13 @@ end
---@param end_range table new_end_range returned by last_difference
---@returns string text extracted from defined region
local function extract_text(lines, start_range, end_range, line_ending)
if not lines[start_range.line_idx] then
return ""
end
if not lines[start_range.line_idx] then
return ''
end
-- Trivial case: start and end range are the same line, directly grab changed text
if start_range.line_idx == end_range.line_idx then
-- string.sub is inclusive, end_range is not
return string.sub(lines[start_range.line_idx], start_range.byte_idx, end_range.byte_idx - 1)
else
-- Handle deletion case
-- Collect the changed portion of the first changed line
@ -303,7 +316,7 @@ local function extract_text(lines, start_range, end_range, line_ending)
-- Collect the changed portion of the last changed line.
table.insert(result, string.sub(lines[end_range.line_idx], 1, end_range.byte_idx - 1))
else
table.insert(result, "")
table.insert(result, '')
end
-- Add line ending between all lines

File diff suppressed because it is too large Load Diff

View File

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

View File

@ -1,32 +1,32 @@
local a = vim.api
local query = require'vim.treesitter.query'
local language = require'vim.treesitter.language'
local LanguageTree = require'vim.treesitter.languagetree'
local query = require('vim.treesitter.query')
local language = require('vim.treesitter.language')
local LanguageTree = require('vim.treesitter.languagetree')
-- 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
-- it.
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.minimum_language_version = vim._ts_get_minimum_language_version()
setmetatable(M, {
__index = function (t, k)
if k == "highlighter" then
t[k] = require'vim.treesitter.highlighter'
return t[k]
elseif k == "language" then
t[k] = require"vim.treesitter.language"
return t[k]
elseif k == "query" then
t[k] = require"vim.treesitter.query"
return t[k]
end
end
})
__index = function(t, k)
if k == 'highlighter' then
t[k] = require('vim.treesitter.highlighter')
return t[k]
elseif k == 'language' then
t[k] = require('vim.treesitter.language')
return t[k]
elseif k == 'query' then
t[k] = require('vim.treesitter.query')
return t[k]
end
end,
})
--- Creates a new parser.
---
@ -63,7 +63,11 @@ function M._create_parser(bufnr, lang, opts)
self:_on_reload(...)
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()
@ -87,7 +91,7 @@ function M.get_parser(bufnr, lang, opts)
bufnr = a.nvim_get_current_buf()
end
if lang == nil then
lang = a.nvim_buf_get_option(bufnr, "filetype")
lang = a.nvim_buf_get_option(bufnr, 'filetype')
end
if parsers[bufnr] == nil or parsers[bufnr]:lang() ~= lang then
@ -105,10 +109,10 @@ end
---@param lang The language of this string
---@param opts Options to pass to the created language tree
function M.get_string_parser(str, lang, opts)
vim.validate {
vim.validate({
str = { str, 'string' },
lang = { lang, 'string' }
}
lang = { lang, 'string' },
})
language.require_language(lang)
return LanguageTree.new(str, lang, opts)

View File

@ -15,24 +15,22 @@ function M.check()
local report_error = vim.fn['health#report_error']
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
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)
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
local lang = ts.language.inspect_language(parsername)
report_ok(string.format("Loaded parser for %s: ABI version %d",
parsername, lang._abi_version))
report_ok(string.format('Loaded parser for %s: ABI version %d', parsername, lang._abi_version))
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
return M

View File

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

View File

@ -22,13 +22,15 @@ function M.require_language(lang, path, silent)
end
-- 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
path = paths[1]
end
if silent then
return pcall(function() vim._ts_add_language(path, lang) end)
return pcall(function()
vim._ts_add_language(path, lang)
end)
else
vim._ts_add_language(path, lang)
end

View File

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

View File

@ -1,5 +1,5 @@
local a = vim.api
local language = require'vim.treesitter.language'
local language = require('vim.treesitter.language')
-- query: pattern matching on trees
-- 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 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 = {}
@ -52,7 +54,7 @@ function M.get_query_files(lang, query_name, is_included)
-- ;+ inherits: ({language},)*{language}
--
-- {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
local modeline = safe_read(file, '*l')
@ -62,7 +64,7 @@ function M.get_query_files(lang, query_name, is_included)
if langlist then
for _, incllang in ipairs(vim.split(langlist, ',', true)) do
local is_optional = incllang:match("%(.*%)")
local is_optional = incllang:match('%(.*%)')
if is_optional then
if not is_included then
@ -90,7 +92,7 @@ end
local function read_query_files(filenames)
local contents = {}
for _,filename in ipairs(filenames) do
for _, filename in ipairs(filenames) do
table.insert(contents, safe_read(filename, '*a'))
end
@ -142,7 +144,7 @@ local query_cache = setmetatable({}, {
__index = function(tbl, key)
rawset(tbl, key, {})
return rawget(tbl, key)
end
end,
})
--- 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 end_row, end_col, end_byte = node:end_()
if type(source) == "number" then
if type(source) == 'number' then
local lines
local eof_row = a.nvim_buf_line_count(source)
if start_row >= eof_row then
@ -201,56 +203,56 @@ function M.get_node_text(node, source)
if #lines > 0 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
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)
end
end
return table.concat(lines, "\n")
elseif type(source) == "string" then
return source:sub(start_byte+1, end_byte)
return table.concat(lines, '\n')
elseif type(source) == 'string' then
return source:sub(start_byte + 1, end_byte)
end
end
-- Predicate handler receive the following arguments
-- (match, pattern, bufnr, predicate)
local predicate_handlers = {
["eq?"] = function(match, _, source, predicate)
local node = match[predicate[2]]
local node_text = M.get_node_text(node, source)
['eq?'] = function(match, _, source, predicate)
local node = match[predicate[2]]
local node_text = M.get_node_text(node, source)
local str
if type(predicate[3]) == "string" then
-- (#eq? @aa "foo")
str = predicate[3]
else
-- (#eq? @aa @bb)
str = M.get_node_text(match[predicate[3]], source)
end
local str
if type(predicate[3]) == 'string' then
-- (#eq? @aa "foo")
str = predicate[3]
else
-- (#eq? @aa @bb)
str = M.get_node_text(match[predicate[3]], source)
end
if node_text ~= str or str == nil then
return false
end
if node_text ~= str or str == nil then
return false
end
return true
return true
end,
["lua-match?"] = function(match, _, source, predicate)
local node = match[predicate[2]]
local regex = predicate[3]
return string.find(M.get_node_text(node, source), regex)
['lua-match?'] = function(match, _, source, predicate)
local node = match[predicate[2]]
local regex = predicate[3]
return string.find(M.get_node_text(node, source), regex)
end,
["match?"] = (function()
local magic_prefixes = {['\\v']=true, ['\\m']=true, ['\\M']=true, ['\\V']=true}
['match?'] = (function()
local magic_prefixes = { ['\\v'] = true, ['\\m'] = true, ['\\M'] = true, ['\\V'] = true }
---@private
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
end
return '\\v'..str
return '\\v' .. str
end
local compiled_vim_regexes = setmetatable({}, {
@ -258,7 +260,7 @@ local predicate_handlers = {
local res = vim.regex(check_magic(pattern))
rawset(t, pattern, res)
return res
end
end,
})
return function(match, _, source, pred)
@ -268,11 +270,11 @@ local predicate_handlers = {
end
end)(),
["contains?"] = function(match, _, source, predicate)
['contains?'] = function(match, _, source, predicate)
local node = match[predicate[2]]
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
return true
end
@ -281,19 +283,19 @@ local predicate_handlers = {
return false
end,
["any-of?"] = function(match, _, source, predicate)
['any-of?'] = function(match, _, source, predicate)
local node = match[predicate[2]]
local node_text = M.get_node_text(node, source)
-- 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.
local string_set = predicate["string_set"]
local string_set = predicate['string_set']
if not string_set then
string_set = {}
for i=3,#predicate do
for i = 3, #predicate do
string_set[predicate[i]] = true
end
predicate["string_set"] = string_set
predicate['string_set'] = string_set
end
return string_set[node_text]
@ -301,15 +303,14 @@ local predicate_handlers = {
}
-- 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 should always end with a `!`.
-- Directive handler receive the following arguments
-- (match, pattern, bufnr, predicate, metadata)
local directive_handlers = {
["set!"] = function(_, _, _, pred, metadata)
['set!'] = function(_, _, _, pred, metadata)
if #pred == 4 then
-- (#set! @capture "key" "value")
local capture = pred[2]
@ -324,9 +325,9 @@ local directive_handlers = {
end,
-- Shifts the range of a node.
-- Example: (#offset! @_node 0 1 0 -1)
["offset!"] = function(match, _, _, pred, metadata)
['offset!'] = function(match, _, _, pred, metadata)
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_col_offset = pred[4] 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 range[1] < range[3] or (range[1] == range[3] and range[2] <= range[4]) then
metadata.content = {range}
metadata.content = { range }
end
end
end,
}
--- Adds a new predicate to be used in queries
@ -351,7 +352,7 @@ local directive_handlers = {
--- signature will be (match, pattern, bufnr, predicate)
function M.add_predicate(name, handler, force)
if predicate_handlers[name] and not force then
error(string.format("Overriding %s", name))
error(string.format('Overriding %s', name))
end
predicate_handlers[name] = handler
@ -364,7 +365,7 @@ end
--- signature will be (match, pattern, bufnr, predicate)
function M.add_directive(name, handler, force)
if directive_handlers[name] and not force then
error(string.format("Overriding %s", name))
error(string.format('Overriding %s', name))
end
directive_handlers[name] = handler
@ -387,7 +388,7 @@ end
---@private
local function is_directive(name)
return string.sub(name, -1) == "!"
return string.sub(name, -1) == '!'
end
---@private
@ -404,7 +405,7 @@ function Query:match_preds(match, pattern, source)
-- Skip over directives... they will get processed after all the predicates.
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)
is_not = true
else
@ -415,7 +416,7 @@ function Query:match_preds(match, pattern, source)
local handler = predicate_handlers[pred_name]
if not handler then
error(string.format("No handler for %s", pred[1]))
error(string.format('No handler for %s', pred[1]))
return false
end
@ -438,7 +439,7 @@ function Query:apply_directives(match, pattern, source, metadata)
local handler = directive_handlers[pred[1]]
if not handler then
error(string.format("No handler for %s", pred[1]))
error(string.format('No handler for %s', pred[1]))
return
end
@ -447,7 +448,6 @@ function Query:apply_directives(match, pattern, source, metadata)
end
end
--- 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
-- to make the search inclusive.
@ -492,7 +492,7 @@ end
---@returns The matching capture id
---@returns The captured node
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()
end
@ -549,7 +549,7 @@ end
---@returns The matching pattern id
---@returns The matching match
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()
end

View File

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

View File

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

View File

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