Builds navigation, search, and content indexes from extracted entries.
This is the default DocShell.Presentation.Source — the one
DocShell.Build uses unless a host substitutes its own. It takes the flat
list of entries the extractors produced and derives the three indexes a
documentation site needs:
- navigation — one
DocShell.Presentation.NavigationItemper entry, sorted by kind then title, so modules, guides, and notebooks group together and each group reads alphabetically. - search — one
DocShell.Presentation.SearchEntryper entry, with the document flattened to plain text and pre-tokenized. - content — a map from entry id to its AST nodes, so a renderer can load one page without parsing the whole set.
Deliberately flat
Every navigation item comes back with no children. That is not an omission: DocShell has no way to know whether your guides should nest under a section, whether modules should group by namespace, or whether the tree should follow the file layout at all. Those are product decisions, and a package that guessed at them would be wrong for most hosts and hard to override for the rest.
A host that wants structure has two options: reshape the flat list after
DocShell.Build.run/1 returns it, or implement
DocShell.Presentation.GraphProjector and own categorization outright.
Paths
Entry paths default to /docs/{kind}/{id}, which is a placeholder more than
a recommendation. Hosts routing documentation anywhere else pass a
:path_builder function:
StaticGenerator.project(
entries: entries,
path_builder: fn entry -> "/handbook/" <> entry["id"] end
)The same function builds both navigation and search paths, so a search result and a nav link can never disagree about where a document lives.
Empty entries
Entries with no content are dropped. DocShell.Generate.ExDoc returns every
module it can read, including ones marked @moduledoc false, because
coverage reporting needs them — but a navigation tree listing every internal
module as a blank page helps nobody. skip_empty: false keeps them.
Search text
Search content is the AST flattened depth-first with a space between nodes, which means code blocks, table cells, and link text are all searchable and no markup leaks into the index.
tokens is off by default and comes back []. It is the same text downcased
and split on non-alphanumeric runs, and it costs roughly three quarters of
the size of the text it duplicates — for a field the shipped renderer does
not read, because it indexes title and content itself. Hosts wiring a
search backend that wants a pre-split form set search_tokens: true.
Options
:entries— the extracted entries to project; defaults to[]:path_builder— a function from entry to path; defaults todefault_path/1:skip_empty— drop entries with no content; defaults totrue:search_tokens— populateSearchEntry.tokens; defaults tofalse
Summary
Functions
Returns the fallback path for an entry: /docs/{kind}/{id}.