Description
mini_snippets_lsp_expansion.mp4
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 the fun
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:
Test setup:
See reproduction in mini.nvim
Test config:
init.lua
-- Clone latest 'mini.nvim' (requires Git CLI installed)
vim.cmd('echo "Installing `mini.nvim`" | redraw')
local mini_path = vim.fn.stdpath("data") .. "/site/pack/deps/start/mini.nvim"
local clone_cmd = { "git", "clone", "--depth=1", "https://github.com/echasnovski/mini.nvim", mini_path }
vim.fn.system(clone_cmd)
vim.cmd('echo "`mini.nvim` is installed" | redraw')
-- Make sure 'mini.nvim' is available
vim.cmd("packadd mini.nvim")
require("mini.deps").setup()
-- Add extra setup steps needed to reproduce the behavior
-- Use `MiniDeps.add('user/repo')` to install another plugin from GitHub
local use_cmp = true -- switch between blink and cmp
local function add_cmp()
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
local function add_blink()
local function build_blink(params)
vim.notify("Building blink.cmp", vim.log.levels.INFO)
local obj = vim.system({ "cargo", "build", "--release" }, { cwd = params.path }):wait()
if obj.code == 0 then
vim.notify("Building blink.cmp done", vim.log.levels.INFO)
else
vim.notify("Building blink.cmp failed", vim.log.levels.ERROR)
end
end
MiniDeps.add({
source = "saghen/blink.cmp", -- no friendly snippets, just lsp expand with mini.snippets
hooks = { post_install = build_blink, post_checkout = build_blink },
})
require("blink.cmp").setup({
sources = { default = { "lsp", "path", "buffer" }, cmdline = {} }, -- no cmdline, no snippets
snippets = {
expand = function(snippet)
local insert = MiniSnippets.config.expand.insert or MiniSnippets.default_insert
insert({ body = snippet })
end,
active = function(_) return MiniSnippets.session.get(false) ~= nil end,
jump = function(direction) MiniSnippets.session.jump(direction == -1 and "prev" or "next") end,
},
keymap = { preset = "default" },
completion = { documentation = { auto_show = true } },
})
end
require("mini.notify").setup() -- lsp loading indicator
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" },
}
})
if use_cmp then
add_cmp()
else
add_blink()
end
MiniDeps.add("williamboman/mason.nvim")
MiniDeps.add("williamboman/mason-lspconfig.nvim")
MiniDeps.add("neovim/nvim-lspconfig")
require("mason").setup()
require("mason-lspconfig").setup({ ensure_installed = { "lua_ls" } })
local capabilities = vim.lsp.protocol.make_client_capabilities()
if use_cmp then
capabilities = vim.tbl_deep_extend("force", capabilities, require("cmp_nvim_lsp").default_capabilities() or {})
else
capabilities = vim.tbl_deep_extend("force", {}, require("blink.cmp").get_lsp_capabilities(capabilities))
end
require("lspconfig").lua_ls.setup({
capabilities = capabilities,
settings = {
Lua = {
runtime = { version = "LuaJIT" },
completion = { callSnippet = "Replace" },
workspace = {
checkThirdParty = false,
library = { vim.env.VIMRUNTIME },
},
},
},
})
Test scenarios:
--[[
TLDR: functions with parameters annotated with `fun()`
fail to expand correctly when the user choses the `fun` completion item from lua_ls
Happens with both blink and nvim-cmp
-- NOTE: Mini.snippets is setup to only provide lsp expansion. No completion sources.
-- NOTE: Completion keys: `<c-y> <c-n> <c-p>``
-- NOTE: In all "wrong" cases, if `callback` is not nested(press <c-c>), expansion is correct!
Conclusion: the lsp snippet `{body = "function ()\n\t$0\nend}"`, having kind "function", is not wrong itself
-- NOTE: After initial snippet expand no completions should be suggested.
However, `nvim-cmp` does suggest completion items, but **only** after
expansion of `demo4` and `demo5`, where callback is the first parameter.
--]]
-- cmp: correct
-- blink: correct
---@param message string
---@param callback function
local function demo1(message, callback) end
-- cmp: correct
-- blink: correct
---@param callback function
---@param message string
local function demo2(callback, message) end
-- cmp: select "fun" completion item -> wrong
-- blink: select "fun" completion item -> wrong
---@param message string
---@param callback fun() -- Two lsp snippet completions: fun and function
local function demo3(message, callback) end
-- cmp: select "fun" completion item -> correct!
-- blink: select "fun" completion item -> wrong
---@param callback fun() -- Two lsp snippet completions: fun and function
---@param message string
local function demo4(callback, message) end
-- cmp: select "fun" completion item -> wrong
-- blink: select "fun" completion item -> wrong
---@param callback fun() -- Two lsp snippet completions: fun and function
local function demo5(callback) end
I could "duplicate" the nvim-cmp
issue to blink.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
in luasnip
and mini.snippets
?
If this is not an issue in mini.snippets
, I think the "why" would be very helpful for the maintainers of nvim-cmp
and blink.cmp
.