From 5b41be0bcbfdc23ff171078aaf6c43d9c415b1cb Mon Sep 17 00:00:00 2001 From: qvalentin Date: Fri, 16 Jan 2026 16:39:26 +0100 Subject: [PATCH 1/3] fix: improve template concealment on wrapped lines (#20) - Use 'inline' virtual text with 'conceal' when 'conceallevel > 0' - Fallback to 'overlay' with padding when 'conceallevel == 0' - Add note to README regarding 'conceallevel' recommendation --- README.md | 2 ++ lua/helm-ls/conceal.lua | 36 +++++++++++++++++++++++++----------- 2 files changed, 27 insertions(+), 11 deletions(-) diff --git a/README.md b/README.md index ca7e5c4..87e2cc8 100644 --- a/README.md +++ b/README.md @@ -11,6 +11,7 @@ The plugin is in early development. - Highlight the current block (`if`, `with`, `range`, etc.) - Jump between the start and end of a block with `%` - experimental: Overwrite templates with their current values using virtual text (See [Demos](#demos)) + - Note: Setting `vim.opt.conceallevel = 2` is recommended for correct handling of wrapped lines. - experimental: Show hints highlighting the effect of `nindent` and `indent` functions (See [Demos](#demos)) ## Keymaps @@ -52,6 +53,7 @@ Default config: { conceal_templates = { -- enable the replacement of templates with virtual text of their current values + -- note: for better wrapping support, set `vim.opt.conceallevel = 2` enabled = true, -- this might change to false in the future }, indent_hints = { diff --git a/lua/helm-ls/conceal.lua b/lua/helm-ls/conceal.lua index 716d02b..2d596ff 100644 --- a/lua/helm-ls/conceal.lua +++ b/lua/helm-ls/conceal.lua @@ -42,17 +42,30 @@ end -- Helper function to set an extmark with the hover result local function set_extmark(bufnr, start_row, start_col, end_row, end_col, hover_text, original_text) - hover_text = pad_text(hover_text, #original_text) - - -- Set conceal for the syntax element - api.nvim_buf_set_extmark(bufnr, ns_id, start_row, start_col, { - end_row = end_row, - end_col = end_col, - virt_text = { { hover_text, "Conceal" } }, - virt_text_pos = "overlay", - hl_mode = "blend", - virt_text_hide = true, - }) + local conceallevel = vim.opt_local.conceallevel:get() + + if conceallevel > 0 then + -- Set conceal for the syntax element (new approach, handles wrapping) + api.nvim_buf_set_extmark(bufnr, ns_id, start_row, start_col, { + end_row = end_row, + end_col = end_col, + virt_text = { { hover_text, "Conceal" } }, + virt_text_pos = "inline", + conceal = "", + }) + else + -- Fallback for conceallevel = 0 (original approach, has issues with wrapping) + hover_text = pad_text(hover_text, #original_text) + + api.nvim_buf_set_extmark(bufnr, ns_id, start_row, start_col, { + end_row = end_row, + end_col = end_col, + virt_text = { { hover_text, "Conceal" } }, + virt_text_pos = "overlay", + hl_mode = "blend", + virt_text_hide = true, + }) + end end -- Function to handle LSP hover requests and apply concealment @@ -128,6 +141,7 @@ local function update_conceal_templates() if vim.tbl_isempty(clients) then return end + conceal_templates_with_hover() clear_extmark_if_cursor_on_line() end From 81a943034c5ebe788ae3c7f85ba4f46879416281 Mon Sep 17 00:00:00 2001 From: qvalentin Date: Fri, 16 Jan 2026 16:47:36 +0100 Subject: [PATCH 2/3] fix: prevent duplication of concealed virtual text - Clear namespace for visible range before applying concealment - Use stable IDs for extmarks based on position - Ensure full range concealment when using inline virtual text --- lua/helm-ls/conceal.lua | 22 +++++++++++++++++----- 1 file changed, 17 insertions(+), 5 deletions(-) diff --git a/lua/helm-ls/conceal.lua b/lua/helm-ls/conceal.lua index 2d596ff..2f90b11 100644 --- a/lua/helm-ls/conceal.lua +++ b/lua/helm-ls/conceal.lua @@ -43,10 +43,13 @@ end -- Helper function to set an extmark with the hover result local function set_extmark(bufnr, start_row, start_col, end_row, end_col, hover_text, original_text) local conceallevel = vim.opt_local.conceallevel:get() + -- Use a stable ID based on position to avoid duplicates + local id = start_row * 10000 + start_col + 1 if conceallevel > 0 then -- Set conceal for the syntax element (new approach, handles wrapping) api.nvim_buf_set_extmark(bufnr, ns_id, start_row, start_col, { + id = id, end_row = end_row, end_col = end_col, virt_text = { { hover_text, "Conceal" } }, @@ -58,6 +61,7 @@ local function set_extmark(bufnr, start_row, start_col, end_row, end_col, hover_ hover_text = pad_text(hover_text, #original_text) api.nvim_buf_set_extmark(bufnr, ns_id, start_row, start_col, { + id = id, end_row = end_row, end_col = end_col, virt_text = { { hover_text, "Conceal" } }, @@ -76,7 +80,7 @@ local function apply_concealment(bufnr, start_row, start_col, end_row, end_col, if hover_text then set_extmark(bufnr, start_row, start_col, end_row, end_col, hover_text, original_text) else - request_hover(bufnr, end_row, end_col - 1, function(err, result, ctx, _) + request_hover(bufnr, end_row, end_col - 1, function(err, result, ctx, _) if err or not result or not result.contents then return end @@ -89,13 +93,17 @@ local function apply_concealment(bufnr, start_row, start_col, end_row, end_col, hover_cache[original_text] = hover_text - set_extmark(bufnr, start_row, start_col, end_row, end_col, hover_text, original_text) + -- Re-verify we are still on the same buffer/context if needed, + -- but for now, applying if buffer is still active + if api.nvim_buf_is_valid(bufnr) then + set_extmark(bufnr, start_row, start_col, end_row, end_col, hover_text, original_text) + end end) end end -- Main function to conceal templates with hover -local conceal_templates_with_hover = function() +local conceal_templates_with_hover = function() local bufnr = api.nvim_get_current_buf() local parser = vim.treesitter.get_parser(bufnr, vim.bo.filetype) if parser == nil then @@ -114,20 +122,24 @@ local conceal_templates_with_hover = function() local start_line = vim.fn.line("w0") - 1 local end_line = vim.fn.line("w$") - 1 + -- Clear existing extmarks in the visible range to avoid duplication + -- and handle deleted/moved templates + api.nvim_buf_clear_namespace(bufnr, ns_id, start_line, end_line + 1) + for _, match in query:iter_matches(root, bufnr, start_line, end_line, { all = true }) do for _, nodes in pairs(match) do local start_row, start_col = nodes[1]:range() local _, _, end_row, end_col = nodes[#nodes]:range() if util.is_cursor_on_line(start_row) then - return + goto continue end apply_concealment(bufnr, start_row, start_col, end_row, end_col, nodes[1]) + ::continue:: end end end - -- Function to clear extmarks on the cursor's current line local clear_extmark_if_cursor_on_line = function() local bufnr = api.nvim_get_current_buf() From 1de72109abe58e97856ba12974d1dd6f7810acc6 Mon Sep 17 00:00:00 2001 From: qvalentin Date: Fri, 16 Jan 2026 16:49:01 +0100 Subject: [PATCH 3/3] fix: format --- lua/helm-ls/conceal.lua | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/lua/helm-ls/conceal.lua b/lua/helm-ls/conceal.lua index 2f90b11..992d471 100644 --- a/lua/helm-ls/conceal.lua +++ b/lua/helm-ls/conceal.lua @@ -80,7 +80,7 @@ local function apply_concealment(bufnr, start_row, start_col, end_row, end_col, if hover_text then set_extmark(bufnr, start_row, start_col, end_row, end_col, hover_text, original_text) else - request_hover(bufnr, end_row, end_col - 1, function(err, result, ctx, _) + request_hover(bufnr, end_row, end_col - 1, function(err, result, ctx, _) if err or not result or not result.contents then return end @@ -93,7 +93,7 @@ local function apply_concealment(bufnr, start_row, start_col, end_row, end_col, hover_cache[original_text] = hover_text - -- Re-verify we are still on the same buffer/context if needed, + -- Re-verify we are still on the same buffer/context if needed, -- but for now, applying if buffer is still active if api.nvim_buf_is_valid(bufnr) then set_extmark(bufnr, start_row, start_col, end_row, end_col, hover_text, original_text) @@ -103,7 +103,7 @@ local function apply_concealment(bufnr, start_row, start_col, end_row, end_col, end -- Main function to conceal templates with hover -local conceal_templates_with_hover = function() +local conceal_templates_with_hover = function() local bufnr = api.nvim_get_current_buf() local parser = vim.treesitter.get_parser(bufnr, vim.bo.filetype) if parser == nil then @@ -122,7 +122,7 @@ local conceal_templates_with_hover = function() local start_line = vim.fn.line("w0") - 1 local end_line = vim.fn.line("w$") - 1 - -- Clear existing extmarks in the visible range to avoid duplication + -- Clear existing extmarks in the visible range to avoid duplication -- and handle deleted/moved templates api.nvim_buf_clear_namespace(bufnr, ns_id, start_line, end_line + 1)