Skip to content

Benjamin-Dobell/pymdown-toc-ext

Folders and files

NameName
Last commit message
Last commit date

Latest commit

 

History

25 Commits
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 

Repository files navigation

pymdown-toc-ext: Table of Contents (Extended) Version License

An extension for Python-Markdown which extends the built-in toc with additional capabilities to customize the generated table of contents.

Setup

Prerequisites

Aside from Python-Markdown itself, you’ll want to enable the official Python-Markdown attr_list extension.

Installation

pip install pymdown-toc-ext

Python-Markdown API

MkDocs

In mkdocs.yml under markdown_extensions replace toc with toc_ext i.e.

markdown_extensions:
  - toc_ext

Configuration

This extension extends the built-in Markdown toc extensions. All its configuration options are supported.

Usage

pymdown-toc-ext looks for the presence of data-toc-* attributes on Markdown elements. Given Markdown itself doesn’t provide support for adding attributes, this is why the Markdown attr_list extension is required.

Inserting a new entry

Whilst the standard toc extension only supports Markdown headers and uses the header depth to nest table of contents entries. toc_ext supports generating table of contents entries linking to any element on the page.

Entries are created based on the presence of data-toc-* attributes contained within your markdown.

Top level

If you provide a data-toc-label attribute on its lonesome, then a ToC entry will be inserted as the first entry (at the root level) in the ToC.

[](){: #top data-toc-label="Goto Top" }

The above will insert an entry with the text "Goto Top", linking to an empty (invisible) anchor, which is explicitly given the ID top.

ℹ️
We strip data-toc-* attributes that we consume. So the resultant generated anchor will simply be <a id="top" href=""></a>.

You may omit an explicit ID, and an ID will be generated for you based on the ToC label e.g.

[](){: data-toc-label="Goto Top" }

This will result in an empty/invisible anchor on the page, <a id="goto-top" href=""></a> and a single ToC entry with the text Goto Top.

Inserting a child entry

By default, we insert entries at the top of the table of contents. However, we may wish to insert the entry as a child of another entry. This is achieved by the inclusion of the data-toc-child-of attribute specifying the ID of another TOC entry.

## Category 1

Some introductory text that doesn't warrant a heading.
{: data-toc-label="Intro" data-toc-child-of="category-1" }

## Category 2

**Technobabble**{: data-toc-child-of="glossary" } is really important:

* We want readers to understand the concept before moving on.
* We don't want unnecessary headings breaking the page's flow.
* We also want this definition appearing in the table of contents, for easy lookup.

## Glossary

### Peripheral

Of secondary or minor importance.

The above will generate:

  • Category 1

    • Intro

  • Category 2

  • Glossary

    • Technobabble

    • Peripheral

💡
You may omit data-toc-label. If you do, the ToC label will be generated from the contents of the target element.

Inserting after another entry (manual ordering)

In the above example, we ended up with "Technobabble" appearing before "Peripheral". Perhaps we wanted the entries to appear in alphabetical order.

Instead of specifying data-toc-child-of, we specify data-toc-after, with the ID of the element that our entry should appear after.

## Alphabet

B
{: data-toc-after="a" }

A
{: data-toc-child-of="alphabet" }

C
{: data-toc-after="b" }

The above will generate:

  • Alphabet

    • A

    • B

    • C

Sorting

Manual ordering each entry can be tedious. Frequently we just want to alphabetically sort child entries. We can mark an entry as sorted by adding the attribute data-toc-sort.

The example above can therefore be alternatively expressed as:

## Alphabet {: data-toc-sort }

B
{: data-toc-child-of="alphabet" }

A
{: data-toc-child-of="alphabet" }

C
{: data-toc-child-of="alphabet" }

Reverse

Reverse ordering is supported by data-toc-sort="reverse" e.g.

## Alphabet {: data-toc-sort="reverse" }

B
{: data-toc-child-of="alphabet" }

A
{: data-toc-child-of="alphabet" }

C
{: data-toc-child-of="alphabet" }

will generate:

  • Alphabet

    • C

    • B

    • A

Top Level (Root) Sorting

The top level entries don’t have a parent that you can mark as sorted. Instead data-toc-root-sort may appear anywhere in your document. Otherwise, it behaves just like data-toc-sort on a TOC entry e.g.

Both:

B
{: data-toc-root-sort data-toc-label="B" }

A
{: data-toc-label="A" }

C
{: data-toc-label="C" }

and:

## B {: data-toc-root-sort }

## A

## C

will result in the table of contents:

  • A

  • B

  • C

Removing an entry

A Markdown heading may be omitted from the generated table of contents by adding the attribute data-toc-omit.

## Heading 1
## Heading 2 {: data-toc-omit }
## Heading 3

will result in the table of contents:

  • Heading 1

  • Heading 3

If you omit a heading that has sub-headings, the sub-headings will be moved up a level, replacing the omitted heading.

## A
## B {: data-toc-omit }
### B1
### B2
## C

will result in the table of contents:

  • A

  • B1

  • B2

  • C

Custom URL entries

Rather than linking to an ID on the same page, you can insert a table of contents entry that links to a URL by providing a data-toc-url attribute.

## A

The sky is falling. [ref 1]
{: #sky-falling data-toc-label="#1 English Fairy Tales" data-toc-url="https://example.com" data-toc-child-of="references" }

## B

The earth is flat. [ref 2]
{: #dawn-treader data-toc-label="#2 The Voyage of the Dawn Treader" data-toc-url="https://example.com" data-toc-child-of="references" }

## References {: data-toc-sort }

Even though the link is to an external website, an ID is still mandatory.

⚠️
The PyMdown MagicLink extension breaks support for URLs appearing in attribute lists. You’re unable to create custom URL entries whilst using this extension.

Custom URL technical details

We inject an additional url field into toc_tokens.

We use this field when generating the content of md.toc, thus both md.toc and md.toc_tokens are URL-aware. If you’ve got direct access to either of these then you’re good to go.

mkdocs

If you’re using mkdocs, it doesn’t expose toc_tokens directly, but rather its own data model. It has a url field, but is simply derived from toc_tokens['id']. Thus, we monkey-patch mkdocs to instead return toc_tokens['url'] when it’s available.

Admittedly, this is quite a wonky solution. If you’re not using the custom URL functionality you can disable the mkdocs patching in your mkdocs.yml with:

markdown_extensions:
  - toc_ext:
    patch_mkdocs: false

Contributing

Contributions welcome.

Development

At the time of writing pip cannot install editable poetry packages.

To appease pip, you can generate a setup.py with:

poetry build --format sdist && tar -xvf dist/*-`poetry version -s`.tar.gz -O '*/setup.py' > setup.py

You’ll then be able to, in another Python project, install your local editable package with:

pip install -e /path/to/pymdown-toc/ext

About

Python-Markdown Table of Contents (Extended) Extension

Resources

License

Stars

Watchers

Forks

Sponsor this project

 

Packages

No packages published

Languages