From 411498c3e0fce5f4b67c5e71318ba85b0ec56f23 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?R=C3=A9mi=20Ferrand?= Date: Sat, 18 Feb 2023 15:10:41 +0100 Subject: [PATCH] feat: Allow get_cwd() function to return an array * Add support for multiple completion directories. Motivation: complete from both the 'buffer cwd' and 'the cwd where neovim was opened' --- README.md | 2 + lua/cmp_path/init.lua | 127 ++++++++++++++++++++++++++++++------------ 2 files changed, 92 insertions(+), 37 deletions(-) diff --git a/README.md b/README.md index 1cfd7a6..df5eff2 100644 --- a/README.md +++ b/README.md @@ -48,3 +48,5 @@ Specify if directory names in the completion menu should include a trailing slas _Default:_ returns the current working directory of the current buffer Specifies the base directory for relative paths. + +If a _table_ is returned, all directories are looked up in order. diff --git a/lua/cmp_path/init.lua b/lua/cmp_path/init.lua index 4e63d27..2b3fcf6 100644 --- a/lua/cmp_path/init.lua +++ b/lua/cmp_path/init.lua @@ -12,7 +12,7 @@ local constants = { ---@class cmp_path.Option ---@field public trailing_slash boolean ---@field public label_trailing_slash boolean ----@field public get_cwd fun(): string +---@field public get_cwd fun(): string | table ---@type cmp_path.Option local defaults = { @@ -23,6 +23,18 @@ local defaults = { end, } +local uniquify_table = function(a_table) + local newArray = {} + local guard = {} + for _, element in ipairs(a_table) do + if not guard[element] then + guard[element] = true + table.insert(newArray, element) + end + end + return newArray +end + source.new = function() return setmetatable({}, { __index = source }) end @@ -38,18 +50,33 @@ end source.complete = function(self, params, callback) local option = self:_validate_option(params) - local dirname = self:_dirname(params, option) - if not dirname then + local dirnames = self:_dirname(params, option) + if not dirnames then return callback() end local include_hidden = string.sub(params.context.cursor_before_line, params.offset, params.offset) == '.' - self:_candidates(dirname, include_hidden, option, function(err, candidates) + local all_candidates = {} + local all_errs = {} + for _, dirname in ipairs(dirnames) do + self:_candidates(dirname, include_hidden, option, function(err, candidates) + if err then + table.insert(all_errs, err) + else + for _, v in ipairs(candidates) do + table.insert(all_candidates, v) + end + end + end) + end + + for _, err in ipairs(all_errs) do if err then return callback() end - callback(candidates) - end) + end + + callback(all_candidates) end source.resolve = function(self, completion_item, callback) @@ -74,43 +101,69 @@ source._dirname = function(self, params, option) local dirname = string.gsub(string.sub(params.context.cursor_before_line, s + 2), '%a*$', '') -- exclude '/' local prefix = string.sub(params.context.cursor_before_line, 1, s + 1) -- include '/' - local buf_dirname = option.get_cwd(params) - if vim.api.nvim_get_mode().mode == 'c' then - buf_dirname = vim.fn.getcwd() - end - if prefix:match('%.%./$') then - return vim.fn.resolve(buf_dirname .. '/../' .. dirname) + local dir_match_fn = function(a_dirname) + if prefix:match('%.%./$') then + return vim.fn.resolve(a_dirname .. '/../' .. dirname) + end + if (prefix:match('%./$') or prefix:match('"$') or prefix:match('\'$')) then + return vim.fn.resolve(a_dirname .. '/' .. dirname) + end + if prefix:match('~/$') then + return vim.fn.resolve(vim.fn.expand('~') .. '/' .. dirname) + end + local env_var_name = prefix:match('%$([%a_]+)/$') + if env_var_name then + local env_var_value = vim.fn.getenv(env_var_name) + if env_var_value ~= vim.NIL then + return vim.fn.resolve(env_var_value .. '/' .. dirname) + end + end + if prefix:match('/$') then + local accept = true + -- Ignore URL components + accept = accept and not prefix:match('%a/$') + -- Ignore URL scheme + accept = accept and not prefix:match('%a+:/$') and not prefix:match('%a+://$') + -- Ignore HTML closing tags + accept = accept and not prefix:match('