mirror of
https://github.com/neovim/neovim.git
synced 2025-02-25 18:55:25 -06:00
Merge pull request #18487 from clason/stylua
CI: format and lint runtime with Stylua
This commit is contained in:
commit
cb7ab98925
7
.github/workflows/ci.yml
vendored
7
.github/workflows/ci.yml
vendored
@ -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
6
.stylua.toml
Normal 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
3
.styluaignore
Normal file
@ -0,0 +1,3 @@
|
||||
/scripts
|
||||
/src
|
||||
/test
|
11
Makefile
11
Makefile
@ -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
|
||||
|
@ -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
|
||||
|
@ -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 ]])
|
||||
|
@ -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 ]])
|
||||
|
@ -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 }
|
||||
|
@ -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
|
||||
|
@ -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')
|
||||
|
@ -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
|
||||
|
@ -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)
|
||||
|
@ -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
@ -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
|
||||
|
@ -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()
|
||||
|
@ -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
|
||||
|
@ -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
@ -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)
|
||||
|
@ -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
|
||||
|
@ -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
|
||||
|
@ -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)
|
||||
|
@ -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
|
||||
|
@ -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
|
||||
|
||||
|
@ -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
|
||||
|
||||
|
@ -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
|
||||
|
@ -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
|
||||
|
@ -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
@ -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
|
||||
|
||||
|
@ -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)
|
||||
|
@ -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
|
||||
|
||||
|
@ -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
|
||||
|
@ -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
|
||||
|
@ -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)
|
||||
|
@ -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
|
||||
|
||||
|
@ -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)
|
||||
|
@ -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)
|
||||
|
@ -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 ]])
|
||||
|
Loading…
Reference in New Issue
Block a user