executablebooks / sphinx-external-toc

A sphinx extension that allows the site-map to be defined in a single YAML file
https://sphinx-external-toc.readthedocs.io
MIT License
32 stars 18 forks source link
sphinx sphinx-extension

sphinx-external-toc

Github-CI Coverage Status Code style: black PyPI

A sphinx extension that allows the documentation site-map (a.k.a Table of Contents) to be defined external to the documentation files. As used by Jupyter Book!

In normal Sphinx documentation, the documentation site-map is defined via a bottom-up approach - adding toctree directives within pages of the documentation.

This extension facilitates a top-down approach to defining the site-map structure, within a single YAML file.

ToC graphic

It also allows for documents not specified in the ToC to be auto-excluded.

User Guide

Sphinx Configuration

Add to your conf.py:

extensions = ["sphinx_external_toc"]
external_toc_path = "_toc.yml"  # optional, default: _toc.yml
external_toc_exclude_missing = False  # optional, default: False

Note the external_toc_path is always read as a Unix path, and can either be specified relative to the source directory (recommended) or as an absolute path.

Basic Structure

A minimal ToC defines the top level root key, for a single root document file:

root: intro

The value of the root key will be a path to a file, in Unix format (folders split by /), relative to the source directory, and can be with or without the file extension.

:::{note} This root file will be set as the master_doc. :::

Document files can then have a subtrees key - denoting a list of individual toctrees for that document - and in-turn each subtree should have a entries key - denoting a list of children links, that are one of:

:::{important} Each document file can only occur once in the ToC! :::

This can proceed recursively to any depth.

root: intro
subtrees:
- entries:
  - file: doc1
    subtrees:
    - entries:
      - file: doc2
        subtrees:
        - entries:
          - file: doc3
  - url: https://example.com
  - glob: subfolder/other*

This is equivalent to having a single toctree directive in intro, containing doc1, and a single toctree directive in doc1, with the :glob: flag and containing doc2, https://example.com and subfolder/other*.

As a shorthand, the entries key can be at the same level as the file, which denotes a document with a single subtree. For example, this file is exactly equivalent to the one above:

root: intro
entries:
- file: doc1
  entries:
  - file: doc2
    entries:
    - file: doc3
- url: https://example.com
- glob: subfolder/other*

File and URL titles

By default, the initial header within a file document will be used as its title in generated Table of Contents. With the title key you can set an alternative title for a document. and also for url:

root: intro
subtrees:
- entries:
  - file: doc1
    title: Document 1 Title
  - url: https://example.com
    title: Example URL Title

ToC tree options

Each subtree can be configured with a number of options (see also sphinx toctree options):

These options can be set at the level of the subtree:

root: intro
subtrees:
- caption: Subtree Caption
  hidden: False
  maxdepth: 1
  numbered: True
  reversed: False
  titlesonly: True
  entries:
  - file: doc1
    subtrees:
    - titlesonly: True
      entries:
      - file: doc2

or, if you are using the shorthand for a single subtree, set options under an options key:

root: intro
options:
  caption: Subtree Caption
  hidden: False
  maxdepth: 1
  numbered: True
  reversed: False
  titlesonly: True
entries:
- file: doc1
  options:
    titlesonly: True
  entries:
  - file: doc2

You can also use the top-level defaults key, to set default options for all subtrees:

root: intro
defaults:
  titlesonly: True
options:
  caption: Subtree Caption
  hidden: False
  maxdepth: 1
  numbered: True
  reversed: False
entries:
- file: doc1
  entries:
  - file: doc2

:::{warning} numbered should not generally be used as a default, since numbering cannot be changed by nested subtrees, and sphinx will log a warning. :::

:::{note} By default, title numbering restarts for each subtree. If you want want this numbering to be continuous, check-out the sphinx-multitoc-numbering extension. :::

Using different key-mappings

For certain use-cases, it is helpful to map the subtrees/entries keys to mirror e.g. an output LaTeX structure.

The format key can be used to provide such mappings (and also initial defaults). Currently available:

For example:

defaults:
  titlesonly: true
root: index
subtrees:
- entries:
  - file: doc1
    entries:
    - file: doc2

is equivalent to:

format: jb-book
root: index
parts:
- chapters:
  - file: doc1
    sections:
    - file: doc2

:::{important} These change in key names do not change the output site-map structure. :::

Add a ToC to a page's content

By default, the toctree generated per document (one per subtree) are appended to the end of the document and hidden (then, for example, most HTML themes show them in a side-bar). But if you would like them to be visible at a certain place within the document body, you may do so by using the tableofcontents directive:

ReStructuredText:

.. tableofcontents::

MyST Markdown:

```{tableofcontents}

Currently, only one `tableofcontents` should be used per page (all `toctree` will be added here), and only if it is a page with child/descendant documents.

Note, this will override the `hidden` option set for a subtree.

## Excluding files not in ToC

By default, Sphinx will build all document files, regardless of whether they are specified in the Table of Contents, if they:

1. Have a file extension relating to a loaded parser (e.g. `.rst` or `.md`)
2. Do not match a pattern in [`exclude_patterns`](https://www.sphinx-doc.org/en/master/usage/configuration.html#confval-exclude_patterns)

To automatically add any document files that do not match a `file` or `glob` in the ToC to the `exclude_patterns` list, add to your `conf.py`:

```python
external_toc_exclude_missing = True

Note that, for performance, files that are in hidden folders (e.g. in .tox or .venv) will not be added to exclude_patterns even if they are not specified in the ToC. You should exclude these folders explicitly.

:::{important} This feature is not currently compatible with orphan files. :::

Command-line

This package comes with the sphinx-etoc command-line program, with some additional tools.

To see all options:

$ sphinx-etoc --help
Usage: sphinx-etoc [OPTIONS] COMMAND [ARGS]...

  Command-line for sphinx-external-toc.

Options:
  --version   Show the version and exit.
  -h, --help  Show this message and exit.

Commands:
  from-project  Create a ToC file from a project directory.
  migrate    Migrate a ToC from a previous revision.
  parse      Parse a ToC file to a site-map YAML.
  to-project    Create a project directory from a ToC file.

To build a template project from only a ToC file:

$ sphinx-etoc to-project -p path/to/site -e rst path/to/_toc.yml

Note, you can also add additional files in meta/create_files amd append text to the end of files with meta/create_append, e.g.

root: intro
entries:
- glob: doc*
meta:
  create_append:
    intro: |
      This is some
      appended text
  create_files:
  - doc1
  - doc2
  - doc3

To build a ToC file from an existing site:

$ sphinx-etoc from-project path/to/folder

Some rules used:

The command can also guess a title for each file, based on its path:

For example, for a project with files:

index.rst
1_a_title.rst
11_another_title.rst
.hidden_file.rst
.hidden_folder/index.rst
1_a_subfolder/index.rst
2_another_subfolder/index.rst
2_another_subfolder/other.rst
3_subfolder/1_no_index.rst
3_subfolder/2_no_index.rst
14_subfolder/index.rst
14_subfolder/subsubfolder/index.rst
14_subfolder/subsubfolder/other.rst

will create the ToC:

$ sphinx-etoc from-project path/to/folder -i index -s ".*" -e ".rst" -t
root: index
entries:
- file: 1_a_title
  title: A title
- file: 11_another_title
  title: Another title
- file: 1_a_subfolder/index
  title: A subfolder
- file: 2_another_subfolder/index
  title: Another subfolder
  entries:
  - file: 2_another_subfolder/other
    title: Other
- file: 3_subfolder/1_no_index
  title: No index
  entries:
  - file: 3_subfolder/2_no_index
    title: No index
- file: 14_subfolder/index
  title: Subfolder
  entries:
  - file: 14_subfolder/subsubfolder/index
    title: Subsubfolder
    entries:
    - file: 14_subfolder/subsubfolder/other
      title: Other

API

The ToC file is parsed to a SiteMap, which is a MutableMapping subclass, with keys representing docnames mapping to a Document that stores information on the toctrees it should contain:

import yaml
from sphinx_external_toc.parsing import parse_toc_yaml
path = "path/to/_toc.yml"
site_map = parse_toc_yaml(path)
yaml.dump(site_map.as_json())

Would produce e.g.

root: intro
documents:
  doc1:
    docname: doc1
    subtrees: []
    title: null
  intro:
    docname: intro
    subtrees:
    - caption: Subtree Caption
      numbered: true
      reversed: false
      items:
      - doc1
      titlesonly: true
    title: null
meta: {}

Development Notes

Questions / TODOs: