Menu

Search toolsChangelog

to move to openDescribe the problem, not the tool

Turn the headings already in your document into navigation

Find Markdown headings outside code blocks, give each included heading an explicit collision-free anchor and download the complete document with one replaceable nested table of contents.

ATX and Setext headings are recognised. Heading-looking lines inside fenced or indented code are ignored.

Level 2 usually keeps the document title out of its own table of contents.

Headings linked
4
Duplicate anchors resolved
1
Deepest level included
3

Markdown with table of contents

document-with-toc.md · 468 B

The original document with one replaceable generated block and explicit HTML anchors before included headings.

Every linked heading

52Equipmentequipment
73Cameracamera
92Resultsresults
113Cameracamera-2

How it works

Why explicit anchors matter

CommonMark specifies what an ATX or Setext heading is. It does not specify that ## Getting started must become #getting-started, nor how a second heading with the same words should be suffixed. Those choices belong to renderers and hosting platforms.

The generated document therefore contains both sides of every link:

- [Camera](#camera-2)

<!-- tessalor-toc:anchor --><a id="camera-2"></a>
### Camera

The comment identifies an anchor that this tool may replace. The id is the actual HTML fragment target. An unrelated anchor without that marker remains authored content and is left alone.

How headings are found

The scanner works from top to bottom. A fence of at least three backticks or tildes starts a code block; only a fence using the same character and at least the same length closes it. Lines inside are never considered headings. Lines with four leading spaces or a leading tab are treated as indented code for the same reason.

Outside code, one to six leading # characters followed by whitespace form an ATX heading. A text line followed by === or --- is recognised as a single-line Setext level 1 or level 2 heading. Optional closing hashes on ATX headings are removed from the visible label.

This is intentionally narrower than pretending to be a complete Markdown renderer. Container-specific extensions and multi-line Setext paragraphs can have parser-dependent behaviour; the tool does not silently promise to model every host.

How anchors are made

Visible inline link text and simple emphasis markers are reduced to their text, then the label is Unicode-normalised and lower-cased. Combining marks are removed, runs of characters other than letters and numbers become one hyphen, and edge hyphens disappear. A heading made only from punctuation falls back to section.

Duplicate detection happens after that normalisation. Café & Crème and Cafe Creme both begin at cafe-creme, so the later heading becomes cafe-creme-2 rather than pointing both entries at the first section.

The worked document

The default document has one title and these included headings:

Source headingLevelInserted anchor
Equipment2equipment
Camera3camera
Results2results
Camera3camera-2

With levels 2 through 4 selected, the result is 4 linked headings, 1 duplicate resolved, and deepest included level 3. The table is placed after the opening # Field notes title; nested level-3 entries receive two spaces of list indentation.

Use the text diff maker to review the inserted lines against the source, or the word counter when the document’s content—not its navigation—is the question.

How it is done

  1. Normalise line endings and remove only a complete table-of-contents block and anchor lines previously generated by this tool.
  2. Scan lines in order, tracking backtick and tilde fences and ignoring four-space or tab-indented code before recognising ATX and Setext headings.
  3. Simplify each included heading to a lower-case Unicode slug, then append -2, -3 and later suffixes when normalised names collide.
  4. Insert the exact HTML anchor before each included heading and place one marked, nested list after an opening level-one title or at the document start.

What it assumes

  • The selected shallowest level is not deeper than the selected maximum level.
  • The scanner targets ordinary CommonMark ATX headings and single-line Setext headings; it is not a full extension-specific Markdown renderer.
  • Raw HTML anchors are allowed by the destination renderer. A platform that strips authored HTML will also strip these explicit targets.
  • Existing unmarked HTML ids and an incomplete generated marker block are treated as authored content and are never deleted.

Common questions

Does Markdown define the automatic id for a heading?

No. Core Markdown defines heading structure but not a host's generated fragment slug, and duplicate suffix rules vary. This tool writes explicit ids so its links and targets are part of the same document.

Will a heading shown inside a code example enter the table?

Not when it is inside a backtick or tilde fence, or an indented code block. The scanner tracks those blocks before testing whether a line has heading syntax.

Can I regenerate the table after editing headings?

Yes. The generated table has start and end comments and each generated anchor has its own marker. A later run removes those marked parts and builds one current copy.

Why might a link fail on a publishing platform?

Some platforms sanitise raw HTML and remove authored anchor elements. The Markdown remains readable, but the explicit targets require a renderer that permits ordinary inline HTML.

Sources