refactor(ts): rewrite around Repo/Parser types
This commit is contained in:
@@ -44,85 +44,52 @@ require("pack").setup({
|
|||||||
})
|
})
|
||||||
|
|
||||||
require("ts").setup({
|
require("ts").setup({
|
||||||
parsers = {
|
"https://github.com/tree-sitter/tree-sitter-bash",
|
||||||
"https://github.com/tree-sitter/tree-sitter-bash",
|
-- required by cpp
|
||||||
-- required by cpp
|
"https://github.com/tree-sitter/tree-sitter-c",
|
||||||
"https://github.com/tree-sitter/tree-sitter-c",
|
"https://github.com/uyha/tree-sitter-cmake",
|
||||||
"https://github.com/uyha/tree-sitter-cmake",
|
"https://github.com/stsewd/tree-sitter-comment",
|
||||||
"https://github.com/stsewd/tree-sitter-comment",
|
"https://github.com/tree-sitter/tree-sitter-cpp",
|
||||||
"https://github.com/tree-sitter/tree-sitter-cpp",
|
-- required by scss
|
||||||
-- required by scss
|
"https://github.com/tree-sitter/tree-sitter-css",
|
||||||
"https://github.com/tree-sitter/tree-sitter-css",
|
"https://github.com/gbprod/tree-sitter-gitcommit",
|
||||||
"https://github.com/gbprod/tree-sitter-gitcommit",
|
"https://github.com/tree-sitter/tree-sitter-go",
|
||||||
"https://github.com/tree-sitter/tree-sitter-go",
|
{
|
||||||
{
|
"https://github.com/ngalaiko/tree-sitter-go-template",
|
||||||
"https://github.com/ngalaiko/tree-sitter-go-template",
|
lang = "gotmpl",
|
||||||
lang = "gotmpl",
|
|
||||||
},
|
|
||||||
"https://github.com/tree-sitter/tree-sitter-html",
|
|
||||||
"https://github.com/tree-sitter/tree-sitter-json",
|
|
||||||
"https://github.com/tree-sitter-grammars/tree-sitter-luadoc",
|
|
||||||
{
|
|
||||||
"https://github.com/tree-sitter/tree-sitter-php",
|
|
||||||
specs = {
|
|
||||||
{ lang = "php", location = "php" },
|
|
||||||
{ lang = "php_only", location = "php_only" },
|
|
||||||
},
|
|
||||||
},
|
|
||||||
"https://github.com/tree-sitter/tree-sitter-python",
|
|
||||||
"https://github.com/tree-sitter/tree-sitter-rust",
|
|
||||||
"https://github.com/serenadeai/tree-sitter-scss",
|
|
||||||
{
|
|
||||||
"https://github.com/derekstride/tree-sitter-sql",
|
|
||||||
version = "gh-pages",
|
|
||||||
},
|
|
||||||
"https://github.com/tree-sitter-grammars/tree-sitter-svelte",
|
|
||||||
{
|
|
||||||
"https://github.com/tree-sitter/tree-sitter-typescript",
|
|
||||||
specs = {
|
|
||||||
{ lang = "typescript", location = "typescript" },
|
|
||||||
{ lang = "tsx", location = "tsx" },
|
|
||||||
},
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"https://github.com/tree-sitter-grammars/tree-sitter-xml",
|
|
||||||
specs = {
|
|
||||||
{ lang = "xml", location = "xml" },
|
|
||||||
{ lang = "dtd", location = "dtd" },
|
|
||||||
},
|
|
||||||
},
|
|
||||||
"https://github.com/tree-sitter-grammars/tree-sitter-yaml",
|
|
||||||
"https://github.com/georgeharker/tree-sitter-zsh",
|
|
||||||
},
|
},
|
||||||
languages = {
|
"https://github.com/tree-sitter/tree-sitter-html",
|
||||||
"bash",
|
"https://github.com/tree-sitter/tree-sitter-json",
|
||||||
"c", -- builtin
|
"https://github.com/tree-sitter-grammars/tree-sitter-luadoc",
|
||||||
"cmake",
|
{
|
||||||
"comment",
|
"https://github.com/tree-sitter/tree-sitter-php",
|
||||||
"cpp",
|
parsers = {
|
||||||
"css",
|
{ lang = "php", location = "php" },
|
||||||
"gitcommit",
|
{ lang = "php_only", location = "php_only" },
|
||||||
"go",
|
},
|
||||||
"gotmpl",
|
|
||||||
"html",
|
|
||||||
"json",
|
|
||||||
"lua", -- builtin
|
|
||||||
"luadoc",
|
|
||||||
"markdown", -- builtin
|
|
||||||
"markdown_inline", -- builtin
|
|
||||||
"php",
|
|
||||||
"python",
|
|
||||||
"query", -- builtin
|
|
||||||
"rust",
|
|
||||||
"scss",
|
|
||||||
"sql",
|
|
||||||
"svelte",
|
|
||||||
"tsx",
|
|
||||||
"typescript",
|
|
||||||
"vim", -- builtin
|
|
||||||
"vimdoc", -- builtin
|
|
||||||
"xml",
|
|
||||||
"yaml",
|
|
||||||
"zsh",
|
|
||||||
},
|
},
|
||||||
|
"https://github.com/tree-sitter/tree-sitter-python",
|
||||||
|
"https://github.com/tree-sitter/tree-sitter-rust",
|
||||||
|
"https://github.com/serenadeai/tree-sitter-scss",
|
||||||
|
{
|
||||||
|
"https://github.com/derekstride/tree-sitter-sql",
|
||||||
|
version = "gh-pages",
|
||||||
|
},
|
||||||
|
"https://github.com/tree-sitter-grammars/tree-sitter-svelte",
|
||||||
|
{
|
||||||
|
"https://github.com/tree-sitter/tree-sitter-typescript",
|
||||||
|
parsers = {
|
||||||
|
{ lang = "typescript", location = "typescript" },
|
||||||
|
{ lang = "tsx", location = "tsx" },
|
||||||
|
},
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"https://github.com/tree-sitter-grammars/tree-sitter-xml",
|
||||||
|
parsers = {
|
||||||
|
{ lang = "xml", location = "xml" },
|
||||||
|
{ lang = "dtd", location = "dtd" },
|
||||||
|
},
|
||||||
|
},
|
||||||
|
"https://github.com/tree-sitter-grammars/tree-sitter-yaml",
|
||||||
|
"https://github.com/georgeharker/tree-sitter-zsh",
|
||||||
})
|
})
|
||||||
|
|||||||
+105
-105
@@ -2,33 +2,27 @@ local log = require("log")
|
|||||||
|
|
||||||
local M = {}
|
local M = {}
|
||||||
|
|
||||||
---@class ow.TS.ParserSpec
|
---@class ow.TS.Parser
|
||||||
---@field lang string
|
---@field lang string
|
||||||
---@field location? string
|
---@field location? string
|
||||||
---@field generate? boolean
|
---@field generate? boolean
|
||||||
---@field from_json? boolean
|
---@field from_json? boolean
|
||||||
|
|
||||||
---@class ow.TS.SingleParser
|
---@class ow.TS.RepoBase
|
||||||
---@field [1] string
|
---@field [1] string
|
||||||
---@field name? string
|
|
||||||
---@field version? string | vim.VersionRange
|
---@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
|
|
||||||
|
|
||||||
---@class ow.TS.MultiParser
|
---@class ow.TS.Repo : ow.TS.RepoBase
|
||||||
---@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 name string
|
||||||
---@field path string
|
---@field parsers ow.TS.Parser[]
|
||||||
---@field specs ow.TS.ParserSpec[]
|
---@field path? string
|
||||||
|
|
||||||
|
---@class ow.TS.ParserOpts : ow.TS.Parser
|
||||||
|
---@field lang? string
|
||||||
|
|
||||||
|
---@class ow.TS.RepoOpts : ow.TS.RepoBase, ow.TS.ParserOpts
|
||||||
|
---@field name? string
|
||||||
|
---@field parsers? ow.TS.Parser[]
|
||||||
|
|
||||||
---@param path string
|
---@param path string
|
||||||
---@param lang string
|
---@param lang string
|
||||||
@@ -39,9 +33,7 @@ end
|
|||||||
|
|
||||||
---@param buf integer
|
---@param buf integer
|
||||||
local function start_treesitter(buf)
|
local function start_treesitter(buf)
|
||||||
local ok, err = pcall(vim.treesitter.start, buf)
|
if not pcall(vim.treesitter.start, buf) then
|
||||||
if not ok then
|
|
||||||
log.error("Failed to enable treesitter for buffer %d: %s", buf, err)
|
|
||||||
return
|
return
|
||||||
end
|
end
|
||||||
for _, win in ipairs(vim.fn.win_findbuf(buf)) do
|
for _, win in ipairs(vim.fn.win_findbuf(buf)) do
|
||||||
@@ -63,25 +55,26 @@ local function activate_open_buffers(lang)
|
|||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
|
---@param repo ow.TS.Repo
|
||||||
---@param parser ow.TS.Parser
|
---@param parser ow.TS.Parser
|
||||||
---@param spec ow.TS.ParserSpec
|
function M.build(repo, parser)
|
||||||
function M.build(parser, spec)
|
local path = assert(repo.path, "repo not installed: " .. repo.name)
|
||||||
local cwd = spec.location and vim.fs.joinpath(parser.path, spec.location)
|
local cwd = parser.location and vim.fs.joinpath(path, parser.location)
|
||||||
or parser.path
|
or path
|
||||||
local out = parser_so(parser.path, spec.lang)
|
local out = parser_so(path, parser.lang)
|
||||||
vim.fn.mkdir(vim.fs.dirname(out), "p")
|
vim.fn.mkdir(vim.fs.dirname(out), "p")
|
||||||
|
|
||||||
local function on_build(r)
|
local function on_build(r)
|
||||||
if r.code ~= 0 then
|
if r.code ~= 0 then
|
||||||
log.error(
|
log.error(
|
||||||
"Failed to build parser for %s: %s",
|
"Failed to build parser for %s: %s",
|
||||||
spec.lang,
|
parser.lang,
|
||||||
r.stderr or ""
|
r.stderr or ""
|
||||||
)
|
)
|
||||||
return
|
return
|
||||||
end
|
end
|
||||||
vim.treesitter.language.add(spec.lang, { path = out })
|
vim.treesitter.language.add(parser.lang, { path = out })
|
||||||
activate_open_buffers(spec.lang)
|
activate_open_buffers(parser.lang)
|
||||||
end
|
end
|
||||||
|
|
||||||
local function do_build()
|
local function do_build()
|
||||||
@@ -92,14 +85,14 @@ function M.build(parser, spec)
|
|||||||
)
|
)
|
||||||
end
|
end
|
||||||
|
|
||||||
if spec.generate then
|
if parser.generate then
|
||||||
local cmd = {
|
local cmd = {
|
||||||
"tree-sitter",
|
"tree-sitter",
|
||||||
"generate",
|
"generate",
|
||||||
"--abi",
|
"--abi",
|
||||||
tostring(vim.treesitter.language_version),
|
tostring(vim.treesitter.language_version),
|
||||||
}
|
}
|
||||||
if spec.from_json ~= false then
|
if parser.from_json ~= false then
|
||||||
table.insert(cmd, "src/grammar.json")
|
table.insert(cmd, "src/grammar.json")
|
||||||
end
|
end
|
||||||
vim.system(
|
vim.system(
|
||||||
@@ -112,7 +105,7 @@ function M.build(parser, spec)
|
|||||||
if r.code ~= 0 then
|
if r.code ~= 0 then
|
||||||
log.error(
|
log.error(
|
||||||
"Failed to generate parser for %s: %s",
|
"Failed to generate parser for %s: %s",
|
||||||
spec.lang,
|
parser.lang,
|
||||||
r.stderr or ""
|
r.stderr or ""
|
||||||
)
|
)
|
||||||
return
|
return
|
||||||
@@ -135,95 +128,102 @@ local function lang_from_name(pack_name)
|
|||||||
return lang
|
return lang
|
||||||
end
|
end
|
||||||
|
|
||||||
---@param entry ow.TS.ParserEntry
|
---@param url string
|
||||||
---@return vim.pack.Spec
|
---@return string
|
||||||
local function entry_pack_spec(entry)
|
local function name_from_url(url)
|
||||||
|
local name = url:match("([^/]+)$") or url
|
||||||
|
return (name:gsub("%.git$", ""))
|
||||||
|
end
|
||||||
|
|
||||||
|
---@param entry string | ow.TS.RepoOpts
|
||||||
|
---@return ow.TS.Repo
|
||||||
|
local function normalize(entry)
|
||||||
|
---@type ow.TS.RepoOpts
|
||||||
|
local opts
|
||||||
if type(entry) == "string" then
|
if type(entry) == "string" then
|
||||||
return { src = entry }
|
opts = { entry } --[[@as ow.TS.RepoOpts]]
|
||||||
end
|
else
|
||||||
return {
|
opts = entry
|
||||||
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[]
|
|
||||||
local function entry_specs(entry, pack_name)
|
|
||||||
if type(entry) == "string" then
|
|
||||||
return { { lang = lang_from_name(pack_name) } }
|
|
||||||
end
|
|
||||||
if entry.specs then
|
|
||||||
return entry.specs
|
|
||||||
end
|
|
||||||
---@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 seen = {}
|
|
||||||
for _, lang in ipairs(languages) do
|
|
||||||
for _, ft in ipairs(vim.treesitter.language.get_filetypes(lang)) do
|
|
||||||
seen[ft] = true
|
|
||||||
end
|
|
||||||
end
|
|
||||||
return vim.tbl_keys(seen)
|
|
||||||
end
|
|
||||||
|
|
||||||
---@param opts { parsers: ow.TS.ParserEntry[], languages: string[] }
|
|
||||||
function M.setup(opts)
|
|
||||||
---@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
|
end
|
||||||
|
local name = opts.name or name_from_url(opts[1])
|
||||||
|
|
||||||
---@type ow.TS.Parser[]
|
---@type ow.TS.Parser[]
|
||||||
local parsers = {}
|
local parsers = {}
|
||||||
local changed = require("pack").install(pack_specs, function(data)
|
local input_parsers = opts.parsers
|
||||||
if not data.spec.name then
|
if input_parsers and vim.islist(input_parsers) then
|
||||||
log.error("Missing name for parser: %s", data.spec.src)
|
for _, s in ipairs(input_parsers) do
|
||||||
return
|
table.insert(parsers, {
|
||||||
end
|
lang = s.lang,
|
||||||
local entry = entries_by_src[data.spec.src]
|
location = s.location,
|
||||||
if not entry then
|
generate = s.generate ~= nil and s.generate or opts.generate,
|
||||||
return
|
from_json = s.from_json ~= nil and s.from_json
|
||||||
|
or opts.from_json,
|
||||||
|
})
|
||||||
end
|
end
|
||||||
|
else
|
||||||
table.insert(parsers, {
|
table.insert(parsers, {
|
||||||
name = data.spec.name,
|
lang = opts.lang or lang_from_name(name),
|
||||||
path = data.path,
|
location = opts.location,
|
||||||
specs = entry_specs(entry, data.spec.name),
|
generate = opts.generate,
|
||||||
|
from_json = opts.from_json,
|
||||||
})
|
})
|
||||||
|
end
|
||||||
|
|
||||||
|
return {
|
||||||
|
[1] = opts[1],
|
||||||
|
name = name,
|
||||||
|
version = opts.version,
|
||||||
|
parsers = parsers,
|
||||||
|
}
|
||||||
|
end
|
||||||
|
|
||||||
|
---@param opts (string | ow.TS.RepoOpts)[]
|
||||||
|
function M.setup(opts)
|
||||||
|
---@type ow.TS.Repo[]
|
||||||
|
local repos = vim.tbl_map(normalize, opts)
|
||||||
|
|
||||||
|
---@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 table<string, ow.TS.Repo>
|
||||||
|
local by_src = {}
|
||||||
|
for _, p in ipairs(repos) do
|
||||||
|
by_src[p[1]] = p
|
||||||
|
end
|
||||||
|
|
||||||
|
local pack = require("pack")
|
||||||
|
local changed = pack.install(pack_specs, function(data)
|
||||||
|
local repo = by_src[data.spec.src]
|
||||||
|
if repo then
|
||||||
|
repo.path = data.path
|
||||||
|
end
|
||||||
end)
|
end)
|
||||||
|
|
||||||
for _, parser in ipairs(parsers) do
|
for _, repo in ipairs(repos) do
|
||||||
for _, spec in ipairs(parser.specs) do
|
pack.register_hook(repo.name, function(ev)
|
||||||
local so = parser_so(parser.path, spec.lang)
|
repo.path = ev.path
|
||||||
if changed[parser.name] or not vim.uv.fs_stat(so) then
|
for _, parser in ipairs(repo.parsers) do
|
||||||
M.build(parser, spec)
|
M.build(repo, parser)
|
||||||
|
end
|
||||||
|
end)
|
||||||
|
if not repo.path then
|
||||||
|
log.error("Repo not installed: %s", repo.name)
|
||||||
|
goto continue
|
||||||
|
end
|
||||||
|
for _, parser in ipairs(repo.parsers) do
|
||||||
|
local so = parser_so(repo.path, parser.lang)
|
||||||
|
if changed[repo.name] or not vim.uv.fs_stat(so) then
|
||||||
|
M.build(repo, parser)
|
||||||
else
|
else
|
||||||
vim.treesitter.language.add(spec.lang, { path = so })
|
vim.treesitter.language.add(parser.lang, { path = so })
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
::continue::
|
||||||
end
|
end
|
||||||
|
|
||||||
vim.api.nvim_create_autocmd("FileType", {
|
vim.api.nvim_create_autocmd("FileType", {
|
||||||
pattern = collect_filetypes(opts.languages),
|
|
||||||
callback = function(ev)
|
callback = function(ev)
|
||||||
start_treesitter(ev.buf)
|
start_treesitter(ev.buf)
|
||||||
end,
|
end,
|
||||||
|
|||||||
Reference in New Issue
Block a user