87 lines
3.5 KiB
Lua
87 lines
3.5 KiB
Lua
-- builtin/runtime/syntax.lua --- T M4.2 auto-attach grammar by extension.
|
|
--
|
|
-- The Rust side (`crate::syntax::BUILTIN_LANGUAGES`) keeps a config
|
|
-- table of `name → loader + extensions`. This file is the Lua-side
|
|
-- glue that fires on every successful file load: it asks
|
|
-- `pmacs.parse.language_for_path` whether the loaded file maps to a
|
|
-- known grammar, and dispatches a parse if so. The settle path is
|
|
-- handled by an after-tick step that drains finished parse jobs and
|
|
-- installs them into the buffer's view.
|
|
--
|
|
-- Adding a new grammar requires only:
|
|
-- 1. A `tree-sitter-foo = "X.Y"` line in `Cargo.toml`.
|
|
-- 2. A new `LanguageEntry` in `crate::syntax::BUILTIN_LANGUAGES`.
|
|
-- Nothing in this file needs to change.
|
|
|
|
-- Set of dispatched-but-not-yet-installed parse job ids. Populated
|
|
-- when `pmacs.parse._dispatch` is called below; drained by the tick
|
|
-- step.
|
|
local pending_parse_jobs = {}
|
|
|
|
local raw_dispatch = pmacs.parse._dispatch
|
|
|
|
-- Wrap `_dispatch` so every dispatched parse job lands in our
|
|
-- pending set. Calls into `_dispatch` from outside this file (e.g.
|
|
-- a hand-rolled user script) get the same tracking for free.
|
|
function pmacs.parse._dispatch(buf, lang)
|
|
local job_id = raw_dispatch(buf, lang)
|
|
pending_parse_jobs[job_id] = true
|
|
return job_id
|
|
end
|
|
|
|
-- Set of buffer ids that already have a highlight overlay
|
|
-- attached, keyed by raw id (number). A buffer that opens, gets
|
|
-- highlights, gets killed, and is reopened needs a fresh overlay
|
|
-- attach; the kill path clears the entry below if/when it lands.
|
|
local highlighted_buffers = {}
|
|
|
|
local function attach_for_active_buffer()
|
|
local buf = pmacs.window.buffer()
|
|
if not buf then return end
|
|
local path = buf:name()
|
|
if not path then return end
|
|
local lang = pmacs.parse.language_for_path(path)
|
|
if not lang then return end
|
|
pmacs.parse._dispatch(buf, lang)
|
|
-- T M4.3: install the syntax-highlight overlay for this buffer.
|
|
-- Idempotent --- repeated calls for the same buffer are a no-op
|
|
-- on our side, and the Rust side is also tolerant of double
|
|
-- attach (it pushes a fresh overlay each call, but `attach_for
|
|
-- _active_buffer` itself is gated by the `highlighted_buffers`
|
|
-- table so we only push once per (buffer, after-load) cycle).
|
|
-- `tostring(buf)` is stable per BufferId (the metamethod
|
|
-- formats the wrapped id), so it's a safe table-key
|
|
-- replacement for a `:id()` method we don't have to expose.
|
|
local key = tostring(buf)
|
|
if not highlighted_buffers[key] then
|
|
local ok = pmacs.parse._attach_highlight(buf, lang)
|
|
if ok then highlighted_buffers[key] = true end
|
|
end
|
|
end
|
|
|
|
pmacs.hook.add("buffer.after-load", function()
|
|
-- Best-effort: a missing grammar / re-entry / stale buffer
|
|
-- mustn't poison the rest of the after-load chain.
|
|
local ok, err = pcall(attach_for_active_buffer)
|
|
if not ok and pmacs.error then
|
|
pmacs.error("syntax.after-load: " .. tostring(err))
|
|
end
|
|
end)
|
|
|
|
-- After-tick step: any parse job that has settled gets its bundle
|
|
-- installed into the buffer's view (and its pending entry drained).
|
|
-- Extension hook on top of the async runtime's tick rather than a
|
|
-- Rust-side tick callback because the install path is policy
|
|
-- (which view to install into), not mechanism.
|
|
local prior_tick = pmacs._async.tick
|
|
pmacs._async.tick = function(...)
|
|
local ret = prior_tick(...)
|
|
for job_id in pairs(pending_parse_jobs) do
|
|
if pmacs._async._is_complete(job_id) then
|
|
pmacs.parse._install_settled(job_id)
|
|
pending_parse_jobs[job_id] = nil
|
|
end
|
|
end
|
|
return ret
|
|
end
|