diff options
author | Norbert Preining <norbert@preining.info> | 2021-09-21 03:02:01 +0000 |
---|---|---|
committer | Norbert Preining <norbert@preining.info> | 2021-09-21 03:02:01 +0000 |
commit | 32e0f5d0a5abc4f2b6c7341f19b854b5945c3ebd (patch) | |
tree | d88327cf96992788ed82a343f071bbbf2ad4cc84 /macros/latex-dev/base/lthooks.dtx | |
parent | c9fcf97bc045a9d020980c33b6c7c1ee5aedef99 (diff) |
CTAN sync 202109210302
Diffstat (limited to 'macros/latex-dev/base/lthooks.dtx')
-rw-r--r-- | macros/latex-dev/base/lthooks.dtx | 1891 |
1 files changed, 1187 insertions, 704 deletions
diff --git a/macros/latex-dev/base/lthooks.dtx b/macros/latex-dev/base/lthooks.dtx index 20e172a57f..3969a2be56 100644 --- a/macros/latex-dev/base/lthooks.dtx +++ b/macros/latex-dev/base/lthooks.dtx @@ -31,8 +31,8 @@ %%% From File: lthooks.dtx % % \begin{macrocode} -\def\lthooksversion{v1.0n} -\def\lthooksdate{2021/05/26} +\def\lthooksversion{v1.0r} +\def\lthooksdate{2021/09/06} % \end{macrocode} % %<*driver> @@ -47,7 +47,7 @@ \vbox_unpack_drop:N \l__codedoc_macro_box \hbox { \llap { \__codedoc_print_macroname:nN {#1} #2 \MacroFont % <----- without it the \ is in lmr10 if a link is made - \ + \ } } } \int_incr:N \l__codedoc_macro_int @@ -74,9 +74,9 @@ % \providecommand\pho[1]{\marginpar{\footnotesize PhO: #1}} % \providecommand\phoinline[1]{\begin{quote}\itshape\footnotesize PhO: #1\end{quote}} % -% % -% \title{The \texttt{lthooks} package\thanks{This package has version +% +% \title{\LaTeX{}'s hook management\thanks{This module has version % \lthooksversion\ dated \lthooksdate, \copyright\ \LaTeX\ % Project.}} % @@ -127,22 +127,18 @@ % % \subsubsection{Declaring hooks} % -% With a few exceptions, hooks have to be declared before they can -% be used. The exceptions are the generic hooks for commands, -% environments (i.e., executed at \cs{begin} and \cs{end}) and -% hooks run when loading files, e.g. before and after a package is -% loaded, etc. Their hook names depend on the command, -% environment or the -% file name and so declaring them beforehand is not practical. -% +% With a few exceptions, hooks have to be declared before they can +% be used. The exceptions are the generic hooks for commands and +% environments (executed at \cs{begin} and \cs{end}), and the +% hooks run when loading files (see section~\ref{sec:generic}). % % \begin{function}{\NewHook} % \begin{syntax} % \cs{NewHook} \Arg{hook} % \end{syntax} % Creates a new \meta{hook}. -% If this is a hook provided as part of a package it is suggested -% that the \meta{hook} name is always structured as follows: +% If this hook is declared within a package it is suggested +% that its name is always structured as follows: % \meta{package-name}\texttt{/}\meta{hook-name}. If necessary you % can further subdivide the name by adding more \texttt{/} parts. % If a hook name is already taken, an error is raised and the hook @@ -180,17 +176,19 @@ % \end{function} % % -% \subsubsection{Special declarations for hooks} +% \subsubsection{Special declarations for generic hooks} % % The declarations here should normally not be used. They are available % to provide support for special use cases mainly involving % generic command hooks. % -% \begin{function}{\DisableHook} +% \changes{v1.0p}{2021/08/20}{Documentation updates for generic hook commands (gh/638)} +% +% \begin{function}{\DisableGenericHook} % \begin{syntax} -% \cs{DisableHook} \Arg{hook} +% \cs{DisableGenericHook} \Arg{hook} % \end{syntax} -% After this declaration the \meta{hook} is no longer usable: Any +% After this declaration\footnotemark{} the \meta{hook} is no longer usable: Any % attempt to add further code to it will result in an error and any % use, e.g., via \cs{UseHook}, will simply do nothing. % @@ -201,37 +199,27 @@ % % The \meta{hook} can be specified using the dot-syntax to denote % the current package name. See section~\ref{sec:default-label}. -% \end{function} +% \end{function}\footnotetext{In the 2020/06 release this command was +% called \cs{DisableHook}, but that name was misleading as it +% shouldn't be used to disable non-generic hooks.} % % -% \begin{function}{\ProvideHook} +% \begin{function}{\ActivateGenericHook} % \begin{syntax} -% \cs{ProvideHook} \Arg{hook} +% \cs{ActivateGenericHook} \Arg{hook} % \end{syntax} -% Like \cs{NewHook} but does nothing if the hook was previously -% declared with \cs{NewHook}. This declaration should only be used -% in special situations, e.g., when command of another package -% need to be altered and it is is not clear if for that command a -% generic hook was already explicitly declared before. -% -% Normally \cs{NewHook} should be used instead. +% This declaration activates a generic hook provided by a package/class +% (e.g., one used in code with \cs{UseHook} or +% \cs{UseOneTimeHook}) without it being explicitly declared with +% \cs{NewHook}). +% This command undoes the effect of \cs{DisableGenericHook}. +% If the hook is already activated, this command does nothing. +% +% See section~\ref{sec:generic-hooks} for a discussion of when this +% declaration is appropriate. % \end{function} % -% \begin{function}{\ProvideReversedHook} -% \begin{syntax} -% \cs{ProvideReversedHook} \Arg{hook} -% \end{syntax} -% Like \cs{NewReversedHook} but does nothing if the hook was -% previously declared as a reversed hook. -% \end{function} % -% \begin{function}{\ProvideMirroredHookPair} -% \begin{syntax} -% \cs{ProvideMirroredHookPair} \Arg{hook-1} \Arg{hook-2} -% \end{syntax} -% A shorthand for -% \cs{ProvideHook}\Arg{hook-1}\cs{ProvideReversedHook}\Arg{hook-2}. -% \end{function} % % % @@ -272,12 +260,20 @@ % consumed and any further attempt to add to it will result in % executing the code to be added immediately. % -% \fmiinline{Maybe add an error version as well?} -% % The \meta{hook} \emph{cannot} be specified using the dot-syntax. % A leading |.| is treated literally. See % section~\ref{sec:default-label} for details. - +% +% Using \cs{UseOneTimeHook} several times with the same +% \Arg{hook} means that it only executes the first time it is used. +% For example, if it is used in a command that can be called several times +% then the hook executes during only the \emph{first} invocation of that +% command; this allows its use as an \enquote{initialization hook}. +% +% Mixing \cs{UseHook} and \cs{UseOneTimeHook} for the same +% \Arg{hook} should be avoided, but if this is done then neither will execute +% after the first \cs{UseOneTimeHook}. +% % \end{function} % % @@ -321,15 +317,19 @@ % When the optional argument \meta{label} is not provided, the % \meta{default label} is used (see section~\ref{sec:default-label}). % -% If the code for that \meta{label} wasn't yet added to the -% \meta{hook}, an order is set so that when some code attempts to add -% that label, the removal order takes action and the code is not -% added. +% If there is no code under the \meta{label} in the \meta{hook}, +% or if the \meta{hook} does not exist, a warning is issued when +% you attempt to \cs{RemoveFromHook}, and the command is ignored. +% \cs{RemoveFromHook} should be used only when you know exactly what +% labels are in a hook. Typically this will be when some code gets added to a hook +% by a package, then later this code is removed by that same package. +% If you want to prevent the execution of code from another +% package, use the |voids| rule instead (see section~\ref{sec:rules}). % % If the optional \meta{label} argument is \texttt{*}, then all code chunks are -% removed. This is rather dangerous as it drops code from other -% packages one may not know about and should therefore not by used -% by packages but only in document preambles! +% removed. This is rather dangerous as it may well drop code from other +% packages (that one may not know about); it should therefore not be used +% in packages but only in document preambles! % % The \meta{hook} and \meta{label} can be specified using the % dot-syntax to denote the current package name. @@ -378,14 +378,28 @@ % The code is executed after the normal hook code has finished and % it is executed only once, i.e. it is deleted after it was used. % -% Using the declaration is a global operation, i.e., the code is -% not lost, even if the declaration is used inside a group and the -% next invocation happens after the group. If the declaration is +% Using this declaration is a global operation, i.e., the code is +% not lost even if the declaration is used inside a group and the +% next invocation of the hook happens after the end of that group. +% If the declaration is % used several times before the hook is executed then all code is % executed in the order in which it was declared.\footnotemark % -% It is possible to nest declarations using the same hook (or -% different hooks), e.g., +% If this declaration is used with a one-time hook then the code +% is only ever used if the declaration comes before the hook’s +% invocation. This is because, in contrast +% to \cs{AddToHook}, the code in this declaration is not +% executed immediately in the case when the invocation of the hook +% has already happened---in other words, this code will truly execute +% only on the next invocation of the hook (and in the case of a +% one-time hook there is no such \enquote{next invocation}). +% This gives you a choice: should my code execute +% always, or should it execute only at the point where the +% one-time hook is used (and not at all if this is impossible)? For +% both of these possibilities there are use cases. +% +% It is possible to nest this declaration 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=}= @@ -404,6 +418,25 @@ % \end{function}\footnotetext{There is % no mechanism to reorder such code chunks (or delete them).} % +% +% +% \begin{function}{\ClearHookNext} +% \begin{syntax} +% \cs{ClearHookNext}\Arg{hook} +% \end{syntax} +% Normally \cs{AddToHookNext} is only used when you know precisely +% where it will apply and why you want some extra code at that +% point. However, there are a few use cases in which such a +% declaration needs to be canceled, for example, when +% discarding a page with \cs{DiscardShipoutBox} (but even then not +% always), and in such situations \cs{ClearHookNext} can be +% used. +% \end{function} +% +% +% +% +% % \subsubsection{Hook names and default labels} % \label{sec:default-label} % @@ -424,7 +457,7 @@ % % Except for \cs{UseHook}, \cs{UseOneTimeHook} and \cs{IfHookEmptyTF} % (and their \pkg{expl3} interfaces \cs{hook_use:n}, -% \cs{hook_use_once:n} and \cs{hook_if_empty:nTF}, all \meta{hook} +% \cs{hook_use_once:n} and \cs{hook_if_empty:nTF}), all \meta{hook} % and \meta{label} arguments are processed in the same way: first, % spaces are trimmed around the argument, then it is fully expanded % until only character tokens remain. If the full expansion of the @@ -458,7 +491,7 @@ % \AddToHook {./hook}[.]{code} % Same as \AddToHook{./hook}{code} % \AddToHook {./hook}[./sub]{code} % \DeclareHookRule{begindocument}{.}{before}{babel} -% \AddToHook {file/after/foo.tex}{code} +% \AddToHook {file/foo.tex/after}{code} % \end{verbatim} % are equivalent to: % \begin{verbatim} @@ -466,7 +499,7 @@ % \AddToHook {mypackage/hook}[mypackage]{code} % \AddToHook {mypackage/hook}[mypackage/sub]{code} % \DeclareHookRule{begindocument}{mypackage}{before}{babel} -% \AddToHook {file/after/foo.tex}{code} % unchanged +% \AddToHook {file/foo.tex/after}{code} % unchanged % \end{verbatim} % % The \meta{default label} is automatically set equal to the name of the @@ -534,7 +567,7 @@ % Packages that provide their own package-like interfaces % (Ti\textit{k}Z's \cs{usetikzlibrary}, for example) can use % \cs{PushDefaultHookLabel} and \cs{PopDefaultHookLabel} to set -% dedicated labels and emulate \cs{usepackage}-like hook behaviour +% dedicated labels and to emulate \cs{usepackage}-like hook behavior % within those contexts. % % The |top-level| label is treated differently, and is reserved to the @@ -588,6 +621,7 @@ % with that label from a package results in an error. % % \subsubsection{Defining relations between hook code} +% \label{sec:rules} % % The default assumption is that code added to hooks by different % packages are independent and the order in which they are executed is @@ -712,8 +746,8 @@ % \item exist and be non-empty; and % \item not exist (in which case emptiness doesn't apply); % \end{itemize} -% Hooks are a bit more complicated: -% a hook may exist or not, and either way it may or may not be empty. +% Hooks are a bit more complicated: +% a hook may exist or not, and independently it may or may not be empty. % This means that even a hook that doesn't exist may be non-empty and % it can also be disabled. % @@ -854,7 +888,7 @@ % \end{quote} % which means that the \meta{relation} applies to \meta{label-1} and % \meta{label-2}, in that order, as detailed in \cs{DeclareHookRule}. -% If the relation is \texttt{default} it means that that rule applies +% If the relation is \texttt{default} it means that this rule applies % to \meta{label-1} and \meta{label-2} in \emph{all} hooks, (unless % overridden by a non-default relation). % @@ -905,9 +939,9 @@ % % % -% \begin{function}{\hook_disable:n} +% \begin{function}{\hook_disable_generic:n} % \begin{syntax} -% \cs{hook_disable:n} \Arg{hook} +% \cs{hook_disable_generic:n} \Arg{hook} % \end{syntax} % Marks \Arg{hook} as disabled. Any further attempt to add code to % it or declare it, will result in an error and any call to @@ -921,34 +955,19 @@ % the current package name. See section~\ref{sec:default-label}. % \end{function} % -% \begin{function}{\hook_provide:n} +% \begin{function}{\hook_activate_generic:n} % \begin{syntax} -% \cs{hook_provide:n} \Arg{hook} +% \cs{hook_activate_generic:n} \Arg{hook} % \end{syntax} % Like \cs{hook_new:n} but does nothing if the hook was previously % declared with \cs{hook_new:n}. This declaration should only be used % in special situations, e.g., when a command of another package -% needs to be altered and it is is not clear if for that command a +% needs to be altered and it is not clear if for that command a % generic \hook{cmd} hook was already explicitly declared before. % % Normally \cs{hook_new:n} should be used instead. % \end{function} % -% \begin{function}{\hook_provide_reversed:n} -% \begin{syntax} -% \cs{hook_provide_reversed:n} \Arg{hook} -% \end{syntax} -% Like \cs{hook_new_reversed:n} but does nothing if the hook was -% previously declared as a reversed hook. -% \end{function} -% -% \begin{function}{\hook_provide_pair:nn} -% \begin{syntax} -% \cs{hook_provide_pair:nn} \Arg{hook-1} \Arg{hook-2} -% \end{syntax} -% A shorthand for -% \cs{hook_provide:n}\Arg{hook-1}\cs{hook_provide_reversed:n}\Arg{hook-2}. -% \end{function} % % % @@ -1013,6 +1032,15 @@ % \end{function} % % +% \begin{function} +% {\hook_gclear_next_code:n} +% \begin{syntax} +% \cs{hook_gclear_next_code:n} \Arg{hook} +% \end{syntax} +% Undo any earlier \cs{hook_gput_next_code:nn}. +% \end{function} +% +% % % \begin{function}{\hook_gremove_code:nn} % \begin{syntax} @@ -1020,10 +1048,9 @@ % \end{syntax} % Removes any code for \meta{hook} labeled \meta{label}. % -% If the code for that \meta{label} wasn't yet added to the -% \meta{hook}, an order is set so that when some code attempts to add -% that label, the removal order takes action and the code is not -% added. +% If there is no code under the \meta{label} in the \meta{hook}, +% or if the \meta{hook} does not exist, a warning is issued when +% you attempt to use \cs{hook_gremove_code:nn}, and the command is ignored. % % If the second argument is \texttt{*}, then all code chunks are % removed. This is rather dangerous as it drops code from other @@ -1138,7 +1165,7 @@ % which key-value pairs got added. However, that is only true if % nothing other than adding happens! % -% Suppose, or example, you want to replace the code chunk for +% Suppose, for example, you want to replace the code chunk for % \texttt{packageA}, e.g., %\begin{verbatim} % \RemoveFromHook{myhook}[packageA] @@ -1297,7 +1324,7 @@ % \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 +% \verb=\UseHook{myhook}= is executed it would % \begin{itemize} % \item % execute \meta{code-1}, @@ -1315,6 +1342,70 @@ % % % +% \subsection{Generic hooks provided by packages} +% \label{sec:generic-hooks} +% +% \changes{v1.0p}{2021/08/20}{Section on generic hooks added (gh/638)} +% +% The hook management system also implements a category of hooks +% that are called \enquote{Generic Hooks}. Normally a hook has to +% be explicitly declared before it can be used in code. This +% ensures that different packages are not using the same hook name +% for unrelated purposes---something that would result in absolute +% chaos. However, there are a number of \enquote{standard} hooks +% where it is unreasonable to declare them beforehand, e.g, each +% and every command has (in theory) an associated \texttt{before} +% and \texttt{after} hook. In such cases, i.e., for command, +% environment or file hooks, they can be used simply by adding code +% to them with \cs{AddToHook}. For more specialized generic hooks, +% e.g., those provided by \pkg{babel}, you have to additionally +% enable them with \cs{ActivateGenericHook} as explained below. +% +% The generic hooks provided by \LaTeX{} are those for +% \hook{cmd}, +% \hook{env}, +% \hook{file}, +% \hook{include} +% \hook{package}, and +% \hook{class}, +% and all these are available out of the box: you only have to +% use \cs{AddToHook} to +% add code to them, but you don't have to add \cs{UseHook} or +% \cs{UseOneTimeHook} to your code, because this is already done for +% you (or, in the case of \hook{cmd} hooks, the command’s code is +% patched at \verb=\begin{document}=, if necessary). +% +% However, if you want to provide further generic hooks in your own +% code, the situation is slightly different. To do this you should +% use \cs{UseHook} or \cs{UseOneTimeHook}, but +% \emph{without declaring the hook} with \cs{NewHook}. As +% mentioned earlier, a call to \cs{UseHook} with an undeclared hook +% name does nothing. So as an additional setup step, you need to +% explicitly activate your generic hook. Note that a generic hook +% produced in this way is always a normal hook. +% +% For a truly generic hook, with a variable part in the hook name, +% such upfront activation would be difficult or impossible, because +% you typically do not know what kind of variable parts may come up +% in real documents. +% +% For example, \pkg{babel} may want to provide hooks such as +% \hook{babel/afterextras/\meta{language}}. Language support in +% \pkg{babel} is often done through external language +% packages. Thus doing the activation for all languages inside the +% core \pkg{babel} code is not a viable approach. Instead it needs +% to be done by each language package (or by the user who wants to +% use a particular hook). +% +% Because the hooks are not declared with \cs{NewHook} their names +% should be carefully chosen to ensure that they are (likely to be) +% unique. Best practice is to include the package or +% command name, as was done in the \pkg{babel} example above. +% +% Generic hooks defined in this way are always normal hooks (i.e., +% you can't implement reversed hooks this way). This is a +% deliberate limitation, because it speeds up the processessing +% conciderably. % % % \subsection{Private \LaTeX{} kernel hooks} @@ -1354,8 +1445,8 @@ % % \subsection{Legacy \LaTeXe{} interfaces} % -% \newcommand\onetimetext{% -% This is a one-time hook, so after it is executed, all further +% \newcommand\onetimetext{This is a one-time hook, so after it +% is executed, all further % attempts to add code to it will execute such code immediately % (see section~\ref{sec:onetime-hooks}).} % @@ -1363,7 +1454,7 @@ % add to them. They are listed here and are retained for backwards % compatibility. % -% With the new hook management several additional hooks have been added +% With the new hook management, several additional hooks have been added % to \LaTeX\ and more will follow. See the next section for what % is already available. % @@ -1374,7 +1465,7 @@ % \end{syntax} % If used without the optional argument \meta{label}, it works essentially % like before, i.e., it is adding \meta{code} to the hook -% \hook{begindocument} +% \hook{begindocument} % (which is executed inside \verb=\begin{document}=). % However, all code added this way is labeled with the label % \hook{top-level} (see section~\ref{sec:top-level}) @@ -1430,10 +1521,54 @@ % the processing. % % -% \subsection{\LaTeXe{} commands and environments augmented by -% hooks} -% -% \emph{intro to be written} +% \section{\LaTeXe{} commands and environments augmented by hooks} +% +% In this section we describe the standard hooks that are now +% offered by \LaTeX{}, or give pointers to other documents in which +% they are described. This section will grow over time (and +% perhaps eventually move to usrguide3). +% +% \subsection{Generic hooks} +% \label{sec:generic} +% +% As stated earlier, with the exception of generic hooks, all hooks must +% be declared with \cs{NewHook} before they can be used. +% All generic hooks have names of the form +% \enquote{\meta{type}/\meta{name}/\meta{position}}, where \meta{type} +% is from the predefined list shown below, and \meta{name} is the variable +% part whose meaning will depend on the \meta{type}. The last component, +% \meta{position}, has more complex possibilities: +% it can always be |before| or |after|; for |env| hooks, it can also be |begin| +% or |end|; and for |include| hooks it can also be |end|. Each specific +% hook is documented below, or in \texttt{ltcmdhooks-doc.pdf} or +% \texttt{ltfilehook-doc.pdf}. +% +% The generic hooks provided by \LaTeX{} belong to one of the six types: +% \begin{description} +% \item[env] Hooks executed before and after environments -- +% \meta{name} is the name of the environment, and available values +% for \meta{position} are |before|, |begin|, |end|, and |after|; +% \item[cmd] Hooks added to and executed before and after commands -- +% \meta{name} is the name of the command, and available values +% for \meta{position} are |before| and |after|; +% \item[file] Hooks executed before and after reading a file -- +% \meta{name} is the name of the file (with extension), and +% available values for \meta{position} are |before| and |after|; +% \item[package] Hooks executed before and after loading packages -- +% \meta{name} is the name of the package, and available values for +% \meta{position} are |before| and |after|; +% \item[class] Hooks executed before and after loading classes -- +% \meta{name} is the name of the class, and available values for +% \meta{position} are |before| and |after|; +% \item[include] Hooks executed before and after \cs{include}d files -- +% \meta{name} is the name of the included file (without the |.tex| +% extension), and available values for \meta{position} are |before|, +% |end|, and |after|. +% \end{description} +% +% Each of the hooks above are detailed in the following sections and +% in linked documentation. +% ^^A^^A^^A \pho{Wouldn't it be better to document all hooks here?} % % \subsubsection{Generic hooks for all environments} % @@ -1495,9 +1630,15 @@ % to add the outer hooks, etc. % % +% Largely for compatibility with existing packages, the following +% four commands are also available to set the environment hooks; but for +% new packages we recommend directly using the hook names and +% \cs{AddToHook}. +% +% % \begin{function}{\BeforeBeginEnvironment} % \begin{syntax} -% \cs{BeforeBeginEnvironment} \oarg{label} \Arg{code} +% \cs{BeforeBeginEnvironment} \oarg{label} \Arg{env} \Arg{code} % \end{syntax} % This declaration adds to the \hook{env/\meta{env}/before} hook % using the \meta{label}. If \meta{label} is not given, the @@ -1506,23 +1647,23 @@ % % \begin{function}{\AtBeginEnvironment} % \begin{syntax} -% \cs{AtBeginEnvironment} \oarg{label} \Arg{code} +% \cs{AtBeginEnvironment} \oarg{label} \Arg{env} \Arg{code} % \end{syntax} -% Like \cs{BeforeBeginEnvironment} but adds to the \hook{env/\meta{env}/begin} hook. +% This is like \cs{BeforeBeginEnvironment} but it adds to the \hook{env/\meta{env}/begin} hook. % \end{function} % % \begin{function}{\AtEndEnvironment} % \begin{syntax} -% \cs{AtEndEnvironment} \oarg{label} \Arg{code} +% \cs{AtEndEnvironment} \oarg{label} \Arg{env} \Arg{code} % \end{syntax} -% Like \cs{BeforeBeginEnvironment} but adds to the \hook{env/\meta{env}/end} hook. +% This is like \cs{BeforeBeginEnvironment} but it adds to the \hook{env/\meta{env}/end} hook. % \end{function} % % \begin{function}{\AfterEndEnvironment} % \begin{syntax} -% \cs{AfterEndEnvironment} \oarg{label} \Arg{code} +% \cs{AfterEndEnvironment} \oarg{label} \Arg{env} \Arg{code} % \end{syntax} -% Like \cs{BeforeBeginEnvironment} but adds to the \hook{env/\meta{env}/after} hook. +% This is like \cs{BeforeBeginEnvironment} but it adds to the \hook{env/\meta{env}/after} hook. % \end{function} % % @@ -1559,7 +1700,7 @@ % % % -% \subsubsection{Hooks provided by \cs{begin}\texttt{\{document\}}} +% \subsection{Hooks provided by \cs{begin}\texttt{\{document\}}} % \label{sec:begindocument-hooks} % % Until 2020 \cs{begin}\texttt{\{document\}} offered exactly one @@ -1618,7 +1759,7 @@ % % % -% \subsubsection{Hooks provided by \cs{end}\texttt{\{document\}}} +% \subsection{Hooks provided by \cs{end}\texttt{\{document\}}} % % \LaTeXe{} always provided \cs{AtEndDocument} to add code to the % execution of \verb=\end{document}= just in front of the code that @@ -1726,7 +1867,7 @@ % % % -% \subsubsection{Hooks provided by \cs{shipout} operations} +% \subsection{Hooks provided by \cs{shipout} operations} % \label{sec:shipout} % % There are several hooks and mechanisms added to \LaTeX{}'s @@ -1735,36 +1876,44 @@ % \texttt{ltshipout-code.pdf}. % % +% \subsection{Hooks provided for paragraphs} +% \label{sec:para} +% +% The paragraph processing has been augmented to include a number of +% internal and public hooks. These are documented in +% \texttt{ltpara-doc.pdf} or with code in +% \texttt{ltpara-code.pdf}. +% +% % -% \subsubsection{Hooks provided in NFSS commands} +% \subsection{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 +% parallel (and thus several sets of fonts, e.g., supporting both 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. +% To support this, several NFSS commands have hooks to 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}. +% font series update, this hook is executed before \cs{selectfont}. % % \item[\hook{sffamily}] % -% Like the \hook{rmfamily} hook but for the \cs{sffamily} command. +% This is 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. +% This is like the \hook{rmfamily} hook, but for the \cs{ttfamily} command. % % \item[\hook{normalfont}] % -% The \cs{normalfont} command resets font encoding family series +% The \cs{normalfont} command resets the font encoding, family, series % and shape to their document defaults. It then executes this % hook and finally calls \cs{selectfont}. % @@ -1779,21 +1928,36 @@ % % \item[\hook{bfseries/defaults}, \hook{bfseries}] % -% If the \cs{bfdefault} was explicitly changed by the user its +% 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 +% families (rm/sf/tt) when \cs{bfseries} is called. The +% \hook{bfseries/defaults} hook allows further adjustments to be made % in this case. This hook is only executed if such a change is -% detected. In contrast the \hook{bfseries} hook is always +% 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 +% These two hooks are like the previous ones but they are in the % \cs{mdseries} command. % +% \item[\hook{selectfont}] +% +% This hook is executed inside \cs{selectfont}, after the current +% values for \textit{encoding}, \textit{family}, \textit{series}, +% \textit{shape}, and \textit{size} are evaluated and the new font +% is selected (and if necessary loaded). After the hook has +% executed, NFSS will still do any updates necessary for a new +% \textit{size} (such as changing the size of \cs{strut}) and any +% updates necessary to a change in \textit{encoding}. +% +% This hook is intended for use cases where, in parallel to a +% change in the main font, some other fonts need to be altered +% (e.g., in CJK processing where you may need to deal with several +% different alphabets). +% % \end{description} % % @@ -1859,7 +2023,7 @@ % % \begin{macro}[EXP]{\@@_str_compare:nn} % Private copy of \cs{__str_if_eq:nn} -% \InternalDetectionOff +% \InternalDetectionOff % \begin{macrocode} \cs_new_eq:NN \@@_str_compare:nn \__str_if_eq:nn % \end{macrocode} @@ -1892,13 +2056,6 @@ % \end{macrocode} % \end{macro} % -% \begin{macro}{\g_@@_removal_list_prop} -% A token list to hold delayed removals. -% \begin{macrocode} -\tl_new:N \g_@@_removal_list_tl -% \end{macrocode} -% \end{macro} -% % \begin{macro}{\l_@@_cur_hook_tl} % Stores the name of the hook currently being sorted. % \begin{macrocode} @@ -1916,13 +2073,6 @@ % \end{macrocode} % \end{macro} % -% \begin{macro}{\g_@@_execute_immediately_prop} -% List of hooks that from no on should not longer receive code. -% \begin{macrocode} -\prop_new:N \g_@@_execute_immediately_prop -% \end{macrocode} -% \end{macro} -% % \begin{macro}{\g_@@_used_prop} % All hooks that receive code (for use in debugging display). % \begin{macrocode} @@ -1963,6 +2113,13 @@ % \end{macrocode} % \end{macro} % +% \begin{macro}{\@@_clean_to_scan:w} +% Removes tokens until the next \cs{s_@@_mark}. +% \begin{macrocode} +\cs_new:Npn \@@_clean_to_scan:w #1 \s_@@_mark { } +% \end{macrocode} +% \end{macro} +% % \begin{macro}{\@@_tl_set:Nn,\@@_tl_set:Nx, % \@@_tl_set:cn,\@@_tl_set:cx} % Private copies of a few \pkg{expl3} functions. \pkg{l3debug} will @@ -2055,7 +2212,7 @@ % \item[\cs{g_@@_\meta{hook}_code_prop}] A property list holding the code % for the hook in separate chunks. The keys are by default the % package names that add code to the hook, but it is possible -% for packages to define other keys. +% for packages to define other keys. % % \item[{\cs[no-index]{g_@@_\meta{hook}_rule_\meta{label1}\string|\meta{label2}_tl}}] % A token list holding the relation between \meta{label1} and @@ -2108,19 +2265,19 @@ % \label{sec:existence} % % A hook may be in different states of existence. Here we give an -% overview of internal commands to set up hooks and explain how the +% overview of the internal commands to set up hooks and explain how the % different states are distinguished. The actual implementation -% then follows in the next sections. +% then follows in subsequent sections. % -% One problem we have to solve is, that we need to be able to add +% One problem we have to solve is that we need to be able to add % code to hooks (e.g., with \cs{AddToHook}) even if that code has -% not been declared yet. For example, one package needs to write -% into a hook of another package, but that package ay not get -% loaded or only loaded later. Another problem most hooks require -% declaration but this is not the case for the generic hooks. +% not yet been declared. For example, one package needs to write +% into a hook of another package, but that package may not get +% loaded, or is loaded only later. Another problem is that most hooks, +% but not the generic hooks, require a declaration. % -% We therefore distinguish the following states for a hook and they -% are managed with four different tests: structure existence +% We therefore distinguish the following states for a hook, which +% are managed by four different tests: structure existence % (\cs{@@_if_structure_exist:nTF}), creation % (\cs{@@_if_usable:nTF}), declaration (\cs{@@_if_declared:nTF}) % and disabled or not (\cs{@@_if_disabled:nTF}) @@ -2151,12 +2308,12 @@ % \item [@@_if_disabled:nTF] returns |false|. % \end{itemize} % -% The allowed acctions are the same as in the \enquote{not +% The allowed actions are the same as in the \enquote{not % existing} state. % % \item[declared] % -% A hook is in this state it is not disabled and was explicity declared (e.g., +% A hook is in this state it is not disabled and was explicitly declared (e.g., % with \cs{NewHook}). In this case the four tests give the % following results: % \begin{itemize} @@ -2187,8 +2344,8 @@ % % \item[disabled] % -% A hook in any state is moved to this state when -% \cs{DisableHook} is used. This changes the tests to give the +% A generic hook in any state is moved to this state when +% \cs{DisableGenericHook} is used. This changes the tests to give the % following results: % \begin{itemize} % \item [@@_if_structure_exist:nTF] \emph{unchanged}. @@ -2197,7 +2354,7 @@ % \item [@@_if_disabled:nTF] returns |true|. % \end{itemize} % The structure test is unchanged (if the hook was unknown before it is -% false, otherwise true). The usable test returns false so that +% |false|, otherwise |true|). The usable test returns |false| so that % any \cs{UseHook} will bypass the hook from now on. The % declared test returns true so that any further \cs{NewHook} % generates an error and the disabled test returns true so that @@ -2240,8 +2397,8 @@ % \end{macrocode} % \end{macro} % -% -% +% +% % \begin{macro}{\@@_make_usable:n} % % This initializes all hook data structures for the hook but if @@ -2315,7 +2472,7 @@ % (\cs[no-index]{g_@@_\meta{hook}_code_prop}) and the |top-level| % and |next| token lists. A hook is initialized with % \cs{@@_init_structure:n} the first time anything is added to it. -% Inizializing a hook just with \cs{@@_init_structure:n} will not +% Initializing a hook just with \cs{@@_init_structure:n} will not % make it usable with \cs{hook_use:n}. % \begin{macrocode} \cs_new_protected:Npn \@@_init_structure:n #1 @@ -2407,7 +2564,9 @@ % % \subsubsection{Disabling and providing hooks} % -% \begin{macro}{\hook_disable:n} +% \changes{v1.0p}{2021/08/20}{Renames of generic hook commands (gh/638)} +% +% \begin{macro}{\hook_disable_generic:n} % \begin{macro}{\@@_disable:n} % \begin{macro}[pTF]{\@@_if_disabled:n} % @@ -2423,11 +2582,11 @@ % % \begin{macrocode} %<latexrelease>\IncludeInRelease{2021/06/01}% -%<latexrelease> {\hook_disable:n}{Disable~hooks} +%<latexrelease> {\hook_disable_generic:n}{Disable~hooks} % \end{macrocode} -% +% % \begin{macrocode} -\cs_new_protected:Npn \hook_disable:n #1 +\cs_new_protected:Npn \hook_disable_generic:n #1 { \@@_normalize_hook_args:Nn \@@_disable:n {#1} } \cs_new_protected:Npn \@@_disable:n #1 { @@ -2447,9 +2606,9 @@ % % \begin{macrocode} %<latexrelease>\IncludeInRelease{2020/10/01} -%<latexrelease> {\hook_disable:n}{Disable~hooks} +%<latexrelease> {\hook_disable_generic:n}{Disable~hooks} %<latexrelease> -%<latexrelease>\cs_new_protected:Npn \hook_disable:n #1 {} +%<latexrelease>\cs_new_protected:Npn \hook_disable_generic:n #1 {} %<latexrelease> %<latexrelease>\EndIncludeInRelease % \end{macrocode} @@ -2457,71 +2616,47 @@ % \end{macro} % \end{macro} % -% \begin{macro}{\hook_provide:n} -% \begin{macro}{\hook_provide_reversed:n} -% \begin{macro}{\@@_provide:n} -% The \cs{hook_provide:n} declaration declares a new hook if it +% \begin{macro}{\hook_activate_generic:n} +% \begin{macro}{\@@_activate_generic:n} +% The \cs{hook_activate_generic:n} declaration declares a new hook if it % wasn't declared already, in which case it only checks that the % already existing hook is not a reversed hook. -% The \cs{hook_provide_reversed:n} does the same for reversed hooks. -% \hook{begindocument}. % \begin{macrocode} %<latexrelease>\IncludeInRelease{2021/06/01}% -%<latexrelease> {\hook_provide:n}{Providing~hooks} +%<latexrelease> {\hook_activate_generic:n}{Providing~hooks} % \end{macrocode} % % \begin{macrocode} -\cs_new_protected:Npn \hook_provide:n #1 - { \@@_normalize_hook_args:Nn \@@_provide:nn {#1} { } } -\cs_new_protected:Npn \hook_provide_reversed:n #1 - { \@@_normalize_hook_args:Nn \@@_provide:nn {#1} { - } } +\cs_new_protected:Npn \hook_activate_generic:n #1 + { \@@_normalize_hook_args:Nn \@@_activate_generic:nn {#1} { } } % \end{macrocode} % % \begin{macrocode} -\cs_new_protected:Npn \@@_provide:nn #1 #2 +\cs_new_protected:Npn \@@_activate_generic:nn #1 #2 { % \end{macrocode} -% If the hook to be provided was disabled we warn (for now --- this +% If the hook to be activated was disabled we warn (for now --- this % may change). % \begin{macrocode} \@@_if_disabled:nTF {#1} - { \msg_warning:nnn { hooks } { provide-disabled } {#1} } + { \msg_warning:nnn { hooks } { activate-disabled } {#1} } % \end{macrocode} -% Otherwise we check if it was already declared. +% Otherwise we check if the hook is not declared, and if it isn't, +% figure out if it's reversed or not, then declare it accordingly. % \begin{macrocode} { - \@@_if_declared:nTF {#1} - { -% \end{macrocode} -% Issue an error if we try to provide a a hook that is reversed and -% the already existing one is not (or vice versa). -% \begin{macrocode} - \str_if_eq:eeF { \tl_use:c { g_@@_#1_reversed_tl } } {#2} - { \msg_error:nnn { hooks } { provide-error } {#1} } - } -% \end{macrocode} -% If it wasn't declared, we declared as a normal or reversed hook -% as appropriate. -% \begin{macrocode} + \@@_if_declared:nF {#1} { \tl_new:c { g_@@_#1_declared_tl } \@@_make_usable:n {#1} - \tl_gset:cn { g_@@_#1_reversed_tl } {#2} + \tl_gset:cx { g_@@_#1_reversed_tl } + { \@@_if_generic_reversed:nT {#1} { - } } } } } % \end{macrocode} % \end{macro} % \end{macro} -% \end{macro} -% -% \begin{macro}{\hook_provide_pair:nn} -% A shorthand for providing a normal and a (matching) reversed hook in one go. -% \begin{macrocode} -\cs_new_protected:Npn \hook_provide_pair:nn #1#2 - { \hook_provide:n {#1} \hook_provide_reversed:n {#2} } -% \end{macrocode} -% \end{macro} % % \begin{macrocode} %<latexrelease>\EndIncludeInRelease @@ -2529,11 +2664,9 @@ % % \begin{macrocode} %<latexrelease>\IncludeInRelease{2020/10/01} -%<latexrelease> {\hook_provide:n}{Providing~hooks} +%<latexrelease> {\hook_activate_generic:n}{Providing~hooks} %<latexrelease> -%<latexrelease>\cs_new_protected:Npn \hook_provide_reversed:n #1 {} -%<latexrelease>\cs_new_protected:Npn \hook_provide:n #1 {} -%<latexrelease>\cs_new_protected:Npn \hook_provide_pair:nn #1#2 {} +%<latexrelease>\cs_new_protected:Npn \hook_activate_generic:n #1 {} %<latexrelease> %<latexrelease>\EndIncludeInRelease % \end{macrocode} @@ -2600,8 +2733,8 @@ % \end{macro} % % \begin{macro}[EXP]{\@@_currname_or_default:} -% Uses \cs{g_@@_hook_curr_name_tl} if it is set, otherwise tries -% \cs{@currname}. If neither is set, raises an error and uses the +% This uses \cs{g_@@_hook_curr_name_tl} if it is set, otherwise it tries +% \cs{@currname}. If neither is set, it raises an error and uses the % fallback value \verb|label-missing|. % \begin{macrocode} \cs_new:Npn \@@_currname_or_default: @@ -2610,7 +2743,7 @@ { \tl_if_empty:NTF \@currname { - \msg_expandable_error:nnn { kernel } { should-not-happen } + \msg_expandable_error:nnn { latex2e } { should-not-happen } { Empty~default~label. } \@@_make_name:n { label-missing } } @@ -2622,7 +2755,7 @@ % \end{macro} % % \begin{macro}[EXP]{\@@_make_name:n,\@@_make_name:w} -% Provides a standard sanitization of a hook's name. +% This provides a standard sanitization of a hook's name. % It uses \cs{cs:w} to build a control sequence out of the hook name, % then uses \cs{cs_to_str:N} to get the string representation of that, % without the escape character. \cs{cs:w}-based expansion is used @@ -2646,7 +2779,7 @@ % \begin{macro}{\@@_normalize_hook_args:Nnn} % \begin{macro}{\@@_normalize_hook_rule_args:Nnnnn} % \begin{macro}{\@@_normalize_hook_args_aux:Nn} -% Standard route for normalising hook and label arguments. The main +% This is the standard route for normalizing hook and label arguments. The main % macro does the entire operation within a group so that csnames made % by \cs{@@_make_name:n} are wiped off before continuing. This means % that this function cannot be used for \cs{hook_use:n}! @@ -2689,16 +2822,151 @@ % \end{macro} % \end{macro} % +% \begin{macro}{\@@_curr_name_push:n,\@@_curr_name_push_aux:n} +% \begin{macro}{\@@_curr_name_pop:} +% \begin{macro}{\@@_end_document_label_check:} +% The token list \cs{g_@@_hook_curr_name_tl} stores the name of the +% current package/file to be used as the default label in hooks. +% Providing a consistent interface is tricky because packages can +% be loaded within packages, and some packages may not use +% \cs{SetDefaultHookLabel} to change the default label (in which +% case \cs{@currname} is used). +% +% To pull that one off, we keep a stack that contains the default +% label for each level of input. The bottom of the stack contains the +% default label for the |top-level| (this stack should never go +% empty). If we're building the format, set the default label to be +% |top-level|: +% \begin{macrocode} +\tl_gset:Nn \g_@@_hook_curr_name_tl { top-level } +% \end{macrocode} +% +% Then, in case we're in \pkg{latexrelease} we push something on +% the stack to support roll forward. But in some rare cases, +% \pkg{latexrelease} may be loaded inside another package (notably +% \pkg{platexrelease}), so we'll first push the |top-level| entry: +% \changes{v1.0i}{2021/03/18} +% {Only add \texttt{top-level} if not already there.} +% \begin{macrocode} +%<latexrelease>\seq_if_empty:NT \g_@@_name_stack_seq +%<latexrelease> { \seq_gput_right:Nn \g_@@_name_stack_seq { top-level } } +% \end{macrocode} +% then we dissect the \cs{@currnamestack}, adding \cs{@currname} to +% the stack: +% \changes{v1.0f}{2020/11/24}{Support for roll forward (gh/434)} +% \begin{macrocode} +%<latexrelease>\cs_set_protected:Npn \@@_tmp:w #1 #2 #3 +%<latexrelease> { +%<latexrelease> \quark_if_recursion_tail_stop:n {#1} +%<latexrelease> \seq_gput_right:Nn \g_@@_name_stack_seq {#1} +%<latexrelease> \@@_tmp:w +%<latexrelease> } +%<latexrelease>\exp_after:wN \@@_tmp:w \@currnamestack +%<latexrelease> \q_recursion_tail \q_recursion_tail +%<latexrelease> \q_recursion_tail \q_recursion_stop +% \end{macrocode} +% and finally set the default label to be the \cs{@currname}: +% \changes{v1.0i}{2021/03/18} +% {Remove the (empty) \enquote{top-level} from \cs{@currnamestack}.} +% \begin{macrocode} +%<latexrelease>\tl_gset:Nx \g_@@_hook_curr_name_tl { \@currname } +%<latexrelease>\seq_gpop_right:NN \g_@@_name_stack_seq \l_@@_tmpa_tl +% \end{macrocode} +% +% Two commands keep track of the stack: when a file is input, +% \cs{@@_curr_name_push:n} pushes the current default label onto the +% stack and sets the new default label (all in one go): +% \begin{macrocode} +\cs_new_protected:Npn \@@_curr_name_push:n #1 + { \exp_args:Nx \@@_curr_name_push_aux:n { \@@_make_name:n {#1} } } +\cs_new_protected:Npn \@@_curr_name_push_aux:n #1 + { + \tl_if_blank:nTF {#1} + { \msg_error:nn { hooks } { no-default-label } } + { + \str_if_eq:nnTF {#1} { top-level } + { + \msg_error:nnnnn { hooks } { set-top-level } + { to } { PushDefaultHookLabel } {#1} + } + { + \seq_gpush:NV \g_@@_name_stack_seq \g_@@_hook_curr_name_tl + \tl_gset:Nn \g_@@_hook_curr_name_tl {#1} + } + } + } +% \end{macrocode} +% and when an input is over, the topmost item of the stack is popped, +% since that label will not be used again, and \cs{g_@@_hook_curr_name_tl} +% is updated to equal the now topmost item of the stack: +% \begin{macrocode} +\cs_new_protected:Npn \@@_curr_name_pop: + { + \seq_gpop:NNTF \g_@@_name_stack_seq \l_@@_return_tl + { \tl_gset_eq:NN \g_@@_hook_curr_name_tl \l_@@_return_tl } + { \msg_error:nn { hooks } { extra-pop-label } } + } +% \end{macrocode} +% +% At the end of the document we want to check if there was no +% \cs{@@_curr_name_push:n} without a matching \cs{@@_curr_name_pop:} +% (not a critical error, but it might indicate that something else is +% not quite right): +% \begin{macrocode} +\tl_gput_right:Nn \@kernel@after@enddocument@afterlastpage + { \@@_end_document_label_check: } +\cs_new_protected:Npn \@@_end_document_label_check: + { + \seq_gpop:NNT \g_@@_name_stack_seq \l_@@_return_tl + { + \msg_error:nnx { hooks } { missing-pop-label } + { \g_@@_hook_curr_name_tl } + \tl_gset_eq:NN \g_@@_hook_curr_name_tl \l_@@_return_tl + \@@_end_document_label_check: + } + } +% \end{macrocode} +% +% The token list \cs{g_@@_hook_curr_name_tl} is but a mirror of the +% top of the stack. % +% \begin{macro}{\@@_set_default_hook_label:n,\@@_set_default_label:n} +% Now define a wrapper that replaces the top of the stack with the +% argument, and updates \cs{g_@@_hook_curr_name_tl} accordingly. +% \begin{macrocode} +\cs_new_protected:Npn \@@_set_default_hook_label:n #1 + { + \seq_if_empty:NTF \g_@@_name_stack_seq + { + \msg_error:nnnnn { hooks } { set-top-level } + { for } { SetDefaultHookLabel } {#1} + } + { \exp_args:Nx \@@_set_default_label:n { \@@_make_name:n {#1} } } + } +\cs_new_protected:Npn \@@_set_default_label:n #1 + { + \str_if_eq:nnTF {#1} { top-level } + { + \msg_error:nnnnn { hooks } { set-top-level } + { to } { SetDefaultHookLabel } {#1} + } + { \tl_gset:Nn \g_@@_hook_curr_name_tl {#1} } + } +% \end{macrocode} +% \end{macro} +% \end{macro} +% \end{macro} +% \end{macro} % % \subsection{Adding or removing hook code} % % \begin{macro}{\hook_gput_code:nnn} -% \begin{macro}{\@@_gput_code:nnn,\@@_gput_code:nxv,\@@_hook_gput_code_do:nnn} +% \begin{macro}{\@@_gput_code:nnn,\@@_hook_gput_code_do:nnn} % % With \cs{hook_gput_code:nnn}\Arg{hook}\Arg{label}\Arg{code} a % chunk of \meta{code} is added to an existing \meta{hook} labeled % with \meta{label}. +% \changes{v1.0o}{2021/07/22}{Do not queue removals (gh/625)} % \begin{macrocode} \cs_new_protected:Npn \hook_gput_code:nnn #1 #2 { \@@_normalize_hook_args:Nnn \@@_gput_code:nnn {#1} {#2} } @@ -2708,61 +2976,46 @@ \cs_new_protected:Npn \@@_gput_code:nnn #1 #2 #3 { % \end{macrocode} -% First check if the hook was used as a one-time hook: +% First check if the code should be executed immediately, rather than +% stored: +% \changes{v1.0r}{2021/09/06}{Use dedicated conditional (gh/606)} % \begin{macrocode} - \prop_if_in:NnTF \g_@@_execute_immediately_prop {#1} + \@@_if_execute_immediately:nTF {#1} {#3} { % \end{macrocode} -% Then 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} -% If no removal is queued, we are free to add. Start by checking if -% the hook exists. +% Then check if the hook is usable. % \begin{macrocode} - \@@_if_usable:nTF {#1} + \@@_if_usable:nTF {#1} % \end{macrocode} % If so we simply add (or append) the new code to the property list % holding different chunks for the hook. At \verb=\begin{document}= % this is then sorted into a token list for fast execution. % \begin{macrocode} - { - \@@_hook_gput_code_do:nnn {#1} {#2} {#3} + { + \@@_hook_gput_code_do:nnn {#1} {#2} {#3} % \end{macrocode} % However, if there is an update within the document we need to alter % this execution code which is done by % \cs{@@_update_hook_code:n}. In the preamble this does nothing. % \begin{macrocode} - \@@_update_hook_code:n {#1} - } + \@@_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. +% If the hook is not usable, before giving up, check if it's not +% disabled and otherwise try to declare it as a generic hook, if its +% name matches one of the valid patterns. % \begin{macrocode} - { - \@@_if_disabled:nTF {#1} - { \msg_error:nnn { hooks } { hook-disabled } {#1} } - { \@@_try_declaring_generic_hook:nnn {#1} {#2} {#3} } - } + { + \@@_if_disabled:nTF {#1} + { \msg_error:nnn { hooks } { hook-disabled } {#1} } + { \@@_try_declaring_generic_hook:nnn {#1} {#2} {#3} } } } } % \end{macrocode} % -% \begin{macrocode} -\cs_generate_variant:Nn \@@_gput_code:nnn { nxv } -% \end{macrocode} -% % 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 @@ -2839,21 +3092,40 @@ % next-execution hooks does the same: it defers the code to % \cs{hook_gput_next_code:nn} if the generic hook was declared, or % to \cs{@@_gput_next_do:nn} otherwise. +% \changes{v1.0p}{2021/08/25}{Standardise generic hook names (gh/648)} % \begin{macrocode} +%<latexrelease>\IncludeInRelease{2021/11/15}{\@@_try_declaring_generic_hook:nnn} +%<latexrelease> {Standardise~generic~hook~names} \cs_new_protected:Npn \@@_try_declaring_generic_hook:nnn #1 { - \@@_try_declaring_generic_hook:nNNnn {#1} - \hook_gput_code:nnn \@@_gput_undeclared_hook:nnn + \@@_try_declaring_generic_hook:wnTF #1 / / / \scan_stop: {#1} + \hook_gput_code:nnn + \@@_gput_undeclared_hook:nnn + {#1} } \cs_new_protected:Npn \@@_try_declaring_generic_next_hook:nn #1 { - \@@_try_declaring_generic_hook:nNNnn {#1} - \hook_gput_next_code:nn \@@_gput_next_do:nn + \@@_try_declaring_generic_hook:wnTF #1 / / / \scan_stop: {#1} + \hook_gput_next_code:nn + \@@_gput_next_do:nn + {#1} } +%<latexrelease>\EndIncludeInRelease +%<latexrelease>\IncludeInRelease{2020/10/01}{\@@_try_declaring_generic_hook:nnn} +%<latexrelease> {Standardise~generic~hook~names} +%<latexrelease>\cs_new_protected:Npn \@@_try_declaring_generic_hook:nnn #1 +%<latexrelease> { +%<latexrelease> \@@_try_declaring_generic_hook:nNNnn {#1} +%<latexrelease> \hook_gput_code:nnn \@@_gput_undeclared_hook:nnn +%<latexrelease> } +%<latexrelease>\cs_new_protected:Npn \@@_try_declaring_generic_next_hook:nn #1 +%<latexrelease> { +%<latexrelease> \@@_try_declaring_generic_hook:nNNnn {#1} +%<latexrelease> \hook_gput_next_code:nn \@@_gput_next_do:nn +%<latexrelease> } % \end{macrocode} % \end{macro} % -% % \begin{macro}{\@@_try_declaring_generic_hook:nNNnn, % \@@_try_declaring_generic_hook_split:nNNnn} % @@ -2867,43 +3139,39 @@ % the function returns \meta{true} for a generic hook and \meta{false} % in other cases. % \begin{macrocode} -\cs_new_protected:Npn \@@_try_declaring_generic_hook:nNNnn #1 - { - \@@_if_file_hook:wTF #1 / / \s_@@_mark - { - \exp_args:Ne \@@_try_declaring_generic_hook_split:nNNnn - { \exp_args:Ne \@@_file_hook_normalize:n {#1} } - } - { \@@_try_declaring_generic_hook_split:nNNnn {#1} } - } +%<latexrelease>\cs_new_protected:Npn \@@_try_declaring_generic_hook:nNNnn #1 +%<latexrelease> { +%<latexrelease> \@@_if_file_hook:wTF #1 / \s_@@_mark {#1} +%<latexrelease> { +%<latexrelease> \exp_args:Ne \@@_try_declaring_generic_hook_split:nNNnn +%<latexrelease> { \exp_args:Ne \@@_file_hook_normalize:n {#1} } +%<latexrelease> } +%<latexrelease> { \@@_try_declaring_generic_hook_split:nNNnn {#1} } +%<latexrelease> } % \end{macrocode} % % \begin{macrocode} -\cs_new_protected:Npn \@@_try_declaring_generic_hook_split:nNNnn #1 #2 #3 - { - \@@_try_declaring_generic_hook:wnTF #1 / / / \scan_stop: {#1} - { #2 } - { #3 } {#1} - } +%<latexrelease>\cs_new_protected:Npn \@@_try_declaring_generic_hook_split:nNNnn #1 #2 #3 +%<latexrelease> { +%<latexrelease> \@@_try_declaring_generic_hook:wnTF #1 / / / \scan_stop: {#1} +%<latexrelease> { #2 } +%<latexrelease> { #3 } {#1} +%<latexrelease> } +%<latexrelease>\EndIncludeInRelease % \end{macrocode} % \end{macro} % -% % \begin{macro}[TF]{\@@_try_declaring_generic_hook:wn} -% % \begin{macrocode} -%<latexrelease>\IncludeInRelease{2021/06/01}% -%<latexrelease> {\@@_try_declaring_generic_hook:wn}{Support~cmd~hooks} +%<latexrelease>\IncludeInRelease{2021/11/15}{\@@_try_declaring_generic_hook:wn}% +%<latexrelease> {Standardise~generic~hook~names} \prg_new_protected_conditional:Npnn \@@_try_declaring_generic_hook:wn #1 / #2 / #3 / #4 \scan_stop: #5 { TF } { - \tl_if_empty:nTF {#2} - { \prg_return_false: } + \@@_if_generic:nTF {#5} { - \prop_if_in:NnTF \c_@@_generics_prop {#1} + \@@_if_usable:nF {#5} { - \@@_if_usable:nF {#5} - { % \end{macrocode} % If the hook doesn't exist yet we check if it is a \texttt{cmd} % hook and if so we attempt patching the command in addition to @@ -2913,8 +3181,8 @@ % \cs{@@_patch_cmd_or_delay:Nnn} (defined in \texttt{ltcmdhooks}) % will generate an appropriate error message. % \begin{macrocode} - \str_if_eq:nnT {#1} { cmd } - { \@@_try_put_cmd_hook:n {#5} } + \str_if_eq:nnT {#1} { cmd } + { \@@_try_put_cmd_hook:n {#5} } % \end{macrocode} % % Declare the hook always even if it can't really be used (error @@ -2923,26 +3191,125 @@ % Here we use \cs{@@_make_usable:n}, so that a \cs{hook_new:n} is still % possible later. % \begin{macrocode} - \@@_make_usable:n {#5} - } - \prop_if_in:NnTF \c_@@_generics_reversed_ii_prop {#2} - { \tl_gset:cn { g_@@_#5_reversed_tl } { - } } - { - \prop_if_in:NnT \c_@@_generics_reversed_iii_prop {#3} - { \tl_gset:cn { g_@@_#5_reversed_tl } { - } } - } - \prg_return_true: + \@@_make_usable:n {#5} + } + \@@_if_generic_reversed:nT {#5} + { \tl_gset:cn { g_@@_#5_reversed_tl } { - } } + \prg_return_true: + } + { +% \end{macrocode} +% +% Generic hooks are all named \meta{type}/\meta{name}/\meta{place}, +% where \meta{type} and \meta{place} are predefined +% (\cs{c_@@_generic_\meta{type}/./\meta{place}_tl}), and \meta{name} +% is the variable component. Older releases had some hooks with the +% \meta{name} in the third part, so the code below supports that +% syntax for a while, with a warning. +% +% The \cs{exp_after:wN} |...| \cs{exp:w} trick is there to remove the +% conditional structure inserted by +% \cs{@@_try_declaring_generic_hook:wnTF} and thus allow access to the +% tokens that follow it, as is needed to keep things going. +% +% When the deprecation cycle ends, the lines below should all be +% replaced by \cs{prg_return_false:}. +% \begin{macrocode} + \@@_if_deprecated_generic:nTF {#5} + { + \@@_deprecated_generic_warn:n {#5} + \exp_after:wN \@@_declare_deprecated_generic:NNn + \exp:w % \exp_end: } { \prg_return_false: } } } +% \end{macrocode} +% +% \begin{macro}{ +% \@@_deprecated_generic_warn:Nn, +% \@@_deprecated_generic_warn:Nw, +% } +% \cs{@@_deprecated_generic_warn:n} will issue a deprecation warning +% for a given hook, and mark that hook such that the warning will not +% be issued again (multiple warnings can be issued, but only once per +% hook). +% \begin{macrocode} +\cs_new_protected:Npn \@@_deprecated_generic_warn:n #1 + { \@@_deprecated_generic_warn:w #1 \s_@@_mark } +\cs_new_protected:Npn \@@_deprecated_generic_warn:w + #1 / #2 / #3 \s_@@_mark + { + \if_cs_exist:w @@~#1/#2/#3 \cs_end: \else: + \msg_warning:nnnnn { hooks } { generic-deprecated } {#1} {#2} {#3} + \fi: + \cs_gset_eq:cN { @@~#1/#2/#3 } \scan_stop: + } +% \end{macrocode} +% \end{macro} +% +% \begin{macro}{ +% \@@_do_deprecated_generic:Nn, +% \@@_do_deprecated_generic:Nw, +% \@@_declare_deprecated_generic:NNw, +% \@@_declare_deprecated_generic:NNw, +% } +% Now that the user has been told about the deprecation, we proceed by +% swapping \meta{name} and \meta{place} and adding the code to the +% correct hook. +% \begin{macrocode} +\cs_new_protected:Npn \@@_do_deprecated_generic:Nn #1 #2 + { \@@_do_deprecated_generic:Nw #1 #2 \s_@@_mark } +\cs_new_protected:Npn \@@_do_deprecated_generic:Nw #1 + #2 / #3 / #4 \s_@@_mark + { #1 { #2 / #4 / #3 } } +\cs_new_protected:Npn \@@_declare_deprecated_generic:NNn #1 #2 #3 + { \@@_declare_deprecated_generic:NNw #1 #2 #3 \s_@@_mark } +\cs_new_protected:Npn \@@_declare_deprecated_generic:NNw #1 #2 + #3 / #4 / #5 \s_@@_mark + { + \@@_try_declaring_generic_hook:wnTF #3 / #5 / #4 / \scan_stop: + { #3 / #5 / #4 } + #1 #2 { #3 / #5 / #4 } + } %<latexrelease>\EndIncludeInRelease % \end{macrocode} +% \end{macro} % % \begin{macrocode} -%<latexrelease>\IncludeInRelease{2020/10/01}% -%<latexrelease> {\@@_try_declaring_generic_hook:wn}{Support~cmd~hooks} -%<latexrelease> +%<latexrelease>\IncludeInRelease{2021/06/01}{\@@_try_declaring_generic_hook:wn} +%<latexrelease> {Support~cmd~hooks} +%<latexrelease>\prg_new_protected_conditional:Npnn \@@_try_declaring_generic_hook:wn +%<latexrelease> #1 / #2 / #3 / #4 \scan_stop: #5 { TF } +%<latexrelease> { +%<latexrelease> \tl_if_empty:nTF {#2} +%<latexrelease> { \prg_return_false: } +%<latexrelease> { +%<latexrelease> \prop_if_in:NnTF \c_@@_generics_prop {#1} +%<latexrelease> { +%<latexrelease> \@@_if_usable:nF {#5} +%<latexrelease> { +%<latexrelease> \str_if_eq:nnT {#1} { cmd } +%<latexrelease> { \@@_try_put_cmd_hook:n {#5} } +%<latexrelease> \@@_make_usable:n {#5} +%<latexrelease> } +%<latexrelease> \prop_if_in:NnTF \c_@@_generics_reversed_ii_prop {#2} +%<latexrelease> { \tl_gset:cn { g_@@_#5_reversed_tl } { - } } +%<latexrelease> { +%<latexrelease> \prop_if_in:NnT \c_@@_generics_reversed_iii_prop {#3} +%<latexrelease> { \tl_gset:cn { g_@@_#5_reversed_tl } { - } } +%<latexrelease> } +%<latexrelease> \prg_return_true: +%<latexrelease> } +%<latexrelease> { \prg_return_false: } +%<latexrelease> } +%<latexrelease> } +%<latexrelease>\EndIncludeInRelease +% \end{macrocode} +% +% \begin{macrocode} +%<latexrelease>\IncludeInRelease{2020/10/01}{\@@_try_declaring_generic_hook:wn}% +%<latexrelease> {Support~cmd~hooks} %<latexrelease>\prg_new_protected_conditional:Npnn \@@_try_declaring_generic_hook:wn %<latexrelease> #1 / #2 / #3 / #4 \scan_stop: #5 { TF } %<latexrelease> { @@ -2968,77 +3335,107 @@ % \end{macrocode} % \end{macro} % +% % \begin{macro}[pTF]{\@@_if_file_hook:w} % \cs{@@_if_file_hook:wTF} checks if the argument is a valid % file-specific hook (not, for example, |file/before|, but -% |file/before/foo.tex|). If it is a file-specific hook, then it +% |file/foo.tex/before|). If it is a file-specific hook, then it % executes the \meta{true} branch, otherwise \meta{false}. -% -% A file-specific hook is \hook{file/\meta{position}/\meta{name}}. -% If any of these parts don't exist, it is a general file hook or not -% a file hook at all, so the conditional evaluates to \meta{false}. -% Otherwise, it checks that the first part is |file| and that the -% \meta{position} is in the \cs{c_@@_generics_file_prop}. -% -% A property list is used here to avoid having to worry with catcodes, -% because \pkg{expl3}'s file name parsing turns all characters into -% catcode-12 tokens, which might differ from hand-input letters. % \begin{macrocode} +%<latexrelease>\IncludeInRelease{2021/11/15}{\@@_if_file_hook:w}% +%<latexrelease> {Standardise~generic~hook~names} \prg_new_conditional:Npnn \@@_if_file_hook:w - #1 / #2 / #3 \s_@@_mark { TF } + #1 / #2 \s_@@_mark #3 { TF } { - \str_if_eq:nnTF {#1} { file } + \@@_if_generic:nTF {#3} { - \bool_lazy_or:nnTF - { \tl_if_empty_p:n {#3} } - { \str_if_eq_p:nn {#3} { / } } + \str_if_eq:nnTF {#1} { file } + { \prg_return_true: } { \prg_return_false: } - { - \prop_if_in:NnTF \c_@@_generics_file_prop {#2} - { \prg_return_true: } - { \prg_return_false: } - } } { \prg_return_false: } } +%<latexrelease>\EndIncludeInRelease +% \end{macrocode} +% +% \begin{macrocode} +%<latexrelease>\IncludeInRelease{2020/10/01}{\@@_if_file_hook:w}% +%<latexrelease> {Standardise~generic~hook~names} +%<latexrelease>\prg_new_conditional:Npnn \@@_if_file_hook:w +%<latexrelease> #1 / #2 / #3 \s_@@_mark { TF } +%<latexrelease> { +%<latexrelease> \str_if_eq:nnTF {#1} { file } +%<latexrelease> { +%<latexrelease> \bool_lazy_or:nnTF +%<latexrelease> { \tl_if_empty_p:n {#3} } +%<latexrelease> { \str_if_eq_p:nn {#3} { / } } +%<latexrelease> { \prg_return_false: } +%<latexrelease> { +%<latexrelease> \prop_if_in:NnTF \c_@@_generics_file_prop {#2} +%<latexrelease> { \prg_return_true: } +%<latexrelease> { \prg_return_false: } +%<latexrelease> } +%<latexrelease> } +%<latexrelease> { \prg_return_false: } +%<latexrelease> } +%<latexrelease>\EndIncludeInRelease % \end{macrocode} % \end{macro} % % \begin{macro}[EXP]{\@@_file_hook_normalize:n} % \begin{macro}[EXP]{\@@_strip_double_slash:n,\@@_strip_double_slash:w} +% +% \begin{macrocode} +%<latexrelease>\IncludeInRelease{2021/11/15}{\@@_file_hook_normalize:n}% +%<latexrelease> {Standardise~generic~hook~names} +%<latexrelease>\EndIncludeInRelease +% \end{macrocode} +% % When a file-specific hook is found, before being declared it is % 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}|. +% have to be \verb|\AddToHook{file/./mypath//file.tex/after}|. % \begin{macrocode} -\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 } +%<latexrelease>\IncludeInRelease{2020/10/01}{\@@_file_hook_normalize:n}% +%<latexrelease> {Standardise~generic~hook~names} +%<latexrelease>\cs_new:Npn \@@_file_hook_normalize:n #1 +%<latexrelease> { \@@_strip_double_slash:n {#1} } +%<latexrelease>\cs_new:Npn \@@_strip_double_slash:n #1 +%<latexrelease> { \@@_strip_double_slash:w #1 // \s_@@_mark } % \end{macrocode} % This function is always called after testing if the argument is a % file hook with \cs{@@_if_file_hook:wTF}, so we can assume it has -% three parts (it is either \verb|file/before/...| or -% \verb|file/after/...|), so we use \verb|#1/#2/#3 //| instead of just +% three parts (it is either \verb|file/.../before| or +% \verb|file/.../after|), so we use \verb|#1/#2/#3 //| instead of just % \verb|#1 //| to prevent losing a slash if the file name is empty. % \changes{v1.0h}{2021/01/07}{Assume hook name has at least three % nonempty parts (gh/464)} % \begin{macrocode} -\cs_new:Npn \@@_strip_double_slash:w #1/#2/#3 // #4 \s_@@_mark - { - \tl_if_empty:nTF {#4} - { #1/#2/#3 } - { \@@_strip_double_slash:w #1/#2/#3 / #4 \s_@@_mark } - } +%<latexrelease>\cs_new:Npn \@@_strip_double_slash:w #1/#2/#3 // #4 \s_@@_mark +%<latexrelease> { +%<latexrelease> \tl_if_empty:nTF {#4} +%<latexrelease> { #1/#2/#3 } +%<latexrelease> { \@@_strip_double_slash:w #1/#2/#3 / #4 \s_@@_mark } +%<latexrelease> } +%<latexrelease>\EndIncludeInRelease % \end{macrocode} % \end{macro} % \end{macro} % -% \begin{macro}{\c_@@_generics_prop} -% Property list holding the generic names. We don't provide any user -% interface to this as this is meant to be static. +% \begin{macro}{ +% \c_@@_generic_cmd/./before_tl,\c_@@_generic_cmd/./after_tl, +% \c_@@_generic_env/./before_tl,\c_@@_generic_env/./after_tl, +% \c_@@_generic_file/./before_tl,\c_@@_generic_file/./after_tl, +% \c_@@_generic_package/./before_tl,\c_@@_generic_package/./after_tl, +% \c_@@_generic_class/./before_tl,\c_@@_generic_class/./after_tl, +% \c_@@_generic_include/./before_tl,\c_@@_generic_include/./after_tl, +% \c_@@_generic_env/./begin_tl,\c_@@_generic_env/./end_tl, +% \c_@@_generic_include/./end_tl +% } +% Token lists defining the possible generic hooks. We don't provide +% any user interface to this as this is meant to be static. % \begin{description} % \item[\texttt{cmd}] % The generic hooks used for commands. @@ -3048,28 +3445,65 @@ % The generic hooks used when loading a file % \end{description} % \begin{macrocode} -\prop_const_from_keyval:Nn \c_@@_generics_prop - {cmd=,env=,file=,package=,class=,include=} +%<latexrelease>\IncludeInRelease{2021/11/15}{\c_@@_generics_prop}% +%<latexrelease> {Standardise~generic~hook~names} +\clist_map_inline:nn { cmd , env , file , package , class , include } + { + \tl_const:cn { c_@@_generic_#1/./before_tl } { + } + \tl_const:cn { c_@@_generic_#1/./after_tl } { - } + } +\tl_const:cn { c_@@_generic_env/./begin_tl } { + } +\tl_const:cn { c_@@_generic_env/./end_tl } { + } +\tl_const:cn { c_@@_generic_include/./end_tl } { - } +% \end{macrocode} +% +% Deprecated generic hooks: +% \begin{macrocode} +\clist_map_inline:nn { file , package , class , include } + { + \tl_const:cn { c_@@_deprecated_#1/./before_tl } { } + \tl_const:cn { c_@@_deprecated_#1/./after_tl } { } + } +\tl_const:cn { c_@@_deprecated_include/./end_tl } { } +%<latexrelease>\EndIncludeInRelease +% \end{macrocode} +% +% \begin{macrocode} +%<latexrelease>\IncludeInRelease{2020/10/01}{\c_@@_generics_prop}% +%<latexrelease> {Standardise~generic~hook~names} +%<latexrelease>\prop_const_from_keyval:Nn \c_@@_generics_prop +%<latexrelease> {cmd=,env=,file=,package=,class=,include=} +%<latexrelease>\EndIncludeInRelease % \end{macrocode} % \end{macro} % % \begin{macro}{\c_@@_generics_reversed_ii_prop, % \c_@@_generics_reversed_iii_prop, % \c_@@_generics_file_prop} -% Some of the generic hooks are supposed to use reverse ordering, these are -% the following (only the second or third sub-component is checked): +% The following generic hooks are supposed to use reverse ordering +% (the |ii| and |iii| names are kept for the deprecation cycle): % \begin{macrocode} -\prop_const_from_keyval:Nn \c_@@_generics_reversed_ii_prop {after=,end=} -\prop_const_from_keyval:Nn \c_@@_generics_reversed_iii_prop {after=} -\prop_const_from_keyval:Nn \c_@@_generics_file_prop {before=,after=} +%<latexrelease>\IncludeInRelease{2021/11/15}{\c_@@_generics_reversed_ii_prop}% +%<latexrelease> {Standardise~generic~hook~names} +%<latexrelease>\EndIncludeInRelease +% \end{macrocode} +% +% \begin{macrocode} +%<latexrelease>\IncludeInRelease{2020/10/01}{\c_@@_generics_reversed_ii_prop}% +%<latexrelease> {Standardise~generic~hook~names} +%<latexrelease>\prop_const_from_keyval:Nn \c_@@_generics_reversed_ii_prop {after=,end=} +%<latexrelease>\prop_const_from_keyval:Nn \c_@@_generics_reversed_iii_prop {after=} +%<latexrelease>\prop_const_from_keyval:Nn \c_@@_generics_file_prop {before=,after=} +%<latexrelease>\EndIncludeInRelease % \end{macrocode} % \end{macro} % % \begin{macro}{\hook_gremove_code:nn} % \begin{macro}{\@@_gremove_code:nn} -% +% % With \cs{hook_gremove_code:nn}\Arg{hook}\Arg{label} any code % for \meta{hook} stored under \meta{label} is removed. +% \changes{v1.0o}{2021/07/22}{Do not queue removals (gh/625)} % \begin{macrocode} \cs_new_protected:Npn \hook_gremove_code:nn #1 #2 { \@@_normalize_hook_args:Nnn \@@_gremove_code:nn {#1} {#2} } @@ -3101,27 +3535,13 @@ { % \end{macrocode} % If the label is |top-level| then clear the token list, as all code -% there is under the same label. Marked removal is not implemented -% for |top-level| because it is hard to reliably know that no code -% was added to \cs{@@_toplevel\textvisiblespace\meta{hook}} (granted that -% an empty code could be interpreted as that, but then it differs in -% behaviour from other labels, in which an empty chunk is still valid -% for removal). Besides, it doesn't make much (if any) sense for -% packages to remove |top-level| code. So here the chunk is just -% cleared unconditionally. +% there is under the same label. % \begin{macrocode} \str_if_eq:nnTF {#2} { top-level } { \@@_tl_gclear:c { @@_toplevel~#1 } } { -% \end{macrocode} -% Otherwise check if the label being removed exists in the code pool. -% If it does, just call \cs{@@_gremove_code_do:nn} to do the removal, -% otherwise mark it to be removed. -% \begin{macrocode} - \prop_get:cnNTF { g_@@_#1_code_prop } {#2} \l_@@_return_tl - { \@@_gremove_code_do:nn } - { \@@_mark_removal:nn } - {#1} {#2} + \prop_gpop:cnNF { g_@@_#1_code_prop } {#2} \l_@@_return_tl + { \msg_warning:nnnn { hooks } { cannot-remove } {#1} {#2} } } } % \end{macrocode} @@ -3132,82 +3552,21 @@ } % \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} -% -% \begin{macro}{\@@_gremove_code_do:nn} -% Remove code for a given label. -% \begin{macrocode} -\cs_new_protected:Npn \@@_gremove_code_do:nn #1 #2 - { \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=). 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. -% -% A hand-crafted token list is used here because property lists don't -% hold repeated items, so multiple usages of \cs{@@_mark_removal:nn} -% would be cancelled by a single \cs{@@_unmark_removal:nn}. -% \begin{macrocode} -\cs_new_protected:Npn \@@_mark_removal:nn #1 #2 - { - \tl_gput_right:Nx \g_@@_removal_list_tl - { \@@_removal_tl:nn {#1} {#2} } - } -% \end{macrocode} -% \end{macro} -% -% \begin{macro}{\@@_unmark_removal:nn} -% Unmarks \meta{label} (\verb=#2=) to be removed from \meta{hook} -% (\verb=#1=). \cs{tl_gremove_once:Nx} is used rather than -% \cs{tl_gremove_all:Nx} so that two additions are needed to cancel -% two marked removals, rather than only one. +% If the code pool for this hook doesn't exist, show a warning: % \begin{macrocode} -\cs_new_protected:Npn \@@_unmark_removal:nn #1 #2 - { - \tl_gremove_once:Nx \g_@@_removal_list_tl - { \@@_removal_tl:nn {#1} {#2} } - } -% \end{macrocode} -% \end{macro} -% -% \begin{macro}[TF]{\@@_if_marked_removal:nn} -% Checks if the \cs{g_@@_removal_list_tl} contains the current -% \meta{label} (\verb=#2=) and \meta{hook} (\verb=#1=). -% \begin{macrocode} -\prg_new_protected_conditional:Npnn \@@_if_marked_removal:nn #1 #2 { TF } - { - \exp_args:NNx \tl_if_in:NnTF \g_@@_removal_list_tl - { \@@_removal_tl:nn {#1} {#2} } - { \prg_return_true: } { \prg_return_false: } + { + \@@_if_deprecated_generic:nTF {#1} + { + \@@_deprecated_generic_warn:n {#1} + \@@_do_deprecated_generic:Nn \@@_gremove_code:nn {#1} {#2} + } + { \msg_warning:nnnn { hooks } { cannot-remove } {#1} {#2} } + } } % \end{macrocode} % \end{macro} -% -% \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=. 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_@@_??_code_prop, % \@@~??, @@ -3263,6 +3622,13 @@ % \begin{macrocode} \cs_new_protected:Npn \@@_gset_rule:nnnn #1#2#3#4 { + \@@_if_deprecated_generic:nT {#1} + { + \@@_deprecated_generic_warn:n {#1} + \@@_do_deprecated_generic:Nn \@@_gset_rule:nnnn {#1} + {#2} {#3} {#4} + \exp_after:wN \use_none:nnnnnnnnn \use_none:n + } % \end{macrocode} % First we ensure the basic data structure of the hook exists: % \begin{macrocode} @@ -3399,12 +3765,12 @@ % |#1| is lexically greater, and \cs{use_iii:nn} otherwise. % \begin{macrocode} \cs_new:Npn \@@_if_label_case:nnnnn #1#2 - { - \cs:w use_ - \if_case:w \@@_str_compare:nn {#1} {#2} - i \or: ii \else: iii \fi: :nnn - \cs_end: - } + { + \cs:w use_ + \if_case:w \@@_str_compare:nn {#1} {#2} + i \or: ii \else: iii \fi: :nnn + \cs_end: + } % \end{macrocode} % \end{macro} % @@ -3440,7 +3806,7 @@ } % \end{macrocode} % If we are debugging we show results hook by hook for all hooks -% that have data. +% that have data. % \begin{macrocode} \@@_debug:n { \iow_term:x{^^JAll~ initialized~ (non-empty)~ hooks:} @@ -3492,7 +3858,7 @@ % \begin{macrocode} \@@_if_usable:nT {#1} { - \prop_if_empty:cTF {g_@@_#1_code_prop} + \prop_if_empty:cTF { g_@@_#1_code_prop } { \@@_tl_gset:co { @@~#1 } { @@ -3503,11 +3869,11 @@ { % \end{macrocode} % By default the algorithm sorts the code chunks and then saves the -% result in a token list for fast execution by adding the code one -% after another using \cs{tl_gput_right:NV}. When we sort code for +% result in a token list for fast execution; this is done by adding the code chunks +% one after another, using \cs{tl_gput_right:NV}. When we sort code for % a reversed hook, all we have to do is to add the code chunks in % the opposite order into the token list. So all we have to do -% in preparation is to change two definitions used later on. +% in preparation is to change two definitions that are used later on. % \begin{macrocode} \@@_if_reversed:nTF {#1} { \cs_set_eq:NN \@@_tl_gput:Nn \@@_tl_gput_left:Nn @@ -3518,8 +3884,9 @@ % % 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, so we temporarily -% save the old code property list so that it can be restored later. +% shouldn't appear in the sorted hook token list, so we make a copy +% of the code property list that we can safely work on without +% changing the main one. % \begin{macrocode} \prop_set_eq:Nc \l_@@_work_prop { g_@@_#1_code_prop } \@@_initialize_single:ccn @@ -3606,8 +3973,8 @@ % alphanumeric labels. As usual Don's data structures are chosen in % a way that one can omit a lot of tests and I have mimicked that as % far as possible. The result is a restriction I do not test for at -% the moment: a label can't be equal to the number 0! \fmi{Needs -% checking for, just in case ... maybe} +% the moment: a label can't be equal to the number 0! +% \fmiinline{Needs checking for, just in case ... maybe} % % ^^A #1 <- \@@~#1 % ^^A #2 <- \g_@@_#1_labels_clist @@ -3626,11 +3993,11 @@ % \begin{macrocode} \tl_set:Nn \l_@@_cur_hook_tl {#3} % \end{macrocode} -% +% % We loop over the property list holding the code and record all -% labels listed there. Only rules for those labels are of interest +% the labels listed there. Only the rules for those labels are of interest % to us. While we are at it we count them (which gives us the $N$ -% in Knuth's algorithm. The prefix |label_| is added to the variables +% in Knuth's algorithm). The prefix |label_| is added to the variables % to ensure that labels named |front|, |rear|, |labels|, or |return| % don't interact with our code. % \begin{macrocode} @@ -3642,11 +4009,11 @@ \seq_clear_new:c { \@@_seq_csname:n {##1} } } % \end{macrocode} -% Steps T2 and T3: Sort the relevant rules into the data structure\ldots -% +% Steps T2 and T3: Here we sort the relevant rules into the data structure\ldots +% % 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 +% 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 % both labels are equal), saving a good amount of time. The way the @@ -3668,12 +4035,12 @@ } } % \end{macrocode} -% Take a breath and take a look at the data structures that have +% Now take a breath, and look at the data structures that have % been set up: % \begin{macrocode} \@@_debug:n { \@@_debug_label_data:N \l_@@_work_prop } % \end{macrocode} -% +% % % Step T4: % \begin{macrocode} @@ -3689,13 +4056,13 @@ } \tl_set_eq:Nc \l_@@_front_tl { \@@_tl_csname:n { 0 } } % \end{macrocode} -% +% % \begin{macrocode} \@@_tl_gclear:N #1 \clist_gclear:N #2 % \end{macrocode} % -% The whole loop combines steps T5--T7: +% The whole loop gets combined in steps T5--T7: % \begin{macrocode} \bool_while_do:nn { ! \str_if_eq_p:Vn \l_@@_front_tl { 0 } } { @@ -3706,13 +4073,13 @@ \prop_get:NVN \l_@@_work_prop \l_@@_front_tl \l_@@_return_tl \exp_args:NNV \@@_tl_gput:Nn #1 \l_@@_return_tl % \end{macrocode} -% +% % \begin{macrocode} \@@_clist_gput:NV #2 \l_@@_front_tl \@@_debug:n{ \iow_term:x{Handled~ code~ for~ \l_@@_front_tl} } % \end{macrocode} % -% This is step T6 except that we don't use a pointer $P$ to move +% This is step T6, except that we don't use a pointer $P$ to move % through the successors, but instead use \verb=##1= of the mapping % function. % \begin{macrocode} @@ -3730,7 +4097,7 @@ } } % \end{macrocode} -% and step T7: +% and here is step T7: % \begin{macrocode} \tl_set_eq:Nc \l_@@_front_tl { \@@_tl_csname:n { \l_@@_front_tl } } @@ -3748,14 +4115,14 @@ % \end{macrocode} % % This is not really the information one needs in the error case -% but will do for now \ldots \fmi{improve output on a rainy day} +% but it will do for now \ldots \fmiinline{improve output on a rainy day} % \begin{macrocode} \@@_debug_label_data:N \l_@@_work_prop \iow_term:x{====================} } % \end{macrocode} -% After we have added all hook code to \verb=#1= we finish it off -% with adding extra code for the |top-level| (\verb=#2=) and for one +% After we have added all hook code to \verb=#1=, we finish it off +% by adding extra code for the |top-level| (\verb=#2=) and for one % time execution (\verb=#3=). These should normally be empty. The % |top-level| code is added with \cs{@@_tl_gput:Nn} as that might % change for a reversed hook (then |top-level| is the very first code @@ -3905,10 +4272,10 @@ % \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 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.\footnote{This also hase the advantage that -% the result of the sorting doesn't change which might otherwise +% everything for that label since it doesn't matter where we put +% such empty code. However that would complicate the algorithm a +% lot with little gain.\footnote{This also has the advantage that +% the result of the sorting doesn't change, as it might otherwise do % (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 @@ -3916,8 +4283,8 @@ % % Here the code is removed from \cs{l_@@_cur_hook_tl} rather than % \verb=#3= because the latter may be \verb=??=, and the default -% hook doesn't store any code. Removing from \cs{l_@@_cur_hook_tl} -% makes default rules \verb=->= and \verb=<-= work properly. +% hook doesn't store any code. Removing it instead from \cs{l_@@_cur_hook_tl} +% makes the default rules \verb=->= and \verb=<-= work properly. % \begin{macrocode} \cs_new_protected:cpn { @@_apply_rule_->:nnn } #1#2#3 { @@ -3978,7 +4345,7 @@ % % % \begin{macro}{\@@_debug_label_data:N} -% +% % \begin{macrocode} \cs_new_protected:Npn \@@_debug_label_data:N #1 { \iow_term:x{Code~ labels~ for~ sorting:} @@ -4024,8 +4391,15 @@ % \begin{macrocode} \cs_new_protected:Npn \@@_log:nN #1 #2 { + \@@_if_deprecated_generic:nT {#1} + { + \@@_deprecated_generic_warn:n {#1} + \@@_do_deprecated_generic:Nn \@@_log:nN {#1} #2 + \exp_after:wN \use_none:nnnnnnnnn \use_none:nnnnn + } \@@_preamble_hook:n {#1} - \@@_log_cmd:x { ^^J ->~The~hook~'#1': } + \@@_log_cmd:x + { ^^J ->~The~ \@@_if_generic:nT {#1} { generic~ } hook~'#1': } % \end{macrocode} % % \begin{macrocode} @@ -4234,16 +4608,14 @@ % % \begin{macro}{\@@_gput_next_code:nn, % \@@_gput_next_do:nn, -% \@@_gput_next_do:Nnn, -% \@@_clear_next:n} +% \@@_gput_next_do:Nnn} % \begin{macrocode} \cs_new_protected:Npn \@@_gput_next_code:nn #1 #2 { \@@_if_disabled:nTF {#1} { \msg_error:nnn { hooks } { hook-disabled } {#1} } { - \@@_init_structure:n {#1} - \@@_if_usable:nTF {#1} + \@@_if_structure_exist:nTF {#1} { \@@_gput_next_do:nn {#1} {#2} } { \@@_try_declaring_generic_next_hook:nn {#1} {#2} } } @@ -4275,13 +4647,21 @@ { \@@_tl_gset:Nn #1 { \@@_clear_next:n {#2} } } \@@_tl_gput_right:Nn #1 } -\cs_new_protected:Npn \@@_clear_next:n #1 - { \cs_gset_eq:cN { @@_next~#1 } \c_empty_tl } % \end{macrocode} % \end{macro} % % % +% \begin{macro}{\hook_gclear_next_code:n,\@@_clear_next:n} +% Discard anything set up for next invocation of the hook. +% \changes{v1.0o}{2021/07/27}{Macro made public} +% \begin{macrocode} +\cs_new_protected:Npn \hook_gclear_next_code:n #1 + { \@@_normalize_hook_args:Nn \@@_clear_next:n {#1} } +\cs_new_protected:Npn \@@_clear_next:n #1 + { \cs_gset_eq:cN { @@_next~#1 } \c_empty_tl } +% \end{macrocode} +% \end{macro} % % % @@ -4302,44 +4682,68 @@ % the preamble. \cs{@@_preamble_hook:n} initializes the hook in the % preamble, and is redefined to \cs{use_none:n} at |\begin{document}|. % -% Both versions do the same internally: check if the hook exist as -% given, and if so use it as quickly as possible. If it doesn't -% exist, the a call to \cs{@@_use:wn} checks for file hooks. +% Both versions do the same thing internally: they check that the hook exists as +% given, and if so they use it as quickly as possible. % % At |\begin{document}|, all hooks are initialized, and any change in % them causes an update, so \cs{hook_use:n} can be made expandable. % This one is better not protected so that it can expand into nothing % if containing no code. Also important in case of generic hooks that -% we do not generate a \cs[no-index]{relax} as a side effect of checking for a -% csname. In contrast to the \TeX{} low-level +% we do not generate a \cs[no-index]{relax} as a side effect of +% checking for a csname. In contrast to the \TeX{} low-level % \verb=\csname ...\endcsname= construct \cs{tl_if_exist:c} is % careful to avoid this. % \begin{macrocode} +%<latexrelease>\IncludeInRelease{2021/11/15}{\hook_use:n} +%<latexrelease> {Standardise~generic~hook~names} \cs_new_protected:Npn \hook_use:n #1 { - \tl_if_exist:cTF { @@~#1 } + \tl_if_exist:cT { @@~#1 } { \@@_preamble_hook:n {#1} \cs:w @@~#1 \cs_end: } - { \@@_use:wn #1 / \s_@@_mark {#1} } } \cs_new:Npn \@@_use_initialized:n #1 { \if_cs_exist:w @@~#1 \cs_end: - \else: - \@@_use_undefined:w + \cs:w @@~#1 \exp_after:wN \cs_end: \fi: - \cs:w @@~#1 \@@_use_end: - } -\cs_new:Npn \@@_use_undefined:w #1 #2 @@~#3 \@@_use_end: - { - #1 % fi - \@@_use:wn #3 / \s_@@_mark {#3} } \cs_new_protected:Npn \@@_preamble_hook:n #1 { \@@_initialize_hook_code:n {#1} } -\cs_new_eq:NN \@@_use_end: \cs_end: +%<latexrelease>\EndIncludeInRelease +% \end{macrocode} +% +% \begin{macrocode} +%<latexrelease>\IncludeInRelease{2020/10/01}{\hook_use:n} +%<latexrelease> {Standardise~generic~hook~names} +%<latexrelease>\cs_new_protected:Npn \hook_use:n #1 +%<latexrelease> { +%<latexrelease> \tl_if_exist:cTF { @@~#1 } +%<latexrelease> { +%<latexrelease> \@@_preamble_hook:n {#1} +%<latexrelease> \cs:w @@~#1 \cs_end: +%<latexrelease> } +%<latexrelease> { \@@_use:wn #1 / \s_@@_mark {#1} } +%<latexrelease> } +%<latexrelease>\cs_new:Npn \@@_use_initialized:n #1 +%<latexrelease> { +%<latexrelease> \if_cs_exist:w @@~#1 \cs_end: +%<latexrelease> \else: +%<latexrelease> \@@_use_undefined:w +%<latexrelease> \fi: +%<latexrelease> \cs:w @@~#1 \@@_use_end: +%<latexrelease> } +%<latexrelease>\cs_new:Npn \@@_use_undefined:w #1 #2 @@~#3 \@@_use_end: +%<latexrelease> { +%<latexrelease> #1 % fi +%<latexrelease> \@@_use:wn #3 / \s_@@_mark {#3} +%<latexrelease> } +%<latexrelease>\cs_new_protected:Npn \@@_preamble_hook:n #1 +%<latexrelease> { \@@_initialize_hook_code:n {#1} } +%<latexrelease>\cs_new_eq:NN \@@_use_end: \cs_end: +%<latexrelease>\EndIncludeInRelease % \end{macrocode} % \end{macro} % \end{macro} @@ -4360,63 +4764,121 @@ % \cs{@@_if_usable_use:n} checks if the hook exist, and calls % \cs{@@_preamble_hook:n} if so, then uses the hook. % \begin{macrocode} -\cs_new:Npn \@@_use:wn #1 / #2 \s_@@_mark #3 - { - \str_if_eq:nnTF {#1} { file } - { \@@_try_file_hook:n {#3} } - { } % Hook doesn't exist - } +%<latexrelease>\IncludeInRelease{2021/11/15}{\@@_use:wn} +%<latexrelease> {Standardise~generic~hook~names} +%<latexrelease>\EndIncludeInRelease +%<latexrelease>\IncludeInRelease{2020/10/01}{\@@_use:wn} +%<latexrelease> {Standardise~generic~hook~names} +%<latexrelease>\cs_new:Npn \@@_use:wn #1 / #2 \s_@@_mark #3 +%<latexrelease> { +%<latexrelease> \str_if_eq:nnTF {#1} { file } +%<latexrelease> { \@@_try_file_hook:n {#3} } +%<latexrelease> { } % Hook doesn't exist +%<latexrelease> } % \end{macrocode} % % \begin{macrocode} -\cs_new_protected:Npn \@@_try_file_hook:n #1 - { - \@@_if_file_hook:wTF #1 / / \s_@@_mark - { - \exp_args:Ne \@@_if_usable_use:n - { \exp_args:Ne \@@_file_hook_normalize:n {#1} } - } - { \@@_if_usable_use:n {#1} } % file/ generic hook (e.g. file/before) - } +%<latexrelease>\cs_new_protected:Npn \@@_try_file_hook:n #1 +%<latexrelease> { +%<latexrelease> \@@_if_file_hook:wTF #1 / \s_@@_mark {#1} +%<latexrelease> { +%<latexrelease> \exp_args:Ne \@@_if_usable_use:n +%<latexrelease> { \exp_args:Ne \@@_file_hook_normalize:n {#1} } +%<latexrelease> } +%<latexrelease> { \@@_if_usable_use:n {#1} } % file/ generic hook (e.g. file/before) +%<latexrelease> } % \end{macrocode} % % \begin{macrocode} -\cs_new_protected:Npn \@@_if_usable_use:n #1 - { - \tl_if_exist:cT { @@~#1 } - { - \@@_preamble_hook:n {#1} - \cs:w @@~#1 \cs_end: - } - } +%<latexrelease>\cs_new_protected:Npn \@@_if_usable_use:n #1 +%<latexrelease> { +%<latexrelease> \tl_if_exist:cT { @@~#1 } +%<latexrelease> { +%<latexrelease> \@@_preamble_hook:n {#1} +%<latexrelease> \cs:w @@~#1 \cs_end: +%<latexrelease> } +%<latexrelease> } +%<latexrelease>\EndIncludeInRelease % \end{macrocode} % \end{macro} % \end{macro} % -% \begin{macro}{\hook_use_once:n} -% For hooks that can and should be used only once we have a special -% use command that remembers the hook name in -% \cs{g_@@_execute_immediately_prop}. This has the effect that any -% further code added to the hook is executed immediately rather -% than stored in the hook. +% \begin{macro}{\hook_use_once:n} +% \begin{macro}{\@@_use_once:n} +% For hooks that can and should be used only once we have a special +% use command that further inhibits the hook from getting more code +% added to it. This has the effect that any +% further code added to the hook is executed immediately rather +% than stored in the hook. % -% The code needs some gymnastics to prevent space trimming from the -% hook name, since \cs{hook_use:n} and \cs{hook_use_once:n} are -% documented to not trim spaces. +% The code needs some gymnastics to prevent space trimming from the +% hook name, since \cs{hook_use:n} and \cs{hook_use_once:n} are +% documented to not trim spaces. % +% \changes{v1.0r}{2021/09/06}{Clean up after \cs{UseOneTimeHook} (gh/606)} % \begin{macrocode} \cs_new_protected:Npn \hook_use_once:n #1 { - \tl_if_exist:cT { @@~#1 } + \@@_if_execute_immediately:nF {#1} + { \@@_normalize_hook_args:Nn \@@_use_once:n { \use:n {#1} } } + } +\cs_new_protected:Npn \@@_use_once:n #1 + { + \@@_preamble_hook:n {#1} + \@@_use_once_set:n {#1} + \@@_use_initialized:n {#1} + \@@_use_once_clear:n {#1} + } +% \end{macrocode} +% +% \begin{macro}{\@@_use_once_set:n} +% \begin{macro}{\@@_use_once_clear:n} +% \cs{@@_use_once_set:n} is used before the actual hook code is +% executed so that any usage of \cs{AddToHook} inside the hook causes +% the code to execute immediately. Setting +% \cs[no-index]{g_@@_\meta{hook}_reversed_tl} to |I| prevents further +% code from being added to the hook. \cs{@@_use_once_clear:n} then +% clears the hook so that any further call to \cs{hook_use:n} or +% \cs{hook_use_once:n} will expand to nothing. +% \changes{v1.0r}{2021/09/06}{Clean up after \cs{UseOneTimeHook} (gh/606)} +% \begin{macrocode} +\cs_new_protected:Npn \@@_use_once_set:n #1 + { \@@_tl_gset:cn { g_@@_#1_reversed_tl } { I } } +\cs_new_protected:Npn \@@_use_once_clear:n #1 + { + \@@_tl_gclear:c { @@~#1 } + \@@_tl_gclear:c { @@_next~#1 } + \@@_tl_gclear:c { @@_toplevel~#1 } + \prop_gclear:c { g_@@_#1_code_prop } + } +% \end{macrocode} +% \end{macro} +% \end{macro} +% \end{macro} +% \end{macro} +% +% \begin{macro}[pTF]{\@@_if_execute_immediately:n} +% To check whether the code being added should be executed immediately (that +% is, if the hook is a one-time hook), we check if it's usable (it +% can't be one-time if it was not already usable), then we check that +% \cs[no-index]{g_@@_\meta{hook}_reversed_tl} is |I|. The gymnastics +% around \cs{if:w} is there to allow the |reversed| token list to be +% empty. +% \changes{v1.0r}{2021/09/06}{Macro added (gh/606)} +% \begin{macrocode} +\prg_new_conditional:Npnn \@@_if_execute_immediately:n #1 { F, TF } + { + \@@_if_usable:nTF {#1} { - \tl_set:Nn \l_@@_return_tl {#1} - \@@_normalize_hook_args:Nn \@@_use_once_store:n - { \l_@@_return_tl } - \hook_use:n {#1} + \exp_after:wN \@@_clean_to_scan:w + \if:w I \cs:w g_@@_#1_reversed_tl \cs_end: + \s_@@_mark \prg_return_true: + \else: + \s_@@_mark \prg_return_false: + \fi: } + { \prg_return_false: } } -\cs_new_protected:Npn \@@_use_once_store:n #1 - { \prop_gput:Nnn \g_@@_execute_immediately_prop {#1} { } } % \end{macrocode} % \end{macro} % @@ -4504,9 +4966,8 @@ } % \end{macrocode} % \end{macro} -% -% -% +% +% % \begin{macro}[pTF]{\@@_if_declared:n} % % Internal test to check if the hook was officially declared with @@ -4526,28 +4987,76 @@ % \begin{macrocode} \prg_new_conditional:Npnn \@@_if_reversed:n #1 { p , T , F , TF } { - \if_int_compare:w \cs:w g_@@_#1_reversed_tl \cs_end: 1 < 0 \exp_stop_f: + \exp_after:wN \@@_clean_to_scan:w + \if:w - \cs:w g_@@_#1_reversed_tl \cs_end: + \s_@@_mark \prg_return_true: + \else: + \s_@@_mark \prg_return_false: + \fi: + } +% \end{macrocode} +% \end{macro} +% +% \begin{macro}[pTF]{\@@_if_generic:n} +% \begin{macro}[pTF]{\@@_if_deprecated_generic:n} +% An internal conditional that checks if a name belongs to a generic +% hook. The deprecated version needs to check if |#3| is empty to +% avoid returning true on \hook{file/before}, for example. +% \begin{macrocode} +\prg_new_conditional:Npnn \@@_if_generic:n #1 { T, TF } + { \@@_if_generic:w #1 / / / \s_@@_mark } +\cs_new:Npn \@@_if_generic:w #1 / #2 / #3 / #4 \s_@@_mark + { + \cs_if_exist:cTF { c_@@_generic_#1/./#3_tl } + { \prg_return_true: } + { \prg_return_false: } + } +\prg_new_conditional:Npnn \@@_if_deprecated_generic:n #1 { T, TF } + { \@@_if_deprecated_generic:w #1 / / / \s_@@_mark } +\cs_new:Npn \@@_if_deprecated_generic:w #1 / #2 / #3 / #4 \s_@@_mark + { + \cs_if_exist:cTF { c_@@_deprecated_#1/./#2_tl } + { + \tl_if_empty:nTF {#3} + { \prg_return_false: } + { \prg_return_true: } + } + { \prg_return_false: } + } +% \end{macrocode} +% \end{macro} +% \end{macro} +% +% \begin{macro}[pTF]{\@@_if_generic_reversed:n} +% An internal conditional that checks if a name belongs to a generic +% reversed hook. +% \begin{macrocode} +\prg_new_conditional:Npnn \@@_if_generic_reversed:n #1 { T } + { \@@_if_generic_reversed:w #1 / / / \scan_stop: } +\cs_new:Npn \@@_if_generic_reversed:w #1 / #2 / #3 / #4 \scan_stop: + { + \if_charcode:w - \cs:w c_@@_generic_#1/./#3_tl \cs_end: \prg_return_true: \else: \prg_return_false: \fi: } % \end{macrocode} -% \end{macro} -% +% \end{macro} % % \subsection{Messages} % -% Hook errors are LaTeX kernel errors: +% Hook errors are LaTeX kernel errors: % \begin{macrocode} \prop_gput:Nnn \g_msg_module_type_prop { hooks } { LaTeX } % \end{macrocode} +% \changes{v1.0q}{2021/08/27}{Internal message name changes} % And so are kernel errors (this should move elsewhere eventually). % \begin{macrocode} -\prop_gput:Nnn \g_msg_module_type_prop { kernel } { LaTeX } -%\prop_gput:Nnn \g_msg_module_name_prop { kernel } { } % <-- currently not working +\prop_gput:Nnn \g_msg_module_type_prop { latex2e } { LaTeX } +\prop_gput:Nnn \g_msg_module_name_prop { latex2e } { kernel } % \end{macrocode} -% +% % \begin{macrocode} \msg_new:nnnn { hooks } { labels-incompatible } { @@ -4560,7 +5069,7 @@ { 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. } @@ -4568,14 +5077,14 @@ name.\\ Please~ use~ a~ different~ name~ for~ your~ hook.} % \end{macrocode} -% +% % % \begin{macrocode} \msg_new:nnnn { hooks } { hook-disabled } { Cannot~add~code~to~disabled~hook~'#1'. } { The~hook~'#1'~you~tried~to~add~code~to~was~previously~disabled~ - with~\iow_char:N\\hook_disable:n~or~\iow_char:N\\DisableHook,~so~ + with~\iow_char:N\\hook_disable_generic:n~or~\iow_char:N\\DisableGenericHook,~so~ it~cannot~have~code~added~to~it. } % \end{macrocode} @@ -4601,7 +5110,7 @@ { Unknown~ relationship~ '#3'~ between~ labels~ '#2'~ and~ '#4'~ \str_if_eq:nnF {#1} {??} { ~in~hook~'#1' }. ~ - Perhaps~ a~ missspelling? + Perhaps~ a~ misspelling? } { The~ relation~ used~ not~ known~ to~ the~ system.~ Allowed~ values~ are~ @@ -4613,7 +5122,7 @@ 'unrelated'. } % \end{macrocode} -% +% % \begin{macrocode} \msg_new:nnnn { hooks } { misused-top-level } { @@ -4651,7 +5160,7 @@ % \end{macrocode} % % \begin{macrocode} -\msg_new:nnn { kernel } { should-not-happen } +\msg_new:nnn { latex2e } { should-not-happen } { This~should~not~happen.~#1 \\ Please~report~at~https://github.com/latex3/latex2e. @@ -4661,32 +5170,31 @@ % % % \begin{macrocode} -\msg_new:nnn { hooks } { provide-disabled } +\msg_new:nnn { hooks } { activate-disabled } { - Cannot~ provide~ hook~ '#1'~ because~ it~ is~ disabled! + Cannot~ activate~ hook~ '#1'~ because~ it~ is~ disabled! } % \end{macrocode} % % \begin{macrocode} -\msg_new:nnnn { hooks } { provide-error } +\msg_new:nnn { hooks } { cannot-remove } { - Hook~'#1'~ already~ declared~ as~ a~ - \@@_if_reversed:nTF {#1} { reversed } { normal }~ hook! + Cannot~remove~chunk~'#2'~from~hook~'#1'~because~ + \@@_if_structure_exist:nTF {#1} + { it~does~not~exist~in~that~hook. } + { the~hook~does~not~exist. } } +% \end{macrocode} +% +% \begin{macrocode} +\msg_new:nnn { hooks } { generic-deprecated } { - You~ attempted~ to~ provide~ the~ hook~'#1'~ as~ a~ - \@@_if_reversed:nTF {#1} { normal } { reversed }~ hook,~ but~ it~ - was~ already~ previously~ declared~ as~ a~ - \@@_if_reversed:nTF {#1} { reversed } { normal }~ hook.~ - A~ redeclaration~ is~ not~ possible. + Generic~hook~'#1/#2/#3'~is~deprecated. \\ + Use~hook~'#1/#3/#2'~instead. } % \end{macrocode} % % -% -% -% -% % \subsection{\LaTeXe{} package interface commands} % % @@ -4704,42 +5212,38 @@ % % \begin{macrocode} %<latexrelease>\IncludeInRelease{2021/06/01}% -%<latexrelease> {\hook_provide:n}{Providing~hooks} +%<latexrelease> {\hook_activate_generic:n}{Providing~hooks} % \end{macrocode} % -% \begin{macro}{\ProvideHook,\ProvideReversedHook,\ProvideMirroredHookPair} +% \begin{macro}{\ActivateGenericHook} % Providing new hooks \ldots % \changes{v1.0m}{2021/04/29}{Add \cs{ProvideHook} etc.} +% \changes{v1.0o}{2021/08/02}{Change name} % \begin{macrocode} -\NewDocumentCommand \ProvideHook { m }{ \hook_provide:n {#1} } -\NewDocumentCommand \ProvideReversedHook { m }{ \hook_provide_reversed:n {#1} } -\NewDocumentCommand \ProvideMirroredHookPair { mm }{ \hook_provide_pair:nn {#1}{#2} } +\NewDocumentCommand \ActivateGenericHook { m }{ \hook_activate_generic:n {#1} } % \end{macrocode} % \end{macro} % -% -% \begin{macro}{\DisableHook} -% Disabling a (generic) hook. +% \begin{macro}{\DisableGenericHook} +% Disabling a generic hook. +% \changes{v1.0o}{2021/08/02}{Change name} % \begin{macrocode} -\NewDocumentCommand \DisableHook { m }{ \hook_disable:n {#1} } +\NewDocumentCommand \DisableGenericHook { m }{ \hook_disable_generic:n {#1} } % \end{macrocode} % \end{macro} % % \begin{macrocode} %<latexrelease>\EndIncludeInRelease %<latexrelease>\IncludeInRelease{2020/10/01} -%<latexrelease> {\hook_provide:n}{Providing~hooks} +%<latexrelease> {\hook_activate_generic:n}{Providing~hooks} %<latexrelease> -%<latexrelease>\def \ProvideHook#1{} -%<latexrelease>\def \ProvideReversedHook#1{} -%<latexrelease>\def \ProvideMirroredHookPair#1#2{} +%<latexrelease>\def \ActivateGenericHook#1{} %<latexrelease> %<latexrelease>\EndIncludeInRelease % \end{macrocode} % % % \begin{macro}{\AddToHook} -% % \begin{macrocode} \NewDocumentCommand \AddToHook { m o +m } { \hook_gput_code:nnn {#1} {#2} {#3} } @@ -4747,16 +5251,22 @@ % \end{macro} % % \begin{macro}{\AddToHookNext} -% % \begin{macrocode} \NewDocumentCommand \AddToHookNext { m +m } { \hook_gput_next_code:nn {#1} {#2} } % \end{macrocode} % \end{macro} % +% \begin{macro}{\ClearHookNext} +% \changes{v1.0o}{2021/07/27}{Macro added} +% \begin{macrocode} +\NewDocumentCommand \ClearHookNext { m } + { \hook_gclear_next_code:n {#1} } +% \end{macrocode} +% \end{macro} +% % % \begin{macro}{\RemoveFromHook} -% % \begin{macrocode} \NewDocumentCommand \RemoveFromHook { m o } { \hook_gremove_code:nn {#1} {#2} } @@ -4766,140 +5276,11 @@ % \begin{macro}{\SetDefaultHookLabel} % \begin{macro}{\PushDefaultHookLabel} % \begin{macro}{\PopDefaultHookLabel} -% -% \fmiinline{Docu task: At some point this code for this should be moved to the -% label section earlier and here we should keep only the interface commands.} -% -% \begin{macro}{\@@_curr_name_push:n,\@@_curr_name_push_aux:n} -% \begin{macro}{\@@_curr_name_pop:} -% \begin{macro}{\@@_end_document_label_check:} -% The token list \cs{g_@@_hook_curr_name_tl} stores the name of the -% current package/file to be used as label for hooks. -% Providing a consistent interface is tricky, because packages can -% be loaded within packages, and some packages may not use -% \cs{SetDefaultHookLabel} to change the default label (in which -% case \cs{@currname} is used). -% -% To pull that one off, we keep a stack that contains the default -% label for each level of input. The bottom of the stack contains the -% default label for the |top-level| (this stack should never go -% empty). If we're building the format, set the default label to be -% |top-level|: -% \begin{macrocode} -\tl_gset:Nn \g_@@_hook_curr_name_tl { top-level } -% \end{macrocode} -% -% Then, in case we're in \pkg{latexrelease} we push something on -% the stack to support roll forward. But in some rare cases, -% \pkg{latexrelease} may be loaded inside another package (notably -% \pkg{platexrelease}), so we'll first push the |top-level| entry: -% \changes{v1.0i}{2021/03/18} -% {Only add \texttt{top-level} if not already there.} -% \begin{macrocode} -%<latexrelease>\seq_if_empty:NT \g_@@_name_stack_seq -%<latexrelease> { \seq_gput_right:Nn \g_@@_name_stack_seq { top-level } } -% \end{macrocode} -% then we dissect the \cs{@currnamestack}, adding \cs{@currname} to -% the stack: -% \changes{v1.0f}{2020/11/24}{Support for roll forward (gh/434)} -% \begin{macrocode} -%<latexrelease>\cs_set_protected:Npn \@@_tmp:w #1 #2 #3 -%<latexrelease> { -%<latexrelease> \quark_if_recursion_tail_stop:n {#1} -%<latexrelease> \seq_gput_right:Nn \g_@@_name_stack_seq {#1} -%<latexrelease> \@@_tmp:w -%<latexrelease> } -%<latexrelease>\exp_after:wN \@@_tmp:w \@currnamestack -%<latexrelease> \q_recursion_tail \q_recursion_tail -%<latexrelease> \q_recursion_tail \q_recursion_stop -% \end{macrocode} -% and finally set the default label to be the \cs{@currname}: -% \changes{v1.0i}{2021/03/18} -% {Remove the (empty) \enquote{top-level} from \cs{@currnamestack}.} -% \begin{macrocode} -%<latexrelease>\tl_gset:Nx \g_@@_hook_curr_name_tl { \@currname } -%<latexrelease>\seq_gpop_right:NN \g_@@_name_stack_seq \l_@@_tmpa_tl -% \end{macrocode} -% -% Two commands keep track of the stack: when a file is input, -% \cs{@@_curr_name_push:n} pushes the current default label to the -% stack, and sets the new default label in one go: -% \begin{macrocode} -\cs_new_protected:Npn \@@_curr_name_push:n #1 - { \exp_args:Nx \@@_curr_name_push_aux:n { \@@_make_name:n {#1} } } -\cs_new_protected:Npn \@@_curr_name_push_aux:n #1 - { - \tl_if_blank:nTF {#1} - { \msg_error:nn { hooks } { no-default-label } } - { - \str_if_eq:nnTF {#1} { top-level } - { - \msg_error:nnnnn { hooks } { set-top-level } - { to } { PushDefaultHookLabel } {#1} - } - { - \seq_gpush:NV \g_@@_name_stack_seq \g_@@_hook_curr_name_tl - \tl_gset:Nn \g_@@_hook_curr_name_tl {#1} - } - } - } -% \end{macrocode} -% and when an input is over, the topmost item of the stack is popped, -% since the label will not be used again, and \cs{g_@@_hook_curr_name_tl} -% is updated to the now topmost item of the stack: -% \begin{macrocode} -\cs_new_protected:Npn \@@_curr_name_pop: - { - \seq_gpop:NNTF \g_@@_name_stack_seq \l_@@_return_tl - { \tl_gset_eq:NN \g_@@_hook_curr_name_tl \l_@@_return_tl } - { \msg_error:nn { hooks } { extra-pop-label } } - } -% \end{macrocode} -% -% At the end of the document we want to check if there was no -% \cs{@@_curr_name_push:n} without a matching \cs{@@_curr_name_pop:} -% (not a critical error, but it might indicate that something else is -% not quite right): -% \begin{macrocode} -\tl_gput_right:Nn \@kernel@after@enddocument@afterlastpage - { \@@_end_document_label_check: } -\cs_new_protected:Npn \@@_end_document_label_check: - { - \seq_gpop:NNT \g_@@_name_stack_seq \l_@@_return_tl - { - \msg_error:nnx { hooks } { missing-pop-label } - { \g_@@_hook_curr_name_tl } - \tl_gset_eq:NN \g_@@_hook_curr_name_tl \l_@@_return_tl - \@@_end_document_label_check: - } - } -% \end{macrocode} -% -% The token list \cs{g_@@_hook_curr_name_tl} is but a mirror of the top -% of the stack. -% % Now define a wrapper that replaces the top of the stack with the % argument, and updates \cs{g_@@_hook_curr_name_tl} accordingly. % \begin{macrocode} \NewDocumentCommand \SetDefaultHookLabel { m } - { - \seq_if_empty:NTF \g_@@_name_stack_seq - { - \msg_error:nnnnn { hooks } { set-top-level } - { for } { SetDefaultHookLabel } {#1} - } - { \exp_args:Nx \@@_set_default_label:n { \@@_make_name:n {#1} } } - } -\cs_new_protected:Npn \@@_set_default_label:n #1 - { - \str_if_eq:nnTF {#1} { top-level } - { - \msg_error:nnnnn { hooks } { set-top-level } - { to } { SetDefaultHookLabel } {#1} - } - { \tl_gset:Nn \g_@@_hook_curr_name_tl {#1} } - } -% \end{macrocode} + { \@@_set_default_hook_label:n {#1} } % % The label is only automatically updated with \cs{@onefilewithoptions} % (\cs{usepackage} and \cs{documentclass}), but some packages, like @@ -4907,7 +5288,7 @@ % \cs{usetikzlibrary} that are wrappers around \cs{input}, so they % inherit the default label currently in force (usually |top-level|, % but it may change if loaded in another package). To provide a -% package-like behaviour also for hooks in these files, we provide +% package-like behavior also for hooks in these files, we provide % high-level access to the default label stack. % \begin{macrocode} \NewDocumentCommand \PushDefaultHookLabel { m } @@ -4935,9 +5316,6 @@ % \end{macro} % \end{macro} % \end{macro} -% \end{macro} -% \end{macro} -% \end{macro} % % % @@ -4961,7 +5339,7 @@ % \end{macro} % % \begin{macro}{\DebugHooksOn,\DebugHooksOff} -% +% % \begin{macrocode} \cs_new_protected:Npn \DebugHooksOn { \hook_debug_on: } \cs_new_protected:Npn \DebugHooksOff { \hook_debug_off: } @@ -4971,7 +5349,7 @@ % % % \begin{macro}{\DeclareHookRule} -% +% % \begin{macrocode} \NewDocumentCommand \DeclareHookRule { m m m m } { \hook_gset_rule:nnnn {#1}{#2}{#3}{#4} } @@ -5001,7 +5379,7 @@ % \begin{macro}[EXP]{\IfHookEmptyTF} % Here we avoid the overhead of \pkg{xparse}, since \cs{IfHookEmptyTF} % is used in \cs{end} (that is, every \LaTeX{} environment). As a -% further optimisation, use \cs{let} rather than \cs{def} to avoid one +% further optimization, use \cs{let} rather than \cs{def} to avoid one % expansion step. % \begin{macrocode} \cs_new_eq:NN \IfHookEmptyTF \hook_if_empty:nTF @@ -5025,6 +5403,111 @@ % %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% % +% \subsection{Deprecated that needs cleanup at some point} +% +% \changes{v1.0p}{2021/08/20}{Added deprecation warnings for +% old generic hook commands (gh/638)} +% +% \begin{macro}{ +% \hook_disable:n, +% \hook_provide:n, +% \hook_provide_reversed:n, +% \hook_provide_pair:nn, +% \@@_activate_generic_reversed:n, +% \@@_activate_generic_pair:nn, +% } +% Deprecated. +% \begin{macrocode} +\cs_new_protected:Npn \hook_disable:n + { + \@@_deprecated_warn:nn + { hook_disable:n } + { hook_disable_generic:n } + \hook_disable_generic:n + } +\cs_new_protected:Npn \hook_provide:n + { + \@@_deprecated_warn:nn + { hook_provide:n } + { hook_activate_generic:n } + \hook_activate_generic:n + } +\cs_new_protected:Npn \hook_provide_reversed:n + { + \@@_deprecated_warn:nn + { hook_provide_reversed:n } + { hook_activate_generic:n } + \@@_activate_generic_reversed:n + } +\cs_new_protected:Npn \hook_provide_pair:nn + { + \@@_deprecated_warn:nn + { hook_provide_pair:nn } + { hook_activate_generic:n } + \@@_activate_generic_pair:nn + } +\cs_new_protected:Npn \@@_activate_generic_reversed:n #1 + { \@@_normalize_hook_args:Nn \@@_activate_generic:nn {#1} { - } } +\cs_new_protected:Npn \@@_activate_generic_pair:nn #1#2 + { \hook_activate_generic:n {#1} \@@_activate_generic_reversed:n {#2} } +% \end{macrocode} +% \end{macro} +% +% \begin{macro}{ +% \DisableHook, +% \ProvideHook, +% \ProvideReversedHook, +% \ProvideMirroredHookPair, +% } +% Deprecated. +% \begin{macrocode} +\cs_new_protected:Npn \DisableHook + { + \@@_deprecated_warn:nn + { DisableHook } + { DisableGenericHook } + \hook_disable_generic:n + } +\cs_new_protected:Npn \ProvideHook + { + \@@_deprecated_warn:nn + { ProvideHook } + { ActivateGenericHook } + \hook_activate_generic:n + } +\cs_new_protected:Npn \ProvideReversedHook + { + \@@_deprecated_warn:nn + { ProvideReversedHook } + { ActivateGenericHook } + \@@_activate_generic_reversed:n + } +\cs_new_protected:Npn \ProvideMirroredHookPair + { + \@@_deprecated_warn:nn + { ProvideMirroredHookPair } + { ActivateGenericHook } + \@@_activate_generic_pair:nn + } +% \end{macrocode} +% \end{macro} +% +% \begin{macro}{\@@_deprecated_warn:nn} +% Warns about a deprecation, telling what should be used instead. +% \begin{macrocode} +\cs_new_protected:Npn \@@_deprecated_warn:nn #1 #2 + { \msg_warning:nnnn { hooks } { deprecated } {#1} {#2} } +\msg_new:nnn { hooks } { deprecated } + { + Command~\iow_char:N\\#1~is~deprecated~and~will~be~removed~in~a~ + future~release. \\ \\ + Use~\iow_char:N\\#2~instead. + } +% \end{macrocode} +% \end{macro} +% +%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% +% % % \subsection{Internal commands needed elsewhere} % @@ -5068,7 +5551,7 @@ %<latexrelease>\def \NewReversedHook#1{} %<latexrelease>\def \NewMirroredHookPair#1#2{} %<latexrelease> -%<latexrelease>\def \DisableHook #1{} +%<latexrelease>\def \DisableGenericHook #1{} %<latexrelease> %<latexrelease>\long\def\AddToHookNext#1#2{} %<latexrelease> @@ -5105,6 +5588,6 @@ % \Finale % % -%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% +%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% \endinput -%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% +%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% |