Improve API stability
This commit is contained in:
104
README.md
104
README.md
@@ -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.
|
|
||||||
local luasnip = require'luasnip'
|
|
||||||
local cmp = require'cmp'
|
|
||||||
cmp.setup {
|
|
||||||
mapping = {
|
mapping = {
|
||||||
['<Tab>'] = cmp.mapping.mode({ 'i', 's' }, function(_, fallback)
|
['<C-p>'] = cmp.mapping.select_prev_item(),
|
||||||
if vim.fn.pumvisible() == 1 then
|
['<C-n>'] = cmp.mapping.select_next_item(),
|
||||||
vim.fn.feedkeys(vim.api.nvim_replace_termcodes('<C-n>', true, true, true), 'n')
|
['<C-d>'] = cmp.mapping.scroll_docs(-4),
|
||||||
elseif luasnip.expand_or_jumpable() then
|
['<C-f>'] = cmp.mapping.scroll_docs(4),
|
||||||
vim.fn.feedkeys(vim.api.nvim_replace_termcodes('<Plug>luasnip-expand-or-jump', true, true, true), '')
|
['<C-Space>'] = cmp.mapping.complete(),
|
||||||
else
|
['<C-e>'] = cmp.mapping.close(),
|
||||||
fallback()
|
['<CR>'] = cmp.mapping.confirm({
|
||||||
end
|
behavior = cmp.ConfirmBehavior.Replace,
|
||||||
end),
|
select = true,
|
||||||
['<S-Tab>'] = cmp.mapping.mode({ 'i', 's' }, function(_, fallback)
|
})
|
||||||
if vim.fn.pumvisible() == 1 then
|
|
||||||
vim.fn.feedkeys(vim.api.nvim_replace_termcodes('<C-p>', true, true, true), 'n')
|
|
||||||
elseif luasnip.jumpable(-1) then
|
|
||||||
vim.fn.feedkeys(vim.api.nvim_replace_termcodes('<Plug>luasnip-jump-prev', true, true, true), '')
|
|
||||||
else
|
|
||||||
fallback()
|
|
||||||
end
|
|
||||||
end)
|
|
||||||
}
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
- *cmp.mapping.select_prev_item()*
|
||||||
|
- *cmp.mapping.select_next_item()*
|
||||||
|
- *cmp.mapping.scroll_docs(number)*
|
||||||
|
- *cmp.mapping.complete()*
|
||||||
|
- *cmp.mapping.close()*
|
||||||
|
- *cmp.mapping.confirm({ select = bool, behavior = cmp.ConfirmBehavior.{Insert,Replace} })*
|
||||||
|
|
||||||
|
In addition, You can specify vim's mode to those mapping functions like this.
|
||||||
|
|
||||||
|
```lua
|
||||||
|
mapping = {
|
||||||
|
...
|
||||||
|
['<Tab>'] = cmp.mapping(cmp.mapping.select_next_item(), { 'i', 's' })
|
||||||
|
...
|
||||||
}
|
}
|
||||||
```
|
```
|
||||||
|
|
||||||
|
|||||||
@@ -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
|
||||||
|
|
||||||
|
|||||||
@@ -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)
|
||||||
|
|||||||
@@ -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)
|
|
||||||
local invoke = action_or_invoke
|
|
||||||
if type(action_or_invoke) == 'table' then
|
|
||||||
if type(action_or_invoke.invoke) == 'function' then
|
|
||||||
invoke = action_or_invoke.invoke
|
|
||||||
else
|
|
||||||
error('`invoke` must be function or result of `cmp.mapping.mode`.')
|
|
||||||
end
|
|
||||||
end
|
|
||||||
return {
|
return {
|
||||||
modes = modes,
|
invoke = function(...)
|
||||||
invoke = invoke,
|
invoke(...)
|
||||||
|
end,
|
||||||
|
modes = modes or { 'i' },
|
||||||
}
|
}
|
||||||
end
|
end,
|
||||||
|
})
|
||||||
|
---Invoke completion
|
||||||
mapping.complete = function()
|
mapping.complete = function()
|
||||||
return mapping.mode({ 'i' }, function(core)
|
return function(fallback)
|
||||||
core.complete(core.get_context({ reason = types.cmp.ContextReason.Manual }))
|
if not require('cmp').complete() then
|
||||||
end)
|
fallback()
|
||||||
|
end
|
||||||
|
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
|
||||||
|
|||||||
@@ -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
|
||||||
|
|||||||
Reference in New Issue
Block a user