diff options
author | Norbert Preining <norbert@preining.info> | 2021-05-05 03:02:17 +0000 |
---|---|---|
committer | Norbert Preining <norbert@preining.info> | 2021-05-05 03:02:17 +0000 |
commit | 75db33dc347ec9cfbaa2f3ec538a2697351f571b (patch) | |
tree | 0ea6d721f8478ed9ae95c93330c94aa8fcfd30b8 /macros/latex-dev/base/lthooks.dtx | |
parent | 342f2e1be699f5ec70b5b1c35d9cd59798d37304 (diff) |
CTAN sync 202105050302
Diffstat (limited to 'macros/latex-dev/base/lthooks.dtx')
-rw-r--r-- | macros/latex-dev/base/lthooks.dtx | 1186 |
1 files changed, 889 insertions, 297 deletions
diff --git a/macros/latex-dev/base/lthooks.dtx b/macros/latex-dev/base/lthooks.dtx index 803914b4a7..45d6a78aba 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.0h} -\def\lthooksdate{2021/01/15} +\def\lthooksversion{v1.0m} +\def\lthooksdate{2021/04/29} % \end{macrocode} % %<*driver> @@ -67,10 +67,13 @@ % \fi % % -% \long\def\fmi#1{\begin{quote}\itshape FMi: #1\end{quote}} -% \long\def\pho#1{\begin{quote}\itshape PhO: #1\end{quote}} +% \providecommand\hook[1]{\texttt{#1}} +% +% \providecommand\fmi[1]{\marginpar{\footnotesize FMi: #1}} +% \providecommand\fmiinline[1]{\begin{quote}\itshape\footnotesize FMi: #1\end{quote}} +% \providecommand\pho[1]{\marginpar{\footnotesize PhO: #1}} +% \providecommand\phoinline[1]{\begin{quote}\itshape\footnotesize PhO: #1\end{quote}} % -% \newcommand\hook[1]{\texttt{#1}} % % % \title{The \texttt{lthooks} package\thanks{This package has version @@ -122,14 +125,15 @@ % % \subsection{\LaTeXe\ interfaces} % -% \subsubsection{Declaring hooks and using them in code} +% \subsubsection{Declaring hooks} % -% With two exceptions, hooks have to be declared before they can be -% used. The exceptions are hooks in 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 environment or the file name and so declaring them -% beforehand is difficult. +% 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. % % % \begin{function}{\NewHook} @@ -176,6 +180,62 @@ % \end{function} % % +% \subsubsection{Special declarations for 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} +% \begin{syntax} +% \cs{DisableHook} \Arg{hook} +% \end{syntax} +% After this declaration 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. +% +% This is intended to be used with generic command hooks (see +% \texttt{ltcmdhooks-doc}) as depending on the definition of the +% command such generic hooks may be unusable. If that is known, a +% package developer can disable such hooks up front. +% +% The \meta{hook} can be specified using the dot-syntax to denote +% the current package name. See section~\ref{sec:default-label}. +% \end{function} +% +% +% \begin{function}{\ProvideHook} +% \begin{syntax} +% \cs{ProvideHook} \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. +% \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} +% +% +% +% \subsubsection{Using hooks in code} % % % \begin{function}{\UseHook} @@ -201,7 +261,7 @@ % Some hooks are only used (and can be only used) in one place, for % example, those in \verb=\begin{document}= or % \verb=\end{document}=. Once we have passed that point adding to -% the hook through a defined \cs{\meta{addto-cmd}} command (e.g., +% the hook through a defined \cs[no-index]{\meta{addto-cmd}} command (e.g., % \cs{AddToHook} or \cs{AtBeginDocument}, etc.\@) would have no % effect (as would the use of such a command inside the hook code % itself). It is therefore customary to redefine @@ -212,10 +272,12 @@ % consumed and any further attempt to add to it will result in % executing the code to be added immediately. % -% \fmi{Maybe add an error version as well?} +% \fmiinline{Maybe add an error version as well?} % % The \meta{hook} \emph{cannot} be specified using the dot-syntax. -% A leading |.| is treated literally. +% A leading |.| is treated literally. See +% section~\ref{sec:default-label} for details. + % \end{function} % % @@ -264,9 +326,10 @@ % that label, the removal order takes action and the code is not % added. % -% If the optional argument is \texttt{*}, then all code chunks are +% 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! +% packages one may not know about and should therefore not by used +% by 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. @@ -299,7 +362,7 @@ %\end{verbatim} % because that only \enquote{adds} a further empty chunk of code to % the hook. Adding \cs{normalsize} would work but that means the hook -% then contained \cs{small}\cs{normalsize} which means to font size +% then contained \cs{small}\cs{normalsize} which means two font size % changes for no good reason. % % The above is only needed if one wants to typeset several quotes in a @@ -359,10 +422,9 @@ % \meta{label} throughout the sub-packages in order to avoid % that the labels change if you internally reorganize your code. % -% Except for \cs{UseHook}, \cs{UseOneTimeHook}, \cs{IfHookEmptyTF}, and -% \cs{IfHookExistsTF} (and their \pkg{expl3} interfaces \cs{hook_use:n}, -% \cs{hook_use_once:n}, \cs{hook_if_empty:nTF}, and -% \cs{hook_if_exist:nTF}), all \meta{hook} +% 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} % 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 @@ -370,8 +432,8 @@ % token, a low-level \TeX{} error is raised (namely, the \meta{hook} is % expanded using \TeX's \cs{csname}\ldots\cs{endcsname}, as such, % Unicode characters are allowed in \meta{hook} and \meta{label} -% arguments). The arguments of \cs{UseHook}, \cs{UseOneTimeHook}, -% \cs{IfHookEmptyTF}, and \cs{IfHookExistsTF} are +% arguments). The arguments of \cs{UseHook}, \cs{UseOneTimeHook}, and +% \cs{IfHookEmptyTF} are % processed much in the same way except that spaces are not trimmed % around the argument, for better performance. % @@ -425,8 +487,7 @@ % this syntax is not available in \cs{UseHook} (and \cs{hook_use:n}) % because the hook is most of the time used outside of the package file % in which it was defined. This syntax is also not available in the hook -% conditionals \cs{IfHookEmptyTF} (and \cs{hook_if_empty:nTF}) and -% \cs{IfHookExistsTF} (and \cs{hook_if_exist:nTF}) because these +% conditionals \cs{IfHookEmptyTF} (and \cs{hook_if_empty:nTF}), because these % conditionals are used in some performance-critical parts of the hook % management code, and because they are usually used to refer to other % package's hooks, so the dot-syntax doesn't make much sense. @@ -435,7 +496,7 @@ % it in logical parts, but still use the main package name as % \meta{label}, then the \meta{default label} can be set using % \cs{SetDefaultHookLabel} or -% \cs{PushDefaultHookLabel}..\cs{PopDefaultHookLabel}. +% \cs{PushDefaultHookLabel}\verb={..}=\,\ldots\cs{PopDefaultHookLabel}. % % \begin{function}{\PushDefaultHookLabel,\PopDefaultHookLabel} % \begin{syntax} @@ -651,9 +712,10 @@ % \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: they have four possible states. -% A hook may exist or not, and either way it may or may not be empty. -% This means that even a hook that doesn't exist may be non-empty. +% Hooks are a bit more complicated: +% a hook may exist or not, and either way 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. % % This seemingly strange state may happen when, for example, package~$A$ % defines hook \hook{A/foo}, and package $B$ adds some code to that @@ -664,11 +726,17 @@ % querying the existence of a hook doesn't imply its emptiness, neither % does the other way around. % +% Given that code or rules can be added to a hook even if it doesn't +% physically exist yet, means that a querying its existence has no +% real use case (in contrast to other variables that can only be +% update if they have already been declared). For that reason only the +% test for emptiness has a public interface. +% % A hook is said to be empty when no code was added to it, either to % its permanent code pool, or to its ``next'' token list. The hook % doesn't need to be declared to have code added to its code pool. % A hook is said to exist when it was declared with \cs{NewHook} or -% some variant thereof. Generic \hook{file} and \hook{env} hooks are +% some variant thereof. Generic hooks such as \hook{file} and \hook{env} hooks are % automatically declared when code is added to them. % % \begin{function}[EXP]{\IfHookEmptyTF} @@ -676,7 +744,8 @@ % \cs{IfHookEmptyTF} \Arg{hook} \Arg{true code} \Arg{false code} % \end{syntax} % Tests if the \meta{hook} is empty (\emph{i.e.}, no code was added to -% it using either \cs{AddToHook} or \cs{AddToHookNext}), and +% it using either \cs{AddToHook} or \cs{AddToHookNext}) or such code +% was removed again (via \cs{RemoveFromHook}), and % branches to either \meta{true code} or \meta{false code} depending % on the result. % @@ -684,30 +753,7 @@ % A leading |.| is treated literally. % \end{function} % -% \begin{function}[EXP]{\IfHookExistsTF} -% \begin{syntax} -% \cs{IfHookExistsTF} \Arg{hook} \Arg{true code} \Arg{false code} -% \end{syntax} -% Tests if the \meta{hook} exists (if it was created with either -% \cs{NewHook}, \cs{NewReversedHook}, or \cs{NewMirroredHookPair}), and -% branches to either \meta{true code} or \meta{false code} depending -% on the result. -% -% The existence of a hook usually doesn't mean much from the viewpoint -% of code that tries to add/remove code from that hook, since package -% loading order may vary, thus the creation of hooks is asynchronous -% to adding and removing code from it, so this test should be used -% sparingly. -% -% Generic hooks are declared at the time code is added to them, so the -% result of \cs{hook_if_exist:n} will change once code is added to -% said hook (unless the hook was previously declared). % -% The \meta{hook} \emph{cannot} be specified using the dot-syntax. -% A leading |.| is treated literally. -% \end{function} -% -% \fmi{Would be helpful if we provide some use cases} % % \subsubsection{Displaying hook code} % @@ -822,7 +868,7 @@ % \begin{syntax} % \cs{DebugHooksOn} % \end{syntax} -% Turn the debugging of hook code on or off. This displays changes +% Turn the debugging of hook code on or off. This displays most changes % made to the hook data structures. The output is rather coarse and % not really intended for normal use. % \end{function} @@ -859,6 +905,53 @@ % % % +% \begin{function}{\hook_disable:n} +% \begin{syntax} +% \cs{hook_disable: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 +% \cs{hook_use:n} will simply do nothing. +% +% This declaration is intended for use with generic hooks that are +% known not to work (see \texttt{ltcmdhooks-doc}) if they receive +% code. +% +% The \meta{hook} can be specified using the dot-syntax to denote +% the current package name. See section~\ref{sec:default-label}. +% \end{function} +% +% \begin{function}{\hook_provide:n} +% \begin{syntax} +% \cs{hook_provide: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 +% 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} +% +% +% % \begin{function}{\hook_use:n} % \begin{syntax} % \cs{hook_use:n} \Arg{hook} @@ -971,28 +1064,6 @@ % A leading |.| is treated literally. % \end{function} % -% \begin{function}[pTF]{\hook_if_exist:n} -% \begin{syntax} -% \cs{hook_if_exist:nTF} \Arg{hook} \Arg{true code} \Arg{false code} -% \end{syntax} -% Tests if the \meta{hook} exists (if it was created with either -% \cs{NewHook}, \cs{NewReversedHook}, or \cs{NewMirroredHookPair}), and -% branches to either \meta{true code} or \meta{false code} depending -% on the result. -% -% The existence of a hook usually doesn't mean much from the viewpoint -% of code that tries to add/remove code from that hook, since package -% loading order may vary, thus the creation of hooks is asynchronous -% to adding and removing code from it, so this test should be used -% sparingly. -% -% Generic hooks are declared at the time code is added to them, so the -% result of \cs{hook_if_exist:n} will change once code is added to -% said hook (unless the hook was previously declared). -% -% The \meta{hook} \emph{cannot} be specified using the dot-syntax. -% A leading |.| is treated literally. -% \end{function} % % \begin{function}{\hook_show:n,\hook_log:n} % \begin{syntax} @@ -1262,8 +1333,8 @@ % For that reason such code is not using the hook management, but % instead private kernel commands directly before or after a public % hook with the following naming -% convention: \cs{@kernel@before@\meta{hookname}} or -% \cs{@kernel@after@\meta{hookname}}. For example, in +% convention: \cs{@kernel@before@\meta{hook}} or +% \cs{@kernel@after@\meta{hook}}. For example, in % \cs{enddocument} you find %\begin{verbatim} % \UseHook{enddocument}% @@ -1412,7 +1483,7 @@ % % The hooks are only executed if \cs{begin}\Arg{env} and % \cs{end}\Arg{env} is used. If the environment code is executed -% via low-level calls to \cs{\meta{env}} and \cs{end\meta{env}} +% via low-level calls to \cs[no-index]{\meta{env}} and \cs[no-index]{end\meta{env}} % (e.g., to avoid the environment grouping) they are not % available. If you want them available in code using this method, % you would need to add them yourself, i.e., write something like @@ -1455,6 +1526,38 @@ % \end{function} % % +% \subsubsection{Generic hooks for commands} +% +% Similar to environments there are now (at least in theory) two +% generic hooks available for any \LaTeX{} command. These are +% \begin{description} +% \item[\hook{cmd/\meta{name}/before}] +% +% This hook is executed at the very start of the command +% execution. +% +% \item[\hook{cmd/\meta{name}/after}] +% This hook is executed at the very end of the command body. It is +% implemented as a reversed hook. +% \end{description} +% In practice there are restrictions and especially the +% \hook{after} hook works only with a subset of commands. Details +% about these restrictions are documented in +% \texttt{ltcmdhooks-doc.pdf} or with code in +% \texttt{ltcmdhooks-code.pdf}. +% +% +% +% +% \subsubsection{Generic hooks provided by file loading operations} +% +% There are several hooks added to \LaTeX{}'s process of loading +% file via its high-level interfaces such as \cs{input}, +% \cs{include}, \cs{usepackage}, \cs{RequirePackage}, etc. These +% are documented in \texttt{ltfilehook-doc.pdf} or with code in +% \texttt{ltfilehook-code.pdf}. +% +% % % \subsubsection{Hooks provided by \cs{begin}\texttt{\{document\}}} % \label{sec:begindocument-hooks} @@ -1554,7 +1657,9 @@ % generates material for further pages. It is the right place to % do some final housekeeping and possibly write out some % information to the \texttt{.aux} file (which is still open at -% this point to receive data). It is also the correct place to +% this point to receive data, but since there will be no more pages you +% need to write to it using \cs{immediate}\cs{write}). It is also the +% correct place to % set up any testing code to be run when the \texttt{.aux} file % is re-read in the next step. % @@ -1621,7 +1726,7 @@ % % % -% \subsubsection{Hooks provided \cs{shipout} operations} +% \subsubsection{Hooks provided by \cs{shipout} operations} % \label{sec:shipout} % % There are several hooks and mechanisms added to \LaTeX{}'s @@ -1630,14 +1735,6 @@ % \texttt{ltshipout-code.pdf}. % % -% \subsubsection{Hooks provided by file loading operations} -% -% There are several hooks added to \LaTeX{}'s -% process of loading file via its high-level interfaces such as -% \cs{input}, \cs{include}, \cs{usepackage}, etc. These are documented in -% \texttt{ltfilehook-doc.pdf} or with code in -% \texttt{ltfilehook-code.pdf}. -% % % \subsubsection{Hooks provided in NFSS commands} % @@ -1706,27 +1803,19 @@ % \section{The Implementation} % % -% \subsection{Loading further extensions} -% % \begin{macrocode} %<@@=hook> % \end{macrocode} % -% -% At the moment the whole module rolls back in one go, but if we -% make any modifications in later releases this will then need -% splitting. +% \changes{v1.0i}{2021/03/18} +% {Use \cs{NewModuleRelease}.} % \begin{macrocode} %<*2ekernel|latexrelease> -%<latexrelease>\IncludeInRelease{2020/10/01}% -%<latexrelease> {\NewHook}{The hook management}% -% \end{macrocode} -% -% \begin{macrocode} \ExplSyntaxOn +%<latexrelease>\NewModuleRelease{2020/10/01}{lthooks} +%<latexrelease> {The~hook~management~system} % \end{macrocode} % -% % \subsection{Debugging} % % \begin{macro}{\g_@@_debug_bool} @@ -1856,8 +1945,9 @@ % \end{macrocode} % \end{macro} % -% \begin{macro}{\tl_gremove_once:Nx,\tl_show:x,\tl_log:x} -% Some variants of \pkg{expl3} functions. \fmi{should be moved to expl3} +% \begin{macro}[int]{\tl_gremove_once:Nx,\tl_show:x,\tl_log:x} +% Some variants of \pkg{expl3} functions. +% \fmiinline{should probably be moved to expl3} % \begin{macrocode} \cs_generate_variant:Nn \tl_gremove_once:Nn { Nx } \cs_generate_variant:Nn \tl_show:n { x } @@ -1949,25 +2039,30 @@ % \end{macro} % % +% % \subsection{Providing new hooks} % -% \begin{macro}{\g_@@_..._code_prop,\@@~...,\@@_next~...} +% \subsubsection{The data structures of a hook} % -% Hooks have a \meta{name} and for each hook we have to provide a number of -% data structures. These are +% \DescribeMacro{\g_@@_\meta{hook}_code_prop} +% \DescribeMacro{\@@\textvisiblespace\meta{hook}} +% \DescribeMacro{\@@_next\textvisiblespace\meta{hook}} +% Hooks have a name (called \meta{hook} in the description below) +% and for each hook we have to +% provide a number of data structures. These are % \begin{description} -% \item[\cs{g_@@_\meta{name}_code_prop}] A property list holding the code +% \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. % -% \item[{\cs[no-index]{g_@@_\meta{name}_rule_\meta{label1}\string|\meta{label2}_tl}}] +% \item[{\cs[no-index]{g_@@_\meta{hook}_rule_\meta{label1}\string|\meta{label2}_tl}}] % A token list holding the relation between \meta{label1} and -% \meta{label2} in the \meta{name}. The \meta{labels} are lexically +% \meta{label2} in the \meta{hook}. The \meta{labels} are lexically % (reverse) sorted to ensure that two labels always point to the same -% token list. For global rules, the \meta{name} is |??|. +% token list. For global rules, the \meta{hook} name is |??|. % -% \item[\cs{@@~\meta{name}}] The code that is actually executed +% \item[\cs{@@\textvisiblespace\meta{hook}}] The code that is actually executed % when the hook is called in the document is stored in this token % list. It is constructed from the code chunks applying the % information. @@ -1976,54 +2071,194 @@ % and to make it simpler to normalize hook names in % \cs{@@_make_name:n}. % -% \item[\cs{g_@@_\meta{name}_reversed_tl}] Some hooks are +% \item[\cs{g_@@_\meta{hook}_reversed_tl}] Some hooks are % \enquote{reversed}. This token list stores a |-| for such hook % so that it can be identified. The |-| character is used because % $\meta{reversed}1$ is $+1$ for normal hooks and $-1$ for reversed % ones. + +% \item[{\cs[no-index]{g_@@_\meta{hook}_declared_tl}}] This token +% list serves as marker for the hook being officially declared. Its +% existence is tested to raise an error in case another declaration +% is attempted. % -% \item[\cs{@@_toplevel~\meta{name}}] This token list stores the code +% \item[\cs{@@_toplevel\textvisiblespace\meta{hook}}] This token list stores the code % inserted in the hook from the user's document, in the |top-level| % label. This label is special, and doesn't participate in sorting. % Instead, all code is appended to it and executed after (or before, % if the hook is reversed) the normal % hook code, but before the |next| code chunk. % -% \item[\cs{@@_next~\meta{name}}] Finally there is extra code -% (normally empty) that is used on the next invocation of the hook -% (and then deleted). This can be used to define some special -% behavior for a single occasion from within the document. This token -% list follows the same naming scheme than the main \cs{@@~\meta{name}} -% token list. It is called \cs{@@_next~\meta{name}} rather than -% \cs{@@~next_\meta{name}} because otherwise a hook whose name is -% |next_|\meta{name} would clash with the next code-token list of the -% hook called \meta{name}. +% \item[\cs{@@_next\textvisiblespace\meta{hook}}] Finally there is +% extra code (normally empty) that is used on the next invocation +% of the hook (and then deleted). This can be used to define some +% special behavior for a single occasion from within the document. +% This token list follows the same naming scheme than the main +% \cs{@@\textvisiblespace\meta{hook}} token list. It is called +% \cs{@@_next\textvisiblespace\meta{hook}} rather than +% \cs[no-index]{@@\textvisiblespace next_\meta{hook}} because +% otherwise a hook whose name is |next_|\meta{hook} would clash +% with the next code-token list of the hook called \meta{hook}. % % \end{description} -% \end{macro} % % +% \subsubsection{On the existence of hooks} +% \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 +% different states are distinguished. The actual implementation +% then follows in the next sections. +% +% 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. +% +% We therefore distinguish the following states for a hook and they +% are managed with 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}) +% \begin{description} +% \setlist[itemize]{leftmargin=5cm,format=\cs} +% \item[not existing] +% +% Nothing is known about the hook so far. This state can be +% detected with \cs{@@_if_structure_exist:nTF} +% (which uses the false branch). +% +% In this state the hook can be declared, disabled, rules can be +% defined or code could be added to it, but it is not possible +% to use the hook (with \cs{UseHook}). + +% \item[basic data structure set up] +% +% A hook is this state when its basic data structure has been +% set up (using \cs{@@_init_structure:n}). The data structure setup happens +% automatically when commands such as \cs{AddToHook} are used +% and the hook is at that point in state \enquote{not existing}. +% +% In this state the four tests give the following results: +% \begin{itemize} +% \item [@@_if_structure_exist:nTF] returns |true|. +% \item [@@_if_usable:nTF] returns |false|. +% \item [@@_if_declared:nTF] returns |false|. +% \item [@@_if_disabled:nTF] returns |false|. +% \end{itemize} +% +% The allowed acctions 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., +% with \cs{NewHook}). In this case the four tests give the +% following results: +% \begin{itemize} +% \item [@@_if_structure_exist:nTF] returns |true|. +% \item [@@_if_usable:nTF] returns |true|. +% \item [@@_if_declared:nTF] returns |true|. +% \item [@@_if_disabled:nTF] returns |false|. +% \end{itemize} +% +% \item[usable] +% +% A hook is in this state if it is not disabled, was not +% explicitly declared but nevertheless is allowed to be used +% (with \cs{UseHook} or \cs{hook_use:n}). This state is only +% possible for generic hooks as they do not need to be +% declared. Therefore such hooks move directly from state +% \enquote{not existing} to \enquote{usable} the moment a +% declaration such as \cs{AddToHook} wants to add to the hook +% data structure. In this state the tests give the following +% results: +% \begin{itemize} +% \item [@@_if_structure_exist:nTF] returns |true|. +% \item [@@_if_usable:nTF] returns |true|. +% \item [@@_if_declared:nTF] returns |false|. +% \item [@@_if_disabled:nTF] returns |false|. +% \end{itemize} +% +% +% \item[disabled] +% +% A hook in any state is moved to this state when +% \cs{DisableHook} is used. This changes the tests to give the +% following results: +% \begin{itemize} +% \item [@@_if_structure_exist:nTF] \emph{unchanged}. +% \item [@@_if_usable:nTF] returns |false|. +% \item [@@_if_declared:nTF] returns |true|. +% \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 +% 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 +% \cs{AddToHook} can return an error. +% \fmiinline{maybe it should do this only after begin document?} +% +% \end{description} +% +% +% +% +% \subsubsection{Setting hooks up} % % -% \begin{macro}{\hook_new:n} -% The \cs{hook_new:n} declaration declare a new hook and expects +% \begin{macro}{\hook_new:n,\@@_new:n} +% The \cs{hook_new:n} declaration declares a new hook and expects % the hook \meta{name} as its argument, e.g., % \hook{begindocument}. % \begin{macrocode} \cs_new_protected:Npn \hook_new:n #1 { \@@_normalize_hook_args:Nn \@@_new:n {#1} } +% \end{macrocode} +% +% \begin{macrocode} \cs_new_protected:Npn \@@_new:n #1 { % \end{macrocode} -% We check for one of the internal data structures and if it -% already exists we complain. +% We check if the hook was already \emph{explicitly} declared with +% \cs{hook_new:n}, and if it already exists we complain, otherwise set +% the \enquote{created} flag for the hook so that it errors next time +% \cs{hook_new:n} is used. +% \begin{macrocode} + \@@_if_declared:nTF {#1} + { \__kernel_msg_error:nnn { hooks } { exists } {#1} } + { + \tl_new:c { g_@@_#1_declared_tl } + \@@_make_usable:n {#1} + } + } +% \end{macrocode} +% \end{macro} +% +% +% +% \begin{macro}{\@@_make_usable:n} +% +% This initializes all hook data structures for the hook but if +% used on its own doesn't mark the hook as declared (as +% \cs{hook_new:n} does, so a later \cs{hook_new:n} on that hook will +% not result in an error. This command is internally used by +% \cs{hook_gput_code:n} when adding code to a generic hook. % \begin{macrocode} - \hook_if_exist:nTF {#1} - { \msg_error:nnn { hooks } { exists } {#1} } +\cs_new_protected:Npn \@@_make_usable:n #1 + { % \end{macrocode} -% Otherwise we add the hook name to the list of all hooks and -% allocate the necessary data structures for the new hook. +% Now we check if the hook's data structure can be safely created +% without \pkg{expl3} raising errors, then +% we add the hook name to the list of all hooks and +% allocate the necessary data structures for the new hook, +% otherwise just do nothing. % \begin{macrocode} + \tl_if_exist:cF { @@~#1 } { \seq_gput_right:Nn \g_@@_all_seq {#1} % \end{macrocode} @@ -2034,12 +2269,12 @@ % \end{macrocode} % Now ensure that the base data structure for the hook exists: % \begin{macrocode} - \@@_declare:n {#1} + \@@_init_structure:n {#1} % \end{macrocode} % The \cs{g_@@_\meta{hook}_labels_clist} holds the sorted list of % labels (once it got sorted). This is used only for debugging. % \begin{macrocode} - \clist_new:c {g_@@_#1_labels_clist} + \clist_new:c { g_@@_#1_labels_clist } % \end{macrocode} % Some hooks should reverse the default order of code chunks. To % signal this we have a token list which is empty for normal hooks @@ -2061,23 +2296,30 @@ % % \begin{macrocode} \@@_include_legacy_code_chunk:n {#1} - } + } } % \end{macrocode} % \end{macro} % % % -% \begin{macro}{\@@_declare:n} -% This function declares the basic data structures for a hook without -% actually declaring the hook itself. This is needed to allow adding -% to undeclared hooks. Here it is unnecessary to check whether all -% variables exist, since all three are declared at the same time -% (either all of them exist, or none). +% \begin{macro}{\@@_init_structure:n} +% This function declares the basic data structures for a hook +% without explicit declaring the hook itself. This is needed to +% allow adding to undeclared hooks. Here it is unnecessary to +% check whether all variables exist, since all three are declared +% at the same time (either all of them exist, or none). +% +% It creates the hook code pool +% (\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 +% make it usable with \cs{hook_use:n}. % \begin{macrocode} -\cs_new_protected:Npn \@@_declare:n #1 +\cs_new_protected:Npn \@@_init_structure:n #1 { - \@@_if_exist:nF {#1} + \@@_if_structure_exist:nF {#1} { \prop_new:c { g_@@_#1_code_prop } \tl_new:c { @@_toplevel~#1 } @@ -2109,6 +2351,9 @@ % \end{macrocode} % \end{macro} % +% +% +% % \begin{macro}{\hook_new_pair:nn} % A shorthand for declaring a normal and a (matching) reversed hook in one go. % \begin{macrocode} @@ -2159,6 +2404,140 @@ % % % +% \subsubsection{Disabling and providing hooks} +% +% \begin{macro}{\hook_disable:n} +% \begin{macro}{\@@_disable:n} +% \begin{macro}[pTF]{\@@_if_disabled:n} +% +% Disables a hook by creating its +% \cs[no-index]{g_@@_\meta{hook}_declared_tl} so that the hook +% errors when used with \cs{hook_new:n}, then it undefines +% \cs{@@\textvisiblespace\meta{hook}} so that it may not be executed. +% +% This does not clear any code that may be already stored in the +% hook's structure, but doesn't allow adding more code. +% \cs{@@_if_disabled:nTF} uses that specific combination to check +% if the hook is disabled. +% +% \begin{macrocode} +%<latexrelease>\IncludeInRelease{2021/06/01}% +%<latexrelease> {\hook_disable:n}{Disable~hooks} +% \end{macrocode} +% +% \begin{macrocode} +\cs_new_protected:Npn \hook_disable:n #1 + { \@@_normalize_hook_args:Nn \@@_disable:n {#1} } +\cs_new_protected:Npn \@@_disable:n #1 + { + \tl_gclear_new:c { g_@@_#1_declared_tl } + \cs_undefine:c { @@~#1 } + } +\prg_new_conditional:Npnn \@@_if_disabled:n #1 { p, T, F, TF } + { + \bool_lazy_and:nnTF + { \tl_if_exist_p:c { g_@@_#1_declared_tl } } + { ! \tl_if_exist_p:c { @@~#1 } } + { \prg_return_true: } + { \prg_return_false: } + } +%<latexrelease>\EndIncludeInRelease +% \end{macrocode} +% +% \begin{macrocode} +%<latexrelease>\IncludeInRelease{2020/10/01} +%<latexrelease> {\hook_disable:n}{Disable~hooks} +%<latexrelease> +%<latexrelease>\cs_new_protected:Npn \hook_disable:n #1 {} +%<latexrelease> +%<latexrelease>\EndIncludeInRelease +% \end{macrocode} +% \end{macro} +% \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 +% 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} +% \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} { - } } +% \end{macrocode} +% +% \begin{macrocode} +\cs_new_protected:Npn \@@_provide:nn #1 #2 + { +% \end{macrocode} +% If the hook to be provided was disabled we warn (for now --- this +% may change). +% \begin{macrocode} + \@@_if_disabled:nTF {#1} + { \__kernel_msg_warning:nnn { hooks } { provide-disabled } {#1} } +% \end{macrocode} +% Otherwise we check if it was already declared. +% \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} + { \__kernel_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} + { + \tl_new:c { g_@@_#1_declared_tl } + \@@_make_usable:n {#1} + \tl_gset:cn { g_@@_#1_reversed_tl } {#2} + } + } + } +% \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 +% \end{macrocode} +% +% \begin{macrocode} +%<latexrelease>\IncludeInRelease{2020/10/01} +%<latexrelease> {\hook_provide: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> +%<latexrelease>\EndIncludeInRelease +% \end{macrocode} +% +% % \subsection{Parsing a label} % % \begin{macro}[EXP]{\@@_parse_label_default:n} @@ -2193,7 +2572,7 @@ { \tl_if_empty:nTF {#1} { - \msg_expandable_error:nn { hooks } { empty-label } + \__kernel_msg_expandable_error:nn { hooks } { empty-label } \@@_currname_or_default: } { @@ -2230,7 +2609,7 @@ { \tl_if_empty:NTF \@currname { - \msg_expandable_error:nnn { hooks } { should-not-happen } + \__kernel_msg_expandable_error:nnn { hooks } { should-not-happen } { Empty~default~label. } \@@_make_name:n { label-missing } } @@ -2247,10 +2626,10 @@ % 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 % instead of |e|-based because Unicode characters don't behave well -% inside \cs{expanded}. The macro adds the \cs{@@~} prefix to the +% inside \cs{expanded}. The macro adds the \cs[no-index]{@@\textvisiblespace} prefix to the % hook name to reuse the hook's code token list to build the csname % and avoid leaving \enquote{public} control sequences defined -% (as~\cs{relax}) in TeX's memory. +% (as~\cs[no-index]{relax}) in TeX's memory. % \begin{macrocode} \cs_new:Npn \@@_make_name:n #1 { @@ -2310,6 +2689,9 @@ % \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} % @@ -2319,6 +2701,9 @@ % \begin{macrocode} \cs_new_protected:Npn \hook_gput_code:nnn #1 #2 { \@@_normalize_hook_args:Nnn \@@_gput_code:nnn {#1} {#2} } +% \end{macrocode} +% +% \begin{macrocode} \cs_new_protected:Npn \@@_gput_code:nnn #1 #2 #3 { % \end{macrocode} @@ -2342,7 +2727,7 @@ % If no removal is queued, we are free to add. Start by checking if % the hook exists. % \begin{macrocode} - \hook_if_exist: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}= @@ -2363,10 +2748,17 @@ % declare it as a generic hook, if its name matches one of the valid % patterns. % \begin{macrocode} - { \@@_try_declaring_generic_hook:nnn {#1} {#2} {#3} } + { + \@@_if_disabled:nTF {#1} + { \__kernel_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} % @@ -2378,7 +2770,7 @@ % However, first some debugging info if debugging is enabled: % \begin{macrocode} \@@_debug:n{\iow_term:x{****~ Add~ to~ - \hook_if_exist:nF {#1} { undeclared~ } + \@@_if_usable:nF {#1} { undeclared~ } hook~ #1~ (#2) \on@line\space <-~ \tl_to_str:n{#3}} } % \end{macrocode} @@ -2386,9 +2778,9 @@ % If there's code already there, then append \verb=#3= to that, % otherwise just put \verb=#3=. If the current label is |top-level|, % the code is added to a dedicated token list -% \cs[no-index]{@@_toplevel~\meta{hook}} that goes at the end of the +% \cs{@@_toplevel\textvisiblespace\meta{hook}} that goes at the end of the % hook (or at the beginning, for a reversed hook), just before -% \cs[no-index]{@@_next~\meta{hook}}. +% \cs[no-index]{@@_next\textvisiblespace\meta{hook}}. % \begin{macrocode} \str_if_eq:nnTF {#2} { top-level } { @@ -2396,12 +2788,12 @@ { % \end{macrocode} % If the hook's basic structure does not exist, we need to declare it -% with \cs{@@_declare:n}. +% with \cs{@@_init_structure:n}. % \begin{macrocode} - \@@_declare:n {#1} + \@@_init_structure:n {#1} \@@_tl_gput_right:cn { @@_toplevel~#1 } {#3} } - { \msg_error:nnn { hooks } { misused-top-level } {#1} } + { \__kernel_msg_error:nnn { hooks } { misused-top-level } {#1} } } { \prop_get:cnNTF { g_@@_#1_code_prop } {#2} \l_@@_return_tl @@ -2423,14 +2815,15 @@ % \begin{macrocode} \cs_new_protected:Npn \@@_gput_undeclared_hook:nnn #1 #2 #3 { - \@@_declare:n {#1} + \@@_init_structure:n {#1} \@@_hook_gput_code_do:nnn {#1} {#2} {#3} } % \end{macrocode} % \end{macro} % -% \begin{macro}{\@@_try_declaring_generic_hook:nnn} -% \begin{macro}{\@@_try_declaring_generic_next_hook:nn} +% \begin{macro}{\@@_try_declaring_generic_hook:nnn, +% \@@_try_declaring_generic_next_hook:nn} +% % These entry-level macros just pass the arguments along to the % common \cs{@@_try_declaring_generic_hook:nNNnn} with the right % functions to execute when some action is to be taken. @@ -2457,13 +2850,14 @@ \hook_gput_next_code:nn \@@_gput_next_do:nn } % \end{macrocode} +% \end{macro} % -% \begin{macro}{ -% \@@_try_declaring_generic_hook:nNNnn, -% \@@_try_declaring_generic_hook_split:nNNnn -% } -% \begin{macro}[TF]{\@@_try_declaring_generic_hook:wn} -% \cs{@@_try_declaring_generic_hook:nNNnn} now splits the hook name +% +% \begin{macro}{\@@_try_declaring_generic_hook:nNNnn, +% \@@_try_declaring_generic_hook_split:nNNnn} +% +% \cs{@@_try_declaring_generic_hook:nNNnn} +% now splits the hook name % at the first \texttt{/} (if any) and first checks if it is a % file-specific hook (they require some normalization) using % \cs{@@_if_file_hook:wTF}. If not then check it is one of a @@ -2481,12 +2875,24 @@ } { \@@_try_declaring_generic_hook_split:nNNnn {#1} } } +% \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} } +% \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} \prg_new_protected_conditional:Npnn \@@_try_declaring_generic_hook:wn #1 / #2 / #3 / #4 \scan_stop: #5 { TF } { @@ -2495,31 +2901,70 @@ { \prop_if_in:NnTF \c_@@_generics_prop {#1} { - \hook_if_exist:nF {#5} { \hook_new:n {#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 +% declaring the hook. +% +% For some commands this will not be possible, in which case +% \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} } % \end{macrocode} -% After having declared the hook we check the second component (for -% file hooks) or the third component for environment hooks) and -% if it is on the list of components for which we should have declared -% a reversed hook we alter the hook data structure accordingly. +% +% Declare the hook always even if it can't really be used (error +% message generated elsewhere). +% +% 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 } { - } } } -% \end{macrocode} -% Now that we know that the hook is declared we can add the code to it. -% \begin{macrocode} \prg_return_true: } { \prg_return_false: } } } +%<latexrelease>\EndIncludeInRelease +% \end{macrocode} +% +% \begin{macrocode} +%<latexrelease>\IncludeInRelease{2020/10/01}% +%<latexrelease> {\@@_try_declaring_generic_hook:wn}{Support~cmd~hooks} +%<latexrelease> +%<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_declared:nF {#5} { \hook_new:n {#5} } +%<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> +%<latexrelease>\EndIncludeInRelease % \end{macrocode} -% \end{macro} -% \end{macro} -% \end{macro} % \end{macro} % % \begin{macro}[pTF]{\@@_if_file_hook:w} @@ -2528,7 +2973,7 @@ % |file/before/foo.tex|). If it is a file-specific hook, then it % executes the \meta{true} branch, otherwise \meta{false}. % -% A file-specific hook is \texttt{file/\meta{position}/\meta{name}}. +% 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 @@ -2594,14 +3039,16 @@ % Property list holding the generic names. We don't provide any user % interface to this as this is meant to be static. % \begin{description} +% \item[\texttt{cmd}] +% The generic hooks used for commands. % \item[\texttt{env}] % The generic hooks used in \cs{begin} and \cs{end}. -% \item[\texttt{file}] +% \item[\texttt{file}, \texttt{package}, \texttt{class}, \texttt{include}] % The generic hooks used when loading a file % \end{description} % \begin{macrocode} \prop_const_from_keyval:Nn \c_@@_generics_prop - {env=,file=,package=,class=,include=} + {cmd=,env=,file=,package=,class=,include=} % \end{macrocode} % \end{macro} % @@ -2628,11 +3075,11 @@ \cs_new_protected:Npn \@@_gremove_code:nn #1 #2 { % \end{macrocode} -% First check that the hook code pool exists. \cs{hook_if_exist:nTF} +% First check that the hook code pool exists. \cs{@@_if_usable:nTF} % isn't used here because it should be possible to remove code from a % hook before its defined (see section~\ref{sec:querying}). % \begin{macrocode} - \@@_if_exist:nTF {#1} + \@@_if_structure_exist:nTF {#1} { % \end{macrocode} % Then remove the chunk and run \cs{@@_update_hook_code:n} so @@ -2640,9 +3087,9 @@ % \verb=\begin{document}=. % % If all code is to be removed, clear the code pool -% \cs[no-index]{g_@@_\meta{hook}_code_prop}, the top-level code -% \cs[no-index]{@@_toplevel~\meta{hook}}, and the next-execution code -% \cs[no-index]{@@_next~\meta{hook}}. +% \cs{g_@@_\meta{hook}_code_prop}, the top-level code +% \cs{@@_toplevel\textvisiblespace\meta{hook}}, and the next-execution code +% \cs{@@_next\textvisiblespace\meta{hook}}. % \begin{macrocode} \str_if_eq:nnTF {#2} {*} { @@ -2655,7 +3102,7 @@ % 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[no-index]{@@_toplevel~\meta{hook}} (granted that +% 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 @@ -2679,7 +3126,7 @@ % \end{macrocode} % Finally update the code, if the hook exists. % \begin{macrocode} - \hook_if_exist:nT {#1} + \@@_if_usable:nT {#1} { \@@_update_hook_code:n {#1} } } % \end{macrocode} @@ -2774,10 +3221,10 @@ % disadvantage of that being not really distinguishable from a real % hook name. I now have settled for \texttt{??} which needs some % gymnastics to get it into the csname, but since this is used a -% lot things should be fast, so this is not done with \texttt{c} +% lot, the code should be fast, so this is not done with \texttt{c} % expansion in the code later on. % -% \cs{@@~??} isn't used, but it has to be defined to trick +% \cs{@@\textvisiblespace??} isn't used, but it has to be defined to trick % the code into thinking that \verb=??= is actually a hook. % \begin{macrocode} \prop_new:c {g_@@_??_code_prop} @@ -2798,14 +3245,12 @@ % % \begin{macro}{\hook_gset_rule:nnnn} % \begin{macro}{\@@_gset_rule:nnnn} -% -% \fmi{needs docu correction given new implementation} -% % With % \cs{hook_gset_rule:nnnn}\Arg{hook}\Arg{label1}\Arg{relation}\Arg{label2} % a relation is defined between the two code labels for the given % \meta{hook}. The special hook \texttt{??} stands for \emph{any} -% hook describing a default rule. +% hook, which sets a default rule (to be used if no other relation +% between the two hooks exist). % \begin{macrocode} \cs_new_protected:Npn \hook_gset_rule:nnnn #1#2#3#4 { @@ -2813,14 +3258,14 @@ {#1} {#2} {#3} {#4} } % \end{macrocode} -% +% % \begin{macrocode} \cs_new_protected:Npn \@@_gset_rule:nnnn #1#2#3#4 { % \end{macrocode} % First we ensure the basic data structure of the hook exists: % \begin{macrocode} - \@@_declare:n {#1} + \@@_init_structure:n {#1} % \end{macrocode} % Then we clear any previous relationship between both labels. % \begin{macrocode} @@ -2834,7 +3279,7 @@ {#1} {#2} {#4} \@@_update_hook_code:n {#1} } - { \msg_error:nnnnnn { hooks } { unknown-rule } + { \__kernel_msg_error:nnnnnn { hooks } { unknown-rule } {#1} {#2} {#3} {#4} } } % \end{macrocode} @@ -2938,7 +3383,7 @@ { \if_int_compare:w \@@_str_compare:nn {#1} {#2} > 0 \exp_stop_f: \prg_return_true: - \else + \else: \prg_return_false: \fi: } @@ -3038,13 +3483,13 @@ % If there aren't any code % chunks for the current hook, there is no point in even starting % the sorting routine so we make a quick test for that and in that -% case just update \cs{@@~\meta{hook}} to hold the |top-level| and +% case just update \cs{@@\textvisiblespace\meta{hook}} to hold the |top-level| and % |next| code chunks. If there are code chunks we call % \cs{@@_initialize_single:NNn} and pass to it ready made csnames % as they are needed several times inside. This way we save a bit % on processing time if we do that up front. % \begin{macrocode} - \hook_if_exist:nT {#1} + \@@_if_usable:nT {#1} { \prop_if_empty:cTF {g_@@_#1_code_prop} { @@ -3097,7 +3542,7 @@ % \begin{macro}[EXP]{\@@_tl_csname:n,\@@_seq_csname:n} % It is faster to pass a single token and expand it when necessary % than to pass a bunch of character tokens around. -% \fmi{note to myself: verify} +% \fmiinline{note to myself: verify} % \begin{macrocode} \cs_new:Npn \@@_tl_csname:n #1 { l_@@_label_#1_tl } \cs_new:Npn \@@_seq_csname:n #1 { l_@@_label_#1_seq } @@ -3161,7 +3606,7 @@ % 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} +% checking for, just in case ... maybe} % % ^^A #1 <- \@@~#1 % ^^A #2 <- \g_@@_#1_labels_clist @@ -3302,7 +3747,7 @@ % \end{macrocode} % % This is not really the information one needs in the error case -% but will do for now \ldots \fmi{fix} +% but will do for now \ldots \fmi{improve output on a rainy day} % \begin{macrocode} \@@_debug_label_data:N \l_@@_work_prop \iow_term:x{====================} @@ -3442,7 +3887,7 @@ \cs_new_protected:cpn { @@_apply_rule_xE:nnn } #1#2#3 { \@@_debug:n { \@@_msg_pair_found:nnn {#1} {#2} {#3} } - \msg_error:nnnnnn { hooks } { labels-incompatible } + \__kernel_msg_error:nnnnnn { hooks } { labels-incompatible } {#1} {#2} {#3} { 1 } \use:c { @@_apply_rule_->:nnn } {#1} {#2} {#3} \use:c { @@_apply_rule_<-:nnn } {#1} {#2} {#3} @@ -3450,7 +3895,7 @@ \cs_new_protected:cpn { @@_apply_rule_xW:nnn } #1#2#3 { \@@_debug:n { \@@_msg_pair_found:nnn {#1} {#2} {#3} } - \msg_warning:nnnnnn { hooks } { labels-incompatible } + \__kernel_msg_warning:nnnnnn { hooks } { labels-incompatible } {#1} {#2} {#3} { 0 } } % \end{macrocode} @@ -3573,15 +4018,20 @@ { \@@_log_cmd:x { >~#1 } } \cs_new_protected:Npn \@@_log_line_indent:x #1 { \@@_log_cmd:x { >~\@spaces #1 } } +% \end{macrocode} +% +% \begin{macrocode} \cs_new_protected:Npn \@@_log:nN #1 #2 { \@@_preamble_hook:n {#1} \@@_log_cmd:x { ^^J ->~The~hook~'#1': } % \end{macrocode} -% +% % \begin{macrocode} - \hook_if_exist:nF {#1} - { \@@_log_line:x { is~not~declared! } } + \@@_if_usable:nF {#1} + { \@@_log_line:x { The~hook~is~not~declared. } } + \@@_if_disabled:nT {#1} + { \@@_log_line:x { The~hook~is~disabled. } } \hook_if_empty:nTF {#1} { #2 { The~hook~is~empty } } { @@ -3599,7 +4049,7 @@ \@@_log_line:x { Document-level~(top-level)~code - \hook_if_exist:nT {#1} + \@@_if_usable:nT {#1} { ~(executed~\@@_if_reversed:nTF {#1} {first} {last} ) } : } \@@_log_line_indent:x @@ -3650,7 +4100,7 @@ % to that hook) and not empty % \begin{macrocode} \bool_lazy_and:nnTF - { \hook_if_exist_p:n {#1} } + { \@@_if_usable_p:n {#1} } { ! \hook_if_empty_p:n {#1} } { \@@_log_line:x @@ -3672,10 +4122,12 @@ } } { + \@@_log_line:x { Execution~order: } #2 { - Hook~ \hook_if_exist:nTF {#1} - {code~pool~empty} {not~declared} + \@spaces Not~set~because~the~hook~ \@@_if_usable:nTF {#1} + { code~pool~is~empty } + { is~\@@_if_disabled:nTF {#1} {disabled} {undeclared} } } } } @@ -3767,25 +4219,37 @@ % \end{macrocode} % \end{macro} % +% +% +% % \subsection{Specifying code for next invocation} % % \begin{macro}{\hook_gput_next_code:nn} -% \begin{macro}{% -% \@@_gput_next_code:nn, -% \@@_gput_next_do:nn, -% \@@_gput_next_do:Nnn, -% \@@_clear_next:n -% } % \begin{macrocode} \cs_new_protected:Npn \hook_gput_next_code:nn #1 { \@@_normalize_hook_args:Nn \@@_gput_next_code:nn {#1} } +% \end{macrocode} +% \end{macro} +% +% \begin{macro}{\@@_gput_next_code:nn, +% \@@_gput_next_do:nn, +% \@@_gput_next_do:Nnn, +% \@@_clear_next:n} +% \begin{macrocode} \cs_new_protected:Npn \@@_gput_next_code:nn #1 #2 { - \@@_declare:n {#1} - \hook_if_exist:nTF {#1} - { \@@_gput_next_do:nn {#1} {#2} } - { \@@_try_declaring_generic_next_hook:nn {#1} {#2} } + \@@_if_disabled:nTF {#1} + { \__kernel_msg_error:nnn { hooks } { hook-disabled } {#1} } + { + \@@_init_structure:n {#1} + \@@_if_usable:nTF {#1} + { \@@_gput_next_do:nn {#1} {#2} } + { \@@_try_declaring_generic_next_hook:nn {#1} {#2} } + } } +% \end{macrocode} +% +% \begin{macrocode} \cs_new_protected:Npn \@@_gput_next_do:nn #1 { \exp_args:Nc \@@_gput_next_do:Nnn @@ -3814,7 +4278,11 @@ { \cs_gset_eq:cN { @@_next~#1 } \c_empty_tl } % \end{macrocode} % \end{macro} -% \end{macro} +% +% +% +% +% % % \subsection{Using the hook} % @@ -3841,7 +4309,7 @@ % 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{relax} as a side effect of checking for a +% 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. @@ -3878,7 +4346,7 @@ % \end{macro} % % \begin{macro}[EXP]{\@@_use:wn} -% \begin{macro}{\@@_try_file_hook:n,\@@_if_exist_use:n} +% \begin{macro}{\@@_try_file_hook:n,\@@_if_usable_use:n} % \cs{@@_use:wn} does a quick check to test if the current hook is a % file hook: those need a special treatment. If it is not, the hook % does not exist. If it is, then \cs{@@_try_file_hook:n} is called, @@ -3888,7 +4356,7 @@ % % If it is a file-specific hook, it passes through the same % normalization as during declaration, and then it is used if defined. -% \cs{@@_if_exist_use:n} checks if the hook exist, and calls +% \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 @@ -3897,16 +4365,22 @@ { \@@_try_file_hook:n {#3} } { } % Hook doesn't exist } +% \end{macrocode} +% +% \begin{macrocode} \cs_new_protected:Npn \@@_try_file_hook:n #1 { \@@_if_file_hook:wTF #1 / / \s_@@_mark { - \exp_args:Ne \@@_if_exist_use:n + \exp_args:Ne \@@_if_usable_use:n { \exp_args:Ne \@@_file_hook_normalize:n {#1} } } - { \@@_if_exist_use:n {#1} } % file/ generic hook (e.g. file/before) + { \@@_if_usable_use:n {#1} } % file/ generic hook (e.g. file/before) } -\cs_new_protected:Npn \@@_if_exist_use:n #1 +% \end{macrocode} +% +% \begin{macrocode} +\cs_new_protected:Npn \@@_if_usable_use:n #1 { \tl_if_exist:cT { @@~#1 } { @@ -3929,7 +4403,6 @@ % hook name, since \cs{hook_use:n} and \cs{hook_use_once:n} are % documented to not trim spaces. % -% \pho{Should this raise an error if the hook doesn't exist?} % \begin{macrocode} \cs_new_protected:Npn \hook_use_once:n #1 { @@ -3953,9 +4426,10 @@ % exist, in which case emptiness doesn't apply (though % \cs{tl_if_empty:N} returns false in this case). % -% Hooks are a bit more complicated: they have four possible states. +% Hooks are a bit more complicated: they have several other states as +% discussed in \ref{sec:existence}. % A hook may exist or not, and either way it may or may not be empty -% (even a hook that doesn't exist may be non-empty). +% (even a hook that doesn't exist may be non-empty) or may be disabled. % % A hook is said to be empty when no code was added to it, either to % its permanent code pool, or to its ``next'' token list. The hook @@ -3965,19 +4439,19 @@ % In this case it is important that the code added by package $B$ is % remembered until package $A$ is loaded). % -% A hook is said to exist when it was declared with \cs{hook_new:n} or -% some variant thereof. -% +% All other states can only be queried with internal tests as the +% different states are irrelevant for package code. + % \begin{macro}[pTF]{\hook_if_empty:n} % Test if a hook is empty (that is, no code was added to that hook). % A \meta{hook} being empty means that all three of its -% \cs[no-index]{g_@@_\meta{hook}_code_prop}, its -% \cs[no-index]{@@_toplevel~\meta{hook}} and its -% \cs[no-index]{@@_next~\meta{hook}} are empty. +% \cs{g_@@_\meta{hook}_code_prop}, its +% \cs{@@_toplevel\textvisiblespace\meta{hook}} and its +% \cs{@@_next\textvisiblespace\meta{hook}} are empty. % \begin{macrocode} \prg_new_conditional:Npnn \hook_if_empty:n #1 { p , T , F , TF } { - \@@_if_exist:nTF {#1} + \@@_if_structure_exist:nTF {#1} { \bool_lazy_and:nnTF { \prop_if_empty_p:c { g_@@_#1_code_prop } } @@ -3994,8 +4468,9 @@ % \end{macrocode} % \end{macro} % -% \begin{macro}[pTF]{\hook_if_exist:n} -% A canonical way to test if a hook exists. A hook exists if the +% +% \begin{macro}[pTF]{\@@_if_usable:n} +% A hook is usable if the % token list that stores the sorted code for that hook, % \cs[no-index]{@@~\meta{hook}}, exists. The property list % \cs[no-index]{g_@@_\meta{hook}_code_prop} cannot be used here @@ -4003,7 +4478,7 @@ % if such hook was already declared, or even if it will ever be % (for example, in case the package that defines it isn't loaded). % \begin{macrocode} -\prg_new_conditional:Npnn \hook_if_exist:n #1 { p , T , F , TF } +\prg_new_conditional:Npnn \@@_if_usable:n #1 { p , T , F , TF } { \tl_if_exist:cTF { @@~#1 } { \prg_return_true: } @@ -4012,13 +4487,15 @@ % \end{macrocode} % \end{macro} % -% \begin{macro}[pTF]{\@@_if_exist:n} -% An internal check if the hook has already been declared with -% \cs{@@_declare:n}. This means that the hook was already used somehow +% \begin{macro}[pTF]{\@@_if_structure_exist:n} +% +% An internal check if the hook has already its basic internal +% structure set up with +% \cs{@@_init_structure:n}. This means that the hook was already used somehow % (a code chunk or rule was added to it), but it still wasn't declared % with \cs{hook_new:n}. % \begin{macrocode} -\prg_new_conditional:Npnn \@@_if_exist:n #1 { p , T , F , TF } +\prg_new_conditional:Npnn \@@_if_structure_exist:n #1 { p , T , F , TF } { \prop_if_exist:cTF { g_@@_#1_code_prop } { \prg_return_true: } @@ -4026,6 +4503,22 @@ } % \end{macrocode} % \end{macro} +% +% +% +% \begin{macro}[pTF]{\@@_if_declared:n} +% +% Internal test to check if the hook was officially declared with +% \cs{hook_new:n} or a variant. +% \begin{macrocode} +\prg_new_conditional:Npnn \@@_if_declared:n #1 { p, T, F, TF } + { + \tl_if_exist:cTF { g_@@_#1_declared_tl } + { \prg_return_true: } + { \prg_return_false: } + } +% \end{macrocode} +% \end{macro} % % \begin{macro}[pTF]{\@@_if_reversed:n} % An internal conditional that checks if a hook is reversed. @@ -4045,10 +4538,10 @@ % \subsection{Messages} % % \begin{macrocode} -\msg_new:nnnn { hooks } { labels-incompatible } +\__kernel_msg_new:nnnn { hooks } { labels-incompatible } { - Labels~`#1'~and~`#2'~are~incompatible - \str_if_eq:nnF {#3} {??} { ~in~hook~`#3' } .~ + Labels~'#1'~and~'#2'~are~incompatible + \str_if_eq:nnF {#3} {??} { ~in~hook~'#3' } .~ \int_compare:nNnTF {#4} = { 1 } { The~ code~ for~ both~ labels~ will~ be~ dropped. } { You~ may~ see~ errors~ later. } @@ -4058,23 +4551,34 @@ % \end{macrocode} % % \begin{macrocode} -\msg_new:nnnn { hooks } { exists } - { Hook~`#1'~ has~ already~ been~ declared. } +\__kernel_msg_new:nnnn { hooks } { exists } + { Hook~'#1'~ has~ already~ been~ declared. } { There~ already~ exists~ a~ hook~ declaration~ with~ this~ name.\\ Please~ use~ a~ different~ name~ for~ your~ hook.} % \end{macrocode} % +% +% \begin{macrocode} +\__kernel_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~ + it~cannot~have~code~added~to~it. + } +% \end{macrocode} +% % \begin{macrocode} -\msg_new:nnn { hooks } { empty-label } +\__kernel_msg_new:nnn { hooks } { empty-label } { Empty~code~label~\msg_line_context:.~ - Using~`\@@_currname_or_default:'~instead. + Using~'\@@_currname_or_default:'~instead. } % \end{macrocode} % % \begin{macrocode} -\msg_new:nnn { hooks } { no-default-label } +\__kernel_msg_new:nnn { hooks } { no-default-label } { Missing~(empty)~default~label~\msg_line_context:. \\ This~command~was~ignored. @@ -4082,25 +4586,25 @@ % \end{macrocode} % % \begin{macrocode} -\msg_new:nnnn { hooks } { unknown-rule } - { Unknown~ relationship~ `#3'~ - between~ labels~ `#2'~ and~ `#4'~ - \str_if_eq:nnF {#1} {??} { ~in~hook~`#1' }. ~ +\__kernel_msg_new:nnnn { hooks } { unknown-rule } + { Unknown~ relationship~ '#3'~ + between~ labels~ '#2'~ and~ '#4'~ + \str_if_eq:nnF {#1} {??} { ~in~hook~'#1' }. ~ Perhaps~ a~ missspelling? } { The~ relation~ used~ not~ known~ to~ the~ system.~ Allowed~ values~ are~ - `before'~ or~ `<',~ - `after'~ or~ `>',~ - `incompatible-warning',~ - `incompatible-error',~ - `voids'~ or~ - `unrelated'. + 'before'~ or~ '<',~ + 'after'~ or~ '>',~ + 'incompatible-warning',~ + 'incompatible-error',~ + 'voids'~ or~ + 'unrelated'. } % \end{macrocode} % % \begin{macrocode} -\msg_new:nnnn { hooks } { misused-top-level } +\__kernel_msg_new:nnnn { hooks } { misused-top-level } { Illegal~\iow_char:N \\AddToHook{#1}[top-level]{...}.\\ 'top-level'~is~reserved~for~the~user's~document. @@ -4114,16 +4618,16 @@ % \end{macrocode} % % \begin{macrocode} -\msg_new:nnn { hooks } { set-top-level } +\__kernel_msg_new:nnn { hooks } { set-top-level } { - You~cannot~change~the~default~label~#1~`top-level'.~Illegal \\ + You~cannot~change~the~default~label~#1~'top-level'.~Illegal \\ \use:nn { ~ } { ~ } \iow_char:N \\#2{#3} \\ \msg_line_context:. } % \end{macrocode} % % \begin{macrocode} -\msg_new:nnn { hooks } { ddhl-deprecated } +\__kernel_msg_new:nnn { hooks } { ddhl-deprecated } { \iow_char:N \\DeclareDefaultHookLabel~is~deprecated.\\ Use~\iow_char:N \\SetDefaultHookLabel~instead.\\ \\ @@ -4132,26 +4636,55 @@ % \end{macrocode} % % \begin{macrocode} -\msg_new:nnn { hooks } { extra-pop-label } +\__kernel_msg_new:nnn { hooks } { extra-pop-label } { Extra~\iow_char:N \\PopDefaultHookLabel. \\ This~command~will~be~ignored. } -\msg_new:nnn { hooks } { missing-pop-label } +\__kernel_msg_new:nnn { hooks } { missing-pop-label } { Missing~\iow_char:N \\PopDefaultHookLabel. \\ - The~label~`#1'~was~pushed~but~never~popped.~Something~is~wrong. + The~label~'#1'~was~pushed~but~never~popped.~Something~is~wrong. } % \end{macrocode} % % \begin{macrocode} -\msg_new:nnn { hooks } { should-not-happen } +\__kernel_msg_new:nnn { hooks } { should-not-happen } { ERROR!~This~should~not~happen.~#1 \\ Please~report~at~https://github.com/latex3/latex2e. } % \end{macrocode} % +% +% +% \begin{macrocode} +\__kernel_msg_new:nnn { hooks } { provide-disabled } + { + Can't~ provide~ hook~ '#1'~ because~ it~ is~ disabled! + } +% \end{macrocode} +% +% \begin{macrocode} +\__kernel_msg_new:nnnn { hooks } { provide-error } + { + Hook~'#1'~ already~ declared~ as~ a~ + \@@_if_reversed:nTF {#1} { reversed } { normal }~ hook! + } + { + 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. + } +% \end{macrocode} +% +% +% +% +% +% % \subsection{\LaTeXe{} package interface commands} % % @@ -4165,6 +4698,44 @@ % \end{macrocode} % \end{macro} % +% +% +% \begin{macrocode} +%<latexrelease>\IncludeInRelease{2021/06/01}% +%<latexrelease> {\hook_provide:n}{Providing~hooks} +% \end{macrocode} +% +% \begin{macro}{\ProvideHook,\ProvideReversedHook,\ProvideMirroredHookPair} +% Providing new hooks \ldots +% \changes{v1.0m}{2021/04/29}{Add \cs{ProvideHook} etc.} +% \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} } +% \end{macrocode} +% \end{macro} +% +% +% \begin{macro}{\DisableHook} +% Disabling a (generic) hook. +% \begin{macrocode} +\NewDocumentCommand \DisableHook { m }{ \hook_disable:n {#1} } +% \end{macrocode} +% \end{macro} +% +% \begin{macrocode} +%<latexrelease>\EndIncludeInRelease +%<latexrelease>\IncludeInRelease{2020/10/01} +%<latexrelease> {\hook_provide:n}{Providing~hooks} +%<latexrelease> +%<latexrelease>\def \ProvideHook#1{} +%<latexrelease>\def \ProvideReversedHook#1{} +%<latexrelease>\def \ProvideMirroredHookPair#1#2{} +%<latexrelease> +%<latexrelease>\EndIncludeInRelease +% \end{macrocode} +% +% % \begin{macro}{\AddToHook} % % \begin{macrocode} @@ -4194,6 +4765,10 @@ % \begin{macro}{\PushDefaultHookLabel} % \begin{macro}{\PopDefaultHookLabel} % \begin{macro}{\DeclareDefaultHookLabel} +% +% \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:} @@ -4217,8 +4792,11 @@ % 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_gput_right:Nn \g_@@_name_stack_seq { top-level } +%<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: @@ -4234,9 +4812,12 @@ %<latexrelease> \q_recursion_tail \q_recursion_tail %<latexrelease> \q_recursion_tail \q_recursion_stop % \end{macrocode} -% and finalle set the default label to be the \cs{@currname}: +% 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, @@ -4248,11 +4829,11 @@ \cs_new_protected:Npn \@@_curr_name_push_aux:n #1 { \tl_if_blank:nTF {#1} - { \msg_error:nn { hooks } { no-default-label } } + { \__kernel_msg_error:nn { hooks } { no-default-label } } { \str_if_eq:nnTF {#1} { top-level } { - \msg_error:nnnnn { hooks } { set-top-level } + \__kernel_msg_error:nnnnn { hooks } { set-top-level } { to } { PushDefaultHookLabel } {#1} } { @@ -4270,12 +4851,12 @@ { \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 } } + { \__kernel_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:} without a matching \cs{@@_curr_name_pop:} +% \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} @@ -4285,7 +4866,7 @@ { \seq_gpop:NNT \g_@@_name_stack_seq \l_@@_return_tl { - \msg_error:nnx { hooks } { missing-pop-label } + \__kernel_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: @@ -4303,7 +4884,7 @@ { \seq_if_empty:NTF \g_@@_name_stack_seq { - \msg_error:nnnnn { hooks } { set-top-level } + \__kernel_msg_error:nnnnn { hooks } { set-top-level } { for } { SetDefaultHookLabel } {#1} } { \exp_args:Nx \@@_set_default_label:n { \@@_make_name:n {#1} } } @@ -4312,14 +4893,14 @@ { \str_if_eq:nnTF {#1} { top-level } { - \msg_error:nnnnn { hooks } { set-top-level } + \__kernel_msg_error:nnnnn { hooks } { set-top-level } { to } { SetDefaultHookLabel } {#1} } { \tl_gset:Nn \g_@@_hook_curr_name_tl {#1} } } \NewDocumentCommand \DeclareDefaultHookLabel { m } { - \msg_error:nn { hooks } { ddhl-deprecated } + \__kernel_msg_error:nn { hooks } { ddhl-deprecated } \SetDefaultHookLabel {#1} } % \end{macrocode} @@ -4414,7 +4995,7 @@ % \begin{macro}{\ClearHookRule} % A special setup rule that removes an existing relation. % Basically {@@_rule_gclear:nnn} plus fixing the property list for debugging. -% \fmi{Need an L3 interface, or maybe it should get dropped?} +% \fmiinline{Needs perhaps an L3 interface, or maybe it should get dropped?} % \begin{macrocode} \NewDocumentCommand \ClearHookRule { m m m } { \hook_gset_rule:nnnn {#1}{#2}{unrelated}{#3} } @@ -4422,19 +5003,33 @@ % \end{macro} % % -% \begin{macro}[EXP]{\IfHookExistsTF,\IfHookEmptyTF} +% \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 % expansion step. % \begin{macrocode} -\cs_new_eq:NN \IfHookExistsTF \hook_if_exist:nTF \cs_new_eq:NN \IfHookEmptyTF \hook_if_empty:nTF % \end{macrocode} % \end{macro} % +% \begin{macro}[EXP]{\IfHookExistsTF} +% Marked for removal and no longer documented in the doc section! +% \phoinline{\cs{IfHookExistsTF} is used in \texttt{jlreq.cls}, +% \texttt{pxatbegshi.sty}, \texttt{pxeverysel.sty}, +% \texttt{pxeveryshi.sty}, so the public name may be an alias of the +% internal conditional for a while. Regardless, those packages' use for +% \cs{IfHookExistsTF} is not really correct and can be changed.} +% \begin{macrocode} +\cs_new_eq:NN \IfHookExistsTF \@@_if_usable:nTF +% \end{macrocode} +% \end{macro} +% +% +% +% +%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% % -%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%5 % % \subsection{Internal commands needed elsewhere} % @@ -4463,12 +5058,6 @@ % \InternalDetectionOn % \end{macro} % -% -% \begin{macrocode} -\ExplSyntaxOff -% \end{macrocode} -% -% % Rolling back here doesn't undefine the interface commands as they % may be used in packages without rollback functionality. So we % just make them do nothing which may or may not work depending on @@ -4476,14 +5065,15 @@ % \changes{v1.0d}{2020/10/04}{Definition \cs{AddToHookNext} was supposed % to be for \cs{AddToHook} vice versa (gh/401)} % \begin{macrocode} -%</2ekernel|latexrelease> -%<latexrelease>\EndIncludeInRelease +% %<latexrelease>\IncludeInRelease{0000/00/00}% -%<latexrelease> {\NewHook}{The hook management}% +%<latexrelease> {lthooks}{The~hook~management}% +%<latexrelease> +%<latexrelease>\def \NewHook#1{} +%<latexrelease>\def \NewReversedHook#1{} +%<latexrelease>\def \NewMirroredHookPair#1#2{} %<latexrelease> -%<latexrelease>\def\NewHook#1{} -%<latexrelease>\def\NewReversedHook#1{} -%<latexrelease>\def\NewMirroredHookPair#1#2{} +%<latexrelease>\def \DisableHook #1{} %<latexrelease> %<latexrelease>\long\def\AddToHookNext#1#2{} %<latexrelease> @@ -4511,7 +5101,9 @@ %<latexrelease>\long\def \IfHookExistsTF #1#2#3{#3} %<latexrelease>\long\def \IfHookEmptyTF #1#2#3{#2} %<latexrelease> -%<latexrelease>\EndIncludeInRelease +%<latexrelease>\EndModuleRelease +\ExplSyntaxOff +%</2ekernel|latexrelease> % \end{macrocode} % % |