feat(install)!: add explicit path field to parser info (#6476)

Problem: Using `url` for both remote repo and local path complicates the
code.

Solution: Add `path` field that overrides `url` and bypasses
git-specific manipulations, i.e., the contents of the `path` are used
as-is (no git repo needed).

This means `:TSUpdate` will skip such parsers; use `:TSInstall!` instead
after making local changes.

---------

Co-authored-by: Lewis Russell <lewis6991@gmail.com>
This commit is contained in:
Christian Clason 2024-04-21 00:14:12 +02:00
parent 0bb981c876
commit c70daa36dc
3 changed files with 55 additions and 77 deletions

View file

@ -123,28 +123,6 @@ end
--- PARSER MANAGEMENT FUNCTIONS --- PARSER MANAGEMENT FUNCTIONS
--- ---
--- @param repo InstallInfo
--- @param project_name string
--- @param cache_dir string
--- @param from_local_path boolean
--- @return string
local function get_compile_location(repo, cache_dir, project_name, from_local_path)
---@type string compile_location only needed for typescript installs.
if from_local_path then
local compile_location = repo.url
if repo.location then
compile_location = fs.joinpath(compile_location, repo.location)
end
return compile_location
end
local repo_location = project_name
if repo.location then
repo_location = fs.joinpath(repo_location, repo.location)
end
return fs.joinpath(cache_dir, repo_location)
end
local function istring(c) local function istring(c)
return type(c) == 'string' return type(c) == 'string'
end end
@ -168,6 +146,10 @@ end
--- @param compile_location string --- @param compile_location string
--- @return string? err --- @return string? err
local function do_generate_from_grammar(logger, repo, compile_location) local function do_generate_from_grammar(logger, repo, compile_location)
if not executable('tree-sitter') then
return logger:error('tree-sitter CLI not found: `tree-sitter` is not executable')
end
if repo.generate_requires_npm then if repo.generate_requires_npm then
if not executable('npm') then if not executable('npm') then
return logger:error('NPM requires to be installed from grammar.js') return logger:error('NPM requires to be installed from grammar.js')
@ -400,23 +382,29 @@ local function can_download_tar(repo)
end end
-- Returns the compile command based on the OS and user options -- Returns the compile command based on the OS and user options
---@param logger Logger
---@param repo InstallInfo ---@param repo InstallInfo
---@param cc string ---@param cc string
---@param compile_location string ---@param compile_location string
---@return vim.SystemCompleted --- @return string? err
local function do_compile(repo, cc, compile_location) local function do_compile(logger, repo, cc, compile_location)
local args = vim.tbl_flatten(select_compiler_args(repo, cc)) local args = vim.tbl_flatten(select_compiler_args(repo, cc))
local cmd = vim.list_extend({ cc }, args) local cmd = vim.list_extend({ cc }, args)
return system(cmd, { cwd = compile_location }) logger:info('Compiling parser')
local r = system(cmd, { cwd = compile_location })
if r.code > 0 then
return logger:error('Error during compilation: %s', r.stderr)
end
end end
---@param lang string ---@param lang string
---@param cache_dir string ---@param cache_dir string
---@param install_dir string ---@param install_dir string
---@param generate_from_grammar? boolean ---@param generate? boolean
---@return string? err ---@return string? err
local function install_lang0(lang, cache_dir, install_dir, generate_from_grammar) local function install_lang0(lang, cache_dir, install_dir, generate)
local logger = log.new('install/' .. lang) local logger = log.new('install/' .. lang)
local repo = get_parser_install_info(lang) local repo = get_parser_install_info(lang)
@ -429,50 +417,43 @@ local function install_lang0(lang, cache_dir, install_dir, generate_from_grammar
local project_name = 'tree-sitter-' .. lang local project_name = 'tree-sitter-' .. lang
generate_from_grammar = repo.requires_generate_from_grammar or generate_from_grammar
if generate_from_grammar and not executable('tree-sitter') then
return logger:error('tree-sitter CLI not found: `tree-sitter` is not executable')
end
if generate_from_grammar and not executable('node') then
return logger:error('Node JS not found: `node` is not executable')
end
local revision = get_target_revision(lang) local revision = get_target_revision(lang)
local maybe_local_path = fs.normalize(repo.url) local compile_location ---@type string
local from_local_path = fn.isdirectory(maybe_local_path) == 1 if repo.path then
if from_local_path then compile_location = fs.normalize(repo.path)
repo.url = maybe_local_path else
end
if not from_local_path then
util.delete(fs.joinpath(cache_dir, project_name))
local project_dir = fs.joinpath(cache_dir, project_name) local project_dir = fs.joinpath(cache_dir, project_name)
util.delete(project_dir)
revision = revision or repo.branch or 'master' revision = revision or repo.branch or 'main'
local do_download = can_download_tar(repo) and do_download_tar or do_download_git local do_download = can_download_tar(repo) and do_download_tar or do_download_git
local err = do_download(logger, repo, project_name, cache_dir, revision, project_dir) local err = do_download(logger, repo, project_name, cache_dir, revision, project_dir)
if err then if err then
return err return err
end end
compile_location = fs.joinpath(cache_dir, project_name)
end end
local compile_location = get_compile_location(repo, cache_dir, project_name, from_local_path) if repo.location then
compile_location = fs.joinpath(compile_location, repo.location)
end
if generate_from_grammar then do
local err = do_generate_from_grammar(logger, repo, compile_location) if repo.generate or generate then
if err then local err = do_generate_from_grammar(logger, repo, compile_location)
return err if err then
return err
end
end end
end end
logger:info('Compiling parser') do
local r = do_compile(repo, cc, compile_location) local err = do_compile(logger, repo, cc, compile_location)
if r.code > 0 then if err then
return logger:error('Error during compilation: %s', r.stderr) return err
end
end end
local parser_lib_name = fs.joinpath(install_dir, lang) .. '.so' local parser_lib_name = fs.joinpath(install_dir, lang) .. '.so'
@ -486,7 +467,7 @@ local function install_lang0(lang, cache_dir, install_dir, generate_from_grammar
local revfile = fs.joinpath(config.get_install_dir('parser-info') or '', lang .. '.revision') local revfile = fs.joinpath(config.get_install_dir('parser-info') or '', lang .. '.revision')
util.write_file(revfile, revision or '') util.write_file(revfile, revision or '')
if not from_local_path then if not repo.path then
util.delete(fs.joinpath(cache_dir, project_name)) util.delete(fs.joinpath(cache_dir, project_name))
end end
end end
@ -561,11 +542,6 @@ local function install(languages, options, _callback)
local generate_from_grammar = options.generate_from_grammar local generate_from_grammar = options.generate_from_grammar
local skip = options.skip local skip = options.skip
if not executable('git') then
log.error('Git is required on your system to run this command')
return
end
local cache_dir = vim.fs.normalize(fn.stdpath('cache')) local cache_dir = vim.fs.normalize(fn.stdpath('cache'))
local install_dir = config.get_install_dir('parser') local install_dir = config.get_install_dir('parser')

View file

@ -1,18 +1,19 @@
---@class InstallInfo ---@class InstallInfo
---@field path? string
---@field url string ---@field url string
---@field branch string|nil ---@field branch? string
---@field revision string|nil Used to override lockfile revision ---@field revision? string
---@field files string[] ---@field files string[]
---@field generate_requires_npm boolean|nil ---@field generate_requires_npm? boolean
---@field requires_generate_from_grammar boolean|nil ---@field generate? boolean
---@field location string|nil ---@field location? string
---@class ParserInfo ---@class ParserInfo
---@field install_info InstallInfo? ---@field install_info? InstallInfo
---@field maintainers string[]? ---@field maintainers? string[]
---@field requires string[]? ---@field requires? string[]
---@field tier integer ---@field tier integer
---@field readme_note string|nil? ---@field readme_note? string
local M = {} local M = {}
@ -1413,7 +1414,7 @@ M.configs = {
install_info = { install_info = {
url = 'https://github.com/latex-lsp/tree-sitter-latex', url = 'https://github.com/latex-lsp/tree-sitter-latex',
files = { 'src/parser.c', 'src/scanner.c' }, files = { 'src/parser.c', 'src/scanner.c' },
requires_generate_from_grammar = true, generate = true,
}, },
maintainers = { '@theHamsta', '@clason' }, maintainers = { '@theHamsta', '@clason' },
tier = 2, tier = 2,
@ -1591,7 +1592,7 @@ M.configs = {
install_info = { install_info = {
url = 'https://github.com/artagnon/tree-sitter-mlir', url = 'https://github.com/artagnon/tree-sitter-mlir',
files = { 'src/parser.c' }, files = { 'src/parser.c' },
requires_generate_from_grammar = true, generate = true,
}, },
maintainers = { '@artagnon' }, maintainers = { '@artagnon' },
tier = 4, tier = 4,
@ -1731,7 +1732,7 @@ M.configs = {
install_info = { install_info = {
url = 'https://github.com/atom-ocaml/tree-sitter-ocamllex', url = 'https://github.com/atom-ocaml/tree-sitter-ocamllex',
files = { 'src/parser.c', 'src/scanner.c' }, files = { 'src/parser.c', 'src/scanner.c' },
requires_generate_from_grammar = true, generate = true,
}, },
maintainers = { '@undu' }, maintainers = { '@undu' },
tier = 3, tier = 3,
@ -2246,7 +2247,7 @@ M.configs = {
install_info = { install_info = {
url = 'https://github.com/rockorager/tree-sitter-scfg', url = 'https://github.com/rockorager/tree-sitter-scfg',
files = { 'src/parser.c' }, files = { 'src/parser.c' },
requires_generate_from_grammar = true, generate = true,
}, },
maintainers = { '@WhyNotHugo' }, maintainers = { '@WhyNotHugo' },
tier = 3, tier = 3,
@ -2489,7 +2490,7 @@ M.configs = {
install_info = { install_info = {
url = 'https://github.com/alex-pinkus/tree-sitter-swift', url = 'https://github.com/alex-pinkus/tree-sitter-swift',
files = { 'src/parser.c', 'src/scanner.c' }, files = { 'src/parser.c', 'src/scanner.c' },
requires_generate_from_grammar = true, generate = true,
}, },
maintainers = { '@alex-pinkus' }, maintainers = { '@alex-pinkus' },
tier = 3, tier = 3,
@ -2544,7 +2545,7 @@ M.configs = {
install_info = { install_info = {
url = 'https://github.com/euclidianAce/tree-sitter-teal', url = 'https://github.com/euclidianAce/tree-sitter-teal',
files = { 'src/parser.c', 'src/scanner.c' }, files = { 'src/parser.c', 'src/scanner.c' },
requires_generate_from_grammar = true, generate = true,
}, },
maintainers = { '@euclidianAce' }, maintainers = { '@euclidianAce' },
tier = 3, tier = 3,
@ -2753,7 +2754,7 @@ M.configs = {
install_info = { install_info = {
url = 'https://github.com/kylegoetz/tree-sitter-unison', url = 'https://github.com/kylegoetz/tree-sitter-unison',
files = { 'src/parser.c', 'src/scanner.c' }, files = { 'src/parser.c', 'src/scanner.c' },
requires_generate_from_grammar = true, generate = true,
}, },
maintainers = { '@tapegram' }, maintainers = { '@tapegram' },
tier = 4, tier = 4,

View file

@ -91,6 +91,7 @@
(tuple_struct_pattern (tuple_struct_pattern
")" @indent.end) ")" @indent.end)
; Typing in "(" inside macro definitions breaks the tree entirely ; Typing in "(" inside macro definitions breaks the tree entirely
; Making macro_definition becoming errors ; Making macro_definition becoming errors
; Offset this by adding back one indent for start of macro rules ; Offset this by adding back one indent for start of macro rules