rework(preview): Ask providers for hover info

Now lsp checks if client supports hover
Addresses #82
This commit is contained in:
simrat39
2021-11-24 21:44:23 -08:00
parent 552b67993e
commit f4f2b5edfb
5 changed files with 251 additions and 207 deletions

View File

@@ -1,228 +1,236 @@
local vim = vim local vim = vim
local main = require('symbols-outline') local main = require("symbols-outline")
local config = require('symbols-outline.config') local config = require("symbols-outline.config")
local buf_request = require('symbols-outline.utils.lsp_utils').request local buf_request = require("symbols-outline.utils.lsp_utils").request
local M = {} local M = {}
local state = { local state = {
preview_buf = nil, preview_buf = nil,
preview_win = nil, preview_win = nil,
hover_buf = nil, hover_buf = nil,
hover_win = nil hover_win = nil,
} }
local function is_current_win_outline() local function is_current_win_outline()
local curwin = vim.api.nvim_get_current_win() local curwin = vim.api.nvim_get_current_win()
return curwin == main.state.outline_win return curwin == main.state.outline_win
end end
local function has_code_win() local function has_code_win()
local isWinValid = vim.api.nvim_win_is_valid(main.state.code_win) local isWinValid = vim.api.nvim_win_is_valid(main.state.code_win)
if not isWinValid then return false end if not isWinValid then
local bufnr = vim.api.nvim_win_get_buf(main.state.code_win) return false
local isBufValid = vim.api.nvim_buf_is_valid(bufnr) end
return isBufValid local bufnr = vim.api.nvim_win_get_buf(main.state.code_win)
local isBufValid = vim.api.nvim_buf_is_valid(bufnr)
return isBufValid
end end
local function get_offset() local function get_offset()
local outline_winnr = main.state.outline_win local outline_winnr = main.state.outline_win
local width = 53 local width = 53
local height = 0 local height = 0
if config.has_numbers() then if config.has_numbers() then
width = width + 4; width = width + 4
end end
if config.options.position == 'right' then if config.options.position == "right" then
width = 0 - width width = 0 - width
else else
width = vim.api.nvim_win_get_width(outline_winnr) + 1 width = vim.api.nvim_win_get_width(outline_winnr) + 1
end end
return {height, width} return { height, width }
end end
local function get_height() local function get_height()
local uis = vim.api.nvim_list_uis() local uis = vim.api.nvim_list_uis()
return math.ceil(uis[1].height / 3) return math.ceil(uis[1].height / 3)
end end
local function get_hovered_node() local function get_hovered_node()
local hovered_line = vim.api.nvim_win_get_cursor(main.state.outline_win)[1] local hovered_line = vim.api.nvim_win_get_cursor(main.state.outline_win)[1]
local node = main.state.flattened_outline_items[hovered_line] local node = main.state.flattened_outline_items[hovered_line]
return node return node
end end
local function update_preview(code_buf) local function update_preview(code_buf)
code_buf = code_buf or vim.api.nvim_win_get_buf(main.state.code_win) code_buf = code_buf or vim.api.nvim_win_get_buf(main.state.code_win)
local node = get_hovered_node() local node = get_hovered_node()
if not node then return end if not node then
local lines = vim.api.nvim_buf_get_lines(code_buf, 0, -1, false) return
end
local lines = vim.api.nvim_buf_get_lines(code_buf, 0, -1, false)
if state.preview_buf ~= nil then if state.preview_buf ~= nil then
vim.api.nvim_buf_set_lines(state.preview_buf, 0, -1, 0, lines) vim.api.nvim_buf_set_lines(state.preview_buf, 0, -1, 0, lines)
vim.api.nvim_win_set_cursor(state.preview_win, vim.api.nvim_win_set_cursor(state.preview_win, { node.line + 1, node.character })
{node.line + 1, node.character}) end
end
end end
local function setup_preview_buf() local function setup_preview_buf()
local code_buf = vim.api.nvim_win_get_buf(main.state.code_win) local code_buf = vim.api.nvim_win_get_buf(main.state.code_win)
local ft = vim.api.nvim_buf_get_option(code_buf, "filetype") local ft = vim.api.nvim_buf_get_option(code_buf, "filetype")
local function treesitter_attach() local function treesitter_attach()
local ts_highlight = require('nvim-treesitter.highlight') local ts_highlight = require("nvim-treesitter.highlight")
ts_highlight.attach(state.preview_buf, ft) ts_highlight.attach(state.preview_buf, ft)
end end
-- user might not have tree sitter installed -- user might not have tree sitter installed
pcall(treesitter_attach) pcall(treesitter_attach)
vim.api.nvim_buf_set_option(state.preview_buf, "syntax", ft) vim.api.nvim_buf_set_option(state.preview_buf, "syntax", ft)
vim.api.nvim_buf_set_option(state.preview_buf, "bufhidden", "delete") vim.api.nvim_buf_set_option(state.preview_buf, "bufhidden", "delete")
vim.api.nvim_win_set_option(state.preview_win, "cursorline", true) vim.api.nvim_win_set_option(state.preview_win, "cursorline", true)
update_preview(code_buf) update_preview(code_buf)
end end
local function get_hover_params(node, winnr) local function get_hover_params(node, winnr)
local bufnr = vim.api.nvim_win_get_buf(winnr) local bufnr = vim.api.nvim_win_get_buf(winnr)
local uri = vim.uri_from_bufnr(bufnr) local uri = vim.uri_from_bufnr(bufnr)
return { return {
textDocument = {uri = uri}, textDocument = { uri = uri },
position = {line = node.line, character = node.character}, position = { line = node.line, character = node.character },
bufnr = bufnr bufnr = bufnr,
} }
end end
local function update_hover() local function update_hover()
if not has_code_win() then return end if not has_code_win() then
return
end
local node = get_hovered_node() local node = get_hovered_node()
if not node then return end if not node then
local params = get_hover_params(node, main.state.code_win) return
end
buf_request(params.bufnr, "textDocument/hover", params, local provider = _G._symbols_outline_current_provider
function(err, result) local params = get_hover_params(node, main.state.code_win)
if err then print(vim.inspect(err)) end
local markdown_lines = {}
if result ~= nil then
markdown_lines = vim.lsp.util.convert_input_to_markdown_lines(
result.contents)
end
markdown_lines = vim.lsp.util.trim_empty_lines(markdown_lines)
if vim.tbl_isempty(markdown_lines) then
markdown_lines = {"###No info available!"}
end
markdown_lines = vim.lsp.util.stylize_markdown(state.hover_buf, markdown_lines, {}) provider.hover_info(params.bufnr, params, function(err, result)
if err then
print(vim.inspect(err))
end
local markdown_lines = {}
if result ~= nil then
markdown_lines = vim.lsp.util.convert_input_to_markdown_lines(result.contents)
end
markdown_lines = vim.lsp.util.trim_empty_lines(markdown_lines)
if vim.tbl_isempty(markdown_lines) then
markdown_lines = { "###No info available!" }
end
if state.hover_buf ~= nil then markdown_lines = vim.lsp.util.stylize_markdown(state.hover_buf, markdown_lines, {})
vim.api.nvim_buf_set_lines(state.hover_buf, 0, -1, 0, markdown_lines)
end if state.hover_buf ~= nil then
end) vim.api.nvim_buf_set_lines(state.hover_buf, 0, -1, 0, markdown_lines)
end
end)
end end
local function setup_hover_buf() local function setup_hover_buf()
if not has_code_win() then return end if not has_code_win() then
local code_buf = vim.api.nvim_win_get_buf(main.state.code_win) return
local ft = vim.api.nvim_buf_get_option(code_buf, "filetype") end
vim.api.nvim_buf_set_option(state.hover_buf, "syntax", ft) local code_buf = vim.api.nvim_win_get_buf(main.state.code_win)
vim.api.nvim_buf_set_option(state.hover_buf, "bufhidden", "delete") local ft = vim.api.nvim_buf_get_option(code_buf, "filetype")
vim.api.nvim_win_set_option(state.hover_win, "wrap", true) vim.api.nvim_buf_set_option(state.hover_buf, "syntax", ft)
vim.api.nvim_win_set_option(state.hover_win, "cursorline", false) vim.api.nvim_buf_set_option(state.hover_buf, "bufhidden", "delete")
update_hover() vim.api.nvim_win_set_option(state.hover_win, "wrap", true)
vim.api.nvim_win_set_option(state.hover_win, "cursorline", false)
update_hover()
end end
local function set_bg_hl() local function set_bg_hl()
local winhi = "Normal:" .. config.options.preview_bg_highlight local winhi = "Normal:" .. config.options.preview_bg_highlight
vim.api.nvim_win_set_option(state.preview_win, "winhighlight", winhi) vim.api.nvim_win_set_option(state.preview_win, "winhighlight", winhi)
vim.api.nvim_win_set_option(state.hover_win, "winhighlight", winhi) vim.api.nvim_win_set_option(state.hover_win, "winhighlight", winhi)
end end
local function show_preview() local function show_preview()
if state.preview_win == nil and state.preview_buf == nil then if state.preview_win == nil and state.preview_buf == nil then
state.preview_buf = vim.api.nvim_create_buf(false, true) state.preview_buf = vim.api.nvim_create_buf(false, true)
vim.api.nvim_buf_attach(state.preview_buf, false, { vim.api.nvim_buf_attach(state.preview_buf, false, {
on_detach = function() on_detach = function()
state.preview_buf = nil state.preview_buf = nil
state.preview_win = nil state.preview_win = nil
end end,
}) })
local offsets = get_offset() local offsets = get_offset()
state.preview_win = vim.api.nvim_open_win(state.preview_buf, false, { state.preview_win = vim.api.nvim_open_win(state.preview_buf, false, {
relative = 'win', relative = "win",
width = 50, width = 50,
height = get_height(), height = get_height(),
bufpos = {0, 0}, bufpos = { 0, 0 },
row = offsets[1], row = offsets[1],
col = offsets[2], col = offsets[2],
border = 'single' border = "single",
}) })
setup_preview_buf() setup_preview_buf()
else else
update_preview() update_preview()
end end
end end
local function show_hover() local function show_hover()
if state.hover_win == nil and state.hover_buf == nil then if state.hover_win == nil and state.hover_buf == nil then
state.hover_buf = vim.api.nvim_create_buf(false, true) state.hover_buf = vim.api.nvim_create_buf(false, true)
vim.api.nvim_buf_attach(state.hover_buf, false, { vim.api.nvim_buf_attach(state.hover_buf, false, {
on_detach = function() on_detach = function()
state.hover_buf = nil state.hover_buf = nil
state.hover_win = nil state.hover_win = nil
end end,
}) })
local offsets = get_offset() local offsets = get_offset()
local height = get_height() local height = get_height()
state.hover_win = vim.api.nvim_open_win(state.hover_buf, false, { state.hover_win = vim.api.nvim_open_win(state.hover_buf, false, {
relative = 'win', relative = "win",
width = 50, width = 50,
height = height, height = height,
bufpos = {0, 0}, bufpos = { 0, 0 },
row = offsets[1] + height + 2, row = offsets[1] + height + 2,
col = offsets[2], col = offsets[2],
border = 'single' border = "single",
}) })
setup_hover_buf() setup_hover_buf()
else else
update_hover() update_hover()
end end
end end
function M.show() function M.show()
if not is_current_win_outline() or if not is_current_win_outline() or #vim.api.nvim_list_wins() < 2 then
#vim.api.nvim_list_wins() < 2 then return
return end
end
show_preview() show_preview()
show_hover() show_hover()
set_bg_hl() set_bg_hl()
end end
function M.close() function M.close()
if has_code_win() then if has_code_win() then
if state.preview_win ~= nil and if state.preview_win ~= nil and vim.api.nvim_win_is_valid(state.preview_win) then
vim.api.nvim_win_is_valid(state.preview_win) then vim.api.nvim_win_close(state.preview_win, true)
vim.api.nvim_win_close(state.preview_win, true) end
end if state.hover_win ~= nil and vim.api.nvim_win_is_valid(state.hover_win) then
if state.hover_win ~= nil and vim.api.nvim_win_close(state.hover_win, true)
vim.api.nvim_win_is_valid(state.hover_win) then end
vim.api.nvim_win_close(state.hover_win, true) end
end
end
end end
function M.toggle() function M.toggle()
if state.preview_win ~= nil then if state.preview_win ~= nil then
M.close() M.close()
else else
M.show() M.show()
end end
end end
return M return M

View File

@@ -1,21 +1,30 @@
local M = {} local M = {}
function M.should_use_provider(_) function M.should_use_provider(_)
local coc_installed = vim.fn.exists("*CocActionAsync") local coc_installed = vim.fn.exists("*CocActionAsync")
if coc_installed == 0 then return end if coc_installed == 0 then
return
end
local coc_attached = vim.fn.call('CocAction', {'ensureDocument'}) local coc_attached = vim.fn.call("CocAction", { "ensureDocument" })
local has_symbols = vim.fn.call('CocHasProvider', {'documentSymbol'}) local has_symbols = vim.fn.call("CocHasProvider", { "documentSymbol" })
return coc_attached and has_symbols; return coc_attached and has_symbols
end
function M.hover_info(_, _, on_info)
on_info(nil, { contents = { kind = "markdown", contents = { "No extra information availaible!" } } })
end end
---@param on_symbols function ---@param on_symbols function
function M.request_symbols(on_symbols) function M.request_symbols(on_symbols)
vim.fn.call('CocActionAsync', {'documentSymbols', function (_, symbols) vim.fn.call("CocActionAsync", {
on_symbols{[1000000]={result=symbols}} "documentSymbols",
end}) function(_, symbols)
on_symbols({ [1000000] = { result = symbols } })
end,
})
end end
return M return M

View File

@@ -1,34 +1,35 @@
local M = {} local M = {}
local providers = { local providers = {
'symbols-outline/providers/nvim-lsp', "symbols-outline/providers/nvim-lsp",
'symbols-outline/providers/coc', "symbols-outline/providers/coc",
'symbols-outline/providers/markdown' "symbols-outline/providers/markdown",
} }
local current_provider; _G._symbols_outline_current_provider = nil
function M.has_provider() function M.has_provider()
local ret = false; local ret = false
for _, value in ipairs(providers) do for _, value in ipairs(providers) do
local provider = require(value) local provider = require(value)
if provider.should_use_provider(0) then if provider.should_use_provider(0) then
ret = true; ret = true
break break
end end
end end
return ret return ret
end end
---@param on_symbols function ---@param on_symbols function
function M.request_symbols(on_symbols) function M.request_symbols(on_symbols)
for _, value in ipairs(providers) do for _, value in ipairs(providers) do
local provider = require(value) local provider = require(value)
if provider.should_use_provider(0) then if provider.should_use_provider(0) then
provider.request_symbols(on_symbols) _G._symbols_outline_current_provider = provider
break provider.request_symbols(on_symbols)
end break
end end
end
end end
return M return M

View File

@@ -1,15 +1,19 @@
local md_parser = require('symbols-outline.markdown') local md_parser = require("symbols-outline.markdown")
local M = {} local M = {}
-- probably change this -- probably change this
function M.should_use_provider(bufnr) function M.should_use_provider(bufnr)
return vim.api.nvim_buf_get_option(bufnr, 'ft') == 'markdown' return vim.api.nvim_buf_get_option(bufnr, "ft") == "markdown"
end
function M.hover_info(_, _, on_info)
on_info(nil, { contents = { kind = "markdown", contents = { "No extra information availaible!" } } })
end end
---@param on_symbols function ---@param on_symbols function
function M.request_symbols(on_symbols) function M.request_symbols(on_symbols)
on_symbols(md_parser.handle_markdown()) on_symbols(md_parser.handle_markdown())
end end
return M return M

View File

@@ -1,35 +1,57 @@
local config = require('symbols-outline.config') local config = require("symbols-outline.config")
local M = {} local M = {}
local function getParams() local function getParams()
return {textDocument = vim.lsp.util.make_text_document_params()} return { textDocument = vim.lsp.util.make_text_document_params() }
end
function M.hover_info(bufnr, params, on_info)
local clients = vim.lsp.buf_get_clients(bufnr)
local used_client
for id, client in pairs(clients) do
if config.is_client_blacklisted(id) then
goto continue
else
if client.server_capabilities.hoverProvider then
used_client = client
break
end
end
::continue::
end
if not used_client then
on_info(nil, { contents = { kind = "markdown", content = { "No extra information availaible!" } } })
end
used_client.request("textDocument/hover", params, on_info, bufnr)
end end
-- probably change this -- probably change this
function M.should_use_provider(bufnr) function M.should_use_provider(bufnr)
local clients = vim.lsp.buf_get_clients(bufnr) local clients = vim.lsp.buf_get_clients(bufnr)
local ret = false local ret = false
for id, client in pairs(clients) do for id, client in pairs(clients) do
if config.is_client_blacklisted(id) then if config.is_client_blacklisted(id) then
goto continue goto continue
else else
if client.server_capabilities.documentSymbolProvider then if client.server_capabilities.documentSymbolProvider then
ret = true ret = true
break break
end end
end end
::continue:: ::continue::
end end
return ret return ret
end end
---@param on_symbols function ---@param on_symbols function
function M.request_symbols(on_symbols) function M.request_symbols(on_symbols)
vim.lsp.buf_request_all(0, "textDocument/documentSymbol", getParams(), vim.lsp.buf_request_all(0, "textDocument/documentSymbol", getParams(), on_symbols)
on_symbols)
end end
return M return M