mirror of
https://github.com/neovim/neovim.git
synced 2025-09-08 12:28:18 +00:00
Merge pull request #18487 from clason/stylua
CI: format and lint runtime with Stylua
This commit is contained in:
7
.github/workflows/ci.yml
vendored
7
.github/workflows/ci.yml
vendored
@@ -86,6 +86,13 @@ jobs:
|
|||||||
name: clint-full
|
name: clint-full
|
||||||
run: ./ci/run_lint.sh 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()"
|
- if: "!cancelled()"
|
||||||
name: lualint
|
name: lualint
|
||||||
run: ./ci/run_lint.sh 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
|
functionaltest-lua: | nvim
|
||||||
+$(BUILD_TOOL) -C build functionaltest-lua
|
+$(BUILD_TOOL) -C build functionaltest-lua
|
||||||
|
|
||||||
|
stylua:
|
||||||
|
stylua --check runtime/
|
||||||
|
|
||||||
lualint: | build/.ran-cmake deps
|
lualint: | build/.ran-cmake deps
|
||||||
$(BUILD_TOOL) -C build lualint
|
$(BUILD_TOOL) -C build lualint
|
||||||
|
|
||||||
|
_opt_stylua:
|
||||||
|
@command -v stylua && { $(MAKE) stylua; exit $$?; } \
|
||||||
|
|| echo "SKIP: stylua (stylua not found)"
|
||||||
|
|
||||||
shlint:
|
shlint:
|
||||||
@shellcheck --version | head -n 2
|
@shellcheck --version | head -n 2
|
||||||
shellcheck scripts/vim-patch.sh
|
shellcheck scripts/vim-patch.sh
|
||||||
@@ -214,7 +221,7 @@ appimage:
|
|||||||
appimage-%:
|
appimage-%:
|
||||||
bash scripts/genappimage.sh $*
|
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.
|
# Generic pattern rules, allowing for `make build/bin/nvim` etc.
|
||||||
# Does not work with "Unix Makefiles".
|
# 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)/%,%,$@)
|
$(BUILD_TOOL) -C $(DEPS_BUILD_DIR) $(patsubst $(DEPS_BUILD_DIR)/%,%,$@)
|
||||||
endif
|
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
|
return
|
||||||
end
|
end
|
||||||
|
|
||||||
vim.api.nvim_create_augroup("filetypedetect", {clear = false})
|
vim.api.nvim_create_augroup('filetypedetect', { clear = false })
|
||||||
|
|
||||||
vim.api.nvim_create_autocmd({"BufRead", "BufNewFile"}, {
|
vim.api.nvim_create_autocmd({ 'BufRead', 'BufNewFile' }, {
|
||||||
group = "filetypedetect",
|
group = 'filetypedetect',
|
||||||
callback = function()
|
callback = function()
|
||||||
vim.filetype.match(vim.fn.expand("<afile>"))
|
vim.filetype.match(vim.fn.expand('<afile>'))
|
||||||
end,
|
end,
|
||||||
})
|
})
|
||||||
|
|
||||||
-- These *must* be sourced after the autocommand above is created
|
-- These *must* be sourced after the autocommand above is created
|
||||||
if not vim.g.did_load_ftdetect then
|
if not vim.g.did_load_ftdetect then
|
||||||
vim.cmd [[
|
vim.cmd([[
|
||||||
augroup filetypedetect
|
augroup filetypedetect
|
||||||
runtime! ftdetect/*.vim
|
runtime! ftdetect/*.vim
|
||||||
runtime! ftdetect/*.lua
|
runtime! ftdetect/*.lua
|
||||||
augroup END
|
augroup END
|
||||||
]]
|
]])
|
||||||
end
|
end
|
||||||
|
|
||||||
-- Set a marker so that the ftdetect scripts are not sourced a second time by filetype.vim
|
-- Set a marker so that the ftdetect scripts are not sourced a second time by filetype.vim
|
||||||
@@ -31,17 +31,17 @@ vim.g.did_load_ftdetect = 1
|
|||||||
|
|
||||||
-- If filetype.vim is disabled, set up the autocmd to use scripts.vim
|
-- If filetype.vim is disabled, set up the autocmd to use scripts.vim
|
||||||
if vim.g.did_load_filetypes then
|
if vim.g.did_load_filetypes then
|
||||||
vim.api.nvim_create_autocmd({"BufRead", "BufNewFile"}, {
|
vim.api.nvim_create_autocmd({ 'BufRead', 'BufNewFile' }, {
|
||||||
group = "filetypedetect",
|
group = 'filetypedetect',
|
||||||
command = "if !did_filetype() && expand('<amatch>') !~ g:ft_ignore_pat | runtime! scripts.vim | endif",
|
command = "if !did_filetype() && expand('<amatch>') !~ g:ft_ignore_pat | runtime! scripts.vim | endif",
|
||||||
})
|
})
|
||||||
|
|
||||||
vim.api.nvim_create_autocmd("StdinReadPost", {
|
vim.api.nvim_create_autocmd('StdinReadPost', {
|
||||||
group = "filetypedetect",
|
group = 'filetypedetect',
|
||||||
command = "if !did_filetype() | runtime! scripts.vim | endif",
|
command = 'if !did_filetype() | runtime! scripts.vim | endif',
|
||||||
})
|
})
|
||||||
end
|
end
|
||||||
|
|
||||||
if not vim.g.ft_ignore_pat then
|
if not vim.g.ft_ignore_pat then
|
||||||
vim.g.ft_ignore_pat = "\\.\\(Z\\|gz\\|bz2\\|zip\\|tgz\\)$"
|
vim.g.ft_ignore_pat = '\\.\\(Z\\|gz\\|bz2\\|zip\\|tgz\\)$'
|
||||||
end
|
end
|
||||||
|
@@ -3,4 +3,4 @@
|
|||||||
-- Last Change: 2022 Mar 29
|
-- Last Change: 2022 Mar 29
|
||||||
|
|
||||||
-- it's a lisp!
|
-- it's a lisp!
|
||||||
vim.cmd [[ runtime! ftplugin/lisp.vim ]]
|
vim.cmd([[ runtime! ftplugin/lisp.vim ]])
|
||||||
|
@@ -3,4 +3,4 @@
|
|||||||
-- Last Change: 2022 Mar 29
|
-- Last Change: 2022 Mar 29
|
||||||
|
|
||||||
-- it's a lisp!
|
-- it's a lisp!
|
||||||
vim.cmd [[ runtime! indent/lisp.vim ]]
|
vim.cmd([[ runtime! indent/lisp.vim ]])
|
||||||
|
@@ -8,7 +8,7 @@ local function highlight_line(line, linenr)
|
|||||||
local overstrike, escape = false, false
|
local overstrike, escape = false, false
|
||||||
local hls = {} -- Store highlight groups as { attr, start, final }
|
local hls = {} -- Store highlight groups as { attr, start, final }
|
||||||
local NONE, BOLD, UNDERLINE, ITALIC = 0, 1, 2, 3
|
local NONE, BOLD, UNDERLINE, ITALIC = 0, 1, 2, 3
|
||||||
local hl_groups = {[BOLD]="manBold", [UNDERLINE]="manUnderline", [ITALIC]="manItalic"}
|
local hl_groups = { [BOLD] = 'manBold', [UNDERLINE] = 'manUnderline', [ITALIC] = 'manItalic' }
|
||||||
local attr = NONE
|
local attr = NONE
|
||||||
local byte = 0 -- byte offset
|
local byte = 0 -- byte offset
|
||||||
|
|
||||||
@@ -63,7 +63,7 @@ local function highlight_line(line, linenr)
|
|||||||
-- can be represented in one byte. Any code point above that is represented by
|
-- can be represented in one byte. Any code point above that is represented by
|
||||||
-- a leading byte (0xc0 and above) and continuation bytes (0x80 to 0xbf, or
|
-- a leading byte (0xc0 and above) and continuation bytes (0x80 to 0xbf, or
|
||||||
-- decimal 128 to 191).
|
-- decimal 128 to 191).
|
||||||
for char in line:gmatch("[^\128-\191][\128-\191]*") do
|
for char in line:gmatch('[^\128-\191][\128-\191]*') do
|
||||||
if overstrike then
|
if overstrike then
|
||||||
local last_hl = hls[#hls]
|
local last_hl = hls[#hls]
|
||||||
if char == prev_char then
|
if char == prev_char then
|
||||||
@@ -106,25 +106,25 @@ local function highlight_line(line, linenr)
|
|||||||
-- We only want to match against SGR sequences, which consist of ESC
|
-- We only want to match against SGR sequences, which consist of ESC
|
||||||
-- followed by '[', then a series of parameter and intermediate bytes in
|
-- followed by '[', then a series of parameter and intermediate bytes in
|
||||||
-- the range 0x20 - 0x3f, then 'm'. (See ECMA-48, sections 5.4 & 8.3.117)
|
-- the range 0x20 - 0x3f, then 'm'. (See ECMA-48, sections 5.4 & 8.3.117)
|
||||||
local sgr = prev_char:match("^%[([\032-\063]*)m$")
|
local sgr = prev_char:match('^%[([\032-\063]*)m$')
|
||||||
-- Ignore escape sequences with : characters, as specified by ITU's T.416
|
-- Ignore escape sequences with : characters, as specified by ITU's T.416
|
||||||
-- Open Document Architecture and interchange format.
|
-- Open Document Architecture and interchange format.
|
||||||
if sgr and not string.find(sgr, ":") then
|
if sgr and not string.find(sgr, ':') then
|
||||||
local match
|
local match
|
||||||
while sgr and #sgr > 0 do
|
while sgr and #sgr > 0 do
|
||||||
-- Match against SGR parameters, which may be separated by ';'
|
-- Match against SGR parameters, which may be separated by ';'
|
||||||
match, sgr = sgr:match("^(%d*);?(.*)")
|
match, sgr = sgr:match('^(%d*);?(.*)')
|
||||||
add_attr_hl(match + 0) -- coerce to number
|
add_attr_hl(match + 0) -- coerce to number
|
||||||
end
|
end
|
||||||
escape = false
|
escape = false
|
||||||
elseif not prev_char:match("^%[[\032-\063]*$") then
|
elseif not prev_char:match('^%[[\032-\063]*$') then
|
||||||
-- Stop looking if this isn't a partial CSI sequence
|
-- Stop looking if this isn't a partial CSI sequence
|
||||||
escape = false
|
escape = false
|
||||||
end
|
end
|
||||||
elseif char == "\027" then
|
elseif char == '\027' then
|
||||||
escape = true
|
escape = true
|
||||||
prev_char = ''
|
prev_char = ''
|
||||||
elseif char == "\b" then
|
elseif char == '\b' then
|
||||||
overstrike = true
|
overstrike = true
|
||||||
prev_char = chars[#chars]
|
prev_char = chars[#chars]
|
||||||
byte = byte - #prev_char
|
byte = byte - #prev_char
|
||||||
@@ -143,7 +143,7 @@ local function highlight_line(line, linenr)
|
|||||||
hl_groups[hl.attr],
|
hl_groups[hl.attr],
|
||||||
linenr - 1,
|
linenr - 1,
|
||||||
hl.start,
|
hl.start,
|
||||||
hl.final
|
hl.final,
|
||||||
}
|
}
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
@@ -152,8 +152,8 @@ local function highlight_line(line, linenr)
|
|||||||
end
|
end
|
||||||
|
|
||||||
local function highlight_man_page()
|
local function highlight_man_page()
|
||||||
local mod = vim.api.nvim_buf_get_option(0, "modifiable")
|
local mod = vim.api.nvim_buf_get_option(0, 'modifiable')
|
||||||
vim.api.nvim_buf_set_option(0, "modifiable", true)
|
vim.api.nvim_buf_set_option(0, 'modifiable', true)
|
||||||
|
|
||||||
local lines = vim.api.nvim_buf_get_lines(0, 0, -1, false)
|
local lines = vim.api.nvim_buf_get_lines(0, 0, -1, false)
|
||||||
for i, line in ipairs(lines) do
|
for i, line in ipairs(lines) do
|
||||||
@@ -166,7 +166,7 @@ local function highlight_man_page()
|
|||||||
end
|
end
|
||||||
buf_hls = {}
|
buf_hls = {}
|
||||||
|
|
||||||
vim.api.nvim_buf_set_option(0, "modifiable", mod)
|
vim.api.nvim_buf_set_option(0, 'modifiable', mod)
|
||||||
end
|
end
|
||||||
|
|
||||||
return { highlight_man_page = highlight_man_page }
|
return { highlight_man_page = highlight_man_page }
|
||||||
|
@@ -5,13 +5,17 @@ local F = {}
|
|||||||
---@param a
|
---@param a
|
||||||
---@param b
|
---@param b
|
||||||
function F.if_nil(a, b)
|
function F.if_nil(a, b)
|
||||||
if a == nil then return b end
|
if a == nil then
|
||||||
|
return b
|
||||||
|
end
|
||||||
return a
|
return a
|
||||||
end
|
end
|
||||||
|
|
||||||
-- Use in combination with pcall
|
-- Use in combination with pcall
|
||||||
function F.ok_or_nil(status, ...)
|
function F.ok_or_nil(status, ...)
|
||||||
if not status then return end
|
if not status then
|
||||||
|
return
|
||||||
|
end
|
||||||
return ...
|
return ...
|
||||||
end
|
end
|
||||||
|
|
||||||
|
@@ -40,26 +40,28 @@ local vim = assert(vim)
|
|||||||
|
|
||||||
-- These are for loading runtime modules lazily since they aren't available in
|
-- These are for loading runtime modules lazily since they aren't available in
|
||||||
-- the nvim binary as specified in executor.c
|
-- the nvim binary as specified in executor.c
|
||||||
for k,v in pairs {
|
for k, v in pairs({
|
||||||
treesitter=true;
|
treesitter = true,
|
||||||
filetype = true;
|
filetype = true,
|
||||||
F=true;
|
F = true,
|
||||||
lsp=true;
|
lsp = true,
|
||||||
highlight=true;
|
highlight = true,
|
||||||
diagnostic=true;
|
diagnostic = true,
|
||||||
keymap=true;
|
keymap = true,
|
||||||
ui=true;
|
ui = true,
|
||||||
} do vim._submodules[k] = v end
|
}) do
|
||||||
|
vim._submodules[k] = v
|
||||||
|
end
|
||||||
|
|
||||||
vim.log = {
|
vim.log = {
|
||||||
levels = {
|
levels = {
|
||||||
TRACE = 0;
|
TRACE = 0,
|
||||||
DEBUG = 1;
|
DEBUG = 1,
|
||||||
INFO = 2;
|
INFO = 2,
|
||||||
WARN = 3;
|
WARN = 3,
|
||||||
ERROR = 4;
|
ERROR = 4,
|
||||||
OFF = 5;
|
OFF = 5,
|
||||||
}
|
},
|
||||||
}
|
}
|
||||||
|
|
||||||
-- Internal-only until comments in #8107 are addressed.
|
-- Internal-only until comments in #8107 are addressed.
|
||||||
@@ -77,14 +79,14 @@ function vim._os_proc_info(pid)
|
|||||||
if pid == nil or pid <= 0 or type(pid) ~= 'number' then
|
if pid == nil or pid <= 0 or type(pid) ~= 'number' then
|
||||||
error('invalid pid')
|
error('invalid pid')
|
||||||
end
|
end
|
||||||
local cmd = { 'ps', '-p', pid, '-o', 'comm=', }
|
local cmd = { 'ps', '-p', pid, '-o', 'comm=' }
|
||||||
local err, name = vim._system(cmd)
|
local err, name = vim._system(cmd)
|
||||||
if 1 == err and vim.trim(name) == '' then
|
if 1 == err and vim.trim(name) == '' then
|
||||||
return {} -- Process not found.
|
return {} -- Process not found.
|
||||||
elseif 0 ~= err then
|
elseif 0 ~= err then
|
||||||
error('command failed: ' .. vim.fn.string(cmd))
|
error('command failed: ' .. vim.fn.string(cmd))
|
||||||
end
|
end
|
||||||
local _, ppid = vim._system({ 'ps', '-p', pid, '-o', 'ppid=', })
|
local _, ppid = vim._system({ 'ps', '-p', pid, '-o', 'ppid=' })
|
||||||
-- Remove trailing whitespace.
|
-- Remove trailing whitespace.
|
||||||
name = vim.trim(name):gsub('^.*/', '')
|
name = vim.trim(name):gsub('^.*/', '')
|
||||||
ppid = tonumber(ppid) or -1
|
ppid = tonumber(ppid) or -1
|
||||||
@@ -101,7 +103,7 @@ function vim._os_proc_children(ppid)
|
|||||||
if ppid == nil or ppid <= 0 or type(ppid) ~= 'number' then
|
if ppid == nil or ppid <= 0 or type(ppid) ~= 'number' then
|
||||||
error('invalid ppid')
|
error('invalid ppid')
|
||||||
end
|
end
|
||||||
local cmd = { 'pgrep', '-P', ppid, }
|
local cmd = { 'pgrep', '-P', ppid }
|
||||||
local err, rv = vim._system(cmd)
|
local err, rv = vim._system(cmd)
|
||||||
if 1 == err and vim.trim(rv) == '' then
|
if 1 == err and vim.trim(rv) == '' then
|
||||||
return {} -- Process not found.
|
return {} -- Process not found.
|
||||||
@@ -252,11 +254,13 @@ end
|
|||||||
---@see |vim.schedule()|
|
---@see |vim.schedule()|
|
||||||
---@see |vim.in_fast_event()|
|
---@see |vim.in_fast_event()|
|
||||||
function vim.schedule_wrap(cb)
|
function vim.schedule_wrap(cb)
|
||||||
return (function (...)
|
return function(...)
|
||||||
local args = vim.F.pack_len(...)
|
local args = vim.F.pack_len(...)
|
||||||
vim.schedule(function() cb(vim.F.unpack_len(args)) end)
|
vim.schedule(function()
|
||||||
|
cb(vim.F.unpack_len(args))
|
||||||
end)
|
end)
|
||||||
end
|
end
|
||||||
|
end
|
||||||
|
|
||||||
-- vim.fn.{func}(...)
|
-- vim.fn.{func}(...)
|
||||||
vim.fn = setmetatable({}, {
|
vim.fn = setmetatable({}, {
|
||||||
@@ -264,7 +268,7 @@ vim.fn = setmetatable({}, {
|
|||||||
local _fn
|
local _fn
|
||||||
if vim.api[key] ~= nil then
|
if vim.api[key] ~= nil then
|
||||||
_fn = function()
|
_fn = function()
|
||||||
error(string.format("Tried to call API function with vim.fn: use vim.api.%s instead", key))
|
error(string.format('Tried to call API function with vim.fn: use vim.api.%s instead', key))
|
||||||
end
|
end
|
||||||
else
|
else
|
||||||
_fn = function(...)
|
_fn = function(...)
|
||||||
@@ -273,7 +277,7 @@ vim.fn = setmetatable({}, {
|
|||||||
end
|
end
|
||||||
t[key] = _fn
|
t[key] = _fn
|
||||||
return _fn
|
return _fn
|
||||||
end
|
end,
|
||||||
})
|
})
|
||||||
|
|
||||||
vim.funcref = function(viml_func_name)
|
vim.funcref = function(viml_func_name)
|
||||||
@@ -291,9 +295,9 @@ do
|
|||||||
|
|
||||||
--@private
|
--@private
|
||||||
local function make_dict_accessor(scope, handle)
|
local function make_dict_accessor(scope, handle)
|
||||||
validate {
|
validate({
|
||||||
scope = {scope, 's'};
|
scope = { scope, 's' },
|
||||||
}
|
})
|
||||||
local mt = {}
|
local mt = {}
|
||||||
function mt:__newindex(k, v)
|
function mt:__newindex(k, v)
|
||||||
return vim._setvar(scope, handle or 0, k, v)
|
return vim._setvar(scope, handle or 0, k, v)
|
||||||
@@ -355,7 +359,7 @@ function vim.region(bufnr, pos1, pos2, regtype, inclusive)
|
|||||||
c2 = vim.str_byteindex(bufline, c2)
|
c2 = vim.str_byteindex(bufline, c2)
|
||||||
end
|
end
|
||||||
else
|
else
|
||||||
c1 = (l == pos1[1]) and (pos1[2]) or 0
|
c1 = (l == pos1[1]) and pos1[2] or 0
|
||||||
c2 = (l == pos2[1]) and (pos2[2] + (inclusive and 1 or 0)) or -1
|
c2 = (l == pos2[1]) and (pos2[2] + (inclusive and 1 or 0)) or -1
|
||||||
end
|
end
|
||||||
table.insert(region, l, { c1, c2 })
|
table.insert(region, l, { c1, c2 })
|
||||||
@@ -372,19 +376,22 @@ end
|
|||||||
---@param timeout Number of milliseconds to wait before calling `fn`
|
---@param timeout Number of milliseconds to wait before calling `fn`
|
||||||
---@return timer luv timer object
|
---@return timer luv timer object
|
||||||
function vim.defer_fn(fn, timeout)
|
function vim.defer_fn(fn, timeout)
|
||||||
vim.validate { fn = { fn, 'c', true}; }
|
vim.validate({ fn = { fn, 'c', true } })
|
||||||
local timer = vim.loop.new_timer()
|
local timer = vim.loop.new_timer()
|
||||||
timer:start(timeout, 0, vim.schedule_wrap(function()
|
timer:start(
|
||||||
|
timeout,
|
||||||
|
0,
|
||||||
|
vim.schedule_wrap(function()
|
||||||
timer:stop()
|
timer:stop()
|
||||||
timer:close()
|
timer:close()
|
||||||
|
|
||||||
fn()
|
fn()
|
||||||
end))
|
end)
|
||||||
|
)
|
||||||
|
|
||||||
return timer
|
return timer
|
||||||
end
|
end
|
||||||
|
|
||||||
|
|
||||||
--- Display a notification to the user.
|
--- Display a notification to the user.
|
||||||
---
|
---
|
||||||
--- This function can be overridden by plugins to display notifications using a
|
--- This function can be overridden by plugins to display notifications using a
|
||||||
@@ -453,10 +460,10 @@ function vim.on_key(fn, ns_id)
|
|||||||
return #on_key_cbs
|
return #on_key_cbs
|
||||||
end
|
end
|
||||||
|
|
||||||
vim.validate {
|
vim.validate({
|
||||||
fn = { fn, 'c', true },
|
fn = { fn, 'c', true },
|
||||||
ns_id = { ns_id, 'n', true }
|
ns_id = { ns_id, 'n', true },
|
||||||
}
|
})
|
||||||
|
|
||||||
if ns_id == nil or ns_id == 0 then
|
if ns_id == nil or ns_id == 0 then
|
||||||
ns_id = vim.api.nvim_create_namespace('')
|
ns_id = vim.api.nvim_create_namespace('')
|
||||||
@@ -481,10 +488,13 @@ function vim._on_key(char)
|
|||||||
end
|
end
|
||||||
|
|
||||||
if failed_ns_ids[1] then
|
if failed_ns_ids[1] then
|
||||||
error(string.format(
|
error(
|
||||||
|
string.format(
|
||||||
"Error executing 'on_key' with ns_ids '%s'\n Messages: %s",
|
"Error executing 'on_key' with ns_ids '%s'\n Messages: %s",
|
||||||
table.concat(failed_ns_ids, ", "),
|
table.concat(failed_ns_ids, ', '),
|
||||||
table.concat(failed_messages, "\n")))
|
table.concat(failed_messages, '\n')
|
||||||
|
)
|
||||||
|
)
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
@@ -511,8 +521,10 @@ function vim._expand_pat(pat, env)
|
|||||||
-- Probably just need to do a smarter match than just `:match`
|
-- Probably just need to do a smarter match than just `:match`
|
||||||
|
|
||||||
-- Get the last part of the pattern
|
-- Get the last part of the pattern
|
||||||
local last_part = pat:match("[%w.:_%[%]'\"]+$")
|
local last_part = pat:match('[%w.:_%[%]\'"]+$')
|
||||||
if not last_part then return {}, 0 end
|
if not last_part then
|
||||||
|
return {}, 0
|
||||||
|
end
|
||||||
|
|
||||||
local parts, search_index = vim._expand_pat_get_parts(last_part)
|
local parts, search_index = vim._expand_pat_get_parts(last_part)
|
||||||
|
|
||||||
@@ -529,7 +541,7 @@ function vim._expand_pat(pat, env)
|
|||||||
|
|
||||||
-- Normally, we just have a string
|
-- Normally, we just have a string
|
||||||
-- Just attempt to get the string directly from the environment
|
-- Just attempt to get the string directly from the environment
|
||||||
if type(part) == "string" then
|
if type(part) == 'string' then
|
||||||
key = part
|
key = part
|
||||||
else
|
else
|
||||||
-- However, sometimes you want to use a variable, and complete on it
|
-- However, sometimes you want to use a variable, and complete on it
|
||||||
@@ -554,7 +566,7 @@ function vim._expand_pat(pat, env)
|
|||||||
local field = rawget(final_env, key)
|
local field = rawget(final_env, key)
|
||||||
if field == nil then
|
if field == nil then
|
||||||
local mt = getmetatable(final_env)
|
local mt = getmetatable(final_env)
|
||||||
if mt and type(mt.__index) == "table" then
|
if mt and type(mt.__index) == 'table' then
|
||||||
field = rawget(mt.__index, key)
|
field = rawget(mt.__index, key)
|
||||||
elseif final_env == vim and vim._submodules[key] then
|
elseif final_env == vim and vim._submodules[key] then
|
||||||
field = vim[key]
|
field = vim[key]
|
||||||
@@ -571,17 +583,17 @@ function vim._expand_pat(pat, env)
|
|||||||
---@private
|
---@private
|
||||||
local function insert_keys(obj)
|
local function insert_keys(obj)
|
||||||
for k, _ in pairs(obj) do
|
for k, _ in pairs(obj) do
|
||||||
if type(k) == "string" and string.sub(k,1,string.len(match_part)) == match_part then
|
if type(k) == 'string' and string.sub(k, 1, string.len(match_part)) == match_part then
|
||||||
table.insert(keys, k)
|
table.insert(keys, k)
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
if type(final_env) == "table" then
|
if type(final_env) == 'table' then
|
||||||
insert_keys(final_env)
|
insert_keys(final_env)
|
||||||
end
|
end
|
||||||
local mt = getmetatable(final_env)
|
local mt = getmetatable(final_env)
|
||||||
if mt and type(mt.__index) == "table" then
|
if mt and type(mt.__index) == 'table' then
|
||||||
insert_keys(mt.__index)
|
insert_keys(mt.__index)
|
||||||
end
|
end
|
||||||
if final_env == vim then
|
if final_env == vim then
|
||||||
@@ -602,12 +614,12 @@ vim._expand_pat_get_parts = function(lua_string)
|
|||||||
for idx = 1, #lua_string do
|
for idx = 1, #lua_string do
|
||||||
local s = lua_string:sub(idx, idx)
|
local s = lua_string:sub(idx, idx)
|
||||||
|
|
||||||
if not in_brackets and (s == "." or s == ":") then
|
if not in_brackets and (s == '.' or s == ':') then
|
||||||
table.insert(parts, accumulator)
|
table.insert(parts, accumulator)
|
||||||
accumulator = ''
|
accumulator = ''
|
||||||
|
|
||||||
search_index = idx + 1
|
search_index = idx + 1
|
||||||
elseif s == "[" then
|
elseif s == '[' then
|
||||||
in_brackets = true
|
in_brackets = true
|
||||||
|
|
||||||
table.insert(parts, accumulator)
|
table.insert(parts, accumulator)
|
||||||
@@ -619,7 +631,7 @@ vim._expand_pat_get_parts = function(lua_string)
|
|||||||
in_brackets = false
|
in_brackets = false
|
||||||
search_index = idx + 1
|
search_index = idx + 1
|
||||||
|
|
||||||
if string_char == "VAR" then
|
if string_char == 'VAR' then
|
||||||
table.insert(parts, { accumulator })
|
table.insert(parts, { accumulator })
|
||||||
accumulator = ''
|
accumulator = ''
|
||||||
|
|
||||||
@@ -631,7 +643,7 @@ vim._expand_pat_get_parts = function(lua_string)
|
|||||||
if s == '"' or s == "'" then
|
if s == '"' or s == "'" then
|
||||||
string_char = s
|
string_char = s
|
||||||
elseif s ~= ' ' then
|
elseif s ~= ' ' then
|
||||||
string_char = "VAR"
|
string_char = 'VAR'
|
||||||
accumulator = s
|
accumulator = s
|
||||||
end
|
end
|
||||||
elseif string_char then
|
elseif string_char then
|
||||||
@@ -649,7 +661,9 @@ vim._expand_pat_get_parts = function(lua_string)
|
|||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
parts = vim.tbl_filter(function(val) return #val > 0 end, parts)
|
parts = vim.tbl_filter(function(val)
|
||||||
|
return #val > 0
|
||||||
|
end, parts)
|
||||||
|
|
||||||
return parts, search_index
|
return parts, search_index
|
||||||
end
|
end
|
||||||
@@ -677,14 +691,14 @@ function vim._cs_remote(rcid, server_addr, connect_error, args)
|
|||||||
local function connection_failure_errmsg(consequence)
|
local function connection_failure_errmsg(consequence)
|
||||||
local explanation
|
local explanation
|
||||||
if server_addr == '' then
|
if server_addr == '' then
|
||||||
explanation = "No server specified with --server"
|
explanation = 'No server specified with --server'
|
||||||
else
|
else
|
||||||
explanation = "Failed to connect to '" .. server_addr .. "'"
|
explanation = "Failed to connect to '" .. server_addr .. "'"
|
||||||
if connect_error ~= "" then
|
if connect_error ~= '' then
|
||||||
explanation = explanation .. ": " .. connect_error
|
explanation = explanation .. ': ' .. connect_error
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
return "E247: " .. explanation .. ". " .. consequence
|
return 'E247: ' .. explanation .. '. ' .. consequence
|
||||||
end
|
end
|
||||||
|
|
||||||
local f_silent = false
|
local f_silent = false
|
||||||
@@ -718,11 +732,13 @@ function vim._cs_remote(rcid, server_addr, connect_error, args)
|
|||||||
|
|
||||||
if rcid == 0 then
|
if rcid == 0 then
|
||||||
if not f_silent then
|
if not f_silent then
|
||||||
vim.notify(connection_failure_errmsg("Editing locally"), vim.log.levels.WARN)
|
vim.notify(connection_failure_errmsg('Editing locally'), vim.log.levels.WARN)
|
||||||
end
|
end
|
||||||
else
|
else
|
||||||
local command = {}
|
local command = {}
|
||||||
if f_tab then table.insert(command, 'tab') end
|
if f_tab then
|
||||||
|
table.insert(command, 'tab')
|
||||||
|
end
|
||||||
table.insert(command, 'drop')
|
table.insert(command, 'drop')
|
||||||
for i = 2, #args do
|
for i = 2, #args do
|
||||||
table.insert(command, vim.fn.fnameescape(args[i]))
|
table.insert(command, vim.fn.fnameescape(args[i]))
|
||||||
@@ -746,7 +762,7 @@ end
|
|||||||
--- from. Defaults to "Nvim".
|
--- from. Defaults to "Nvim".
|
||||||
function vim.deprecate(name, alternative, version, plugin)
|
function vim.deprecate(name, alternative, version, plugin)
|
||||||
local message = name .. ' is deprecated'
|
local message = name .. ' is deprecated'
|
||||||
plugin = plugin or "Nvim"
|
plugin = plugin or 'Nvim'
|
||||||
message = alternative and (message .. ', use ' .. alternative .. ' instead.') or message
|
message = alternative and (message .. ', use ' .. alternative .. ' instead.') or message
|
||||||
message = message .. ' See :h deprecated\nThis function will be removed in ' .. plugin .. ' version ' .. version
|
message = message .. ' See :h deprecated\nThis function will be removed in ' .. plugin .. ' version ' .. version
|
||||||
vim.notify_once(message, vim.log.levels.WARN)
|
vim.notify_once(message, vim.log.levels.WARN)
|
||||||
|
@@ -17,7 +17,7 @@ end
|
|||||||
|
|
||||||
function vim._load_package(name)
|
function vim._load_package(name)
|
||||||
local basename = name:gsub('%.', '/')
|
local basename = name:gsub('%.', '/')
|
||||||
local paths = {"lua/"..basename..".lua", "lua/"..basename.."/init.lua"}
|
local paths = { 'lua/' .. basename .. '.lua', 'lua/' .. basename .. '/init.lua' }
|
||||||
local found = vim.api.nvim__get_runtime(paths, false, { is_lua = true })
|
local found = vim.api.nvim__get_runtime(paths, false, { is_lua = true })
|
||||||
if #found > 0 then
|
if #found > 0 then
|
||||||
local f, err = loadfile(found[1])
|
local f, err = loadfile(found[1])
|
||||||
@@ -26,7 +26,7 @@ function vim._load_package(name)
|
|||||||
|
|
||||||
local so_paths = {}
|
local so_paths = {}
|
||||||
for _, trail in ipairs(vim._so_trails) do
|
for _, trail in ipairs(vim._so_trails) do
|
||||||
local path = "lua"..trail:gsub('?', basename) -- so_trails contains a leading slash
|
local path = 'lua' .. trail:gsub('?', basename) -- so_trails contains a leading slash
|
||||||
table.insert(so_paths, path)
|
table.insert(so_paths, path)
|
||||||
end
|
end
|
||||||
|
|
||||||
@@ -37,9 +37,9 @@ function vim._load_package(name)
|
|||||||
-- b) replace all dots by underscores
|
-- b) replace all dots by underscores
|
||||||
-- c) prepend "luaopen_"
|
-- c) prepend "luaopen_"
|
||||||
-- So "foo-bar.baz" should result in "luaopen_bar_baz"
|
-- So "foo-bar.baz" should result in "luaopen_bar_baz"
|
||||||
local dash = name:find("-", 1, true)
|
local dash = name:find('-', 1, true)
|
||||||
local modname = dash and name:sub(dash + 1) or name
|
local modname = dash and name:sub(dash + 1) or name
|
||||||
local f, err = package.loadlib(found[1], "luaopen_"..modname:gsub("%.", "_"))
|
local f, err = package.loadlib(found[1], 'luaopen_' .. modname:gsub('%.', '_'))
|
||||||
return f or error(err)
|
return f or error(err)
|
||||||
end
|
end
|
||||||
return nil
|
return nil
|
||||||
@@ -49,7 +49,7 @@ end
|
|||||||
table.insert(package.loaders, 2, vim._load_package)
|
table.insert(package.loaders, 2, vim._load_package)
|
||||||
|
|
||||||
-- builtin functions which always should be available
|
-- builtin functions which always should be available
|
||||||
require'vim.shared'
|
require('vim.shared')
|
||||||
|
|
||||||
vim._submodules = { inspect = true }
|
vim._submodules = { inspect = true }
|
||||||
|
|
||||||
@@ -67,7 +67,7 @@ setmetatable(vim, {
|
|||||||
return t[key]
|
return t[key]
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
end
|
end,
|
||||||
})
|
})
|
||||||
|
|
||||||
--- <Docs described in |vim.empty_dict()| >
|
--- <Docs described in |vim.empty_dict()| >
|
||||||
@@ -79,5 +79,5 @@ end
|
|||||||
|
|
||||||
-- only on main thread: functions for interacting with editor state
|
-- only on main thread: functions for interacting with editor state
|
||||||
if not vim.is_thread() then
|
if not vim.is_thread() then
|
||||||
require'vim._editor'
|
require('vim._editor')
|
||||||
end
|
end
|
||||||
|
@@ -13,7 +13,9 @@ local SET_TYPES = setmetatable({
|
|||||||
local options_info = {}
|
local options_info = {}
|
||||||
for _, v in pairs(a.nvim_get_all_options_info()) do
|
for _, v in pairs(a.nvim_get_all_options_info()) do
|
||||||
options_info[v.name] = v
|
options_info[v.name] = v
|
||||||
if v.shortname ~= "" then options_info[v.shortname] = v end
|
if v.shortname ~= '' then
|
||||||
|
options_info[v.shortname] = v
|
||||||
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
local get_scoped_options = function(scope)
|
local get_scoped_options = function(scope)
|
||||||
@@ -27,19 +29,21 @@ local get_scoped_options = function(scope)
|
|||||||
return result
|
return result
|
||||||
end
|
end
|
||||||
|
|
||||||
local buf_options = get_scoped_options("buf")
|
local buf_options = get_scoped_options('buf')
|
||||||
local glb_options = get_scoped_options("global")
|
local glb_options = get_scoped_options('global')
|
||||||
local win_options = get_scoped_options("win")
|
local win_options = get_scoped_options('win')
|
||||||
|
|
||||||
local function make_meta_accessor(get, set, del, validator)
|
local function make_meta_accessor(get, set, del, validator)
|
||||||
validator = validator or function() return true end
|
validator = validator or function()
|
||||||
|
return true
|
||||||
|
end
|
||||||
|
|
||||||
validate {
|
validate({
|
||||||
get = {get, 'f'};
|
get = { get, 'f' },
|
||||||
set = {set, 'f'};
|
set = { set, 'f' },
|
||||||
del = {del, 'f', true};
|
del = { del, 'f', true },
|
||||||
validator = {validator, 'f'};
|
validator = { validator, 'f' },
|
||||||
}
|
})
|
||||||
|
|
||||||
local mt = {}
|
local mt = {}
|
||||||
function mt:__newindex(k, v)
|
function mt:__newindex(k, v)
|
||||||
@@ -73,7 +77,7 @@ end, vim.fn.setenv)
|
|||||||
do -- buffer option accessor
|
do -- buffer option accessor
|
||||||
local function new_buf_opt_accessor(bufnr)
|
local function new_buf_opt_accessor(bufnr)
|
||||||
local function get(k)
|
local function get(k)
|
||||||
if bufnr == nil and type(k) == "number" then
|
if bufnr == nil and type(k) == 'number' then
|
||||||
return new_buf_opt_accessor(k)
|
return new_buf_opt_accessor(k)
|
||||||
end
|
end
|
||||||
|
|
||||||
@@ -103,7 +107,7 @@ end
|
|||||||
do -- window option accessor
|
do -- window option accessor
|
||||||
local function new_win_opt_accessor(winnr)
|
local function new_win_opt_accessor(winnr)
|
||||||
local function get(k)
|
local function get(k)
|
||||||
if winnr == nil and type(k) == "number" then
|
if winnr == nil and type(k) == 'number' then
|
||||||
return new_win_opt_accessor(k)
|
return new_win_opt_accessor(k)
|
||||||
end
|
end
|
||||||
return a.nvim_win_get_option(winnr or 0, k)
|
return a.nvim_win_get_option(winnr or 0, k)
|
||||||
@@ -131,17 +135,19 @@ end
|
|||||||
|
|
||||||
-- vim global option
|
-- vim global option
|
||||||
-- this ONLY sets the global option. like `setglobal`
|
-- this ONLY sets the global option. like `setglobal`
|
||||||
vim.go = make_meta_accessor(
|
vim.go = make_meta_accessor(function(k)
|
||||||
function(k) return a.nvim_get_option_value(k, {scope = "global"}) end,
|
return a.nvim_get_option_value(k, { scope = 'global' })
|
||||||
function(k, v) return a.nvim_set_option_value(k, v, {scope = "global"}) end
|
end, function(k, v)
|
||||||
)
|
return a.nvim_set_option_value(k, v, { scope = 'global' })
|
||||||
|
end)
|
||||||
|
|
||||||
-- vim `set` style options.
|
-- vim `set` style options.
|
||||||
-- it has no additional metamethod magic.
|
-- it has no additional metamethod magic.
|
||||||
vim.o = make_meta_accessor(
|
vim.o = make_meta_accessor(function(k)
|
||||||
function(k) return a.nvim_get_option_value(k, {}) end,
|
return a.nvim_get_option_value(k, {})
|
||||||
function(k, v) return a.nvim_set_option_value(k, v, {}) end
|
end, function(k, v)
|
||||||
)
|
return a.nvim_set_option_value(k, v, {})
|
||||||
|
end)
|
||||||
|
|
||||||
---@brief [[
|
---@brief [[
|
||||||
--- vim.opt, vim.opt_local and vim.opt_global implementation
|
--- vim.opt, vim.opt_local and vim.opt_global implementation
|
||||||
@@ -154,7 +160,9 @@ vim.o = make_meta_accessor(
|
|||||||
--- Preserves the order and does not mutate the original list
|
--- Preserves the order and does not mutate the original list
|
||||||
local remove_duplicate_values = function(t)
|
local remove_duplicate_values = function(t)
|
||||||
local result, seen = {}, {}
|
local result, seen = {}, {}
|
||||||
if type(t) == "function" then error(debug.traceback("asdf")) end
|
if type(t) == 'function' then
|
||||||
|
error(debug.traceback('asdf'))
|
||||||
|
end
|
||||||
for _, v in ipairs(t) do
|
for _, v in ipairs(t) do
|
||||||
if not seen[v] then
|
if not seen[v] then
|
||||||
table.insert(result, v)
|
table.insert(result, v)
|
||||||
@@ -184,24 +192,28 @@ local OptionTypes = setmetatable({
|
|||||||
MAP = 4,
|
MAP = 4,
|
||||||
SET = 5,
|
SET = 5,
|
||||||
}, {
|
}, {
|
||||||
__index = function(_, k) error("Not a valid OptionType: " .. k) end,
|
__index = function(_, k)
|
||||||
__newindex = function(_, k) error("Cannot set a new OptionType: " .. k) end,
|
error('Not a valid OptionType: ' .. k)
|
||||||
|
end,
|
||||||
|
__newindex = function(_, k)
|
||||||
|
error('Cannot set a new OptionType: ' .. k)
|
||||||
|
end,
|
||||||
})
|
})
|
||||||
|
|
||||||
--- Convert a vimoption_T style dictionary to the correct OptionType associated with it.
|
--- Convert a vimoption_T style dictionary to the correct OptionType associated with it.
|
||||||
---@return OptionType
|
---@return OptionType
|
||||||
local get_option_type = function(name, info)
|
local get_option_type = function(name, info)
|
||||||
if info.type == "boolean" then
|
if info.type == 'boolean' then
|
||||||
return OptionTypes.BOOLEAN
|
return OptionTypes.BOOLEAN
|
||||||
elseif info.type == "number" then
|
elseif info.type == 'number' then
|
||||||
return OptionTypes.NUMBER
|
return OptionTypes.NUMBER
|
||||||
elseif info.type == "string" then
|
elseif info.type == 'string' then
|
||||||
if not info.commalist and not info.flaglist then
|
if not info.commalist and not info.flaglist then
|
||||||
return OptionTypes.STRING
|
return OptionTypes.STRING
|
||||||
end
|
end
|
||||||
|
|
||||||
if key_value_options[name] then
|
if key_value_options[name] then
|
||||||
assert(info.commalist, "Must be a comma list to use key:value style")
|
assert(info.commalist, 'Must be a comma list to use key:value style')
|
||||||
return OptionTypes.MAP
|
return OptionTypes.MAP
|
||||||
end
|
end
|
||||||
|
|
||||||
@@ -211,13 +223,12 @@ local get_option_type = function(name, info)
|
|||||||
return OptionTypes.ARRAY
|
return OptionTypes.ARRAY
|
||||||
end
|
end
|
||||||
|
|
||||||
error("Fallthrough in OptionTypes")
|
error('Fallthrough in OptionTypes')
|
||||||
else
|
else
|
||||||
error("Not a known info.type:" .. info.type)
|
error('Not a known info.type:' .. info.type)
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
|
|
||||||
-- Check whether the OptionTypes is allowed for vim.opt
|
-- Check whether the OptionTypes is allowed for vim.opt
|
||||||
-- If it does not match, throw an error which indicates which option causes the error.
|
-- If it does not match, throw an error which indicates which option causes the error.
|
||||||
local function assert_valid_value(name, value, types)
|
local function assert_valid_value(name, value, types)
|
||||||
@@ -228,16 +239,18 @@ local function assert_valid_value(name, value, types)
|
|||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
error(string.format("Invalid option type '%s' for '%s', should be %s", type_of_value, name, table.concat(types, " or ")))
|
error(
|
||||||
|
string.format("Invalid option type '%s' for '%s', should be %s", type_of_value, name, table.concat(types, ' or '))
|
||||||
|
)
|
||||||
end
|
end
|
||||||
|
|
||||||
local valid_types = {
|
local valid_types = {
|
||||||
[OptionTypes.BOOLEAN] = { "boolean" },
|
[OptionTypes.BOOLEAN] = { 'boolean' },
|
||||||
[OptionTypes.NUMBER] = { "number" },
|
[OptionTypes.NUMBER] = { 'number' },
|
||||||
[OptionTypes.STRING] = { "string" },
|
[OptionTypes.STRING] = { 'string' },
|
||||||
[OptionTypes.SET] = { "string", "table" },
|
[OptionTypes.SET] = { 'string', 'table' },
|
||||||
[OptionTypes.ARRAY] = { "string", "table" },
|
[OptionTypes.ARRAY] = { 'string', 'table' },
|
||||||
[OptionTypes.MAP] = { "string", "table" },
|
[OptionTypes.MAP] = { 'string', 'table' },
|
||||||
}
|
}
|
||||||
|
|
||||||
--- Convert a lua value to a vimoption_T value
|
--- Convert a lua value to a vimoption_T value
|
||||||
@@ -245,12 +258,20 @@ local convert_value_to_vim = (function()
|
|||||||
-- Map of functions to take a Lua style value and convert to vimoption_T style value.
|
-- Map of functions to take a Lua style value and convert to vimoption_T style value.
|
||||||
-- Each function takes (info, lua_value) -> vim_value
|
-- Each function takes (info, lua_value) -> vim_value
|
||||||
local to_vim_value = {
|
local to_vim_value = {
|
||||||
[OptionTypes.BOOLEAN] = function(_, value) return value end,
|
[OptionTypes.BOOLEAN] = function(_, value)
|
||||||
[OptionTypes.NUMBER] = function(_, value) return value end,
|
return value
|
||||||
[OptionTypes.STRING] = function(_, value) return value end,
|
end,
|
||||||
|
[OptionTypes.NUMBER] = function(_, value)
|
||||||
|
return value
|
||||||
|
end,
|
||||||
|
[OptionTypes.STRING] = function(_, value)
|
||||||
|
return value
|
||||||
|
end,
|
||||||
|
|
||||||
[OptionTypes.SET] = function(info, value)
|
[OptionTypes.SET] = function(info, value)
|
||||||
if type(value) == "string" then return value end
|
if type(value) == 'string' then
|
||||||
|
return value
|
||||||
|
end
|
||||||
|
|
||||||
if info.flaglist and info.commalist then
|
if info.flaglist and info.commalist then
|
||||||
local keys = {}
|
local keys = {}
|
||||||
@@ -261,7 +282,7 @@ local convert_value_to_vim = (function()
|
|||||||
end
|
end
|
||||||
|
|
||||||
table.sort(keys)
|
table.sort(keys)
|
||||||
return table.concat(keys, ",")
|
return table.concat(keys, ',')
|
||||||
else
|
else
|
||||||
local result = ''
|
local result = ''
|
||||||
for k, v in pairs(value) do
|
for k, v in pairs(value) do
|
||||||
@@ -275,23 +296,27 @@ local convert_value_to_vim = (function()
|
|||||||
end,
|
end,
|
||||||
|
|
||||||
[OptionTypes.ARRAY] = function(info, value)
|
[OptionTypes.ARRAY] = function(info, value)
|
||||||
if type(value) == "string" then return value end
|
if type(value) == 'string' then
|
||||||
|
return value
|
||||||
|
end
|
||||||
if not info.allows_duplicates then
|
if not info.allows_duplicates then
|
||||||
value = remove_duplicate_values(value)
|
value = remove_duplicate_values(value)
|
||||||
end
|
end
|
||||||
return table.concat(value, ",")
|
return table.concat(value, ',')
|
||||||
end,
|
end,
|
||||||
|
|
||||||
[OptionTypes.MAP] = function(_, value)
|
[OptionTypes.MAP] = function(_, value)
|
||||||
if type(value) == "string" then return value end
|
if type(value) == 'string' then
|
||||||
|
return value
|
||||||
|
end
|
||||||
|
|
||||||
local result = {}
|
local result = {}
|
||||||
for opt_key, opt_value in pairs(value) do
|
for opt_key, opt_value in pairs(value) do
|
||||||
table.insert(result, string.format("%s:%s", opt_key, opt_value))
|
table.insert(result, string.format('%s:%s', opt_key, opt_value))
|
||||||
end
|
end
|
||||||
|
|
||||||
table.sort(result)
|
table.sort(result)
|
||||||
return table.concat(result, ",")
|
return table.concat(result, ',')
|
||||||
end,
|
end,
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -312,12 +337,18 @@ local convert_value_to_lua = (function()
|
|||||||
-- Map of OptionType to functions that take vimoption_T values and convert to lua values.
|
-- Map of OptionType to functions that take vimoption_T values and convert to lua values.
|
||||||
-- Each function takes (info, vim_value) -> lua_value
|
-- Each function takes (info, vim_value) -> lua_value
|
||||||
local to_lua_value = {
|
local to_lua_value = {
|
||||||
[OptionTypes.BOOLEAN] = function(_, value) return value end,
|
[OptionTypes.BOOLEAN] = function(_, value)
|
||||||
[OptionTypes.NUMBER] = function(_, value) return value end,
|
return value
|
||||||
[OptionTypes.STRING] = function(_, value) return value end,
|
end,
|
||||||
|
[OptionTypes.NUMBER] = function(_, value)
|
||||||
|
return value
|
||||||
|
end,
|
||||||
|
[OptionTypes.STRING] = function(_, value)
|
||||||
|
return value
|
||||||
|
end,
|
||||||
|
|
||||||
[OptionTypes.ARRAY] = function(info, value)
|
[OptionTypes.ARRAY] = function(info, value)
|
||||||
if type(value) == "table" then
|
if type(value) == 'table' then
|
||||||
if not info.allows_duplicates then
|
if not info.allows_duplicates then
|
||||||
value = remove_duplicate_values(value)
|
value = remove_duplicate_values(value)
|
||||||
end
|
end
|
||||||
@@ -332,41 +363,43 @@ local convert_value_to_lua = (function()
|
|||||||
end
|
end
|
||||||
|
|
||||||
-- Handles unescaped commas in a list.
|
-- Handles unescaped commas in a list.
|
||||||
if string.find(value, ",,,") then
|
if string.find(value, ',,,') then
|
||||||
local comma_split = vim.split(value, ",,,")
|
local comma_split = vim.split(value, ',,,')
|
||||||
local left = comma_split[1]
|
local left = comma_split[1]
|
||||||
local right = comma_split[2]
|
local right = comma_split[2]
|
||||||
|
|
||||||
local result = {}
|
local result = {}
|
||||||
vim.list_extend(result, vim.split(left, ","))
|
vim.list_extend(result, vim.split(left, ','))
|
||||||
table.insert(result, ",")
|
table.insert(result, ',')
|
||||||
vim.list_extend(result, vim.split(right, ","))
|
vim.list_extend(result, vim.split(right, ','))
|
||||||
|
|
||||||
table.sort(result)
|
table.sort(result)
|
||||||
|
|
||||||
return result
|
return result
|
||||||
end
|
end
|
||||||
|
|
||||||
if string.find(value, ",^,,", 1, true) then
|
if string.find(value, ',^,,', 1, true) then
|
||||||
local comma_split = vim.split(value, ",^,,", true)
|
local comma_split = vim.split(value, ',^,,', true)
|
||||||
local left = comma_split[1]
|
local left = comma_split[1]
|
||||||
local right = comma_split[2]
|
local right = comma_split[2]
|
||||||
|
|
||||||
local result = {}
|
local result = {}
|
||||||
vim.list_extend(result, vim.split(left, ","))
|
vim.list_extend(result, vim.split(left, ','))
|
||||||
table.insert(result, "^,")
|
table.insert(result, '^,')
|
||||||
vim.list_extend(result, vim.split(right, ","))
|
vim.list_extend(result, vim.split(right, ','))
|
||||||
|
|
||||||
table.sort(result)
|
table.sort(result)
|
||||||
|
|
||||||
return result
|
return result
|
||||||
end
|
end
|
||||||
|
|
||||||
return vim.split(value, ",")
|
return vim.split(value, ',')
|
||||||
end,
|
end,
|
||||||
|
|
||||||
[OptionTypes.SET] = function(info, value)
|
[OptionTypes.SET] = function(info, value)
|
||||||
if type(value) == "table" then return value end
|
if type(value) == 'table' then
|
||||||
|
return value
|
||||||
|
end
|
||||||
|
|
||||||
-- Empty strings mean that there is nothing there,
|
-- Empty strings mean that there is nothing there,
|
||||||
-- so empty table should be returned.
|
-- so empty table should be returned.
|
||||||
@@ -374,10 +407,10 @@ local convert_value_to_lua = (function()
|
|||||||
return {}
|
return {}
|
||||||
end
|
end
|
||||||
|
|
||||||
assert(info.flaglist, "That is the only one I know how to handle")
|
assert(info.flaglist, 'That is the only one I know how to handle')
|
||||||
|
|
||||||
if info.flaglist and info.commalist then
|
if info.flaglist and info.commalist then
|
||||||
local split_value = vim.split(value, ",")
|
local split_value = vim.split(value, ',')
|
||||||
local result = {}
|
local result = {}
|
||||||
for _, v in ipairs(split_value) do
|
for _, v in ipairs(split_value) do
|
||||||
result[v] = true
|
result[v] = true
|
||||||
@@ -395,15 +428,17 @@ local convert_value_to_lua = (function()
|
|||||||
end,
|
end,
|
||||||
|
|
||||||
[OptionTypes.MAP] = function(info, raw_value)
|
[OptionTypes.MAP] = function(info, raw_value)
|
||||||
if type(raw_value) == "table" then return raw_value end
|
if type(raw_value) == 'table' then
|
||||||
|
return raw_value
|
||||||
|
end
|
||||||
|
|
||||||
assert(info.commalist, "Only commas are supported currently")
|
assert(info.commalist, 'Only commas are supported currently')
|
||||||
|
|
||||||
local result = {}
|
local result = {}
|
||||||
|
|
||||||
local comma_split = vim.split(raw_value, ",")
|
local comma_split = vim.split(raw_value, ',')
|
||||||
for _, key_value_str in ipairs(comma_split) do
|
for _, key_value_str in ipairs(comma_split) do
|
||||||
local key, value = unpack(vim.split(key_value_str, ":"))
|
local key, value = unpack(vim.split(key_value_str, ':'))
|
||||||
key = vim.trim(key)
|
key = vim.trim(key)
|
||||||
|
|
||||||
result[key] = value
|
result[key] = value
|
||||||
@@ -443,17 +478,21 @@ local prepend_value = (function()
|
|||||||
end,
|
end,
|
||||||
|
|
||||||
[OptionTypes.MAP] = function(left, right)
|
[OptionTypes.MAP] = function(left, right)
|
||||||
return vim.tbl_extend("force", left, right)
|
return vim.tbl_extend('force', left, right)
|
||||||
end,
|
end,
|
||||||
|
|
||||||
[OptionTypes.SET] = function(left, right)
|
[OptionTypes.SET] = function(left, right)
|
||||||
return vim.tbl_extend("force", left, right)
|
return vim.tbl_extend('force', left, right)
|
||||||
end,
|
end,
|
||||||
}
|
}
|
||||||
|
|
||||||
return function(name, info, current, new)
|
return function(name, info, current, new)
|
||||||
return value_mutator(
|
return value_mutator(
|
||||||
name, info, convert_value_to_lua(name, info, current), convert_value_to_lua(name, info, new), methods
|
name,
|
||||||
|
info,
|
||||||
|
convert_value_to_lua(name, info, current),
|
||||||
|
convert_value_to_lua(name, info, new),
|
||||||
|
methods
|
||||||
)
|
)
|
||||||
end
|
end
|
||||||
end)()
|
end)()
|
||||||
@@ -478,17 +517,21 @@ local add_value = (function()
|
|||||||
end,
|
end,
|
||||||
|
|
||||||
[OptionTypes.MAP] = function(left, right)
|
[OptionTypes.MAP] = function(left, right)
|
||||||
return vim.tbl_extend("force", left, right)
|
return vim.tbl_extend('force', left, right)
|
||||||
end,
|
end,
|
||||||
|
|
||||||
[OptionTypes.SET] = function(left, right)
|
[OptionTypes.SET] = function(left, right)
|
||||||
return vim.tbl_extend("force", left, right)
|
return vim.tbl_extend('force', left, right)
|
||||||
end,
|
end,
|
||||||
}
|
}
|
||||||
|
|
||||||
return function(name, info, current, new)
|
return function(name, info, current, new)
|
||||||
return value_mutator(
|
return value_mutator(
|
||||||
name, info, convert_value_to_lua(name, info, current), convert_value_to_lua(name, info, new), methods
|
name,
|
||||||
|
info,
|
||||||
|
convert_value_to_lua(name, info, current),
|
||||||
|
convert_value_to_lua(name, info, new),
|
||||||
|
methods
|
||||||
)
|
)
|
||||||
end
|
end
|
||||||
end)()
|
end)()
|
||||||
@@ -518,11 +561,11 @@ local remove_value = (function()
|
|||||||
end,
|
end,
|
||||||
|
|
||||||
[OptionTypes.STRING] = function()
|
[OptionTypes.STRING] = function()
|
||||||
error("Subtraction not supported for strings.")
|
error('Subtraction not supported for strings.')
|
||||||
end,
|
end,
|
||||||
|
|
||||||
[OptionTypes.ARRAY] = function(left, right)
|
[OptionTypes.ARRAY] = function(left, right)
|
||||||
if type(right) == "string" then
|
if type(right) == 'string' then
|
||||||
remove_one_item(left, right)
|
remove_one_item(left, right)
|
||||||
else
|
else
|
||||||
for _, v in ipairs(right) do
|
for _, v in ipairs(right) do
|
||||||
@@ -534,7 +577,7 @@ local remove_value = (function()
|
|||||||
end,
|
end,
|
||||||
|
|
||||||
[OptionTypes.MAP] = function(left, right)
|
[OptionTypes.MAP] = function(left, right)
|
||||||
if type(right) == "string" then
|
if type(right) == 'string' then
|
||||||
left[right] = nil
|
left[right] = nil
|
||||||
else
|
else
|
||||||
for _, v in ipairs(right) do
|
for _, v in ipairs(right) do
|
||||||
@@ -546,7 +589,7 @@ local remove_value = (function()
|
|||||||
end,
|
end,
|
||||||
|
|
||||||
[OptionTypes.SET] = function(left, right)
|
[OptionTypes.SET] = function(left, right)
|
||||||
if type(right) == "string" then
|
if type(right) == 'string' then
|
||||||
left[right] = nil
|
left[right] = nil
|
||||||
else
|
else
|
||||||
for _, v in ipairs(right) do
|
for _, v in ipairs(right) do
|
||||||
@@ -567,9 +610,9 @@ local create_option_metatable = function(set_type)
|
|||||||
local set_mt, option_mt
|
local set_mt, option_mt
|
||||||
|
|
||||||
local make_option = function(name, value)
|
local make_option = function(name, value)
|
||||||
local info = assert(options_info[name], "Not a valid option name: " .. name)
|
local info = assert(options_info[name], 'Not a valid option name: ' .. name)
|
||||||
|
|
||||||
if type(value) == "table" and getmetatable(value) == option_mt then
|
if type(value) == 'table' and getmetatable(value) == option_mt then
|
||||||
assert(name == value._name, "must be the same value, otherwise that's weird.")
|
assert(name == value._name, "must be the same value, otherwise that's weird.")
|
||||||
|
|
||||||
value = value._value
|
value = value._value
|
||||||
@@ -584,9 +627,9 @@ local create_option_metatable = function(set_type)
|
|||||||
|
|
||||||
local scope
|
local scope
|
||||||
if set_type == SET_TYPES.GLOBAL then
|
if set_type == SET_TYPES.GLOBAL then
|
||||||
scope = "global"
|
scope = 'global'
|
||||||
elseif set_type == SET_TYPES.LOCAL then
|
elseif set_type == SET_TYPES.LOCAL then
|
||||||
scope = "local"
|
scope = 'local'
|
||||||
end
|
end
|
||||||
|
|
||||||
option_mt = {
|
option_mt = {
|
||||||
@@ -625,7 +668,7 @@ local create_option_metatable = function(set_type)
|
|||||||
|
|
||||||
__sub = function(self, right)
|
__sub = function(self, right)
|
||||||
return make_option(self._name, remove_value(self._name, self._info, self._value, right))
|
return make_option(self._name, remove_value(self._name, self._info, self._value, right))
|
||||||
end
|
end,
|
||||||
}
|
}
|
||||||
option_mt.__index = option_mt
|
option_mt.__index = option_mt
|
||||||
|
|
||||||
|
@@ -7,6 +7,6 @@
|
|||||||
|
|
||||||
local lua_version = _VERSION:sub(-3)
|
local lua_version = _VERSION:sub(-3)
|
||||||
|
|
||||||
if lua_version >= "5.2" then
|
if lua_version >= '5.2' then
|
||||||
unpack = table.unpack -- luacheck: ignore 121 143
|
unpack = table.unpack -- luacheck: ignore 121 143
|
||||||
end
|
end
|
||||||
|
@@ -28,7 +28,7 @@ local global_diagnostic_options = {
|
|||||||
|
|
||||||
M.handlers = setmetatable({}, {
|
M.handlers = setmetatable({}, {
|
||||||
__newindex = function(t, name, handler)
|
__newindex = function(t, name, handler)
|
||||||
vim.validate { handler = {handler, "t" } }
|
vim.validate({ handler = { handler, 't' } })
|
||||||
rawset(t, name, handler)
|
rawset(t, name, handler)
|
||||||
if global_diagnostic_options[name] == nil then
|
if global_diagnostic_options[name] == nil then
|
||||||
global_diagnostic_options[name] = true
|
global_diagnostic_options[name] = true
|
||||||
@@ -39,7 +39,7 @@ M.handlers = setmetatable({}, {
|
|||||||
-- Metatable that automatically creates an empty table when assigning to a missing key
|
-- Metatable that automatically creates an empty table when assigning to a missing key
|
||||||
local bufnr_and_namespace_cacher_mt = {
|
local bufnr_and_namespace_cacher_mt = {
|
||||||
__index = function(t, bufnr)
|
__index = function(t, bufnr)
|
||||||
assert(bufnr > 0, "Invalid buffer number")
|
assert(bufnr > 0, 'Invalid buffer number')
|
||||||
t[bufnr] = {}
|
t[bufnr] = {}
|
||||||
return t[bufnr]
|
return t[bufnr]
|
||||||
end,
|
end,
|
||||||
@@ -47,11 +47,11 @@ local bufnr_and_namespace_cacher_mt = {
|
|||||||
|
|
||||||
local diagnostic_cache = setmetatable({}, {
|
local diagnostic_cache = setmetatable({}, {
|
||||||
__index = function(t, bufnr)
|
__index = function(t, bufnr)
|
||||||
assert(bufnr > 0, "Invalid buffer number")
|
assert(bufnr > 0, 'Invalid buffer number')
|
||||||
vim.api.nvim_buf_attach(bufnr, false, {
|
vim.api.nvim_buf_attach(bufnr, false, {
|
||||||
on_detach = function()
|
on_detach = function()
|
||||||
rawset(t, bufnr, nil) -- clear cache
|
rawset(t, bufnr, nil) -- clear cache
|
||||||
end
|
end,
|
||||||
})
|
})
|
||||||
t[bufnr] = {}
|
t[bufnr] = {}
|
||||||
return t[bufnr]
|
return t[bufnr]
|
||||||
@@ -68,7 +68,7 @@ local all_namespaces = {}
|
|||||||
---@private
|
---@private
|
||||||
local function to_severity(severity)
|
local function to_severity(severity)
|
||||||
if type(severity) == 'string' then
|
if type(severity) == 'string' then
|
||||||
return assert(M.severity[string.upper(severity)], string.format("Invalid severity: %s", severity))
|
return assert(M.severity[string.upper(severity)], string.format('Invalid severity: %s', severity))
|
||||||
end
|
end
|
||||||
return severity
|
return severity
|
||||||
end
|
end
|
||||||
@@ -79,15 +79,19 @@ local function filter_by_severity(severity, diagnostics)
|
|||||||
return diagnostics
|
return diagnostics
|
||||||
end
|
end
|
||||||
|
|
||||||
if type(severity) ~= "table" then
|
if type(severity) ~= 'table' then
|
||||||
severity = to_severity(severity)
|
severity = to_severity(severity)
|
||||||
return vim.tbl_filter(function(t) return t.severity == severity end, diagnostics)
|
return vim.tbl_filter(function(t)
|
||||||
|
return t.severity == severity
|
||||||
|
end, diagnostics)
|
||||||
end
|
end
|
||||||
|
|
||||||
local min_severity = to_severity(severity.min) or M.severity.HINT
|
local min_severity = to_severity(severity.min) or M.severity.HINT
|
||||||
local max_severity = to_severity(severity.max) or M.severity.ERROR
|
local max_severity = to_severity(severity.max) or M.severity.ERROR
|
||||||
|
|
||||||
return vim.tbl_filter(function(t) return t.severity <= min_severity and t.severity >= max_severity end, diagnostics)
|
return vim.tbl_filter(function(t)
|
||||||
|
return t.severity <= min_severity and t.severity >= max_severity
|
||||||
|
end, diagnostics)
|
||||||
end
|
end
|
||||||
|
|
||||||
---@private
|
---@private
|
||||||
@@ -113,17 +117,17 @@ local function prefix_source(diagnostics)
|
|||||||
end
|
end
|
||||||
|
|
||||||
local t = vim.deepcopy(d)
|
local t = vim.deepcopy(d)
|
||||||
t.message = string.format("%s: %s", d.source, d.message)
|
t.message = string.format('%s: %s', d.source, d.message)
|
||||||
return t
|
return t
|
||||||
end, diagnostics)
|
end, diagnostics)
|
||||||
end
|
end
|
||||||
|
|
||||||
---@private
|
---@private
|
||||||
local function reformat_diagnostics(format, diagnostics)
|
local function reformat_diagnostics(format, diagnostics)
|
||||||
vim.validate {
|
vim.validate({
|
||||||
format = { format, 'f' },
|
format = { format, 'f' },
|
||||||
diagnostics = { diagnostics, 't' },
|
diagnostics = { diagnostics, 't' },
|
||||||
}
|
})
|
||||||
|
|
||||||
local formatted = vim.deepcopy(diagnostics)
|
local formatted = vim.deepcopy(diagnostics)
|
||||||
for _, diagnostic in ipairs(formatted) do
|
for _, diagnostic in ipairs(formatted) do
|
||||||
@@ -135,11 +139,11 @@ end
|
|||||||
---@private
|
---@private
|
||||||
local function enabled_value(option, namespace)
|
local function enabled_value(option, namespace)
|
||||||
local ns = namespace and M.get_namespace(namespace) or {}
|
local ns = namespace and M.get_namespace(namespace) or {}
|
||||||
if ns.opts and type(ns.opts[option]) == "table" then
|
if ns.opts and type(ns.opts[option]) == 'table' then
|
||||||
return ns.opts[option]
|
return ns.opts[option]
|
||||||
end
|
end
|
||||||
|
|
||||||
if type(global_diagnostic_options[option]) == "table" then
|
if type(global_diagnostic_options[option]) == 'table' then
|
||||||
return global_diagnostic_options[option]
|
return global_diagnostic_options[option]
|
||||||
end
|
end
|
||||||
|
|
||||||
@@ -162,7 +166,7 @@ local function resolve_optional_value(option, value, namespace, bufnr)
|
|||||||
elseif type(value) == 'table' then
|
elseif type(value) == 'table' then
|
||||||
return value
|
return value
|
||||||
else
|
else
|
||||||
error("Unexpected option type: " .. vim.inspect(value))
|
error('Unexpected option type: ' .. vim.inspect(value))
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
@@ -181,10 +185,10 @@ end
|
|||||||
|
|
||||||
-- Default diagnostic highlights
|
-- Default diagnostic highlights
|
||||||
local diagnostic_severities = {
|
local diagnostic_severities = {
|
||||||
[M.severity.ERROR] = { ctermfg = 1, guifg = "Red" };
|
[M.severity.ERROR] = { ctermfg = 1, guifg = 'Red' },
|
||||||
[M.severity.WARN] = { ctermfg = 3, guifg = "Orange" };
|
[M.severity.WARN] = { ctermfg = 3, guifg = 'Orange' },
|
||||||
[M.severity.INFO] = { ctermfg = 4, guifg = "LightBlue" };
|
[M.severity.INFO] = { ctermfg = 4, guifg = 'LightBlue' },
|
||||||
[M.severity.HINT] = { ctermfg = 7, guifg = "LightGrey" };
|
[M.severity.HINT] = { ctermfg = 7, guifg = 'LightGrey' },
|
||||||
}
|
}
|
||||||
|
|
||||||
-- Make a map from DiagnosticSeverity -> Highlight Name
|
-- Make a map from DiagnosticSeverity -> Highlight Name
|
||||||
@@ -194,16 +198,16 @@ local function make_highlight_map(base_name)
|
|||||||
for k in pairs(diagnostic_severities) do
|
for k in pairs(diagnostic_severities) do
|
||||||
local name = M.severity[k]
|
local name = M.severity[k]
|
||||||
name = name:sub(1, 1) .. name:sub(2):lower()
|
name = name:sub(1, 1) .. name:sub(2):lower()
|
||||||
result[k] = "Diagnostic" .. base_name .. name
|
result[k] = 'Diagnostic' .. base_name .. name
|
||||||
end
|
end
|
||||||
|
|
||||||
return result
|
return result
|
||||||
end
|
end
|
||||||
|
|
||||||
local virtual_text_highlight_map = make_highlight_map("VirtualText")
|
local virtual_text_highlight_map = make_highlight_map('VirtualText')
|
||||||
local underline_highlight_map = make_highlight_map("Underline")
|
local underline_highlight_map = make_highlight_map('Underline')
|
||||||
local floating_highlight_map = make_highlight_map("Floating")
|
local floating_highlight_map = make_highlight_map('Floating')
|
||||||
local sign_highlight_map = make_highlight_map("Sign")
|
local sign_highlight_map = make_highlight_map('Sign')
|
||||||
|
|
||||||
---@private
|
---@private
|
||||||
local define_default_signs = (function()
|
local define_default_signs = (function()
|
||||||
@@ -244,7 +248,7 @@ local function is_disabled(namespace, bufnr)
|
|||||||
return true
|
return true
|
||||||
end
|
end
|
||||||
|
|
||||||
if type(diagnostic_disabled[bufnr]) == "table" then
|
if type(diagnostic_disabled[bufnr]) == 'table' then
|
||||||
return diagnostic_disabled[bufnr][namespace]
|
return diagnostic_disabled[bufnr][namespace]
|
||||||
end
|
end
|
||||||
return diagnostic_disabled[bufnr]
|
return diagnostic_disabled[bufnr]
|
||||||
@@ -271,8 +275,8 @@ end
|
|||||||
---@private
|
---@private
|
||||||
local function set_diagnostic_cache(namespace, bufnr, diagnostics)
|
local function set_diagnostic_cache(namespace, bufnr, diagnostics)
|
||||||
for _, diagnostic in ipairs(diagnostics) do
|
for _, diagnostic in ipairs(diagnostics) do
|
||||||
assert(diagnostic.lnum, "Diagnostic line number is required")
|
assert(diagnostic.lnum, 'Diagnostic line number is required')
|
||||||
assert(diagnostic.col, "Diagnostic column is required")
|
assert(diagnostic.col, 'Diagnostic column is required')
|
||||||
diagnostic.severity = diagnostic.severity and to_severity(diagnostic.severity) or M.severity.ERROR
|
diagnostic.severity = diagnostic.severity and to_severity(diagnostic.severity) or M.severity.ERROR
|
||||||
diagnostic.end_lnum = diagnostic.end_lnum or diagnostic.lnum
|
diagnostic.end_lnum = diagnostic.end_lnum or diagnostic.lnum
|
||||||
diagnostic.end_col = diagnostic.end_col or diagnostic.col
|
diagnostic.end_col = diagnostic.end_col or diagnostic.col
|
||||||
@@ -314,10 +318,17 @@ local function save_extmarks(namespace, bufnr)
|
|||||||
end,
|
end,
|
||||||
on_detach = function()
|
on_detach = function()
|
||||||
diagnostic_cache_extmarks[bufnr] = nil
|
diagnostic_cache_extmarks[bufnr] = nil
|
||||||
end})
|
end,
|
||||||
|
})
|
||||||
diagnostic_attached_buffers[bufnr] = true
|
diagnostic_attached_buffers[bufnr] = true
|
||||||
end
|
end
|
||||||
diagnostic_cache_extmarks[bufnr][namespace] = vim.api.nvim_buf_get_extmarks(bufnr, namespace, 0, -1, {details = true})
|
diagnostic_cache_extmarks[bufnr][namespace] = vim.api.nvim_buf_get_extmarks(
|
||||||
|
bufnr,
|
||||||
|
namespace,
|
||||||
|
0,
|
||||||
|
-1,
|
||||||
|
{ details = true }
|
||||||
|
)
|
||||||
end
|
end
|
||||||
|
|
||||||
local registered_autocmds = {}
|
local registered_autocmds = {}
|
||||||
@@ -325,11 +336,11 @@ local registered_autocmds = {}
|
|||||||
---@private
|
---@private
|
||||||
local function make_augroup_key(namespace, bufnr)
|
local function make_augroup_key(namespace, bufnr)
|
||||||
local ns = M.get_namespace(namespace)
|
local ns = M.get_namespace(namespace)
|
||||||
return string.format("DiagnosticInsertLeave:%s:%s", bufnr, ns.name)
|
return string.format('DiagnosticInsertLeave:%s:%s', bufnr, ns.name)
|
||||||
end
|
end
|
||||||
|
|
||||||
--- Table of autocmd events to fire the update for displaying new diagnostic information
|
--- Table of autocmd events to fire the update for displaying new diagnostic information
|
||||||
local insert_leave_auto_cmds = { "InsertLeave", "CursorHoldI" }
|
local insert_leave_auto_cmds = { 'InsertLeave', 'CursorHoldI' }
|
||||||
|
|
||||||
---@private
|
---@private
|
||||||
local function schedule_display(namespace, bufnr, args)
|
local function schedule_display(namespace, bufnr, args)
|
||||||
@@ -337,15 +348,17 @@ local function schedule_display(namespace, bufnr, args)
|
|||||||
|
|
||||||
local key = make_augroup_key(namespace, bufnr)
|
local key = make_augroup_key(namespace, bufnr)
|
||||||
if not registered_autocmds[key] then
|
if not registered_autocmds[key] then
|
||||||
vim.cmd(string.format([[augroup %s
|
vim.cmd(string.format(
|
||||||
|
[[augroup %s
|
||||||
au!
|
au!
|
||||||
autocmd %s <buffer=%s> lua vim.diagnostic._execute_scheduled_display(%s, %s)
|
autocmd %s <buffer=%s> lua vim.diagnostic._execute_scheduled_display(%s, %s)
|
||||||
augroup END]],
|
augroup END]],
|
||||||
key,
|
key,
|
||||||
table.concat(insert_leave_auto_cmds, ","),
|
table.concat(insert_leave_auto_cmds, ','),
|
||||||
bufnr,
|
bufnr,
|
||||||
namespace,
|
namespace,
|
||||||
bufnr))
|
bufnr
|
||||||
|
))
|
||||||
registered_autocmds[key] = true
|
registered_autocmds[key] = true
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
@@ -355,9 +368,12 @@ local function clear_scheduled_display(namespace, bufnr)
|
|||||||
local key = make_augroup_key(namespace, bufnr)
|
local key = make_augroup_key(namespace, bufnr)
|
||||||
|
|
||||||
if registered_autocmds[key] then
|
if registered_autocmds[key] then
|
||||||
vim.cmd(string.format([[augroup %s
|
vim.cmd(string.format(
|
||||||
|
[[augroup %s
|
||||||
au!
|
au!
|
||||||
augroup END]], key))
|
augroup END]],
|
||||||
|
key
|
||||||
|
))
|
||||||
registered_autocmds[key] = nil
|
registered_autocmds[key] = nil
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
@@ -382,7 +398,7 @@ local function get_diagnostics(bufnr, opts, clamp)
|
|||||||
if not opts.lnum or d.lnum == opts.lnum then
|
if not opts.lnum or d.lnum == opts.lnum then
|
||||||
if clamp and vim.api.nvim_buf_is_loaded(b) then
|
if clamp and vim.api.nvim_buf_is_loaded(b) then
|
||||||
local line_count = buf_line_count[b] - 1
|
local line_count = buf_line_count[b] - 1
|
||||||
if (d.lnum > line_count or d.end_lnum > line_count or d.lnum < 0 or d.end_lnum < 0) then
|
if d.lnum > line_count or d.end_lnum > line_count or d.lnum < 0 or d.end_lnum < 0 then
|
||||||
d = vim.deepcopy(d)
|
d = vim.deepcopy(d)
|
||||||
d.lnum = math.max(math.min(d.lnum, line_count), 0)
|
d.lnum = math.max(math.min(d.lnum, line_count), 0)
|
||||||
d.end_lnum = math.max(math.min(d.end_lnum, line_count), 0)
|
d.end_lnum = math.max(math.min(d.end_lnum, line_count), 0)
|
||||||
@@ -431,7 +447,7 @@ end
|
|||||||
local function set_list(loclist, opts)
|
local function set_list(loclist, opts)
|
||||||
opts = opts or {}
|
opts = opts or {}
|
||||||
local open = vim.F.if_nil(opts.open, true)
|
local open = vim.F.if_nil(opts.open, true)
|
||||||
local title = opts.title or "Diagnostics"
|
local title = opts.title or 'Diagnostics'
|
||||||
local winnr = opts.winnr or 0
|
local winnr = opts.winnr or 0
|
||||||
local bufnr
|
local bufnr
|
||||||
if loclist then
|
if loclist then
|
||||||
@@ -447,7 +463,7 @@ local function set_list(loclist, opts)
|
|||||||
vim.fn.setqflist({}, ' ', { title = title, items = items })
|
vim.fn.setqflist({}, ' ', { title = title, items = items })
|
||||||
end
|
end
|
||||||
if open then
|
if open then
|
||||||
vim.api.nvim_command(loclist and "lopen" or "botright copen")
|
vim.api.nvim_command(loclist and 'lopen' or 'botright copen')
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
@@ -457,7 +473,7 @@ local function next_diagnostic(position, search_forward, bufnr, opts, namespace)
|
|||||||
bufnr = get_bufnr(bufnr)
|
bufnr = get_bufnr(bufnr)
|
||||||
local wrap = vim.F.if_nil(opts.wrap, true)
|
local wrap = vim.F.if_nil(opts.wrap, true)
|
||||||
local line_count = vim.api.nvim_buf_line_count(bufnr)
|
local line_count = vim.api.nvim_buf_line_count(bufnr)
|
||||||
local diagnostics = get_diagnostics(bufnr, vim.tbl_extend("keep", opts, {namespace = namespace}), true)
|
local diagnostics = get_diagnostics(bufnr, vim.tbl_extend('keep', opts, { namespace = namespace }), true)
|
||||||
local line_diagnostics = diagnostic_lines(diagnostics)
|
local line_diagnostics = diagnostic_lines(diagnostics)
|
||||||
for i = 0, line_count do
|
for i = 0, line_count do
|
||||||
local offset = i * (search_forward and 1 or -1)
|
local offset = i * (search_forward and 1 or -1)
|
||||||
@@ -472,11 +488,19 @@ local function next_diagnostic(position, search_forward, bufnr, opts, namespace)
|
|||||||
local line_length = #vim.api.nvim_buf_get_lines(bufnr, lnum, lnum + 1, true)[1]
|
local line_length = #vim.api.nvim_buf_get_lines(bufnr, lnum, lnum + 1, true)[1]
|
||||||
local sort_diagnostics, is_next
|
local sort_diagnostics, is_next
|
||||||
if search_forward then
|
if search_forward then
|
||||||
sort_diagnostics = function(a, b) return a.col < b.col end
|
sort_diagnostics = function(a, b)
|
||||||
is_next = function(d) return math.min(d.col, line_length - 1) > position[2] end
|
return a.col < b.col
|
||||||
|
end
|
||||||
|
is_next = function(d)
|
||||||
|
return math.min(d.col, line_length - 1) > position[2]
|
||||||
|
end
|
||||||
else
|
else
|
||||||
sort_diagnostics = function(a, b) return a.col > b.col end
|
sort_diagnostics = function(a, b)
|
||||||
is_next = function(d) return math.min(d.col, line_length - 1) < position[2] end
|
return a.col > b.col
|
||||||
|
end
|
||||||
|
is_next = function(d)
|
||||||
|
return math.min(d.col, line_length - 1) < position[2]
|
||||||
|
end
|
||||||
end
|
end
|
||||||
table.sort(line_diagnostics[lnum], sort_diagnostics)
|
table.sort(line_diagnostics[lnum], sort_diagnostics)
|
||||||
if i == 0 then
|
if i == 0 then
|
||||||
@@ -500,7 +524,7 @@ local function diagnostic_move_pos(opts, pos)
|
|||||||
local win_id = opts.win_id or vim.api.nvim_get_current_win()
|
local win_id = opts.win_id or vim.api.nvim_get_current_win()
|
||||||
|
|
||||||
if not pos then
|
if not pos then
|
||||||
vim.api.nvim_echo({{"No more valid diagnostics to move to", "WarningMsg"}}, true, {})
|
vim.api.nvim_echo({ { 'No more valid diagnostics to move to', 'WarningMsg' } }, true, {})
|
||||||
return
|
return
|
||||||
end
|
end
|
||||||
|
|
||||||
@@ -509,19 +533,17 @@ local function diagnostic_move_pos(opts, pos)
|
|||||||
vim.cmd("normal! m'")
|
vim.cmd("normal! m'")
|
||||||
vim.api.nvim_win_set_cursor(win_id, { pos[1] + 1, pos[2] })
|
vim.api.nvim_win_set_cursor(win_id, { pos[1] + 1, pos[2] })
|
||||||
-- Open folds under the cursor
|
-- Open folds under the cursor
|
||||||
vim.cmd("normal! zv")
|
vim.cmd('normal! zv')
|
||||||
end)
|
end)
|
||||||
|
|
||||||
if float then
|
if float then
|
||||||
local float_opts = type(float) == "table" and float or {}
|
local float_opts = type(float) == 'table' and float or {}
|
||||||
vim.schedule(function()
|
vim.schedule(function()
|
||||||
M.open_float(
|
M.open_float(vim.tbl_extend('keep', float_opts, {
|
||||||
vim.tbl_extend("keep", float_opts, {
|
|
||||||
bufnr = vim.api.nvim_win_get_buf(win_id),
|
bufnr = vim.api.nvim_win_get_buf(win_id),
|
||||||
scope = "cursor",
|
scope = 'cursor',
|
||||||
focus = false,
|
focus = false,
|
||||||
})
|
}))
|
||||||
)
|
|
||||||
end)
|
end)
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
@@ -599,10 +621,10 @@ end
|
|||||||
---@param namespace number|nil Update the options for the given namespace. When omitted, update the
|
---@param namespace number|nil Update the options for the given namespace. When omitted, update the
|
||||||
--- global diagnostic options.
|
--- global diagnostic options.
|
||||||
function M.config(opts, namespace)
|
function M.config(opts, namespace)
|
||||||
vim.validate {
|
vim.validate({
|
||||||
opts = { opts, 't', true },
|
opts = { opts, 't', true },
|
||||||
namespace = { namespace, 'n', true },
|
namespace = { namespace, 'n', true },
|
||||||
}
|
})
|
||||||
|
|
||||||
local t
|
local t
|
||||||
if namespace then
|
if namespace then
|
||||||
@@ -645,16 +667,16 @@ end
|
|||||||
---@param diagnostics table A list of diagnostic items |diagnostic-structure|
|
---@param diagnostics table A list of diagnostic items |diagnostic-structure|
|
||||||
---@param opts table|nil Display options to pass to |vim.diagnostic.show()|
|
---@param opts table|nil Display options to pass to |vim.diagnostic.show()|
|
||||||
function M.set(namespace, bufnr, diagnostics, opts)
|
function M.set(namespace, bufnr, diagnostics, opts)
|
||||||
vim.validate {
|
vim.validate({
|
||||||
namespace = { namespace, 'n' },
|
namespace = { namespace, 'n' },
|
||||||
bufnr = { bufnr, 'n' },
|
bufnr = { bufnr, 'n' },
|
||||||
diagnostics = {
|
diagnostics = {
|
||||||
diagnostics,
|
diagnostics,
|
||||||
vim.tbl_islist,
|
vim.tbl_islist,
|
||||||
"a list of diagnostics",
|
'a list of diagnostics',
|
||||||
},
|
},
|
||||||
opts = { opts, 't', true },
|
opts = { opts, 't', true },
|
||||||
}
|
})
|
||||||
|
|
||||||
bufnr = get_bufnr(bufnr)
|
bufnr = get_bufnr(bufnr)
|
||||||
|
|
||||||
@@ -668,7 +690,7 @@ function M.set(namespace, bufnr, diagnostics, opts)
|
|||||||
M.show(namespace, bufnr, nil, opts)
|
M.show(namespace, bufnr, nil, opts)
|
||||||
end
|
end
|
||||||
|
|
||||||
vim.api.nvim_exec_autocmds("DiagnosticChanged", {
|
vim.api.nvim_exec_autocmds('DiagnosticChanged', {
|
||||||
modeline = false,
|
modeline = false,
|
||||||
buffer = bufnr,
|
buffer = bufnr,
|
||||||
})
|
})
|
||||||
@@ -679,7 +701,7 @@ end
|
|||||||
---@param namespace number Diagnostic namespace
|
---@param namespace number Diagnostic namespace
|
||||||
---@return table Namespace metadata
|
---@return table Namespace metadata
|
||||||
function M.get_namespace(namespace)
|
function M.get_namespace(namespace)
|
||||||
vim.validate { namespace = { namespace, 'n' } }
|
vim.validate({ namespace = { namespace, 'n' } })
|
||||||
if not all_namespaces[namespace] then
|
if not all_namespaces[namespace] then
|
||||||
local name
|
local name
|
||||||
for k, v in pairs(vim.api.nvim_get_namespaces()) do
|
for k, v in pairs(vim.api.nvim_get_namespaces()) do
|
||||||
@@ -689,7 +711,7 @@ function M.get_namespace(namespace)
|
|||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
assert(name, "namespace does not exist or is anonymous")
|
assert(name, 'namespace does not exist or is anonymous')
|
||||||
|
|
||||||
all_namespaces[namespace] = {
|
all_namespaces[namespace] = {
|
||||||
name = name,
|
name = name,
|
||||||
@@ -717,10 +739,10 @@ end
|
|||||||
--- - severity: See |diagnostic-severity|.
|
--- - severity: See |diagnostic-severity|.
|
||||||
---@return table A list of diagnostic items |diagnostic-structure|.
|
---@return table A list of diagnostic items |diagnostic-structure|.
|
||||||
function M.get(bufnr, opts)
|
function M.get(bufnr, opts)
|
||||||
vim.validate {
|
vim.validate({
|
||||||
bufnr = { bufnr, 'n', true },
|
bufnr = { bufnr, 'n', true },
|
||||||
opts = { opts, 't', true },
|
opts = { opts, 't', true },
|
||||||
}
|
})
|
||||||
|
|
||||||
return get_diagnostics(bufnr, opts, false)
|
return get_diagnostics(bufnr, opts, false)
|
||||||
end
|
end
|
||||||
@@ -755,10 +777,7 @@ end
|
|||||||
--- Move to the previous diagnostic in the current buffer.
|
--- Move to the previous diagnostic in the current buffer.
|
||||||
---@param opts table See |vim.diagnostic.goto_next()|
|
---@param opts table See |vim.diagnostic.goto_next()|
|
||||||
function M.goto_prev(opts)
|
function M.goto_prev(opts)
|
||||||
return diagnostic_move_pos(
|
return diagnostic_move_pos(opts, M.get_prev_pos(opts))
|
||||||
opts,
|
|
||||||
M.get_prev_pos(opts)
|
|
||||||
)
|
|
||||||
end
|
end
|
||||||
|
|
||||||
--- Get the next diagnostic closest to the cursor position.
|
--- Get the next diagnostic closest to the cursor position.
|
||||||
@@ -803,24 +822,21 @@ end
|
|||||||
--- the "scope" option).
|
--- the "scope" option).
|
||||||
--- - win_id: (number, default 0) Window ID
|
--- - win_id: (number, default 0) Window ID
|
||||||
function M.goto_next(opts)
|
function M.goto_next(opts)
|
||||||
return diagnostic_move_pos(
|
return diagnostic_move_pos(opts, M.get_next_pos(opts))
|
||||||
opts,
|
|
||||||
M.get_next_pos(opts)
|
|
||||||
)
|
|
||||||
end
|
end
|
||||||
|
|
||||||
M.handlers.signs = {
|
M.handlers.signs = {
|
||||||
show = function(namespace, bufnr, diagnostics, opts)
|
show = function(namespace, bufnr, diagnostics, opts)
|
||||||
vim.validate {
|
vim.validate({
|
||||||
namespace = { namespace, 'n' },
|
namespace = { namespace, 'n' },
|
||||||
bufnr = { bufnr, 'n' },
|
bufnr = { bufnr, 'n' },
|
||||||
diagnostics = {
|
diagnostics = {
|
||||||
diagnostics,
|
diagnostics,
|
||||||
vim.tbl_islist,
|
vim.tbl_islist,
|
||||||
"a list of diagnostics",
|
'a list of diagnostics',
|
||||||
},
|
},
|
||||||
opts = { opts, 't', true },
|
opts = { opts, 't', true },
|
||||||
}
|
})
|
||||||
|
|
||||||
bufnr = get_bufnr(bufnr)
|
bufnr = get_bufnr(bufnr)
|
||||||
opts = opts or {}
|
opts = opts or {}
|
||||||
@@ -835,7 +851,7 @@ M.handlers.signs = {
|
|||||||
local priority = opts.signs and opts.signs.priority or 10
|
local priority = opts.signs and opts.signs.priority or 10
|
||||||
local get_priority
|
local get_priority
|
||||||
if opts.severity_sort then
|
if opts.severity_sort then
|
||||||
if type(opts.severity_sort) == "table" and opts.severity_sort.reverse then
|
if type(opts.severity_sort) == 'table' and opts.severity_sort.reverse then
|
||||||
get_priority = function(severity)
|
get_priority = function(severity)
|
||||||
return priority + (severity - vim.diagnostic.severity.ERROR)
|
return priority + (severity - vim.diagnostic.severity.ERROR)
|
||||||
end
|
end
|
||||||
@@ -852,21 +868,15 @@ M.handlers.signs = {
|
|||||||
|
|
||||||
local ns = M.get_namespace(namespace)
|
local ns = M.get_namespace(namespace)
|
||||||
if not ns.user_data.sign_group then
|
if not ns.user_data.sign_group then
|
||||||
ns.user_data.sign_group = string.format("vim.diagnostic.%s", ns.name)
|
ns.user_data.sign_group = string.format('vim.diagnostic.%s', ns.name)
|
||||||
end
|
end
|
||||||
|
|
||||||
local sign_group = ns.user_data.sign_group
|
local sign_group = ns.user_data.sign_group
|
||||||
for _, diagnostic in ipairs(diagnostics) do
|
for _, diagnostic in ipairs(diagnostics) do
|
||||||
vim.fn.sign_place(
|
vim.fn.sign_place(0, sign_group, sign_highlight_map[diagnostic.severity], bufnr, {
|
||||||
0,
|
|
||||||
sign_group,
|
|
||||||
sign_highlight_map[diagnostic.severity],
|
|
||||||
bufnr,
|
|
||||||
{
|
|
||||||
priority = get_priority(diagnostic.severity),
|
priority = get_priority(diagnostic.severity),
|
||||||
lnum = diagnostic.lnum + 1
|
lnum = diagnostic.lnum + 1,
|
||||||
}
|
})
|
||||||
)
|
|
||||||
end
|
end
|
||||||
end,
|
end,
|
||||||
hide = function(namespace, bufnr)
|
hide = function(namespace, bufnr)
|
||||||
@@ -879,16 +889,16 @@ M.handlers.signs = {
|
|||||||
|
|
||||||
M.handlers.underline = {
|
M.handlers.underline = {
|
||||||
show = function(namespace, bufnr, diagnostics, opts)
|
show = function(namespace, bufnr, diagnostics, opts)
|
||||||
vim.validate {
|
vim.validate({
|
||||||
namespace = { namespace, 'n' },
|
namespace = { namespace, 'n' },
|
||||||
bufnr = { bufnr, 'n' },
|
bufnr = { bufnr, 'n' },
|
||||||
diagnostics = {
|
diagnostics = {
|
||||||
diagnostics,
|
diagnostics,
|
||||||
vim.tbl_islist,
|
vim.tbl_islist,
|
||||||
"a list of diagnostics",
|
'a list of diagnostics',
|
||||||
},
|
},
|
||||||
opts = { opts, 't', true },
|
opts = { opts, 't', true },
|
||||||
}
|
})
|
||||||
|
|
||||||
bufnr = get_bufnr(bufnr)
|
bufnr = get_bufnr(bufnr)
|
||||||
opts = opts or {}
|
opts = opts or {}
|
||||||
@@ -899,7 +909,7 @@ M.handlers.underline = {
|
|||||||
|
|
||||||
local ns = M.get_namespace(namespace)
|
local ns = M.get_namespace(namespace)
|
||||||
if not ns.user_data.underline_ns then
|
if not ns.user_data.underline_ns then
|
||||||
ns.user_data.underline_ns = vim.api.nvim_create_namespace("")
|
ns.user_data.underline_ns = vim.api.nvim_create_namespace('')
|
||||||
end
|
end
|
||||||
|
|
||||||
local underline_ns = ns.user_data.underline_ns
|
local underline_ns = ns.user_data.underline_ns
|
||||||
@@ -928,21 +938,21 @@ M.handlers.underline = {
|
|||||||
diagnostic_cache_extmarks[bufnr][ns.user_data.underline_ns] = {}
|
diagnostic_cache_extmarks[bufnr][ns.user_data.underline_ns] = {}
|
||||||
vim.api.nvim_buf_clear_namespace(bufnr, ns.user_data.underline_ns, 0, -1)
|
vim.api.nvim_buf_clear_namespace(bufnr, ns.user_data.underline_ns, 0, -1)
|
||||||
end
|
end
|
||||||
end
|
end,
|
||||||
}
|
}
|
||||||
|
|
||||||
M.handlers.virtual_text = {
|
M.handlers.virtual_text = {
|
||||||
show = function(namespace, bufnr, diagnostics, opts)
|
show = function(namespace, bufnr, diagnostics, opts)
|
||||||
vim.validate {
|
vim.validate({
|
||||||
namespace = { namespace, 'n' },
|
namespace = { namespace, 'n' },
|
||||||
bufnr = { bufnr, 'n' },
|
bufnr = { bufnr, 'n' },
|
||||||
diagnostics = {
|
diagnostics = {
|
||||||
diagnostics,
|
diagnostics,
|
||||||
vim.tbl_islist,
|
vim.tbl_islist,
|
||||||
"a list of diagnostics",
|
'a list of diagnostics',
|
||||||
},
|
},
|
||||||
opts = { opts, 't', true },
|
opts = { opts, 't', true },
|
||||||
}
|
})
|
||||||
|
|
||||||
bufnr = get_bufnr(bufnr)
|
bufnr = get_bufnr(bufnr)
|
||||||
opts = opts or {}
|
opts = opts or {}
|
||||||
@@ -952,10 +962,7 @@ M.handlers.virtual_text = {
|
|||||||
if opts.virtual_text.format then
|
if opts.virtual_text.format then
|
||||||
diagnostics = reformat_diagnostics(opts.virtual_text.format, diagnostics)
|
diagnostics = reformat_diagnostics(opts.virtual_text.format, diagnostics)
|
||||||
end
|
end
|
||||||
if
|
if opts.virtual_text.source and (opts.virtual_text.source ~= 'if_many' or count_sources(bufnr) > 1) then
|
||||||
opts.virtual_text.source
|
|
||||||
and (opts.virtual_text.source ~= "if_many" or count_sources(bufnr) > 1)
|
|
||||||
then
|
|
||||||
diagnostics = prefix_source(diagnostics)
|
diagnostics = prefix_source(diagnostics)
|
||||||
end
|
end
|
||||||
if opts.virtual_text.severity then
|
if opts.virtual_text.severity then
|
||||||
@@ -965,7 +972,7 @@ M.handlers.virtual_text = {
|
|||||||
|
|
||||||
local ns = M.get_namespace(namespace)
|
local ns = M.get_namespace(namespace)
|
||||||
if not ns.user_data.virt_text_ns then
|
if not ns.user_data.virt_text_ns then
|
||||||
ns.user_data.virt_text_ns = vim.api.nvim_create_namespace("")
|
ns.user_data.virt_text_ns = vim.api.nvim_create_namespace('')
|
||||||
end
|
end
|
||||||
|
|
||||||
local virt_text_ns = ns.user_data.virt_text_ns
|
local virt_text_ns = ns.user_data.virt_text_ns
|
||||||
@@ -978,7 +985,7 @@ M.handlers.virtual_text = {
|
|||||||
|
|
||||||
if virt_texts then
|
if virt_texts then
|
||||||
vim.api.nvim_buf_set_extmark(bufnr, virt_text_ns, line, 0, {
|
vim.api.nvim_buf_set_extmark(bufnr, virt_text_ns, line, 0, {
|
||||||
hl_mode = "combine",
|
hl_mode = 'combine',
|
||||||
virt_text = virt_texts,
|
virt_text = virt_texts,
|
||||||
})
|
})
|
||||||
end
|
end
|
||||||
@@ -1006,11 +1013,11 @@ function M._get_virt_text_chunks(line_diags, opts)
|
|||||||
end
|
end
|
||||||
|
|
||||||
opts = opts or {}
|
opts = opts or {}
|
||||||
local prefix = opts.prefix or "■"
|
local prefix = opts.prefix or '■'
|
||||||
local spacing = opts.spacing or 4
|
local spacing = opts.spacing or 4
|
||||||
|
|
||||||
-- Create a little more space between virtual text and contents
|
-- Create a little more space between virtual text and contents
|
||||||
local virt_texts = {{string.rep(" ", spacing)}}
|
local virt_texts = { { string.rep(' ', spacing) } }
|
||||||
|
|
||||||
for i = 1, #line_diags - 1 do
|
for i = 1, #line_diags - 1 do
|
||||||
table.insert(virt_texts, { prefix, virtual_text_highlight_map[line_diags[i].severity] })
|
table.insert(virt_texts, { prefix, virtual_text_highlight_map[line_diags[i].severity] })
|
||||||
@@ -1020,13 +1027,10 @@ function M._get_virt_text_chunks(line_diags, opts)
|
|||||||
-- TODO(tjdevries): Allow different servers to be shown first somehow?
|
-- TODO(tjdevries): Allow different servers to be shown first somehow?
|
||||||
-- TODO(tjdevries): Display server name associated with these?
|
-- TODO(tjdevries): Display server name associated with these?
|
||||||
if last.message then
|
if last.message then
|
||||||
table.insert(
|
table.insert(virt_texts, {
|
||||||
virt_texts,
|
string.format('%s %s', prefix, last.message:gsub('\r', ''):gsub('\n', ' ')),
|
||||||
{
|
virtual_text_highlight_map[last.severity],
|
||||||
string.format("%s %s", prefix, last.message:gsub("\r", ""):gsub("\n", " ")),
|
})
|
||||||
virtual_text_highlight_map[last.severity]
|
|
||||||
}
|
|
||||||
)
|
|
||||||
|
|
||||||
return virt_texts
|
return virt_texts
|
||||||
end
|
end
|
||||||
@@ -1066,10 +1070,10 @@ end
|
|||||||
---@param bufnr number|nil Buffer number, or 0 for current buffer. When
|
---@param bufnr number|nil Buffer number, or 0 for current buffer. When
|
||||||
--- omitted, hide diagnostics in all buffers.
|
--- omitted, hide diagnostics in all buffers.
|
||||||
function M.hide(namespace, bufnr)
|
function M.hide(namespace, bufnr)
|
||||||
vim.validate {
|
vim.validate({
|
||||||
namespace = { namespace, 'n', true },
|
namespace = { namespace, 'n', true },
|
||||||
bufnr = { bufnr, 'n', true },
|
bufnr = { bufnr, 'n', true },
|
||||||
}
|
})
|
||||||
|
|
||||||
local buffers = bufnr and { get_bufnr(bufnr) } or vim.tbl_keys(diagnostic_cache)
|
local buffers = bufnr and { get_bufnr(bufnr) } or vim.tbl_keys(diagnostic_cache)
|
||||||
for _, iter_bufnr in ipairs(buffers) do
|
for _, iter_bufnr in ipairs(buffers) do
|
||||||
@@ -1098,19 +1102,21 @@ end
|
|||||||
--- or {bufnr} is nil.
|
--- or {bufnr} is nil.
|
||||||
---@param opts table|nil Display options. See |vim.diagnostic.config()|.
|
---@param opts table|nil Display options. See |vim.diagnostic.config()|.
|
||||||
function M.show(namespace, bufnr, diagnostics, opts)
|
function M.show(namespace, bufnr, diagnostics, opts)
|
||||||
vim.validate {
|
vim.validate({
|
||||||
namespace = { namespace, 'n', true },
|
namespace = { namespace, 'n', true },
|
||||||
bufnr = { bufnr, 'n', true },
|
bufnr = { bufnr, 'n', true },
|
||||||
diagnostics = {
|
diagnostics = {
|
||||||
diagnostics,
|
diagnostics,
|
||||||
function(v) return v == nil or vim.tbl_islist(v) end,
|
function(v)
|
||||||
"a list of diagnostics",
|
return v == nil or vim.tbl_islist(v)
|
||||||
|
end,
|
||||||
|
'a list of diagnostics',
|
||||||
},
|
},
|
||||||
opts = { opts, 't', true },
|
opts = { opts, 't', true },
|
||||||
}
|
})
|
||||||
|
|
||||||
if not bufnr or not namespace then
|
if not bufnr or not namespace then
|
||||||
assert(not diagnostics, "Cannot show diagnostics without a buffer and namespace")
|
assert(not diagnostics, 'Cannot show diagnostics without a buffer and namespace')
|
||||||
if not bufnr then
|
if not bufnr then
|
||||||
for iter_bufnr in pairs(diagnostic_cache) do
|
for iter_bufnr in pairs(diagnostic_cache) do
|
||||||
M.show(namespace, iter_bufnr, nil, opts)
|
M.show(namespace, iter_bufnr, nil, opts)
|
||||||
@@ -1150,10 +1156,14 @@ function M.show(namespace, bufnr, diagnostics, opts)
|
|||||||
end
|
end
|
||||||
|
|
||||||
if vim.F.if_nil(opts.severity_sort, false) then
|
if vim.F.if_nil(opts.severity_sort, false) then
|
||||||
if type(opts.severity_sort) == "table" and opts.severity_sort.reverse then
|
if type(opts.severity_sort) == 'table' and opts.severity_sort.reverse then
|
||||||
table.sort(diagnostics, function(a, b) return a.severity < b.severity end)
|
table.sort(diagnostics, function(a, b)
|
||||||
|
return a.severity < b.severity
|
||||||
|
end)
|
||||||
else
|
else
|
||||||
table.sort(diagnostics, function(a, b) return a.severity > b.severity end)
|
table.sort(diagnostics, function(a, b)
|
||||||
|
return a.severity > b.severity
|
||||||
|
end)
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
@@ -1209,13 +1219,13 @@ end
|
|||||||
function M.open_float(opts, ...)
|
function M.open_float(opts, ...)
|
||||||
-- Support old (bufnr, opts) signature
|
-- Support old (bufnr, opts) signature
|
||||||
local bufnr
|
local bufnr
|
||||||
if opts == nil or type(opts) == "number" then
|
if opts == nil or type(opts) == 'number' then
|
||||||
bufnr = opts
|
bufnr = opts
|
||||||
opts = ...
|
opts = ...
|
||||||
else
|
else
|
||||||
vim.validate {
|
vim.validate({
|
||||||
opts = { opts, 't', true },
|
opts = { opts, 't', true },
|
||||||
}
|
})
|
||||||
end
|
end
|
||||||
|
|
||||||
opts = opts or {}
|
opts = opts or {}
|
||||||
@@ -1228,41 +1238,39 @@ function M.open_float(opts, ...)
|
|||||||
-- with its `opts` table which also includes "keyword" parameters. So we create a dedicated
|
-- with its `opts` table which also includes "keyword" parameters. So we create a dedicated
|
||||||
-- options table that inherits missing keys from the global configuration before resolving.
|
-- options table that inherits missing keys from the global configuration before resolving.
|
||||||
local t = global_diagnostic_options.float
|
local t = global_diagnostic_options.float
|
||||||
local float_opts = vim.tbl_extend("keep", opts, type(t) == "table" and t or {})
|
local float_opts = vim.tbl_extend('keep', opts, type(t) == 'table' and t or {})
|
||||||
opts = get_resolved_options({ float = float_opts }, nil, bufnr).float
|
opts = get_resolved_options({ float = float_opts }, nil, bufnr).float
|
||||||
end
|
end
|
||||||
|
|
||||||
local scope = ({l = "line", c = "cursor", b = "buffer"})[opts.scope] or opts.scope or "line"
|
local scope = ({ l = 'line', c = 'cursor', b = 'buffer' })[opts.scope] or opts.scope or 'line'
|
||||||
local lnum, col
|
local lnum, col
|
||||||
if scope == "line" or scope == "cursor" then
|
if scope == 'line' or scope == 'cursor' then
|
||||||
if not opts.pos then
|
if not opts.pos then
|
||||||
local pos = vim.api.nvim_win_get_cursor(0)
|
local pos = vim.api.nvim_win_get_cursor(0)
|
||||||
lnum = pos[1] - 1
|
lnum = pos[1] - 1
|
||||||
col = pos[2]
|
col = pos[2]
|
||||||
elseif type(opts.pos) == "number" then
|
elseif type(opts.pos) == 'number' then
|
||||||
lnum = opts.pos
|
lnum = opts.pos
|
||||||
elseif type(opts.pos) == "table" then
|
elseif type(opts.pos) == 'table' then
|
||||||
lnum, col = unpack(opts.pos)
|
lnum, col = unpack(opts.pos)
|
||||||
else
|
else
|
||||||
error("Invalid value for option 'pos'")
|
error("Invalid value for option 'pos'")
|
||||||
end
|
end
|
||||||
elseif scope ~= "buffer" then
|
elseif scope ~= 'buffer' then
|
||||||
error("Invalid value for option 'scope'")
|
error("Invalid value for option 'scope'")
|
||||||
end
|
end
|
||||||
|
|
||||||
local diagnostics = get_diagnostics(bufnr, opts, true)
|
local diagnostics = get_diagnostics(bufnr, opts, true)
|
||||||
|
|
||||||
if scope == "line" then
|
if scope == 'line' then
|
||||||
diagnostics = vim.tbl_filter(function(d)
|
diagnostics = vim.tbl_filter(function(d)
|
||||||
return d.lnum == lnum
|
return d.lnum == lnum
|
||||||
end, diagnostics)
|
end, diagnostics)
|
||||||
elseif scope == "cursor" then
|
elseif scope == 'cursor' then
|
||||||
-- LSP servers can send diagnostics with `end_col` past the length of the line
|
-- LSP servers can send diagnostics with `end_col` past the length of the line
|
||||||
local line_length = #vim.api.nvim_buf_get_lines(bufnr, lnum, lnum + 1, true)[1]
|
local line_length = #vim.api.nvim_buf_get_lines(bufnr, lnum, lnum + 1, true)[1]
|
||||||
diagnostics = vim.tbl_filter(function(d)
|
diagnostics = vim.tbl_filter(function(d)
|
||||||
return d.lnum == lnum
|
return d.lnum == lnum and math.min(d.col, line_length - 1) <= col and (d.end_col >= col or d.end_lnum > lnum)
|
||||||
and math.min(d.col, line_length - 1) <= col
|
|
||||||
and (d.end_col >= col or d.end_lnum > lnum)
|
|
||||||
end, diagnostics)
|
end, diagnostics)
|
||||||
end
|
end
|
||||||
|
|
||||||
@@ -1272,29 +1280,39 @@ function M.open_float(opts, ...)
|
|||||||
|
|
||||||
local severity_sort = vim.F.if_nil(opts.severity_sort, global_diagnostic_options.severity_sort)
|
local severity_sort = vim.F.if_nil(opts.severity_sort, global_diagnostic_options.severity_sort)
|
||||||
if severity_sort then
|
if severity_sort then
|
||||||
if type(severity_sort) == "table" and severity_sort.reverse then
|
if type(severity_sort) == 'table' and severity_sort.reverse then
|
||||||
table.sort(diagnostics, function(a, b) return a.severity > b.severity end)
|
table.sort(diagnostics, function(a, b)
|
||||||
|
return a.severity > b.severity
|
||||||
|
end)
|
||||||
else
|
else
|
||||||
table.sort(diagnostics, function(a, b) return a.severity < b.severity end)
|
table.sort(diagnostics, function(a, b)
|
||||||
|
return a.severity < b.severity
|
||||||
|
end)
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
local lines = {}
|
local lines = {}
|
||||||
local highlights = {}
|
local highlights = {}
|
||||||
local header = if_nil(opts.header, "Diagnostics:")
|
local header = if_nil(opts.header, 'Diagnostics:')
|
||||||
if header then
|
if header then
|
||||||
vim.validate { header = { header, function(v)
|
vim.validate({
|
||||||
return type(v) == "string" or type(v) == "table"
|
header = {
|
||||||
end, "'string' or 'table'" } }
|
header,
|
||||||
if type(header) == "table" then
|
function(v)
|
||||||
|
return type(v) == 'string' or type(v) == 'table'
|
||||||
|
end,
|
||||||
|
"'string' or 'table'",
|
||||||
|
},
|
||||||
|
})
|
||||||
|
if type(header) == 'table' then
|
||||||
-- Don't insert any lines for an empty string
|
-- Don't insert any lines for an empty string
|
||||||
if string.len(if_nil(header[1], "")) > 0 then
|
if string.len(if_nil(header[1], '')) > 0 then
|
||||||
table.insert(lines, header[1])
|
table.insert(lines, header[1])
|
||||||
table.insert(highlights, {0, header[2] or "Bold"})
|
table.insert(highlights, { 0, header[2] or 'Bold' })
|
||||||
end
|
end
|
||||||
elseif #header > 0 then
|
elseif #header > 0 then
|
||||||
table.insert(lines, header)
|
table.insert(lines, header)
|
||||||
table.insert(highlights, {0, "Bold"})
|
table.insert(highlights, { 0, 'Bold' })
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
@@ -1302,30 +1320,36 @@ function M.open_float(opts, ...)
|
|||||||
diagnostics = reformat_diagnostics(opts.format, diagnostics)
|
diagnostics = reformat_diagnostics(opts.format, diagnostics)
|
||||||
end
|
end
|
||||||
|
|
||||||
if opts.source and (opts.source ~= "if_many" or count_sources(bufnr) > 1) then
|
if opts.source and (opts.source ~= 'if_many' or count_sources(bufnr) > 1) then
|
||||||
diagnostics = prefix_source(diagnostics)
|
diagnostics = prefix_source(diagnostics)
|
||||||
end
|
end
|
||||||
|
|
||||||
local prefix_opt = if_nil(opts.prefix, (scope == "cursor" and #diagnostics <= 1) and "" or function(_, i)
|
local prefix_opt = if_nil(opts.prefix, (scope == 'cursor' and #diagnostics <= 1) and '' or function(_, i)
|
||||||
return string.format("%d. ", i)
|
return string.format('%d. ', i)
|
||||||
end)
|
end)
|
||||||
|
|
||||||
local prefix, prefix_hl_group
|
local prefix, prefix_hl_group
|
||||||
if prefix_opt then
|
if prefix_opt then
|
||||||
vim.validate { prefix = { prefix_opt, function(v)
|
vim.validate({
|
||||||
return type(v) == "string" or type(v) == "table" or type(v) == "function"
|
prefix = {
|
||||||
end, "'string' or 'table' or 'function'" } }
|
prefix_opt,
|
||||||
if type(prefix_opt) == "string" then
|
function(v)
|
||||||
prefix, prefix_hl_group = prefix_opt, "NormalFloat"
|
return type(v) == 'string' or type(v) == 'table' or type(v) == 'function'
|
||||||
elseif type(prefix_opt) == "table" then
|
end,
|
||||||
prefix, prefix_hl_group = prefix_opt[1] or "", prefix_opt[2] or "NormalFloat"
|
"'string' or 'table' or 'function'",
|
||||||
|
},
|
||||||
|
})
|
||||||
|
if type(prefix_opt) == 'string' then
|
||||||
|
prefix, prefix_hl_group = prefix_opt, 'NormalFloat'
|
||||||
|
elseif type(prefix_opt) == 'table' then
|
||||||
|
prefix, prefix_hl_group = prefix_opt[1] or '', prefix_opt[2] or 'NormalFloat'
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
for i, diagnostic in ipairs(diagnostics) do
|
for i, diagnostic in ipairs(diagnostics) do
|
||||||
if prefix_opt and type(prefix_opt) == "function" then
|
if prefix_opt and type(prefix_opt) == 'function' then
|
||||||
prefix, prefix_hl_group = prefix_opt(diagnostic, i, #diagnostics)
|
prefix, prefix_hl_group = prefix_opt(diagnostic, i, #diagnostics)
|
||||||
prefix, prefix_hl_group = prefix or "", prefix_hl_group or "NormalFloat"
|
prefix, prefix_hl_group = prefix or '', prefix_hl_group or 'NormalFloat'
|
||||||
end
|
end
|
||||||
local hiname = floating_highlight_map[diagnostic.severity]
|
local hiname = floating_highlight_map[diagnostic.severity]
|
||||||
local message_lines = vim.split(diagnostic.message, '\n')
|
local message_lines = vim.split(diagnostic.message, '\n')
|
||||||
@@ -1365,10 +1389,10 @@ end
|
|||||||
---@param bufnr number|nil Remove diagnostics for the given buffer. When omitted,
|
---@param bufnr number|nil Remove diagnostics for the given buffer. When omitted,
|
||||||
--- diagnostics are removed for all buffers.
|
--- diagnostics are removed for all buffers.
|
||||||
function M.reset(namespace, bufnr)
|
function M.reset(namespace, bufnr)
|
||||||
vim.validate {
|
vim.validate({
|
||||||
namespace = { namespace, 'n', true },
|
namespace = { namespace, 'n', true },
|
||||||
bufnr = { bufnr, 'n', true },
|
bufnr = { bufnr, 'n', true },
|
||||||
}
|
})
|
||||||
|
|
||||||
local buffers = bufnr and { get_bufnr(bufnr) } or vim.tbl_keys(diagnostic_cache)
|
local buffers = bufnr and { get_bufnr(bufnr) } or vim.tbl_keys(diagnostic_cache)
|
||||||
for _, iter_bufnr in ipairs(buffers) do
|
for _, iter_bufnr in ipairs(buffers) do
|
||||||
@@ -1378,7 +1402,7 @@ function M.reset(namespace, bufnr)
|
|||||||
M.hide(iter_namespace, iter_bufnr)
|
M.hide(iter_namespace, iter_bufnr)
|
||||||
end
|
end
|
||||||
|
|
||||||
vim.api.nvim_exec_autocmds("DiagnosticChanged", {
|
vim.api.nvim_exec_autocmds('DiagnosticChanged', {
|
||||||
modeline = false,
|
modeline = false,
|
||||||
buffer = iter_bufnr,
|
buffer = iter_bufnr,
|
||||||
})
|
})
|
||||||
@@ -1414,7 +1438,7 @@ end
|
|||||||
--- omitted, disable diagnostics in all buffers.
|
--- omitted, disable diagnostics in all buffers.
|
||||||
---@param namespace number|nil Only disable diagnostics for the given namespace.
|
---@param namespace number|nil Only disable diagnostics for the given namespace.
|
||||||
function M.disable(bufnr, namespace)
|
function M.disable(bufnr, namespace)
|
||||||
vim.validate { bufnr = {bufnr, 'n', true}, namespace = {namespace, 'n', true} }
|
vim.validate({ bufnr = { bufnr, 'n', true }, namespace = { namespace, 'n', true } })
|
||||||
if bufnr == nil then
|
if bufnr == nil then
|
||||||
if namespace == nil then
|
if namespace == nil then
|
||||||
-- Disable everything (including as yet non-existing buffers and
|
-- Disable everything (including as yet non-existing buffers and
|
||||||
@@ -1422,7 +1446,9 @@ function M.disable(bufnr, namespace)
|
|||||||
-- its metatable to always return true. This metatable is removed
|
-- its metatable to always return true. This metatable is removed
|
||||||
-- in enable()
|
-- in enable()
|
||||||
diagnostic_disabled = setmetatable({}, {
|
diagnostic_disabled = setmetatable({}, {
|
||||||
__index = function() return true end,
|
__index = function()
|
||||||
|
return true
|
||||||
|
end,
|
||||||
})
|
})
|
||||||
else
|
else
|
||||||
local ns = M.get_namespace(namespace)
|
local ns = M.get_namespace(namespace)
|
||||||
@@ -1433,7 +1459,7 @@ function M.disable(bufnr, namespace)
|
|||||||
if namespace == nil then
|
if namespace == nil then
|
||||||
diagnostic_disabled[bufnr] = true
|
diagnostic_disabled[bufnr] = true
|
||||||
else
|
else
|
||||||
if type(diagnostic_disabled[bufnr]) ~= "table" then
|
if type(diagnostic_disabled[bufnr]) ~= 'table' then
|
||||||
diagnostic_disabled[bufnr] = {}
|
diagnostic_disabled[bufnr] = {}
|
||||||
end
|
end
|
||||||
diagnostic_disabled[bufnr][namespace] = true
|
diagnostic_disabled[bufnr][namespace] = true
|
||||||
@@ -1449,7 +1475,7 @@ end
|
|||||||
--- omitted, enable diagnostics in all buffers.
|
--- omitted, enable diagnostics in all buffers.
|
||||||
---@param namespace number|nil Only enable diagnostics for the given namespace.
|
---@param namespace number|nil Only enable diagnostics for the given namespace.
|
||||||
function M.enable(bufnr, namespace)
|
function M.enable(bufnr, namespace)
|
||||||
vim.validate { bufnr = {bufnr, 'n', true}, namespace = {namespace, 'n', true} }
|
vim.validate({ bufnr = { bufnr, 'n', true }, namespace = { namespace, 'n', true } })
|
||||||
if bufnr == nil then
|
if bufnr == nil then
|
||||||
if namespace == nil then
|
if namespace == nil then
|
||||||
-- Enable everything by setting diagnostic_disabled to an empty table
|
-- Enable everything by setting diagnostic_disabled to an empty table
|
||||||
@@ -1463,7 +1489,7 @@ function M.enable(bufnr, namespace)
|
|||||||
if namespace == nil then
|
if namespace == nil then
|
||||||
diagnostic_disabled[bufnr] = nil
|
diagnostic_disabled[bufnr] = nil
|
||||||
else
|
else
|
||||||
if type(diagnostic_disabled[bufnr]) ~= "table" then
|
if type(diagnostic_disabled[bufnr]) ~= 'table' then
|
||||||
return
|
return
|
||||||
end
|
end
|
||||||
diagnostic_disabled[bufnr][namespace] = nil
|
diagnostic_disabled[bufnr][namespace] = nil
|
||||||
@@ -1500,13 +1526,13 @@ end
|
|||||||
--- ERROR.
|
--- ERROR.
|
||||||
---@return diagnostic |diagnostic-structure| or `nil` if {pat} fails to match {str}.
|
---@return diagnostic |diagnostic-structure| or `nil` if {pat} fails to match {str}.
|
||||||
function M.match(str, pat, groups, severity_map, defaults)
|
function M.match(str, pat, groups, severity_map, defaults)
|
||||||
vim.validate {
|
vim.validate({
|
||||||
str = { str, 's' },
|
str = { str, 's' },
|
||||||
pat = { pat, 's' },
|
pat = { pat, 's' },
|
||||||
groups = { groups, 't' },
|
groups = { groups, 't' },
|
||||||
severity_map = { severity_map, 't', true },
|
severity_map = { severity_map, 't', true },
|
||||||
defaults = { defaults, 't', true },
|
defaults = { defaults, 't', true },
|
||||||
}
|
})
|
||||||
|
|
||||||
severity_map = severity_map or M.severity
|
severity_map = severity_map or M.severity
|
||||||
|
|
||||||
@@ -1518,15 +1544,15 @@ function M.match(str, pat, groups, severity_map, defaults)
|
|||||||
|
|
||||||
for i, match in ipairs(matches) do
|
for i, match in ipairs(matches) do
|
||||||
local field = groups[i]
|
local field = groups[i]
|
||||||
if field == "severity" then
|
if field == 'severity' then
|
||||||
match = severity_map[match]
|
match = severity_map[match]
|
||||||
elseif field == "lnum" or field == "end_lnum" or field == "col" or field == "end_col" then
|
elseif field == 'lnum' or field == 'end_lnum' or field == 'col' or field == 'end_col' then
|
||||||
match = assert(tonumber(match)) - 1
|
match = assert(tonumber(match)) - 1
|
||||||
end
|
end
|
||||||
diagnostic[field] = match
|
diagnostic[field] = match
|
||||||
end
|
end
|
||||||
|
|
||||||
diagnostic = vim.tbl_extend("keep", diagnostic, defaults or {})
|
diagnostic = vim.tbl_extend('keep', diagnostic, defaults or {})
|
||||||
diagnostic.severity = diagnostic.severity or M.severity.ERROR
|
diagnostic.severity = diagnostic.severity or M.severity.ERROR
|
||||||
diagnostic.col = diagnostic.col or 0
|
diagnostic.col = diagnostic.col or 0
|
||||||
diagnostic.end_lnum = diagnostic.end_lnum or diagnostic.lnum
|
diagnostic.end_lnum = diagnostic.end_lnum or diagnostic.lnum
|
||||||
@@ -1547,13 +1573,13 @@ local errlist_type_map = {
|
|||||||
---@param diagnostics table List of diagnostics |diagnostic-structure|.
|
---@param diagnostics table List of diagnostics |diagnostic-structure|.
|
||||||
---@return array of quickfix list items |setqflist-what|
|
---@return array of quickfix list items |setqflist-what|
|
||||||
function M.toqflist(diagnostics)
|
function M.toqflist(diagnostics)
|
||||||
vim.validate {
|
vim.validate({
|
||||||
diagnostics = {
|
diagnostics = {
|
||||||
diagnostics,
|
diagnostics,
|
||||||
vim.tbl_islist,
|
vim.tbl_islist,
|
||||||
"a list of diagnostics",
|
'a list of diagnostics',
|
||||||
},
|
},
|
||||||
}
|
})
|
||||||
|
|
||||||
local list = {}
|
local list = {}
|
||||||
for _, v in ipairs(diagnostics) do
|
for _, v in ipairs(diagnostics) do
|
||||||
@@ -1584,13 +1610,13 @@ end
|
|||||||
--- |getloclist()|.
|
--- |getloclist()|.
|
||||||
---@return array of diagnostics |diagnostic-structure|
|
---@return array of diagnostics |diagnostic-structure|
|
||||||
function M.fromqflist(list)
|
function M.fromqflist(list)
|
||||||
vim.validate {
|
vim.validate({
|
||||||
list = {
|
list = {
|
||||||
list,
|
list,
|
||||||
vim.tbl_islist,
|
vim.tbl_islist,
|
||||||
"a list of quickfix items",
|
'a list of quickfix items',
|
||||||
},
|
},
|
||||||
}
|
})
|
||||||
|
|
||||||
local diagnostics = {}
|
local diagnostics = {}
|
||||||
for _, item in ipairs(list) do
|
for _, item in ipairs(list) do
|
||||||
@@ -1599,7 +1625,7 @@ function M.fromqflist(list)
|
|||||||
local col = math.max(0, item.col - 1)
|
local col = math.max(0, item.col - 1)
|
||||||
local end_lnum = item.end_lnum > 0 and (item.end_lnum - 1) or lnum
|
local end_lnum = item.end_lnum > 0 and (item.end_lnum - 1) or lnum
|
||||||
local end_col = item.end_col > 0 and (item.end_col - 1) or col
|
local end_col = item.end_col > 0 and (item.end_col - 1) or col
|
||||||
local severity = item.type ~= "" and M.severity[item.type] or M.severity.ERROR
|
local severity = item.type ~= '' and M.severity[item.type] or M.severity.ERROR
|
||||||
table.insert(diagnostics, {
|
table.insert(diagnostics, {
|
||||||
bufnr = item.bufnr,
|
bufnr = item.bufnr,
|
||||||
lnum = lnum,
|
lnum = lnum,
|
||||||
|
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)
|
local lines = vim.api.nvim_buf_get_lines(bufnr, start_lnum - 1, end_lnum, false)
|
||||||
opts = opts or {}
|
opts = opts or {}
|
||||||
return opts.concat and (table.concat(lines) or "") or lines
|
return opts.concat and (table.concat(lines) or '') or lines
|
||||||
end
|
end
|
||||||
|
|
||||||
---@private
|
---@private
|
||||||
@@ -35,23 +35,23 @@ function M.bindzone(path, bufnr) end
|
|||||||
|
|
||||||
function M.btm(bufnr)
|
function M.btm(bufnr)
|
||||||
if vim.g.dosbatch_syntax_for_btm and vim.g.dosbatch_syntax_for_btm ~= 0 then
|
if vim.g.dosbatch_syntax_for_btm and vim.g.dosbatch_syntax_for_btm ~= 0 then
|
||||||
vim.bo[bufnr].filetype = "dosbatch"
|
vim.bo[bufnr].filetype = 'dosbatch'
|
||||||
else
|
else
|
||||||
vim.bo[bufnr].filetype = "btm"
|
vim.bo[bufnr].filetype = 'btm'
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
-- Returns true if file content looks like RAPID
|
-- Returns true if file content looks like RAPID
|
||||||
local function is_rapid(bufnr, extension)
|
local function is_rapid(bufnr, extension)
|
||||||
if extension == "cfg" then
|
if extension == 'cfg' then
|
||||||
local line = getlines(bufnr, 1):lower()
|
local line = getlines(bufnr, 1):lower()
|
||||||
return findany(line, { "eio:cfg", "mmc:cfg", "moc:cfg", "proc:cfg", "sio:cfg", "sys:cfg" })
|
return findany(line, { 'eio:cfg', 'mmc:cfg', 'moc:cfg', 'proc:cfg', 'sio:cfg', 'sys:cfg' })
|
||||||
end
|
end
|
||||||
local first = "^%s*module%s+%S+%s*"
|
local first = '^%s*module%s+%S+%s*'
|
||||||
-- Called from mod, prg or sys functions
|
-- Called from mod, prg or sys functions
|
||||||
for _, line in ipairs(getlines(bufnr, 1, -1)) do
|
for _, line in ipairs(getlines(bufnr, 1, -1)) do
|
||||||
if not line:find("^%s*$") then
|
if not line:find('^%s*$') then
|
||||||
return findany(line:lower(), { "^%s*%%%%%%", first .. "(", first .. "$" })
|
return findany(line:lower(), { '^%s*%%%%%%', first .. '(', first .. '$' })
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
-- Only found blank lines
|
-- Only found blank lines
|
||||||
@@ -61,10 +61,10 @@ end
|
|||||||
function M.cfg(bufnr)
|
function M.cfg(bufnr)
|
||||||
if vim.g.filetype_cfg then
|
if vim.g.filetype_cfg then
|
||||||
vim.bo[bufnr].filetype = vim.g.filetype_cfg
|
vim.bo[bufnr].filetype = vim.g.filetype_cfg
|
||||||
elseif is_rapid(bufnr, "cfg") then
|
elseif is_rapid(bufnr, 'cfg') then
|
||||||
vim.bo[bufnr].filetype = "rapid"
|
vim.bo[bufnr].filetype = 'rapid'
|
||||||
else
|
else
|
||||||
vim.bo[bufnr].filetype = "cfg"
|
vim.bo[bufnr].filetype = 'cfg'
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
@@ -85,23 +85,23 @@ function M.e(path, bufnr) end
|
|||||||
-- If not found, assume SGML.
|
-- If not found, assume SGML.
|
||||||
function M.ent(bufnr)
|
function M.ent(bufnr)
|
||||||
for _, line in ipairs(getlines(bufnr, 1, 5)) do
|
for _, line in ipairs(getlines(bufnr, 1, 5)) do
|
||||||
if line:find("^%s*[#{]") then
|
if line:find('^%s*[#{]') then
|
||||||
vim.bo[bufnr].filetype = "cl"
|
vim.bo[bufnr].filetype = 'cl'
|
||||||
return
|
return
|
||||||
elseif not line:find("^%s*$") then
|
elseif not line:find('^%s*$') then
|
||||||
-- Not a blank line, not a comment, and not a block start,
|
-- Not a blank line, not a comment, and not a block start,
|
||||||
-- so doesn't look like valid cl code.
|
-- so doesn't look like valid cl code.
|
||||||
break
|
break
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
vim.bo[bufnr].filetype = "dtd"
|
vim.bo[bufnr].filetype = 'dtd'
|
||||||
end
|
end
|
||||||
|
|
||||||
function M.euphoria(bufnr)
|
function M.euphoria(bufnr)
|
||||||
if vim.g.filetype_euphoria then
|
if vim.g.filetype_euphoria then
|
||||||
vim.bo[bufnr].filetype = vim.g.filetype_euphoria
|
vim.bo[bufnr].filetype = vim.g.filetype_euphoria
|
||||||
else
|
else
|
||||||
vim.bo[bufnr].filetype = "euphoria3"
|
vim.bo[bufnr].filetype = 'euphoria3'
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
@@ -111,12 +111,12 @@ function M.ex(bufnr)
|
|||||||
else
|
else
|
||||||
for _, line in ipairs(getlines(bufnr, 1, 100)) do
|
for _, line in ipairs(getlines(bufnr, 1, 100)) do
|
||||||
-- TODO: in the Vim regex, \> is used to match the end of the word, can this be omitted?
|
-- TODO: in the Vim regex, \> is used to match the end of the word, can this be omitted?
|
||||||
if findany(line, { "^%-%-", "^ifdef", "^include" }) then
|
if findany(line, { '^%-%-', '^ifdef', '^include' }) then
|
||||||
vim.bo[bufnr].filetype = "euphoria3"
|
vim.bo[bufnr].filetype = 'euphoria3'
|
||||||
return
|
return
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
vim.bo[bufnr].filetype = "elixir"
|
vim.bo[bufnr].filetype = 'elixir'
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
@@ -126,10 +126,10 @@ end
|
|||||||
function M.foam(bufnr)
|
function M.foam(bufnr)
|
||||||
local foam_file = false
|
local foam_file = false
|
||||||
for _, line in ipairs(getlines(bufnr, 1, 15)) do
|
for _, line in ipairs(getlines(bufnr, 1, 15)) do
|
||||||
if line:find("^FoamFile") then
|
if line:find('^FoamFile') then
|
||||||
foam_file = true
|
foam_file = true
|
||||||
elseif foam_file and line:find("^%s*object") then
|
elseif foam_file and line:find('^%s*object') then
|
||||||
vim.bo[bufnr].filetype = "foam"
|
vim.bo[bufnr].filetype = 'foam'
|
||||||
return
|
return
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
@@ -141,10 +141,10 @@ function M.frm(bufnr)
|
|||||||
else
|
else
|
||||||
-- Always ignore case
|
-- Always ignore case
|
||||||
local lines = getlines(bufnr, 1, 5, { concat = true }):lower()
|
local lines = getlines(bufnr, 1, 5, { concat = true }):lower()
|
||||||
if findany(lines, { "vb_name", "begin vb%.form", "begin vb%.mdiform" }) then
|
if findany(lines, { 'vb_name', 'begin vb%.form', 'begin vb%.mdiform' }) then
|
||||||
vim.bo[bufnr].filetype = "vb"
|
vim.bo[bufnr].filetype = 'vb'
|
||||||
else
|
else
|
||||||
vim.bo[bufnr].filetype = "form"
|
vim.bo[bufnr].filetype = 'form'
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
@@ -153,21 +153,21 @@ function M.fs(path, bufnr) end
|
|||||||
|
|
||||||
function M.header(bufnr)
|
function M.header(bufnr)
|
||||||
for _, line in ipairs(getlines(bufnr, 1, 200)) do
|
for _, line in ipairs(getlines(bufnr, 1, 200)) do
|
||||||
if findany(line, { "^@interface", "^@end", "^@class" }) then
|
if findany(line, { '^@interface', '^@end', '^@class' }) then
|
||||||
if vim.g.c_syntax_for_h then
|
if vim.g.c_syntax_for_h then
|
||||||
vim.bo[bufnr].filetype = "objc"
|
vim.bo[bufnr].filetype = 'objc'
|
||||||
else
|
else
|
||||||
vim.bo[bufnr].filetype = "objcpp"
|
vim.bo[bufnr].filetype = 'objcpp'
|
||||||
end
|
end
|
||||||
return
|
return
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
if vim.g.c_syntax_for_h then
|
if vim.g.c_syntax_for_h then
|
||||||
vim.bo[bufnr].filetype = "c"
|
vim.bo[bufnr].filetype = 'c'
|
||||||
elseif vim.g.ch_syntax_for_h then
|
elseif vim.g.ch_syntax_for_h then
|
||||||
vim.bo[bufnr].filetype = "ch"
|
vim.bo[bufnr].filetype = 'ch'
|
||||||
else
|
else
|
||||||
vim.bo[bufnr].filetype = "cpp"
|
vim.bo[bufnr].filetype = 'cpp'
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
@@ -176,22 +176,22 @@ function M.idl(bufnr)
|
|||||||
-- Always ignore case
|
-- Always ignore case
|
||||||
line = line:lower()
|
line = line:lower()
|
||||||
if findany(line, { '^%s*import%s+"unknwn"%.idl', '^%s*import%s+"objidl"%.idl' }) then
|
if findany(line, { '^%s*import%s+"unknwn"%.idl', '^%s*import%s+"objidl"%.idl' }) then
|
||||||
vim.bo[bufnr].filetype = "msidl"
|
vim.bo[bufnr].filetype = 'msidl'
|
||||||
return
|
return
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
vim.bo[bufnr].filetype = "idl"
|
vim.bo[bufnr].filetype = 'idl'
|
||||||
end
|
end
|
||||||
|
|
||||||
function M.inc(path, bufnr) end
|
function M.inc(path, bufnr) end
|
||||||
|
|
||||||
function M.inp(bufnr)
|
function M.inp(bufnr)
|
||||||
if getlines(bufnr, 1):find("^%*") then
|
if getlines(bufnr, 1):find('^%*') then
|
||||||
vim.bo[bufnr].filetype = "abaqus"
|
vim.bo[bufnr].filetype = 'abaqus'
|
||||||
else
|
else
|
||||||
for _, line in ipairs(getlines(bufnr, 1, 500)) do
|
for _, line in ipairs(getlines(bufnr, 1, 500)) do
|
||||||
if line:lower():find("^header surface data") then
|
if line:lower():find('^header surface data') then
|
||||||
vim.bo[bufnr].filetype = "trasys"
|
vim.bo[bufnr].filetype = 'trasys'
|
||||||
return
|
return
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
@@ -208,32 +208,32 @@ function M.m(path, bufnr) end
|
|||||||
-- MS message text files use ';', Sendmail files use '#' or 'dnl'
|
-- MS message text files use ';', Sendmail files use '#' or 'dnl'
|
||||||
function M.mc(bufnr)
|
function M.mc(bufnr)
|
||||||
for _, line in ipairs(getlines(bufnr, 1, 20)) do
|
for _, line in ipairs(getlines(bufnr, 1, 20)) do
|
||||||
if findany(line:lower(), { "^%s*#", "^%s*dnl" }) then
|
if findany(line:lower(), { '^%s*#', '^%s*dnl' }) then
|
||||||
-- Sendmail .mc file
|
-- Sendmail .mc file
|
||||||
vim.bo[bufnr].filetype = "m4"
|
vim.bo[bufnr].filetype = 'm4'
|
||||||
return
|
return
|
||||||
elseif line:find("^%s*;") then
|
elseif line:find('^%s*;') then
|
||||||
vim.bo[bufnr].filetype = "msmessages"
|
vim.bo[bufnr].filetype = 'msmessages'
|
||||||
return
|
return
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
-- Default: Sendmail .mc file
|
-- Default: Sendmail .mc file
|
||||||
vim.bo[bufnr].filetype = "m4"
|
vim.bo[bufnr].filetype = 'm4'
|
||||||
end
|
end
|
||||||
|
|
||||||
function M.mm(path, bufnr) end
|
function M.mm(path, bufnr) end
|
||||||
|
|
||||||
function M.mms(bufnr)
|
function M.mms(bufnr)
|
||||||
for _, line in ipairs(getlines(bufnr, 1, 20)) do
|
for _, line in ipairs(getlines(bufnr, 1, 20)) do
|
||||||
if findany(line, { "^%s*%%", "^%s*//", "^%*" }) then
|
if findany(line, { '^%s*%%', '^%s*//', '^%*' }) then
|
||||||
vim.bo[bufnr].filetype = "mmix"
|
vim.bo[bufnr].filetype = 'mmix'
|
||||||
return
|
return
|
||||||
elseif line:find("^%s*#") then
|
elseif line:find('^%s*#') then
|
||||||
vim.bo[bufnr].filetype = "make"
|
vim.bo[bufnr].filetype = 'make'
|
||||||
return
|
return
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
vim.bo[bufnr].filetype = "mmix"
|
vim.bo[bufnr].filetype = 'mmix'
|
||||||
end
|
end
|
||||||
|
|
||||||
function M.mod(path, bufnr) end
|
function M.mod(path, bufnr) end
|
||||||
@@ -242,8 +242,8 @@ function M.mod(path, bufnr) end
|
|||||||
-- that case it is probably an nroff file: 'filetype' is set and 1 is returned.
|
-- that case it is probably an nroff file: 'filetype' is set and 1 is returned.
|
||||||
function M.nroff(bufnr)
|
function M.nroff(bufnr)
|
||||||
for _, line in ipairs(getlines(bufnr, 1, 5)) do
|
for _, line in ipairs(getlines(bufnr, 1, 5)) do
|
||||||
if line:find("^%.") then
|
if line:find('^%.') then
|
||||||
vim.bo[bufnr].filetype = "nroff"
|
vim.bo[bufnr].filetype = 'nroff'
|
||||||
return 1
|
return 1
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
@@ -264,10 +264,10 @@ function M.progress_cweb(bufnr)
|
|||||||
if vim.g.filetype_w then
|
if vim.g.filetype_w then
|
||||||
vim.bo[bufnr].filetype = vim.g.filetype_w
|
vim.bo[bufnr].filetype = vim.g.filetype_w
|
||||||
else
|
else
|
||||||
if getlines(bufnr, 1):find("^&ANALYZE") or getlines(bufnr, 3):find("^&GLOBAL%-DEFINE") then
|
if getlines(bufnr, 1):find('^&ANALYZE') or getlines(bufnr, 3):find('^&GLOBAL%-DEFINE') then
|
||||||
vim.bo[bufnr].filetype = "progress"
|
vim.bo[bufnr].filetype = 'progress'
|
||||||
else
|
else
|
||||||
vim.bo[bufnr].filetype = "cweb"
|
vim.bo[bufnr].filetype = 'cweb'
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
@@ -280,20 +280,20 @@ function M.r(bufnr)
|
|||||||
local lines = getlines(bufnr, 1, 50)
|
local lines = getlines(bufnr, 1, 50)
|
||||||
-- TODO: \< / \> which match the beginning / end of a word
|
-- TODO: \< / \> which match the beginning / end of a word
|
||||||
-- Rebol is easy to recognize, check for that first
|
-- Rebol is easy to recognize, check for that first
|
||||||
if table.concat(lines):lower():find("rebol") then
|
if table.concat(lines):lower():find('rebol') then
|
||||||
vim.bo[bufnr].filetype = "rebol"
|
vim.bo[bufnr].filetype = 'rebol'
|
||||||
return
|
return
|
||||||
end
|
end
|
||||||
|
|
||||||
for _, line in ipairs(lines) do
|
for _, line in ipairs(lines) do
|
||||||
-- R has # comments
|
-- R has # comments
|
||||||
if line:find("^%s*#") then
|
if line:find('^%s*#') then
|
||||||
vim.bo[bufnr].filetype = "r"
|
vim.bo[bufnr].filetype = 'r'
|
||||||
return
|
return
|
||||||
end
|
end
|
||||||
-- Rexx has /* comments */
|
-- Rexx has /* comments */
|
||||||
if line:find("^%s*/%*") then
|
if line:find('^%s*/%*') then
|
||||||
vim.bo[bufnr].filetype = "rexx"
|
vim.bo[bufnr].filetype = 'rexx'
|
||||||
return
|
return
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
@@ -303,14 +303,14 @@ function M.r(bufnr)
|
|||||||
vim.bo[bufnr].filetype = vim.g.filetype_r
|
vim.bo[bufnr].filetype = vim.g.filetype_r
|
||||||
else
|
else
|
||||||
-- Rexx used to be the default, but R appears to be much more popular.
|
-- Rexx used to be the default, but R appears to be much more popular.
|
||||||
vim.bo[bufnr].filetype = "r"
|
vim.bo[bufnr].filetype = 'r'
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
function M.redif(bufnr)
|
function M.redif(bufnr)
|
||||||
for _, line in ipairs(getlines(bufnr, 1, 5)) do
|
for _, line in ipairs(getlines(bufnr, 1, 5)) do
|
||||||
if line:lower():find("^template%-type:") then
|
if line:lower():find('^template%-type:') then
|
||||||
vim.bo[bufnr].filetype = "redif"
|
vim.bo[bufnr].filetype = 'redif'
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
@@ -321,24 +321,29 @@ function M.rules(path, bufnr) end
|
|||||||
-- detection between scala and SuperCollider
|
-- detection between scala and SuperCollider
|
||||||
function M.sc(bufnr)
|
function M.sc(bufnr)
|
||||||
for _, line in ipairs(getlines(bufnr, 1, 25)) do
|
for _, line in ipairs(getlines(bufnr, 1, 25)) do
|
||||||
if findany(line, { "[A-Za-z0-9]*%s:%s[A-Za-z0-9]", "var%s<", "classvar%s<", "%^this.*", "|%w*|", "%+%s%w*%s{", "%*ar%s" }) then
|
if
|
||||||
vim.bo[bufnr].filetype = "supercollider"
|
findany(
|
||||||
|
line,
|
||||||
|
{ '[A-Za-z0-9]*%s:%s[A-Za-z0-9]', 'var%s<', 'classvar%s<', '%^this.*', '|%w*|', '%+%s%w*%s{', '%*ar%s' }
|
||||||
|
)
|
||||||
|
then
|
||||||
|
vim.bo[bufnr].filetype = 'supercollider'
|
||||||
return
|
return
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
vim.bo[bufnr].filetype = "scala"
|
vim.bo[bufnr].filetype = 'scala'
|
||||||
end
|
end
|
||||||
|
|
||||||
-- This function checks the first line of file extension "scd" to resolve
|
-- This function checks the first line of file extension "scd" to resolve
|
||||||
-- detection between scdoc and SuperCollider
|
-- detection between scdoc and SuperCollider
|
||||||
function M.scd(bufnr)
|
function M.scd(bufnr)
|
||||||
local first = "^%S+%(%d[0-9A-Za-z]*%)"
|
local first = '^%S+%(%d[0-9A-Za-z]*%)'
|
||||||
local opt = [[%s+"[^"]*"]]
|
local opt = [[%s+"[^"]*"]]
|
||||||
local line = getlines(bufnr, 1)
|
local line = getlines(bufnr, 1)
|
||||||
if findany(line, { first .. "$", first .. opt .. "$", first .. opt .. opt .. "$" }) then
|
if findany(line, { first .. '$', first .. opt .. '$', first .. opt .. opt .. '$' }) then
|
||||||
vim.bo[bufnr].filetype = "scdoc"
|
vim.bo[bufnr].filetype = 'scdoc'
|
||||||
else
|
else
|
||||||
vim.bo[bufnr].filetype = "supercollider"
|
vim.bo[bufnr].filetype = 'supercollider'
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
@@ -350,7 +355,7 @@ function M.sql(bufnr)
|
|||||||
if vim.g.filetype_sql then
|
if vim.g.filetype_sql then
|
||||||
vim.bo[bufnr].filetype = vim.g.filetype_sql
|
vim.bo[bufnr].filetype = vim.g.filetype_sql
|
||||||
else
|
else
|
||||||
vim.bo[bufnr].filetype = "sql"
|
vim.bo[bufnr].filetype = 'sql'
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
@@ -365,46 +370,46 @@ function M.tf(bufnr)
|
|||||||
for _, line in ipairs(getlines(bufnr, 1, -1)) do
|
for _, line in ipairs(getlines(bufnr, 1, -1)) do
|
||||||
-- Assume terraform file on a non-empty line (not whitespace-only)
|
-- Assume terraform file on a non-empty line (not whitespace-only)
|
||||||
-- and when the first non-whitespace character is not a ; or /
|
-- and when the first non-whitespace character is not a ; or /
|
||||||
if not line:find("^%s*$") and not line:find("^%s*[;/]") then
|
if not line:find('^%s*$') and not line:find('^%s*[;/]') then
|
||||||
vim.bo[bufnr].filetype = "terraform"
|
vim.bo[bufnr].filetype = 'terraform'
|
||||||
return
|
return
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
vim.bo[bufnr].filetype = "tf"
|
vim.bo[bufnr].filetype = 'tf'
|
||||||
end
|
end
|
||||||
|
|
||||||
function M.xml(bufnr)
|
function M.xml(bufnr)
|
||||||
for _, line in ipairs(getlines(bufnr, 1, 100)) do
|
for _, line in ipairs(getlines(bufnr, 1, 100)) do
|
||||||
line = line:lower()
|
line = line:lower()
|
||||||
local is_docbook4 = line:find("<!doctype.*docbook")
|
local is_docbook4 = line:find('<!doctype.*docbook')
|
||||||
local is_docbook5 = line:find([[ xmlns="http://docbook.org/ns/docbook"]])
|
local is_docbook5 = line:find([[ xmlns="http://docbook.org/ns/docbook"]])
|
||||||
if is_docbook4 or is_docbook5 then
|
if is_docbook4 or is_docbook5 then
|
||||||
vim.b[bufnr].docbk_type = "xml"
|
vim.b[bufnr].docbk_type = 'xml'
|
||||||
vim.b[bufnr].docbk_ver = is_docbook4 and 4 or 5
|
vim.b[bufnr].docbk_ver = is_docbook4 and 4 or 5
|
||||||
vim.bo[bufnr].filetype = "docbk"
|
vim.bo[bufnr].filetype = 'docbk'
|
||||||
return
|
return
|
||||||
end
|
end
|
||||||
if line:find([[xmlns:xbl="http://www.mozilla.org/xbl"]]) then
|
if line:find([[xmlns:xbl="http://www.mozilla.org/xbl"]]) then
|
||||||
vim.bo[bufnr].filetype = "xbl"
|
vim.bo[bufnr].filetype = 'xbl'
|
||||||
return
|
return
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
vim.bo[bufnr].filetype = "xml"
|
vim.bo[bufnr].filetype = 'xml'
|
||||||
end
|
end
|
||||||
|
|
||||||
function M.y(bufnr)
|
function M.y(bufnr)
|
||||||
for _, line in ipairs(getlines(bufnr, 1, 100)) do
|
for _, line in ipairs(getlines(bufnr, 1, 100)) do
|
||||||
if line:find("^%s*%%") then
|
if line:find('^%s*%%') then
|
||||||
vim.bo[bufnr].filetype = "yacc"
|
vim.bo[bufnr].filetype = 'yacc'
|
||||||
return
|
return
|
||||||
end
|
end
|
||||||
-- TODO: in the Vim regex, \> is used to match the end of the word after "class",
|
-- TODO: in the Vim regex, \> is used to match the end of the word after "class",
|
||||||
-- can this be omitted?
|
-- can this be omitted?
|
||||||
if findany(line, { "^%s*#", "^%class", "^%s*#%s*include" }) then
|
if findany(line, { '^%s*#', '^%class', '^%s*#%s*include' }) then
|
||||||
vim.bo[bufnr].filetype = "racc"
|
vim.bo[bufnr].filetype = 'racc'
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
vim.bo[bufnr].filetype = "yacc"
|
vim.bo[bufnr].filetype = 'yacc'
|
||||||
end
|
end
|
||||||
|
|
||||||
-- luacheck: pop
|
-- luacheck: pop
|
||||||
|
@@ -14,14 +14,14 @@ function M.create(higroup, hi_info, default)
|
|||||||
local options = {}
|
local options = {}
|
||||||
-- TODO: Add validation
|
-- TODO: Add validation
|
||||||
for k, v in pairs(hi_info) do
|
for k, v in pairs(hi_info) do
|
||||||
table.insert(options, string.format("%s=%s", k, v))
|
table.insert(options, string.format('%s=%s', k, v))
|
||||||
end
|
end
|
||||||
vim.cmd(string.format([[highlight %s %s %s]], default and "default" or "", higroup, table.concat(options, " ")))
|
vim.cmd(string.format([[highlight %s %s %s]], default and 'default' or '', higroup, table.concat(options, ' ')))
|
||||||
end
|
end
|
||||||
|
|
||||||
---@private
|
---@private
|
||||||
function M.link(higroup, link_to, force)
|
function M.link(higroup, link_to, force)
|
||||||
vim.cmd(string.format([[highlight%s link %s %s]], force and "!" or " default", higroup, link_to))
|
vim.cmd(string.format([[highlight%s link %s %s]], force and '!' or ' default', higroup, link_to))
|
||||||
end
|
end
|
||||||
|
|
||||||
--- Highlight range between two positions
|
--- Highlight range between two positions
|
||||||
@@ -37,7 +37,7 @@ end
|
|||||||
-- - priority number indicating priority of highlight (default priorities.user)
|
-- - priority number indicating priority of highlight (default priorities.user)
|
||||||
function M.range(bufnr, ns, higroup, start, finish, opts)
|
function M.range(bufnr, ns, higroup, start, finish, opts)
|
||||||
opts = opts or {}
|
opts = opts or {}
|
||||||
local regtype = opts.regtype or "v"
|
local regtype = opts.regtype or 'v'
|
||||||
local inclusive = opts.inclusive or false
|
local inclusive = opts.inclusive or false
|
||||||
local priority = opts.priority or M.priorities.user
|
local priority = opts.priority or M.priorities.user
|
||||||
|
|
||||||
@@ -63,7 +63,7 @@ function M.range(bufnr, ns, higroup, start, finish, opts)
|
|||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
local yank_ns = api.nvim_create_namespace("hlyank")
|
local yank_ns = api.nvim_create_namespace('hlyank')
|
||||||
--- Highlight the yanked region
|
--- Highlight the yanked region
|
||||||
---
|
---
|
||||||
--- use from init.vim via
|
--- use from init.vim via
|
||||||
@@ -87,10 +87,10 @@ function M.on_yank(opts)
|
|||||||
if t == nil then
|
if t == nil then
|
||||||
return true
|
return true
|
||||||
else
|
else
|
||||||
return type(t) == "table"
|
return type(t) == 'table'
|
||||||
end
|
end
|
||||||
end,
|
end,
|
||||||
"a table or nil to configure options (see `:h highlight.on_yank`)",
|
'a table or nil to configure options (see `:h highlight.on_yank`)',
|
||||||
},
|
},
|
||||||
})
|
})
|
||||||
opts = opts or {}
|
opts = opts or {}
|
||||||
@@ -98,17 +98,17 @@ function M.on_yank(opts)
|
|||||||
local on_macro = opts.on_macro or false
|
local on_macro = opts.on_macro or false
|
||||||
local on_visual = (opts.on_visual ~= false)
|
local on_visual = (opts.on_visual ~= false)
|
||||||
|
|
||||||
if not on_macro and vim.fn.reg_executing() ~= "" then
|
if not on_macro and vim.fn.reg_executing() ~= '' then
|
||||||
return
|
return
|
||||||
end
|
end
|
||||||
if event.operator ~= "y" or event.regtype == "" then
|
if event.operator ~= 'y' or event.regtype == '' then
|
||||||
return
|
return
|
||||||
end
|
end
|
||||||
if not on_visual and event.visual then
|
if not on_visual and event.visual then
|
||||||
return
|
return
|
||||||
end
|
end
|
||||||
|
|
||||||
local higroup = opts.higroup or "IncSearch"
|
local higroup = opts.higroup or 'IncSearch'
|
||||||
local timeout = opts.timeout or 150
|
local timeout = opts.timeout or 150
|
||||||
|
|
||||||
local bufnr = api.nvim_get_current_buf()
|
local bufnr = api.nvim_get_current_buf()
|
||||||
|
@@ -1,7 +1,7 @@
|
|||||||
local inspect = {
|
local inspect = {
|
||||||
_VERSION = "inspect.lua 3.1.0",
|
_VERSION = 'inspect.lua 3.1.0',
|
||||||
_URL = "http://github.com/kikito/inspect.lua",
|
_URL = 'http://github.com/kikito/inspect.lua',
|
||||||
_DESCRIPTION = "human-readable representations of tables",
|
_DESCRIPTION = 'human-readable representations of tables',
|
||||||
_LICENSE = [[
|
_LICENSE = [[
|
||||||
MIT LICENSE
|
MIT LICENSE
|
||||||
|
|
||||||
@@ -30,12 +30,12 @@ local inspect = {
|
|||||||
|
|
||||||
inspect.KEY = setmetatable({}, {
|
inspect.KEY = setmetatable({}, {
|
||||||
__tostring = function()
|
__tostring = function()
|
||||||
return "inspect.KEY"
|
return 'inspect.KEY'
|
||||||
end,
|
end,
|
||||||
})
|
})
|
||||||
inspect.METATABLE = setmetatable({}, {
|
inspect.METATABLE = setmetatable({}, {
|
||||||
__tostring = function()
|
__tostring = function()
|
||||||
return "inspect.METATABLE"
|
return 'inspect.METATABLE'
|
||||||
end,
|
end,
|
||||||
})
|
})
|
||||||
|
|
||||||
@@ -61,52 +61,52 @@ end
|
|||||||
|
|
||||||
-- \a => '\\a', \0 => '\\0', 31 => '\31'
|
-- \a => '\\a', \0 => '\\0', 31 => '\31'
|
||||||
local shortControlCharEscapes = {
|
local shortControlCharEscapes = {
|
||||||
["\a"] = "\\a",
|
['\a'] = '\\a',
|
||||||
["\b"] = "\\b",
|
['\b'] = '\\b',
|
||||||
["\f"] = "\\f",
|
['\f'] = '\\f',
|
||||||
["\n"] = "\\n",
|
['\n'] = '\\n',
|
||||||
["\r"] = "\\r",
|
['\r'] = '\\r',
|
||||||
["\t"] = "\\t",
|
['\t'] = '\\t',
|
||||||
["\v"] = "\\v",
|
['\v'] = '\\v',
|
||||||
["\127"] = "\\127",
|
['\127'] = '\\127',
|
||||||
}
|
}
|
||||||
local longControlCharEscapes = { ["\127"] = "\127" }
|
local longControlCharEscapes = { ['\127'] = '\127' }
|
||||||
for i = 0, 31 do
|
for i = 0, 31 do
|
||||||
local ch = char(i)
|
local ch = char(i)
|
||||||
if not shortControlCharEscapes[ch] then
|
if not shortControlCharEscapes[ch] then
|
||||||
shortControlCharEscapes[ch] = "\\" .. i
|
shortControlCharEscapes[ch] = '\\' .. i
|
||||||
longControlCharEscapes[ch] = fmt("\\%03d", i)
|
longControlCharEscapes[ch] = fmt('\\%03d', i)
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
local function escape(str)
|
local function escape(str)
|
||||||
return (gsub(gsub(gsub(str, "\\", "\\\\"), "(%c)%f[0-9]", longControlCharEscapes), "%c", shortControlCharEscapes))
|
return (gsub(gsub(gsub(str, '\\', '\\\\'), '(%c)%f[0-9]', longControlCharEscapes), '%c', shortControlCharEscapes))
|
||||||
end
|
end
|
||||||
|
|
||||||
local function isIdentifier(str)
|
local function isIdentifier(str)
|
||||||
return type(str) == "string" and not not str:match("^[_%a][_%a%d]*$")
|
return type(str) == 'string' and not not str:match('^[_%a][_%a%d]*$')
|
||||||
end
|
end
|
||||||
|
|
||||||
local flr = math.floor
|
local flr = math.floor
|
||||||
local function isSequenceKey(k, sequenceLength)
|
local function isSequenceKey(k, sequenceLength)
|
||||||
return type(k) == "number" and flr(k) == k and 1 <= k and k <= sequenceLength
|
return type(k) == 'number' and flr(k) == k and 1 <= k and k <= sequenceLength
|
||||||
end
|
end
|
||||||
|
|
||||||
local defaultTypeOrders = {
|
local defaultTypeOrders = {
|
||||||
["number"] = 1,
|
['number'] = 1,
|
||||||
["boolean"] = 2,
|
['boolean'] = 2,
|
||||||
["string"] = 3,
|
['string'] = 3,
|
||||||
["table"] = 4,
|
['table'] = 4,
|
||||||
["function"] = 5,
|
['function'] = 5,
|
||||||
["userdata"] = 6,
|
['userdata'] = 6,
|
||||||
["thread"] = 7,
|
['thread'] = 7,
|
||||||
}
|
}
|
||||||
|
|
||||||
local function sortKeys(a, b)
|
local function sortKeys(a, b)
|
||||||
local ta, tb = type(a), type(b)
|
local ta, tb = type(a), type(b)
|
||||||
|
|
||||||
-- strings and numbers are sorted numerically/alphabetically
|
-- strings and numbers are sorted numerically/alphabetically
|
||||||
if ta == tb and (ta == "string" or ta == "number") then
|
if ta == tb and (ta == 'string' or ta == 'number') then
|
||||||
return a < b
|
return a < b
|
||||||
end
|
end
|
||||||
|
|
||||||
@@ -137,7 +137,7 @@ local function getKeys(t)
|
|||||||
end
|
end
|
||||||
|
|
||||||
local function countCycles(x, cycles)
|
local function countCycles(x, cycles)
|
||||||
if type(x) == "table" then
|
if type(x) == 'table' then
|
||||||
if cycles[x] then
|
if cycles[x] then
|
||||||
cycles[x] = cycles[x] + 1
|
cycles[x] = cycles[x] + 1
|
||||||
else
|
else
|
||||||
@@ -173,7 +173,7 @@ local function processRecursive(process, item, path, visited)
|
|||||||
end
|
end
|
||||||
|
|
||||||
local processed = process(item, path)
|
local processed = process(item, path)
|
||||||
if type(processed) == "table" then
|
if type(processed) == 'table' then
|
||||||
local processedCopy = {}
|
local processedCopy = {}
|
||||||
visited[item] = processedCopy
|
visited[item] = processedCopy
|
||||||
local processedKey
|
local processedKey
|
||||||
@@ -186,7 +186,7 @@ local function processRecursive(process, item, path, visited)
|
|||||||
end
|
end
|
||||||
|
|
||||||
local mt = processRecursive(process, getmetatable(processed), makePath(path, inspect.METATABLE), visited)
|
local mt = processRecursive(process, getmetatable(processed), makePath(path, inspect.METATABLE), visited)
|
||||||
if type(mt) ~= "table" then
|
if type(mt) ~= 'table' then
|
||||||
mt = nil
|
mt = nil
|
||||||
end
|
end
|
||||||
setmetatable(processedCopy, mt)
|
setmetatable(processedCopy, mt)
|
||||||
@@ -222,27 +222,27 @@ end
|
|||||||
function Inspector:putValue(v)
|
function Inspector:putValue(v)
|
||||||
local buf = self.buf
|
local buf = self.buf
|
||||||
local tv = type(v)
|
local tv = type(v)
|
||||||
if tv == "string" then
|
if tv == 'string' then
|
||||||
puts(buf, smartQuote(escape(v)))
|
puts(buf, smartQuote(escape(v)))
|
||||||
elseif
|
elseif
|
||||||
tv == "number"
|
tv == 'number'
|
||||||
or tv == "boolean"
|
or tv == 'boolean'
|
||||||
or tv == "nil"
|
or tv == 'nil'
|
||||||
or tv == "cdata"
|
or tv == 'cdata'
|
||||||
or tv == "ctype"
|
or tv == 'ctype'
|
||||||
or (vim and v == vim.NIL)
|
or (vim and v == vim.NIL)
|
||||||
then
|
then
|
||||||
puts(buf, tostring(v))
|
puts(buf, tostring(v))
|
||||||
elseif tv == "table" and not self.ids[v] then
|
elseif tv == 'table' and not self.ids[v] then
|
||||||
local t = v
|
local t = v
|
||||||
|
|
||||||
if t == inspect.KEY or t == inspect.METATABLE then
|
if t == inspect.KEY or t == inspect.METATABLE then
|
||||||
puts(buf, tostring(t))
|
puts(buf, tostring(t))
|
||||||
elseif self.level >= self.depth then
|
elseif self.level >= self.depth then
|
||||||
puts(buf, "{...}")
|
puts(buf, '{...}')
|
||||||
else
|
else
|
||||||
if self.cycles[t] > 1 then
|
if self.cycles[t] > 1 then
|
||||||
puts(buf, fmt("<%d>", self:getId(t)))
|
puts(buf, fmt('<%d>', self:getId(t)))
|
||||||
end
|
end
|
||||||
|
|
||||||
local keys, keysLen, seqLen = getKeys(t)
|
local keys, keysLen, seqLen = getKeys(t)
|
||||||
@@ -253,15 +253,15 @@ function Inspector:putValue(v)
|
|||||||
return
|
return
|
||||||
end
|
end
|
||||||
|
|
||||||
puts(buf, "{")
|
puts(buf, '{')
|
||||||
self.level = self.level + 1
|
self.level = self.level + 1
|
||||||
|
|
||||||
for i = 1, seqLen + keysLen do
|
for i = 1, seqLen + keysLen do
|
||||||
if i > 1 then
|
if i > 1 then
|
||||||
puts(buf, ",")
|
puts(buf, ',')
|
||||||
end
|
end
|
||||||
if i <= seqLen then
|
if i <= seqLen then
|
||||||
puts(buf, " ")
|
puts(buf, ' ')
|
||||||
self:putValue(t[i])
|
self:putValue(t[i])
|
||||||
else
|
else
|
||||||
local k = keys[i - seqLen]
|
local k = keys[i - seqLen]
|
||||||
@@ -269,36 +269,36 @@ function Inspector:putValue(v)
|
|||||||
if isIdentifier(k) then
|
if isIdentifier(k) then
|
||||||
puts(buf, k)
|
puts(buf, k)
|
||||||
else
|
else
|
||||||
puts(buf, "[")
|
puts(buf, '[')
|
||||||
self:putValue(k)
|
self:putValue(k)
|
||||||
puts(buf, "]")
|
puts(buf, ']')
|
||||||
end
|
end
|
||||||
puts(buf, " = ")
|
puts(buf, ' = ')
|
||||||
self:putValue(t[k])
|
self:putValue(t[k])
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
if type(mt) == "table" then
|
if type(mt) == 'table' then
|
||||||
if seqLen + keysLen > 0 then
|
if seqLen + keysLen > 0 then
|
||||||
puts(buf, ",")
|
puts(buf, ',')
|
||||||
end
|
end
|
||||||
tabify(self)
|
tabify(self)
|
||||||
puts(buf, "<metatable> = ")
|
puts(buf, '<metatable> = ')
|
||||||
self:putValue(mt)
|
self:putValue(mt)
|
||||||
end
|
end
|
||||||
|
|
||||||
self.level = self.level - 1
|
self.level = self.level - 1
|
||||||
|
|
||||||
if keysLen > 0 or type(mt) == "table" then
|
if keysLen > 0 or type(mt) == 'table' then
|
||||||
tabify(self)
|
tabify(self)
|
||||||
elseif seqLen > 0 then
|
elseif seqLen > 0 then
|
||||||
puts(buf, " ")
|
puts(buf, ' ')
|
||||||
end
|
end
|
||||||
|
|
||||||
puts(buf, "}")
|
puts(buf, '}')
|
||||||
end
|
end
|
||||||
else
|
else
|
||||||
puts(buf, fmt("<%s %d>", tv, self:getId(v)))
|
puts(buf, fmt('<%s %d>', tv, self:getId(v)))
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
@@ -306,8 +306,8 @@ function inspect.inspect(root, options)
|
|||||||
options = options or {}
|
options = options or {}
|
||||||
|
|
||||||
local depth = options.depth or math.huge
|
local depth = options.depth or math.huge
|
||||||
local newline = options.newline or "\n"
|
local newline = options.newline or '\n'
|
||||||
local indent = options.indent or " "
|
local indent = options.indent or ' '
|
||||||
local process = options.process
|
local process = options.process
|
||||||
|
|
||||||
if process then
|
if process then
|
||||||
|
@@ -49,15 +49,15 @@ local keymap = {}
|
|||||||
--- Default `false`.
|
--- Default `false`.
|
||||||
---@see |nvim_set_keymap()|
|
---@see |nvim_set_keymap()|
|
||||||
function keymap.set(mode, lhs, rhs, opts)
|
function keymap.set(mode, lhs, rhs, opts)
|
||||||
vim.validate {
|
vim.validate({
|
||||||
mode = { mode, { 's', 't' } },
|
mode = { mode, { 's', 't' } },
|
||||||
lhs = { lhs, 's' },
|
lhs = { lhs, 's' },
|
||||||
rhs = { rhs, { 's', 'f' } },
|
rhs = { rhs, { 's', 'f' } },
|
||||||
opts = {opts, 't', true}
|
opts = { opts, 't', true },
|
||||||
}
|
})
|
||||||
|
|
||||||
opts = vim.deepcopy(opts) or {}
|
opts = vim.deepcopy(opts) or {}
|
||||||
local is_rhs_luaref = type(rhs) == "function"
|
local is_rhs_luaref = type(rhs) == 'function'
|
||||||
mode = type(mode) == 'string' and { mode } or mode
|
mode = type(mode) == 'string' and { mode } or mode
|
||||||
|
|
||||||
if is_rhs_luaref and opts.expr then
|
if is_rhs_luaref and opts.expr then
|
||||||
@@ -118,11 +118,11 @@ end
|
|||||||
---@see |vim.keymap.set()|
|
---@see |vim.keymap.set()|
|
||||||
---
|
---
|
||||||
function keymap.del(modes, lhs, opts)
|
function keymap.del(modes, lhs, opts)
|
||||||
vim.validate {
|
vim.validate({
|
||||||
mode = { modes, { 's', 't' } },
|
mode = { modes, { 's', 't' } },
|
||||||
lhs = { lhs, 's' },
|
lhs = { lhs, 's' },
|
||||||
opts = {opts, 't', true}
|
opts = { opts, 't', true },
|
||||||
}
|
})
|
||||||
|
|
||||||
opts = opts or {}
|
opts = opts or {}
|
||||||
modes = type(modes) == 'string' and { modes } or modes
|
modes = type(modes) == 'string' and { modes } or modes
|
||||||
|
@@ -1,58 +1,58 @@
|
|||||||
local if_nil = vim.F.if_nil
|
local if_nil = vim.F.if_nil
|
||||||
|
|
||||||
local default_handlers = require 'vim.lsp.handlers'
|
local default_handlers = require('vim.lsp.handlers')
|
||||||
local log = require 'vim.lsp.log'
|
local log = require('vim.lsp.log')
|
||||||
local lsp_rpc = require 'vim.lsp.rpc'
|
local lsp_rpc = require('vim.lsp.rpc')
|
||||||
local protocol = require 'vim.lsp.protocol'
|
local protocol = require('vim.lsp.protocol')
|
||||||
local util = require 'vim.lsp.util'
|
local util = require('vim.lsp.util')
|
||||||
local sync = require 'vim.lsp.sync'
|
local sync = require('vim.lsp.sync')
|
||||||
|
|
||||||
local vim = vim
|
local vim = vim
|
||||||
local nvim_err_writeln, nvim_buf_get_lines, nvim_command, nvim_buf_get_option
|
local nvim_err_writeln, nvim_buf_get_lines, nvim_command, nvim_buf_get_option =
|
||||||
= vim.api.nvim_err_writeln, vim.api.nvim_buf_get_lines, vim.api.nvim_command, vim.api.nvim_buf_get_option
|
vim.api.nvim_err_writeln, vim.api.nvim_buf_get_lines, vim.api.nvim_command, vim.api.nvim_buf_get_option
|
||||||
local uv = vim.loop
|
local uv = vim.loop
|
||||||
local tbl_isempty, tbl_extend = vim.tbl_isempty, vim.tbl_extend
|
local tbl_isempty, tbl_extend = vim.tbl_isempty, vim.tbl_extend
|
||||||
local validate = vim.validate
|
local validate = vim.validate
|
||||||
|
|
||||||
local lsp = {
|
local lsp = {
|
||||||
protocol = protocol;
|
protocol = protocol,
|
||||||
|
|
||||||
handlers = default_handlers;
|
handlers = default_handlers,
|
||||||
|
|
||||||
buf = require'vim.lsp.buf';
|
buf = require('vim.lsp.buf'),
|
||||||
diagnostic = require'vim.lsp.diagnostic';
|
diagnostic = require('vim.lsp.diagnostic'),
|
||||||
codelens = require'vim.lsp.codelens';
|
codelens = require('vim.lsp.codelens'),
|
||||||
util = util;
|
util = util,
|
||||||
|
|
||||||
-- Allow raw RPC access.
|
-- Allow raw RPC access.
|
||||||
rpc = lsp_rpc;
|
rpc = lsp_rpc,
|
||||||
|
|
||||||
-- Export these directly from rpc.
|
-- Export these directly from rpc.
|
||||||
rpc_response_error = lsp_rpc.rpc_response_error;
|
rpc_response_error = lsp_rpc.rpc_response_error,
|
||||||
}
|
}
|
||||||
|
|
||||||
-- maps request name to the required server_capability in the client.
|
-- maps request name to the required server_capability in the client.
|
||||||
lsp._request_name_to_capability = {
|
lsp._request_name_to_capability = {
|
||||||
['textDocument/hover'] = { 'hoverProvider' };
|
['textDocument/hover'] = { 'hoverProvider' },
|
||||||
['textDocument/signatureHelp'] = { 'signatureHelpProvider' };
|
['textDocument/signatureHelp'] = { 'signatureHelpProvider' },
|
||||||
['textDocument/definition'] = { 'definitionProvider' };
|
['textDocument/definition'] = { 'definitionProvider' },
|
||||||
['textDocument/implementation'] = { 'implementationProvider' };
|
['textDocument/implementation'] = { 'implementationProvider' },
|
||||||
['textDocument/declaration'] = { 'declarationProvider' };
|
['textDocument/declaration'] = { 'declarationProvider' },
|
||||||
['textDocument/typeDefinition'] = { 'typeDefinitionProvider' };
|
['textDocument/typeDefinition'] = { 'typeDefinitionProvider' },
|
||||||
['textDocument/documentSymbol'] = { 'documentSymbolProvider' };
|
['textDocument/documentSymbol'] = { 'documentSymbolProvider' },
|
||||||
['textDocument/prepareCallHierarchy'] = { 'callHierarchyProvider' };
|
['textDocument/prepareCallHierarchy'] = { 'callHierarchyProvider' },
|
||||||
['textDocument/rename'] = { 'renameProvider' };
|
['textDocument/rename'] = { 'renameProvider' },
|
||||||
['textDocument/prepareRename'] = { 'renameProvider', 'prepareProvider'} ;
|
['textDocument/prepareRename'] = { 'renameProvider', 'prepareProvider' },
|
||||||
['textDocument/codeAction'] = { 'codeActionProvider' };
|
['textDocument/codeAction'] = { 'codeActionProvider' },
|
||||||
['textDocument/codeLens'] = { 'codeLensProvider' };
|
['textDocument/codeLens'] = { 'codeLensProvider' },
|
||||||
['codeLens/resolve'] = { 'codeLensProvider', 'resolveProvider' };
|
['codeLens/resolve'] = { 'codeLensProvider', 'resolveProvider' },
|
||||||
['workspace/executeCommand'] = { 'executeCommandProvider' };
|
['workspace/executeCommand'] = { 'executeCommandProvider' },
|
||||||
['workspace/symbol'] = { 'workspaceSymbolProvider' };
|
['workspace/symbol'] = { 'workspaceSymbolProvider' },
|
||||||
['textDocument/references'] = { 'referencesProvider' };
|
['textDocument/references'] = { 'referencesProvider' },
|
||||||
['textDocument/rangeFormatting'] = { 'documentRangeFormattingProvider' };
|
['textDocument/rangeFormatting'] = { 'documentRangeFormattingProvider' },
|
||||||
['textDocument/formatting'] = { 'documentFormattingProvider' };
|
['textDocument/formatting'] = { 'documentFormattingProvider' },
|
||||||
['textDocument/completion'] = { 'completionProvider' };
|
['textDocument/completion'] = { 'completionProvider' },
|
||||||
['textDocument/documentHighlight'] = { 'documentHighlightProvider' };
|
['textDocument/documentHighlight'] = { 'documentHighlightProvider' },
|
||||||
}
|
}
|
||||||
|
|
||||||
-- TODO improve handling of scratch buffers with LSP attached.
|
-- TODO improve handling of scratch buffers with LSP attached.
|
||||||
@@ -62,8 +62,8 @@ lsp._request_name_to_capability = {
|
|||||||
---
|
---
|
||||||
---@param {...} (List of strings) List to write to the buffer
|
---@param {...} (List of strings) List to write to the buffer
|
||||||
local function err_message(...)
|
local function err_message(...)
|
||||||
nvim_err_writeln(table.concat(vim.tbl_flatten{...}))
|
nvim_err_writeln(table.concat(vim.tbl_flatten({ ... })))
|
||||||
nvim_command("redraw")
|
nvim_command('redraw')
|
||||||
end
|
end
|
||||||
|
|
||||||
---@private
|
---@private
|
||||||
@@ -73,7 +73,7 @@ end
|
|||||||
---buffer if not given.
|
---buffer if not given.
|
||||||
---@returns bufnr (number) Number of requested buffer
|
---@returns bufnr (number) Number of requested buffer
|
||||||
local function resolve_bufnr(bufnr)
|
local function resolve_bufnr(bufnr)
|
||||||
validate { bufnr = { bufnr, 'n', true } }
|
validate({ bufnr = { bufnr, 'n', true } })
|
||||||
if bufnr == nil or bufnr == 0 then
|
if bufnr == nil or bufnr == 0 then
|
||||||
return vim.api.nvim_get_current_buf()
|
return vim.api.nvim_get_current_buf()
|
||||||
end
|
end
|
||||||
@@ -85,7 +85,10 @@ end
|
|||||||
--- supported in any of the servers registered for the current buffer.
|
--- supported in any of the servers registered for the current buffer.
|
||||||
---@param method (string) name of the method
|
---@param method (string) name of the method
|
||||||
function lsp._unsupported_method(method)
|
function lsp._unsupported_method(method)
|
||||||
local msg = string.format("method %s is not supported by any of the servers registered for the current buffer", method)
|
local msg = string.format(
|
||||||
|
'method %s is not supported by any of the servers registered for the current buffer',
|
||||||
|
method
|
||||||
|
)
|
||||||
log.warn(msg)
|
log.warn(msg)
|
||||||
return msg
|
return msg
|
||||||
end
|
end
|
||||||
@@ -96,23 +99,29 @@ end
|
|||||||
---@param filename (string) path to check
|
---@param filename (string) path to check
|
||||||
---@returns true if {filename} exists and is a directory, false otherwise
|
---@returns true if {filename} exists and is a directory, false otherwise
|
||||||
local function is_dir(filename)
|
local function is_dir(filename)
|
||||||
validate{filename={filename,'s'}}
|
validate({ filename = { filename, 's' } })
|
||||||
local stat = uv.fs_stat(filename)
|
local stat = uv.fs_stat(filename)
|
||||||
return stat and stat.type == 'directory' or false
|
return stat and stat.type == 'directory' or false
|
||||||
end
|
end
|
||||||
|
|
||||||
local wait_result_reason = { [-1] = "timeout"; [-2] = "interrupted"; [-3] = "error" }
|
local wait_result_reason = { [-1] = 'timeout', [-2] = 'interrupted', [-3] = 'error' }
|
||||||
|
|
||||||
local valid_encodings = {
|
local valid_encodings = {
|
||||||
["utf-8"] = 'utf-8'; ["utf-16"] = 'utf-16'; ["utf-32"] = 'utf-32';
|
['utf-8'] = 'utf-8',
|
||||||
["utf8"] = 'utf-8'; ["utf16"] = 'utf-16'; ["utf32"] = 'utf-32';
|
['utf-16'] = 'utf-16',
|
||||||
UTF8 = 'utf-8'; UTF16 = 'utf-16'; UTF32 = 'utf-32';
|
['utf-32'] = 'utf-32',
|
||||||
|
['utf8'] = 'utf-8',
|
||||||
|
['utf16'] = 'utf-16',
|
||||||
|
['utf32'] = 'utf-32',
|
||||||
|
UTF8 = 'utf-8',
|
||||||
|
UTF16 = 'utf-16',
|
||||||
|
UTF32 = 'utf-32',
|
||||||
}
|
}
|
||||||
|
|
||||||
local format_line_ending = {
|
local format_line_ending = {
|
||||||
["unix"] = '\n',
|
['unix'] = '\n',
|
||||||
["dos"] = '\r\n',
|
['dos'] = '\r\n',
|
||||||
["mac"] = '\r',
|
['mac'] = '\r',
|
||||||
}
|
}
|
||||||
|
|
||||||
---@private
|
---@private
|
||||||
@@ -138,10 +147,10 @@ local uninitialized_clients = {}
|
|||||||
|
|
||||||
---@private
|
---@private
|
||||||
local function for_each_buffer_client(bufnr, fn, restrict_client_ids)
|
local function for_each_buffer_client(bufnr, fn, restrict_client_ids)
|
||||||
validate {
|
validate({
|
||||||
fn = { fn, 'f' };
|
fn = { fn, 'f' },
|
||||||
restrict_client_ids = { restrict_client_ids, 't' , true};
|
restrict_client_ids = { restrict_client_ids, 't', true },
|
||||||
}
|
})
|
||||||
bufnr = resolve_bufnr(bufnr)
|
bufnr = resolve_bufnr(bufnr)
|
||||||
local client_ids = all_buffer_active_clients[bufnr]
|
local client_ids = all_buffer_active_clients[bufnr]
|
||||||
if not client_ids or tbl_isempty(client_ids) then
|
if not client_ids or tbl_isempty(client_ids) then
|
||||||
@@ -169,9 +178,13 @@ end
|
|||||||
-- Error codes to be used with `on_error` from |vim.lsp.start_client|.
|
-- Error codes to be used with `on_error` from |vim.lsp.start_client|.
|
||||||
-- Can be used to look up the string from a the number or the number
|
-- Can be used to look up the string from a the number or the number
|
||||||
-- from the string.
|
-- from the string.
|
||||||
lsp.client_errors = tbl_extend("error", lsp_rpc.client_errors, vim.tbl_add_reverse_lookup {
|
lsp.client_errors = tbl_extend(
|
||||||
ON_INIT_CALLBACK_ERROR = table.maxn(lsp_rpc.client_errors) + 1;
|
'error',
|
||||||
|
lsp_rpc.client_errors,
|
||||||
|
vim.tbl_add_reverse_lookup({
|
||||||
|
ON_INIT_CALLBACK_ERROR = table.maxn(lsp_rpc.client_errors) + 1,
|
||||||
})
|
})
|
||||||
|
)
|
||||||
|
|
||||||
---@private
|
---@private
|
||||||
--- Normalizes {encoding} to valid LSP encoding names.
|
--- Normalizes {encoding} to valid LSP encoding names.
|
||||||
@@ -179,9 +192,9 @@ lsp.client_errors = tbl_extend("error", lsp_rpc.client_errors, vim.tbl_add_rever
|
|||||||
---@param encoding (string) Encoding to normalize
|
---@param encoding (string) Encoding to normalize
|
||||||
---@returns (string) normalized encoding name
|
---@returns (string) normalized encoding name
|
||||||
local function validate_encoding(encoding)
|
local function validate_encoding(encoding)
|
||||||
validate {
|
validate({
|
||||||
encoding = { encoding, 's' };
|
encoding = { encoding, 's' },
|
||||||
}
|
})
|
||||||
return valid_encodings[encoding:lower()]
|
return valid_encodings[encoding:lower()]
|
||||||
or error(string.format("Invalid offset encoding %q. Must be one of: 'utf-8', 'utf-16', 'utf-32'", encoding))
|
or error(string.format("Invalid offset encoding %q. Must be one of: 'utf-8', 'utf-16', 'utf-32'", encoding))
|
||||||
end
|
end
|
||||||
@@ -194,16 +207,19 @@ end
|
|||||||
---@returns (string) the command
|
---@returns (string) the command
|
||||||
---@returns (list of strings) its arguments
|
---@returns (list of strings) its arguments
|
||||||
function lsp._cmd_parts(input)
|
function lsp._cmd_parts(input)
|
||||||
vim.validate{cmd={
|
vim.validate({ cmd = {
|
||||||
input,
|
input,
|
||||||
function() return vim.tbl_islist(input) end,
|
function()
|
||||||
"list"}}
|
return vim.tbl_islist(input)
|
||||||
|
end,
|
||||||
|
'list',
|
||||||
|
} })
|
||||||
|
|
||||||
local cmd = input[1]
|
local cmd = input[1]
|
||||||
local cmd_args = {}
|
local cmd_args = {}
|
||||||
-- Don't mutate our input.
|
-- Don't mutate our input.
|
||||||
for i, v in ipairs(input) do
|
for i, v in ipairs(input) do
|
||||||
vim.validate{["cmd argument"]={v, "s"}}
|
vim.validate({ ['cmd argument'] = { v, 's' } })
|
||||||
if i > 1 then
|
if i > 1 then
|
||||||
table.insert(cmd_args, v)
|
table.insert(cmd_args, v)
|
||||||
end
|
end
|
||||||
@@ -233,31 +249,29 @@ end
|
|||||||
---
|
---
|
||||||
---@see |vim.lsp.start_client()|
|
---@see |vim.lsp.start_client()|
|
||||||
local function validate_client_config(config)
|
local function validate_client_config(config)
|
||||||
validate {
|
validate({
|
||||||
config = { config, 't' };
|
config = { config, 't' },
|
||||||
}
|
})
|
||||||
validate {
|
validate({
|
||||||
handlers = { config.handlers, "t", true };
|
handlers = { config.handlers, 't', true },
|
||||||
capabilities = { config.capabilities, "t", true };
|
capabilities = { config.capabilities, 't', true },
|
||||||
cmd_cwd = { config.cmd_cwd, optional_validator(is_dir), "directory" };
|
cmd_cwd = { config.cmd_cwd, optional_validator(is_dir), 'directory' },
|
||||||
cmd_env = { config.cmd_env, "t", true };
|
cmd_env = { config.cmd_env, 't', true },
|
||||||
detached = { config.detached, "b", true };
|
detached = { config.detached, 'b', true },
|
||||||
name = { config.name, 's', true };
|
name = { config.name, 's', true },
|
||||||
on_error = { config.on_error, "f", true };
|
on_error = { config.on_error, 'f', true },
|
||||||
on_exit = { config.on_exit, "f", true };
|
on_exit = { config.on_exit, 'f', true },
|
||||||
on_init = { config.on_init, "f", true };
|
on_init = { config.on_init, 'f', true },
|
||||||
settings = { config.settings, "t", true };
|
settings = { config.settings, 't', true },
|
||||||
commands = { config.commands, 't', true };
|
commands = { config.commands, 't', true },
|
||||||
before_init = { config.before_init, "f", true };
|
before_init = { config.before_init, 'f', true },
|
||||||
offset_encoding = { config.offset_encoding, "s", true };
|
offset_encoding = { config.offset_encoding, 's', true },
|
||||||
flags = { config.flags, "t", true };
|
flags = { config.flags, 't', true },
|
||||||
get_language_id = { config.get_language_id, "f", true };
|
get_language_id = { config.get_language_id, 'f', true },
|
||||||
}
|
})
|
||||||
assert(
|
assert(
|
||||||
(not config.flags
|
(not config.flags or not config.flags.debounce_text_changes or type(config.flags.debounce_text_changes) == 'number'),
|
||||||
or not config.flags.debounce_text_changes
|
'flags.debounce_text_changes must be a number with the debounce time in milliseconds'
|
||||||
or type(config.flags.debounce_text_changes) == 'number'),
|
|
||||||
"flags.debounce_text_changes must be a number with the debounce time in milliseconds"
|
|
||||||
)
|
)
|
||||||
|
|
||||||
local cmd, cmd_args = lsp._cmd_parts(config.cmd)
|
local cmd, cmd_args = lsp._cmd_parts(config.cmd)
|
||||||
@@ -267,9 +281,9 @@ local function validate_client_config(config)
|
|||||||
end
|
end
|
||||||
|
|
||||||
return {
|
return {
|
||||||
cmd = cmd;
|
cmd = cmd,
|
||||||
cmd_args = cmd_args;
|
cmd_args = cmd_args,
|
||||||
offset_encoding = offset_encoding;
|
offset_encoding = offset_encoding,
|
||||||
}
|
}
|
||||||
end
|
end
|
||||||
|
|
||||||
@@ -329,14 +343,15 @@ do
|
|||||||
function changetracking.init(client, bufnr)
|
function changetracking.init(client, bufnr)
|
||||||
local use_incremental_sync = (
|
local use_incremental_sync = (
|
||||||
if_nil(client.config.flags.allow_incremental_sync, true)
|
if_nil(client.config.flags.allow_incremental_sync, true)
|
||||||
and vim.tbl_get(client.server_capabilities, "textDocumentSync", "change") == protocol.TextDocumentSyncKind.Incremental
|
and vim.tbl_get(client.server_capabilities, 'textDocumentSync', 'change')
|
||||||
|
== protocol.TextDocumentSyncKind.Incremental
|
||||||
)
|
)
|
||||||
local state = state_by_client[client.id]
|
local state = state_by_client[client.id]
|
||||||
if not state then
|
if not state then
|
||||||
state = {
|
state = {
|
||||||
buffers = {};
|
buffers = {},
|
||||||
debounce = client.config.flags.debounce_text_changes or 150,
|
debounce = client.config.flags.debounce_text_changes or 150,
|
||||||
use_incremental_sync = use_incremental_sync;
|
use_incremental_sync = use_incremental_sync,
|
||||||
}
|
}
|
||||||
state_by_client[client.id] = state
|
state_by_client[client.id] = state
|
||||||
end
|
end
|
||||||
@@ -405,7 +420,6 @@ do
|
|||||||
---@private
|
---@private
|
||||||
function changetracking.prepare(bufnr, firstline, lastline, new_lastline)
|
function changetracking.prepare(bufnr, firstline, lastline, new_lastline)
|
||||||
local incremental_changes = function(client, buf_state)
|
local incremental_changes = function(client, buf_state)
|
||||||
|
|
||||||
local prev_lines = buf_state.lines
|
local prev_lines = buf_state.lines
|
||||||
local curr_lines = buf_state.lines_tmp
|
local curr_lines = buf_state.lines_tmp
|
||||||
|
|
||||||
@@ -426,7 +440,14 @@ do
|
|||||||
|
|
||||||
local line_ending = buf_get_line_ending(bufnr)
|
local line_ending = buf_get_line_ending(bufnr)
|
||||||
local incremental_change = sync.compute_diff(
|
local incremental_change = sync.compute_diff(
|
||||||
buf_state.lines, curr_lines, firstline, lastline, new_lastline, client.offset_encoding or 'utf-16', line_ending)
|
buf_state.lines,
|
||||||
|
curr_lines,
|
||||||
|
firstline,
|
||||||
|
lastline,
|
||||||
|
new_lastline,
|
||||||
|
client.offset_encoding or 'utf-16',
|
||||||
|
line_ending
|
||||||
|
)
|
||||||
|
|
||||||
-- Double-buffering of lines tables is used to reduce the load on the garbage collector.
|
-- Double-buffering of lines tables is used to reduce the load on the garbage collector.
|
||||||
-- At this point the prev_lines table is useless, but its internal storage has already been allocated,
|
-- At this point the prev_lines table is useless, but its internal storage has already been allocated,
|
||||||
@@ -443,12 +464,14 @@ do
|
|||||||
end
|
end
|
||||||
local full_changes = once(function()
|
local full_changes = once(function()
|
||||||
return {
|
return {
|
||||||
text = buf_get_full_text(bufnr);
|
text = buf_get_full_text(bufnr),
|
||||||
};
|
}
|
||||||
end)
|
end)
|
||||||
local uri = vim.uri_from_bufnr(bufnr)
|
local uri = vim.uri_from_bufnr(bufnr)
|
||||||
return function(client)
|
return function(client)
|
||||||
if vim.tbl_get(client.server_capabilities, "textDocumentSync", "change") == protocol.TextDocumentSyncKind.None then
|
if
|
||||||
|
vim.tbl_get(client.server_capabilities, 'textDocumentSync', 'change') == protocol.TextDocumentSyncKind.None
|
||||||
|
then
|
||||||
return
|
return
|
||||||
end
|
end
|
||||||
local state = state_by_client[client.id]
|
local state = state_by_client[client.id]
|
||||||
@@ -467,7 +490,7 @@ do
|
|||||||
return
|
return
|
||||||
end
|
end
|
||||||
local changes = state.use_incremental_sync and buf_state.pending_changes or { full_changes() }
|
local changes = state.use_incremental_sync and buf_state.pending_changes or { full_changes() }
|
||||||
client.notify("textDocument/didChange", {
|
client.notify('textDocument/didChange', {
|
||||||
textDocument = {
|
textDocument = {
|
||||||
uri = uri,
|
uri = uri,
|
||||||
version = util.buf_versions[bufnr],
|
version = util.buf_versions[bufnr],
|
||||||
@@ -519,7 +542,6 @@ do
|
|||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
|
|
||||||
---@private
|
---@private
|
||||||
--- Default handler for the 'textDocument/didOpen' LSP notification.
|
--- Default handler for the 'textDocument/didOpen' LSP notification.
|
||||||
---
|
---
|
||||||
@@ -527,7 +549,7 @@ end
|
|||||||
---@param client Client object
|
---@param client Client object
|
||||||
local function text_document_did_open_handler(bufnr, client)
|
local function text_document_did_open_handler(bufnr, client)
|
||||||
changetracking.init(client, bufnr)
|
changetracking.init(client, bufnr)
|
||||||
if not vim.tbl_get(client.server_capabilities, "textDocumentSync", "openClose") then
|
if not vim.tbl_get(client.server_capabilities, 'textDocumentSync', 'openClose') then
|
||||||
return
|
return
|
||||||
end
|
end
|
||||||
if not vim.api.nvim_buf_is_loaded(bufnr) then
|
if not vim.api.nvim_buf_is_loaded(bufnr) then
|
||||||
@@ -537,11 +559,11 @@ local function text_document_did_open_handler(bufnr, client)
|
|||||||
|
|
||||||
local params = {
|
local params = {
|
||||||
textDocument = {
|
textDocument = {
|
||||||
version = 0;
|
version = 0,
|
||||||
uri = vim.uri_from_bufnr(bufnr);
|
uri = vim.uri_from_bufnr(bufnr),
|
||||||
languageId = client.config.get_language_id(bufnr, filetype);
|
languageId = client.config.get_language_id(bufnr, filetype),
|
||||||
text = buf_get_full_text(bufnr);
|
text = buf_get_full_text(bufnr),
|
||||||
}
|
},
|
||||||
}
|
}
|
||||||
client.notify('textDocument/didOpen', params)
|
client.notify('textDocument/didOpen', params)
|
||||||
util.buf_versions[bufnr] = params.textDocument.version
|
util.buf_versions[bufnr] = params.textDocument.version
|
||||||
@@ -763,13 +785,15 @@ function lsp.start_client(config)
|
|||||||
-- By default, get_language_id just returns the exact filetype it is passed.
|
-- By default, get_language_id just returns the exact filetype it is passed.
|
||||||
-- It is possible to pass in something that will calculate a different filetype,
|
-- It is possible to pass in something that will calculate a different filetype,
|
||||||
-- to be sent by the client.
|
-- to be sent by the client.
|
||||||
config.get_language_id = config.get_language_id or function(_, filetype) return filetype end
|
config.get_language_id = config.get_language_id or function(_, filetype)
|
||||||
|
return filetype
|
||||||
|
end
|
||||||
|
|
||||||
local client_id = next_client_id()
|
local client_id = next_client_id()
|
||||||
|
|
||||||
local handlers = config.handlers or {}
|
local handlers = config.handlers or {}
|
||||||
local name = config.name or tostring(client_id)
|
local name = config.name or tostring(client_id)
|
||||||
local log_prefix = string.format("LSP[%s]", name)
|
local log_prefix = string.format('LSP[%s]', name)
|
||||||
|
|
||||||
local dispatch = {}
|
local dispatch = {}
|
||||||
|
|
||||||
@@ -807,10 +831,10 @@ function lsp.start_client(config)
|
|||||||
local _ = log.trace() and log.trace('server_request', method, params)
|
local _ = log.trace() and log.trace('server_request', method, params)
|
||||||
local handler = resolve_handler(method)
|
local handler = resolve_handler(method)
|
||||||
if handler then
|
if handler then
|
||||||
local _ = log.trace() and log.trace("server_request: found handler for", method)
|
local _ = log.trace() and log.trace('server_request: found handler for', method)
|
||||||
return handler(nil, params, { method = method, client_id = client_id })
|
return handler(nil, params, { method = method, client_id = client_id })
|
||||||
end
|
end
|
||||||
local _ = log.warn() and log.warn("server_request: no handler found for", method)
|
local _ = log.warn() and log.warn('server_request: no handler found for', method)
|
||||||
return nil, lsp.rpc_response_error(protocol.ErrorCodes.MethodNotFound)
|
return nil, lsp.rpc_response_error(protocol.ErrorCodes.MethodNotFound)
|
||||||
end
|
end
|
||||||
|
|
||||||
@@ -822,12 +846,12 @@ function lsp.start_client(config)
|
|||||||
---@see |vim.lsp.rpc.client_errors| for possible errors. Use
|
---@see |vim.lsp.rpc.client_errors| for possible errors. Use
|
||||||
---`vim.lsp.rpc.client_errors[code]` to get a human-friendly name.
|
---`vim.lsp.rpc.client_errors[code]` to get a human-friendly name.
|
||||||
function dispatch.on_error(code, err)
|
function dispatch.on_error(code, err)
|
||||||
local _ = log.error() and log.error(log_prefix, "on_error", { code = lsp.client_errors[code], err = err })
|
local _ = log.error() and log.error(log_prefix, 'on_error', { code = lsp.client_errors[code], err = err })
|
||||||
err_message(log_prefix, ': Error ', lsp.client_errors[code], ': ', vim.inspect(err))
|
err_message(log_prefix, ': Error ', lsp.client_errors[code], ': ', vim.inspect(err))
|
||||||
if config.on_error then
|
if config.on_error then
|
||||||
local status, usererr = pcall(config.on_error, code, err)
|
local status, usererr = pcall(config.on_error, code, err)
|
||||||
if not status then
|
if not status then
|
||||||
local _ = log.error() and log.error(log_prefix, "user on_error failed", { err = usererr })
|
local _ = log.error() and log.error(log_prefix, 'user on_error failed', { err = usererr })
|
||||||
err_message(log_prefix, ' user on_error failed: ', tostring(usererr))
|
err_message(log_prefix, ' user on_error failed: ', tostring(usererr))
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
@@ -853,7 +877,7 @@ function lsp.start_client(config)
|
|||||||
end
|
end
|
||||||
|
|
||||||
if code ~= 0 or (signal ~= 0 and signal ~= 15) then
|
if code ~= 0 or (signal ~= 0 and signal ~= 15) then
|
||||||
local msg = string.format("Client %s quit with exit code %s and signal %s", client_id, code, signal)
|
local msg = string.format('Client %s quit with exit code %s and signal %s', client_id, code, signal)
|
||||||
vim.schedule(function()
|
vim.schedule(function()
|
||||||
vim.notify(msg, vim.log.levels.WARN)
|
vim.notify(msg, vim.log.levels.WARN)
|
||||||
end)
|
end)
|
||||||
@@ -862,38 +886,41 @@ function lsp.start_client(config)
|
|||||||
|
|
||||||
-- Start the RPC client.
|
-- Start the RPC client.
|
||||||
local rpc = lsp_rpc.start(cmd, cmd_args, dispatch, {
|
local rpc = lsp_rpc.start(cmd, cmd_args, dispatch, {
|
||||||
cwd = config.cmd_cwd;
|
cwd = config.cmd_cwd,
|
||||||
env = config.cmd_env;
|
env = config.cmd_env,
|
||||||
detached = config.detached;
|
detached = config.detached,
|
||||||
})
|
})
|
||||||
|
|
||||||
-- Return nil if client fails to start
|
-- Return nil if client fails to start
|
||||||
if not rpc then return end
|
if not rpc then
|
||||||
|
return
|
||||||
|
end
|
||||||
|
|
||||||
local client = {
|
local client = {
|
||||||
id = client_id;
|
id = client_id,
|
||||||
name = name;
|
name = name,
|
||||||
rpc = rpc;
|
rpc = rpc,
|
||||||
offset_encoding = offset_encoding;
|
offset_encoding = offset_encoding,
|
||||||
config = config;
|
config = config,
|
||||||
attached_buffers = {};
|
attached_buffers = {},
|
||||||
|
|
||||||
handlers = handlers;
|
handlers = handlers,
|
||||||
commands = config.commands or {};
|
commands = config.commands or {},
|
||||||
|
|
||||||
requests = {};
|
requests = {},
|
||||||
-- for $/progress report
|
-- for $/progress report
|
||||||
messages = { name = name, messages = {}, progress = {}, status = {} };
|
messages = { name = name, messages = {}, progress = {}, status = {} },
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
-- Store the uninitialized_clients for cleanup in case we exit before initialize finishes.
|
-- Store the uninitialized_clients for cleanup in case we exit before initialize finishes.
|
||||||
uninitialized_clients[client_id] = client;
|
uninitialized_clients[client_id] = client
|
||||||
|
|
||||||
---@private
|
---@private
|
||||||
local function initialize()
|
local function initialize()
|
||||||
local valid_traces = {
|
local valid_traces = {
|
||||||
off = 'off'; messages = 'messages'; verbose = 'verbose';
|
off = 'off',
|
||||||
|
messages = 'messages',
|
||||||
|
verbose = 'verbose',
|
||||||
}
|
}
|
||||||
local version = vim.version()
|
local version = vim.version()
|
||||||
|
|
||||||
@@ -902,10 +929,12 @@ function lsp.start_client(config)
|
|||||||
local root_path
|
local root_path
|
||||||
if config.workspace_folders or config.root_dir then
|
if config.workspace_folders or config.root_dir then
|
||||||
if config.root_dir and not config.workspace_folders then
|
if config.root_dir and not config.workspace_folders then
|
||||||
workspace_folders = {{
|
workspace_folders = {
|
||||||
uri = vim.uri_from_fname(config.root_dir);
|
{
|
||||||
name = string.format("%s", config.root_dir);
|
uri = vim.uri_from_fname(config.root_dir),
|
||||||
}};
|
name = string.format('%s', config.root_dir),
|
||||||
|
},
|
||||||
|
}
|
||||||
else
|
else
|
||||||
workspace_folders = config.workspace_folders
|
workspace_folders = config.workspace_folders
|
||||||
end
|
end
|
||||||
@@ -922,41 +951,41 @@ function lsp.start_client(config)
|
|||||||
-- the process has not been started by another process. If the parent
|
-- the process has not been started by another process. If the parent
|
||||||
-- process is not alive then the server should exit (see exit notification)
|
-- process is not alive then the server should exit (see exit notification)
|
||||||
-- its process.
|
-- its process.
|
||||||
processId = uv.getpid();
|
processId = uv.getpid(),
|
||||||
-- Information about the client
|
-- Information about the client
|
||||||
-- since 3.15.0
|
-- since 3.15.0
|
||||||
clientInfo = {
|
clientInfo = {
|
||||||
name = "Neovim",
|
name = 'Neovim',
|
||||||
version = string.format("%s.%s.%s", version.major, version.minor, version.patch)
|
version = string.format('%s.%s.%s', version.major, version.minor, version.patch),
|
||||||
};
|
},
|
||||||
-- The rootPath of the workspace. Is null if no folder is open.
|
-- The rootPath of the workspace. Is null if no folder is open.
|
||||||
--
|
--
|
||||||
-- @deprecated in favour of rootUri.
|
-- @deprecated in favour of rootUri.
|
||||||
rootPath = root_path or vim.NIL;
|
rootPath = root_path or vim.NIL,
|
||||||
-- The rootUri of the workspace. Is null if no folder is open. If both
|
-- The rootUri of the workspace. Is null if no folder is open. If both
|
||||||
-- `rootPath` and `rootUri` are set `rootUri` wins.
|
-- `rootPath` and `rootUri` are set `rootUri` wins.
|
||||||
rootUri = root_uri or vim.NIL;
|
rootUri = root_uri or vim.NIL,
|
||||||
-- The workspace folders configured in the client when the server starts.
|
-- The workspace folders configured in the client when the server starts.
|
||||||
-- This property is only available if the client supports workspace folders.
|
-- This property is only available if the client supports workspace folders.
|
||||||
-- It can be `null` if the client supports workspace folders but none are
|
-- It can be `null` if the client supports workspace folders but none are
|
||||||
-- configured.
|
-- configured.
|
||||||
workspaceFolders = workspace_folders or vim.NIL;
|
workspaceFolders = workspace_folders or vim.NIL,
|
||||||
-- User provided initialization options.
|
-- User provided initialization options.
|
||||||
initializationOptions = config.init_options;
|
initializationOptions = config.init_options,
|
||||||
-- The capabilities provided by the client (editor or tool)
|
-- The capabilities provided by the client (editor or tool)
|
||||||
capabilities = config.capabilities or protocol.make_client_capabilities();
|
capabilities = config.capabilities or protocol.make_client_capabilities(),
|
||||||
-- The initial trace setting. If omitted trace is disabled ("off").
|
-- The initial trace setting. If omitted trace is disabled ("off").
|
||||||
-- trace = "off" | "messages" | "verbose";
|
-- trace = "off" | "messages" | "verbose";
|
||||||
trace = valid_traces[config.trace] or 'off';
|
trace = valid_traces[config.trace] or 'off',
|
||||||
}
|
}
|
||||||
if config.before_init then
|
if config.before_init then
|
||||||
-- TODO(ashkan) handle errors here.
|
-- TODO(ashkan) handle errors here.
|
||||||
pcall(config.before_init, initialize_params, config)
|
pcall(config.before_init, initialize_params, config)
|
||||||
end
|
end
|
||||||
local _ = log.trace() and log.trace(log_prefix, "initialize_params", initialize_params)
|
local _ = log.trace() and log.trace(log_prefix, 'initialize_params', initialize_params)
|
||||||
rpc.request('initialize', initialize_params, function(init_err, result)
|
rpc.request('initialize', initialize_params, function(init_err, result)
|
||||||
assert(not init_err, tostring(init_err))
|
assert(not init_err, tostring(init_err))
|
||||||
assert(result, "server sent empty result")
|
assert(result, 'server sent empty result')
|
||||||
rpc.notify('initialized', vim.empty_dict())
|
rpc.notify('initialized', vim.empty_dict())
|
||||||
client.initialized = true
|
client.initialized = true
|
||||||
uninitialized_clients[client_id] = nil
|
uninitialized_clients[client_id] = nil
|
||||||
@@ -973,10 +1002,13 @@ function lsp.start_client(config)
|
|||||||
local mt = {}
|
local mt = {}
|
||||||
mt.__index = function(table, key)
|
mt.__index = function(table, key)
|
||||||
if key == 'resolved_capabilities' then
|
if key == 'resolved_capabilities' then
|
||||||
vim.notify_once("[LSP] Accessing client.resolved_capabilities is deprecated, " ..
|
vim.notify_once(
|
||||||
"update your plugins or configuration to access client.server_capabilities instead." ..
|
'[LSP] Accessing client.resolved_capabilities is deprecated, '
|
||||||
"The new key/value pairs in server_capabilities directly match those " ..
|
.. 'update your plugins or configuration to access client.server_capabilities instead.'
|
||||||
"defined in the language server protocol", vim.log.levels.WARN)
|
.. 'The new key/value pairs in server_capabilities directly match those '
|
||||||
|
.. 'defined in the language server protocol',
|
||||||
|
vim.log.levels.WARN
|
||||||
|
)
|
||||||
rawset(table, key, protocol._resolve_capabilities_compat(client.server_capabilities))
|
rawset(table, key, protocol._resolve_capabilities_compat(client.server_capabilities))
|
||||||
return rawget(table, key)
|
return rawget(table, key)
|
||||||
else
|
else
|
||||||
@@ -1004,7 +1036,8 @@ function lsp.start_client(config)
|
|||||||
pcall(handlers.on_error, lsp.client_errors.ON_INIT_CALLBACK_ERROR, err)
|
pcall(handlers.on_error, lsp.client_errors.ON_INIT_CALLBACK_ERROR, err)
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
local _ = log.info() and log.info(log_prefix, "server_capabilities", { server_capabilities = client.server_capabilities })
|
local _ = log.info()
|
||||||
|
and log.info(log_prefix, 'server_capabilities', { server_capabilities = client.server_capabilities })
|
||||||
|
|
||||||
-- Only assign after initialized.
|
-- Only assign after initialized.
|
||||||
active_clients[client_id] = client
|
active_clients[client_id] = client
|
||||||
@@ -1039,22 +1072,22 @@ function lsp.start_client(config)
|
|||||||
function client.request(method, params, handler, bufnr)
|
function client.request(method, params, handler, bufnr)
|
||||||
if not handler then
|
if not handler then
|
||||||
handler = resolve_handler(method)
|
handler = resolve_handler(method)
|
||||||
or error(string.format("not found: %q request handler for client %q.", method, client.name))
|
or error(string.format('not found: %q request handler for client %q.', method, client.name))
|
||||||
end
|
end
|
||||||
-- Ensure pending didChange notifications are sent so that the server doesn't operate on a stale state
|
-- Ensure pending didChange notifications are sent so that the server doesn't operate on a stale state
|
||||||
changetracking.flush(client, bufnr)
|
changetracking.flush(client, bufnr)
|
||||||
bufnr = resolve_bufnr(bufnr)
|
bufnr = resolve_bufnr(bufnr)
|
||||||
local _ = log.debug() and log.debug(log_prefix, "client.request", client_id, method, params, handler, bufnr)
|
local _ = log.debug() and log.debug(log_prefix, 'client.request', client_id, method, params, handler, bufnr)
|
||||||
local success, request_id = rpc.request(method, params, function(err, result)
|
local success, request_id = rpc.request(method, params, function(err, result)
|
||||||
handler(err, result, { method = method, client_id = client_id, bufnr = bufnr, params = params })
|
handler(err, result, { method = method, client_id = client_id, bufnr = bufnr, params = params })
|
||||||
end, function(request_id)
|
end, function(request_id)
|
||||||
client.requests[request_id] = nil
|
client.requests[request_id] = nil
|
||||||
nvim_command("doautocmd <nomodeline> User LspRequest")
|
nvim_command('doautocmd <nomodeline> User LspRequest')
|
||||||
end)
|
end)
|
||||||
|
|
||||||
if success then
|
if success then
|
||||||
client.requests[request_id] = { type = 'pending', bufnr = bufnr, method = method }
|
client.requests[request_id] = { type = 'pending', bufnr = bufnr, method = method }
|
||||||
nvim_command("doautocmd <nomodeline> User LspRequest")
|
nvim_command('doautocmd <nomodeline> User LspRequest')
|
||||||
end
|
end
|
||||||
|
|
||||||
return success, request_id
|
return success, request_id
|
||||||
@@ -1081,9 +1114,10 @@ function lsp.start_client(config)
|
|||||||
request_result = { err = err, result = result }
|
request_result = { err = err, result = result }
|
||||||
end
|
end
|
||||||
|
|
||||||
local success, request_id = client.request(method, params, _sync_handler,
|
local success, request_id = client.request(method, params, _sync_handler, bufnr)
|
||||||
bufnr)
|
if not success then
|
||||||
if not success then return nil end
|
return nil
|
||||||
|
end
|
||||||
|
|
||||||
local wait_result, reason = vim.wait(timeout_ms or 1000, function()
|
local wait_result, reason = vim.wait(timeout_ms or 1000, function()
|
||||||
return request_result ~= nil
|
return request_result ~= nil
|
||||||
@@ -1118,13 +1152,13 @@ function lsp.start_client(config)
|
|||||||
---@returns true if any client returns true; false otherwise
|
---@returns true if any client returns true; false otherwise
|
||||||
---@see |vim.lsp.client.notify()|
|
---@see |vim.lsp.client.notify()|
|
||||||
function client.cancel_request(id)
|
function client.cancel_request(id)
|
||||||
validate{id = {id, 'n'}}
|
validate({ id = { id, 'n' } })
|
||||||
local request = client.requests[id]
|
local request = client.requests[id]
|
||||||
if request and request.type == 'pending' then
|
if request and request.type == 'pending' then
|
||||||
request.type = 'cancel'
|
request.type = 'cancel'
|
||||||
nvim_command("doautocmd <nomodeline> User LspRequest")
|
nvim_command('doautocmd <nomodeline> User LspRequest')
|
||||||
end
|
end
|
||||||
return rpc.notify("$/cancelRequest", { id = id })
|
return rpc.notify('$/cancelRequest', { id = id })
|
||||||
end
|
end
|
||||||
|
|
||||||
-- Track this so that we can escalate automatically if we've already tried a
|
-- Track this so that we can escalate automatically if we've already tried a
|
||||||
@@ -1139,7 +1173,6 @@ function lsp.start_client(config)
|
|||||||
---
|
---
|
||||||
---@param force (bool, optional)
|
---@param force (bool, optional)
|
||||||
function client.stop(force)
|
function client.stop(force)
|
||||||
|
|
||||||
lsp.diagnostic.reset(client_id, all_buffer_active_clients)
|
lsp.diagnostic.reset(client_id, all_buffer_active_clients)
|
||||||
changetracking.reset(client_id)
|
changetracking.reset(client_id)
|
||||||
for _, client_ids in pairs(all_buffer_active_clients) do
|
for _, client_ids in pairs(all_buffer_active_clients) do
|
||||||
@@ -1150,7 +1183,7 @@ function lsp.start_client(config)
|
|||||||
if handle:is_closing() then
|
if handle:is_closing() then
|
||||||
return
|
return
|
||||||
end
|
end
|
||||||
if force or (not client.initialized) or graceful_shutdown_failed then
|
if force or not client.initialized or graceful_shutdown_failed then
|
||||||
handle:kill(15)
|
handle:kill(15)
|
||||||
return
|
return
|
||||||
end
|
end
|
||||||
@@ -1198,7 +1231,6 @@ end
|
|||||||
local text_document_did_change_handler
|
local text_document_did_change_handler
|
||||||
do
|
do
|
||||||
text_document_did_change_handler = function(_, bufnr, changedtick, firstline, lastline, new_lastline)
|
text_document_did_change_handler = function(_, bufnr, changedtick, firstline, lastline, new_lastline)
|
||||||
|
|
||||||
-- Detach (nvim_buf_attach) via returning True to on_lines if no clients are attached
|
-- Detach (nvim_buf_attach) via returning True to on_lines if no clients are attached
|
||||||
if tbl_isempty(all_buffer_active_clients[bufnr] or {}) then
|
if tbl_isempty(all_buffer_active_clients[bufnr] or {}) then
|
||||||
return true
|
return true
|
||||||
@@ -1215,17 +1247,17 @@ function lsp._text_document_did_save_handler(bufnr)
|
|||||||
local uri = vim.uri_from_bufnr(bufnr)
|
local uri = vim.uri_from_bufnr(bufnr)
|
||||||
local text = once(buf_get_full_text)
|
local text = once(buf_get_full_text)
|
||||||
for_each_buffer_client(bufnr, function(client)
|
for_each_buffer_client(bufnr, function(client)
|
||||||
local save_capability = vim.tbl_get(client.server_capabilities, "textDocumentSync", "save")
|
local save_capability = vim.tbl_get(client.server_capabilities, 'textDocumentSync', 'save')
|
||||||
if save_capability then
|
if save_capability then
|
||||||
local included_text
|
local included_text
|
||||||
if type(save_capability) == "table" and save_capability.includeText then
|
if type(save_capability) == 'table' and save_capability.includeText then
|
||||||
included_text = text(bufnr)
|
included_text = text(bufnr)
|
||||||
end
|
end
|
||||||
client.notify('textDocument/didSave', {
|
client.notify('textDocument/didSave', {
|
||||||
textDocument = {
|
textDocument = {
|
||||||
uri = uri;
|
uri = uri,
|
||||||
};
|
},
|
||||||
text = included_text;
|
text = included_text,
|
||||||
})
|
})
|
||||||
end
|
end
|
||||||
end)
|
end)
|
||||||
@@ -1239,15 +1271,13 @@ end
|
|||||||
---@param bufnr (number) Buffer handle, or 0 for current
|
---@param bufnr (number) Buffer handle, or 0 for current
|
||||||
---@param client_id (number) Client id
|
---@param client_id (number) Client id
|
||||||
function lsp.buf_attach_client(bufnr, client_id)
|
function lsp.buf_attach_client(bufnr, client_id)
|
||||||
validate {
|
validate({
|
||||||
bufnr = {bufnr, 'n', true};
|
bufnr = { bufnr, 'n', true },
|
||||||
client_id = {client_id, 'n'};
|
client_id = { client_id, 'n' },
|
||||||
}
|
})
|
||||||
bufnr = resolve_bufnr(bufnr)
|
bufnr = resolve_bufnr(bufnr)
|
||||||
if not vim.api.nvim_buf_is_loaded(bufnr) then
|
if not vim.api.nvim_buf_is_loaded(bufnr) then
|
||||||
local _ = log.warn() and log.warn(
|
local _ = log.warn() and log.warn(string.format('buf_attach_client called on unloaded buffer (id: %d): ', bufnr))
|
||||||
string.format("buf_attach_client called on unloaded buffer (id: %d): ", bufnr)
|
|
||||||
)
|
|
||||||
return false
|
return false
|
||||||
end
|
end
|
||||||
local buffer_client_ids = all_buffer_active_clients[bufnr]
|
local buffer_client_ids = all_buffer_active_clients[bufnr]
|
||||||
@@ -1266,36 +1296,38 @@ function lsp.buf_attach_client(bufnr, client_id)
|
|||||||
vim.api.nvim_exec(string.format(buf_did_save_autocommand, client_id, bufnr, bufnr), false)
|
vim.api.nvim_exec(string.format(buf_did_save_autocommand, client_id, bufnr, bufnr), false)
|
||||||
-- First time, so attach and set up stuff.
|
-- First time, so attach and set up stuff.
|
||||||
vim.api.nvim_buf_attach(bufnr, false, {
|
vim.api.nvim_buf_attach(bufnr, false, {
|
||||||
on_lines = text_document_did_change_handler;
|
on_lines = text_document_did_change_handler,
|
||||||
on_reload = function()
|
on_reload = function()
|
||||||
local params = { textDocument = { uri = uri; } }
|
local params = { textDocument = { uri = uri } }
|
||||||
for_each_buffer_client(bufnr, function(client, _)
|
for_each_buffer_client(bufnr, function(client, _)
|
||||||
changetracking.reset_buf(client, bufnr)
|
changetracking.reset_buf(client, bufnr)
|
||||||
if vim.tbl_get(client.server_capabilities, "textDocumentSync", "openClose") then
|
if vim.tbl_get(client.server_capabilities, 'textDocumentSync', 'openClose') then
|
||||||
client.notify('textDocument/didClose', params)
|
client.notify('textDocument/didClose', params)
|
||||||
end
|
end
|
||||||
text_document_did_open_handler(bufnr, client)
|
text_document_did_open_handler(bufnr, client)
|
||||||
end)
|
end)
|
||||||
end;
|
end,
|
||||||
on_detach = function()
|
on_detach = function()
|
||||||
local params = { textDocument = { uri = uri; } }
|
local params = { textDocument = { uri = uri } }
|
||||||
for_each_buffer_client(bufnr, function(client, _)
|
for_each_buffer_client(bufnr, function(client, _)
|
||||||
changetracking.reset_buf(client, bufnr)
|
changetracking.reset_buf(client, bufnr)
|
||||||
if vim.tbl_get(client.server_capabilities, "textDocumentSync", "openClose") then
|
if vim.tbl_get(client.server_capabilities, 'textDocumentSync', 'openClose') then
|
||||||
client.notify('textDocument/didClose', params)
|
client.notify('textDocument/didClose', params)
|
||||||
end
|
end
|
||||||
end)
|
end)
|
||||||
util.buf_versions[bufnr] = nil
|
util.buf_versions[bufnr] = nil
|
||||||
all_buffer_active_clients[bufnr] = nil
|
all_buffer_active_clients[bufnr] = nil
|
||||||
end;
|
end,
|
||||||
-- TODO if we know all of the potential clients ahead of time, then we
|
-- TODO if we know all of the potential clients ahead of time, then we
|
||||||
-- could conditionally set this.
|
-- could conditionally set this.
|
||||||
-- utf_sizes = size_index > 1;
|
-- utf_sizes = size_index > 1;
|
||||||
utf_sizes = true;
|
utf_sizes = true,
|
||||||
})
|
})
|
||||||
end
|
end
|
||||||
|
|
||||||
if buffer_client_ids[client_id] then return end
|
if buffer_client_ids[client_id] then
|
||||||
|
return
|
||||||
|
end
|
||||||
-- This is our first time attaching this client to this buffer.
|
-- This is our first time attaching this client to this buffer.
|
||||||
buffer_client_ids[client_id] = true
|
buffer_client_ids[client_id] = true
|
||||||
|
|
||||||
@@ -1315,25 +1347,23 @@ end
|
|||||||
---@param bufnr number Buffer handle, or 0 for current
|
---@param bufnr number Buffer handle, or 0 for current
|
||||||
---@param client_id number Client id
|
---@param client_id number Client id
|
||||||
function lsp.buf_detach_client(bufnr, client_id)
|
function lsp.buf_detach_client(bufnr, client_id)
|
||||||
validate {
|
validate({
|
||||||
bufnr = {bufnr, 'n', true};
|
bufnr = { bufnr, 'n', true },
|
||||||
client_id = {client_id, 'n'};
|
client_id = { client_id, 'n' },
|
||||||
}
|
})
|
||||||
bufnr = resolve_bufnr(bufnr)
|
bufnr = resolve_bufnr(bufnr)
|
||||||
|
|
||||||
local client = lsp.get_client_by_id(client_id)
|
local client = lsp.get_client_by_id(client_id)
|
||||||
if not client or not client.attached_buffers[bufnr] then
|
if not client or not client.attached_buffers[bufnr] then
|
||||||
vim.notify(
|
vim.notify(string.format('Buffer (id: %d) is not attached to client (id: %d). Cannot detach.', client_id, bufnr))
|
||||||
string.format('Buffer (id: %d) is not attached to client (id: %d). Cannot detach.', client_id, bufnr)
|
|
||||||
)
|
|
||||||
return
|
return
|
||||||
end
|
end
|
||||||
|
|
||||||
changetracking.reset_buf(client, bufnr)
|
changetracking.reset_buf(client, bufnr)
|
||||||
|
|
||||||
if vim.tbl_get(client.server_capabilities, "textDocumentSync", "openClose") then
|
if vim.tbl_get(client.server_capabilities, 'textDocumentSync', 'openClose') then
|
||||||
local uri = vim.uri_from_bufnr(bufnr)
|
local uri = vim.uri_from_bufnr(bufnr)
|
||||||
local params = { textDocument = { uri = uri; } }
|
local params = { textDocument = { uri = uri } }
|
||||||
client.notify('textDocument/didClose', params)
|
client.notify('textDocument/didClose', params)
|
||||||
end
|
end
|
||||||
|
|
||||||
@@ -1349,7 +1379,6 @@ function lsp.buf_detach_client(bufnr, client_id)
|
|||||||
vim.diagnostic.reset(namespace, bufnr)
|
vim.diagnostic.reset(namespace, bufnr)
|
||||||
|
|
||||||
vim.notify(string.format('Detached buffer (id: %d) from client (id: %d)', bufnr, client_id))
|
vim.notify(string.format('Detached buffer (id: %d) from client (id: %d)', bufnr, client_id))
|
||||||
|
|
||||||
end
|
end
|
||||||
|
|
||||||
--- Checks if a buffer is attached for a particular client.
|
--- Checks if a buffer is attached for a particular client.
|
||||||
@@ -1414,7 +1443,7 @@ function lsp.get_active_clients()
|
|||||||
end
|
end
|
||||||
|
|
||||||
function lsp._vim_exit_handler()
|
function lsp._vim_exit_handler()
|
||||||
log.info("exit_handler", active_clients)
|
log.info('exit_handler', active_clients)
|
||||||
for _, client in pairs(uninitialized_clients) do
|
for _, client in pairs(uninitialized_clients) do
|
||||||
client.stop(true)
|
client.stop(true)
|
||||||
end
|
end
|
||||||
@@ -1466,8 +1495,7 @@ function lsp._vim_exit_handler()
|
|||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
nvim_command("autocmd VimLeavePre * lua vim.lsp._vim_exit_handler()")
|
nvim_command('autocmd VimLeavePre * lua vim.lsp._vim_exit_handler()')
|
||||||
|
|
||||||
|
|
||||||
--- Sends an async request for all active clients attached to the
|
--- Sends an async request for all active clients attached to the
|
||||||
--- buffer.
|
--- buffer.
|
||||||
@@ -1483,11 +1511,11 @@ nvim_command("autocmd VimLeavePre * lua vim.lsp._vim_exit_handler()")
|
|||||||
--- - Function which can be used to cancel all the requests. You could instead
|
--- - Function which can be used to cancel all the requests. You could instead
|
||||||
--- iterate all clients and call their `cancel_request()` methods.
|
--- iterate all clients and call their `cancel_request()` methods.
|
||||||
function lsp.buf_request(bufnr, method, params, handler)
|
function lsp.buf_request(bufnr, method, params, handler)
|
||||||
validate {
|
validate({
|
||||||
bufnr = { bufnr, 'n', true };
|
bufnr = { bufnr, 'n', true },
|
||||||
method = { method, 's' };
|
method = { method, 's' },
|
||||||
handler = { handler, 'f', true };
|
handler = { handler, 'f', true },
|
||||||
}
|
})
|
||||||
|
|
||||||
local supported_clients = {}
|
local supported_clients = {}
|
||||||
local method_supported = false
|
local method_supported = false
|
||||||
@@ -1501,7 +1529,7 @@ function lsp.buf_request(bufnr, method, params, handler)
|
|||||||
-- if has client but no clients support the given method, notify the user
|
-- if has client but no clients support the given method, notify the user
|
||||||
if not tbl_isempty(all_buffer_active_clients[resolve_bufnr(bufnr)] or {}) and not method_supported then
|
if not tbl_isempty(all_buffer_active_clients[resolve_bufnr(bufnr)] or {}) and not method_supported then
|
||||||
vim.notify(lsp._unsupported_method(method), vim.log.levels.ERROR)
|
vim.notify(lsp._unsupported_method(method), vim.log.levels.ERROR)
|
||||||
vim.api.nvim_command("redraw")
|
vim.api.nvim_command('redraw')
|
||||||
return {}, function() end
|
return {}, function() end
|
||||||
end
|
end
|
||||||
|
|
||||||
@@ -1607,18 +1635,19 @@ end
|
|||||||
---
|
---
|
||||||
---@returns true if any client returns true; false otherwise
|
---@returns true if any client returns true; false otherwise
|
||||||
function lsp.buf_notify(bufnr, method, params)
|
function lsp.buf_notify(bufnr, method, params)
|
||||||
validate {
|
validate({
|
||||||
bufnr = { bufnr, 'n', true };
|
bufnr = { bufnr, 'n', true },
|
||||||
method = { method, 's' };
|
method = { method, 's' },
|
||||||
}
|
})
|
||||||
local resp = false
|
local resp = false
|
||||||
for_each_buffer_client(bufnr, function(client, _client_id, _resolved_bufnr)
|
for_each_buffer_client(bufnr, function(client, _client_id, _resolved_bufnr)
|
||||||
if client.rpc.notify(method, params) then resp = true end
|
if client.rpc.notify(method, params) then
|
||||||
|
resp = true
|
||||||
|
end
|
||||||
end)
|
end)
|
||||||
return resp
|
return resp
|
||||||
end
|
end
|
||||||
|
|
||||||
|
|
||||||
---@private
|
---@private
|
||||||
local function adjust_start_col(lnum, line, items, encoding)
|
local function adjust_start_col(lnum, line, items, encoding)
|
||||||
local min_start_char = nil
|
local min_start_char = nil
|
||||||
@@ -1650,7 +1679,7 @@ end
|
|||||||
--- - findstart=0: column where the completion starts, or -2 or -3
|
--- - findstart=0: column where the completion starts, or -2 or -3
|
||||||
--- - findstart=1: list of matches (actually just calls |complete()|)
|
--- - findstart=1: list of matches (actually just calls |complete()|)
|
||||||
function lsp.omnifunc(findstart, base)
|
function lsp.omnifunc(findstart, base)
|
||||||
local _ = log.debug() and log.debug("omnifunc.findstart", { findstart = findstart, base = base })
|
local _ = log.debug() and log.debug('omnifunc.findstart', { findstart = findstart, base = base })
|
||||||
|
|
||||||
local bufnr = resolve_bufnr()
|
local bufnr = resolve_bufnr()
|
||||||
local has_buffer_clients = not tbl_isempty(all_buffer_active_clients[bufnr] or {})
|
local has_buffer_clients = not tbl_isempty(all_buffer_active_clients[bufnr] or {})
|
||||||
@@ -1663,12 +1692,12 @@ function lsp.omnifunc(findstart, base)
|
|||||||
end
|
end
|
||||||
|
|
||||||
-- Then, perform standard completion request
|
-- Then, perform standard completion request
|
||||||
local _ = log.info() and log.info("base ", base)
|
local _ = log.info() and log.info('base ', base)
|
||||||
|
|
||||||
local pos = vim.api.nvim_win_get_cursor(0)
|
local pos = vim.api.nvim_win_get_cursor(0)
|
||||||
local line = vim.api.nvim_get_current_line()
|
local line = vim.api.nvim_get_current_line()
|
||||||
local line_to_cursor = line:sub(1, pos[2])
|
local line_to_cursor = line:sub(1, pos[2])
|
||||||
local _ = log.trace() and log.trace("omnifunc.line", pos, line)
|
local _ = log.trace() and log.trace('omnifunc.line', pos, line)
|
||||||
|
|
||||||
-- Get the start position of the current keyword
|
-- Get the start position of the current keyword
|
||||||
local textMatch = vim.fn.match(line_to_cursor, '\\k*$')
|
local textMatch = vim.fn.match(line_to_cursor, '\\k*$')
|
||||||
@@ -1677,7 +1706,9 @@ function lsp.omnifunc(findstart, base)
|
|||||||
|
|
||||||
local items = {}
|
local items = {}
|
||||||
lsp.buf_request(bufnr, 'textDocument/completion', params, function(err, result, ctx)
|
lsp.buf_request(bufnr, 'textDocument/completion', params, function(err, result, ctx)
|
||||||
if err or not result or vim.fn.mode() ~= "i" then return end
|
if err or not result or vim.fn.mode() ~= 'i' then
|
||||||
|
return
|
||||||
|
end
|
||||||
|
|
||||||
-- Completion response items may be relative to a position different than `textMatch`.
|
-- Completion response items may be relative to a position different than `textMatch`.
|
||||||
-- Concrete example, with sumneko/lua-language-server:
|
-- Concrete example, with sumneko/lua-language-server:
|
||||||
@@ -1734,14 +1765,14 @@ function lsp.formatexpr(opts)
|
|||||||
|
|
||||||
if start_line > 0 and end_line > 0 then
|
if start_line > 0 and end_line > 0 then
|
||||||
local params = {
|
local params = {
|
||||||
textDocument = util.make_text_document_params();
|
textDocument = util.make_text_document_params(),
|
||||||
range = {
|
range = {
|
||||||
start = { line = start_line - 1; character = 0; };
|
start = { line = start_line - 1, character = 0 },
|
||||||
["end"] = { line = end_line - 1; character = 0; };
|
['end'] = { line = end_line - 1, character = 0 },
|
||||||
};
|
},
|
||||||
};
|
}
|
||||||
params.options = util.make_formatting_params().options
|
params.options = util.make_formatting_params().options
|
||||||
local client_results = vim.lsp.buf_request_sync(0, "textDocument/rangeFormatting", params, timeout_ms)
|
local client_results = vim.lsp.buf_request_sync(0, 'textDocument/rangeFormatting', params, timeout_ms)
|
||||||
|
|
||||||
-- Apply the text edits from one and only one of the clients.
|
-- Apply the text edits from one and only one of the clients.
|
||||||
for client_id, response in pairs(client_results) do
|
for client_id, response in pairs(client_results) do
|
||||||
@@ -1815,7 +1846,7 @@ function lsp.set_log_level(level)
|
|||||||
if type(level) == 'string' or type(level) == 'number' then
|
if type(level) == 'string' or type(level) == 'number' then
|
||||||
log.set_level(level)
|
log.set_level(level)
|
||||||
else
|
else
|
||||||
error(string.format("Invalid log level: %q", level))
|
error(string.format('Invalid log level: %q', level))
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
@@ -1845,7 +1876,7 @@ end
|
|||||||
---@param override_config (table) Table containing the keys to override behavior of the {handler}
|
---@param override_config (table) Table containing the keys to override behavior of the {handler}
|
||||||
function lsp.with(handler, override_config)
|
function lsp.with(handler, override_config)
|
||||||
return function(err, result, ctx, config)
|
return function(err, result, ctx, config)
|
||||||
return handler(err, result, ctx, vim.tbl_deep_extend("force", config or {}, override_config))
|
return handler(err, result, ctx, vim.tbl_deep_extend('force', config or {}, override_config))
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
@@ -1860,12 +1891,16 @@ function lsp._with_extend(name, options, user_config)
|
|||||||
local resulting_config = {}
|
local resulting_config = {}
|
||||||
for k, v in pairs(user_config) do
|
for k, v in pairs(user_config) do
|
||||||
if options[k] == nil then
|
if options[k] == nil then
|
||||||
error(debug.traceback(string.format(
|
error(
|
||||||
"Invalid option for `%s`: %s. Valid options are:\n%s",
|
debug.traceback(
|
||||||
|
string.format(
|
||||||
|
'Invalid option for `%s`: %s. Valid options are:\n%s',
|
||||||
name,
|
name,
|
||||||
k,
|
k,
|
||||||
vim.inspect(vim.tbl_keys(options))
|
vim.inspect(vim.tbl_keys(options))
|
||||||
)))
|
)
|
||||||
|
)
|
||||||
|
)
|
||||||
end
|
end
|
||||||
|
|
||||||
resulting_config[k] = v
|
resulting_config[k] = v
|
||||||
@@ -1880,7 +1915,6 @@ function lsp._with_extend(name, options, user_config)
|
|||||||
return resulting_config
|
return resulting_config
|
||||||
end
|
end
|
||||||
|
|
||||||
|
|
||||||
--- Registry for client side commands.
|
--- Registry for client side commands.
|
||||||
--- This is an extension point for plugins to handle custom commands which are
|
--- This is an extension point for plugins to handle custom commands which are
|
||||||
--- not part of the core language server protocol specification.
|
--- not part of the core language server protocol specification.
|
||||||
@@ -1902,12 +1936,11 @@ end
|
|||||||
--- The second argument is the `ctx` of |lsp-handler|
|
--- The second argument is the `ctx` of |lsp-handler|
|
||||||
lsp.commands = setmetatable({}, {
|
lsp.commands = setmetatable({}, {
|
||||||
__newindex = function(tbl, key, value)
|
__newindex = function(tbl, key, value)
|
||||||
assert(type(key) == 'string', "The key for commands in `vim.lsp.commands` must be a string")
|
assert(type(key) == 'string', 'The key for commands in `vim.lsp.commands` must be a string')
|
||||||
assert(type(value) == 'function', "Command added to `vim.lsp.commands` must be a function")
|
assert(type(value) == 'function', 'Command added to `vim.lsp.commands` must be a function')
|
||||||
rawset(tbl, key, value)
|
rawset(tbl, key, value)
|
||||||
end;
|
end,
|
||||||
})
|
})
|
||||||
|
|
||||||
|
|
||||||
return lsp
|
return lsp
|
||||||
-- vim:sw=2 ts=2 et
|
-- vim:sw=2 ts=2 et
|
||||||
|
@@ -41,7 +41,7 @@ P.take_until = function(targets, specials)
|
|||||||
parsed = true,
|
parsed = true,
|
||||||
value = {
|
value = {
|
||||||
raw = table.concat(raw, ''),
|
raw = table.concat(raw, ''),
|
||||||
esc = table.concat(esc, '')
|
esc = table.concat(esc, ''),
|
||||||
},
|
},
|
||||||
pos = new_pos,
|
pos = new_pos,
|
||||||
}
|
}
|
||||||
@@ -248,48 +248,66 @@ S.format = P.any(
|
|||||||
capture_index = values[3],
|
capture_index = values[3],
|
||||||
}, Node)
|
}, Node)
|
||||||
end),
|
end),
|
||||||
P.map(P.seq(S.dollar, S.open, S.int, S.colon, S.slash, P.any(
|
P.map(
|
||||||
P.token('upcase'),
|
P.seq(
|
||||||
P.token('downcase'),
|
S.dollar,
|
||||||
P.token('capitalize'),
|
S.open,
|
||||||
P.token('camelcase'),
|
S.int,
|
||||||
P.token('pascalcase')
|
S.colon,
|
||||||
), S.close), function(values)
|
S.slash,
|
||||||
|
P.any(P.token('upcase'), P.token('downcase'), P.token('capitalize'), P.token('camelcase'), P.token('pascalcase')),
|
||||||
|
S.close
|
||||||
|
),
|
||||||
|
function(values)
|
||||||
return setmetatable({
|
return setmetatable({
|
||||||
type = Node.Type.FORMAT,
|
type = Node.Type.FORMAT,
|
||||||
capture_index = values[3],
|
capture_index = values[3],
|
||||||
modifier = values[6],
|
modifier = values[6],
|
||||||
}, Node)
|
}, Node)
|
||||||
end),
|
end
|
||||||
P.map(P.seq(S.dollar, S.open, S.int, S.colon, P.any(
|
),
|
||||||
|
P.map(
|
||||||
|
P.seq(
|
||||||
|
S.dollar,
|
||||||
|
S.open,
|
||||||
|
S.int,
|
||||||
|
S.colon,
|
||||||
|
P.any(
|
||||||
P.seq(S.question, P.take_until({ ':' }, { '\\' }), S.colon, P.take_until({ '}' }, { '\\' })),
|
P.seq(S.question, P.take_until({ ':' }, { '\\' }), S.colon, P.take_until({ '}' }, { '\\' })),
|
||||||
P.seq(S.plus, P.take_until({ '}' }, { '\\' })),
|
P.seq(S.plus, P.take_until({ '}' }, { '\\' })),
|
||||||
P.seq(S.minus, P.take_until({ '}' }, { '\\' }))
|
P.seq(S.minus, P.take_until({ '}' }, { '\\' }))
|
||||||
), S.close), function(values)
|
),
|
||||||
|
S.close
|
||||||
|
),
|
||||||
|
function(values)
|
||||||
return setmetatable({
|
return setmetatable({
|
||||||
type = Node.Type.FORMAT,
|
type = Node.Type.FORMAT,
|
||||||
capture_index = values[3],
|
capture_index = values[3],
|
||||||
if_text = values[5][2].esc,
|
if_text = values[5][2].esc,
|
||||||
else_text = (values[5][4] or {}).esc,
|
else_text = (values[5][4] or {}).esc,
|
||||||
}, Node)
|
}, Node)
|
||||||
end)
|
end
|
||||||
|
)
|
||||||
)
|
)
|
||||||
|
|
||||||
S.transform = P.map(P.seq(
|
S.transform = P.map(
|
||||||
|
P.seq(
|
||||||
S.slash,
|
S.slash,
|
||||||
P.take_until({ '/' }, { '\\' }),
|
P.take_until({ '/' }, { '\\' }),
|
||||||
S.slash,
|
S.slash,
|
||||||
P.many(P.any(S.format, S.text({ '$', '/' }, { '\\' }))),
|
P.many(P.any(S.format, S.text({ '$', '/' }, { '\\' }))),
|
||||||
S.slash,
|
S.slash,
|
||||||
P.opt(P.pattern('[ig]+'))
|
P.opt(P.pattern('[ig]+'))
|
||||||
), function(values)
|
),
|
||||||
|
function(values)
|
||||||
return setmetatable({
|
return setmetatable({
|
||||||
type = Node.Type.TRANSFORM,
|
type = Node.Type.TRANSFORM,
|
||||||
pattern = values[2].raw,
|
pattern = values[2].raw,
|
||||||
format = values[4],
|
format = values[4],
|
||||||
option = values[6],
|
option = values[6],
|
||||||
}, Node)
|
}, Node)
|
||||||
end)
|
end
|
||||||
|
)
|
||||||
|
|
||||||
S.tabstop = P.any(
|
S.tabstop = P.any(
|
||||||
P.map(P.seq(S.dollar, S.int), function(values)
|
P.map(P.seq(S.dollar, S.int), function(values)
|
||||||
@@ -314,34 +332,38 @@ S.tabstop = P.any(
|
|||||||
)
|
)
|
||||||
|
|
||||||
S.placeholder = P.any(
|
S.placeholder = P.any(
|
||||||
P.map(P.seq(S.dollar, S.open, S.int, S.colon, P.many(P.any(S.toplevel, S.text({ '$', '}' }, { '\\' }))), S.close), function(values)
|
P.map(
|
||||||
|
P.seq(S.dollar, S.open, S.int, S.colon, P.many(P.any(S.toplevel, S.text({ '$', '}' }, { '\\' }))), S.close),
|
||||||
|
function(values)
|
||||||
return setmetatable({
|
return setmetatable({
|
||||||
type = Node.Type.PLACEHOLDER,
|
type = Node.Type.PLACEHOLDER,
|
||||||
tabstop = values[3],
|
tabstop = values[3],
|
||||||
children = values[5],
|
children = values[5],
|
||||||
}, Node)
|
}, Node)
|
||||||
end)
|
end
|
||||||
|
)
|
||||||
)
|
)
|
||||||
|
|
||||||
S.choice = P.map(P.seq(
|
S.choice = P.map(
|
||||||
|
P.seq(
|
||||||
S.dollar,
|
S.dollar,
|
||||||
S.open,
|
S.open,
|
||||||
S.int,
|
S.int,
|
||||||
S.pipe,
|
S.pipe,
|
||||||
P.many(
|
P.many(P.map(P.seq(S.text({ ',', '|' }), P.opt(S.comma)), function(values)
|
||||||
P.map(P.seq(S.text({ ',', '|' }), P.opt(S.comma)), function(values)
|
|
||||||
return values[1].esc
|
return values[1].esc
|
||||||
end)
|
end)),
|
||||||
),
|
|
||||||
S.pipe,
|
S.pipe,
|
||||||
S.close
|
S.close
|
||||||
), function(values)
|
),
|
||||||
|
function(values)
|
||||||
return setmetatable({
|
return setmetatable({
|
||||||
type = Node.Type.CHOICE,
|
type = Node.Type.CHOICE,
|
||||||
tabstop = values[3],
|
tabstop = values[3],
|
||||||
items = values[5],
|
items = values[5],
|
||||||
}, Node)
|
}, Node)
|
||||||
end)
|
end
|
||||||
|
)
|
||||||
|
|
||||||
S.variable = P.any(
|
S.variable = P.any(
|
||||||
P.map(P.seq(S.dollar, S.var), function(values)
|
P.map(P.seq(S.dollar, S.var), function(values)
|
||||||
@@ -363,13 +385,16 @@ S.variable = P.any(
|
|||||||
transform = values[4],
|
transform = values[4],
|
||||||
}, Node)
|
}, Node)
|
||||||
end),
|
end),
|
||||||
P.map(P.seq(S.dollar, S.open, S.var, S.colon, P.many(P.any(S.toplevel, S.text({ '$', '}' }, { '\\' }))), S.close), function(values)
|
P.map(
|
||||||
|
P.seq(S.dollar, S.open, S.var, S.colon, P.many(P.any(S.toplevel, S.text({ '$', '}' }, { '\\' }))), S.close),
|
||||||
|
function(values)
|
||||||
return setmetatable({
|
return setmetatable({
|
||||||
type = Node.Type.VARIABLE,
|
type = Node.Type.VARIABLE,
|
||||||
name = values[3],
|
name = values[3],
|
||||||
children = values[5],
|
children = values[5],
|
||||||
}, Node)
|
}, Node)
|
||||||
end)
|
end
|
||||||
|
)
|
||||||
)
|
)
|
||||||
|
|
||||||
S.snippet = P.map(P.many(P.any(S.toplevel, S.text({ '$' }, { '}', '\\' }))), function(values)
|
S.snippet = P.map(P.many(P.any(S.toplevel, S.text({ '$' }, { '}', '\\' }))), function(values)
|
||||||
|
@@ -1,7 +1,7 @@
|
|||||||
local vim = vim
|
local vim = vim
|
||||||
local validate = vim.validate
|
local validate = vim.validate
|
||||||
local vfn = vim.fn
|
local vfn = vim.fn
|
||||||
local util = require 'vim.lsp.util'
|
local util = require('vim.lsp.util')
|
||||||
|
|
||||||
local M = {}
|
local M = {}
|
||||||
|
|
||||||
@@ -9,7 +9,9 @@ local M = {}
|
|||||||
--- Returns nil if {status} is false or nil, otherwise returns the rest of the
|
--- Returns nil if {status} is false or nil, otherwise returns the rest of the
|
||||||
--- arguments.
|
--- arguments.
|
||||||
local function ok_or_nil(status, ...)
|
local function ok_or_nil(status, ...)
|
||||||
if not status then return end
|
if not status then
|
||||||
|
return
|
||||||
|
end
|
||||||
return ...
|
return ...
|
||||||
end
|
end
|
||||||
|
|
||||||
@@ -39,10 +41,10 @@ end
|
|||||||
---
|
---
|
||||||
---@see |vim.lsp.buf_request()|
|
---@see |vim.lsp.buf_request()|
|
||||||
local function request(method, params, handler)
|
local function request(method, params, handler)
|
||||||
validate {
|
validate({
|
||||||
method = {method, 's'};
|
method = { method, 's' },
|
||||||
handler = {handler, 'f', true};
|
handler = { handler, 'f', true },
|
||||||
}
|
})
|
||||||
return vim.lsp.buf_request(0, method, params, handler)
|
return vim.lsp.buf_request(0, method, params, handler)
|
||||||
end
|
end
|
||||||
|
|
||||||
@@ -51,7 +53,7 @@ end
|
|||||||
---
|
---
|
||||||
---@returns `true` if server responds.
|
---@returns `true` if server responds.
|
||||||
function M.server_ready()
|
function M.server_ready()
|
||||||
return not not vim.lsp.buf_notify(0, "window/progress", {})
|
return not not vim.lsp.buf_notify(0, 'window/progress', {})
|
||||||
end
|
end
|
||||||
|
|
||||||
--- Displays hover information about the symbol under the cursor in a floating
|
--- Displays hover information about the symbol under the cursor in a floating
|
||||||
@@ -117,9 +119,9 @@ end
|
|||||||
--
|
--
|
||||||
---@returns The client that the user selected or nil
|
---@returns The client that the user selected or nil
|
||||||
local function select_client(method, on_choice)
|
local function select_client(method, on_choice)
|
||||||
validate {
|
validate({
|
||||||
on_choice = { on_choice, 'function', false },
|
on_choice = { on_choice, 'function', false },
|
||||||
}
|
})
|
||||||
local clients = vim.tbl_values(vim.lsp.buf_get_clients())
|
local clients = vim.tbl_values(vim.lsp.buf_get_clients())
|
||||||
clients = vim.tbl_filter(function(client)
|
clients = vim.tbl_filter(function(client)
|
||||||
return client.supports_method(method)
|
return client.supports_method(method)
|
||||||
@@ -191,24 +193,21 @@ function M.format(options)
|
|||||||
if options.filter then
|
if options.filter then
|
||||||
clients = options.filter(clients)
|
clients = options.filter(clients)
|
||||||
elseif options.id then
|
elseif options.id then
|
||||||
clients = vim.tbl_filter(
|
clients = vim.tbl_filter(function(client)
|
||||||
function(client) return client.id == options.id end,
|
return client.id == options.id
|
||||||
clients
|
end, clients)
|
||||||
)
|
|
||||||
elseif options.name then
|
elseif options.name then
|
||||||
clients = vim.tbl_filter(
|
clients = vim.tbl_filter(function(client)
|
||||||
function(client) return client.name == options.name end,
|
return client.name == options.name
|
||||||
clients
|
end, clients)
|
||||||
)
|
|
||||||
end
|
end
|
||||||
|
|
||||||
clients = vim.tbl_filter(
|
clients = vim.tbl_filter(function(client)
|
||||||
function(client) return client.supports_method("textDocument/formatting") end,
|
return client.supports_method('textDocument/formatting')
|
||||||
clients
|
end, clients)
|
||||||
)
|
|
||||||
|
|
||||||
if #clients == 0 then
|
if #clients == 0 then
|
||||||
vim.notify("[LSP] Format request failed, no matching language servers.")
|
vim.notify('[LSP] Format request failed, no matching language servers.')
|
||||||
end
|
end
|
||||||
|
|
||||||
if options.async then
|
if options.async then
|
||||||
@@ -218,7 +217,7 @@ function M.format(options)
|
|||||||
return
|
return
|
||||||
end
|
end
|
||||||
local params = util.make_formatting_params(options.formatting_options)
|
local params = util.make_formatting_params(options.formatting_options)
|
||||||
client.request("textDocument/formatting", params, function(...)
|
client.request('textDocument/formatting', params, function(...)
|
||||||
local handler = client.handlers['textDocument/formatting'] or vim.lsp.handlers['textDocument/formatting']
|
local handler = client.handlers['textDocument/formatting'] or vim.lsp.handlers['textDocument/formatting']
|
||||||
handler(...)
|
handler(...)
|
||||||
do_format(next(clients, idx))
|
do_format(next(clients, idx))
|
||||||
@@ -229,11 +228,11 @@ function M.format(options)
|
|||||||
local timeout_ms = options.timeout_ms or 1000
|
local timeout_ms = options.timeout_ms or 1000
|
||||||
for _, client in pairs(clients) do
|
for _, client in pairs(clients) do
|
||||||
local params = util.make_formatting_params(options.formatting_options)
|
local params = util.make_formatting_params(options.formatting_options)
|
||||||
local result, err = client.request_sync("textDocument/formatting", params, timeout_ms, bufnr)
|
local result, err = client.request_sync('textDocument/formatting', params, timeout_ms, bufnr)
|
||||||
if result and result.result then
|
if result and result.result then
|
||||||
util.apply_text_edits(result.result, bufnr, client.offset_encoding)
|
util.apply_text_edits(result.result, bufnr, client.offset_encoding)
|
||||||
elseif err then
|
elseif err then
|
||||||
vim.notify(string.format("[LSP][%s] %s", client.name, err), vim.log.levels.WARN)
|
vim.notify(string.format('[LSP][%s] %s', client.name, err), vim.log.levels.WARN)
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
@@ -310,7 +309,7 @@ end
|
|||||||
---the remaining clients in the order as they occur in the `order` list.
|
---the remaining clients in the order as they occur in the `order` list.
|
||||||
function M.formatting_seq_sync(options, timeout_ms, order)
|
function M.formatting_seq_sync(options, timeout_ms, order)
|
||||||
vim.notify_once('vim.lsp.buf.formatting_seq_sync is deprecated. Use vim.lsp.buf.format instead', vim.log.levels.WARN)
|
vim.notify_once('vim.lsp.buf.formatting_seq_sync is deprecated. Use vim.lsp.buf.format instead', vim.log.levels.WARN)
|
||||||
local clients = vim.tbl_values(vim.lsp.buf_get_clients());
|
local clients = vim.tbl_values(vim.lsp.buf_get_clients())
|
||||||
local bufnr = vim.api.nvim_get_current_buf()
|
local bufnr = vim.api.nvim_get_current_buf()
|
||||||
|
|
||||||
-- sort the clients according to `order`
|
-- sort the clients according to `order`
|
||||||
@@ -326,13 +325,18 @@ function M.formatting_seq_sync(options, timeout_ms, order)
|
|||||||
|
|
||||||
-- loop through the clients and make synchronous formatting requests
|
-- loop through the clients and make synchronous formatting requests
|
||||||
for _, client in pairs(clients) do
|
for _, client in pairs(clients) do
|
||||||
if vim.tbl_get(client.server_capabilities, "documentFormattingProvider") then
|
if vim.tbl_get(client.server_capabilities, 'documentFormattingProvider') then
|
||||||
local params = util.make_formatting_params(options)
|
local params = util.make_formatting_params(options)
|
||||||
local result, err = client.request_sync("textDocument/formatting", params, timeout_ms, vim.api.nvim_get_current_buf())
|
local result, err = client.request_sync(
|
||||||
|
'textDocument/formatting',
|
||||||
|
params,
|
||||||
|
timeout_ms,
|
||||||
|
vim.api.nvim_get_current_buf()
|
||||||
|
)
|
||||||
if result and result.result then
|
if result and result.result then
|
||||||
util.apply_text_edits(result.result, bufnr, client.offset_encoding)
|
util.apply_text_edits(result.result, bufnr, client.offset_encoding)
|
||||||
elseif err then
|
elseif err then
|
||||||
vim.notify(string.format("vim.lsp.buf.formatting_seq_sync: (%s) %s", client.name, err), vim.log.levels.WARN)
|
vim.notify(string.format('vim.lsp.buf.formatting_seq_sync: (%s) %s', client.name, err), vim.log.levels.WARN)
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
@@ -377,20 +381,18 @@ function M.rename(new_name, options)
|
|||||||
if options.filter then
|
if options.filter then
|
||||||
clients = options.filter(clients)
|
clients = options.filter(clients)
|
||||||
elseif options.name then
|
elseif options.name then
|
||||||
clients = vim.tbl_filter(
|
clients = vim.tbl_filter(function(client)
|
||||||
function(client) return client.name == options.name end,
|
return client.name == options.name
|
||||||
clients
|
end, clients)
|
||||||
)
|
|
||||||
end
|
end
|
||||||
|
|
||||||
-- Clients must at least support rename, prepareRename is optional
|
-- Clients must at least support rename, prepareRename is optional
|
||||||
clients = vim.tbl_filter(
|
clients = vim.tbl_filter(function(client)
|
||||||
function(client) return client.supports_method("textDocument/rename") end,
|
return client.supports_method('textDocument/rename')
|
||||||
clients
|
end, clients)
|
||||||
)
|
|
||||||
|
|
||||||
if #clients == 0 then
|
if #clients == 0 then
|
||||||
vim.notify("[LSP] Rename, no matching language servers with rename capability.")
|
vim.notify('[LSP] Rename, no matching language servers with rename capability.')
|
||||||
end
|
end
|
||||||
|
|
||||||
local win = vim.api.nvim_get_current_win()
|
local win = vim.api.nvim_get_current_win()
|
||||||
@@ -427,7 +429,7 @@ function M.rename(new_name, options)
|
|||||||
end, bufnr)
|
end, bufnr)
|
||||||
end
|
end
|
||||||
|
|
||||||
if client.supports_method("textDocument/prepareRename") then
|
if client.supports_method('textDocument/prepareRename') then
|
||||||
local params = util.make_position_params(win, client.offset_encoding)
|
local params = util.make_position_params(win, client.offset_encoding)
|
||||||
client.request('textDocument/prepareRename', params, function(err, result)
|
client.request('textDocument/prepareRename', params, function(err, result)
|
||||||
if err or result == nil then
|
if err or result == nil then
|
||||||
@@ -446,7 +448,7 @@ function M.rename(new_name, options)
|
|||||||
end
|
end
|
||||||
|
|
||||||
local prompt_opts = {
|
local prompt_opts = {
|
||||||
prompt = "New Name: "
|
prompt = 'New Name: ',
|
||||||
}
|
}
|
||||||
-- result: Range | { range: Range, placeholder: string }
|
-- result: Range | { range: Range, placeholder: string }
|
||||||
if result.placeholder then
|
if result.placeholder then
|
||||||
@@ -466,15 +468,15 @@ function M.rename(new_name, options)
|
|||||||
end)
|
end)
|
||||||
end, bufnr)
|
end, bufnr)
|
||||||
else
|
else
|
||||||
assert(client.supports_method("textDocument/rename"), 'Client must support textDocument/rename')
|
assert(client.supports_method('textDocument/rename'), 'Client must support textDocument/rename')
|
||||||
if new_name then
|
if new_name then
|
||||||
rename(new_name)
|
rename(new_name)
|
||||||
return
|
return
|
||||||
end
|
end
|
||||||
|
|
||||||
local prompt_opts = {
|
local prompt_opts = {
|
||||||
prompt = "New Name: ",
|
prompt = 'New Name: ',
|
||||||
default = cword
|
default = cword,
|
||||||
}
|
}
|
||||||
vim.ui.input(prompt_opts, function(input)
|
vim.ui.input(prompt_opts, function(input)
|
||||||
if not input or #input == 0 then
|
if not input or #input == 0 then
|
||||||
@@ -493,10 +495,10 @@ end
|
|||||||
---@param context (table) Context for the request
|
---@param context (table) Context for the request
|
||||||
---@see https://microsoft.github.io/language-server-protocol/specifications/specification-current/#textDocument_references
|
---@see https://microsoft.github.io/language-server-protocol/specifications/specification-current/#textDocument_references
|
||||||
function M.references(context)
|
function M.references(context)
|
||||||
validate { context = { context, 't', true } }
|
validate({ context = { context, 't', true } })
|
||||||
local params = util.make_position_params()
|
local params = util.make_position_params()
|
||||||
params.context = context or {
|
params.context = context or {
|
||||||
includeDeclaration = true;
|
includeDeclaration = true,
|
||||||
}
|
}
|
||||||
request('textDocument/references', params)
|
request('textDocument/references', params)
|
||||||
end
|
end
|
||||||
@@ -510,14 +512,16 @@ end
|
|||||||
|
|
||||||
---@private
|
---@private
|
||||||
local function pick_call_hierarchy_item(call_hierarchy_items)
|
local function pick_call_hierarchy_item(call_hierarchy_items)
|
||||||
if not call_hierarchy_items then return end
|
if not call_hierarchy_items then
|
||||||
|
return
|
||||||
|
end
|
||||||
if #call_hierarchy_items == 1 then
|
if #call_hierarchy_items == 1 then
|
||||||
return call_hierarchy_items[1]
|
return call_hierarchy_items[1]
|
||||||
end
|
end
|
||||||
local items = {}
|
local items = {}
|
||||||
for i, item in pairs(call_hierarchy_items) do
|
for i, item in pairs(call_hierarchy_items) do
|
||||||
local entry = item.detail or item.name
|
local entry = item.detail or item.name
|
||||||
table.insert(items, string.format("%d. %s", i, entry))
|
table.insert(items, string.format('%d. %s', i, entry))
|
||||||
end
|
end
|
||||||
local choice = vim.fn.inputlist(items)
|
local choice = vim.fn.inputlist(items)
|
||||||
if choice < 1 or choice > #items then
|
if choice < 1 or choice > #items then
|
||||||
@@ -539,8 +543,8 @@ local function call_hierarchy(method)
|
|||||||
if client then
|
if client then
|
||||||
client.request(method, { item = call_hierarchy_item }, nil, ctx.bufnr)
|
client.request(method, { item = call_hierarchy_item }, nil, ctx.bufnr)
|
||||||
else
|
else
|
||||||
vim.notify(string.format(
|
vim.notify(
|
||||||
'Client with id=%d disappeared during call hierarchy request', ctx.client_id),
|
string.format('Client with id=%d disappeared during call hierarchy request', ctx.client_id),
|
||||||
vim.log.levels.WARN
|
vim.log.levels.WARN
|
||||||
)
|
)
|
||||||
end
|
end
|
||||||
@@ -576,20 +580,25 @@ end
|
|||||||
--- Add the folder at path to the workspace folders. If {path} is
|
--- Add the folder at path to the workspace folders. If {path} is
|
||||||
--- not provided, the user will be prompted for a path using |input()|.
|
--- not provided, the user will be prompted for a path using |input()|.
|
||||||
function M.add_workspace_folder(workspace_folder)
|
function M.add_workspace_folder(workspace_folder)
|
||||||
workspace_folder = workspace_folder or npcall(vfn.input, "Workspace Folder: ", vfn.expand('%:p:h'), 'dir')
|
workspace_folder = workspace_folder or npcall(vfn.input, 'Workspace Folder: ', vfn.expand('%:p:h'), 'dir')
|
||||||
vim.api.nvim_command("redraw")
|
vim.api.nvim_command('redraw')
|
||||||
if not (workspace_folder and #workspace_folder > 0) then return end
|
if not (workspace_folder and #workspace_folder > 0) then
|
||||||
if vim.fn.isdirectory(workspace_folder) == 0 then
|
|
||||||
print(workspace_folder, " is not a valid directory")
|
|
||||||
return
|
return
|
||||||
end
|
end
|
||||||
local params = util.make_workspace_params({{uri = vim.uri_from_fname(workspace_folder); name = workspace_folder}}, {{}})
|
if vim.fn.isdirectory(workspace_folder) == 0 then
|
||||||
|
print(workspace_folder, ' is not a valid directory')
|
||||||
|
return
|
||||||
|
end
|
||||||
|
local params = util.make_workspace_params(
|
||||||
|
{ { uri = vim.uri_from_fname(workspace_folder), name = workspace_folder } },
|
||||||
|
{ {} }
|
||||||
|
)
|
||||||
for _, client in pairs(vim.lsp.buf_get_clients()) do
|
for _, client in pairs(vim.lsp.buf_get_clients()) do
|
||||||
local found = false
|
local found = false
|
||||||
for _, folder in pairs(client.workspace_folders or {}) do
|
for _, folder in pairs(client.workspace_folders or {}) do
|
||||||
if folder.name == workspace_folder then
|
if folder.name == workspace_folder then
|
||||||
found = true
|
found = true
|
||||||
print(workspace_folder, "is already part of this workspace")
|
print(workspace_folder, 'is already part of this workspace')
|
||||||
break
|
break
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
@@ -607,10 +616,15 @@ end
|
|||||||
--- {path} is not provided, the user will be prompted for
|
--- {path} is not provided, the user will be prompted for
|
||||||
--- a path using |input()|.
|
--- a path using |input()|.
|
||||||
function M.remove_workspace_folder(workspace_folder)
|
function M.remove_workspace_folder(workspace_folder)
|
||||||
workspace_folder = workspace_folder or npcall(vfn.input, "Workspace Folder: ", vfn.expand('%:p:h'))
|
workspace_folder = workspace_folder or npcall(vfn.input, 'Workspace Folder: ', vfn.expand('%:p:h'))
|
||||||
vim.api.nvim_command("redraw")
|
vim.api.nvim_command('redraw')
|
||||||
if not (workspace_folder and #workspace_folder > 0) then return end
|
if not (workspace_folder and #workspace_folder > 0) then
|
||||||
local params = util.make_workspace_params({{}}, {{uri = vim.uri_from_fname(workspace_folder); name = workspace_folder}})
|
return
|
||||||
|
end
|
||||||
|
local params = util.make_workspace_params(
|
||||||
|
{ {} },
|
||||||
|
{ { uri = vim.uri_from_fname(workspace_folder), name = workspace_folder } }
|
||||||
|
)
|
||||||
for _, client in pairs(vim.lsp.buf_get_clients()) do
|
for _, client in pairs(vim.lsp.buf_get_clients()) do
|
||||||
for idx, folder in pairs(client.workspace_folders) do
|
for idx, folder in pairs(client.workspace_folders) do
|
||||||
if folder.name == workspace_folder then
|
if folder.name == workspace_folder then
|
||||||
@@ -620,7 +634,7 @@ function M.remove_workspace_folder(workspace_folder)
|
|||||||
end
|
end
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
print(workspace_folder, "is not currently part of the workspace")
|
print(workspace_folder, 'is not currently part of the workspace')
|
||||||
end
|
end
|
||||||
|
|
||||||
--- Lists all symbols in the current workspace in the quickfix window.
|
--- Lists all symbols in the current workspace in the quickfix window.
|
||||||
@@ -631,7 +645,7 @@ end
|
|||||||
---
|
---
|
||||||
---@param query (string, optional)
|
---@param query (string, optional)
|
||||||
function M.workspace_symbol(query)
|
function M.workspace_symbol(query)
|
||||||
query = query or npcall(vfn.input, "Query: ")
|
query = query or npcall(vfn.input, 'Query: ')
|
||||||
if query == nil then
|
if query == nil then
|
||||||
return
|
return
|
||||||
end
|
end
|
||||||
@@ -665,7 +679,6 @@ function M.clear_references()
|
|||||||
util.buf_clear_references()
|
util.buf_clear_references()
|
||||||
end
|
end
|
||||||
|
|
||||||
|
|
||||||
---@private
|
---@private
|
||||||
--
|
--
|
||||||
--- This is not public because the main extension point is
|
--- This is not public because the main extension point is
|
||||||
@@ -728,10 +741,11 @@ local function on_code_action_results(results, ctx, options)
|
|||||||
--
|
--
|
||||||
local client = vim.lsp.get_client_by_id(action_tuple[1])
|
local client = vim.lsp.get_client_by_id(action_tuple[1])
|
||||||
local action = action_tuple[2]
|
local action = action_tuple[2]
|
||||||
if not action.edit
|
if
|
||||||
|
not action.edit
|
||||||
and client
|
and client
|
||||||
and vim.tbl_get(client.server_capabilities, "codeActionProvider", "resolveProvider") then
|
and vim.tbl_get(client.server_capabilities, 'codeActionProvider', 'resolveProvider')
|
||||||
|
then
|
||||||
client.request('codeAction/resolve', action, function(err, resolved_action)
|
client.request('codeAction/resolve', action, function(err, resolved_action)
|
||||||
if err then
|
if err then
|
||||||
vim.notify(err.code .. ': ' .. err.message, vim.log.levels.ERROR)
|
vim.notify(err.code .. ': ' .. err.message, vim.log.levels.ERROR)
|
||||||
@@ -761,7 +775,6 @@ local function on_code_action_results(results, ctx, options)
|
|||||||
}, on_user_choice)
|
}, on_user_choice)
|
||||||
end
|
end
|
||||||
|
|
||||||
|
|
||||||
--- Requests code actions from all clients and calls the handler exactly once
|
--- Requests code actions from all clients and calls the handler exactly once
|
||||||
--- with all aggregated results
|
--- with all aggregated results
|
||||||
---@private
|
---@private
|
||||||
@@ -794,7 +807,7 @@ end
|
|||||||
--- (after filtering), the action is applied without user query.
|
--- (after filtering), the action is applied without user query.
|
||||||
---@see https://microsoft.github.io/language-server-protocol/specifications/specification-current/#textDocument_codeAction
|
---@see https://microsoft.github.io/language-server-protocol/specifications/specification-current/#textDocument_codeAction
|
||||||
function M.code_action(options)
|
function M.code_action(options)
|
||||||
validate { options = { options, 't', true } }
|
validate({ options = { options, 't', true } })
|
||||||
options = options or {}
|
options = options or {}
|
||||||
-- Detect old API call code_action(context) which should now be
|
-- Detect old API call code_action(context) which should now be
|
||||||
-- code_action({ context = context} )
|
-- code_action({ context = context} )
|
||||||
@@ -826,7 +839,7 @@ end
|
|||||||
---@param end_pos ({number, number}, optional) mark-indexed position.
|
---@param end_pos ({number, number}, optional) mark-indexed position.
|
||||||
---Defaults to the end of the last visual selection.
|
---Defaults to the end of the last visual selection.
|
||||||
function M.range_code_action(context, start_pos, end_pos)
|
function M.range_code_action(context, start_pos, end_pos)
|
||||||
validate { context = { context, 't', true } }
|
validate({ context = { context, 't', true } })
|
||||||
context = context or {}
|
context = context or {}
|
||||||
if not context.diagnostics then
|
if not context.diagnostics then
|
||||||
context.diagnostics = vim.lsp.diagnostic.get_line_diagnostics()
|
context.diagnostics = vim.lsp.diagnostic.get_line_diagnostics()
|
||||||
@@ -841,10 +854,10 @@ end
|
|||||||
---@param command_params table A valid `ExecuteCommandParams` object
|
---@param command_params table A valid `ExecuteCommandParams` object
|
||||||
---@see https://microsoft.github.io/language-server-protocol/specifications/specification-current/#workspace_executeCommand
|
---@see https://microsoft.github.io/language-server-protocol/specifications/specification-current/#workspace_executeCommand
|
||||||
function M.execute_command(command_params)
|
function M.execute_command(command_params)
|
||||||
validate {
|
validate({
|
||||||
command = { command_params.command, 's' },
|
command = { command_params.command, 's' },
|
||||||
arguments = { command_params.arguments, 't', true }
|
arguments = { command_params.arguments, 't', true },
|
||||||
}
|
})
|
||||||
command_params = {
|
command_params = {
|
||||||
command = command_params.command,
|
command = command_params.command,
|
||||||
arguments = command_params.arguments,
|
arguments = command_params.arguments,
|
||||||
|
@@ -12,7 +12,7 @@ local lens_cache_by_buf = setmetatable({}, {
|
|||||||
__index = function(t, b)
|
__index = function(t, b)
|
||||||
local key = b > 0 and b or api.nvim_get_current_buf()
|
local key = b > 0 and b or api.nvim_get_current_buf()
|
||||||
return rawget(t, key)
|
return rawget(t, key)
|
||||||
end
|
end,
|
||||||
})
|
})
|
||||||
|
|
||||||
local namespaces = setmetatable({}, {
|
local namespaces = setmetatable({}, {
|
||||||
@@ -20,13 +20,12 @@ local namespaces = setmetatable({}, {
|
|||||||
local value = api.nvim_create_namespace('vim_lsp_codelens:' .. key)
|
local value = api.nvim_create_namespace('vim_lsp_codelens:' .. key)
|
||||||
rawset(t, key, value)
|
rawset(t, key, value)
|
||||||
return value
|
return value
|
||||||
end;
|
end,
|
||||||
})
|
})
|
||||||
|
|
||||||
---@private
|
---@private
|
||||||
M.__namespaces = namespaces
|
M.__namespaces = namespaces
|
||||||
|
|
||||||
|
|
||||||
---@private
|
---@private
|
||||||
local function execute_lens(lens, bufnr, client_id)
|
local function execute_lens(lens, bufnr, client_id)
|
||||||
local line = lens.range.start.line
|
local line = lens.range.start.line
|
||||||
@@ -44,9 +43,13 @@ local function execute_lens(lens, bufnr, client_id)
|
|||||||
local command_provider = client.server_capabilities.executeCommandProvider
|
local command_provider = client.server_capabilities.executeCommandProvider
|
||||||
local commands = type(command_provider) == 'table' and command_provider.commands or {}
|
local commands = type(command_provider) == 'table' and command_provider.commands or {}
|
||||||
if not vim.tbl_contains(commands, command.command) then
|
if not vim.tbl_contains(commands, command.command) then
|
||||||
vim.notify(string.format(
|
vim.notify(
|
||||||
"Language server does not support command `%s`. This command may require a client extension.", command.command),
|
string.format(
|
||||||
vim.log.levels.WARN)
|
'Language server does not support command `%s`. This command may require a client extension.',
|
||||||
|
command.command
|
||||||
|
),
|
||||||
|
vim.log.levels.WARN
|
||||||
|
)
|
||||||
return
|
return
|
||||||
end
|
end
|
||||||
client.request('workspace/executeCommand', command, function(...)
|
client.request('workspace/executeCommand', command, function(...)
|
||||||
@@ -56,14 +59,15 @@ local function execute_lens(lens, bufnr, client_id)
|
|||||||
end, bufnr)
|
end, bufnr)
|
||||||
end
|
end
|
||||||
|
|
||||||
|
|
||||||
--- Return all lenses for the given buffer
|
--- Return all lenses for the given buffer
|
||||||
---
|
---
|
||||||
---@param bufnr number Buffer number. 0 can be used for the current buffer.
|
---@param bufnr number Buffer number. 0 can be used for the current buffer.
|
||||||
---@return table (`CodeLens[]`)
|
---@return table (`CodeLens[]`)
|
||||||
function M.get(bufnr)
|
function M.get(bufnr)
|
||||||
local lenses_by_client = lens_cache_by_buf[bufnr or 0]
|
local lenses_by_client = lens_cache_by_buf[bufnr or 0]
|
||||||
if not lenses_by_client then return {} end
|
if not lenses_by_client then
|
||||||
|
return {}
|
||||||
|
end
|
||||||
local lenses = {}
|
local lenses = {}
|
||||||
for _, client_lenses in pairs(lenses_by_client) do
|
for _, client_lenses in pairs(lenses_by_client) do
|
||||||
vim.list_extend(lenses, client_lenses)
|
vim.list_extend(lenses, client_lenses)
|
||||||
@@ -71,7 +75,6 @@ function M.get(bufnr)
|
|||||||
return lenses
|
return lenses
|
||||||
end
|
end
|
||||||
|
|
||||||
|
|
||||||
--- Run the code lens in the current line
|
--- Run the code lens in the current line
|
||||||
---
|
---
|
||||||
function M.run()
|
function M.run()
|
||||||
@@ -105,7 +108,6 @@ function M.run()
|
|||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
|
|
||||||
--- Display the lenses using virtual text
|
--- Display the lenses using virtual text
|
||||||
---
|
---
|
||||||
---@param lenses table of lenses to display (`CodeLens[] | null`)
|
---@param lenses table of lenses to display (`CodeLens[] | null`)
|
||||||
@@ -139,13 +141,14 @@ function M.display(lenses, bufnr, client_id)
|
|||||||
end
|
end
|
||||||
end
|
end
|
||||||
if #chunks > 0 then
|
if #chunks > 0 then
|
||||||
api.nvim_buf_set_extmark(bufnr, ns, i, 0, { virt_text = chunks,
|
api.nvim_buf_set_extmark(bufnr, ns, i, 0, {
|
||||||
hl_mode="combine" })
|
virt_text = chunks,
|
||||||
|
hl_mode = 'combine',
|
||||||
|
})
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
|
|
||||||
--- Store lenses for a specific buffer and client
|
--- Store lenses for a specific buffer and client
|
||||||
---
|
---
|
||||||
---@param lenses table of lenses to store (`CodeLens[] | null`)
|
---@param lenses table of lenses to store (`CodeLens[] | null`)
|
||||||
@@ -158,16 +161,17 @@ function M.save(lenses, bufnr, client_id)
|
|||||||
lens_cache_by_buf[bufnr] = lenses_by_client
|
lens_cache_by_buf[bufnr] = lenses_by_client
|
||||||
local ns = namespaces[client_id]
|
local ns = namespaces[client_id]
|
||||||
api.nvim_buf_attach(bufnr, false, {
|
api.nvim_buf_attach(bufnr, false, {
|
||||||
on_detach = function(b) lens_cache_by_buf[b] = nil end,
|
on_detach = function(b)
|
||||||
|
lens_cache_by_buf[b] = nil
|
||||||
|
end,
|
||||||
on_lines = function(_, b, _, first_lnum, last_lnum)
|
on_lines = function(_, b, _, first_lnum, last_lnum)
|
||||||
api.nvim_buf_clear_namespace(b, ns, first_lnum, last_lnum)
|
api.nvim_buf_clear_namespace(b, ns, first_lnum, last_lnum)
|
||||||
end
|
end,
|
||||||
})
|
})
|
||||||
end
|
end
|
||||||
lenses_by_client[client_id] = lenses
|
lenses_by_client[client_id] = lenses
|
||||||
end
|
end
|
||||||
|
|
||||||
|
|
||||||
---@private
|
---@private
|
||||||
local function resolve_lenses(lenses, bufnr, client_id, callback)
|
local function resolve_lenses(lenses, bufnr, client_id, callback)
|
||||||
lenses = lenses or {}
|
lenses = lenses or {}
|
||||||
@@ -201,8 +205,7 @@ local function resolve_lenses(lenses, bufnr, client_id, callback)
|
|||||||
ns,
|
ns,
|
||||||
lens.range.start.line,
|
lens.range.start.line,
|
||||||
0,
|
0,
|
||||||
{ virt_text = {{ lens.command.title, 'LspCodeLens' }},
|
{ virt_text = { { lens.command.title, 'LspCodeLens' } }, hl_mode = 'combine' }
|
||||||
hl_mode="combine" }
|
|
||||||
)
|
)
|
||||||
end
|
end
|
||||||
countdown()
|
countdown()
|
||||||
@@ -211,13 +214,12 @@ local function resolve_lenses(lenses, bufnr, client_id, callback)
|
|||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
|
|
||||||
--- |lsp-handler| for the method `textDocument/codeLens`
|
--- |lsp-handler| for the method `textDocument/codeLens`
|
||||||
---
|
---
|
||||||
function M.on_codelens(err, result, ctx, _)
|
function M.on_codelens(err, result, ctx, _)
|
||||||
if err then
|
if err then
|
||||||
active_refreshes[ctx.bufnr] = nil
|
active_refreshes[ctx.bufnr] = nil
|
||||||
local _ = log.error() and log.error("codelens", err)
|
local _ = log.error() and log.error('codelens', err)
|
||||||
return
|
return
|
||||||
end
|
end
|
||||||
|
|
||||||
@@ -232,7 +234,6 @@ function M.on_codelens(err, result, ctx, _)
|
|||||||
end)
|
end)
|
||||||
end
|
end
|
||||||
|
|
||||||
|
|
||||||
--- Refresh the codelens for the current buffer
|
--- Refresh the codelens for the current buffer
|
||||||
---
|
---
|
||||||
--- It is recommended to trigger this using an autocmd or via keymap.
|
--- It is recommended to trigger this using an autocmd or via keymap.
|
||||||
@@ -243,7 +244,7 @@ end
|
|||||||
---
|
---
|
||||||
function M.refresh()
|
function M.refresh()
|
||||||
local params = {
|
local params = {
|
||||||
textDocument = util.make_text_document_params()
|
textDocument = util.make_text_document_params(),
|
||||||
}
|
}
|
||||||
local bufnr = api.nvim_get_current_buf()
|
local bufnr = api.nvim_get_current_buf()
|
||||||
if active_refreshes[bufnr] then
|
if active_refreshes[bufnr] then
|
||||||
@@ -253,5 +254,4 @@ function M.refresh()
|
|||||||
vim.lsp.buf_request(0, 'textDocument/codeLens', params, M.on_codelens)
|
vim.lsp.buf_request(0, 'textDocument/codeLens', params, M.on_codelens)
|
||||||
end
|
end
|
||||||
|
|
||||||
|
|
||||||
return M
|
return M
|
||||||
|
@@ -50,12 +50,12 @@ end
|
|||||||
|
|
||||||
---@private
|
---@private
|
||||||
local function line_byte_from_position(lines, lnum, col, offset_encoding)
|
local function line_byte_from_position(lines, lnum, col, offset_encoding)
|
||||||
if not lines or offset_encoding == "utf-8" then
|
if not lines or offset_encoding == 'utf-8' then
|
||||||
return col
|
return col
|
||||||
end
|
end
|
||||||
|
|
||||||
local line = lines[lnum + 1]
|
local line = lines[lnum + 1]
|
||||||
local ok, result = pcall(vim.str_byteindex, line, col, offset_encoding == "utf-16")
|
local ok, result = pcall(vim.str_byteindex, line, col, offset_encoding == 'utf-16')
|
||||||
if ok then
|
if ok then
|
||||||
return result
|
return result
|
||||||
end
|
end
|
||||||
@@ -75,7 +75,7 @@ local function get_buf_lines(bufnr)
|
|||||||
return
|
return
|
||||||
end
|
end
|
||||||
|
|
||||||
local content = f:read("*a")
|
local content = f:read('*a')
|
||||||
if not content then
|
if not content then
|
||||||
-- Some LSP servers report diagnostics at a directory level, in which case
|
-- Some LSP servers report diagnostics at a directory level, in which case
|
||||||
-- io.read() returns nil
|
-- io.read() returns nil
|
||||||
@@ -83,7 +83,7 @@ local function get_buf_lines(bufnr)
|
|||||||
return
|
return
|
||||||
end
|
end
|
||||||
|
|
||||||
local lines = vim.split(content, "\n")
|
local lines = vim.split(content, '\n')
|
||||||
f:close()
|
f:close()
|
||||||
return lines
|
return lines
|
||||||
end
|
end
|
||||||
@@ -92,10 +92,10 @@ end
|
|||||||
local function diagnostic_lsp_to_vim(diagnostics, bufnr, client_id)
|
local function diagnostic_lsp_to_vim(diagnostics, bufnr, client_id)
|
||||||
local buf_lines = get_buf_lines(bufnr)
|
local buf_lines = get_buf_lines(bufnr)
|
||||||
local client = vim.lsp.get_client_by_id(client_id)
|
local client = vim.lsp.get_client_by_id(client_id)
|
||||||
local offset_encoding = client and client.offset_encoding or "utf-16"
|
local offset_encoding = client and client.offset_encoding or 'utf-16'
|
||||||
return vim.tbl_map(function(diagnostic)
|
return vim.tbl_map(function(diagnostic)
|
||||||
local start = diagnostic.range.start
|
local start = diagnostic.range.start
|
||||||
local _end = diagnostic.range["end"]
|
local _end = diagnostic.range['end']
|
||||||
return {
|
return {
|
||||||
lnum = start.line,
|
lnum = start.line,
|
||||||
col = line_byte_from_position(buf_lines, start.line, start.character, offset_encoding),
|
col = line_byte_from_position(buf_lines, start.line, start.character, offset_encoding),
|
||||||
@@ -122,14 +122,14 @@ end
|
|||||||
---@private
|
---@private
|
||||||
local function diagnostic_vim_to_lsp(diagnostics)
|
local function diagnostic_vim_to_lsp(diagnostics)
|
||||||
return vim.tbl_map(function(diagnostic)
|
return vim.tbl_map(function(diagnostic)
|
||||||
return vim.tbl_extend("keep", {
|
return vim.tbl_extend('keep', {
|
||||||
-- "keep" the below fields over any duplicate fields in diagnostic.user_data.lsp
|
-- "keep" the below fields over any duplicate fields in diagnostic.user_data.lsp
|
||||||
range = {
|
range = {
|
||||||
start = {
|
start = {
|
||||||
line = diagnostic.lnum,
|
line = diagnostic.lnum,
|
||||||
character = diagnostic.col,
|
character = diagnostic.col,
|
||||||
},
|
},
|
||||||
["end"] = {
|
['end'] = {
|
||||||
line = diagnostic.end_lnum,
|
line = diagnostic.end_lnum,
|
||||||
character = diagnostic.end_col,
|
character = diagnostic.end_col,
|
||||||
},
|
},
|
||||||
@@ -148,10 +148,10 @@ local _client_namespaces = {}
|
|||||||
---
|
---
|
||||||
---@param client_id number The id of the LSP client
|
---@param client_id number The id of the LSP client
|
||||||
function M.get_namespace(client_id)
|
function M.get_namespace(client_id)
|
||||||
vim.validate { client_id = { client_id, 'n' } }
|
vim.validate({ client_id = { client_id, 'n' } })
|
||||||
if not _client_namespaces[client_id] then
|
if not _client_namespaces[client_id] then
|
||||||
local client = vim.lsp.get_client_by_id(client_id)
|
local client = vim.lsp.get_client_by_id(client_id)
|
||||||
local name = string.format("vim.lsp.%s.%d", client and client.name or "unknown", client_id)
|
local name = string.format('vim.lsp.%s.%d', client and client.name or 'unknown', client_id)
|
||||||
_client_namespaces[client_id] = vim.api.nvim_create_namespace(name)
|
_client_namespaces[client_id] = vim.api.nvim_create_namespace(name)
|
||||||
end
|
end
|
||||||
return _client_namespaces[client_id]
|
return _client_namespaces[client_id]
|
||||||
@@ -240,7 +240,6 @@ end
|
|||||||
|
|
||||||
-- Deprecated Functions {{{
|
-- Deprecated Functions {{{
|
||||||
|
|
||||||
|
|
||||||
--- Save diagnostics to the current buffer.
|
--- Save diagnostics to the current buffer.
|
||||||
---
|
---
|
||||||
---@deprecated Prefer |vim.diagnostic.set()|
|
---@deprecated Prefer |vim.diagnostic.set()|
|
||||||
@@ -288,7 +287,9 @@ end
|
|||||||
---@param predicate function|nil Optional function for filtering diagnostics
|
---@param predicate function|nil Optional function for filtering diagnostics
|
||||||
function M.get(bufnr, client_id, predicate)
|
function M.get(bufnr, client_id, predicate)
|
||||||
vim.deprecate('vim.lsp.diagnostic.get', 'vim.diagnostic.get', '0.8')
|
vim.deprecate('vim.lsp.diagnostic.get', 'vim.diagnostic.get', '0.8')
|
||||||
predicate = predicate or function() return true end
|
predicate = predicate or function()
|
||||||
|
return true
|
||||||
|
end
|
||||||
if client_id == nil then
|
if client_id == nil then
|
||||||
local all_diagnostics = {}
|
local all_diagnostics = {}
|
||||||
vim.lsp.for_each_buffer_client(bufnr, function(_, iter_client_id, _)
|
vim.lsp.for_each_buffer_client(bufnr, function(_, iter_client_id, _)
|
||||||
@@ -558,7 +559,7 @@ end
|
|||||||
function M.show_position_diagnostics(opts, buf_nr, position)
|
function M.show_position_diagnostics(opts, buf_nr, position)
|
||||||
vim.deprecate('vim.lsp.diagnostic.show_position_diagnostics', 'vim.diagnostic.open_float', '0.8')
|
vim.deprecate('vim.lsp.diagnostic.show_position_diagnostics', 'vim.diagnostic.open_float', '0.8')
|
||||||
opts = opts or {}
|
opts = opts or {}
|
||||||
opts.scope = "cursor"
|
opts.scope = 'cursor'
|
||||||
opts.pos = position
|
opts.pos = position
|
||||||
if opts.severity then
|
if opts.severity then
|
||||||
opts.severity = severity_lsp_to_vim(opts.severity)
|
opts.severity = severity_lsp_to_vim(opts.severity)
|
||||||
@@ -582,7 +583,7 @@ end
|
|||||||
function M.show_line_diagnostics(opts, buf_nr, line_nr, client_id)
|
function M.show_line_diagnostics(opts, buf_nr, line_nr, client_id)
|
||||||
vim.deprecate('vim.lsp.diagnostic.show_line_diagnostics', 'vim.diagnostic.open_float', '0.8')
|
vim.deprecate('vim.lsp.diagnostic.show_line_diagnostics', 'vim.diagnostic.open_float', '0.8')
|
||||||
opts = opts or {}
|
opts = opts or {}
|
||||||
opts.scope = "line"
|
opts.scope = 'line'
|
||||||
opts.pos = line_nr
|
opts.pos = line_nr
|
||||||
if client_id then
|
if client_id then
|
||||||
opts.namespace = M.get_namespace(client_id)
|
opts.namespace = M.get_namespace(client_id)
|
||||||
|
@@ -1,6 +1,6 @@
|
|||||||
local log = require 'vim.lsp.log'
|
local log = require('vim.lsp.log')
|
||||||
local protocol = require 'vim.lsp.protocol'
|
local protocol = require('vim.lsp.protocol')
|
||||||
local util = require 'vim.lsp.util'
|
local util = require('vim.lsp.util')
|
||||||
local vim = vim
|
local vim = vim
|
||||||
local api = vim.api
|
local api = vim.api
|
||||||
|
|
||||||
@@ -12,8 +12,8 @@ local M = {}
|
|||||||
--- Writes to error buffer.
|
--- Writes to error buffer.
|
||||||
---@param ... (table of strings) Will be concatenated before being written
|
---@param ... (table of strings) Will be concatenated before being written
|
||||||
local function err_message(...)
|
local function err_message(...)
|
||||||
vim.notify(table.concat(vim.tbl_flatten{...}), vim.log.levels.ERROR)
|
vim.notify(table.concat(vim.tbl_flatten({ ... })), vim.log.levels.ERROR)
|
||||||
api.nvim_command("redraw")
|
api.nvim_command('redraw')
|
||||||
end
|
end
|
||||||
|
|
||||||
--see: https://microsoft.github.io/language-server-protocol/specifications/specification-current/#workspace_executeCommand
|
--see: https://microsoft.github.io/language-server-protocol/specifications/specification-current/#workspace_executeCommand
|
||||||
@@ -25,15 +25,17 @@ end
|
|||||||
local function progress_handler(_, result, ctx, _)
|
local function progress_handler(_, result, ctx, _)
|
||||||
local client_id = ctx.client_id
|
local client_id = ctx.client_id
|
||||||
local client = vim.lsp.get_client_by_id(client_id)
|
local client = vim.lsp.get_client_by_id(client_id)
|
||||||
local client_name = client and client.name or string.format("id=%d", client_id)
|
local client_name = client and client.name or string.format('id=%d', client_id)
|
||||||
if not client then
|
if not client then
|
||||||
err_message("LSP[", client_name, "] client has shut down during progress update")
|
err_message('LSP[', client_name, '] client has shut down during progress update')
|
||||||
return vim.NIL
|
return vim.NIL
|
||||||
end
|
end
|
||||||
local val = result.value -- unspecified yet
|
local val = result.value -- unspecified yet
|
||||||
local token = result.token -- string or number
|
local token = result.token -- string or number
|
||||||
|
|
||||||
if type(val) ~= 'table' then val = { content=val } end
|
if type(val) ~= 'table' then
|
||||||
|
val = { content = val }
|
||||||
|
end
|
||||||
if val.kind then
|
if val.kind then
|
||||||
if val.kind == 'begin' then
|
if val.kind == 'begin' then
|
||||||
client.messages.progress[token] = {
|
client.messages.progress[token] = {
|
||||||
@@ -42,11 +44,11 @@ local function progress_handler(_, result, ctx, _)
|
|||||||
percentage = val.percentage,
|
percentage = val.percentage,
|
||||||
}
|
}
|
||||||
elseif val.kind == 'report' then
|
elseif val.kind == 'report' then
|
||||||
client.messages.progress[token].message = val.message;
|
client.messages.progress[token].message = val.message
|
||||||
client.messages.progress[token].percentage = val.percentage;
|
client.messages.progress[token].percentage = val.percentage
|
||||||
elseif val.kind == 'end' then
|
elseif val.kind == 'end' then
|
||||||
if client.messages.progress[token] == nil then
|
if client.messages.progress[token] == nil then
|
||||||
err_message("LSP[", client_name, "] received `end` message with no corresponding `begin`")
|
err_message('LSP[', client_name, '] received `end` message with no corresponding `begin`')
|
||||||
else
|
else
|
||||||
client.messages.progress[token].message = val.message
|
client.messages.progress[token].message = val.message
|
||||||
client.messages.progress[token].done = true
|
client.messages.progress[token].done = true
|
||||||
@@ -57,7 +59,7 @@ local function progress_handler(_, result, ctx, _)
|
|||||||
client.messages.progress[token].done = true
|
client.messages.progress[token].done = true
|
||||||
end
|
end
|
||||||
|
|
||||||
vim.api.nvim_command("doautocmd <nomodeline> User LspProgressUpdate")
|
vim.api.nvim_command('doautocmd <nomodeline> User LspProgressUpdate')
|
||||||
end
|
end
|
||||||
|
|
||||||
--see: https://microsoft.github.io/language-server-protocol/specifications/specification-current/#progress
|
--see: https://microsoft.github.io/language-server-protocol/specifications/specification-current/#progress
|
||||||
@@ -68,9 +70,9 @@ M['window/workDoneProgress/create'] = function(_, result, ctx)
|
|||||||
local client_id = ctx.client_id
|
local client_id = ctx.client_id
|
||||||
local client = vim.lsp.get_client_by_id(client_id)
|
local client = vim.lsp.get_client_by_id(client_id)
|
||||||
local token = result.token -- string or number
|
local token = result.token -- string or number
|
||||||
local client_name = client and client.name or string.format("id=%d", client_id)
|
local client_name = client and client.name or string.format('id=%d', client_id)
|
||||||
if not client then
|
if not client then
|
||||||
err_message("LSP[", client_name, "] client has shut down while creating progress report")
|
err_message('LSP[', client_name, '] client has shut down while creating progress report')
|
||||||
return vim.NIL
|
return vim.NIL
|
||||||
end
|
end
|
||||||
client.messages.progress[token] = {}
|
client.messages.progress[token] = {}
|
||||||
@@ -79,14 +81,13 @@ end
|
|||||||
|
|
||||||
--see: https://microsoft.github.io/language-server-protocol/specifications/specification-current/#window_showMessageRequest
|
--see: https://microsoft.github.io/language-server-protocol/specifications/specification-current/#window_showMessageRequest
|
||||||
M['window/showMessageRequest'] = function(_, result)
|
M['window/showMessageRequest'] = function(_, result)
|
||||||
|
|
||||||
local actions = result.actions
|
local actions = result.actions
|
||||||
print(result.message)
|
print(result.message)
|
||||||
local option_strings = {result.message, "\nRequest Actions:"}
|
local option_strings = { result.message, '\nRequest Actions:' }
|
||||||
for i, action in ipairs(actions) do
|
for i, action in ipairs(actions) do
|
||||||
local title = action.title:gsub('\r\n', '\\r\\n')
|
local title = action.title:gsub('\r\n', '\\r\\n')
|
||||||
title = title:gsub('\n', '\\n')
|
title = title:gsub('\n', '\\n')
|
||||||
table.insert(option_strings, string.format("%d. %s", i, title))
|
table.insert(option_strings, string.format('%d. %s', i, title))
|
||||||
end
|
end
|
||||||
|
|
||||||
-- window/showMessageRequest can return either MessageActionItem[] or null.
|
-- window/showMessageRequest can return either MessageActionItem[] or null.
|
||||||
@@ -101,11 +102,11 @@ end
|
|||||||
--see: https://microsoft.github.io/language-server-protocol/specifications/specification-current/#client_registerCapability
|
--see: https://microsoft.github.io/language-server-protocol/specifications/specification-current/#client_registerCapability
|
||||||
M['client/registerCapability'] = function(_, _, ctx)
|
M['client/registerCapability'] = function(_, _, ctx)
|
||||||
local client_id = ctx.client_id
|
local client_id = ctx.client_id
|
||||||
local warning_tpl = "The language server %s triggers a registerCapability "..
|
local warning_tpl = 'The language server %s triggers a registerCapability '
|
||||||
"handler despite dynamicRegistration set to false. "..
|
.. 'handler despite dynamicRegistration set to false. '
|
||||||
"Report upstream, this warning is harmless"
|
.. 'Report upstream, this warning is harmless'
|
||||||
local client = vim.lsp.get_client_by_id(client_id)
|
local client = vim.lsp.get_client_by_id(client_id)
|
||||||
local client_name = client and client.name or string.format("id=%d", client_id)
|
local client_name = client and client.name or string.format('id=%d', client_id)
|
||||||
local warning = string.format(warning_tpl, client_name)
|
local warning = string.format(warning_tpl, client_name)
|
||||||
log.warn(warning)
|
log.warn(warning)
|
||||||
return vim.NIL
|
return vim.NIL
|
||||||
@@ -113,17 +114,19 @@ end
|
|||||||
|
|
||||||
--see: https://microsoft.github.io/language-server-protocol/specifications/specification-current/#workspace_applyEdit
|
--see: https://microsoft.github.io/language-server-protocol/specifications/specification-current/#workspace_applyEdit
|
||||||
M['workspace/applyEdit'] = function(_, workspace_edit, ctx)
|
M['workspace/applyEdit'] = function(_, workspace_edit, ctx)
|
||||||
if not workspace_edit then return end
|
if not workspace_edit then
|
||||||
|
return
|
||||||
|
end
|
||||||
-- TODO(ashkan) Do something more with label?
|
-- TODO(ashkan) Do something more with label?
|
||||||
local client_id = ctx.client_id
|
local client_id = ctx.client_id
|
||||||
local client = vim.lsp.get_client_by_id(client_id)
|
local client = vim.lsp.get_client_by_id(client_id)
|
||||||
if workspace_edit.label then
|
if workspace_edit.label then
|
||||||
print("Workspace edit", workspace_edit.label)
|
print('Workspace edit', workspace_edit.label)
|
||||||
end
|
end
|
||||||
local status, result = pcall(util.apply_workspace_edit, workspace_edit.edit, client.offset_encoding)
|
local status, result = pcall(util.apply_workspace_edit, workspace_edit.edit, client.offset_encoding)
|
||||||
return {
|
return {
|
||||||
applied = status;
|
applied = status,
|
||||||
failureReason = result;
|
failureReason = result,
|
||||||
}
|
}
|
||||||
end
|
end
|
||||||
|
|
||||||
@@ -132,7 +135,7 @@ M['workspace/configuration'] = function(_, result, ctx)
|
|||||||
local client_id = ctx.client_id
|
local client_id = ctx.client_id
|
||||||
local client = vim.lsp.get_client_by_id(client_id)
|
local client = vim.lsp.get_client_by_id(client_id)
|
||||||
if not client then
|
if not client then
|
||||||
err_message("LSP[", client_id, "] client has shut down after sending a workspace/configuration request")
|
err_message('LSP[', client_id, '] client has shut down after sending a workspace/configuration request')
|
||||||
return
|
return
|
||||||
end
|
end
|
||||||
if not result.items then
|
if not result.items then
|
||||||
@@ -158,7 +161,7 @@ M['workspace/workspaceFolders'] = function(_, _, ctx)
|
|||||||
local client_id = ctx.client_id
|
local client_id = ctx.client_id
|
||||||
local client = vim.lsp.get_client_by_id(client_id)
|
local client = vim.lsp.get_client_by_id(client_id)
|
||||||
if not client then
|
if not client then
|
||||||
err_message("LSP[id=", client_id, "] client has shut down after sending the message")
|
err_message('LSP[id=', client_id, '] client has shut down after sending the message')
|
||||||
return
|
return
|
||||||
end
|
end
|
||||||
return client.workspace_folders or vim.NIL
|
return client.workspace_folders or vim.NIL
|
||||||
@@ -172,7 +175,6 @@ M['textDocument/codeLens'] = function(...)
|
|||||||
return require('vim.lsp.codelens').on_codelens(...)
|
return require('vim.lsp.codelens').on_codelens(...)
|
||||||
end
|
end
|
||||||
|
|
||||||
|
|
||||||
--see: https://microsoft.github.io/language-server-protocol/specifications/specification-current/#textDocument_references
|
--see: https://microsoft.github.io/language-server-protocol/specifications/specification-current/#textDocument_references
|
||||||
M['textDocument/references'] = function(_, result, ctx, config)
|
M['textDocument/references'] = function(_, result, ctx, config)
|
||||||
if not result or vim.tbl_isempty(result) then
|
if not result or vim.tbl_isempty(result) then
|
||||||
@@ -182,23 +184,22 @@ M['textDocument/references'] = function(_, result, ctx, config)
|
|||||||
config = config or {}
|
config = config or {}
|
||||||
if config.loclist then
|
if config.loclist then
|
||||||
vim.fn.setloclist(0, {}, ' ', {
|
vim.fn.setloclist(0, {}, ' ', {
|
||||||
title = 'References';
|
title = 'References',
|
||||||
items = util.locations_to_items(result, client.offset_encoding);
|
items = util.locations_to_items(result, client.offset_encoding),
|
||||||
context = ctx;
|
context = ctx,
|
||||||
})
|
})
|
||||||
api.nvim_command("lopen")
|
api.nvim_command('lopen')
|
||||||
else
|
else
|
||||||
vim.fn.setqflist({}, ' ', {
|
vim.fn.setqflist({}, ' ', {
|
||||||
title = 'References';
|
title = 'References',
|
||||||
items = util.locations_to_items(result, client.offset_encoding);
|
items = util.locations_to_items(result, client.offset_encoding),
|
||||||
context = ctx;
|
context = ctx,
|
||||||
})
|
})
|
||||||
api.nvim_command("botright copen")
|
api.nvim_command('botright copen')
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
|
|
||||||
---@private
|
---@private
|
||||||
--- Return a function that converts LSP responses to list items and opens the list
|
--- Return a function that converts LSP responses to list items and opens the list
|
||||||
---
|
---
|
||||||
@@ -218,27 +219,26 @@ local function response_to_list(map_result, entity, title_fn)
|
|||||||
config = config or {}
|
config = config or {}
|
||||||
if config.loclist then
|
if config.loclist then
|
||||||
vim.fn.setloclist(0, {}, ' ', {
|
vim.fn.setloclist(0, {}, ' ', {
|
||||||
title = title_fn(ctx);
|
title = title_fn(ctx),
|
||||||
items = map_result(result, ctx.bufnr);
|
items = map_result(result, ctx.bufnr),
|
||||||
context = ctx;
|
context = ctx,
|
||||||
})
|
})
|
||||||
api.nvim_command("lopen")
|
api.nvim_command('lopen')
|
||||||
else
|
else
|
||||||
vim.fn.setqflist({}, ' ', {
|
vim.fn.setqflist({}, ' ', {
|
||||||
title = title_fn(ctx);
|
title = title_fn(ctx),
|
||||||
items = map_result(result, ctx.bufnr);
|
items = map_result(result, ctx.bufnr),
|
||||||
context = ctx;
|
context = ctx,
|
||||||
})
|
})
|
||||||
api.nvim_command("botright copen")
|
api.nvim_command('botright copen')
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
|
|
||||||
--see: https://microsoft.github.io/language-server-protocol/specifications/specification-current/#textDocument_documentSymbol
|
--see: https://microsoft.github.io/language-server-protocol/specifications/specification-current/#textDocument_documentSymbol
|
||||||
M['textDocument/documentSymbol'] = response_to_list(util.symbols_to_items, 'document symbols', function(ctx)
|
M['textDocument/documentSymbol'] = response_to_list(util.symbols_to_items, 'document symbols', function(ctx)
|
||||||
local fname = vim.fn.fnamemodify(vim.uri_to_fname(ctx.params.textDocument.uri), ":.")
|
local fname = vim.fn.fnamemodify(vim.uri_to_fname(ctx.params.textDocument.uri), ':.')
|
||||||
return string.format('Symbols in %s', fname)
|
return string.format('Symbols in %s', fname)
|
||||||
end)
|
end)
|
||||||
|
|
||||||
@@ -249,28 +249,36 @@ end)
|
|||||||
|
|
||||||
--see: https://microsoft.github.io/language-server-protocol/specifications/specification-current/#textDocument_rename
|
--see: https://microsoft.github.io/language-server-protocol/specifications/specification-current/#textDocument_rename
|
||||||
M['textDocument/rename'] = function(_, result, ctx, _)
|
M['textDocument/rename'] = function(_, result, ctx, _)
|
||||||
if not result then return end
|
if not result then
|
||||||
|
return
|
||||||
|
end
|
||||||
local client = vim.lsp.get_client_by_id(ctx.client_id)
|
local client = vim.lsp.get_client_by_id(ctx.client_id)
|
||||||
util.apply_workspace_edit(result, client.offset_encoding)
|
util.apply_workspace_edit(result, client.offset_encoding)
|
||||||
end
|
end
|
||||||
|
|
||||||
--see: https://microsoft.github.io/language-server-protocol/specifications/specification-current/#textDocument_rangeFormatting
|
--see: https://microsoft.github.io/language-server-protocol/specifications/specification-current/#textDocument_rangeFormatting
|
||||||
M['textDocument/rangeFormatting'] = function(_, result, ctx, _)
|
M['textDocument/rangeFormatting'] = function(_, result, ctx, _)
|
||||||
if not result then return end
|
if not result then
|
||||||
|
return
|
||||||
|
end
|
||||||
local client = vim.lsp.get_client_by_id(ctx.client_id)
|
local client = vim.lsp.get_client_by_id(ctx.client_id)
|
||||||
util.apply_text_edits(result, ctx.bufnr, client.offset_encoding)
|
util.apply_text_edits(result, ctx.bufnr, client.offset_encoding)
|
||||||
end
|
end
|
||||||
|
|
||||||
--see: https://microsoft.github.io/language-server-protocol/specifications/specification-current/#textDocument_formatting
|
--see: https://microsoft.github.io/language-server-protocol/specifications/specification-current/#textDocument_formatting
|
||||||
M['textDocument/formatting'] = function(_, result, ctx, _)
|
M['textDocument/formatting'] = function(_, result, ctx, _)
|
||||||
if not result then return end
|
if not result then
|
||||||
|
return
|
||||||
|
end
|
||||||
local client = vim.lsp.get_client_by_id(ctx.client_id)
|
local client = vim.lsp.get_client_by_id(ctx.client_id)
|
||||||
util.apply_text_edits(result, ctx.bufnr, client.offset_encoding)
|
util.apply_text_edits(result, ctx.bufnr, client.offset_encoding)
|
||||||
end
|
end
|
||||||
|
|
||||||
--see: https://microsoft.github.io/language-server-protocol/specifications/specification-current/#textDocument_completion
|
--see: https://microsoft.github.io/language-server-protocol/specifications/specification-current/#textDocument_completion
|
||||||
M['textDocument/completion'] = function(_, result, _, _)
|
M['textDocument/completion'] = function(_, result, _, _)
|
||||||
if vim.tbl_isempty(result or {}) then return end
|
if vim.tbl_isempty(result or {}) then
|
||||||
|
return
|
||||||
|
end
|
||||||
local row, col = unpack(api.nvim_win_get_cursor(0))
|
local row, col = unpack(api.nvim_win_get_cursor(0))
|
||||||
local line = assert(api.nvim_buf_get_lines(0, row - 1, row, false)[1])
|
local line = assert(api.nvim_buf_get_lines(0, row - 1, row, false)[1])
|
||||||
local line_to_cursor = line:sub(col + 1)
|
local line_to_cursor = line:sub(col + 1)
|
||||||
@@ -307,7 +315,7 @@ function M.hover(_, result, ctx, config)
|
|||||||
vim.notify('No information available')
|
vim.notify('No information available')
|
||||||
return
|
return
|
||||||
end
|
end
|
||||||
return util.open_floating_preview(markdown_lines, "markdown", config)
|
return util.open_floating_preview(markdown_lines, 'markdown', config)
|
||||||
end
|
end
|
||||||
|
|
||||||
--see: https://microsoft.github.io/language-server-protocol/specifications/specification-current/#textDocument_hover
|
--see: https://microsoft.github.io/language-server-protocol/specifications/specification-current/#textDocument_hover
|
||||||
@@ -335,9 +343,9 @@ local function location_handler(_, result, ctx, _)
|
|||||||
if #result > 1 then
|
if #result > 1 then
|
||||||
vim.fn.setqflist({}, ' ', {
|
vim.fn.setqflist({}, ' ', {
|
||||||
title = 'LSP locations',
|
title = 'LSP locations',
|
||||||
items = util.locations_to_items(result, client.offset_encoding)
|
items = util.locations_to_items(result, client.offset_encoding),
|
||||||
})
|
})
|
||||||
api.nvim_command("botright copen")
|
api.nvim_command('botright copen')
|
||||||
end
|
end
|
||||||
else
|
else
|
||||||
util.jump_to_location(result, client.offset_encoding)
|
util.jump_to_location(result, client.offset_encoding)
|
||||||
@@ -379,7 +387,7 @@ function M.signature_help(_, result, ctx, config)
|
|||||||
return
|
return
|
||||||
end
|
end
|
||||||
local client = vim.lsp.get_client_by_id(ctx.client_id)
|
local client = vim.lsp.get_client_by_id(ctx.client_id)
|
||||||
local triggers = vim.tbl_get(client.server_capabilities, "signatureHelpProvider", "triggerCharacters")
|
local triggers = vim.tbl_get(client.server_capabilities, 'signatureHelpProvider', 'triggerCharacters')
|
||||||
local ft = api.nvim_buf_get_option(ctx.bufnr, 'filetype')
|
local ft = api.nvim_buf_get_option(ctx.bufnr, 'filetype')
|
||||||
local lines, hl = util.convert_signature_help_to_markdown_lines(result, ft, triggers)
|
local lines, hl = util.convert_signature_help_to_markdown_lines(result, ft, triggers)
|
||||||
lines = util.trim_empty_lines(lines)
|
lines = util.trim_empty_lines(lines)
|
||||||
@@ -389,9 +397,9 @@ function M.signature_help(_, result, ctx, config)
|
|||||||
end
|
end
|
||||||
return
|
return
|
||||||
end
|
end
|
||||||
local fbuf, fwin = util.open_floating_preview(lines, "markdown", config)
|
local fbuf, fwin = util.open_floating_preview(lines, 'markdown', config)
|
||||||
if hl then
|
if hl then
|
||||||
api.nvim_buf_add_highlight(fbuf, -1, "LspSignatureActiveParameter", 0, unpack(hl))
|
api.nvim_buf_add_highlight(fbuf, -1, 'LspSignatureActiveParameter', 0, unpack(hl))
|
||||||
end
|
end
|
||||||
return fbuf, fwin
|
return fbuf, fwin
|
||||||
end
|
end
|
||||||
@@ -401,10 +409,14 @@ M['textDocument/signatureHelp'] = M.signature_help
|
|||||||
|
|
||||||
--see: https://microsoft.github.io/language-server-protocol/specifications/specification-current/#textDocument_documentHighlight
|
--see: https://microsoft.github.io/language-server-protocol/specifications/specification-current/#textDocument_documentHighlight
|
||||||
M['textDocument/documentHighlight'] = function(_, result, ctx, _)
|
M['textDocument/documentHighlight'] = function(_, result, ctx, _)
|
||||||
if not result then return end
|
if not result then
|
||||||
|
return
|
||||||
|
end
|
||||||
local client_id = ctx.client_id
|
local client_id = ctx.client_id
|
||||||
local client = vim.lsp.get_client_by_id(client_id)
|
local client = vim.lsp.get_client_by_id(client_id)
|
||||||
if not client then return end
|
if not client then
|
||||||
|
return
|
||||||
|
end
|
||||||
util.buf_highlight_references(ctx.bufnr, result, client.offset_encoding)
|
util.buf_highlight_references(ctx.bufnr, result, client.offset_encoding)
|
||||||
end
|
end
|
||||||
|
|
||||||
@@ -417,7 +429,9 @@ end
|
|||||||
---@returns `CallHierarchyOutgoingCall[]` if {direction} is `"to"`,
|
---@returns `CallHierarchyOutgoingCall[]` if {direction} is `"to"`,
|
||||||
local make_call_hierarchy_handler = function(direction)
|
local make_call_hierarchy_handler = function(direction)
|
||||||
return function(_, result)
|
return function(_, result)
|
||||||
if not result then return end
|
if not result then
|
||||||
|
return
|
||||||
|
end
|
||||||
local items = {}
|
local items = {}
|
||||||
for _, call_hierarchy_call in pairs(result) do
|
for _, call_hierarchy_call in pairs(result) do
|
||||||
local call_hierarchy_item = call_hierarchy_call[direction]
|
local call_hierarchy_item = call_hierarchy_call[direction]
|
||||||
@@ -431,7 +445,7 @@ local make_call_hierarchy_handler = function(direction)
|
|||||||
end
|
end
|
||||||
end
|
end
|
||||||
vim.fn.setqflist({}, ' ', { title = 'LSP call hierarchy', items = items })
|
vim.fn.setqflist({}, ' ', { title = 'LSP call hierarchy', items = items })
|
||||||
api.nvim_command("botright copen")
|
api.nvim_command('botright copen')
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
@@ -447,9 +461,9 @@ M['window/logMessage'] = function(_, result, ctx, _)
|
|||||||
local message = result.message
|
local message = result.message
|
||||||
local client_id = ctx.client_id
|
local client_id = ctx.client_id
|
||||||
local client = vim.lsp.get_client_by_id(client_id)
|
local client = vim.lsp.get_client_by_id(client_id)
|
||||||
local client_name = client and client.name or string.format("id=%d", client_id)
|
local client_name = client and client.name or string.format('id=%d', client_id)
|
||||||
if not client then
|
if not client then
|
||||||
err_message("LSP[", client_name, "] client has shut down after sending ", message)
|
err_message('LSP[', client_name, '] client has shut down after sending ', message)
|
||||||
end
|
end
|
||||||
if message_type == protocol.MessageType.Error then
|
if message_type == protocol.MessageType.Error then
|
||||||
log.error(message)
|
log.error(message)
|
||||||
@@ -469,15 +483,15 @@ M['window/showMessage'] = function(_, result, ctx, _)
|
|||||||
local message = result.message
|
local message = result.message
|
||||||
local client_id = ctx.client_id
|
local client_id = ctx.client_id
|
||||||
local client = vim.lsp.get_client_by_id(client_id)
|
local client = vim.lsp.get_client_by_id(client_id)
|
||||||
local client_name = client and client.name or string.format("id=%d", client_id)
|
local client_name = client and client.name or string.format('id=%d', client_id)
|
||||||
if not client then
|
if not client then
|
||||||
err_message("LSP[", client_name, "] client has shut down after sending ", message)
|
err_message('LSP[', client_name, '] client has shut down after sending ', message)
|
||||||
end
|
end
|
||||||
if message_type == protocol.MessageType.Error then
|
if message_type == protocol.MessageType.Error then
|
||||||
err_message("LSP[", client_name, "] ", message)
|
err_message('LSP[', client_name, '] ', message)
|
||||||
else
|
else
|
||||||
local message_type_name = protocol.MessageType[message_type]
|
local message_type_name = protocol.MessageType[message_type]
|
||||||
api.nvim_out_write(string.format("LSP[%s][%s] %s\n", client_name, message_type_name, message))
|
api.nvim_out_write(string.format('LSP[%s][%s] %s\n', client_name, message_type_name, message))
|
||||||
end
|
end
|
||||||
return result
|
return result
|
||||||
end
|
end
|
||||||
@@ -485,8 +499,12 @@ end
|
|||||||
-- Add boilerplate error validation and logging for all of these.
|
-- Add boilerplate error validation and logging for all of these.
|
||||||
for k, fn in pairs(M) do
|
for k, fn in pairs(M) do
|
||||||
M[k] = function(err, result, ctx, config)
|
M[k] = function(err, result, ctx, config)
|
||||||
local _ = log.trace() and log.trace('default_handler', ctx.method, {
|
local _ = log.trace()
|
||||||
err = err, result = result, ctx=vim.inspect(ctx), config = config
|
and log.trace('default_handler', ctx.method, {
|
||||||
|
err = err,
|
||||||
|
result = result,
|
||||||
|
ctx = vim.inspect(ctx),
|
||||||
|
config = config,
|
||||||
})
|
})
|
||||||
|
|
||||||
if err then
|
if err then
|
||||||
@@ -499,7 +517,7 @@ for k, fn in pairs(M) do
|
|||||||
-- Per LSP, don't show ContentModified error to the user.
|
-- Per LSP, don't show ContentModified error to the user.
|
||||||
if err.code ~= protocol.ErrorCodes.ContentModified then
|
if err.code ~= protocol.ErrorCodes.ContentModified then
|
||||||
local client = vim.lsp.get_client_by_id(ctx.client_id)
|
local client = vim.lsp.get_client_by_id(ctx.client_id)
|
||||||
local client_name = client and client.name or string.format("client_id=%d", ctx.client_id)
|
local client_name = client and client.name or string.format('client_id=%d', ctx.client_id)
|
||||||
|
|
||||||
err_message(client_name .. ': ' .. tostring(err.code) .. ': ' .. err.message)
|
err_message(client_name .. ': ' .. tostring(err.code) .. ': ' .. err.message)
|
||||||
end
|
end
|
||||||
|
@@ -8,20 +8,19 @@ function M.check()
|
|||||||
local log = require('vim.lsp.log')
|
local log = require('vim.lsp.log')
|
||||||
local current_log_level = log.get_level()
|
local current_log_level = log.get_level()
|
||||||
local log_level_string = log.levels[current_log_level]
|
local log_level_string = log.levels[current_log_level]
|
||||||
report_info(string.format("LSP log level : %s", log_level_string))
|
report_info(string.format('LSP log level : %s', log_level_string))
|
||||||
|
|
||||||
if current_log_level < log.levels.WARN then
|
if current_log_level < log.levels.WARN then
|
||||||
report_warn(string.format("Log level %s will cause degraded performance and high disk usage", log_level_string))
|
report_warn(string.format('Log level %s will cause degraded performance and high disk usage', log_level_string))
|
||||||
end
|
end
|
||||||
|
|
||||||
local log_path = vim.lsp.get_log_path()
|
local log_path = vim.lsp.get_log_path()
|
||||||
report_info(string.format("Log path: %s", log_path))
|
report_info(string.format('Log path: %s', log_path))
|
||||||
|
|
||||||
local log_size = vim.loop.fs_stat(log_path).size
|
local log_size = vim.loop.fs_stat(log_path).size
|
||||||
|
|
||||||
local report_fn = (log_size / 1000000 > 100 and report_warn or report_info)
|
local report_fn = (log_size / 1000000 > 100 and report_warn or report_info)
|
||||||
report_fn(string.format("Log size: %d KB", log_size / 1000 ))
|
report_fn(string.format('Log size: %d KB', log_size / 1000))
|
||||||
end
|
end
|
||||||
|
|
||||||
return M
|
return M
|
||||||
|
|
||||||
|
@@ -14,21 +14,23 @@ log.levels = vim.deepcopy(vim.log.levels)
|
|||||||
|
|
||||||
-- Default log level is warn.
|
-- Default log level is warn.
|
||||||
local current_log_level = log.levels.WARN
|
local current_log_level = log.levels.WARN
|
||||||
local log_date_format = "%F %H:%M:%S"
|
local log_date_format = '%F %H:%M:%S'
|
||||||
local format_func = function(arg) return vim.inspect(arg, {newline=''}) end
|
local format_func = function(arg)
|
||||||
|
return vim.inspect(arg, { newline = '' })
|
||||||
|
end
|
||||||
|
|
||||||
do
|
do
|
||||||
local path_sep = vim.loop.os_uname().version:match("Windows") and "\\" or "/"
|
local path_sep = vim.loop.os_uname().version:match('Windows') and '\\' or '/'
|
||||||
---@private
|
---@private
|
||||||
local function path_join(...)
|
local function path_join(...)
|
||||||
return table.concat(vim.tbl_flatten{...}, path_sep)
|
return table.concat(vim.tbl_flatten({ ... }), path_sep)
|
||||||
end
|
end
|
||||||
local logfilename = path_join(vim.fn.stdpath('cache'), 'lsp.log')
|
local logfilename = path_join(vim.fn.stdpath('cache'), 'lsp.log')
|
||||||
|
|
||||||
-- TODO: Ideally the directory should be created in open_logfile(), right
|
-- TODO: Ideally the directory should be created in open_logfile(), right
|
||||||
-- before opening the log file, but open_logfile() can be called from libuv
|
-- before opening the log file, but open_logfile() can be called from libuv
|
||||||
-- callbacks, where using fn.mkdir() is not allowed.
|
-- callbacks, where using fn.mkdir() is not allowed.
|
||||||
vim.fn.mkdir(vim.fn.stdpath('cache'), "p")
|
vim.fn.mkdir(vim.fn.stdpath('cache'), 'p')
|
||||||
|
|
||||||
--- Returns the log filename.
|
--- Returns the log filename.
|
||||||
---@returns (string) log filename
|
---@returns (string) log filename
|
||||||
@@ -41,28 +43,28 @@ do
|
|||||||
--- Opens log file. Returns true if file is open, false on error
|
--- Opens log file. Returns true if file is open, false on error
|
||||||
local function open_logfile()
|
local function open_logfile()
|
||||||
-- Try to open file only once
|
-- Try to open file only once
|
||||||
if logfile then return true end
|
if logfile then
|
||||||
if openerr then return false end
|
return true
|
||||||
|
end
|
||||||
|
if openerr then
|
||||||
|
return false
|
||||||
|
end
|
||||||
|
|
||||||
logfile, openerr = io.open(logfilename, "a+")
|
logfile, openerr = io.open(logfilename, 'a+')
|
||||||
if not logfile then
|
if not logfile then
|
||||||
local err_msg = string.format("Failed to open LSP client log file: %s", openerr)
|
local err_msg = string.format('Failed to open LSP client log file: %s', openerr)
|
||||||
vim.notify(err_msg, vim.log.levels.ERROR)
|
vim.notify(err_msg, vim.log.levels.ERROR)
|
||||||
return false
|
return false
|
||||||
end
|
end
|
||||||
|
|
||||||
local log_info = vim.loop.fs_stat(logfilename)
|
local log_info = vim.loop.fs_stat(logfilename)
|
||||||
if log_info and log_info.size > 1e9 then
|
if log_info and log_info.size > 1e9 then
|
||||||
local warn_msg = string.format(
|
local warn_msg = string.format('LSP client log is large (%d MB): %s', log_info.size / (1000 * 1000), logfilename)
|
||||||
"LSP client log is large (%d MB): %s",
|
|
||||||
log_info.size / (1000 * 1000),
|
|
||||||
logfilename
|
|
||||||
)
|
|
||||||
vim.notify(warn_msg)
|
vim.notify(warn_msg)
|
||||||
end
|
end
|
||||||
|
|
||||||
-- Start message for logging
|
-- Start message for logging
|
||||||
logfile:write(string.format("[START][%s] LSP logging initiated\n", os.date(log_date_format)))
|
logfile:write(string.format('[START][%s] LSP logging initiated\n', os.date(log_date_format)))
|
||||||
return true
|
return true
|
||||||
end
|
end
|
||||||
|
|
||||||
@@ -83,24 +85,36 @@ do
|
|||||||
-- ```
|
-- ```
|
||||||
--
|
--
|
||||||
-- This way you can avoid string allocations if the log level isn't high enough.
|
-- This way you can avoid string allocations if the log level isn't high enough.
|
||||||
if level ~= "OFF" then
|
if level ~= 'OFF' then
|
||||||
log[level:lower()] = function(...)
|
log[level:lower()] = function(...)
|
||||||
local argc = select("#", ...)
|
local argc = select('#', ...)
|
||||||
if levelnr < current_log_level then return false end
|
if levelnr < current_log_level then
|
||||||
if argc == 0 then return true end
|
return false
|
||||||
if not open_logfile() then return false end
|
end
|
||||||
local info = debug.getinfo(2, "Sl")
|
if argc == 0 then
|
||||||
local header = string.format("[%s][%s] ...%s:%s", level, os.date(log_date_format), string.sub(info.short_src, #info.short_src - 15), info.currentline)
|
return true
|
||||||
|
end
|
||||||
|
if not open_logfile() then
|
||||||
|
return false
|
||||||
|
end
|
||||||
|
local info = debug.getinfo(2, 'Sl')
|
||||||
|
local header = string.format(
|
||||||
|
'[%s][%s] ...%s:%s',
|
||||||
|
level,
|
||||||
|
os.date(log_date_format),
|
||||||
|
string.sub(info.short_src, #info.short_src - 15),
|
||||||
|
info.currentline
|
||||||
|
)
|
||||||
local parts = { header }
|
local parts = { header }
|
||||||
for i = 1, argc do
|
for i = 1, argc do
|
||||||
local arg = select(i, ...)
|
local arg = select(i, ...)
|
||||||
if arg == nil then
|
if arg == nil then
|
||||||
table.insert(parts, "nil")
|
table.insert(parts, 'nil')
|
||||||
else
|
else
|
||||||
table.insert(parts, format_func(arg))
|
table.insert(parts, format_func(arg))
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
logfile:write(table.concat(parts, '\t'), "\n")
|
logfile:write(table.concat(parts, '\t'), '\n')
|
||||||
logfile:flush()
|
logfile:flush()
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
@@ -115,10 +129,10 @@ vim.tbl_add_reverse_lookup(log.levels)
|
|||||||
---@param level (string or number) One of `vim.lsp.log.levels`
|
---@param level (string or number) One of `vim.lsp.log.levels`
|
||||||
function log.set_level(level)
|
function log.set_level(level)
|
||||||
if type(level) == 'string' then
|
if type(level) == 'string' then
|
||||||
current_log_level = assert(log.levels[level:upper()], string.format("Invalid log level: %q", level))
|
current_log_level = assert(log.levels[level:upper()], string.format('Invalid log level: %q', level))
|
||||||
else
|
else
|
||||||
assert(type(level) == 'number', "level must be a number or string")
|
assert(type(level) == 'number', 'level must be a number or string')
|
||||||
assert(log.levels[level], string.format("Invalid log level: %d", level))
|
assert(log.levels[level], string.format('Invalid log level: %d', level))
|
||||||
current_log_level = level
|
current_log_level = level
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
@@ -132,7 +146,7 @@ end
|
|||||||
--- Sets formatting function used to format logs
|
--- Sets formatting function used to format logs
|
||||||
---@param handle function function to apply to logging arguments, pass vim.inspect for multi-line formatting
|
---@param handle function function to apply to logging arguments, pass vim.inspect for multi-line formatting
|
||||||
function log.set_format_func(handle)
|
function log.set_format_func(handle)
|
||||||
assert(handle == vim.inspect or type(handle) == 'function', "handle must be a function")
|
assert(handle == vim.inspect or type(handle) == 'function', 'handle must be a function')
|
||||||
format_func = handle
|
format_func = handle
|
||||||
end
|
end
|
||||||
|
|
||||||
|
@@ -23,150 +23,150 @@ end
|
|||||||
local constants = {
|
local constants = {
|
||||||
DiagnosticSeverity = {
|
DiagnosticSeverity = {
|
||||||
-- Reports an error.
|
-- Reports an error.
|
||||||
Error = 1;
|
Error = 1,
|
||||||
-- Reports a warning.
|
-- Reports a warning.
|
||||||
Warning = 2;
|
Warning = 2,
|
||||||
-- Reports an information.
|
-- Reports an information.
|
||||||
Information = 3;
|
Information = 3,
|
||||||
-- Reports a hint.
|
-- Reports a hint.
|
||||||
Hint = 4;
|
Hint = 4,
|
||||||
};
|
},
|
||||||
|
|
||||||
DiagnosticTag = {
|
DiagnosticTag = {
|
||||||
-- Unused or unnecessary code
|
-- Unused or unnecessary code
|
||||||
Unnecessary = 1;
|
Unnecessary = 1,
|
||||||
-- Deprecated or obsolete code
|
-- Deprecated or obsolete code
|
||||||
Deprecated = 2;
|
Deprecated = 2,
|
||||||
};
|
},
|
||||||
|
|
||||||
MessageType = {
|
MessageType = {
|
||||||
-- An error message.
|
-- An error message.
|
||||||
Error = 1;
|
Error = 1,
|
||||||
-- A warning message.
|
-- A warning message.
|
||||||
Warning = 2;
|
Warning = 2,
|
||||||
-- An information message.
|
-- An information message.
|
||||||
Info = 3;
|
Info = 3,
|
||||||
-- A log message.
|
-- A log message.
|
||||||
Log = 4;
|
Log = 4,
|
||||||
};
|
},
|
||||||
|
|
||||||
-- The file event type.
|
-- The file event type.
|
||||||
FileChangeType = {
|
FileChangeType = {
|
||||||
-- The file got created.
|
-- The file got created.
|
||||||
Created = 1;
|
Created = 1,
|
||||||
-- The file got changed.
|
-- The file got changed.
|
||||||
Changed = 2;
|
Changed = 2,
|
||||||
-- The file got deleted.
|
-- The file got deleted.
|
||||||
Deleted = 3;
|
Deleted = 3,
|
||||||
};
|
},
|
||||||
|
|
||||||
-- The kind of a completion entry.
|
-- The kind of a completion entry.
|
||||||
CompletionItemKind = {
|
CompletionItemKind = {
|
||||||
Text = 1;
|
Text = 1,
|
||||||
Method = 2;
|
Method = 2,
|
||||||
Function = 3;
|
Function = 3,
|
||||||
Constructor = 4;
|
Constructor = 4,
|
||||||
Field = 5;
|
Field = 5,
|
||||||
Variable = 6;
|
Variable = 6,
|
||||||
Class = 7;
|
Class = 7,
|
||||||
Interface = 8;
|
Interface = 8,
|
||||||
Module = 9;
|
Module = 9,
|
||||||
Property = 10;
|
Property = 10,
|
||||||
Unit = 11;
|
Unit = 11,
|
||||||
Value = 12;
|
Value = 12,
|
||||||
Enum = 13;
|
Enum = 13,
|
||||||
Keyword = 14;
|
Keyword = 14,
|
||||||
Snippet = 15;
|
Snippet = 15,
|
||||||
Color = 16;
|
Color = 16,
|
||||||
File = 17;
|
File = 17,
|
||||||
Reference = 18;
|
Reference = 18,
|
||||||
Folder = 19;
|
Folder = 19,
|
||||||
EnumMember = 20;
|
EnumMember = 20,
|
||||||
Constant = 21;
|
Constant = 21,
|
||||||
Struct = 22;
|
Struct = 22,
|
||||||
Event = 23;
|
Event = 23,
|
||||||
Operator = 24;
|
Operator = 24,
|
||||||
TypeParameter = 25;
|
TypeParameter = 25,
|
||||||
};
|
},
|
||||||
|
|
||||||
-- How a completion was triggered
|
-- How a completion was triggered
|
||||||
CompletionTriggerKind = {
|
CompletionTriggerKind = {
|
||||||
-- Completion was triggered by typing an identifier (24x7 code
|
-- Completion was triggered by typing an identifier (24x7 code
|
||||||
-- complete), manual invocation (e.g Ctrl+Space) or via API.
|
-- complete), manual invocation (e.g Ctrl+Space) or via API.
|
||||||
Invoked = 1;
|
Invoked = 1,
|
||||||
-- Completion was triggered by a trigger character specified by
|
-- Completion was triggered by a trigger character specified by
|
||||||
-- the `triggerCharacters` properties of the `CompletionRegistrationOptions`.
|
-- the `triggerCharacters` properties of the `CompletionRegistrationOptions`.
|
||||||
TriggerCharacter = 2;
|
TriggerCharacter = 2,
|
||||||
-- Completion was re-triggered as the current completion list is incomplete.
|
-- Completion was re-triggered as the current completion list is incomplete.
|
||||||
TriggerForIncompleteCompletions = 3;
|
TriggerForIncompleteCompletions = 3,
|
||||||
};
|
},
|
||||||
|
|
||||||
-- A document highlight kind.
|
-- A document highlight kind.
|
||||||
DocumentHighlightKind = {
|
DocumentHighlightKind = {
|
||||||
-- A textual occurrence.
|
-- A textual occurrence.
|
||||||
Text = 1;
|
Text = 1,
|
||||||
-- Read-access of a symbol, like reading a variable.
|
-- Read-access of a symbol, like reading a variable.
|
||||||
Read = 2;
|
Read = 2,
|
||||||
-- Write-access of a symbol, like writing to a variable.
|
-- Write-access of a symbol, like writing to a variable.
|
||||||
Write = 3;
|
Write = 3,
|
||||||
};
|
},
|
||||||
|
|
||||||
-- A symbol kind.
|
-- A symbol kind.
|
||||||
SymbolKind = {
|
SymbolKind = {
|
||||||
File = 1;
|
File = 1,
|
||||||
Module = 2;
|
Module = 2,
|
||||||
Namespace = 3;
|
Namespace = 3,
|
||||||
Package = 4;
|
Package = 4,
|
||||||
Class = 5;
|
Class = 5,
|
||||||
Method = 6;
|
Method = 6,
|
||||||
Property = 7;
|
Property = 7,
|
||||||
Field = 8;
|
Field = 8,
|
||||||
Constructor = 9;
|
Constructor = 9,
|
||||||
Enum = 10;
|
Enum = 10,
|
||||||
Interface = 11;
|
Interface = 11,
|
||||||
Function = 12;
|
Function = 12,
|
||||||
Variable = 13;
|
Variable = 13,
|
||||||
Constant = 14;
|
Constant = 14,
|
||||||
String = 15;
|
String = 15,
|
||||||
Number = 16;
|
Number = 16,
|
||||||
Boolean = 17;
|
Boolean = 17,
|
||||||
Array = 18;
|
Array = 18,
|
||||||
Object = 19;
|
Object = 19,
|
||||||
Key = 20;
|
Key = 20,
|
||||||
Null = 21;
|
Null = 21,
|
||||||
EnumMember = 22;
|
EnumMember = 22,
|
||||||
Struct = 23;
|
Struct = 23,
|
||||||
Event = 24;
|
Event = 24,
|
||||||
Operator = 25;
|
Operator = 25,
|
||||||
TypeParameter = 26;
|
TypeParameter = 26,
|
||||||
};
|
},
|
||||||
|
|
||||||
-- Represents reasons why a text document is saved.
|
-- Represents reasons why a text document is saved.
|
||||||
TextDocumentSaveReason = {
|
TextDocumentSaveReason = {
|
||||||
-- Manually triggered, e.g. by the user pressing save, by starting debugging,
|
-- Manually triggered, e.g. by the user pressing save, by starting debugging,
|
||||||
-- or by an API call.
|
-- or by an API call.
|
||||||
Manual = 1;
|
Manual = 1,
|
||||||
-- Automatic after a delay.
|
-- Automatic after a delay.
|
||||||
AfterDelay = 2;
|
AfterDelay = 2,
|
||||||
-- When the editor lost focus.
|
-- When the editor lost focus.
|
||||||
FocusOut = 3;
|
FocusOut = 3,
|
||||||
};
|
},
|
||||||
|
|
||||||
ErrorCodes = {
|
ErrorCodes = {
|
||||||
-- Defined by JSON RPC
|
-- Defined by JSON RPC
|
||||||
ParseError = -32700;
|
ParseError = -32700,
|
||||||
InvalidRequest = -32600;
|
InvalidRequest = -32600,
|
||||||
MethodNotFound = -32601;
|
MethodNotFound = -32601,
|
||||||
InvalidParams = -32602;
|
InvalidParams = -32602,
|
||||||
InternalError = -32603;
|
InternalError = -32603,
|
||||||
serverErrorStart = -32099;
|
serverErrorStart = -32099,
|
||||||
serverErrorEnd = -32000;
|
serverErrorEnd = -32000,
|
||||||
ServerNotInitialized = -32002;
|
ServerNotInitialized = -32002,
|
||||||
UnknownErrorCode = -32001;
|
UnknownErrorCode = -32001,
|
||||||
-- Defined by the protocol.
|
-- Defined by the protocol.
|
||||||
RequestCancelled = -32800;
|
RequestCancelled = -32800,
|
||||||
ContentModified = -32801;
|
ContentModified = -32801,
|
||||||
};
|
},
|
||||||
|
|
||||||
-- Describes the content type that a client supports in various
|
-- Describes the content type that a client supports in various
|
||||||
-- result literals like `Hover`, `ParameterInfo` or `CompletionItem`.
|
-- result literals like `Hover`, `ParameterInfo` or `CompletionItem`.
|
||||||
@@ -175,88 +175,88 @@ local constants = {
|
|||||||
-- are reserved for internal usage.
|
-- are reserved for internal usage.
|
||||||
MarkupKind = {
|
MarkupKind = {
|
||||||
-- Plain text is supported as a content format
|
-- Plain text is supported as a content format
|
||||||
PlainText = 'plaintext';
|
PlainText = 'plaintext',
|
||||||
-- Markdown is supported as a content format
|
-- Markdown is supported as a content format
|
||||||
Markdown = 'markdown';
|
Markdown = 'markdown',
|
||||||
};
|
},
|
||||||
|
|
||||||
ResourceOperationKind = {
|
ResourceOperationKind = {
|
||||||
-- Supports creating new files and folders.
|
-- Supports creating new files and folders.
|
||||||
Create = 'create';
|
Create = 'create',
|
||||||
-- Supports renaming existing files and folders.
|
-- Supports renaming existing files and folders.
|
||||||
Rename = 'rename';
|
Rename = 'rename',
|
||||||
-- Supports deleting existing files and folders.
|
-- Supports deleting existing files and folders.
|
||||||
Delete = 'delete';
|
Delete = 'delete',
|
||||||
};
|
},
|
||||||
|
|
||||||
FailureHandlingKind = {
|
FailureHandlingKind = {
|
||||||
-- Applying the workspace change is simply aborted if one of the changes provided
|
-- Applying the workspace change is simply aborted if one of the changes provided
|
||||||
-- fails. All operations executed before the failing operation stay executed.
|
-- fails. All operations executed before the failing operation stay executed.
|
||||||
Abort = 'abort';
|
Abort = 'abort',
|
||||||
-- All operations are executed transactionally. That means they either all
|
-- All operations are executed transactionally. That means they either all
|
||||||
-- succeed or no changes at all are applied to the workspace.
|
-- succeed or no changes at all are applied to the workspace.
|
||||||
Transactional = 'transactional';
|
Transactional = 'transactional',
|
||||||
-- If the workspace edit contains only textual file changes they are executed transactionally.
|
-- If the workspace edit contains only textual file changes they are executed transactionally.
|
||||||
-- If resource changes (create, rename or delete file) are part of the change the failure
|
-- If resource changes (create, rename or delete file) are part of the change the failure
|
||||||
-- handling strategy is abort.
|
-- handling strategy is abort.
|
||||||
TextOnlyTransactional = 'textOnlyTransactional';
|
TextOnlyTransactional = 'textOnlyTransactional',
|
||||||
-- The client tries to undo the operations already executed. But there is no
|
-- The client tries to undo the operations already executed. But there is no
|
||||||
-- guarantee that this succeeds.
|
-- guarantee that this succeeds.
|
||||||
Undo = 'undo';
|
Undo = 'undo',
|
||||||
};
|
},
|
||||||
|
|
||||||
-- Known error codes for an `InitializeError`;
|
-- Known error codes for an `InitializeError`;
|
||||||
InitializeError = {
|
InitializeError = {
|
||||||
-- If the protocol version provided by the client can't be handled by the server.
|
-- If the protocol version provided by the client can't be handled by the server.
|
||||||
-- @deprecated This initialize error got replaced by client capabilities. There is
|
-- @deprecated This initialize error got replaced by client capabilities. There is
|
||||||
-- no version handshake in version 3.0x
|
-- no version handshake in version 3.0x
|
||||||
unknownProtocolVersion = 1;
|
unknownProtocolVersion = 1,
|
||||||
};
|
},
|
||||||
|
|
||||||
-- Defines how the host (editor) should sync document changes to the language server.
|
-- Defines how the host (editor) should sync document changes to the language server.
|
||||||
TextDocumentSyncKind = {
|
TextDocumentSyncKind = {
|
||||||
-- Documents should not be synced at all.
|
-- Documents should not be synced at all.
|
||||||
None = 0;
|
None = 0,
|
||||||
-- Documents are synced by always sending the full content
|
-- Documents are synced by always sending the full content
|
||||||
-- of the document.
|
-- of the document.
|
||||||
Full = 1;
|
Full = 1,
|
||||||
-- Documents are synced by sending the full content on open.
|
-- Documents are synced by sending the full content on open.
|
||||||
-- After that only incremental updates to the document are
|
-- After that only incremental updates to the document are
|
||||||
-- send.
|
-- send.
|
||||||
Incremental = 2;
|
Incremental = 2,
|
||||||
};
|
},
|
||||||
|
|
||||||
WatchKind = {
|
WatchKind = {
|
||||||
-- Interested in create events.
|
-- Interested in create events.
|
||||||
Create = 1;
|
Create = 1,
|
||||||
-- Interested in change events
|
-- Interested in change events
|
||||||
Change = 2;
|
Change = 2,
|
||||||
-- Interested in delete events
|
-- Interested in delete events
|
||||||
Delete = 4;
|
Delete = 4,
|
||||||
};
|
},
|
||||||
|
|
||||||
-- Defines whether the insert text in a completion item should be interpreted as
|
-- Defines whether the insert text in a completion item should be interpreted as
|
||||||
-- plain text or a snippet.
|
-- plain text or a snippet.
|
||||||
InsertTextFormat = {
|
InsertTextFormat = {
|
||||||
-- The primary text to be inserted is treated as a plain string.
|
-- The primary text to be inserted is treated as a plain string.
|
||||||
PlainText = 1;
|
PlainText = 1,
|
||||||
-- The primary text to be inserted is treated as a snippet.
|
-- The primary text to be inserted is treated as a snippet.
|
||||||
--
|
--
|
||||||
-- A snippet can define tab stops and placeholders with `$1`, `$2`
|
-- A snippet can define tab stops and placeholders with `$1`, `$2`
|
||||||
-- and `${3:foo};`. `$0` defines the final tab stop, it defaults to
|
-- and `${3:foo};`. `$0` defines the final tab stop, it defaults to
|
||||||
-- the end of the snippet. Placeholders with equal identifiers are linked,
|
-- the end of the snippet. Placeholders with equal identifiers are linked,
|
||||||
-- that is typing in one will update others too.
|
-- that is typing in one will update others too.
|
||||||
Snippet = 2;
|
Snippet = 2,
|
||||||
};
|
},
|
||||||
|
|
||||||
-- A set of predefined code action kinds
|
-- A set of predefined code action kinds
|
||||||
CodeActionKind = {
|
CodeActionKind = {
|
||||||
-- Empty kind.
|
-- Empty kind.
|
||||||
Empty = '';
|
Empty = '',
|
||||||
-- Base kind for quickfix actions
|
-- Base kind for quickfix actions
|
||||||
QuickFix = 'quickfix';
|
QuickFix = 'quickfix',
|
||||||
-- Base kind for refactoring actions
|
-- Base kind for refactoring actions
|
||||||
Refactor = 'refactor';
|
Refactor = 'refactor',
|
||||||
-- Base kind for refactoring extraction actions
|
-- Base kind for refactoring extraction actions
|
||||||
--
|
--
|
||||||
-- Example extract actions:
|
-- Example extract actions:
|
||||||
@@ -266,7 +266,7 @@ local constants = {
|
|||||||
-- - Extract variable
|
-- - Extract variable
|
||||||
-- - Extract interface from class
|
-- - Extract interface from class
|
||||||
-- - ...
|
-- - ...
|
||||||
RefactorExtract = 'refactor.extract';
|
RefactorExtract = 'refactor.extract',
|
||||||
-- Base kind for refactoring inline actions
|
-- Base kind for refactoring inline actions
|
||||||
--
|
--
|
||||||
-- Example inline actions:
|
-- Example inline actions:
|
||||||
@@ -275,7 +275,7 @@ local constants = {
|
|||||||
-- - Inline variable
|
-- - Inline variable
|
||||||
-- - Inline constant
|
-- - Inline constant
|
||||||
-- - ...
|
-- - ...
|
||||||
RefactorInline = 'refactor.inline';
|
RefactorInline = 'refactor.inline',
|
||||||
-- Base kind for refactoring rewrite actions
|
-- Base kind for refactoring rewrite actions
|
||||||
--
|
--
|
||||||
-- Example rewrite actions:
|
-- Example rewrite actions:
|
||||||
@@ -286,14 +286,14 @@ local constants = {
|
|||||||
-- - Make method static
|
-- - Make method static
|
||||||
-- - Move method to base class
|
-- - Move method to base class
|
||||||
-- - ...
|
-- - ...
|
||||||
RefactorRewrite = 'refactor.rewrite';
|
RefactorRewrite = 'refactor.rewrite',
|
||||||
-- Base kind for source actions
|
-- Base kind for source actions
|
||||||
--
|
--
|
||||||
-- Source code actions apply to the entire file.
|
-- Source code actions apply to the entire file.
|
||||||
Source = 'source';
|
Source = 'source',
|
||||||
-- Base kind for an organize imports source action
|
-- Base kind for an organize imports source action
|
||||||
SourceOrganizeImports = 'source.organizeImports';
|
SourceOrganizeImports = 'source.organizeImports',
|
||||||
};
|
},
|
||||||
}
|
}
|
||||||
|
|
||||||
for k, v in pairs(constants) do
|
for k, v in pairs(constants) do
|
||||||
@@ -620,19 +620,19 @@ function protocol.make_client_capabilities()
|
|||||||
return {
|
return {
|
||||||
textDocument = {
|
textDocument = {
|
||||||
synchronization = {
|
synchronization = {
|
||||||
dynamicRegistration = false;
|
dynamicRegistration = false,
|
||||||
|
|
||||||
-- TODO(ashkan) Send textDocument/willSave before saving (BufWritePre)
|
-- TODO(ashkan) Send textDocument/willSave before saving (BufWritePre)
|
||||||
willSave = false;
|
willSave = false,
|
||||||
|
|
||||||
-- TODO(ashkan) Implement textDocument/willSaveWaitUntil
|
-- TODO(ashkan) Implement textDocument/willSaveWaitUntil
|
||||||
willSaveWaitUntil = false;
|
willSaveWaitUntil = false,
|
||||||
|
|
||||||
-- Send textDocument/didSave after saving (BufWritePost)
|
-- Send textDocument/didSave after saving (BufWritePost)
|
||||||
didSave = true;
|
didSave = true,
|
||||||
};
|
},
|
||||||
codeAction = {
|
codeAction = {
|
||||||
dynamicRegistration = false;
|
dynamicRegistration = false,
|
||||||
|
|
||||||
codeActionLiteralSupport = {
|
codeActionLiteralSupport = {
|
||||||
codeActionKind = {
|
codeActionKind = {
|
||||||
@@ -640,138 +640,146 @@ function protocol.make_client_capabilities()
|
|||||||
local res = vim.tbl_values(protocol.CodeActionKind)
|
local res = vim.tbl_values(protocol.CodeActionKind)
|
||||||
table.sort(res)
|
table.sort(res)
|
||||||
return res
|
return res
|
||||||
end)();
|
end)(),
|
||||||
};
|
},
|
||||||
};
|
},
|
||||||
isPreferredSupport = true;
|
isPreferredSupport = true,
|
||||||
dataSupport = true;
|
dataSupport = true,
|
||||||
resolveSupport = {
|
resolveSupport = {
|
||||||
properties = { 'edit', }
|
properties = { 'edit' },
|
||||||
};
|
},
|
||||||
};
|
},
|
||||||
completion = {
|
completion = {
|
||||||
dynamicRegistration = false;
|
dynamicRegistration = false,
|
||||||
completionItem = {
|
completionItem = {
|
||||||
-- Until we can actually expand snippet, move cursor and allow for true snippet experience,
|
-- Until we can actually expand snippet, move cursor and allow for true snippet experience,
|
||||||
-- this should be disabled out of the box.
|
-- this should be disabled out of the box.
|
||||||
-- However, users can turn this back on if they have a snippet plugin.
|
-- However, users can turn this back on if they have a snippet plugin.
|
||||||
snippetSupport = false;
|
snippetSupport = false,
|
||||||
|
|
||||||
commitCharactersSupport = false;
|
commitCharactersSupport = false,
|
||||||
preselectSupport = false;
|
preselectSupport = false,
|
||||||
deprecatedSupport = false;
|
deprecatedSupport = false,
|
||||||
documentationFormat = { protocol.MarkupKind.Markdown; protocol.MarkupKind.PlainText };
|
documentationFormat = { protocol.MarkupKind.Markdown, protocol.MarkupKind.PlainText },
|
||||||
};
|
},
|
||||||
completionItemKind = {
|
completionItemKind = {
|
||||||
valueSet = (function()
|
valueSet = (function()
|
||||||
local res = {}
|
local res = {}
|
||||||
for k in ipairs(protocol.CompletionItemKind) do
|
for k in ipairs(protocol.CompletionItemKind) do
|
||||||
if type(k) == 'number' then table.insert(res, k) end
|
if type(k) == 'number' then
|
||||||
|
table.insert(res, k)
|
||||||
|
end
|
||||||
end
|
end
|
||||||
return res
|
return res
|
||||||
end)();
|
end)(),
|
||||||
};
|
},
|
||||||
|
|
||||||
-- TODO(tjdevries): Implement this
|
-- TODO(tjdevries): Implement this
|
||||||
contextSupport = false;
|
contextSupport = false,
|
||||||
};
|
},
|
||||||
declaration = {
|
declaration = {
|
||||||
linkSupport = true;
|
linkSupport = true,
|
||||||
};
|
},
|
||||||
definition = {
|
definition = {
|
||||||
linkSupport = true;
|
linkSupport = true,
|
||||||
};
|
},
|
||||||
implementation = {
|
implementation = {
|
||||||
linkSupport = true;
|
linkSupport = true,
|
||||||
};
|
},
|
||||||
typeDefinition = {
|
typeDefinition = {
|
||||||
linkSupport = true;
|
linkSupport = true,
|
||||||
};
|
},
|
||||||
hover = {
|
hover = {
|
||||||
dynamicRegistration = false;
|
dynamicRegistration = false,
|
||||||
contentFormat = { protocol.MarkupKind.Markdown; protocol.MarkupKind.PlainText };
|
contentFormat = { protocol.MarkupKind.Markdown, protocol.MarkupKind.PlainText },
|
||||||
};
|
},
|
||||||
signatureHelp = {
|
signatureHelp = {
|
||||||
dynamicRegistration = false;
|
dynamicRegistration = false,
|
||||||
signatureInformation = {
|
signatureInformation = {
|
||||||
activeParameterSupport = true;
|
activeParameterSupport = true,
|
||||||
documentationFormat = { protocol.MarkupKind.Markdown; protocol.MarkupKind.PlainText };
|
documentationFormat = { protocol.MarkupKind.Markdown, protocol.MarkupKind.PlainText },
|
||||||
parameterInformation = {
|
parameterInformation = {
|
||||||
labelOffsetSupport = true;
|
labelOffsetSupport = true,
|
||||||
};
|
},
|
||||||
};
|
},
|
||||||
};
|
},
|
||||||
references = {
|
references = {
|
||||||
dynamicRegistration = false;
|
dynamicRegistration = false,
|
||||||
};
|
},
|
||||||
documentHighlight = {
|
documentHighlight = {
|
||||||
dynamicRegistration = false
|
dynamicRegistration = false,
|
||||||
};
|
},
|
||||||
documentSymbol = {
|
documentSymbol = {
|
||||||
dynamicRegistration = false;
|
dynamicRegistration = false,
|
||||||
symbolKind = {
|
symbolKind = {
|
||||||
valueSet = (function()
|
valueSet = (function()
|
||||||
local res = {}
|
local res = {}
|
||||||
for k in ipairs(protocol.SymbolKind) do
|
for k in ipairs(protocol.SymbolKind) do
|
||||||
if type(k) == 'number' then table.insert(res, k) end
|
if type(k) == 'number' then
|
||||||
|
table.insert(res, k)
|
||||||
|
end
|
||||||
end
|
end
|
||||||
return res
|
return res
|
||||||
end)();
|
end)(),
|
||||||
};
|
},
|
||||||
hierarchicalDocumentSymbolSupport = true;
|
hierarchicalDocumentSymbolSupport = true,
|
||||||
};
|
},
|
||||||
rename = {
|
rename = {
|
||||||
dynamicRegistration = false;
|
dynamicRegistration = false,
|
||||||
prepareSupport = true;
|
prepareSupport = true,
|
||||||
};
|
},
|
||||||
publishDiagnostics = {
|
publishDiagnostics = {
|
||||||
relatedInformation = true;
|
relatedInformation = true,
|
||||||
tagSupport = {
|
tagSupport = {
|
||||||
valueSet = (function()
|
valueSet = (function()
|
||||||
local res = {}
|
local res = {}
|
||||||
for k in ipairs(protocol.DiagnosticTag) do
|
for k in ipairs(protocol.DiagnosticTag) do
|
||||||
if type(k) == 'number' then table.insert(res, k) end
|
if type(k) == 'number' then
|
||||||
|
table.insert(res, k)
|
||||||
|
end
|
||||||
end
|
end
|
||||||
return res
|
return res
|
||||||
end)();
|
end)(),
|
||||||
};
|
},
|
||||||
};
|
},
|
||||||
};
|
},
|
||||||
workspace = {
|
workspace = {
|
||||||
symbol = {
|
symbol = {
|
||||||
dynamicRegistration = false;
|
dynamicRegistration = false,
|
||||||
symbolKind = {
|
symbolKind = {
|
||||||
valueSet = (function()
|
valueSet = (function()
|
||||||
local res = {}
|
local res = {}
|
||||||
for k in ipairs(protocol.SymbolKind) do
|
for k in ipairs(protocol.SymbolKind) do
|
||||||
if type(k) == 'number' then table.insert(res, k) end
|
if type(k) == 'number' then
|
||||||
|
table.insert(res, k)
|
||||||
|
end
|
||||||
end
|
end
|
||||||
return res
|
return res
|
||||||
end)();
|
end)(),
|
||||||
};
|
},
|
||||||
hierarchicalWorkspaceSymbolSupport = true;
|
hierarchicalWorkspaceSymbolSupport = true,
|
||||||
};
|
},
|
||||||
workspaceFolders = true;
|
workspaceFolders = true,
|
||||||
applyEdit = true;
|
applyEdit = true,
|
||||||
workspaceEdit = {
|
workspaceEdit = {
|
||||||
resourceOperations = {'rename', 'create', 'delete',},
|
resourceOperations = { 'rename', 'create', 'delete' },
|
||||||
};
|
},
|
||||||
};
|
},
|
||||||
callHierarchy = {
|
callHierarchy = {
|
||||||
dynamicRegistration = false;
|
dynamicRegistration = false,
|
||||||
};
|
},
|
||||||
experimental = nil;
|
experimental = nil,
|
||||||
window = {
|
window = {
|
||||||
workDoneProgress = true;
|
workDoneProgress = true,
|
||||||
showMessage = {
|
showMessage = {
|
||||||
messageActionItem = {
|
messageActionItem = {
|
||||||
additionalPropertiesSupport = false;
|
additionalPropertiesSupport = false,
|
||||||
};
|
},
|
||||||
};
|
},
|
||||||
showDocument = {
|
showDocument = {
|
||||||
support = false;
|
support = false,
|
||||||
};
|
},
|
||||||
};
|
},
|
||||||
}
|
}
|
||||||
end
|
end
|
||||||
|
|
||||||
@@ -791,12 +799,12 @@ function protocol.resolve_capabilities(server_capabilities)
|
|||||||
willSaveWaitUntil = false,
|
willSaveWaitUntil = false,
|
||||||
save = {
|
save = {
|
||||||
includeText = false,
|
includeText = false,
|
||||||
}
|
},
|
||||||
}
|
}
|
||||||
elseif type(textDocumentSync) == 'number' then
|
elseif type(textDocumentSync) == 'number' then
|
||||||
-- Backwards compatibility
|
-- Backwards compatibility
|
||||||
if not TextDocumentSyncKind[textDocumentSync] then
|
if not TextDocumentSyncKind[textDocumentSync] then
|
||||||
return nil, "Invalid server TextDocumentSyncKind for textDocumentSync"
|
return nil, 'Invalid server TextDocumentSyncKind for textDocumentSync'
|
||||||
end
|
end
|
||||||
server_capabilities.textDocumentSync = {
|
server_capabilities.textDocumentSync = {
|
||||||
openClose = true,
|
openClose = true,
|
||||||
@@ -805,10 +813,10 @@ function protocol.resolve_capabilities(server_capabilities)
|
|||||||
willSaveWaitUntil = false,
|
willSaveWaitUntil = false,
|
||||||
save = {
|
save = {
|
||||||
includeText = false,
|
includeText = false,
|
||||||
}
|
},
|
||||||
}
|
}
|
||||||
elseif type(textDocumentSync) ~= 'table' then
|
elseif type(textDocumentSync) ~= 'table' then
|
||||||
return nil, string.format("Invalid type for textDocumentSync: %q", type(textDocumentSync))
|
return nil, string.format('Invalid type for textDocumentSync: %q', type(textDocumentSync))
|
||||||
end
|
end
|
||||||
return server_capabilities
|
return server_capabilities
|
||||||
end
|
end
|
||||||
@@ -827,39 +835,41 @@ function protocol._resolve_capabilities_compat(server_capabilities)
|
|||||||
if textDocumentSync == nil then
|
if textDocumentSync == nil then
|
||||||
-- Defaults if omitted.
|
-- Defaults if omitted.
|
||||||
text_document_sync_properties = {
|
text_document_sync_properties = {
|
||||||
text_document_open_close = false;
|
text_document_open_close = false,
|
||||||
text_document_did_change = TextDocumentSyncKind.None;
|
text_document_did_change = TextDocumentSyncKind.None,
|
||||||
-- text_document_did_change = false;
|
-- text_document_did_change = false;
|
||||||
text_document_will_save = false;
|
text_document_will_save = false,
|
||||||
text_document_will_save_wait_until = false;
|
text_document_will_save_wait_until = false,
|
||||||
text_document_save = false;
|
text_document_save = false,
|
||||||
text_document_save_include_text = false;
|
text_document_save_include_text = false,
|
||||||
}
|
}
|
||||||
elseif type(textDocumentSync) == 'number' then
|
elseif type(textDocumentSync) == 'number' then
|
||||||
-- Backwards compatibility
|
-- Backwards compatibility
|
||||||
if not TextDocumentSyncKind[textDocumentSync] then
|
if not TextDocumentSyncKind[textDocumentSync] then
|
||||||
return nil, "Invalid server TextDocumentSyncKind for textDocumentSync"
|
return nil, 'Invalid server TextDocumentSyncKind for textDocumentSync'
|
||||||
end
|
end
|
||||||
text_document_sync_properties = {
|
text_document_sync_properties = {
|
||||||
text_document_open_close = true;
|
text_document_open_close = true,
|
||||||
text_document_did_change = textDocumentSync;
|
text_document_did_change = textDocumentSync,
|
||||||
text_document_will_save = false;
|
text_document_will_save = false,
|
||||||
text_document_will_save_wait_until = false;
|
text_document_will_save_wait_until = false,
|
||||||
text_document_save = true;
|
text_document_save = true,
|
||||||
text_document_save_include_text = false;
|
text_document_save_include_text = false,
|
||||||
}
|
}
|
||||||
elseif type(textDocumentSync) == 'table' then
|
elseif type(textDocumentSync) == 'table' then
|
||||||
text_document_sync_properties = {
|
text_document_sync_properties = {
|
||||||
text_document_open_close = if_nil(textDocumentSync.openClose, false);
|
text_document_open_close = if_nil(textDocumentSync.openClose, false),
|
||||||
text_document_did_change = if_nil(textDocumentSync.change, TextDocumentSyncKind.None);
|
text_document_did_change = if_nil(textDocumentSync.change, TextDocumentSyncKind.None),
|
||||||
text_document_will_save = if_nil(textDocumentSync.willSave, false);
|
text_document_will_save = if_nil(textDocumentSync.willSave, false),
|
||||||
text_document_will_save_wait_until = if_nil(textDocumentSync.willSaveWaitUntil, false);
|
text_document_will_save_wait_until = if_nil(textDocumentSync.willSaveWaitUntil, false),
|
||||||
text_document_save = if_nil(textDocumentSync.save, false);
|
text_document_save = if_nil(textDocumentSync.save, false),
|
||||||
text_document_save_include_text = if_nil(type(textDocumentSync.save) == 'table'
|
text_document_save_include_text = if_nil(
|
||||||
and textDocumentSync.save.includeText, false);
|
type(textDocumentSync.save) == 'table' and textDocumentSync.save.includeText,
|
||||||
|
false
|
||||||
|
),
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
return nil, string.format("Invalid type for textDocumentSync: %q", type(textDocumentSync))
|
return nil, string.format('Invalid type for textDocumentSync: %q', type(textDocumentSync))
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
general_properties.completion = server_capabilities.completionProvider ~= nil
|
general_properties.completion = server_capabilities.completionProvider ~= nil
|
||||||
@@ -889,16 +899,18 @@ function protocol._resolve_capabilities_compat(server_capabilities)
|
|||||||
general_properties.code_lens = true
|
general_properties.code_lens = true
|
||||||
general_properties.code_lens_resolve = server_capabilities.codeLensProvider.resolveProvider or false
|
general_properties.code_lens_resolve = server_capabilities.codeLensProvider.resolveProvider or false
|
||||||
else
|
else
|
||||||
error("The server sent invalid codeLensProvider")
|
error('The server sent invalid codeLensProvider')
|
||||||
end
|
end
|
||||||
|
|
||||||
if server_capabilities.codeActionProvider == nil then
|
if server_capabilities.codeActionProvider == nil then
|
||||||
general_properties.code_action = false
|
general_properties.code_action = false
|
||||||
elseif type(server_capabilities.codeActionProvider) == 'boolean'
|
elseif
|
||||||
or type(server_capabilities.codeActionProvider) == 'table' then
|
type(server_capabilities.codeActionProvider) == 'boolean'
|
||||||
|
or type(server_capabilities.codeActionProvider) == 'table'
|
||||||
|
then
|
||||||
general_properties.code_action = server_capabilities.codeActionProvider
|
general_properties.code_action = server_capabilities.codeActionProvider
|
||||||
else
|
else
|
||||||
error("The server sent invalid codeActionProvider")
|
error('The server sent invalid codeActionProvider')
|
||||||
end
|
end
|
||||||
|
|
||||||
if server_capabilities.declarationProvider == nil then
|
if server_capabilities.declarationProvider == nil then
|
||||||
@@ -908,7 +920,7 @@ function protocol._resolve_capabilities_compat(server_capabilities)
|
|||||||
elseif type(server_capabilities.declarationProvider) == 'table' then
|
elseif type(server_capabilities.declarationProvider) == 'table' then
|
||||||
general_properties.declaration = server_capabilities.declarationProvider
|
general_properties.declaration = server_capabilities.declarationProvider
|
||||||
else
|
else
|
||||||
error("The server sent invalid declarationProvider")
|
error('The server sent invalid declarationProvider')
|
||||||
end
|
end
|
||||||
|
|
||||||
if server_capabilities.typeDefinitionProvider == nil then
|
if server_capabilities.typeDefinitionProvider == nil then
|
||||||
@@ -918,7 +930,7 @@ function protocol._resolve_capabilities_compat(server_capabilities)
|
|||||||
elseif type(server_capabilities.typeDefinitionProvider) == 'table' then
|
elseif type(server_capabilities.typeDefinitionProvider) == 'table' then
|
||||||
general_properties.type_definition = server_capabilities.typeDefinitionProvider
|
general_properties.type_definition = server_capabilities.typeDefinitionProvider
|
||||||
else
|
else
|
||||||
error("The server sent invalid typeDefinitionProvider")
|
error('The server sent invalid typeDefinitionProvider')
|
||||||
end
|
end
|
||||||
|
|
||||||
if server_capabilities.implementationProvider == nil then
|
if server_capabilities.implementationProvider == nil then
|
||||||
@@ -928,7 +940,7 @@ function protocol._resolve_capabilities_compat(server_capabilities)
|
|||||||
elseif type(server_capabilities.implementationProvider) == 'table' then
|
elseif type(server_capabilities.implementationProvider) == 'table' then
|
||||||
general_properties.implementation = server_capabilities.implementationProvider
|
general_properties.implementation = server_capabilities.implementationProvider
|
||||||
else
|
else
|
||||||
error("The server sent invalid implementationProvider")
|
error('The server sent invalid implementationProvider')
|
||||||
end
|
end
|
||||||
|
|
||||||
local workspace = server_capabilities.workspace
|
local workspace = server_capabilities.workspace
|
||||||
@@ -937,43 +949,43 @@ function protocol._resolve_capabilities_compat(server_capabilities)
|
|||||||
-- Defaults if omitted.
|
-- Defaults if omitted.
|
||||||
workspace_properties = {
|
workspace_properties = {
|
||||||
workspace_folder_properties = {
|
workspace_folder_properties = {
|
||||||
supported = false;
|
supported = false,
|
||||||
changeNotifications=false;
|
changeNotifications = false,
|
||||||
}
|
},
|
||||||
}
|
}
|
||||||
elseif type(workspace.workspaceFolders) == 'table' then
|
elseif type(workspace.workspaceFolders) == 'table' then
|
||||||
workspace_properties = {
|
workspace_properties = {
|
||||||
workspace_folder_properties = {
|
workspace_folder_properties = {
|
||||||
supported = if_nil(workspace.workspaceFolders.supported, false);
|
supported = if_nil(workspace.workspaceFolders.supported, false),
|
||||||
changeNotifications = if_nil(workspace.workspaceFolders.changeNotifications, false);
|
changeNotifications = if_nil(workspace.workspaceFolders.changeNotifications, false),
|
||||||
|
},
|
||||||
}
|
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
error("The server sent invalid workspace")
|
error('The server sent invalid workspace')
|
||||||
end
|
end
|
||||||
|
|
||||||
local signature_help_properties
|
local signature_help_properties
|
||||||
if server_capabilities.signatureHelpProvider == nil then
|
if server_capabilities.signatureHelpProvider == nil then
|
||||||
signature_help_properties = {
|
signature_help_properties = {
|
||||||
signature_help = false;
|
signature_help = false,
|
||||||
signature_help_trigger_characters = {};
|
signature_help_trigger_characters = {},
|
||||||
}
|
}
|
||||||
elseif type(server_capabilities.signatureHelpProvider) == 'table' then
|
elseif type(server_capabilities.signatureHelpProvider) == 'table' then
|
||||||
signature_help_properties = {
|
signature_help_properties = {
|
||||||
signature_help = true;
|
signature_help = true,
|
||||||
-- The characters that trigger signature help automatically.
|
-- The characters that trigger signature help automatically.
|
||||||
signature_help_trigger_characters = server_capabilities.signatureHelpProvider.triggerCharacters or {};
|
signature_help_trigger_characters = server_capabilities.signatureHelpProvider.triggerCharacters or {},
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
error("The server sent invalid signatureHelpProvider")
|
error('The server sent invalid signatureHelpProvider')
|
||||||
end
|
end
|
||||||
|
|
||||||
local capabilities = vim.tbl_extend("error"
|
local capabilities = vim.tbl_extend(
|
||||||
, text_document_sync_properties
|
'error',
|
||||||
, signature_help_properties
|
text_document_sync_properties,
|
||||||
, workspace_properties
|
signature_help_properties,
|
||||||
, general_properties
|
workspace_properties,
|
||||||
|
general_properties
|
||||||
)
|
)
|
||||||
|
|
||||||
return capabilities
|
return capabilities
|
||||||
|
@@ -45,10 +45,12 @@ end
|
|||||||
---@param encoded_message (string)
|
---@param encoded_message (string)
|
||||||
---@returns (table) table containing encoded message and `Content-Length` attribute
|
---@returns (table) table containing encoded message and `Content-Length` attribute
|
||||||
local function format_message_with_content_length(encoded_message)
|
local function format_message_with_content_length(encoded_message)
|
||||||
return table.concat {
|
return table.concat({
|
||||||
'Content-Length: '; tostring(#encoded_message); '\r\n\r\n';
|
'Content-Length: ',
|
||||||
encoded_message;
|
tostring(#encoded_message),
|
||||||
}
|
'\r\n\r\n',
|
||||||
|
encoded_message,
|
||||||
|
})
|
||||||
end
|
end
|
||||||
|
|
||||||
---@private
|
---@private
|
||||||
@@ -65,23 +67,25 @@ local function parse_headers(header)
|
|||||||
if line == '' then
|
if line == '' then
|
||||||
break
|
break
|
||||||
end
|
end
|
||||||
local key, value = line:match("^%s*(%S+)%s*:%s*(.+)%s*$")
|
local key, value = line:match('^%s*(%S+)%s*:%s*(.+)%s*$')
|
||||||
if key then
|
if key then
|
||||||
key = key:lower():gsub('%-', '_')
|
key = key:lower():gsub('%-', '_')
|
||||||
headers[key] = value
|
headers[key] = value
|
||||||
else
|
else
|
||||||
local _ = log.error() and log.error("invalid header line %q", line)
|
local _ = log.error() and log.error('invalid header line %q', line)
|
||||||
error(string.format("invalid header line %q", line))
|
error(string.format('invalid header line %q', line))
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
headers.content_length = tonumber(headers.content_length)
|
headers.content_length = tonumber(headers.content_length)
|
||||||
or error(string.format("Content-Length not found in headers. %q", header))
|
or error(string.format('Content-Length not found in headers. %q', header))
|
||||||
return headers
|
return headers
|
||||||
end
|
end
|
||||||
|
|
||||||
-- This is the start of any possible header patterns. The gsub converts it to a
|
-- This is the start of any possible header patterns. The gsub converts it to a
|
||||||
-- case insensitive pattern.
|
-- case insensitive pattern.
|
||||||
local header_start_pattern = ("content"):gsub("%w", function(c) return "["..c..c:upper().."]" end)
|
local header_start_pattern = ('content'):gsub('%w', function(c)
|
||||||
|
return '[' .. c .. c:upper() .. ']'
|
||||||
|
end)
|
||||||
|
|
||||||
---@private
|
---@private
|
||||||
--- The actual workhorse.
|
--- The actual workhorse.
|
||||||
@@ -109,8 +113,7 @@ local function request_parser_loop()
|
|||||||
local body_length = #body_chunks[1]
|
local body_length = #body_chunks[1]
|
||||||
-- Keep waiting for data until we have enough.
|
-- Keep waiting for data until we have enough.
|
||||||
while body_length < content_length do
|
while body_length < content_length do
|
||||||
local chunk = coroutine.yield()
|
local chunk = coroutine.yield() or error('Expected more data for the body. The server may have died.') -- TODO hmm.
|
||||||
or error("Expected more data for the body. The server may have died.") -- TODO hmm.
|
|
||||||
table.insert(body_chunks, chunk)
|
table.insert(body_chunks, chunk)
|
||||||
body_length = body_length + #chunk
|
body_length = body_length + #chunk
|
||||||
end
|
end
|
||||||
@@ -123,25 +126,24 @@ local function request_parser_loop()
|
|||||||
end
|
end
|
||||||
local body = table.concat(body_chunks)
|
local body = table.concat(body_chunks)
|
||||||
-- Yield our data.
|
-- Yield our data.
|
||||||
buffer = rest..(coroutine.yield(headers, body)
|
buffer = rest
|
||||||
or error("Expected more data for the body. The server may have died.")) -- TODO hmm.
|
.. (coroutine.yield(headers, body) or error('Expected more data for the body. The server may have died.')) -- TODO hmm.
|
||||||
else
|
else
|
||||||
-- Get more data since we don't have enough.
|
-- Get more data since we don't have enough.
|
||||||
buffer = buffer..(coroutine.yield()
|
buffer = buffer .. (coroutine.yield() or error('Expected more data for the header. The server may have died.')) -- TODO hmm.
|
||||||
or error("Expected more data for the header. The server may have died.")) -- TODO hmm.
|
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
--- Mapping of error codes used by the client
|
--- Mapping of error codes used by the client
|
||||||
local client_errors = {
|
local client_errors = {
|
||||||
INVALID_SERVER_MESSAGE = 1;
|
INVALID_SERVER_MESSAGE = 1,
|
||||||
INVALID_SERVER_JSON = 2;
|
INVALID_SERVER_JSON = 2,
|
||||||
NO_RESULT_CALLBACK_FOUND = 3;
|
NO_RESULT_CALLBACK_FOUND = 3,
|
||||||
READ_ERROR = 4;
|
READ_ERROR = 4,
|
||||||
NOTIFICATION_HANDLER_ERROR = 5;
|
NOTIFICATION_HANDLER_ERROR = 5,
|
||||||
SERVER_REQUEST_HANDLER_ERROR = 6;
|
SERVER_REQUEST_HANDLER_ERROR = 6,
|
||||||
SERVER_RESULT_CALLBACK_ERROR = 7;
|
SERVER_RESULT_CALLBACK_ERROR = 7,
|
||||||
}
|
}
|
||||||
|
|
||||||
client_errors = vim.tbl_add_reverse_lookup(client_errors)
|
client_errors = vim.tbl_add_reverse_lookup(client_errors)
|
||||||
@@ -151,26 +153,26 @@ client_errors = vim.tbl_add_reverse_lookup(client_errors)
|
|||||||
---@param err (table) The error object
|
---@param err (table) The error object
|
||||||
---@returns (string) The formatted error message
|
---@returns (string) The formatted error message
|
||||||
local function format_rpc_error(err)
|
local function format_rpc_error(err)
|
||||||
validate {
|
validate({
|
||||||
err = { err, 't' };
|
err = { err, 't' },
|
||||||
}
|
})
|
||||||
|
|
||||||
-- There is ErrorCodes in the LSP specification,
|
-- There is ErrorCodes in the LSP specification,
|
||||||
-- but in ResponseError.code it is not used and the actual type is number.
|
-- but in ResponseError.code it is not used and the actual type is number.
|
||||||
local code
|
local code
|
||||||
if protocol.ErrorCodes[err.code] then
|
if protocol.ErrorCodes[err.code] then
|
||||||
code = string.format("code_name = %s,", protocol.ErrorCodes[err.code])
|
code = string.format('code_name = %s,', protocol.ErrorCodes[err.code])
|
||||||
else
|
else
|
||||||
code = string.format("code_name = unknown, code = %s,", err.code)
|
code = string.format('code_name = unknown, code = %s,', err.code)
|
||||||
end
|
end
|
||||||
|
|
||||||
local message_parts = {"RPC[Error]", code}
|
local message_parts = { 'RPC[Error]', code }
|
||||||
if err.message then
|
if err.message then
|
||||||
table.insert(message_parts, "message =")
|
table.insert(message_parts, 'message =')
|
||||||
table.insert(message_parts, string.format("%q", err.message))
|
table.insert(message_parts, string.format('%q', err.message))
|
||||||
end
|
end
|
||||||
if err.data then
|
if err.data then
|
||||||
table.insert(message_parts, "data =")
|
table.insert(message_parts, 'data =')
|
||||||
table.insert(message_parts, vim.inspect(err.data))
|
table.insert(message_parts, vim.inspect(err.data))
|
||||||
end
|
end
|
||||||
return table.concat(message_parts, ' ')
|
return table.concat(message_parts, ' ')
|
||||||
@@ -185,11 +187,11 @@ local function rpc_response_error(code, message, data)
|
|||||||
-- TODO should this error or just pick a sane error (like InternalError)?
|
-- TODO should this error or just pick a sane error (like InternalError)?
|
||||||
local code_name = assert(protocol.ErrorCodes[code], 'Invalid RPC error code')
|
local code_name = assert(protocol.ErrorCodes[code], 'Invalid RPC error code')
|
||||||
return setmetatable({
|
return setmetatable({
|
||||||
code = code;
|
code = code,
|
||||||
message = message or code_name;
|
message = message or code_name,
|
||||||
data = data;
|
data = data,
|
||||||
}, {
|
}, {
|
||||||
__tostring = format_rpc_error;
|
__tostring = format_rpc_error,
|
||||||
})
|
})
|
||||||
end
|
end
|
||||||
|
|
||||||
@@ -220,7 +222,7 @@ end
|
|||||||
---@param signal (number): Number describing the signal used to terminate (if
|
---@param signal (number): Number describing the signal used to terminate (if
|
||||||
---any)
|
---any)
|
||||||
function default_dispatchers.on_exit(code, signal)
|
function default_dispatchers.on_exit(code, signal)
|
||||||
local _ = log.info() and log.info("client_exit", { code = code, signal = signal })
|
local _ = log.info() and log.info('client_exit', { code = code, signal = signal })
|
||||||
end
|
end
|
||||||
---@private
|
---@private
|
||||||
--- Default dispatcher for client errors.
|
--- Default dispatcher for client errors.
|
||||||
@@ -258,15 +260,15 @@ end
|
|||||||
--- - {handle} A handle for low-level interaction with the LSP server process
|
--- - {handle} A handle for low-level interaction with the LSP server process
|
||||||
--- |vim.loop|.
|
--- |vim.loop|.
|
||||||
local function start(cmd, cmd_args, dispatchers, extra_spawn_params)
|
local function start(cmd, cmd_args, dispatchers, extra_spawn_params)
|
||||||
local _ = log.info() and log.info("Starting RPC client", {cmd = cmd, args = cmd_args, extra = extra_spawn_params})
|
local _ = log.info() and log.info('Starting RPC client', { cmd = cmd, args = cmd_args, extra = extra_spawn_params })
|
||||||
validate {
|
validate({
|
||||||
cmd = { cmd, 's' };
|
cmd = { cmd, 's' },
|
||||||
cmd_args = { cmd_args, 't' };
|
cmd_args = { cmd_args, 't' },
|
||||||
dispatchers = { dispatchers, 't', true };
|
dispatchers = { dispatchers, 't', true },
|
||||||
}
|
})
|
||||||
|
|
||||||
if extra_spawn_params and extra_spawn_params.cwd then
|
if extra_spawn_params and extra_spawn_params.cwd then
|
||||||
assert(is_dir(extra_spawn_params.cwd), "cwd must be a directory")
|
assert(is_dir(extra_spawn_params.cwd), 'cwd must be a directory')
|
||||||
end
|
end
|
||||||
if dispatchers then
|
if dispatchers then
|
||||||
local user_dispatchers = dispatchers
|
local user_dispatchers = dispatchers
|
||||||
@@ -275,11 +277,11 @@ local function start(cmd, cmd_args, dispatchers, extra_spawn_params)
|
|||||||
local user_dispatcher = user_dispatchers[dispatch_name]
|
local user_dispatcher = user_dispatchers[dispatch_name]
|
||||||
if user_dispatcher then
|
if user_dispatcher then
|
||||||
if type(user_dispatcher) ~= 'function' then
|
if type(user_dispatcher) ~= 'function' then
|
||||||
error(string.format("dispatcher.%s must be a function", dispatch_name))
|
error(string.format('dispatcher.%s must be a function', dispatch_name))
|
||||||
end
|
end
|
||||||
-- server_request is wrapped elsewhere.
|
-- server_request is wrapped elsewhere.
|
||||||
if not (dispatch_name == 'server_request'
|
if
|
||||||
or dispatch_name == 'on_exit') -- TODO this blocks the loop exiting for some reason.
|
not (dispatch_name == 'server_request' or dispatch_name == 'on_exit') -- TODO this blocks the loop exiting for some reason.
|
||||||
then
|
then
|
||||||
user_dispatcher = schedule_wrap(user_dispatcher)
|
user_dispatcher = schedule_wrap(user_dispatcher)
|
||||||
end
|
end
|
||||||
@@ -317,9 +319,9 @@ local function start(cmd, cmd_args, dispatchers, extra_spawn_params)
|
|||||||
dispatchers.on_exit(code, signal)
|
dispatchers.on_exit(code, signal)
|
||||||
end
|
end
|
||||||
local spawn_params = {
|
local spawn_params = {
|
||||||
args = cmd_args;
|
args = cmd_args,
|
||||||
stdio = {stdin, stdout, stderr};
|
stdio = { stdin, stdout, stderr },
|
||||||
detached = true;
|
detached = true,
|
||||||
}
|
}
|
||||||
if extra_spawn_params then
|
if extra_spawn_params then
|
||||||
spawn_params.cwd = extra_spawn_params.cwd
|
spawn_params.cwd = extra_spawn_params.cwd
|
||||||
@@ -330,11 +332,11 @@ local function start(cmd, cmd_args, dispatchers, extra_spawn_params)
|
|||||||
end
|
end
|
||||||
handle, pid = uv.spawn(cmd, spawn_params, onexit)
|
handle, pid = uv.spawn(cmd, spawn_params, onexit)
|
||||||
if handle == nil then
|
if handle == nil then
|
||||||
local msg = string.format("Spawning language server with cmd: `%s` failed", cmd)
|
local msg = string.format('Spawning language server with cmd: `%s` failed', cmd)
|
||||||
if string.match(pid, "ENOENT") then
|
if string.match(pid, 'ENOENT') then
|
||||||
msg = msg .. ". The language server is either not installed, missing from PATH, or not executable."
|
msg = msg .. '. The language server is either not installed, missing from PATH, or not executable.'
|
||||||
else
|
else
|
||||||
msg = msg .. string.format(" with error message: %s", pid)
|
msg = msg .. string.format(' with error message: %s', pid)
|
||||||
end
|
end
|
||||||
vim.notify(msg, vim.log.levels.WARN)
|
vim.notify(msg, vim.log.levels.WARN)
|
||||||
return
|
return
|
||||||
@@ -348,8 +350,10 @@ local function start(cmd, cmd_args, dispatchers, extra_spawn_params)
|
|||||||
---@param payload table
|
---@param payload table
|
||||||
---@returns true if the payload could be scheduled, false if the main event-loop is in the process of closing.
|
---@returns true if the payload could be scheduled, false if the main event-loop is in the process of closing.
|
||||||
local function encode_and_send(payload)
|
local function encode_and_send(payload)
|
||||||
local _ = log.debug() and log.debug("rpc.send", payload)
|
local _ = log.debug() and log.debug('rpc.send', payload)
|
||||||
if handle == nil or handle:is_closing() then return false end
|
if handle == nil or handle:is_closing() then
|
||||||
|
return false
|
||||||
|
end
|
||||||
local encoded = vim.json.encode(payload)
|
local encoded = vim.json.encode(payload)
|
||||||
stdin:write(format_message_with_content_length(encoded))
|
stdin:write(format_message_with_content_length(encoded))
|
||||||
return true
|
return true
|
||||||
@@ -363,22 +367,22 @@ local function start(cmd, cmd_args, dispatchers, extra_spawn_params)
|
|||||||
---@param params (table): Parameters for the invoked LSP method
|
---@param params (table): Parameters for the invoked LSP method
|
||||||
---@returns (bool) `true` if notification could be sent, `false` if not
|
---@returns (bool) `true` if notification could be sent, `false` if not
|
||||||
local function notify(method, params)
|
local function notify(method, params)
|
||||||
return encode_and_send {
|
return encode_and_send({
|
||||||
jsonrpc = "2.0";
|
jsonrpc = '2.0',
|
||||||
method = method;
|
method = method,
|
||||||
params = params;
|
params = params,
|
||||||
}
|
})
|
||||||
end
|
end
|
||||||
|
|
||||||
---@private
|
---@private
|
||||||
--- sends an error object to the remote LSP process.
|
--- sends an error object to the remote LSP process.
|
||||||
local function send_response(request_id, err, result)
|
local function send_response(request_id, err, result)
|
||||||
return encode_and_send {
|
return encode_and_send({
|
||||||
id = request_id;
|
id = request_id,
|
||||||
jsonrpc = "2.0";
|
jsonrpc = '2.0',
|
||||||
error = err;
|
error = err,
|
||||||
result = result;
|
result = result,
|
||||||
}
|
})
|
||||||
end
|
end
|
||||||
|
|
||||||
-- FIXME: DOC: Should be placed on the RPC client object returned by
|
-- FIXME: DOC: Should be placed on the RPC client object returned by
|
||||||
@@ -392,18 +396,18 @@ local function start(cmd, cmd_args, dispatchers, extra_spawn_params)
|
|||||||
---@param notify_reply_callback (function|nil) Callback to invoke as soon as a request is no longer pending
|
---@param notify_reply_callback (function|nil) Callback to invoke as soon as a request is no longer pending
|
||||||
---@returns (bool, number) `(true, message_id)` if request could be sent, `false` if not
|
---@returns (bool, number) `(true, message_id)` if request could be sent, `false` if not
|
||||||
local function request(method, params, callback, notify_reply_callback)
|
local function request(method, params, callback, notify_reply_callback)
|
||||||
validate {
|
validate({
|
||||||
callback = { callback, 'f' };
|
callback = { callback, 'f' },
|
||||||
notify_reply_callback = { notify_reply_callback, 'f', true };
|
notify_reply_callback = { notify_reply_callback, 'f', true },
|
||||||
}
|
})
|
||||||
message_index = message_index + 1
|
message_index = message_index + 1
|
||||||
local message_id = message_index
|
local message_id = message_index
|
||||||
local result = encode_and_send {
|
local result = encode_and_send({
|
||||||
id = message_id;
|
id = message_id,
|
||||||
jsonrpc = "2.0";
|
jsonrpc = '2.0',
|
||||||
method = method;
|
method = method,
|
||||||
params = params;
|
params = params,
|
||||||
}
|
})
|
||||||
if result then
|
if result then
|
||||||
if message_callbacks then
|
if message_callbacks then
|
||||||
message_callbacks[message_id] = schedule_wrap(callback)
|
message_callbacks[message_id] = schedule_wrap(callback)
|
||||||
@@ -421,7 +425,7 @@ local function start(cmd, cmd_args, dispatchers, extra_spawn_params)
|
|||||||
|
|
||||||
stderr:read_start(function(_err, chunk)
|
stderr:read_start(function(_err, chunk)
|
||||||
if chunk then
|
if chunk then
|
||||||
local _ = log.error() and log.error("rpc", cmd, "stderr", chunk)
|
local _ = log.error() and log.error('rpc', cmd, 'stderr', chunk)
|
||||||
end
|
end
|
||||||
end)
|
end)
|
||||||
|
|
||||||
@@ -455,7 +459,7 @@ local function start(cmd, cmd_args, dispatchers, extra_spawn_params)
|
|||||||
on_error(client_errors.INVALID_SERVER_JSON, decoded)
|
on_error(client_errors.INVALID_SERVER_JSON, decoded)
|
||||||
return
|
return
|
||||||
end
|
end
|
||||||
local _ = log.debug() and log.debug("rpc.receive", decoded)
|
local _ = log.debug() and log.debug('rpc.receive', decoded)
|
||||||
|
|
||||||
if type(decoded.method) == 'string' and decoded.id then
|
if type(decoded.method) == 'string' and decoded.id then
|
||||||
local err
|
local err
|
||||||
@@ -463,17 +467,30 @@ local function start(cmd, cmd_args, dispatchers, extra_spawn_params)
|
|||||||
-- we can still use the result.
|
-- we can still use the result.
|
||||||
schedule(function()
|
schedule(function()
|
||||||
local status, result
|
local status, result
|
||||||
status, result, err = try_call(client_errors.SERVER_REQUEST_HANDLER_ERROR,
|
status, result, err = try_call(
|
||||||
dispatchers.server_request, decoded.method, decoded.params)
|
client_errors.SERVER_REQUEST_HANDLER_ERROR,
|
||||||
local _ = log.debug() and log.debug("server_request: callback result", { status = status, result = result, err = err })
|
dispatchers.server_request,
|
||||||
|
decoded.method,
|
||||||
|
decoded.params
|
||||||
|
)
|
||||||
|
local _ = log.debug()
|
||||||
|
and log.debug('server_request: callback result', { status = status, result = result, err = err })
|
||||||
if status then
|
if status then
|
||||||
if not (result or err) then
|
if not (result or err) then
|
||||||
-- TODO this can be a problem if `null` is sent for result. needs vim.NIL
|
-- TODO this can be a problem if `null` is sent for result. needs vim.NIL
|
||||||
error(string.format("method %q: either a result or an error must be sent to the server in response", decoded.method))
|
error(
|
||||||
|
string.format(
|
||||||
|
'method %q: either a result or an error must be sent to the server in response',
|
||||||
|
decoded.method
|
||||||
|
)
|
||||||
|
)
|
||||||
end
|
end
|
||||||
if err then
|
if err then
|
||||||
assert(type(err) == 'table', "err must be a table. Use rpc_response_error to help format errors.")
|
assert(type(err) == 'table', 'err must be a table. Use rpc_response_error to help format errors.')
|
||||||
local code_name = assert(protocol.ErrorCodes[err.code], "Errors must use protocol.ErrorCodes. Use rpc_response_error to help format errors.")
|
local code_name = assert(
|
||||||
|
protocol.ErrorCodes[err.code],
|
||||||
|
'Errors must use protocol.ErrorCodes. Use rpc_response_error to help format errors.'
|
||||||
|
)
|
||||||
err.message = err.message or code_name
|
err.message = err.message or code_name
|
||||||
end
|
end
|
||||||
else
|
else
|
||||||
@@ -485,16 +502,15 @@ local function start(cmd, cmd_args, dispatchers, extra_spawn_params)
|
|||||||
end)
|
end)
|
||||||
-- This works because we are expecting vim.NIL here
|
-- This works because we are expecting vim.NIL here
|
||||||
elseif decoded.id and (decoded.result ~= vim.NIL or decoded.error ~= vim.NIL) then
|
elseif decoded.id and (decoded.result ~= vim.NIL or decoded.error ~= vim.NIL) then
|
||||||
|
|
||||||
-- We sent a number, so we expect a number.
|
-- We sent a number, so we expect a number.
|
||||||
local result_id = tonumber(decoded.id)
|
local result_id = tonumber(decoded.id)
|
||||||
|
|
||||||
-- Notify the user that a response was received for the request
|
-- Notify the user that a response was received for the request
|
||||||
local notify_reply_callback = notify_reply_callbacks and notify_reply_callbacks[result_id]
|
local notify_reply_callback = notify_reply_callbacks and notify_reply_callbacks[result_id]
|
||||||
if notify_reply_callback then
|
if notify_reply_callback then
|
||||||
validate {
|
validate({
|
||||||
notify_reply_callback = { notify_reply_callback, 'f' };
|
notify_reply_callback = { notify_reply_callback, 'f' },
|
||||||
}
|
})
|
||||||
notify_reply_callback(result_id)
|
notify_reply_callback(result_id)
|
||||||
notify_reply_callbacks[result_id] = nil
|
notify_reply_callbacks[result_id] = nil
|
||||||
end
|
end
|
||||||
@@ -503,7 +519,7 @@ local function start(cmd, cmd_args, dispatchers, extra_spawn_params)
|
|||||||
if decoded.error then
|
if decoded.error then
|
||||||
local mute_error = false
|
local mute_error = false
|
||||||
if decoded.error.code == protocol.ErrorCodes.RequestCancelled then
|
if decoded.error.code == protocol.ErrorCodes.RequestCancelled then
|
||||||
local _ = log.debug() and log.debug("Received cancellation ack", decoded)
|
local _ = log.debug() and log.debug('Received cancellation ack', decoded)
|
||||||
mute_error = true
|
mute_error = true
|
||||||
end
|
end
|
||||||
|
|
||||||
@@ -523,24 +539,22 @@ local function start(cmd, cmd_args, dispatchers, extra_spawn_params)
|
|||||||
local callback = message_callbacks and message_callbacks[result_id]
|
local callback = message_callbacks and message_callbacks[result_id]
|
||||||
if callback then
|
if callback then
|
||||||
message_callbacks[result_id] = nil
|
message_callbacks[result_id] = nil
|
||||||
validate {
|
validate({
|
||||||
callback = { callback, 'f' };
|
callback = { callback, 'f' },
|
||||||
}
|
})
|
||||||
if decoded.error then
|
if decoded.error then
|
||||||
decoded.error = setmetatable(decoded.error, {
|
decoded.error = setmetatable(decoded.error, {
|
||||||
__tostring = format_rpc_error;
|
__tostring = format_rpc_error,
|
||||||
})
|
})
|
||||||
end
|
end
|
||||||
try_call(client_errors.SERVER_RESULT_CALLBACK_ERROR,
|
try_call(client_errors.SERVER_RESULT_CALLBACK_ERROR, callback, decoded.error, decoded.result)
|
||||||
callback, decoded.error, decoded.result)
|
|
||||||
else
|
else
|
||||||
on_error(client_errors.NO_RESULT_CALLBACK_FOUND, decoded)
|
on_error(client_errors.NO_RESULT_CALLBACK_FOUND, decoded)
|
||||||
local _ = log.error() and log.error("No callback found for server response id "..result_id)
|
local _ = log.error() and log.error('No callback found for server response id ' .. result_id)
|
||||||
end
|
end
|
||||||
elseif type(decoded.method) == 'string' then
|
elseif type(decoded.method) == 'string' then
|
||||||
-- Notification
|
-- Notification
|
||||||
try_call(client_errors.NOTIFICATION_HANDLER_ERROR,
|
try_call(client_errors.NOTIFICATION_HANDLER_ERROR, dispatchers.notification, decoded.method, decoded.params)
|
||||||
dispatchers.notification, decoded.method, decoded.params)
|
|
||||||
else
|
else
|
||||||
-- Invalid server message
|
-- Invalid server message
|
||||||
on_error(client_errors.INVALID_SERVER_MESSAGE, decoded)
|
on_error(client_errors.INVALID_SERVER_MESSAGE, decoded)
|
||||||
@@ -556,7 +570,9 @@ local function start(cmd, cmd_args, dispatchers, extra_spawn_params)
|
|||||||
return
|
return
|
||||||
end
|
end
|
||||||
-- This should signal that we are done reading from the client.
|
-- This should signal that we are done reading from the client.
|
||||||
if not chunk then return end
|
if not chunk then
|
||||||
|
return
|
||||||
|
end
|
||||||
-- Flush anything in the parser by looping until we don't get a result
|
-- Flush anything in the parser by looping until we don't get a result
|
||||||
-- anymore.
|
-- anymore.
|
||||||
while true do
|
while true do
|
||||||
@@ -574,17 +590,17 @@ local function start(cmd, cmd_args, dispatchers, extra_spawn_params)
|
|||||||
end)
|
end)
|
||||||
|
|
||||||
return {
|
return {
|
||||||
pid = pid;
|
pid = pid,
|
||||||
handle = handle;
|
handle = handle,
|
||||||
request = request;
|
request = request,
|
||||||
notify = notify
|
notify = notify,
|
||||||
}
|
}
|
||||||
end
|
end
|
||||||
|
|
||||||
return {
|
return {
|
||||||
start = start;
|
start = start,
|
||||||
rpc_response_error = rpc_response_error;
|
rpc_response_error = rpc_response_error,
|
||||||
format_rpc_error = format_rpc_error;
|
format_rpc_error = format_rpc_error,
|
||||||
client_errors = client_errors;
|
client_errors = client_errors,
|
||||||
}
|
}
|
||||||
-- vim:sw=2 ts=2 et
|
-- vim:sw=2 ts=2 et
|
||||||
|
@@ -203,14 +203,30 @@ end
|
|||||||
---@param new_lastline integer
|
---@param new_lastline integer
|
||||||
---@param offset_encoding string
|
---@param offset_encoding string
|
||||||
---@returns (int, int) end_line_idx and end_col_idx of range
|
---@returns (int, int) end_line_idx and end_col_idx of range
|
||||||
local function compute_end_range(prev_lines, curr_lines, start_range, firstline, lastline, new_lastline, offset_encoding)
|
local function compute_end_range(
|
||||||
|
prev_lines,
|
||||||
|
curr_lines,
|
||||||
|
start_range,
|
||||||
|
firstline,
|
||||||
|
lastline,
|
||||||
|
new_lastline,
|
||||||
|
offset_encoding
|
||||||
|
)
|
||||||
-- If firstline == new_lastline, the first change occurred on a line that was deleted.
|
-- If firstline == new_lastline, the first change occurred on a line that was deleted.
|
||||||
-- In this case, the last_byte...
|
-- In this case, the last_byte...
|
||||||
if firstline == new_lastline then
|
if firstline == new_lastline then
|
||||||
return { line_idx = (lastline - new_lastline + firstline), byte_idx = 1, char_idx = 1 }, { line_idx = firstline, byte_idx = 1, char_idx = 1 }
|
return { line_idx = (lastline - new_lastline + firstline), byte_idx = 1, char_idx = 1 }, {
|
||||||
|
line_idx = firstline,
|
||||||
|
byte_idx = 1,
|
||||||
|
char_idx = 1,
|
||||||
|
}
|
||||||
end
|
end
|
||||||
if firstline == lastline then
|
if firstline == lastline then
|
||||||
return { line_idx = firstline, byte_idx = 1, char_idx = 1 }, { line_idx = new_lastline - lastline + firstline, byte_idx = 1, char_idx = 1 }
|
return { line_idx = firstline, byte_idx = 1, char_idx = 1 }, {
|
||||||
|
line_idx = new_lastline - lastline + firstline,
|
||||||
|
byte_idx = 1,
|
||||||
|
char_idx = 1,
|
||||||
|
}
|
||||||
end
|
end
|
||||||
-- Compare on last line, at minimum will be the start range
|
-- Compare on last line, at minimum will be the start range
|
||||||
local start_line_idx = start_range.line_idx
|
local start_line_idx = start_range.line_idx
|
||||||
@@ -239,9 +255,7 @@ local function compute_end_range(prev_lines, curr_lines, start_range, firstline,
|
|||||||
end
|
end
|
||||||
for idx = 0, max_length do
|
for idx = 0, max_length do
|
||||||
byte_offset = idx
|
byte_offset = idx
|
||||||
if
|
if str_byte(prev_line, prev_line_length - byte_offset) ~= str_byte(curr_line, curr_line_length - byte_offset) then
|
||||||
str_byte(prev_line, prev_line_length - byte_offset) ~= str_byte(curr_line, curr_line_length - byte_offset)
|
|
||||||
then
|
|
||||||
break
|
break
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
@@ -282,13 +296,12 @@ end
|
|||||||
---@returns string text extracted from defined region
|
---@returns string text extracted from defined region
|
||||||
local function extract_text(lines, start_range, end_range, line_ending)
|
local function extract_text(lines, start_range, end_range, line_ending)
|
||||||
if not lines[start_range.line_idx] then
|
if not lines[start_range.line_idx] then
|
||||||
return ""
|
return ''
|
||||||
end
|
end
|
||||||
-- Trivial case: start and end range are the same line, directly grab changed text
|
-- Trivial case: start and end range are the same line, directly grab changed text
|
||||||
if start_range.line_idx == end_range.line_idx then
|
if start_range.line_idx == end_range.line_idx then
|
||||||
-- string.sub is inclusive, end_range is not
|
-- string.sub is inclusive, end_range is not
|
||||||
return string.sub(lines[start_range.line_idx], start_range.byte_idx, end_range.byte_idx - 1)
|
return string.sub(lines[start_range.line_idx], start_range.byte_idx, end_range.byte_idx - 1)
|
||||||
|
|
||||||
else
|
else
|
||||||
-- Handle deletion case
|
-- Handle deletion case
|
||||||
-- Collect the changed portion of the first changed line
|
-- Collect the changed portion of the first changed line
|
||||||
@@ -303,7 +316,7 @@ local function extract_text(lines, start_range, end_range, line_ending)
|
|||||||
-- Collect the changed portion of the last changed line.
|
-- Collect the changed portion of the last changed line.
|
||||||
table.insert(result, string.sub(lines[end_range.line_idx], 1, end_range.byte_idx - 1))
|
table.insert(result, string.sub(lines[end_range.line_idx], 1, end_range.byte_idx - 1))
|
||||||
else
|
else
|
||||||
table.insert(result, "")
|
table.insert(result, '')
|
||||||
end
|
end
|
||||||
|
|
||||||
-- Add line ending between all lines
|
-- Add line ending between all lines
|
||||||
|
File diff suppressed because it is too large
Load Diff
@@ -24,7 +24,9 @@ vim.deepcopy = (function()
|
|||||||
|
|
||||||
local deepcopy_funcs = {
|
local deepcopy_funcs = {
|
||||||
table = function(orig, cache)
|
table = function(orig, cache)
|
||||||
if cache[orig] then return cache[orig] end
|
if cache[orig] then
|
||||||
|
return cache[orig]
|
||||||
|
end
|
||||||
local copy = {}
|
local copy = {}
|
||||||
|
|
||||||
cache[orig] = copy
|
cache[orig] = copy
|
||||||
@@ -46,7 +48,7 @@ vim.deepcopy = (function()
|
|||||||
if f then
|
if f then
|
||||||
return f(orig, cache or {})
|
return f(orig, cache or {})
|
||||||
else
|
else
|
||||||
error("Cannot deepcopy object of type "..type(orig))
|
error('Cannot deepcopy object of type ' .. type(orig))
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
end)()
|
end)()
|
||||||
@@ -62,14 +64,14 @@ end)()
|
|||||||
---@param plain If `true` use `sep` literally (passed to String.find)
|
---@param plain If `true` use `sep` literally (passed to String.find)
|
||||||
---@returns Iterator over the split components
|
---@returns Iterator over the split components
|
||||||
function vim.gsplit(s, sep, plain)
|
function vim.gsplit(s, sep, plain)
|
||||||
vim.validate{s={s,'s'},sep={sep,'s'},plain={plain,'b',true}}
|
vim.validate({ s = { s, 's' }, sep = { sep, 's' }, plain = { plain, 'b', true } })
|
||||||
|
|
||||||
local start = 1
|
local start = 1
|
||||||
local done = false
|
local done = false
|
||||||
|
|
||||||
local function _pass(i, j, ...)
|
local function _pass(i, j, ...)
|
||||||
if i then
|
if i then
|
||||||
assert(j+1 > start, "Infinite loop detected")
|
assert(j + 1 > start, 'Infinite loop detected')
|
||||||
local seg = s:sub(start, i - 1)
|
local seg = s:sub(start, i - 1)
|
||||||
start = j + 1
|
start = j + 1
|
||||||
return seg, ...
|
return seg, ...
|
||||||
@@ -119,7 +121,7 @@ function vim.split(s, sep, kwargs)
|
|||||||
-- Support old signature for backward compatibility
|
-- Support old signature for backward compatibility
|
||||||
plain = kwargs
|
plain = kwargs
|
||||||
else
|
else
|
||||||
vim.validate { kwargs = {kwargs, 't', true} }
|
vim.validate({ kwargs = { kwargs, 't', true } })
|
||||||
kwargs = kwargs or {}
|
kwargs = kwargs or {}
|
||||||
plain = kwargs.plain
|
plain = kwargs.plain
|
||||||
trimempty = kwargs.trimempty
|
trimempty = kwargs.trimempty
|
||||||
@@ -128,7 +130,7 @@ function vim.split(s, sep, kwargs)
|
|||||||
local t = {}
|
local t = {}
|
||||||
local skip = trimempty
|
local skip = trimempty
|
||||||
for c in vim.gsplit(s, sep, plain) do
|
for c in vim.gsplit(s, sep, plain) do
|
||||||
if c ~= "" then
|
if c ~= '' then
|
||||||
skip = false
|
skip = false
|
||||||
end
|
end
|
||||||
|
|
||||||
@@ -139,7 +141,7 @@ function vim.split(s, sep, kwargs)
|
|||||||
|
|
||||||
if trimempty then
|
if trimempty then
|
||||||
for i = #t, 1, -1 do
|
for i = #t, 1, -1 do
|
||||||
if t[i] ~= "" then
|
if t[i] ~= '' then
|
||||||
break
|
break
|
||||||
end
|
end
|
||||||
table.remove(t, i)
|
table.remove(t, i)
|
||||||
@@ -157,7 +159,7 @@ end
|
|||||||
---@param t Table
|
---@param t Table
|
||||||
---@returns list of keys
|
---@returns list of keys
|
||||||
function vim.tbl_keys(t)
|
function vim.tbl_keys(t)
|
||||||
assert(type(t) == 'table', string.format("Expected table, got %s", type(t)))
|
assert(type(t) == 'table', string.format('Expected table, got %s', type(t)))
|
||||||
|
|
||||||
local keys = {}
|
local keys = {}
|
||||||
for k, _ in pairs(t) do
|
for k, _ in pairs(t) do
|
||||||
@@ -172,7 +174,7 @@ end
|
|||||||
---@param t Table
|
---@param t Table
|
||||||
---@returns list of values
|
---@returns list of values
|
||||||
function vim.tbl_values(t)
|
function vim.tbl_values(t)
|
||||||
assert(type(t) == 'table', string.format("Expected table, got %s", type(t)))
|
assert(type(t) == 'table', string.format('Expected table, got %s', type(t)))
|
||||||
|
|
||||||
local values = {}
|
local values = {}
|
||||||
for _, v in pairs(t) do
|
for _, v in pairs(t) do
|
||||||
@@ -186,7 +188,7 @@ end
|
|||||||
---@param func function or callable table
|
---@param func function or callable table
|
||||||
---@param t table
|
---@param t table
|
||||||
function vim.tbl_map(func, t)
|
function vim.tbl_map(func, t)
|
||||||
vim.validate{func={func,'c'},t={t,'t'}}
|
vim.validate({ func = { func, 'c' }, t = { t, 't' } })
|
||||||
|
|
||||||
local rettab = {}
|
local rettab = {}
|
||||||
for k, v in pairs(t) do
|
for k, v in pairs(t) do
|
||||||
@@ -200,7 +202,7 @@ end
|
|||||||
---@param func function or callable table
|
---@param func function or callable table
|
||||||
---@param t table
|
---@param t table
|
||||||
function vim.tbl_filter(func, t)
|
function vim.tbl_filter(func, t)
|
||||||
vim.validate{func={func,'c'},t={t,'t'}}
|
vim.validate({ func = { func, 'c' }, t = { t, 't' } })
|
||||||
|
|
||||||
local rettab = {}
|
local rettab = {}
|
||||||
for _, entry in pairs(t) do
|
for _, entry in pairs(t) do
|
||||||
@@ -217,7 +219,7 @@ end
|
|||||||
---@param value Value to compare
|
---@param value Value to compare
|
||||||
---@returns true if `t` contains `value`
|
---@returns true if `t` contains `value`
|
||||||
function vim.tbl_contains(t, value)
|
function vim.tbl_contains(t, value)
|
||||||
vim.validate{t={t,'t'}}
|
vim.validate({ t = { t, 't' } })
|
||||||
|
|
||||||
for _, v in ipairs(t) do
|
for _, v in ipairs(t) do
|
||||||
if v == value then
|
if v == value then
|
||||||
@@ -233,18 +235,18 @@ end
|
|||||||
---
|
---
|
||||||
---@param t Table to check
|
---@param t Table to check
|
||||||
function vim.tbl_isempty(t)
|
function vim.tbl_isempty(t)
|
||||||
assert(type(t) == 'table', string.format("Expected table, got %s", type(t)))
|
assert(type(t) == 'table', string.format('Expected table, got %s', type(t)))
|
||||||
return next(t) == nil
|
return next(t) == nil
|
||||||
end
|
end
|
||||||
|
|
||||||
--- we only merge empty tables or tables that are not a list
|
--- we only merge empty tables or tables that are not a list
|
||||||
---@private
|
---@private
|
||||||
local function can_merge(v)
|
local function can_merge(v)
|
||||||
return type(v) == "table" and (vim.tbl_isempty(v) or not vim.tbl_islist(v))
|
return type(v) == 'table' and (vim.tbl_isempty(v) or not vim.tbl_islist(v))
|
||||||
end
|
end
|
||||||
|
|
||||||
local function tbl_extend(behavior, deep_extend, ...)
|
local function tbl_extend(behavior, deep_extend, ...)
|
||||||
if (behavior ~= 'error' and behavior ~= 'keep' and behavior ~= 'force') then
|
if behavior ~= 'error' and behavior ~= 'keep' and behavior ~= 'force' then
|
||||||
error('invalid "behavior": ' .. tostring(behavior))
|
error('invalid "behavior": ' .. tostring(behavior))
|
||||||
end
|
end
|
||||||
|
|
||||||
@@ -259,7 +261,7 @@ local function tbl_extend(behavior, deep_extend, ...)
|
|||||||
|
|
||||||
for i = 1, select('#', ...) do
|
for i = 1, select('#', ...) do
|
||||||
local tbl = select(i, ...)
|
local tbl = select(i, ...)
|
||||||
vim.validate{["after the second argument"] = {tbl,'t'}}
|
vim.validate({ ['after the second argument'] = { tbl, 't' } })
|
||||||
if tbl then
|
if tbl then
|
||||||
for k, v in pairs(tbl) do
|
for k, v in pairs(tbl) do
|
||||||
if deep_extend and can_merge(v) and can_merge(ret[k]) then
|
if deep_extend and can_merge(v) and can_merge(ret[k]) then
|
||||||
@@ -311,8 +313,12 @@ end
|
|||||||
---@param b second value
|
---@param b second value
|
||||||
---@returns `true` if values are equals, else `false`.
|
---@returns `true` if values are equals, else `false`.
|
||||||
function vim.deep_equal(a, b)
|
function vim.deep_equal(a, b)
|
||||||
if a == b then return true end
|
if a == b then
|
||||||
if type(a) ~= type(b) then return false end
|
return true
|
||||||
|
end
|
||||||
|
if type(a) ~= type(b) then
|
||||||
|
return false
|
||||||
|
end
|
||||||
if type(a) == 'table' then
|
if type(a) == 'table' then
|
||||||
for k, v in pairs(a) do
|
for k, v in pairs(a) do
|
||||||
if not vim.deep_equal(v, b[k]) then
|
if not vim.deep_equal(v, b[k]) then
|
||||||
@@ -340,7 +346,13 @@ function vim.tbl_add_reverse_lookup(o)
|
|||||||
for _, k in ipairs(keys) do
|
for _, k in ipairs(keys) do
|
||||||
local v = o[k]
|
local v = o[k]
|
||||||
if o[v] then
|
if o[v] then
|
||||||
error(string.format("The reverse lookup found an existing value for %q while processing key %q", tostring(v), tostring(k)))
|
error(
|
||||||
|
string.format(
|
||||||
|
'The reverse lookup found an existing value for %q while processing key %q',
|
||||||
|
tostring(v),
|
||||||
|
tostring(k)
|
||||||
|
)
|
||||||
|
)
|
||||||
end
|
end
|
||||||
o[v] = k
|
o[v] = k
|
||||||
end
|
end
|
||||||
@@ -389,12 +401,12 @@ end
|
|||||||
---@param finish Final index on src. defaults to #src
|
---@param finish Final index on src. defaults to #src
|
||||||
---@returns dst
|
---@returns dst
|
||||||
function vim.list_extend(dst, src, start, finish)
|
function vim.list_extend(dst, src, start, finish)
|
||||||
vim.validate {
|
vim.validate({
|
||||||
dst = {dst, 't'};
|
dst = { dst, 't' },
|
||||||
src = {src, 't'};
|
src = { src, 't' },
|
||||||
start = {start, 'n', true};
|
start = { start, 'n', true },
|
||||||
finish = {finish, 'n', true};
|
finish = { finish, 'n', true },
|
||||||
}
|
})
|
||||||
for i = start or 1, finish or #src do
|
for i = start or 1, finish or #src do
|
||||||
table.insert(dst, src[i])
|
table.insert(dst, src[i])
|
||||||
end
|
end
|
||||||
@@ -414,7 +426,7 @@ function vim.tbl_flatten(t)
|
|||||||
local n = #_t
|
local n = #_t
|
||||||
for i = 1, n do
|
for i = 1, n do
|
||||||
local v = _t[i]
|
local v = _t[i]
|
||||||
if type(v) == "table" then
|
if type(v) == 'table' then
|
||||||
_tbl_flatten(v)
|
_tbl_flatten(v)
|
||||||
elseif v then
|
elseif v then
|
||||||
table.insert(result, v)
|
table.insert(result, v)
|
||||||
@@ -441,7 +453,7 @@ function vim.tbl_islist(t)
|
|||||||
local count = 0
|
local count = 0
|
||||||
|
|
||||||
for k, _ in pairs(t) do
|
for k, _ in pairs(t) do
|
||||||
if type(k) == "number" then
|
if type(k) == 'number' then
|
||||||
count = count + 1
|
count = count + 1
|
||||||
else
|
else
|
||||||
return false
|
return false
|
||||||
@@ -471,10 +483,12 @@ end
|
|||||||
---@param t Table
|
---@param t Table
|
||||||
---@returns Number that is the number of the value in table
|
---@returns Number that is the number of the value in table
|
||||||
function vim.tbl_count(t)
|
function vim.tbl_count(t)
|
||||||
vim.validate{t={t,'t'}}
|
vim.validate({ t = { t, 't' } })
|
||||||
|
|
||||||
local count = 0
|
local count = 0
|
||||||
for _ in pairs(t) do count = count + 1 end
|
for _ in pairs(t) do
|
||||||
|
count = count + 1
|
||||||
|
end
|
||||||
return count
|
return count
|
||||||
end
|
end
|
||||||
|
|
||||||
@@ -498,7 +512,7 @@ end
|
|||||||
---@param s String to trim
|
---@param s String to trim
|
||||||
---@returns String with whitespace removed from its beginning and end
|
---@returns String with whitespace removed from its beginning and end
|
||||||
function vim.trim(s)
|
function vim.trim(s)
|
||||||
vim.validate{s={s,'s'}}
|
vim.validate({ s = { s, 's' } })
|
||||||
return s:match('^%s*(.*%S)') or ''
|
return s:match('^%s*(.*%S)') or ''
|
||||||
end
|
end
|
||||||
|
|
||||||
@@ -508,7 +522,7 @@ end
|
|||||||
---@param s String to escape
|
---@param s String to escape
|
||||||
---@returns %-escaped pattern string
|
---@returns %-escaped pattern string
|
||||||
function vim.pesc(s)
|
function vim.pesc(s)
|
||||||
vim.validate{s={s,'s'}}
|
vim.validate({ s = { s, 's' } })
|
||||||
return s:gsub('[%(%)%.%%%+%-%*%?%[%]%^%$]', '%%%1')
|
return s:gsub('[%(%)%.%%%+%-%*%?%[%]%^%$]', '%%%1')
|
||||||
end
|
end
|
||||||
|
|
||||||
@@ -518,7 +532,7 @@ end
|
|||||||
---@param prefix (string) a prefix
|
---@param prefix (string) a prefix
|
||||||
---@return (boolean) true if `prefix` is a prefix of s
|
---@return (boolean) true if `prefix` is a prefix of s
|
||||||
function vim.startswith(s, prefix)
|
function vim.startswith(s, prefix)
|
||||||
vim.validate { s = {s, 's'}; prefix = {prefix, 's'}; }
|
vim.validate({ s = { s, 's' }, prefix = { prefix, 's' } })
|
||||||
return s:sub(1, #prefix) == prefix
|
return s:sub(1, #prefix) == prefix
|
||||||
end
|
end
|
||||||
|
|
||||||
@@ -528,7 +542,7 @@ end
|
|||||||
---@param suffix (string) a suffix
|
---@param suffix (string) a suffix
|
||||||
---@return (boolean) true if `suffix` is a suffix of s
|
---@return (boolean) true if `suffix` is a suffix of s
|
||||||
function vim.endswith(s, suffix)
|
function vim.endswith(s, suffix)
|
||||||
vim.validate { s = {s, 's'}; suffix = {suffix, 's'}; }
|
vim.validate({ s = { s, 's' }, suffix = { suffix, 's' } })
|
||||||
return #suffix == 0 or s:sub(-#suffix) == suffix
|
return #suffix == 0 or s:sub(-#suffix) == suffix
|
||||||
end
|
end
|
||||||
|
|
||||||
@@ -586,12 +600,18 @@ function vim.validate(opt) end -- luacheck: no unused
|
|||||||
|
|
||||||
do
|
do
|
||||||
local type_names = {
|
local type_names = {
|
||||||
['table'] = 'table', t = 'table',
|
['table'] = 'table',
|
||||||
['string'] = 'string', s = 'string',
|
t = 'table',
|
||||||
['number'] = 'number', n = 'number',
|
['string'] = 'string',
|
||||||
['boolean'] = 'boolean', b = 'boolean',
|
s = 'string',
|
||||||
['function'] = 'function', f = 'function',
|
['number'] = 'number',
|
||||||
['callable'] = 'callable', c = 'callable',
|
n = 'number',
|
||||||
|
['boolean'] = 'boolean',
|
||||||
|
b = 'boolean',
|
||||||
|
['function'] = 'function',
|
||||||
|
f = 'function',
|
||||||
|
['callable'] = 'callable',
|
||||||
|
c = 'callable',
|
||||||
['nil'] = 'nil',
|
['nil'] = 'nil',
|
||||||
['thread'] = 'thread',
|
['thread'] = 'thread',
|
||||||
['userdata'] = 'userdata',
|
['userdata'] = 'userdata',
|
||||||
@@ -624,9 +644,9 @@ do
|
|||||||
-- Check user-provided validation function.
|
-- Check user-provided validation function.
|
||||||
local valid, optional_message = types(val)
|
local valid, optional_message = types(val)
|
||||||
if not valid then
|
if not valid then
|
||||||
local error_message = string.format("%s: expected %s, got %s", param_name, (spec[3] or '?'), tostring(val))
|
local error_message = string.format('%s: expected %s, got %s', param_name, (spec[3] or '?'), tostring(val))
|
||||||
if optional_message ~= nil then
|
if optional_message ~= nil then
|
||||||
error_message = error_message .. string.format(". Info: %s", optional_message)
|
error_message = error_message .. string.format('. Info: %s', optional_message)
|
||||||
end
|
end
|
||||||
|
|
||||||
return false, error_message
|
return false, error_message
|
||||||
@@ -646,10 +666,10 @@ do
|
|||||||
end
|
end
|
||||||
end
|
end
|
||||||
if not success then
|
if not success then
|
||||||
return false, string.format("%s: expected %s, got %s", param_name, table.concat(types, '|'), type(val))
|
return false, string.format('%s: expected %s, got %s', param_name, table.concat(types, '|'), type(val))
|
||||||
end
|
end
|
||||||
else
|
else
|
||||||
return false, string.format("invalid type name: %s", tostring(types))
|
return false, string.format('invalid type name: %s', tostring(types))
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
@@ -668,9 +688,13 @@ end
|
|||||||
---@param f Any object
|
---@param f Any object
|
||||||
---@return true if `f` is callable, else false
|
---@return true if `f` is callable, else false
|
||||||
function vim.is_callable(f)
|
function vim.is_callable(f)
|
||||||
if type(f) == 'function' then return true end
|
if type(f) == 'function' then
|
||||||
|
return true
|
||||||
|
end
|
||||||
local m = getmetatable(f)
|
local m = getmetatable(f)
|
||||||
if m == nil then return false end
|
if m == nil then
|
||||||
|
return false
|
||||||
|
end
|
||||||
return type(m.__call) == 'function'
|
return type(m.__call) == 'function'
|
||||||
end
|
end
|
||||||
|
|
||||||
|
@@ -1,31 +1,31 @@
|
|||||||
local a = vim.api
|
local a = vim.api
|
||||||
local query = require'vim.treesitter.query'
|
local query = require('vim.treesitter.query')
|
||||||
local language = require'vim.treesitter.language'
|
local language = require('vim.treesitter.language')
|
||||||
local LanguageTree = require'vim.treesitter.languagetree'
|
local LanguageTree = require('vim.treesitter.languagetree')
|
||||||
|
|
||||||
-- TODO(bfredl): currently we retain parsers for the lifetime of the buffer.
|
-- TODO(bfredl): currently we retain parsers for the lifetime of the buffer.
|
||||||
-- Consider use weak references to release parser if all plugins are done with
|
-- Consider use weak references to release parser if all plugins are done with
|
||||||
-- it.
|
-- it.
|
||||||
local parsers = {}
|
local parsers = {}
|
||||||
|
|
||||||
local M = vim.tbl_extend("error", query, language)
|
local M = vim.tbl_extend('error', query, language)
|
||||||
|
|
||||||
M.language_version = vim._ts_get_language_version()
|
M.language_version = vim._ts_get_language_version()
|
||||||
M.minimum_language_version = vim._ts_get_minimum_language_version()
|
M.minimum_language_version = vim._ts_get_minimum_language_version()
|
||||||
|
|
||||||
setmetatable(M, {
|
setmetatable(M, {
|
||||||
__index = function(t, k)
|
__index = function(t, k)
|
||||||
if k == "highlighter" then
|
if k == 'highlighter' then
|
||||||
t[k] = require'vim.treesitter.highlighter'
|
t[k] = require('vim.treesitter.highlighter')
|
||||||
return t[k]
|
return t[k]
|
||||||
elseif k == "language" then
|
elseif k == 'language' then
|
||||||
t[k] = require"vim.treesitter.language"
|
t[k] = require('vim.treesitter.language')
|
||||||
return t[k]
|
return t[k]
|
||||||
elseif k == "query" then
|
elseif k == 'query' then
|
||||||
t[k] = require"vim.treesitter.query"
|
t[k] = require('vim.treesitter.query')
|
||||||
return t[k]
|
return t[k]
|
||||||
end
|
end
|
||||||
end
|
end,
|
||||||
})
|
})
|
||||||
|
|
||||||
--- Creates a new parser.
|
--- Creates a new parser.
|
||||||
@@ -63,7 +63,11 @@ function M._create_parser(bufnr, lang, opts)
|
|||||||
self:_on_reload(...)
|
self:_on_reload(...)
|
||||||
end
|
end
|
||||||
|
|
||||||
a.nvim_buf_attach(self:source(), false, {on_bytes=bytes_cb, on_detach=detach_cb, on_reload=reload_cb, preview=true})
|
a.nvim_buf_attach(
|
||||||
|
self:source(),
|
||||||
|
false,
|
||||||
|
{ on_bytes = bytes_cb, on_detach = detach_cb, on_reload = reload_cb, preview = true }
|
||||||
|
)
|
||||||
|
|
||||||
self:parse()
|
self:parse()
|
||||||
|
|
||||||
@@ -87,7 +91,7 @@ function M.get_parser(bufnr, lang, opts)
|
|||||||
bufnr = a.nvim_get_current_buf()
|
bufnr = a.nvim_get_current_buf()
|
||||||
end
|
end
|
||||||
if lang == nil then
|
if lang == nil then
|
||||||
lang = a.nvim_buf_get_option(bufnr, "filetype")
|
lang = a.nvim_buf_get_option(bufnr, 'filetype')
|
||||||
end
|
end
|
||||||
|
|
||||||
if parsers[bufnr] == nil or parsers[bufnr]:lang() ~= lang then
|
if parsers[bufnr] == nil or parsers[bufnr]:lang() ~= lang then
|
||||||
@@ -105,10 +109,10 @@ end
|
|||||||
---@param lang The language of this string
|
---@param lang The language of this string
|
||||||
---@param opts Options to pass to the created language tree
|
---@param opts Options to pass to the created language tree
|
||||||
function M.get_string_parser(str, lang, opts)
|
function M.get_string_parser(str, lang, opts)
|
||||||
vim.validate {
|
vim.validate({
|
||||||
str = { str, 'string' },
|
str = { str, 'string' },
|
||||||
lang = { lang, 'string' }
|
lang = { lang, 'string' },
|
||||||
}
|
})
|
||||||
language.require_language(lang)
|
language.require_language(lang)
|
||||||
|
|
||||||
return LanguageTree.new(str, lang, opts)
|
return LanguageTree.new(str, lang, opts)
|
||||||
|
@@ -15,24 +15,22 @@ function M.check()
|
|||||||
local report_error = vim.fn['health#report_error']
|
local report_error = vim.fn['health#report_error']
|
||||||
local parsers = M.list_parsers()
|
local parsers = M.list_parsers()
|
||||||
|
|
||||||
report_info(string.format("Runtime ABI version : %d", ts.language_version))
|
report_info(string.format('Runtime ABI version : %d', ts.language_version))
|
||||||
|
|
||||||
for _, parser in pairs(parsers) do
|
for _, parser in pairs(parsers) do
|
||||||
local parsername = vim.fn.fnamemodify(parser, ":t:r")
|
local parsername = vim.fn.fnamemodify(parser, ':t:r')
|
||||||
|
|
||||||
local is_loadable, ret = pcall(ts.language.require_language, parsername)
|
local is_loadable, ret = pcall(ts.language.require_language, parsername)
|
||||||
|
|
||||||
if not is_loadable then
|
if not is_loadable then
|
||||||
report_error(string.format("Impossible to load parser for %s: %s", parsername, ret))
|
report_error(string.format('Impossible to load parser for %s: %s', parsername, ret))
|
||||||
elseif ret then
|
elseif ret then
|
||||||
local lang = ts.language.inspect_language(parsername)
|
local lang = ts.language.inspect_language(parsername)
|
||||||
report_ok(string.format("Loaded parser for %s: ABI version %d",
|
report_ok(string.format('Loaded parser for %s: ABI version %d', parsername, lang._abi_version))
|
||||||
parsername, lang._abi_version))
|
|
||||||
else
|
else
|
||||||
report_error(string.format("Unable to load parser for %s", parsername))
|
report_error(string.format('Unable to load parser for %s', parsername))
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
return M
|
return M
|
||||||
|
|
||||||
|
@@ -1,5 +1,5 @@
|
|||||||
local a = vim.api
|
local a = vim.api
|
||||||
local query = require"vim.treesitter.query"
|
local query = require('vim.treesitter.query')
|
||||||
|
|
||||||
-- support reload for quick experimentation
|
-- support reload for quick experimentation
|
||||||
local TSHighlighter = rawget(vim.treesitter, 'TSHighlighter') or {}
|
local TSHighlighter = rawget(vim.treesitter, 'TSHighlighter') or {}
|
||||||
@@ -10,13 +10,13 @@ TSHighlighter.active = TSHighlighter.active or {}
|
|||||||
local TSHighlighterQuery = {}
|
local TSHighlighterQuery = {}
|
||||||
TSHighlighterQuery.__index = TSHighlighterQuery
|
TSHighlighterQuery.__index = TSHighlighterQuery
|
||||||
|
|
||||||
local ns = a.nvim_create_namespace("treesitter/highlighter")
|
local ns = a.nvim_create_namespace('treesitter/highlighter')
|
||||||
|
|
||||||
local _default_highlights = {}
|
local _default_highlights = {}
|
||||||
local _link_default_highlight_once = function(from, to)
|
local _link_default_highlight_once = function(from, to)
|
||||||
if not _default_highlights[from] then
|
if not _default_highlights[from] then
|
||||||
_default_highlights[from] = true
|
_default_highlights[from] = true
|
||||||
vim.cmd(string.format("highlight default link %s %s", from, to))
|
vim.cmd(string.format('highlight default link %s %s', from, to))
|
||||||
end
|
end
|
||||||
|
|
||||||
return from
|
return from
|
||||||
@@ -31,65 +31,65 @@ local subcapture_fallback = {
|
|||||||
shortened = shortened:match('(.*)%.')
|
shortened = shortened:match('(.*)%.')
|
||||||
rtn = shortened and rawget(self, shortened)
|
rtn = shortened and rawget(self, shortened)
|
||||||
end
|
end
|
||||||
rawset(self, capture, rtn or "__notfound")
|
rawset(self, capture, rtn or '__notfound')
|
||||||
return rtn
|
return rtn
|
||||||
end
|
end,
|
||||||
}
|
}
|
||||||
|
|
||||||
TSHighlighter.hl_map = setmetatable({
|
TSHighlighter.hl_map = setmetatable({
|
||||||
["error"] = "Error",
|
['error'] = 'Error',
|
||||||
["text.underline"] = "Underlined",
|
['text.underline'] = 'Underlined',
|
||||||
["todo"] = "Todo",
|
['todo'] = 'Todo',
|
||||||
["debug"] = "Debug",
|
['debug'] = 'Debug',
|
||||||
|
|
||||||
-- Miscs
|
-- Miscs
|
||||||
["comment"] = "Comment",
|
['comment'] = 'Comment',
|
||||||
["punctuation.delimiter"] = "Delimiter",
|
['punctuation.delimiter'] = 'Delimiter',
|
||||||
["punctuation.bracket"] = "Delimiter",
|
['punctuation.bracket'] = 'Delimiter',
|
||||||
["punctuation.special"] = "Delimiter",
|
['punctuation.special'] = 'Delimiter',
|
||||||
|
|
||||||
-- Constants
|
-- Constants
|
||||||
["constant"] = "Constant",
|
['constant'] = 'Constant',
|
||||||
["constant.builtin"] = "Special",
|
['constant.builtin'] = 'Special',
|
||||||
["constant.macro"] = "Define",
|
['constant.macro'] = 'Define',
|
||||||
["define"] = "Define",
|
['define'] = 'Define',
|
||||||
["macro"] = "Macro",
|
['macro'] = 'Macro',
|
||||||
["string"] = "String",
|
['string'] = 'String',
|
||||||
["string.regex"] = "String",
|
['string.regex'] = 'String',
|
||||||
["string.escape"] = "SpecialChar",
|
['string.escape'] = 'SpecialChar',
|
||||||
["character"] = "Character",
|
['character'] = 'Character',
|
||||||
["character.special"] = "SpecialChar",
|
['character.special'] = 'SpecialChar',
|
||||||
["number"] = "Number",
|
['number'] = 'Number',
|
||||||
["boolean"] = "Boolean",
|
['boolean'] = 'Boolean',
|
||||||
["float"] = "Float",
|
['float'] = 'Float',
|
||||||
|
|
||||||
-- Functions
|
-- Functions
|
||||||
["function"] = "Function",
|
['function'] = 'Function',
|
||||||
["function.special"] = "Function",
|
['function.special'] = 'Function',
|
||||||
["function.builtin"] = "Special",
|
['function.builtin'] = 'Special',
|
||||||
["function.macro"] = "Macro",
|
['function.macro'] = 'Macro',
|
||||||
["parameter"] = "Identifier",
|
['parameter'] = 'Identifier',
|
||||||
["method"] = "Function",
|
['method'] = 'Function',
|
||||||
["field"] = "Identifier",
|
['field'] = 'Identifier',
|
||||||
["property"] = "Identifier",
|
['property'] = 'Identifier',
|
||||||
["constructor"] = "Special",
|
['constructor'] = 'Special',
|
||||||
|
|
||||||
-- Keywords
|
-- Keywords
|
||||||
["conditional"] = "Conditional",
|
['conditional'] = 'Conditional',
|
||||||
["repeat"] = "Repeat",
|
['repeat'] = 'Repeat',
|
||||||
["label"] = "Label",
|
['label'] = 'Label',
|
||||||
["operator"] = "Operator",
|
['operator'] = 'Operator',
|
||||||
["keyword"] = "Keyword",
|
['keyword'] = 'Keyword',
|
||||||
["exception"] = "Exception",
|
['exception'] = 'Exception',
|
||||||
|
|
||||||
["type"] = "Type",
|
['type'] = 'Type',
|
||||||
["type.builtin"] = "Type",
|
['type.builtin'] = 'Type',
|
||||||
["type.qualifier"] = "Type",
|
['type.qualifier'] = 'Type',
|
||||||
["type.definition"] = "Typedef",
|
['type.definition'] = 'Typedef',
|
||||||
["storageclass"] = "StorageClass",
|
['storageclass'] = 'StorageClass',
|
||||||
["structure"] = "Structure",
|
['structure'] = 'Structure',
|
||||||
["include"] = "Include",
|
['include'] = 'Include',
|
||||||
["preproc"] = "PreProc",
|
['preproc'] = 'PreProc',
|
||||||
}, subcapture_fallback)
|
}, subcapture_fallback)
|
||||||
|
|
||||||
---@private
|
---@private
|
||||||
@@ -113,13 +113,13 @@ function TSHighlighterQuery.new(lang, query_string)
|
|||||||
|
|
||||||
rawset(table, capture, id)
|
rawset(table, capture, id)
|
||||||
return id
|
return id
|
||||||
end
|
end,
|
||||||
})
|
})
|
||||||
|
|
||||||
if query_string then
|
if query_string then
|
||||||
self._query = query.parse_query(lang, query_string)
|
self._query = query.parse_query(lang, query_string)
|
||||||
else
|
else
|
||||||
self._query = query.get_query(lang, "highlights")
|
self._query = query.get_query(lang, 'highlights')
|
||||||
end
|
end
|
||||||
|
|
||||||
return self
|
return self
|
||||||
@@ -152,17 +152,23 @@ end
|
|||||||
function TSHighlighter.new(tree, opts)
|
function TSHighlighter.new(tree, opts)
|
||||||
local self = setmetatable({}, TSHighlighter)
|
local self = setmetatable({}, TSHighlighter)
|
||||||
|
|
||||||
if type(tree:source()) ~= "number" then
|
if type(tree:source()) ~= 'number' then
|
||||||
error("TSHighlighter can not be used with a string parser source.")
|
error('TSHighlighter can not be used with a string parser source.')
|
||||||
end
|
end
|
||||||
|
|
||||||
opts = opts or {}
|
opts = opts or {}
|
||||||
self.tree = tree
|
self.tree = tree
|
||||||
tree:register_cbs {
|
tree:register_cbs({
|
||||||
on_changedtree = function(...) self:on_changedtree(...) end;
|
on_changedtree = function(...)
|
||||||
on_bytes = function(...) self:on_bytes(...) end;
|
self:on_changedtree(...)
|
||||||
on_detach = function(...) self:on_detach(...) end;
|
end,
|
||||||
}
|
on_bytes = function(...)
|
||||||
|
self:on_bytes(...)
|
||||||
|
end,
|
||||||
|
on_detach = function(...)
|
||||||
|
self:on_detach(...)
|
||||||
|
end,
|
||||||
|
})
|
||||||
|
|
||||||
self.bufnr = tree:source()
|
self.bufnr = tree:source()
|
||||||
self.edit_count = 0
|
self.edit_count = 0
|
||||||
@@ -181,7 +187,7 @@ function TSHighlighter.new(tree, opts)
|
|||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
a.nvim_buf_set_option(self.bufnr, "syntax", "")
|
a.nvim_buf_set_option(self.bufnr, 'syntax', '')
|
||||||
|
|
||||||
TSHighlighter.active[self.bufnr] = self
|
TSHighlighter.active[self.bufnr] = self
|
||||||
|
|
||||||
@@ -190,7 +196,7 @@ function TSHighlighter.new(tree, opts)
|
|||||||
-- syntax FileType autocmds. Later on we should integrate with the
|
-- syntax FileType autocmds. Later on we should integrate with the
|
||||||
-- `:syntax` and `set syntax=...` machinery properly.
|
-- `:syntax` and `set syntax=...` machinery properly.
|
||||||
if vim.g.syntax_on ~= 1 then
|
if vim.g.syntax_on ~= 1 then
|
||||||
vim.api.nvim_command("runtime! syntax/synload.vim")
|
vim.api.nvim_command('runtime! syntax/synload.vim')
|
||||||
end
|
end
|
||||||
|
|
||||||
self.tree:parse()
|
self.tree:parse()
|
||||||
@@ -210,7 +216,7 @@ function TSHighlighter:get_highlight_state(tstree)
|
|||||||
if not self._highlight_states[tstree] then
|
if not self._highlight_states[tstree] then
|
||||||
self._highlight_states[tstree] = {
|
self._highlight_states[tstree] = {
|
||||||
next_row = 0,
|
next_row = 0,
|
||||||
iter = nil
|
iter = nil,
|
||||||
}
|
}
|
||||||
end
|
end
|
||||||
|
|
||||||
@@ -253,19 +259,25 @@ end
|
|||||||
---@private
|
---@private
|
||||||
local function on_line_impl(self, buf, line)
|
local function on_line_impl(self, buf, line)
|
||||||
self.tree:for_each_tree(function(tstree, tree)
|
self.tree:for_each_tree(function(tstree, tree)
|
||||||
if not tstree then return end
|
if not tstree then
|
||||||
|
return
|
||||||
|
end
|
||||||
|
|
||||||
local root_node = tstree:root()
|
local root_node = tstree:root()
|
||||||
local root_start_row, _, root_end_row, _ = root_node:range()
|
local root_start_row, _, root_end_row, _ = root_node:range()
|
||||||
|
|
||||||
-- Only worry about trees within the line range
|
-- Only worry about trees within the line range
|
||||||
if root_start_row > line or root_end_row < line then return end
|
if root_start_row > line or root_end_row < line then
|
||||||
|
return
|
||||||
|
end
|
||||||
|
|
||||||
local state = self:get_highlight_state(tstree)
|
local state = self:get_highlight_state(tstree)
|
||||||
local highlighter_query = self:get_query(tree:lang())
|
local highlighter_query = self:get_query(tree:lang())
|
||||||
|
|
||||||
-- Some injected languages may not have highlight queries.
|
-- Some injected languages may not have highlight queries.
|
||||||
if not highlighter_query:query() then return end
|
if not highlighter_query:query() then
|
||||||
|
return
|
||||||
|
end
|
||||||
|
|
||||||
if state.iter == nil then
|
if state.iter == nil then
|
||||||
state.iter = highlighter_query:query():iter_captures(root_node, self.bufnr, line, root_end_row + 1)
|
state.iter = highlighter_query:query():iter_captures(root_node, self.bufnr, line, root_end_row + 1)
|
||||||
@@ -274,14 +286,17 @@ local function on_line_impl(self, buf, line)
|
|||||||
while line >= state.next_row do
|
while line >= state.next_row do
|
||||||
local capture, node, metadata = state.iter()
|
local capture, node, metadata = state.iter()
|
||||||
|
|
||||||
if capture == nil then break end
|
if capture == nil then
|
||||||
|
break
|
||||||
|
end
|
||||||
|
|
||||||
local start_row, start_col, end_row, end_col = node:range()
|
local start_row, start_col, end_row, end_col = node:range()
|
||||||
local hl = highlighter_query.hl_cache[capture]
|
local hl = highlighter_query.hl_cache[capture]
|
||||||
|
|
||||||
if hl and end_row >= line then
|
if hl and end_row >= line then
|
||||||
a.nvim_buf_set_extmark(buf, ns, start_row, start_col,
|
a.nvim_buf_set_extmark(buf, ns, start_row, start_col, {
|
||||||
{ end_line = end_row, end_col = end_col,
|
end_line = end_row,
|
||||||
|
end_col = end_col,
|
||||||
hl_group = hl,
|
hl_group = hl,
|
||||||
ephemeral = true,
|
ephemeral = true,
|
||||||
priority = tonumber(metadata.priority) or 100, -- Low but leaves room below
|
priority = tonumber(metadata.priority) or 100, -- Low but leaves room below
|
||||||
@@ -298,7 +313,9 @@ end
|
|||||||
---@private
|
---@private
|
||||||
function TSHighlighter._on_line(_, _win, buf, line, _)
|
function TSHighlighter._on_line(_, _win, buf, line, _)
|
||||||
local self = TSHighlighter.active[buf]
|
local self = TSHighlighter.active[buf]
|
||||||
if not self then return end
|
if not self then
|
||||||
|
return
|
||||||
|
end
|
||||||
|
|
||||||
on_line_impl(self, buf, line)
|
on_line_impl(self, buf, line)
|
||||||
end
|
end
|
||||||
@@ -324,9 +341,9 @@ function TSHighlighter._on_win(_, _win, buf, _topline)
|
|||||||
end
|
end
|
||||||
|
|
||||||
a.nvim_set_decoration_provider(ns, {
|
a.nvim_set_decoration_provider(ns, {
|
||||||
on_buf = TSHighlighter._on_buf;
|
on_buf = TSHighlighter._on_buf,
|
||||||
on_win = TSHighlighter._on_win;
|
on_win = TSHighlighter._on_win,
|
||||||
on_line = TSHighlighter._on_line;
|
on_line = TSHighlighter._on_line,
|
||||||
})
|
})
|
||||||
|
|
||||||
return TSHighlighter
|
return TSHighlighter
|
||||||
|
@@ -28,7 +28,9 @@ function M.require_language(lang, path, silent)
|
|||||||
end
|
end
|
||||||
|
|
||||||
if silent then
|
if silent then
|
||||||
return pcall(function() vim._ts_add_language(path, lang) end)
|
return pcall(function()
|
||||||
|
vim._ts_add_language(path, lang)
|
||||||
|
end)
|
||||||
else
|
else
|
||||||
vim._ts_add_language(path, lang)
|
vim._ts_add_language(path, lang)
|
||||||
end
|
end
|
||||||
|
@@ -1,6 +1,6 @@
|
|||||||
local a = vim.api
|
local a = vim.api
|
||||||
local query = require'vim.treesitter.query'
|
local query = require('vim.treesitter.query')
|
||||||
local language = require'vim.treesitter.language'
|
local language = require('vim.treesitter.language')
|
||||||
|
|
||||||
local LanguageTree = {}
|
local LanguageTree = {}
|
||||||
LanguageTree.__index = LanguageTree
|
LanguageTree.__index = LanguageTree
|
||||||
@@ -32,9 +32,10 @@ function LanguageTree.new(source, lang, opts)
|
|||||||
_regions = {},
|
_regions = {},
|
||||||
_trees = {},
|
_trees = {},
|
||||||
_opts = opts,
|
_opts = opts,
|
||||||
_injection_query = injections[lang]
|
_injection_query = injections[lang] and query.parse_query(lang, injections[lang]) or query.get_query(
|
||||||
and query.parse_query(lang, injections[lang])
|
lang,
|
||||||
or query.get_query(lang, "injections"),
|
'injections'
|
||||||
|
),
|
||||||
_valid = false,
|
_valid = false,
|
||||||
_parser = vim._create_ts_parser(lang),
|
_parser = vim._create_ts_parser(lang),
|
||||||
_callbacks = {
|
_callbacks = {
|
||||||
@@ -42,11 +43,10 @@ function LanguageTree.new(source, lang, opts)
|
|||||||
bytes = {},
|
bytes = {},
|
||||||
detach = {},
|
detach = {},
|
||||||
child_added = {},
|
child_added = {},
|
||||||
child_removed = {}
|
child_removed = {},
|
||||||
},
|
},
|
||||||
}, LanguageTree)
|
}, LanguageTree)
|
||||||
|
|
||||||
|
|
||||||
return self
|
return self
|
||||||
end
|
end
|
||||||
|
|
||||||
@@ -264,11 +264,11 @@ end
|
|||||||
---@param regions A list of regions this tree should manage and parse.
|
---@param regions A list of regions this tree should manage and parse.
|
||||||
function LanguageTree:set_included_regions(regions)
|
function LanguageTree:set_included_regions(regions)
|
||||||
-- TODO(vigoux): I don't think string parsers are useful for now
|
-- TODO(vigoux): I don't think string parsers are useful for now
|
||||||
if type(self._source) == "number" then
|
if type(self._source) == 'number' then
|
||||||
-- Transform the tables from 4 element long to 6 element long (with byte offset)
|
-- Transform the tables from 4 element long to 6 element long (with byte offset)
|
||||||
for _, region in ipairs(regions) do
|
for _, region in ipairs(regions) do
|
||||||
for i, range in ipairs(region) do
|
for i, range in ipairs(region) do
|
||||||
if type(range) == "table" and #range == 4 then
|
if type(range) == 'table' and #range == 4 then
|
||||||
local start_row, start_col, end_row, end_col = unpack(range)
|
local start_row, start_col, end_row, end_col = unpack(range)
|
||||||
-- Easy case, this is a buffer parser
|
-- Easy case, this is a buffer parser
|
||||||
-- TODO(vigoux): proper byte computation here, and account for EOL ?
|
-- TODO(vigoux): proper byte computation here, and account for EOL ?
|
||||||
@@ -303,7 +303,9 @@ end
|
|||||||
--- instead of using the entire nodes range.
|
--- instead of using the entire nodes range.
|
||||||
---@private
|
---@private
|
||||||
function LanguageTree:_get_injections()
|
function LanguageTree:_get_injections()
|
||||||
if not self._injection_query then return {} end
|
if not self._injection_query then
|
||||||
|
return {}
|
||||||
|
end
|
||||||
|
|
||||||
local injections = {}
|
local injections = {}
|
||||||
|
|
||||||
@@ -311,7 +313,9 @@ function LanguageTree:_get_injections()
|
|||||||
local root_node = tree:root()
|
local root_node = tree:root()
|
||||||
local start_line, _, end_line, _ = root_node:range()
|
local start_line, _, end_line, _ = root_node:range()
|
||||||
|
|
||||||
for pattern, match, metadata in self._injection_query:iter_matches(root_node, self._source, start_line, end_line+1) do
|
for pattern, match, metadata in
|
||||||
|
self._injection_query:iter_matches(root_node, self._source, start_line, end_line + 1)
|
||||||
|
do
|
||||||
local lang = nil
|
local lang = nil
|
||||||
local ranges = {}
|
local ranges = {}
|
||||||
local combined = metadata.combined
|
local combined = metadata.combined
|
||||||
@@ -322,7 +326,7 @@ function LanguageTree:_get_injections()
|
|||||||
local content = metadata.content
|
local content = metadata.content
|
||||||
|
|
||||||
-- Allow for captured nodes to be used
|
-- Allow for captured nodes to be used
|
||||||
if type(content) == "number" then
|
if type(content) == 'number' then
|
||||||
content = { match[content] }
|
content = { match[content] }
|
||||||
end
|
end
|
||||||
|
|
||||||
@@ -342,15 +346,15 @@ function LanguageTree:_get_injections()
|
|||||||
local name = self._injection_query.captures[id]
|
local name = self._injection_query.captures[id]
|
||||||
|
|
||||||
-- Lang should override any other language tag
|
-- Lang should override any other language tag
|
||||||
if name == "language" and not lang then
|
if name == 'language' and not lang then
|
||||||
lang = query.get_node_text(node, self._source)
|
lang = query.get_node_text(node, self._source)
|
||||||
elseif name == "combined" then
|
elseif name == 'combined' then
|
||||||
combined = true
|
combined = true
|
||||||
elseif name == "content" and #ranges == 0 then
|
elseif name == 'content' and #ranges == 0 then
|
||||||
table.insert(ranges, node)
|
table.insert(ranges, node)
|
||||||
-- Ignore any tags that start with "_"
|
-- Ignore any tags that start with "_"
|
||||||
-- Allows for other tags to be used in matches
|
-- Allows for other tags to be used in matches
|
||||||
elseif string.sub(name, 1, 1) ~= "_" then
|
elseif string.sub(name, 1, 1) ~= '_' then
|
||||||
if not lang then
|
if not lang then
|
||||||
lang = name
|
lang = name
|
||||||
end
|
end
|
||||||
@@ -414,10 +418,19 @@ function LanguageTree:_do_callback(cb_name, ...)
|
|||||||
end
|
end
|
||||||
|
|
||||||
---@private
|
---@private
|
||||||
function LanguageTree:_on_bytes(bufnr, changed_tick,
|
function LanguageTree:_on_bytes(
|
||||||
start_row, start_col, start_byte,
|
bufnr,
|
||||||
old_row, old_col, old_byte,
|
changed_tick,
|
||||||
new_row, new_col, new_byte)
|
start_row,
|
||||||
|
start_col,
|
||||||
|
start_byte,
|
||||||
|
old_row,
|
||||||
|
old_col,
|
||||||
|
old_byte,
|
||||||
|
new_row,
|
||||||
|
new_col,
|
||||||
|
new_byte
|
||||||
|
)
|
||||||
self:invalidate()
|
self:invalidate()
|
||||||
|
|
||||||
local old_end_col = old_col + ((old_row == 0) and start_col or 0)
|
local old_end_col = old_col + ((old_row == 0) and start_col or 0)
|
||||||
@@ -426,16 +439,33 @@ function LanguageTree:_on_bytes(bufnr, changed_tick,
|
|||||||
-- Edit all trees recursively, together BEFORE emitting a bytes callback.
|
-- Edit all trees recursively, together BEFORE emitting a bytes callback.
|
||||||
-- In most cases this callback should only be called from the root tree.
|
-- In most cases this callback should only be called from the root tree.
|
||||||
self:for_each_tree(function(tree)
|
self:for_each_tree(function(tree)
|
||||||
tree:edit(start_byte,start_byte+old_byte,start_byte+new_byte,
|
tree:edit(
|
||||||
start_row, start_col,
|
start_byte,
|
||||||
start_row+old_row, old_end_col,
|
start_byte + old_byte,
|
||||||
start_row+new_row, new_end_col)
|
start_byte + new_byte,
|
||||||
|
start_row,
|
||||||
|
start_col,
|
||||||
|
start_row + old_row,
|
||||||
|
old_end_col,
|
||||||
|
start_row + new_row,
|
||||||
|
new_end_col
|
||||||
|
)
|
||||||
end)
|
end)
|
||||||
|
|
||||||
self:_do_callback('bytes', bufnr, changed_tick,
|
self:_do_callback(
|
||||||
start_row, start_col, start_byte,
|
'bytes',
|
||||||
old_row, old_col, old_byte,
|
bufnr,
|
||||||
new_row, new_col, new_byte)
|
changed_tick,
|
||||||
|
start_row,
|
||||||
|
start_col,
|
||||||
|
start_byte,
|
||||||
|
old_row,
|
||||||
|
old_col,
|
||||||
|
old_byte,
|
||||||
|
new_row,
|
||||||
|
new_col,
|
||||||
|
new_byte
|
||||||
|
)
|
||||||
end
|
end
|
||||||
|
|
||||||
---@private
|
---@private
|
||||||
@@ -443,7 +473,6 @@ function LanguageTree:_on_reload()
|
|||||||
self:invalidate(true)
|
self:invalidate(true)
|
||||||
end
|
end
|
||||||
|
|
||||||
|
|
||||||
---@private
|
---@private
|
||||||
function LanguageTree:_on_detach(...)
|
function LanguageTree:_on_detach(...)
|
||||||
self:invalidate(true)
|
self:invalidate(true)
|
||||||
@@ -459,7 +488,9 @@ end
|
|||||||
--- - `on_child_added` : emitted when a child is added to the tree.
|
--- - `on_child_added` : emitted when a child is added to the tree.
|
||||||
--- - `on_child_removed` : emitted when a child is removed from the tree.
|
--- - `on_child_removed` : emitted when a child is removed from the tree.
|
||||||
function LanguageTree:register_cbs(cbs)
|
function LanguageTree:register_cbs(cbs)
|
||||||
if not cbs then return end
|
if not cbs then
|
||||||
|
return
|
||||||
|
end
|
||||||
|
|
||||||
if cbs.on_changedtree then
|
if cbs.on_changedtree then
|
||||||
table.insert(self._callbacks.changedtree, cbs.on_changedtree)
|
table.insert(self._callbacks.changedtree, cbs.on_changedtree)
|
||||||
|
@@ -1,5 +1,5 @@
|
|||||||
local a = vim.api
|
local a = vim.api
|
||||||
local language = require'vim.treesitter.language'
|
local language = require('vim.treesitter.language')
|
||||||
|
|
||||||
-- query: pattern matching on trees
|
-- query: pattern matching on trees
|
||||||
-- predicate matching is implemented in lua
|
-- predicate matching is implemented in lua
|
||||||
@@ -43,7 +43,9 @@ function M.get_query_files(lang, query_name, is_included)
|
|||||||
local query_path = string.format('queries/%s/%s.scm', lang, query_name)
|
local query_path = string.format('queries/%s/%s.scm', lang, query_name)
|
||||||
local lang_files = dedupe_files(a.nvim_get_runtime_file(query_path, true))
|
local lang_files = dedupe_files(a.nvim_get_runtime_file(query_path, true))
|
||||||
|
|
||||||
if #lang_files == 0 then return {} end
|
if #lang_files == 0 then
|
||||||
|
return {}
|
||||||
|
end
|
||||||
|
|
||||||
local base_langs = {}
|
local base_langs = {}
|
||||||
|
|
||||||
@@ -52,7 +54,7 @@ function M.get_query_files(lang, query_name, is_included)
|
|||||||
-- ;+ inherits: ({language},)*{language}
|
-- ;+ inherits: ({language},)*{language}
|
||||||
--
|
--
|
||||||
-- {language} ::= {lang} | ({lang})
|
-- {language} ::= {lang} | ({lang})
|
||||||
local MODELINE_FORMAT = "^;+%s*inherits%s*:?%s*([a-z_,()]+)%s*$"
|
local MODELINE_FORMAT = '^;+%s*inherits%s*:?%s*([a-z_,()]+)%s*$'
|
||||||
|
|
||||||
for _, file in ipairs(lang_files) do
|
for _, file in ipairs(lang_files) do
|
||||||
local modeline = safe_read(file, '*l')
|
local modeline = safe_read(file, '*l')
|
||||||
@@ -62,7 +64,7 @@ function M.get_query_files(lang, query_name, is_included)
|
|||||||
|
|
||||||
if langlist then
|
if langlist then
|
||||||
for _, incllang in ipairs(vim.split(langlist, ',', true)) do
|
for _, incllang in ipairs(vim.split(langlist, ',', true)) do
|
||||||
local is_optional = incllang:match("%(.*%)")
|
local is_optional = incllang:match('%(.*%)')
|
||||||
|
|
||||||
if is_optional then
|
if is_optional then
|
||||||
if not is_included then
|
if not is_included then
|
||||||
@@ -142,7 +144,7 @@ local query_cache = setmetatable({}, {
|
|||||||
__index = function(tbl, key)
|
__index = function(tbl, key)
|
||||||
rawset(tbl, key, {})
|
rawset(tbl, key, {})
|
||||||
return rawget(tbl, key)
|
return rawget(tbl, key)
|
||||||
end
|
end,
|
||||||
})
|
})
|
||||||
|
|
||||||
--- Parse {query} as a string. (If the query is in a file, the caller
|
--- Parse {query} as a string. (If the query is in a file, the caller
|
||||||
@@ -185,7 +187,7 @@ function M.get_node_text(node, source)
|
|||||||
local start_row, start_col, start_byte = node:start()
|
local start_row, start_col, start_byte = node:start()
|
||||||
local end_row, end_col, end_byte = node:end_()
|
local end_row, end_col, end_byte = node:end_()
|
||||||
|
|
||||||
if type(source) == "number" then
|
if type(source) == 'number' then
|
||||||
local lines
|
local lines
|
||||||
local eof_row = a.nvim_buf_line_count(source)
|
local eof_row = a.nvim_buf_line_count(source)
|
||||||
if start_row >= eof_row then
|
if start_row >= eof_row then
|
||||||
@@ -208,8 +210,8 @@ function M.get_node_text(node, source)
|
|||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
return table.concat(lines, "\n")
|
return table.concat(lines, '\n')
|
||||||
elseif type(source) == "string" then
|
elseif type(source) == 'string' then
|
||||||
return source:sub(start_byte + 1, end_byte)
|
return source:sub(start_byte + 1, end_byte)
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
@@ -217,12 +219,12 @@ end
|
|||||||
-- Predicate handler receive the following arguments
|
-- Predicate handler receive the following arguments
|
||||||
-- (match, pattern, bufnr, predicate)
|
-- (match, pattern, bufnr, predicate)
|
||||||
local predicate_handlers = {
|
local predicate_handlers = {
|
||||||
["eq?"] = function(match, _, source, predicate)
|
['eq?'] = function(match, _, source, predicate)
|
||||||
local node = match[predicate[2]]
|
local node = match[predicate[2]]
|
||||||
local node_text = M.get_node_text(node, source)
|
local node_text = M.get_node_text(node, source)
|
||||||
|
|
||||||
local str
|
local str
|
||||||
if type(predicate[3]) == "string" then
|
if type(predicate[3]) == 'string' then
|
||||||
-- (#eq? @aa "foo")
|
-- (#eq? @aa "foo")
|
||||||
str = predicate[3]
|
str = predicate[3]
|
||||||
else
|
else
|
||||||
@@ -237,13 +239,13 @@ local predicate_handlers = {
|
|||||||
return true
|
return true
|
||||||
end,
|
end,
|
||||||
|
|
||||||
["lua-match?"] = function(match, _, source, predicate)
|
['lua-match?'] = function(match, _, source, predicate)
|
||||||
local node = match[predicate[2]]
|
local node = match[predicate[2]]
|
||||||
local regex = predicate[3]
|
local regex = predicate[3]
|
||||||
return string.find(M.get_node_text(node, source), regex)
|
return string.find(M.get_node_text(node, source), regex)
|
||||||
end,
|
end,
|
||||||
|
|
||||||
["match?"] = (function()
|
['match?'] = (function()
|
||||||
local magic_prefixes = { ['\\v'] = true, ['\\m'] = true, ['\\M'] = true, ['\\V'] = true }
|
local magic_prefixes = { ['\\v'] = true, ['\\m'] = true, ['\\M'] = true, ['\\V'] = true }
|
||||||
---@private
|
---@private
|
||||||
local function check_magic(str)
|
local function check_magic(str)
|
||||||
@@ -258,7 +260,7 @@ local predicate_handlers = {
|
|||||||
local res = vim.regex(check_magic(pattern))
|
local res = vim.regex(check_magic(pattern))
|
||||||
rawset(t, pattern, res)
|
rawset(t, pattern, res)
|
||||||
return res
|
return res
|
||||||
end
|
end,
|
||||||
})
|
})
|
||||||
|
|
||||||
return function(match, _, source, pred)
|
return function(match, _, source, pred)
|
||||||
@@ -268,7 +270,7 @@ local predicate_handlers = {
|
|||||||
end
|
end
|
||||||
end)(),
|
end)(),
|
||||||
|
|
||||||
["contains?"] = function(match, _, source, predicate)
|
['contains?'] = function(match, _, source, predicate)
|
||||||
local node = match[predicate[2]]
|
local node = match[predicate[2]]
|
||||||
local node_text = M.get_node_text(node, source)
|
local node_text = M.get_node_text(node, source)
|
||||||
|
|
||||||
@@ -281,19 +283,19 @@ local predicate_handlers = {
|
|||||||
return false
|
return false
|
||||||
end,
|
end,
|
||||||
|
|
||||||
["any-of?"] = function(match, _, source, predicate)
|
['any-of?'] = function(match, _, source, predicate)
|
||||||
local node = match[predicate[2]]
|
local node = match[predicate[2]]
|
||||||
local node_text = M.get_node_text(node, source)
|
local node_text = M.get_node_text(node, source)
|
||||||
|
|
||||||
-- Since 'predicate' will not be used by callers of this function, use it
|
-- Since 'predicate' will not be used by callers of this function, use it
|
||||||
-- to store a string set built from the list of words to check against.
|
-- to store a string set built from the list of words to check against.
|
||||||
local string_set = predicate["string_set"]
|
local string_set = predicate['string_set']
|
||||||
if not string_set then
|
if not string_set then
|
||||||
string_set = {}
|
string_set = {}
|
||||||
for i = 3, #predicate do
|
for i = 3, #predicate do
|
||||||
string_set[predicate[i]] = true
|
string_set[predicate[i]] = true
|
||||||
end
|
end
|
||||||
predicate["string_set"] = string_set
|
predicate['string_set'] = string_set
|
||||||
end
|
end
|
||||||
|
|
||||||
return string_set[node_text]
|
return string_set[node_text]
|
||||||
@@ -301,15 +303,14 @@ local predicate_handlers = {
|
|||||||
}
|
}
|
||||||
|
|
||||||
-- As we provide lua-match? also expose vim-match?
|
-- As we provide lua-match? also expose vim-match?
|
||||||
predicate_handlers["vim-match?"] = predicate_handlers["match?"]
|
predicate_handlers['vim-match?'] = predicate_handlers['match?']
|
||||||
|
|
||||||
|
|
||||||
-- Directives store metadata or perform side effects against a match.
|
-- Directives store metadata or perform side effects against a match.
|
||||||
-- Directives should always end with a `!`.
|
-- Directives should always end with a `!`.
|
||||||
-- Directive handler receive the following arguments
|
-- Directive handler receive the following arguments
|
||||||
-- (match, pattern, bufnr, predicate, metadata)
|
-- (match, pattern, bufnr, predicate, metadata)
|
||||||
local directive_handlers = {
|
local directive_handlers = {
|
||||||
["set!"] = function(_, _, _, pred, metadata)
|
['set!'] = function(_, _, _, pred, metadata)
|
||||||
if #pred == 4 then
|
if #pred == 4 then
|
||||||
-- (#set! @capture "key" "value")
|
-- (#set! @capture "key" "value")
|
||||||
local capture = pred[2]
|
local capture = pred[2]
|
||||||
@@ -324,7 +325,7 @@ local directive_handlers = {
|
|||||||
end,
|
end,
|
||||||
-- Shifts the range of a node.
|
-- Shifts the range of a node.
|
||||||
-- Example: (#offset! @_node 0 1 0 -1)
|
-- Example: (#offset! @_node 0 1 0 -1)
|
||||||
["offset!"] = function(match, _, _, pred, metadata)
|
['offset!'] = function(match, _, _, pred, metadata)
|
||||||
local offset_node = match[pred[2]]
|
local offset_node = match[pred[2]]
|
||||||
local range = { offset_node:range() }
|
local range = { offset_node:range() }
|
||||||
local start_row_offset = pred[3] or 0
|
local start_row_offset = pred[3] or 0
|
||||||
@@ -341,7 +342,7 @@ local directive_handlers = {
|
|||||||
if range[1] < range[3] or (range[1] == range[3] and range[2] <= range[4]) then
|
if range[1] < range[3] or (range[1] == range[3] and range[2] <= range[4]) then
|
||||||
metadata.content = { range }
|
metadata.content = { range }
|
||||||
end
|
end
|
||||||
end
|
end,
|
||||||
}
|
}
|
||||||
|
|
||||||
--- Adds a new predicate to be used in queries
|
--- Adds a new predicate to be used in queries
|
||||||
@@ -351,7 +352,7 @@ local directive_handlers = {
|
|||||||
--- signature will be (match, pattern, bufnr, predicate)
|
--- signature will be (match, pattern, bufnr, predicate)
|
||||||
function M.add_predicate(name, handler, force)
|
function M.add_predicate(name, handler, force)
|
||||||
if predicate_handlers[name] and not force then
|
if predicate_handlers[name] and not force then
|
||||||
error(string.format("Overriding %s", name))
|
error(string.format('Overriding %s', name))
|
||||||
end
|
end
|
||||||
|
|
||||||
predicate_handlers[name] = handler
|
predicate_handlers[name] = handler
|
||||||
@@ -364,7 +365,7 @@ end
|
|||||||
--- signature will be (match, pattern, bufnr, predicate)
|
--- signature will be (match, pattern, bufnr, predicate)
|
||||||
function M.add_directive(name, handler, force)
|
function M.add_directive(name, handler, force)
|
||||||
if directive_handlers[name] and not force then
|
if directive_handlers[name] and not force then
|
||||||
error(string.format("Overriding %s", name))
|
error(string.format('Overriding %s', name))
|
||||||
end
|
end
|
||||||
|
|
||||||
directive_handlers[name] = handler
|
directive_handlers[name] = handler
|
||||||
@@ -387,7 +388,7 @@ end
|
|||||||
|
|
||||||
---@private
|
---@private
|
||||||
local function is_directive(name)
|
local function is_directive(name)
|
||||||
return string.sub(name, -1) == "!"
|
return string.sub(name, -1) == '!'
|
||||||
end
|
end
|
||||||
|
|
||||||
---@private
|
---@private
|
||||||
@@ -404,7 +405,7 @@ function Query:match_preds(match, pattern, source)
|
|||||||
|
|
||||||
-- Skip over directives... they will get processed after all the predicates.
|
-- Skip over directives... they will get processed after all the predicates.
|
||||||
if not is_directive(pred[1]) then
|
if not is_directive(pred[1]) then
|
||||||
if string.sub(pred[1], 1, 4) == "not-" then
|
if string.sub(pred[1], 1, 4) == 'not-' then
|
||||||
pred_name = string.sub(pred[1], 5)
|
pred_name = string.sub(pred[1], 5)
|
||||||
is_not = true
|
is_not = true
|
||||||
else
|
else
|
||||||
@@ -415,7 +416,7 @@ function Query:match_preds(match, pattern, source)
|
|||||||
local handler = predicate_handlers[pred_name]
|
local handler = predicate_handlers[pred_name]
|
||||||
|
|
||||||
if not handler then
|
if not handler then
|
||||||
error(string.format("No handler for %s", pred[1]))
|
error(string.format('No handler for %s', pred[1]))
|
||||||
return false
|
return false
|
||||||
end
|
end
|
||||||
|
|
||||||
@@ -438,7 +439,7 @@ function Query:apply_directives(match, pattern, source, metadata)
|
|||||||
local handler = directive_handlers[pred[1]]
|
local handler = directive_handlers[pred[1]]
|
||||||
|
|
||||||
if not handler then
|
if not handler then
|
||||||
error(string.format("No handler for %s", pred[1]))
|
error(string.format('No handler for %s', pred[1]))
|
||||||
return
|
return
|
||||||
end
|
end
|
||||||
|
|
||||||
@@ -447,7 +448,6 @@ function Query:apply_directives(match, pattern, source, metadata)
|
|||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
|
|
||||||
--- Returns the start and stop value if set else the node's range.
|
--- Returns the start and stop value if set else the node's range.
|
||||||
-- When the node's range is used, the stop is incremented by 1
|
-- When the node's range is used, the stop is incremented by 1
|
||||||
-- to make the search inclusive.
|
-- to make the search inclusive.
|
||||||
@@ -492,7 +492,7 @@ end
|
|||||||
---@returns The matching capture id
|
---@returns The matching capture id
|
||||||
---@returns The captured node
|
---@returns The captured node
|
||||||
function Query:iter_captures(node, source, start, stop)
|
function Query:iter_captures(node, source, start, stop)
|
||||||
if type(source) == "number" and source == 0 then
|
if type(source) == 'number' and source == 0 then
|
||||||
source = vim.api.nvim_get_current_buf()
|
source = vim.api.nvim_get_current_buf()
|
||||||
end
|
end
|
||||||
|
|
||||||
@@ -549,7 +549,7 @@ end
|
|||||||
---@returns The matching pattern id
|
---@returns The matching pattern id
|
||||||
---@returns The matching match
|
---@returns The matching match
|
||||||
function Query:iter_matches(node, source, start, stop)
|
function Query:iter_matches(node, source, start, stop)
|
||||||
if type(source) == "number" and source == 0 then
|
if type(source) == 'number' and source == 0 then
|
||||||
source = vim.api.nvim_get_current_buf()
|
source = vim.api.nvim_get_current_buf()
|
||||||
end
|
end
|
||||||
|
|
||||||
|
@@ -37,10 +37,10 @@ local M = {}
|
|||||||
--- </pre>
|
--- </pre>
|
||||||
|
|
||||||
function M.select(items, opts, on_choice)
|
function M.select(items, opts, on_choice)
|
||||||
vim.validate {
|
vim.validate({
|
||||||
items = { items, 'table', false },
|
items = { items, 'table', false },
|
||||||
on_choice = { on_choice, 'function', false },
|
on_choice = { on_choice, 'function', false },
|
||||||
}
|
})
|
||||||
opts = opts or {}
|
opts = opts or {}
|
||||||
local choices = { opts.prompt or 'Select one of:' }
|
local choices = { opts.prompt or 'Select one of:' }
|
||||||
local format_item = opts.format_item or tostring
|
local format_item = opts.format_item or tostring
|
||||||
@@ -83,9 +83,9 @@ end
|
|||||||
--- end)
|
--- end)
|
||||||
--- </pre>
|
--- </pre>
|
||||||
function M.input(opts, on_confirm)
|
function M.input(opts, on_confirm)
|
||||||
vim.validate {
|
vim.validate({
|
||||||
on_confirm = { on_confirm, 'function', false },
|
on_confirm = { on_confirm, 'function', false },
|
||||||
}
|
})
|
||||||
|
|
||||||
opts = opts or {}
|
opts = opts or {}
|
||||||
local input = vim.fn.input(opts)
|
local input = vim.fn.input(opts)
|
||||||
|
@@ -3,7 +3,6 @@
|
|||||||
-- https://tools.ietf.org/html/rfc2732
|
-- https://tools.ietf.org/html/rfc2732
|
||||||
-- https://tools.ietf.org/html/rfc2396
|
-- https://tools.ietf.org/html/rfc2396
|
||||||
|
|
||||||
|
|
||||||
local uri_decode
|
local uri_decode
|
||||||
do
|
do
|
||||||
local schar = string.char
|
local schar = string.char
|
||||||
@@ -14,7 +13,7 @@ do
|
|||||||
return schar(tonumber(hex, 16))
|
return schar(tonumber(hex, 16))
|
||||||
end
|
end
|
||||||
uri_decode = function(str)
|
uri_decode = function(str)
|
||||||
return str:gsub("%%([a-fA-F0-9][a-fA-F0-9])", hex_to_char)
|
return str:gsub('%%([a-fA-F0-9][a-fA-F0-9])', hex_to_char)
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
@@ -23,33 +22,36 @@ do
|
|||||||
local PATTERNS = {
|
local PATTERNS = {
|
||||||
--- RFC 2396
|
--- RFC 2396
|
||||||
-- https://tools.ietf.org/html/rfc2396#section-2.2
|
-- https://tools.ietf.org/html/rfc2396#section-2.2
|
||||||
rfc2396 = "^A-Za-z0-9%-_.!~*'()";
|
rfc2396 = "^A-Za-z0-9%-_.!~*'()",
|
||||||
--- RFC 2732
|
--- RFC 2732
|
||||||
-- https://tools.ietf.org/html/rfc2732
|
-- https://tools.ietf.org/html/rfc2732
|
||||||
rfc2732 = "^A-Za-z0-9%-_.!~*'()[]";
|
rfc2732 = "^A-Za-z0-9%-_.!~*'()[]",
|
||||||
--- RFC 3986
|
--- RFC 3986
|
||||||
-- https://tools.ietf.org/html/rfc3986#section-2.2
|
-- https://tools.ietf.org/html/rfc3986#section-2.2
|
||||||
rfc3986 = "^A-Za-z0-9%-._~!$&'()*+,;=:@/";
|
rfc3986 = "^A-Za-z0-9%-._~!$&'()*+,;=:@/",
|
||||||
}
|
}
|
||||||
local sbyte, tohex = string.byte
|
local sbyte, tohex = string.byte
|
||||||
if jit then
|
if jit then
|
||||||
tohex = require'bit'.tohex
|
tohex = require('bit').tohex
|
||||||
else
|
else
|
||||||
tohex = function(b) return string.format("%02x", b) end
|
tohex = function(b)
|
||||||
|
return string.format('%02x', b)
|
||||||
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
---@private
|
---@private
|
||||||
local function percent_encode_char(char)
|
local function percent_encode_char(char)
|
||||||
return "%"..tohex(sbyte(char), 2)
|
return '%' .. tohex(sbyte(char), 2)
|
||||||
end
|
end
|
||||||
uri_encode = function(text, rfc)
|
uri_encode = function(text, rfc)
|
||||||
if not text then return end
|
if not text then
|
||||||
|
return
|
||||||
|
end
|
||||||
local pattern = PATTERNS[rfc] or PATTERNS.rfc3986
|
local pattern = PATTERNS[rfc] or PATTERNS.rfc3986
|
||||||
return text:gsub("(["..pattern.."])", percent_encode_char)
|
return text:gsub('([' .. pattern .. '])', percent_encode_char)
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
|
|
||||||
---@private
|
---@private
|
||||||
local function is_windows_file_uri(uri)
|
local function is_windows_file_uri(uri)
|
||||||
return uri:match('^file:/+[a-zA-Z]:') ~= nil
|
return uri:match('^file:/+[a-zA-Z]:') ~= nil
|
||||||
@@ -59,16 +61,16 @@ end
|
|||||||
---@param path string Path to file
|
---@param path string Path to file
|
||||||
---@return string URI
|
---@return string URI
|
||||||
local function uri_from_fname(path)
|
local function uri_from_fname(path)
|
||||||
local volume_path, fname = path:match("^([a-zA-Z]:)(.*)")
|
local volume_path, fname = path:match('^([a-zA-Z]:)(.*)')
|
||||||
local is_windows = volume_path ~= nil
|
local is_windows = volume_path ~= nil
|
||||||
if is_windows then
|
if is_windows then
|
||||||
path = volume_path..uri_encode(fname:gsub("\\", "/"))
|
path = volume_path .. uri_encode(fname:gsub('\\', '/'))
|
||||||
else
|
else
|
||||||
path = uri_encode(path)
|
path = uri_encode(path)
|
||||||
end
|
end
|
||||||
local uri_parts = {"file://"}
|
local uri_parts = { 'file://' }
|
||||||
if is_windows then
|
if is_windows then
|
||||||
table.insert(uri_parts, "/")
|
table.insert(uri_parts, '/')
|
||||||
end
|
end
|
||||||
table.insert(uri_parts, path)
|
table.insert(uri_parts, path)
|
||||||
return table.concat(uri_parts)
|
return table.concat(uri_parts)
|
||||||
@@ -82,11 +84,11 @@ local WINDOWS_URI_SCHEME_PATTERN = '^([a-zA-Z]+[a-zA-Z0-9.+-]*):[a-zA-Z]:.*'
|
|||||||
---@return string URI
|
---@return string URI
|
||||||
local function uri_from_bufnr(bufnr)
|
local function uri_from_bufnr(bufnr)
|
||||||
local fname = vim.api.nvim_buf_get_name(bufnr)
|
local fname = vim.api.nvim_buf_get_name(bufnr)
|
||||||
local volume_path = fname:match("^([a-zA-Z]:).*")
|
local volume_path = fname:match('^([a-zA-Z]:).*')
|
||||||
local is_windows = volume_path ~= nil
|
local is_windows = volume_path ~= nil
|
||||||
local scheme
|
local scheme
|
||||||
if is_windows then
|
if is_windows then
|
||||||
fname = fname:gsub("\\", "/")
|
fname = fname:gsub('\\', '/')
|
||||||
scheme = fname:match(WINDOWS_URI_SCHEME_PATTERN)
|
scheme = fname:match(WINDOWS_URI_SCHEME_PATTERN)
|
||||||
else
|
else
|
||||||
scheme = fname:match(URI_SCHEME_PATTERN)
|
scheme = fname:match(URI_SCHEME_PATTERN)
|
||||||
|
@@ -3,4 +3,4 @@
|
|||||||
-- Last Change: 2022 Apr 13
|
-- Last Change: 2022 Apr 13
|
||||||
|
|
||||||
-- it's a lisp!
|
-- it's a lisp!
|
||||||
vim.cmd [[ runtime! syntax/lisp.vim ]]
|
vim.cmd([[ runtime! syntax/lisp.vim ]])
|
||||||
|
Reference in New Issue
Block a user