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