diff options
Diffstat (limited to 'macros/latex-dev/base/lthooks.dtx')
-rw-r--r-- | macros/latex-dev/base/lthooks.dtx | 1729 |
1 files changed, 821 insertions, 908 deletions
diff --git a/macros/latex-dev/base/lthooks.dtx b/macros/latex-dev/base/lthooks.dtx index 603b472e44..b1e3985704 100644 --- a/macros/latex-dev/base/lthooks.dtx +++ b/macros/latex-dev/base/lthooks.dtx @@ -1,29 +1,41 @@ % \iffalse meta-comment +%% +%% File: lthooks.dtx (C) Copyright 2020 Frank Mittelbach, +%% Phelype Oleinik & LaTeX Team % -%% File: lthooks.dtx (C) Copyright 2020 Frank Mittelbach, LaTeX Team -% -% 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 +% This file is part of the LaTeX base system. +% ------------------------------------------- % +% It 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 or later. +% +% This file has the LPPL maintenance status "maintained". % +% The list of all files belonging to the LaTeX base distribution is +% given in the file `manifest.txt'. See also `legal.txt' for additional +% information. % -% The development version of the bundle can be found below +% The list of derived (unpacked) files belonging to the distribution +% and covered by LPPL is defined by the unpacking scripts (with +% extension .ins) which are part of the distribution. % -% https://github.com/FrankMittelbach/... +% \fi % -% for those people who are interested or want to report an issue. +% \iffalse +% +%%% From File: lthooks.dtx % % \begin{macrocode} -\providecommand\lthooksversion{v0.9b} -\providecommand\lthooksdate{2020/07/19} +\def\lthooksversion{v1.0a} +\def\lthooksdate{2020/08/31} % \end{macrocode} % %<*driver> -\RequirePackage[debug]{lthooks} - \documentclass{l3doc} % bug fix fo l3doc.cls @@ -160,17 +172,14 @@ % the current package name. See section~\ref{sec:default-label}. % \end{function} % - - - +% +% +% % \begin{function}{\UseHook} % \begin{syntax} % \cs{UseHook} \Arg{hook} % \end{syntax} -% Execute the hook code inside a command or environment.\footnote{For -% legacy hooks such as \hook{begindocument} it is also -% possible to call \cs{@...hook}, e.g., \cs{@begindocumenthook}, -% but this syntax is discouraged.} +% Execute the hook code inside a command or environment. % % Before \verb=\begin{document}= the fast execution code for a hook % is not set up, so in order to use a hook there it is explicitly @@ -258,6 +267,12 @@ % % \medskip % +% In contrast to the \texttt{voids} relationship between two labels +% in a \cs{DeclareHookrule} this is a destructive operation as the +% labeled code is removed from the hook data structure, whereas the +% relationship setting can be undone by providing a different +% relationship later. +% % A useful application for this declaration inside the document body % is when one wants to temporarily add code to hooks and later remove % it again, e.g., @@ -298,8 +313,19 @@ % used several times before the hook is executed then all code is % executed in the order in which it was declared.\footnotemark % -% The hook doesn't have to exist for code to be added to it. This -% allows for hooks to work regardless of package loading order. +% It is possible to nest declarations using the same hook (or +% different hooks), e.g., +% \begin{quote} +% \cs{AddToHookNext}\Arg{hook}\verb={=\meta{code-1}^^A +% \cs{AddToHookNext}\Arg{hook}\Arg{code-2}\verb=}= +% \end{quote} +% will execute \meta{code-1} next time the \meta{hook} is used and at +% that point puts \meta{code-2} into the \meta{hook} so that it gets +% executed on following time the hook is run. +% +% A hook doesn't have to exist for code to be added to it. This +% allows for hooks to work regardless of package loading +% order. % See section~\ref{sec:querying}. % % The \meta{hook} can be specified using the dot-syntax to denote @@ -337,7 +363,7 @@ % name---see~\cs{DeclareDefaultHookLabel}). % A \enquote{|.|} or \enquote{|./|} anywhere else in a \meta{hook} or in % \meta{label} is treated literally and is not replaced. - +% % For example, % inside the package \texttt{mypackage.sty}, the default label is % \texttt{mypackage}, so the instructions: @@ -345,15 +371,15 @@ % \NewHook {./hook} % \AddToHook {./hook}[.]{code} % Same as \AddToHook{./hook}{code} % \AddToHook {./hook}[./sub]{code} -% \DeclareHookRule{begindocument}{.}{<}{babel} +% \DeclareHookRule{begindocument}{.}{before}{babel} % \AddToHook {file/after/foo.tex}{code} % \end{verbatim} % are equivalent to: % \begin{verbatim} -% \NewHook {mypackage/hook}[mypackage]{code} +% \NewHook {mypackage/hook} % \AddToHook {mypackage/hook}[mypackage]{code} % \AddToHook {mypackage/hook}[mypackage/sub]{code} -% \DeclareHookRule{begindocument}{mypackage}{<}{babel} +% \DeclareHookRule{begindocument}{mypackage}{before}{babel} % \AddToHook {file/after/foo.tex}{code} % unchanged % \end{verbatim} % @@ -376,7 +402,7 @@ % because the hook is most of the time used outside of the package file % in which it was defined. This syntax is also not available in the hook % conditionals \cs{IfHookEmptyTF} (and \cs{hook_if_empty:nTF}) and -% \cs{IfHookExistTF} (and \cs{hook_if_exist:nTF}) because these +% \cs{IfHookExistsTF} (and \cs{hook_if_exist:nTF}) because these % conditionals are used in some performance-critical parts of the hook % management code, and because they are usually used to refer to other % package's hooks, so the dot-syntax doesn't make much sense. @@ -426,7 +452,7 @@ % \cs{DeclareHookRule} \Arg{hook}\Arg{label1}\Arg{relation}\Arg{label2} % \end{syntax} % Defines a relation between \meta{label1} and \meta{label2} for a -% given \meta{hook}. If \meta{hook} is \texttt{??} this defines a +% given \meta{hook}. If \meta{hook} is \texttt{??} this defines a default % relation for all hooks that use the two labels, i.e., that have % chunks of code labeled with \meta{label1} and \meta{label2}. % Rules specific to a given hook take precedence over default @@ -455,7 +481,7 @@ % \LaTeX{} error is raised, and the code for both labels are % dropped from that hook until the conflict is resolved. % -% \item[\texttt{removes}] +% \item[\texttt{voids}] % % Code for \meta{label1} overwrites code for \meta{label2}. More % precisely, code for \meta{label2} is dropped for that @@ -470,10 +496,15 @@ % specified earlier. % % \end{itemize} +% There can only be a single relation between two labels for a +% given hook, +% i.e., a later \cs{DeclareHookrule} overwrites any previous +% delcaration. % % The \meta{hook} and \meta{label} can be specified using the % dot-syntax to denote the current package name. % See section~\ref{sec:default-label}. +% % \end{function} % % @@ -553,9 +584,9 @@ % A leading |.| is treated literally. % \end{function} % -% \begin{function}[EXP]{\IfHookExistTF} +% \begin{function}[EXP]{\IfHookExistsTF} % \begin{syntax} -% \cs{IfHookExistTF} \Arg{hook} \Arg{true code} \Arg{false code} +% \cs{IfHookExistsTF} \Arg{hook} \Arg{true code} \Arg{false code} % \end{syntax} % Tests if the \meta{hook} exists (if it was created with either % \cs{NewHook}, \cs{NewReversedHook}, or \cs{NewMirroredHookPair}), and @@ -590,10 +621,8 @@ % the code chunks (and their labels) added to it, % \item % any rules set up to order them, -% \fmi{currently this is missing the default rules that apply, -% guess that needs fixing} % \item -% the computed order (if already defined), +% the computed order in which the chunks are executed, % \item % any code executed on the next invocation only. % \end{itemize} @@ -605,9 +634,9 @@ % % \subsubsection{Debugging hook code} % -% \begin{function}{\DebugHookOn,\DebugHookOff} +% \begin{function}{\DebugHooksOn,\DebugHooksOff} % \begin{syntax} -% \cs{DebugHookOn} +% \cs{DebugHooksOn} % \end{syntax} % Turn the debugging of hook code on or off. This displays changes % made to the hook data structures. The output is rather coarse and @@ -706,8 +735,8 @@ % the current package name. See section~\ref{sec:default-label}. % \end{function} % - - +% +% % \begin{function}{\hook_gremove_code:nn} % \begin{syntax} % \cs{hook_gremove_code:nn} \Arg{hook} \Arg{label} @@ -728,8 +757,8 @@ % dot-syntax to denote the current package name. % See section~\ref{sec:default-label}. % \end{function} - - +% +% % \begin{function}{\hook_gset_rule:nnnn} % \begin{syntax} % \cs{hook_gset_rule:nnnn} \Arg{hook} \Arg{label1} \Arg{relation} \Arg{label2} @@ -747,7 +776,7 @@ % % \begin{function}[pTF]{\hook_if_empty:n} % \begin{syntax} -% \cs{hook_if_empty:n} \Arg{hook} \Arg{true code} \Arg{false code} +% \cs{hook_if_empty:nTF} \Arg{hook} \Arg{true code} \Arg{false code} % \end{syntax} % Tests if the \meta{hook} is empty (\emph{i.e.}, no code was added to % it using either \cs{AddToHook} or \cs{AddToHookNext}), and @@ -760,7 +789,7 @@ % % \begin{function}[pTF]{\hook_if_exist:n} % \begin{syntax} -% \cs{hook_if_exist:n} \Arg{hook} \Arg{true code} \Arg{false code} +% \cs{hook_if_exist:nTF} \Arg{hook} \Arg{true code} \Arg{false code} % \end{syntax} % Tests if the \meta{hook} exists (if it was created with either % \cs{NewHook}, \cs{NewReversedHook}, or \cs{NewMirroredHookPair}), and @@ -938,6 +967,69 @@ % % % +% \subsection{Difference between \enquote{normal} and +% \enquote{one-time} hooks} +% +% When executing a hook a developer has the choice of using +% either \cs{UseHook} or \cs{UseOneTimeHook} (or their \pkg{expl3} +% equivalents \cs{hook_use:n} and \cs{hook_use_once:n}). +% This choice affects how \cs{AddToHook} is handled after the hook +% has been executed for the first time. +% +% With normal hooks adding code via \cs{AddToHook} means that the +% code chunk is added to the hook data structure and then used each time +% \cs{UseHook} is called. +% +% With one-time hooks it this is handled slightly differently: +% After \cs{UseOneTimeHook} has been called, any further attempts to +% add code to the hook via \cs{AddToHook} will simply execute the +% \meta{code} immediately. +% +% This has some consequences one needs to be aware of: +% \begin{itemize} +% \item +% +% If \meta{code} is added to a normal hook after the hook was +% executed and it is never executed again for one or the other +% reason, then this new \meta{code} will never be executed. +% +% \item +% +% In contrast if that happens with a one-time hook the \meta{code} is +% executed immediately. +% +% \end{itemize} +% In particular this means that construct such as +%\begin{quote} +% \cs{AddToHook}\verb={myhook}=\\ +% \phantom{\cs{AddToHook}}\verb={= \meta{code-1} +% \cs{AddToHook}\verb={myhook}=\Arg{code-2} +% \meta{code-3} \verb=}= +%\end{quote} +% works for one-time hooks\footnote{This is sometimes used with +% \cs{AtBeginDocument} which is why it is supported.} (all three +% code chunks are executed one after another), but it makes little +% sense with a normal hook, because with a normal hook the first time +% \verb=\UseHook{myhook}= is executed it would +% \begin{itemize} +% \item +% execute \meta{code-1}, +% \item +% then execute \verb=\AddToHook{myhook}{code-2}= which adds the +% code chunk \meta{code-2} to the hook for use on the next invocation, +% \item +% and finally execute \meta{code-3}. +% \end{itemize} +% The second time \cs{UseHook} is called it would execute the +% above and in addition \meta{code-2} as that was added as a code +% chunk to the hook in the meantime. So each time the hook is used +% another copy of \meta{code-2} is added and so that code chunk +% is executed $\meta{\# of invocations} -1$ times. +% +% +% +% +% % \subsection{Private \LaTeX{} kernel hooks} % % There are a few places where it is absolutely essential for @@ -1025,6 +1117,21 @@ % This hook is discussed in conjunction with the shipout hooks. % \end{function} % +% \bigskip +% +% The few hooks that existed previously in \LaTeXe{} used internally +% commands such as \cs{@begindocumenthook} and packages sometimes +% augemented them directly rather than working through +% \cs{AtBeginDocumement}. For that reason there is currently support +% for this, that is, if the system detects that such an internal +% legacy hook command contains code it adds it to the new hook +% system under the label \texttt{legacy} so that it doesn't get +% lost. +% +% However, over time the remaining cases of direct usage need +% updating because in one of the future release of \LaTeX{} we will +% turn this legacy support off, as it does unnecessary slow down +% the processing. % % % \subsection{\LaTeXe{} commands and environments augmented by @@ -1052,26 +1159,31 @@ % % \item[\hook{env/\meta{env}/end}] % -% This is executed as part of \cs{end} directly in front of the +% This hook is executed as part of \cs{end} directly in front of the % code specific to the end of the environment (e.g., the third % argument of \cs{newenvironment}). % % \item[\hook{env/\meta{env}/after}] % -% This is executed as part of \cs{end} after the +% This hook is executed as part of \cs{end} after the % code specific to the environment end and after the environment % group has ended. % Its scope is therefore not restricted by the environment. % -% This hook is implemented as a reversed hook so if two packages +% The hook is implemented as a reversed hook so if two packages % add code to \hook{env/\meta{env}/before} and to % \hook{env/\meta{env}/after} they can add surrounding % environments and the order of closing them happens in the % right sequence. % % \end{description} -% In contrast to other hooks these hooks do not need to be declared -% using \cs{NewHook}. +% Generic environment hooks are never one-time hooks even with +% environments that are supposed to appear only once in a +% document.\footnote{Thus if one adds code to such hooks after the +% environment has been processed, it will only be executed if the +% environment appears again and if that doesn't happen the code +% will never get executed.} In contrast to other hooks there is +% also no need to declare them using \cs{NewHook}. % % The hooks are only executed if \cs{begin}\Arg{env} and % \cs{end}\Arg{env} is used. If the environment code is executed @@ -1087,47 +1199,78 @@ % to add the outer hooks, etc. % % +% \begin{function}{\BeforeBeginEnvironment} +% \begin{syntax} +% \cs{BeforeBeginEnvironment} \oarg{label} \Arg{code} +% \end{syntax} +% This declaration adds to the \hook{env/\meta{env}/before} hook +% using by default the current package or class name as a label or +% \texttt{top-level} if used in the document directly. +% \end{function} +% +% \begin{function}{\AtBeginEnvironment} +% \begin{syntax} +% \cs{AtBeginEnvironment} \oarg{label} \Arg{code} +% \end{syntax} +% Like \cs{BeforeBeginEnvironment} but adds to the \hook{env/\meta{env}/begin} hook. +% \end{function} +% +% \begin{function}{\AtEndEnvironment} +% \begin{syntax} +% \cs{AtEndEnvironment} \oarg{label} \Arg{code} +% \end{syntax} +% Like \cs{BeforeBeginEnvironment} but adds to the \hook{env/\meta{env}/end} hook. +% \end{function} +% +% \begin{function}{\AfterEndEnvironment} +% \begin{syntax} +% \cs{AfterEndEnvironment} \oarg{label} \Arg{code} +% \end{syntax} +% Like \cs{BeforeBeginEnvironment} but adds to the \hook{env/\meta{env}/after} hook. +% \end{function} +% +% +% % % \subsubsection{Hooks provided by \cs{begin}\texttt{\{document\}}} % % Until 2020 \cs{begin}\texttt{\{document\}} offered exactly one -% hook that one had to fill using \cs{AtBeginDocument}. Experience -% has shown that this single hook in one place was not enough and -% as part of adding the general hook management system a number of -% additional hooks have been added at this point. The places for -% hooks have been chosen to provide the same support as offered by -% external packages, such as \pkg{etoolbox} and others that -% augmented \cs{document} to gain better control. -% -% Supported are now the following hooks: +% hook that one could add to using +% \cs{AtBeginDocument}. Experiences over the years have shown that +% this single hook in one place was not enough and as part of +% adding the general hook management system a number of additional +% hooks have been added at this point. The places for these hooks have +% been chosen to provide the same support as offered by external +% packages, such as \pkg{etoolbox} and others that augmented +% \cs{document} to gain better control. +% +% Supported are now the following hooks (all of them one-time hooks): % \begin{description} % -% \item[\hook{env/document/before}] % -% This is the generic environment hook executed effectively -% before \verb=\begin{document}= starts, i.e., one can think of -% it as a hook for code at the end of the preamble section. +% \item[\hook{begindocument/before}] % -% \item[\hook{env/document/begin}] +% This hook is executed at the very start of \cs{document}, one can +% think of it as a hook for code at the end of the preamble +% section and this is how it is used by \pkg{etoolbox}'s +% \cs{AtEndPreamble}. % -% This is the second generic environment hook that is executed -% after the environment has started its group. But given that for the -% \texttt{document} environment this group is canceled there is -% little difference to the previous one as the two are directly -% executed one after another (the only difference is that in this -% hook \cs{@currenvir} is now set to \texttt{document} but -% anybody adding to this hook would know that already). % % % \item[\hook{begindocument}] % -% This hook is added to by \cs{AtBeginDocument} and is executed +% This hook is added to when using \cs{AtBeginDocument} and it is executed % after the \texttt{.aux} file as be read in and most % initialization are done, so they can be altered and inspected by % the hook code. It is followed by a small number of further % initializations that shouldn't be altered and are therefore % coming later. % +% The hook should not be used to add material for typesetting as +% we are still in \LaTeX's initialization phase and not in the +% document body. If such material needs to be added to the document +% body use the next hook instead. +% % \item[\hook{begindocument/end}] % % This hook is executed at the end of the \cs{document} code in @@ -1135,6 +1278,10 @@ % command that follows it is \cs{ignorespaces}. % % \end{description} +% The generic hooks executed by \cs{begin} also exist, i.e., +% \hook{env/document/before} and \hook{env/document/begin}, but +% with this special environment it is better use the dedicated +% one-time hooks above. % % % @@ -1156,17 +1303,13 @@ % to add code in various places in a controlled way without the % need for overwriting or patching the core code. % -% Supported are now the following hooks: +% Supported are now the following hooks (all of them one-time hooks): % \begin{description} % -% \item[\hook{env/document/end}] The generic hook inside \cs{end}. -% % \item[\hook{enddocument}] - +% % The hook associated with \cs{AtEndDocument}. It is immediately -% called after the previous hook so there could be just -% one.\footnote{We could make \cs{AtEndDocument} just fill the -% \hook{env/document/end} but maybe that is a bit confusing.} +% called at the beginning of \cs{enddocument}. % % When this hook is executed there may be still unprocessed % material (e.g., floats on the deferlist) and the hook may add @@ -1230,6 +1373,16 @@ % page. See section~\ref{sec:shipout} for where to find the details. % % +% It is in also possible to use the generic \hook{env/document/end} +% hook which is execuded by \cs{end}, i.e., just in front of the +% first hook above. Note however that the other generic \cs{end} +% environment hook, i.e., \hook{env/document/after} will never get +% executed, because by that time \LaTeX{} has finished the document +% processing. +% +% +% +% % \subsubsection{Hooks provided \cs{shipout} operations} % \label{sec:shipout} % @@ -1239,7 +1392,7 @@ % \texttt{ltshipout-code.pdf}. % % -% \subsubsection{Hooks provided file loading operations} +% \subsubsection{Hooks provided by file loading operations} % % There are several hooks added to \LaTeX{}'s % process of loading file via its high-level interfaces such as @@ -1248,17 +1401,87 @@ % \texttt{ltfilehook-code.pdf}. % % - +% \subsubsection{Hooks provided in NFSS commands} +% +% In languages that need to support for more than one script in +% parallel (and thus several sets of fonts), e.g., Latin and +% Japanese fonts, NFSS font commands, such as \cs{sffamily}, need +% to switch both the Latin family to ``Sans Serif'' and in addition +% alter a second set of fonts. +% +% To support this several NFSS have hooks in which such support can +% be added. +% \begin{description} +% +% \item[\hook{rmfamily}] +% +% After \cs{rmfamily} has done its initial checks and prepared a +% any font series update this hook is executed and only +% afterwards \cs{selectfont}. +% +% \item[\hook{sffamily}] +% +% Like the \hook{rmfamily} hook but for the \cs{sffamily} command. +% +% \item[\hook{ttfamily}] +% +% Like the \hook{rmfamily} hook but for the \cs{ttfamily} command. +% +% \item[\hook{normalfont}] +% +% The \cs{normalfont} command resets font encoding family series +% and shape to their document defaults. It then executes this +% hook and finally calls \cs{selectfont}. +% +% \item[\hook{expand@font@defaults}] +% +% The internal \cs{expand@font@defaults} command expands and +% saves the current defaults for the meta families (rm/sf/tt) and +% the meta series (bf/md). If the NFSS machinery has been +% augmented, e.g., for Chinese or Japanese fonts, then further +% defaults may need to be set at this point. This can be done in +% this hook which is executed at the end of this macro. +% +% \item[\hook{bfseries/defaults}, \hook{bfseries}] +% +% If the \cs{bfdefault} was explicitly changed by the user its +% new value is used to set the bf series defaults for the meta +% families (rm/sf/tt) when \cs{bfseries} is called. In the +% \hook{bfseries/defaults} hook further adjustments can be made +% in this case. This hook is only executed if such a change is +% detected. In contrast the \hook{bfseries} hook is always +% executed just before \cs{selectfont} is called to change to the +% new series. +% +% +% \item[\hook{mdseries/defaults}, \hook{mdseries}] +% +% These two hooks are like the previous ones but used in +% \cs{mdseries} command. +% +% \end{description} +% +% % \StopEventually{\setlength\IndexMin{200pt} \PrintIndex } % % % \section{The Implementation} +% +% +% \subsection{Loading further extensions} +% % \begin{macrocode} %<@@=hook> % \end{macrocode} % +% +% At the moment the whole module rolls back in one go, but if we +% make any modifications in later releases this will then need +% splitting. % \begin{macrocode} -%<*2ekernel> +%<*2ekernel|latexrelease> +%<latexrelease>\IncludeInRelease{2020/10/01}% +%<latexrelease> {\NewHook}{The hook management}% % \end{macrocode} % % \begin{macrocode} @@ -1300,7 +1523,7 @@ % \end{macro} % \end{macro} % \end{macro} - +% % % % \subsection{Borrowing from internals of other kernel modules} @@ -1315,6 +1538,13 @@ % % \subsection{Declarations} % +% \begin{macro}{\l_@@_tmpa_bool} +% Scratch boolean used throughout the package. +% \begin{macrocode} +\bool_new:N \l_@@_tmpa_bool +% \end{macrocode} +% \end{macro} +% % \begin{macro}{\l_@@_return_tl,\l_@@_tmpa_tl,\l_@@_tmpb_tl} % Scratch variables used throughout the package. % \begin{macrocode} @@ -1333,7 +1563,7 @@ % \end{macro} % % \begin{macro}{\g_@@_removal_list_prop} -% A property list to hold delayed removals. +% A token list to hold delayed removals. % \begin{macrocode} \tl_new:N \g_@@_removal_list_tl % \end{macrocode} @@ -1346,11 +1576,13 @@ % \end{macrocode} % \end{macro} % -% \begin{macro}{\g_@@_code_temp_prop} -% A property list to temporarily save the original one so that it -% isn't permanently changed during sorting. +% \begin{macro}{\l_@@_work_prop} +% A property list holding a copy of the +% \cs[no-index]{g_@@_\meta{hook}_code_prop} of the hook being sorted +% to work on, so that changes don't act destructively on the hook data +% structure. % \begin{macrocode} -\prop_new:N \g_@@_code_temp_prop +\prop_new:N \l_@@_work_prop % \end{macrocode} % \end{macro} % @@ -1387,8 +1619,7 @@ % % \subsection{Providing new hooks} % -% \begin{macro}{\g_@@_..._code_prop,\g_@@_..._rules_prop, -% \g_@@_..._code_tl,\g_@@_..._next_code_tl} +% \begin{macro}{\g_@@_..._code_prop,\g_@@_..._code_tl,\g_@@_..._code_next_tl} % % Hooks have a \meta{name} and for each hook we have to provide a number of % data structures. These are @@ -1398,27 +1629,36 @@ % package names that add code to the hook, but it is possible % for packages to define other keys. % -% \item[\cs{g_@@_\meta{name}_rules_prop}] A property listing holding -% relation info how the code chunks should be ordered within a -% hook. This is used for debugging only. The actual rule for a -% \meta{hook} is stored in a separate token lists named -% \cs[no-index]{g_@@_\meta{hook}_rule_\meta{label1}\string|\meta{label2}_tl} -% for a pair of labels. +% \item[{\cs[no-index]{g_@@_\meta{name}_rule_\meta{label1}\string|\meta{label2}_tl}}] +% A token list holding the relation between \meta{label1} and +% \meta{label2} in the \meta{name}. The \meta{labels} are lexically +% (reverse) sorted to ensure that two labels always point to the same +% token list. For global rules, the \meta{name} is |??|. % % \item[\cs{g_@@_\meta{name}_code_tl}] The code that is actually executed % when the hook is called in the document is stored in this token % list. It is constructed from the code chunks applying the % information. % -% \item[\cs{g_@@_\meta{name}_next_code_tl}] Finally there is extra code +% \item[\cs{g_@@_\meta{name}_reversed_tl}] Some hooks are +% \enquote{reversed}. This token list stores a |-| for such hook +% so that it can be identified. The |-| character is used because +% $\meta{reversed}1$ is $+1$ for normal hooks and $-1$ for reversed +% ones. +% +% \item[\cs{g_@@_\meta{name}_code_next_tl}] Finally there is extra code % (normally empty) that is used on the next invocation of the hook % (and then deleted). This can be used to define some special -% behavior for a single occasion from within the document. +% behavior for a single occasion from within the document. This token +% list is called |code_next| rather than |next_code| because otherwise +% a hook called \meta{name}|_next| would have its code-token list +% named \cs[no-index]{g_@@_\meta{name}_code_next_tl}, which would +% clash with the next code-token list of a hook called \meta{name}. % % \end{description} % \end{macro} - - +% +% % % % \begin{macro}{\hook_new:n} @@ -1437,7 +1677,7 @@ % already exists we complain. % \begin{macrocode} \hook_if_exist:nTF {#1} - { \ErrorHookExists } + { \msg_error:nnn { hooks } { exists } {#1} } % \end{macrocode} % Otherwise we add the hook name to the list of all hooks and % allocate the necessary data structures for the new hook. @@ -1465,15 +1705,24 @@ \tl_new:c { g_@@_#1_reversed_tl } % \end{macrocode} % The above is all in L3 convention, but we also provide an -% interface to legacy \LaTeXe{} for use in the current kernel. This -% is done in a separate macro. -% \begin{macrocode} - \@@_provide_legacy_interface:n {#1} +% interface to legacy \LaTeXe{} hooks of the form \cs{@...hook}, +% e.g., \cs{@begindocumenthook}. +% there have been a few of them and they have been added to +% using \cs{g@addto@macro}. If there exists such a macro matching +% the name of the new hook, i.e., +% \verb+\@+\meta{hook-name}\texttt{hook} and it is not empty then +% we add its contents as a code chunk under the label \texttt{legacy}. +% \begin{quote} +% \textbf{Warning: this support will vanish in future releases!} +% \end{quote} +% +% \begin{macrocode} + \@@_include_legacy_code_chunk:n {#1} } } % \end{macrocode} % \end{macro} - +% % % % \begin{macro}{\@@_declare:n} @@ -1488,15 +1737,14 @@ \@@_if_exist:nF {#1} { \prop_new:c { g_@@_#1_code_prop } - \tl_new:c { g_@@_#1_next_code_tl } - \prop_new:c { g_@@_#1_rules_prop } % only for debugging + \tl_new:c { g_@@_#1_code_next_tl } } } % \end{macrocode} % \end{macro} - - - +% +% +% % \begin{macro}{\hook_new_reversed:n} % % Declare a new hook. The default ordering of code chunks is @@ -1504,11 +1752,16 @@ % \begin{macrocode} \cs_new_protected:Npn \hook_new_reversed:n #1 { \hook_new:n {#1} +% \end{macrocode} +% If the hook already exists the above will generate an error +% message, so the next line should be executed (but it is --- too +% bad). +% \begin{macrocode} \tl_gset:cn { g_@@_#1_reversed_tl } { - } } % \end{macrocode} % \end{macro} - +% % \begin{macro}{\hook_new_pair:nn} % A shorthand for declaring a normal and a (matching) reversed hook in one go. % \begin{macrocode} @@ -1517,23 +1770,21 @@ } % \end{macrocode} % \end{macro} - - -% \begin{macro}{\@@_provide_legacy_interface:n} +% +% +% \begin{macro}{\@@_include_legacy_code_chunk:n} % The \LaTeX{} legacy concept for hooks uses with hooks the % following naming scheme in the code: \cs{@...hook}. % -% We follow this convention and insert the hook code using this -% naming scheme in \LaTeXe{}. At least as long as this code is in a -% package, some such hooks are already filled with data when we move -% them over to the new scheme. We therefore insert already existing -% code under the label \texttt{legacy} into the hook management -% machinery and then replace the \cs{@...hook} with its counterpart -% which is \cs{g_@@_\#1_code_tl}.\footnote{This means one extra -% unnecessary expansion on each invocation in the document but -% keeps the \LaTeXe{} and the L3 coding side properly separated.} +% If this macro is not empty we add it under the label +% \texttt{legacy} to the current hook and then empty it globally. +% This way packages or classes directly manipulating commands such +% as \cs{@begindocumenthook} still get their hook data added. +% \begin{quote} +% \textbf{Warning: this support will vanish in future releases!} +% \end{quote} % \begin{macrocode} -\cs_new_protected:Npn \@@_provide_legacy_interface:n #1 +\cs_new_protected:Npn \@@_include_legacy_code_chunk:n #1 { % \end{macrocode} % If the \pkg{expl3} code is run with checking on then assigning or @@ -1542,32 +1793,34 @@ % debugging explicitly suspended. % \begin{macrocode} \debug_suspend: +% \end{macrocode} +% If the macro doesn't exist (which is the usual case) then nothing +% needs to be done. +% \begin{macrocode} \tl_if_exist:cT { @#1hook } % \end{macrocode} -% Of course if the hook exists but is still empty, there is no need -% to add anything under \texttt{legacy} or the current package name. +% Of course if the legacy hook exists but is empty, there is no need +% to add anything under \texttt{legacy} the legacy label. % \begin{macrocode} { \tl_if_empty:cF { @#1hook } { - \@@_gput_code:nxv {#1} - { \@@_parse_label_default:Vn \c_novalue_tl { legacy } } - { @#1hook } - } - } + \exp_args:Nnnv \@@_hook_gput_code_do:nnn {#1} + { legacy } { @#1hook } % \end{macrocode} -% We need a global definition in case the declaration is done -% inside a group (which happens below at the end of the file). -% This is another reason why need to suspend checking, otherwise -% \cs{tl_gset:co} would complain about \cs{@...hook} not starting -% with \cs{g_}. +% Once added to the hook, we need to clear it otherwise it might +% get added again later if the hook data gets updated. % \begin{macrocode} - \tl_gset:co{@#1hook}{\cs:w g_@@_#1_code_tl\cs_end:} + \tl_gclear:c { @#1hook } + } + } \debug_resume: } % \end{macrocode} % \end{macro} % +% +% % \subsection{Parsing a label} % % \begin{macro}[EXP]{\@@_parse_label_default:nn,\@@_parse_label_default:Vn} @@ -1647,10 +1900,10 @@ } % \end{macrocode} % \end{macro} - - - - +% +% +% +% % \begin{macro}{\hook_gput_code:nnn} % \begin{macro}{\@@_gput_code:nnn,\@@_gput_code:nxv,\@@_hook_gput_code_do:nnn} % @@ -1667,13 +1920,19 @@ \cs_new_protected:Npn \@@_gput_code:nnn #1 #2 #3 { % \end{macrocode} -% First we check if the hook exists. +% First check if the current \meta{hook}/\meta{label} pair was marked +% for removal, in which case \cs{@@_unmark_removal:nn} is used to +% remove that mark (once). This may happen when a package removes +% code from another package which was not yet loaded: the removal +% order is stored, and at this stage it is executed by not adding to +% the hook. % \begin{macrocode} \@@_if_marked_removal:nnTF {#1} {#2} { \@@_unmark_removal:nn {#1} {#2} } { % \end{macrocode} -% First we check if the hook exists. +% If no removal is queued, we are free to add. Start by checking if +% the hook exists. % \begin{macrocode} \hook_if_exist:nTF {#1} % \end{macrocode} @@ -1691,7 +1950,10 @@ \@@_update_hook_code:n {#1} } % \end{macrocode} -% +% +% If the hook does not exist, however, before giving up try to +% declare it as a generic hook, if its name matches one of the valid +% patterns. % \begin{macrocode} { \@@_try_declaring_generic_hook:nnn {#1} {#2} {#3} } } @@ -1699,20 +1961,21 @@ \cs_generate_variant:Nn \@@_gput_code:nnn { nxv } % \end{macrocode} % -% This macro will unconditionally add a chunk of code to the given hook. +% This macro will unconditionally add a chunk of code to the given hook. % \begin{macrocode} \cs_new_protected:Npn \@@_hook_gput_code_do:nnn #1 #2 #3 { -% However, first some debugging info if debugging is enabled: +% \end{macrocode} +% However, first some debugging info if debugging is enabled: % \begin{macrocode} \@@_debug:n{\iow_term:x{****~ Add~ to~ \hook_if_exist:nF {#1} { undeclared~ } hook~ #1~ (#2) \on@line\space <-~ \tl_to_str:n{#3}} } % \end{macrocode} -% Then try to get the code chunk labeled \verb=#2= from the hook. -% If there's code already there, then append \verb=#3= to that, -% otherwise just put \verb=#3=. +% Then try to get the code chunk labeled \verb=#2= from the hook. +% If there's code already there, then append \verb=#3= to that, +% otherwise just put \verb=#3=. % \begin{macrocode} \prop_get:cnNTF { g_@@_#1_code_prop } {#2} \l_@@_return_tl { @@ -1786,7 +2049,7 @@ \@@_if_file_hook:wTF #1 / / \s_@@_mark { \exp_args:Ne \@@_try_declaring_generic_hook_split:nNNnn - { \exp_args:Ne \@@_file_hook_normalise:n {#1} } + { \exp_args:Ne \@@_file_hook_normalize:n {#1} } } { \@@_try_declaring_generic_hook_split:nNNnn {#1} } } @@ -1867,16 +2130,16 @@ % \end{macrocode} % \end{macro} % -% \begin{macro}[EXP]{\@@_file_hook_normalise:n} +% \begin{macro}[EXP]{\@@_file_hook_normalize:n} % \begin{macro}[EXP]{\@@_strip_double_slash:n,\@@_strip_double_slash:w} % When a file-specific hook is found, before being declared it is -% lightly normalized by \cs{@@_file_hook_normalise:n}. The current +% lightly normalized by \cs{@@_file_hook_normalize:n}. The current % implementation just replaces two consecutive slashes (|//|) by a % single one, to cope with simple cases where the user did something % like \verb|\def\input@path{{./mypath/}}|, in which case a hook would % have to be \verb|\AddToHook{file/after/./mypath//file.tex}|. % \begin{macrocode} -\cs_new:Npn \@@_file_hook_normalise:n #1 +\cs_new:Npn \@@_file_hook_normalize:n #1 { \@@_strip_double_slash:n {#1} } \cs_new:Npn \@@_strip_double_slash:n #1 { \@@_strip_double_slash:w #1 // \s_@@_mark } @@ -1889,13 +2152,9 @@ % \end{macrocode} % \end{macro} % \end{macro} - - - - - +% % \begin{macro}{\c_@@_generics_prop} -% Clist holding the generic names. We don't provide any user +% Property list holding the generic names. We don't provide any user % interface to this as this is meant to be static. % \begin{description} % \item[\texttt{env}] @@ -1920,17 +2179,7 @@ \prop_const_from_keyval:Nn \c_@@_generics_file_prop {before=,after=} % \end{macrocode} % \end{macro} - - -% \begin{macro}{\@@_update_hook_code:n} -% Before \verb=\begin{document}= this does nothing, in the body it -% reinitializes the hook code using the altered data. -% \begin{macrocode} -\cs_new_eq:NN \@@_update_hook_code:n \use_none:n -% \end{macrocode} -% \end{macro} - - +% % \begin{macro}{\hook_gremove_code:nn} % \begin{macro}{\@@_gremove_code:nn} % @@ -1958,10 +2207,7 @@ % \begin{macrocode} { \str_if_eq:nnTF {#2} {*} - { - \prop_gclear:c { g_@@_#1_code_prop } - \clist_gclear:c { g_@@_#1_labels_clist } % for debugging only - } + { \prop_gclear:c { g_@@_#1_code_prop } } { % \end{macrocode} % Check if the label being removed exists in the code pool. If it does, @@ -1979,6 +2225,12 @@ \hook_if_exist:nT {#1} { \@@_update_hook_code:n {#1} } } +% \end{macrocode} +% +% If the code pool for this hook doesn't exist it means that nothing +% tried to add to it before, so we just queue this removal order for +% later. +% \begin{macrocode} { \@@_mark_removal:nn {#1} {#2} } } % \end{macrocode} @@ -1986,26 +2238,18 @@ % \begin{macro}{\@@_gremove_code_do:nn} % \begin{macrocode} \cs_new_protected:Npn \@@_gremove_code_do:nn #1 #2 - { - \prop_gremove:cn { g_@@_#1_code_prop } {#2} -% \end{macrocode} -% Removing the dropped label from \verb=\g_@@_#1_labels_clist= is -% rather tricky, because that clists holds the labels as strings -% (i.e., not ordinary text which is what we have in \verb=#2=). -% \begin{macrocode} - \exp_args:Nco \clist_gremove_all:Nn - { g_@@_#1_labels_clist } { \tl_to_str:n {#2} } % for debugging only - } + { \prop_gremove:cn { g_@@_#1_code_prop } {#2} } % \end{macrocode} % \end{macro} % \end{macro} % \end{macro} - - % % \begin{macro}{\@@_mark_removal:nn} % Marks \meta{label} (\verb=#2=) to be removed from \meta{hook} -% (\verb=#1=). +% (\verb=#1=). The number of removals should be fairly small, and +% \cs{tl_gremove_once:Nx} is fairly efficient even for longer token +% lists, so we use a single global token list, rather than one for +% each hook. % \begin{macrocode} \cs_new_protected:Npn \@@_mark_removal:nn #1 #2 { @@ -2044,29 +2288,24 @@ % % \begin{macro}[rEXP]{\@@_removal_tl:nn} % Builds a token list with \verb=#1= and \verb=#2= which can only be -% matched by \verb=#1= and \verb=#2=. +% matched by \verb=#1= and \verb=#2=. The |&|$_4$ anchors a removal, +% so that \verb=#1= can't be mistaken by \verb=#2= and vice versa, and +% the two |$|$_3$ delimit the two arguments % \begin{macrocode} \cs_new:Npn \@@_removal_tl:nn #1 #2 { & \tl_to_str:n {#2} $ \tl_to_str:n {#1} $ } % \end{macrocode} % \end{macro} - - - - -% % % % \begin{macro}{ -% \g_@@_??_rules_prop, % \g_@@_??_code_prop, % \g_@@_??_code_tl, % \g_@@_??_reversed_tl, % } % -% Default rules applying to all hooks are stored in this property -% list. Initially it simply used an empty ``label'' name (not two -% question marks). This was a bit unfortunate, because then +% Initially these variables simply used an empty ``label'' name (not +% two question marks). This was a bit unfortunate, because then % \texttt{l3doc} complains about \verb=__= in the middle of a % command name when trying to typeset the documentation. However % using a ``normal'' name such as \texttt{default} has the @@ -2079,7 +2318,6 @@ % \cs{g_@@_??_code_tl} isn't used, but it has to be defined to trick % the code into thinking that \verb=??= is actually a hook. % \begin{macrocode} -\prop_new:c {g_@@_??_rules_prop} \prop_new:c {g_@@_??_code_prop} \prop_new:c {g_@@_??_code_tl} % \end{macrocode} @@ -2093,53 +2331,6 @@ \tl_new:c {g_@@_??_reversed_tl} % \end{macrocode} % \end{macro} - - - -% \begin{macro}{\@@_debug_gset_rule:nnnn} -% -% \fmi{this needs cleanup and docu correction!} -% -% With -% \cs{@@_debug_gset_rule:nnnn}\Arg{hook}\Arg{label1}\Arg{relation}\Arg{label2} -% a relation is defined between the two code labels for the given -% \meta{hook}. The special hook \texttt{??} stands for \emph{any} -% hook describing a default rule. -% \begin{macrocode} -\cs_new_protected:Npn \@@_debug_gset_rule:nnnn #1#2#3#4 - { -% \end{macrocode} -% If so we drop any existing rules with the two labels (in case -% there are any). -% \begin{macrocode} - \prop_gremove:cn{g_@@_#1_rules_prop}{#2|#4} - \prop_gremove:cn{g_@@_#1_rules_prop}{#4|#2} -% \end{macrocode} -% Then we add the new one (normalizing the input a bit, e.g., we -% always use \texttt{before} and not \texttt{after} and -% instead reorder the labels): -% \begin{macrocode} - \str_case_e:nnF {#3} - { - {before} { \prop_gput:cnn {g_@@_#1_rules_prop}{#2|#4}{<} } - {after} { \prop_gput:cnn {g_@@_#1_rules_prop}{#4|#2}{<} } -% \end{macrocode} -% More special rule types \ldots -% \begin{macrocode} - {incompatible-error} { \prop_gput:cnn {g_@@_#1_rules_prop}{#2|#4}{xE} } - {incompatible-warning} { \prop_gput:cnn {g_@@_#1_rules_prop}{#2|#4}{xW} } - {removes} { \prop_gput:cnn {g_@@_#1_rules_prop}{#2|#4}{->} } -% \end{macrocode} -% Undo a setting: -% \begin{macrocode} - {unrelated}{ \prop_gremove:cn {g_@@_#1_rules_prop}{#2|#4} - \prop_gremove:cn {g_@@_#1_rules_prop}{#4|#2} } - } - { \ERRORunknownrule } - } -% \end{macrocode} -% \end{macro} -% % % \subsection{Setting rules for hooks code} % @@ -2188,9 +2379,9 @@ {#1} {#2} {#4} \@@_update_hook_code:n {#1} } - { \ERRORunknownrule } + { \msg_error:nnnnnn { hooks } { unknown-rule } + {#1} {#2} {#3} {#4} } \debug_resume: - \@@_debug_gset_rule:nnnn {#1} {#2} {#3} {#4} % for debugging } % \end{macrocode} % \end{macro} @@ -2200,10 +2391,10 @@ % \@@_rule_<_gset:nnn, \@@_rule_>_gset:nnn} % Then we add the new rule. We need to normalize the rules here to % allow for faster processing later. Given a pair of labels -% $l_A$ and $l_B$, the rule $l_A>l_B$ is the same as $l_B<l_A$\fmi{} -% said differently. But normalizing the -% forms of the rule to a single representation, say, $l_B<l_A$, then -% the time spent looking for the rules later is considerably reduced. +% $l_A$ and $l_B$, the rule $l_A>l_B$ is the same as $l_B<l_A$ +% only presented differently. But by normalizing the +% forms of the rule to a single representation, say, $l_B<l_A$, reduces +% the time spent looking for the rules later considerably. % % Here we do that normalization by using \cs[no-index]{(pdf)strcmp} to % lexically sort labels $l_A$ and $l_B$ to a fixed order. This order @@ -2232,11 +2423,11 @@ % \end{macrocode} % \end{macro} % -% \begin{macro}{\@@_rule_removes_gset:nnn} +% \begin{macro}{\@@_rule_voids_gset:nnn} % This rule removes (clears, actually) the code from label |#3| if % label |#2| is in the hook |#1|. % \begin{macrocode} -\cs_new_protected:Npn \@@_rule_removes_gset:nnn #1#2#3 +\cs_new_protected:Npn \@@_rule_voids_gset:nnn #1#2#3 { \tl_gset:cx { g_@@_#1_rule_ \@@_label_pair:nn {#2} {#3} _tl } { \@@_label_ordered:nnTF {#2} {#3} { -> } { <- } } @@ -2299,7 +2490,7 @@ % \end{macro} % % \begin{macro}[EXP]{\@@_if_label_case:nnnnn} -% To avoid doing the string comparison twice in \cs{@@_initialize_single:NNNNn} +% To avoid doing the string comparison twice in \cs{@@_initialize_single:NNNn} % (once with \cs{str_if_eq:nn} and again with \cs{@@_label_ordered:nn}), % we use a three-way branching macro that will compare |#1| and |#2| % and expand to \cs{use_i:nnn} if they are equal, \cs{use_ii:nn} if @@ -2314,8 +2505,15 @@ } % \end{macrocode} % \end{macro} - - +% +% \begin{macro}{\@@_update_hook_code:n} +% Before \verb=\begin{document}= this does nothing, in the body it +% reinitializes the hook code using the altered data. +% \begin{macrocode} +\cs_new_eq:NN \@@_update_hook_code:n \use_none:n +% \end{macrocode} +% \end{macro} +% % \begin{macro}{\@@_initialize_all:} % Initialize all known hooks (at \verb=\begin{document}=), i.e., % update the fast execution token lists to hold the necessary code @@ -2349,7 +2547,6 @@ \exp_not:v {g_@@_##1_code_tl}~ } } } -% % \end{macrocode} % After all hooks are initialized we change the ``use'' to just % call the hook code and not initialize it (as it was done in the @@ -2360,9 +2557,9 @@ } % \end{macrocode} % \end{macro} - - - +% +% +% % \begin{macro}{\@@_initialize_hook_code:n} % Initializing or reinitializing the fast execution hook code. In % the preamble this is selectively done in case a hook gets used @@ -2373,12 +2570,20 @@ \@@_debug:n{ \iow_term:x{^^JUpdate~ code~ for~ hook~ '#1' \on@line :^^J} } % \end{macrocode} -% This does the sorting and the updates. If there aren't any code +% This does the sorting and the updates. +% First thing we do is to check if a legacy hook macro exists and +% if so we add it to the hook under the label \texttt{legacy}. This +% might make the hook non-empty so we have to do this before +% the then following test. +% \begin{macrocode} + \@@_include_legacy_code_chunk:n {#1} +% \end{macrocode} +% If there aren't any code % chunks for the current hook, there is no point in even starting % the sorting routine so we make a quick test for that and in that % case just update \cs{g_@@_\meta{hook}_code_tl} to hold the next % code. If there are code chunks we call -% \cs{@@_initialize_single:NNNNn} and pass to it ready made csnames +% \cs{@@_initialize_single:NNNn} and pass to it ready made csnames % as they are needed several times inside. This way we save a bit % on processing time if we do that up front. % \begin{macrocode} @@ -2386,7 +2591,7 @@ { \prop_if_empty:cTF {g_@@_#1_code_prop} { \tl_gset:co {g_@@_#1_code_tl} - {\cs:w g_@@_#1_next_code_tl \cs_end: } } + {\cs:w g_@@_#1_code_next_tl \cs_end: } } { % \end{macrocode} % By default the algorithm sorts the code chunks and then saves the @@ -2403,38 +2608,39 @@ \cs_set_eq:NN \@@_clist_gput:NV \clist_gput_right:NV } % \end{macrocode} % -% When sorting, some relations (namely \verb|->| \verb|<-|) need to +% When sorting, some relations (namely \verb|voids|) need to % act destructively on the code property lists to remove code that -% shouldn't appear in the sorted hook token list. +% shouldn't appear in the sorted hook token list, so we temporarily +% save the old code property list so that it can be restored later. % \begin{macrocode} - \prop_gset_eq:Nc \g_@@_code_temp_prop { g_@@_#1_code_prop } - \@@_initialize_single:ccccn - { g_@@_#1_code_prop } { g_@@_#1_code_tl } - { g_@@_#1_next_code_tl } { g_@@_#1_labels_clist } - {#1} - \prop_gset_eq:cN { g_@@_#1_code_prop } \g_@@_code_temp_prop + \prop_set_eq:Nc \l_@@_work_prop { g_@@_#1_code_prop } + \@@_initialize_single:cccn + { g_@@_#1_code_tl } { g_@@_#1_code_next_tl } + { g_@@_#1_labels_clist } {#1} % \end{macrocode} % For debug display we want to keep track of those hooks that % actually got code added to them, so we record that in plist. We % use a plist to ensure that we record each hook name only once, -% i.e., we are only interested in storing the keys and the value is arbitrary +% i.e., we are only interested in storing the keys and the value is +% arbitrary. % \begin{macrocode} - \@@_debug:n{ \exp_args:NNx \prop_gput:Nnn \g_@@_used_prop {#1}{} } + \@@_debug:n{ \exp_args:NNx \prop_gput:Nnn + \g_@@_used_prop {#1}{} } } } } % \end{macrocode} % \end{macro} - - +% +% % \begin{macro}{\g_@@_used_prop} % All hooks that receive code (for use in debugging display). % \begin{macrocode} \prop_new:N\g_@@_used_prop % \end{macrocode} % \end{macro} - - +% +% % % \begin{macro}[EXP]{\@@_tl_csname:n,\@@_seq_csname:n} % It is faster to pass a single token and expand it when necessary @@ -2446,7 +2652,7 @@ % \end{macrocode} % \end{macro} % - +% % % % \begin{macro}{\l_@@_labels_seq,\l_@@_labels_int,\l_@@_front_tl, @@ -2486,11 +2692,11 @@ % % \end{itemize} % \end{macro} - - -% \begin{macro}{\@@_initialize_single:NNNNn,\@@_initialize_single:ccccn} % -% \cs{@@_initialize_single:NNNNn} implements the sorting of the code +% +% \begin{macro}{\@@_initialize_single:NNNn,\@@_initialize_single:cccn} +% +% \cs{@@_initialize_single:NNNn} implements the sorting of the code % chunks for a hook and saves the result in the token list for fast % execution (\verb=#3=). The arguments are \meta{hook-code-plist}, % \meta{hook-code-tl}, \meta{hook-next-code-tl}, @@ -2506,7 +2712,7 @@ % the moment: a label can't be equal to the number 0! \fmi{Needs % checking for, just in case} % \begin{macrocode} -\cs_new_protected:Npn \@@_initialize_single:NNNNn #1#2#3#4#5 { +\cs_new_protected:Npn \@@_initialize_single:NNNn #1#2#3#4 { % \end{macrocode} % % \begin{macrocode} @@ -2520,7 +2726,7 @@ % % Store the name of the hook: % \begin{macrocode} - \tl_set:Nn \l_@@_cur_hook_tl {#5} + \tl_set:Nn \l_@@_cur_hook_tl {#4} % \end{macrocode} % % We loop over the property list holding the code and record all @@ -2530,20 +2736,18 @@ % to ensure that labels named |front|, |rear|, |labels|, or |return| % don't interact with our code. % \begin{macrocode} - \prop_map_inline:Nn #1 + \prop_map_inline:Nn \l_@@_work_prop { \int_incr:N \l_@@_labels_int \seq_put_right:Nn \l_@@_labels_seq {##1} - \tl_set:cn { \@@_tl_csname:n {##1} }{0} % the counter k for number of - % j before k rules - \seq_clear_new:c { \@@_seq_csname:n {##1} } % sequence of successors to k - % i.e., k before j rules (stores - % the names of the j's) + \tl_set:cn { \@@_tl_csname:n {##1} }{0} + \seq_clear_new:c { \@@_seq_csname:n {##1} } } % \end{macrocode} % Steps T2 and T3: Sort the relevant rules into the data structure\ldots % -% This loop constitutes a square matrix of the labels in |#1| in the +% This loop constitutes a square matrix of the labels in +% \cs{l_@@_work_prop} in the % vertical and the horizontal directions. However since the rule % $l_A\meta{rel}l_B$ is the same as $l_B\meta{rel}^{-1}l_A$ we can cut % the loop short at the diagonal of the matrix (\emph{i.e.}, when @@ -2554,29 +2758,29 @@ % \cs{@@_apply_label_pair:nnn}, which takes the properly-ordered pair % of labels as argument. % \begin{macrocode} - \prop_map_inline:Nn #1 + \prop_map_inline:Nn \l_@@_work_prop { - \prop_map_inline:Nn #1 + \prop_map_inline:Nn \l_@@_work_prop { \@@_if_label_case:nnnnn {##1} {####1} { \prop_map_break: } { \@@_apply_label_pair:nnn {##1} {####1} } { \@@_apply_label_pair:nnn {####1} {##1} } - {#5} + {#4} } } % \end{macrocode} % Take a breath and take a look at the data structures that have % been set up: % \begin{macrocode} - \@@_debug:n { \@@_debug_label_data:N #1 } + \@@_debug:n { \@@_debug_label_data:N \l_@@_work_prop } % \end{macrocode} % % % Step T4: % \begin{macrocode} \tl_set:Nn \l_@@_rear_tl { 0 } - \tl_set:cn { \@@_tl_csname:n { 0 } } { 0 } % really {l_@@_label_ \l_@@_rear_tl _tl} + \tl_set:cn { \@@_tl_csname:n { 0 } } { 0 } \seq_map_inline:Nn \l_@@_labels_seq { \int_compare:nNnT { \cs:w \@@_tl_csname:n {##1} \cs_end: } = 0 @@ -2589,8 +2793,8 @@ % \end{macrocode} % % \begin{macrocode} - \tl_gclear:N #2 - \clist_gclear:N #4 + \tl_gclear:N #1 + \clist_gclear:N #3 % \end{macrocode} % % The whole loop combines steps T5--T7: @@ -2601,12 +2805,12 @@ % This part is step T5: % \begin{macrocode} \int_decr:N \l_@@_labels_int - \prop_get:NVN #1 \l_@@_front_tl \l_@@_return_tl - \@@_tl_gput:NV #2 \l_@@_return_tl + \prop_get:NVN \l_@@_work_prop \l_@@_front_tl \l_@@_return_tl + \@@_tl_gput:NV #1 \l_@@_return_tl % \end{macrocode} % % \begin{macrocode} - \@@_clist_gput:NV #4 \l_@@_front_tl + \@@_clist_gput:NV #3 \l_@@_front_tl \@@_debug:n{ \iow_term:x{Handled~ code~ for~ \l_@@_front_tl} } % \end{macrocode} % @@ -2617,8 +2821,11 @@ \seq_map_inline:cn { \@@_seq_csname:n { \l_@@_front_tl } } { \tl_set:cx { \@@_tl_csname:n {##1} } - { \int_eval:n { \cs:w \@@_tl_csname:n {##1} \cs_end: - 1 } } - \int_compare:nNnT { \cs:w \@@_tl_csname:n {##1} \cs_end: } = 0 + { \int_eval:n + { \cs:w \@@_tl_csname:n {##1} \cs_end: - 1 } + } + \int_compare:nNnT + { \cs:w \@@_tl_csname:n {##1} \cs_end: } = 0 { \tl_set:cn { \@@_tl_csname:n { \l_@@_rear_tl } } {##1} \tl_set:Nn \l_@@_rear_tl {##1} @@ -2627,7 +2834,8 @@ % \end{macrocode} % and step T7: % \begin{macrocode} - \tl_set_eq:Nc \l_@@_front_tl { \@@_tl_csname:n { \l_@@_front_tl } } + \tl_set_eq:Nc \l_@@_front_tl + { \@@_tl_csname:n { \l_@@_front_tl } } % \end{macrocode} % % This is step T8: If we haven't moved the code for all labels @@ -2644,15 +2852,15 @@ % This is not really the information one needs in the error case % but will do for now \ldots \fmi{fix} % \begin{macrocode} - \@@_debug_label_data:N #1 + \@@_debug_label_data:N \l_@@_work_prop \iow_term:x{====================} } % \end{macrocode} -% After we have added all hook code to \verb=#2= we finish it off +% After we have added all hook code to \verb=#1= we finish it off % with adding extra code for a one time execution. That is stored -% in \verb=#3= but is normally empty. +% in \verb=#2= but is normally empty. % \begin{macrocode} - \tl_gput_right:Nn #2 {#3} + \tl_gput_right:Nn #1 {#2} % \end{macrocode} % % \begin{macrocode} @@ -2661,12 +2869,12 @@ % \end{macrocode} % % \begin{macrocode} -\cs_generate_variant:Nn \@@_initialize_single:NNNNn {cccc} +\cs_generate_variant:Nn \@@_initialize_single:NNNn {ccc} % \end{macrocode} % \end{macro} - - - +% +% +% % \begin{macro}{\@@_tl_gput:NV,\@@_clist_gput:NV} % These append either on the right (normal hook) or on the left % (reversed hook). This is setup up in @@ -2676,8 +2884,8 @@ \cs_new:Npn \@@_clist_gput:NV {\ERROR} % \end{macrocode} % \end{macro} - - +% +% % % \begin{macro}{\@@_apply_label_pair:nnn,\@@_label_if_exist_apply:nnnF} % @@ -2732,10 +2940,10 @@ } % \end{macrocode} % \end{macro} - - - - +% +% +% +% % \begin{macro}{\@@_apply_rule:nnn} % This is the code executed in steps T2 and T3 while looping through % the matrix This is part of step T3. We are about to apply the next @@ -2798,13 +3006,15 @@ % % \begin{macro}{\@@_apply_rule_->:nnn,\@@_apply_rule_<-:nnn} % If we see \texttt{\detokenize{->}} we have to drop code for label -% \verb=#3= and carry on. We could do a little better and trop +% \verb=#3= and carry on. We could do a little better and drop % everything for that label since it doesn't matter where we sort % in the empty code. However that would complicate the algorithm a -% lot with little gain. So we still unnecessarily try to sort it in -% and depending on the rules that might result in a loop that is -% otherwise resolved. If that turns out to be a real issue, we can -% improve the code. +% lot with little gain.\footnote{This also hase the advantage that +% the result of the sorting doesn't change which might otherwise +% (for unrelated chunks) if we aren't careful.} So we still +% unnecessarily try to sort it in and depending on the rules that +% might result in a loop that is otherwise resolved. If that turns +% out to be a real issue, we can improve the code. % % Here the code is removed from \cs{l_@@_cur_hook_tl} rather than % \verb=#3= because the latter may be \verb=??=, and the default @@ -2817,9 +3027,10 @@ { \@@_msg_pair_found:nnn {#1} {#2} {#3} \iow_term:x{--->~ Drop~ '#2'~ code~ from~ - \iow_char:N \\ g_@@_ \l_@@_cur_hook_tl _code_prop ~ because~ of~ '#1' } + \iow_char:N \\ g_@@_ \l_@@_cur_hook_tl _code_prop ~ + because~ of~ '#1' } } - \prop_gput:cnn { g_@@_ \l_@@_cur_hook_tl _code_prop } {#2} { } + \prop_put:Nnn \l_@@_work_prop {#2} { } } \cs_new_protected:cpn { @@_apply_rule_<-:nnn } #1#2#3 { @@ -2827,13 +3038,14 @@ { \@@_msg_pair_found:nnn {#1} {#2} {#3} \iow_term:x{--->~ Drop~ '#1'~ code~ from~ - \iow_char:N \\ g_@@_ \l_@@_cur_hook_tl _code_prop ~ because~ of~ '#2' } + \iow_char:N \\ g_@@_ \l_@@_cur_hook_tl _code_prop ~ + because~ of~ '#2' } } - \prop_gput:cnn { g_@@_ \l_@@_cur_hook_tl _code_prop } {#1} { } + \prop_put:Nnn \l_@@_work_prop {#1} { } } % \end{macrocode} % \end{macro} - +% % \begin{macro}{ % \@@_apply_-rule_<:nnn, % \@@_apply_-rule_>:nnn, @@ -2851,27 +3063,28 @@ \cs_new_eq:cc { @@_apply_-rule_xW:nnn } { @@_apply_rule_xW:nnn } % \end{macrocode} % \end{macro} - - +% +% % \begin{macro}{\@@_msg_pair_found:nnn} % A macro to avoid moving this many tokens around. % \begin{macrocode} \cs_new_protected:Npn \@@_msg_pair_found:nnn #1#2#3 { \iow_term:x{~ \str_if_eq:nnTF {#3} {??} {default} {~normal} ~ - rule~ \@@_label_pair:nn {#1} {#2}:~ - \use:c { g_@@_#3_rule_ \@@_label_pair:nn {#1} {#2} _tl } ~ found} + rule~ \@@_label_pair:nn {#1} {#2}:~ + \use:c { g_@@_#3_rule_ \@@_label_pair:nn {#1} {#2} _tl } ~ + found} } % \end{macrocode} % \end{macro} - - +% +% % \begin{macro}{\@@_debug_label_data:N} % % \begin{macrocode} \cs_new_protected:Npn \@@_debug_label_data:N #1 { \iow_term:x{Code~ labels~ for~ sorting:} - \iow_term:x{~ \seq_use:Nnnn\l_@@_labels_seq {~and~}{,~}{~and~} } % fix name! + \iow_term:x{~ \seq_use:Nnnn\l_@@_labels_seq {~and~}{,~}{~and~} } \iow_term:x{^^J Data~ structure~ for~ label~ rules:} \prop_map_inline:Nn #1 { @@ -2883,9 +3096,9 @@ } % \end{macrocode} % \end{macro} - - - +% +% +% % \begin{macro}{\hook_log:n} % This writes out information about the hook given in its argument % onto the terminal and the \texttt{.log} file. @@ -2897,6 +3110,7 @@ } \cs_new_protected:Npn \@@_log:n #1 { + \@@_preamble_hook:n {#1} \iow_term:x{^^JThe~ hook~ '#1':} % \end{macrocode} % @@ -2917,26 +3131,42 @@ % \begin{macrocode} \iow_term:x{~Extra~ code~ next~ invocation:} \iow_term:x{\@spaces - \tl_if_empty:cTF { g_@@_#1_next_code_tl } - {---} {->~ \str_use:c{g_@@_#1_next_code_tl} } } + \tl_if_empty:cTF { g_@@_#1_code_next_tl } + {---} +% \end{macrocode} +% If the token list is not empty we want to display it but without +% the first tokens (the code to clear itself) so we call a helper +% command to get rid of them. +% \begin{macrocode} + {->~ \exp_args:Nv + \@@_log_next_code:n {g_@@_#1_code_next_tl} } } % \end{macrocode} % -% \fmi{This is currently only displaying the local rules, but it -% should also show the matching global rules!} -% +% Loop through the rules in a hook and for every rule found, print it. +% If no rule is there, print |---|. The boolean \cs{l_@@_tmpa_bool} +% here indicates if the hook has no rules. % \begin{macrocode} - \iow_term:x{~Rules:} - \prop_if_empty:cTF {g_@@_#1_rules_prop} - { \iow_term:x{\@spaces ---} } - { \prop_map_inline:cn {g_@@_#1_rules_prop} - { \iow_term:x{\@spaces ##1~ with~ relation~ ##2} } + \iow_term:x { ~Rules: } + \bool_set_true:N \l_@@_tmpa_bool + \@@_list_rules:nn {#1} + { + \bool_set_false:N \l_@@_tmpa_bool + \iow_term:x + { + \@spaces ##2~ with~ + \str_if_eq:nnT {##3} {??} { default~ } + relation~ ##1 } } + \bool_if:NT \l_@@_tmpa_bool + { \iow_term:x { \@spaces --- } } % \end{macrocode} % % \begin{macrocode} - \hook_if_exist:nT {#1} + \bool_lazy_and:nnT + { \hook_if_exist_p:n {#1} } + { ! \hook_if_empty_p:n {#1} } { \iow_term:x { ~Execution~ order - \prop_if_empty:cTF {g_@@_#1_rules_prop} + \bool_if:NTF \l_@@_tmpa_bool { \@@_if_reversed:nT {#1} { ~ (after~ reversal) } } @@ -2944,30 +3174,97 @@ \@@_if_reversed:nT {#1} {reversal~ and~} applying~ rules) } - : + : } - \iow_term:x { \@spaces - \clist_if_empty:cTF{g_@@_#1_labels_clist} - {not~ set~ yet} - { \clist_use:cnnn {g_@@_#1_labels_clist} - { ,~ } { ,~ } { ,~ } } - } + \iow_term:x + { \@spaces \clist_use:cn {g_@@_#1_labels_clist} { ,~ } } } } { \iow_term:n { ~The~hook~is~empty. } } \iow_term:n { } } % \end{macrocode} -% +% +% To display the code for next invocation only (i.e., from +% \cs{AddToHookNext} we have to remove the first two tokens at the +% front which are \cs{tl_gclear:N} and the token list to clear. +% \begin{macrocode} +\cs_new:Npn \@@_log_next_code:n #1 { + \exp_args:No \tl_to_str:n {\use_none:nn #1} +} +% \end{macrocode} % \end{macro} - - - -% \subsection{Specifying code for next invocation} % +% \begin{macro}{\@@_list_rules:nn} +% \begin{macro}{\@@_list_one_rule:nnn,\@@_list_if_rule_exists:nnnF} +% This macro takes a \meta{hook} and an \meta{inline function} and +% loops through each pair of \meta{labels} in the \meta{hook}, and if +% there is a relation between this pair of \meta{labels}, the +% \meta{inline function} is executed with |#1|${}={}$\meta{relation}, +% |#2|${}={}$\meta{label_1}\verb=|=\meta{label_2}, +% and |#3|${}={}$\meta{hook} (the latter may be the argument |#1| to +% \cs{@@_list_rules:nn}, or |??| if it is a default rule). +% \begin{macrocode} +\cs_new_protected:Npn \@@_list_rules:nn #1 #2 + { + \cs_set_protected:Npn \@@_tmp:w ##1 ##2 ##3 {#2} + \prop_map_inline:cn { g_@@_#1_code_prop } + { + \prop_map_inline:cn { g_@@_#1_code_prop } + { + \@@_if_label_case:nnnnn {##1} {####1} + { \prop_map_break: } + { \@@_list_one_rule:nnn {##1} {####1} } + { \@@_list_one_rule:nnn {####1} {##1} } + {#1} + } + } + } +% \end{macrocode} % +% These two are quite similar to \cs{@@_apply_label_pair:nnn} and +% \cs{@@_label_if_exist_apply:nnnF}, respectively, but rather than +% applying the rule, they pass it to the \meta{inline function}. +% \begin{macrocode} +\cs_new_protected:Npn \@@_list_one_rule:nnn #1#2#3 + { + \@@_list_if_rule_exists:nnnF {#1} {#2} {#3} { } + { \@@_list_if_rule_exists:nnnF {#1} {#2} { ?? } { } } + } +\cs_new_protected:Npn \@@_list_if_rule_exists:nnnF #1#2#3 + { + \if_cs_exist:w g_@@_ #3 _rule_ #1 | #2 _tl \cs_end: + \exp_args:Nv \@@_tmp:w + { g_@@_ #3 _rule_ #1 | #2 _tl } { #1 | #2 } {#3} + \fi: + } +% \end{macrocode} +% \end{macro} +% \end{macro} % +% \begin{macro}{\@@_debug_print_rules:n} +% A shorthand for debugging that prints similar to \cs{prop_show:N}. +% \begin{macrocode} +\cs_new_protected:Npn \@@_debug_print_rules:n #1 + { + \iow_term:n { The~hook~#1~contains~the~rules: } + \cs_set_protected:Npn \@@_tmp:w ##1 + { + \@@_list_rules:nn {#1} + { + \iow_term:x + { + > ##1 {####2} ##1 => ##1 {####1} + \str_if_eq:nnT {####3} {??} { ~(default) } + } + } + } + \exp_args:No \@@_tmp:w { \use:nn { ~ } { ~ } } + } +% \end{macrocode} +% \end{macro} % +% \subsection{Specifying code for next invocation} % % \begin{macro}{\hook_gput_next_code:nn} % @@ -2984,10 +3281,29 @@ { \@@_gput_next_do:nn {#1} {#2} } { \@@_try_declaring_generic_next_hook:nn {#1} {#2} } } -\cs_new_protected:Npn \@@_gput_next_do:nn #1 #2 +\cs_new_protected:Npn \@@_gput_next_do:nn #1 { - \tl_gput_right:cn { g_@@_#1_next_code_tl } - { #2 \tl_gclear:c { g_@@_#1_next_code_tl } } + \exp_args:Nc \@@_gput_next_do:Nnn + { g_@@_#1_code_next_tl } {#1} + } +% \end{macrocode} +% First check if the ``next code'' token list is empty: if so we need +% to add a \cs{tl_gclear:c} to clear it, so the code lasts for one +% usage only. The token list is cleared early so that nested usages +% don't get lost. \cs{tl_gclear:c} is used instead of +% \cs{tl_gclear:N} in case the hook is used in an expansion-only +% context, so the token list doesn't expand before \cs{tl_gclear:N}: +% that would make an infinite loop. Also in case the main code token +% list is empty, the hook code has to be updated to add the next +% execution token list. +% \begin{macrocode} +\cs_new_protected:Npn \@@_gput_next_do:Nnn #1 #2 + { + \tl_if_empty:cT { g_@@_#2_code_tl } + { \@@_update_hook_code:n {#2} } + \tl_if_empty:NT #1 + { \tl_gset:Nn #1 { \tl_gclear:c { g_@@_#2_code_next_tl } } } + \tl_gput_right:Nn #1 } % \end{macrocode} % \end{macro} @@ -3069,7 +3385,7 @@ \@@_if_file_hook:wTF #1 / / \s_@@_mark { \exp_args:Ne \@@_if_exist_use:n - { \exp_args:Ne \@@_file_hook_normalise:n {#1} } + { \exp_args:Ne \@@_file_hook_normalize:n {#1} } } { \@@_if_exist_use:n {#1} } % file/ generic hook (e.g. file/before) } @@ -3102,7 +3418,7 @@ } % \end{macrocode} % \end{macro} - +% % \subsection{Querying a hook} % % Simpler data types, like token lists, have three possible states; they @@ -3129,7 +3445,7 @@ % Test if a hook is empty (that is, no code was added to that hook). % A hook being empty means that \emph{both} its % \cs[no-index]{g_@@_\meta{hook}_code_prop} and its -% \cs[no-index]{g_@@_\meta{hook}_next_code_tl} are empty. +% \cs[no-index]{g_@@_\meta{hook}_code_next_tl} are empty. % \begin{macrocode} \prg_new_conditional:Npnn \hook_if_empty:n #1 { p , T , F , TF } { @@ -3137,7 +3453,7 @@ { \bool_lazy_and:nnTF { \prop_if_empty_p:c { g_@@_#1_code_prop } } - { \tl_if_empty_p:c { g_@@_#1_next_code_tl } } + { \tl_if_empty_p:c { g_@@_#1_code_next_tl } } { \prg_return_true: } { \prg_return_false: } } @@ -3145,7 +3461,7 @@ } % \end{macrocode} % \end{macro} - +% % \begin{macro}[pTF]{\hook_if_exist:n} % A canonical way to test if a hook exists. A hook exists if the % token list that stores the sorted code for that hook, @@ -3163,7 +3479,7 @@ } % \end{macrocode} % \end{macro} - +% % \begin{macro}[pTF]{\@@_if_exist:n} % An internal check if the hook has already been declared with % \cs{@@_declare:n}. This means that the hook was already used somehow @@ -3192,8 +3508,8 @@ } % \end{macrocode} % \end{macro} - - +% +% % \begin{macro}{\g_@@_execute_immediately_clist} % List of hooks that from no on should not longer receive code. % \begin{macrocode} @@ -3208,21 +3524,57 @@ { Labels~`#1'~and~`#2'~are~incompatible \str_if_eq:nnF {#3} {??} { ~in~hook~`#3' } .~ - \int_compare:nNnT {#4} = { 1 } - { The~code~for~both~labels~will~be~dropped. } - } - { - LaTeX~found~two~incompatible~labels~in~the~same~hook.~ - This~indicates~an~incompatibility~between~packages. + \int_compare:nNnTF {#4} = { 1 } + { The~ code~ for~ both~ labels~ will~ be~ dropped. } + { You~ may~ see~ errors~ later. } } + { LaTeX~found~two~incompatible~labels~in~the~same~hook.~ + This~indicates~an~incompatibility~between~packages. } +% \end{macrocode} +% +% \begin{macrocode} +\msg_new:nnnn { hooks } { exists } + { Hook~`#1'~ has~ already~ been~ declared. } + { There~ already~ exists~ a~ hook~ declaration~ with~ this~ + name.\\ + Please~ use~ a~ different~ name~ for~ your~ hook.} +% \end{macrocode} +% +% \begin{macrocode} \msg_new:nnn { hooks } { empty-label } { Empty~code~label~\msg_line_context:.~Using~`#1'~instead. } % \end{macrocode} +% +% \begin{macrocode} +\msg_new:nnnn { hooks } { unknown-rule } + { Unknown~ relationship~ `#3'~ + between~ labels~ `#2'~ and~ `#4'~ + \str_if_eq:nnF {#1} {??} { ~in~hook~`#1' }. ~ + Perhaps~ a~ missspelling? + } + { + The~ relation~ used~ not~ known~ to~ the~ system.~ Allowed~ values~ are~ + `before'~ or~ `<',~ + `after'~ or~ `>',~ + `incompatible-warning',~ + `incompatible-error',~ + `voids'~ or~ + `unrelated'. + } +% \end{macrocode} +% +% \begin{macrocode} +\msg_new:nnn { hooks } { should-not-happen } + { + ERROR!~This~should~not~happen.~#1 \\ + Please~report~at~https://github.com/latex3/latex2e. + } +% \end{macrocode} % % \subsection{\LaTeXe{} package interface commands} % - - +% +% % \begin{macro}{\NewHook,\NewReversedHook,\NewMirroredHookPair} % Declaring new hooks \ldots % \begin{macrocode} @@ -3231,7 +3583,7 @@ \NewDocumentCommand \NewMirroredHookPair { mm }{ \hook_new_pair:nn {#1}{#2} } % \end{macrocode} % \end{macro} - +% % \begin{macro}{\AddToHook} % % \begin{macrocode} @@ -3243,7 +3595,7 @@ } % \end{macrocode} % \end{macro} - +% % \begin{macro}{\AddToHookNext} % % \begin{macrocode} @@ -3251,8 +3603,8 @@ { \hook_gput_next_code:nn {#1} {#2} } % \end{macrocode} % \end{macro} - - +% +% % \begin{macro}{\RemoveFromHook} % % \begin{macrocode} @@ -3275,32 +3627,15 @@ % default label for the top-level. Since the string \verb|top-level| % is hardcoded, here this item of the stack is empty. Also, since % we're in an input level, add \verb|lthooks| to the stack as well. -% This stack should never go empty, so we loop through \LaTeXe's -% file name stack, and add empty entries to \cs{g_@@_name_stack_seq} -% for each item in that stack. The last item is the \verb|top-level|, -% which also gets an empty entry. -% -% Also check for the case we're loading \texttt{lthooks} in the -% \LaTeXe{} kernel. In that case, \cs{@currname} isn't \verb|lthooks| -% and just the top-level is added to the stack as an empty entry. +% This stack should never go empty. An empty entry is added to the +% stack to account for the \verb|top-level|. The item is empty so +% that the hard-coded default is used, but a call to +% \cs{DeclareDefaultHookLabel} will change it if needed. % \begin{macrocode} -\str_if_eq:VnTF \@currname { lthooks } - { - \seq_gpush:Nn \g_@@_name_stack_seq { lthooks } - \cs_set_protected:Npn \@@_tmp:w #1 #2 #3 - { - \quark_if_recursion_tail_stop:n {#1} - \seq_gput_right:Nn \g_@@_name_stack_seq { } - \@@_tmp:w - } - \exp_after:wN \@@_tmp:w - \@currnamestack - \q_recursion_tail \q_recursion_tail - \q_recursion_tail \q_recursion_stop - } - { \seq_gpush:Nn \g_@@_name_stack_seq { } } +\seq_gpush:Nn \g_@@_name_stack_seq { } % \end{macrocode} % +% % Two commands keep track of the stack: when a file is input, % \cs{@@_curr_name_push:n} pushes an (empty by default) label to the % stack: @@ -3321,7 +3656,10 @@ \seq_gpop:NN \g_@@_name_stack_seq \l_@@_return_tl \seq_get:NNTF \g_@@_name_stack_seq \l_@@_return_tl { \tl_gset_eq:NN \g_@@_hook_curr_name_tl \l_@@_return_tl } - { \ERROR_should_not_happen } + { + \msg_error:nnn { hooks } { should-not-happen } + { Tried~to~pop~from~an~empty~default~label~stack. } + } } % \end{macrocode} % @@ -3336,15 +3674,6 @@ \seq_gpop:NN \g_@@_name_stack_seq \l_@@_return_tl \@@_curr_name_push:n {#1} } -% \begin{macrocode} -% -% The push and pop macros are injected in \cs{@pushfilename} and -% \cs{@popfilename} so that they correctly keep track of the label.s -% \begin{macrocode} -% TODO! \pho{Properly integrate in the kernel} -\tl_gput_left:Nn \@pushfilename { \@@_curr_name_push:n { } } -\tl_gput_left:Nn \@popfilename { \@@_curr_name_pop: } -% TODO! \pho{Properly integrate in the kernel} % \end{macrocode} % \end{macro} % \end{macro} @@ -3352,48 +3681,42 @@ % % % -% \begin{macro}{\UseHook} +% \begin{macro}{\UseHook,\UseOneTimeHook} % Avoid the overhead of \pkg{xparse} and its protection that we % don't want here (since the hook should vanish without trace if empty)! % \begin{macrocode} -\newcommand \UseHook { \hook_use:n } +\newcommand \UseHook { \hook_use:n } +\newcommand \UseOneTimeHook { \hook_use_once:n } % \end{macrocode} % \end{macro} % -% \begin{macro}{\UseOneTimeHook} % -% \begin{macrocode} -\cs_new_protected:Npn \UseOneTimeHook { \hook_use_once:n } -% \end{macrocode} -% \end{macro} - - - +% % \begin{macro}{\ShowHook} % % \begin{macrocode} \cs_new_protected:Npn \ShowHook { \hook_log:n } % \end{macrocode} % \end{macro} - -% \begin{macro}{\DebugHookOn,\DebugHookOff} +% +% \begin{macro}{\DebugHooksOn,\DebugHooksOff} % % \begin{macrocode} -\cs_new_protected:Npn \DebugHookOn { \hook_debug_on: } -\cs_new_protected:Npn \DebugHookOff { \hook_debug_off: } +\cs_new_protected:Npn \DebugHooksOn { \hook_debug_on: } +\cs_new_protected:Npn \DebugHooksOff { \hook_debug_off: } % \end{macrocode} % \end{macro} - - - +% +% +% % \begin{macro}{\DeclareHookRule} % % \begin{macrocode} \NewDocumentCommand \DeclareHookRule { m m m m } -{ \hook_gset_rule:nnnn {#1}{#2}{#3}{#4} } + { \hook_gset_rule:nnnn {#1}{#2}{#3}{#4} } % \end{macrocode} % \end{macro} - +% % \begin{macro}{\DeclareDefaultHookRule} % This declaration is only supported before \verb=\begin{document}=. % \begin{macrocode} @@ -3406,7 +3729,7 @@ % \begin{macro}{\ClearHookRule} % A special setup rule that removes an existing relation. % Basically {@@_rule_gclear:nnn} plus fixing the property list for debugging. -% \fmi{Need an L3 interface, or maybe it should get dropped} +% \fmi{Need an L3 interface, or maybe it should get dropped?} % \begin{macrocode} \NewDocumentCommand \ClearHookRule { m m m } { \hook_gset_rule:nnnn {#1}{#2}{unrelated}{#3} } @@ -3414,512 +3737,102 @@ % \end{macro} % % -% \begin{macro}{\IfHookExistTF,\IfHookEmptyTF} +% \begin{macro}{\IfHookExistsTF,\IfHookEmptyTF} % \begin{macrocode} -\NewExpandableDocumentCommand \IfHookExistTF { m } +\NewExpandableDocumentCommand \IfHookExistsTF { m } { \hook_if_exist:nTF {#1} } \NewExpandableDocumentCommand \IfHookEmptyTF { m } { \hook_if_empty:nTF {#1} } % \end{macrocode} % \end{macro} - - - - -% \begin{macro}{\AtBeginDocument} -% -% \begin{macrocode} -\renewcommand\AtBeginDocument{\AddToHook{begindocument}} -% \end{macrocode} -% \end{macro} - -% \begin{macro}{\AtEndDocument} -% -% \begin{macrocode} -\renewcommand\AtEndDocument {\AddToHook{enddocument}} -%\renewcommand\AtEndDocument {\AddToHook{env/document/end}} % alternative impl -% \end{macrocode} -% -% \end{macro} - - - -% \subsection{Set up existing \LaTeXe{} hooks} -% -% As we are in a package calling \cs{NewHook} would label any -% already set up hook code under the package name, but we want it -% under the name \hook{top-level} so we pretend that \cs{@currname} -% is empty. -% \begin{macrocode} -\begingroup - \def\@currname{} -% \end{macrocode} -% -% \begin{macrocode} - \NewHook{begindocument} - \NewHook{enddocument} -% \end{macrocode} -% We need to initialize the mechanism at \verb=\begin{document}= -% but obviously before everything else, so we sneak\footnote{This -% needs to move to \cs{document} directly.} -% \cs{@@_initialize_all:} into the \LaTeXe{} hook name. -% -% We can't use \cs{tl_gput_left:Nn} because that complains about -% \cs{@begindocumenthook} not starting with \cs{g_} so we do this -% through the backdoor. -% \begin{macrocode} -% \tex_global:D\tl_put_left:Nn \@begindocumenthook -% {\@@_initialize_all:} -% \end{macrocode} -% There aren't many other hooks at the moment: -% \begin{macrocode} - \NewHook{rmfamily} - \NewHook{sffamily} - \NewHook{ttfamily} - \NewHook{defaultfamily} -% \end{macrocode} -% Not checked what this one does and whether it should be there (or -% is a real ``hook''. -% \begin{macrocode} - \NewHook{documentclass} -% \end{macrocode} -% -% \begin{macrocode} -\endgroup -% \end{macrocode} -% -% -% -% \section{Generic hooks for environments} -% -% -% \begin{macrocode} -\let\begin\relax % avoid redeclaration message -% \end{macrocode} -% -% \begin{macrocode} -\DeclareRobustCommand*\begin[1]{% - \UseHook{env/#1/before}% - \@ifundefined{#1}% - {\def\reserved@a{\@latex@error{Environment~#1~undefined}\@eha}}% - {\def\reserved@a{\def\@currenvir{#1}% - \edef\@currenvline{\on@line}% - \@execute@begin@hook{#1}% - \csname #1\endcsname}}% - \@ignorefalse - \begingroup\@endpefalse\reserved@a} -% \end{macrocode} -% -% Before the \cs{document} code is executed we have to first undo -% the \cs{endgroup} as there should be none for this environment to -% avoid that changes on top-level unnecessarily go to \TeX's -% savestack, and we have to initialize all hooks in the hook system. -% So we need to test for this environment name. But once it has be -% found all this testing is no longer needed and so we redefine -% \cs{@execute@begin@hook} to simply use the hook -% \begin{macrocode} -\def\@execute@begin@hook #1{% - \expandafter\ifx\csname #1\endcsname\document - \endgroup - \gdef\@execute@begin@hook##1{\UseHook{env/##1/begin}}% - \@@_initialize_all: - \@execute@begin@hook{#1}% -% \end{macrocode} -% If this is an environment before \verb=\begin{document}= we just -% run the hook. -% \begin{macrocode} - \else - \UseHook{env/#1/begin}% - \fi -} -% \end{macrocode} -% -% \begin{macrocode} -\@namedef{end~}#1{% - \UseHook{env/#1/end}% - \csname end#1\endcsname\@checkend{#1}% - \expandafter\endgroup\if@endpe\@doendpe\fi - \UseHook{env/#1/after}% - \if@ignore\@ignorefalse\ignorespaces\fi}% -% \end{macrocode} -% Version that fixes tlb3722 but the change should perhaps be made in -% \pkg{tabularx} instead. -% \begin{macrocode} -\@namedef{end~}#1{% -\romannumeral -\IfHookEmptyTF{env/#1/end}% - {\expandafter\z@}% - {\z@\UseHook{env/#1/end}}% -\csname end#1\endcsname\@checkend{#1}% -\expandafter\endgroup\if@endpe\@doendpe\fi -\UseHook{env/#1/after}% -\if@ignore\@ignorefalse\ignorespaces\fi}% -% \end{macrocode} -% -% % -% We provide 4 high-level hook interfaces directly, the others only when -% etoolbox is loaded -% \begin{macrocode} -\newcommand\AtBeginEnvironment[1] {\AddToHook{env/#1/begin}} -\newcommand\AtEndEnvironment[1] {\AddToHook{env/#1/end}} -\newcommand\BeforeBeginEnvironment[1]{\AddToHook{env/#1/before}} -\newcommand\AfterEndEnvironment[1] {\AddToHook{env/#1/after}} -% \end{macrocode} -% -% -% -% -% -% -% -% -% -% -% -% \section{Generic hooks for file loads} % +%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%5 % +% \subsection{Internal commands needed elsewhere} % +% Here we set up a few horrible (but consistent) \LaTeXe{} names to +% allow for internal commands to be used outside this module. We +% have to unset the \texttt{@\/@} since we want double ``at'' sign +% in place of double underscores. % -% -% -% -% \section{Hooks in \cs{begin} document} -% -% Can't have \texttt{@{}@} notation here as this is \LaTeXe{} code -% \ldots{} and makes for puzzling errors if the double \texttt{@} -% signs get substituted. % \begin{macrocode} %<@@=> -\ExplSyntaxOff -% \end{macrocode} -% -% The \hook{begindocument} hook was already set up earlier, here is now -% the additional one (which was originally from the \pkg{etoolbox} -% package under the name \texttt{afterpreamble}. -% \begin{macrocode} -\NewHook{begindocument/end} % \end{macrocode} % -% - -% \begin{macro}{\document} +% \begin{macro}{\@expl@@@initialize@all@@, +% \@expl@@@hook@curr@name@push@@n, +% \@expl@@@hook@curr@name@pop@@} % % \begin{macrocode} -\def\document{% +\cs_new_eq:NN \@expl@@@initialize@all@@ + \__hook_initialize_all: % \end{macrocode} -% We do cancel the grouping as part of the \cs{begin} handling -% (this is now done inside \cs{begin} instead) so that the -% \hook{env/\meta{env}/begin} hook is not hidden inside \cs{begingroup} -% \texttt{...} \cs{endgroup}. -% \begin{macrocode} -% \endgroup -% \end{macrocode} -% -% \begin{macrocode} - \@kernel@after@env@document@begin -% \end{macrocode} -% -% Added hook to load \textsf{l3backend} code: -% \begin{macrocode} - \@expl@sys@load@backend@@ - \ifx\@unusedoptionlist\@empty\else - \@latex@warning@no@line{Unused global option(s):^^J% - \@spaces[\@unusedoptionlist]}% - \fi - \@colht\textheight - \@colroom\textheight \vsize\textheight - \columnwidth\textwidth - \@clubpenalty\clubpenalty - \if@twocolumn - \advance\columnwidth -\columnsep - \divide\columnwidth\tw@ \hsize\columnwidth \@firstcolumntrue - \fi - \hsize\columnwidth \linewidth\hsize - \begingroup\@floatplacement\@dblfloatplacement - \makeatletter\let\@writefile\@gobbletwo - \global \let \@multiplelabels \relax - \@input{\jobname.aux}% - \endgroup - \if@filesw - \immediate\openout\@mainaux\jobname.aux - \immediate\write\@mainaux{\relax}% - \fi - \process@table - \let\glb@currsize\@empty % Force math initialization. - \normalsize - \everypar{}% - \ifx\normalsfcodes\@empty - \ifnum\sfcode`\.=\@m - \let\normalsfcodes\frenchspacing - \else - \let\normalsfcodes\nonfrenchspacing - \fi - \fi - \ifx\document@default@language\m@ne - \chardef\document@default@language\language - \fi - \@noskipsecfalse - \let \@refundefined \relax -% \end{macrocode} -% -% \begin{macrocode} -% \let\AtBeginDocument\@firstofone -% \@begindocumenthook - \UseOneTimeHook{begindocument}% - \@kernel@after@begindocument -% \end{macrocode} -% -% \begin{macrocode} - \ifdim\topskip<1sp\global\topskip 1sp\relax\fi - \global\@maxdepth\maxdepth - \global\let\@begindocumenthook\@undefined - \ifx\@listfiles\@undefined - \global\let\@filelist\relax - \global\let\@addtofilelist\@gobble - \fi - \gdef\do##1{\global\let ##1\@notprerr}% - \@preamblecmds - \global\let \@nodocument \relax - \global\let\do\noexpand -% \end{macrocode} -% -% \begin{macrocode} - \UseOneTimeHook{begindocument/end}% - \ignorespaces} -% \end{macrocode} -% -% \begin{macrocode} -\let\@kernel@after@begindocument\@empty -% \end{macrocode} -% \end{macro} -% -% -% \begin{macro}{\@kernel@after@env@document@begin,\@kernel@hook@begindocument} -% -% \begin{macrocode} -\edef \@kernel@after@env@document@begin{% - \let\expandafter\noexpand\csname - g__hook_env/document/begin_code_tl\endcsname - \noexpand\@empty} -% \end{macrocode} -% \begin{macrocode} -\let\@kernel@hook@begindocument\@empty -% \end{macrocode} -% -% \end{macro} % -% -% \section{Hooks in \cs{enddocument}} -% -% -% -% The \hook{enddocument} hook was already set up earlier, here are now -% the additional ones: % \begin{macrocode} -\NewHook{enddocument/afterlastpage} -\NewHook{enddocument/afteraux} -\NewHook{enddocument/info} -\NewHook{enddocument/end} +\cs_new_eq:NN \@expl@@@hook@curr@name@push@@n + \__hook_curr_name_push:n % \end{macrocode} - - - -% \begin{macro}{\enddocument} -% -% -% \begin{macrocode} -\def\enddocument{% - \UseHook{enddocument}% - \@kernel@after@enddocument - \@checkend{document}% - \clearpage - \UseHook{enddocument/afterlastpage}% - \@kernel@after@enddocument@afterlastpage - \begingroup - \if@filesw - \immediate\closeout\@mainaux - \let\@setckpt\@gobbletwo - \let\@newl@bel\@testdef - \@tempswafalse - \makeatletter \@@input\jobname.aux - \fi - \UseHook{enddocument/afteraux}% -% \end{macrocode} -% Next hook is expect to contain only code for writing info -% messages on the terminal. -% \begin{macrocode} - \UseHook{enddocument/info}% - \endgroup - \UseHook{enddocument/end}% - \deadcycles\z@\@@end} -% \end{macrocode} -% The two kernel hooks above are used by the shipout code. -% \begin{macrocode} -\let\@kernel@after@enddocument\@empty -\let\@kernel@after@enddocument@afterlastpage\@empty -% \end{macrocode} -% \end{macro} -% % -% -% \begin{macro}{\@enddocument@kernel@warnings} -% -% \begin{macrocode} -\def\@enddocument@kernel@warnings{% - \ifdim \font@submax >\fontsubfuzz\relax - \@font@warning{Size substitutions with differences\MessageBreak - up to \font@submax\space have occurred.\@gobbletwo}% - \fi - \@defaultsubs - \@refundefined - \if@filesw - \ifx \@multiplelabels \relax - \if@tempswa - \@latex@warning@no@line{Label(s) may have changed. - Rerun to get cross-references right}% - \fi - \else - \@multiplelabels - \fi - \fi -} -% \end{macrocode} -% % \begin{macrocode} -\AddToHook{enddocument/info}[kernel/filelist]{\@dofilelist} -\AddToHook{enddocument/info}[kernel/warnings]{\@enddocument@kernel@warnings} -\DeclareHookRule{enddocument/info}{kernel/filelist}{before}{kernel/warnings} +\cs_new_eq:NN \@expl@@@hook@curr@name@pop@@ + \__hook_curr_name_pop: % \end{macrocode} % \end{macro} % -% -% \subsection{Adjusting at \pkg{atveryend} interfaces} -% -% With the new hook management all of \pkg{atveryend} is taken care -% of. -% -% We therefore prevent the package from loading: -% \begin{macrocode} -\expandafter\let\csname ver@atveryend.sty\endcsname\fmtversion -% \end{macrocode} -% -% -% Here are new definitions for its interfaces now pointing to the -% hooks in \cs{enddocument} -% \begin{macrocode} -\newcommand\AfterLastShipout {\AddToHook{enddocument/afterlastpage}} -\newcommand\AtVeryEndDocument {\AddToHook{enddocument/afteraux}} -% \end{macrocode} -% Next one is a bit of a fake, but the result should normally be as -% expected. If not one needs to add a rule to sort the code chunks -% in \hook{enddocument/info}. -% \begin{macrocode} -\newcommand\AtEndAfterFileList{\AddToHook{enddocument/info}} -% \end{macrocode} -% -% \begin{macrocode} -\newcommand\AtVeryVeryEnd {\AddToHook{enddocument/end}} -% \end{macrocode} - -% \begin{macro}{\BeforeClearDocument} -% This one is the only one we don't implement or rather don't have -% a dedicated hook in the code. -% \begin{macrocode} -\ExplSyntaxOn -\newcommand\BeforeClearDocument[1] - { \AtEndDocument{#1} - \@DEPRECATED{BeforeClearDocument \tl_to_str:n{#1}} - } -\cs_new:Npn\@DEPRECATED #1 - {\iow_term:x{======~DEPRECATED~USAGE~#1~==========}} -% \end{macrocode} % % \begin{macrocode} \ExplSyntaxOff % \end{macrocode} -% \end{macro} -% -% \begin{macrocode} -%</2ekernel> -% \end{macrocode} - % -% \section{A package version of the code for testing} % - -% \begin{macrocode} -%<*package> -% \end{macrocode} - - - -% \begin{macrocode} -\RequirePackage{xparse} -\ProvidesExplPackage{lthooks}{\lthooksdate}{\lthooksversion} - {Hook management interface for LaTeX2e} +% Rolling back here doesn't undefine the interface commands as they +% may be used in packages without rollback functionality. So we +% just make them do nothing which may or may not work depending on +% the code usage. +% \begin{macrocode} +%</2ekernel|latexrelease> +%<latexrelease>\EndIncludeInRelease +%<latexrelease>\IncludeInRelease{0000/00/00}% +%<latexrelease> {\NewHook}{The hook management}% +%<latexrelease> +%<latexrelease>\def \NewHook#1{} +%<latexrelease>\def \NewReversedHook#1{} +%<latexrelease>\def \NewMirroredHookPair#1#2{} +%<latexrelease> +%<latexrelease>\long\def \AddToHook#1#2{} +%<latexrelease> +%<latexrelease>\def \AddToHookNext#1{\@gobble@AddToHook@args} +%<latexrelease>\providecommand\@gobble@AddToHook@args[2][]{} +%<latexrelease> +%<latexrelease>\def\RemoveFromHook#1{\@gobble@RemoveFromHook@arg} +%<latexrelease>\providecommand\@gobble@RemoveFromHook@arg[1][]{} +%<latexrelease> +%<latexrelease>\def \UseHook #1{} +%<latexrelease>\def \UseOneTimeHook #1{} +%<latexrelease>\def \ShowHook #1{} +%<latexrelease>\let \DebugHooksOn \@empty +%<latexrelease>\let \DebugHooksOff\@empty +%<latexrelease> +%<latexrelease>\def \DeclareHookRul#1#2#3#4{} +%<latexrelease>\def \DeclareDefaultHookRule #1#2#3{} +%<latexrelease>\def \ClearHookRule#1#2#3{} +% \end{macrocode} +% If the hook management is not provided we make the test for existence +% false and the test for empty true in the hope that this is most +% of the time reasonable. If not a package would need to guard +% against running in an old kernel. +% \begin{macrocode} +%<latexrelease>\def \IfHookExistTF #1#2#3{#3} +%<latexrelease>\def \IfHookEmptyTF #1#2#3{#2} +%<latexrelease> +%<latexrelease>\EndIncludeInRelease % \end{macrocode} % % -% \subsection{Core hook management code (kernel part)} -% -% This should run in older formats so we can't use -% \cs{IfFormatAtLeastTF} right now. -% \begin{macrocode} -\@ifl@t@r\fmtversion{2020/10/01} - {} - {\input{lthooks.ltx} - \input{ltshipout.ltx} - \input{ltfilehook.ltx} - } -% \end{macrocode} -% -% \subsection{Package options} -% -% For now we offer a simple debug option which turns on a lot of -% strange \cs{typeout} messages, nothing fancy. -% \begin{macrocode} -\ExplSyntaxOn -% \end{macrocode} -% -% \begin{macrocode} -\hook_debug_off: -\DeclareOption { debug } { \hook_debug_on: - \shipout_debug_on: } -% \end{macrocode} -% -% -% For now we offer a simple debug option which turns on a lot of -% strange \cs{typeout} messages, nothing fancy. -% \begin{macrocode} -\shipout_debug_off: -\DeclareOption { debug-shipout } { \shipout_debug_on: } -% \end{macrocode} -% -% \begin{macrocode} -\ProcessOptions -% \end{macrocode} -% -% -% \subsection{Temporarily patching package until changed} -% -% -% -% \pkg{filehook} support until that package is patched: -% \begin{macrocode} -\RequirePackage{filehook-ltx} -% \end{macrocode} -% -% -% -% \begin{macrocode} -%</package> -% \end{macrocode} -% -% -% % \Finale % - - +% +%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% +\endinput +%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% |