% \iffalse meta-comment
%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
% forest-ext-tagging.dtx
% Additions and changes Copyright (C) 2025-2026 Clea F. Rees.
% Code from skeleton.dtx Copyright (C) 2015-2024 Scott Pakin (see below).
%
% This work may be distributed and/or modified under the
% conditions of the LaTeX Project Public License, either version 1.3c
% of this license or (at your option) any later version.
% The latest version of this license is in
%   https://www.latex-project.org/lppl.txt
% and version 1.3c or later is part of all distributions of LaTeX
% version 2008-05-04 or later.
%
% This work has the LPPL maintenance status 'muaintained'.
%
% The Current Maintainer of this work is Clea F. Rees.
%
% This work consists of all files listed in manifest.txt.
%
% The file forest-ext-tagging.dtx is a derived work under the terms of the
% LPPL. It is based on version 2.4 of skeleton.dtx which is part of
% dtxtut by Scott Pakin. A copy of dtxtut, including the
% unmodified version of skeleton.dtx is available from
% https://www.ctan.org/pkg/dtxtut and released under the LPPL.
%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
% \fi
%
% \iffalse
%<*driver>
\RequirePackage{svn-prov}
% ref. ateb Max Chernoff: https://tex.stackexchange.com/a/723294/
\def\MakePrivateLetters{\makeatletter\ExplSyntaxOn\endlinechar13}
\ExplSyntaxOff
\ProvidesFileSVN{$Id: forest-ext-tagging.dtx 11668 2026-02-21 02:34:38Z cfrees $}[v0.3 \revinfo][\filebase DTX: ]
\DefineFileInfoSVN[forest-ext-tagging]
\documentclass[11pt,british]{ltxdoc}
% l3doc loads fancyvrb
% fancyvrb overwrites svn-prov's macros without warning
% restore \fileversion \filerev in case we're using l3doc
\GetFileInfoSVN{forest-ext-tagging}
\begin{document}
% \let\MakePrivateLetters\MyMakePrivateLetters
\DocInput{\filename}
\end{document}
%</driver>
% \fi
% \title{ext.tagging}
% \author{Clea F. Rees\thanks{%
%     Bug tracker:
%     \href{https://codeberg.org/cfr/prooftrees/issues}{\url{codeberg.org/cfr/prooftrees/issues}}
%     \textbar{} Code:
%     \href{https://codeberg.org/cfr/prooftrees}{\url{codeberg.org/cfr/prooftrees}}
%     % \textbar{} Mirror:
%     % \href{https://github.com/cfr42/prooftrees}{\url{github.com/cfr42/prooftrees}}% 
% }}
% \date{\forestextdocdate}
% \maketitle
%
% ^^A forest-lib-ext.tagging{-debug} 
% ^^A <<< 
%<*sty>
%<@@=tagforest>
% \permissivelines
%    \begin{macrocode}
\NeedsTeXFormat{LaTeX2e}
%% $Id: forest-ext-tagging.dtx 11668 2026-02-21 02:34:38Z cfrees $}
%<!debug>    \ProvidesForestLibrary{ext.tagging}[v0.1]
%<debug>    \ProvidesForestLibrary{ext.tagging-debug}[v0.1]
%
%<!debug>    \disable@package@load {forest-lib-ext.tagging-debug}
%<debug>    \disable@package@load {forest-lib-ext.tagging}
{%
%<!debug>      \PackageWarning {ext.tagging (forest library)}
%<debug>      \PackageWarning {ext.tagging-debug (forest library)}
  {Only one of ext.tagging and ext.tagging-debug should be loaded.
    Since the
%<!debug>        ext.tagging
%<debug>        ext.tagging-debug
    library has already been loaded, I will ignore your request for
%<!debug>        ext.tagging-debug.%
%<debug>        ext.tagging.%
  }%
}
%    \end{macrocode}
% We don't want inconsistent names in hooks.
%    \begin{macrocode}
\SetDefaultHookLabel{forest-ext/tagging}
%    \end{macrocode}
% \iffalse
% ^^A Paid â defnyddio \GetFileInfoSVN*/\GetFileInfoSVN{} yn y fan hon!!
% \fi
% As the name suggests, we need \emph{tagging keylists} from \pkg{ext.utils}.
%    \begin{macrocode}
%<!debug>    \useforestlibrary*{ext.utils}
%<debug>    \useforestlibrary*{ext.utils-debug}
%    \end{macrocode}
% \changes{v0.2}{2026-02-21}{Require \pkg{memoize-ext}.}
% If \pkg{memoize} is loaded, we need \pkg{memoize-ext}.
%    \begin{macrocode}
\@ifpackageloaded{memoize}{%
%<!debug>    \RequirePackage{memoize-ext}%
%<debug>    \RequirePackage{memoize-ext-debug}%
}{}
\ExplSyntaxOn
%    \end{macrocode}
% \begin{macro}{\l_@@_toks_tl}
%   \lpack{expl3} variable to store non-\lpack{expl3} \emph{toks}.
%    \begin{macrocode}
\tl_new:N \l_@@_toks_tl
%    \end{macrocode}
% \end{macro}
% \begin{macro}{\l_@@_tmpa_str}
%    \begin{macrocode}
\str_new:N \l_@@_tmpa_str
%    \end{macrocode}
% \end{macro}
%    \begin{macrocode}
%    \end{macrocode}
% \begin{macro}{\l_forestext_tagging_custom_bool}
% \begin{fcodekey}{custom~tagging}
%   Public boolean to allow custom config to override e.g.~\pkg{prooftrees}.
%    \begin{macrocode}
\bool_new:N \l_forestext_tagging_custom_bool
\bool_set_false:N \l_forestext_tagging_custom_bool
\forestset{
  custom~tagging/.code={
    \use:c {bool_set_#1:N} \l_forestext_tagging_custom_bool
  },
  custom~tagging/.default=true,
  not~custom~tagging/.code={
    \bool_set_false:N \l_forestext_tagging_custom_bool
  },
}
%    \end{macrocode}
% \end{fcodekey}
% \end{macro}
% \begin{macro}{%
%   \@@_pgftikz_tag_bbox:nnn,
%   \@@_pgftikz_tag_bbox:enn,
%   }
%   Retrieve saved coordinates.
%    \begin{macrocode}
\cs_new_nopar:Npn \@@_pgftikz_tag_bbox:nnn #1#2#3
{
  \@@_pgftikz_tag_bbox_aux:eenn
  {
    \@@_property_ref_orig:ee {#1}{xpos}
  }
  {
    \@@_property_ref_orig:ee {#1}{ypos}
  }
  {#2}{#3}
}
\cs_generate_variant:Nn \@@_pgftikz_tag_bbox:nnn {enn}
%    \end{macrocode}
% \end{macro}
% \begin{macro}{%
%   \@@_pgftikz_tag_bbox_aux:nnnn,
%   \@@_pgftikz_tag_bbox_aux:eenn,
% }
%   The tagging code requires the bounding box for \emph{alt}.
%   Getting the exact value would require something more complicated, but this calculates a reasonable approximation for simple cases.
%   It is not exact because it does not, for instance, account for line widths at the least and greatest coordinates.
%   A more serious deficiency is that it ignores any annotations added to the tree, including labels, edge labels, additional drawing commands etc.
%
%   The problem here is that if we wait until the end of the \env{tikzpicture}, the tokens required to create the \texttt{alt} text no longer exist.
%   A better solution might be to \pkg{memoize} the tree and get the bounding box that way.
%   Then we can simply write the tokens we need to file and access them at any point during subsequent compilations.
%   Or maybe it would be better to just save the tokens and write this after the tree is drawn?
%   But then we probably have to save them globally in order to ‘smuggle’ them out, which is a bit obnoxious.
%    \begin{macrocode}
\cs_new_nopar:Npn \@@_pgftikz_tag_bbox_aux:nnnn #1#2#3#4
{
  \dim_to_decimal_in_bp:n {#1sp}
  \c_space_tl
  \dim_to_decimal_in_bp:n {#2sp}
  \c_space_tl
  \dim_to_decimal_in_bp:n {#1sp+#3}
  \c_space_tl
  \dim_to_decimal_in_bp:n {#2sp+#4}
}
\cs_generate_variant:Nn \@@_pgftikz_tag_bbox_aux:nnnn {eenn}
%    \end{macrocode}
% \end{macro}
% \begin{socket}{tagsupport/forest/init,tagsupport/forest/tag,tagsupport/forest/tag/mmz,tagsupport/forest/setup}
% Is there an equivalent of the \env{macro} environment for sockets/plugs/hooks?
%
% The support for \pkg{memoize} is currently \texttt{noop}, but we create the sockets.
%
% \texttt{tagsupport/forest/setup} doesn't correspond to anything in \pkg{latex-lab} \autocite{latex-project-latex-lab} because I'm not sure how to make it.
%    \begin{macrocode}
\socket_new:nn {tagsupport/forest/init}{0}
\socket_new:nn {tagsupport/forest/tag}{2}
\socket_new:nn {tagsupport/forest/tag/mmz}{2}
\socket_new:nn {tagsupport/forest/setup}{0}
%    \end{macrocode}
% \end{socket}
% \begin{macro}{%
%   \@@_tag_suspend:n,
%   \@@_tag_resume:n,
% }
%   We are going to redefine the standard \cs{tag\_suspend:n} and \cs{tag\_resume:n} to prevent the tagging code being continuously stopped and started during tree construction.
%   Before doing that, we make private copies of both commands so that we can \begin{enumerate*}[label=(\roman*)]\item still stop/start tagging ourselves and \item restore the original definitions when we're done\end{enumerate*}.
%
%   This rather less than ideal solution is required because there is no way to disable the tagging support for \pkg{tikz} locally: the only documented way to disable is global.
%   But we do not want to interfere with \pkg{latex-lab}'s tagging code for \emph{other} \env{tikzpicture} environments.
%   We just want to stop it interfering in \env{forest} trees.
%   Hence the hacks.
%    \begin{macrocode}
\cs_new_eq:NN \@@_tag_suspend:n \tag_suspend:n
\cs_new_eq:NN \@@_tag_resume:n \tag_resume:n
\tl_new:N \mmzxSpace
\tl_set:NV \mmzxSpace \c_space_tl
\text_declare_purify_equivalent:Nn \mmzxSpace {~}
%    \end{macrocode}
% \end{macro}
% \begin{macro}{\@@_noop:n}
%   Something to \cs{let} the suspend/resume functions to.
%    \begin{macrocode}
\cs_new_nopar:Npn \@@_noop:n #1 {}
%    \end{macrocode}
% \end{macro}
% \begin{splug}{tagsupport/forest/init tag}
% This plug corresponds roughly to \texttt{tagsupport/tikz/picture/init}, but the division of labour between sockets/plugs is a bit different for \env{forest}.
%    \begin{macrocode}
\socket_new_plug:nnn {tagsupport/forest/init}{tag}
{
%    \end{macrocode}
%   This part is modified from \textcite{latex-project-latex-lab-tikz}, but runs in a different socket.
%   I had a note that using socket \texttt{para/begin} didn't work here.
%   That's probably from tableaux?
%   But I can't remember what I thought the problem was \dots.
%    \begin{macrocode}
  \mode_if_vertical:T
  {
    \if@inlabel 
      \mode_leave_vertical:
    \else
      \tag_socket_use:n {para/begin}
    \fi
  } 
  \tag_mc_end_push:
%    \end{macrocode}
% Note that assigning \texttt{noop} to all of the \pkg{latex-lab} sockets and suspending tagging is \textbf{not} sufficient to suspend tagging. 
% This is because hook code includes tagging commands, including commands which start/stop tagging, unconditionally.
%    \begin{macrocode}
  \socket_assign_plug:nn {tagsupport/tikz/picture/init}{noop}
  \socket_assign_plug:nn {tagsupport/tikz/picture/begin}{noop}
  \socket_assign_plug:nn {tagsupport/tikz/picture/end}{noop}
%    \end{macrocode}
% This does the \pkg{forest} \autocite{saso-forest} setup before the \emph{tagging keylists} are turned into \emph{keylist options}.
%    \begin{macrocode}
  \socket_use:n {tagsupport/forest/setup}
%    \end{macrocode}
% Since we can't disable that code only locally, we instead redefine the relevant commands.
% Even this does not completely pause the tagging code, but it stops enough to yield a valid structure, albeit one with a lot of empty \texttt{mc}s.
%    \begin{macrocode}
  \cs_set_eq:NN \tag_suspend:n \@@_noop:n
  \cs_set_eq:NN \tag_resume:n \@@_noop:n
%    \end{macrocode}
% Suspend tagging using private copy of public function.
%    \begin{macrocode}
  \@@_tag_suspend:n {tagforest}
}
%    \end{macrocode}
% \end{splug}
% \begin{splug}{tagsupport/forest/setup~alt}
%   This doesn't correspond to anything in \textcite{latex-project-latex-lab-tikz} because I'm not sure how to make it.
%    \begin{macrocode}
\socket_new_plug:nnn {tagsupport/forest/setup}{alt}
{
  \forestset{
    tag~plug=alt,
    tag~nodes~uses=alt~text,
    collate~tags~uses=alt~text,
    tag~tree~uses=alt,
  }
}
%    \end{macrocode}
% \end{splug}
% \begin{splug}{tagsupport/forest/tag~alt}
% This plug corresponds to \pkg{latex-lab}'s \texttt{alt} plug for \pkg{tikz} \autocite{latex-project-latex-lab-tikz}.
% So far as possible, these plugs are verbatim copies of the official plugs\footnote{%
%   In other words, the bits that work are shamelessly copied from Ulrike Fischer's code, while the bits which don't are mine.%
% }, but some changes are necessary for \pkg{forest}.
%    \begin{macrocode}
\socket_new_plug:nnn {tagsupport/forest/tag}{alt}
{
%    \end{macrocode}
% Straight from \pkg{latex-lab}.
%    \begin{macrocode}
  \tag_struct_begin:n 
  {
    tag=Figure,
    alt=\l_@@_toks_tl,
  }
  \tag_mc_begin:n {tag=Figure}
  \cs_new:cpe {tagforest@mark@pos@\the\tagforest@id}
  {
%    \end{macrocode}
% The only real differences are that, as noted above, some code is used in the \texttt{init} socket rather than here and that we use different functions to determine the coordinates of the origin and size of the bounding box.
% Whereas \pkg{latex-lab} uses \pkg{pgf}'s \texttt{remember picture} functionality to record the origin, we use \pkg{ltproperties}.
% Similarly, where \pkg{latex-lab} uses \pkg{pgf} to determine the extent of the bounding box, we use \pkg{forest} to calculate approximate dimensions for the tree before it is drawn.
%
% The use of \pkg{ltproperties} is quite all right, I think, and necessary as \pkg{latex-lab}'s method is not compatible with \pkg{forest}.
% However, \pkg{latex-lab}'s bounding box calculation is \emph{far} superior to the method used here, so it would be useful to see if that can be modified for use once the rest of the code works.
% (It should also be significantly faster.)
%    \begin{macrocode}
    \@@_pgftikz_tag_bbox:enn {tagforest-id\the\tagforest@id}
    {#1}{#2}
  }
%    \end{macrocode}
% Revert to copying \pkg{latex-lab} verbatim.
%    \begin{macrocode}
  \tag_struct_gput:ene
  {\tag_get:n {struct_num}}
  {attribute}
  {
    /O /Layout /BBox~
    [
      \use:c
      {tagforest@mark@pos@\the\tagforest@id}
    ]
  }
}
%    \end{macrocode}
% \end{splug}
% \begin{macro}{\@@_init:}
%   Corresponds to the analogous \pkg{latex-lab} function. 
%   Tests whether tagging is active and sets a \pkg{forest} boolean accordingly.
%    \begin{macrocode}
\cs_new_nopar:Npn \@@_init:
{
  \global\advance\tagforest@id~by~1\relax
  \tag_if_active:TF
  {
    \forestset{
      tagging=1,
%<debug>    debug~tagforest={Tagging~active.},
    }
%    \end{macrocode}
%   If tagging is active and unless custom tagging is set, installs sets register \texttt{plut} to \texttt{alt} and assigns plugs and sockets.
%   If custom tagging is set, we just set the \pkg{forest} boolean \texttt{tagging} and make \cs{\_\_tagforest\_end:} noop.
%   The custom stages are still in place, but these should hopefully have no effect on anything.
%   In any case, they are partially overridden by e.g.~\pkg{prooftrees} which installs a somewhat different and more complicated set.
%
% ^^A tag nodes uses=alt text is a style which sets the tag nodes and collate tags keylists; tag tree is a noop style by default; tag tree uses alt text is a get dims and call tagging fn.
%    \begin{macrocode}
    \bool_if:NTF \l_forestext_tagging_custom_bool
    {
%<debug>        \tagforest@debug@typeout{Custom~tagging~configured.}
      \cs_set_eq:NN \@@_end: \@@_noop:n
    }{
      \cs_set_eq:NN \@@_end: \@@_tag_end:
%<debug>    \tagforest@debug@typeout{Custom~tagging~not~configured.}
      \str_if_eq:eeT {tagforest@plug@NONE} {\foresteregister{setup~plug}}
      {
%<debug>    \tagforest@debug@typeout{Looking~for~setup~plug~as~none~configured.}
        \socket_get_plug:nN {tagsupport/tikz/picture/begin} \l_@@_tmpa_str
        \exp_args:NnV \socket_if_plug_exist:nnTF {tagsupport/forest/setup} 
        \l_@@_tmpa_str
        {
          \PackageInfo{ext.tagging~(forest~lib)}{
            Installing~setup~plug~for~tagging~forest~trees~to~match~selection~for~
            tikz~pictures.
          }
          \exp_args:Ne \forestset{setup~plug \exp_not:N = \l_@@_tmpa_str}
        } {
          \PackageWarning{ext.tagging~(forest~lib)}{
            Using~alt~setup~plug~for~tagging~as~no~match~exists~for~plug~
            selected~for~tikz~pictures
          }
          \forestset{setup~plug=alt}
        }
      }
      \str_if_eq:eeT {tagforest@plug@NONE} {\foresteregister{tag~plug}}
      {
%<debug>    \tagforest@debug@typeout{Looking~for~tag~plug~as~none~configured.}
        \exp_args:NnV \socket_if_plug_exist:nnTF {tagsupport/forest/tag} 
        \l_@@_tmpa_str
        {
          \PackageInfo{ext.tagging~(forest~lib)}{
            Installing~tag~plug~for~tagging~forest~trees~to~match~selection~for~
            tikz~pictures.
          }
          \exp_args:Ne \forestset{tag~plug \exp_not:N = \l_@@_tmpa_str}
        } {
          \PackageWarning{ext.tagging~(forest~lib)}{
            Using~alt~tag~plug~for~tagging~as~no~match~exists~for~plug~
            selected~for~tikz~pictures
          }
          \forestset{tag~plug=alt}
        }
      }
%<debug>    \tagforest@debug@typeout{Assigning~plug~tag~to~tagsupport/forest/init.}
      \socket_assign_plug:nn {tagsupport/forest/init}{tag}
%<debug>    \tagforest@debug@typeout{Using~socket~tagsupport/forest/init.}
      \socket_use:n {tagsupport/forest/init}
%    \end{macrocode}
%   We also do what we can to stop the residual tagging code from marking up useless content.
%   This mitigates the problem, but does not entirely solve it.
%    \begin{macrocode}
      \def\pgfsys@begin@text{}
      \def\pgfsys@end@text{}
    }
  }{
    \forestset{tagging=0,
%<debug>    debug~tagforest={Tagging~inactive.},
    }
    \cs_set_eq:NN \@@_end: \@@_noop:n
  }
}
%    \end{macrocode}
% \end{macro}
% \begin{macro}{\@@_end:,
%   \@@_tag_end:}
%   Again, analogous to the corresponding \pkg{latex-lab} function \autocite{latex-project-latex-lab-tikz}.
%    \begin{macrocode}
\cs_new_eq:NN \@@_end: \@@_noop:n
\cs_new_nopar:Npn \@@_tag_end: {
  \@@_tag_resume:n {tagforest}
%    \end{macrocode}
%   Restore the format's definitions of \cs{tag\_suspend:n} and \cs{tag\_resume:n}.
%    \begin{macrocode}
  \cs_set_eq:NN \tag_suspend:n \@@_tag_suspend:n
  \cs_set_eq:NN \tag_resume:n \@@_tag_resume:n
%    \end{macrocode}
%   Standardish?
%    \begin{macrocode}
%<debug>    \if@tagforest@debug
%<debug>      \ShowTagging{mc-current}
%<debug>    \fi
  \tag_mc_end:
  \tag_struct_end:
  \tag_mc_begin_pop:n {}
}
%    \end{macrocode}
% \end{macro}
% \begin{macro}{\@@_tag_tree_tag:nnn,\tagforest@tag@tree@tag}
%   This function is responsible for recording the tree's page coordinates, tidying up the collected tokens for the \texttt{alt} text and utilising the tagging socket.
%    \begin{macrocode}
\cs_new_nopar:Npn \@@_tag_tree_tag:nnn #1#2#3
{
  \tex_savepos:D
  \@@_property_record_orig:ee {tagforest-id\the\tagforest@id}
  {xpos,ypos}
  \tex_savepos:D
  \tl_set:Ne \l_@@_toks_tl {
    \exp_args:No \text_purify:n { \the\tagforest@toks }
  }
%    \end{macrocode}
%   Utilising the socket requires briefly reenabling tagging else the commands would have no useful effects.
%    \begin{macrocode}
  \@@_tag_resume:n {tagforest}
  \socket_assign_plug:nn {tagsupport/forest/tag}{#1}
  \ifmemoizing
    \socket_assign_plug:nn {tagsupport/forest/tag/mmz}{#1}
  \fi
  \socket_use:nnn {tagsupport/forest/tag}{#2}{#3}
  \socket_use:nnn {tagsupport/forest/tag/mmz}{#2}{#3}
  \@@_tag_suspend:n {tagforest}
}
%    \end{macrocode}
%   Alias for use in \env{pgf} syntax.
%    \begin{macrocode}
\cs_new_eq:NN \tagforest@tag@tree@tag \@@_tag_tree_tag:nnn
%    \end{macrocode}
% \end{macro}
% \begin{macro}{\tagforest@init}
%   Alias.
%    \begin{macrocode}
\cs_new_eq:NN \tagforest@init \@@_init:
%    \end{macrocode}
% \end{macro}
% \changes{v0.2}{2026-01-18}{Add hook ordering rule as switching utils (back) to begin env hook.}
%    \begin{macrocode}
\hook_gput_code:nnn {env/forest/end}{.}
{
  \@@_end:
}
\hook_gput_code:nnn {env/forest/begin}{.}
{
  \@@_init:
}
\hook_gset_rule:nnnn {env/forest/begin}{.}<{forest-ext/utils}
%    \end{macrocode}
%    \begin{macrocode}
\ExplSyntaxOff
%    \end{macrocode}
% \begin{macro}{\tagforest@toks}
% \begin{macro}{\LogTagForestToks}
%   Name for a new toks and some ways to peek when debugging.
%    \begin{macrocode}
\newtoks\tagforest@toks
%<debug>    \newcommand \LogTagForestToks{%
%<debug>      \expandafter\typeout\expandafter{\expanded{%
%<debug>          \detokenize{[tagforest debug]:: current toks: }% 
%<debug>          \expandafter\detokenize\expandafter{\the\tagforest@toks}%
%<debug>        }% 
%<debug>      }%
%<debug>    }
%    \end{macrocode}
% \end{macro}
% \end{macro}
% \begin{macro}{\tagforest@id}
% \begin{macro}{\LogTagForestId}
%   Name for a new count.
%    \begin{macrocode}
\newcount\tagforest@id
%<debug>    \newcommand \LogTagForestId{%
%<debug>      \expandafter\typeout\expandafter{\expanded{%
%<debug>          \detokenize{[tagforest debug]:: current id: }% 
%<debug>          \the\tagforest@id
%<debug>        }% 
%<debug>      }%
%<debug>    }
%    \end{macrocode}
% \end{macro}
% \end{macro}
% \begin{macro}{%
%   \if@tagforest@debug,%
%   \@tagforest@debugfalse,%
%   \@tagforest@debugtrue,%
%   \tagforest@debug@typeout
% }
%   Conditional for debugging.
%    \begin{macrocode}
\newif\if@tagforest@debug
%<!debug>    \@tagforest@debugfalse
%<debug>    \@tagforest@debugtrue
\newcommand \tagforest@debug@typeout [1]{%
  \if@tagforest@debug
    \ExpandArgs {e} \typeout{[tagforest debug]:: \detokenize{#1}}%
  \fi
}
%    \end{macrocode}
% \end{macro}
%    \begin{macrocode}
\forestset{
%    \end{macrocode}
% For debugging.
%<*debug>
%    \begin{macrocode}
  debug tagforest/.code={
    \tagforest@debug@typeout{#1}%
  },
%    \end{macrocode}
%</debug>
% Various additions in the form of \pkg{forest} options and registers.
% By default, these are noop.
%    \begin{macrocode}
  declare boolean register={tagging},
  tagging=0,
  declare toks register={setup plug},
  setup plug=tagforest@plug@NONE,
  declare toks register={tag plug},
  tag plug=tagforest@plug@NONE,
  Autoforward register={setup plug}{%
    TeX={%
      \IfSocketPlugExistsTF {tagsupport/forest/setup}{#1}{%
        \AssignSocketPlug {tagsupport/forest/setup}{#1}%
      }{%
        \PackageError{ext.tagging (forest library)}{%
          No plug named '#1' exists for socket 'tagsupport/forest/setup'.%
        }{%
          See the forest-ext manual for details.%
        }%
      }%
    },
  },
  Autoforward register={tag plug}{%
    TeX={%
      \IfSocketPlugExistsTF {tagsupport/forest/tag}{#1}{%
        \AssignSocketPlug {tagsupport/forest/tag}{#1}%
      }{%
        \PackageError{ext.tagging (forest library)}{%
          No plug named '#1' exists for socket 'tagsupport/forest/tag'.%
        }{%
          See the forest-ext manual for details.%
        }%
      }%
    },
  },
  plug/.style={%
    setup plug={#1},
    tag plug={#1},
  },
%    \end{macrocode}
% \begin{ftagkeylist}{%
%   tag~nodes,collate~tags%
% }
% I wanted to use a nodewalk styles for tagging and collation, but couldn't (easily) figure out how, so sticking to keylists and processing orders for now.
% Hence, only the final step is a stage \dots.
% But I suspect there's a performance hit here \dots.
% (Or maybe not without comparing internals with public interfaces?
% \pkg{prooftrees} uses keylists and I don't think Sašo suggested substituting code keys for speed at any point?
% But that was a long time ago \dots.)
%    \begin{macrocode}
  declare tagging keylist={tag nodes}{},
  declare tagging keylist={collate tags}{},
%    \end{macrocode}
% \end{ftagkeylist}
% \begin{fkeylist}{%
%  before~tagging~nodes,
%  before~collating~tags, 
%  before~tagging~tree%
% }
%   Regular keylist options.
%    \begin{macrocode}
  declare keylist={before tagging nodes}{},
  declare keylist={before collating tags}{},
  declare keylist={before tagging tree}{},
%    \end{macrocode}
% \end{fkeylist}
% \begin{autotoks}{%
%  node@ttoks, 
%  alt text%
% }
%   Private and public. 
%    \begin{macrocode}
  declare autowrapped toks={node@ttoks}{},
  declare autowrapped toks={alt text}{},
%    \end{macrocode}
% \end{autotoks}
% \begin{autotoksr}{%
%  is~root,
%  is~leaf,
%  is~child,
%  is~edge~label,
%  has~branches,
%  is~branch%
% }
%   Structural descriptors.
% \changes{v0.2}{2026-01-21}{Reduce default structural descriptors.}
%    \begin{macrocode}
  declare autowrapped toks register={is root},
  is root={},
  declare autowrapped toks register={is leaf},
  is leaf={},
  declare autowrapped toks register={is child},
  is child={child},
  declare autowrapped toks register={is edge label},
  is edge label={edge label},
  declare autowrapped toks register={has branches},
  has branches={children},
  declare autowrapped toks register={is branch},
  is branch={},
%    \end{macrocode}
% \end{autotoksr}
% \begin{autotoks}{alt~pre,alt~post}
% \changes{v0.2}{2026-01-21}{Add \texttt{alt pre}, \texttt{alt post}.}
%   Options to prepend/append to \texttt{alt text}, especially in combination with auto-generation.
%   These are per-node options.
%    \begin{macrocode}
  declare autowrapped toks={alt pre}{},
  declare autowrapped toks={alt post}{},
%    \end{macrocode}
% \end{autotoks}
% \begin{ftoksr}{%
%   punctuation~after~parent,%
%   punctuation~after~leaf%
% }
% \begin{ftoks}{punct@mark}
% Punctuation.
% \changes{v0.2}{2026-01-22}{Punctuation registers and options.}
%    \begin{macrocode}
  declare toks register={punctuation after parent},
  punctuation after parent={:\mmzxSpace },
  declare toks register={punctuation after leaf},
  punctuation after leaf={;\mmzxSpace },
  declare toks option={punct@mark}{},
%    \end{macrocode}
% \end{ftoks}
% \end{ftoksr}
% The tagging code depends on injecting additional processing steps into \pkg{forest}'s processing of the tree. 
% This requires redefining \texttt{stages} to include the extra steps.
% This has global effect, but hopefully does no harm \dots.
%    \begin{macrocode}
  stages/.style={
    for root'={
      process keylist register=default preamble,
      process keylist register=preamble
    },
    process keylist=given options,
    process keylist=before typesetting nodes,
    typeset nodes stage,
    process keylist=before packing,
    pack stage,
    process keylist=before computing xy,
    compute xy stage,
%    \end{macrocode}
% The additions for tagging are inserted between \texttt{compute xy stage} and \texttt{before drawing tree}.
%    \begin{macrocode}
%<debug>    debug tagforest={Process keylist: before tagging nodes ...},
    process keylist=before tagging nodes,
%<debug>    debug tagforest={Process keylist: tag nodes ...},
    process keylist=tag nodes,
%<debug>    debug tagforest={Process keylist: before collating tags ...},
    process keylist=before collating tags,
%<debug>    debug tagforest={Process keylist: collate tags ...},
    process keylist=collate tags,
%<debug>    debug tagforest={Process keylist: before tagging tree ...},
    process keylist=before tagging tree,
%<debug>    debug tagforest={Stage: tag tree stage ...},
    tag tree stage,
%<debug>    debug tagforest={Completed all tagging stages!},
    process keylist=before drawing tree,
    draw tree stage
  },
  tag nodes processing order/.nodewalk style={unique=tree},
%    \end{macrocode}
% \changes{v0.2}{2026-01-22}{Apply \texttt{filter} to nodewalk used when collating tags.}
%    \begin{macrocode}
  collate tags processing order={%
    filter={%
      unique=tree depth first%
    }{>Ot=!{alt text}{}}%
  },
  tag tree stage/.style={for root'=tag tree},
%    \end{macrocode}
% By default, the crucial stage does nothing.
%    \begin{macrocode}
  tag tree/.style={},
%    \end{macrocode}
% Redefine various of the additions to \texttt{stages} to do something useful.
% The remaining additions are to allow user interventions.
% 
% We split this up so bits can be used more flexibly e.g.~by \pkg{prooftrees}.
% \pkg{prooftrees} doesn't want the code which generates tags, but it does want \texttt{tag tree}.
% (Well, \pkg{prooftrees} had this code first, so it wants what \pkg{ext.tagging} is pinching.
% \begin{fstyle}{tag@nodes@aux@l,tag@nodes@aux@r}
%   Auxiliaries.
%    \begin{macrocode}
  tag@nodes@aux@l/.style 2 args={%
    if={>__={#1}{}}{#1={#2}}{%
      if={>__={#2}{}}{}{%
        +#1={#2\mmzxSpace },
      },
    },
  },
  tag@nodes@aux@r/.style 2 args={%
    if={>__={#1}{}}{#1=#2}{%
      if={>__={#2}{}}{}{%
        #1+={\mmzxSpace #2},
      },
    },
  },
%    \end{macrocode}
% \end{fstyle}
% \begin{choicekey}{tag~nodes~uses}
%   How to tag individual nodes.
%   Currently, only nodes can be tagged.
%    \begin{macrocode}
  tag nodes uses/.is choice,
  tag nodes uses/noop/.style={%
    redeclare tagging keylist={tag nodes}{},
  },
%    \end{macrocode}
% \changes{v0.2}{2026-02-17}{Correct processor input.}
% \changes{v0.2}{2026-02-17}{Work around my failure to figure out how to process the punctuation.}
% The punctuations works this way, but I've no idea why it only works this way or why it otherwise fails so utterly.
% NOTE: KEYLIST ORDER NOT GUARANTEED!!!
%    \begin{macrocode}
  tag nodes uses/alt text/.style={%
    redeclare tagging keylist={tag nodes}{%
      delay={%
        if level=0{%
          if alt text={}{%
            tag@nodes@aux@l/.process={Rw{is root}{{node@ttoks}{##1}}},
          }{},
        }{},
      },
      if phantom={}{%
        if alt text={}{%
          if edge label={}{%
%    \end{macrocode}
% \changes{v0.2}{2026-01-21}{Add pre/post for alt.}
% \changes{v0.2}{2026-01-22}{Add punctuation.}
%    \begin{macrocode}
%<debug>    debug tagforest/.process={Ow {id}{Node id: ##1}},
%<debug>    debug tagforest/.process={OOOw3{content}{alt pre}{alt post}{No edge label. 
%<debug>      alt pre is ##2.
%<debug>      Content is ##1.
%<debug>      alt post is ##3.}%
%<debug>    },
            node@ttoks/.option=content,
            tag@nodes@aux@l/.process={Ow{alt pre}{{node@ttoks}{##1}}},
            tag@nodes@aux@r/.process={Ow{alt post}{{node@ttoks}{##1}}},
          }{%
%    \end{macrocode}
% \changes{v0.2}{2026-01-21}{Correct ordering of content and label; add pre/post for alt.}
%    \begin{macrocode}
%<debug>    debug tagforest/.process={Ow {id}{Node id: ##1}},
%<debug>    debug tagforest/.process={Ow{edge label}{Edge label is ##1}},
%<debug>    debug tagforest/.process={OOOw3{content}{alt pre}{alt post}{% 
%<debug>      alt pre is ##2.
%<debug>      Content is ##1.
%<debug>      alt post is ##3.}%
%<debug>    },
            node@ttoks/.option=content,
            tag@nodes@aux@l/.process={Ow{alt pre}{{node@ttoks}{##1}}},
            tag@nodes@aux@r/.process={Ow{alt post}{{node@ttoks}{##1}}},
            tag@nodes@aux@l/.process={Ow{edge label}{{node@ttoks}{##1}}},
            tag@nodes@aux@l/.process={Rw{is edge label}{{node@ttoks}{##1}}},
          },
          if={>O_>{n children}{1}}{%
%<debug>    debug tagforest/.process={OOw2 {id}{n children}{Node id: ##1 has ##2 branches}},
            if={>Rt={has branches}{}}{}{%
              node@ttoks+/.process={ORw2{n children}{has branches}{\mmzxSpace ##1 ##2\mmzxSpace }},
            },
            delay={%
%    \end{macrocode}
% \changes{v0.2}{2026-01-21}{Prepend \texttt{is branch} and number only if register not empty.}
%    \begin{macrocode}
              if={>Rt={is branch}{}}{%
%<debug>                    for children={%
%<debug>    debug tagforest/.process={Ow {id}{Node id: ##1}},
%<debug>    debug tagforest/.process={Ow{n}{Branch no. is ##1}},
%<debug>    debug tagforest=Nothing to prepend.,
%<debug>                    },
              }{%
                for children={%
%<debug>    debug tagforest/.process={Ow {id}{Node id: ##1}},
%<debug>    debug tagforest/.process={Ow{n}{Branch no. is ##1}},
                  +node@ttoks/.process={ROw2{is branch}{n}{\mmzxSpace ##1 ##2\mmzxSpace }},
                },
              },
            },
          }{%
            if n children=1{%
              delay={%
%<debug>    debug tagforest/.process={OOw2 {id}{!1.id}{Node id: ##1 has 1 child with id ##2}},
% ^^A tag@nodes@aux@l does not work here ...
                !1.+node@ttoks/.process={Rw{is child}{##1\mmzxSpace }},
              },
            }{%
%<debug>    debug tagforest/.process={Ow {id}{Node id: ##1 is a leaf}},
              tag@nodes@aux@r/.process={Rw{is leaf}{{node@ttoks}{##1}}},
            },
          },
          delay n=2{%
            alt text'/.option=node@ttoks,
            if alt text={}{}{%
              if n children=0{% ^^A why does *only* here work, plîs?
                alt text+/.register=punctuation after leaf,
              }{%
                alt text+/.register=punctuation after parent,
              },
            },
          },
        }{},
      },
    },
  },
%    \end{macrocode}
% \end{choicekey}
% \begin{choicekey}{collate~tags~uses}
%   I don't really like this way of doing this.
%   I'd rather use e.g.~a \texttt{.choice} key or something for \texttt{collate tags}, but I'm not sure how to do that and have the keylist be public \dots.
%    \begin{macrocode}
  collate tags uses/.is choice,
  collate tags uses/noop/.style={%
    redeclare tagging keylist={collate tags}{},
  },
  collate tags uses/alt text/.style={%
    redeclare tagging keylist={collate tags}{%
      collate tag/.option=alt text,
    },
  },
%    \end{macrocode}
% \end{choicekey}
% \begin{fcodekey}{collate~tag}
%   How to collate the tags.
%    \begin{macrocode}
  collate tag/.code={%
%<debug>        \tagforest@debug@typeout{Appending toks #1 .}%
    \forestext@toksapp\tagforest@toks{#1 }%
  },
%    \end{macrocode}
% \end{fcodekey}
% \begin{choicekey}{tag~tree~uses}
% Calculate dimensions used to determine an approximate bounding box size. 
%    \begin{macrocode}
  tag tree uses/.is choice,
  tag tree uses/noop/.style={%
    tag tree/.style={},
  },
  tag tree uses/alt/.style={% wrong bbox!!
    tag tree/.style={%
      tempdimc/.max={>OOw2+d{x}{max x}{####1+####2}}{tree},
      tempdimc-/.min={>OOw2+d{x}{min x}{####1+####2}}{tree},
      tempdimd/.max={>OOw2+d{y}{max y}{####1+####2}}{tree},
      tempdimd-/.min={>OOw2+d{y}{min y}{####1+####2}}{tree},
%<debug>    debug tagforest={Dimensions (x then y) are },
%<debug>    debug tagforest/.register=tempdimc,
%<debug>    debug tagforest/.register=tempdimd,
%    \end{macrocode}
% The next line should create the tagging structure and insert the assembled \texttt{alt} text.
%    \begin{macrocode}
%<debug>    debug tagforest/.process={RRRw3{tag plug}{tempdimc}{tempdimd}{%
%<debug>      Tagging tree now with tag plug=####1, x=####2, y=####3 ...}},
    TeX/.process={RRRw3{tag plug}{tempdimc}{tempdimd}{%
        \tagforest@tag@tree@tag{####1}{####2}{####3}}%
      },
    },
  },
%    \end{macrocode}
% \end{choicekey}
%    \begin{macrocode}
}
\ExplSyntaxOn
%    \end{macrocode}
% ^^A % \begin{splug}{tagsupport/memoize/include/extern/after~tagforest/alt}
% ^^A %   \changes{v0.2}{2026-02-16}{Use sockets from \pkg{memoize-ext}.}
% ^^A %    \begin{macrocode}
% ^^A \socket_new_plug:nnn {tagsupport/memoize/include/extern/after} {tagforest/alt}
% ^^A {
% ^^A   \tag_mc_end:
% ^^A   \tag_struct_end:
% ^^A   \tag_mc_begin_pop:n{}
% ^^A }
% ^^A %    \end{macrocode}
% ^^A % \end{splug}
% ^^A % \begin{splug}{tagsupport/memoize/include/extern/before~tagforest/alt}
% ^^A %   \changes{v0.2}{2026-02-16}{Use sockets from \pkg{memoize-ext}.}
% ^^A %    \begin{macrocode}
% ^^A \socket_new_plug:nnn {tagsupport/memoize/include/extern/before} {tagforest/alt}
% ^^A {
% ^^A   \global\advance\tagforest@id~by~1\relax
% ^^A   \mode_if_vertical:T
% ^^A   {
% ^^A     \if@inlabel
% ^^A       \mode_leave_vertical:
% ^^A     \else
% ^^A       \tag_socket_use:n {para/begin}
% ^^A     \fi
% ^^A   }
% ^^A   \tag_mc_end_push:
% ^^A   \tl_set:No \l_@@_toks_tl {\the\tagforesttoks}
% ^^A   \tag_struct_begin:n
% ^^A   {
% ^^A     tag=Figure,
% ^^A     alt=\l_@@_toks_tl,
% ^^A   }
% ^^A   \tag_mc_begin:n {tag=Figure}
% ^^A   \cs_new:cpe {tagforest@mark@pos@\the\tagforest@id}
% ^^A   {
% ^^A     \@@_pgftikz_tag_bbox:ennn {tagforest-id\the\tagforest@id}
% ^^A     {#1}{#2}{#3}
% ^^A   }
% ^^A   \tag_struct_gput:ene
% ^^A   {\tag_get:n {struct_num}}
% ^^A   {attribute}
% ^^A   {
% ^^A     /O /Layout /BBox~
% ^^A     [
% ^^A       \use:c
% ^^A       {tagforest@mark@pos@\the\tagforest@id}
% ^^A     ]
% ^^A   }
% ^^A   \tex_savepos:D
% ^^A   \@@_property_record_orig:ee {tagforest-id\the\tagforest@id}
% ^^A   {xpos,ypos}
% ^^A   \tex_savepos:D
% ^^A }
% ^^A %    \end{macrocode}
% ^^A % \end{splug}
% \begin{splug}{tagsupport/forest/tag/mmz~alt}
%   From \pkg{prooftrees}, which will use it from here, hopefully.
%
%   \changes{v0.2}{2026-02-21}{Use extern sockets from \pkg{memoize-ext}.}
%   This greatly simplifies the syntax here and avoids complications with \pkg{expl3} in memos etc.
%   We also no longer need use \texttt{direct input}, so drop its limitations.
%    \begin{macrocode}
\socket_new_plug:nnn {tagsupport/forest/tag/mmz}{alt}
{
  \xtoksapp\mmzCCMemo{
    \exp_not:N \mmzxtagtoks
    =
    \c_left_brace_str
    \exp_args:No \text_purify:n {\the\tagforest@toks}
    \c_right_brace_str
    \exp_not:N \AssignSocketPlug
    \c_left_brace_str
    tagsupport/memoize/include/extern/before
    \c_right_brace_str
    \c_left_brace_str
    mmzx/alt
    \c_right_brace_str
    \exp_not:N \AssignSocketPlug
    \c_left_brace_str
    tagsupport/memoize/include/extern/after
    \c_right_brace_str
    \c_left_brace_str
    mmzx/alt
    \c_right_brace_str
  }
}
%    \end{macrocode}
% \end{splug}
% \changes{v0.2}{2026-02-21}{Code for memos is now much simpler!
% But need to allow for possible redefinition of \ecs{property_ref:nn} for memoization.}
%    \begin{macrocode}
\hook_gput_code:nnn {begindocument/before}{.}
{
  \cs_if_free:NTF \mmzx_property_ref_orig:nn
  {
    \cs_new_eq:NN \@@_property_ref_orig:nn \property_ref:nn
  } {
    \cs_new_eq:NN \@@_property_ref_orig:nn \mmzx_property_ref_orig:nn
  }
  \cs_generate_variant:Nn \@@_property_ref_orig:nn {ee}
  \cs_if_free:NTF \mmzx_property_record_orig:nn
  {
    \cs_new_eq:NN \@@_property_record_orig:nn \property_record:nn
  } {
    \cs_new_eq:NN \@@_property_record_orig:nn \mmzx_property_record_orig:nn
  }
  \cs_generate_variant:Nn \@@_property_record_orig:nn {ee}
% ^^A   \@ifpackageloaded{memoize}
% ^^A   {
% ^^A     \tag_if_active:T
% ^^A     {
% ^^A       \mmzset{direct~ccmemo~input=true,}
% ^^A     }
% ^^A   }{}
% ^^A   \@ifpackageloaded{memoize-ext}
% ^^A   {
% ^^A   \cs_new_eq:NN \@@_property_ref_orig:nn \__mmzx_property_ref_orig:nn
% ^^A   \cs_new_eq:NN \c_@@_nexpl_at_cctab \c__mmzx_nexpl_at_cctab
% ^^A   }{
% ^^A     \cs_new_eq:NN \@@_property_ref_orig:nn \property_ref:nn
% ^^A     \cctab_const:Nn \c_@@_nexpl_at_cctab {
% ^^A       \cctab_select:N \c_code_cctab 
% ^^A       \makeatletter
% ^^A       \int_set:Nn \tex_endlinechar:D { 13 }
% ^^A       \char_set_catcode_space:n      { 9 }
% ^^A       \char_set_catcode_space:n      { 32 }
% ^^A       \char_set_catcode_active:n     { 126 } % tilde
% ^^A     }
% ^^A   }
}
\ExplSyntaxOff
%    \end{macrocode}
%</sty>
%^^A >>>
%
% ^^A \Finale
% %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% 
%^^A vim: et:tw=0:sw=2:ts=2:foldmethod=marker:fmr=<<<,>>>:
