|
| 1 | +local lib = {} |
| 2 | + |
| 3 | +--- @param path_regex vim.regex |
| 4 | +--- @param get_cwd fun(context: blink.cmp.CompletionContext): string |
| 5 | +--- @param context blink.cmp.CompletionContext |
| 6 | +function lib.dirname(path_regex, get_cwd, context) |
| 7 | + local line_before_cursor = context.line:sub(1, context.cursor[2]) |
| 8 | + local s = path_regex:match_str(line_before_cursor) |
| 9 | + if not s then return nil end |
| 10 | + |
| 11 | + local dirname = string.gsub(string.sub(line_before_cursor, s + 2), '%a*$', '') -- exclude '/' |
| 12 | + local prefix = string.sub(line_before_cursor, 1, s + 1) -- include '/' |
| 13 | + |
| 14 | + local buf_dirname = get_cwd(context) |
| 15 | + if vim.api.nvim_get_mode().mode == 'c' then buf_dirname = vim.fn.getcwd() end |
| 16 | + if prefix:match('%.%./$') then return vim.fn.resolve(buf_dirname .. '/../' .. dirname) end |
| 17 | + if prefix:match('%./$') or prefix:match('"$') or prefix:match("'$") then |
| 18 | + return vim.fn.resolve(buf_dirname .. '/' .. dirname) |
| 19 | + end |
| 20 | + if prefix:match('~/$') then return vim.fn.resolve(vim.fn.expand('~') .. '/' .. dirname) end |
| 21 | + local env_var_name = prefix:match('%$([%a_]+)/$') |
| 22 | + if env_var_name then |
| 23 | + local env_var_value = vim.fn.getenv(env_var_name) |
| 24 | + if env_var_value ~= vim.NIL then return vim.fn.resolve(env_var_value .. '/' .. dirname) end |
| 25 | + end |
| 26 | + if prefix:match('/$') then |
| 27 | + local accept = true |
| 28 | + -- Ignore URL components |
| 29 | + accept = accept and not prefix:match('%a/$') |
| 30 | + -- Ignore URL scheme |
| 31 | + accept = accept and not prefix:match('%a+:/$') and not prefix:match('%a+://$') |
| 32 | + -- Ignore HTML closing tags |
| 33 | + accept = accept and not prefix:match('</$') |
| 34 | + -- Ignore math calculation |
| 35 | + accept = accept and not prefix:match('[%d%)]%s*/$') |
| 36 | + -- Ignore / comment |
| 37 | + accept = accept and (not prefix:match('^[%s/]*$') or not self:_is_slash_comment()) |
| 38 | + if accept then return vim.fn.resolve('/' .. dirname) end |
| 39 | + end |
| 40 | + return nil |
| 41 | +end |
| 42 | + |
| 43 | +--- @param dirname string |
| 44 | +--- @param include_hidden boolean |
| 45 | +--- @param opts table |
| 46 | +function lib.candidates(dirname, include_hidden, opts) |
| 47 | + local fs = require('blink.cmp.sources.path.fs') |
| 48 | + return fs.scan_dir_async(dirname) |
| 49 | + :map(function(entries) return fs.fs_stat_all(dirname, entries) end) |
| 50 | + :map(function(entries) |
| 51 | + return vim.tbl_filter(function(entry) return include_hidden or entry.name ~= '.' end, entries) |
| 52 | + end) |
| 53 | + :map(function(entries) |
| 54 | + return vim.tbl_map(function(entry) return lib.entry_to_completion_item(entry, opts) end, entries) |
| 55 | + end) |
| 56 | +end |
| 57 | + |
| 58 | +function lib.is_slash_comment(_) |
| 59 | + local commentstring = vim.bo.commentstring or '' |
| 60 | + local no_filetype = vim.bo.filetype == '' |
| 61 | + local is_slash_comment = false |
| 62 | + is_slash_comment = is_slash_comment or commentstring:match('/%*') |
| 63 | + is_slash_comment = is_slash_comment or commentstring:match('//') |
| 64 | + return is_slash_comment and not no_filetype |
| 65 | +end |
| 66 | + |
| 67 | +--- @param entry { name: string, type: string, stat: table } |
| 68 | +--- @param opts table |
| 69 | +--- @return blink.cmp.CompletionItem[] |
| 70 | +function lib.entry_to_completion_item(entry, opts) |
| 71 | + local is_dir = entry.type == 'directory' |
| 72 | + return { |
| 73 | + label = (opts.label_trailing_slash and is_dir) and entry.name .. '/' or entry.name, |
| 74 | + kind = is_dir and vim.lsp.protocol.CompletionItemKind.Folder or vim.lsp.protocol.CompletionItemKind.File, |
| 75 | + insertText = is_dir and entry.name .. '/' or entry.name, |
| 76 | + word = opts.trailing_slash and entry.name or nil, |
| 77 | + data = { path = entry.name, type = entry.type, stat = entry.stat }, |
| 78 | + } |
| 79 | +end |
| 80 | + |
| 81 | +return lib |
0 commit comments