Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
9 changes: 8 additions & 1 deletion lua/blink/cmp/completion/accept/init.lua
Original file line number Diff line number Diff line change
Expand Up @@ -58,6 +58,12 @@ local function apply_item(ctx, item)
temp_text_edit.newText = ''
text_edits_lib.apply(temp_text_edit, all_text_edits)

-- Because we resolve the item asynchronously, we must ensure we're still in insert mode
-- and set the cursor back to its position before accepting the item, since the snippet expansion
-- inserts at the cursor position
if not vim.api.nvim_get_mode().mode:match('i') then vim.cmd('startinsert') end
vim.api.nvim_win_set_cursor(0, { ctx.cursor[1], ctx.cursor[2] })
Copy link

@geril07 geril07 Jul 29, 2025

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

does not behave well if used after 59 row text_edits_lib.apply(temp_text_edit, all_text_edits) in some cases

output.mp4


-- Expand the snippet
require('blink.cmp.config').snippets.expand(item.textEdit.newText)

Expand Down Expand Up @@ -105,8 +111,9 @@ local function accept(ctx, item, callback)
:catch(function() return item end)
:map(function(resolved_item)
-- Updates the text edit based on the cursor position and converts it to utf-8
-- Use the cursor at the time of resolving the item, since it may have moved since then
resolved_item = vim.deepcopy(resolved_item)
resolved_item.textEdit = text_edits_lib.get_from_item(resolved_item)
resolved_item.textEdit = text_edits_lib.get_from_item(resolved_item, ctx.cursor)

return sources.execute(
ctx,
Expand Down
23 changes: 12 additions & 11 deletions lua/blink/cmp/lib/text_edits.lua
Original file line number Diff line number Diff line change
Expand Up @@ -130,12 +130,14 @@ end
--- Gets the text edit from an item, handling insert/replace ranges and converts
--- offset encodings (utf-16 | utf-32) to utf-8
--- @param item blink.cmp.CompletionItem
--- @param cursor? [number, number]
--- @return lsp.TextEdit
function text_edits.get_from_item(item)
function text_edits.get_from_item(item, cursor)
cursor = cursor or context.get_cursor()
local text_edit = vim.deepcopy(item.textEdit)

-- Guess the text edit if the item doesn't define it
if text_edit == nil then return text_edits.guess(item) end
if text_edit == nil then return text_edits.guess(item, cursor) end

-- FIXME: temporarily convert insertReplaceEdit to regular textEdit
if text_edit.range == nil then
Expand All @@ -150,7 +152,7 @@ function text_edits.get_from_item(item)
--- @cast text_edit lsp.TextEdit

local offset_encoding = text_edits.offset_encoding_from_item(item)
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, item.cursor_column, cursor[2])

-- 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
Expand Down Expand Up @@ -194,17 +196,14 @@ end

--- Uses the keyword_regex to guess the text edit ranges
--- @param item blink.cmp.CompletionItem
--- @param cursor [number, number]
--- TODO: doesnt work when the item contains characters not included in the context regex
function text_edits.guess(item)
function text_edits.guess(item, cursor)
local word = item.insertText or item.label

local start_col, end_col = require('blink.cmp.fuzzy').guess_edit_range(
item,
context.get_line(),
context.get_cursor()[2],
config.completion.keyword.range
)
local current_line = context.get_cursor()[1]
local start_col, end_col =
require('blink.cmp.fuzzy').guess_edit_range(item, context.get_line(), cursor[2], config.completion.keyword.range)
local current_line = cursor[1]

-- convert to 0-index
return {
Expand Down Expand Up @@ -344,6 +343,8 @@ end
--- https://github.com/neovim/neovim/issues/19806#issuecomment-2365146298
--- @param text_edit lsp.TextEdit
function text_edits.write_to_dot_repeat(text_edit)
if not vim.api.nvim_get_mode().mode:match('i') then return end

local chars_to_delete = #table.concat(
vim.api.nvim_buf_get_text(
0,
Expand Down
Loading