Skip to content

Commit 13adac8

Browse files
committed
gen-tm(vue): full block-language coverage — exotic langs + custom blocks (PR #6085)
Monogram's Vue grammar only embedded ts/tsx/jsx (script), scss/less/stylus/ postcss (style), pug (template) — far short of the hand-written grammar, which embeds via a generic `<anytag lang="X">` catch-all. Add the rest: - named blocks: <script lang="coffee">→source.coffee, <style lang="sass"> →source.sass (added to their lang maps; emitRaw handles them robustly). - NEW customBlockEmbed map (markup config) for the data/custom-block langs on ANY tag: md→text.html.markdown, json→source.json, jsonc→source.json.comments, json5→source.json5, yaml→source.yaml, toml→source.toml, gql/graphql→ source.graphql. So `<i18n lang="yaml">`, `<docs lang="md">`, `<gql lang= "graphql">` all embed correctly — Vue SFC custom blocks Monogram had no mechanism for. gen-tm emits one begin/end per custom lang: the tag NAME is captured and the close is its backreference (`</\2>`), so it's tag-agnostic; a `lang=<x>` lookahead gates it; start-tag attrs re-tokenize via #attribute; body embeds the mapped grammar. Emitted before the named rawText blocks. Vue-only (driven by vue.ts data); the 5 non-Vue grammars stay byte-identical. agnostic 8/8, tsc clean, vue-issues + vue-dropin 19/19, gen deterministic.
1 parent a7cf6bd commit 13adac8

4 files changed

Lines changed: 615 additions & 6 deletions

File tree

src/gen-tm.ts

Lines changed: 19 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -3247,6 +3247,25 @@ function generateMarkupTm(grammar: CstGrammar, grammarName: string, scopeName: s
32473247
};
32483248
top.push({ include: `#raw-${tag}-ml` });
32493249
};
3250+
// Custom-block embeds: ANY block tag carrying `lang="<lang>"` embeds the mapped scope in its body
3251+
// (the SFC custom-block convention — `<i18n lang="yaml">`, `<docs lang="md">`, `<gql lang="gql">`).
3252+
// One begin/end per lang: the tag NAME is captured (group 2) and the close is its BACKREFERENCE
3253+
// (`</\2>`), so the region is tag-agnostic. Emitted BEFORE the named rawText blocks so a data lang
3254+
// on a named tag still resolves here when that tag's lang map doesn't list it. The lookahead asserts
3255+
// `lang=<lang>` in the start tag; the start-tag attrs re-tokenize via #attribute; body → the grammar.
3256+
for (const [lang, scope] of Object.entries(m.customBlockEmbed ?? {})) {
3257+
const key = `block-${lang}`;
3258+
repository[key] = {
3259+
name: `meta.embedded.block.${L}`,
3260+
begin: `(${o})(${namePat})\\b(?=[^${ccClose}]*\\blang\\s*=\\s*["']?${escapeRegex(lang)}\\b)([^${ccClose}]*)(${c})`,
3261+
beginCaptures: { '1': { name: sOpen }, '2': { name: sName }, '3': { patterns: [{ include: '#attribute' }] }, '4': { name: sClose } },
3262+
end: `(${o}${slash})(\\2)\\s*(${c})`,
3263+
endCaptures: { '1': { name: sOpen }, '2': { name: sName }, '3': { name: sClose } },
3264+
contentName: scope,
3265+
patterns: [{ include: scope }],
3266+
};
3267+
top.push({ include: `#${key}` });
3268+
}
32503269
for (const tag of m.rawText?.tags ?? []) {
32513270
const spec = m.rawText!.embed?.[tag] ?? (tokScope(m.rawText!.token) ?? `source.${L}`);
32523271
if (typeof spec === 'string') {

src/types.ts

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -95,6 +95,14 @@ export interface MarkupConfig {
9595
// instead map to `{ default, lang }` to pick the embed by a `lang="…"` attribute on the
9696
// start tag (Vue: `<script lang="ts">`→source.ts, `<style lang="scss">`→source.css.scss).
9797
rawText?: { tags: string[]; token: string; embed?: Record<string, string | RawEmbed> };
98+
// Custom-block embeds: ANY top-level block tag (not just the named rawText ones) whose start
99+
// tag carries `lang="<lang>"` embeds the mapped scope in its body — the Vue SFC custom-block
100+
// convention (`<i18n lang="yaml">`→source.yaml, `<docs lang="md">`→text.html.markdown, also
101+
// `<script lang="coffee">`, `<style lang="sass">`). Keyed by lang → embed scope; matches a
102+
// generic tag name and closes on its backreferenced `</tag>`. Tried BEFORE the named rawText
103+
// blocks, so an exotic lang wins over a named block's default (a `<script lang="coffee">`
104+
// embeds source.coffee, not the script default). The common langs stay on the named blocks.
105+
customBlockEmbed?: Record<string, string>;
98106
comment?: { open: string; close: string; token: string }; // e.g. `<!--` … `-->`
99107
// Void elements (`<br>`, `<img>`, `<meta>`, …) — no children, no close tag. The
100108
// lexer RETAGS an OPEN void-tag name from the identifier token to `voidNameToken`,

0 commit comments

Comments
 (0)