Skip to content

Numbering and cross-reference support for long-form books (chapters, theorems, equations) #12

Description

@mmcky

Summary

mystmd currently lacks the numbering and cross-reference machinery needed to faithfully reproduce a printed academic book in HTML. This issue collects three closely-related gaps we hit while converting Dynamic Programming Volume I (Sargent & Stachurski) from LaTeX to MyST Markdown — see QuantEcon/book-dp1#336 and the downstream issues #343, #345, #346.

The three gaps share a common root: MyST's numbering and cross-reference roles do not give book authors enough control to match a published volume's numbering scheme. Books (unlike papers or web articles) need:

  • chapter / appendix / part numbering that flows through cross-references,
  • theorem-class counters that can be offset, shared, or formatted per project conventions,
  • equation numbering that distinguishes labelled vs unlabelled display math.

We believe these belong in mystmd core (or a first-party plugin) rather than as per-project post-processors. Without them, every long-form book project ends up shipping its own ad-hoc Python sed scripts — exactly what we are doing today in book-dp1 and previously in book-dp2.


Gap 1 — Chapter cross-references render the title, not "Chapter N"

Symptom. Given a chapter labelled c-rdps and a cross-reference In Chapter {ref}\c-rdps``, MyST renders "In Chapter Recursive Decision Processes". Print convention — and the corresponding PDF — renders "In Chapter 6".

Why {numref} does not solve this today. Top-level pages are not enumerated targets in MyST's current model; {numref} only works against figures, tables, equations, and prf:* blocks. Even if pages were enumerated, the appendix collision matters:

label TOC group desired render
c-rdps Chapters "Chapter 6"
c-areal Appendix "Appendix" (or "Appendix A")

Other front/back-matter pages (preface, common_symbols, licensing) have no labels today but in principle should be referenceable as e.g. "the Preface" — i.e. by role within the book, not by number.

Proposed mystmd feature.

  1. Treat top-level TOC entries as enumerable, with the enumerator scoped to the TOC group ("Chapters", "Appendix", "Parts", …).
  2. Expose a per-group enumerator template, e.g.
    toc:
      - title: "Chapters"
        enumerator: "Chapter %s"
        children: [...]
      - title: "Appendix"
        enumerator: "Appendix %s"   # or just "Appendix" for a single-entry group
        children: [...]
  3. Make {numref}\c-rdps`render the enumerator-formatted string, and make a new fallback (e.g.{chapref}` or auto-detect) collapse the prose word + number where the source already wrote "Chapter".
  4. Provide a clean way to refer to unnumbered pages by display title or by an explicit override ({ref}\c-preface` `), without falling back to the page's heading text in a way that produces ungrammatical output ("In Chapter Preface").

A non-trivial design point: the cross-reference role needs to know the TOC group of its target, which means the build needs to thread group/parent information into the reference resolver.


Gap 2 — Theorem / proposition / lemma numbers do not match the printed volume

Symptom. prf:theorem, prf:proposition, prf:lemma, etc. each maintain an independent per-chapter counter starting at 1. The printed book's counters often:

  • share a single counter across multiple types (e.g. theorems, propositions, lemmas all draw from one sequence),
  • start at non-1 offsets in some chapters (continuation across parts),
  • format as Theorem 6.2 (chapter.local) where the chapter prefix should match Gap 1's enumerator.

We hit the identical problem in book-dp2 and could not resolve it cleanly inside the project — fundamentally requires upstream control.

Proposed mystmd feature. Configuration knobs on the proof-numbering renderer:

numbering:
  proof:
    counter: shared          # or: per-type (current default)
    types: [theorem, proposition, lemma, corollary]
    offset:
      ch_fps: 0
      ch_mdps: 5             # start chapter 5 at theorem 6
    template: "{chapter}.{n}"  # uses Gap 1's chapter enumerator

This need not solve every numbering scheme, but the three knobs (shared-counter, offset, format-template-with-chapter) cover the common cases in mathematical books.


Gap 3 — Equation numbering does not distinguish labelled vs unlabelled display math

Symptom. LaTeX distinguishes equation (numbered) from equation* (unnumbered) and treats $$ ... $$ without a \label as unnumbered. After pandoc → MyST conversion, every display-math block tends to get a sequential number, which both clutters the page and breaks alignment with the PDF's equation numbers.

Proposed mystmd feature.

  1. Default: only number display math that has an explicit label (matches LaTeX equation* semantics for unlabelled blocks).
  2. Provide :no-number: / :number: overrides on the math directive.
  3. Allow a per-chapter offset / counter format consistent with Gap 1's enumerator ((6.12) instead of (12)).

Implementation path: a mystmd extension for "book-style" numbering

Rather than land all of this in mystmd core, a natural first step is to develop a first-party extension that opts a project into book-style numbering as a unit — analogous to sphinx-multitoc-numbering, which exists precisely because Sphinx's default toctree numbering doesn't fit multi-part books.

Sketch:

  • Package name (working title): mystmd-book-numbering (or similar), shipped from QuantEcon/mystmd initially.
  • Activation via myst.yml:
    extensions:
      - book-numbering
    numbering:
      style: book          # turns on the bundled scheme
  • The extension owns:
    1. TOC-group enumerators ("Chapter %s", "Appendix %s") and the {numref} / {ref} rendering hook for top-level page targets (Gap 1).
    2. Proof-counter sharing, offsets, and {chapter}.{n} formatting (Gap 2).
    3. Labelled-only equation numbering with chapter-prefixed format (Gap 3).
  • Benefits:
    • Decouples experimentation from mystmd release cadence — we can iterate on the QuantEcon fork.
    • Easy on/off for book authors who want it; zero impact on article authors who don't.
    • Provides a clear migration path: once stable, the extension's behaviour can be folded into core (or stay as the supported plugin, the way sphinx-multitoc-numbering did).
  • Reference prior art to study:
    • sphinx-multitoc-numbering (numbering)
    • sphinx-proof (proof-counter implementation)
    • sphinx-book-theme (theme-level book affordances)

If we go this route, the umbrella issue becomes: agree the design above, then implement in mystmd-book-numbering, then dogfood on book-dp1 and book-dp2.

Metadata

Metadata

Assignees

No one assigned

    Labels

    No labels
    No labels

    Type

    No type
    No fields configured for issues without a type.

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions