RFC: cmdline completion (#362)
* manual support dot-repeat * cmdwin and terminal * cmdline only * Fix * fix * Improve * Fix test * Support macro * disable cmdline for now * Simplify * fmt * consume once * Ignore = type * cmdline * fmt * Improve * update * fmt * Support incsearch * fix * Add api * Avoid cmdline completion if the native_menu enabled * fix for macro * Improve * fmt * Insert-mode only by default * Update * avoid conflict * Improve default mapping * Fix * fix * similar to native * Update * Fix README.md * Improve * Use <afile>
This commit is contained in:
43
README.md
43
README.md
@@ -44,6 +44,8 @@ call plug#begin(s:plug_dir)
|
|||||||
Plug 'neovim/nvim-lspconfig'
|
Plug 'neovim/nvim-lspconfig'
|
||||||
Plug 'hrsh7th/cmp-nvim-lsp'
|
Plug 'hrsh7th/cmp-nvim-lsp'
|
||||||
Plug 'hrsh7th/cmp-buffer'
|
Plug 'hrsh7th/cmp-buffer'
|
||||||
|
Plug 'hrsh7th/cmp-path'
|
||||||
|
Plug 'hrsh7th/cmp-cmdline'
|
||||||
Plug 'hrsh7th/nvim-cmp'
|
Plug 'hrsh7th/nvim-cmp'
|
||||||
|
|
||||||
" For vsnip users.
|
" For vsnip users.
|
||||||
@@ -80,11 +82,14 @@ lua <<EOF
|
|||||||
end,
|
end,
|
||||||
},
|
},
|
||||||
mapping = {
|
mapping = {
|
||||||
['<C-d>'] = cmp.mapping.scroll_docs(-4),
|
['<C-d>'] = cmp.mapping(cmp.mapping.scroll_docs(-4), { 'i', 'c' }),
|
||||||
['<C-f>'] = cmp.mapping.scroll_docs(4),
|
['<C-f>'] = cmp.mapping(cmp.mapping.scroll_docs(4), { 'i', 'c' }),
|
||||||
['<C-Space>'] = cmp.mapping.complete(),
|
['<C-Space>'] = cmp.mapping(cmp.mapping.complete(), { 'i', 'c' }),
|
||||||
['<C-e>'] = cmp.mapping.close(),
|
|
||||||
['<C-y>'] = cmp.config.disable, -- If you want to remove the default `<C-y>` mapping, You can specify `cmp.config.disable` value.
|
['<C-y>'] = cmp.config.disable, -- If you want to remove the default `<C-y>` mapping, You can specify `cmp.config.disable` value.
|
||||||
|
['<C-e>'] = cmp.mapping({
|
||||||
|
i = cmp.mapping.abort(),
|
||||||
|
c = cmp.mapping.close(),
|
||||||
|
}),
|
||||||
['<CR>'] = cmp.mapping.confirm({ select = true }),
|
['<CR>'] = cmp.mapping.confirm({ select = true }),
|
||||||
},
|
},
|
||||||
sources = cmp.config.sources({
|
sources = cmp.config.sources({
|
||||||
@@ -98,6 +103,22 @@ lua <<EOF
|
|||||||
})
|
})
|
||||||
})
|
})
|
||||||
|
|
||||||
|
-- Use buffer source for `/`.
|
||||||
|
cmp.setup.cmdline('/', {
|
||||||
|
sources = {
|
||||||
|
{ name = 'buffer' }
|
||||||
|
}
|
||||||
|
})
|
||||||
|
|
||||||
|
-- Use cmdline & path source for ':'.
|
||||||
|
cmp.setup.cmdline(':', {
|
||||||
|
sources = cmp.config.sources({
|
||||||
|
{ name = 'path' }
|
||||||
|
}, {
|
||||||
|
{ name = 'cmdline' }
|
||||||
|
})
|
||||||
|
})
|
||||||
|
|
||||||
-- Setup lspconfig.
|
-- Setup lspconfig.
|
||||||
local capabilities = require('cmp_nvim_lsp').update_capabilities(vim.lsp.protocol.make_client_capabilities())
|
local capabilities = require('cmp_nvim_lsp').update_capabilities(vim.lsp.protocol.make_client_capabilities())
|
||||||
require('lspconfig')[%YOUR_LSP_SERVER%].setup {
|
require('lspconfig')[%YOUR_LSP_SERVER%].setup {
|
||||||
@@ -125,6 +146,7 @@ If you want to remove an option, you can set it to `false` instead.
|
|||||||
|
|
||||||
Built in helper `cmd.mappings` are:
|
Built in helper `cmd.mappings` are:
|
||||||
|
|
||||||
|
- *cmp.mapping(...)*
|
||||||
- *cmp.mapping.select_prev_item({ cmp.SelectBehavior.{Insert,Select} } })*
|
- *cmp.mapping.select_prev_item({ cmp.SelectBehavior.{Insert,Select} } })*
|
||||||
- *cmp.mapping.select_next_item({ cmp.SelectBehavior.{Insert,Select} })*
|
- *cmp.mapping.select_next_item({ cmp.SelectBehavior.{Insert,Select} })*
|
||||||
- *cmp.mapping.scroll_docs(number)*
|
- *cmp.mapping.scroll_docs(number)*
|
||||||
@@ -133,7 +155,7 @@ Built in helper `cmd.mappings` are:
|
|||||||
- *cmp.mapping.abort()*
|
- *cmp.mapping.abort()*
|
||||||
- *cmp.mapping.confirm({ select = bool, behavior = cmp.ConfirmBehavior.{Insert,Replace} })*
|
- *cmp.mapping.confirm({ select = bool, behavior = cmp.ConfirmBehavior.{Insert,Replace} })*
|
||||||
|
|
||||||
You can configure `nvim-cmp` to use these `cmd.mappings` like this:
|
You can configure `nvim-cmp` to use these `cmd.mapping` like this:
|
||||||
|
|
||||||
```lua
|
```lua
|
||||||
mapping = {
|
mapping = {
|
||||||
@@ -162,6 +184,17 @@ mapping = {
|
|||||||
}
|
}
|
||||||
```
|
```
|
||||||
|
|
||||||
|
One more addition, the mapping mode can be specified by table key. It's useful to specify different function for each modes.
|
||||||
|
|
||||||
|
```lua
|
||||||
|
mapping = {
|
||||||
|
['<CR>'] = cmp.mapping({
|
||||||
|
i = cmp.mapping.confirm({ select = true }),
|
||||||
|
c = cmp.mapping.confirm({ select = false }),
|
||||||
|
})
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
You can specify your own custom mapping function.
|
You can specify your own custom mapping function.
|
||||||
|
|
||||||
```lua
|
```lua
|
||||||
|
|||||||
@@ -15,6 +15,9 @@ config.global = require('cmp.config.default')()
|
|||||||
---@type table<number, cmp.ConfigSchema>
|
---@type table<number, cmp.ConfigSchema>
|
||||||
config.buffers = {}
|
config.buffers = {}
|
||||||
|
|
||||||
|
---@type table<string, cmp.ConfigSchema>
|
||||||
|
config.cmdline = {}
|
||||||
|
|
||||||
---Set configuration for global.
|
---Set configuration for global.
|
||||||
---@param c cmp.ConfigSchema
|
---@param c cmp.ConfigSchema
|
||||||
config.set_global = function(c)
|
config.set_global = function(c)
|
||||||
@@ -32,14 +35,29 @@ config.set_buffer = function(c, bufnr)
|
|||||||
config.buffers[bufnr].revision = revision + 1
|
config.buffers[bufnr].revision = revision + 1
|
||||||
end
|
end
|
||||||
|
|
||||||
|
---Set configuration for cmdline
|
||||||
|
config.set_cmdline = function(c, type)
|
||||||
|
local revision = (config.cmdline[type] or {}).revision or 1
|
||||||
|
config.cmdline[type] = c
|
||||||
|
config.cmdline[type].revision = revision + 1
|
||||||
|
end
|
||||||
|
|
||||||
---@return cmp.ConfigSchema
|
---@return cmp.ConfigSchema
|
||||||
config.get = function()
|
config.get = function()
|
||||||
local global = config.global
|
local global = config.global
|
||||||
|
if api.is_cmdline_mode() then
|
||||||
|
local type = vim.fn.getcmdtype()
|
||||||
|
local cmdline = config.cmdline[type] or { revision = 1, sources = {} }
|
||||||
|
return config.cache:ensure({ 'get_cmdline', type, global.revision or 0, cmdline.revision or 0 }, function()
|
||||||
|
return misc.merge(cmdline, global)
|
||||||
|
end)
|
||||||
|
else
|
||||||
local bufnr = vim.api.nvim_get_current_buf()
|
local bufnr = vim.api.nvim_get_current_buf()
|
||||||
local buffer = config.buffers[bufnr] or { revision = 1 }
|
local buffer = config.buffers[bufnr] or { revision = 1 }
|
||||||
return config.cache:ensure({ 'get_buffer', bufnr, global.revision or 0, buffer.revision or 0 }, function()
|
return config.cache:ensure({ 'get_buffer', bufnr, global.revision or 0, buffer.revision or 0 }, function()
|
||||||
return misc.merge(buffer, global)
|
return misc.merge(buffer, global)
|
||||||
end)
|
end)
|
||||||
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
---Return cmp is enabled or not.
|
---Return cmp is enabled or not.
|
||||||
|
|||||||
@@ -81,12 +81,28 @@ return function()
|
|||||||
event = {},
|
event = {},
|
||||||
|
|
||||||
mapping = {
|
mapping = {
|
||||||
['<Down>'] = mapping(mapping.select_next_item({ behavior = types.cmp.SelectBehavior.Select }), { 'i' }),
|
['<Down>'] = mapping({
|
||||||
['<Up>'] = mapping(mapping.select_prev_item({ behavior = types.cmp.SelectBehavior.Select }), { 'i' }),
|
i = mapping.select_next_item({ behavior = types.cmp.SelectBehavior.Select }),
|
||||||
['<C-n>'] = mapping(mapping.select_next_item({ behavior = types.cmp.SelectBehavior.Insert }), { 'i' }),
|
c = function(fallback)
|
||||||
['<C-p>'] = mapping(mapping.select_prev_item({ behavior = types.cmp.SelectBehavior.Insert }), { 'i' }),
|
local cmp = require('cmp')
|
||||||
['<C-y>'] = mapping(mapping.confirm({ select = false }), { 'i' }),
|
cmp.close()
|
||||||
['<C-e>'] = mapping(mapping.abort(), { 'i' }),
|
vim.schedule(cmp.suspend())
|
||||||
|
fallback()
|
||||||
|
end,
|
||||||
|
}),
|
||||||
|
['<Up>'] = mapping({
|
||||||
|
i = mapping.select_prev_item({ behavior = types.cmp.SelectBehavior.Select }),
|
||||||
|
c = function(fallback)
|
||||||
|
local cmp = require('cmp')
|
||||||
|
cmp.close()
|
||||||
|
vim.schedule(cmp.suspend())
|
||||||
|
fallback()
|
||||||
|
end,
|
||||||
|
}),
|
||||||
|
['<C-n>'] = mapping(mapping.select_next_item({ behavior = types.cmp.SelectBehavior.Insert }), { 'i', 'c' }),
|
||||||
|
['<C-p>'] = mapping(mapping.select_prev_item({ behavior = types.cmp.SelectBehavior.Insert }), { 'i', 'c' }),
|
||||||
|
['<C-y>'] = mapping.confirm({ select = false }),
|
||||||
|
['<C-e>'] = mapping.abort(),
|
||||||
},
|
},
|
||||||
|
|
||||||
formatting = {
|
formatting = {
|
||||||
|
|||||||
@@ -1,3 +1,5 @@
|
|||||||
|
local api = require('cmp.utils.api')
|
||||||
|
|
||||||
local mapping
|
local mapping
|
||||||
mapping = setmetatable({}, {
|
mapping = setmetatable({}, {
|
||||||
__call = function(_, invoke, modes)
|
__call = function(_, invoke, modes)
|
||||||
@@ -7,9 +9,25 @@ mapping = setmetatable({}, {
|
|||||||
invoke(...)
|
invoke(...)
|
||||||
end,
|
end,
|
||||||
modes = modes or { 'i' },
|
modes = modes or { 'i' },
|
||||||
|
__type = 'mapping',
|
||||||
}
|
}
|
||||||
end
|
elseif type(invoke) == 'table' then
|
||||||
|
if invoke.__type == 'mapping' then
|
||||||
return invoke
|
return invoke
|
||||||
|
else
|
||||||
|
return mapping(function(fallback)
|
||||||
|
if api.is_insert_mode() and invoke.i then
|
||||||
|
return invoke.i(fallback)
|
||||||
|
elseif api.is_cmdline_mode() and invoke.c then
|
||||||
|
return invoke.c(fallback)
|
||||||
|
elseif api.is_select_mode() and invoke.s then
|
||||||
|
return invoke.s(fallback)
|
||||||
|
else
|
||||||
|
fallback()
|
||||||
|
end
|
||||||
|
end, vim.tbl_keys(invoke))
|
||||||
|
end
|
||||||
|
end
|
||||||
end,
|
end,
|
||||||
})
|
})
|
||||||
|
|
||||||
@@ -53,7 +71,9 @@ end
|
|||||||
mapping.select_next_item = function(option)
|
mapping.select_next_item = function(option)
|
||||||
return function(fallback)
|
return function(fallback)
|
||||||
if not require('cmp').select_next_item(option) then
|
if not require('cmp').select_next_item(option) then
|
||||||
|
local release = require('cmp').core:suspend()
|
||||||
fallback()
|
fallback()
|
||||||
|
vim.schedule(release)
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
@@ -62,7 +82,9 @@ end
|
|||||||
mapping.select_prev_item = function(option)
|
mapping.select_prev_item = function(option)
|
||||||
return function(fallback)
|
return function(fallback)
|
||||||
if not require('cmp').select_prev_item(option) then
|
if not require('cmp').select_prev_item(option) then
|
||||||
|
local release = require('cmp').core:suspend()
|
||||||
fallback()
|
fallback()
|
||||||
|
vim.schedule(release)
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|||||||
@@ -70,10 +70,6 @@ cmp.close = function()
|
|||||||
cmp.core:reset()
|
cmp.core:reset()
|
||||||
vim.schedule(release)
|
vim.schedule(release)
|
||||||
return true
|
return true
|
||||||
elseif vim.fn.pumvisible() == 1 then
|
|
||||||
vim.fn.complete(1, {})
|
|
||||||
cmp.core:reset()
|
|
||||||
return true
|
|
||||||
else
|
else
|
||||||
return false
|
return false
|
||||||
end
|
end
|
||||||
@@ -86,14 +82,16 @@ cmp.abort = function()
|
|||||||
cmp.core.view:abort()
|
cmp.core.view:abort()
|
||||||
vim.schedule(release)
|
vim.schedule(release)
|
||||||
return true
|
return true
|
||||||
elseif vim.fn.pumvisible() == 1 then
|
|
||||||
vim.api.nvim_select_popupmenu_item(-1, true, true, {})
|
|
||||||
return true
|
|
||||||
else
|
else
|
||||||
return false
|
return false
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
|
---Suspend completion.
|
||||||
|
cmp.suspend = function()
|
||||||
|
return cmp.core:suspend()
|
||||||
|
end
|
||||||
|
|
||||||
---Select next item if possible
|
---Select next item if possible
|
||||||
cmp.select_next_item = function(option)
|
cmp.select_next_item = function(option)
|
||||||
option = option or {}
|
option = option or {}
|
||||||
@@ -252,6 +250,9 @@ cmp.setup = setmetatable({
|
|||||||
buffer = function(c)
|
buffer = function(c)
|
||||||
config.set_buffer(c, vim.api.nvim_get_current_buf())
|
config.set_buffer(c, vim.api.nvim_get_current_buf())
|
||||||
end,
|
end,
|
||||||
|
cmdline = function(type, c)
|
||||||
|
config.set_cmdline(c, type)
|
||||||
|
end,
|
||||||
}, {
|
}, {
|
||||||
__call = function(self, c)
|
__call = function(self, c)
|
||||||
self.global(c)
|
self.global(c)
|
||||||
|
|||||||
@@ -267,7 +267,7 @@ source.complete = function(self, ctx, callback)
|
|||||||
completion_context = {
|
completion_context = {
|
||||||
triggerKind = types.lsp.CompletionTriggerKind.TriggerForIncompleteCompletions,
|
triggerKind = types.lsp.CompletionTriggerKind.TriggerForIncompleteCompletions,
|
||||||
}
|
}
|
||||||
elseif self.request_offset ~= offset then
|
elseif not vim.tbl_contains({ self.request_offset, self.offset }, offset) then
|
||||||
completion_context = {
|
completion_context = {
|
||||||
triggerKind = types.lsp.CompletionTriggerKind.Invoked,
|
triggerKind = types.lsp.CompletionTriggerKind.Invoked,
|
||||||
}
|
}
|
||||||
@@ -303,8 +303,7 @@ source.complete = function(self, ctx, callback)
|
|||||||
option = self:get_config().opts,
|
option = self:get_config().opts,
|
||||||
completion_context = completion_context,
|
completion_context = completion_context,
|
||||||
},
|
},
|
||||||
async.timeout(
|
self.complete_dedup(vim.schedule_wrap(misc.once(function(response)
|
||||||
self.complete_dedup(vim.schedule_wrap(function(response)
|
|
||||||
if #((response or {}).items or response or {}) > 0 then
|
if #((response or {}).items or response or {}) > 0 then
|
||||||
debug.log(self:get_debug_name(), 'retrieve', #(response.items or response))
|
debug.log(self:get_debug_name(), 'retrieve', #(response.items or response))
|
||||||
local old_offset = self.offset
|
local old_offset = self.offset
|
||||||
@@ -334,9 +333,7 @@ source.complete = function(self, ctx, callback)
|
|||||||
self.status = prev_status
|
self.status = prev_status
|
||||||
end
|
end
|
||||||
callback()
|
callback()
|
||||||
end)),
|
end)))
|
||||||
2000
|
|
||||||
)
|
|
||||||
)
|
)
|
||||||
return true
|
return true
|
||||||
end
|
end
|
||||||
|
|||||||
@@ -9,10 +9,11 @@ api.is_insert_mode = function()
|
|||||||
end
|
end
|
||||||
|
|
||||||
api.is_cmdline_mode = function()
|
api.is_cmdline_mode = function()
|
||||||
return vim.tbl_contains({
|
local is_cmdline_mode = vim.tbl_contains({
|
||||||
'c',
|
'c',
|
||||||
'cv',
|
'cv',
|
||||||
}, vim.api.nvim_get_mode().mode)
|
}, vim.api.nvim_get_mode().mode)
|
||||||
|
return is_cmdline_mode and vim.fn.getcmdtype() ~= '='
|
||||||
end
|
end
|
||||||
|
|
||||||
api.is_select_mode = function()
|
api.is_select_mode = function()
|
||||||
@@ -41,16 +42,17 @@ api.get_cursor = function()
|
|||||||
end
|
end
|
||||||
|
|
||||||
api.get_screen_cursor = function()
|
api.get_screen_cursor = function()
|
||||||
local cursor = api.get_cursor()
|
|
||||||
if api.is_cmdline_mode() then
|
if api.is_cmdline_mode() then
|
||||||
return cursor
|
return api.get_cursor()
|
||||||
end
|
end
|
||||||
|
local cursor = api.get_cursor()
|
||||||
local pos = vim.fn.screenpos(0, cursor[1], cursor[2] + 1)
|
local pos = vim.fn.screenpos(0, cursor[1], cursor[2] + 1)
|
||||||
return { pos.row, pos.col - 1 }
|
return { pos.row, pos.col - 1 }
|
||||||
end
|
end
|
||||||
|
|
||||||
api.get_cursor_before_line = function()
|
api.get_cursor_before_line = function()
|
||||||
return string.sub(api.get_current_line(), 1, api.get_cursor()[2] + 1)
|
local cursor = api.get_cursor()
|
||||||
|
return string.sub(api.get_current_line(), 1, cursor[2] + 1)
|
||||||
end
|
end
|
||||||
|
|
||||||
return api
|
return api
|
||||||
|
|||||||
@@ -64,7 +64,7 @@ keymap.to_keymap = setmetatable({
|
|||||||
|
|
||||||
---Mode safe break undo
|
---Mode safe break undo
|
||||||
keymap.undobreak = function()
|
keymap.undobreak = function()
|
||||||
if api.is_cmdline_mode() then
|
if not api.is_insert_mode() then
|
||||||
return ''
|
return ''
|
||||||
end
|
end
|
||||||
return keymap.t('<C-g>u')
|
return keymap.t('<C-g>u')
|
||||||
@@ -72,7 +72,7 @@ end
|
|||||||
|
|
||||||
---Mode safe join undo
|
---Mode safe join undo
|
||||||
keymap.undojoin = function()
|
keymap.undojoin = function()
|
||||||
if api.is_cmdline_mode() then
|
if not api.is_insert_mode() then
|
||||||
return ''
|
return ''
|
||||||
end
|
end
|
||||||
return keymap.t('<C-g>U')
|
return keymap.t('<C-g>U')
|
||||||
@@ -244,12 +244,12 @@ keymap.listen = setmetatable({
|
|||||||
misc.set(_G, { 'cmp', 'utils', 'keymap', 'listen', 'run' }, function(id)
|
misc.set(_G, { 'cmp', 'utils', 'keymap', 'listen', 'run' }, function(id)
|
||||||
local definition = keymap.listen.cache:get({ 'definition', id })
|
local definition = keymap.listen.cache:get({ 'definition', id })
|
||||||
if definition.mode == 'c' and vim.fn.getcmdtype() == '=' then
|
if definition.mode == 'c' and vim.fn.getcmdtype() == '=' then
|
||||||
return vim.api.nvim_feedkeys(keymap.t(definition.fallback), 'it', true)
|
return vim.api.nvim_feedkeys(keymap.t(definition.fallback.keys), definition.fallback.mode, true)
|
||||||
end
|
end
|
||||||
definition.callback(
|
definition.callback(
|
||||||
definition.keys,
|
definition.keys,
|
||||||
misc.once(function()
|
misc.once(function()
|
||||||
vim.api.nvim_feedkeys(keymap.t(definition.fallback), 'it', true)
|
vim.api.nvim_feedkeys(keymap.t(definition.fallback.keys), definition.fallback.mode, true)
|
||||||
end)
|
end)
|
||||||
)
|
)
|
||||||
return keymap.t('<Ignore>')
|
return keymap.t('<Ignore>')
|
||||||
@@ -258,18 +258,27 @@ end)
|
|||||||
---Evacuate existing key mapping
|
---Evacuate existing key mapping
|
||||||
---@param mode string
|
---@param mode string
|
||||||
---@param lhs string
|
---@param lhs string
|
||||||
---@return string
|
---@return { keys: string, mode: string }
|
||||||
keymap.evacuate = function(mode, lhs)
|
keymap.evacuate = function(mode, lhs)
|
||||||
local map = keymap.find_map_by_lhs(mode, lhs)
|
local map = keymap.find_map_by_lhs(mode, lhs)
|
||||||
|
|
||||||
-- Keep existing mapping as <Plug> mapping. We escape fisrt recursive key sequence. See `:help recursive_mapping`)
|
-- Keep existing mapping as <Plug> mapping. We escape fisrt recursive key sequence. See `:help recursive_mapping`)
|
||||||
local rhs = map.rhs
|
local rhs = map.rhs
|
||||||
if map.noremap == 0 then
|
if map.noremap == 0 and map.expr == 1 then
|
||||||
if map.expr == 1 then
|
-- remap & expr mapping should evacuate as <Plug> mapping with solving recursive mapping.
|
||||||
rhs = string.format('v:lua.cmp.utils.keymap.evacuate.expr("%s", "%s", "%s")', mode, str.escape(keymap.escape(lhs), { '"' }), str.escape(keymap.escape(rhs), { '"' }))
|
rhs = string.format('v:lua.cmp.utils.keymap.evacuate.expr("%s", "%s", "%s")', mode, str.escape(keymap.escape(lhs), { '"' }), str.escape(keymap.escape(rhs), { '"' }))
|
||||||
else
|
elseif map.noremap ~= 0 and map.expr == 1 then
|
||||||
|
-- noremap & expr mapping should always evacuate as <Plug> mapping.
|
||||||
|
rhs = rhs
|
||||||
|
elseif map.noremap == 0 then
|
||||||
|
-- remap & non-expr mapping should be checked if recursive or not.
|
||||||
rhs = keymap.recursive(mode, lhs, rhs)
|
rhs = keymap.recursive(mode, lhs, rhs)
|
||||||
|
if rhs == map.rhs or map.noremap ~= 0 then
|
||||||
|
return { keys = rhs, mode = 'it' .. (map.noremap == 1 and 'n' or '') }
|
||||||
end
|
end
|
||||||
|
else
|
||||||
|
-- noremap & non-expr mapping doesn't need to evacuate.
|
||||||
|
return { keys = rhs, mode = 'it' .. (map.noremap == 1 and 'n' or '') }
|
||||||
end
|
end
|
||||||
|
|
||||||
local fallback = ('<Plug>(cmp-utils-keymap-evacuate-rhs:%s)'):format(map.lhs)
|
local fallback = ('<Plug>(cmp-utils-keymap-evacuate-rhs:%s)'):format(map.lhs)
|
||||||
@@ -280,7 +289,7 @@ keymap.evacuate = function(mode, lhs)
|
|||||||
silent = true,
|
silent = true,
|
||||||
nowait = true,
|
nowait = true,
|
||||||
})
|
})
|
||||||
return fallback
|
return { keys = fallback, mode = 'it' }
|
||||||
end
|
end
|
||||||
misc.set(_G, { 'cmp', 'utils', 'keymap', 'evacuate', 'expr' }, function(mode, lhs, rhs)
|
misc.set(_G, { 'cmp', 'utils', 'keymap', 'evacuate', 'expr' }, function(mode, lhs, rhs)
|
||||||
return keymap.t(keymap.recursive(mode, lhs, vim.api.nvim_eval(rhs)))
|
return keymap.t(keymap.recursive(mode, lhs, vim.api.nvim_eval(rhs)))
|
||||||
|
|||||||
@@ -43,7 +43,7 @@ describe('keymap', function()
|
|||||||
noremap = false,
|
noremap = false,
|
||||||
})
|
})
|
||||||
local fallback = keymap.evacuate('i', '(')
|
local fallback = keymap.evacuate('i', '(')
|
||||||
vim.api.nvim_feedkeys('i' .. keymap.t(fallback), 'x', true)
|
vim.api.nvim_feedkeys('i' .. keymap.t(fallback.keys), fallback.mode .. 'x', true)
|
||||||
assert.are.same({ '(' }, vim.api.nvim_buf_get_lines(0, 0, -1, true))
|
assert.are.same({ '(' }, vim.api.nvim_buf_get_lines(0, 0, -1, true))
|
||||||
end)
|
end)
|
||||||
|
|
||||||
@@ -57,7 +57,7 @@ describe('keymap', function()
|
|||||||
noremap = false,
|
noremap = false,
|
||||||
})
|
})
|
||||||
local fallback = keymap.evacuate('i', '(')
|
local fallback = keymap.evacuate('i', '(')
|
||||||
vim.api.nvim_feedkeys('i' .. keymap.t(fallback), 'x', true)
|
vim.api.nvim_feedkeys('i' .. keymap.t(fallback.keys), fallback.mode .. 'x', true)
|
||||||
assert.are.same({ '()' }, vim.api.nvim_buf_get_lines(0, 0, -1, true))
|
assert.are.same({ '()' }, vim.api.nvim_buf_get_lines(0, 0, -1, true))
|
||||||
end)
|
end)
|
||||||
|
|
||||||
@@ -70,7 +70,7 @@ describe('keymap', function()
|
|||||||
noremap = false,
|
noremap = false,
|
||||||
})
|
})
|
||||||
local fallback = keymap.evacuate('i', '<Tab>')
|
local fallback = keymap.evacuate('i', '<Tab>')
|
||||||
vim.api.nvim_feedkeys('i' .. keymap.t(fallback), 'x', true)
|
vim.api.nvim_feedkeys('i' .. keymap.t(fallback.keys), fallback.mode .. 'x', true)
|
||||||
assert.are.same({ 'foobar' }, vim.api.nvim_buf_get_lines(0, 0, -1, true))
|
assert.are.same({ 'foobar' }, vim.api.nvim_buf_get_lines(0, 0, -1, true))
|
||||||
end)
|
end)
|
||||||
it('false', function()
|
it('false', function()
|
||||||
@@ -79,7 +79,7 @@ describe('keymap', function()
|
|||||||
noremap = false,
|
noremap = false,
|
||||||
})
|
})
|
||||||
local fallback = keymap.evacuate('i', '<Tab>')
|
local fallback = keymap.evacuate('i', '<Tab>')
|
||||||
vim.api.nvim_feedkeys('i' .. keymap.t(fallback), 'x', true)
|
vim.api.nvim_feedkeys('i' .. keymap.t(fallback.keys), fallback.mode .. 'x', true)
|
||||||
assert.are.same({ '\taiueo' }, vim.api.nvim_buf_get_lines(0, 0, -1, true))
|
assert.are.same({ '\taiueo' }, vim.api.nvim_buf_get_lines(0, 0, -1, true))
|
||||||
end)
|
end)
|
||||||
end)
|
end)
|
||||||
|
|||||||
@@ -5,12 +5,12 @@ local misc = {}
|
|||||||
---@return function
|
---@return function
|
||||||
misc.once = function(callback)
|
misc.once = function(callback)
|
||||||
local done = false
|
local done = false
|
||||||
return function()
|
return function(...)
|
||||||
if done then
|
if done then
|
||||||
return
|
return
|
||||||
end
|
end
|
||||||
done = true
|
done = true
|
||||||
callback()
|
callback(...)
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
|
|||||||
@@ -1,6 +1,7 @@
|
|||||||
local cache = require('cmp.utils.cache')
|
local cache = require('cmp.utils.cache')
|
||||||
local misc = require('cmp.utils.misc')
|
local misc = require('cmp.utils.misc')
|
||||||
local buffer = require('cmp.utils.buffer')
|
local buffer = require('cmp.utils.buffer')
|
||||||
|
local api = require('cmp.utils.api')
|
||||||
|
|
||||||
---@class cmp.WindowStyle
|
---@class cmp.WindowStyle
|
||||||
---@field public relative string
|
---@field public relative string
|
||||||
@@ -31,7 +32,6 @@ window.new = function()
|
|||||||
self.style = {}
|
self.style = {}
|
||||||
self.cache = cache.new()
|
self.cache = cache.new()
|
||||||
self.opt = {}
|
self.opt = {}
|
||||||
self.id = 0
|
|
||||||
return self
|
return self
|
||||||
end
|
end
|
||||||
|
|
||||||
@@ -76,8 +76,6 @@ end
|
|||||||
---Open window
|
---Open window
|
||||||
---@param style cmp.WindowStyle
|
---@param style cmp.WindowStyle
|
||||||
window.open = function(self, style)
|
window.open = function(self, style)
|
||||||
self.id = self.id + 1
|
|
||||||
|
|
||||||
if style then
|
if style then
|
||||||
self:set_style(style)
|
self:set_style(style)
|
||||||
end
|
end
|
||||||
@@ -146,13 +144,17 @@ window.update = function(self)
|
|||||||
self.swin2 = nil
|
self.swin2 = nil
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
|
-- In cmdline, vim does not redraw automatically.
|
||||||
|
if api.is_cmdline_mode() then
|
||||||
|
vim.api.nvim_win_call(self.win, function()
|
||||||
|
vim.cmd([[redraw]])
|
||||||
|
end)
|
||||||
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
---Close window
|
---Close window
|
||||||
window.close = function(self)
|
window.close = function(self)
|
||||||
local id = self.id
|
|
||||||
vim.schedule(function()
|
|
||||||
if id == self.id then
|
|
||||||
if self.win and vim.api.nvim_win_is_valid(self.win) then
|
if self.win and vim.api.nvim_win_is_valid(self.win) then
|
||||||
if self.win and vim.api.nvim_win_is_valid(self.win) then
|
if self.win and vim.api.nvim_win_is_valid(self.win) then
|
||||||
vim.api.nvim_win_hide(self.win)
|
vim.api.nvim_win_hide(self.win)
|
||||||
@@ -167,8 +169,6 @@ window.close = function(self)
|
|||||||
self.swin2 = nil
|
self.swin2 = nil
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
end
|
|
||||||
end)
|
|
||||||
end
|
end
|
||||||
|
|
||||||
---Return the window is visible or not.
|
---Return the window is visible or not.
|
||||||
|
|||||||
@@ -6,6 +6,8 @@ local types = require('cmp.types')
|
|||||||
local keymap = require('cmp.utils.keymap')
|
local keymap = require('cmp.utils.keymap')
|
||||||
local api = require('cmp.utils.api')
|
local api = require('cmp.utils.api')
|
||||||
|
|
||||||
|
local SIDE_PADDING = 1
|
||||||
|
|
||||||
---@class cmp.CustomEntriesView
|
---@class cmp.CustomEntriesView
|
||||||
---@field private entries_win cmp.Window
|
---@field private entries_win cmp.Window
|
||||||
---@field private offset number
|
---@field private offset number
|
||||||
@@ -50,7 +52,7 @@ custom_entries_view.new = function()
|
|||||||
local e = self.entries[i + 1]
|
local e = self.entries[i + 1]
|
||||||
if e then
|
if e then
|
||||||
local v = e:get_view(self.offset)
|
local v = e:get_view(self.offset)
|
||||||
local o = 1
|
local o = SIDE_PADDING
|
||||||
local a = 0
|
local a = 0
|
||||||
for _, field in ipairs(fields) do
|
for _, field in ipairs(fields) do
|
||||||
if field == types.cmp.ItemField.Abbr then
|
if field == types.cmp.ItemField.Abbr then
|
||||||
@@ -133,6 +135,7 @@ custom_entries_view.open = function(self, offset, entries)
|
|||||||
local height = vim.api.nvim_get_option('pumheight')
|
local height = vim.api.nvim_get_option('pumheight')
|
||||||
height = height == 0 and #self.entries or height
|
height = height == 0 and #self.entries or height
|
||||||
height = math.min(height, #self.entries)
|
height = math.min(height, #self.entries)
|
||||||
|
|
||||||
if (vim.o.lines - pos[1]) <= 8 and pos[1] - 8 > 0 then
|
if (vim.o.lines - pos[1]) <= 8 and pos[1] - 8 > 0 then
|
||||||
height = math.min(height, pos[1] - 1)
|
height = math.min(height, pos[1] - 1)
|
||||||
pos[1] = pos[1] - height - 1
|
pos[1] = pos[1] - height - 1
|
||||||
@@ -151,8 +154,8 @@ custom_entries_view.open = function(self, offset, entries)
|
|||||||
self.entries_win:open({
|
self.entries_win:open({
|
||||||
relative = 'editor',
|
relative = 'editor',
|
||||||
style = 'minimal',
|
style = 'minimal',
|
||||||
row = row,
|
row = math.max(0, row),
|
||||||
col = col,
|
col = math.max(0, col),
|
||||||
width = width,
|
width = width,
|
||||||
height = height,
|
height = height,
|
||||||
zindex = 1001,
|
zindex = 1001,
|
||||||
@@ -196,16 +199,22 @@ custom_entries_view.draw = function(self)
|
|||||||
if e then
|
if e then
|
||||||
local view = e:get_view(self.offset)
|
local view = e:get_view(self.offset)
|
||||||
local text = {}
|
local text = {}
|
||||||
table.insert(text, ' ')
|
table.insert(text, string.rep(' ', SIDE_PADDING))
|
||||||
for _, field in ipairs(fields) do
|
for _, field in ipairs(fields) do
|
||||||
table.insert(text, view[field].text)
|
table.insert(text, view[field].text)
|
||||||
table.insert(text, string.rep(' ', 1 + self.column_width[field] - view[field].width))
|
table.insert(text, string.rep(' ', 1 + self.column_width[field] - view[field].width))
|
||||||
end
|
end
|
||||||
table.insert(text, ' ')
|
table.insert(text, string.rep(' ', SIDE_PADDING))
|
||||||
table.insert(texts, table.concat(text, ''))
|
table.insert(texts, table.concat(text, ''))
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
vim.api.nvim_buf_set_lines(self.entries_win:get_buffer(), topline, botline, false, texts)
|
vim.api.nvim_buf_set_lines(self.entries_win:get_buffer(), topline, botline, false, texts)
|
||||||
|
|
||||||
|
if api.is_cmdline_mode() then
|
||||||
|
vim.api.nvim_win_call(self.entries_win.win, function()
|
||||||
|
vim.cmd([[redraw]])
|
||||||
|
end)
|
||||||
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
custom_entries_view.visible = function(self)
|
custom_entries_view.visible = function(self)
|
||||||
@@ -282,20 +291,33 @@ custom_entries_view._select = function(self, cursor, option)
|
|||||||
vim.api.nvim_win_set_cursor(self.entries_win.win, { math.max(cursor, 1), is_insert and 0 or 1 })
|
vim.api.nvim_win_set_cursor(self.entries_win.win, { math.max(cursor, 1), is_insert and 0 or 1 })
|
||||||
|
|
||||||
if is_insert then
|
if is_insert then
|
||||||
self:_insert(self.entries[cursor] and self.entries[cursor]:get_vim_item(self.offset).word or self.prefix)
|
self:_insert(self.entries[cursor] and self.entries[cursor]:get_vim_item(self.offset).word or self.prefix or '')
|
||||||
end
|
end
|
||||||
|
|
||||||
self.entries_win:update()
|
self.entries_win:update()
|
||||||
self:draw()
|
self:draw()
|
||||||
self.event:emit('change')
|
self.event:emit('change')
|
||||||
end
|
end
|
||||||
|
|
||||||
custom_entries_view._insert = function(self, word)
|
custom_entries_view._insert = function(self, word)
|
||||||
keymap.feedkeys('', 'n', function()
|
if api.is_cmdline_mode() then
|
||||||
local release = require('cmp').core:suspend()
|
|
||||||
local cursor = api.get_cursor()
|
local cursor = api.get_cursor()
|
||||||
local length = vim.str_utfindex(string.sub(api.get_current_line(), self.offset, cursor[2]))
|
local length = vim.str_utfindex(string.sub(api.get_current_line(), self.offset, cursor[2]))
|
||||||
keymap.feedkeys(keymap.backspace(length) .. word, 'int', vim.schedule_wrap(release))
|
vim.api.nvim_feedkeys(keymap.backspace(length) .. word, 'int', true)
|
||||||
|
else
|
||||||
|
local release = require('cmp').core:suspend()
|
||||||
|
keymap.feedkeys('', 'n', function()
|
||||||
|
local cursor = api.get_cursor()
|
||||||
|
local length = vim.str_utfindex(string.sub(api.get_current_line(), self.offset, cursor[2]))
|
||||||
|
keymap.feedkeys(
|
||||||
|
keymap.backspace(length) .. word,
|
||||||
|
'int',
|
||||||
|
vim.schedule_wrap(function()
|
||||||
|
release()
|
||||||
end)
|
end)
|
||||||
|
)
|
||||||
|
end)
|
||||||
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
return custom_entries_view
|
return custom_entries_view
|
||||||
|
|||||||
@@ -5,11 +5,12 @@ vim.g.loaded_cmp = true
|
|||||||
|
|
||||||
local api = require "cmp.utils.api"
|
local api = require "cmp.utils.api"
|
||||||
local misc = require('cmp.utils.misc')
|
local misc = require('cmp.utils.misc')
|
||||||
|
local config = require('cmp.config')
|
||||||
local highlight = require('cmp.utils.highlight')
|
local highlight = require('cmp.utils.highlight')
|
||||||
|
|
||||||
-- TODO: https://github.com/neovim/neovim/pull/14661
|
-- TODO: https://github.com/neovim/neovim/pull/14661
|
||||||
vim.cmd [[
|
vim.cmd [[
|
||||||
augroup cmp
|
augroup ___cmp___
|
||||||
autocmd!
|
autocmd!
|
||||||
autocmd InsertEnter * lua require'cmp.utils.autocmd'.emit('InsertEnter')
|
autocmd InsertEnter * lua require'cmp.utils.autocmd'.emit('InsertEnter')
|
||||||
autocmd InsertLeave * lua require'cmp.utils.autocmd'.emit('InsertLeave')
|
autocmd InsertLeave * lua require'cmp.utils.autocmd'.emit('InsertLeave')
|
||||||
@@ -18,9 +19,42 @@ vim.cmd [[
|
|||||||
autocmd CompleteChanged * lua require'cmp.utils.autocmd'.emit('CompleteChanged')
|
autocmd CompleteChanged * lua require'cmp.utils.autocmd'.emit('CompleteChanged')
|
||||||
autocmd CompleteDone * lua require'cmp.utils.autocmd'.emit('CompleteDone')
|
autocmd CompleteDone * lua require'cmp.utils.autocmd'.emit('CompleteDone')
|
||||||
autocmd ColorScheme * call v:lua.cmp.plugin.colorscheme()
|
autocmd ColorScheme * call v:lua.cmp.plugin.colorscheme()
|
||||||
|
autocmd CmdlineEnter * call v:lua.cmp.plugin.cmdline.enter()
|
||||||
|
autocmd CmdlineLeave * call v:lua.cmp.plugin.cmdline.leave()
|
||||||
augroup END
|
augroup END
|
||||||
]]
|
]]
|
||||||
|
|
||||||
|
misc.set(_G, { 'cmp', 'plugin', 'cmdline', 'enter' }, function()
|
||||||
|
if config.get().experimental.native_menu then
|
||||||
|
return
|
||||||
|
end
|
||||||
|
local cmdtype = vim.fn.expand('<afile>')
|
||||||
|
if cmdtype ~= '=' then
|
||||||
|
vim.cmd [[
|
||||||
|
augroup cmp-cmdline
|
||||||
|
autocmd!
|
||||||
|
autocmd CmdlineChanged * lua require'cmp.utils.autocmd'.emit('TextChanged')
|
||||||
|
augroup END
|
||||||
|
]]
|
||||||
|
require('cmp.utils.autocmd').emit('InsertEnter')
|
||||||
|
end
|
||||||
|
end)
|
||||||
|
|
||||||
|
misc.set(_G, { 'cmp', 'plugin', 'cmdline', 'leave' }, function()
|
||||||
|
if config.get().experimental.native_menu then
|
||||||
|
return
|
||||||
|
end
|
||||||
|
local cmdtype = vim.fn.expand('<afile>')
|
||||||
|
if cmdtype ~= '=' then
|
||||||
|
vim.cmd [[
|
||||||
|
augroup cmp-cmdline
|
||||||
|
autocmd!
|
||||||
|
augroup END
|
||||||
|
]]
|
||||||
|
require('cmp.utils.autocmd').emit('InsertLeave')
|
||||||
|
end
|
||||||
|
end)
|
||||||
|
|
||||||
misc.set(_G, { 'cmp', 'plugin', 'colorscheme' }, function()
|
misc.set(_G, { 'cmp', 'plugin', 'colorscheme' }, function()
|
||||||
highlight.inherit('CmpItemAbbrDefault', 'Pmenu', {
|
highlight.inherit('CmpItemAbbrDefault', 'Pmenu', {
|
||||||
guibg = 'NONE',
|
guibg = 'NONE',
|
||||||
|
|||||||
Reference in New Issue
Block a user