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:
hrsh7th
2021-10-27 12:38:46 +09:00
committed by GitHub
parent b5899f05c5
commit cae2e8f48b
13 changed files with 263 additions and 109 deletions

View File

@@ -9,10 +9,11 @@ api.is_insert_mode = function()
end
api.is_cmdline_mode = function()
return vim.tbl_contains({
local is_cmdline_mode = vim.tbl_contains({
'c',
'cv',
}, vim.api.nvim_get_mode().mode)
return is_cmdline_mode and vim.fn.getcmdtype() ~= '='
end
api.is_select_mode = function()
@@ -41,16 +42,17 @@ api.get_cursor = function()
end
api.get_screen_cursor = function()
local cursor = api.get_cursor()
if api.is_cmdline_mode() then
return cursor
return api.get_cursor()
end
local cursor = api.get_cursor()
local pos = vim.fn.screenpos(0, cursor[1], cursor[2] + 1)
return { pos.row, pos.col - 1 }
end
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
return api

View File

@@ -64,7 +64,7 @@ keymap.to_keymap = setmetatable({
---Mode safe break undo
keymap.undobreak = function()
if api.is_cmdline_mode() then
if not api.is_insert_mode() then
return ''
end
return keymap.t('<C-g>u')
@@ -72,7 +72,7 @@ end
---Mode safe join undo
keymap.undojoin = function()
if api.is_cmdline_mode() then
if not api.is_insert_mode() then
return ''
end
return keymap.t('<C-g>U')
@@ -244,12 +244,12 @@ keymap.listen = setmetatable({
misc.set(_G, { 'cmp', 'utils', 'keymap', 'listen', 'run' }, function(id)
local definition = keymap.listen.cache:get({ 'definition', id })
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
definition.callback(
definition.keys,
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)
)
return keymap.t('<Ignore>')
@@ -258,18 +258,27 @@ end)
---Evacuate existing key mapping
---@param mode string
---@param lhs string
---@return string
---@return { keys: string, mode: string }
keymap.evacuate = function(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`)
local rhs = map.rhs
if map.noremap == 0 then
if map.expr == 1 then
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
rhs = keymap.recursive(mode, lhs, rhs)
if map.noremap == 0 and 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), { '"' }))
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)
if rhs == map.rhs or map.noremap ~= 0 then
return { keys = rhs, mode = 'it' .. (map.noremap == 1 and 'n' or '') }
end
else
-- noremap & non-expr mapping doesn't need to evacuate.
return { keys = rhs, mode = 'it' .. (map.noremap == 1 and 'n' or '') }
end
local fallback = ('<Plug>(cmp-utils-keymap-evacuate-rhs:%s)'):format(map.lhs)
@@ -280,7 +289,7 @@ keymap.evacuate = function(mode, lhs)
silent = true,
nowait = true,
})
return fallback
return { keys = fallback, mode = 'it' }
end
misc.set(_G, { 'cmp', 'utils', 'keymap', 'evacuate', 'expr' }, function(mode, lhs, rhs)
return keymap.t(keymap.recursive(mode, lhs, vim.api.nvim_eval(rhs)))

View File

@@ -43,7 +43,7 @@ describe('keymap', function()
noremap = false,
})
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))
end)
@@ -57,7 +57,7 @@ describe('keymap', function()
noremap = false,
})
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))
end)
@@ -70,7 +70,7 @@ describe('keymap', function()
noremap = false,
})
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))
end)
it('false', function()
@@ -79,7 +79,7 @@ describe('keymap', function()
noremap = false,
})
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))
end)
end)

View File

@@ -5,12 +5,12 @@ local misc = {}
---@return function
misc.once = function(callback)
local done = false
return function()
return function(...)
if done then
return
end
done = true
callback()
callback(...)
end
end

View File

@@ -1,6 +1,7 @@
local cache = require('cmp.utils.cache')
local misc = require('cmp.utils.misc')
local buffer = require('cmp.utils.buffer')
local api = require('cmp.utils.api')
---@class cmp.WindowStyle
---@field public relative string
@@ -31,7 +32,6 @@ window.new = function()
self.style = {}
self.cache = cache.new()
self.opt = {}
self.id = 0
return self
end
@@ -76,8 +76,6 @@ end
---Open window
---@param style cmp.WindowStyle
window.open = function(self, style)
self.id = self.id + 1
if style then
self:set_style(style)
end
@@ -146,29 +144,31 @@ window.update = function(self)
self.swin2 = nil
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
---Close window
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
vim.api.nvim_win_hide(self.win)
self.win = nil
end
if self.swin1 and vim.api.nvim_win_is_valid(self.swin1) then
vim.api.nvim_win_hide(self.swin1)
self.swin1 = nil
end
if self.swin2 and vim.api.nvim_win_is_valid(self.swin2) then
vim.api.nvim_win_hide(self.swin2)
self.swin2 = nil
end
end
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)
self.win = nil
end
end)
if self.swin1 and vim.api.nvim_win_is_valid(self.swin1) then
vim.api.nvim_win_hide(self.swin1)
self.swin1 = nil
end
if self.swin2 and vim.api.nvim_win_is_valid(self.swin2) then
vim.api.nvim_win_hide(self.swin2)
self.swin2 = nil
end
end
end
---Return the window is visible or not.