Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Incorrect cursor position for multi-line completion items #1123

Closed
2 tasks done
milanglacier opened this issue Jan 31, 2025 · 3 comments
Closed
2 tasks done

Incorrect cursor position for multi-line completion items #1123

milanglacier opened this issue Jan 31, 2025 · 3 comments
Labels
bug Something isn't working

Comments

@milanglacier
Copy link
Contributor

milanglacier commented Jan 31, 2025

Make sure you have done the following

  • Updated to the latest version of blink.cmp
  • Searched for existing issues and documentation (try <C-k> on https://cmp.saghen.dev)

Bug Description

For a completion item with multi-line insertText, when accept the completion, the cursor will put at the end of first line not the end of the last line.

Below is two screenshots of the behavior:

  1. During completion
Image
  1. After accepting completion
Image

Cursor should be at the end of last line, not the first line.

Relevant configuration

Below is a minimal source for blink to replicate this problem:



-- hello.lua
local M = {}

function M:enabled()
    return true
end

function M.new()
    local source = setmetatable({}, { __index = M })
    return source
end

function M:get_completions(_, callback)
    local data = {
        'hello\nworld\n!\n2',
    }

    local items = {}
    for _, result in ipairs(data) do
        table.insert(items, {
            label = 'hello',
            insertText = result,
            documentation = {
                kind = 'markdown',
                value = '' .. (vim.bo.ft or '') .. '\n' .. result .. '\n',
            },
            kind = vim.lsp.protocol.CompletionItemKind.Text,
        })
    end
    callback {
        is_incomplete_forward = false,
        is_incomplete_backward = false,
        items = items,
    }
end

return M

And this is my neovim config:

            require('blink-cmp').setup {

                keymap = {
                    preset = 'super-tab',
                },

                completion = {
                    accept = {
                        auto_brackets = {
                            enabled = false,
                        },
                    },
                    list = {
                        selection = {
                            preselect = true,
                            auto_insert = true,
                        },
                    },
                    documentation = {
                        window = {
                            border = 'rounded',
                        },
                        auto_show = true,
                        auto_show_delay_ms = 200,
                    },
                    menu = {
                        border = 'rounded',
                        draw = {
                            treesitter = { 'lsp' },
                            columns = { { 'label', 'label_description', gap = 1 }, { 'kind_icon', 'kind' } },
                        },
                    },

                    trigger = { prefetch_on_insert = false },
                },

                sources = {
                    default = { 'hello' },
                        hello = {
                            name = 'hello',
                            module = 'hello',
                            score_offset = 1000,
                        },
                    },
                },

                appearance = {
                    use_nvim_cmp_as_default = true,
                    nerd_font_variant = 'normal',
                    kind_icons = require('plugins.completion').kind_icons,
                },
            }
        end,
    },
}

return M

neovim version

0.10.3

blink.cmp version

0.11

@milanglacier milanglacier added the bug Something isn't working label Jan 31, 2025
@Saghen
Copy link
Owner

Saghen commented Jan 31, 2025

@milanglacier
Copy link
Contributor Author

milanglacier commented Jan 31, 2025

After reading the file suggested in your file, I realized that currently blink-cmp hard-coded the way to set the cursor position at current line, so there would be no way for a plugin developer that provides a source to change this behavior. So we have to change the source code from blink to fix it.

Below is a simple patch that can partially work (should be better than now), but there must be a lot of scenarios that are not considered, like how we should properly compute the cursor column position in a multi-line scenario.

For single line completion item, it is the same as what it is now, and for multi-line completion item, it will likely set the cursor to the end of last line. In general, for multi-line completion (typically AI source), it typically ends up in the end of last lime. That's why I said it should be better than now.

Let me know if this simple approach would work for you. I can submit a PR for this if you want.

diff --git a/lua/blink/cmp/completion/accept/init.lua b/lua/blink/cmp/completion/accept/init.lua
index db8e288..9263196 100644
--- a/lua/blink/cmp/completion/accept/init.lua
+++ b/lua/blink/cmp/completion/accept/init.lua
@@ -86,7 +86,8 @@ local function accept(ctx, item, callback)
         table.insert(all_text_edits, item.textEdit)
         text_edits_lib.apply(all_text_edits)
         -- TODO: should move the cursor only by the offset since text edit handles everything else?
-        ctx.set_cursor({ ctx.get_cursor()[1], item.textEdit.range.start.character + #item.textEdit.newText + offset })
+        local _, n_newlines = string.gsub(item.textEdit.newText, "\n", "")
+        ctx.set_cursor({ ctx.get_cursor()[1] + n_newlines, item.textEdit.range.start.character + #item.textEdit.newText + offset })
       end
 
       -- Let the source execute the item itself

milanglacier added a commit to milanglacier/minuet-ai.nvim that referenced this issue Jan 31, 2025
Replace snippet-based insertion with plain text insertion for completion
items generated by LLMs. Since LLM responses already contain appropriate
indentation, using VSCode's snippet-style insertion mechanism would
cause unwanted double-indentation, as the editor attempts to apply
indentation rules on top of the pre-formatted text.

Note: Currently, plain text insertion has a known upstream issue where
the cursor position is incorrectly set to the end of the first suggested
line instead of the last line.

See Saghen/blink.cmp#1123 for progress.
milanglacier added a commit to milanglacier/minuet-ai.nvim that referenced this issue Jan 31, 2025
Replace snippet-based insertion with plain text insertion for completion
items generated by LLMs. Since LLM responses already contain appropriate
indentation, using VSCode's snippet-style insertion mechanism would
cause unwanted double-indentation, as the editor attempts to apply
indentation rules on top of the pre-formatted text.

Note: Currently, plain text insertion has a known upstream issue where
the cursor position is incorrectly set to the end of the first suggested
line instead of the last line.

See Saghen/blink.cmp#1123 for progress.
@Saghen Saghen closed this as completed in ac2ecd6 Feb 1, 2025
@Saghen
Copy link
Owner

Saghen commented Feb 1, 2025

Thanks for the patch! That helped me realize I should've been looking at the newText, not the .end range. Lmk if you're still running into issues

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
bug Something isn't working
Projects
None yet
Development

No branches or pull requests

2 participants