refactor(pack,ts): switch specs to src field and decentralize update hooks
This commit is contained in:
+94
-76
@@ -7,31 +7,30 @@ local M = {}
|
||||
---@field location? string
|
||||
---@field generate? boolean
|
||||
---@field from_json? boolean
|
||||
---@field filetypes? string[]
|
||||
|
||||
---@class ow.TS.RepoBase
|
||||
---@field [1] string
|
||||
---@field src string
|
||||
---@field version? string | vim.VersionRange
|
||||
|
||||
---@class ow.TS.Repo : ow.TS.RepoBase
|
||||
---@field name string
|
||||
---@field parsers ow.TS.Parser[]
|
||||
|
||||
---@class RepoState
|
||||
---@field repo ow.TS.Repo
|
||||
---@field path? string
|
||||
|
||||
---@class ow.TS.SingleRepoOpts : ow.TS.RepoBase
|
||||
---@class ow.TS.SingleSpec : ow.TS.RepoBase
|
||||
---@field lang? string
|
||||
---@field location? string
|
||||
---@field generate? boolean
|
||||
---@field from_json? boolean
|
||||
---@field filetypes? string[]
|
||||
|
||||
---@class ow.TS.MultiRepoOpts : ow.TS.RepoBase
|
||||
---@class ow.TS.MultiSpec : ow.TS.RepoBase
|
||||
---@field parsers ow.TS.Parser[]
|
||||
---@field generate? boolean
|
||||
---@field from_json? boolean
|
||||
|
||||
---@alias ow.TS.RepoOpts ow.TS.SingleRepoOpts | ow.TS.MultiRepoOpts
|
||||
---@alias ow.TS.Spec ow.TS.SingleSpec | ow.TS.MultiSpec
|
||||
|
||||
---@param path string
|
||||
---@param lang string
|
||||
@@ -53,6 +52,28 @@ local function start_treesitter(buf)
|
||||
end
|
||||
end
|
||||
|
||||
---@param parser ow.TS.Parser
|
||||
---@param so string
|
||||
local function register(parser, so)
|
||||
vim.treesitter.language.add(parser.lang, { path = so })
|
||||
local filetypes = { parser.lang }
|
||||
if parser.filetypes then
|
||||
vim.treesitter.language.register(parser.lang, parser.filetypes)
|
||||
vim.list_extend(filetypes, parser.filetypes)
|
||||
end
|
||||
|
||||
vim.api.nvim_create_autocmd("FileType", {
|
||||
group = vim.api.nvim_create_augroup(
|
||||
"ow.ts.parser." .. parser.lang,
|
||||
{ clear = true }
|
||||
),
|
||||
pattern = filetypes,
|
||||
callback = function(ev)
|
||||
start_treesitter(ev.buf)
|
||||
end,
|
||||
})
|
||||
end
|
||||
|
||||
---@param lang string
|
||||
local function activate_open_buffers(lang)
|
||||
local fts = vim.treesitter.language.get_filetypes(lang)
|
||||
@@ -66,10 +87,10 @@ local function activate_open_buffers(lang)
|
||||
end
|
||||
end
|
||||
|
||||
---@param state RepoState
|
||||
---@param repo ow.TS.Repo
|
||||
---@param parser ow.TS.Parser
|
||||
local function build(state, parser)
|
||||
local path = assert(state.path, "repo not installed: " .. state.repo.name)
|
||||
local function build(repo, parser)
|
||||
local path = assert(repo.path, "repo not installed: " .. repo.name)
|
||||
local cwd = parser.location and vim.fs.joinpath(path, parser.location)
|
||||
or path
|
||||
local out = parser_so(path, parser.lang)
|
||||
@@ -88,7 +109,7 @@ local function build(state, parser)
|
||||
)
|
||||
return
|
||||
end
|
||||
vim.treesitter.language.add(parser.lang, { path = out })
|
||||
register(parser, out)
|
||||
activate_open_buffers(parser.lang)
|
||||
end
|
||||
|
||||
@@ -137,13 +158,13 @@ local function build(state, parser)
|
||||
end
|
||||
end
|
||||
|
||||
---@param pack_name string
|
||||
---@param name string
|
||||
---@return string? lang
|
||||
---@return string? err
|
||||
local function lang_from_name(pack_name)
|
||||
local lang = pack_name:match("^tree%-sitter%-(.+)$")
|
||||
local function lang_from_name(name)
|
||||
local lang = name:match("^tree%-sitter%-(.+)$")
|
||||
if not lang then
|
||||
return nil, "cannot derive lang from pack name: " .. pack_name
|
||||
return nil, "cannot derive lang from pack name: " .. name
|
||||
end
|
||||
return lang
|
||||
end
|
||||
@@ -166,34 +187,30 @@ local function pick(child, parent)
|
||||
return parent
|
||||
end
|
||||
|
||||
---@param entry string | ow.TS.RepoOpts
|
||||
---@param spec string | ow.TS.Spec
|
||||
---@return ow.TS.Repo?
|
||||
local function normalize(entry)
|
||||
---@type ow.TS.RepoOpts
|
||||
local opts
|
||||
if type(entry) == "string" then
|
||||
opts = { entry } --[[@as ow.TS.SingleRepoOpts]]
|
||||
else
|
||||
opts = entry
|
||||
local function spec_to_repo(spec)
|
||||
if type(spec) == "string" then
|
||||
spec = { src = spec } --[[@as ow.TS.SingleSpec]]
|
||||
end
|
||||
local name = name_from_url(opts[1])
|
||||
local name = name_from_url(spec.src)
|
||||
|
||||
---@type ow.TS.Parser[]
|
||||
local parsers = {}
|
||||
local input_parsers = (opts --[[@as ow.TS.MultiRepoOpts]]).parsers
|
||||
if input_parsers then
|
||||
---@cast opts ow.TS.MultiRepoOpts
|
||||
for _, s in ipairs(opts.parsers) do
|
||||
if spec.parsers then
|
||||
---@cast spec ow.TS.MultiSpec
|
||||
for _, s in ipairs(spec.parsers) do
|
||||
table.insert(parsers, {
|
||||
lang = s.lang,
|
||||
location = s.location,
|
||||
generate = pick(s.generate, opts.generate),
|
||||
from_json = pick(s.from_json, opts.from_json),
|
||||
generate = pick(s.generate, spec.generate),
|
||||
from_json = pick(s.from_json, spec.from_json),
|
||||
filetypes = s.filetypes,
|
||||
})
|
||||
end
|
||||
else
|
||||
---@cast opts ow.TS.SingleRepoOpts
|
||||
local lang = opts.lang
|
||||
---@cast spec ow.TS.SingleSpec
|
||||
local lang = spec.lang
|
||||
if not lang then
|
||||
local derived, err = lang_from_name(name)
|
||||
if not derived then
|
||||
@@ -204,79 +221,80 @@ local function normalize(entry)
|
||||
end
|
||||
table.insert(parsers, {
|
||||
lang = lang,
|
||||
location = opts.location,
|
||||
generate = opts.generate,
|
||||
from_json = opts.from_json,
|
||||
location = spec.location,
|
||||
generate = spec.generate,
|
||||
from_json = spec.from_json,
|
||||
filetypes = spec.filetypes,
|
||||
})
|
||||
end
|
||||
|
||||
return {
|
||||
[1] = opts[1],
|
||||
src = spec.src,
|
||||
name = name,
|
||||
version = opts.version,
|
||||
version = spec.version,
|
||||
parsers = parsers,
|
||||
path = nil,
|
||||
}
|
||||
end
|
||||
|
||||
---@param opts (string | ow.TS.RepoOpts)[]
|
||||
function M.setup(opts)
|
||||
---@type ow.TS.Repo[]
|
||||
---@param specs (string | ow.TS.Spec)[]
|
||||
function M.setup(specs)
|
||||
---@type table<string, ow.TS.Repo>
|
||||
local repos = {}
|
||||
for _, entry in ipairs(opts) do
|
||||
local repo = normalize(entry)
|
||||
local pack = require("pack")
|
||||
for _, spec in ipairs(specs) do
|
||||
local repo = spec_to_repo(spec)
|
||||
if repo then
|
||||
table.insert(repos, repo)
|
||||
repos[repo.src] = repo
|
||||
end
|
||||
end
|
||||
|
||||
---@type vim.pack.Spec[]
|
||||
local pack_specs = vim.tbl_map(function(p)
|
||||
return { src = p[1], name = p.name, version = p.version }
|
||||
end, repos)
|
||||
|
||||
---@type RepoState[]
|
||||
local states = {}
|
||||
---@type table<string, RepoState>
|
||||
local by_src = {}
|
||||
for _, repo in ipairs(repos) do
|
||||
local state = { repo = repo }
|
||||
table.insert(states, state)
|
||||
by_src[repo[1]] = state
|
||||
local vim_specs = {}
|
||||
for _, repo in pairs(repos) do
|
||||
table.insert(
|
||||
vim_specs,
|
||||
{ src = repo.src, name = repo.name, version = repo.version }
|
||||
)
|
||||
end
|
||||
|
||||
local pack = require("pack")
|
||||
local changed = pack.install(pack_specs, function(data)
|
||||
local state = by_src[data.spec.src]
|
||||
if state then
|
||||
state.path = data.path
|
||||
local changed = pack.install(vim_specs, function(data)
|
||||
local repo = repos[data.spec.src]
|
||||
if repo then
|
||||
repo.path = data.path
|
||||
end
|
||||
end)
|
||||
|
||||
for _, state in ipairs(states) do
|
||||
pack.register_hook(state.repo.name, function(ev)
|
||||
state.path = ev.path
|
||||
for _, parser in ipairs(state.repo.parsers) do
|
||||
build(state, parser)
|
||||
end
|
||||
end)
|
||||
if not state.path then
|
||||
log.error("Repo not installed: %s", state.repo.name)
|
||||
for _, repo in pairs(repos) do
|
||||
if not repo.path then
|
||||
log.error("Repo not installed: %s", repo.name)
|
||||
goto continue
|
||||
end
|
||||
for _, parser in ipairs(state.repo.parsers) do
|
||||
local so = parser_so(state.path, parser.lang)
|
||||
if changed[state.repo.name] or not vim.uv.fs_stat(so) then
|
||||
build(state, parser)
|
||||
for _, parser in ipairs(repo.parsers) do
|
||||
local so = parser_so(repo.path, parser.lang)
|
||||
if changed[repo.src] or not vim.uv.fs_stat(so) then
|
||||
build(repo, parser)
|
||||
else
|
||||
vim.treesitter.language.add(parser.lang, { path = so })
|
||||
register(parser, so)
|
||||
end
|
||||
end
|
||||
::continue::
|
||||
end
|
||||
|
||||
vim.api.nvim_create_autocmd("FileType", {
|
||||
vim.api.nvim_create_autocmd("PackChanged", {
|
||||
group = vim.api.nvim_create_augroup("ow.ts.updates", { clear = true }),
|
||||
callback = function(ev)
|
||||
start_treesitter(ev.buf)
|
||||
if ev.data.kind ~= "update" then
|
||||
return
|
||||
end
|
||||
local repo = repos[ev.data.spec.src]
|
||||
if not repo then
|
||||
return
|
||||
end
|
||||
repo.path = ev.data.path
|
||||
for _, parser in ipairs(repo.parsers) do
|
||||
build(repo, parser)
|
||||
end
|
||||
end,
|
||||
})
|
||||
end
|
||||
|
||||
Reference in New Issue
Block a user