diff --git a/builtin/runtime/syntax.lua b/builtin/runtime/syntax.lua index b639377..18164dc 100644 --- a/builtin/runtime/syntax.lua +++ b/builtin/runtime/syntax.lua @@ -22,6 +22,10 @@ local inflight_parse_by_buffer = {} local parse_buffer_by_key = {} local parse_lang_by_buffer = {} local reparse_requested_by_buffer = {} +-- Buffers already warned about hitting the injection layer cap (Q#IJ3); +-- keyed like the others so we warn once, and re-arm if the file stops +-- capping (an edit removed the excess regions). +local injection_cap_warned = {} local raw_dispatch = pmacs.parse._dispatch @@ -44,6 +48,20 @@ function pmacs.parse._dispatch(buf, lang) return job_id end +-- Injection language aliases (framing Q#IJ4). The registry holds the +-- merged map (seeded with defaults on the Rust side), and each dispatch +-- snapshots it into the parse request so the worker can resolve dynamic +-- fence names (`py` → python, `ts` → typescript). Exposed as a +-- write-through proxy so users add fence-name aliases from init.lua: +-- pmacs.parse.injection_aliases.mylang = "rust" +-- Reads are not proxied (the canonical map lives Rust-side); this is a +-- write-only extension surface. +pmacs.parse.injection_aliases = setmetatable({}, { + __newindex = function(_, alias, lang) + pmacs.parse._register_injection_alias(alias, lang) + end, +}) + -- Shebang → language detection ------------------------------------------ -- -- Extension detection (`language_for_path`) misses extensionless scripts @@ -324,6 +342,19 @@ pmacs._async.tick = function(...) if pmacs._async._is_complete(job_id) then local key = parse_job_buffer_keys[job_id] pmacs.parse._install_settled(job_id) + -- Surface the injection layer backstop (Q#IJ3) once per buffer rather + -- than dropping embedded regions silently. Best-effort: a missing + -- buffer or error here must not stall the settle loop. + local capped_buf = key and parse_buffer_by_key[key] + if capped_buf and pmacs.parse._injection_capped(capped_buf) then + if not injection_cap_warned[key] and pmacs.error then + injection_cap_warned[key] = true + pmacs.error( + "syntax: injection layer cap reached; some embedded regions are unhighlighted") + end + elseif key then + injection_cap_warned[key] = nil + end pending_parse_jobs[job_id] = nil parse_job_buffer_keys[job_id] = nil if key and inflight_parse_by_buffer[key] == job_id then diff --git a/docs/multi-language-injections-framing.md b/docs/multi-language-injections-framing.md new file mode 100644 index 0000000..2e3b111 --- /dev/null +++ b/docs/multi-language-injections-framing.md @@ -0,0 +1,387 @@ +# Multi-language injections — framing (side quest, highlight family) + +**Intent.** Teach the syntax engine that one buffer can hold more than +one language. Today pmacs parses a buffer with exactly one grammar and +runs exactly one `highlights.scm` over it ("first language wins"). This +adds tree-sitter *injection layers*: after the root parse, run the +grammar's `injections.scm` to find embedded regions (a markdown code +fence, an HTML `