Skip to content

Commit

Permalink
Handle completion of % and # completion correctly.
Browse files Browse the repository at this point in the history
The current behavior if you try to complete :e %:p:h or something similar
replaces only the h with the path, which isn't the desired behavior, because
then nvim tries to open the wrong filename. Detect this case and produce edits
that will replace the entire `%` chain with modifiers.
  • Loading branch information
iteratee committed Jan 5, 2024
1 parent 8ee981b commit 8cb39fd
Showing 1 changed file with 41 additions and 6 deletions.
47 changes: 41 additions & 6 deletions lua/cmp_cmdline/init.lua
Original file line number Diff line number Diff line change
Expand Up @@ -78,6 +78,8 @@ local definitions = {
kind = cmp.lsp.CompletionItemKind.Variable,
isIncomplete = true,
exec = function(option, arglead, cmdline, force)
-- Any edits we produce are relative to the whole command line.
local cmdline_length = #cmdline
-- Ignore range only cmdline. (e.g.: 4, '<,'>)
if not force and ONLY_RANGE_REGEX:match_str(cmdline) then
return {}
Expand Down Expand Up @@ -115,18 +117,21 @@ local definitions = {
-- In this case, the `vim.fn.getcompletion` will return only `get_query` for `vim.treesitter.get_|`.
-- We should detect `vim.treesitter.` and `get_query` separately.
-- TODO: The `\h\w*` was choosed by huristic. We should consider more suitable detection.
local fixed_input
do
local suffix_pos = vim.regex([[\h\w*$]]):match_str(arglead)
fixed_input = string.sub(arglead, 1, suffix_pos or #arglead)
end
local fixed_input = arglead

-- The `vim.fn.getcompletion` does not return `*no*cursorline` option.
-- cmp-cmdline corrects `no` prefix for option name.
local is_option_name_completion = OPTION_NAME_COMPLETION_REGEX:match_str(cmdline) ~= nil

local items = {}
local escaped = cmdline:gsub([[\\]], [[\\\\]]);
local is_magic_file = false
local input_start = string.sub(fixed_input, 1, 1)
if input_start == '%' then
is_magic_file = true
elseif input_start == '#' then
is_magic_file = true
end
for _, word_or_item in ipairs(vim.fn.getcompletion(escaped, 'cmdline')) do
local word = type(word_or_item) == 'string' and word_or_item or word_or_item.word
local item = { label = word }
Expand All @@ -139,9 +144,25 @@ local definitions = {
end
end
for _, item in ipairs(items) do
if not string.find(item.label, fixed_input, 1, true) then
if not is_magic_file and not string.find(item.label, fixed_input, 1, true) then
item.label = fixed_input .. item.label
end
if is_magic_file then
local replace_range = {
start = {
character = cmdline_length - #fixed_input - 1
},
['end'] = {
character = cmdline_length - 1
}
}
item.textEdit = {
replace = replace_range,
range = replace_range,
newText = item.label
}
item.label = fixed_input .. '' .. item.label
end
end
return items
end
Expand Down Expand Up @@ -196,6 +217,20 @@ source.complete = function(self, params, callback)
for _, item in ipairs(items) do
item.kind = kind
labels[item.label] = true
if item['textEdit'] ~= nil then
local new_range = {
start = {
character = item.textEdit.range.start.character,
line = params.context.cursor.line
},
['end'] = {
character = item.textEdit.range['end'].character,
line = params.context.cursor.line
}
}
item.textEdit.replace = new_range
item.textEdit.range = new_range
end
end

-- `vim.fn.getcompletion` does not handle fuzzy matches. So, we must return all items, including items that were matched in the previous input.
Expand Down

0 comments on commit 8cb39fd

Please sign in to comment.