From 44d2cf3b022429e630bab49f60e2b574edfebdba Mon Sep 17 00:00:00 2001 From: Kyren223 Date: Mon, 22 Jun 2026 03:01:56 +0300 Subject: [PATCH] LSP hover is kinda in a state that is fine, so gonna live it at that --- .config/nvim/lua/extra.lua | 6 +- .config/nvim/lua/keymaps.lua | 31 +-- .config/nvim/lua/lsphover.lua | 331 ++++++++++++++++------------- .config/nvim/lua/plugins/noice.lua | 2 +- 4 files changed, 204 insertions(+), 166 deletions(-) diff --git a/.config/nvim/lua/extra.lua b/.config/nvim/lua/extra.lua index 1ec5d96..067f63e 100644 --- a/.config/nvim/lua/extra.lua +++ b/.config/nvim/lua/extra.lua @@ -940,7 +940,11 @@ vim.api.nvim_create_autocmd({ 'TextChanged', 'TextChangedI', 'BufEnter', 'BufWri end, }) +---------------------------------------------------------------------------- +-- NOTE: LSP hover +---------------------------------------------------------------------------- +require('lsphover') + ---------------------------------------------------------------------------- -- NOTE: ---------------------------------------------------------------------------- - diff --git a/.config/nvim/lua/keymaps.lua b/.config/nvim/lua/keymaps.lua index 258b89e..98d9b6f 100644 --- a/.config/nvim/lua/keymaps.lua +++ b/.config/nvim/lua/keymaps.lua @@ -120,21 +120,22 @@ local keymaps = { -- { 'n', 'W', prev_diagnostic(severity.WARN), { desc = 'Goto [W]arning (prev)' } }, { 'n', 'D', cursor_diagnostics, { desc = '[D]iagnostics under cursor' } }, -- MARK lsphover: - -- { - -- 'n', - -- 'K', - -- function() - -- vim.lsp.buf.hover({ - -- border = 'rounded', - -- focusable = true, - -- relative = 'cursor', - -- wrap = true, - -- wrap_at = 120, - -- width = 120, - -- }) - -- end, - -- { desc = '[D]iagnostics under cursor' }, - -- }, + { + 'n', + 'K', + function() + vim.lsp.buf.hover({ + border = 'rounded', + focusable = true, + relative = 'cursor', + wrap = true, + wrap_at = 120, + width = 120, + height = 20, + }) + end, + { desc = '[D]iagnostics under cursor' }, + }, } local function set_keymaps(tbl, bufnr) diff --git a/.config/nvim/lua/lsphover.lua b/.config/nvim/lua/lsphover.lua index 02a6baf..90be438 100644 --- a/.config/nvim/lua/lsphover.lua +++ b/.config/nvim/lua/lsphover.lua @@ -1,41 +1,3 @@ --- vim.api.nvim_create_autocmd('FileType', { --- pattern = { 'markdown', 'noice' }, --- callback = function() --- vim.opt_local.conceallevel = 2 --- vim.opt_local.concealcursor = 'nc' --- end, --- }) --- --- vim.api.nvim_create_autocmd('FileType', { --- pattern = { 'noice', 'markdown' }, --- callback = function(args) --- -- Target only floating windows --- if vim.api.nvim_win_get_config(0).relative == '' then --- return --- end --- --- -- Bind 'gd' specifically inside the hover popup buffer --- vim.keymap.set('n', 'gd', function() --- -- Grab word under cursor in the popup --- local cword = vim.fn.expand('') --- local win_ids = vim.api.nvim_list_wins() --- --- -- Find the main file window underneath the float --- for _, win in ipairs(win_ids) do --- if vim.api.nvim_win_get_config(win).relative == '' then --- -- Jump back to main source code window --- vim.api.nvim_set_current_win(win) --- -- Execute search/definition lookup based on the word extracted from the doc --- vim.cmd('lua vim.lsp.buf.definition()') --- -- Alternatively, just use lookups if it's a plain keyword: --- vim.fn.search(cword) --- break --- end --- end --- end, { buffer = args.buf, silent = true, desc = 'Follow Definition from Hover' }) --- end, --- }) - -- local orig_convert = vim.lsp.util.convert_input_to_markdown_lines -- vim.lsp.util.convert_input_to_markdown_lines = function(input, contents) -- local lines = orig_convert(input, contents) @@ -61,34 +23,101 @@ -- return lines -- end +local orig_convert = vim.lsp.util.convert_input_to_markdown_lines +vim.lsp.util.convert_input_to_markdown_lines = function(input, contents) + local lines = orig_convert(input, contents) + if not lines then + return lines + end + + if vim.bo.filetype == 'java' then + vim.notify('boop') + -- vim.notify('lines before: ' .. vim.inspect(lines)) + -- vim.notify('lines after: ' .. vim.inspect(lines)) + + for i, line in ipairs(lines) do + -- Nuke the Markdown link syntax completely to solve the gap issue. + -- Converts "[foo](https://...)" directly into "foo" + line = line:gsub('%[([^%]]+)%]%([^%)]+%)', '%1') + + -- Clean up Java/Javadoc specific formatting if we are in a Java file + line = line:gsub('

', ''):gsub('

', '') + line = line:gsub('', '') + line = line:gsub('@param%s+', '* **Param:** ') + line = line:gsub('@return%s+', '* **Returns:** ') + line = line:gsub('@throws%s+', '* **Throws:** ') + line = line:gsub('%%{@link%s+([^}]+)%%}', '`%1`') + + if line:sub(1, 2) ~= ' ' and line ~= '' and line ~= '---' then + line = ' ' .. line + end + + lines[i] = line + end + + if #lines >= 4 and lines[4] ~= '---' then + -- vim.notify('yas line' .. #lines .. vim.inspect(lines[4] ~= '---') .. ' "' .. lines[4] .. '"') + table.insert(lines, 4, '---') + elseif #lines >= 4 then + -- vim.notify('nay' .. #lines .. vim.inspect(lines[4] ~= '---') .. ' "' .. lines[4] .. '"') + end + + table.insert(lines, ' ') + end + + vim.notify('bomp') + return lines +end + -- vim.defer_fn(function() --- vim.notify('DID IT') --- -- local orig_convert = vim.lsp.util.convert_input_to_markdown_lines -- vim.lsp.util.convert_input_to_markdown_lines = function(input, contents) -- local lines = orig_convert(input, contents) -- --- for i, line in ipairs(lines) do --- -- 1. Nuke the Markdown link syntax completely to solve the gap issue. --- -- Converts "[foo](https://...)" directly into "foo" --- line = line:gsub('%[([^%]]+)%]%([^%)]+%)', '%1') +-- -- local en_space = ' ' -- not a regular space +-- local en_space = 'X' -- not a regular space +-- if lines and #lines > 2 then +-- if lines[2]:find(en_space) then +-- return lines +-- end -- --- -- 2. Clean up Java/Javadoc specific formatting if we are in a Java file --- if vim.bo.filetype == 'java' then +-- lines[2] = en_space .. lines[2] +-- end +-- +-- if vim.bo.filetype == 'java' then +-- vim.notify('boop') +-- -- vim.notify('lines before: ' .. vim.inspect(lines)) +-- -- vim.notify('lines after: ' .. vim.inspect(lines)) +-- +-- for i, line in ipairs(lines) do +-- -- Nuke the Markdown link syntax completely to solve the gap issue. +-- -- Converts "[foo](https://...)" directly into "foo" +-- line = line:gsub('%[([^%]]+)%]%([^%)]+%)', '%1') +-- vim.notify('beep') +-- +-- -- Clean up Java/Javadoc specific formatting if we are in a Java file -- line = line:gsub('

', ''):gsub('

', '') -- line = line:gsub('', '') -- line = line:gsub('@param%s+', '* **Param:** ') -- line = line:gsub('@return%s+', '* **Returns:** ') -- line = line:gsub('@throws%s+', '* **Throws:** ') -- line = line:gsub('%%{@link%s+([^}]+)%%}', '`%1`') +-- +-- -- if i > 3 then +-- -- line = ' ' .. line .. ' ' +-- -- end +-- +-- lines[i] = line +-- vim.notify('bap') -- end -- --- lines[i] = line +-- -- table.insert(lines, 4, '---') -- end -- +-- vim.notify('bomp') -- return lines -- end --- end, 1000) +-- end, 2000) -- To handle window-local options like conceallevel and concealcursor, -- we need to hook into the buffer creation event for the LSP floating window. @@ -107,110 +136,114 @@ vim.api.nvim_create_autocmd('BufWinEnter', { vim.wo[win].concealcursor = 'n' vim.wo[win].linebreak = true - if not vim.b[args.buf].padded then - vim.b[args.buf].padded = true -- Guard flag against infinite loops - local lines = { 'foo' } - vim.api.nvim_buf_set_lines(args.buf, 0, -1, false, lines) - - -- local lines = vim.api.nvim_buf_get_lines(args.buf, 0, -1, false) - -- for i, line in ipairs(lines) do - -- lines[i] = ' ' .. line .. ' ' -- 2 spaces left, 2 spaces right - -- end - -- vim.api.nvim_buf_set_lines(args.buf, 0, -1, false, lines) - -- - -- -- 3. Expand the window width by 4 so the text doesn't wrap awkwardly - -- local config = vim.api.nvim_win_get_config(win) - -- config.width = config.width + 4 - -- vim.api.nvim_win_set_config(win, config) - end + -- if not vim.b[args.buf].padded then + -- vim.b[args.buf].padded = true -- Guard flag against infinite loops + -- local lines = { 'foo' } + -- vim.api.nvim_buf_set_lines(args.buf, 0, -1, false, lines) + -- + -- -- local lines = vim.api.nvim_buf_get_lines(args.buf, 0, -1, false) + -- -- for i, line in ipairs(lines) do + -- -- lines[i] = ' ' .. line .. ' ' -- 2 spaces left, 2 spaces right + -- -- end + -- -- vim.api.nvim_buf_set_lines(args.buf, 0, -1, false, lines) + -- -- + -- -- -- 3. Expand the window width by 4 so the text doesn't wrap awkwardly + -- -- local config = vim.api.nvim_win_get_config(win) + -- -- config.width = config.width + 4 + -- -- vim.api.nvim_win_set_config(win, config) + -- end end end end, }) --- Helper function to perform your exact regex formatting -local function clean_hover_text(text, ft) - if type(text) ~= 'string' then - return text - end +---------------------------------------------------------------------- +-- NOTE(kyren): below is for trying to make it work with noice.nvim -- +---------------------------------------------------------------------- - -- 1. Nuke Markdown links: "[foo](https://...)" -> "foo" - text = text:gsub('%[([^%]]+)%]%([^%)]+%)', '%1') - - -- 2. Clean Java/Javadoc specific formatting safely - if ft == 'java' then - text = text:gsub('

', ''):gsub('

', '') - text = text:gsub('', '') - text = text:gsub('@param%s+', '* **Param:** ') - text = text:gsub('@return%s+', '* **Returns:** ') - text = text:gsub('@throws%s+', '* **Throws:** ') - text = text:gsub('%%{@link%s+([^}]+)%%}', '`%1`') - end - return text -end - --- Core interceptor logic -local function intercept_hover_for_client(client) - -- Prevent double-wrapping the same client - if not client or client._hover_intercepted then - return - end - client._hover_intercepted = true - - local orig_request = client.request - - client.request = function(method, params, handler, bufnr) - if method == 'textDocument/hover' then - -- CRITICAL FIX: If Noice or Neovim passes a `nil` handler, they expect the system - -- to use the global fallback. We must resolve it exactly like Neovim does natively. - local resolved_handler = handler or client.handlers[method] or vim.lsp.handlers[method] - - if resolved_handler then - local orig_handler = resolved_handler - - -- Wrap the resolved handler to mutate the text payload before it reaches Noice - handler = function(err, result, ctx, config) - if result and result.contents then - local buf = bufnr or (ctx and ctx.bufnr) or vim.api.nvim_get_current_buf() - local ft = vim.api.nvim_get_option_value('filetype', { buf = buf }) - - local contents = result.contents - if type(contents) == 'table' and contents.value then - contents.value = clean_hover_text(contents.value, ft) - elseif type(contents) == 'string' then - result.contents = clean_hover_text(contents, ft) - elseif type(contents) == 'table' then - for i, item in ipairs(contents) do - if type(item) == 'string' then - contents[i] = clean_hover_text(item, ft) - elseif type(item) == 'table' and item.value then - item.value = clean_hover_text(item.value, ft) - end - end - end - end - - -- Pass the sanitized text down the pipeline - return orig_handler(err, result, ctx, config) - end - end - end - - return orig_request(method, params, handler, bufnr) - end -end - ---- 1. Catch clients that are ALREADY attached (Fixes the race condition) -local get_clients = vim.lsp.get_clients or vim.lsp.get_active_clients -for _, client in ipairs(get_clients()) do - intercept_hover_for_client(client) -end - ---- 2. Catch any future clients that attach later -vim.api.nvim_create_autocmd('LspAttach', { - desc = 'Intercept raw LSP hover responses to format before Noice reads them', - callback = function(args) - local client = vim.lsp.get_client_by_id(args.data.client_id) - intercept_hover_for_client(client) - end, -}) +-- -- Helper function to perform your exact regex formatting +-- local function clean_hover_text(text, ft) +-- if type(text) ~= 'string' then +-- return text +-- end +-- +-- -- 1. Nuke Markdown links: "[foo](https://...)" -> "foo" +-- text = text:gsub('%[([^%]]+)%]%([^%)]+%)', '%1') +-- +-- -- 2. Clean Java/Javadoc specific formatting safely +-- if ft == 'java' then +-- text = text:gsub('

', ''):gsub('

', '') +-- text = text:gsub('', '') +-- text = text:gsub('@param%s+', '* **Param:** ') +-- text = text:gsub('@return%s+', '* **Returns:** ') +-- text = text:gsub('@throws%s+', '* **Throws:** ') +-- text = text:gsub('%%{@link%s+([^}]+)%%}', '`%1`') +-- end +-- return text +-- end +-- +-- -- Core interceptor logic +-- local function intercept_hover_for_client(client) +-- -- Prevent double-wrapping the same client +-- if not client or client._hover_intercepted then +-- return +-- end +-- client._hover_intercepted = true +-- +-- local orig_request = client.request +-- +-- client.request = function(method, params, handler, bufnr) +-- if method == 'textDocument/hover' then +-- -- CRITICAL FIX: If Noice or Neovim passes a `nil` handler, they expect the system +-- -- to use the global fallback. We must resolve it exactly like Neovim does natively. +-- local resolved_handler = handler or client.handlers[method] or vim.lsp.handlers[method] +-- +-- if resolved_handler then +-- local orig_handler = resolved_handler +-- +-- -- Wrap the resolved handler to mutate the text payload before it reaches Noice +-- handler = function(err, result, ctx, config) +-- if result and result.contents then +-- local buf = bufnr or (ctx and ctx.bufnr) or vim.api.nvim_get_current_buf() +-- local ft = vim.api.nvim_get_option_value('filetype', { buf = buf }) +-- +-- local contents = result.contents +-- if type(contents) == 'table' and contents.value then +-- contents.value = clean_hover_text(contents.value, ft) +-- elseif type(contents) == 'string' then +-- result.contents = clean_hover_text(contents, ft) +-- elseif type(contents) == 'table' then +-- for i, item in ipairs(contents) do +-- if type(item) == 'string' then +-- contents[i] = clean_hover_text(item, ft) +-- elseif type(item) == 'table' and item.value then +-- item.value = clean_hover_text(item.value, ft) +-- end +-- end +-- end +-- end +-- +-- -- Pass the sanitized text down the pipeline +-- return orig_handler(err, result, ctx, config) +-- end +-- end +-- end +-- +-- return orig_request(method, params, handler, bufnr) +-- end +-- end +-- +-- --- 1. Catch clients that are ALREADY attached (Fixes the race condition) +-- local get_clients = vim.lsp.get_clients or vim.lsp.get_active_clients +-- for _, client in ipairs(get_clients()) do +-- intercept_hover_for_client(client) +-- end +-- +-- --- 2. Catch any future clients that attach later +-- vim.api.nvim_create_autocmd('LspAttach', { +-- desc = 'Intercept raw LSP hover responses to format before Noice reads them', +-- callback = function(args) +-- local client = vim.lsp.get_client_by_id(args.data.client_id) +-- intercept_hover_for_client(client) +-- end, +-- }) diff --git a/.config/nvim/lua/plugins/noice.lua b/.config/nvim/lua/plugins/noice.lua index 3648b55..32f6a38 100644 --- a/.config/nvim/lua/plugins/noice.lua +++ b/.config/nvim/lua/plugins/noice.lua @@ -10,7 +10,7 @@ return { override = { -- override markdown rendering so that **cmp** and other plugins use **Treesitter** -- override the default lsp markdown formatter with Noice - ['vim.lsp.util.convert_input_to_markdown_lines'] = true, + -- ['vim.lsp.util.convert_input_to_markdown_lines'] = false, -- override the lsp markdown formatter with Noice -- ['vim.lsp.util.stylize_markdown'] = true, -- ['cmp.entry.get_documentation'] = true,