local cmp = require'cmp' local NAME_REGEX = '\\%([^/\\\\:\\*?<>\'"`\\|]\\)' local PATH_REGEX = vim.regex(([[\%(/PAT\+\)*/\zePAT*$]]):gsub('PAT', NAME_REGEX)) local source = {} source.new = function() return setmetatable({}, { __index = source }) end source.get_trigger_characters = function() return { '/', '.' } end source.get_keyword_pattern = function() return NAME_REGEX .. '*' end source.complete = function(self, params, callback) local dirname = self:_dirname(params) if not dirname then return callback() end local stat = self:_stat(dirname) if not stat then return callback() end self:_candidates(params, dirname, params.offset, function(err, candidates) if err then return callback() end callback(candidates) end) end source._dirname = function(self, params) local s = PATH_REGEX:match_str(params.context.cursor_before_line) if not s then return nil end 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 = vim.fn.expand(('#%d:p:h'):format(params.context.bufnr)) 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) elseif prefix:match('%./$') then return vim.fn.resolve(buf_dirname .. '/' .. dirname) elseif prefix:match('~/$') then return vim.fn.resolve(vim.fn.expand('~') .. '/' .. dirname) elseif prefix:match('%$[%a_]+/$') then return vim.fn.resolve(vim.fn.getenv(prefix:match('%$([%a_]+)/$')) .. '/' .. dirname) elseif 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('