diff options
| author | Mike Vink <mike@pionative.com> | 2025-01-19 13:52:52 +0100 |
|---|---|---|
| committer | Mike Vink <mike@pionative.com> | 2025-01-19 13:52:52 +0100 |
| commit | b77413ff8f59f380612074f0c9bd49093d8db695 (patch) | |
| tree | 32c39a811ba96ed4ab0a1c81cce9f8d518ed7e31 /lua/blink/cmp/sources/cmdline | |
Squashed 'mut/neovim/pack/plugins/start/blink.cmp/' content from commit 1cc3b1a
git-subtree-dir: mut/neovim/pack/plugins/start/blink.cmp
git-subtree-split: 1cc3b1a908fbcfd15451c4772759549724f38524
Diffstat (limited to 'lua/blink/cmp/sources/cmdline')
| -rw-r--r-- | lua/blink/cmp/sources/cmdline/constants.lua | 40 | ||||
| -rw-r--r-- | lua/blink/cmp/sources/cmdline/help.lua | 53 | ||||
| -rw-r--r-- | lua/blink/cmp/sources/cmdline/init.lua | 107 |
3 files changed, 200 insertions, 0 deletions
diff --git a/lua/blink/cmp/sources/cmdline/constants.lua b/lua/blink/cmp/sources/cmdline/constants.lua new file mode 100644 index 0000000..fd2ee85 --- /dev/null +++ b/lua/blink/cmp/sources/cmdline/constants.lua @@ -0,0 +1,40 @@ +return { + help_commands = { + 'help', + 'hel', + 'he', + 'h', + }, + file_commands = { + 'edit', + 'e', + 'read', + 'r', + 'write', + 'w', + 'saveas', + 'sav', + 'split', + 'sp', + 'vsplit', + 'vs', + 'tabedit', + 'tabe', + 'badd', + 'bad', + 'next', + 'n', + 'previous', + 'prev', + 'args', + 'source', + 'so', + 'find', + 'fin', + 'diffsplit', + 'diffs', + 'diffpatch', + 'diffp', + 'make', + }, +} diff --git a/lua/blink/cmp/sources/cmdline/help.lua b/lua/blink/cmp/sources/cmdline/help.lua new file mode 100644 index 0000000..bae4988 --- /dev/null +++ b/lua/blink/cmp/sources/cmdline/help.lua @@ -0,0 +1,53 @@ +local async = require('blink.cmp.lib.async') + +local help = {} + +--- Processes a help file and returns a list of tags asynchronously +--- @param file string +--- @return blink.cmp.Task +--- TODO: rewrite using async lib, shared as a library in lib/fs.lua +local function read_tags_from_file(file) + return async.task.new(function(resolve) + vim.uv.fs_open(file, 'r', 438, function(err, fd) + if err or fd == nil then return resolve({}) end + + -- Read file content + vim.uv.fs_fstat(fd, function(stat_err, stat) + if stat_err or stat == nil then + vim.uv.fs_close(fd) + return resolve({}) + end + + vim.uv.fs_read(fd, stat.size, 0, function(read_err, data) + vim.uv.fs_close(fd) + + if read_err or data == nil then return resolve({}) end + + -- Process the file content + local tags = {} + for line in data:gmatch('[^\r\n]+') do + local tag = line:match('^([^\t]+)') + if tag then table.insert(tags, tag) end + end + + resolve(tags) + end) + end) + end) + end) +end + +--- @param arg_prefix string +function help.get_completions(arg_prefix) + local help_files = vim.api.nvim_get_runtime_file('doc/tags', true) + + return async.task + .await_all(vim.tbl_map(read_tags_from_file, help_files)) + :map(function(tags_arrs) return require('blink.cmp.lib.utils').flatten(tags_arrs) end) + :map(function(tags) + -- TODO: remove after adding support for fuzzy matching on custom range + return vim.tbl_filter(function(tag) return vim.startswith(tag, arg_prefix) end, tags) + end) +end + +return help diff --git a/lua/blink/cmp/sources/cmdline/init.lua b/lua/blink/cmp/sources/cmdline/init.lua new file mode 100644 index 0000000..5ff5e11 --- /dev/null +++ b/lua/blink/cmp/sources/cmdline/init.lua @@ -0,0 +1,107 @@ +-- Credit goes to @hrsh7th for the code that this was based on +-- https://github.com/hrsh7th/cmp-cmdline +-- License: MIT + +local async = require('blink.cmp.lib.async') +local constants = require('blink.cmp.sources.cmdline.constants') + +--- @class blink.cmp.Source +local cmdline = {} + +function cmdline.new() + local self = setmetatable({}, { __index = cmdline }) + self.before_line = '' + self.offset = -1 + self.ctype = '' + self.items = {} + return self +end + +function cmdline:get_trigger_characters() return { ' ', '.', '#', '-', '=', '/', ':' } end + +function cmdline:get_completions(context, callback) + local arguments = vim.split(context.line, ' ', { plain = true }) + local arg_number = #vim.split(context.line:sub(1, context.cursor[2] + 1), ' ', { plain = true }) + local text_before_argument = table.concat(require('blink.cmp.lib.utils').slice(arguments, 1, arg_number - 1), ' ') + .. (arg_number > 1 and ' ' or '') + + local current_arg = arguments[arg_number] + local keyword_config = require('blink.cmp.config').completion.keyword + local keyword = context.get_bounds(keyword_config.range) + local current_arg_prefix = current_arg:sub(1, keyword.start_col - #text_before_argument - 1) + + local task = async.task + .empty() + :map(function() + -- Special case for help where we read all the tags ourselves + if vim.tbl_contains(constants.help_commands, arguments[1] or '') then + return require('blink.cmp.sources.cmdline.help').get_completions(current_arg_prefix) + end + + local completions = {} + local completion_type = vim.fn.getcmdcompltype() + -- Handle custom completions explicitly, since otherwise they won't work in input() mode (getcmdtype() == '@') + if vim.startswith(completion_type, 'custom,') or vim.startswith(completion_type, 'customlist,') then + local fun = completion_type:gsub('custom,', ''):gsub('customlist,', '') + completions = vim.fn.call(fun, { current_arg_prefix, vim.fn.getcmdline(), vim.fn.getcmdpos() }) + -- `custom,` type returns a string, delimited by newlines + if type(completions) == 'string' then completions = vim.split(completions, '\n') end + else + local query = (text_before_argument .. current_arg_prefix):gsub([[\\]], [[\\\\]]) + completions = vim.fn.getcompletion(query, 'cmdline') + end + + -- Special case for files, escape special characters + if vim.tbl_contains(constants.file_commands, arguments[1] or '') then + completions = vim.tbl_map(function(completion) return vim.fn.fnameescape(completion) end, completions) + end + + return completions + end) + :map(function(completions) + local items = {} + for _, completion in ipairs(completions) do + local has_prefix = string.find(completion, current_arg_prefix, 1, true) == 1 + + -- remove prefix from the filter text + local filter_text = completion + if has_prefix then filter_text = completion:sub(#current_arg_prefix + 1) end + + -- for lua, use the filter text as the label since it doesn't include the prefix + local label = arguments[1] == 'lua' and filter_text or completion + + -- add prefix to the newText + local new_text = completion + if not has_prefix then new_text = current_arg_prefix .. completion end + + table.insert(items, { + label = label, + filterText = filter_text, + -- move items starting with special characters to the end of the list + sortText = label:lower():gsub('^([!-@\\[-`])', '~%1'), + textEdit = { + newText = new_text, + range = { + start = { line = 0, character = #text_before_argument }, + ['end'] = { line = 0, character = #text_before_argument + #current_arg }, + }, + }, + kind = require('blink.cmp.types').CompletionItemKind.Property, + }) + end + + callback({ + is_incomplete_backward = true, + is_incomplete_forward = false, + items = items, + }) + end) + :catch(function(err) + vim.notify('Error while fetching completions: ' .. err, vim.log.levels.ERROR, { title = 'blink.cmp' }) + callback({ is_incomplete_backward = false, is_incomplete_forward = false, items = {} }) + end) + + return function() task:cancel() end +end + +return cmdline |
