Expose nvim-cmp preselect option
⚠️ Please verify that this feature request has NOT been suggested before.
- [x] I checked and didn't find a similar feature request
🏷️ Feature Type
API Additions
🔖 Feature description
Edit: Later found that it was more to do with the cmd.mapping.complete() function mentioned in https://github.com/NotAShelf/nvf/issues/670#issuecomment-2708471460
I've been trying to use NeoVim with autocomplete but the behavior I have always preferred is to not autoselect a completion until the tab key is pressed. That way if I type something quickly that ends in Enter, it does not insert a completion.
My old config had lsp-zero (which seems deprecated)
local lsp = require('lsp-zero')
...
lsp.setup_nvim_cmp({
preselect = 'none',
completion = {
completeopt = 'menu,menuone,noinsert,noselect'
},
})
I believe the correct way to do this without lsp-zero is
local cmp = require'cmp'
cmp.setup{
...
preselect = cmp.PreselectMode.None
}
I found this here: https://www.devonmorris.dev/posts/nvim-cmp-preselect/
I tried the following with NVF:
settings = {
vim = {
autocomplete.nvim-cmp = {
enable = true;
setupOpts = {
completion.completeopt = "menu,menuone,noinsert,noselect";
preselect = "none";
};
};
Somehow this still doesn't work. The completion menu is visible but no item is selected, pressing enter selects the first item instead of inserting a newline. Somehow the preselect option is not taking effect. Am I doing something wrong? Could this just be exposed via NVF to make this a little easier?
✔️ Solution
I would like an option to configure the preslect behaviour of nvim-cmp
❓ Alternatives
If this can be done without exposing a nix option, I'd be happy with that solution too.
📝 Additional Context
No response
setupOpts is a special submodule that converts everything passed to Lua as much as it can. String literals, as they are string literals, get converted to Lua strings- which is actually not the same as Lua functions.
Use lib.generators.mkLuaInline for anything that you need interpreted as "raw" Lua. For example, preselect = lib.generators.mkLuaInline "cmp.PreselectMode.None";
I understand that this is a little confusing, I'll add a type-checked option for future reference when I get back to my system.
@NotAShelf That is useful info, I was wondering how that was done.
I think "none" should have worked as well because cmp.PreselectMode.None seems to evaluate to "none" when I print it out with
:lua cmp = require("cmp"); vim.print(cmp.PreselectMode.None)
However, neither seems to work correctly. I can try debug this on my own, do you have any suggestions for debugging? I tried to copy the generated config to a temporary dir and then make modifications to it, then run it with vim -u /path/to/new/config.lua but I get other errors about the plugin paths.
If there aren't any errors, then my guess would be on a configuration option conflict. Starting neovim with the init.lua in the store won't work because plugins are missing in the environment. Your best bet would be isolating nvim-cmp configuration, and creating a minimal standalone nvf environment to mess with nvim-cmp only.
@NotAShelf I've narrowed the issue down to the select=true of the cmp.mapping.confirm function, which seems to be hardcoded in the source. https://github.com/NotAShelf/nvf/blob/c3b9c979eec7db96a6d4a7f4e84e7492928610cd/modules/plugins/completion/nvim-cmp/config.nix#L87
The documentation from here seems to confirm this:
https://github.com/hrsh7th/nvim-cmp/blob/c27370703e798666486e3064b64d59eaf4bdc6d5/doc/cmp.txt#L264C6-L264C13
"If you didn't select any item and the option table contains select = true,
nvim-cmp will automatically select the first item."
Do you think this could become configurable?
I think we could try and make this commands configurable, but I'd like to hear @diniamo's opinion on this as I believe the mappings became what they are with his refactor.
Until then, you should be able to override them as any other setupOpts option. I believe the below configuration would work:
setupOpts = {
mappings."<CR>" = lib.generators.mkLuaInline "<your preferred action here>";
};
The mappings were copied over, just to a different place, so that select = true was always there. We could add an option under vim.autocomplete.nvim-cmp to toggle it, but that's not a very good solution. It's impossible to support every use-case, and that's why it's possible to override internals, if you want a workflow that's not aligned with what the person implementing the module thought to be the most common one.
I agree that if a user has the power to override things, there is no need to specify an option for every possible use case. I like the principal if it were just slightly better documented 😄
On to why this didn't seem to work: After I added
mappings."<CR>" = lib.generators.mkLuaInline "cmp.mapping.confirm()";
I end up with the following config, formatted for my sanity
cmp.setup({
["completion"] = {["completeopt"] = "menu,menuone,noinsert,noselect"},
["formatting"] = {["format"] = function(entry, vim_item)
vim_item.menu = ({["buffer"] = "[Buffer]",["nvim_lsp"] = "[LSP]",["path"] = "[Path]"})[entry.source.name]
return vim_item
end },
["mapping"] = {
["<C-Space>"] = cmp.mapping.complete(),
["<C-d>"] = cmp.mapping.scroll_docs(-4),
["<C-e>"] = cmp.mapping.abort(),
["<C-f>"] = cmp.mapping.scroll_docs(4),
["<CR>"] = cmp.mapping.confirm({ select = true }),
["<S-Tab>"] = cmp.mapping(function(fallback)
if cmp.visible() then
cmp.select_prev_item()
else
fallback()
end
end) ,
["<Tab>"] = cmp.mapping(function(fallback)
local has_words_before = function()
local line, col = unpack(vim.api.nvim_win_get_cursor(0))
return col ~= 0 and vim.api.nvim_buf_get_lines(0, line - 1, line, true)[1]:sub(col, col):match("%s") == nil
end
if cmp.visible() then
cmp.select_next_item()
elseif has_words_before() then
cmp.complete()
else
fallback()
end
end) },
["mappings"] = {["<CR>"] = cmp.mapping.confirm()},
Notice the mappings (with an s at the end) entry which contains my config, instead of "mapping". I assumed you had a typo in your response, so I tried mapping as well, however that results in a conflicting value
error: The option `home-manager.users.pico.programs.nvf.settings.vim.autocomplete.nvim-cmp.setupOpts.mapping."<CR>".expr' has conflicting definition values:
- In `/nix/store/k88dyinq172mhdh4rmvn780c4qapfvas-source/hm/hm_modules/editor': "cmp.mapping.confirm()"
- In `/nix/store/nryl8x9vgi6wqvad9ianwvpvlbwhr28b-source/modules/plugins/completion/nvim-cmp/config.nix': "cmp.mapping.confirm({ select = true })"
Use `lib.mkForce value` or `lib.mkDefault value` to change the priority on any of these definitions.
The error does advice that lib.mkForce can be used, so I tried that and now I finally have a working config 😁
setupOpts = {
completion.completeopt = "menu,menuone,noinsert,noselect";
preselect = lib.generators.mkLuaInline "cmp.PreselectMode.None";
mapping."<CR>" = lib.mkForce (lib.generators.mkLuaInline "cmp.mapping.confirm()");
};
Do you think it could be defined in by default with lib.mkDefault so that it's easier to override?