-
Notifications
You must be signed in to change notification settings - Fork 0
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
Beta testing 'mini.snippets': expanding snippets from lsp using 'nvim-cmp' or 'blink.cmp' #9
Comments
This sounds plausible. |
The problem exists on gopls too. Snippets contains a textedits will do. iShot_2025-01-12_20.49.49.mp4 |
@xzbdmw, thank you for this comment. Especially for this line:
Now that I understand a bit more, I am able to succesfully complete Demo: mini_snippets_lsp_expansion_ctrl_o.mp4@echasnovski, regarding your comment:
That would also need a change in I wonder, would it be possible to hook into the expected
Perhaps: --- cmp opts
...
local snippet = { -- configure mini.snippets to handle `lsp snippets`
expand = function(args)
local insert = MiniSnippets.config.expand.insert or MiniSnippets.default_insert
insert({ body = args.body }) -- TODO: Add a flag "from_lsp" ?
end,
}
... |
'mini.snippets' does nothing wrong here: it "immediately" (i.e. in the same event loop, not through |
Just to be sure: I did not say that As a POC, and perhaps whilst waiting for the issue in -- ...
local mini_snippets = require("mini.snippets")
mini_snippets.setup({
snippets = { -- just one dummy custom snippet:
{ prefix = "dummy", body = "T1=fu$1", desc = "fu before $1" },
},
expand = {
insert = function(snippet)
-- NOTE: Prevent any completion directly after snippet insert:
if use_cmp then require("cmp.config").set_onetime({ sources = {} }) end
MiniSnippets.default_insert(snippet)
end,
},
})
if use_cmp then
add_cmp()
-- NOTE: Make sure the config prevents any completion directly after snippet insert
-- NOTE: Now, it's possible to prevent outdated completion items:
local cmp = require("cmp")
vim.api.nvim_create_autocmd("User", {
group = vim.api.nvim_create_augroup("mini_snippets_cmp_nvim_lsp", { clear = true }),
pattern = "CmpRegisterSource",
callback = function(ev)
local id = ev.data.source_id
for _, source in ipairs(cmp.get_registered_sources()) do
if source.id == id and source.name == "nvim_lsp" then
local cmp_nvim_lsp = source.source
local org_complete = cmp_nvim_lsp.complete
cmp_nvim_lsp.complete = vim.schedule_wrap(org_complete)
end
end
end,
})
else
add_blink()
end
-- ...
I also tested the |
Right 👍 you may consider #6 (comment) to conditionally schedule. |
I managed to address all local function add_cmp()
local group = vim.api.nvim_create_augroup("mini_snippets_nvim_cmp", { clear = true })
-- NOTE: Firstly, make sure that nvim-cmp never provides completion items directly after snippet insert
-- See https://github.com/abeldekat/cmp-mini-snippets/issues/6
vim.api.nvim_create_autocmd("User", {
group = group,
pattern = "MiniSnippetsSessionStart",
callback = function(_) require("cmp.config").set_onetime({ sources = {} }) end,
})
-- HACK: Secondly, it's now possible to prevent outdated completion items
-- See https://github.com/hrsh7th/nvim-cmp/issues/2119
local function make_complete_override(complete_fn)
return function(self, params, callback)
local override_fn = complete_fn
-- TODO: Perhaps mini.snippets can expose a public function to allow the wrapping to be more specific,
-- according to the example @xzbdmw provided in this comment:
-- https://github.com/abeldekat/cmp-mini-snippets/issues/6#issuecomment-2583446264
--
if MiniSnippets.session.get(false) ~= nil then override_fn = vim.schedule_wrap(override_fn) end
override_fn(self, params, callback)
end
end
local function find_cmp_nvim_lsp(id)
for _, source in ipairs(require("cmp").get_registered_sources()) do
if source.id == id and source.name == "nvim_lsp" then return source.source end
end
end
vim.api.nvim_create_autocmd("User", {
group = group,
pattern = "CmpRegisterSource",
callback = function(ev)
local cmp_nvim_lsp = find_cmp_nvim_lsp(ev.data.source_id)
if cmp_nvim_lsp then
local org_complete = cmp_nvim_lsp.complete
cmp_nvim_lsp.complete = make_complete_override(org_complete)
end
end,
})
MiniDeps.add({ source = "hrsh7th/nvim-cmp", depends = { "hrsh7th/cmp-nvim-lsp", "hrsh7th/cmp-buffer" } })
local cmp = require("cmp")
require("cmp").setup({
snippet = {
expand = function(args) -- use mini.snippets to expand snippets from lsp
local insert = MiniSnippets.config.expand.insert or MiniSnippets.default_insert
insert({ body = args.body }) -- Insert at cursor
end,
},
sources = cmp.config.sources({ { name = "nvim_lsp" }, { name = "buffer" } }),
mapping = cmp.mapping.preset.insert(),
completion = { completeopt = "menu,menuone,noinsert" },
})
end
I think we need:
@echasnovski, would you like to create these PR's yourself? |
If I wanted to play with other completion engines (with or without 'mini.snippets' integration), I'd already done it :) I'd rather add 'mini.snippets' support for 'mini.completion' (which is in the process). It is totally fine for you to do this. One thing I have in mind is that if it is indeed the issue of 'nvim-cmp'/'blink.cmp' making text edits on the outdated buffer, then this is a more general problem than 'mini.snippets' removing placeholder while in the Insert mode. In theory, a slow language server can take significant amount of time to process the request. If the text edit is done by the server, then user can have already updated buffer manually by the time text edit is executed. Maybe cancelling text edit or some kind of undo/repo can be a better idea. |
I agree, there are some sort of “smart” treatment of TextEdits based on context (diff between when requests sending and when user really confirming) on both nvim-cmp and blink, they failed to do smartly in this case. Given that nvim-cmp author is rather inactive to issues, maybe blink author has more insight. |
@echasnovski, for the past few days, I have been thinking really hard about this issue. So, I hope you'll allow me another suggestion.
Would that happen when a user manually updates the buffer? I experimented a bit having the explanation of @xzbdmw in mind:
Other snippet engines avoid the manual removal of the placeholder via As suggested, we can PR Alternatively, we can solve the issue in the config of local function add_cmp()
local group = vim.api.nvim_create_augroup("mini_snippets_nvim_cmp", { clear = true })
-- NOTE: First, make sure that nvim-cmp never provides completion items directly after snippet insert
-- See https://github.com/abeldekat/cmp-mini-snippets/issues/6
vim.api.nvim_create_autocmd("User", {
group = group,
pattern = "MiniSnippetsSessionStart",
callback = function(_) require("cmp.config").set_onetime({ sources = {} }) end,
})
-- NOTE: Second, it's now possible to prevent outdated completion items
-- See https://github.com/hrsh7th/nvim-cmp/issues/2119
-- TODO: after line 2264, the call to nodes_del, add to mini.snippets
-- H.trigger_event('MiniSnippetsSessionPlaceholderDel', {})
vim.api.nvim_create_autocmd("User", {
group = group,
pattern = "MiniSnippetsSessionPlaceholderDel",
callback = function(_)
local core = require("cmp").core
local ctx = core.context -- the context is good, keep it
core:reset()
core:complete(ctx) -- now we have the up-to-date completion items!
end,
})
MiniDeps.add({ source = "hrsh7th/nvim-cmp", depends = { "hrsh7th/cmp-nvim-lsp", "hrsh7th/cmp-buffer" } })
local cmp = require("cmp")
require("cmp").setup({
snippet = {
expand = function(args) -- use mini.snippets to expand snippets from lsp
local insert = MiniSnippets.config.expand.insert or MiniSnippets.default_insert
insert({ body = args.body }) -- Insert at cursor
end,
},
sources = cmp.config.sources({ { name = "nvim_lsp" }, { name = "buffer" } }),
mapping = cmp.mapping.preset.insert(),
completion = { completeopt = "menu,menuone,noinsert" },
})
end These instructions specific to |
'nvim-cmp' is notified of this change via separate 'TextChangedI' which is triggered during removing placeholder in 'mini.snippets' (I just checked). And this is a more general approach as 'TextChangedI' can be triggered by any source (plugin or user action) and is an indication that buffer text has changed (thus in theory making any pending text edit outdated). |
I submitted the PR |
If it solves the 'mini.snippets' issue, then that one-line fix does look to me like a good solution indeed. |
Two lines...) I forgot the piece of code that prevents completion popup directly after snippet expand. I am closing this issue. |
FYI: |
The |
mini_snippets_lsp_expansion.mp4
@echasnovski,
I created this issue in order to avoid too much "noice" inside the actual beta testing issue, although the problem is not related to this
cmp-mini-snippets
repository.Functions with parameters annotated with
fun()
fail to expand correctly when the user chooses thefun
completion item from lua_ls.Apart from
vim.schedule
, there are potentially lots of cases where the expansion is not correct.Happens with both blink and nvim-cmp.
Related:
nvim-cmp
Test setup:
See reproduction in
mini.nvim
Test config:
init.lua
Test scenarios:
I could "duplicate" the
nvim-cmp
issue toblink.cmp
.In this comment, and in this comment, @xzbdmw provides an explanation.
I also tried to find an explanation as to why this happens, and did not succeed yet. What is difference between the handling of those
lsp snippets
inluasnip
andmini.snippets
?If this is not an issue in
mini.snippets
, I think the "why" would be very helpful for the maintainers ofnvim-cmp
andblink.cmp
.The text was updated successfully, but these errors were encountered: