diff --git a/lua/lsp/codelens.lua b/lua/lsp/codelens.lua index d2e59bf..6057f09 100644 --- a/lua/lsp/codelens.lua +++ b/lua/lsp/codelens.lua @@ -6,9 +6,11 @@ local REFRESH_DEBOUNCE_MS = 200 local M = {} ----@type "eol" | "right_align" | "inline" -M.virt_text_pos = "eol" -M.separator = " | " +---@alias ow.lsp.codelens.Position "eol" | "right_align" | "inline" | "above" + +---@type ow.lsp.codelens.Position +local position = "above" +local separator = " | " ---@type table local state_by_buf = {} @@ -69,12 +71,38 @@ function Row:render(buf) for i, lens in ipairs(self.ready) do table.insert(parts, { lens.command.title, "LspCodeLens" }) if i < #self.ready then - table.insert(parts, { M.separator, "LspCodeLensSeparator" }) + table.insert(parts, { separator, "LspCodeLensSeparator" }) end end - local col = M.virt_text_pos == "inline" - and self.ready[1].range.start.character - or 0 + + local col, opts + if position == "above" then + local line = vim.api.nvim_buf_get_lines( + buf, + self.row, + self.row + 1, + false + )[1] or "" + local indent = line:match("^%s*") or "" + local chunks = parts + if indent ~= "" then + chunks = { { indent, "LspCodeLensSeparator" } } + vim.list_extend(chunks, parts) + end + col = 0 + opts = { + virt_lines = { chunks }, + virt_lines_above = true, + hl_mode = "combine", + } + else + col = position == "inline" and self.ready[1].range.start.character or 0 + opts = { + virt_text = parts, + virt_text_pos = position, + hl_mode = "combine", + } + end -- One extmark per row. Extmarks auto-shift with edits, so multiple from -- prior refreshes can end up on the same row; pick the first and drop @@ -95,22 +123,21 @@ function Row:render(buf) end end - if - primary - and primary.col == col - and primary.details - and primary.details.virt_text_pos == M.virt_text_pos - and vim.deep_equal(primary.details.virt_text, parts) - then - return + if primary and primary.col == col and primary.details then + local d = primary.details --[[@as vim.api.keyset.extmark_details]] + local same = opts.virt_lines + and vim.deep_equal(d.virt_lines, opts.virt_lines) + and d.virt_lines_above == opts.virt_lines_above + or not opts.virt_lines + and d.virt_text_pos == opts.virt_text_pos + and vim.deep_equal(d.virt_text, opts.virt_text) + if same then + return + end end - vim.api.nvim_buf_set_extmark(buf, NS, self.row, col, { - id = primary and primary.id or nil, - virt_text = parts, - virt_text_pos = M.virt_text_pos, - hl_mode = "combine", - }) + opts.id = primary and primary.id or nil + vim.api.nvim_buf_set_extmark(buf, NS, self.row, col, opts) end ---@class ow.lsp.codelens.State @@ -292,7 +319,20 @@ function M.toggle(buf) get_state(buf):toggle() end -function M.setup() +---@class ow.lsp.codelens.SetupOpts +---@field position? ow.lsp.codelens.Position +---@field separator? string + +---@param opts? ow.lsp.codelens.SetupOpts +function M.setup(opts) + opts = opts or {} + if opts.position ~= nil then + position = opts.position + end + if opts.separator ~= nil then + separator = opts.separator + end + vim.api.nvim_create_autocmd({ "BufEnter", "LspAttach" }, { group = GROUP, callback = function(ev)