refactor(ts): own parser installation, split from pack

This commit is contained in:
2026-04-20 22:50:16 +02:00
parent c7dd083083
commit 21f566112e
3 changed files with 201 additions and 212 deletions
+108 -38
View File
@@ -8,13 +8,33 @@ local M = {}
---@field generate? boolean
---@field from_json? boolean
---@alias ow.TS.ParserField string | ow.TS.ParserSpec | ow.TS.ParserSpec[]
---@class ow.TS.SingleParser
---@field [1] string
---@field name? string
---@field version? string | vim.VersionRange
---@field lang? string inferred from URL (strip "tree-sitter-" prefix) if omitted
---@field location? string
---@field generate? boolean
---@field from_json? boolean
---@param plugin ow.Pack.Plugin
---@class ow.TS.MultiParser
---@field [1] string
---@field name? string
---@field version? string | vim.VersionRange
---@field specs ow.TS.ParserSpec[]
---@alias ow.TS.ParserEntry string | ow.TS.SingleParser | ow.TS.MultiParser
---@class ow.TS.Parser
---@field name string
---@field path string
---@field specs ow.TS.ParserSpec[]
---@param path string
---@param lang string
---@return string
local function parser_path(plugin, lang)
return vim.fs.joinpath(plugin.path, "parser", lang .. ".so")
local function parser_so(path, lang)
return vim.fs.joinpath(path, "parser", lang .. ".so")
end
---@param buf integer
@@ -43,12 +63,12 @@ local function activate_open_buffers(lang)
end
end
---@param plugin ow.Pack.Plugin
---@param parser ow.TS.Parser
---@param spec ow.TS.ParserSpec
function M.build(plugin, spec)
local cwd = spec.location and vim.fs.joinpath(plugin.path, spec.location)
or plugin.path
local out = parser_path(plugin, spec.lang)
function M.build(parser, spec)
local cwd = spec.location and vim.fs.joinpath(parser.path, spec.location)
or parser.path
local out = parser_so(parser.path, spec.lang)
vim.fn.mkdir(vim.fs.dirname(out), "p")
local function on_build(r)
@@ -105,49 +125,99 @@ function M.build(plugin, spec)
end
end
---@param field ow.TS.ParserField
---@param pack_name string
---@return string
local function lang_from_name(pack_name)
local lang = pack_name:match("^tree%-sitter%-(.+)$")
if not lang then
error("cannot derive lang from pack name: " .. pack_name)
end
return lang
end
---@param entry ow.TS.ParserEntry
---@return vim.pack.Spec
local function entry_pack_spec(entry)
if type(entry) == "string" then
return { src = entry }
end
return {
src = entry[1],
name = entry.name,
version = entry.version,
}
end
---@param entry ow.TS.ParserEntry
---@param pack_name string fallback for lang when the entry doesn't set it
---@return ow.TS.ParserSpec[]
function M.normalize(field)
if type(field) == "string" then
return { { lang = field } }
local function entry_specs(entry, pack_name)
if type(entry) == "string" then
return { { lang = lang_from_name(pack_name) } }
end
if type(field) == "table" then
if type(field.lang) == "string" then
---@cast field ow.TS.ParserSpec
return { field }
end
if type(field[1]) == "table" then
---@cast field ow.TS.ParserSpec[]
return field
end
if entry.specs then
return entry.specs
end
error("invalid ts_parser value: " .. vim.inspect(field))
---@cast entry ow.TS.SingleParser
return {
{
lang = entry.lang or lang_from_name(pack_name),
location = entry.location,
generate = entry.generate,
from_json = entry.from_json,
},
}
end
---@param languages string[]
---@return string[]
local function collect_filetypes(languages)
local filetypes = {}
local seen = {}
for _, lang in ipairs(languages) do
for _, ft in ipairs(vim.treesitter.language.get_filetypes(lang)) do
if not vim.list_contains(filetypes, ft) then
table.insert(filetypes, ft)
end
seen[ft] = true
end
end
return filetypes
return vim.tbl_keys(seen)
end
---@param opts { languages: string[] }
---@param opts { parsers: ow.TS.ParserEntry[], languages: string[] }
function M.setup(opts)
for _, plugin in ipairs(require("pack").plugins) do
if plugin.ts_parser then
for _, p in ipairs(M.normalize(plugin.ts_parser)) do
local path = parser_path(plugin, p.lang)
if vim.uv.fs_stat(path) then
vim.treesitter.language.add(p.lang, { path = path })
end
---@type vim.pack.Spec[]
local pack_specs = {}
---@type table<string, ow.TS.ParserEntry>
local entries_by_src = {}
for _, entry in ipairs(opts.parsers) do
local pack_spec = entry_pack_spec(entry)
table.insert(pack_specs, pack_spec)
entries_by_src[pack_spec.src] = entry
end
---@type ow.TS.Parser[]
local parsers = {}
local changed = require("pack").install(pack_specs, function(data)
if not data.spec.name then
log.error("Missing name for parser: %s", data.spec.src)
return
end
local entry = entries_by_src[data.spec.src]
if not entry then
return
end
table.insert(parsers, {
name = data.spec.name,
path = data.path,
specs = entry_specs(entry, data.spec.name),
})
end)
for _, parser in ipairs(parsers) do
for _, spec in ipairs(parser.specs) do
local so = parser_so(parser.path, spec.lang)
if changed[parser.name] or not vim.uv.fs_stat(so) then
M.build(parser, spec)
else
vim.treesitter.language.add(spec.lang, { path = so })
end
end
end