Skip to content

Index Generation

In static site generators such as MkDocs, every build emits a search_index.json file consumed by Lunr.js or Wasabi directly in the browser. It lists every word encountered in the documentation along with its locations, enabling instant client-side search. That automation works wonderfully for HTML, but printed documents require a static index compiled ahead of time.

Traditional LaTeX editing relies on \index{term} commands sprinkled throughout the source. After compilation you run makeindex or xindy, which produces the final index file included near the end of the document. TeXSmith mirrors that workflow: it turns Markdown annotations into LaTeX \index{...} calls and triggers makeindex/xindy while building the PDF.

That run is the build driver's, not imakeidx's: the package can only call the index program through shell-escape, which TeXSmith does not enable. Between two engine passes the driver runs the program over every .idx the document wrote — its own and one per named registry — and the engine's next pass reads the .ind back. It picks xindy when one is installed — a printed index sorts accented terms by the document's language, which makeindex cannot — and makeindex otherwise. texsmith doc.md --build and texsmith site build run the same step, so a book's index is a book's index.

The LaTeX form still looks familiar:

\index{term!subterm}
\index{another term}
\index{\textbf{important term}}
\index{\emph{emphasized term}}

Thus, index entries can:

  • be nested up to 3 levels,
  • be rendered in bold, italic, or both,
  • appear multiple times in the document, with all page numbers listed.

TMark spells an index entry with the index role, and #[…] is its shorthand — # defines, and the term is content, hence the brackets. The two spellings are one node.

{index}[endianness]                     one level, default registry
#[endianness]                           the same thing, shorthand

#[byte order][endianness]               nested (three levels at most)

{index main=true}[endianness]           main topic: a bold page number
#[**endianness**]                       the same thing, shorthand

#[*endianness*]                         emphasis is content markup

{index registry=physics}[Foo][Bar]      nested twice under a named registry

#[cake] #[chocolate]                    several entries in one place

A registry other than the default is named by registry=; it is created on first use and each produces its own index at the position the template chooses.

{index:reg} and the {b} / {i} suffixes are deprecated

The 0.6 spellings {index:registry}[…] and {index}[…]{b} / {i} are still parsed with a deprecation warning. tmark lint --fix rewrites them to {index registry=…}[…] and {index main=true}[…]; italics become content markup. See Migrating to TMark.

Emphasis and Formatting

Printed indexes often differentiate how important an entry is within a section:

  • Normal text: the term is discussed in that section (default).
  • Bold: the term is the main topic of that section.
  • Italic: the term is mentioned but not deeply discussed.
  • Bold italic: the term is the main topic and also referenced elsewhere in the same section.

Emphasis inside the term is content markup, so wrap the indexed term in the appropriate markers (#[*topic*]); "main topic" is the main=true attribute, for which #[**topic**] is the shorthand.

Nested Entries

Consider a cooking book where you want to index the recipe for "Chocolate Cake". You might want to add an index entry for "Cake" with a sub-entry for "Chocolate" and also in "Chocolate Cake":

## Chocolate Cake

#[cake][**chocolate**] #[chocolate cake]

LaTeX only supports up to 3 levels of nesting:

#[cake]

#[cake][**chocolate**]

#[dessert][cake][chocolate]

Tags

In MkDocs, search powered by Lunr.js automatically adds tags on headings to improve searchability. An index entry is a zero-width node: the whitespace on both sides collapses to one space and disappears before punctuation, so it never shows in the flow. The LaTeX writer emits \tsindex[registry=r, main]{sort@formatted!sub} from the ts-index fragment, and the MkDocs plugin collects the same entries to enrich Lunr's search index. This keeps the PDF index and the interactive site search in sync even though they are generated through different pipelines.