feat(treesitter): replace nvim-treesitter with pack-managed parsers

This commit is contained in:
2026-04-12 11:46:54 +02:00
parent ec81afbab7
commit cf898d1fee
107 changed files with 8555 additions and 13 deletions
+159
View File
@@ -0,0 +1,159 @@
local log = require("log")
local M = {}
---@class ow.TS.ParserSpec
---@field lang string
---@field location? string
---@field generate? boolean
---@field from_json? boolean
---@alias ow.TS.ParserField string | ow.TS.ParserSpec | ow.TS.ParserSpec[]
---@param plugin ow.Pack.Plugin
---@param lang string
---@return string
local function parser_path(plugin, lang)
return vim.fs.joinpath(plugin.path, "parser", lang .. ".so")
end
---@param buf integer
local function start_treesitter(buf)
local ok, err = pcall(vim.treesitter.start, buf)
if not ok then
log.error(
"Failed to enable treesitter for buffer %d: %s",
buf,
err
)
return
end
for _, win in ipairs(vim.fn.win_findbuf(buf)) do
vim.wo[win].foldmethod = "expr"
vim.wo[win].foldexpr = "v:lua.vim.treesitter.foldexpr()"
end
end
---@param lang string
local function activate_open_buffers(lang)
local fts = vim.treesitter.language.get_filetypes(lang)
for _, buf in ipairs(vim.api.nvim_list_bufs()) do
if vim.api.nvim_buf_is_loaded(buf)
and vim.list_contains(fts, vim.bo[buf].filetype)
then
start_treesitter(buf)
end
end
end
---@param plugin ow.Pack.Plugin
---@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)
vim.fn.mkdir(vim.fs.dirname(out), "p")
local function on_build(r)
if r.code ~= 0 then
log.error(
"Failed to build parser for %s: %s",
spec.lang,
r.stderr or ""
)
return
end
vim.treesitter.language.add(spec.lang, { path = out })
activate_open_buffers(spec.lang)
end
local function do_build()
vim.system(
{ "tree-sitter", "build", "-o", out },
{ cwd = cwd },
vim.schedule_wrap(on_build)
)
end
if spec.generate then
local cmd = {
"tree-sitter", "generate",
"--abi", tostring(vim.treesitter.language_version),
}
if spec.from_json ~= false then
table.insert(cmd, "src/grammar.json")
end
vim.system(cmd, {
cwd = cwd,
env = { TREE_SITTER_JS_RUNTIME = "native" },
}, vim.schedule_wrap(function(r)
if r.code ~= 0 then
log.error(
"Failed to generate parser for %s: %s",
spec.lang,
r.stderr or ""
)
return
end
do_build()
end))
else
do_build()
end
end
---@param field ow.TS.ParserField
---@return ow.TS.ParserSpec[]
function M.normalize(field)
if type(field) == "string" then
return { { lang = field } }
end
if type(field) == "table" then
if type(field.lang) == "string" then
return { field }
end
if type(field[1]) == "table" then
return field
end
end
error("invalid ts_parser value: " .. vim.inspect(field))
end
---@param languages string[]
local function collect_filetypes(languages)
local filetypes = {}
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
end
end
return filetypes
end
---@param opts { 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
end
end
end
vim.api.nvim_create_autocmd("FileType", {
pattern = collect_filetypes(opts.languages),
callback = function(ev)
start_treesitter(ev.buf)
end,
})
end
return M