Forest
Gallery
5.2 forest-index¶
The indexing system used to document the Forest package uses the package itself quite heavily. While this might be a bit surprising at first sight, as indexing draws no trees, the indexing package illustrates the usage of some of the more exotic features and usage-cases of the Forest package, which is why its source is included in this documentation.1
This package has three main functions:
It is possible to index subentries using a short form of their index key, i.e. without referring to their ancestor entries. For example, instead of writing \index{option>content} one can simply write \index{content}. (Obviously, the subentry must “content” be defined as belonging to entry “option” first. This is done using \indexdef{option>content}.) This works for all keys which are a subentry of a single entry.
All subentries are automatically entered as main entries as well, with a qualificator of which entry they belong to. So, \index{option>content} produces two index entries: entry “option” with subentry “content” and entry “content option”. This works for an arbitrary number of subentry levels.
Entries can be given options that format the appearance of the entry and/or its descendants in both text and index. (Entries that format the appearance of their descendants are called categories below.)
If hyperref package is loaded, the following hyperlinks are created besides the standard ones linking the page numbers in index to text:
The Forest package mainly enters the picture with respect to the entry formatting. A simple (narrow) tree is built containing an entry and all its ancestors. Formatting instructions are then processed using Forest’s option processing mechanisms.
Finally, note that this package might change without retaining backwards compatibility, and that changes of this package will not be entered into the changelog.
Identification.
\ProvidesPackage{forest-index}
\RequirePackage{forest}
Remember the original LaTeX’s \index command.
\let\forestindex@LaTeX@index\index
5.2.1 The user interface macros¶
\index is the general purpose macro. \indexdef and \indexex are shorthands for indexing definitions and examples. \indexitem is a combination of \indexdef and the \item of the lstdoc package. It automatically indexes the command being documented. \indexset neither typesets or indexes the entry, but only configures it; it is usually used to configure categories. All these macros parse their arguments using xparse. The arguments, listed in the reverse order:
The final argument, which is the only mandatory argument, is a comma-separated list of index keys.
The boolean switch > just before the mandatory argument signals that the keys are given in the full form. Otherwise, keys without a level separator are considered short.
Indexing options are given by the [optional ] argument.
The first (optional) argument of:
\indexitem: specifies the default value of the command.
\index: is used to provide “early” options.
Among the options of these commands, three keylists are of special importance: index_key_format, index_form_format and print_format. These hold instructions on how to format the index key, the form of the entry in the index and the form of the entry in the main text. They work by modifying the contents of an ⟨autowrapped toks⟩ register result.
An example: how macros are indexed in this documentation. Style macro defined below does everything needed to format a macro name: it detokenizes the given name (in case the name contains some funny characters), prefixes the backslash, wraps in in the typewriter font, adds color and hyperlink (the final two styles are defined in below this package). Note the usage of \protect: it is needed because we want to use these styles to format entries not just in the main next, but also in the index.
\forestset{
detokenize/.style={result=\protect\detokenize{##1}},
tt/.style={result=\protect\texttt{##1}},
macro/.style={detokenize, +result={\char\escapechar}, tt, print in color, hyper},
}
Then, we configure the main level entry “macro”: the child of this entry will be formatted (both in index and in the main text) using the previously defined style.
\indexset
[for first={format=macro}]
>{macro}
Usage is then simple: we write \indexex{macro>forestoption} (or simply \indexex{forestoption} to get \ forestoption.
\DeclareDocumentCommand\indexdef{O{} t> m}{%
\IfBooleanTF{#2}
{\let\forestindex@resolvekey\forestindex@resolvekey@long}
{\let\forestindex@resolvekey\forestindex@resolvekey@shortorlong}%
\forestindex@index{definition}{#1}{#3}}
\DeclareDocumentCommand\indexex{O{} t> m}{%
\IfBooleanTF{#2}
{\let\forestindex@resolvekey\forestindex@resolvekey@long}
{\let\forestindex@resolvekey\forestindex@resolvekey@shortorlong}%
\forestindex@index{example}{#1}{#3}}
% \DeclareDocumentCommand\indexitem{D(){} O{} t> m}{%
% \IfBooleanTF{#3}
% {\let\forestindex@resolvekey\forestindex@resolvekey@long}
% {\let\forestindex@resolvekey\forestindex@resolvekey@shortorlong}%
% \forestindex@index{definition}{default={#1},print format=item,#2}{#4}}
\DeclareDocumentCommand\indexitem{D(){} O{} t> m}{%
\let\forestindex@resolvekey\forestindex@resolvekey@long
\forestindex@index{definition}{default={#1},#2,print format+=item}{#4}}
\DeclareDocumentCommand\indexset{O{} t> m}{%
\IfBooleanTF{#2}
{\let\forestindex@resolvekey\forestindex@resolvekey@long}
{\let\forestindex@resolvekey\forestindex@resolvekey@shortorlong}%
\forestindex@index{not print,not index,definition}{set={#1}}{#3}}
\DeclareDocumentCommand\index{D(){} O{} t> m}{%
\IfBooleanTF{#3}
{\let\forestindex@resolvekey\forestindex@resolvekey@long}
{\let\forestindex@resolvekey\forestindex@resolvekey@shortorlong}%
\forestindex@index{#1}{#2}{#4}%
}
All UI macros call this macro.
early option keylist
late option keylist
a comma-sep list of forest index key (full or short form). A key can be given an argument using key=argument syntax. How the argument is used is up to the user. For example, the “environment” entry of the Forest documentation uses it to typeset the contents of the environment:
\indexitem{environment>forest={[\texttt{(}\meta{config}\texttt{)}]\meta{tree}}}
\def\forestindex@index#1#2#3{%
Partition the index keylist into single keys. And put it all in a group: the persistent stuff is saved globally.
{\forcsvlist{\forestindex@forkey{#1}{#2}}{#3}}%
}
\def\forestindex@forkey#1#2#3{%
Short-key resolution. The result is stored into \forestindex@fullkey.
\forestindex@resolvekey{#3}%
Manipulate arguments a bit, so that we can use our quick-and-dirty one-arg memoization.
%\forestset{@index/.process={__o}{#1}{#2}{\forestindex@fullkey}}
\edef\forest@marshal{%
\noexpand\forestindex@index@{%
{\unexpanded{#1}}%
{\unexpanded{#2}}%
{\expandonce{\forestindex@fullkey}}%
}%
}\forest@marshal
}
Call the central processing command, style @index. See how .process is used to expand (once) the last argument.
\def\forestindex@index@#1{\forestset{@index/.process={__o}#1}}
\forestset{
5.2.2 Declarations¶
Should we print and/or index the entry? For example, \index[not_print]{...} will index silently (as LaTeX’s \index command does).
declare boolean register=print,
declare boolean register=index,
declare boolean register=short,
Options name, content, key and argument hold info about the current entry. We need to declare only the latter two, the former two we steal from Forest.
declare toks={key}{},
declare toks={argument}{},
These options will hold first the initial, and then the calculated values of the index key, index form and the form in text. When (late) options are executed, these options are initialized to the value of option key; it is safe to modify them at this point. Afterwards, they will be further processed by keylists index_key_format, index_form_format and print_format, respectively.
declare toks={index key}{},
declare toks={index form}{},
declare toks={print form}{},
The customization of entries’ appearance is done by specifying the following three keylists. The keylists work by modifying register result.
declare keylist={index key format}{},
declare keylist={index format}{},
declare keylist={print format}{},
declare autowrapped toks register=result,
Some shorthands.
format'/.style={print format'={#1}, index format'={#1}},
format/.style={print format={#1}, index format={#1}},
format+/.style={print format+={#1}, index format+={#1}},
+format/.style={+print format={#1}, +index format={#1}},
form/.style={print form={#1},index form={#1}},
form+/.style={print form+={#1},index form+={#1}},
+form/.style={+print form={#1},+index form={#1}},
Entry types are normal (default), definition, example. Only definitions are special, as their options are automatically saved.
declare toks register=index entry type,
definition/.style={index entry type=definition},
normal/.style={index entry type=normal},
example/.style={index entry type=example},
normal,
This option is used internally to store the hyper ids.
declare toks={index@hypertarget}{},
every index begin/.style={},
every index end/.style={},
Some formatting tools need to know whether we’re typesetting text or index: this info is stored in the stage register.
declare toks register=stage,
5.2.3 The central processing command¶
early option keylist (these are only used to define category “@unknown” at the end of this package)
late option keylist
index key (full form)
@index/.style n args={3}{
Set the defaults.
print, index, index entry type=normal, set'={}, short,
Create the tree structure: [entry[subentry[subsubentry...]]]. Three options of every node created:
key contains the key of the (sub)entry
name contains the full path to the (sub)entry
arguments contains the arguments given to the (sub)entry’s key
content contains the full key, with arguments for all but the most deeply embedded subentry
for nodewalk is used because create@subentry@node walks down the created tree. At if n=0 below, we’re thus positioned at the lowest node.
for nodewalk={
The components of the full key are separated using split, with different keys being executed for the first component and the rest.
split={#3}{>}{create@main@entry@node, create@subentry@node},
Remove the argument from the most deeply embedded subentry.
if n=0{
content/.option=key,
}{
content/.process={OOw2} {!parent.content} {key} {##1>##2},
}
}{},
for root'={
Don’t memoize if the key is of an unknown category.
if strequal/.process={O}{!root.name}{@unknown}{TeX=\global\forest@memoizing@ok@false}{},
Option print_form is what will be typeset in the text. Option index_key is the key that will be used for sorting the index. Option index_form is what will be typeset in the index. All these are initialized to the key. See how .option is used to assign an option value to another option.
for tree={
print form/.option=key,
index key/.option=key,
index form/.option=key,
},
Below, on invalid is set to fake at four points. This is so we won’t get in trouble when \indexsetting the categories: when the category formatting code will try to step into the child, it will fail as the child does not exist when \indexset is called for the category; but we ignore the failure.
Go to the the most deeply embedded subentry.
for first leaf'={
Execute every index options and the given early options.
on invalid={fake}{
every index begin,
#1,
},
Ancestors are walked in the reverse order (top down). At every node, the saved configuration is executed (if it exists).
for reverse={current and ancestors}{on invalid={fake}{@@index/\forestoption{name}/.try}},
We don’t execute the saved configuration for definitions, as definitions are where the configuration is set.
if index entry type={definition}{}{%
on invalid={fake}{@@index/\forestoption{name}/.try},
},
Execute late (well, normal) options. See the discussion about early options above.
on invalid={fake}{
#2,
every index end
},
Remember the given config for the rest of the document.
if set={}{}{save@session},
If we’re at a definition, save the config into the auxiliary file.
if index entry type={definition}{save@foridx}{},
},
stage={},
Create hyperlink targets of the form .entry.subentry.subsubentry....
Forest points:
if index={
index@hypertarget/.process={OS_= ? l_ w2}
{index key}
{}
{}{.}
{##2##1},
for descendants={
index@hypertarget/.process={OO S _l1= ? w2 }
{!parent.index@hypertarget}{index key}
{}
{##1} % empty index key
{##1.##2} % otherwise
},
}{},
Index.
if index={
begingroup,
stage=index,
For each (sub)entry, format the index_key using the instructions in index_key_format.
for tree={
result/.option=index key,
process keylist'={index key format}{current},
index key/.register=result,
},
For each (sub)entry, format the index_form using the instructions in index_form_format.
for tree={
result/.option=index form,
process keylist'={index format}{current},
index form/.register=result,
},
Create an index entry for all nodes where index_form is non-empty.
where index form={}{}{
All the ancestor nodes with an non-empty index_form will be appended (in script size, as a hyperlink) to the index_form of the current node.
if n=0{
temptoksb={},
}{
temptoksc={},
for ancestors={
if index form={}{}{
temptoksb+/.expanded={\forestregister{temptoksc}%
\noexpand\protect\noexpand\hyperlinknocolor{%
\forestoption{index@hypertarget}}{\forestoption{index form}}},
temptoksc={,\space},
},
},
if temptoksb={}{}{
+temptoksb={\protect\space\begingroup\protect\scriptsize},
temptoksb+={\endgroup},
},
},
temptoksa={},
result'={},
if n children=0{tempboola}{not tempboola},
where index form={}{}{
Create the hypertarget that the definitions in text and other index entries will point to.
temptoksd/.expanded={\noexpand\protect\noexpand\hypertarget{%
\forestoption{index@hypertarget}}{}},
Add the (inner) current node to the index entry of the (outer) current node.
result+/.expanded={%
\forestregister{temptoksa}%
\forestoption{index key}%
=\forestoption{index form}%
\forestregister{temptoksd}%
\forestregister{temptoksb}%
},
temptoksa={>},
temptoksb={},
},
Do the actual indexing.
result+/.expanded={|indexpagenumber\forestregister{index entry type}},
TeX and memoize/.expanded={\noexpand\forestindex@LaTeX@index{\forestregister{result}}},
},
endgroup
}{},
if print={
begingroup,
stage=print,
For each (sub)entry, format the print_form using the instructions in print_form_format.
for tree={
result/.option=print form,
process keylist'={print format}{current},
print form/.register=result,
},
Typeset the entry in the text.
for first leaf'={TeX and memoize/.expanded={\forestoption{print form}}},
endgroup,
}{},
}
},
Create the main entry node and set to be the root.
create@main@entry@node/.style={% #1 = subentry
set root={[]},
do dynamics, for root'={process delayed=tree},
root',
setup@entry@node={#1}
},
Create a subentry node and move into it.
create@subentry@node/.style={
append={[]},
do dynamics, for root'={process delayed=tree},
n=1,
setup@entry@node={#1}
},
Parse #1 into key and argument, and assign name and content.
setup@entry@node/.style={
options={
split={#1}{=}{key,argument},
if n=0{
name'/.option=key,
content={#1},
}{
name'/.process={OOw2} {!parent.name} {key} {##1>##2},
content/.process={Ow1} {!parent.content} {##1>#1},
},
}
},
}
5.2.4 Saving and loading the options¶
\forestset{
This register holds whatever we need to remember.
declare keylist register=set,
Besides storing the keylist in the register, also immediately execute it..
Autoforward register={set}{##1},
Remember things by saving them in a global style.
save@session/.style={@@index/\forestoption{name}/.global style/.register=set},
Save thinks to the auxiliary file.
save@foridx/.style={
Don’t save entries of unknown category.
if strequal/.process={O}{!root.name}{@unknown}{}{
Don’t save if nothing is set.
if set={}{}{
TeX and memoize/.expanded={%
\noexpand\immediate\noexpand\write\noexpand\forestindex@out{%
\noexpand\string\noexpand\indexloadsettings\noexpand\unexpanded{{\forestoption{name}}{\forestregister{set}}}%
}%
},
},
},
Save the full form of the key in the auxiliary file. Obviously, do it only for subentries. The full form contains whatever arguments were given to the non-last component.
if key/.process={O}{content} {} {%
if short={
TeX and memoize/.expanded={%
\noexpand\immediate\noexpand\write\noexpand\forestindex@out{%
\noexpand\string\noexpand\indexdefineshortkey\noexpand\unexpanded{{\forestoption{key}}{\forestoption{content}}}%
}%
}%
}{}
}
}
}
Load settings from the auxiliary file into the global style. Warn if anything was configured more than once (e.g. by \indexdefing the same key twice).
\def\indexloadsettings#1#2{%
\pgfkeysifdefined{/forest/@@index/#1/.@cmd}{%
\forestindex@loadsettings@warning{#1}%
}{}%
% #s in #2 are doubled; the following \def removes one layer of doubling
\def\forest@temp{#2}%
\forestset{@@index/#1/.global style/.expand once=\forest@temp}%
}
\def\forestindex@loadsettings@warning#1{%
\PackageWarning{forest-index}{Forest index key "#1" was configured more than once!
I'm using the last configuration.}%
}
Load the full form of a short key from the auxiliary file. Out of kindness for the end user, remember all the full keys corresponding to a short key: this will make a more informative warning below.
\def\indexdefineshortkey#1#2{%
\def\forestindex@temp@short{#1}%
\def\forestindex@temp@long{#2}%
\ifx\forestindex@temp@short\forestindex@temp@long
\else
\ifcsdef{index@long@#1}{%
\global\cslet{index@long@#1}\relax
\csgappto{index@alllong@#1}{,#2}%
}{%
\global\csgdef{index@long@#1}{#2}%
\global\csgdef{index@alllong@#1}{#2}%
}%
\fi
}
5.2.5 Short key resolution¶
Nothing to do for a long key.
\def\forestindex@resolvekey@long#1{\def\forestindex@fullkey{#1}}
Decide whether a key is short or long based on the absence or presence of the level separator >.
\def\forestindex@resolvekey@shortorlong#1{%
\pgfutil@in@>{#1}%
\ifpgfutil@in@
\expandafter\def\expandafter\forestindex@fullkey
\else
\expandafter\forestindex@resolvekey@short
\fi
{#1}%
}
Before resolving the short key, we need to split the user input into the key and the argument. The latter is then appended to the full key (which can, in principle, contain arguments for other components as well).
\def\forestindex@resolvekey@short#1{%
\forestset{split={#1}{=}{index@resolveshortkey@key,index@resolveshortkey@arg}}%
}
\forestset{
index@resolveshortkey@key/.code={%
\ifcsvoid{index@long@#1}{%
\forestindex@resolveshortkey@warning{#1}%
\def\forestindex@fullkey{@unknown>#1}%
}{%
\letcs\forestindex@fullkey{index@long@#1}%
}%
},
index@resolveshortkey@arg/.code={%
\appto\forestindex@fullkey{=#1}%
},
}
\def\forestindex@resolveshortkey@warning#1{%
\PackageWarning{forest-index}{Cannot resolve short forest index key "#1".
These are the definitions I found (from the previous run):
"\csuse{index@alllong@#1}"}%
}
5.2.6 Formatting styles¶
Define default colors for index entry types and provide a style that typesets the entry in text (but not index) in the desired color.
\forestset{
normal color/.initial=blue,
definition color/.initial=red,
example color/.initial=darkgreen,
print in color/.style={if stage={print}{result/.expanded=\noexpand\protect\noexpand\textcolor{%
\pgfkeysvalueof{/forest/#1 color}}{\unexpanded{##1}}}{}},
print in color/.default=\forestregister{index entry type},
Use this style in ..._format keylists if you want the index entries to be hyperlinks to the definition, and the definition to be a hyperlink to the index.
hyper/.style={
if stage={index}{}{
if index entry type={definition}{
result/.expanded={\noexpand\hypertarget{\forestoption{name}}%
{\noexpand\hyperlink{\forestoption{index@hypertarget}}{\forestregister{result}}}}
}{
result/.expanded=\noexpand\hyperlink{\forestoption{name}}{\forestregister{result}}
}
}
},
}
Color page numbers in the index, with or without hyperref package.
\ifdef\hyperpage{%
\newcommand\indexpagenumbernormal[1]{{%
\hypersetup{linkcolor=\pgfkeysvalueof{/forest/normal color}}\hyperpage{#1}}}
\newcommand\indexpagenumberdefinition[1]{{%
\hypersetup{linkcolor=\pgfkeysvalueof{/forest/definition color}}\hyperpage{#1}}}
\newcommand\indexpagenumberexample[1]{{%
\hypersetup{linkcolor=\pgfkeysvalueof{/forest/example color}}\hyperpage{#1}}}
}{
\newcommand\indexpagenumbernormal[1]{%
\textcolor{\pgfkeysvalueof{/forest/normal color}}{#1}}
\newcommand\indexpagenumberdefinition[1]{%
\textcolor{\pgfkeysvalueof{/forest/definition color}}{#1}}
\newcommand\indexpagenumberexample[1]{%
\textcolor{\pgfkeysvalueof{/forest/example color}}{#1}}
}
Provide dummy \hyper... commands if hyperref is not loaded.
\providecommand\hyperlink[2]{#2}
\providecommand\hypertarget[2]{#2}
\providecommand\hypersetup[1]{}
This is used by entry qualifiers: we want them to be hyperlinks, but black.
\newcommand\hyperlinknocolor[2]{{\hypersetup{linkcolor=black}\hyperlink{#1}{#2}}}
Use style item to have the index entry (in text) function as the \item of a lstdoc’s syntax environment.
\forestset{
declare toks register=default,
default={},
item/.style={
result/.process= {_RORw4}
{} {default} {!parent.print form} {result}
{\item[,##2,##3]{##4}},
},
}
5.2.7 Utilities¶
We will need a global version of several pgfkeys commands.
\pgfkeys{/handlers/.global style/.code=\pgfkeys{\pgfkeyscurrentpath/.global code=\pgfkeysalso{#1}}}
\pgfkeysdef{/handlers/.global code}{\pgfkeysglobaldef{\pgfkeyscurrentpath}{#1}}
\long\def\pgfkeysglobaldef#1#2{%
\long\def\pgfkeys@temp##1\pgfeov{#2}%
\pgfkeysgloballet{#1/.@cmd}{\pgfkeys@temp}%
\pgfkeysglobalsetvalue{#1/.@body}{#2}%
}
\def\pgfkeysgloballet#1#2{%
\expandafter\global\expandafter\let\csname pgfk@#1\endcsname#2%
}
\long\def\pgfkeysglobalsetvalue#1#2{%
\pgfkeys@temptoks{#2}\expandafter\xdef\csname pgfk@#1\endcsname{\the\pgfkeys@temptoks}%
}
\forestset{
% unlike pgfmath function strequal, |if strequal| does not expand the compared args!
if strequal/.code n args={4}{\ifstrequal{#1}{#2}{\pgfkeysalso{#3}}{\pgfkeysalso{#4}}},
}
Begin and end group, Forest-style:
\forestset{
begingroup/.code={\begingroup},
endgroup/.code={\endgroup},
}
5.2.8 Memoize¶
Quick and dirty memoization. Single argument macros only. Does not support nesting.
\newtoks\forest@memo@key
\newtoks\forest@memo
\newif\ifforest@memoizing@now@
\newif\ifforest@memoizing@ok@
\newif\ifforest@execandmemoize@
\def\forest@memoize#1{% #1 = \cs
\cslet{forest@memo@orig@\string#1}#1%
\def#1##1{%
\ifforest@memoizing@now@
\forest@globalsaveandrestoreifcs{forest@execandmemoize@}{%
\global\forest@execandmemoize@false
\csname forest@memo@orig@\string#1\endcsname{##1}%
}%
\else
\expandafter\global\expandafter\forest@memo@key\expandafter{\detokenize{forest@memo@#1{##1}}}%
\ifcsname\the\forest@memo@key\endcsname
\@escapeifif{\csname\the\forest@memo@key\endcsname}%
\else
\@escapeifif{%
\global\forest@memo{}%
\global\forest@memoizing@ok@true
\global\forest@memoizing@now@true
\global\forest@execandmemoize@true
\csname forest@memo@orig@\string#1\endcsname{##1}%
\global\forest@execandmemoize@false
\global\forest@memoizing@now@false
\ifforest@memoizing@ok@
\csxdef{\the\forest@memo@key}{\the\forest@memo}%
\immediate\write\forest@memo@out{%
\noexpand\forest@memo@load{\the\forest@memo@key}{\the\forest@memo}%
}%
\fi
}%
\fi
\fi
}%
}
\def\forest@memo@load#1#2{%
The following two \defs remove one level of hash-doubling from the arguments, introduced by \write.
\def\forest@temp@key{#1}%
\def\forest@temp@value{#2}%
\csxdef{\detokenize\expandafter{\forest@temp@key}}{\expandonce\forest@temp@value}%
\immediate\write\forest@memo@out{%
\noexpand\forest@memo@load{\detokenize\expandafter{\forest@temp@key}}{\detokenize\expandafter{\forest@temp@value}}%
}%
}
\forestset{
TeX and memoize/.code={\forest@execandmemoize{#1}},
}
\def\forest@execandmemoize#1{%
\ifforest@execandmemoize@
\let\forest@memo@next\forest@execandmemoize@
\else
\let\forest@memo@next\@gobble
\fi
\forest@memo@next{#1}%
#1%
}
\def\forest@execandmemoize@#1{%
\gapptotoks\forest@memo{#1}%
}
\def\forest@memo@filename{\jobname.memo}
\newwrite\forest@memo@out
\immediate\openout\forest@memo@out=\forest@memo@filename.tmp
\IfFileExists{\forest@memo@filename}{%
\input\forest@memo@filename\relax
}{}%
\AtEndDocument{%
\immediate\closeout\forest@memo@out
\forest@file@copy{\forest@memo@filename.tmp}{\forest@memo@filename}%
}
Commenting the following line turns off memoization.
\forest@memoize\forestindex@index@
5.2.9 Initialize¶
Declare category “@unknown”.
\index(not print,not index)[%
set={
index key=unknown,
form={\textbf{unknown!!}},
for first={format={result/.expanded=\noexpand\textbf{\forestregister{result}??}}}
},
]>{@unknown}
Load the auxiliary file made in the previous compilation, and open it for writing to save data from this compilation.
\def\forestindex@filename{\jobname.foridx}
\IfFileExists{\forestindex@filename}{%
\input\forestindex@filename\relax
}{}%
\newwrite\forestindex@out
\immediate\openout\forestindex@out=\forestindex@filename.tmp
\AtEndDocument{%
\immediate\closeout\forestindex@out
\forest@file@copy{\forestindex@filename.tmp}{\forestindex@filename}%
}
\endinput
1 Indexing with this package makes the compilation very slow, so I cannot whole-heartedly recommend it, but I still hope that it will make a useful example.