diff options
Diffstat (limited to 'Master/texmf-dist/source/latex-dev/base')
32 files changed, 3315 insertions, 754 deletions
diff --git a/Master/texmf-dist/source/latex-dev/base/format.ins b/Master/texmf-dist/source/latex-dev/base/format.ins index 9c90555b9f3..eece2f57ef6 100644 --- a/Master/texmf-dist/source/latex-dev/base/format.ins +++ b/Master/texmf-dist/source/latex-dev/base/format.ins @@ -166,12 +166,14 @@ the system are in the document `cfgguide.tex'. % we have the extensions as soon as possible. \from{ltexpl.dtx}{2ekernel} % expl3-based extensions \from{ltdefns.dtx}{2ekernel} - \from{ltcmd.dtx}{2ekernel} % Used to be xparse + \from{ltcmd.dtx}{2ekernel} % L3 layer module (used to be xparse) \from{lthooks.dtx}{2ekernel} % L3 layer module + \from{ltcmdhooks.dtx}{2ekernel} % L3 layer module \from{ltalloc.dtx}{2ekernel} \from{ltcntrl.dtx}{2ekernel} \from{lterror.dtx}{2ekernel} \from{ltpar.dtx}{2ekernel} + \from{ltpara.dtx}{2ekernel} % L3 layer module \from{ltspace.dtx}{2ekernel} \from{ltlogos.dtx}{2ekernel} \from{ltfiles.dtx}{2ekernel} @@ -240,11 +242,6 @@ the system are in the document `cfgguide.tex'. \generate{\file{structuredlog.sty}{\from{ltfilehook.dtx}{structuredlog}}} -% hook managed para - -\generate{\file{ltpara.ltx}{\from{ltpara.dtx}{2ekernel}}} - - % luatex support (TeX part) \generate{\file{ltluatex.tex}{\from{ltluatex.dtx}{tex,plain}}} diff --git a/Master/texmf-dist/source/latex-dev/base/latexrelease.dtx b/Master/texmf-dist/source/latex-dev/base/latexrelease.dtx index cfcac4b3971..7d2290f4d8a 100644 --- a/Master/texmf-dist/source/latex-dev/base/latexrelease.dtx +++ b/Master/texmf-dist/source/latex-dev/base/latexrelease.dtx @@ -37,13 +37,13 @@ %<fixltx2e>\ProvidesPackage{fixltx2e} %<fixltx2e> [2016/12/29 v2.1a fixes to LaTeX (obsolete)] %<latexrelease>\ProvidesPackage{latexrelease} -%<*!fixltx2e> +%<*latexrelease> % \fi % \ProvidesFile{latexrelease.dtx} - [2021/01/23 v1.0l LaTeX release emulation and tests + [2021/04/29 v1.0n LaTeX release emulation and tests (including releases up to \latexreleaseversion)] % \iffalse -%</!fixltx2e> +%</latexrelease> %<*driver> \documentclass{ltxdoc} \newcommand\Lopt[1]{\textsf{#1}} @@ -51,6 +51,8 @@ \providecommand{\file}[1]{\texttt{#1}} \providecommand{\MF}{\textsf{Metafont}} \providecommand{\danger}{\marginpar[\hfill\protect\Huge!!]{\protect\Huge!!\hfill}} + \DeclareRobustCommand\cs[2][]{\texttt{\char`\\\detokenize{#2}}} + \DeclareRobustCommand\pkg[1]{\textsf{#1}} \begin{document} \DocInput{latexrelease.dtx} \end{document} @@ -69,7 +71,7 @@ % \title{The \Lpack{latexrelease} package\thanks{This file % has version number \fileversion, last % revised \filedate.}} -% \author{The \LaTeX Project} +% \author{The \LaTeX{} Project} % \date{\filedate} % \MaintainedByLaTeXTeam{latex} % \maketitle @@ -364,6 +366,99 @@ % necessary as the new code only gets defined if that release is % chosen. % +% \section{Declaring entire modules} +% +% Sometimes a large chunk of code is added as a module to another larger +% code base. As example of that in the 2020-10-01 release \LaTeX{} got +% a new hook management system, \pkg{lthooks}, which was added in one go +% and, as with all changes to the kernel, it was added to +% \pkg{latexrelease}. However rolling back from a future date to the +% 2020-10-01 release didn't work because \pkg{latexrelease} would try to +% define again all those commands, which would result in many +% ``already defined'' errors and similar issues. +% +% To solve that problem, completely new modules can be defined in +% \pkg{latexrelease} using the commands: +% \begin{quote} +% \cs{NewModuleRelease}%^^A +% \verb|{|\meta{initial release date}%^^A +% \verb|}{|\meta{name}\verb|}{|\meta{message}\verb|}|\\ +% \null\quad\meta{module code}\\ +% \cs{IncludeInRelease}%^^A +% \verb|{0000/00/00}{|\meta{name}\verb|}{|\meta{message}\verb|}|\\ +% \null\quad\meta{undefine module code}\\ +% \cs{EndModuleRelease} +% \end{quote} +% With that setup, the module \meta{name} will be declared to exist only +% in releases equal or later \meta{initial release date}. +% +% If \pkg{latexrelease} is rolling backwards or forwards between dates +% after \meta{initial release date}, then all the \meta{module code} is +% skipped, except when inside \meta{IncludeInRelease} guards, in which +% case the code is applied or skipped as discussed above. +% +% If rolling forward from a date before the module's \meta{initial +% release date} to a date after that, then all the \meta{module code} is +% executed to define the module, and \cs{IncludeInRelease} guards are +% executed accordingly, depending on the date declared and the target +% date. +% +% If \pkg{latexrelease} is rolling back to a date before \meta{release +% date}, then the code in the \cs{IncludeInRelease} guard dated +% \verb|0000/00/00| is executed instead to undefine the module. This +% guard \emph{is not} ended by the usual \cs{EndIncludeInRelease}, but +% instead by \cs{EndModuleRelease}. +% +% Finally, if rolling backwards or forwards between dates both before +% \meta{initial release date}, the entire code between +% \meta{NewModuleRelease} and \meta{EndModuleRelease} is entirely +% skipped. +% +% \subsection{Example} +% +% Here is an example usage of the structure described above, as it would +% be used in the \LaTeX{} kernel, taking \pkg{lthooks} as example: +% \begin{verbatim} +% %<*2ekernel|latexrelease> +% \ExplSyntaxOn +% %<latexrelease>\NewModuleRelease{2020/10/01}{lthooks}% +% %<latexrelease> {The~hook~management~system} +% \NewDocumentCommand \NewHook { m } +% { \hook_new:n {#1} } +% %<latexrelease>\IncludeInRelease{2021/06/01}{\AddToHook}{Long~argument} +% \NewDocumentCommand \AddToHook { m o +m } +% { \hook_gput_code:nnn {#1} {#2} {#3} } +% %<latexrelease>\EndIncludeInRelease +% %<latexrelease> +% %<latexrelease>\IncludeInRelease{2020/10/01}{\AddToHook}{Long~argument} +% %<latexrelease>\NewDocumentCommand \AddToHook { m o m } +% %<latexrelease> { \hook_gput_code:nnn {#1} {#2} {#3} } +% %<latexrelease>\EndIncludeInRelease +% %<latexrelease> +% %<latexrelease>\IncludeInRelease{0000/00/00}{lthooks}{Undefine~lthooks} +% %<latexrelease>\cs_undefine:N \NewHook +% %<latexrelease>\cs_undefine:N \AddToHook +% %<latexrelease>\EndModuleRelease +% \ExplSyntaxOff +% %</2ekernel|latexrelease> +% \end{verbatim} +% +% In the example above, \cs{NewHook} is declared only once, and +% unchanged in the next release (2021/06/01 in the example), so it has +% no \cs{IncludeInRelease} guards, and will only be defined if needed. +% \cs{AddToHook}, on the other hand, changed between the two releases +% (made up for the example; it didn't really happen) +% and has an \cs{IncludeInRelease} block for the current release (off +% \pkg{docstrip} guards, so it goes into the kernel too), and another +% for the previous release (in \pkg{docstrip} guards so it goes only +% into \pkg{latexrelease}). +% +% Note that in the example above, \cs{ExplSyntaxOn} and +% \cs{ExplSyntaxOff} were added \emph{outside} the module code because, +% as discussed above, sometimes the code outside \cs{IncludeInRelease} +% guards may be skipped, but not the code inside them, and in that case +% the catcodes would be wrong when defining the code. +% % \section{fixltx2e} % % As noted above, prior to the 2015 \LaTeX\ release updates to the @@ -387,27 +482,34 @@ \NeedsTeXFormat{LaTeX2e}[1996/06/01] % \end{macrocode} % -% \section{Setup} +% \subsection{Setup} % % \begin{macro}{\sourceLaTeXdate} % \changes{v1.0l}{2021/01/23}{\cs{sourceLaTeXdate} added} +% \changes{v1.0m}{2021/03/18}{Define \cs{sourceLaTeXdate} conditionally} % Store the original \LaTeX{} format version -% as a number in the format |YYYYMMDD|: +% as a number in the format |YYYYMMDD|. This macro has to +% be defined conditionally, so that it isn't changed in case +% \file{latexrelease.sty} is loaded twice (for tests), but it +% can't be defined in the kernel only, otherwise +% \file{latexrelease.sty} wouldn't work in older \LaTeX{} due +% to the missing macro. % \begin{macrocode} -\edef\sourceLaTeXdate{% - \expandafter\@parse@version\fmtversion//00\@nil} +\@ifundefined{sourceLaTeXdate}{% + \edef\sourceLaTeXdate{% + \expandafter\@parse@version\fmtversion//00\@nil}}{}% % \end{macrocode} % \end{macro} % % \begin{macro}{\IncludeInRelease} % \begin{macro}{\EndIncludeInRelease} -% \begin{macrocode} -% \end{macrocode} +% These are defined in \file{ltvers.dtx}. % \end{macro} % \end{macro} % % \changes{v1.0c}{2015/02/19}{Swap argument order} -% \changes{v1.0k}{2018/05/08}{reset \cs{\requestedLaTeXdate} for current and latest options, github issue 43} +% \changes{v1.0k}{2018/05/08}{reset \cs{\requestedLaTeXdate} for current +% and latest options, github issue 43} % \begin{macrocode} \DeclareOption*{% \def\@IncludeInRelease#1[#2]{\@IncludeInRele@se{#1}}% @@ -494,16 +596,213 @@ of this package available from CTAN} % \end{macrocode} % % -% \section{Individual Changes} +% \subsection{Ignoring \texttt{\string_new} errors when rolling back} % -% The code for each change will be inserted at this point, extracted -% from the kernel source files. +% Enforce \cs{ExplSyntaxOn} and \cs{ExplSyntaxOff} to be \cs{relax} in +% \pkg{latexrelease} if they are not yet defined. They are later +% restored to be undefined if needed. +% \begin{macrocode} +\csname ExplSyntaxOn\endcsname +\csname ExplSyntaxOff\endcsname +% \end{macrocode} +% +% \changes{v1.0m}{2021/03/18} +% {Make \texttt{\string_new} definitions harmless} +% +% Define a set of changes here, but we'll only use them later to make +% sure they are applied after \pkg{expl3} is loaded. If loading from a +% rather old format, we don't have \cs{ExplSyntaxOn} yet. +% \begin{macrocode} +\begingroup + \endlinechar=-1 + \catcode95=11 % _ + \catcode58=11 % : + \catcode126=10 % ~ + \catcode32=09 % <space> + \xdef\latexrelease@postexpl{\unexpanded{% +%<@@=latexrelease> +% \end{macrocode} +% +% First we'll define a \cs{declarecommand} that does \cs{renewcommand} +% if the command being defined already exists, and \cs{newcommand} +% otherwise. +% \begin{macrocode} +\cs_gset_protected:Npn \@@_declare_command:w + { \@star@or@long \@@_declare_command:Nw } +\cs_gset_protected:Npn \@@_declare_command:Nw #1 + { \cs_if_exist:NTF #1 { \renew@command } { \new@command } #1 } +% \end{macrocode} +% +% Then define a version of \cs{e@alloc} that checks if the control +% sequence being defined already exists, and if so, checks if its +% meaning is the same as the one that would be defined with the call to +% \cs{e@alloc}. If both tests pass, nothing is defined to save a +% register. This version also takes care of setting +% \cs{allocationnumber} to the value it would have after the register is +% allocated. +% \begin{macrocode} +\cs_gset_protected:Npn \@@_e@alloc:NnnnnN #1 #2 #3 #4 #5 #6 + { + \cs_if_free:NTF #6 + { \use:n } + { + \exp_after:wN \@@_e@alloc:N + \token_to_meaning:N #6 \scan_stop: {#2} #6 + } + { \@@_e@alloc #1 {#2} {#3} {#4} {#5} #6 } + } +% \end{macrocode} +% Walk through the meaning of the control sequence token by token, +% looking for the register allocation number. +% \begin{macrocode} +\cs_gset_protected:Npn \@@_e@alloc:N #1 + { + \if_int_compare:w 0 < 0 + \if_int_compare:w 10 < 9#1 ~ 1 \fi: + \if_charcode:w " #1 1 \fi: \exp_stop_f: + \tex_afterassignment:D \@@_e@alloc:w + \@tempcnta #1 + \use_i:nnn + \fi: + \use:n + { + \if_meaning:w \scan_stop: #1 + \exp_after:wN \use_iv:nnnn + \fi: + \@@_e@alloc:N + } + } +% \end{macrocode} +% When found, check if it is the exact same register as it would be +% allocated, and if it is, set \cs{allocationnumber} accordingly and +% exit, otherwise undefine the register and allocate from scratch. +% \begin{macrocode} +\cs_gset_protected:Npn \@@_e@alloc:w #1 \scan_stop: #2 #3 + { + #2 \@@_tmp:w = \@tempcnta + \token_if_eq_meaning:NNTF #3 \@@_tmp:w + { \int_set_eq:NN \allocationnumber \@tempcnta \use_none:n } + { \cs_set_eq:NN #3 \tex_undefined:D \use:n } + } +% \end{macrocode} +% +% Now create a token list to hold the list of changed commands, and +% define a temporary macro that will loop through the command list, +% store each in \cs{l_@@_restores_tl}, save a copy, and redefine each. +% \begin{macrocode} +\tl_clear_new:N \l_@@_restores_tl +\cs_gset:Npn \@@_tmp:w #1 #2 + { + \quark_if_recursion_tail_stop_do:Nn #1 + { \cs_undefine:N \@@_tmp:w } + \tl_put_right:Nn \l_@@_restores_tl {#1} + \cs_set_eq:cN { @@_ \cs_to_str:N #1 } #1 + \cs_set_eq:NN #1 #2 + \@@_tmp:w + } +% \end{macrocode} % +% The redefinitions below are needed because: +% \begin{description} +% \def\makelabel#1{\cs{#1}} +% \item[__kernel_chk_if_free_cs:N] This function is used ubiquitously +% in the \pkg{l3kernel} to check if a control sequence is definable, +% and give an error otherwise (similar to \cs{@ifdefinable}). +% Making it a no-op is enough for most cases (except when defining +% new registers); +% \item[e@alloc] In the case of new registers, we waste an allocation +% number if we do \cs[no-index]{new\meta{thing}} in a register +% that's already allocated, so the redefinition of \cs{e@alloc} +% checks if the new register is really necessary. This code does +% not clear the register, which might cause problems in the future, +% if a register is allocated but not properly cleared before using; +% \item[__kernel_msg_error:nnx] This command is used to error on +% already defined scan marks. Just making the error do nothing is +% enough, as no action is taken in that case; +% \item[msg_new:nnnn] Used to define new messages. Making it +% \texttt{\string_gset} is enough. Other msg commands like +% \cs{msg_new:nnn} and \cs[no-index]{__kernel_msg_new:nnn(n)} are +% defined in terms of \cs{msg_new:nnnn}, so there is no need to +% change the other ones; +% \item[NewDocumentCommand] Used to define user-level commands in the +% kernel. Making it equal to \cs{DeclareDocumentCommand} solves the +% problem; +% \item[newcommand] Same as above. +% \end{description} +% And here we go: +% \begin{macrocode} +\@@_tmp:w + \__kernel_chk_if_free_cs:N \use_none:n + \e@alloc \@@_e@alloc:NnnnnN + \__kernel_msg_error:nnx \use_none:nnn + \msg_new:nnnn \msg_gset:nnnn + \NewDocumentCommand \DeclareDocumentCommand + \newcommand \@@_declare_command:w +% \end{macrocode} +% Temp addition \ldots % \begin{macrocode} + \__kernel_msg_error:nnn \use_none:nnn % needed while redirect for kernel msgs doesn't work + \q_recursion_tail \q_recursion_tail + \q_recursion_stop +% \end{macrocode} +% +% Finally, redirect the error thrown by \cs{NewHook} to nowhere so it +% can be safely reused (the hook isn't redeclared if it already exists). +% \begin{macrocode} +\msg_redirect_name:nnn { hooks } { exists } { none } +% \end{macrocode} +% +% \begin{macrocode} + }}% +\endgroup %</latexrelease> % \end{macrocode} % -% \section{fixltx2e} +% \subsection{Undoing the temp modifications} +% +% If \cs{ExplSyntaxOn} exists (defined and not equal \cs{relax}), then +% use the \pkg{expl3} restore code, otherwise restore \cs{ExplSyntaxOn} +% and \cs{ExplSyntaxOff} to be undefined. +% \begin{macrocode} +%<*latexrelease-finish> +\@ifundefined{ExplSyntaxOn}% + {\let\ExplSyntaxOn\@undefined + \let\ExplSyntaxOff\@undefined + \@gobble}% + {\ExplSyntaxOn + \@firstofone}% + {% +% \end{macrocode} +% +% Now just loop through the list of redefined commands and restore their +% previous meanings. +% \begin{macrocode} +\tl_map_inline:Nn \l_@@_restores_tl + { + \cs_set_eq:Nc #1 { @@_ \cs_to_str:N #1 } + \cs_undefine:c { @@_ \cs_to_str:N #1 } + } +\tl_clear:N \l_@@_restores_tl +% \end{macrocode} +% +% And restore the hook error message. +% \begin{macrocode} +\msg_redirect_name:nnn { hooks } { exists } { } +% \end{macrocode} +% +% \begin{macrocode} +%<@@=> + \ExplSyntaxOff}% +%</latexrelease-finish> +% \end{macrocode} +% +% \subsection{Individual Changes} +% +% The code for each change will be inserted at this point, extracted +% from the kernel source files. +% +% +% \subsection{fixltx2e} % % Generate a stub \Lpack{fixltx2e} package: % \begin{macrocode} diff --git a/Master/texmf-dist/source/latex-dev/base/latexrelease.ins b/Master/texmf-dist/source/latex-dev/base/latexrelease.ins index b4d0aef5677..0b65114a2f9 100644 --- a/Master/texmf-dist/source/latex-dev/base/latexrelease.ins +++ b/Master/texmf-dist/source/latex-dev/base/latexrelease.ins @@ -94,13 +94,15 @@ extension .ins) which are part of the distribution. \from{ltexpl.dtx} {latexrelease}% empty \from{ltdefns.dtx} {latexrelease}% \from{ltcmd.dtx} {latexrelease}% - \from{lthooks.dtx} {latexrelease}% empty % L3 layer module + \from{lthooks.dtx} {latexrelease}% L3 layer module + \from{ltcmdhooks.dtx} {latexrelease}% L3 layer module \from{ltalloc.dtx} {latexrelease}% empty \from{ltcntrl.dtx} {latexrelease}% empty \from{lterror.dtx} {latexrelease}% empty \from{ltpar.dtx} {latexrelease}% empty + \from{ltpara.dtx} {latexrelease}% empty \from{ltspace.dtx} {latexrelease}% - \from{ltlogos.dtx} {latexrelease}% empty + \from{ltlogos.dtx} {latexrelease}% L3 layer module \from{ltfiles.dtx} {latexrelease}% \from{ltoutenc.dtx} {latexrelease}% \from{ltcounts.dtx} {latexrelease}% @@ -133,6 +135,7 @@ extension .ins) which are part of the distribution. \from{ltoutput.dtx} {latexrelease}% \from{ltfsscmp.dtx} {latexrelease}% only in latexrelease! \from{ltfinal.dtx} {latexrelease}% + \from{latexrelease.dtx}{latexrelease-finish}% } diff --git a/Master/texmf-dist/source/latex-dev/base/letter.dtx b/Master/texmf-dist/source/latex-dev/base/letter.dtx index 3c7fe56ba15..6beee81735a 100644 --- a/Master/texmf-dist/source/latex-dev/base/letter.dtx +++ b/Master/texmf-dist/source/latex-dev/base/letter.dtx @@ -45,7 +45,7 @@ %<*driver> \ProvidesFile{letter.drv} %</driver> - [2021/01/07 v1.3c + [2021/03/05 v1.3c %<+letter> Standard LaTeX document class] % \end{macrocode} % @@ -1034,7 +1034,7 @@ % \texttt{extletter.cls},\\ % \texttt{frletter.cls}, % \texttt{hletter.cls}, -% \texttt{scrlttr2.cls}, +% \texttt{scrlttr2.cls} (change rejected), % \texttt{lettre.cls}, % \texttt{beletter.cls},\\ % \texttt{brief.cls} and perhaps others. diff --git a/Master/texmf-dist/source/latex-dev/base/ltbibl.dtx b/Master/texmf-dist/source/latex-dev/base/ltbibl.dtx index 0319342dd49..cda0fa160ad 100644 --- a/Master/texmf-dist/source/latex-dev/base/ltbibl.dtx +++ b/Master/texmf-dist/source/latex-dev/base/ltbibl.dtx @@ -329,7 +329,7 @@ % \begin{macrocode} %</2ekernel> %<*2ekernel|latexrelease> -%<latexrelease>\IncludeInRelease{2021/05/01}% +%<latexrelease>\IncludeInRelease{2021/06/01}% %<latexrelease> {\nocite}{Allow nocite in preamble}% \def\nocite#1{\@bsphack % \end{macrocode} diff --git a/Master/texmf-dist/source/latex-dev/base/ltboxes.dtx b/Master/texmf-dist/source/latex-dev/base/ltboxes.dtx index 8281b4b712b..8499d3ab8b9 100644 --- a/Master/texmf-dist/source/latex-dev/base/ltboxes.dtx +++ b/Master/texmf-dist/source/latex-dev/base/ltboxes.dtx @@ -464,7 +464,7 @@ % \begin{macrocode} %</2ekernel> %<*2ekernel|latexrelease> -%<latexrelease>\IncludeInRelease{2021/05/01}% +%<latexrelease>\IncludeInRelease{2021/06/01}% %<latexrelease> {\color@begingroup}{color group settings}% \let\color@begingroup\begingroup \def\color@endgroup{\endgraf\endgroup} @@ -1140,7 +1140,7 @@ % \begin{macrocode} %</2ekernel> %<*2ekernel|latexrelease> -%<latexrelease>\IncludeInRelease{2021/05/01}% +%<latexrelease>\IncludeInRelease{2021/06/01}% %<latexrelease> {\@mpfootnotetext}{footnotetext tagging}% \long\def\@mpfootnotetext#1{% \global\setbox\@mpfootins\vbox{% diff --git a/Master/texmf-dist/source/latex-dev/base/ltclass.dtx b/Master/texmf-dist/source/latex-dev/base/ltclass.dtx index 26667cbeb1b..06b0e1cf43f 100644 --- a/Master/texmf-dist/source/latex-dev/base/ltclass.dtx +++ b/Master/texmf-dist/source/latex-dev/base/ltclass.dtx @@ -33,7 +33,7 @@ %<*driver> % \fi \ProvidesFile{ltclass.dtx} - [2021/02/18 v1.3x LaTeX Kernel (Class & Package Interface)] + [2021/03/27 v1.4a LaTeX Kernel (Class & Package Interface)] % \iffalse \documentclass{ltxdoc} \GetFileInfo{ltclass.dtx} @@ -640,14 +640,38 @@ %<latexrelease> \@expl@push@filename@aux@@} %<latexrelease>\EndIncludeInRelease %<latexrelease> +% \end{macrocode} +% +% When we roll back from a release that has \pkg{expl3} preloaded, the +% definitions of \cs{@pushfilename} and \cs{@popfilename} can't be +% completely rolled back otherwise \pkg{expl3}-based packages won't +% have the automatic \cs{ExplSyntaxOff} at the end. Here and below for +% \cs{@popfilename}, we don't roll back all the way through if coming +% from \LaTeX${}>2020-02-02$. +% \changes{v1.4a}{2021/03/27} +% {Do not completely roll back if \pkg{expl3} is loaded.} +% \begin{macrocode} %<latexrelease>\IncludeInRelease{0000/00/00}{\@pushfilename}% %<latexrelease> {Add \@expl@push@filename@@ and \@expl@push@filename@aux@@}% +%<latexrelease>\ifnum\sourceLaTeXdate<20200202\relax +%<latexrelease> \GenericInfo{}{Defining 00-00-00\string\@pushfilename.} %<latexrelease>\def\@pushfilename{% %<latexrelease> \xdef\@currnamestack{% %<latexrelease> {\@currname}% %<latexrelease> {\@currext}% %<latexrelease> {\the\catcode`\@}% %<latexrelease> \@currnamestack}} +%<latexrelease>\else +%<latexrelease> \GenericInfo{}{Defining 2020-02-02\string\@pushfilename.} +%<latexrelease>\def\@pushfilename{% +%<latexrelease> \@expl@push@filename@@ +%<latexrelease> \xdef\@currnamestack{% +%<latexrelease> {\@currname}% +%<latexrelease> {\@currext}% +%<latexrelease> {\the\catcode`\@}% +%<latexrelease> \@currnamestack}% +%<latexrelease> \@expl@push@filename@aux@@} +%<latexrelease>\fi %<latexrelease>\EndIncludeInRelease \@onlypreamble\@pushfilename % \end{macrocode} @@ -676,9 +700,21 @@ %<latexrelease> \@expl@pop@filename@@} %<latexrelease>\EndIncludeInRelease %<latexrelease> +% \end{macrocode} +% +% \changes{v1.4a}{2021/03/27} +% {Do not completely roll back if \pkg{expl3} is loaded.} +% \begin{macrocode} %<latexrelease>\IncludeInRelease{0000/00/00}{\@popfilename}% %<latexrelease> {Add \@expl@push@filename@@ and \@expl@push@filename@aux@@}% +%<latexrelease>\ifnum\sourceLaTeXdate<20200202\relax +%<latexrelease> \GenericInfo{}{Defining 00-00-00\string\@popfilename.} %<latexrelease>\def\@popfilename{\expandafter\@p@pfilename\@currnamestack\@nil} +%<latexrelease>\else +%<latexrelease> \GenericInfo{}{Defining 2020-02-02\string\@popfilename.} +%<latexrelease>\def\@popfilename{\expandafter\@p@pfilename\@currnamestack\@nil +%<latexrelease> \@expl@pop@filename@@} +%<latexrelease>\fi %<latexrelease>\EndIncludeInRelease \@onlypreamble\@popfilename % \end{macrocode} @@ -1172,7 +1208,7 @@ % \changes{v1.3x}{2021/02/18}{save raw option lists (gh/85)} % \begin{macrocode} %</2ekernel> -%<latexrelease>\IncludeInRelease{2021/05/01}% +%<latexrelease>\IncludeInRelease{2021/06/01}% %<latexrelease> {\@pass@ptions}{Raw option lists}% %<*2ekernel|latexrelease> \def\@pass@ptions#1#2#3{% @@ -1283,7 +1319,7 @@ % unused options. Otherwise, in a package file do nothing. % \begin{macrocode} %</2ekernel> -%<latexrelease>\IncludeInRelease{2021/05/01}% +%<latexrelease>\IncludeInRelease{2021/06/01}% %<latexrelease> {\OptionNotUsed}{filter unused option list}% %<*2ekernel|latexrelease> \def\@remove@eq@value#1=#2\@nil{#1} @@ -1378,19 +1414,50 @@ % % \changes{v0.2y}{1994/02/07} % {Add extra ,s so `two' is not matched with `twocolumn'} +% \changes{v1.3z}{2021/03/05}{modify so braces to not give errors (gh/513)} % \begin{macrocode} +%</2ekernel> +%<latexrelease>\IncludeInRelease{2021/06/01}% +%<latexrelease> {\@xprocess@ptions}{safer @xprocess@ptions}% +%<*2ekernel|latexrelease> \def\@xprocess@ptions{% \ifx\@currext\@clsextension\else + \ifx\@classoptionslist\relax\else \@for\CurrentOption:=\@classoptionslist\do{% \ifx\CurrentOption\@empty\else - \@expandtwoargs\in@{,\CurrentOption,}{,\@declaredoptions,}% - \ifin@ + \@ifundefined{ds@\CurrentOption}{}{% \@use@ption \expandafter\let\csname ds@\CurrentOption\endcsname\@empty - \fi + }% \fi}% + \fi \fi \@process@pti@ns} +%</2ekernel|latexrelease> +%<latexrelease>\EndIncludeInRelease +%<latexrelease>\IncludeInRelease{0000/00/00}% +%<latexrelease> {\@xprocess@ptions}{safer @xprocess@ptions}% +%<latexrelease>\let\@remove@eq@value\@undefined +% \end{macrocode} +% +% \begin{macrocode} +%<latexrelease>\def\@xprocess@ptions{% +%<latexrelease> \ifx\@currext\@clsextension\else +%<latexrelease> \@for\CurrentOption:=\@classoptionslist\do{% +%<latexrelease> \ifx\CurrentOption\@empty\else +%<latexrelease> \@expandtwoargs\in@{,\CurrentOption,}{,\@declaredoptions,}% +%<latexrelease> \ifin@ +%<latexrelease> \@use@ption +%<latexrelease> \expandafter\let\csname ds@\CurrentOption\endcsname\@empty +%<latexrelease> \fi +%<latexrelease> \fi}% +%<latexrelease> \fi +%<latexrelease> \@process@pti@ns} +%<latexrelease>\EndIncludeInRelease +%<*2ekernel> +% \end{macrocode} +% +% \begin{macrocode} \@onlypreamble\@xprocess@ptions % \end{macrocode} % @@ -1472,7 +1539,7 @@ % \changes{v1.3x}{2021/02/18}{filter out =value from unused option list (gh/85)} % \begin{macrocode} %</2ekernel> -%<latexrelease>\IncludeInRelease{2021/05/01}% +%<latexrelease>\IncludeInRelease{2021/06/01}% %<latexrelease> {\@use@ption}{filter unused option list}% %<*2ekernel|latexrelease> \def\@use@ption{% @@ -1954,7 +2021,7 @@ % \usepackage{some/local/path/array} % \usepackage{array} % \end{verbatim} -% won't load \file{array.sty} twice. It is remotely possible that +% won't load |array.sty| twice. It is remotely possible that % those are two different files, but as a matter of principles, we % will consider that the base file name uniquely identifies a % package, regardless of where it lives. This assumption already @@ -2669,15 +2736,15 @@ % this only works for input files ending in \texttt{.tex}. If a % different extension is used there is no way to see that we are % overwriting ourselves! +% \changes{v1.3y}{2021/03/03} +% {Fix overwrite check for files with UTF-8 (gh/415)} % \begin{macrocode} - \edef\reserved@a{#1}% - \edef\reserved@a{\detokenize\expandafter{\reserved@a}}% \edef\reserved@b{\detokenize\expandafter{\jobname}}% - \ifx\reserved@a\reserved@b% + \ifx\@curr@file\reserved@b% \@fileswtrue% \else% \edef\reserved@b{\reserved@b\detokenize{.tex}}% - \ifx\reserved@a\reserved@b + \ifx\@curr@file\reserved@b \@fileswtrue% \fi% \fi% diff --git a/Master/texmf-dist/source/latex-dev/base/ltcmd.dtx b/Master/texmf-dist/source/latex-dev/base/ltcmd.dtx index 26ce75224fb..fee555586da 100644 --- a/Master/texmf-dist/source/latex-dev/base/ltcmd.dtx +++ b/Master/texmf-dist/source/latex-dev/base/ltcmd.dtx @@ -34,14 +34,15 @@ %%% From File: ltcmd.dtx % % \begin{macrocode} -\def\ltcmdversion{v1.0a} -\def\ltcmddate{2021/01/21} +\def\ltcmdversion{v1.0d} +\def\ltcmddate{2021-04-19} % \end{macrocode} % %<*driver> % \fi \ProvidesFile{ltcmd.dtx} - [2020-11-20 v1.0 LaTeX Kernel (Document commands)] + [\ltcmddate\space \ltcmdversion\space + LaTeX Kernel (Document commands)] % \iffalse \documentclass{l3doc} \GetFileInfo{ltcmd.dtx} @@ -57,7 +58,7 @@ % % \section{Creating document commands} % -% \changes{v1.0}{2020-11-20}{Initial version derived from \texttt{xparse.dtx}} +% \changes{v1.0a}{2020/11/20}{Initial version derived from \texttt{xparse.dtx}} % % Document commands should be created using the tools provided by this module: % \cs{NewDocumentCommand}, etc.\@, in almost all cases. This allows clean @@ -78,6 +79,7 @@ %</2ekernel> % \end{macrocode} % +% \changes{v1.0b}{2021/03/18}{Use \cs{NewModuleRelease}.} % \pkg{ltcmd} code contains an |^^@| character, which usually has % catcode~15, so \cs{IncludeInRelease} will break when this code is % being skipped, so we'll save the catcode of |^^@| to restore later: @@ -85,12 +87,9 @@ %<*2ekernel|latexrelease> %<latexrelease>\edef\@latexrelease@catcode@null{\the\catcode`\^^@ } %<latexrelease>\catcode`\^^@=12 -%<latexrelease>\IncludeInRelease{2021/05/01}% -%<latexrelease> {\NewDocumentCommand}{Document commands}% -% \end{macrocode} -% -% \begin{macrocode} \ExplSyntaxOn +%<latexrelease>\NewModuleRelease{2020/10/01}{ltcmd} +%<latexrelease> {Document~command~parser}% % \end{macrocode} % % \subsection{Variables and constants} @@ -3196,7 +3195,7 @@ % \begin{macrocode} \cs_new_protected:Npn \@@_get_arg_spec:NTF #1#2#3 { - \@@_cmd_if_xparse:NTF #1 + \__kernel_cmd_if_xparse:NTF #1 { \tl_set:Nx \ArgumentSpecification { \tl_item:Nn #1 { 2 } } #2 @@ -3374,15 +3373,22 @@ % \end{macro} % \end{macro} % -% \begin{macro}{\@@_cmd_if_xparse:NTF} +% \begin{macro}{\__kernel_cmd_if_xparse:NTF} +% \changes{v1.0d}{2021/04/19}{Renamed \cs{__cmd_cmd_if_xparse:NTF} to +% \cs{__kernel_cmd_if_xparse:NTF} for cross-module usage} % \begin{macro}{\@@_cmd_if_xparse_aux:N} -% To determine whether the command is an \pkg{xparse} command check -% that its |arg_spec| is empty (this also excludes non-macros) and -% that its |replacement_spec| starts with either \cs{@@_start:nNNnnn} -% (non-expandable command) or \cs{@@_start_expandable:nNNNNn} -% (expandable command) or \cs{@@_start_env:nnnnn} (environment). +% +% To determine whether the command is an \pkg{xparse} command check +% that its |arg_spec| is empty (this also excludes non-macros) and +% that its |replacement_spec| starts with either +% \cs{@@_start:nNNnnn} (non-expandable command) or +% \cs{@@_start_expandable:nNNNNn} (expandable command) or +% \cs{@@_start_env:nnnnn} (environment). +% +% This conditional is needed in several kernel modules and is +% therefore has a kernel-internal name. % \begin{macrocode} -\cs_new_protected:Npn \@@_cmd_if_xparse:NTF #1 +\cs_new_protected:Npn \__kernel_cmd_if_xparse:NTF #1 { \exp_args:Nf \str_case_e:nnTF { @@ -4008,29 +4014,15 @@ % \end{macrocode} % % \begin{macrocode} -\ExplSyntaxOff -% \end{macrocode} -% -% \begin{macrocode} -%</2ekernel|latexrelease> -%<latexrelease>\EndIncludeInRelease -%<latexrelease>\catcode`\^^@=\@latexrelease@catcode@null\relax -%<latexrelease> -%<latexrelease>\IncludeInRelease{2020/10/01}% -%<latexrelease> {\NewDocumentCommand}{Document commands}% -%<latexrelease>\@ifundefined{NewDocumentCommand} -%<latexrelease> {% -%<latexrelease> \IfFileExists{xparse.ltx} -%<latexrelease> {\input xparse.ltx } -%<latexrelease> {\@latex@error{LaTeX requires xparse}\@ehd}% -%<latexrelease> } -%<latexrelease> {} -%<latexrelease>\EndIncludeInRelease %<latexrelease> -%<latexrelease>\IncludeInRelease{0000/00/00}% -%<latexrelease> {\NewDocumentCommand}{Document commands}% +%<latexrelease>\IncludeInRelease{0000/00/00}{ltcmd}% +%<latexrelease> {Document~command~parser}% %<latexrelease> -%<latexrelease>\EndIncludeInRelease +%<latexrelease>\EndModuleRelease +\ExplSyntaxOff +%<latexrelease>\@ifundefined{ExplSyntaxOff}{}{\latexrelease@postexpl} +%<latexrelease>\catcode`\^^@=\@latexrelease@catcode@null\relax +%</2ekernel|latexrelease> % \end{macrocode} % % We need to stop DocStrip treating |@@| in a special way at this point. diff --git a/Master/texmf-dist/source/latex-dev/base/ltcmdhooks.dtx b/Master/texmf-dist/source/latex-dev/base/ltcmdhooks.dtx new file mode 100644 index 00000000000..cc67f5eaa75 --- /dev/null +++ b/Master/texmf-dist/source/latex-dev/base/ltcmdhooks.dtx @@ -0,0 +1,1093 @@ +% \iffalse meta-comment +% +%% File: ltcmdhooks.dtx (C) Copyright 2020-2021 +%% Frank Mittelbach, Phelype Oleinik, LaTeX Team +% +% It may be distributed and/or modified under the conditions of the +% LaTeX Project Public License (LPPL), either version 1.3c of this +% license or (at your option) any later version. The latest version +% of this license is in the file +% +% https://www.latex-project.org/lppl.txt +% +% +%%% From File: ltcmdhooks.dtx +% +\def\ltcmdhooksversion{v1.0a} +\def\ltcmdhooksdate{2021/04/30} +% +% +% +%<*driver> +\documentclass{l3doc} +%\usepackage{ltcmdhooks} +\makeatletter +\providecommand\footref[1]{% + \begingroup + \unrestored@protected@xdef\@thefnmark{\ref{#1}}% + \endgroup + \@footnotemark} +\makeatother +\EnableCrossrefs +\CodelineIndex +\begin{document} + \DocInput{ltcmdhooks.dtx} +\end{document} +%</driver> +% +% \fi +% +% +% \providecommand\hook[1]{\texttt{#1}} +% \providecommand\fmi[1]{\marginpar{\footnotesize FMi: #1}} +% \providecommand\pho[1]{\marginpar{\footnotesize PhO: #1}} +% \providecommand\phoinline[1]{\begin{quote}\itshape\footnotesize PhO: #1\end{quote}} +% +% \title{The \texttt{ltcmdhooks} module\thanks{This file has version +% \ltcmdhooksversion\ dated \ltcmdhooksdate, \copyright\ \LaTeX\ +% Project.}} +% \author{Frank Mittelbach \and Phelype Oleinik} +% +% \maketitle +% +% +% \tableofcontents +% +% +% \section{Introduction} +% +% This file implements generic hooks for (arbitrary) commands. +% In theory every command \tn[no-index]{\meta{name}} offers now two +% associated hooks to which code can be added using \tn{AddToHook} +% or \tn{AddToHookNext}.\footnote{In practice this is not supported +% for all types of commands, see section~\ref{sec:look-ahead} for +% the restrictions that apply and what happens if one tries to use +% this with commands for which this is not supported.} These are +% \begin{description} +% \item[\hook{cmd/\meta{name}/before}] +% +% This hook is executed at the very start of the command +% execution after its arguments (if any) are parsed. The hook +% \meta{code} is wrapped in the command inside a call to +% \cs{UseHook}|{cmd/|\meta{name}|/before}|, so the arguments +% passed to the command are \emph{not} available in the hook +% \meta{code}. +% +% \item[\hook{cmd/\meta{name}/after}] +% +% This hook is similar to \hook{cmd/\meta{name}/before}, but it is +% executed at the very end of the command body. This hook is +% implemented as a reversed hook. +% \end{description} +% +% The hooks are not physically present before +% \verb=\begin{document}= (i.e., using a command in the preamble +% will never execute them) and if nobody has declared any code for +% them, then they are not added to the command code ever. For +% example, if we have the following definition +%\begin{verbatim} +% \newcommand\foo[2]{Code #1 for #2!} +%\end{verbatim} +% then executing \verb=\foo{A}{B}= will simply run +% \verb*=Code A for B!= +% as it was always the case. However, if somebody, somewhere (e.g., +% in a package) adds +%\begin{verbatim} +% \AddToHook{cmd/foo/before}{<before code>} +%\end{verbatim} +% then, after |\begin{document}| the definition of \cs[no-index]{foo} will be: +%\begin{verbatim} +% \renewcommand\foo[2]{\UseHook{cmd/foo/before}Code #1 for #2!} +%\end{verbatim} +% and similarly \verb=\AddToHook{cmd/foo/after}{<after code>}= +% alters the definition to +%\begin{verbatim} +% \renewcommand\foo[2]{Code #1 for #2!\UseHook{cmd/foo/after}} +%\end{verbatim} +% +% In other words, the mechanism is similar to what \pkg{etoolbox} +% offers with \tn{pretocmd} and \tn{apptocmd} with the important +% differences +% \begin{itemize} +% \item +% +% that code can be prepended or appended (i.e., added to the +% hooks) even if the command itself is not defined, because the +% defining package has not yet been loaded +% +% \item +% +% and that by using the hook management interface it is now +% possible to define how the code chunks added in these places +% are ordered, if different packages want to add code at these +% points. +% +% \end{itemize} +% +% +% +% +% \section{Restrictions and Operational details} +% \label{sec:restrictions} +% +% Adding arbitrary material to commands is tricky because most of the +% time we do not know what the macro expects as arguments when expanding +% and \TeX{} doesn't have a reliable way to see that, so some guesswork +% has to be employed. +% +% \subsection{Patching} +% +% The code here tries to find out if a command was defined with +% \tn{newcommand} or \tn{DeclareRobustCommand} or +% \tn{NewDocumentCommand}, and if so it \emph{assumes} that the argument +% specification of the command is as expected (which is not fail-proof, +% if someone redefines the internals of these commands in devious ways, +% but is a reasonable assumption). +% +% If the command is one of the defined types, the code here does a +% sandboxed expansion of the command such that it can be redefined again +% exactly as before, but with the hook code added. +% +% If however the command is not a known type (it was defined with +% \tn{def}, for example), then the code uses an approach similar to +% \pkg{etoolbox}'s \tn{patchcmd} to retokenize the command with the hook +% code in place. This procedure, however, is more likely to fail if the +% catcode settings are not the same as the ones at the time of command's +% definition, so not always adding a hook to a command will work. +% +% \subsubsection{Timing} +% +% When \cs{AddToHook} (or its \pkg{expl3} equivalent) is called with +% a generic |cmd| hook, say, \hook{cmd/foo/before}, for the first time +% (that is, no code was added to that same hook before), in the preamble +% of a document, it will store a patch instruction for that command +% until |\begin{document}|, and only then all the commands which had +% hooks added will be patched in one go. That means that no command in +% the preamble will have hooks patched into them. +% +% At |\begin{document}| all the delayed patches will be executed, and +% if the command doesn't exist the code is still added to the hook, +% but it will not be executed. After |\begin{document}|, when +% \cs{AddToHook} is called with a generic |cmd| hook the first time, +% the command will be immediately patched to include the hook, and if +% it doesn't exist or if it can't be patched for any reason, an error +% is thrown; if \cs{AddToHook} was already used in the preamble no new +% patching is attempted. +% +% This has the consequence that a command defined or redefined after +% |\begin{document}| only uses generic |cmd| hook code if +% \cs{AddToHook} is called for the first time after the definition is +% made, or if the command explicitly uses the generic hook in its +% definition by declaring it with \cs{NewHookPair} adding \cs{UseHook} as +% part of the code.\footnote{We might change this behavior in the main +% document slightly after gaining some usage experience.} +% +% +% \subsection{Commands that look ahead} +% \label{sec:look-ahead} +% +% Some commands are defined in different ``steps'' and they look ahead +% in the input stream to find more arguments. If you try to add some +% code to the \hook{cmd/\meta{name}/after} hook of such command, it will +% not work, and it is not possible to detect that programmatically, so +% the user has to know (or find out) which commands can or cannot have +% hooks attached to them. +% +% One good example is the \tn{section} command. You can add something +% to the \hook{cmd/section/before} hook, but if you try to add something +% to the \hook{cmd/section/after} hook, \tn{section} will no longer +% work. That happens because the \tn{section} macro takes no argument, +% but instead calls a few internal \LaTeX{} macros to look for the +% optional and mandatory arguments. By adding code to the +% \hook{cmd/section/after} hook, you get in the way of that scanning. +% +% +% +% \section{Package Author Interface} +% +% The \hook{cmd} hooks are, by default, available for all commands +% that can be patched to add the hooks. For some commands, however, +% the very beginning or the very end of the code is not the best place +% to put the hooks, for example, if the command looks ahead for +% arguments (see section~\ref{sec:look-ahead}). +% +% If you are a package author and you want to add the hooks to your +% own commands in the proper position you can define the command and +% manually add the \cs{UseHook} calls inside the command in the proper +% positions, and manually define the hooks with \cs{NewHook} or +% \cs{NewReversedHook}. When the hooks are explicitly defined, +% patching is not attempted so you can make sure your command works +% properly. For example, an (admittedly not really useful) command +% that typesets its contents in a framed box with width optionally +% given in parentheses: +% \begin{verbatim} +% \newcommand\fancybox{\@ifnextchar({\@fancybox}{\@fancybox(5cm)}} +% \def\@fancybox(#1)#2{\fbox{\parbox{#1}{#2}}} +% \end{verbatim} +% If you try that definition, then add some code after it with +% \begin{verbatim} +% \AddToHook{cmd/fancybox/after}{<code>} +% \end{verbatim} +% and then use the \cs[no-index]{fancybox} command you will see that it +% will be completely broken, because the hook will get executed in the +% middle of parsing for optional \texttt{(...)} argument. +% +% If, on the other hand, you want to add hooks to your command you can +% do something like: +% \begin{verbatim} +% \newcommand\fancybox{\@ifnextchar({\@fancybox}{\@fancybox(5cm)}} +% \def\@fancybox(#1)#2{\fbox{% +% \UseHook{cmd/fancybox/before}% +% \parbox{#1}{#2}% +% \UseHook{cmd/fancybox/after}}} +% \NewHook{cmd/fancybox/before} +% \NewReversedHook{cmd/fancybox/after} +% \end{verbatim} +% then the hooks will be executed where they should and no patching +% will be attempted. It is important that the hooks are declared with +% \cs{NewHook} or \cs{NewReversedHook}, otherwise the command hook +% code will try to patch the command. Note also that the call to +% |\UseHook{cmd/fancybox/before}| does not need to be in the +% definition of \cs[no-index]{fancybox}, but anywhere it makes sense +% to insert it (in this case in the internal +% \cs[no-index]{@fancybox}). +% +% Alternatively, if for whatever reason your command does not support +% the generic hooks provided here, you can disable a hook with +% \cs{DisableHook}\footnote{Please use \cs{DisableHook} if at all, only +% on hooks that you \enquote{own}, i.e., for commands that your +% package or class defines and not second guess +% whether or not hooks of other packages should get disabled!}, so +% that when someone tries to add code to it they will get an error. +% Or if you don't want the error, you can simply declare the hook with +% \cs{NewHook} and never use it. +% +% +% The above approach is useful for really complex commands where for +% one or the other reason the hooks can't be placed at the very +% beginning and end of the command body and some hand-crafting is +% needed. However, in the example above the real (and in fact only) +% issue is the cascading argument parsing in the style developed long +% ago in \LaTeX~2.09. Thus, a much simpler solution for this case is +% to replace it with the modern \cs{NewDocumentCommand} syntax and +% define the command as follows: +% \begin{verbatim} +% \DeclareDocumentCommand\fancybox{D(){5cm}m}{\fbox{\parbox{#1}{#2}}} +% \end{verbatim} +% If you do that then both hooks automatically work and are patched +% into the right places. +% +% \StopEventually{\setlength\IndexMin{200pt} \PrintIndex } +% +% +% +% \section{The Implementation} +% +% \subsection{Execution plan} +% +% To add |before| and |after| hooks to a command we will need to peek +% into the definition of a command, which is always a tricky thing to +% do. Some cases are easy because we know how the command was defined, +% so we can assume how its \meta{parameter text} looks like (for example +% a command defined with \tn{newcommand} may have an optional argument +% followed by a run of mandatory arguments), so we can just expand that +% command and make it grab |#1|, |#2|, etc.\@ as arguments and +% define it all back with the hooks added. +% +% Life's usually not that easy, so with some commands we can't do that +% (a |#1| might as well be |#|$_{12}$|1|$_{12}$ instead of the expected +% |#|$_{6}$|1|$_{12}$, for example) so we need to resort to ``patching'' +% the command: read its \tn{meaning}, and tokenize it again with +% \tn{scantokens} and hope for the best. +% +% So the overall plan is: +% \begin{enumerate} +% \item +% Check if a command is of a known type (that is, defined with +% \tn{newcommand}\footnote{It's not always possible to reliably +% detect this case because a command defined with no optional +% argument is indistinguishable from a \tn{def}ed command.}, +% \cs[no-index]{DeclareRobustCommand}, or +% \cs[no-index]{New(Expandable)DocumentCommand}), and if is, take +% appropriate action. +% \item +% If the command is not a known type, we'll check if the command can +% be patched. Two things will prevent a command from being +% patched: if it was defined in a nonstandard catcode setting, or +% if it is an internal expl3 command with |__|\meta{module} in its +% name, in which case we refuse to patch. +% \item +% If the command was defined in nonstandard catcode settings, we +% will try a few standard ones to try our best to carry out the +% pathing. If this doesn't help either, the code will give up and +% throw an error. +% \end{enumerate} +% +% \begin{macrocode} +%<@@=hook> +% \end{macrocode} +% +% \begin{macrocode} +%<*2ekernel|latexrelease> +\ExplSyntaxOn +%<latexrelease>\NewModuleRelease{2021/06/01}{ltcmdhooks} +%<latexrelease> {The~hook~management~system~for~commands} +% \end{macrocode} +% +% \subsection{Variables} +% +% \begin{macro}[int]{\g_hook_patch_action_list_tl} +% Pairs of |\if<cmd>..\patch<cmd>| to be used with +% \tn{robust@command@act} when looking for a known patching +% rule. This token list is exposed because we see some future +% applications (with very specialized packages, such as +% \pkg{etoolbox} that may want to extend the pairs processed. It is +% not meant for general use which is why it is not documented in +% the interface documentation above. +% \begin{macrocode} +\tl_new:N \g_hook_patch_action_list_tl +% \end{macrocode} +% \end{macro} +% +% \begin{macro}{\l_@@_patch_num_args_int} +% The number of arguments in a macro being patched. +% \begin{macrocode} +\int_new:N \l_@@_patch_num_args_int +% \end{macrocode} +% \end{macro} +% +% \begin{macro}{\l_@@_patch_prefixes_tl} +% \begin{macro}{\l_@@_patch_param_text_tl} +% \begin{macro}{\l_@@_patch_replacement_tl} +% The prefixes and parameters of the definition for the macro being +% patched. +% \begin{macrocode} +\tl_new:N \l_@@_patch_prefixes_tl +\tl_new:N \l_@@_patch_param_text_tl +\tl_new:N \l_@@_patch_replacement_tl +% \end{macrocode} +% \end{macro} +% \end{macro} +% \end{macro} +% +% \begin{macro}{\g_@@_delayed_patches_prop} +% A list containing the patches delayed to |\begin{document}|, so that +% patching is not attempted twice. +% \begin{macrocode} +\prop_new:N \g_@@_delayed_patches_prop +% \end{macrocode} +% \end{macro} +% +% \begin{macro}{\@@_patch_debug:x} +% A helper for patching debug info. +% \begin{macrocode} +\cs_new_protected:Npn \@@_patch_debug:x #1 + { \@@_debug:n { \iow_term:x { [lthooks]~#1 } } } +% \end{macrocode} +% \end{macro} +% +% \subsection{Variants} +% +% \begin{macro}[int]{\tl_rescan:nV} +% \pkg{expl3} function variants used throughout the code. +% \begin{macrocode} +\cs_generate_variant:Nn \tl_rescan:nn { nV } +% \end{macrocode} +% \end{macro} +% +% \subsection{Patching or delaying} +% +% \begin{macro}{\@@_try_put_cmd_hook:n,\@@_try_put_cmd_hook:w} +% Before |\begin{document}| all patching is delayed. +% This function is called from within \cs{AddToHook}, when code is +% added to a generic |cmd| hook is newly declared. It checks whether +% the patch position is valid, then proceeds to trying to patch or +% delaying to |\begin{document}| if in the preamble. +% \begin{macrocode} +\cs_new_protected:Npn \@@_try_put_cmd_hook:n #1 + { \@@_try_put_cmd_hook:w #1 / / / \s_@@_mark {#1} } +\cs_new_protected:Npn \@@_try_put_cmd_hook:w + #1 / #2 / #3 / #4 \s_@@_mark #5 + { + \@@_debug:n { \iow_term:n { ->~Adding~cmd~hook~to~'#2'~(#3): } } + \str_case:nnTF {#3} + { { before } { } { after } { } } + { \exp_args:Nc \@@_patch_cmd_or_delay:Nnn {#2} {#2} {#3} } + { \__kernel_msg_error:nnn { hooks } { wrong-cmd-hook } {#2} {#3} } + } +% \end{macrocode} +% \end{macro} +% +% \begin{macro}{\@@_patch_cmd_or_delay:Nnn} +% \begin{macro}{\@@_cmd_begindocument_code:} +% In the preamble, \cs{@@_patch_cmd_or_delay:Nnn} just adds the patch +% instruction to a property list to be executed later. +% \begin{macrocode} +\cs_new_protected:Npn \@@_patch_cmd_or_delay:Nnn #1 #2 #3 + { + \@@_debug:n { \iow_term:n { ->~Add~generic~cmd~hook~for~#2~(#3). } } + \@@_debug:n + { \iow_term:n { !~In~the~preamble:~delaying. } } + \prop_gput:Nnn \g_@@_delayed_patches_prop { #2 / #3 } + { \@@_cmd_try_patch:nn {#2} {#3} } + } +% \end{macrocode} +% +% The delayed patches are added to a property list to prevent +% duplication, and the code stored in the property list for each +% key is executed. The function \cs{@@_patch_cmd_or_delay:Nnn} is +% also redefined to be \cs{@@_patch_command:Nnn} so that no further +% delaying is attempted. +% \begin{macrocode} +\cs_new_protected:Npn \@@_cmd_begindocument_code: + { + \cs_gset_eq:NN \@@_patch_cmd_or_delay:Nnn \@@_patch_command:Nnn + \prop_map_function:NN \g_@@_delayed_patches_prop { \use_ii:nn } + \prop_gclear:N \g_@@_delayed_patches_prop + \cs_undefine:N \@@_cmd_begindocument_code: + } +\g@addto@macro \@kernel@after@begindocument + { \@@_cmd_begindocument_code: } +% \end{macrocode} +% \end{macro} +% \end{macro} +% +% \begin{macro}{\@@_cmd_try_patch:nn} +% At |\begin{document}| tries patching the command if the hook +% was not manually created in the meantime. If the document does not +% exist, no error is raised here as it may hook into a package that +% wasn't loaded. Hooks added to commands in the document body still +% raise an error if the command is not defined. +% \begin{macrocode} +\cs_new_protected:Npn \@@_cmd_try_patch:nn #1 #2 + { + \@@_debug:n + { \iow_term:x { ->~\string\begin{document}~try~cmd / #1 / #2. } } + \@@_if_declared:nTF { cmd / #1 / #2 } + { + \@@_debug:n + { \iow_term:n { .->~Giving~up:~hook~already~created. } } + } + { + \cs_if_exist:cT {#1} + { \exp_args:Nc \@@_patch_command:Nnn {#1} {#1} {#2} } + } + } +% \end{macrocode} +% \end{macro} +% +% +% +% +% +% \subsection{Patching commands} +% +% \begin{macro}{\@@_patch_command:Nnn} +% \begin{macro}{\@@_patch_check:NNnn} +% \begin{macro}[TF]{\@@_if_public_command:N} +% \begin{macro}{\@@_if_public_command:w} +% \cs{@@_patch_command:Nnn} will do some sanity checks on the +% argument to detect if it is possible to add hooks to the command, +% and raises an error otherwise. If the command can contain hooks, +% then it uses \tn{robust@command@act} to find out what type is the +% command, and patch it accordingly. +% \begin{macrocode} +\cs_new_protected:Npn \@@_patch_command:Nnn #1 #2 #3 + { + \@@_patch_debug:x { analyzing~'\token_to_str:N #1' } + \@@_patch_debug:x { \token_to_str:N #1 = \token_to_meaning:N #1 } + \@@_patch_check:NNnn \cs_if_exist:NTF #1 { undef } + { + \@@_patch_debug:x { ++~control~sequence~is~defined } + \@@_patch_check:NNnn \token_if_macro:NTF #1 { macro } + { + \@@_patch_debug:x { ++~control~sequence~is~a~macro } + \@@_patch_check:NNnn \@@_if_public_command:NTF #1 { expl3 } + { + \@@_patch_debug:x { ++~macro~is~not~private } + \robust@command@act + \g_hook_patch_action_list_tl #1 + \@@_retokenize_patch:Nnn { #1 {#2} {#3} } + } + } + } + } +% \end{macrocode} +% +% And here's the auxiliary used above: +% \begin{macrocode} +\cs_new_protected:Npn \@@_patch_check:NNnn #1 #2 #3 #4 + { + #1 #2 {#4} + { + \__kernel_msg_error:nnxx { hooks } { cant-patch } + { \token_to_str:N #2 } {#3} + } + } +% \end{macrocode} +% and a conditional \cs{@@_if_public_command:N} to check if a command +% has |__| in its name (no other checking is performed). Primitives +% with |:D| in their name could be included here, but they are already +% discarded in the \cs{token_if_macro:NTF} test above. +% \begin{macrocode} +\use:x + { + \prg_new_protected_conditional:Npnn + \exp_not:N \@@_if_public_command:N ##1 { TF } + { + \exp_not:N \exp_last_unbraced:Nf + \exp_not:N \@@_if_public_command:w + { \exp_not:N \cs_to_str:N ##1 } + \tl_to_str:n { _ _ } \s_@@_mark + } + } +\exp_last_unbraced:NNNNo +\cs_new_protected:Npn \@@_if_public_command:w + #1 \tl_to_str:n { _ _ } #2 \s_@@_mark + { + \tl_if_empty:nTF {#2} + { \prg_return_true: } + { \prg_return_false: } + } +% \end{macrocode} +% \end{macro} +% \end{macro} +% \end{macro} +% \end{macro} +% +% +% +% +% +% +% +% \subsubsection{Patching by expansion and redefinition} +% +% \begin{macro}[int]{\g_hook_patch_action_list_tl} +% This is the list of known command types and the function that +% patches the command hooks into them. The conditionals are taken +% from \tn{ShowCommand}, \tn{NewCommandCopy} and +% \cs{__kernel_cmd_if_xparse:NTF} defined in \texttt{ltcmd}. +% \begin{macrocode} +\tl_gset:Nn \g_hook_patch_action_list_tl + { + { \@if@DeclareRobustCommand \@@_patch_DeclareRobustCommand:Nnn } + { \@if@newcommand \@@_patch_newcommand:Nnn } + { \__kernel_cmd_if_xparse:NTF \@@_cmd_patch_xparse:Nnn } + } +% \end{macrocode} +% \end{macro} +% +% +% +% +% \begin{macro}{\@@_patch_DeclareRobustCommand:Nnn} +% At this point we know that the commands can be patched by expanding +% then redefining. These are the cases of commands defined with +% \tn{newcommand} with an optional argument or with +% \tn{DeclareRobustCommand}. +% +% With \cs{@@_patch_DeclareRobustCommand:Nnn} we check if the command +% has an optional argument (with a test counter-intuitively called +% \tn{@if@newcommand}). If so, we forward the action to +% \cs{@@_patch_newcommand:Nnn}, otherwise call the patching engine +% \cs{@@_patch_expand_redefine:NNnn} with a \cs{c_false_bool} to +% indicate that there is no optional argument. +% \begin{macrocode} +\cs_new_protected:Npn \@@_patch_DeclareRobustCommand:Nnn #1 + { + \exp_args:Nc \@if@newcommand { \cs_to_str:N #1 ~ } + { \exp_args:Nc \@@_patch_newcommand:Nnn } + { \exp_args:NNc \@@_patch_expand_redefine:NNnn \c_false_bool } + { \cs_to_str:N #1 ~ } + } +% \end{macrocode} +% \end{macro} +% +% +% +% \begin{macro}{\@@_patch_newcommand:Nnn} +% If the command was defined with \tn{newcommand} and an optional +% argument, call the patching engine with a \cs{c_true_bool} to flag +% the presence of an optional argument, and with +% \cs[no-index]{\string\command} to patch the actual code for +% \cs[no-index]{command}. +% \begin{macrocode} +\cs_new_protected:Npn \@@_patch_newcommand:Nnn #1 + { + \exp_args:NNc \@@_patch_expand_redefine:NNnn \c_true_bool + { \c_backslash_str \cs_to_str:N #1 } + } +% \end{macrocode} +% \end{macro} +% +% \begin{macro}{\@@_cmd_patch_xparse:Nnn} +% And for commands defined by the \pkg{xparse} commands use this +% for patching: +% \begin{macrocode} +\cs_new_protected:Npn \@@_cmd_patch_xparse:Nnn #1 + { + \exp_args:NNc \@@_patch_expand_redefine:NNnn \c_false_bool + { \cs_to_str:N #1 ~ code } + } +% \end{macrocode} +% \end{macro} +% +% +% +% +% \begin{macro}{\@@_patch_expand_redefine:NNnn} +% \begin{macro}[EXP]{\@@_make_prefixes:w} +% Now the real action begins. Here we have in |#1| a boolean +% indicating if the command has a |[|\ldots|]|-delimited argument, in +% |#2| the command control sequence, in |#3| the name of the command +% (note that |#1|${}\ne{}$|\csname#2\endcsname| at this point!), and +% in |#4| the hook position, either |before| or |after|. +% \begin{macrocode} +\cs_new_protected:Npn \@@_patch_expand_redefine:NNnn #1 #2 #3 #4 + { + \@@_patch_debug:x { ++~command~can~be~patched~without~rescanning } +% \end{macrocode} +% We'll start by counting the number of arguments in the command by +% counting the number of characters in the \cs{cs_argument_spec:N} of +% the macro, divided by two, and subtracting one if the command has an +% optional argument (that is, an extra |[]| in its +% \meta{parameter text}). +% \begin{macrocode} + \int_set:Nn \l_@@_patch_num_args_int + { + \exp_args:Nf \str_count:n { \cs_argument_spec:N #2 } / 2 + \bool_if:NT #1 { -1 } + } +% \end{macrocode} +% Now build two token lists: +% \begin{description} +% \item[\cs{l_@@_patch_param_text_tl}] will contain the +% \meta{parameter text} to be used when redefining the macro. It +% should be identical to the \meta{parameter text} used when +% originally defining that macro. +% \item[\cs{l_@@_patch_replacement_tl}] will contain braced pairs of +% |#|$_{12}$\meta{num} to feed to the macro when expanded. This +% token list as well as the previous will have the first item +% surrounded by |[|\ldots|]| in the case of an optional argument. +% \end{description} +% \begin{macrocode} + \int_compare:nNnTF { \l_@@_patch_num_args_int } > { \c_zero_int } + { + \tl_set:Nx \l_@@_patch_param_text_tl + { \bool_if:NTF #1 { [####1] } { ####1 } } + \tl_set:Nx \l_@@_patch_replacement_tl + { \bool_if:NTF #1 { [####1] } { {####1} } } + \int_step_inline:nnn { 2 } { \l_@@_patch_num_args_int } + { + \tl_put_right:Nn \l_@@_patch_param_text_tl { ## ####1 } + \tl_put_right:Nn \l_@@_patch_replacement_tl { { ## ####1 } } + } + } + { + \tl_clear:N \l_@@_patch_param_text_tl + \tl_clear:N \l_@@_patch_replacement_tl + } +% \end{macrocode} +% Finally, before redefining, we need to also get the prefixes used +% when defining the command. Here we ensure that the \tn{escapechar} +% is printable, otherwise a macro defined with prefixes +% |\protected \long| will have it \tn{meaning} printed as +% |protectedlong|, making life unnecessarily complicated. Here the +% \tn{escapechar} is changed to |/|, then we loop between pairs of +% |/|\ldots|/| extracting the prefixes. +% \begin{macrocode} + \group_begin: + \int_set:Nn \tex_escapechar:D { `\/ } + \use:x + { + \group_end: + \tl_set:Nx \exp_not:N \l_@@_patch_prefixes_tl + { \exp_not:N \@@_make_prefixes:w \cs_prefix_spec:N #2 / / } + } +% \end{macrocode} +% Now that all the needed tools are ready, without further ado we'll +% redefine the command |#2|. The definition uses the prefixes +% gathered in \cs{l_@@_patch_prefixes_tl}, a primitive \cs{tex_def:D} +% to avoid adding extra prefixes, and the \meta{parameter text} from +% \cs{l_@@_patch_param_text_tl}. +% +% Then finally, in the body of the definition, we insert +% \hook{cmd/\#3/before} if |#4| is |before|, the code of the +% command expanded once grabbing the parameters in +% \cs{l_@@_patch_replacement_tl}, and \hook{cmd/\#3/after} if |#4| is +% |after|. +% \begin{macrocode} + \use:x + { + \l_@@_patch_prefixes_tl \tex_def:D + \exp_not:N #2 \exp_not:V \l_@@_patch_param_text_tl + { + \str_if_eq:nnT {#4} { before } + { \exp_not:N \UseHook { cmd / #3 / #4 } } + \exp_args:No \exp_not:o + { \exp_after:wN #2 \l_@@_patch_replacement_tl } + \str_if_eq:nnT {#4} { after } + { \exp_not:N \UseHook { cmd / #3 / #4 } } + } + } + } +% \end{macrocode} +% +% Here's the auxiliary that makes the prefix control sequences for the +% redefinition. Each item has to be \cs{tl_trim_spaces:n}'d because +% the last item (and not any other) has a trailing space. +% \begin{macrocode} +\cs_new:Npn \@@_make_prefixes:w / #1 / + { + \tl_if_empty:nF {#1} + { + \exp_not:c { tex_ \tl_trim_spaces:n {#1} :D } + \@@_make_prefixes:w / + } + } +% \end{macrocode} +% \end{macro} +% \end{macro} +% +% +% +% +% +% +% \subsubsection{Patching by retokenization} +% +% At this point we've drained the possibilities of patching a command by +% expansion-and-redefinition, so we have to resort to patching by +% retokenizing the command. Patching by retokenization is done by +% getting the \tn{meaning} of the command, doing the necessary +% manipulations on the generated string, and the retokenizing that again +% by using \tn{scantokens}. +% +% Patching by retokenization is definitely a riskier business, because +% it relies that the tokens printed by \tn{meaning} produce the exact +% same tokens as the ones in the original definition. That is, the +% catcode régime must be exactly(ish) the same, and there is no way of +% telling except by trial and error. +% +% \begin{macro}{\@@_retokenize_patch:Nnn} +% This is the macro that will control the whole process. First we'll +% try out one final, rather trivial case, of a command with no +% arguments; that is, a token list. This case can be patched with +% the expand-and-redefine routine but it has to be the very last case +% tested for, because most (all?) robust commands start with a +% top-level macro with no arguments, so testing this first would +% short-circuit \tn{robust@command@act} and the top-level macros would +% be incorrectly patched. In that case, we just check if the +% \cs{cs_argument_spec:N} is empty, and call +% \cs{@@_patch_expand_redefine:NNnn}. +% \begin{macrocode} +\cs_new_protected:Npn \@@_retokenize_patch:Nnn #1 #2 #3 + { + \@@_patch_debug:x { ..~command~can~only~be~patched~by~rescanning } + \str_if_eq:eeTF { \cs_argument_spec:N #1 } { } + { \@@_patch_expand_redefine:NNnn \c_false_bool #1 {#2} {#3} } + { +% \end{macrocode} +% +% Otherwise, we start the actual patching by retokenization job. The +% code calls \cs{@@_try_patch_with_catcodes:Nnnnw} with a different +% catcode setting: +% \begin{itemize} +% \item The current catcode setting; +% \item Switching the catcode of |@|; +% \item Switching the \pkg{expl3} syntax on or off; +% \item Both of the above. +% \end{itemize} +% +% If patching succeeds, \cs{@@_try_patch_with_catcodes:Nnnnw} has the +% side-effect of patching the macro |#1| (which may be an internal +% from the command whose name is~|#2|). +% \begin{macrocode} + \tl_set:Nx \l_@@_tmpa_tl + { + \int_compare:nNnTF { \char_value_catcode:n {`\@ } } = { 12 } + { \exp_not:N \makeatletter } { \exp_not:N \makeatother } + } + \tl_set:Nx \l_@@_tmpb_tl + { + \bool_if:NTF \l__kernel_expl_bool + { \ExplSyntaxOff } { \ExplSyntaxOn } + } + \use:x + { + \exp_not:N \@@_try_patch_with_catcodes:Nnnnw + \exp_not:n { #1 {#2} {#3} } + { \prg_do_nothing: } + { \exp_not:V \l_@@_tmpa_tl } % @ + { \exp_not:V \l_@@_tmpb_tl } % _: + { + \exp_not:V \l_@@_tmpa_tl % @ + \exp_not:V \l_@@_tmpb_tl % _: + } + } + \q_recursion_tail \q_recursion_stop +% \end{macrocode} +% +% If no catcode setting succeeds, give up and raise an error. The +% command isn't changed in any way in that case. +% \begin{macrocode} + { + \__kernel_msg_error:nnxx { hooks } { cant-patch } + { \c_backslash_str #2 } { retok } + } + } + } +% \end{macrocode} +% \end{macro} +% +% +% +% \begin{macro}{\@@_try_patch_with_catcodes:Nnnnw} +% This function is a simple wrapper around +% \cs{@@_cmd_if_scanable:NnTF} and \cs{@@_patch_retokenize:Nnnn} if +% the former returns \meta{true}, plus some debug messages. +% \begin{macrocode} +\cs_new_protected:Npn \@@_try_patch_with_catcodes:Nnnnw #1 #2 #3 #4 + { + \quark_if_recursion_tail_stop_do:nn {#4} { \use:n } + \@@_patch_debug:x { ++~trying~to~patch~by~retokenization } + \@@_cmd_if_scanable:NnTF {#1} {#4} + { + \@@_patch_debug:x { ++~macro~can~be~retokenized~cleanly } + \@@_patch_debug:x { ==~retokenizing~macro~now } + \@@_patch_retokenize:Nnnn #1 {#2} {#3} {#4} + \use_i_delimit_by_q_recursion_stop:nw \use_none:n + } + { + \@@_patch_debug:x { --~macro~cannot~be~retokenized~cleanly } + \@@_try_patch_with_catcodes:Nnnnw #1 {#2} {#3} + } + } +% \end{macrocode} +% \end{macro} +% +% +% +% +% \begin{macro}[int]{\kerneltmpDoNotUse} +% This is an oddity required to be safe (as safe as reasonably +% possible) when patching the command. The entirety of +% \begin{quote} +% \meta{prefixes} \tn{def} \meta{cs} \meta{parameter text} +% |{|\meta{replacement text}|}| +% \end{quote} +% will go through \tn{scantokens}. The \meta{parameter text} and +% \meta{replacement text} are what we are trying to retokenize, so not +% much worry there. The other items, however, should ``just work'', +% so some care is needed to not use too fancy catcode settings. +% Therefore we can't use an \pkg{expl3}-named macro for \meta{cs}, nor +% the \pkg{expl3} versions of \tn{def} or the \meta{prefixes}. +% That is why the definitions that will eventually go into +% \tn{scantokens} will use the oddly (but hopefully clearly)-named +% \cs{kerneltmpDoNotUse}: +% \begin{macrocode} +\cs_new_eq:NN \kerneltmpDoNotUse ! +% \end{macrocode} +% \phoinline{Maybe this can be avoided by running the \meta{parameter text} +% and the \meta{replacement text} separately through \tn{scantokens} +% and then putting everything together at the end.} +% \end{macro} +% +% +% +% \begin{macro}{\@@_patch_required_catcodes:} +% Here are the catcode settings that are \emph{mandatory} when +% retokenizing commands. These are the minimum necessary settings to +% perform the definitions: they identify control sequences, which +% must be escaped with |\|$_0$, delimit the definition with |{|$_1$ +% and |}|$_2$, and mark parameters with |#|$_6$. Everything else may +% be changed, but not these. +% \begin{macrocode} +\cs_new_protected:Npn \@@_patch_required_catcodes: + { + \char_set_catcode_escape:N \\ + \char_set_catcode_group_begin:N \{ + \char_set_catcode_group_end:N \} + \char_set_catcode_parameter:N \# + % \int_set:Nn \tex_endlinechar:D { -1 } + % \int_set:Nn \tex_newlinechar:D { -1 } + } +% \end{macrocode} +% \phoinline{\pkg{etoolbox} sets the \tn{endlinechar} and \tn{newlinechar} +% when patching, but as far as I tested these didn't make much of +% a difference, so I left them out for now. Maybe +% \tn{newlinechar}|=-1| avoids a space token being added after the +% definition.} +% \phoinline{If the patching is split by \meta{parameter text} and +% \meta{replacement text}, then only \# will have to stay in that +% list.} +% \phoinline{Actually now that we patch +% \texttt{\cs{UseHook}\{cmd/foo/before\}}, all the tokens there need +% to have the right catcodes, so this list now includes all +% lowercase letters, U and H, the slash, and whatever characters in +% the command name\ldots sigh\ldots} +% \end{macro} +% +% +% +% +% \begin{macro}[TF]{\@@_cmd_if_scanable:Nn} +% Here we'll do a quick test if the command being patched can in fact +% be retokenized with the specific catcode setting without changing +% in meaning. The test is straightforward: +% \begin{enumerate} +% \item apply \tn{meaning} to the command; +% \item split the \meta{prefixes}, \meta{parameter text} and +% \meta{replacement text} and arrange them as +% \begin{quote} +% \meta{prefixes}\tn{def}\cs{kerneltmpDoNotUse}%^^A +% \meta{parameter text}|{|\meta{replacement text}|}| +% \end{quote} +% \item rescan that with the given catcode settings, and do +% the definition; then finally +% \item compare \cs{kerneltmpDoNotUse} with the original command. +% \end{enumerate} +% If both are \tn{ifx}-equal, the command can be safely patched. +% \begin{macrocode} +\prg_new_protected_conditional:Npnn \@@_cmd_if_scanable:Nn #1 #2 { TF } + { + \cs_set_eq:NN \kerneltmpDoNotUse \scan_stop: + \cs_set_eq:NN \@@_tmp:w \scan_stop: + \use:x + { + \cs_set:Npn \@@_tmp:w + ####1 \tl_to_str:n { macro: } ####2 -> ####3 \s_@@_mark + { ####1 \def \kerneltmpDoNotUse ####2 {####3} } + \tl_set:Nx \exp_not:N \l_@@_tmpa_tl + { \exp_not:N \@@_tmp:w \token_to_meaning:N #1 \s_@@_mark } + } + \tl_rescan:nV { #2 \@@_patch_required_catcodes: } \l_@@_tmpa_tl + \token_if_eq_meaning:NNTF #1 \kerneltmpDoNotUse + { \prg_return_true: } + { \prg_return_false: } + } +% \end{macrocode} +% \end{macro} +% +% +% +% \begin{macro}{\@@_patch_retokenize:Nnnn} +% Then, if \cs{@@_cmd_if_scanable:NnTF} returned true, we can go on +% and patch the command. +% \begin{macrocode} +\cs_new_protected:Npn \@@_patch_retokenize:Nnnn #1 #2 #3 #4 + { +% \end{macrocode} +% Start off by making some things \tn{relax} to avoid lots of +% \tn{noexpand} below. +% \begin{macrocode} + \cs_set_eq:NN \kerneltmpDoNotUse \scan_stop: + \cs_set_eq:NN \@@_tmp:w \scan_stop: + \use:x + { +% \end{macrocode} +% Now we'll define \cs{@@_tmp:w} such that it splits the \tn{meaning} +% of the macro (|#1|) into its three parts: +% \begin{enumerate} +% \def\makelabel#1{\texttt{\#\#\#\##1}} +% \item \meta{prefixes} +% \item \meta{parameter text} +% \item \meta{replacement text} +% \end{enumerate} +% and arrange that a complete definition, then place the |before| +% or |after| hooks around the \meta{replacement text}: +% accordingly. +% \begin{macrocode} + \cs_set:Npn \@@_tmp:w + ####1 \tl_to_str:n { macro: } ####2 -> ####3 \s_@@_mark + { + ####1 \def \kerneltmpDoNotUse ####2 + { + \str_if_eq:nnT {#3} { before } + { \token_to_str:N \UseHook { cmd / #2 / #3 } } + ####3 + \str_if_eq:nnT {#3} { after } + { \token_to_str:N \UseHook { cmd / #2 / #3 } } + } + } +% \end{macrocode} +% Now we just have to get the \tn{meaning} of the command being +% patched and pass it through the meat grinder above. +% \begin{macrocode} + \tl_set:Nx \exp_not:N \l_@@_tmpa_tl + { \exp_not:N \@@_tmp:w \token_to_meaning:N #1 \s_@@_mark } + } +% \end{macrocode} +% Now rescan with the given catcode settings (overridden by the +% \cs{@@_patch_required_catcodes:}), and implicitly (by using the +% rescanned token list) carry out the definition from above. +% \begin{macrocode} + \tl_rescan:nV { #4 \@@_patch_required_catcodes: } \l_@@_tmpa_tl +% \end{macrocode} +% And to close, copy the newly-defined command into the old name and +% the patching is finally completed: +% \begin{macrocode} + \cs_set_eq:NN #1 \kerneltmpDoNotUse + } +% \end{macrocode} +% \end{macro} +% +% \subsection{Messages} +% +% \begin{macrocode} +\__kernel_msg_new:nnnn { hooks } { wrong-cmd-hook } + { + Command~hook~`cmd/#1/#2'~invalid.\\ + The~hook~should~be~`cmd/#1/before'~or~`cmd/#1/after'. + } + { + You~tried~to~add~a~hook~to~command~\iow_char:N \\#1,~but~`#2'~ + is~an~invalid~position.~Only~`before'~or~`after'~are~allowed. + } +\__kernel_msg_new:nnnn { hooks } { cant-patch } + { + Command~`#1'~cannot~have~hooks~because~it~ + \@@_unpatchable_cases:n {#2} . + } + { + You~tried~to~add~a~hook~to~`#1',~but~LaTeX~was~not~able~to~ + add~the~hook~to~that~command~because~`#1'~ + \@@_unpatchable_cases:n {#2} . + } +\cs_new:Npn \@@_unpatchable_cases:n #1 + { + \str_case:nn {#1} + { + { undef } { doesn't~exist } + { macro } { is~not~a~macro } + { expl3 } { is~a~private~expl3~macro } + { retok } { can't~be~retokenized~cleanly } + } + } +% \end{macrocode} +% +% +% \begin{macrocode} +%<latexrelease>\IncludeInRelease{0000/00/00}{ltcmdhooks}% +%<latexrelease> {The~hook~management~system~for~commands} +%<latexrelease> +% \end{macrocode} +% The command \cs{@@_cmd_begindocument_code:} is used in an +% internal hook, so we need to make sure it has a harmless +% definition after rollback as that will not remove it from the +% kernel hook. +% \begin{macrocode} +%<latexrelease>\cs_set_eq:NN \@@_cmd_begindocument_code: \prg_do_nothing: +%<latexrelease> +%<latexrelease>\EndModuleRelease +\ExplSyntaxOff +%</2ekernel|latexrelease> +% \end{macrocode} +% +% \begin{macrocode} +%<@@=> +% \end{macrocode} +% +% \Finale +% diff --git a/Master/texmf-dist/source/latex-dev/base/ltdefns.dtx b/Master/texmf-dist/source/latex-dev/base/ltdefns.dtx index ab9d4d0da16..7236083dfa2 100644 --- a/Master/texmf-dist/source/latex-dev/base/ltdefns.dtx +++ b/Master/texmf-dist/source/latex-dev/base/ltdefns.dtx @@ -32,7 +32,7 @@ %<*driver> % \fi \ProvidesFile{ltdefns.dtx} - [2021/01/15 v1.5o LaTeX Kernel (definition commands)] + [2021/04/19 v1.5o LaTeX Kernel (definition commands)] % \iffalse \documentclass{ltxdoc} \GetFileInfo{ltdefns.dtx} @@ -2419,7 +2419,8 @@ % % % \begin{macro}{\g@addto@macro} -% Globally add to the end of a macro. +% Globally add to the end of a macro. +% This macro is used by the kernel to add to its internal hooks. % \changes{v0.2a}{1993/11/14}{Made global} % \changes{v0.2w}{1994/01/31} % {Use toks register to avoid `hash' problems} diff --git a/Master/texmf-dist/source/latex-dev/base/ltexpl.dtx b/Master/texmf-dist/source/latex-dev/base/ltexpl.dtx index a9be1b05211..6d7ec3ff60b 100644 --- a/Master/texmf-dist/source/latex-dev/base/ltexpl.dtx +++ b/Master/texmf-dist/source/latex-dev/base/ltexpl.dtx @@ -33,7 +33,7 @@ %<*driver> % \fi \ProvidesFile{ltexpl.dtx} - [2021/01/24 v1.3a LaTeX Kernel (expl3-dependent code)] + [2021/04/20 v1.3c LaTeX Kernel (expl3-dependent code)] % \iffalse \documentclass{ltxdoc} \GetFileInfo{ltexpl.dtx} @@ -82,21 +82,46 @@ % \begin{macrocode} %<*2ekernel|latexrelease> %<latexrelease>\IncludeInRelease{2020/10/01}% -%<latexrelease> {kernel@enddocument hooks}{Define kernel enddocument Hooks}% -\let\@kernel@after@enddocument\@empty -\let\@kernel@after@enddocument@afterlastpage\@empty +%<latexrelease> {kernel@enddocument hooks}{Define several kernel hooks} +% \end{macrocode} +% We only initialize these kernel hooks if they are not already +% existing. Otherwise they would be set to \cs{@empty} on rollback +% which would be wrong because code that has been added to to them +% may still have to be executed in the rollback situation . Instead +% code that writes to them needs to handle the rollback as needed. +% It is likely that we have to change that approach in the future, +% but for now it should do. +% (It is enough to test only for the existence of one hook, as all +% got added at the same time.) +% \changes{v1.3c}{2021/04/20}{Don't empty kernel hooks on rollback} +% \begin{macrocode} +\ifx\@kernel@after@enddocument\@undefined + \let \@kernel@after@enddocument \@empty + \let \@kernel@after@enddocument@afterlastpage \@empty +% \end{macrocode} +% +% \begin{macro}{\@kernel@before@begindocument,\@kernel@after@begindocument} +% For the similar reasons we also define those that are used in +% \cs{document} because they too get material added to in early modules. +% \begin{macrocode} + \let \@kernel@before@begindocument \@empty + \let \@kernel@after@begindocument \@empty +\fi %<latexrelease>\EndIncludeInRelease % \end{macrocode} % % \begin{macrocode} %<latexrelease>\IncludeInRelease{0000/00/00}% -%<latexrelease> {kernel@enddocument hooks}{Define kernel enddocument Hooks}% +%<latexrelease> {kernel@enddocument hooks}{Define several kernel hooks} %<latexrelease>\let\@kernel@after@enddocument\@undefined %<latexrelease>\let\@kernel@after@enddocument@afterlastpage\@undefined -%<latexrelease>\EndIncludeInRelease +%<latexrelease>\let\@kernel@before@begindocument\@undefined +%<latexrelease>\let\@kernel@after@begindocument\@undefined %</2ekernel|latexrelease> +%<latexrelease>\EndIncludeInRelease % \end{macrocode} % \end{macro} +% \end{macro} % % First define some blank commands, so that in case something goes wrong while % loading \textsf{expl3}, we won't get strange \texttt{Undefined control diff --git a/Master/texmf-dist/source/latex-dev/base/ltfilehook.dtx b/Master/texmf-dist/source/latex-dev/base/ltfilehook.dtx index 85c758ae608..e9309c41f78 100644 --- a/Master/texmf-dist/source/latex-dev/base/ltfilehook.dtx +++ b/Master/texmf-dist/source/latex-dev/base/ltfilehook.dtx @@ -31,8 +31,8 @@ %%% From File: ltfilehook.dtx % % \begin{macrocode} -\providecommand\ltfilehookversion{v1.0g} -\providecommand\ltfilehookdate{2021/02/08} +\providecommand\ltfilehookversion{v1.0j} +\providecommand\ltfilehookdate{2021/04/29} % \end{macrocode} % %<*driver> @@ -585,9 +585,13 @@ % places don't use \cs{InputIfFileExists} directly (\cs{include}) or % need \cs{CurrentFile} earlier (\cs{@onefilewithoptions}), so these % are manually used elsewhere as well. +% \changes{v1.0h}{2021/03/18} +% {Define \cs{g_@@_input_file_seq} to avoid losing data when +% rolling back.} % \begin{macrocode} \tl_new:N \l_@@_internal_tl -\seq_new:N \g_@@_input_file_seq +\seq_if_exist:NF \g_@@_input_file_seq + { \seq_new:N \g_@@_input_file_seq } \cs_new_protected:Npn \@@_file_push: { \seq_gpush:Nx \g_@@_input_file_seq @@ -601,7 +605,7 @@ \seq_gpop:NNTF \g_@@_input_file_seq \l_@@_internal_tl { \exp_after:wN \@@_file_pop_assign:nnnn \l_@@_internal_tl } { - \msg_error:nnn { hooks } { should-not-happen } + \__kernel_msg_error:nnn { hooks } { should-not-happen } { Tried~to~pop~from~an~empty~file~name~stack. } } } @@ -1031,15 +1035,18 @@ % false positives. Here we define \cs{csname\cs{endcsname}} to % expand to itself to avoid it matching the definition of some other % control sequence. +% \changes{v1.0i}{2021/04/20} +% {Make \string~ expand to a string (tracks change in l3kernel)} % \begin{macrocode} %</2ekernel> %<*2ekernel|latexrelease> -%<latexrelease>\IncludeInRelease{2021/05/01}% +%<latexrelease>\IncludeInRelease{2021/06/01}% %<latexrelease> {\set@curr@file}{Setting current file name}% \def\set@curr@file#1{% \begingroup \escapechar\m@ne \let\protect\string + \edef~{\string~}% \expandafter\def\csname\expandafter\endcsname \expandafter{\csname\endcsname}% % \end{macrocode} @@ -1078,6 +1085,7 @@ % % \begin{macrocode} %<latexrelease>\IncludeInRelease{2020/10/01}% +%<latexrelease> {\set@curr@file}{Setting current file name}% %<latexrelease>\def\set@curr@file#1{% %<latexrelease> \begingroup %<latexrelease> \escapechar\m@ne @@ -1529,7 +1537,7 @@ % % \begin{macrocode} \cs_new_eq:NN \@expl@@@filehook@file@push@@ - \__filehook_file_push: + \__filehook_file_push: % \end{macrocode} % % \begin{macrocode} @@ -1720,8 +1728,8 @@ % \Finale % % -%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% +%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% \endinput -%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% +%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% % diff --git a/Master/texmf-dist/source/latex-dev/base/ltfiles.dtx b/Master/texmf-dist/source/latex-dev/base/ltfiles.dtx index b27f9105873..bcaac582208 100644 --- a/Master/texmf-dist/source/latex-dev/base/ltfiles.dtx +++ b/Master/texmf-dist/source/latex-dev/base/ltfiles.dtx @@ -32,7 +32,7 @@ %<*driver> % \fi \ProvidesFile{ltfiles.dtx} - [2021/01/15 v1.2j LaTeX Kernel (File Handling)] + [2021/04/17 v1.2m LaTeX Kernel (File Handling)] % \iffalse \documentclass{ltxdoc} \GetFileInfo{ltfiles.dtx} @@ -195,19 +195,6 @@ % \end{oldcomments} % % -% \task{???}{Do we use @unused or mainaux?} -% \begin{macro}{\@inputcheck} -% \begin{macro}{\@unused} -% Allocate read stream for testing and output stream. -% \changes{v1.0l}{1994/11/07} -% {move here from ltdefns, remove duplicate \cs{@mainaux}} -% \begin{macrocode} -\newread\@inputcheck -\newwrite\@unused -% \end{macrocode} -% \end{macro} -% \end{macro} -% % \begin{macro}{\@mainaux} % \begin{macro}{\@partaux} % \begin{macrocode} @@ -470,9 +457,14 @@ % \end{macrocode} % \InternalDetectionOn % +% These internal hooks are already declared earlier (in +% \texttt{ltexpl}) so that other modules could write to them. +% \changes{v1.2m}{2021/04/17}{Move \cs{@kernel@before@begindocument} and +% \cs{@kernel@after@begindocument} init earlier so that other modules can +% write to the hooks} % \begin{macrocode} -\let \@kernel@before@begindocument \@empty -\let \@kernel@after@begindocument \@empty +%\let \@kernel@before@begindocument \@empty +%\let \@kernel@after@begindocument \@empty % \end{macrocode} % % \end{macro} @@ -1072,30 +1064,114 @@ % \end{macrocode} % \end{macro} % +% \begin{macrocode} +%</2ekernel|latexrelease> +%<latexrelease>\EndIncludeInRelease +%<latexrelease>\IncludeInRelease{0000/00/00}% +%<latexrelease> {\quote@name}{Quote file names}% +%<latexrelease> +%<latexrelease>\let\quote@name\@undefined +%<latexrelease>\let\quote@@name\@undefined +%<latexrelease>\let\unquote@name\@undefined +%<latexrelease> +%<latexrelease>\long\def \IfFileExists#1#2#3{% +%<latexrelease> \openin\@inputcheck#1 % +%<latexrelease> \ifeof\@inputcheck +%<latexrelease> \ifx\input@path\@undefined +%<latexrelease> \def\reserved@a{#3}% +%<latexrelease> \else +%<latexrelease> \def\reserved@a{\@iffileonpath{#1}{#2}{#3}}% +%<latexrelease> \fi +%<latexrelease> \else +%<latexrelease> \closein\@inputcheck +%<latexrelease> \edef\@filef@und{#1 }% +%<latexrelease> \def\reserved@a{#2}% +%<latexrelease> \fi +%<latexrelease> \reserved@a} +%<latexrelease> +%<latexrelease>\EndIncludeInRelease +%<*2ekernel> +% \end{macrocode} +% +% +% % \begin{macro}{\IfFileExists@} % \changes{v0.9b}{1993/12/04}{Macro added} % \changes{v0.9p}{1994/01/18}{New Definition} % \changes{v1.0t}{1995/05/25}{(CAR) added \cs{long}} -% \changes{v1.2d}{2019/10/26}{quote on openin}% +% \changes{v1.2d}{2019/10/26}{quote on openin} +% \changes{v1.2k}{2021/03/12}{Allow unbalanced conditionals (gh/530)} % Argument |#1| is |\@curr@file| so catcode 12 string with no quotes. +% +% The original definition picked up arguments |#2| and |#3| in a +% way that they couldn't contain unbalanced conditionals. A better +% implementation would have been not to pick up the arguments at +% all but instead use the usual \cs{@firstoftwo} and +% \cs{secondoftwo}. However, that changes how |#| is interpreted +% and so we can't do that nowaways without invalidating a lot of +% code. Therefore the somewhat curious construction near the end. % \begin{macrocode} +%</2ekernel> +%<*2ekernel|latexrelease> +%<latexrelease>\IncludeInRelease{2021/06/01}% +%<latexrelease> {\IfFileExists@}{manage unbalanced conditionals} \long\def \IfFileExists@#1#2#3{% \openin\@inputcheck"#1" % \ifeof\@inputcheck \ifx\input@path\@undefined - \def\reserved@a{#3}% + \let\reserved@a\@secondoftwo \else - \def\reserved@a{\@iffileonpath{#1}{#2}{#3}}% + \def\reserved@a{\@iffileonpath{#1}}% \fi \else \closein\@inputcheck \edef\@filef@und{"#1" }% - \def\reserved@a{#2}% + \let\reserved@a\@firstoftwo \fi - \reserved@a} +% \end{macrocode} +% This is just there so that any |#| inside |#2| or |#3| needs +% doubling (as that was the case in the past). +% \begin{macrocode} + \expandafter\def\expandafter\reserved@a + \expandafter{\reserved@a{#2}{#3}}% +\reserved@a} +%</2ekernel|latexrelease> +%<latexrelease>\EndIncludeInRelease +%<latexrelease>\IncludeInRelease{2019/10/01}% +%<latexrelease> {\IfFileExists@}{manage unbalanced conditionals} +%<latexrelease> +%<latexrelease>\long\def \IfFileExists@#1#2#3{% +%<latexrelease> \openin\@inputcheck"#1" % +%<latexrelease> \ifeof\@inputcheck +%<latexrelease> \ifx\input@path\@undefined +%<latexrelease> \def\reserved@a{#3}% +%<latexrelease> \else +%<latexrelease> \def\reserved@a{\@iffileonpath{#1}{#2}{#3}}% +%<latexrelease> \fi +%<latexrelease> \else +%<latexrelease> \closein\@inputcheck +%<latexrelease> \edef\@filef@und{"#1" }% +%<latexrelease> \def\reserved@a{#2}% +%<latexrelease> \fi +%<latexrelease> \reserved@a} +%<latexrelease>\EndIncludeInRelease +%<latexrelease>\IncludeInRelease{0000/00/00}% +%<latexrelease> {\IfFileExists@}{manage unbalanced conditionals} +%<latexrelease> +%<latexrelease>\let\IfFileExists@\@undefined +%<latexrelease> +%<latexrelease> +%<latexrelease>\EndIncludeInRelease +%<*2ekernel> % \end{macrocode} % \end{macro} % +% +% +% +% +% +% % \begin{macro}{\@iffileonpath} % If the file is not found by |\openin|, and |\input@path| is defined, % look in all the directories specified in |\input@path|. @@ -1109,6 +1185,10 @@ % \changes{v1.2d}{2019/10/26}{quote on openin}% % \changes{v1.2f}{2019/11/11}{make \cs{@filef@und} match quoting used on \cs{openin}}% % \begin{macrocode} +%</2ekernel> +%<*2ekernel|latexrelease> +%<latexrelease>\IncludeInRelease{2019/10/01}% +%<latexrelease> {\@iffileonpath}{Quote file names} \long\def\@iffileonpath#1{% \let\reserved@a\@secondoftwo \expandafter\@tfor\expandafter\reserved@b\expandafter @@ -1128,28 +1208,7 @@ %</2ekernel|latexrelease> %<latexrelease>\EndIncludeInRelease %<latexrelease>\IncludeInRelease{0000/00/00}% -%<latexrelease> {\quote@name}{Quote file names}% -%<latexrelease> -%<latexrelease>\let\quote@name\@undefined -%<latexrelease>\let\quote@@name\@undefined -%<latexrelease>\let\unquote@name\@undefined -%<latexrelease> -%<latexrelease>\let\IfFileExists@\@undefined -%<latexrelease> -%<latexrelease>\long\def \IfFileExists#1#2#3{% -%<latexrelease> \openin\@inputcheck#1 % -%<latexrelease> \ifeof\@inputcheck -%<latexrelease> \ifx\input@path\@undefined -%<latexrelease> \def\reserved@a{#3}% -%<latexrelease> \else -%<latexrelease> \def\reserved@a{\@iffileonpath{#1}{#2}{#3}}% -%<latexrelease> \fi -%<latexrelease> \else -%<latexrelease> \closein\@inputcheck -%<latexrelease> \edef\@filef@und{#1 }% -%<latexrelease> \def\reserved@a{#2}% -%<latexrelease> \fi -%<latexrelease> \reserved@a} +%<latexrelease> {\quote@name}{Quote file names} %<latexrelease> %<latexrelease>\long\def\@iffileonpath#1{% %<latexrelease> \let\reserved@a\@secondoftwo diff --git a/Master/texmf-dist/source/latex-dev/base/ltfinal.dtx b/Master/texmf-dist/source/latex-dev/base/ltfinal.dtx index d6fa6e8a7bf..19f82934aa6 100644 --- a/Master/texmf-dist/source/latex-dev/base/ltfinal.dtx +++ b/Master/texmf-dist/source/latex-dev/base/ltfinal.dtx @@ -33,7 +33,7 @@ %<*driver> % \fi \ProvidesFile{ltfinal.dtx} - [2021/02/25 v2.2m LaTeX Kernel (Final Settings)] + [2021/04/18 v2.2o LaTeX Kernel (Final Settings)] % \iffalse \documentclass{ltxdoc} \GetFileInfo{ltfinal.dtx} @@ -242,7 +242,73 @@ % \end{macro} % \end{macro} % -% +% \begin{macro}{trace_stack_levels} +% Now define the Lua function to emulate \cs{tracingstacklevels} and +% install it in the \texttt{input_level_string} callback. +% \begin{macrocode} +%</2ekernel> +%<*2ekernel|latexrelease> +% \end{macrocode} +% In \texttt{latexrelease} mode we always remove the function from +% the callback, then add the correct version later. +% \begin{macrocode} +%<latexrelease>\ifx\directlua\@undefined +%<latexrelease>\else +%<latexrelease> \directlua{% +%<latexrelease> if luatexbase.callbacktypes['input_level_string'] and % +%<latexrelease> luatexbase.in_callback('input_level_string','tracingstacklevels') then +%<latexrelease> luatexbase.remove_from_callback('input_level_string','tracingstacklevels') +%<latexrelease> end}% +%<latexrelease>\fi +%<latexrelease>\IncludeInRelease{2021/06/01}{trace_stack_levels}% +%<latexrelease> {Lua trace_stack_levels function}% +\ifx\directlua\@undefined +\else +%<*2ekernel> + \expanded{% + \everyjob{\the\everyjob + \noexpand%\directlua +%</2ekernel> + \directlua{% + local function trace_stack_levels (input_ptr) + local tracingstacklevels = tex.count.tracingstacklevels + if tex.tracingmacros > 0 or input_ptr < tracingstacklevels then + if tracingstacklevels > 0 then + if input_ptr < tracingstacklevels then + return "\string\n\string~" .. string.rep(".",input_ptr) + else + return "\string~\string~" + end + else + return "\string\n" + end + else + return "" + end + end +%<latexrelease> if luatexbase.callbacktypes['input_level_string'] then + luatexbase.add_to_callback('input_level_string', + trace_stack_levels,'tracingstacklevels') +%<latexrelease> end + }% +%<*2ekernel> + }}% +%</2ekernel> +\fi +%<latexrelease>\EndIncludeInRelease +%<latexrelease> +% \end{macrocode} +% Then for the full rollback, just do nothing, since the function +% was already taken out of the rollback above. +% \begin{macrocode} +%<latexrelease>\IncludeInRelease{0000/00/00}{trace_stack_levels}% +%<latexrelease> {Lua trace_stack_levels function}% +%<latexrelease>% Nothing here +%<latexrelease>\EndIncludeInRelease +%</2ekernel|latexrelease> +%<*2ekernel> +% \end{macrocode} +% \end{macro} % % The default values of the picture and |\fbox| parameters: % \begin{macrocode} @@ -574,7 +640,7 @@ % \begin{macrocode} %</2ekernel> %<*2ekernel|latexrelease> -%<latexrelease>\IncludeInRelease{2021/05/01}% +%<latexrelease>\IncludeInRelease{2021/06/01}% %<latexrelease> {\pdfgentounicode}{Preload glyphtounicode}% \ifx \pdfgentounicode \@undefined \else %<*2ekernel> @@ -1194,7 +1260,6 @@ % {Load first aid file if existing} % \begin{macrocode} \@input{latex2e-first-aid-for-external-files.ltx} -\@input{ltpara.ltx} % \end{macrocode} % % diff --git a/Master/texmf-dist/source/latex-dev/base/ltfloat.dtx b/Master/texmf-dist/source/latex-dev/base/ltfloat.dtx index f2324118a04..ed25db3ed10 100644 --- a/Master/texmf-dist/source/latex-dev/base/ltfloat.dtx +++ b/Master/texmf-dist/source/latex-dev/base/ltfloat.dtx @@ -31,7 +31,7 @@ % %<*driver> % \fi -\ProvidesFile{ltfloat.dtx}[2021/02/10 v1.2f LaTeX Kernel (Floats)] +\ProvidesFile{ltfloat.dtx}[2021/03/03 v1.2f LaTeX Kernel (Floats)] % \iffalse \documentclass{ltxdoc} \GetFileInfo{ltfloat.dtx} @@ -1008,7 +1008,7 @@ % \begin{macrocode} %</2ekernel> %<*2ekernel|latexrelease> -%<latexrelease>\IncludeInRelease{2021/05/01}% +%<latexrelease>\IncludeInRelease{2021/06/01}% %<latexrelease> {\@savemarbox}{Explicit par for marginpar}% \long\def \@savemarbox #1#2{% \global\setbox #1% @@ -1513,7 +1513,7 @@ % \begin{macrocode} %</2ekernel> %<*2ekernel|latexrelease> -%<latexrelease>\IncludeInRelease{2021/05/01}% +%<latexrelease>\IncludeInRelease{2021/06/01}% %<latexrelease> {\@footnotetext}{footnotetext tagging}% \long\def\@footnotetext#1{\insert\footins{% \reset@font\footnotesize @@ -1633,7 +1633,7 @@ % \begin{macrocode} %</2ekernel> %<*2ekernel|latexrelease> -%<latexrelease>\IncludeInRelease{2021/05/01}% +%<latexrelease>\IncludeInRelease{2021/06/01}% %<latexrelease> {\footref}{Add footref}% \def\footref#1{% \begingroup @@ -1644,14 +1644,16 @@ %</2ekernel|latexrelease> %<latexrelease>\EndIncludeInRelease % \end{macrocode} -% We don't remove it when rolling back. +% We don't remove it when rolling back so that packages offered it +% in the past do not need to alter their behavior in a rollback +% situation. % \begin{macrocode} -%%<latexrelease>\IncludeInRelease{0000/00/00}% -%%<latexrelease> {\footref}{Add footref}% -%%<latexrelease> -%%<latexrelease>\let\footref\@undefined -%%<latexrelease> -%%<latexrelease>\EndIncludeInRelease +%<latexrelease>\IncludeInRelease{0000/00/00}% +%<latexrelease> {\footref}{Add footref}% +%<latexrelease> +%<latexrelease> % \let\footref\@undefined +%<latexrelease> +%<latexrelease>\EndIncludeInRelease %<*2ekernel> % \end{macrocode} % \end{macro} diff --git a/Master/texmf-dist/source/latex-dev/base/ltfssaxes.dtx b/Master/texmf-dist/source/latex-dev/base/ltfssaxes.dtx index e0c07874556..86c84b111fb 100644 --- a/Master/texmf-dist/source/latex-dev/base/ltfssaxes.dtx +++ b/Master/texmf-dist/source/latex-dev/base/ltfssaxes.dtx @@ -35,7 +35,7 @@ % % \ProvidesFile{ltfssaxes.dtx} - [2021/01/15 v1.0h LaTeX Kernel (NFSS Axes handing)] + [2021/03/18 v1.0i LaTeX Kernel (NFSS Axes handing)] % \iffalse \documentclass{ltxdoc} \begin{document} @@ -699,7 +699,7 @@ % % \begin{macrocode} %<*2ekernel|latexrelease> -%<latexrelease>\IncludeInRelease{2021/05/01}% +%<latexrelease>\IncludeInRelease{2021/06/01}% %<latexrelease> {\fontseries}{delay fontseries update}% % \end{macrocode} % @@ -772,11 +772,12 @@ %<latexrelease> % \end{macrocode} % For a roll forward we may have to define \cs{if@forced@series} -% but this needs doing in a somewhat roundabout way. +% but this needs doing in a way that \TeX{} doesn't see it when +% skipping over conditionals. +% \changes{v1.0i}{2021/03/18} +% {Fix rollforward definition.} % \begin{macrocode} -%<latexrelease>\ifx\@forced@seriestrue\@undefined \else -%<latexrelease> \expandafter\newif\csname if@forced@series\endcsname -%<latexrelease>\fi +%<latexrelease>\expandafter\newif\csname if@forced@series\endcsname %<latexrelease> %<latexrelease>\EndIncludeInRelease % \end{macrocode} @@ -1328,7 +1329,7 @@ % % \begin{macrocode} %<*2ekernel|latexrelease> -%<latexrelease>\IncludeInRelease{2021/05/01}% +%<latexrelease>\IncludeInRelease{2021/06/01}% %<latexrelease> {\fontshape}{Font shape change}% % \end{macrocode} % diff --git a/Master/texmf-dist/source/latex-dev/base/ltfssbas.dtx b/Master/texmf-dist/source/latex-dev/base/ltfssbas.dtx index 6d575fc9d0b..df265d0b0fc 100644 --- a/Master/texmf-dist/source/latex-dev/base/ltfssbas.dtx +++ b/Master/texmf-dist/source/latex-dev/base/ltfssbas.dtx @@ -35,7 +35,7 @@ % % \ProvidesFile{ltfssbas.dtx} - [2020/12/10 v3.2h LaTeX Kernel (NFSS Basic Macros)] + [2021/04/26 v3.2i LaTeX Kernel (NFSS Basic Macros)] % \iffalse \documentclass{ltxdoc} \begin{document} @@ -896,15 +896,35 @@ % \begin{macrocode} %</2ekernel> %<*2ekernel|latexrelease> -%<latexrelease>\IncludeInRelease{2020/02/02}% -%<latexrelease> {\usefont}{Drop m in usefont}% +%<latexrelease>\IncludeInRelease{2021/06/01}% +%<latexrelease> {\usefont}{Force font face}% \DeclareRobustCommand\usefont[4]{\fontencoding{#1}% \edef\f@family{#2}% \set@target@series{#3}% - \edef\f@shape{#4}\selectfont + \edef\f@shape{#4}% +% \end{macrocode} +% Any earlier \cs{fontseries}, etc.\ should be canceled and we +% should switch unconditionally to the requested font face so we +% drop any code that may have been stored in +% \cs{delayed@f@adjustment}. +% \changes{v3.2i}{2021/04/26}{Unconditionally switch to the requested +% font face (gh/444)} +% \begin{macrocode} + \let\delayed@f@adjustment\@empty + \selectfont \ignorespaces} %</2ekernel|latexrelease> %<latexrelease>\EndIncludeInRelease +%<latexrelease>\IncludeInRelease{2020/02/02}% +%<latexrelease> {\usefont}{Drop m in usefont}% +%<latexrelease> +%<latexrelease>\DeclareRobustCommand\usefont[4]{\fontencoding{#1}% +%<latexrelease> \edef\f@family{#2}% +%<latexrelease> \set@target@series{#3}% +%<latexrelease> \edef\f@shape{#4}\selectfont +%<latexrelease> \ignorespaces} +%<latexrelease> +%<latexrelease>\EndIncludeInRelease %<latexrelease>\IncludeInRelease{0000/00/00}% %<latexrelease> {\usefont}{Drop m in usefont}% %<latexrelease> diff --git a/Master/texmf-dist/source/latex-dev/base/ltfssini.dtx b/Master/texmf-dist/source/latex-dev/base/ltfssini.dtx index 361adac9c36..b06fc161b08 100644 --- a/Master/texmf-dist/source/latex-dev/base/ltfssini.dtx +++ b/Master/texmf-dist/source/latex-dev/base/ltfssini.dtx @@ -36,7 +36,7 @@ % % \ProvidesFile{ltfssini.dtx} - [2021/01/15 v3.2f LaTeX Kernel (NFSS Initialisation)] + [2021/04/26 v3.2h LaTeX Kernel (NFSS Initialisation)] % \iffalse \documentclass{ltxdoc} \begin{document} @@ -1088,6 +1088,8 @@ % \end{macro} % % +% \changes{v3.2g}{2021/03/18} +% {Add legacy hook definitions for rollback.} % \begin{macrocode} %</2ekernel|latexrelease> %<latexrelease>\EndIncludeInRelease @@ -1140,6 +1142,9 @@ %<latexrelease> \prepare@family@series@update{tt}\ttdefault %<latexrelease> \@ttfamilyhook %<latexrelease> \selectfont} +%<latexrelease>\let\@rmfamilyhook\@empty +%<latexrelease>\let\@sffamilyhook\@empty +%<latexrelease>\let\@ttfamilyhook\@empty %<latexrelease> % \end{macrocode} % @@ -1765,7 +1770,7 @@ % \begin{macrocode} %</2ekernel> %<*2ekernel|latexrelease> -%<latexrelease>\IncludeInRelease{2020/02/02}% +%<latexrelease>\IncludeInRelease{2021/06/01}% %<latexrelease> {\normalfont}{Add hook to \normalfont}% \DeclareRobustCommand\normalfont{% % \end{macrocode} @@ -1778,6 +1783,15 @@ \edef\f@series{\seriesdefault}% \edef\f@shape{\shapedefault}% % \end{macrocode} +% Any earlier \cs{fontseries}, etc.\ should be canceled and we +% should switch unconditionally to the requested font face so we +% drop any code that may have been stored in +% \cs{delayed@f@adjustment}. +% \changes{v3.2h}{2021/04/26}{Unconditionally switch to the requested +% font face (gh/444)} +% \begin{macrocode} + \let\delayed@f@adjustment\@empty +% \end{macrocode} % % \changes{v3.2b}{2020/08/21}{Integration of new hook management interface} % \begin{macrocode} @@ -1789,18 +1803,54 @@ \@defaultfamilyhook % hookname from 2020/02 will vanish \selectfont} % \end{macrocode} -% +% % \begin{macrocode} \let\reset@font\normalfont % \end{macrocode} % \end{macro} % \end{macro} -% +% % % % \begin{macrocode} +% \changes{v3.2g}{2021/03/18} +% {Add missing 2020/02/02 latexrelease entry.} %</2ekernel|latexrelease> %<latexrelease>\EndIncludeInRelease +%<latexrelease> +%<latexrelease>\IncludeInRelease{2020/10/01}% +%<latexrelease> {\normalfont}{Add hook to \normalfont}% +%<latexrelease> +%<latexrelease>\DeclareRobustCommand\normalfont{% +%<latexrelease> \fontencoding\encodingdefault +%<latexrelease> \edef\f@family{\familydefault}% +%<latexrelease> \edef\f@series{\seriesdefault}% +%<latexrelease> \edef\f@shape{\shapedefault}% +%<latexrelease> \UseHook{normalfont}% +%<latexrelease> \@defaultfamilyhook % hookname from 2020/02 will vanish +%<latexrelease> \selectfont} +%<latexrelease> +%<latexrelease>\let\reset@font\normalfont +%<latexrelease> +%<latexrelease>\EndIncludeInRelease +%<latexrelease> +%<latexrelease>\IncludeInRelease{2020/02/02}% +%<latexrelease> {\normalfont}{Add hook to \normalfont}% +%<latexrelease> +%<latexrelease>\DeclareRobustCommand\normalfont{% +%<latexrelease> \fontencoding\encodingdefault +%<latexrelease> \edef\f@family{\familydefault}% +%<latexrelease> \edef\f@series{\seriesdefault}% +%<latexrelease> \edef\f@shape{\shapedefault}% +%<latexrelease> \@defaultfamilyhook +%<latexrelease> \selectfont} +%<latexrelease> +%<latexrelease>\let\reset@font\normalfont +%<latexrelease> +%<latexrelease>\let\@defaultfamilyhook\@empty +%<latexrelease> +%<latexrelease>\EndIncludeInRelease +%<latexrelease> %<latexrelease>\IncludeInRelease{0000/00/00}% %<latexrelease> {\normalfont}{Add hook to \normalfont}% %<latexrelease> diff --git a/Master/texmf-dist/source/latex-dev/base/ltfsstrc.dtx b/Master/texmf-dist/source/latex-dev/base/ltfsstrc.dtx index 961abafc8a3..c6aec105ba5 100644 --- a/Master/texmf-dist/source/latex-dev/base/ltfsstrc.dtx +++ b/Master/texmf-dist/source/latex-dev/base/ltfsstrc.dtx @@ -32,12 +32,12 @@ %% Copyright (C) 1989-97 by Frank Mittelbach and Rainer Sch\"opf. %% Copyright (C) 1994-97 by LaTeX Project. All rights reserved. % -%<package>\NeedsTeXFormat{LaTeX2e}[2021/05/01] +%<package>\NeedsTeXFormat{LaTeX2e}[2021/06/01] %<package>\ProvidesPackage{tracefnt} %<package> [2020/12/22 v3.0n Standard LaTeX package (font tracing)] % \fi % \ProvidesFile{ltfsstrc.dtx} -% [2020/12/22 v3.0n LaTeX Kernel (NFSS tracing)] +% [2021/04/26 v3.0o LaTeX Kernel (NFSS tracing)] % % \iffalse %<+checkmem>\CHECKMEM @@ -423,7 +423,7 @@ % \begin{macrocode} %<*2ekernel|latexrelease|package> -%<latexrelease>\IncludeInRelease{2021/05/01}% +%<latexrelease>\IncludeInRelease{2021/06/01}% %<latexrelease> {\selectfont}{Add hook to \selectfont}% % \end{macrocode} % @@ -525,6 +525,14 @@ \let\delayed@f@adjustment\@empty \fi % \end{macrocode} +% If the series was forced we should now cancel that in case the +% next series change is done with some low-level setting to +% \cs{f@series}. +% \changes{v3.0o}{2021/04/26}{Unset the forced series boolean when reaching +% \cs{selectfont} (gh/444)} +% \begin{macrocode} + \@forced@seriesfalse +% \end{macrocode} % Then we generate the internal name of the font % by concatenating {\em family}, {\em series}, % {\em shape}, and current {\em size}, @@ -610,7 +618,7 @@ % package to support rollback. In packages that works a bit % differently and therefore we have to provide an empty block there. % \begin{macrocode} -%<package>\IncludeInRelease{2021/05/01}% +%<package>\IncludeInRelease{2021/06/01}% %<package> {\selectfont}{Add hook to \selectfont}% %<package>\EndIncludeInRelease % \end{macrocode} diff --git a/Master/texmf-dist/source/latex-dev/base/lthooks.dtx b/Master/texmf-dist/source/latex-dev/base/lthooks.dtx index 803914b4a74..45d6a78aba1 100644 --- a/Master/texmf-dist/source/latex-dev/base/lthooks.dtx +++ b/Master/texmf-dist/source/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} % % diff --git a/Master/texmf-dist/source/latex-dev/base/ltluatex.dtx b/Master/texmf-dist/source/latex-dev/base/ltluatex.dtx index e3fa9f93437..3b9a3377312 100644 --- a/Master/texmf-dist/source/latex-dev/base/ltluatex.dtx +++ b/Master/texmf-dist/source/latex-dev/base/ltluatex.dtx @@ -28,7 +28,7 @@ \ProvidesFile{ltluatex.dtx} %</driver> %<*tex> -[2021/01/15 v1.1s +[2021/04/18 v1.1t %</tex> %<plain> LuaTeX support for plain TeX (core) %<*tex> @@ -1412,6 +1412,7 @@ local callbacktypes = callbacktypes or { % \changes{v1.1j}{2019/06/18}{make\_extensible added} % \changes{v1.1j}{2019/06/18}{font\_descriptor\_objnum\_provider added} % \changes{v1.1l}{2020/02/02}{glyph\_info added} +% \changes{v1.1t}{2021/04/18}{input\_level\_string added} % \begin{macrocode} define_font = exclusive, glyph_info = exclusive, @@ -1419,6 +1420,7 @@ local callbacktypes = callbacktypes or { glyph_stream_provider = exclusive, make_extensible = exclusive, font_descriptor_objnum_provider = exclusive, + input_level_string = exclusive, % \end{macrocode} % \changes{v1.0m}{2016/02/11}{pdf\_stream\_filter\_callback removed} % \begin{macrocode} @@ -1477,7 +1479,7 @@ end % functions in inverse order. % \item[exclusive] is for functions with more complex signatures; functions in % this type of callback are \emph{not} combined: An error is raised if -% a second callback is registered.. +% a second callback is registered. % \end{description} % % Handler for |data| callbacks. diff --git a/Master/texmf-dist/source/latex-dev/base/ltmath.dtx b/Master/texmf-dist/source/latex-dev/base/ltmath.dtx index ca90e1980d2..aadb96fb023 100644 --- a/Master/texmf-dist/source/latex-dev/base/ltmath.dtx +++ b/Master/texmf-dist/source/latex-dev/base/ltmath.dtx @@ -38,7 +38,7 @@ %<*driver> % \fi \ProvidesFile{ltmath.dtx} - [2020/12/05 v1.2h LaTeX Kernel (Math Setup)] + [2021/04/20 v1.2i LaTeX Kernel (Math Setup)] % \iffalse %</driver> % @@ -961,11 +961,12 @@ % \end{macro} % % \begin{macro}{\@eqncr} +% \changes{v1.2i}{2021/04/20}{Use \cs{protected} for \cs{\bslash} variant (gh/548)} % \begin{macro}{\@xeqncr} % \begin{macro}{\@yeqncr} % \changes{v1.0y}{1995/10/16}{(DPC) Use \cs{@testopt} /1911} % \begin{macrocode} -\def\@eqncr{% +\protected\def\@eqncr{% {\ifnum0=`}\fi \@ifstar{% \global\@eqpen\@M\@yeqncr @@ -1033,8 +1034,9 @@ \let\@seqncr=\@eqncr % \end{macrocode} % +% \changes{v1.2i}{2021/04/20}{Use \cs{protected} for \cs{\bslash} variant (gh/548)} % \begin{macrocode} -\@namedef{eqnarray*}{\def\@eqncr{\nonumber\@seqncr}\eqnarray} +\@namedef{eqnarray*}{\protected\def\@eqncr{\nonumber\@seqncr}\eqnarray} % \end{macrocode} % % \begin{macrocode} diff --git a/Master/texmf-dist/source/latex-dev/base/ltmiscen.dtx b/Master/texmf-dist/source/latex-dev/base/ltmiscen.dtx index 939bd51a2da..8bae3f9bfa3 100644 --- a/Master/texmf-dist/source/latex-dev/base/ltmiscen.dtx +++ b/Master/texmf-dist/source/latex-dev/base/ltmiscen.dtx @@ -400,7 +400,7 @@ % \begin{macrocode} %</2ekernel> %<*2ekernel|latexrelease> -%<latexrelease>\IncludeInRelease{2021/05/01}% +%<latexrelease>\IncludeInRelease{2021/06/01}% %<latexrelease> {\@kernel@before@enddocument}{kernel before hook}% \def\@kernel@before@enddocument{\par} %</2ekernel|latexrelease> diff --git a/Master/texmf-dist/source/latex-dev/base/ltoutenc.dtx b/Master/texmf-dist/source/latex-dev/base/ltoutenc.dtx index 73146428c0d..eb2ecd1e537 100644 --- a/Master/texmf-dist/source/latex-dev/base/ltoutenc.dtx +++ b/Master/texmf-dist/source/latex-dev/base/ltoutenc.dtx @@ -37,14 +37,14 @@ %<TS1>\ProvidesFile{ts1enc.def}[2001/06/05 v3.0e (jk/car/fm) %<TU>\ProvidesFile{tuenc.def} %<package>\ProvidesPackage{fontenc} -%<OT1|T1|OMS|OML|OT4|TU|package> [2021/02/19 v2.0u +%<OT1|T1|OMS|OML|OT4|TU|package> [2021/04/29 v2.0v %<OT1|T1|OMS|OML|OT4|TS1|TU> Standard LaTeX file] %<package> Standard LaTeX package] % %<*driver> % \fi \ProvidesFile{ltoutenc.dtx} - [2021/02/19 v2.0u LaTeX Kernel (font encodings)] + [2021/04/29 v2.0v LaTeX Kernel (font encodings)] % \iffalse \documentclass{ltxdoc} \GetFileInfo{ltoutenc.dtx} @@ -3468,8 +3468,19 @@ \DeclareUnicodeComposite{\v} {o}{"01D2} \DeclareUnicodeComposite{\v} {U}{"01D3} \DeclareUnicodeComposite{\v} {u}{"01D4} +% \end{macrocode} +% +% \changes{v2.0v}{2021/04/29}{Add composites for +% \texttt{\protect\string\ae/\protect\string\AE/\ae/\AE} (gh/552)} +% \begin{macrocode} +\DeclareUnicodeComposite{\'} \AE{"01FC} +\DeclareUnicodeComposite{\'} {Æ}{"01FC} +\DeclareUnicodeComposite{\'} \ae{"01FD} +\DeclareUnicodeComposite{\'} {æ}{"01FD} \DeclareUnicodeComposite{\=} \AE{"01E2} +\DeclareUnicodeComposite{\=} {Æ}{"01E2} \DeclareUnicodeComposite{\=} \ae{"01E3} +\DeclareUnicodeComposite{\=} {æ}{"01E3} \DeclareUnicodeComposite{\v} {G}{"01E6} \DeclareUnicodeComposite{\v} {g}{"01E7} \DeclareUnicodeComposite{\v} {K}{"01E8} diff --git a/Master/texmf-dist/source/latex-dev/base/ltpar.dtx b/Master/texmf-dist/source/latex-dev/base/ltpar.dtx index f4f3d5e63fc..969954be0a7 100644 --- a/Master/texmf-dist/source/latex-dev/base/ltpar.dtx +++ b/Master/texmf-dist/source/latex-dev/base/ltpar.dtx @@ -33,7 +33,7 @@ %<*driver> % \fi \ProvidesFile{ltpar.dtx} - [1995/04/29 v1.1c LaTeX Kernel (paragraphs)] + [2021/04/13 v1.1c LaTeX Kernel (paragraphs)] % \iffalse \documentclass{ltxdoc} \GetFileInfo{ltpar.dtx} @@ -67,9 +67,11 @@ % |\par| and |\everypar| when ever their function needs to be % changed for a long time. % -% \StopEventually{} -% -% \subsection{Implementation} +% This file here describes the interfaces that have been in the kernel +% forever, used to implement the scenarios described below. They +% remain valid but are now augmented in the next file +% (\texttt{ltpara.dtx}) to add hooks to paragraphs. At some point we +% will consolidate the two files further. % % % There are two situations in which |\par| may be changed: @@ -93,6 +95,12 @@ % \end{itemize} % \end{itemize} % +% +% \StopEventually{} +% +% \subsection{Implementation} +% +% % \DescribeMacro{\@setpar} % To permit the proper interaction of these two situations, long-term % changes are made by the |\@setpar{|\meta{VAL}|}| command. diff --git a/Master/texmf-dist/source/latex-dev/base/ltpara.dtx b/Master/texmf-dist/source/latex-dev/base/ltpara.dtx index 1a3df95a6e2..ad503b68e25 100644 --- a/Master/texmf-dist/source/latex-dev/base/ltpara.dtx +++ b/Master/texmf-dist/source/latex-dev/base/ltpara.dtx @@ -11,11 +11,11 @@ % https://www.latex-project.org/lppl.txt % % -%%% From File: lthooks.dtx +%%% From File: ltpara.dtx % % \begin{macrocode} -\def\ltparaversion{v1.0d} -\def\ltparadate{2021/02/18} +\def\ltparaversion{v1.0f} +\def\ltparadate{2021/04/16} % \end{macrocode} %<*driver> \documentclass{l3doc} @@ -302,13 +302,14 @@ % \cs{noindent}). % % The indentation box to be typeset is available to the hook as -% \cs{IndentationBox} and its automatic placement (after the hook is -% executed) can be prevented through \cs{SuppressIndentationBox}. +% \cs{IndentBox} and its automatic placement (after the hook is +% executed) can be prevented through \cs{OmitIndent}. +% More precisely \cs{OmitIndent} voids the box. % -% If not suppressed, then the indentation is typeset directly +% The indentation box is then typeset directly % after the hook execution by something equivalent to -% \cs{box}\cs{IndentationBox} followed by the current content of -% the token register \cs{everypar} as it is available to the +% \cs{box}\cs{IndentBox} followed by the current content of +% the token register \cs{everypar} that it is available to the % kernel or to packages (that run some legacy code). % % One has to be careful not to add any code to the hook that @@ -375,7 +376,7 @@ % \item[\cs{@kernel@before@para@before}] % For future extensions, not currently used by the kernel. % - +% % \item[\cs{@kernel@after@para@after}] % For future extensions, not currently used by the kernel. % @@ -437,26 +438,67 @@ % Spring release! % \end{quote} % -% \begin{function}{\SuppressIndentationBox,\para_suppress_indentation:} +% \begin{function}{\OmitIndent,\para_omit_indent:} % Inside the \hook{para/begin} hook one can use this command to % suppress the indentation box at the start of the % paragraph. (Technically it is possible to use this command % outside the hook as well, but this should not be relied upon.) % The box itself remains available for use. % -% The \pkg{expl3} name for the function is \cs{para_suppress_indentation:}. +% The \pkg{expl3} name for the function is \cs{para_omit_indent:}. % \end{function} % -% \begin{variable}{\IndentationBox,\g_para_indentation_box} +% \begin{variable}{\IndentBox,\g_para_indent_box} % The box register holding the indentation box for the paragraph is % available for inspection (or changes) inside hooks. It remains -% available even if the \cs{SuppressIndentationBox} command was +% available even if the \cs{OmitIndent} command was % used; in that case it will just not be automatically placed. % -% The \pkg{expl3} name for the box register is \cs{g_para_indentation_box}. +% The \pkg{expl3} name for the box register is \cs{g_para_indent_box}. % \end{variable} % % +% \begin{function}{\RawIndent,\para_raw_indent:, +% \RawNoindent,\para_raw_noindent:, +% \RawParEnd,\para_raw_end:} +% \begin{syntax} +% \cs{RawIndent} \textit{hmode material} \cs{RawParEnd} +% \cs{RawNoindent} \textit{hmode material} \cs{RawParEnd} +% \end{syntax} +% +% The commands \cs{RawIndent} and \cs{RawNoindent} are not meant +% for normal paragraph building (where the result is a textual +% paragraph in the the traditional meaning of the word), but for +% special cases where \TeX{}'s low-level algorithm is used to +% achieve special effects, but where the result is not a +% \enquote{paragraph}. +% +% They are called \enquote{raw}, because they bypass \LaTeX{}'s +% hook mechanism for paragraphs and simply invoke the low-level +% \TeX{} algorithm. I.e., they are like the original \TeX{} +% primitives \cs{indent} and \cs{noindent} (that is they execute no +% hooks other than \cs{everypar}) except that they can only be used +% in vertical mode and generate an error if found elsewhere. +% +% To avoid issues a paragraph started by them should always be +% ended by \cs{RawParEnd}\footnote{Technical note for those who +% know their \textit{\TeX book\/}: the \cs{RawParEnd} comand +% invokes the original \TeX{} engine definition of \cs{par} that +% (soley) triggers the paragraph builder in \TeX{} when found +% inside unrestricted horizontal mode and does nothing in other +% processing modes.} +% and not by \cs{par} (or a blank line), because the latter will execute +% hooks which then have no counterpart at the beginning of the +% paragraph. It is the responsibility of the programmer to make +% sure that they are properly paired. This also means that one +% should not put arbitrary user content between these commands if +% that content could contain stray \cs{par}s. +% +% The \pkg{expl3} names for the functions are +% \cs{para_raw_indent:}, \cs{para_raw_indent:} and +% \cs{para_raw_end:}. +% \end{function} +% % % % \subsection{Examples} @@ -574,21 +616,70 @@ % in blue. % % +% +% \subsection{Some technical notes} +% +% The code tries hard to be transparent for package code, but of +% course any change means that there is a potential for breaking +% other code. So in section we collect a few cases that may be of +% importance if low-level code is dealing with paragraphs that are +% now behaving slightly differently. The notes are from issues we +% observed and will probably grow over time. +% +% \subsubsection{Glue items between paragraphs (found with \pkg{fancypar})} +% +% In the past \LaTeX{} placed two glue items between two +% consecutive paragraph, e.g., +%\begin{verbatim} +% text1 \par text2 \par +%\end{verbatim} +% would show something like +%\begin{verbatim} +% \glue(\parskip) 0.0 plus 1.0 +% \glue(\baselineskip) 5.16669 +%\end{verbatim} +% but now there is anothe \cs{parskip} glue (that is always 0pt): +%\begin{verbatim} +% \glue(\parskip) 0.0 plus 1.0 +% \glue(\parskip) 0.0 +% \glue(\baselineskip) 5.16669 +%\end{verbatim} +% The reason is that we generate a \enquote{fake}'' paragraph to +% gain control and safely add the early hooks, but this generates +% an additional glue item. That item doesn't contribute anything +% vertically but ifsomebody writes code the unravels a constructed +% list using \cs{lastbox}, \cs{unskip} and \cs{unpenalty} then the +% code has to remove one additional glue item or else will fail. + +% +% ^^A \subsubsection{} +% +% +% +% ^^A \subsubsection{} +% +% +% +% +% +% % \StopEventually{\setlength\IndexMin{200pt} \PrintIndex } % % % \section{The Implementation} % % \begin{macrocode} -%<*2ekernel> +%<@@=para> % \end{macrocode} % +% % \begin{macrocode} -%<@@=para> +%<*2ekernel|latexrelease> \ExplSyntaxOn +%<latexrelease>\NewModuleRelease{2021/06/01}{ltpara} +%<latexrelease> {Paragraph~handling~and~hooks} % \end{macrocode} - - +% % % % \subsection{Providing hooks for paragraphs} @@ -641,10 +732,10 @@ \tl_gset:Nn \g_@@_standard_everypar_tl { % \end{macrocode} % First we remove the indentation box and store it in -% \cs{g_para_indentation_box}. If there was none because the paragraph +% \cs{g_para_indent_box}. If there was none because the paragraph % was started by \cs{noindent} the box register will be void. % \begin{macrocode} - \box_gset_to_last:N \g_para_indentation_box + \box_gset_to_last:N \g_para_indent_box % \end{macrocode} % % This will make the newly started horizontal list empty, so if we @@ -683,13 +774,6 @@ % case there are nested paragraphs coming up. % \begin{macrocode} \tex_everypar:D{\g_@@_standard_everypar_tl} -% \showthe\tex_everypar:D -% \end{macrocode} -% Then we set up \cs{@@_handle_indent:} to reinsert the indentation -% box later. This can be undone via \cs{SuppressIndentationBox} -% inside the hook(s). -% \begin{macrocode} - \cs_set_eq:NN \@@_handle_indent: \@@_add_indentation_box: % \end{macrocode} % % This is followed by executing the kernel and the public hook. The @@ -710,7 +794,7 @@ % we add that later, and indirectly. % \begin{macrocode} \@@_handle_indent: -% \the \everypar % <--- done differently +% \the \everypar % <--- done differently below } % \end{macrocode} % \end{macro} @@ -782,40 +866,38 @@ % \end{macro} % % -% \begin{macro}{\g_para_indentation_box} +% \begin{macro}{\g_para_indent_box} % For managing the indentation we need to provide a public % accessible box register % \begin{macrocode} -\box_new:N \g_para_indentation_box +\box_new:N \g_para_indent_box % \end{macrocode} % \end{macro} -% \begin{macro}{\@@_handle_indent:,\@@_add_indentation_box:} -% Adding (typesetting) the indent box is straight forward, the -% default action for is \cs{@@_handle_indent:} is -% \cs{@@_add_indentation_box:}. +% \begin{macro}{\@@_handle_indent:} +% Adding (typesetting) the indent box is straight forward. +% If it was emptied before it does nothing. % \begin{macrocode} -\cs_new:Npn \@@_add_indentation_box: { - \box_use_drop:N \g_para_indentation_box +\cs_new:Npn \@@_handle_indent: { + \box_use_drop:N \g_para_indent_box } -\cs_new_eq:NN \@@_handle_indent: \@@_add_indentation_box: % \end{macrocode} -% The declaration \cs{para_suppress_indentation:} (or -% \cs{SuppressIndentationBox}) changes that to do nothing. +% The declaration \cs{para_omit_indent:} (or +% \cs{OmitIndent}) changes that to do nothing. % \begin{macrocode} -\cs_new:Npn \para_suppress_indentation: { - \cs_set_eq:NN \@@_handle_indent: \prg_do_nothing: +\cs_new:Npn \para_omit_indent: { + \box_gclear:N \g_para_indent_box } % \end{macrocode} % \end{macro} % % -% \begin{macro}{\IndentationBox,\SuppressIndentationBox} +% \begin{macro}{\IndentBox,\OmitIndent} % The \LaTeXe{} names for the indentation box and for suppressing it % for use in the \hook{para/begin} hook. % \begin{macrocode} -\cs_set_eq:NN \IndentationBox \g_para_indentation_box -\cs_set_eq:NN \SuppressIndentationBox \para_suppress_indentation: +\cs_set_eq:NN \IndentBox \g_para_indent_box +\cs_set_eq:NN \OmitIndent \para_omit_indent: % \end{macrocode} % \end{macro} % @@ -979,6 +1061,67 @@ % \end{macrocode} % \end{macro} % +% +% +% +% \begin{macro}{\para_raw_indent:, +% \para_raw_noindent:, +% \para_raw_end:} +% +% The commands \cs{para_raw_indent:} and \cs{para_raw_noindent:} +% are like the primitives \cs{indent} and \cs{noindent} except that +% they can only be used in vertical mode. +% +% To avoid issues a paragraph started by them should always be +% ended by \cs{para_raw_end:} and not by \cs{para_end:} or +% \cs{par} as the latter will execute hooks which then have no +% counterpart at the beginning of the paragraph. It is the +% responsibility of the programmer to make sure that they are +% properly paired. +% \begin{macrocode} +\cs_new:Npn \para_raw_indent: { + \mode_if_vertical:TF + { + \tex_everypar:D { + \box_gset_to_last:N \g_para_indent_box + \tex_everypar:D { \g_@@_standard_everypar_tl } + \@@_handle_indent: + \the\everypar } + } + { \__kernel_msg_error:nn {para}{raw} } + \tex_indent:D +} +% \end{macrocode} +% +% \begin{macrocode} +\cs_new:Npn \para_raw_noindent: { + \mode_if_vertical:TF + { + \tex_everypar:D { + \tex_everypar:D { \g_@@_standard_everypar_tl } + \the\everypar } + } + { \__kernel_msg_error:nn {para}{raw} } + \tex_noindent:D +} +% \end{macrocode} +% +% \begin{macrocode} +\cs_new_eq:NN \para_raw_end: \tex_par:D +% \end{macrocode} +% \end{macro} +% +% +% \begin{macro}{\RawIndent,\RawNoIndent,\RawParEnd} +% The \LaTeXe{} names for starting and ending a paragraph without adding any hooks. +% \begin{macrocode} +\cs_set_eq:NN \RawIndent \para_raw_indent: +\cs_set_eq:NN \RawNoindent \para_raw_noindent: +\cs_set_eq:NN \RawParEnd \para_raw_end: +% \end{macrocode} +% \end{macro} +% +% % This ends the \texttt{para} module code. % \begin{macrocode} %<@@=> @@ -1010,7 +1153,10 @@ % % \subsection{The error messages} % -% Well, one really, first argument is the hook name second the mode +% This one is used when we detect that some hook code has changed +% the mode where it shouldn't, e.g., by starting or ending a +% paragraph. +% The first argument is the hook name second the mode % it should have stayed in but didn't. % \begin{macrocode} \__kernel_msg_new:nnnn {para} {mode} @@ -1026,72 +1172,43 @@ } % \end{macrocode} % +% And here is one used in the \enquote{raw} commands when they are +% used outside of vertical mode. % \begin{macrocode} -\ExplSyntaxOff -% \end{macrocode} -% -% \subsection{Kernel corrections} -% -% These corrections/changes are already part of the 2021/05 -% release, i.e., but for testing with older releases they have been -% included here for now. So this section will vanish soon. -% -% -% Marginpar boxes are missing a final internal \cs{par}. -% \begin{macrocode} -\IfFormatAtLeastTF{2021/05/01}{} -{ -\long\def \@savemarbox #1#2{% - \global\setbox #1% - \color@vbox - \vtop{% - \hsize\marginparwidth - \@parboxrestore - \@marginparreset - #2\par % <-- needed - \@minipagefalse - \outer@nobreak - }% - \color@endbox -} +\__kernel_msg_new:nnnn {para} {raw} + { + Paragraph started while not in vertical mode. + } + { + Starting a paragraph with \RawIndent or \RawNoindent + (or \para_raw_indent: or \para_raw_noindent:) is only + allowed if LaTeX is in vertical mode. + } % \end{macrocode} -% -% Two sample definitions for the kernel hooks to help a bit with -% tracing. + + % \begin{macrocode} -\newcounter{paracnt} -\def\@kernel@before@para@begin{% - \stepcounter{paracnt}% - \typeout{PARA: \arabic{paracnt} start - (\@currenvir\space level\on@line)}% -} -\def\@kernel@after@para@end {% - \typeout{PARA: \arabic{paracnt} end \space\space - (\@currenvir\space level\on@line)}% - \addtocounter{paracnt}{-1}% -} -% \end{macrocode} -% % -% The next lines belong in the kernel and just make sure that we -% avoid the these days unnecessary optimization that \TeX{} doesn't -% call on \cs{par} if a vertical box ends in hmode. Instead \TeX{} -% directly calls the par builder in that case :-(. -% \begin{macrocode} -\let\color@begingroup\begingroup -\def\color@endgroup{\endgraf\endgroup} -\def\color@setgroup{\color@begingroup} % \def\color@setgroup{\color@begingroup\set@color} -\def\color@hbox{\hbox\bgroup\color@begingroup} -\def\color@vbox{\vbox\bgroup\color@begingroup} -\def\color@endbox{\color@endgroup\egroup} -} +%<latexrelease>\IncludeInRelease{0000/00/00}% +%<latexrelease> {ltpara}{Undo~hooks~for~paragraphs} +%<latexrelease> +%<latexrelease>\let \OmitIndent \@undefined +%<latexrelease>\let \IndentBox \@undefined +%<latexrelease>\let \RawIndent \@undefined +%<latexrelease>\let \RawNoindent \@undefined +%<latexrelease>\let \RawParEnd \@undefined +%<latexrelease> +%<latexrelease>\cs_set_eq:NN \par \tex_par:D +%<latexrelease>\cs_set_eq:NN \@@par \tex_par:D +%<latexrelease>\cs_set_eq:NN \endgraf \tex_par:D +%<latexrelease> +%<latexrelease>\EndModuleRelease +\ExplSyntaxOff +%</2ekernel|latexrelease> % \end{macrocode} % % % -% \begin{macrocode} -%<*2ekernel> -% \end{macrocode} % % %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% @@ -1099,61 +1216,3 @@ %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% % -%%%% unused as hok was dropped - -% \subsubsection{Put the first word of each paragraph in an \cs{fbox} and append -% a \P{} sign at the end of the paragraph} -% -% We make the assumption that the first word ends in a space so -% that we can grab it easily (which -% is of course not quite right). So here is the code for that. -%\begin{verbatim} -% \def\firstwordboxed#1 {\fbox{#1} } -%\end{verbatim} -% Installing that is then just a matter of putting it into the right -% hook. Since we don't want to box the indentation (or what comes -% from \cs{everypar} we shouldn't use \hook{para/begin} but -% \hook{para/textbegin}. -% Adding the \P{} is equally simple. -%\begin{verbatim} -% \AddToHook{para/textbegin}{\firstwordboxed} -% \AddToHook{para/end}{\P} -%\end{verbatim} -% While this will work more or less flawless (if you accept that it -% will pick up any punctuation after the first word), it will fail -% in at least two cases. -% \begin{itemize} -% \item Single word paragraphs ending with \cs{par} instead of an -% empty line will blow up because the \cs{par} will be picked up as -% part of the argument and error as our \cs{firstwordboxed} doesn't -% accept a \cs{par} not being long. That is actually good, because -% picking it up would be worse: it would get ignored inside -% \cs{fbox} and so the paragraph would be combined with the next -% (or worse). -% -% \item The second problem is, of course, that sticking it into -% every \hook{para/textbegin} means it get applied to places like -% \cs{section} which is not really what you want. So some more -% elaborate mechanism would be necessary---a task for another rainy -% day. -% \end{itemize} -% -% There is one other point to note here. The \cs{firstwordboxed} is -% expected to scan ahead. So can that kind of code be placed into a -% hook? The answer is a kind of yes and no. - -% Hooks can receive code from different places, but obviously only -% one chunk of code can do some scanning ahead, the one that comes -% last. Any hook code in a different position would see any -% following code from later chunks as part of the scanning which -% may or may not be a problem. In fact, even in our simple example, -% the argument picked up by \cs{firstwordboxed} isn't really the -% first word of the paragraph. It is that but in front of it there -% is also the code responsible for running -% \verb=\AddTohookNext{para/textbegin}=. As long as this is not -% doing anything harmful it doesn't hurt in this case, but it -% clearly shows that this example is not as simple as it -% claims to be and on the whole we have to conclude that hooks -% aren't really meant to do such scanning or at least not without a -% lot of precaution. -% diff --git a/Master/texmf-dist/source/latex-dev/base/ltpictur.dtx b/Master/texmf-dist/source/latex-dev/base/ltpictur.dtx index b958d43760c..ae8b58995da 100644 --- a/Master/texmf-dist/source/latex-dev/base/ltpictur.dtx +++ b/Master/texmf-dist/source/latex-dev/base/ltpictur.dtx @@ -32,7 +32,7 @@ %<*driver> % \fi \ProvidesFile{ltpictur.dtx} - [2020/12/05 v1.2a LaTeX Kernel (Picture Mode)] + [2021/04/20 v1.2b LaTeX Kernel (Picture Mode)] % \iffalse \documentclass{ltxdoc} \GetFileInfo{ltpictur.dtx} @@ -454,9 +454,10 @@ % \end{macro} % % \begin{macro}{\@stackcr} +% \changes{v1.2b}{2021/04/20}{Use \cs{protected} for \cs{\bslash} variant (gh/548)} % \begin{macro}{\@ixstackcr} % \begin{macrocode} -\def\@stackcr{\@ifstar\@ixstackcr\@ixstackcr} +\protected\def\@stackcr{\@ifstar\@ixstackcr\@ixstackcr} \def\@ixstackcr{\@ifnextchar[\@istackcr{\cr\ignorespaces}} % \end{macrocode} % \end{macro} diff --git a/Master/texmf-dist/source/latex-dev/base/ltplain.dtx b/Master/texmf-dist/source/latex-dev/base/ltplain.dtx index fa53cc17ece..e0fbcecf1b6 100644 --- a/Master/texmf-dist/source/latex-dev/base/ltplain.dtx +++ b/Master/texmf-dist/source/latex-dev/base/ltplain.dtx @@ -32,7 +32,7 @@ %<*driver> % \fi \ProvidesFile{ltplain.dtx} - [2020/11/26 v2.3d LaTeX Kernel (Plain TeX)] + [2021/04/18 v2.3f LaTeX Kernel (Plain TeX)] % \iffalse \documentclass{ltxdoc} \GetFileInfo{ltplain.dtx} @@ -823,6 +823,28 @@ % \end{macrocode} % \end{macro} % +% +% +% \begin{macro}{\@inputcheck} +% \begin{macro}{\@unused} +% Allocate read stream for testing and output stream that is never open an +% thus writes to the terminal. +% \changes{v1.0l}{1994/11/07} +% {move here from ltdefns, remove duplicate \cs{@mainaux}} +% \changes{v2.3e}{2021/03/26}{Allocate \cs{@inputcheck} and +% \cs{@unused} early so that they are before expl3 allocates +% more streams (gh/538)} +% \begin{macrocode} +\newread\@inputcheck +\newwrite\@unused +% \end{macrocode} +% \end{macro} +% \end{macro} +% +% +% +% +% % \begin{macro}{\maxdimen} % \begin{macro}{\hideskip} % Here are some examples of allocation. @@ -898,6 +920,37 @@ % \end{macrocode} % \tracingcommands=0 % \tracingrestores=0 +% \end{macrocode} +% +% \begin{macro}{\tracingstacklevels} +% For Lua\TeX, the \cs{tracingstacklevels} functionality was +% implemented as a callback, so here we just define the count register +% to hold the value of the parameter. +% \begin{macrocode} +%</2ekernel> +%<*2ekernel|latexrelease> +%<latexrelease>\IncludeInRelease{2021/06/01}{\tracingstacklevels}% +%<latexrelease> {tracingstacklevels}% +\ifx\directlua\@undefined + % \tracingstacklevels=0 % added in 2021 +\else + \newcount\tracingstacklevels + % Code for \tracingstacklevels defined in ltfinal.dtx +\fi +%<latexrelease>\EndIncludeInRelease +%<latexrelease> +%<latexrelease>\IncludeInRelease{0000/00/00}{\tracingstacklevels}% +%<latexrelease> {tracingstacklevels}% +%<latexrelease>\ifx\directlua\@undefined +%<latexrelease>\else +%<latexrelease> \let\tracingstacklevels\@undefined +%<latexrelease>\fi +%<latexrelease>\EndIncludeInRelease +%</2ekernel|latexrelease> +%<*2ekernel> +% \end{macrocode} +% \end{macro} +% % \language=0 % \begin{macrocode} \uchyph=1 @@ -1482,42 +1535,68 @@ % \changes{v2.0b}{2012/01/20}{etex tracing if available} % \changes{v2.0d}{2015/02/20}{Spell commands correctly :-)} % \changes{v2.0g}{2015/03/10}{Reorganize to be less noisy} +% \changes{v2.3f}{2021/04/18}{Drop pre-$\varepsilon$-\TeX{} support} +% \changes{v2.3f}{2021/04/18} +% {Add \cs{tracingstacklevels} and \cs{tracinglostchars}=3} % \begin{macrocode} -%<latexrelease>\IncludeInRelease{2015/01/01}{\loggingall}{etex tracing}% +%<latexrelease>\IncludeInRelease{2021/06/01}{\loggingall} +%<latexrelease> {\tracingstacklevels and \tracinglostchars=3}% %<*2ekernel|latexrelease> -\ifx\tracingscantokens\@undefined -\gdef\loggingall{% +\edef\loggingall{% \tracingstats\tw@ \tracingpages\@ne - \tracinglostchars\@ne - \tracingparagraphs\@ne - \errorcontextlines\maxdimen - \loggingoutput - \tracingmacros\tw@ - \tracingcommands\tw@ - \tracingrestores\@ne - }% -\else -\gdef\loggingall{% - \tracingstats\tw@ - \tracingpages\@ne - \tracinglostchars\tw@ + \tracinglostchars\thr@@ \tracingparagraphs\@ne \tracinggroups\@ne \tracingifs\@ne \tracingscantokens\@ne \tracingnesting\@ne \errorcontextlines\maxdimen - \loggingoutput + \ifdefined\tracingstacklevels \tracingstacklevels\maxdimen \fi + \noexpand \loggingoutput \tracingmacros\tw@ \tracingcommands\thr@@ \tracingrestores\@ne \tracingassigns\@ne }% -\fi -\gdef\tracingall{\showoverfull\loggingall} +\def\tracingall{\showoverfull\loggingall} %</2ekernel|latexrelease> %<latexrelease>\EndIncludeInRelease +%<latexrelease> +%<latexrelease>\IncludeInRelease{2015/01/01}{\loggingall}{etex tracing}% +%<latexrelease>\ifx\tracingscantokens\@undefined +%<latexrelease>\gdef\loggingall{% +%<latexrelease> \tracingstats\tw@ +%<latexrelease> \tracingpages\@ne +%<latexrelease> \tracinglostchars\@ne +%<latexrelease> \tracingparagraphs\@ne +%<latexrelease> \errorcontextlines\maxdimen +%<latexrelease> \loggingoutput +%<latexrelease> \tracingmacros\tw@ +%<latexrelease> \tracingcommands\tw@ +%<latexrelease> \tracingrestores\@ne +%<latexrelease> }% +%<latexrelease>\else +%<latexrelease>\gdef\loggingall{% +%<latexrelease> \tracingstats\tw@ +%<latexrelease> \tracingpages\@ne +%<latexrelease> \tracinglostchars\tw@ +%<latexrelease> \tracingparagraphs\@ne +%<latexrelease> \tracinggroups\@ne +%<latexrelease> \tracingifs\@ne +%<latexrelease> \tracingscantokens\@ne +%<latexrelease> \tracingnesting\@ne +%<latexrelease> \errorcontextlines\maxdimen +%<latexrelease> \loggingoutput +%<latexrelease> \tracingmacros\tw@ +%<latexrelease> \tracingcommands\thr@@ +%<latexrelease> \tracingrestores\@ne +%<latexrelease> \tracingassigns\@ne +%<latexrelease>}% +%<latexrelease>\fi +%<latexrelease>\gdef\tracingall{\showoverfull\loggingall} +%<latexrelease>\EndIncludeInRelease +%<latexrelease> %<latexrelease>\IncludeInRelease{0000/00/00}{\loggingall}{etex tracing}% %<latexrelease>\gdef\loggingall{\tracingcommands\tw@\tracingstats\tw@ %<latexrelease> \tracingpages\@ne\tracinglostchars\@ne @@ -1532,29 +1611,14 @@ % % \begin{macro}{\tracingnone} % \changes{v2.0g}{2015/03/10}{macro added} -% \begin{macro}{\hideoutput} -% \changes{v2.0g}{2015/03/10}{macro added} +% \changes{v2.3f}{2021/04/18}{Drop pre-$\varepsilon$-\TeX{} support} +% \changes{v2.3f}{2021/04/18} +% {Add \cs{tracingstacklevels} and \cs{tracinglostchars}=3} % \begin{macrocode} %<latexrelease>\IncludeInRelease{2015/01/01}{\tracingnone}% %<latexrelease> {turn off etex tracing}% %<*2ekernel|latexrelease> -\ifx\tracingscantokens\@undefined -\def\tracingnone{% - \tracingonline\z@ - \tracingcommands\z@ - \showboxdepth\m@ne - \showboxbreadth\m@ne - \tracingoutput\z@ - \errorcontextlines\m@ne - \tracingrestores\z@ - \tracingparagraphs\z@ - \tracingmacros\z@ - \tracinglostchars\@ne - \tracingpages\z@ - \tracingstats\z@ -}% -\else -\def\tracingnone{% +\edef\tracingnone{% \tracingassigns\z@ \tracingrestores\z@ \tracingonline\z@ @@ -1563,6 +1627,7 @@ \showboxbreadth\m@ne \tracingoutput\z@ \errorcontextlines\m@ne + \ifdefined\tracingstacklevels \tracingstacklevels\z@ \fi \tracingnesting\z@ \tracingscantokens\z@ \tracingifs\z@ @@ -1573,30 +1638,77 @@ \tracingpages\z@ \tracingstats\z@ }% -\fi +%</2ekernel|latexrelease> +%<latexrelease>\EndIncludeInRelease +%<latexrelease> +%<latexrelease>\IncludeInRelease{2015/01/01}{\tracingnone}% +%<latexrelease> {turn off etex tracing}% +%<latexrelease>\ifx\tracingscantokens\@undefined +%<latexrelease>\def\tracingnone{% +%<latexrelease> \tracingonline\z@ +%<latexrelease> \tracingcommands\z@ +%<latexrelease> \showboxdepth\m@ne +%<latexrelease> \showboxbreadth\m@ne +%<latexrelease> \tracingoutput\z@ +%<latexrelease> \errorcontextlines\m@ne +%<latexrelease> \tracingrestores\z@ +%<latexrelease> \tracingparagraphs\z@ +%<latexrelease> \tracingmacros\z@ +%<latexrelease> \tracinglostchars\@ne +%<latexrelease> \tracingpages\z@ +%<latexrelease> \tracingstats\z@ +%<latexrelease>}% +%<latexrelease>\else +%<latexrelease>\def\tracingnone{% +%<latexrelease> \tracingassigns\z@ +%<latexrelease> \tracingrestores\z@ +%<latexrelease> \tracingonline\z@ +%<latexrelease> \tracingcommands\z@ +%<latexrelease> \showboxdepth\m@ne +%<latexrelease> \showboxbreadth\m@ne +%<latexrelease> \tracingoutput\z@ +%<latexrelease> \errorcontextlines\m@ne +%<latexrelease> \tracingnesting\z@ +%<latexrelease> \tracingscantokens\z@ +%<latexrelease> \tracingifs\z@ +%<latexrelease> \tracinggroups\z@ +%<latexrelease> \tracingparagraphs\z@ +%<latexrelease> \tracingmacros\z@ +%<latexrelease> \tracinglostchars\@ne +%<latexrelease> \tracingpages\z@ +%<latexrelease> \tracingstats\z@ +%<latexrelease>}% +%<latexrelease>\fi +%<latexrelease>\EndIncludeInRelease +%<latexrelease> +%<latexrelease>\IncludeInRelease{0000/00/00}{\tracingnone}% +%<latexrelease> {turn off etex tracing}% +%<latexrelease>\let\tracingnone\@undefined +%<latexrelease>\EndIncludeInRelease % \end{macrocode} +% \end{macro} % +% \begin{macro}{\hideoutput} +% \changes{v2.0g}{2015/03/10}{macro added} % \begin{macrocode} +%<*2ekernel|latexrelease> +%<latexrelease>\IncludeInRelease{2015/01/01}{\hideoutput}% +%<latexrelease> {hide output from tracing}% \def\hideoutput{% \tracingoutput\z@ \showboxbreadth\m@ne \showboxdepth\m@ne \tracingonline\m@ne }% -% \end{macrocode} -% -% \begin{macrocode} -%</2ekernel|latexrelease> %<latexrelease>\EndIncludeInRelease -%<latexrelease>\IncludeInRelease{0000/00/00}{\tracingnone}% -%<latexrelease> {turn off etex tracing}% -%<latexrelease>\let\tracingnone\@undefined +%<latexrelease> +%<latexrelease>\IncludeInRelease{0000/00/00}{\hideoutput}% +%<latexrelease> {hide output from tracing}% %<latexrelease>\let\hideoutput\@undefined %<latexrelease>\EndIncludeInRelease +%</2ekernel|latexrelease> % \end{macrocode} % \end{macro} -% \end{macro} -% % % \LaTeX\ change: |\showhyphens| Defined later. % diff --git a/Master/texmf-dist/source/latex-dev/base/ltshipout.dtx b/Master/texmf-dist/source/latex-dev/base/ltshipout.dtx index 565f35f529c..f773cd23da2 100644 --- a/Master/texmf-dist/source/latex-dev/base/ltshipout.dtx +++ b/Master/texmf-dist/source/latex-dev/base/ltshipout.dtx @@ -33,7 +33,7 @@ % % \begin{macrocode} \providecommand\ltshipoutversion{v1.0i} -\providecommand\ltshipoutdate{2021/01/25} +\providecommand\ltshipoutdate{2021/03/17} % \end{macrocode} % %<*driver> @@ -144,6 +144,12 @@ % This box register is called \cs{ShipoutBox} (alternatively % available via the L3 name \cs{l_shipout_box}). % +% This box is a ``local'' box and assignments to it should be done +% only locally. Global assignments (as done by some packages with +% older code where this is box is known as 255) may work but they are +% conceptually wrong and may result in errors under certain +% circumstances. +% % During the execution of \hook{shipout/before} this box contains % the accumulated material for the page, but not yet any material % added by other shipout hooks. diff --git a/Master/texmf-dist/source/latex-dev/base/lttab.dtx b/Master/texmf-dist/source/latex-dev/base/lttab.dtx index a964b365753..48627f40a3c 100644 --- a/Master/texmf-dist/source/latex-dev/base/lttab.dtx +++ b/Master/texmf-dist/source/latex-dev/base/lttab.dtx @@ -31,7 +31,7 @@ %%% From File: lttab.dtx %<*driver> % \fi -\ProvidesFile{lttab.dtx}[2020/04/24 v1.1r LaTeX Kernel (Columns)] +\ProvidesFile{lttab.dtx}[2021/04/20 v1.1s LaTeX Kernel (Columns)] % \iffalse \documentclass{ltxdoc} \GetFileInfo{lttab.dtx} @@ -464,7 +464,7 @@ % % \begin{macro}{\@tabcr} % \begin{macrocode} -\def\@tabcr{\@stopline \@ifstar{\penalty \@M \@xtabcr}\@xtabcr} +\protected\def\@tabcr{\@stopline \@ifstar{\penalty \@M \@xtabcr}\@xtabcr} % \end{macrocode} % \end{macro} % @@ -964,8 +964,9 @@ % % \begin{macro}{\@arraycr} % Array version of |\\|. +% \changes{v1.1s}{2021/04/20}{Use \cs{protected} for \cs{\bslash} variant (gh/548)} % \begin{macrocode} -\def\@arraycr{% +\protected\def\@arraycr{% ${\ifnum0=`}\fi\@ifstar\@xarraycr\@xarraycr} % \end{macrocode} % \end{macro} @@ -994,8 +995,9 @@ % \end{macro} % % \begin{macro}{\@tabularcr} +% \changes{v1.1s}{2021/04/20}{Use \cs{protected} for \cs{\bslash} variant (gh/548)} % \begin{macrocode} -\def\@tabularcr{% +\protected\def\@tabularcr{% {\ifnum0=`}\fi\@ifstar\@xtabularcr\@xtabularcr} % \end{macrocode} % \end{macro} diff --git a/Master/texmf-dist/source/latex-dev/base/lttextcomp.dtx b/Master/texmf-dist/source/latex-dev/base/lttextcomp.dtx index d840f0448b9..1efc36d5637 100644 --- a/Master/texmf-dist/source/latex-dev/base/lttextcomp.dtx +++ b/Master/texmf-dist/source/latex-dev/base/lttextcomp.dtx @@ -36,7 +36,7 @@ % % \ProvidesFile{lttextcomp.dtx} - [2021/01/20 v1.0d LaTeX Kernel (text companion symbols)] + [2021/04/16 v1.0f LaTeX Kernel (text companion symbols)] % \iffalse \documentclass{ltxdoc} \begin{document} @@ -70,20 +70,23 @@ % % \StopEventually{} % +% \changes{v1.0e}{2021/03/19} +% {Use \cs{NewModuleRelease}} +% \begin{macrocode} +%<*2ekernel|latexrelease> +%<latexrelease>\NewModuleRelease{2020/02/02}{lttextcomp} +%<latexrelease> {Text Companion symbols} +% \end{macrocode} % % \begin{macro}{\oldstylenums} % \begin{macro}{\legacyoldstylenums} % -% % Preserve the old definition of \cs{oldstylenums} under a different name. % % This macro implements old style numerals but only works if we % assume that the standard math fonts are used. Thus it needs % changing in case other math encodings are used. % \begin{macrocode} -%<*2ekernel|latexrelease> -%<latexrelease>\IncludeInRelease{2020/02/02}% -%<latexrelease> {\oldstylenums}{Old style numerals}% \DeclareRobustCommand\legacyoldstylenums[1]{% \begingroup % \end{macrocode} @@ -157,44 +160,6 @@ % \end{macrocode} % \end{macro} % -% -% \begin{macrocode} -%</2ekernel|latexrelease> -%<latexrelease>\EndIncludeInRelease -%<latexrelease>\IncludeInRelease{0000/00/00}% -%<latexrelease> {\oldstylenums}{Old style numerals}% -%<latexrelease> -%<latexrelease>\DeclareRobustCommand\oldstylenums[1]{% -%<latexrelease> \begingroup -%<latexrelease> \spaceskip\fontdimen\tw@\font -%<latexrelease> \usefont{OML}{\rmdefault}{\f@series}{it}% -%<latexrelease> \mathgroup\symletters #1% -%<latexrelease> \endgroup -%<latexrelease>} -%<latexrelease>\let\legacyoldstylenums\@undefined -%<latexrelease>\def\textcompsubstdefault{cmr} -%<latexrelease> -%<latexrelease>\EndIncludeInRelease -% \end{macrocode} -% -% - - - - - -% Everything else in the this file got introduced 2020/02/02, so we do a -% single rollback (for now). -% \begin{macrocode} -%<*2ekernel> -%</2ekernel> -%<*2ekernel|latexrelease> -%<latexrelease>\IncludeInRelease{2020/02/02}% -%<latexrelease> {\DeclareEncodingSubset}{Text companion symbols}% -% \end{macrocode} -% -% -% % \begin{macro}{\DeclareEncodingSubset} % % The declaration takes 3 mandatory arguments: an \emph{encoding} @@ -1300,9 +1265,19 @@ % Supporting rollback \ldots % \begin{macrocode} %</2ekernel|latexrelease> -%<latexrelease>\EndIncludeInRelease +%<latexrelease> %<latexrelease>\IncludeInRelease{0000/00/00}% -%<latexrelease> {\DeclareEncodingSubset}{Text companion symbols}% +%<latexrelease> {lttextcomp}{Undefine text companion symbols}% +%<latexrelease> +%<latexrelease>\DeclareRobustCommand\oldstylenums[1]{% +%<latexrelease> \begingroup +%<latexrelease> \spaceskip\fontdimen\tw@\font +%<latexrelease> \usefont{OML}{\rmdefault}{\f@series}{it}% +%<latexrelease> \mathgroup\symletters #1% +%<latexrelease> \endgroup +%<latexrelease>} +%<latexrelease>\let\legacyoldstylenums\@undefined +%<latexrelease>\def\textcompsubstdefault{cmr} %<latexrelease> %<latexrelease>\let\DeclareEncodingSubset\@undefined %<latexrelease>\let\CheckEncodingSubset\@undefined @@ -1493,9 +1468,7 @@ %<latexrelease>\let\textlegacyperiodcentered\@undefined %<latexrelease>\let\textlegacysection\@undefined %<latexrelease> -%<latexrelease>\EndIncludeInRelease -%<*2ekernel> -%</2ekernel> +%<latexrelease>\EndModuleRelease % \end{macrocode} % % diff --git a/Master/texmf-dist/source/latex-dev/base/ltvers.dtx b/Master/texmf-dist/source/latex-dev/base/ltvers.dtx index 26eaee98901..fe50d75a8f1 100644 --- a/Master/texmf-dist/source/latex-dev/base/ltvers.dtx +++ b/Master/texmf-dist/source/latex-dev/base/ltvers.dtx @@ -33,7 +33,7 @@ %<*driver> % \fi \ProvidesFile{ltvers.dtx} - [2021/01/15 v1.1e LaTeX Kernel (Version Info)] + [2021/04/16 v1.1g LaTeX Kernel (Version Info)] % \iffalse \documentclass{ltxdoc} \GetFileInfo{ltvers.dtx} @@ -112,10 +112,10 @@ %</2ekernel> %<latexrelease>\edef\latexreleaseversion %<*2ekernel|latexrelease> - {2021-05-01} + {2021-06-01} %</2ekernel|latexrelease> %<*2ekernel> -\def\patch@level{-2} +\def\patch@level{-3} % \end{macrocode} % % \begin{macro}{\development@branch@name} @@ -214,6 +214,11 @@ % \end{macrocode} % % \begin{macro}{\IncludeInRelease} +% \begin{macro}{\EndIncludeInRelease} +% \begin{macro}{\@IncludeInRelease} +% \begin{macro}{\@IncludeInRele@se} +% \begin{macro}{\@gobble@IncludeInRelease} +% \begin{macro}{\@check@IncludeInRelease} % \changes{v1.0w}{2015/02/21} % {set \cs{@currname} empty here (in case \cs{IncludeInRelease} input early)} % \begin{macrocode} @@ -224,6 +229,7 @@ % \changes{v1.0m}{2015/01/17}{modified with \cs{@currname}} % \changes{v1.0o}{2015/01/19}{Optional argument} % \changes{v1.0q}{2015/02/19}{Swap argument order} +% \changes{v1.1f}{2021/03/18}{Add support for usage in \cs{NewModuleRelease}} % % \begin{macrocode} %<*2ekernel|latexrelease> @@ -237,9 +243,29 @@ {There is an \string\EndIncludeRelease\space missing}% \@includeinreleasefalse \fi - \kernel@ifnextchar[% - {\@IncludeInRelease{#1}} - {\@IncludeInRelease{#1}[#1]}} + \ifnum0% + \ifx\new@moduledate\@empty\else 1\fi + \ifnum \expandafter\@parse@version#1//00\@nil=0 1\fi + =11 + \expandafter\@firstoftwo + \else + \expandafter\@secondoftwo + \fi + {\finish@module@release{#1}}% + {\kernel@ifnextchar[% + {\@IncludeInRelease{#1}} + {\@IncludeInRelease{#1}[#1]}}} +\def\finish@module@release#1#2#3{% + \toks@{[#1] #3}% + \ifnum\expandafter\@parse@version\new@moduledate//00\@nil + >\expandafter\@parse@version\fmtversion//00\@nil + \GenericInfo{}{Applying: \the\toks@}% + \else + \GenericInfo{}{Skipping: \the\toks@}% + \expandafter\gobble@finish@module@release + \fi} +\long\def\gobble@finish@module@release#1\EndModuleRelease{% + \EndModuleRelease} % \end{macrocode} % % If a specific date has not been specified in |latexrelease| @@ -261,9 +287,9 @@ % only really needed for rolling forward. So maybe one day \dots % \begin{macrocode} \ifnum\expandafter\@parse@version#1//00\@nil - >\expandafter\@parse@version\fmtversion//00\@nil + >\expandafter\@parse@version\fmtversion//00\@nil \GenericInfo{}{Skipping: \the\toks@}% - \expandafter\expandafter\expandafter\@gobble@IncludeInRelease + \expandafter\expandafter\expandafter\@gobble@IncludeInRelease \else \GenericInfo{}{Applying: \the\toks@}% \@includeinreleasetrue @@ -282,6 +308,9 @@ \@includeinreleasefalse \else \PackageError{latexrelease}{mis-matched EndIncludeInRelease}{}% +\fi +\if@skipping@module + \expandafter\new@module@skip \fi} % \end{macrocode} % @@ -296,13 +325,87 @@ #2#3\@end@check@IncludeInRelease{% \ifx\@check@IncludeInRelease#2\else \PackageError{latexrelease}{skipped IncludeInRelease for tag \string#2}{}% + \fi + \if@skipping@module + \expandafter\new@module@skip \fi} % \end{macrocode} +% \end{macro} +% \end{macro} +% \end{macro} +% \end{macro} +% \end{macro} +% \end{macro} % +% \subsection{Declaring an all-new module} +% +% \begin{macro}{\if@skipping@module} +% \begin{macro}{\NewModuleRelease} +% \begin{macro}{\EndModuleRelease} +% \begin{macro}{\new@module@skip} +% \begin{macro}{\new@modulename} +% \begin{macro}{\new@moduledate} +% +% When we have a whole new module, we can't roll back to a date where +% such module exists, otherwise hundreds of \enquote{command already +% defined} errors will pop up. But we can't skip it altogether +% either, because the module might have changes we still want applied, +% so a more detailed cherry-picking of code chunks have to be done. +% +% \changes{v1.1f}{2021/03/18}{Added \cs{NewModuleRelease}.} +% \changes{v1.1g}{2021/04/16} +% {\cs{NewModuleRelease} with the same arguments as \cs{IncludeInRelease}.} % \begin{macrocode} -%</2ekernel|latexrelease> +\let\if@skipping@module\iffalse +\def\@skipping@moduletrue{\let\if@skipping@module\iftrue} +\def\@skipping@modulefalse{\let\if@skipping@module\iffalse} +\let\new@modulename\@empty +\let\new@moduledate\@empty +\def\NewModuleRelease#1#2#3{% + \ifx\new@modulename\@empty \else + \@latex@error{Nested \noexpand\NewModuleRelease forbidden.}\@ehd \fi + \edef\new@moduledate{#1}% + \edef\new@modulename{#2}% + \GenericInfo{}{BEGIN module: \new@modulename\space (\new@moduledate)}% + \GenericInfo{}{ \@spaces\@spaces\@spaces\space#3\@gobble}% + \ifnum\sourceLaTeXdate<% + \expandafter\@parse@version\new@moduledate//00\@nil\relax + \ifnum\expandafter\@parse@version\fmtversion//00\@nil<% + \expandafter\@parse@version\new@moduledate//00\@nil\relax + \GenericInfo{}{Skipping module \new@modulename}% + \expandafter\expandafter + \expandafter\gobble@finish@module@release + \else + \GenericInfo{}{Applying module \new@modulename} + \@skipping@modulefalse + \fi + \else + \GenericInfo{}{Skipping module \new@modulename} + \@skipping@moduletrue + \expandafter\new@module@skip + \fi} +\long\def\new@module@skip#1\IncludeInRelease{\IncludeInRelease} +\def\EndModuleRelease{% + \ifx\new@modulename\@empty + \@latex@error{Extra \string\EndModuleRelease.}\@eha + \else + \GenericInfo{}{END module: \new@modulename\space (\new@moduledate)}% + \let\new@modulename\@empty + \let\new@moduledate\@empty + \@skipping@modulefalse + \fi} % \end{macrocode} % \end{macro} +% \end{macro} +% \end{macro} +% \end{macro} +% \end{macro} +% \end{macro} +% +% \begin{macrocode} +%</2ekernel|latexrelease> +% \end{macrocode} +% % \Finale % \endinput |