diff --git a/README.md b/README.md index 1b5849d..9107a98 100644 --- a/README.md +++ b/README.md @@ -43,3 +43,10 @@ Default: `{ "Man", "!" }` You can specify ignore command name. +### treat_trailing_slash: boolean +Default: `true` + +`vim.fn.getcompletion` can return path items. +unfortunately, that items has trailing slash so we don't narrowing with next directory with pressing `/`. + +if you turnd on this option, `cmp-cmdline` removes trailing slash automatically. diff --git a/lua/cmp_cmdline/init.lua b/lua/cmp_cmdline/init.lua index 78bd80d..5451d3a 100644 --- a/lua/cmp_cmdline/init.lua +++ b/lua/cmp_cmdline/init.lua @@ -11,7 +11,11 @@ local function create_regex(patterns, head) return vim.regex(pattern) end +---@class cmp-cmdline.Option +---@field treat_trailing_slash boolean +---@field ignore_cmds string[] local DEFAULT_OPTION = { + treat_trailing_slash = true, ignore_cmds = { 'Man', '!' } } @@ -77,7 +81,10 @@ local definitions = { regex = [=[[^[:blank:]]*$]=], kind = cmp.lsp.CompletionItemKind.Variable, isIncomplete = true, + ---@param option cmp-cmdline.Option 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 {} @@ -125,8 +132,16 @@ local definitions = { -- cmp-cmdline corrects `no` prefix for option name. local is_option_name_completion = OPTION_NAME_COMPLETION_REGEX:match_str(cmdline) ~= nil + --- create items. 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 } @@ -138,10 +153,41 @@ local definitions = { })) end end + + -- fix label with `fixed_input` 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 + + -- fix trailing slash for path like item + if option.treat_trailing_slash then + for _, item in ipairs(items) do + local is_target = string.match(item.label, [[/$]]) + is_target = is_target and not (string.match(item.label, [[~/$]])) + is_target = is_target and not (string.match(item.label, [[%./$]])) + is_target = is_target and not (string.match(item.label, [[%.%./$]])) + if is_target then + item.label = item.label:sub(1, -2) + end + end end return items end @@ -196,6 +242,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.