% \iffalse meta-comment
%
%% File: latex-lab-context.dtx (C) Copyright 2025 LaTeX Project
%
% It may be distributed and/or modified under the conditions of the
% LaTeX Project Public License (LPPL), either version 1.3c of this
% license or (at your option) any later version.  The latest version
% of this license is in the file
%
%    https://www.latex-project.org/lppl.txt
%
%
% The latex-lab bundle is developed in the LaTeX2e GitHub.
% Issues may be reported at
%
%    https://github.com/latex3/latex2e/issues
%
\def\ltlabcontextversion{0.6a}
\def\ltlabcontextdate{2026-05-26}
%<*driver>
\DocumentMetadata{tagging=on,pdfstandard=ua-2,lang=en}
\documentclass{l3in2edoc}

\EnableCrossrefs
\CodelineIndex
\begin{document}
  \DocInput{latex-lab-context.dtx}
\end{document}
%</driver>
%
% \fi
%
% \title{The \textsf{latex-lab-context} package\\
%    Providing context for template instances and code that needs
%    to know where and when it is executed}
%
% \author{\LaTeX{} Project\thanks{Initial implementation done by Frank Mittelbach}}
% \date{v\ltlabcontextversion\ \ltlabcontextdate}
%
% \maketitle
%
% \newcommand{\xt}[1]{\textsl{\textsf{#1}}}
% \newcommand{\TODO}[1]{\textbf{[TODO:} #1\textbf{]}}
%
% \providecommand\hook[1]{\texttt{#1}}
%
% \begin{abstract}
% \end{abstract}
%
% \tableofcontents
%
% \section{Introduction}
%
%    In this module we implement the concept of \enquote{contexts}
%    within the document and depending on the current context
%    formatting behavior might change. The main application for this
%    is in instances of templates (where things can be easily automated
%    to react to context), but it is probably also applicable to other
%    situations (but these might need more thought).
%
% \subsection{Definition of a  \enquote{context}}
%
%   The \enquote{context} is an attribute of every point of the
%   formatted document, i.e., at each point during the formatting one
%   can determine in which \enquote{context} the formatting happens
%   and based on this adjust the formatting method.
%   
%   The \enquote{context} is not an entirely flat structure: we
%   distinguish between the \enquote{primary context}, the
%   \enquote{secondary context}, and \enquote{tertiary context}, all of
%   which can be changed individually based on a number of rules as
%   discussed below.
%   
%   Any context is denoted by a name (\verb=[a-z]*= letters only). The
%   empty name is allowed to ease specification of the common case
%   (i.e., the default \enquote{primary context} is the main galley
%   and by default no \enquote{secondary context} and
%   \enquote{tertiary context} are specified).
%   
% \subsection{The \enquote{primary context}}
%   
%   The \enquote{primary context} is described with a fixed (but
%   extensible?) set of names:
%   \begin{itemize}
%
%   \item \meta{empty} denotes that we are in \enquote{galley} context
%      producing text for the page;
%
%   \item \texttt{caption}
%      denotes that we are typesetting the caption text of a \cs{caption} command
%      --- this is also used for captions outside of floats,
%
%   \item \texttt{footnote}
%      denotes that we are typesetting the footnote text;
%
%   \item \texttt{float}
%      denotes that we are typesetting a float;
%
%   \item \texttt{marginal}
%      denotes that we are typesetting a marginal;
%
%   \item \texttt{header}
%      denotes that we are typesetting the running header;
%
%   \item \texttt{footer}
%      denotes that we are typesetting the running footer.
%
%   \end{itemize}
%   
%   When the \enquote{primary context} is set it replaces the current
%   \enquote{primary context} and resets the \enquote{secondary
%   context} and \enquote{tertiary context} to \meta{empty}.  The
%   setting is local, i.e., it obeys grouping.
%   
%   We may want to require that a context name is declared before its
%   first use, e.g., via \cs{DeclarePrimaryContext} (right now this is
%   not done).
%   
%   
% \subsection{The \enquote{secondary context}}
%   
%   The \enquote{secondary context} can be used to further
%   sub-structure the primary context, e.g., with \texttt{float} as a
%   primary, \texttt{figure} and \texttt{table} (e.g., the float types
%   could form a secondary context).
%   
%   
%   
% \subsection{The \enquote{tertiary (font size) context}}
%   
%   The \enquote{tertiary context} concerns itself only with font size
%   changes, because that is most commonly a cause for layout changes.
%   It is described through a fixed (but extensible) set of names:
%   
%   \begin{itemize}
%   \item
%      \meta{empty} denotes that there is no special tertiary
%      context, i.e., that we are producing material in \cs{normalsize};
%
%   \item \texttt{tiny}
%      denotes that we are typesetting in \cs{tiny} font size;
%   
%   \item and similarly for \texttt{scriptsize},
%     \texttt{footnotesize}, \texttt{small}, \texttt{large},
%     \texttt{Large}, \texttt{LARGE}, \texttt{huge}, and \texttt{Huge}.  
%   \end{itemize}
%   The above list of tertiary contexts are automatically set by the
%   standard \LaTeX{} font size commands (as part of
%   \cs{@setfontsize}). Classes that use additional font size commands
%   but do not use the \LaTeXe{} command for this need to explicitly
%   set the tertiary context with \cs{SetTertiaryContext} if they
%   want their size be recognized as a context.
%   
%   The \enquote{tertiary context} serves a double role: on the one
%   hand it substructures the primary and secondary contexts further,
%   e.g.,
%   \meta{primary}\texttt{:}\meta{secondary}\texttt{:}\meta{tertiary}
%   allows you to specify different settings compared to
%   \meta{primary}\texttt{:}\meta{secondary}
%   but if there are only instance declarations for
%   \meta{primary}
%   or
%   \meta{primary}\texttt{:}\meta{secondary}
%   and for 
%   \texttt{::}\meta{tertiary}
%   then the latter is chosen over the the former.
% 
% 
% 
% \section{Setting context}
%
% \begin{function}{\SetPrimaryContext}
%   \begin{syntax}
%     \cs{SetPrimaryContext} \Arg{new context}
%   \end{syntax}
%   The \meta{new context} should be a name consisting only of
%   lowercase letters a--z (and to make sense only a name that is
%   actually used---typos will go undetected).
%   
%   This declaration checks if there is a rule for the combination of
%   the current context and the requested \meta{new context} (see
%   \cs{DeclarePrimaryContextRule}) and if so applies it to determine
%   to which context the \enquote{primary context} should be set. 
%   If there is no such rule then \meta{new context} will be used
%   unconditionally.
%
%   As a second action the command resets the \enquote{secondary
%   context} and \enquote{teriary context} to \meta{empty}, i.e., it
%   clears the lower-level contexts. This is only done for the primary
%   context. The reason is that when a primary context changes any
%   sub-division (secondary and tertiary context) is no longer
%   applicable and should therefore be reset.
%
%   The effects are local, i.e., both context changes revert to the
%   previous value at the end of the current group.
% \end{function}
%
%
%
% \begin{function}{\DeclarePrimaryContextRule}
%   \begin{syntax}
%     \cs{DeclarePrimaryContextRule} \Arg{current}\Arg{new}\Arg{result}
%   \end{syntax}
%   This declaration defines the rule that when the current primary
%   context is \meta{current} and \meta{new} is requested as the new
%   context then instead \meta{result} is made the new
%   \enquote{primary context}.
%
%   If \texttt{*} is used in the first argument then this indicates
%   any current context and thus \meta{new} is always replaced by
%   \meta{result}. This can be more concisely written as
%   \begin{quote}
%     \cs{DeclarePrimaryContextRule*} \Arg{new}\Arg{result}
%   \end{quote}
%   if preferred.
%
%   Declaring such rules is a global operation.
% \end{function}
%
%
%
% \begin{function}{\SetSecondaryContext}
%   \begin{syntax}
%     \cs{SetSecondaryContext} \Arg{new context}
%   \end{syntax}
%   The \meta{new context} should be a name consisting only of
%   lowercase letters a--z (and to make sense only a name that is
%   actually used---typos will go undetected).
%   
%   This declaration checks if there is a rule for the combination of
%   the current secondary context and the requested \meta{new context}
%   (see \cs{DeclareSecondaryContextRule}) and if so applies it to
%   determine to which context the \enquote{secondary context} should
%   be set. If there is no such rule then \meta{new context} will
%   be used unconditionally.
%
%   This does not reset the \enquote{tertiary context}.
%
%   The effect is local, i.e., the context change reverts to the
%   previous value at the end of the current group.
% \end{function}
%
%
%
% \begin{function}{\DeclareSecondaryContextRule}
%   \begin{syntax}
%     \cs{DeclareSecondaryContextRule} \Arg{current}\Arg{new}\Arg{result}
%   \end{syntax}
%   This declaration defines the rule that when the current secondary
%   context is \meta{current} and \meta{new} is requested as the new
%   context then instead \meta{result} is made the new
%   \enquote{secondary context}.
%
%   If \texttt{*} is used in the first argument then this indicates
%   any current context and thus \meta{new} is always replaced by
%   \meta{result}.
%
%   Declaring such rules is a global operation.
%
%   TODO: we should perhaps provide
%   \begin{quote}
%     \cs{DeclareSecondaryContextRule} \Arg{current
%   primary}\Arg{current secondary}\Arg{new secondary}\Arg{result}
%   \end{quote}
%   instead of the above. This would be a breaking change, but as this
%   is currently only a trial implementation that should be ok.
% \end{function}
%
%
%
% \section{Context usage in template instances}
%
%   If a template instance is used via
%   \cs{UseInstance}\Arg{type}\Arg{inst-name} then this normally
%   results in calling up an instance of type \meta{type} with the
%   name \meta{inst-name}.\footnote{Such instances are defined with
%   \cs{DeclareInstance} or \cs{DeclareInstanceCopy}, see the
%   documentation in \texttt{lttemplates-doc.pdf}.}
%
%   However, when the \enquote{primary context} and/or the
%   \enquote{secondary context} or \enquote{tertiary context} is
%   non-empty then \cs{UseInstance} searches for an instance that is
%   especially tailored to the current context.  This works as
%   follows:
%   \begin{itemize}
%   \item
%
%     The string \texttt{:}\meta{primary
%     context}\texttt{:}\meta{secondary
%     context}\texttt{:}\meta{tertiary context} is appended to
%     \meta{inst-name} and if that instance exist it is
%     used.\footnote{Note that this means that if the \meta{primary
%     context} and \enquote{secondary context} are empty we
%     effectively append \texttt{:::}\meta{tertiary context}.}
%
%   \item
%
%     If not then \meta{inst-name}\texttt{:}\meta{primary
%     context}\texttt{:}\meta{secondary context} is tried next.
%
%   \item
%
%     If not then \meta{inst-name}\texttt{:}\meta{primary context} is
%     tried next.
%
%   \item
%
%     If that doesn't exist either than the \enquote{tertiary context}
%     is considered once more on its own by checking for
%     \meta{inst-name}\texttt{:::}\meta{tertiary context} and using
%     that if it exist.
%
%   \item
%     Otherwise \meta{inst-name} is used as usual.
%
%   \end{itemize}
% 
%   This means it becomes trivial to alter the behavior of
%   instances if they appear in a special typesetting context. For
%   example, in \LaTeXe{} display blocks, e.g., lists, center, etc., all
%   changed their vertical spacing setup if the surrounding text was
%   in \cs{small} or in \cs{footnotesize} (did nothing if you
%   typeset in, say, \cs{Large} which had the strange effect that
%   lists in \cs{Large} or \cs{huge} got whatever was set by a size
%   command that changed list parameters).
%
%   With the new context model all you have to do is to provide
%   additional instances, e.g., if \texttt{itemize-1} is the instance
%   name for itemized lists on the first level then
%   \texttt{itemize-1:footnote} would be the instance to be used if an
%   itemize environment is used within a footnote.
%
%   In addition (or alternatively) you could setup
%   \texttt{itemize-1:::Large} which would be selected if itemize is
%   used anywhere and the fontsize is \cs{Large} (and there are no
%   explicit specifications for the context otherwise).
% 
% 
% \section{Notes}
%
%   With special classes, e.g., \texttt{ltx-talk}, additional primary
%   and secondary contexts could be added (e.g., using the modes
%   there).Could do with some experimentation what works best.
%   
%   If the design of a class requires identical behavior in different
%   contexts, e.g., the same behavior in \texttt{header} and
%   \texttt{footer}, it is possible to simplify the setup by only
%   specifying the design for the \texttt{header} context and then
%   declaring:
%\begin{verbatim}
%   \DeclarePrimaryContextRule{*}{footer}{header}
%\end{verbatim}
%
%   One can also add new primary or secondary contexts simply by making
%   use of \cs{Set...Context} declarations with a new name and then
%   use these named contexts when setting up special instance
%   versions.
%
%   Bottom line, what is here is nothing more than a first, or rather
%   by now, second prototype and likely to change to some extent.
%
%
%
% \section{Currently mainly internal interfaces}
%
% \begin{variable}{\l_context_primary_str}
%   This variable holds the current primary context name (or is empty
%   if we are in the context of the main galley).
% \end{variable}
%
% \begin{variable}{\l_context_secondary_str}
%   A sub-structure of the primary context, e.g., for the
%   \texttt{float} context the secondary could be the float type.
% \end{variable}
%
% \begin{variable}{\l_context_tertiary_str}
%   This variable holds the current tertiary (font size) context name (or is
%   empty if we are typesetting in \cs{normalsize}). It is by default
%   automatically set by all fontsize commands in core \LaTeX{}.
% \end{variable}

% \section{Debugging}
% 
% \begin{function}{\DebugContextsOn,\DebugContextsOff, \context_debug_on:, \context_debug_off:}
% 
%   These commands enable/disable debugging messages for context related
%   processing.
% 
% \end{function}
%
%
% \section{Changes to internals of \LaTeX}
%
% \begin{function}{\@setfontsize}
%   This command has be augmented to execute \cs{SetSecondaryContext}
%   with the name of the current fontsize command as the context argument.
% \end{function}
%
% \begin{function}{\@float}
%
%   This command has been augmented to execute
%   \cs{SetPrimaryContext}\texttt{\{float\}}.  This results in the
%   body of all real floats being typeset in the primary
%   \texttt{float} context. Not covered are packages or methods that
%   make floats not using \cs{@float} or making pseudo floats, e.g.,
%   something not floating but having a caption.
% \end{function}
%
%
%
%
%
% \section{Implementation}
%    \begin{macrocode}
%<*package>
%<@@=tag>
%    \end{macrocode}
%    \begin{macrocode}
\ProvidesExplPackage {latex-lab-testphase-context}
                     {\ltlabcontextdate}
                     {\ltlabcontextversion}
  {Providing context for instance, etc.}
%    \end{macrocode}
%
%    \begin{macrocode}
%<*package>
%<@@=context>
%    \end{macrocode}
%
% \changes{v0.5c}{2026-03-12}{Switch from \texttt{x}- to \texttt{e}-type
%   expansion in expl3 code}
%    
%    
% \subsubsection{Debugging}
%
%    
%  \begin{variable}{\g_@@_debug_bool}
%    
%    \begin{macrocode}
\bool_new:N \g_@@_debug_bool
%    \end{macrocode}
%  \end{variable}
%
%
%  \begin{macro}{\@@_debug:n,\@@_debug_typeout:n}
%    
%    \begin{macrocode}
\cs_new_eq:NN \@@_debug:n \use_none:n
\cs_new_eq:NN \@@_debug_typeout:n \use_none:n
%    \end{macrocode}
%  \end{macro}
%
%  \begin{macro}{\context_debug_on:,\context_debug_off:,
%                \@@_debug_gset:}
%    \begin{macrocode}
\cs_new_protected:Npn \context_debug_on:
  {
    \bool_gset_true:N \g_@@_debug_bool
    \@@_debug_gset:
  }
%    \end{macrocode}
%
%    \begin{macrocode}
\cs_new_protected:Npn \context_debug_off:
  {
    \bool_gset_false:N \g_@@_debug_bool
    \@@_debug_gset:
  }
%    \end{macrocode}
%
%    \begin{macrocode}
\cs_new_protected:Npn \@@_debug_gset:
  {
    \cs_gset_protected:Npe \@@_debug:n ##1
      { \bool_if:NT \g_@@_debug_bool {##1} }
    \cs_gset_protected:Npe \@@_debug_typeout:n ##1
      { \bool_if:NT \g_@@_debug_bool { \typeout{[Context]~ ==>~ ##1} } }
  }
%    \end{macrocode}
%  \end{macro}
%
%
%  \begin{macro}{\DebugContextsOn,\DebugContextsOff}
%    
%    \begin{macrocode}
\cs_new_protected:Npn \DebugContextsOn  { \context_debug_on:  }
\cs_new_protected:Npn \DebugContextsOff { \context_debug_off: }
%    \end{macrocode}
%    
%    \begin{macrocode}
\DebugContextsOff
%    \end{macrocode}
%  \end{macro}
%
%
%
%
%  \begin{macro}{\l_context_primary_str}
%    Variable to hold the name of the current primary context.
%    \begin{macrocode}
\str_new:N \l_context_primary_str
%    \end{macrocode}
%  \end{macro}
%
%
%
%
%  \begin{macro}{\SetPrimaryContext}
%    Change the context: if the target context is empty, just do
%   it. Otherwise if we have a rule for the current context and new context
%   use that; otherwise if there is a star rule apply that; otherwise
%   set the new value as requested,
%    \begin{macrocode}
\cs_new_protected:Npn \SetPrimaryContext #1 {
  \str_set:Ne \l_context_primary_str
     { \tl_if_empty:nF { #1 }
       { \cs_if_exist_use:cF
         { g_@@_primary_ \l_context_primary_str _ #1 _tl }
         { \cs_if_exist_use:cF
           { g_@@_primary_ * _ #1 _tl }
           { #1 }
         }
       }
     }
  \@@_debug_typeout:n{set~primary~ <-~ \l_context_primary_str }   
%    \end{macrocode}
%    Also reset the secondary and tertiary context.
%    \begin{macrocode}
  \SetSecondaryContext {}
  \SetTertiaryContext {}
}
%    \end{macrocode}
%  \end{macro}
%
%
%  \begin{macro}{\DeclarePrimaryContextRule}
%    Rules are simply stored in macro names:
%    \begin{macrocode}
\cs_new_protected:Npn \DeclarePrimaryContextRule #1#2#3 {
  \tl_gclear_new:c { g_@@_primary_ #1 _ #2 _tl }
  \tl_gset:cn      { g_@@_primary_ #1 _ #2 _tl } {#3}
}
%    \end{macrocode}
%  \end{macro}
%
%
%  \begin{macro}{\l_context_secondary_str}
%    Variable to hold the name of the current secondary context.
%    \begin{macrocode}
\str_new:N \l_context_secondary_str
%    \end{macrocode}
%  \end{macro}
%
%
%  \begin{macro}{\SetSecondaryContext}
%    Similar to \cs{SetPrimaryContext} but without resetting the
%     tertiary context.
%    \begin{macrocode}
\cs_new_protected:Npn \SetSecondaryContext #1 {
  \str_set:Ne \l_context_secondary_str
     { \tl_if_empty:nF { #1 }
       { \cs_if_exist_use:cF
         { g_@@_secondary_ \l_context_secondary_str _ #1 _tl }
         { \cs_if_exist_use:cF
           { g_@@_secondary_ * _ #1 _tl }
           { #1 }
         }
       }
     }
  \@@_debug_typeout:n{set~ secondary~ <-~ \l_context_secondary_str }   
}
%    \end{macrocode}
%  \end{macro}
%
%
%  \begin{macro}{\DeclareSecondaryContextRule}
%    
%    \begin{macrocode}
\cs_new_protected:Npn \DeclareSecondaryContextRule #1#2#3 {
  \tl_gclear_new:c { g_@@_secondary_ #1 _ #2 _tl }
  \tl_gset:cn      { g_@@_secondary_ #1 _ #2 _tl } {#3}
}
%    \end{macrocode}
%  \end{macro}
%
%
%
%  \begin{macro}{\SetTertiaryContext,\DeclareTertiaryContextRule,\l_context_tertiary_str}
%    
% \changes{v0.6a}{2026-05-26}{Implement tertiary context}
%     \texttt{e}-type
%    \begin{macrocode}
\cs_new_protected:Npn \SetTertiaryContext #1 {
  \str_set:Ne \l_context_tertiary_str
     { \tl_if_empty:nF { #1 }
       { \cs_if_exist_use:cF
         { g_@@_tertiary_ \l_context_tertiary_str _ #1 _tl }
         { \cs_if_exist_use:cF
           { g_@@_tertiary_ * _ #1 _tl }
           { #1 }
         }
       }
     }
  \@@_debug_typeout:n{set~ tertiary~ <-~ \l_context_tertiary_str }   
}
%    \end{macrocode}
%    
%    \begin{macrocode}
\cs_new_protected:Npn \DeclareTertiaryContextRule #1#2#3 {
  \tl_gclear_new:c { g_@@_tertiary_ #1 _ #2 _tl }
  \tl_gset:cn      { g_@@_tertiary_ #1 _ #2 _tl } {#3}
}
%    \end{macrocode}
%    
%    \begin{macrocode}
\str_new:N \l_context_tertiary_str

%    \end{macrocode}
%  \end{macro}
%
%
%
%
%
%
% \subsection{Supporting font size changes as a tertiary context}
%
%
%
%  \begin{macro}{\@setfontsize}
%    We try to be careful when setting the name as we don't know if
%   \cs{string} returns a backslash or not.
%    \begin{macrocode}
\def\@setfontsize#1#2#3{\@nomath#1%
  \ifx\protect\@typeset@protect
  \let\@currsize#1%
  \begingroup
    \escapechar\m@ne
    \expandafter
  \endgroup
    \expandafter
  \SetTertiaryContext
    \expandafter {\string#1}%
  \fi
  \fontsize{#2}{#3}\selectfont
}
%    \end{macrocode}
%
%    If we are typesetting in \cs{normalsize} we don't want that as 
%    the context, so here is a first application of a rule.
%    \begin{macrocode}
\DeclareTertiaryContextRule{*}{normalsize}{}
%    \end{macrocode}
%  \end{macro}
%
%
%
% \subsection{Supporting primary context}
%
% \subsubsection{Float context}
%
%  \begin{macro}{\@float}
%    This should  work with most float pages. There are of course some
%    classes or packages that produce pseudo-floats without calling
%    \cs{@float}. These need to be handled separately by adding a
%    \cs{SetPrimaryContext} in their code.
%    \begin{macrocode}
\AddToHook{cmd/@float/before}{\SetPrimaryContext{float}}
%    \end{macrocode}
%  \end{macro}
%
%
% \subsubsection{Caption context}
%
% \TODO{in latex-lab-float}
% \cs{@makecaption} is redefined there and the context should be set in that
% command (or its replacement one day).
%
% \TODO{longtable and anything else that runs around producing \cs{caption}s}
%
% \TODO{Again, the secondary context chould be the current float type}
%
%
%
% \subsubsection{Footnote context}
%
% \TODO{in latex-lab-footnote}
%
%
%
% \subsubsection{Header context}
%
% \TODO{}
%
%
%
% \subsubsection{Footer context}
%
% \TODO{}
%
%
%
% \subsubsection{Marginal context}
%
% \TODO{}
%
%
%
%
%
% \subsection{Changes to lttemplates.dtx code}
%
%    \begin{macrocode}
%<@@=template>
%    \end{macrocode}
%
%
%
%
%  \begin{macro}{\@@_use_instance_auxii:nn, \@@_use_instance_finally:nn}
%
%    The \cs{@@_use_instance_auxii:nn} is the command that is used by
%    \cs{UseInstance} to select and execute a requested instance. So
%    we now have to have a little more logic here.

%    In the normal case (both primary and secondary context are empty)
%    we now have 2 tests. We have up to 6 tests if one of the contexts
%    is non-empty.
% \changes{v0.6a}{2026-05-26}{Re-implement logic determining the
%     instance name with a tertiary context }
%    \begin{macrocode}
\cs_new_eq:NN \@@_use_instance_finally:nn \@@_use_instance_auxii:nn


\cs_set_protected:Npn \@@_use_instance_auxii:nn #1#2 {
  \exp_args:Ne\str_if_empty:nTF
     { \l_context_primary_str \l_context_secondary_str \l_context_tertiary_str }
     {
       \__context_debug_typeout:n{no~ special~ context}
       \@@_if_instance_exist:nnTF {#1} {#2}
          { 
            \__context_debug_typeout:n{basic~ instance~ exists}
            \@@_use_instance_finally:nn {#1} {#2} }
          { 
            \msg_error:nnnn { template } { unknown-instance } {#1} {#2} }
     }
     {
       \@@_debug:n {
         \str_if_empty:NF \l_context_primary_str
             { \@@_debug_typeout:n {primary~ context~ is~
                 '\l_context_primary_str' } }
         \str_if_empty:NF \l_context_secondary_str
             { \@@_debug_typeout:n {secondary~ context~ is~
                 '\l_context_secondary_str' } }
         \str_if_empty:NF \l_context_tertiary_str
             { \@@_debug_typeout:n {tertiary~ context~ is~
                 '\l_context_tertiary_str' } }
       }
       \@@_if_instance_exist:nnTF {#1}
          { #2 : \l_context_primary_str :
            \l_context_secondary_str :
            \l_context_tertiary_str  }
          { 
            \__context_debug_typeout:n{:primary:secondary:tertiary~
              exists~ (primary~ or~ secondary~ might~ be~ empty)}
            \@@_use_instance_finally:nn {#1}
               { #2 : \l_context_primary_str :
                 \l_context_secondary_str :
                 \l_context_tertiary_str  }
          }
          { 
            \__context_debug_typeout:n{:primary:secondary:tertiary~ does~ not~ exist}
            \@@_if_instance_exist:nnTF {#1}
               { #2 : \l_context_primary_str : \l_context_secondary_str  }
               { 
                 \__context_debug_typeout:n{:primary:secondary~
                   or~ ::secondary~ exists}
                 \@@_use_instance_finally:nn {#1}
                    { #2 : \l_context_primary_str : \l_context_secondary_str }
               }
               { 
                 \__context_debug_typeout:n{:primary:secondary~ and~
                   ::secondary~ do~ not~ exist}
                 \@@_if_instance_exist:nnTF {#1}
                    { #2 : \l_context_primary_str }
                    { 
                      \__context_debug_typeout:n{:primary~ exists}
                      \@@_use_instance_finally:nn {#1}
                         { #2 : \l_context_primary_str }
                    }
                    {
                      \__context_debug_typeout:n{:primary~ does~ not~ exist}
                      \@@_if_instance_exist:nnTF {#1}
                        { #2 : : : \l_context_tertiary_str  }
                         { 
                           \__context_debug_typeout:n{:::tertiary~ exists}
                           \@@_use_instance_finally:nn {#1}
                              { #2 : : : \l_context_tertiary_str  }
                         }
                         { 
                           \@@_if_instance_exist:nnTF {#1} {#2}
                              { 
                                \__context_debug_typeout:n{basic~ instance~ exists}
                                \@@_use_instance_finally:nn {#1} {#2} }
                              { 
                                \msg_error:nnnn { template } { unknown-instance } {#1} {#2} }
                         }
                    }
               }
          }
     }
}

%    \end{macrocode}
%  \end{macro}
%
% \changes{2026-03-19}{v0.5c}{Move template debugging support to kernel}
%
%    \begin{macrocode}
%</package>
%    \end{macrocode}
