Improve API stability

This commit is contained in:
hrsh7th
2021-08-24 22:43:02 +09:00
parent cbb80af6dd
commit c0fcdd9c61
5 changed files with 165 additions and 119 deletions

108
README.md
View File

@@ -1,6 +1,6 @@
# nvim-cmp # nvim-cmp
A completion plugin for neovim written in Lua. A completion plugin for neovim coded by Lua.
Status Status
@@ -9,11 +9,11 @@ Status
not yet stable but ok to use (for testing). not yet stable but ok to use (for testing).
Features Concept
==================== ====================
- Support pairs-wise plugin automatically - Support pairs-wise plugin automatically
- Fully customizable via Lua functions (WIP) - Fully customizable via Lua functions
- Fully supported LSP's Completion capabilities - Fully supported LSP's Completion capabilities
- Snippets - Snippets
- CommitCharacters - CommitCharacters
@@ -29,7 +29,7 @@ Features
Setup Setup
==================== ====================
First, You should install core and sources by your favorite plugin manager. First, You should install nvim-cmp itself and completion sources by your favorite plugin manager.
The `nvim-cmp` sources can be found in [here](https://github.com/topics/nvim-cmp). The `nvim-cmp` sources can be found in [here](https://github.com/topics/nvim-cmp).
@@ -46,20 +46,19 @@ Then setup configuration.
lua <<EOF lua <<EOF
local cmp = require('cmp') local cmp = require('cmp')
cmp.setup { cmp.setup {
-- You should change this example to your chosen snippet engine.
snippet = { snippet = {
expand = function(args) expand = function(args)
-- You must install `vim-vsnip` if you set up as same as the following. -- You must install `vim-vsnip` if you use the following as-is.
vim.fn['vsnip#anonymous'](args.body) vim.fn['vsnip#anonymous'](args.body)
end end
}, },
-- You must set mapping. -- You must set mapping if you want.
mapping = { mapping = {
['<C-p>'] = cmp.mapping.prev_item(), ['<C-p>'] = cmp.mapping.select_prev_item(),
['<C-n>'] = cmp.mapping.next_item(), ['<C-n>'] = cmp.mapping.select_next_item(),
['<C-d>'] = cmp.mapping.scroll(-4), ['<C-d>'] = cmp.mapping.scroll_docs(-4),
['<C-f>'] = cmp.mapping.scroll(4), ['<C-f>'] = cmp.mapping.scroll_docs(4),
['<C-Space>'] = cmp.mapping.complete(), ['<C-Space>'] = cmp.mapping.complete(),
['<C-e>'] = cmp.mapping.close(), ['<C-e>'] = cmp.mapping.close(),
['<CR>'] = cmp.mapping.confirm({ ['<CR>'] = cmp.mapping.confirm({
@@ -70,7 +69,7 @@ lua <<EOF
-- You should specify your *installed* sources. -- You should specify your *installed* sources.
sources = { sources = {
{ name = 'buffer' } { name = 'buffer' },
}, },
} }
EOF EOF
@@ -78,6 +77,7 @@ EOF
" Setup buffer configuration (nvim-lua source only enables in Lua filetype). " Setup buffer configuration (nvim-lua source only enables in Lua filetype).
autocmd FileType lua lua require'cmp'.setup.buffer { autocmd FileType lua lua require'cmp'.setup.buffer {
\ sources = { \ sources = {
{ name = 'buffer' },
\ { name = 'nvim_lua' }, \ { name = 'nvim_lua' },
\ }, \ },
\ } \ }
@@ -90,79 +90,61 @@ Configuration
The default configuration can be found in [here](./lua/cmp/config/default.lua) The default configuration can be found in [here](./lua/cmp/config/default.lua)
You can use your own configuration like this: You can use your own configuration like this:
```lua ```lua
local cmp = require'cmp' local cmp = require'cmp'
cmp.setup { cmp.setup {
... ...
completion = { completion = {
autocomplete = { .. }, autocomplete = { ... },
completeopt = 'menu,menuone,noselect',
keyword_pattern = [[\%(-\?\d\+\%(\.\d\+\)\?\|\h\w*\%(-\w*\)*\)]],
keyword_length = 1,
}, },
sorting = { sorting = {
priority_weight = 2., priority_weight = 2.,
comparators = { ... }, comparators = { ... },
}, },
mapping = { mapping = {
['<C-p>'] = cmp.mapping.prev_item(), ...
['<C-n>'] = cmp.mapping.next_item(),
['<C-d>'] = cmp.mapping.scroll(-4),
['<C-f>'] = cmp.mapping.scroll(4),
['<C-Space>'] = cmp.mapping.complete(),
['<C-e>'] = cmp.mapping.close(),
['<CR>'] = cmp.mapping.confirm({
behavior = cmp.ConfirmBehavior.Replace,
select = true,
})
}, },
sources = { ... }, sources = { ... },
... ...
} }
``` ```
### mapping (type: table<string, fun(core: cmp.Core, fallback: function)>) ### mapping (type: table<string, fun(fallback: function)>)
_TODO: This API is not stable yet. It can be changed with no announcement._
Define mappings with `cmp.mapping` helper. Define mappings with `cmp.mapping` helper.
The `cmp.mapping` helper has the below functions. You can use the following functions as mapping configuration like this.
- *cmp.mapping.confirm({ select = true or false, behavior = cmp.ConfirmBehavior.Insert or cmp.ConfirmBehavior.Replace })*
- *cmp.mapping.complete()*
- *cmp.mapping.close()*
- *cmp.mapping.next_item()*
- *cmp.mapping.prev_item()*
- *cmp.mapping.scroll(delta = number)*
You can use `<Tab>`and `<S-Tab>` for navigating menu.
```lua ```lua
-- This is just an example of LusSnip integration. You have to adjust it yourself. mapping = {
local luasnip = require'luasnip' ['<C-p>'] = cmp.mapping.select_prev_item(),
local cmp = require'cmp' ['<C-n>'] = cmp.mapping.select_next_item(),
cmp.setup { ['<C-d>'] = cmp.mapping.scroll_docs(-4),
mapping = { ['<C-f>'] = cmp.mapping.scroll_docs(4),
['<Tab>'] = cmp.mapping.mode({ 'i', 's' }, function(_, fallback) ['<C-Space>'] = cmp.mapping.complete(),
if vim.fn.pumvisible() == 1 then ['<C-e>'] = cmp.mapping.close(),
vim.fn.feedkeys(vim.api.nvim_replace_termcodes('<C-n>', true, true, true), 'n') ['<CR>'] = cmp.mapping.confirm({
elseif luasnip.expand_or_jumpable() then behavior = cmp.ConfirmBehavior.Replace,
vim.fn.feedkeys(vim.api.nvim_replace_termcodes('<Plug>luasnip-expand-or-jump', true, true, true), '') select = true,
else })
fallback() }
end ```
end),
['<S-Tab>'] = cmp.mapping.mode({ 'i', 's' }, function(_, fallback) - *cmp.mapping.select_prev_item()*
if vim.fn.pumvisible() == 1 then - *cmp.mapping.select_next_item()*
vim.fn.feedkeys(vim.api.nvim_replace_termcodes('<C-p>', true, true, true), 'n') - *cmp.mapping.scroll_docs(number)*
elseif luasnip.jumpable(-1) then - *cmp.mapping.complete()*
vim.fn.feedkeys(vim.api.nvim_replace_termcodes('<Plug>luasnip-jump-prev', true, true, true), '') - *cmp.mapping.close()*
else - *cmp.mapping.confirm({ select = bool, behavior = cmp.ConfirmBehavior.{Insert,Replace} })*
fallback()
end In addition, You can specify vim's mode to those mapping functions like this.
end)
} ```lua
mapping = {
...
['<Tab>'] = cmp.mapping(cmp.mapping.select_next_item(), { 'i', 's' })
...
} }
``` ```

View File

@@ -75,10 +75,11 @@ core.on_keymap = function(keys, fallback)
for key, action in pairs(config.get().mapping) do for key, action in pairs(config.get().mapping) do
if key == keys then if key == keys then
if type(action) == 'function' then if type(action) == 'function' then
return action(core, fallback) action(fallback)
else else
return action.invoke(core, fallback) action.invoke(fallback)
end end
return
end end
end end

View File

@@ -1,4 +1,5 @@
local core = require('cmp.core') local core = require('cmp.core')
local keymap = require('cmp.utils.keymap')
local source = require('cmp.source') local source = require('cmp.source')
local config = require('cmp.config') local config = require('cmp.config')
local autocmd = require('cmp.autocmd') local autocmd = require('cmp.autocmd')
@@ -31,6 +32,68 @@ cmp.unregister_source = function(id)
core.unregister_source(id) core.unregister_source(id)
end end
---Invoke completion manually
cmp.complete = function()
core.complete(core.get_context({ reason = cmp.ContextReason.Manual }))
return true
end
---Close current completion
cmp.close = function()
if vim.fn.pumvisible() == 1 then
core.reset()
return true
else
return false
end
end
---Select next item if possible
cmp.select_next_item = function()
if vim.fn.pumvisible() == 1 then
vim.fn.feedkeys(keymap.t('<C-n>'), 'n')
return true
else
return false
end
end
---Select prev item if possible
cmp.select_prev_item = function()
if vim.fn.pumvisible() == 1 then
vim.fn.feedkeys(keymap.t('<C-p>'), 'n')
return true
else
return false
end
end
---Scrolling documentation window if possible
cmp.scroll_docs = function(delta)
if core.menu.float:is_visible() then
core.menu.float:scroll(delta)
return true
else
return false
end
end
---Confirm completion
cmp.confirm = function(option)
option = option or {}
local e = core.menu:get_selected_entry() or (option.select and core.menu:get_first_entry() or nil)
if e then
core.confirm(e, {
behavior = option.behavior,
}, function()
core.complete(core.get_context({ reason = cmp.ContextReason.TriggerOnly }))
end)
return true
else
return false
end
end
---@type cmp.Setup ---@type cmp.Setup
cmp.setup = setmetatable({ cmp.setup = setmetatable({
global = function(c) global = function(c)

View File

@@ -1,82 +1,70 @@
local types = require('cmp.types') local misc = require('cmp.utils.misc')
local mapping = {} local mapping = setmetatable({}, {
__call = function(_, invoke, modes)
mapping.mode = function(modes, action_or_invoke) return {
local invoke = action_or_invoke invoke = function(...)
if type(action_or_invoke) == 'table' then invoke(...)
if type(action_or_invoke.invoke) == 'function' then end,
invoke = action_or_invoke.invoke modes = modes or { 'i' },
else }
error('`invoke` must be function or result of `cmp.mapping.mode`.') end,
})
---Invoke completion
mapping.complete = function()
return function(fallback)
if not require('cmp').complete() then
fallback()
end end
end end
return {
modes = modes,
invoke = invoke,
}
end
mapping.complete = function()
return mapping.mode({ 'i' }, function(core)
core.complete(core.get_context({ reason = types.cmp.ContextReason.Manual }))
end)
end end
---Close current completion menu if it displayed.
mapping.close = function() mapping.close = function()
return mapping.mode({ 'i' }, function(core, fallback) return function(fallback)
if vim.fn.pumvisible() == 1 then if not require('cmp').close() then
core.reset()
else
fallback() fallback()
end end
end) end
end end
mapping.scroll = function(delta) ---Scroll documentation window.
return mapping.mode({ 'i' }, function(core, fallback) mapping.scroll_docs = function(delta)
if core.menu.float:is_visible() then return function(fallback)
core.menu.float:scroll(delta) if not require('cmp').scroll_docs(delta) then
else
fallback() fallback()
end end
end) end
end end
mapping.scroll = misc.deprecated(mapping.scroll_docs, '`cmp.mapping.scroll` is deprecated. Please change it to `cmp.mapping.scroll_docs` instead.')
mapping.next_item = function() ---Select next completion item.
return mapping.mode({ 'i' }, function(_, fallback) mapping.select_next_item = function()
if vim.fn.pumvisible() == 1 then return function(fallback)
vim.fn.feedkeys(vim.api.nvim_replace_termcodes('<C-n>', true, true, true), 'n') if not require('cmp').select_next_item() then
else
fallback() fallback()
end end
end) end
end end
mapping.next_item = misc.deprecated(mapping.select_next_item, '`cmp.mapping.next_item` is deprecated. Please change it to `cmp.mapping.select_next_item` instead.')
mapping.prev_item = function() ---Select prev completion item.
return mapping.mode({ 'i' }, function(_, fallback) mapping.select_prev_item = function()
if vim.fn.pumvisible() == 1 then return function(fallback)
vim.fn.feedkeys(vim.api.nvim_replace_termcodes('<C-p>', true, true, true), 'n') if not require('cmp').select_prev_item() then
else
fallback() fallback()
end end
end) end
end end
mapping.prev_item = misc.deprecated(mapping.select_prev_item, '`cmp.mapping.prev_item` is deprecated. Please change it to `cmp.mapping.select_prev_item` instead.')
---Confirm selection
mapping.confirm = function(option) mapping.confirm = function(option)
option = option or {} return function(fallback)
return mapping.mode({ 'i' }, function(core, fallback) if not require('cmp').confirm(option) then
local e = core.menu:get_selected_entry() or (option.select and core.menu:get_first_entry() or nil)
if e then
core.confirm(e, {
behavior = option.behavior,
}, function()
core.complete(core.get_context({ reason = types.cmp.ContextReason.TriggerOnly }))
end)
else
fallback() fallback()
end end
end) end
end end
return mapping return mapping

View File

@@ -136,4 +136,16 @@ misc.to_vimindex = function(text, utfindex)
return utfindex + 1 return utfindex + 1
end end
---Mark the function as deprecated
misc.deprecated = function(fn, msg)
local printed = false
return function(...)
if not printed then
print(msg)
printed = true
end
return fn(...)
end
end
return misc return misc