Skip to content

Optional-variable syntax for doctemplate: $variable?$ #158

@cscheid

Description

@cscheid

Context

While working on bd-xdnk (plumbing doctemplate diagnostics through quarto render so undefined-variable warnings reach the user), it became clear that making the warnings visible is only half the story. Once they show up, users will reasonably ask: "what's the cleanest way to silence one I actually meant to be optional?"

Today the answer is to wrap each reference in a guard:

$if(author-greeting)$$author-greeting$$endif$

This is verbose, repeats the variable name, and obscures the intent ("this variable is optional") behind boilerplate. With the warning surfaced, every optional reference becomes a place users will reach for the suppression — so the cost compounds.

Proposal

A short suffix-? form that is exactly equivalent to the $if$/$endif$ wrapper:

$variable?$

would render the variable's value if the variable is defined and truthy, and produce nothing (no diagnostic) otherwise.

Feasibility (already studied)

I sketched the implementation while doing bd-xdnk so we'd know whether this is a small-ish lift or a re-design. Notes from the plan (see claude-notes/plans/2026-05-05-doctemplate-diagnostics-quarto-render.md § Follow-up):

  • ? is unused as a sigil in the current grammar. Pipes use / as their separator (tree-sitter-doctemplate/grammar/grammar.js:84-86).
  • No collision with any existing pipe name: pairs, first, last, rest, allbutlast, uppercase, lowercase, length, reverse, chomp, nowrap, alpha, roman, left, center, right.
  • Implementation cost is small: one new grammar token, one bool optional field on VariableRef, one branch in render_variable that returns Doc::Empty silently when optional is set and the lookup fails (skipping the warn_or_error_with_code(\"Q-10-2\", ...) call).
  • Behavioral parity with $if(var)$$var$$endif$ is exact for scalar values. For lists/maps, the $if$ truthy semantics already match is_truthy(), so no surprises there.

Open design questions for discussion

  1. Spelling. Suffix $variable?$ reads as "optional variable" and keeps pipes pure value-to-value. The alternative $variable/?$ would treat ? as a degenerate pipe in the existing repeat(seq(\"/\", $.pipe)) chain — more uniform syntactically, but ? doesn't transform a value, it changes resolution semantics, so the suffix form feels more honest.

  2. Applied partials. Should ? propagate to applied partials ($var?:partial()$ — render the partial only if var is defined)? Probably yes, same intent. Not applicable to $if$ itself.

  3. Should existing built-in templates adopt it? Phase 0 of bd-xdnk found the built-in MINIMAL_HTML_TEMPLATE and FULL_HTML_TEMPLATE are clean (every $var$ is already wrapped in $if(...)$). If $variable?$ lands, those wrappers could collapse for readability — but that's a stylistic choice, not load-bearing.

  4. Pandoc compatibility. Pandoc's template syntax (which we are mostly compatible with) does not have this. Adopting ? is a Q2 extension. We should weigh "qmd is a dialect, this is a useful extension" against "users may copy templates between Pandoc and qmd and now hit a parse error in one direction." (The Pandoc parser would treat $variable?$ as just a literal $variable?$ — variable name variable?, which is also invalid syntax in their grammar — so we need to be deliberate about the divergence.)

Why now, why this issue

Not asking for an implementation decision — just asking for a place to discuss before deciding whether to build it.

The bd-xdnk work uncovered the use case in a concrete way (warning noise on optional metadata variables in custom templates), but the syntax decision is the kind of thing that benefits from more eyes. If you're a Quarto contributor and you have an opinion on grammar extensions, optional-variable ergonomics, or Pandoc-compat tradeoffs, please weigh in here.

Internal tracking: bd-x5r4 (linked discovered-from to bd-xdnk).

Metadata

Metadata

Assignees

No one assigned

    Labels

    No labels
    No labels

    Type

    No type

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions