-
Notifications
You must be signed in to change notification settings - Fork 63
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Path aliases #71
Comments
Seconding this idea cause with vue projects in particular it's really convenient. ❤️ |
Here's my modified code to achieve the @ -> src effect. local cmp = require 'cmp'
local NAME_REGEX = '\\%([^/\\\\:\\*?<>\'"`\\|]\\)'
-- edit one
local PATH_REGEX = vim.regex(([[\%(\%(/PAT*[^(@/|/)\\\\:\\*?<>\'"`\\| .~]\)\|\%(/\.\.\)\)*/\zePAT*$]]):gsub('PAT', NAME_REGEX))
local source = {}
local constants = {
max_lines = 20,
}
---@class cmp_path.Option
---@field public trailing_slash boolean
---@field public label_trailing_slash boolean
---@field public get_cwd fun(): string
---@type cmp_path.Option
local defaults = {
trailing_slash = false,
label_trailing_slash = true,
get_cwd = function(params)
return vim.fn.expand(('#%d:p:h'):format(params.context.bufnr))
end,
}
source.new = function()
return setmetatable({}, { __index = source })
end
source.get_trigger_characters = function()
return { '/', '.' }
end
source.get_keyword_pattern = function(self, params)
return NAME_REGEX .. '*'
end
source.complete = function(self, params, callback)
local option = self:_validate_option(params)
local dirname = self:_dirname(params, option)
if not dirname then
return callback()
end
-- print(dirname)
local include_hidden = string.sub(params.context.cursor_before_line, params.offset, params.offset) == '.'
self:_candidates(dirname, include_hidden, option, function(err, candidates)
if err then
return callback()
end
callback(candidates)
end)
end
source.resolve = function(self, completion_item, callback)
local data = completion_item.data
if data.stat and data.stat.type == 'file' then
local ok, documentation = pcall(function()
return self:_get_documentation(data.path, constants.max_lines)
end)
if ok then
completion_item.documentation = documentation
end
end
callback(completion_item)
end
source._dirname = function(self, params, option)
local current_directory = vim.fn.getcwd()
-- edit two
local replaced_string = string.gsub(params.context.cursor_before_line, '"(@)', '"' .. current_directory .. '/src')
local s = PATH_REGEX:match_str(replaced_string)
if not s then
return nil
end
-- edit two
local dirname = string.gsub(string.sub(replaced_string, s + 2), '%a*$', '') -- exclude '/'
local prefix = string.sub(replaced_string, 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)
end
if (prefix:match('%./$') or prefix:match('"$') or prefix:match('\'$')) then
return vim.fn.resolve(buf_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('</$')
-- Ignore math calculation
accept = accept and not prefix:match('[%d%)]%s*/$')
-- Ignore / comment
accept = accept and (not prefix:match('^[%s/]*$') or not self:_is_slash_comment())
if accept then
return vim.fn.resolve('/' .. dirname)
end
end
return nil
end
source._candidates = function(_, dirname, include_hidden, option, callback)
local fs, err = vim.loop.fs_scandir(dirname)
if err then
return callback(err, nil)
end
local items = {}
local function create_item(name, fs_type)
if not (include_hidden or string.sub(name, 1, 1) ~= '.') then
return
end
local path = dirname .. '/' .. name
local stat = vim.loop.fs_stat(path)
local lstat = nil
if stat then
fs_type = stat.type
elseif fs_type == 'link' then
-- Broken symlink
lstat = vim.loop.fs_lstat(dirname)
if not lstat then
return
end
else
return
end
local item = {
label = name,
filterText = name,
insertText = name,
kind = cmp.lsp.CompletionItemKind.File,
data = {
path = path,
type = fs_type,
stat = stat,
lstat = lstat,
},
}
if fs_type == 'directory' then
item.kind = cmp.lsp.CompletionItemKind.Folder
if option.label_trailing_slash then
item.label = name .. '/'
else
item.label = name
end
item.insertText = name .. '/'
if not option.trailing_slash then
item.word = name
end
end
table.insert(items, item)
end
while true do
local name, fs_type, e = vim.loop.fs_scandir_next(fs)
if e then
return callback(fs_type, nil)
end
if not name then
break
end
create_item(name, fs_type)
end
callback(nil, items)
end
source._is_slash_comment = function(_)
local commentstring = vim.bo.commentstring or ''
local no_filetype = vim.bo.filetype == ''
local is_slash_comment = false
is_slash_comment = is_slash_comment or commentstring:match('/%*')
is_slash_comment = is_slash_comment or commentstring:match('//')
return is_slash_comment and not no_filetype
end
---@return cmp_path.Option
source._validate_option = function(_, params)
local option = vim.tbl_deep_extend('keep', params.option, defaults)
vim.validate({
trailing_slash = { option.trailing_slash, 'boolean' },
label_trailing_slash = { option.label_trailing_slash, 'boolean' },
get_cwd = { option.get_cwd, 'function' },
})
return option
end
source._get_documentation = function(_, filename, count)
local binary = assert(io.open(filename, 'rb'))
local first_kb = binary:read(1024)
if first_kb:find('\0') then
return { kind = cmp.lsp.MarkupKind.PlainText, value = 'binary file' }
end
local contents = {}
for content in first_kb:gmatch("[^\r\n]+") do
table.insert(contents, content)
if count ~= nil and #contents >= count then
break
end
end
local filetype = vim.filetype.match({ filename = filename })
if not filetype then
return { kind = cmp.lsp.MarkupKind.PlainText, value = table.concat(contents, '\n') }
end
table.insert(contents, 1, '```' .. filetype)
table.insert(contents, '```')
return { kind = cmp.lsp.MarkupKind.Markdown, value = table.concat(contents, '\n') }
end
return source
|
Would really appreciate it if you can submit that as a PR! |
@kola-web |
First of all, let's talk/check about path-autocompletion mentioned by OP. By path-autocompletion itself, it's probably useless without compiler/bundler support. That is, you can tweak your editor settings to have something like this without an error(which doesn't mean it's correct): import Foo from '@cool_prefix/foo' // it "work" from the point of view of your editor.
// since it can understand those jargons after your tweak. But those environments that run your code do NOT have your path-autocompletion settings. So you still need a compiler/bundler, e.g. import Foo from '/expanded/real/path/foo' With this background knowledge in mind, I just wrote a working version (so yes, you still need a compiler/transpiler, as explained ^) to fulfill the requirement of OP. It has some no-harm caveats(linked to my comments of code) Or maybe I should say "TODOs". In short, It reads the By the way, it's probably the is currently the only way I know to auto-complete So given this {
"compilerOptions": {
"baseUrl": ".",
"paths": {
"/*": [ "./public/*" ],
"@/*": [ "./src/reactApp/*" ],
"@components/*": [ "./src/reactApp/components/*" ],
"@another/*": [ "./src/firebase/" ]
},
},
} You can have what OP described in the title: |
By the way, if you need to use this kind of amplification in vue, you need to add "allowJs": true |
@kola-web Thanks for your concern. it's indeed required for not just vue, but also webpack in my case. That's because |
VSCode's Path Autocomplete has a feature called
path-autocomplete.pathMappings
which allows you to define aliases for paths:For example, you can define your root path (
/
) to point to yourpublic
directory and other useful things.It would be nice if we can have a
get_aliases
function to support this.The text was updated successfully, but these errors were encountered: