repositories / dotfiles
dotfiles
bugabingas dorkfiles
owned by admin
neovim/lua/bugabinga/treesitter.lua
Rawlocal puk = require 'std.puk'
local auto = require 'std.auto'
local ignored = require 'std.ignored'
local dbg = require 'std.dbg'
local add_dependency = require 'bugabinga.health'.add_dependency
add_dependency { name_of_executable = 'zig' }
add_dependency { name = 'TreeSitter CLI', name_of_executable = 'tree-sitter' }
add_dependency { name_of_executable = 'tar' }
add_dependency { name_of_executable = 'curl' }
local function is_ignored( match )
for _, v in ipairs( ignored.filetypes ) do
if v == match or match:match( v ) then
return true
end
end
return false
end
local treesitter = puk 'nvim-treesitter'
treesitter.setup {}
treesitter.install { 'bash', 'nu', 'ini' }
-- TODO: in 0.12 check if this behaviour is builtin on vim.treesitter.start()
-- if so, remove my stuff
auto 'look_for_treesitter_parser' {
description = 'Try loading a treesitter parser, if available.',
events = 'FileType',
pattern = '*',
command = function ( data )
local match = data.match
if match == 'text' then return end
if is_ignored( match ) then
dbg( 'Ignore %s for treesitter.', match )
return
end
local ok, _ = pcall( vim.treesitter.start, data.buf )
if not ok then
-- resolve filetype to its treesitter language (sh -> bash, quadlet -> ini,
-- cucumber -> gherkin) before consulting the registry
local lang = vim.treesitter.language.get_lang( match ) or match
local available = require 'nvim-treesitter.parsers'
local potential_parser = available[lang]
if potential_parser then
vim.notify( 'Found potential treesitter parser for current filetype. Trying to install it. ' .. lang )
local task = require 'nvim-treesitter.install'.install( lang, { force = false } )
task:await( function ( err, success )
if not success then
vim.notify( 'Failed to install treesitter parser: ' .. lang, vim.log.levels.ERROR )
return
end
vim.schedule( function ()
if pcall( vim.treesitter.start, data.buf ) then
vim.opt_local.foldmethod = 'expr'
vim.opt_local.foldexpr = 'v:lua.vim.treesitter.foldexpr()'
else
-- revision marker lied (e.g. .so wiped out of band): force rebuild once
local repair = require 'nvim-treesitter.install'.install( { lang }, { force = true } )
repair:await( function ( _, repaired )
if repaired then
vim.schedule( function()
pcall( vim.treesitter.start, data.buf )
vim.opt_local.foldmethod = 'expr'
vim.opt_local.foldexpr = 'v:lua.vim.treesitter.foldexpr()'
end )
end
end )
end
end )
end )
end
return
end
vim.opt_local.foldmethod = 'expr'
vim.opt_local.foldexpr = 'v:lua.vim.treesitter.foldexpr()'
end,
}
-- add d2 parser; the .so and queries are installed out of band by puk (puk.parsers.nuon)
vim.treesitter.language.register( 'd2', 'd2' )
-- systemd units and podman quadlets are INI-dialect files
vim.treesitter.language.register( 'ini', 'systemd' )
-- podman quadlet units get their own semantic filetype, same INI grammar
vim.treesitter.language.register( 'ini', 'quadlet' )
-- sh files are POSIX-ish, the bash grammar covers them
vim.treesitter.language.register( 'bash', 'sh' )
-- opentofu files are HCL
vim.treesitter.language.register( 'hcl', { 'opentofu', 'opentofu-vars' } )
-- nvim calls the gherkin filetype 'cucumber'
vim.treesitter.language.register( 'gherkin', 'cucumber' )
-- surface treesitter parse errors as ERROR diagnostics in every language;
-- refreshed on change/insert-leave/save, so typing stays flicker-free
local ts_errors = vim.api.nvim_create_namespace 'treesitter-errors'
local function collect_errors( node, out )
if node:type() == 'ERROR' or node:missing() then
local lnum, col = node:range()
out[#out + 1] = { lnum = lnum, col = col, end_lnum = lnum, end_col = col, message = 'syntax error', severity = vim.diagnostic.severity.ERROR, source = 'treesitter' }
return
end
for child in node:iter_children() do
collect_errors( child, out )
end
end
local function show_ts_errors( args )
local ok, parser = pcall( vim.treesitter.get_parser, args.buf )
if not ok or not parser then return end
local trees = parser:parse()
if not trees then return end
local tree = trees[1]
if not tree then return end
local out = {}
local root = tree:root()
if not root then return end
collect_errors( root, out )
vim.diagnostic.set( ts_errors, args.buf, out )
end
auto 'treesitter_error_diagnostics' {
description = 'Report treesitter parse errors as ERROR diagnostics.',
events = { 'FileType', 'TextChanged', 'InsertLeave', 'BufWritePost' },
pattern = '*',
command = function( args )
vim.schedule( function() show_ts_errors( args ) end )
end,
}
-- this module loads deferred, so the startup file's FileType event has already
-- passed: catch up on every loaded buffer once
vim.schedule( function()
for _, buf in ipairs( vim.api.nvim_list_bufs() ) do
if vim.api.nvim_buf_is_loaded( buf ) then
show_ts_errors( { buf = buf } )
end
end
end )
-- runs out of band via puk post-install (headless nvim, editor closed):
-- updates nvim-treesitter-managed parsers to the revisions pinned upstream.
local function post_install()
require 'nvim-treesitter.install'.update():wait()
end
_G.BugabingaTreesitter = {
is_ignored = is_ignored,
post_install = post_install,
}
return BugabingaTreesitter