forked from Archives/navigator.lua
codelens support, foldelsp
parent
48faeb69ab
commit
58ac955777
@ -0,0 +1,3 @@
|
|||||||
|
function! foldlsp#foldexpr()
|
||||||
|
return luaeval(printf('require"navigator.foldlsp".get_fold_indic(%d)', v:lnum))
|
||||||
|
endfunction
|
@ -0,0 +1,145 @@
|
|||||||
|
-- codelenses
|
||||||
|
-- https://github.com/josa42/nvim-lsp-codelenses/blob/master/lua/jg/lsp/codelenses.lua
|
||||||
|
-- https://github.com/neovim/neovim/blob/master/runtime/lua/vim/lsp/codelens.lua
|
||||||
|
local codelens = require('vim.lsp.codelens')
|
||||||
|
|
||||||
|
local log = require"navigator.util".log
|
||||||
|
|
||||||
|
local lsphelper = require "navigator.lspwrapper"
|
||||||
|
local api = vim.api
|
||||||
|
local gui = require "navigator.gui"
|
||||||
|
local M = {}
|
||||||
|
|
||||||
|
local config = require("navigator").config_values()
|
||||||
|
local sign_name = "NavigatorCodeLensLightBulb"
|
||||||
|
if vim.tbl_isempty(vim.fn.sign_getdefined(sign_name)) then
|
||||||
|
vim.fn.sign_define(sign_name,
|
||||||
|
{text = config.icons.code_lens_action_icon, texthl = "LspDiagnosticsSignHint"})
|
||||||
|
end
|
||||||
|
|
||||||
|
local sign_group = "nvcodelensaction"
|
||||||
|
|
||||||
|
local get_current_winid = require('navigator.util').get_current_winid
|
||||||
|
|
||||||
|
local code_lens_action = {}
|
||||||
|
|
||||||
|
local function _update_sign(line)
|
||||||
|
log("update sign at line ", line)
|
||||||
|
local winid = get_current_winid()
|
||||||
|
if code_lens_action[winid] == nil then
|
||||||
|
code_lens_action[winid] = {}
|
||||||
|
end
|
||||||
|
if code_lens_action[winid].lightbulb_line ~= 0 then
|
||||||
|
vim.fn.sign_unplace(sign_group, {id = code_lens_action[winid].lightbulb_line, buffer = "%"})
|
||||||
|
end
|
||||||
|
|
||||||
|
if line then
|
||||||
|
-- log("updatasign", line, sign_group, sign_name)
|
||||||
|
vim.fn.sign_place(line, sign_group, sign_name, "%",
|
||||||
|
{lnum = line + 1, priority = config.code_lens_action_prompt.sign_priority})
|
||||||
|
code_lens_action[winid].lightbulb_line = line
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
local function codelens_hdlr(err, _, result, client_id, bufnr)
|
||||||
|
if err then
|
||||||
|
warn("lsp code lens", vim.inspect(err))
|
||||||
|
return
|
||||||
|
end
|
||||||
|
log("codelenes result", result)
|
||||||
|
for _, v in pairs(result) do
|
||||||
|
_update_sign(v.range.start.line)
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
function M.setup()
|
||||||
|
vim.cmd('highlight! link LspCodeLens LspDiagnosticsHint')
|
||||||
|
vim.cmd('highlight! link LspCodeLensText LspDiagnosticsInformation')
|
||||||
|
vim.cmd('highlight! link LspCodeLensTextSign LspDiagnosticsSignInformation')
|
||||||
|
vim.cmd('highlight! link LspCodeLensTextSeparator Boolean')
|
||||||
|
|
||||||
|
vim.cmd('augroup navigator.codelenses')
|
||||||
|
vim.cmd(' autocmd!')
|
||||||
|
vim.cmd(
|
||||||
|
"autocmd BufEnter,CursorHold,InsertLeave <buffer> lua require('navigator.codelens').refresh()")
|
||||||
|
vim.cmd('augroup end')
|
||||||
|
local on_codelens = vim.lsp.handlers["textDocument/codeLens"]
|
||||||
|
vim.lsp.handlers["textDocument/codeLens"] = function(err, _, result, client_id, bufnr)
|
||||||
|
on_codelens(err, _, result, client_id, bufnr)
|
||||||
|
codelens_hdlr(err, _, result, client_id, bufnr)
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
M.lsp_clients = {}
|
||||||
|
|
||||||
|
function M.refresh()
|
||||||
|
assert(#vim.lsp.buf_get_clients() > 0, "Must have a client running to use lsp code action")
|
||||||
|
if not lsphelper.check_capabilities("code_lens") then
|
||||||
|
return
|
||||||
|
end
|
||||||
|
vim.lsp.codelens.refresh()
|
||||||
|
end
|
||||||
|
|
||||||
|
function M.run_action()
|
||||||
|
log("run code len action")
|
||||||
|
|
||||||
|
assert(#vim.lsp.buf_get_clients() > 0, "Must have a client running to use lsp code action")
|
||||||
|
if not lsphelper.check_capabilities("code_lens") then
|
||||||
|
return
|
||||||
|
end
|
||||||
|
|
||||||
|
local line = api.nvim_win_get_cursor(0)[1]
|
||||||
|
local bufnr = api.nvim_get_current_buf()
|
||||||
|
|
||||||
|
local lenses = codelens.get(bufnr)
|
||||||
|
if lenses == nil or #lenses == 0 then
|
||||||
|
return
|
||||||
|
end
|
||||||
|
local width = 40
|
||||||
|
|
||||||
|
local data = {" Auto Fix <C-o> Apply <C-e> Exit"}
|
||||||
|
for i, lens in pairs(lenses) do
|
||||||
|
if lens.range.start.line == (line - 1) then
|
||||||
|
local title = lens.command.title:gsub("\r\n", "\\r\\n")
|
||||||
|
title = title:gsub("\n", "\\n")
|
||||||
|
title = string.format("[%d] %s", i, title)
|
||||||
|
table.insert(data, title)
|
||||||
|
lenses[i].display_title = title
|
||||||
|
width = math.max(width, #lens.command.title)
|
||||||
|
end
|
||||||
|
end
|
||||||
|
local apply = require('navigator.lspwrapper').apply_action
|
||||||
|
local function apply_action(action)
|
||||||
|
local action_chosen = nil
|
||||||
|
for key, value in pairs(lenses) do
|
||||||
|
if value.display_title == action then
|
||||||
|
action_chosen = value
|
||||||
|
end
|
||||||
|
end
|
||||||
|
if action_chosen == nil then
|
||||||
|
log("no match for ", action, lenses)
|
||||||
|
return
|
||||||
|
end
|
||||||
|
apply(action_chosen)
|
||||||
|
end
|
||||||
|
|
||||||
|
gui.new_list_view {
|
||||||
|
items = data,
|
||||||
|
width = width + 4,
|
||||||
|
loc = "top_center",
|
||||||
|
relative = "cursor",
|
||||||
|
rawdata = true,
|
||||||
|
data = data,
|
||||||
|
on_confirm = function(pos)
|
||||||
|
log(pos)
|
||||||
|
apply_action(pos)
|
||||||
|
end,
|
||||||
|
on_move = function(pos)
|
||||||
|
log(pos)
|
||||||
|
return pos
|
||||||
|
end
|
||||||
|
}
|
||||||
|
|
||||||
|
end
|
||||||
|
|
||||||
|
return M
|
@ -0,0 +1,171 @@
|
|||||||
|
local log = require"navigator.util".log
|
||||||
|
|
||||||
|
local lsp = vim.lsp
|
||||||
|
local api = vim.api
|
||||||
|
|
||||||
|
local M = {}
|
||||||
|
|
||||||
|
-- TODO: per-buffer fold table?
|
||||||
|
M.current_buf_folds = {}
|
||||||
|
|
||||||
|
-- Informative table keeping track of language servers that implement textDocument/foldingRange.
|
||||||
|
-- Not used at runtime (capability is resolved dynamically)
|
||||||
|
M.servers_supporting_folding = {
|
||||||
|
pylsp = true,
|
||||||
|
pyright = false,
|
||||||
|
sumneko_lua = true,
|
||||||
|
texlab = true,
|
||||||
|
clangd = false,
|
||||||
|
gopls = true,
|
||||||
|
julials = false
|
||||||
|
}
|
||||||
|
|
||||||
|
M.active_folding_clients = {}
|
||||||
|
|
||||||
|
function M.on_attach()
|
||||||
|
M.setup_plugin()
|
||||||
|
M.update_folds()
|
||||||
|
end
|
||||||
|
|
||||||
|
function M.setup_plugin()
|
||||||
|
api.nvim_command("augroup FoldingCommand")
|
||||||
|
api.nvim_command("autocmd! * <buffer>")
|
||||||
|
api.nvim_command("autocmd BufEnter <buffer> lua require'navigator.foldlsp'.update_folds()")
|
||||||
|
api.nvim_command("autocmd BufWritePost <buffer> lua require'navigator.foldlsp'.update_folds()")
|
||||||
|
api.nvim_command("augroup end")
|
||||||
|
|
||||||
|
-- vim.cmd([[
|
||||||
|
--
|
||||||
|
-- function! folding_nvim#foldexpr()
|
||||||
|
-- return luaeval(printf('require"navigator.foldlsp".get_fold_indic(%d)', v:lnum))
|
||||||
|
-- endfunction
|
||||||
|
--
|
||||||
|
-- ]])
|
||||||
|
|
||||||
|
local clients = vim.lsp.buf_get_clients()
|
||||||
|
|
||||||
|
for _, client in pairs(clients) do
|
||||||
|
local client_id = client['id']
|
||||||
|
if M.active_folding_clients[client_id] == nil then
|
||||||
|
local server_supports_folding = client['server_capabilities']['foldingRangeProvider'] or false
|
||||||
|
-- if not server_supports_folding then
|
||||||
|
-- api.nvim_command(string.format('echom "lsp-folding: %s does not provide folding requests"',
|
||||||
|
-- client['name']))
|
||||||
|
-- end
|
||||||
|
|
||||||
|
M.active_folding_clients[client_id] = server_supports_folding
|
||||||
|
end
|
||||||
|
end
|
||||||
|
print(vim.inspect(M.active_folding_clients))
|
||||||
|
end
|
||||||
|
|
||||||
|
function M.update_folds()
|
||||||
|
local current_window = api.nvim_get_current_win()
|
||||||
|
local in_diff_mode = api.nvim_win_get_option(current_window, 'diff')
|
||||||
|
if in_diff_mode then
|
||||||
|
-- In diff mode, use diff folding.
|
||||||
|
api.nvim_win_set_option(current_window, 'foldmethod', 'diff')
|
||||||
|
else
|
||||||
|
local clients = lsp.buf_get_clients(0)
|
||||||
|
for client_id, client in pairs(clients) do
|
||||||
|
if M.active_folding_clients[client_id] then
|
||||||
|
-- XXX: better to pass callback in this method or add it directly in the config?
|
||||||
|
-- client.config.callbacks['textDocument/foldingRange'] = M.fold_handler
|
||||||
|
local current_bufnr = api.nvim_get_current_buf()
|
||||||
|
local params = {uri = vim.uri_from_bufnr(current_bufnr)}
|
||||||
|
client.request('textDocument/foldingRange', {textDocument = params}, M.fold_handler,
|
||||||
|
current_bufnr)
|
||||||
|
end
|
||||||
|
end
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
function M.debug_folds()
|
||||||
|
for _, table in ipairs(M.current_buf_folds) do
|
||||||
|
local start_line = table['startLine']
|
||||||
|
local end_line = table['endLine']
|
||||||
|
log('startline', start_line, 'endline', end_line)
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
function M.fold_handler(err, method, result, client, bufnr)
|
||||||
|
-- params: err, method, result, client_id, bufnr
|
||||||
|
-- XXX: handle err?
|
||||||
|
if err or result == nil or #result == 0 then
|
||||||
|
print(err, method, client)
|
||||||
|
return
|
||||||
|
end
|
||||||
|
M.debug_folds()
|
||||||
|
local current_bufnr = api.nvim_get_current_buf()
|
||||||
|
-- Discard the folding result if buffer focus has changed since the request was
|
||||||
|
-- done.
|
||||||
|
if current_bufnr == bufnr then
|
||||||
|
for _, fold in ipairs(result) do
|
||||||
|
fold['startLine'] = M.adjust_foldstart(fold['startLine'])
|
||||||
|
fold['endLine'] = M.adjust_foldend(fold['endLine'])
|
||||||
|
end
|
||||||
|
table.sort(result, function(a, b)
|
||||||
|
return a['startLine'] < b['startLine']
|
||||||
|
end)
|
||||||
|
M.current_buf_folds = result
|
||||||
|
local current_window = api.nvim_get_current_win()
|
||||||
|
api.nvim_win_set_option(current_window, 'foldmethod', 'expr')
|
||||||
|
api.nvim_win_set_option(current_window, 'foldexpr', 'foldlsp#foldexpr()')
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
function M.adjust_foldstart(line_no)
|
||||||
|
return line_no + 1
|
||||||
|
end
|
||||||
|
|
||||||
|
function M.adjust_foldend(line_no)
|
||||||
|
local bufnr = api.nvim_get_current_buf()
|
||||||
|
local filetype = api.nvim_buf_get_option(bufnr, 'filetype')
|
||||||
|
if filetype == 'lua' then
|
||||||
|
return line_no + 2
|
||||||
|
else
|
||||||
|
return line_no + 1
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
function M.get_fold_indic(lnum)
|
||||||
|
local fold_level = 0
|
||||||
|
local is_foldstart = false
|
||||||
|
local is_foldend = false
|
||||||
|
|
||||||
|
for _, table in ipairs(M.current_buf_folds) do
|
||||||
|
local start_line = table['startLine']
|
||||||
|
local end_line = table['endLine']
|
||||||
|
|
||||||
|
-- can exit early b/c folds get pre-orderered manually
|
||||||
|
if lnum < start_line then
|
||||||
|
break
|
||||||
|
end
|
||||||
|
|
||||||
|
if lnum >= start_line and lnum <= end_line then
|
||||||
|
fold_level = fold_level + 1
|
||||||
|
if lnum == start_line then
|
||||||
|
is_foldstart = true
|
||||||
|
end
|
||||||
|
if lnum == end_line then
|
||||||
|
is_foldend = true
|
||||||
|
end
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
if is_foldend and is_foldstart then
|
||||||
|
-- If line marks both start and end of folds (like ``else`` statement),
|
||||||
|
-- merge the two folds into one by returning the current foldlevel
|
||||||
|
-- without any marker.
|
||||||
|
return fold_level
|
||||||
|
elseif is_foldstart then
|
||||||
|
return string.format(">%d", fold_level)
|
||||||
|
elseif is_foldend then
|
||||||
|
return string.format("<%d", fold_level)
|
||||||
|
else
|
||||||
|
return fold_level
|
||||||
|
end
|
||||||
|
|
||||||
|
end
|
||||||
|
|
||||||
|
return M
|
Loading…
Reference in New Issue