Skip to content

feat(syntax): a bare ::: figure opener is a composite figure - #29

Merged
dereuromark merged 1 commit into
mainfrom
feat/composite-figures-composite-sat-6436a4ec
Aug 15, 2026
Merged

feat(syntax): a bare ::: figure opener is a composite figure#29
dereuromark merged 1 commit into
mainfrom
feat/composite-figures-composite-sat-6436a4ec

Conversation

@dereuromark

Copy link
Copy Markdown
Contributor

Carries PART 9 §4c (markup-carve/carve#1215) into Carve.sublime-syntax. Ports markup-carve/carve-grammars#223; tracked by markup-carve/carve-grammars#222, which stays open.

The distinction

The kind word figure is reserved among the ::: types. A bare opener - the fence, its separator, the word, and nothing else - is one figure of ordered panels:

::: figure

![One](one.png)
^ Panel one.

:::
^ The group caption.

The same opener carrying a quoted title or a [label] is not that production at all. It stays the generic Tier-2 container it has always been, with both preserved:

::: figure "Titled"
Generic container.
:::

::: figure [g]
Generic container.
:::

Before this the two scoped identically, so the editor could not show which reading a line had - and the whole distinction sits in the tail of one line.

What changed

A figure-groups context sits ahead of divs in body, because the divs pattern matches every ::: line. It claims the bare opener only, and names it with this syntax's own vocabulary rather than carve-grammars': markup.other.figure-group.carve on the line, punctuation.definition.figure-group.carve on the fence, and entity.name.type.figure-group.carve on the kind word, beside the entity.name.type.div.carve the generic context already uses.

The separator is a space run, never a tab (grammar.ebnf PART 7, MARKER SEPARATORS; corpus 254): :::<TAB>note renders as a paragraph. So the pattern is spelled + rather than the \s* divs uses beside it. Claiming the tab form would be a stronger claim than the generic context makes, since that one merely over-colours a line this syntax has always over-coloured. A tab-separated opener falls to it and reads exactly as it did.

The group caption needed no rule, verified rather than assumed: divs pops at end of line, so no ::: container is ever open across lines here, and the ^ line below the closing fence is reached by captions at document level like any other. The new assertions pin that position so a later narrowing of captions cannot take it away silently.

README's feature list gains the opener. No messages/ entry: upgrade messages are written at release time in this repo (the last one is its own commit, "Add the 0.1.2 upgrade message"), and no feature or fix PR since has added one.

Residual, deliberately

Groups do not nest, so a bare ::: figure at any depth inside an open group is a generic container. But divs pops on $, so no ::: region is ever open across lines here and this syntax has no notion of being inside one - the inner opener over-colours as a group. carve-grammars carries that rule through a begin/end pair plus a second container variant; giving divs a multi-line region is what it would take here as well, and is out of scope. It is written into the context's comment rather than left to be rediscovered.

Proof

syntax_test_carve.crv gains 22 assertions over seven opener spellings and the two caption positions: the bare opener, the titled one, the labelled one, both together, the tab-separated one, a longer fence, and ::: figures, which is a different word. Each generic-container case carries a negative against the new scope alongside the positive for the old one.

Measured both directions with the headless Sublime runner (build 4200) over the whole file, with the bundled default packages installed so the fenced-code embeds resolve:

  • With the include: figure-groups line removed, 5 assertions fail: the four on ::: figure and the one on :::: figure. Nothing else moves.
  • With the pattern widened to accept a trailing title and label, 9 different assertions fail - every one on ::: figure "Titled", ::: figure [g] and ::: figure "Titled" [g], positives and negatives alike. So those control cases are reached by the rule and pass because it rejects them, not because it never sees them.

The second half matters here specifically: removing the rule leaves the negatives passing trivially, so the revert alone would not have told us the controls were live.

Gates

gate-run reports outcome: "no-gates" for this repo, which is not green - it found no gate it knows how to run, since the checks here are Python scripts plus a Sublime binary rather than a package-manager test script. Every check CI runs was run locally instead, against the committed state:

  • syntax_tests (headless build 4200, default packages installed): exit 0, 0 errors
  • tools/lint-syntax-tests.py: exit 0, 231 assertions, none dead
  • tools/generate-battery-tests.py --check: exit 0, generated block in sync
  • Carve.sublime-syntax parses as YAML

PART 9 S4c (markup-carve/carve#1215) reserves the kind word `figure` among the
`:::` types: a BARE opener - the fence, its separator, the word, and NOTHING
else - is ONE figure of ordered panels. An opener carrying a quoted title or a
`[label]` is not that production at all and stays the generic Tier-2 container,
with both preserved. Before this the two scoped identically, so a reader of the
editor could not tell which of the two readings a line had - and the whole
distinction lives in the tail of one line.

A `figure-groups` context sits ahead of `divs` in `body`, because the `divs`
pattern matches every `:::` line. It claims the bare opener only, and names it
with this syntax's own vocabulary rather than carve-grammars':
`markup.other.figure-group.carve` on the line,
`punctuation.definition.figure-group.carve` on the fence and
`entity.name.type.figure-group.carve` on the kind word, beside the
`entity.name.type.div.carve` the generic context already uses.

THE SEPARATOR IS A SPACE RUN, NEVER A TAB (grammar.ebnf PART 7, MARKER
SEPARATORS; corpus 254): `:::<TAB>note` renders as a paragraph. So the new
pattern is spelled ` +` rather than the `\s*` `divs` uses beside it. Claiming
the tab form would be a stronger claim than the generic context makes, since
that one merely over-colours a line this syntax has always over-coloured; a
tab-separated opener falls to it and reads exactly as it did.

THE GROUP CAPTION NEEDED NO RULE, verified rather than assumed: `divs` pops at
end of line, so no `:::` container is ever open across lines here, and the `^ `
line below the closing fence is reached by `captions` at document level like
any other. The assertions pin that position so a later narrowing of `captions`
cannot take it away silently.

RESIDUAL, written into the context's comment rather than left to be
rediscovered: groups do not nest, so a bare `::: figure` at any depth inside an
open group is a generic container - but with no multi-line `:::` region this
syntax has no notion of being inside one, and over-colours the inner opener as
a group. carve-grammars carries the rule through a begin/end pair plus a second
container variant; giving `divs` a region is what it would take here too, and
is out of scope for this change.

Tests: `syntax_test_carve.crv` gains 22 assertions over seven opener
spellings and the two caption positions - the bare opener, the titled one, the
labelled one, both together, the tab-separated one, a longer fence, and
`::: figures`, which is a different word. Each generic-container case carries a
negative against the new scope as well as a positive for the old one, so the
pair fails in both directions. Verified: with the `figure-groups` include
removed the bare opener and the longer fence fail against the new scope, and
with the pattern widened to accept a trailing title and label the titled,
labelled and both-together lines fail their negatives - so those controls are
reached by the rule and pass because it rejects them, not because it never
sees them.

No upgrade message: `messages/` is written at release time in this repo, and
the last released version is 0.1.2. README's feature list gains the opener.

Ports markup-carve/carve-grammars#223. Tracking: markup-carve/carve-grammars#222.
@dereuromark
dereuromark merged commit 4bcbbfb into main Aug 15, 2026
3 checks passed
@dereuromark
dereuromark deleted the feat/composite-figures-composite-sat-6436a4ec branch August 15, 2026 12:58
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant