From a2b6040bbdd276f85eee4e5da83ed8370166cd21 Mon Sep 17 00:00:00 2001 From: Soifou Date: Fri, 11 Jul 2025 17:53:51 +0200 Subject: [PATCH] fix: use edit range start for compensation instead of old cursor Using the edit's start as the reference keeps the range correct even if completions are refreshed or the cursor moves. Closes #1736 Closes #1978 --- lua/blink/cmp/completion/list.lua | 4 +--- lua/blink/cmp/lib/text_edits.lua | 27 ++++++++++++++++++--------- 2 files changed, 19 insertions(+), 12 deletions(-) diff --git a/lua/blink/cmp/completion/list.lua b/lua/blink/cmp/completion/list.lua index c38ac3ccb..966e17a28 100644 --- a/lua/blink/cmp/completion/list.lua +++ b/lua/blink/cmp/completion/list.lua @@ -238,9 +238,7 @@ function list.undo_preview() -- The text edit may be out of date due to the user typing more characters -- so we adjust the range to compensate - local old_cursor_col = list.preview_undo.cursor_after[2] - local new_cursor_col = context.get_cursor()[2] - text_edit = text_edits_lib.compensate_for_cursor_movement(text_edit, old_cursor_col, new_cursor_col) + text_edit = text_edits_lib.compensate_for_cursor_movement(text_edit) require('blink.cmp.lib.text_edits').apply(text_edit) if list.preview_undo.cursor_before ~= nil then diff --git a/lua/blink/cmp/lib/text_edits.lua b/lua/blink/cmp/lib/text_edits.lua index 1adcc30b0..61a032c66 100644 --- a/lua/blink/cmp/lib/text_edits.lua +++ b/lua/blink/cmp/lib/text_edits.lua @@ -149,7 +149,7 @@ function text_edits.get_from_item(item) text_edit.replace = nil --- @cast text_edit lsp.TextEdit - text_edit = text_edits.compensate_for_cursor_movement(text_edit, item.cursor_column, context.get_cursor()[2]) + text_edit = text_edits.compensate_for_cursor_movement(text_edit) -- convert the offset encoding to utf-8 -- TODO: we have to do this last because it applies a max on the position based on the length of the line @@ -162,18 +162,27 @@ function text_edits.get_from_item(item) return text_edit end ---- Adjust the position of the text edit to be the current cursor position ---- since the data might be outdated. We compare the cursor column position ---- from when the items were fetched versus the current. +--- Adjust the position of the text edit to match the current cursor position. +--- This is necessary because the user may have typed additional characters +--- after the completion items were fetched, or because new completion items +--- may have been fetched after typing a trigger character. --- HACK: is there a better way? --- TODO: take into account the offset_encoding --- @param text_edit lsp.TextEdit ---- @param old_cursor_col number Position of the cursor when the text edit was created ---- @param new_cursor_col number New position of the cursor -function text_edits.compensate_for_cursor_movement(text_edit, old_cursor_col, new_cursor_col) +function text_edits.compensate_for_cursor_movement(text_edit) text_edit = vim.deepcopy(text_edit) - local offset = new_cursor_col - old_cursor_col - text_edit.range['end'].character = text_edit.range['end'].character + offset + + local orig_end = text_edit.range['end'].character + local new_cursor_col = context.get_cursor()[2] + + -- Only adjust if the cursor moved past the original end of the edit range. + -- This ensures the edit range expands to include all newly typed characters, + -- so completion replaces exactly what the user has typed. + if new_cursor_col ~= orig_end then + local end_offset = new_cursor_col - text_edit.range.start.character + text_edit.range['end'].character = orig_end + end_offset + end + return text_edit end