|
| 1 | +local require = require("noice.util.lazy") |
| 2 | + |
| 3 | +local Message = require("noice.message") |
| 4 | +local Manager = require("noice.message.manager") |
| 5 | +local NoiceText = require("noice.text") |
| 6 | +local Config = require("noice.config") |
| 7 | + |
| 8 | +local M = {} |
| 9 | + |
| 10 | +---@alias LspEvent "lsp" |
| 11 | +M.event = "lsp" |
| 12 | + |
| 13 | +---@enum LspKind |
| 14 | +M.kinds = { |
| 15 | + progress = "progress", |
| 16 | + hover = "hover", |
| 17 | +} |
| 18 | + |
| 19 | +---@alias MarkedString string | { language: string; value: string } |
| 20 | +---@alias MarkupContent { kind: ('plaintext' | 'markdown'), value: string} |
| 21 | +---@alias MarkupContents MarkedString | MarkedString[] | MarkupContent |
| 22 | + |
| 23 | +---@param contents MarkupContents |
| 24 | +---@param kind LspKind |
| 25 | +function M.format(contents, kind) |
| 26 | + if type(contents) ~= "table" or not vim.tbl_islist(contents) then |
| 27 | + contents = { contents } |
| 28 | + end |
| 29 | + |
| 30 | + local parts = {} |
| 31 | + |
| 32 | + for _, content in ipairs(contents) do |
| 33 | + if type(content) == "string" then |
| 34 | + table.insert(parts, content) |
| 35 | + elseif content.language then |
| 36 | + table.insert(parts, ("```%s\n%s\n```"):format(content.language, content.value)) |
| 37 | + elseif content.kind == "markdown" then |
| 38 | + table.insert(parts, content.value) |
| 39 | + elseif content.kind == "plaintext" then |
| 40 | + table.insert(parts, ("```\n%s\n```"):format(content.value)) |
| 41 | + end |
| 42 | + end |
| 43 | + |
| 44 | + local text = table.concat(parts, "\n") |
| 45 | + text = text:gsub("\n\n\n", "\n\n") |
| 46 | + text = text:gsub("\n%s*\n```", "\n```") |
| 47 | + text = text:gsub("```\n%s*\n", "```\n") |
| 48 | + |
| 49 | + local lines = vim.split(text, "\n") |
| 50 | + |
| 51 | + local width = 50 |
| 52 | + for _, line in pairs(lines) do |
| 53 | + width = math.max(width, vim.api.nvim_strwidth(line)) |
| 54 | + end |
| 55 | + |
| 56 | + local message = Message(M.event, kind) |
| 57 | + message.once = true |
| 58 | + message.opts.title = kind |
| 59 | + |
| 60 | + for _, line in ipairs(lines) do |
| 61 | + message:newline() |
| 62 | + -- Make the horizontal ruler extend the whole window width |
| 63 | + if line:find("^[%*%-_][%*%-_][%*%-_]+$") then |
| 64 | + message:append(NoiceText("", { |
| 65 | + virt_text_win_col = 0, |
| 66 | + virt_text = { { ("─"):rep(width), "@punctuation.special.markdown" } }, |
| 67 | + priority = 100, |
| 68 | + })) |
| 69 | + else |
| 70 | + message:append(line) |
| 71 | + end |
| 72 | + end |
| 73 | + return message |
| 74 | +end |
| 75 | + |
| 76 | +function M.setup() |
| 77 | + if Config.options.lsp.hover.enabled then |
| 78 | + vim.lsp.handlers["textDocument/hover"] = M.hover |
| 79 | + end |
| 80 | + if Config.options.lsp.progress.enabled then |
| 81 | + require("noice.source.lsp.progress").setup() |
| 82 | + end |
| 83 | +end |
| 84 | + |
| 85 | +---@param message NoiceMessage |
| 86 | +function M.close_on_move(message) |
| 87 | + local open = true |
| 88 | + message.opts.timeout = 100 |
| 89 | + message.opts.keep = function() |
| 90 | + return open |
| 91 | + end |
| 92 | + vim.api.nvim_create_autocmd("CursorMoved", { |
| 93 | + callback = function() |
| 94 | + open = false |
| 95 | + end, |
| 96 | + once = true, |
| 97 | + }) |
| 98 | +end |
| 99 | + |
| 100 | +function M.hover(_, result) |
| 101 | + if not (result and result.contents) then |
| 102 | + vim.notify("No information available") |
| 103 | + return |
| 104 | + end |
| 105 | + |
| 106 | + local message = M.format(result.contents, "hover") |
| 107 | + M.close_on_move(message) |
| 108 | + Manager.add(message) |
| 109 | +end |
| 110 | + |
| 111 | +return M |
0 commit comments