diff options
author | Karl Berry <karl@freefriends.org> | 2020-07-21 21:39:07 +0000 |
---|---|---|
committer | Karl Berry <karl@freefriends.org> | 2020-07-21 21:39:07 +0000 |
commit | a4824d556273f90fbb37d24449cca150397ab7f2 (patch) | |
tree | 27fa3e556270c65afd257af5c0e69168c8c2633b /Master/texmf-dist/source/latex-dev | |
parent | a5ed6b49fd24fce0a996e8db92f76022c41c5007 (diff) |
latex-dev (21jul20)
git-svn-id: svn://tug.org/texlive/trunk@55898 c570f23f-e606-0410-a88d-b1316a301751
Diffstat (limited to 'Master/texmf-dist/source/latex-dev')
22 files changed, 7527 insertions, 155 deletions
diff --git a/Master/texmf-dist/source/latex-dev/base/doc.dtx b/Master/texmf-dist/source/latex-dev/base/doc.dtx index 3011994d9d1..7e7817153c8 100644 --- a/Master/texmf-dist/source/latex-dev/base/doc.dtx +++ b/Master/texmf-dist/source/latex-dev/base/doc.dtx @@ -35,7 +35,7 @@ %<+package|shortvrb>\NeedsTeXFormat{LaTeX2e}[1994/12/01] %<+package> \ProvidesPackage{doc} %<+shortvrb>\ProvidesPackage{shortvrb} -%<+package|shortvrb> [2019/12/16 v2.1l +%<+package|shortvrb> [2020/06/15 v2.1m %<+package|shortvrb> Standard LaTeX documentation package (FMi)] %\catcode`\<=12 % @@ -139,7 +139,7 @@ % \changes{v1.9t}{1995/05/11}{Use \cs{GetFileInfo}} % \GetFileInfo{doc.sty} % -% \CheckSum{2208} ^^A % keep the checksum in this file +% \CheckSum{2213} ^^A % keep the checksum in this file % % \title{The \DOC{} and \texttt{shortvrb} Packages\thanks % {This file has version number \fileversion{} dated \filedate{}.}} @@ -1324,6 +1324,16 @@ \if@inlabel\leavevmode\fi \trivlist \parskip \z@ \item[]% % \end{macrocode} +% The \cs{item} command sets the \cs{@labels} box but that box is +% never typeset (as \cs{everypar} that normally does this gets +% redefined later). That is normally not an issue, but produces a +% problem when typesetting in mixed directions, (e.g., in +% Japanese), so we explicitly clear it for that use case. +% \changes{v2.1m}{2020/06/15}{Void \cs{@labels} for vertical +% typesetting (gh/344)} +% \begin{macrocode} + \global\setbox\@labels\box\voidb@x +% \end{macrocode} % Additionally, everything should be set in \texttt{typewriter} font. % Some people might prefer it somewhat differently; because of this % the font choice is @@ -1448,7 +1458,8 @@ % bothered since doc-sources should be up-to-date but since the % request came from someone called David Carlisle \ldots :-) % \changes{v1.9y}{1996/01/26}{Support compat mode} -% \changes{v2.1l}{2019/12/16}{Use \cs{shapedefault} not \cs{updefault} for extended NFSS} +% \changes{v2.1l}{2019/12/16}{Use \cs{shapedefault} not +% \cs{updefault} for extended NFSS} % \begin{macrocode} \def\MacroFont{\small \usefont\encodingdefault diff --git a/Master/texmf-dist/source/latex-dev/base/docstrip.dtx b/Master/texmf-dist/source/latex-dev/base/docstrip.dtx index 3ce6ed56967..a0a06311edb 100644 --- a/Master/texmf-dist/source/latex-dev/base/docstrip.dtx +++ b/Master/texmf-dist/source/latex-dev/base/docstrip.dtx @@ -29,9 +29,9 @@ \catcode`\{=1 \catcode`\}=2 \def\filename{docstrip.dtx} -\def\fileversion{v2.5h} -\def\filedate{2020/04/18} -\def\docdate {2018/05/03} +\def\fileversion{v2.6a} +\def\filedate{2020-07-07} +\def\docdate {2020-07-11} %% % %\iffalse @@ -42,7 +42,7 @@ %% Frank Mittelbach %% Copyright (C) 1995 Marcin Woli\'nski %% Copyright (C) 1996-1997 Mark Wooding, Marcin Woli\'nski -%% Copyright (C) 1998-2003 LaTeX3 project and the above authors +%% Copyright (C) 1998-2020 LaTeX3 project and the above authors %% All rights are reserved. %% % @@ -82,6 +82,8 @@ % \changes{2.3e}{1996/10/02}{Introduced ``open lists''} % \changes{2.4a}{1996/06/06}{Add stream limits (MDW)} % \changes{2.4c}{1996/06/11}{Add initex support (DPC)} +% \changes{v2.6a}{2020-07-07}{Added the handling of @@-modules from +% \texttt{l3docstrip.dtx} (gh/337)} % % \DoNotIndex{\#,\$,\%,\&,\@,\\,\{,\},\^,\_,\~,\ } % \DoNotIndex{\@ne} @@ -785,6 +787,57 @@ % When a block of code is {\em not\/} included, any guards that occur % within that block are {\em not\/} evaluated. % +% \section{Internal functions and variables} +% +% An important consideration for \LaTeX3 development is separating +% out public and internal functions. Functions and variables which +% are private to one module should not be used or modified by any +% other module. As \TeX{} does not have any formal namespacing +% system, this requires a convention for indicating which functions +% in a code-level module are public and which are private. +% +% Using \ds\ allows internal functions to be indicated +% using a `two part' system. Within the \texttt{.dtx} file, +% internal functions may be indicated using |@@| in place of the +% module name, for example +% \begin{verbatim} +% \cs_new_protected:Npn \@@_some_function:nn #1#2 +% { +% % Some code here +% } +% \tl_new:N \l_@@_internal_tl +% \end{verbatim} +% +% To extract the code using \ds, the original `guard' +% mecahnism is extended by the introduction of the syntax +% \texttt{\%<@@=\meta{module}>}. The \meta{module} name then +% replaces the |@@| when the code is extracted, so that +% \begin{verbatim} +% %<*package> +% %<@@=foo> +% \cs_new_protected:Npn \@@_some_function:nn #1#2 +% { +% % Some code here +% } +% \tl_new:N \l_@@_internal_tl +% %</package> +% \end{verbatim} +% is extracted as +% \begin{verbatim} +% \cs_new_protected:Npn \__foo_some_function:nn #1#2 +% { +% % Some code here +% } +% \tl_new:N \l__foo_internal_tl +% \end{verbatim} +% where the |__| indicates that the functions and variables are +% internal to the \texttt{foo} module. +% +% Use |@@@@| to obtain |@@| in the output (|@@@@@| to get |@@@|). +% For longer pieces of code the replacement can be completely +% suppressed by giving an empty module name, namely using the +% syntax \texttt{\%<@@=>}. +% % \section{Those other languages} % Since \TeX\ is an open system some of \TeX\ packages include % non-\TeX\ files. Some authors use \ds\ to generate PostScript @@ -853,7 +906,7 @@ % % \subsection{Verbatim mode} % If your programming language uses some construct that can -% interferes badly with \ds\ (e.g., percent in column one) you may +% interfere badly with \ds\ (e.g., percent in column one) you may % need a way for preventing it from being stripped off. For that % purpose \ds\ features `verbatim mode'. % @@ -2245,9 +2298,14 @@ Z % \subsection{Processing the input lines} % % \begin{macro}{\normalLine} +% \changes{v2.6a}{2020-07-07}{The search-and-replace macro +% \cs{replaceModuleInLine} added from \textsf{l3docstrip.dtx} +% (gh/337)} % The macro |\normalLine| writes its argument (which has to be % delimited with |\endLine|) on all active output files i.e. -% those with off-counters equal to zero. +% those with off-counters equal to zero. It uses the +% search-and-replace macro \cs{replaceModuleInLine} to replace any +% occurences of \texttt{@@} with the current module name. % If statistics are included, the counter % |\codeLinesPassed| is incremented by $1$. % \begin{macrocode} @@ -2257,6 +2315,7 @@ Z %</stats> \maybeMsg{.}% \def\inLine{#1}% + \replaceModuleInLine \let\do\putline@do \activefiles } @@ -2417,14 +2476,17 @@ Z % \changes{2.3a}{1995/08/18}{Adapted to concurrent version} % \changes{2.3a}{1995/08/20}{Trying to avoid assignments} % \changes{2.3e}{1996/09/16}{Verbatim mode} +% \changes{v2.6a}{2020-07-07}{Add the @-sign option from +% \textsf{l3docstrip.dtx} (gh/337)} +% % When the macros that process a line have found that the line % starts with `\texttt{\%<}', a guard line has been encountered. % The first character of a guard can be an asterisk (\texttt{*}), a % slash (\texttt{/}) a plus (\texttt{+}), a minus (\texttt{-}), a -% less-than sign (\texttt{<}) starting verbatim mode or -% any other character that can be found in an option name. This -% means that we have to peek at the next token and -% decide what kind of guard we have. +% less-than sign (\texttt{<}) starting verbatim mode, a commercial +% at (\texttt{@}) or any other character that can be found in an +% option name. This means that we have to peek at the next token +% and decide what kind of guard we have. % % We reinsert |#1| as it may be needed by |\doOption|. % \begin{macrocode} @@ -2432,12 +2494,13 @@ Z \ifcase \ifx*#10\else \ifx/#11\else \ifx+#12\else \ifx-#13\else - \ifx<#14\else 5\fi\fi\fi\fi\fi\relax + \ifx<#14\else \ifx @#15\else 6\fi\fi\fi\fi\fi\fi\relax \expandafter\starOption\or \expandafter\slashOption\or \expandafter\plusOption\or \expandafter\minusOption\or \expandafter\verbOption\or + \expandafter\moduleOption\or \expandafter\doOption\fi #1} % \end{macrocode} @@ -2452,12 +2515,17 @@ Z % result of the test |\if1\Expr{|\meta{options}|}|, the current % line is either copied to the output stream or removed. Then % the test is computed for all active output files. +% \changes{v2.6a}{2020-07-07}{Now use \cs{InLine} and call +% \cs{replaceModuleInline} (gh/337)} % \begin{macrocode} \def\doOption#1>#2\endLine{% \maybeMsg{<#1 . >}% \Evaluate{#1}% \def\do##1##2##3{% - \if1\Expr{##2}\StreamPut##1{#2}\fi + \if1\Expr{##2}% + \def\inLine{#2}% + \replaceModuleInLine + \StreamPut##1{\inLine}\fi }% \activefiles } @@ -2681,6 +2749,94 @@ Z % \end{macrocode} % \end{macro} % +% \begin{macro}{\moduleOption} +% \changes{v2.6a}{2020-07-07}{Macro added from +% \textsf{l3docstrip.dtx} (gh/337)} +% +% In the case where the line starts |%<@|: the defined syntax requires that +% this continues to |%<@@=|. At the moment, we assume that the syntax is +% correct and |#1| here is the module name for substitution into any +% internal functions in the extracted material. +% \begin{macrocode} +\def\moduleOption @@=#1>#2\endLine{% + \maybeMsg{<@@=#1>}% + \prepareActiveModule{#1}% +} +% \end{macrocode} +% \end{macro} +% +% \begin{macro}{\prepareActiveModule} +% \changes{v2.6a}{2020-07-07}{Macro added from +% \textsf{l3docstrip.dtx} (gh/337)} +% \begin{macro}{\replaceModuleInLine} +% \changes{v2.6a}{2020-07-07}{Macro added from +% \textsf{l3docstrip.dtx} (gh/337)} +% Here, we set up to do the search-and-replace when doing the +% extraction. The argument (|#1|) is the replacement text to use, +% or if empty an indicator that no replacement should be done. The +% search material is one of |__@@|, |_@@| or |@@|, done in order +% such that all three end up the same in the output. The string +% |@@@@| is hidden from these replacements by temporarily turning +% it into a pair of letters with different category codes, not +% produced by \ds; this allows to get |@@| in the +% output. The replacement function is initialised as a do-nothing +% for the case where |%<@@=| is never seen. +% \begin{macrocode} +\begingroup + \catcode`\_ = 12 % + \long\gdef\prepareActiveModule#1{% + \ifx\relax#1\relax + \let\replaceModuleInLine\empty + \else + \edef\replaceModuleInLine{% + \noexpand\replaceAllIn\noexpand\inLine{@@@@}{\string aa}% + \noexpand\replaceAllIn\noexpand\inLine{__@@}{__#1}% + \noexpand\replaceAllIn\noexpand\inLine{_@@}{__#1}% + \noexpand\replaceAllIn\noexpand\inLine{@@}{__#1}% + \noexpand\replaceAllIn\noexpand\inLine{\string aa}{@@}% + }% + \fi + } +\endgroup +\let\replaceModuleInLine\empty +% \end{macrocode} +% \end{macro} +% \end{macro} +% +% \begin{macro}{\replaceAllIn} +% \changes{v2.6a}{2020-07-07}{Macro added from +% \textsf{l3docstrip.dtx} (gh/337)} +% \begin{macro}{\replaceAllInAuxI} +% \changes{v2.6a}{2020-07-07}{Macro added from +% \textsf{l3docstrip.dtx} (gh/337)} +% \begin{macro}{\replaceAllInAuxII} +% \changes{v2.6a}{2020-07-07}{Macro added from +% \textsf{l3docstrip.dtx} (gh/337)} +% \begin{macro}{\replaceAllInAuxIII} +% \changes{v2.6a}{2020-07-07}{Macro added from +% \textsf{l3docstrip.dtx} (gh/337)} +% The code here is a simple search-and-replace routine for a macro +% |#1|, replacing |#2| by |#3|. As set up here, there is an +% assumption that nothing is going to be expandable, which is +% reasonable as \ds\ deals with `string' material. +% \begin{macrocode} +\long\def\replaceAllIn#1#2#3{% + \long\def\tempa##1##2#2{% + ##2\qMark\replaceAllInAuxIII#3##1% + }% + \edef#1{\expandafter\replaceAllInAuxI#1\qMark#2\qStop}% +} +\def\replaceAllInAuxI{% + \expandafter\replaceAllInAuxII\tempa\replaceAllInAuxI\empty +} +\long\def\replaceAllInAuxII#1\qMark#2{#1} +\long\def\replaceAllInAuxIII#1\qStop{} +% \end{macrocode} +% \end{macro} +% \end{macro} +% \end{macro} +% \end{macro} +% % \subsection{Batchfile commands} % \changes{2.3e}{1996/10/02}{Added doc} % \ds{} keeps information needed to control inclusion of sources in diff --git a/Master/texmf-dist/source/latex-dev/base/format.ins b/Master/texmf-dist/source/latex-dev/base/format.ins index 940161edd5b..5d96b5e1f35 100644 --- a/Master/texmf-dist/source/latex-dev/base/format.ins +++ b/Master/texmf-dist/source/latex-dev/base/format.ins @@ -161,6 +161,7 @@ the system are in the document `cfgguide.tex'. \from{ltdirchk.dtx}{initex,2ekernel,dircheck} \from{ltplain.dtx}{2ekernel} \from{ltvers.dtx}{2ekernel} + \from{ltluatex.dtx}{2ekernel} \from{ltdefns.dtx}{2ekernel} \from{ltalloc.dtx}{2ekernel} \from{ltcntrl.dtx}{2ekernel} @@ -196,7 +197,6 @@ the system are in the document `cfgguide.tex'. \from{ltpage.dtx}{2ekernel} \from{ltoutput.dtx}{2ekernel} \from{ltclass.dtx}{2ekernel,tracerollback} - \from{ltluatex.dtx}{2ekernel} \from{ltfinal.dtx}{2ekernel}} \file{tracefnt.sty}{% \from{ltfsstrc.dtx}{package,trace}} diff --git a/Master/texmf-dist/source/latex-dev/base/ltclass.dtx b/Master/texmf-dist/source/latex-dev/base/ltclass.dtx index dd35be9ae41..152adf82ff2 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} - [2020/04/24 v1.3k LaTeX Kernel (Class & Package Interface)] + [2020/06/05 v1.3l LaTeX Kernel (Class & Package Interface)] % \iffalse \documentclass{ltxdoc} \GetFileInfo{ltclass.dtx} @@ -552,22 +552,58 @@ % |#2| current extension. \\ % |#3| current catcode of |@|. \\ % |#4| Rest of the stack. +% \changes{v1.3l}{2020/06/05}{Added \cs{@expl@push@filename@@} +% and \cs{@expl@push@filename@aux@@}} % \begin{macrocode} +%</2ekernel> +%<*2ekernel|latexrelease> +%<latexrelease> +%<latexrelease>\IncludeInRelease{2020/10/01}{\@pushfilename}% +%<latexrelease> {Add \@expl@push@filename@@ and \@expl@push@filename@aux@@}% \def\@pushfilename{% + \@expl@push@filename@@ \xdef\@currnamestack{% {\@currname}% {\@currext}% {\the\catcode`\@}% - \@currnamestack}} + \@currnamestack}% + \@expl@push@filename@aux@@} +%<latexrelease>\EndIncludeInRelease +%<latexrelease> +%<latexrelease>\IncludeInRelease{0000/00/00}{\@pushfilename}% +%<latexrelease> {Add \@expl@push@filename@@ and \@expl@push@filename@aux@@}% +%<latexrelease>\def\@pushfilename{% +%<latexrelease> \xdef\@currnamestack{% +%<latexrelease> {\@currname}% +%<latexrelease> {\@currext}% +%<latexrelease> {\the\catcode`\@}% +%<latexrelease> \@currnamestack}} +%<latexrelease>\EndIncludeInRelease \@onlypreamble\@pushfilename % \end{macrocode} % +% \changes{v1.3l}{2020/06/05}{Added \cs{@expl@pop@filename@@}} % \begin{macrocode} -\def\@popfilename{\expandafter\@p@pfilename\@currnamestack\@nil} +%<latexrelease> +%<latexrelease>\IncludeInRelease{2020/10/01}{\@popfilename}% +%<latexrelease> {Add \@expl@pop@filename@@}% +\def\@popfilename{\expandafter\@p@pfilename\@currnamestack\@nil + \@expl@pop@filename@@} +%<latexrelease>\EndIncludeInRelease +%<latexrelease> +%<latexrelease>\IncludeInRelease{0000/00/00}{\@pushfilename}% +%<latexrelease> {Add \@expl@push@filename@@ and \@expl@push@filename@aux@@}% +%<latexrelease>\def\@popfilename{\expandafter\@p@pfilename\@currnamestack\@nil} +%<latexrelease>\EndIncludeInRelease \@onlypreamble\@popfilename % \end{macrocode} % % \begin{macrocode} +%</2ekernel|latexrelease> +%<*2ekernel> +% \end{macrocode} +% +% \begin{macrocode} \def\@p@pfilename#1#2#3#4\@nil{% \gdef\@currname{#1}% \gdef\@currext{#2}% diff --git a/Master/texmf-dist/source/latex-dev/base/ltdefns.dtx b/Master/texmf-dist/source/latex-dev/base/ltdefns.dtx index 8b7a307ad2d..3abd251e4c9 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} - [2020/05/15 v1.5h LaTeX Kernel (definition commands)] + [2020/06/04 v1.5i LaTeX Kernel (definition commands)] % \iffalse \documentclass{ltxdoc} \GetFileInfo{ltdefns.dtx} @@ -101,6 +101,16 @@ %<*2ekernel> % \end{macrocode} % +% \subsection{Loading further extensions} +% +% \changes{v1.5i}{2020-06-04}{Load \textsf{ltexpl} in \texttt{ltdefns}} +% Simply load further code: it's deliberately separate. +% This is done as soon as possible here in \texttt{ltdefns.dtx} so that +% we have the extensions as soon as possible. +% \begin{macrocode} +\input ltexpl.ltx +% \end{macrocode} +% % \subsection{Initex initialisations} % % \task{???}{This section needs extension} diff --git a/Master/texmf-dist/source/latex-dev/base/ltexpl.dtx b/Master/texmf-dist/source/latex-dev/base/ltexpl.dtx index 5e726298b26..7a567e378f3 100644 --- a/Master/texmf-dist/source/latex-dev/base/ltexpl.dtx +++ b/Master/texmf-dist/source/latex-dev/base/ltexpl.dtx @@ -33,10 +33,10 @@ %<*driver> % \fi \ProvidesFile{ltexpl.dtx} - [2020-05-03 v1.2b LaTeX Kernel (expl3-dependent code)] + [2020-07-08 v1.2d LaTeX Kernel (expl3-dependent code)] % \iffalse \documentclass{ltxdoc} -\GetFileInfo{ltfinal.dtx} +\GetFileInfo{ltexpl.dtx} \title{\filename} \date{\filedate} \author{% @@ -44,7 +44,7 @@ \begin{document} \MaintainedByLaTeXTeam{latex} \maketitle - \DocInput{ltfinal.dtx} + \DocInput{ltexpl.dtx} \end{document} %</driver> % \fi @@ -55,69 +55,170 @@ % % \changes{v0.0}{2019-10-02}{Initial version} % +% \subsection{Loader} +% +% \changes{v1.0a}{2020/03/02} +% {Don't load expl3 if already in the format (gh/295)} +% \changes{v1.1}{2020/03/05} +% {Load xparse.ltx if \cs{NewDocumentCommand} is not defined +% by expl3.ltx} +% \changes{v1.2c}{2020/06/04} +% {Define a local version of some \LaTeXe{} basic macros to support +% package loading} +% +% First define some blank commands, so that in case something goes wrong while +% loading \textsf{expl3}, we won't get strange \texttt{Undefined control +% sequence} errors. % \begin{macrocode} %<*2ekernel> +\def\@expl@sys@load@backend@@{} +\def\@expl@push@filename@@{} +\def\@expl@push@filename@aux@@{} +\def\@expl@pop@filename@@{} +%</2ekernel> % \end{macrocode} % -% \subsection{Loader} +% \changes{v1.2d}{2020/07/08} +% {Add a last-minute hook for \textsf{expl3}} +% Create a hook for last-minute \pkg{expl3} material. +% \begin{macrocode} +%<*2ekernel> +\def\@expl@finalise@setup@@{} +%</2ekernel> +% \end{macrocode} % -% Check for the required primitive/engine support and the existence of -% a loader. +% Now define some basics to support loading \textsf{expl3}. These macros can +% be defined here safely, because they are redefined later on by the kernel, +% so we define simpler versions just to suit our needs. % \begin{macrocode} +%<*2ekernel> +\long\def\@gobble#1{} +\long\def\@firstofone#1{#1} +\long\def\@firstoftwo#1#2{#1} +\long\def\@secondoftwo#1#2{#2} +\long\def\IfFileExists#1{% + \openin\@inputcheck"#1" % + \ifeof\@inputcheck + \expandafter\@secondoftwo + \else + \closein\@inputcheck + \expandafter\@firstoftwo + \fi} +\long\def\@ifnextchar#1#2#3{% + \let\reserved@d=#1% + \def\reserved@a{#2}% + \def\reserved@b{#3}% + \futurelet\@let@token\@ifnch} +\def\@ifnch{% + \ifx\@let@token\reserved@d + \expandafter\reserved@a + \else + \expandafter\reserved@b + \fi} %</2ekernel> -%<*2ekernel|latexrelease> -%<latexrelease>\IncludeInRelease{2020/02/02}% -%<latexrelease> {expl3}{Pre-load expl3}% % \end{macrocode} +% % If we are doing a rollback with a format containing expl3 we % aren't reloading it as that creates havoc. This may need a % refined version! -% \changes{v1.0a}{2020/03/02} -% {Don't load expl3 if already in the format (gh/295)} -% \changes{v1.1}{2020/03/05} -% {Load xparse.ltx if \cs{NewDocumentCommand} is not defined -% by expl3.ltx} % \begin{macrocode} +%<*2ekernel|latexrelease> +%<latexrelease>\IncludeInRelease{2020/10/01}% +%<latexrelease> {expl3}{Pre-load expl3}% \expandafter\ifx\csname tex\string _let:D\endcsname\relax - \IfFileExists{expl3.ltx} - {% - \ifnum0% - \ifdefined\pdffilesize 1\fi - \ifdefined\filesize 1\fi - \ifdefined\luatexversion\ifnum\luatexversion>94 1\fi\fi - >0 % - \expandafter\@firstofone - \else - \message{Skipping expl3-dependent extensions} -%<2ekernel> \expandafter\endinput -%<latexrelease> \expandafter\@gobble - \fi - } - {% - \message{Skipping expl3-dependent extensions}% -%<2ekernel> \endinput -%<latexrelease> \@gobble - }% - {% - \input{expl3.ltx} - \ifdefined\NewDocumentCommand - \else - \IfFileExists{xparse.ltx} - {\input{xparse.ltx}} - {}% - \fi - }% + \expandafter\@firstofone \else - \GenericInfo{}{Skipping: expl3 code already part of the format} + \GenericInfo{}{Skipping: expl3 code already part of the format}% +%<2ekernel> \expandafter\endinput +%<latexrelease> \expandafter\@gobble \fi -%</2ekernel|latexrelease> +% \end{macrocode} +% +% Check for the required primitive/engine support and the existence of +% a loader. +% \begin{macrocode} + {% + \IfFileExists{expl3.ltx} + {% + \ifnum0% + \ifdefined\pdffilesize 1\fi + \ifdefined\filesize 1\fi + \ifdefined\luatexversion\ifnum\luatexversion>94 1\fi\fi + \ifdefined\kanjiskip 1\fi + >0 % + \expandafter\@firstofone + \else +% \end{macrocode} +% +% In \texttt{2ekernel} mode, an error is fatal and building the format +% is aborted. Use \verb=\batchmode \read -1 to \tokenlist=, which errors +% with +% \verb=! Emergency stop. (cannot \read from terminal in nonstop modes)= +% and aborts the \TeX{} run. In \texttt{latexrelease} mode, raise an +% error and do nothing. Both ways, the error message shows the minimum +% \textsf{expl3} engine requirements. +% \begin{macrocode} +%<2ekernel> \def~{ }\def\MessageBreak{^^J~~~~~~~~~~~~~~~}% +%<2ekernel> \errmessage{LaTeX Error: +%<latexrelease> \@latex@error{% + LaTeX requires the e-TeX primitives and additional\MessageBreak + functionality available in the engines:\MessageBreak + - pdfTeX v1.40\MessageBreak + - XeTeX v0.99992\MessageBreak + - LuaTeX v0.95\MessageBreak + - e-(u)pTeX mid-2012\MessageBreak + or later% +%<latexrelease> }\@ehd \expandafter\@gobble +%<2ekernel> }\batchmode \read -1 to \reserved@a + \fi + } + {% +%<*2ekernel> + \errmessage{LaTeX requires expl3}% + \batchmode \read -1 to \reserved@a +%</2ekernel> +%<*latexrelease> + \@latex@error{LaTeX requires expl3}\@ehd + \@gobble +%</latexrelease> + }% + {% + \input expl3.ltx + \ifdefined\NewDocumentCommand + \else + \IfFileExists{xparse.ltx} + {\input xparse.ltx } + {}% + \fi + }% + } +%<latexrelease>\EndIncludeInRelease +%<latexrelease> +%<latexrelease>\IncludeInRelease{2020/02/02}% +%<latexrelease> {expl3}{Pre-load expl3}% +%<latexrelease>\IfFileExists{expl3.ltx} +%<latexrelease> {% +%<latexrelease> \ifnum0% +%<latexrelease> \ifdefined\pdffilesize 1\fi +%<latexrelease> \ifdefined\filesize 1\fi +%<latexrelease> \ifdefined\luatexversion\ifnum\luatexversion>94 1\fi\fi +%<latexrelease> >0 % +%<latexrelease> \else +%<latexrelease> \message{Skipping expl3-dependent extensions} +%<latexrelease> \expandafter\@gobbletwo +%<latexrelease> \fi +%<latexrelease> } +%<latexrelease> {% +%<latexrelease> \message{Skipping expl3-dependent extensions}% +%<latexrelease> \@gobbletwo +%<latexrelease> }% +%<latexrelease>\input{expl3.ltx} %<latexrelease>\EndIncludeInRelease % \end{macrocode} % % \subsection{Using expl3 code} % % \begin{macrocode} -%<*2ekernel|latexrelease> %<latexrelease>\IncludeInRelease{2020/10/01}% %<latexrelease> {\@expl@tl@trim@spaces@apply@@nN} %<latexrelease> {l3 macro for use in include}% @@ -141,7 +242,6 @@ % \end{macrocode} % % \begin{macrocode} -%</2ekernel|latexrelease> %<latexrelease>\EndIncludeInRelease %<latexrelease>\IncludeInRelease{0000/00/00}% %<latexrelease> {\@expl@tl@trim@spaces@apply@@nN} @@ -150,11 +250,7 @@ %<latexrelease>\let\@expl@tl@trim@spaces@apply@@nN\@undefined %<latexrelease> %<latexrelease>\EndIncludeInRelease -%<*2ekernel> -% \end{macrocode} -% -% \begin{macrocode} -%</2ekernel> +%</2ekernel|latexrelease> % \end{macrocode} % % \Finale diff --git a/Master/texmf-dist/source/latex-dev/base/ltfilehook.dtx b/Master/texmf-dist/source/latex-dev/base/ltfilehook.dtx new file mode 100644 index 00000000000..6c16a9832ea --- /dev/null +++ b/Master/texmf-dist/source/latex-dev/base/ltfilehook.dtx @@ -0,0 +1,1161 @@ +% \iffalse meta-comment +% +%% File: ltfilehook.dtx (C) Copyright 2020 Frank Mittelbach, LaTeX Team +% +% It may be distributed and/or modified under the conditions of the +% LaTeX Project Public License (LPPL), either version 1.3c of this +% license or (at your option) any later version. The latest version +% of this license is in the file +% +% https://www.latex-project.org/lppl.txt +% +% +% The development version of the bundle can be found below +% +% https://github.com/FrankMittelbach/... +% +% for those people who are interested or want to report an issue. +% +% \begin{macrocode} +\providecommand\ltfilehookversion{v0.9a} +\providecommand\ltfilehookdate{2020/07/19} +% \end{macrocode} +% +%<*driver> + +\RequirePackage{lthooks} % needed for structedlog if running old format + +\documentclass{l3doc} + +% bug fix fo l3doc.cls +\ExplSyntaxOn +\cs_set_protected:Npn \__codedoc_macro_typeset_one:nN #1#2 + { + \vbox_set:Nn \l__codedoc_macro_box + { + \vbox_unpack_drop:N \l__codedoc_macro_box + \hbox { \llap { \__codedoc_print_macroname:nN {#1} #2 + \MacroFont % <----- without it the \ is in lmr10 if a link is made + \ + } } + } + \int_incr:N \l__codedoc_macro_int + } +\ExplSyntaxOff + +\usepackage{structuredlog} % for demonstration + +\EnableCrossrefs +\CodelineIndex +\begin{document} + \DocInput{ltfilehook.dtx} +\end{document} +%</driver> +% +% \fi +% +% +% \long\def\fmi#1{\begin{quote}\itshape Todo: #1\end{quote}} +% +% \let\hook\texttt +% +% \title{The \texttt{ltfilehook} package\thanks{This package has version +% \ltfilehookversion\ dated \ltfilehookdate, \copyright\ \LaTeX\ +% Project.}} +% +% \author{Frank Mittelbach} +% +% \maketitle +% +% +% +% \tableofcontents +% +% \section{Introduction} +% +% +% +% \subsection{Provided hooks} +% +% The code offers a number of hooks into which packages (or the +% user) can add code to support different use cases. +% Many hooks are offered as pairs (i.e., the second hook is +% reversed. Also important to know is that these pairs are +% properly nested with respect to other pairs of hooks. +% +% There are hooks that are executed for all files of a certain type +% (if they contain code), e.g., for all \enquote{include files} or +% all \enquote{packages}, +% and there are also hooks that are specific to a single file, +% e.g., do something after the package \texttt{foo.sty} has been +% loaded. +% +% +% \subsection{General hooks for file reading} +% \label{sec:general-file-hooks} +% +% There are four hooks that are called for each file that is read +% using document-level commands such as \cs{input}, \cs{include}, +% \cs{usepackage}, etc. They are not called for files read using +% internal low-level methods, such as \cs{@input} or \cs{openin}. +% +% \begin{variable}{file/before,file/before/..., +% file/after/...,file/after, +% } +% These are: +% \begin{description} +% \item[\texttt{file/before}, \texttt{file/before/\meta{file-name}}] +% +% These hooks are executed in that order just before the file is +% loaded for reading. The code of the first hook is used is used +% with every file, while the second is executed only for the +% file with matching \meta{file-name} allowing you to specify +% code that only applies to one file. +% +% \item[\texttt{file/after/\meta{file-name}}, \texttt{file/after}] +% +% These hooks are after the file with name \meta{file-name} has +% been fully consumed. The order is swapped (the specific one +% comes first) so that the \texttt{before} and \texttt{after} +% hooks nest properly, which is important if any of them involve +% grouping (e.g., contain environments, for example). +% furthermore both hooks are reversed hooks to support correct +% nesting of different packages add code to both +% \texttt{/before} and \texttt{/after} hooks. +% +% \end{description} +% \end{variable} +% +% +% So the overall sequence of hook processing for any file read +% through the user interface commands of \LaTeX{} is: +% +% \begin{tabbing} +% mm\=mm\=mm\=mm\=\kill +% \>\cs{UseHook}\marg{\hook{file/before}} \\ +% \>\cs{UseHook}\marg{\hook{file/before/\meta{file name}}} \\ +% \>\> \meta{file contents} \\ +% \>\cs{UseHook}\marg{\hook{file/after/\meta{file name}}} \\ +% \>\cs{UseHook}\marg{\hook{file/after}} +% \end{tabbing} +% +% \fmi{With a higher-level interface that doesn't matter, but it +% is a bit weird, if you use \cs{AddToHook} or \cs{hook_gput:nnn} +% directly, so I guess that has to be done differently!} +% +% The file hooks only refer to the file by its name and extension, +% so the \meta{file name} should be the file name as it is on the +% filesystem with extension (if any) and without paths. Different +% from \cs{input} and similar commands, for hooks the \texttt{.tex} +% extension is not assumed, so \texttt{.tex} files must also be given +% with their extension. +% Files within subfolders should also be addressed by their name and +% extension only. +% +% Extensionless files also work, and should then be given without +% extension. Note however that \TeX{} prioritizes \texttt{.tex} +% files, so if two files \texttt{foo} and \texttt{foo.tex} exist in +% the search path, only the latter will be seen. +% +% When a file is input, the \meta{file name} is available in +% \cs{CurrentFile}, which is then used when accessing the +% \hook{file/before/\meta{file name}} and +% \hook{file/after/\meta{file name}}. +% +% \begin{variable}{\CurrentFile} +% The name of the file about to be read (or just finished) is +% available to the hooks through \cs{CurrentFile} (there is no +% \texttt{expl3} name for it for now). The file is always provided +% with its extension, i.e., how it appears on your hard drive, but +% without any specified path to it. For example, +% \verb=\input{sample}= and \verb=\input{app/sample.tex}= would +% both have \cs{CurrentFile} being \texttt{sample.tex}. +% \end{variable} +% +% \begin{variable}{\CurrentFilePath} +% The path to the current file (complement to \cs{CurrentFile}) is +% available in \cs{CurrentFilePath} if needed. +% The paths returned in \cs{CurrentFilePath} are only user paths, +% given through \cs{input@path} (or \pkg{expl3}'s equivalent +% \cs{l_file_search_path_seq}) or by directly typing in the path +% in the \cs{input} command or equivalent. Files located by +% \texttt{kpsewhich} get the path added internally by the \TeX{} +% implementation, so at the macro level it looks as if the file were +% in the current folder, so the path in \cs{CurrentFilePath} is empty +% in these cases (package and class files, mostly). +% \end{variable} +% +% \subsection{Hooks for package and class files} +% +% Commands to load package and class files (e.g., \cs{usepackage}, +% \cs{RequirePackage}, \cs{LoadPackageWithOptions}, etc.) offer the +% hooks from section~\ref{sec:general-file-hooks} when they are +% used to load a package or class file, e.g., +% \texttt{file/after/array.sty} would be called after the +% \pkg{array} package got loaded. But as packages and classes form as special group +% of files, there are some additional hooks available that only +% apply when a package or class is loaded. +% +% +% \begin{variable}{ +% package/before,package/after, +% package/before/...,package/after/..., +% class/before,class/after, +% class/before/...,class/after/..., +% } +% These are: +% \begin{description} +% \item[\texttt{package/before}, \texttt{package/after}] +% +% These hooks are called for each package being loaded. +% +% \item[\texttt{package/before/\meta{name}}, +% \texttt{package/after/\meta{name}}] +% +% These hooks are additionally called if the package name is +% \meta{name} (without extension). +% +% \item[\texttt{class/before}, \texttt{class/after}] +% +% These hooks are called for each class being loaded. +% +% \item[\texttt{class/before/\meta{name}}, \texttt{class/after/\meta{name}}] +% +% These hooks are additionally called if the class name is +% \meta{name} (without extension). +% +% \end{description} +% \end{variable} +% All \hook{/after} hooks are implemented as reversed hooks. +% +% \noindent The overall sequence of execution for \cs{usepackage} +% and friends is therefore: +% \begin{tabbing} +% mm\=mm\=mm\=mm\=\kill +% \>\cs{UseHook}\marg{\hook{package/before}} \\ +% \>\cs{UseHook}\marg{\hook{package/before/\meta{package name}}} \\[5pt] +% \>\>\cs{UseHook}\marg{\hook{file/before}} \\ +% \>\>\cs{UseHook}\marg{\hook{file/before/\meta{package name}.sty}} \\ +% \>\>\> \meta{package contents} \\ +% \>\>\cs{UseHook}\marg{\hook{file/after/\meta{package name}.sty}} \\ +% \>\>\cs{UseHook}\marg{\hook{file/after}} \\[5pt] +% \>\>\emph{code from \cs{AtEndOfPackage} if +% used inside the package} \\[5pt] +% \>\cs{UseHook}\marg{\hook{package/after/\meta{package name}}} \\ +% \>\cs{UseHook}\marg{\hook{package/after}} +% \end{tabbing} +% and similar for class file loading, except that \texttt{package/} +% is replaced by \texttt{class/} and \cs{AtEndOfPackage} by +% \cs{AtEndOfClass}. +% +% If a package or class is not loaded (or it was loaded before the +% hooks were set) none of the hooks are executed! +% +% \subsection{Hooks for \cs{include} files} +% +% To manage \cs{include} files, \LaTeX{} issues a \cs{clearpage} +% before and after loading such a file. Depending on the use case +% one may want to execute code before or after these +% \cs{clearpage}s especially for the one that is issued at the end. +% +% Executing code before the final \cs{clearpage}, means that the +% code is processed while the last page of the included material is +% still under construction. Executing code after it means that all +% floats from inside the include file are placed (which +% might have added further pages) and the final page has finished. +% +% Because of these different scenarios we offer hooks in three +% places.\footnote{If you want to execute code before the first +% \cs{clearpage} there is no need to use a hook---you can write it +% directly in front of the \cs{include}.} +% None of the hooks are executed when an \cs{include} file is +% bypassed because of an \cs{includeonly} declaration. They are, +% however, all executed if \LaTeX{} makes an attempt to load the +% \cs{include} file (even if it doesn't exist and all that happens +% is \enquote{\texttt{No file \meta{filename}.tex}}). +% +% +% \begin{variable}{include/before,include/before/..., +% include/end,include/end/..., +% include/after,include/after/..., +% } +% These are: +% \begin{description} +% +% \item[\texttt{include/before}, \texttt{include/before/\meta{name}}] +% +% These hooks are executed one after another after the initial +% \cs{clearpage} and after \texttt{.aux} file is changed to use +% \texttt{\meta{name}.aux}, but before the +% \texttt{\meta{name}.tex} file is loaded. In other words they are executed +% at the very beginning of the first page of the \cs{include} +% file. +% +% +% \item[\texttt{include/end/\meta{name}}, \texttt{include/end}] +% +% These hooks are executed (in that order) after \LaTeX{} has +% stopped reading from the \cs{include} file, but before it has +% issued a \cs{clearpage} to output any deferred floats. +% +% +% \item[\texttt{include/after/\meta{name}}, \texttt{include/after}] +% +% These hooks are executed (in that order) after \LaTeX{} has +% issued the \cs{clearpage} but before is has switched back +% writing to the main \texttt{.aux} file. Thus technically we are +% still inside the \cs{include} and if the hooks generate any +% further typeset material including anything that writes to the +% \texttt{.aux} file, then it would be considered part of the +% included material and bypassed if it is not loaded because of +% some \cs{includeonly} statement.\footnotemark +% +% \end{description} +% \end{variable}\footnotetext{For that reason +% another \cs{clearpage} is executed after these hooks which +% normally does nothing, but starts a new page if further material +% got added this way.} +% +% \subsection{High-level interfaces for \LaTeX{}} +% +% We do not provide any high-level \LaTeX{} commands (like +% \pkg{filehook} or \pkg{scrlfile} do) but think that for package +% writers the commands from for hook management are sufficient. +% +% \subsection{A sample package for structuring the log output} +% +% As an application we provide the package \pkg{structuredlog} that +% adds lines to the \texttt{.log} when a file is opened and closed +% for reading keeping track of nesting level es well. +% For example, for the current document it adds the lines +%\begin{verbatim} +% = (LEVEL 1 START) t1lmr.fd +% = (LEVEL 1 STOP) t1lmr.fd +% = (LEVEL 1 START) supp-pdf.mkii +% = (LEVEL 1 STOP) supp-pdf.mkii +% = (LEVEL 1 START) nameref.sty +% == (LEVEL 2 START) refcount.sty +% == (LEVEL 2 STOP) refcount.sty +% == (LEVEL 2 START) gettitlestring.sty +% == (LEVEL 2 STOP) gettitlestring.sty +% = (LEVEL 1 STOP) nameref.sty +% = (LEVEL 1 START) ltfilehook-doc.out +% = (LEVEL 1 STOP) ltfilehook-doc.out +% = (LEVEL 1 START) ltfilehook-doc.out +% = (LEVEL 1 STOP) ltfilehook-doc.out +% = (LEVEL 1 START) ltfilehook-doc.hd +% = (LEVEL 1 STOP) ltfilehook-doc.hd +% = (LEVEL 1 START) ltfilehook.dtx +% == (LEVEL 2 START) ot1lmr.fd +% == (LEVEL 2 STOP) ot1lmr.fd +% == (LEVEL 2 START) omllmm.fd +% == (LEVEL 2 STOP) omllmm.fd +% == (LEVEL 2 START) omslmsy.fd +% == (LEVEL 2 STOP) omslmsy.fd +% == (LEVEL 2 START) omxlmex.fd +% == (LEVEL 2 STOP) omxlmex.fd +% == (LEVEL 2 START) umsa.fd +% == (LEVEL 2 STOP) umsa.fd +% == (LEVEL 2 START) umsb.fd +% == (LEVEL 2 STOP) umsb.fd +% == (LEVEL 2 START) ts1lmr.fd +% == (LEVEL 2 STOP) ts1lmr.fd +% == (LEVEL 2 START) t1lmss.fd +% == (LEVEL 2 STOP) t1lmss.fd +% = (LEVEL 1 STOP) ltfilehook.dtx +%\end{verbatim} +% Thus if you inspect an issue in the \texttt{.log} it is easy to +% figure out in which file it occurred, simply by searching back for +% \texttt{LEVEL} and if it is a \texttt{STOP} then remove 1 from +% the level value and search further for \texttt{LEVEL} with that value +% which should then be the \texttt{START} level of the file you are in. +% +% \StopEventually{\setlength\IndexMin{200pt} \PrintIndex } +% +% +% \section{The Implementation} +% \begin{macrocode} +%<*2ekernel> +% \end{macrocode} +% +% \subsection{\pkg{expl3} helpers} +% +% \begin{macrocode} +%<@@=filehook> +% \end{macrocode} +% +% \begin{macro}{\CurrentFile,\CurrentFilePath} +% User-level macros that hold the current file name and file path. +% These are used internally as well because the code takes care to +% protect against a possible redefinition of these macros in the loaded +% file (it's necessary anyway to make hooks work with nested +% \cs{input}). +% \begin{macrocode} +\def\CurrentFile{} +\def\CurrentFilePath{} +% \end{macrocode} +% \end{macro} +% +% \begin{macro}{\l_@@_internal_tl} +% \begin{macro}{\@filehook@set@curr@file,\@@_normalise_file_name:n} +% When inputting a file, \cs{@filehook@set@curr@file} does a file +% lookup (in \cs{input@path} and \cs{l_file_search_path_seq}) and +% returns the actual file name (\meta{base} plus \meta{ext}) in +% \cs{CurrentFile}. Only the base and extension are returned, +% regardless of the input (both \texttt{path/to/file.tex} and +% \texttt{file.tex} end up as \texttt{file.tex} in \cs{CurrentFile}). +% The path is returned in \cs{CurrentFilePath}, in case it's needed. +% \cs{CurrentFile} is then used to run the file hooks with +% \texttt{file/before/\cs{CurrentFile}} and +% \texttt{file/after/\cs{CurrentFile}}. +% \begin{macrocode} +\ExplSyntaxOn +\tl_new:N \l_@@_internal_tl +\cs_new_protected:Npn \@filehook@set@curr@file #1 + { \exp_args:NV \@@_normalise_file_name:n #1 } +\cs_new_protected:Npn \@@_normalise_file_name:n #1 + { + \file_if_exist:nTF {#1} + { + \exp_args:Nx \file_parse_full_name:nNNN + { \file_full_name:n {#1} } + } + { \file_parse_full_name:nNNN {#1} } + \CurrentFilePath \CurrentFile \l_@@_internal_tl + \tl_set:Nx \CurrentFile { \CurrentFile \l_@@_internal_tl } + } +% \end{macrocode} +% \end{macro} +% \end{macro} +% +% \begin{macro}{\g_@@_input_file_seq} +% \begin{macro}{\@filehook@file@push,\@filehook@file@pop} +% \begin{macro}{\@@_file_pop_assign:nn} +% Yet another stack, to keep track of \cs{CurrentFile} and +% \cs{CurrentFilePath} with nested \cs{input}s. At the beginning of +% \cs{InputIfFileExists}, the current value of \cs{CurrentFilePath} +% and \cs{CurrentFile} is pushed to \cs{g_@@_input_file_seq}, and +% at the end, it is popped and the value reassigned. +% \cs{IfFileExists} does \cs{set@curr@file} internally, which changes +% \cs{CurrentFile}, so \cs{@filehook@file@push} has to be executed +% before \cs{IfFileExists}. +% \begin{macrocode} +\seq_new:N \g_@@_input_file_seq +\cs_new_protected:Npn \@filehook@file@push + { + \seq_gpush:Nx \g_@@_input_file_seq + { { \CurrentFilePath } { \CurrentFile } } + } +\cs_new_protected:Npn \@filehook@file@pop + { + \seq_gpop:NNTF \g_@@_input_file_seq \l_@@_internal_tl + { \exp_after:wN \@@_file_pop_assign:nn \l_@@_internal_tl } + { \ERROR_should_not_happen } + } +\cs_new_protected:Npn \@@_file_pop_assign:nn #1 #2 + { + \tl_set:Nn \CurrentFilePath {#1} + \tl_set:Nn \CurrentFile {#2} + } +\ExplSyntaxOff +% \end{macrocode} +% \end{macro} +% \end{macro} +% \end{macro} +% +% +% \subsection{Declaring the file-related hooks} +% +% All hooks starting with \texttt{file/} \texttt{include/}, +% \texttt{class/} or \texttt{package/} are generic and will be +% allocated if code is added to them. Thus there is no need to +% explicitly declare any hook in the code below. +% +% Furthermore, those named \texttt{.../after} or \texttt{.../end} are +% automatically declared as reversed hooks if filled with code, so this +% is also automatically taken care of. +% +% \subsection{Patching \LaTeX{} commands (need proper integration later)} +% +% Most of what we have to do is adding \cs{UseHook} into several +% \LaTeXe{} core commands, which is done for now by patching them. +% \begin{macrocode} +%<@@=> +% \end{macrocode} +% +% \begin{macro}{\InputIfFileExists} +% \cs{InputIfFileExists} loads any file if it is available so we +% have to add the hooks \texttt{file/before} and +% \texttt{file/after} in the right places. If the file doesn't +% exist no hooks should be executed. +% \begin{macrocode} +\let\InputIfFileExists\@undefined +\DeclareRobustCommand \InputIfFileExists[3]{% + \@filehook@file@push + \IfFileExists{#1}% + {% +% \end{macrocode} +% If the file exists then \cs{@curr@file} holds its name. But we +% can't rely on that still being true after the file has been +% processed. Thus for using the name in the file hooks we need to +% preserve the name and then restored it for the +% \texttt{file/after/...} hook. +% +% The hook always refers to the \emph{actual} file that will be +% operated on, regardless of how the user had it written in the +% document. \pkg{expl3}'s \cs{file_full_name:n} normalizes the file +% name (to factor out differences in the |.tex| extension), and +% then does a file lookup to take into account a possible path from +% \cs{l_file_search_path_seq} and \cs{input@path}. However only +% the file name and extension are returned so that file hooks can +% refer to the file by their name only. The path to the file is +% returned in \cs{CurrentFilePath}. +% \begin{macrocode} + \edef\reserved@a{\@filef@und + \def\noexpand\CurrentFile{\CurrentFile}% + \def\noexpand\CurrentFilePath{\CurrentFilePath}% + }% + \expandafter\@swaptwoargs\expandafter + {\reserved@a}% + {% + #2% + \@addtofilelist{#1}% + \UseHook{file/before}% +% \end{macrocode} +% The current file name is available in \cs{@curr@file} so we use +% that in the specific hook. +% \begin{macrocode} + \UseHook{file/before/\CurrentFile}% + \@@input + }% +% \end{macrocode} +% And it is restored here so we can use it once more. +% \begin{macrocode} + \UseHook{file/after/\CurrentFile}% + \UseHook{file/after}% + \@filehook@file@pop + }% + {\@filehook@file@pop #3}% +} +% \end{macrocode} +% \end{macro} +% +% \begin{macro}{\set@curr@file} +% Now we just hook into \cs{set@curr@file} to add +% \cs{@filehook@set@curr@file} at the end, after \cs{@curr@file} is +% set. +% \begin{macrocode} +\def\set@curr@file#1{% + \begingroup + \escapechar\m@ne + \xdef\@curr@file{% + \expandafter\expandafter\expandafter\unquote@name + \expandafter\expandafter\expandafter{% + \expandafter\string + \csname\@firstofone#1\@empty\endcsname}}% + \endgroup + \@filehook@set@curr@file{\@curr@file}% +} +% \end{macrocode} +% \end{macro} +% +% \begin{macro}{\load@onefilewithoptions} +% This macro is used when loading packages or classes. +% \begin{macrocode} +\def\load@onefilewithoptions#1[#2][#3]#4{% + \@pushfilename + \xdef\@currname{#1}% + \global\let\@currext#4% + \let\CurrentOption\@empty + \@reset@ptions + \makeatletter + \def\reserved@a{% + \@ifl@aded\@currext{#1}% + {\@if@ptions\@currext{#1}{#2}{}% + {\@latex@error + {Option clash for \@cls@pkg\space #1}% + {The package #1 has already been loaded + with options:\MessageBreak + \space\space[\@ptionlist{#1.\@currext}]\MessageBreak + There has now been an attempt to load it + with options\MessageBreak + \space\space[#2]\MessageBreak + Adding the global options:\MessageBreak + \space\space + \@ptionlist{#1.\@currext},#2\MessageBreak + to your \noexpand\documentclass declaration may fix this.% + \MessageBreak + Try typing \space <return> \space to proceed.}}}% + {\@pass@ptions\@currext{#2}{#1}% + \global\expandafter + \let\csname ver@\@currname.\@currext\endcsname\@empty + \expandafter\let\csname\@currname.\@currext-h@@k\endcsname\@empty + \InputIfFileExists + {\@currname.\@currext}% + {% +%----------------------------------------- +% \end{macrocode} +% When the current extension is \cs{@pkgextension} we are loading a +% package otherwise, if it is \cs{@clsextension}, a class, so +% depending on that we execute different hooks. If the extension is +% neither, then it is another type of file without special hooks. +% \begin{macrocode} + \ifx\@currext\@pkgextension + \UseHook{package/before}% + \UseHook{package/before/\@currname}% + \else + \ifx\@currext\@clsextension + \UseHook{class/before}% + \UseHook{class/before/\@currname}% + \fi + \fi +% \end{macrocode} +% The value of \cs{CurrentFile} holds during \cs{InputIfFileExists}, +% so the hooks above have that available for using. However at this +% point \cs{CurrentFile} is reset to its previous value by +% \cs{@filehook@file@pop} because it doesn't know that we'll have +% more hooks ahead. So here (still in the \meta{true} branch of +% \cs{InputIfFileExists}, right after actually reading the file in) +% we'll cheat: use \cs{@filehook@file@push} once more, so there are +% two entries for the current file in the name stack, so that when +% \cs{InputIfFileExists} pops it, there's still one (identical) left. +% \begin{macrocode} + \@filehook@file@push +%----------------------------------------- + }% + {\@missingfileerror\@currname\@currext}% + \let\@unprocessedoptions\@@unprocessedoptions + \csname\@currname.\@currext-h@@k\endcsname + \expandafter\let\csname\@currname.\@currext-h@@k\endcsname + \@undefined +%----------------------------------------- +% \end{macrocode} +% And same procedure, James, when we are finished loading, except +% that the hook order is now reversed. +% \begin{macrocode} + \ifx\@currext\@pkgextension + \UseHook{package/after/\@currname}% + \UseHook{package/after}% + \else + \ifx\@currext\@clsextension + \UseHook{class/after/\@currname}% + \UseHook{class/after}% + \fi + \fi +% \end{macrocode} +% Now here we do \cs{@filehook@file@pop} to restore the +% \cs{CurrentFile} before this file being loaded and fix what we've +% done in the stack right above. +% \begin{macrocode} + \@filehook@file@pop +%----------------------------------------- + \@unprocessedoptions}% + \@ifl@ter\@currext{#1}{#3}{}% + {\@latex@warning@no@line + {You have requested,\on@line, + version\MessageBreak + `#3' of \@cls@pkg\space #1,\MessageBreak + but only version\MessageBreak + `\csname ver@#1.\@currext\endcsname'\MessageBreak + is available}}% + \ifx\@currext\@clsextension\let\LoadClass\@twoloadclasserror\fi + \@popfilename + \@reset@ptions}% + \reserved@a} +% \end{macrocode} +% +% The code for this macro has changed between 2020/02/02 and +% 2020/10/01 so the never version is this: +% \begin{macrocode} +\@ifl@t@r\fmtversion{2020/10/01} +{% +\def\load@onefilewithoptions#1[#2][#3]#4{% + \@pushfilename + \xdef\@currname{#1}% + \global\let\@currext#4% + \let\CurrentOption\@empty + \@reset@ptions + \makeatletter + \def\reserved@a{% + \@ifl@aded\@currext{#1}% + {\@if@ptions\@currext{#1}{#2}{}% + {\@latex@error + {Option clash for \@cls@pkg\space #1}% + {The package #1 has already been loaded + with options:\MessageBreak + \space\space[\@ptionlist{#1.\@currext}]\MessageBreak + There has now been an attempt to load it + with options\MessageBreak + \space\space[#2]\MessageBreak + Adding the global options:\MessageBreak + \space\space + \@ptionlist{#1.\@currext},#2\MessageBreak + to your \noexpand\documentclass declaration may fix this.% + \MessageBreak + Try typing \space <return> \space to proceed.}}}% + {\@pass@ptions\@currext{#2}{#1}% + \global\expandafter + \let\csname ver@\@currname.\@currext\endcsname\@empty + \expandafter\let\csname\@currname.\@currext-h@@k\endcsname\@empty + \InputIfFileExists + {\@currname.\@currext}% + {% +%----------------------------------------- + \ifx\@currext\@pkgextension + \UseHook{package/before}% + \UseHook{package/before/\@currname}% + \else + \ifx\@currext\@clsextension + \UseHook{class/before}% + \UseHook{class/before/\@currname}% + \fi + \fi + \@filehook@file@push +%----------------------------------------- + }% + {\@missingfileerror\@currname\@currext}% + \expandafter\let\csname unprocessedoptions-\@currname.\@currext\endcsname + \@@unprocessedoptions + \csname\@currname.\@currext-h@@k\endcsname + \expandafter\let\csname\@currname.\@currext-h@@k\endcsname + \@undefined + \ifx\@unprocessedoptions\relax + \let\@unprocessedoptions\@undefined + \else + \csname unprocessedoptions-\@currname.\@currext\endcsname + \fi + \expandafter\let + \csname unprocessedoptions-\@currname.\@currext\endcsname + \@undefined +%----------------------------------------- + \ifx\@currext\@pkgextension + \UseHook{package/after/\@currname}% + \UseHook{package/after}% + \else + \ifx\@currext\@clsextension + \UseHook{class/after/\@currname}% + \UseHook{class/after}% + \fi + \fi + \@filehook@file@pop +%----------------------------------------- + }% + \@ifl@ter\@currext{#1}{#3}{}% + {\@latex@warning@no@line + {You have requested,\on@line, + version\MessageBreak + `#3' of \@cls@pkg\space #1,\MessageBreak + but only version\MessageBreak + `\csname ver@#1.\@currext\endcsname'\MessageBreak + is available}}% + \ifx\@currext\@clsextension\let\LoadClass\@twoloadclasserror\fi + \@popfilename + \@reset@ptions}% + \reserved@a} +}{}% +% \end{macrocode} +% \end{macro} + + + + +% \begin{macro}{\@include} +% \begin{macrocode} +\def\@include#1 {% + \clearpage + \if@filesw + \immediate\write\@mainaux{\string\@input{#1.aux}}% + \fi + \@tempswatrue + \if@partsw + \@tempswafalse + \edef\reserved@b{#1}% + \@for\reserved@a:=\@partlist\do + {\ifx\reserved@a\reserved@b\@tempswatrue\fi}% + \fi + \if@tempswa + \let\@auxout\@partaux + \if@filesw + \immediate\openout\@partaux #1.aux + \immediate\write\@partaux{\relax}% + \fi +%----------------------------------------- +% \end{macrocode} +% First we need to fix \cs{CurrentFile}, because due the pesky +% space-delimited \cs{@include}, \cs{CurrentFile} contains the +% \cs{include}'d file with a space. To fix that, we re-do +% \cs{set@curr@file}. The 2020/10/01 release doesn't need this as +% \cs{include} was changed to do \cs{set@curr@file} on the correct +% file name, rather than one with a trailing space. +% \begin{macrocode} + \set@curr@file{#1}% +% \end{macrocode} +% Execute the \texttt{before} hooks just after we switched the +% \texttt{.aux} file \ldots +% \begin{macrocode} + \UseHook{include/before}% + \UseHook{include/before/#1}% +%----------------------------------------- + \@input@{#1.tex}% +% \end{macrocode} +% \ldots{} then \texttt{end} hooks \ldots +% \begin{macrocode} +%----------------------------------------- + \UseHook{include/end/#1}% + \UseHook{include/end}% +%----------------------------------------- + \clearpage +% \end{macrocode} +% \ldots{} and after the \cs{clearpage} the \texttt{after} hooks +% followed by another \cs{clearpage} just in case new material got +% added (after all we need to be in well defined state after the +% \cs{include}). +% \begin{macrocode} +%----------------------------------------- + \UseHook{include/after/#1}% + \UseHook{include/after}% +% \end{macrocode} +% The additional \cs{clearpage} is needed to ensure that switching +% the \texttt{.aux} files happen at a defined point even if the +% above hooks add further material. +% \begin{macrocode} + \clearpage +%----------------------------------------- + \@writeckpt{#1}% + \if@filesw + \immediate\closeout\@partaux + \fi + \else + \deadcycles\z@ + \@nameuse{cp@#1}% + \fi + \let\@auxout\@mainaux +} +% \end{macrocode} +% +% The code for this macro has changed between 2020/02/02 and +% 2020/10/01 so the never version is this: +% \begin{macrocode} +\@ifl@t@r\fmtversion{2020/10/01} +{% +\def\@include#1 {% + \clearpage + \if@filesw + \immediate\write\@mainaux{\string\@input{"#1.aux"}}% + \fi + \@tempswatrue + \if@partsw + \@tempswafalse + \edef\reserved@b{#1}% + \@for\reserved@a:=\@partlist\do + {\ifx\reserved@a\reserved@b\@tempswatrue\fi}% + \fi + \if@tempswa + \let\@auxout\@partaux + \if@filesw + \immediate\openout\@partaux "#1.aux" + \immediate\write\@partaux{\relax}% + \fi +%----------------------------------------- + \UseHook{include/before}% + \UseHook{include/before/#1}% +%----------------------------------------- + \@input@{#1.tex}% +%----------------------------------------- + \UseHook{include/end/#1}% + \UseHook{include/end}% +%----------------------------------------- + \clearpage +%----------------------------------------- + \UseHook{include/after/#1}% + \UseHook{include/after}% + \clearpage +%----------------------------------------- + \@writeckpt{#1}% + \if@filesw + \immediate\closeout\@partaux + \fi + \else + \deadcycles\z@ + \@nameuse{cp@#1}% + \fi + \let\@auxout\@mainaux} +}{} +% \end{macrocode} +% \end{macro} +% +% +% +% \subsection{High-level interfaces for \LaTeX{}} +% +% None so far and the general feeling for now is that the hooks are +% enough. Packages like \pkg{filehook}, etc., may use them to set +% up their interfaces (samples are given below) but for the now the +% kernel will not provide any. +% +% +% +% \begin{macrocode} +%</2ekernel> +% \end{macrocode} +% +% +% +% \section{Package emulation for compatibility} +% +% +% \subsection{Package \pkg{filehook} emulation} +% +% This is a partial implementation of the \pkg{filehook} +% interfaces. It is only meant for guidance in case that package +% gets updated to use the hook management. +% +% Not implemented are: +%\begin{verbatim} +% \AtBeginOfFiles +% \AtEndOfFiles +% \AtBeginOfInputs +% \AtEndOfInputs +% \AtBeginOfInputFile +% \AtEndOfInputFile +%\end{verbatim} +% +% \begin{macrocode} +%<*filehook-draft> +% \end{macrocode} +% +% \begin{macrocode} +\newcommand\AtBeginOfEveryFile [1] + {\AddToHook{file/before}{#1}} +\newcommand\AtEndOfEveryFile [1] + {\AddToHook{file/after}{#1}} +% \end{macrocode} +% +% \begin{macrocode} +\newcommand\AtBeginOfIncludes [1] + {\AddToHook{include/before}{#1}} +\newcommand\AtEndOfIncludes [1] + {\AddToHook{include/end}{#1}} +\newcommand\AfterIncludes [1] + {\AddToHook{include/after}{#1}} +% \end{macrocode} +% +% \begin{macrocode} +\newcommand\AtBeginOfPackages [1] + {\AddToHook{package/before}{#1}} +\newcommand\AtEndOfPackages [1] + {\AddToHook{package/after}{#1}} +% \end{macrocode} +% +% \begin{macrocode} +\newcommand\AtBeginOfClasses [1] + {\AddToHook{class/before}{#1}} +\newcommand\AtEndOfClasses [1] + {\AddToHook{class/after}{#1}} +% \end{macrocode} +% +% For normal files we drop the \texttt{.tex} extension for now: +% \begin{macrocode} +\newcommand\AtBeginOfFile [2] + {\AddToHook{file/before/#1}{#2}} +\newcommand\AtEndOfFile [2] + {\AddToHook{file/after/#1}{#2}} +% \end{macrocode} +% +% \begin{macrocode} +\DeclareDocumentCommand \AtBeginOfPackageFile {smm} + {\IfBooleanTF{#1}% + {\@ifpackageloaded{#2}% + {#3}% + {\AddToHook{package/before/#2}{#3}}}% + {\AddToHook{package/before/#2}{#3}}% + } +\DeclareDocumentCommand \AtEndOfPackageFile {smm} + {\IfBooleanTF{#1}% + {\@ifpackageloaded{#2}% + {#3}% + {\AddToHook{package/after/#2}{#3}}}% + {\AddToHook{package/after/#2}{#3}}% + } +% \end{macrocode} +% +% Are the * forms here of any use? I know they are use 3--4 times +% on CTAN but I wonder if those are real or mistaken usages. +% \begin{macrocode} +\DeclareDocumentCommand \AtBeginOfClassFile {smm} + {\IfBooleanTF{#1}% + {\@ifclassloaded{#2}% + {#3}% + {\AddToHook{class/before/#2}{#3}}}% + {\AddToHook{class/before/#2}{#3}}% + } +\DeclareDocumentCommand \AtEndOfClassFile {smm} + {\IfBooleanTF{#1}% + {\@ifclassloaded{#2}% + {#3}% + {\AddToHook{class/after/#2}{#3}}}% + {\AddToHook{class/after/#2}{#3}}% + } +% \end{macrocode} +% +% \begin{macrocode} +\newcommand\AtBeginOfIncludeFile [2] + {\AddToHook{include/before/#1}{#2}} +\newcommand\AtEndOfIncludeFile [2] + {\AddToHook{include/end/#1}{#2}} +\newcommand\AfterIncludeFile [2] + {\AddToHook{include/after/#1}{#2}} +% \end{macrocode} +% +% +% This is missing some interfaces so disabling the package isn't +% really correct, but then this code above is not supposed to stay +% like this anyway. +% \begin{macrocode} +\expandafter\let\csname ver@filehook.sty\endcsname\fmtversion +\@namedef {ver@filehook.sty}{2020/10/01} +% \end{macrocode} +% +% +% \begin{macrocode} +%</filehook-draft> +% \end{macrocode} +% +% +% +% +% \subsection{Package \pkg{scrlfile} emulation} +% +% This is a partial implementation of the \pkg{scrlfile} +% interfaces. It is only meant for guidance in case that package +% gets updated to use the hook management. +% +% \begin{macrocode} +%<*scrlfile-draft> +% \end{macrocode} +% +% I think this is roughly correct (using the \texttt{file/...} +% hooks rather than the class or package hooks at least for the +% \cs{After...} commands but it needs some further verification. +% +% The star and plus variants haven't been implemented so far, this +% is only a rough draft. +% \begin{macrocode} +\newcommand\BeforeClass[2] + {\AddToHook{file/before/#1.cls}{#2}} +\newcommand\AfterClass [2] + {\AddToHook{file/after/#1.cls}{#2}} +\newcommand\AfterAtEndOfClass [2] + {\AddToHook{class/after/#1}{#2}} +% \end{macrocode} +% +% \begin{macrocode} +\newcommand\BeforePackage [2] + {\AddToHook{package/before/#1.sty}{#2}} +\newcommand\AfterPackage [2] + {\AddToHook{file/after/#1.sty}{#2}} +\newcommand\AfterEndOfPackage [2] + {\AddToHook{package/after/#1}{#2}} +% \end{macrocode} +% +% \begin{macrocode} +\newcommand\BeforeFile [2] + {% + \typeout{BeforeFile: #1!!!}% + \AddToHook{file/before/#1}{#2}} +\newcommand\AfterFile [2] + {% + \typeout{AfterFile: #1!!!}% + \AddToHook{file/after/#1}{#2}} +% \end{macrocode} +% +% This is missing some interfaces so disabling the package isn't +% really correct, but then this code above is not supposed to stay +% like this anyway. +% \begin{macrocode} +\expandafter\let\csname ver@scrlfile.sty\endcsname\fmtversion +\@namedef {ver@scrlfile.sty}{2020/10/01} +% \end{macrocode} +% +% \begin{macrocode} +%</scrlfile-draft> +% \end{macrocode} +% +% +% +% \section{A sample package for structuring the log output} +% +% \begin{macrocode} +%<*structuredlog> +%<@@=filehook> +% \end{macrocode} +% +% \begin{macrocode} +\ProvidesExplPackage + {structuredlog}{\ltfilehookdate}{\ltfilehookversion} + {Structuring the TeX transcript file} +% \end{macrocode} + + + +% \begin{macrocode} +\int_new:N \g_@@_nesting_level_int +% \end{macrocode} + +% \begin{macrocode} +\tl_new:N \g_@@_nesting_prefix_tl +\tl_gset:Nn \g_@@_nesting_prefix_tl { } +% \end{macrocode} +% +% \begin{macrocode} +\AddToHook{file/before}{ + \int_gincr:N \g_@@_nesting_level_int + \tl_gput_right:Nn\g_@@_nesting_prefix_tl {=} + \iow_term:x { + \g_@@_nesting_prefix_tl \space + ( LEVEL~ \int_use:N \g_@@_nesting_level_int \space START )~ + \CurrentFile ^^J + } +} +% \end{macrocode} +% We don't want to install the \hook{file/after} hook immediately, +% because that would mean it is the first time executed when the +% package finishes. We therefore put the declaration inside +% \cs{AddToHookNext} so that it gets only installed when we have +% left the package. +% \begin{macrocode} +\AddToHookNext{file/after}{ + \AddToHook{file/after}{ + \iow_term:x { + \g_@@_nesting_prefix_tl \space + ( LEVEL~ \int_use:N \g_@@_nesting_level_int \space STOP )~ + \CurrentFile ^^J + } + \int_gdecr:N \g_@@_nesting_level_int + \tl_gset:Nx \g_@@_nesting_prefix_tl + {\exp_after:wN \use_none:n \g_@@_nesting_prefix_tl} + } +} +% \end{macrocode} +% +% We have to manually increment the level because now that we have +% installed the code in \texttt{file/after} it gets decremented +% when we leave the package without ever being incremented upon +% entry. +% \begin{macrocode} +%\int_incr:N\g_@@_nesting_level_int +% \end{macrocode} +% +% \begin{macrocode} +%</structuredlog> +% \end{macrocode} + +% \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 8e9611d9180..60b6a23e5c2 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} - [2020-05-05 v1.2g LaTeX Kernel (File Handling)] + [2020-06-05 v1.2h LaTeX Kernel (File Handling)] % \iffalse \documentclass{ltxdoc} \GetFileInfo{ltfiles.dtx} @@ -234,14 +234,18 @@ % \changes{v0.9e}{1993/12/09}{Hook added} % \begin{macrocode} %</2ekernel> -%<latexrelease>\IncludeInRelease{2017/04/15}% -%<latexrelease> {\document}{Save language for hyphenation}% +%<latexrelease>\IncludeInRelease{2020/06/05}% +%<latexrelease> {\document}{Added hook to load l3backend code}% %<*2ekernel|latexrelease> % \end{macrocode} % Cancel the |\begingroup| from |\begin|. % \begin{macrocode} \def\document{\endgroup % \end{macrocode} +% \changes{v1.2h}{2020/06/05}{Added hook to load \textsf{l3backend} code} +% \begin{macrocode} + \@expl@sys@load@backend@@ +% \end{macrocode} % If some options on |\documentclass| haven't been used by any % package we will now give a warning since this is most certainly a % misspelling. @@ -406,6 +410,64 @@ \ignorespaces} %</2ekernel|latexrelease> %<latexrelease>\EndIncludeInRelease +%<latexrelease> +%<latexrelease>\IncludeInRelease{2017/04/15}% +%<latexrelease> {\document}{Save language for hyphenation}% +%<latexrelease>\def\document{\endgroup +%<latexrelease> \ifx\@unusedoptionlist\@empty\else +%<latexrelease> \@latex@warning@no@line{Unused global option(s):^^J% +%<latexrelease> \@spaces[\@unusedoptionlist]}% +%<latexrelease> \fi +%<latexrelease> \@colht\textheight +%<latexrelease> \@colroom\textheight \vsize\textheight +%<latexrelease> \columnwidth\textwidth +%<latexrelease> \@clubpenalty\clubpenalty +%<latexrelease> \if@twocolumn +%<latexrelease> \advance\columnwidth -\columnsep +%<latexrelease> \divide\columnwidth\tw@ \hsize\columnwidth \@firstcolumntrue +%<latexrelease> \fi +%<latexrelease> \hsize\columnwidth \linewidth\hsize +%<latexrelease> \begingroup\@floatplacement\@dblfloatplacement +%<latexrelease> \makeatletter\let\@writefile\@gobbletwo +%<latexrelease> \global \let \@multiplelabels \relax +%<latexrelease> \@input{\jobname.aux}% +%<latexrelease> \endgroup +%<latexrelease> \if@filesw +%<latexrelease> \immediate\openout\@mainaux\jobname.aux +%<latexrelease> \immediate\write\@mainaux{\relax}% +%<latexrelease> \fi +%<latexrelease> \process@table +%<latexrelease> \let\glb@currsize\@empty % Force math initialization. +%<latexrelease> \normalsize +%<latexrelease> \everypar{}% +%<latexrelease> \ifx\normalsfcodes\@empty +%<latexrelease> \ifnum\sfcode`\.=\@m +%<latexrelease> \let\normalsfcodes\frenchspacing +%<latexrelease> \else +%<latexrelease> \let\normalsfcodes\nonfrenchspacing +%<latexrelease> \fi +%<latexrelease> \fi +%<latexrelease> \ifx\document@default@language\m@ne +%<latexrelease> \chardef\document@default@language\language +%<latexrelease> \fi +%<latexrelease> \@noskipsecfalse +%<latexrelease> \let \@refundefined \relax +%<latexrelease> \let\AtBeginDocument\@firstofone +%<latexrelease> \@begindocumenthook +%<latexrelease> \ifdim\topskip<1sp\global\topskip 1sp\relax\fi +%<latexrelease> \global\@maxdepth\maxdepth +%<latexrelease> \global\let\@begindocumenthook\@undefined +%<latexrelease> \ifx\@listfiles\@undefined +%<latexrelease> \global\let\@filelist\relax +%<latexrelease> \global\let\@addtofilelist\@gobble +%<latexrelease> \fi +%<latexrelease> \gdef\do##1{\global\let ##1\@notprerr}% +%<latexrelease> \@preamblecmds +%<latexrelease> \global\let \@nodocument \relax +%<latexrelease> \global\let\do\noexpand +%<latexrelease> \ignorespaces} +%<latexrelease>\EndIncludeInRelease +%<latexrelease> %<latexrelease>\IncludeInRelease{0000/00/00}% %<latexrelease> {\document}{Save language for hyphenation} %<latexrelease>\def\document{\endgroup diff --git a/Master/texmf-dist/source/latex-dev/base/ltfinal.dtx b/Master/texmf-dist/source/latex-dev/base/ltfinal.dtx index 56549507624..9089c8a7055 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} - [2019-10-02 v2.2 LaTeX Kernel (Final Settings)] + [2020-07-16 v2.2f LaTeX Kernel (Final Settings)] % \iffalse \documentclass{ltxdoc} \GetFileInfo{ltfinal.dtx} @@ -460,6 +460,14 @@ \fi % \end{macrocode} % +% \changes{v2.2b}{2020/07/08} +% {Add a last-minute hook for \textsf{expl3}} +% At this stage, we can install any last-minute \textsf{expl3} set-up. +% \begin{macrocode} +\@expl@finalise@setup@@ +\def\@expl@finalise@setup@@{} +% \end{macrocode} +% % This is as good a place as any to active a few Xe\TeX{}-specific % settings % \begin{macrocode} @@ -1044,13 +1052,8 @@ % }{} % \end{macrocode} % -% \subsection{Loading further extensions} -% % \changes{v2.2}{2019-10-02}{Load \textsf{ltexpl}} -% Simply load further code: it's deliberately separate -% \begin{macrocode} -\input{ltexpl.ltx} -% \end{macrocode} +% \changes{v2.2a}{2020-06-04}{Load \textsf{ltexpl} in \texttt{ltdefns}} % % \subsection{Freeing Memory} % @@ -1124,14 +1127,6 @@ % \end{macro} % \end{macro} % -% \subsection{Some last minute initializations \ldots} -% -% This initializes the 2020/02/02 extensions to NFSS after any changes -% in the preamble. -% \begin{macrocode} -\AtBeginDocument{\reinstall@nfss@defs\init@series@setup} -% \end{macrocode} -% % % % \subsection{Do some temporary work for pre-release} @@ -1139,9 +1134,233 @@ % This is a good place to load code that hasn't yet been % integrated into the other files \ldots % \begin{macrocode} -%\input ... +%</2ekernel> +%<*2ekernel|latexrelease> +%<latexrelease>\IncludeInRelease{2020/10/01}% +%<latexrelease> {\ShowHook}{The hook management}% +\input lthooks.ltx +\input ltshipout.ltx +\input ltfilehook.ltx +%</2ekernel|latexrelease> +%<latexrelease>\EndIncludeInRelease +%<latexrelease>\IncludeInRelease{0000/00/00}% +%<latexrelease> {\ShowHook}{The hook management}% + +%<latexrelease> +%<latexrelease>\DeclareRobustCommand*\begin[1]{% +%<latexrelease> \@ifundefined{#1}% +%<latexrelease> {\def\reserved@a{\@latex@error{Environment #1 undefined}\@eha}}% +%<latexrelease> {\def\reserved@a{\def\@currenvir{#1}% +%<latexrelease> \edef\@currenvline{\on@line}% +%<latexrelease> \csname #1\endcsname}}% +%<latexrelease> \@ignorefalse +%<latexrelease> \begingroup\@endpefalse\reserved@a} +%<latexrelease> +%<latexrelease>\@namedef{end }#1{% +%<latexrelease> \csname end#1\endcsname\@checkend{#1}% +%<latexrelease> \expandafter\endgroup\if@endpe\@doendpe\fi +%<latexrelease> \if@ignore\@ignorefalse\ignorespaces\fi} +%<latexrelease> +%<latexrelease>\DeclareDocumentCommand \NewHook {m}{} +%<latexrelease>\DeclareDocumentCommand \NewReversedHook {m}{} +%<latexrelease>\DeclareDocumentCommand \NewMirroredHookPair {mm}{} +%<latexrelease> +%<latexrelease>\DeclareDocumentCommand \AddToHookNext {m+m}{} +%<latexrelease>\DeclareDocumentCommand \RemoveFromHook {mo}{} +%<latexrelease>\def \UseHook #1{} +%<latexrelease>\def \UseOneTimeHook #1{} +%<latexrelease>\def \ShowHook #1{} +%<latexrelease>\let \DebugHookOn \@empty +%<latexrelease>\let \DebugHookOff\@empty +%<latexrelease> +%<latexrelease>\DeclareDocumentCommand \DeclareHookRule {mmmm}{} +%<latexrelease>\DeclareDocumentCommand \DeclareDefaultHookRule {mmm}{} +%<latexrelease>\DeclareDocumentCommand \ClearHookRule {mmm}{} +% \end{macrocode} +% If the hook management is not provided we make the test for existence +% false and the test for empty true in the hope that this is most +% of the time reasonable. If not a package would need to guard +% against running in an old kernel. +% \begin{macrocode} +%<latexrelease>\DeclareExpandableDocumentCommand \IfHookExistTF {mmm}{#3} +%<latexrelease>\DeclareExpandableDocumentCommand \IfHookEmptyTF {mmm}{#2} +%<latexrelease> +%<latexrelease>\DeclareRobustCommand\AtBeginDocument{\g@addto@macro\@begindocumenthook} +%<latexrelease>\DeclareRobustCommand\AtEndDocument{\g@addto@macro\@enddocumenthook} +%<latexrelease> +%<latexrelease>\let\AtBeginEnvironment \@undefined +%<latexrelease>\let\AtEndEnvironment \@undefined +%<latexrelease>\let\BeforeBeginEnvironment \@undefined +%<latexrelease>\let\AfterEndEnvironment \@undefined +%<latexrelease> +%<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>\def\@popfilename{\expandafter\@p@pfilename\@currnamestack\@nil +%<latexrelease> \@expl@pop@filename@@} +%<latexrelease> +%<latexrelease> +%<latexrelease>% next goes away again when etoolbox is updated ... +%<latexrelease>\expandafter\let\csname ver@etoolbox.sty\endcsname\@undefined +%<latexrelease> +%<latexrelease>\def\document{\endgroup +%<latexrelease> \@expl@sys@load@backend@@ +%<latexrelease> \ifx\@unusedoptionlist\@empty\else +%<latexrelease> \@latex@warning@no@line{Unused global option(s):^^J% +%<latexrelease> \@spaces[\@unusedoptionlist]}% +%<latexrelease> \fi +%<latexrelease> \@colht\textheight +%<latexrelease> \@colroom\textheight \vsize\textheight +%<latexrelease> \columnwidth\textwidth +%<latexrelease> \@clubpenalty\clubpenalty +%<latexrelease> \if@twocolumn +%<latexrelease> \advance\columnwidth -\columnsep +%<latexrelease> \divide\columnwidth\tw@ \hsize\columnwidth \@firstcolumntrue +%<latexrelease> \fi +%<latexrelease> \hsize\columnwidth \linewidth\hsize +%<latexrelease> \begingroup\@floatplacement\@dblfloatplacement +%<latexrelease> \makeatletter\let\@writefile\@gobbletwo +%<latexrelease> \global \let \@multiplelabels \relax +%<latexrelease> \@input{\jobname.aux}% +%<latexrelease> \endgroup +%<latexrelease> \if@filesw +%<latexrelease> \immediate\openout\@mainaux\jobname.aux +%<latexrelease> \immediate\write\@mainaux{\relax}% +%<latexrelease> \fi +%<latexrelease> \process@table +%<latexrelease> \let\glb@currsize\@empty % Force math initialization. +%<latexrelease> \normalsize +%<latexrelease> \everypar{}% +%<latexrelease> \ifx\normalsfcodes\@empty +%<latexrelease> \ifnum\sfcode`\.=\@m +%<latexrelease> \let\normalsfcodes\frenchspacing +%<latexrelease> \else +%<latexrelease> \let\normalsfcodes\nonfrenchspacing +%<latexrelease> \fi +%<latexrelease> \fi +%<latexrelease> \ifx\document@default@language\m@ne +%<latexrelease> \chardef\document@default@language\language +%<latexrelease> \fi +%<latexrelease> \@noskipsecfalse +%<latexrelease> \let \@refundefined \relax +%<latexrelease> \let\AtBeginDocument\@firstofone +%<latexrelease> \@begindocumenthook +%<latexrelease> \ifdim\topskip<1sp\global\topskip 1sp\relax\fi +%<latexrelease> \global\@maxdepth\maxdepth +%<latexrelease> \global\let\@begindocumenthook\@undefined +%<latexrelease> \ifx\@listfiles\@undefined +%<latexrelease> \global\let\@filelist\relax +%<latexrelease> \global\let\@addtofilelist\@gobble +%<latexrelease> \fi +%<latexrelease> \gdef\do##1{\global\let ##1\@notprerr}% +%<latexrelease> \@preamblecmds +%<latexrelease> \global\let \@nodocument \relax +%<latexrelease> \global\let\do\noexpand +%<latexrelease> \ignorespaces} +%<latexrelease> +%<latexrelease>\def\enddocument{% +%<latexrelease> \let\AtEndDocument\@firstofone +%<latexrelease> \@enddocumenthook +%<latexrelease> \@checkend{document}% +%<latexrelease> \clearpage +%<latexrelease> \begingroup +%<latexrelease> \if@filesw +%<latexrelease> \immediate\closeout\@mainaux +%<latexrelease> \let\@setckpt\@gobbletwo +%<latexrelease> \let\@newl@bel\@testdef +%<latexrelease> \@tempswafalse +%<latexrelease> \makeatletter \@@input\jobname.aux +%<latexrelease> \fi +%<latexrelease> \@dofilelist +%<latexrelease> \ifdim \font@submax >\fontsubfuzz\relax +%<latexrelease> \@font@warning{Size substitutions with differences\MessageBreak +%<latexrelease> up to \font@submax\space have occurred.\@gobbletwo}% +%<latexrelease> \fi +%<latexrelease> \@defaultsubs +%<latexrelease> \@refundefined +%<latexrelease> \if@filesw +%<latexrelease> \ifx \@multiplelabels \relax +%<latexrelease> \if@tempswa +%<latexrelease> \@latex@warning@no@line{Label(s) may have changed. +%<latexrelease> Rerun to get cross-references right}% +%<latexrelease> \fi +%<latexrelease> \else +%<latexrelease> \@multiplelabels +%<latexrelease> \fi +%<latexrelease> \fi +%<latexrelease> \endgroup +%<latexrelease> \deadcycles\z@\@@end} +%<latexrelease> +%<latexrelease>\expandafter\let\csname ver@atveryend.sty\endcsname\@undefined +%<latexrelease>\let\AfterLastShipout \@undefined +%<latexrelease>\let\AtVeryEndDocument \@undefined +%<latexrelease>\let\AtEndAfterFileList \@undefined +%<latexrelease>\let\AtVeryVeryEnd \@undefined +%<latexrelease>\let\BeforeClearDocument \@undefined +%<latexrelease> +%<latexrelease> +%<latexrelease>\expandafter\let\expandafter\shipout\csname tex_shipout:D\endcsname +%<latexrelease> +%<latexrelease>\let \ShipoutBox\@undefined +%<latexrelease>\let \ReadonlyShipoutCounter \@undefined +%<latexrelease>\let \c@totalpages \@undefined +%<latexrelease>\let \thetotalpages \@undefined +%<latexrelease> +%<latexrelease>\let \DiscardShipoutBox \@undefined +%<latexrelease>\let \DebugShipoutOn \@undefined +%<latexrelease>\let \DebugShipoutOff \@undefined +%<latexrelease> +%<latexrelease>\DeclareRobustCommand \AtBeginDvi [1]{% +%<latexrelease> \global \setbox \@begindvibox +%<latexrelease> \vbox{\unvbox \@begindvibox #1}% +%<latexrelease>} +%<latexrelease> +%<latexrelease>\let \AtBeginShipout \@undefined +%<latexrelease>\let \AtBeginShipoutNext \@undefined +%<latexrelease> +%<latexrelease>\let \AtBeginShipoutFirst \@undefined +%<latexrelease> +%<latexrelease>\let \ShipoutBoxHeight \@undefined +%<latexrelease>\let \ShipoutBoxDepth \@undefined +%<latexrelease>\let \ShipoutBoxWidth \@undefined +%<latexrelease> +%<latexrelease>\let \AtBeginShipoutDiscard \@undefined +%<latexrelease> +%<latexrelease>\let \AtBeginShipoutAddToBox \@undefined +%<latexrelease>\let \AtBeginShipoutAddToBoxForeground \@undefined +%<latexrelease>\let \AtBeginShipoutUpperLeft \@undefined +%<latexrelease>\let \AtBeginShipoutUpperLeftForeground \@undefined +%<latexrelease> +%<latexrelease>\expandafter\let\csname ver@atbegshi.sty\endcsname \@undefined +%<latexrelease> +%<latexrelease>\let \EveryShipout \@undefined +%<latexrelease>\let \AtNextShipout \@undefined +%<latexrelease>\expandafter \let \csname ver@everyshi.sty\endcsname \@undefined +%<latexrelease> +%<latexrelease>\let \AtEndDvi \@undefined +%<latexrelease>\expandafter\let \csname ver@atenddvi.sty\endcsname \@undefined +%<latexrelease> +%<latexrelease>\AtBeginDocument{\reinstall@nfss@defs\init@series@setup} +%<latexrelease> +%<latexrelease>\EndIncludeInRelease +%<*2ekernel> +% \end{macrocode} +% +% \subsection{Some last minute initializations \ldots} +% +% This initializes the 2020/02/02 extensions to NFSS after any changes +% in the preamble. +% \begin{macrocode} +\g@addto@macro\@kernel@after@env@document@begin + {\reinstall@nfss@defs\init@series@setup} % \end{macrocode} % +% % \subsection{Dumping the format} % Finally we make |@| into a letter, ensure the format will % be in the `normal' error mode, and dump everything into the diff --git a/Master/texmf-dist/source/latex-dev/base/lthooks.dtx b/Master/texmf-dist/source/latex-dev/base/lthooks.dtx new file mode 100644 index 00000000000..603b472e44a --- /dev/null +++ b/Master/texmf-dist/source/latex-dev/base/lthooks.dtx @@ -0,0 +1,3925 @@ +% \iffalse meta-comment +% +%% File: lthooks.dtx (C) Copyright 2020 Frank Mittelbach, LaTeX Team +% +% It may be distributed and/or modified under the conditions of the +% LaTeX Project Public License (LPPL), either version 1.3c of this +% license or (at your option) any later version. The latest version +% of this license is in the file +% +% https://www.latex-project.org/lppl.txt +% +% +% The development version of the bundle can be found below +% +% https://github.com/FrankMittelbach/... +% +% for those people who are interested or want to report an issue. +% +% \begin{macrocode} +\providecommand\lthooksversion{v0.9b} +\providecommand\lthooksdate{2020/07/19} +% \end{macrocode} +% +%<*driver> +\RequirePackage[debug]{lthooks} + +\documentclass{l3doc} + +% bug fix fo l3doc.cls +\ExplSyntaxOn +\cs_set_protected:Npn \__codedoc_macro_typeset_one:nN #1#2 + { + \vbox_set:Nn \l__codedoc_macro_box + { + \vbox_unpack_drop:N \l__codedoc_macro_box + \hbox { \llap { \__codedoc_print_macroname:nN {#1} #2 + \MacroFont % <----- without it the \ is in lmr10 if a link is made + \ + } } + } + \int_incr:N \l__codedoc_macro_int + } +\ExplSyntaxOff + +\EnableCrossrefs +\CodelineIndex +\begin{document} + \DocInput{lthooks.dtx} +\end{document} +%</driver> +% +% \fi +% +% +% \long\def\fmi#1{\begin{quote}\itshape FMi: #1\end{quote}} +% \long\def\pho#1{\begin{quote}\itshape PhO: #1\end{quote}} +% +% \newcommand\hook[1]{\texttt{#1}} +% +% +% \title{The \texttt{lthooks} package\thanks{This package has version +% \lthooksversion\ dated \lthooksdate, \copyright\ \LaTeX\ +% Project.}} +% +% \author{Frank Mittelbach\thanks{Code improvements for speed and other goodies by Phelype Oleinik}} +% +% \maketitle +% +% +% \tableofcontents +% +% \section{Introduction} +% +% Hooks are points in the code of commands or environments where it +% is possible to add processing code into existing commands. This +% can be done by different packages that do not know about each +% other and to allow for hopefully safe processing it is necessary +% to sort different chunks of code added by different packages into +% a suitable processing order. +% +% This is done by the packages adding chunks of code (via +% \cs{AddToHook}) and labeling their code with some label by +% default using the package name as a label. +% +% At \verb=\begin{document}= all code for a hook is then sorted +% according to some rules (given by \cs{DeclareHookRule}) for fast +% execution without processing overhead. If the hook code is +% modified afterwards (or the rules are changed), +% a new version for fast processing is generated. +% +% Some hooks are used already in the preamble of the document. If +% that happens then the hook is prepared for execution (and sorted) +% already at that point. +% +% +% \section{Package writer interface} +% +% The hook management system is offered as a set of CamelCase +% commands for traditional \LaTeXe{} packages (and for use in the +% document preamble if needed) as well as \texttt{expl3} commands +% for modern packages, that use the L3 programming layer of +% \LaTeX{}. Behind the scenes, a single set of data structures is +% accessed so that packages from both worlds can coexist and access +% hooks in other packages. +% +% +% +% \subsection{\LaTeXe\ interfaces} +% +% \subsubsection{Declaring hooks and using them in code} +% +% 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. +% +% +% \begin{function}{\NewHook} +% \begin{syntax} +% \cs{NewHook} \Arg{hook} +% \end{syntax} +% Creates a new \meta{hook}. +% If this is a hook provided as part of a package it is suggested +% that the \meta{hook} name is always structured as follows: +% \meta{package-name}\texttt{/}\meta{hook-name}. If necessary you +% can further subdivide the name by adding more \texttt{/} parts. +% If a hook name is already taken, an error is raised and the hook +% is not created. +% +% 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}{\NewReversedHook} +% \begin{syntax} +% \cs{NewReversedHook} \Arg{hook} +% \end{syntax} +% Like \cs{NewHook} declares a new \meta{hook}. +% the difference is that the code chunks for this hook are in +% reverse order by default (those added last are executed first). +% Any rules for the hook are applied after the default ordering. +% See sections~\ref{sec:order} and \ref{sec:reversed-order} +% for further details. +% +% 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}{\NewMirroredHookPair} +% \begin{syntax} +% \cs{NewMirroredHookPair} \Arg{hook-1} \Arg{hook-2} +% \end{syntax} +% A shorthand for +% \cs{NewHook}\Arg{hook-1}\cs{NewReversedHook}\Arg{hook-2}. +% +% The \meta{hooks} can be specified using the dot-syntax to denote +% the current package name. See section~\ref{sec:default-label}. +% \end{function} +% + + + +% \begin{function}{\UseHook} +% \begin{syntax} +% \cs{UseHook} \Arg{hook} +% \end{syntax} +% Execute the hook code inside a command or environment.\footnote{For +% legacy hooks such as \hook{begindocument} it is also +% possible to call \cs{@...hook}, e.g., \cs{@begindocumenthook}, +% but this syntax is discouraged.} +% +% Before \verb=\begin{document}= the fast execution code for a hook +% is not set up, so in order to use a hook there it is explicitly +% initialized first. As that involves assignments using a hook at +% those times is not 100\% the same as using it after +% \verb=\begin{document}=. +% +% The \meta{hook} \emph{cannot} be specified using the dot-syntax. +% A leading |.| is treated literally. +% \end{function} +% +% \begin{function}{\UseOneTimeHook} +% \begin{syntax} +% \cs{UseOneTimeHook} \Arg{hook} +% \end{syntax} +% 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., +% \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 +% \cs{\meta{addto-cmd}} to simply process its argument, i.e., +% essentially make it behave like \cs{@firstofone}. +% +% \cs{UseOneTimeHook} does that: it records that the hook has been +% 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?} +% +% The \meta{hook} \emph{cannot} be specified using the dot-syntax. +% A leading |.| is treated literally. +% \end{function} +% +% +% \subsubsection{Updating code for hooks} +% +% \begin{function}{\AddToHook} +% \begin{syntax} +% \cs{AddToHook} \Arg{hook}\oarg{label}\Arg{code} +% \end{syntax} +% Adds \meta{code} to the \meta{hook} labeled by \meta{label}. If +% the optional argument \meta{label} is not provided, if \cs{AddToHook} +% is used in a package/class, then the current +% package/class name is used, otherwise \hook{top-level} is +% used~(see section~\ref{sec:default-label}). +% +% If there already exists code under the \meta{label} then the new +% \meta{code} is appended to the existing one (even if this is a reversed hook). +% If you want to replace existing code under the +% \meta{label}, first apply \cs{RemoveFromHook}. +% +% The hook doesn't have to exist for code to be added to +% it. However, if it is not declared later then obviously the +% added \meta{code} will never be executed. This +% allows for hooks to work regardless of package loading order and +% enables packages to add to hook of other packages without +% worrying whether they are actually used in the current document. +% See section~\ref{sec:querying}. +% +% The \meta{hook} and \meta{label} can be specified using the +% dot-syntax to denote the current package name. +% See section~\ref{sec:default-label}. +% \end{function} +% +% \begin{function}{\RemoveFromHook} +% \begin{syntax} +% \cs{RemoveFromHook} \Arg{hook}\oarg{label} +% \end{syntax} +% Removes any code labeled by \meta{label} from the \meta{hook}. +% If the optional +% argument \meta{label} is not provided, if \cs{AddToHook} +% is used in a package/class, then the current +% package/class name is used, otherwise \hook{top-level} is used. +% +% If the optional 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! +% +% The \meta{hook} and \meta{label} can be specified using the +% dot-syntax to denote the current package name. +% See section~\ref{sec:default-label}. +% \end{function} +% +% \medskip +% +% A useful application for this declaration inside the document body +% is when one wants to temporarily add code to hooks and later remove +% it again, e.g., +%\begin{verbatim} +% \AddToHook{env/quote/before}{\small} +% \begin{quote} +% A quote set in a smaller typeface +% \end{quote} +% ... +% \RemoveFromHook{env/quote/before} +% ... now back to normal for further quotes +%\end{verbatim} +% Note that you can't cancel the setting with +%\begin{verbatim} +% \AddToHook{env/quote/before}{} +%\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 +% changes for no good reason. +% +% The above is only needed if one wants to typeset several quotes in a +% smaller typeface. If the hook is only needed once then +% \cs{AddToHookNext} is simpler, because it resets itself after one use. +% +% +% \begin{function}{\AddToHookNext} +% \begin{syntax} +% \cs{AddToHookNext} \Arg{hook}\Arg{code} +% \end{syntax} +% Adds \meta{code} to the next invocation of the \meta{hook}. +% The code is executed after the normal hook code has finished and +% it is executed only once, i.e. it is deleted after it was used. +% +% Using the declaration is a global operation, i.e., the code is +% not lost, even if the declaration is used inside a group and the +% next invocation happens after the group. If the declaration is +% used several times before the hook is executed then all code is +% executed in the order in which it was declared.\footnotemark +% +% The hook doesn't have to exist for code to be added to it. This +% allows for hooks to work regardless of package loading order. +% See section~\ref{sec:querying}. +% +% The \meta{hook} can be specified using the dot-syntax to denote +% the current package name. See section~\ref{sec:default-label}. +% \end{function}\footnotetext{There is +% no mechanism to reorder such code chunks (or delete them).} +% +% \subsubsection{Hook names and default labels} +% \label{sec:default-label} +% +% It is best practice to use \cs{AddToHook} in packages or classes +% \emph{without specifying a \meta{label}} because then the package +% or class name is automatically used, which is helpful if rules are +% needed, and avoids mistyping the \meta{label}. +% +% Using an explicit \meta{label} is only necessary in very specific +% situations, e.g., if you want to add several chunks of code into a +% single hook and have them placed in different parts of the hook +% (by providing some rules). +% +% The other case is when you develop a larger package with several +% sub-packages. In that case you may want to use the same +% \meta{label} throughout the sub-packages in order to avoid +% that the labels change if you internally reorganize your code. +% +% It is not enforced, but highly recommended that the hooks defined by +% a package, and the \meta{labels} used to add code to other hooks +% contain the package name to easily identify the source of the code +% chunk and to prevent clashes. This should be the standard practice, +% so this hook management code provides a shortcut to refer to the +% current package in the name of a \meta{hook} and in a \meta{label}. +% If \meta{hook} name or \meta{label} consist just of a single dot +% (|.|), or starts with a dot followed by a slash (|./|) then the dot +% denotes the \meta{default label} (usually the current package or class +% name---see~\cs{DeclareDefaultHookLabel}). +% A \enquote{|.|} or \enquote{|./|} anywhere else in a \meta{hook} or in +% \meta{label} is treated literally and is not replaced. + +% For example, +% inside the package \texttt{mypackage.sty}, the default label is +% \texttt{mypackage}, so the instructions: +% \begin{verbatim} +% \NewHook {./hook} +% \AddToHook {./hook}[.]{code} % Same as \AddToHook{./hook}{code} +% \AddToHook {./hook}[./sub]{code} +% \DeclareHookRule{begindocument}{.}{<}{babel} +% \AddToHook {file/after/foo.tex}{code} +% \end{verbatim} +% are equivalent to: +% \begin{verbatim} +% \NewHook {mypackage/hook}[mypackage]{code} +% \AddToHook {mypackage/hook}[mypackage]{code} +% \AddToHook {mypackage/hook}[mypackage/sub]{code} +% \DeclareHookRule{begindocument}{mypackage}{<}{babel} +% \AddToHook {file/after/foo.tex}{code} % unchanged +% \end{verbatim} +% +% The \meta{default label} is automatically set to the name of the +% current package or class (using \cs{@currname}). If \cs{@currname} +% is not set (because the hook command is used outside of a package, or +% the current file wasn't loaded with \cs{usepackage} or +% \cs{documentclass}), then the \texttt{top-level} is used as the +% \meta{default label}. +% +% This syntax is available in all \meta{label} arguments and most +% \meta{hook}, both in the \LaTeXe{} interface, and the \LaTeX3 +% interface described in section~\ref{sec:l3hook-interface}. +% +% Note, however, that the replacement of |.| by the \meta{default label} +% takes place when the hook command is executed, so actions that are +% somehow executed after the package ends will have the wrong +% \meta{default label} if the dot-syntax is used. For that reason, +% 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{IfHookExistTF} (and \cs{hook_if_exist:nTF}) because these +% conditionals are used in some performance-critical parts of the hook +% management code, and because they are usually used to refer to other +% package's hooks, so the dot-syntax doesn't make much sense. +% +% In some cases, for example in large packages, one may want to separate +% it in logical parts, but still use the main package name as +% \meta{label}, then the \meta{default label} can be set using +% \cs{DeclareDefaultHookLabel}: +% +% \begin{function}{\DeclareDefaultHookLabel} +% \begin{syntax} +% \cs{DeclareDefaultHookLabel} \Arg{default label} +% \end{syntax} +% Sets the \meta{default label} to be used in \meta{label} arguments. +% If \cs{DeclareDefaultHookLabel} is not used in the +% current package, \cs{@currname} is used instead. If \cs{@currname} +% is not set, the code is assumed to be in the main document, in which +% case \texttt{top-level} is used. +% +% The effect of \cs{DeclareDefaultHookLabel} holds for the current +% file, and is reset to the previous value when the file is closed. +% \end{function} +% +% +% \subsubsection{Defining relations between hook code} +% +% The default assumption is that code added to hooks by different +% packages is independent and the order in which it is executed is +% irrelevant. While this is true in many case it is obviously false +% in many others. +% +% Before the hook management system was introduced +% packages had to take elaborate precaution to determine of some other +% package got loaded as well (before or after) and find some ways to +% alter its behavior accordingly. In addition is was often the user's +% responsibility to load packages in the right order so that code +% added to hooks got added in the right order and some cases even +% altering the loading order wouldn't resolve the conflicts. +% +% With the new hook management system it is now possible to define +% rules (i.e., relationships) between code chunks added by different +% packages and explicitly describe in which order they should be +% processed. +% +% \begin{function}{\DeclareHookRule} +% \begin{syntax} +% \cs{DeclareHookRule} \Arg{hook}\Arg{label1}\Arg{relation}\Arg{label2} +% \end{syntax} +% Defines a relation between \meta{label1} and \meta{label2} for a +% given \meta{hook}. If \meta{hook} is \texttt{??} this defines a +% relation for all hooks that use the two labels, i.e., that have +% chunks of code labeled with \meta{label1} and \meta{label2}. +% Rules specific to a given hook take precedence over default +% rules that use \texttt{??} as the \meta{hook}. +% +% Currently, the supported relations are the following: +% \begin{itemize} +% +% \item[\texttt{before} or \texttt{\string<}] +% +% Code for \meta{label1} comes before code for \meta{label2}. +% +% \item[\texttt{after} or \texttt{\string>}] +% Code for \meta{label1} comes after code for \meta{label2}. +% +% \item[\texttt{incompatible-warning}] +% +% Only code for either \meta{label1} or \meta{label2} can appear +% for that hook (a way to say that two packages---or parts of +% them---are incompatible). A warning is raised if both labels +% appear in the same hook. +% +% \item[\texttt{incompatible-error}] +% +% Like \texttt{incompatible-error} but instead of a warning a +% \LaTeX{} error is raised, and the code for both labels are +% dropped from that hook until the conflict is resolved. +% +% \item[\texttt{removes}] +% +% Code for \meta{label1} overwrites code for \meta{label2}. More +% precisely, code for \meta{label2} is dropped for that +% hook. This can be used, for example if one package is a +% superset in functionality of another one and therefore wants to +% undo code in some hook and replace it with its own version. +% +% \item[\texttt{unrelated}] +% +% The order of code for \meta{label1} and \meta{label2} is +% irrelevant. This rule is there to undo an incorrect rule +% specified earlier. +% +% \end{itemize} +% +% The \meta{hook} and \meta{label} can be specified using the +% dot-syntax to denote the current package name. +% See section~\ref{sec:default-label}. +% \end{function} +% +% +% \begin{function}{\ClearHookRule} +% \begin{syntax} +% \cs{ClearHookRule}\Arg{hook}\Arg{label1}\Arg{label2} +% \end{syntax} +% Syntactic sugar for saying that \meta{label1} and \meta{label2} +% are unrelated for the given \meta{hook}. +% \end{function} +% +% +% +% \begin{function}{\DeclareDefaultHookRule} +% \begin{syntax} +% \cs{DeclareDefaultHookRule}\Arg{label1}\Arg{relation}\Arg{label2} +% \end{syntax} +% This sets up a relation between \meta{label1} and \meta{label2} +% for all hooks unless overwritten by a specific rule for a hook. +% Useful for cases where one package has a specific relation to +% some other package, e.g., is \texttt{incompatible} or always +% needs a special ordering \texttt{before} or \texttt{after}. +% (Technically it is just a shorthand for using \cs{DeclareHookRule} +% with \texttt{??} as the hook name.) +% +% Declaring default rules is only supported in the document +% preamble.\footnotemark{} +% +% The \meta{label} can be specified using the dot-syntax to denote +% the current package name. See section~\ref{sec:default-label}. +% \end{function}\footnotetext{Trying to do so, e.g., via +% \cs{DeclareHookRule} with \texttt{??} has bad side-effects and +% is not supported (though not explicitly caught for performance +% reasons).} +% +% +% +% \subsubsection{Querying hooks} +% \label{sec:querying} +% +% Simpler data types, like token lists, have three possible states; they +% can: +% \begin{itemize} +% \item exist and be empty; +% \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. +% +% This seemingly strange state may happen when, for example, package~$A$ +% defines hook \hook{A/foo}, and package $B$ adds some code to that +% hook. However, a document may load package $B$ before package $A$, or +% may not load package $A$ at all. In both cases some code is added to +% hook \hook{A/foo} without that hook being defined yet, thus that +% hook is said to be non-empty, whereas it doesn't exist. Therefore, +% querying the existence of a hook doesn't imply its emptiness, neither +% does the other way around. +% +% 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. +% +% \begin{function}[EXP]{\IfHookEmptyTF} +% \begin{syntax} +% \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 +% branches to either \meta{true code} or \meta{false code} depending +% on the result. +% +% The \meta{hook} \emph{cannot} be specified using the dot-syntax. +% A leading |.| is treated literally. +% \end{function} +% +% \begin{function}[EXP]{\IfHookExistTF} +% \begin{syntax} +% \cs{IfHookExistTF} \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. +% +% 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} +% +% If one has to adjust the code execution in a hook using a hook +% rule it is helpful to get some information about the code +% associated with a hook, its current order and the existing rules. +% +% \begin{function}{\ShowHook} +% \begin{syntax} +% \cs{ShowHook} \Arg{hook} +% \end{syntax} +% Displays information about the \meta{hook} such as +% \begin{itemize} +% \item +% the code chunks (and their labels) added to it, +% \item +% any rules set up to order them, +% \fmi{currently this is missing the default rules that apply, +% guess that needs fixing} +% \item +% the computed order (if already defined), +% \item +% any code executed on the next invocation only. +% \end{itemize} +% +% The \meta{hook} can be specified using the dot-syntax to denote +% the current package name. See section~\ref{sec:default-label}. +% \end{function} +% +% +% \subsubsection{Debugging hook code} +% +% \begin{function}{\DebugHookOn,\DebugHookOff} +% \begin{syntax} +% \cs{DebugHookOn} +% \end{syntax} +% Turn the debugging of hook code on or off. This displays changes +% made to the hook data structures. The output is rather coarse and +% not really intended for normal use. +% \end{function} +% +% +% \subsection{L3 programming layer (\texttt{expl3}) interfaces} +% \label{sec:l3hook-interface} +% +% +% This is a quick summary of the \LaTeX3 programming interfaces for +% use with packages written in \texttt{expl3}. In contrast to the +% \LaTeXe{} interfaces they always use mandatory arguments only, e.g., +% you always have to specify the \meta{label} for a code chunk. We +% therefore suggest to use the declarations discussed in the previous +% section even in \texttt{expl3} packages, but the choice is yours. +% +% +% \begin{function} +% {\hook_new:n,\hook_new_reversed:n,\hook_new_pair:nn} +% \begin{syntax} +% \cs{hook_new:n}\Arg{hook} +% \cs{hook_new_pair:nn}\Arg{hook-1}\Arg{hook-2} +% \end{syntax} +% Creates a new \meta{hook} with normal or reverse ordering of code +% chunks. \cs{hook_new_pair:nn} creates a pair of such hooks with +% \Arg{hook-2} being a reversed hook. +% If a hook name is already taken, an error is raised and the hook +% is not created. +% +% 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_use:n} +% \begin{syntax} +% \cs{hook_use:n} \Arg{hook} +% \end{syntax} +% Executes the \Arg{hook} code followed (if set up) by the code for next +% invocation only, then empties that next invocation code. +% +% The \meta{hook} \emph{cannot} be specified using the dot-syntax. +% A leading |.| is treated literally. +% \end{function} +% +% \begin{function}{\hook_use_once:n} +% \begin{syntax} +% \cs{hook_use_once:n} \Arg{hook} +% \end{syntax} +% Changes the \Arg{hook} status so that from now on any addition to +% the hook code is executed immediately. Then execute any +% \Arg{hook} code already set up. +% \fmi{better L3 name?} +% +% The \meta{hook} \emph{cannot} be specified using the dot-syntax. +% A leading |.| is treated literally. +% \end{function} +% +% \begin{function}{\hook_gput_code:nnn} +% \begin{syntax} +% \cs{hook_gput_code:nnn} \Arg{hook} \Arg{label} \Arg{code} +% \end{syntax} +% Adds a chunk of \meta{code} to the \meta{hook} labeled +% \meta{label}. If the label already exists the \meta{code} is +% appended to the already existing code. +% +% If code is added to an external \meta{hook} (of the kernel or +% another package) then the convention is to use the package name +% as the \meta{label} not some internal module name or some other +% arbitrary string. +% +% The \meta{hook} and \meta{label} can be specified using the +% dot-syntax to denote the current package name. +% See section~\ref{sec:default-label}. +% \end{function} +% +% \begin{function} +% {\hook_gput_next_code:nn} +% \begin{syntax} +% \cs{hook_gput_next_code:nn} \Arg{hook} \Arg{code} +% \end{syntax} +% Adds a chunk of \meta{code} for use only in the next invocation of the +% \meta{hook}. Once used it is gone. +% +% This is simpler than \cs{hook_gput_code:nnn}, the code is simply +% appended to the hook in the order of declaration at the very end, +% i.e., after all standard code for the hook got executed. +% +% Thus if one needs to undo what the standard does one has to do +% that as part of \meta{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_gremove_code:nn} +% \begin{syntax} +% \cs{hook_gremove_code:nn} \Arg{hook} \Arg{label} +% \end{syntax} +% Removes any code for \meta{hook} labeled \meta{label}. +% +% If the code for that \meta{label} wasn't yet added to the +% \meta{hook}, an order is set so that when some code attempts to add +% that label, the removal order takes action and the code is not +% added. +% +% If the second 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, so think twice before using +% that! +% +% The \meta{hook} and \meta{label} can be specified using the +% dot-syntax to denote the current package name. +% See section~\ref{sec:default-label}. +% \end{function} + + +% \begin{function}{\hook_gset_rule:nnnn} +% \begin{syntax} +% \cs{hook_gset_rule:nnnn} \Arg{hook} \Arg{label1} \Arg{relation} \Arg{label2} +% \end{syntax} +% Relate \meta{label1} with \meta{label2} when used in \meta{hook}. +% See \cs{DeclareHookRule} for the allowed \meta{relation}s. +% If \meta{hook} is \texttt{??} a default rule is specified. +% +% The \meta{hook} and \meta{label} can be specified using the +% dot-syntax to denote the current package name. +% See section~\ref{sec:default-label}. +% The dot-syntax is parsed in both \meta{label} arguments, but it +% usually makes sense to be used in only one of them. +% \end{function} +% +% \begin{function}[pTF]{\hook_if_empty:n} +% \begin{syntax} +% \cs{hook_if_empty:n} \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 +% branches to either \meta{true code} or \meta{false code} depending +% on the result. +% +% The \meta{hook} \emph{cannot} be specified using the dot-syntax. +% A leading |.| is treated literally. +% \end{function} +% +% \begin{function}[pTF]{\hook_if_exist:n} +% \begin{syntax} +% \cs{hook_if_exist:n} \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. +% +% \fmi{what are the results for generic hooks that do not need to be declared?} +% +% 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. +% +% The \meta{hook} \emph{cannot} be specified using the dot-syntax. +% A leading |.| is treated literally. +% \end{function} +% +% +% \begin{function}{\hook_debug_on:,\hook_debug_off:} +% \begin{syntax} +% \cs{hook_debug_on:} +% \end{syntax} +% Turns the debugging of hook code on or off. This displays changes +% to the hook data. +% \end{function} +% +% +% +% \subsection{On the order of hook code execution} \label{sec:order} +% +% Chunks of code for a \meta{hook} under different labels are supposed +% to be independent if there are no special rules set up that +% define a relation between the chunks. This means that you can't +% make assumptions about the order of execution! +% +% Suppose you have the following declarations: +%\begin{verbatim} +% \NewHook{myhook} +% \AddToHook{myhook}[packageA]{\typeout{A}} +% \AddToHook{myhook}[packageB]{\typeout{B}} +% \AddToHook{myhook}[packageC]{\typeout{C}} +%\end{verbatim} +% then executing the hook with \cs{UseHook} will produce the +% typeout \texttt{A} \texttt{B} \texttt{C} in that order. In other +% words, the execution order is computed to be \texttt{packageA}, +% \texttt{packageB}, \texttt{packageC} which you can verify with +% \cs{ShowHook}\texttt{\{myhook\}}: +%\begin{verbatim} +% The hook 'myhook': +% Code chunks: +% packageA -> \typeout {A} +% packageB -> \typeout {B} +% packageC -> \typeout {C} +% Extra code next invocation: +% --- +% Rules: +% --- +% Execution order: +% packageA, packageB, packageC +%\end{verbatim} +% The reason is that the code chunks are internally saved in a property list +% and the initial order of such a property list is the order in +% which key-value pairs got added. However, that is only true if +% nothing other than adding happens! +% +% Suppose, or example, you want to replace the code chunk for +% \texttt{packageA}, e.g., +%\begin{verbatim} +% \RemoveFromHook{myhook}[packageA] +% \AddToHook{myhook}[packageA]{\typeout{A alt}} +%\end{verbatim} +% then your order becomes \texttt{packageB}, +% \texttt{packageC}, \texttt{packageA} because the label got removed +% from the property list and then re-added (at its end). +% +% While that may not be too surprising, the execution order is +% also sometimes altered if you add a redundant rule, e.g. if you specify +%\begin{verbatim} +% \DeclareHookRule{myhook}{packageA}{before}{packageB} +%\end{verbatim} +% instead of the previous lines we get +%\begin{verbatim} +% The hook 'myhook': +% Code chunks: +% packageA -> \typeout {A} +% packageB -> \typeout {B} +% packageC -> \typeout {C} +% Extra code next invocation: +% --- +% Rules: +% packageA|packageB with relation before +% Execution order (after applying rules): +% packageA, packageC, packageB +%\end{verbatim} +% As you can see the code chunks are still in the same order, but +% in the execution order for the labels \texttt{packageB} and +% \texttt{packageC} have +% swapped places. +% The reason is that, with the rule there are two orders that +% satisfy it, and the algorithm for sorting happened to pick a +% different one compared to the case without rules (where it +% doesn't run at all as there is nothing to resolve). +% Incidentally, if we had instead specified the redundant rule +%\begin{verbatim} +% \DeclareHookRule{myhook}{packageB}{before}{label-3} +%\end{verbatim} +% the execution order would not have changed. +% +% In summary: it is not possible to rely on the order of execution +% unless there are rules that partially or fully define the order +% (in which you can rely on them being fulfilled). +% +% +% \subsection{The use of \enquote{reversed} hooks} \label{sec:reversed-order} +% +% You may have wondered why you can declare a \enquote{reversed} hook +% with \cs{NewReversedHook} and what that does exactly. +% +% In short: the execution order of a reversed hook (without any +% rules!) is exactly reversed to the order you would have gotten for +% a hook declared with \cs{NewHook}. +% +% This is helpful if you have a pair of hooks where you expect to see +% code added that involves grouping, e.g., starting an environment +% in the first and closing that environment in the second hook. +% To give a somewhat contrived example\footnote{there are simpler +% ways to achieve the same effect.}, suppose there is a package +% adding the following: +%\begin{verbatim} +% \AddToHook{env/quote/before}[package-1]{\begin{itshape}} +% \AddToHook{env/quote/after} [package-1]{\end{itshape}} +%\end{verbatim} +% As a result, all quotes will be in italics. +% Now suppose further that the user wants the quotes also in blue +% and therefore adds: +%\begin{verbatim} +% \usepackage{color} +% \AddToHook{env/quote/before}{\begin{color}{blue}} +% \AddToHook{env/quote/after} {\end{color}} +%\end{verbatim} +% Now if the \hook{env/quote/after} hook would be a normal hook we +% would get the same execution order in both hooks, namely: +%\begin{verbatim} +% package-1, top-level +%\end{verbatim} +% (or vice versa) and as a result, would get: +%\begin{verbatim} +% \begin{itshape}\begin{color}{blue} ... +% \end{itshape}\end{color} +%\end{verbatim} +% and an error message that \verb=\begin{color}= ended by +% \verb=\end{itshape}=. +% With \hook{env/quote/after} declared as a reversed hook the +% execution order is reversed and so all environments are closed in +% the correct sequence and \cs{ShowHook} would give us the +% following output: +%\begin{verbatim} +% The hook 'env/quote/after': +% Code chunks: +% package-1 -> \end {itshape} +% top-level -> \end {color} +% Extra code next invocation: +% --- +% Rules: +% --- +% Execution order (after reversal): +% top-level, package-1 +%\end{verbatim} +% +% The reversal of the execution order happens before applying any +% rules, so if you alter the order you will probably have to alter +% it in both hooks, not just in one, but that depends on the use case. +% +% +% +% +% \subsection{Private \LaTeX{} kernel hooks} +% +% There are a few places where it is absolutely essential for +% \LaTeX{} to function correctly that code is executed in a precisely +% defined order. Even that could have been implemented with the +% hook management (by adding various rules to ensure the +% appropriate ordering with respect to other code added by +% packages). However, this makes every document unnecessary +% slow, because there has to be sorting even through the result is +% predetermined. Furthermore it forces package writers to +% unnecessarily add such rules if they add further code to the hook +% (or break \LaTeX{}). +% +% 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 +% \cs{enddocument} you find +%\begin{verbatim} +% \UseHook{enddocument}% +% \@kernel@after@enddocument +%\end{verbatim} +% which means first the user/package-accessible \hook{enddocument} +% hook is executed and then the internal kernel hook. As their name +% indicates these kernel commands should not be altered by third-party +% packages, so please refrain from that in the interest of +% stability and instead use the public hook next to it.\footnote{As +% with everything in \TeX{} there is not enforcement of this rule, +% and by looking at the code it is easy to find out how the kernel +% adds to them. The main reason of this section is therefore to say +% \enquote{please don't do that, this is unconfigurable code!}} +% +% +% +% \subsection{Legacy \LaTeXe{} interfaces} +% +% \LaTeXe{} offered a small number of hooks together with commands to +% add to them. They are listed here and are retained for backwards +% compatibility. +% +% With the new hook management several additional hooks have been added +% to \LaTeX\ and more will follow. See the next section for what +% is already available. +% +% +% \begin{function}{\AtBeginDocument} +% \begin{syntax} +% \cs{AtBeginDocument} \oarg{label} \Arg{code} +% \end{syntax} +% If used without the optional argument \meta{label}, it works essentially +% like before, i.e., it is adding \meta{code} to the hook +% \hook{begindocument} +% (which is executed inside \verb=\begin{document}=). +% However, all code added this way is labeled with the label +% \hook{top-level} if done outside of a package or class or with the +% package/class name if called inside such a file. +% +% This way one can add further code to the hook using +% \cs{AddToHook} or \cs{AtBeginDocument} using a different label +% and explicitly order the code chunks as necessary, e.g., run some +% code before or after the \hook{top-level} code. When using the +% optional argument the call is equivalent to running +% \cs{AddToHook} \texttt{\{begindocument\}} \oarg{label} +% \Arg{code}. +% +% For important packages with known order requirement we may over +% time add rules to the kernel (or to those packages) so that they +% work regardless of the loading-order in the document. +% \end{function} +% +% \begin{function}{\AtEndDocument} +% \begin{syntax} +% \cs{AtEndDocument} \oarg{label} \Arg{code} +% \end{syntax} +% Like \cs{AtBeginDocument} but for the \hook{enddocument} hook. +% \end{function} +% +% +% +% \begin{function}{\AtBeginDvi} +% \begin{syntax} +% \cs{AtBeginDvi} \oarg{label} \Arg{code} +% \end{syntax} +% This hook is discussed in conjunction with the shipout hooks. +% \end{function} +% +% +% +% \subsection{\LaTeXe{} commands and environments augmented by +% hooks} +% +% \emph{intro to be written} +% +% \subsubsection{Generic hooks for all environments} +% +% Every environment \meta{env} has now four associated hooks coming +% with it: +% \begin{description} +% \item[\hook{env/\meta{env}/before}] +% +% This hook is executed as part of \cs{begin} as the very first +% action, in particular prior to starting the environment group. +% Its scope is therefore not restricted by the environment. +% +% \item[\hook{env/\meta{env}/begin}] +% +% This hook is executed as part of \cs{begin} directly in front +% of the code specific to the environment start (e.g., the +% second argument of \cs{newenvironment}). Its scope is the +% environment body. +% +% \item[\hook{env/\meta{env}/end}] +% +% This is executed as part of \cs{end} directly in front of the +% code specific to the end of the environment (e.g., the third +% argument of \cs{newenvironment}). +% +% \item[\hook{env/\meta{env}/after}] +% +% This is executed as part of \cs{end} after the +% code specific to the environment end and after the environment +% group has ended. +% Its scope is therefore not restricted by the environment. +% +% This hook is implemented as a reversed hook so if two packages +% add code to \hook{env/\meta{env}/before} and to +% \hook{env/\meta{env}/after} they can add surrounding +% environments and the order of closing them happens in the +% right sequence. +% +% \end{description} +% In contrast to other hooks these hooks do not need to be declared +% using \cs{NewHook}. +% +% 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}} +% (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 +%\begin{verbatim} +% \UseHook{env/quote/before}\quote +% ... +% \endquote\UseHook{env/quote/after} +%\end{verbatim} +% to add the outer hooks, etc. +% +% +% +% \subsubsection{Hooks provided by \cs{begin}\texttt{\{document\}}} +% +% Until 2020 \cs{begin}\texttt{\{document\}} offered exactly one +% hook that one had to fill using \cs{AtBeginDocument}. Experience +% has shown that this single hook in one place was not enough and +% as part of adding the general hook management system a number of +% additional hooks have been added at this point. The places for +% hooks have been chosen to provide the same support as offered by +% external packages, such as \pkg{etoolbox} and others that +% augmented \cs{document} to gain better control. +% +% Supported are now the following hooks: +% \begin{description} +% +% \item[\hook{env/document/before}] +% +% This is the generic environment hook executed effectively +% before \verb=\begin{document}= starts, i.e., one can think of +% it as a hook for code at the end of the preamble section. +% +% \item[\hook{env/document/begin}] +% +% This is the second generic environment hook that is executed +% after the environment has started its group. But given that for the +% \texttt{document} environment this group is canceled there is +% little difference to the previous one as the two are directly +% executed one after another (the only difference is that in this +% hook \cs{@currenvir} is now set to \texttt{document} but +% anybody adding to this hook would know that already). +% +% +% \item[\hook{begindocument}] +% +% This hook is added to by \cs{AtBeginDocument} and is executed +% after the \texttt{.aux} file as be read in and most +% initialization are done, so they can be altered and inspected by +% the hook code. It is followed by a small number of further +% initializations that shouldn't be altered and are therefore +% coming later. +% +% \item[\hook{begindocument/end}] +% +% This hook is executed at the end of the \cs{document} code in +% other words at the beginning of the document body. The only +% command that follows it is \cs{ignorespaces}. +% +% \end{description} +% +% +% +% +% \subsubsection{Hooks provided by \cs{end}\texttt{\{document\}}} +% +% \LaTeXe{} always provided \cs{AtEndDocument} to add code to the +% execution of \verb=\end{document}= just in front of the code that +% is normally executed there. While this was a big improvement over +% the situation in \LaTeX\,2.09 it was not flexible enough for a +% number of use cases and so packages, such as \pkg{etoolbox}, +% \pkg{atveryend} and others patched \cs{enddocument} to add +% additional points where code could be hooked into. +% +% Patching using packages is always problematical as leads to +% conflicts (code availability, ordering of patches, incompatible +% patches, etc.). For this reason a number of additional hooks +% have been added to the \cs{enddocument} code to allow packages +% to add code in various places in a controlled way without the +% need for overwriting or patching the core code. +% +% Supported are now the following hooks: +% \begin{description} +% +% \item[\hook{env/document/end}] The generic hook inside \cs{end}. +% +% \item[\hook{enddocument}] + +% The hook associated with \cs{AtEndDocument}. It is immediately +% called after the previous hook so there could be just +% one.\footnote{We could make \cs{AtEndDocument} just fill the +% \hook{env/document/end} but maybe that is a bit confusing.} +% +% When this hook is executed there may be still unprocessed +% material (e.g., floats on the deferlist) and the hook may add +% further material to be typeset. After it, \cs{clearpage} is +% called to ensure that all such material gets typeset. If there +% is nothing waiting the \cs{clearpage} has no effect. +% +% +% \item[\hook{enddocument/afterlastpage}] +% +% As the name indicates this hook should not receive code that +% 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 +% set up any testing code to be run when the \texttt{.aux} file +% is re-read in the next step. +% +% +% After this hook has been executed the \texttt{.aux} file is +% closed for writing and then read back in to do some tests +% (e.g., looking for missing references or duplicated labels, etc.). +% +% \item[\hook{enddocument/afteraux}] +% +% At this point, the \texttt{.aux} file has been reprocessed and so +% this is a possible place for final checks and display of +% information to the user. However, for the latter you might +% prefer the next hook, so that your information is displayed after the +% (possibly longish) list of files if that got requested via \cs{listfiles}. +% +% \item[\hook{enddocument/info}] +% +% This hook is meant to receive code that write final information +% messages to the terminal. It follows immediately after the +% previous hook (so both could have been combined, but then +% packages adding further code would always need to also supply +% an explicit rule to specify where it should go. +% +% This hook already contains some code added by the kernel (under +% the labels \texttt{kernel/filelist} and +% \texttt{kernel/warnings}), namely the list of files when +% \cs{listfiles} has been used and the warnings for duplicate +% labels, missing references, font substitutions etc. +% +% \item[\hook{enddocument/end}] +% +% Finally, this hook is executed just in front of the final call +% to \cs{@{}@end}. +% +% \end{description} +% +% +% There is also the hook \hook{shipout/lastpage}. This hook is +% executed as part of the last \cs{shipout} in the document to +% allow package to add final \cs{special}'s to that page. Where +% this hook is executed in relation to those from the above list +% can vary from document to document. Furthermore to determine correctly +% which of the \cs{shipout}s is the last one, \LaTeX{} needs to be run +% several times, so initially it might get executed on the wrong +% page. See section~\ref{sec:shipout} for where to find the details. +% +% +% \subsubsection{Hooks provided \cs{shipout} operations} +% \label{sec:shipout} +% +% There are several hooks and mechanisms added to \LaTeX{}'s +% process of generating pages. These are documented in +% \texttt{ltshipout-doc.pdf} or with code in +% \texttt{ltshipout-code.pdf}. +% +% +% \subsubsection{Hooks provided 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}. +% +% + +% \StopEventually{\setlength\IndexMin{200pt} \PrintIndex } +% +% +% \section{The Implementation} +% \begin{macrocode} +%<@@=hook> +% \end{macrocode} +% +% \begin{macrocode} +%<*2ekernel> +% \end{macrocode} +% +% \begin{macrocode} +\ExplSyntaxOn +% \end{macrocode} +% +% +% \subsection{Debugging} +% +% \begin{macro}{\g_@@_debug_bool} +% Holds the current debugging state. +% \begin{macrocode} +\bool_new:N \g_@@_debug_bool +% \end{macrocode} +% \end{macro} +% +% \begin{macro}{\hook_debug_on:,\hook_debug_off:} +% \begin{macro}{\@@_debug:n} +% \begin{macro}{\@@_debug_gset:} +% Turns debugging on and off by redefining \cs{@@_debug:n}. +% \begin{macrocode} +\cs_new_eq:NN \@@_debug:n \use_none:n +\cs_new_protected:Npn \hook_debug_on: + { + \bool_gset_true:N \g_@@_debug_bool + \@@_debug_gset: + } +\cs_new_protected:Npn \hook_debug_off: + { + \bool_gset_false:N \g_@@_debug_bool + \@@_debug_gset: + } +\cs_new_protected:Npn \@@_debug_gset: + { + \cs_gset_protected:Npx \@@_debug:n ##1 + { \bool_if:NT \g_@@_debug_bool {##1} } + } +% \end{macrocode} +% \end{macro} +% \end{macro} +% \end{macro} + +% +% +% \subsection{Borrowing from internals of other kernel modules} +% +% +% \begin{macro}[EXP]{\@@_str_compare:nn} +% Private copy of \cs{__str_if_eq:nn} +% \begin{macrocode} +\cs_new_eq:NN \@@_str_compare:nn \__str_if_eq:nn +% \end{macrocode} +% \end{macro} +% +% \subsection{Declarations} +% +% \begin{macro}{\l_@@_return_tl,\l_@@_tmpa_tl,\l_@@_tmpb_tl} +% Scratch variables used throughout the package. +% \begin{macrocode} +\tl_new:N \l_@@_return_tl +\tl_new:N \l_@@_tmpa_tl +\tl_new:N \l_@@_tmpb_tl +% \end{macrocode} +% \end{macro} +% +% \begin{macro}{\g_@@_all_seq} +% In a few places we need a list of all hook names ever defined so +% we keep track if them in this sequence. +% \begin{macrocode} +\seq_new:N \g_@@_all_seq +% \end{macrocode} +% \end{macro} +% +% \begin{macro}{\g_@@_removal_list_prop} +% A property list to hold delayed removals. +% \begin{macrocode} +\tl_new:N \g_@@_removal_list_tl +% \end{macrocode} +% \end{macro} +% +% \begin{macro}{\l_@@_cur_hook_tl} +% Stores the name of the hook currently being sorted. +% \begin{macrocode} +\tl_new:N \l_@@_cur_hook_tl +% \end{macrocode} +% \end{macro} +% +% \begin{macro}{\g_@@_code_temp_prop} +% A property list to temporarily save the original one so that it +% isn't permanently changed during sorting. +% \begin{macrocode} +\prop_new:N \g_@@_code_temp_prop +% \end{macrocode} +% \end{macro} +% +% \begin{macro}{\g_@@_hook_curr_name_tl,\g_@@_name_stack_seq} +% Default label used for hook commands, and a stack to keep track of +% packages within packages. +% \begin{macrocode} +\tl_new:N \g_@@_hook_curr_name_tl +\seq_new:N \g_@@_name_stack_seq +% \end{macrocode} +% \end{macro} +% +% \begin{macro}{\@@_tmp:w} +% Temporary macro for generic usage. +% \begin{macrocode} +\cs_new_eq:NN \@@_tmp:w ? +% \end{macrocode} +% \end{macro} +% +% \begin{macro}{\tl_gremove_once:Nx} +% Some variants of \pkg{expl3} functions. \fmi{should be moved to expl3} +% \begin{macrocode} +\cs_generate_variant:Nn \tl_gremove_once:Nn { Nx } +% \end{macrocode} +% \end{macro} +% +% \begin{macro}{\s_@@_mark} +% Scan mark used for delimited arguments. +% \begin{macrocode} +\scan_new:N \s_@@_mark +% \end{macrocode} +% \end{macro} +% +% +% \subsection{Providing new hooks} +% +% \begin{macro}{\g_@@_..._code_prop,\g_@@_..._rules_prop, +% \g_@@_..._code_tl,\g_@@_..._next_code_tl} +% +% Hooks have a \meta{name} 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 +% 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{g_@@_\meta{name}_rules_prop}] A property listing holding +% relation info how the code chunks should be ordered within a +% hook. This is used for debugging only. The actual rule for a +% \meta{hook} is stored in a separate token lists named +% \cs[no-index]{g_@@_\meta{hook}_rule_\meta{label1}\string|\meta{label2}_tl} +% for a pair of labels. +% +% \item[\cs{g_@@_\meta{name}_code_tl}] The code that is actually executed +% when the hook is called in the document is stored in this token +% list. It is constructed from the code chunks applying the +% information. +% +% \item[\cs{g_@@_\meta{name}_next_code_tl}] Finally there is extra code +% (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. +% +% \end{description} +% \end{macro} + + +% +% +% \begin{macro}{\hook_new:n} +% The \cs{hook_new:n} declaration declare 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 + { + \exp_args:Nx \@@_new:n + { \@@_parse_label_default:nn {#1} { top-level } } + } +\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. +% \begin{macrocode} + \hook_if_exist:nTF {#1} + { \ErrorHookExists } +% \end{macrocode} +% Otherwise we add the hook name to the list of all hooks and +% allocate the necessary data structures for the new hook. +% \begin{macrocode} + { \seq_gput_right:Nn \g_@@_all_seq {#1} +% \end{macrocode} +% This is only used by the actual code of the current hook, so +% declare it normally: +% \begin{macrocode} + \tl_new:c { g_@@_#1_code_tl } +% \end{macrocode} +% Now ensure that the base data structure for the hook exists: +% \begin{macrocode} + \@@_declare: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} +% \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 +% and contains a \verb=-= for reversed hooks. +% \begin{macrocode} + \tl_new:c { g_@@_#1_reversed_tl } +% \end{macrocode} +% The above is all in L3 convention, but we also provide an +% interface to legacy \LaTeXe{} for use in the current kernel. This +% is done in a separate macro. +% \begin{macrocode} + \@@_provide_legacy_interface:n {#1} + } +} +% \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 both +% variables exist, since both are declared at the same time (either +% both exist, or neither). +% \begin{macrocode} +\cs_new_protected:Npn \@@_declare:n #1 + { + \@@_if_exist:nF {#1} + { + \prop_new:c { g_@@_#1_code_prop } + \tl_new:c { g_@@_#1_next_code_tl } + \prop_new:c { g_@@_#1_rules_prop } % only for debugging + } + } +% \end{macrocode} +% \end{macro} + + + +% \begin{macro}{\hook_new_reversed:n} +% +% Declare a new hook. The default ordering of code chunks is +% reversed, signaled by setting the token list to a minus sign. +% \begin{macrocode} +\cs_new_protected:Npn \hook_new_reversed:n #1 { + \hook_new:n {#1} + \tl_gset:cn { g_@@_#1_reversed_tl } { - } +} +% \end{macrocode} +% \end{macro} + +% \begin{macro}{\hook_new_pair:nn} +% A shorthand for declaring a normal and a (matching) reversed hook in one go. +% \begin{macrocode} +\cs_new_protected:Npn \hook_new_pair:nn #1#2 { + \hook_new:n {#1} \hook_new_reversed:n {#2} +} +% \end{macrocode} +% \end{macro} + + +% \begin{macro}{\@@_provide_legacy_interface:n} +% The \LaTeX{} legacy concept for hooks uses with hooks the +% following naming scheme in the code: \cs{@...hook}. +% +% We follow this convention and insert the hook code using this +% naming scheme in \LaTeXe{}. At least as long as this code is in a +% package, some such hooks are already filled with data when we move +% them over to the new scheme. We therefore insert already existing +% code under the label \texttt{legacy} into the hook management +% machinery and then replace the \cs{@...hook} with its counterpart +% which is \cs{g_@@_\#1_code_tl}.\footnote{This means one extra +% unnecessary expansion on each invocation in the document but +% keeps the \LaTeXe{} and the L3 coding side properly separated.} +% \begin{macrocode} +\cs_new_protected:Npn \@@_provide_legacy_interface:n #1 + { +% \end{macrocode} +% If the \pkg{expl3} code is run with checking on then assigning or +% using non L3 names such as \cs{@enddocumenthook} with \pkg{expl3} +% functions will trigger warnings so we run this code with +% debugging explicitly suspended. +% \begin{macrocode} + \debug_suspend: + \tl_if_exist:cT { @#1hook } +% \end{macrocode} +% Of course if the hook exists but is still empty, there is no need +% to add anything under \texttt{legacy} or the current package name. +% \begin{macrocode} + { + \tl_if_empty:cF { @#1hook } + { + \@@_gput_code:nxv {#1} + { \@@_parse_label_default:Vn \c_novalue_tl { legacy } } + { @#1hook } + } + } +% \end{macrocode} +% We need a global definition in case the declaration is done +% inside a group (which happens below at the end of the file). +% This is another reason why need to suspend checking, otherwise +% \cs{tl_gset:co} would complain about \cs{@...hook} not starting +% with \cs{g_}. +% \begin{macrocode} + \tl_gset:co{@#1hook}{\cs:w g_@@_#1_code_tl\cs_end:} + \debug_resume: + } +% \end{macrocode} +% \end{macro} +% +% \subsection{Parsing a label} +% +% \begin{macro}[EXP]{\@@_parse_label_default:nn,\@@_parse_label_default:Vn} +% This macro checks if a label was given (not \cs{c_novalue_tl}), and +% if so, tries to parse the label looking for a leading \verb|.| to +% replace for \cs{@currname}. Otherwise \cs{@@_currname_or_default:n} +% is used to pick \cs{@currname} or the fallback value. +% \begin{macrocode} +\cs_new:Npn \@@_parse_label_default:nn #1 #2 + { + \tl_if_novalue:nTF {#1} + { \@@_currname_or_default:n {#2} } + { \tl_trim_spaces_apply:nN {#1} \@@_parse_dot_label:nn {#2} } + } +\cs_generate_variant:Nn \@@_parse_label_default:nn { V } +% \end{macrocode} +% \end{macro} +% +% \begin{macro}[EXP]{\@@_parse_dot_label:nn} +% \begin{macro}[EXP]{ +% \@@_parse_dot_label:nw, +% \@@_parse_dot_label_cleanup:w, +% \@@_parse_dot_label_aux:nw +% } +% Start by checking if the label is empty, which raises an error, and +% uses the fallback value. If not, +% split the label at a \verb|./|, if any, and check if no tokens are +% before the \verb|./|, or if the only character is a \verb|.|. +% If these requirements are fulfilled, the leading +% \verb|.| is replaced with \cs{@@_currname_or_default:n}. Otherwise +% the label is returned unchanged. +% \begin{macrocode} +\cs_new:Npn \@@_parse_dot_label:nn #1 #2 + { + \tl_if_empty:nTF {#1} + { + \msg_expandable_error:nnn { hooks } { empty-label } {#2} + #2 + } + { + \str_if_eq:nnTF {#1} { . } + { \@@_currname_or_default:n {#1} } + { \@@_parse_dot_label:nw {#2} #1 ./ \s_@@_mark } + } + } +\cs_new:Npn \@@_parse_dot_label:nw #1 #2 ./ #3 \s_@@_mark + { + \tl_if_empty:nTF {#2} + { \@@_parse_dot_label_aux:nw {#1} #3 \s_@@_mark } + { + \tl_if_empty:nTF {#3} + {#2} + { \@@_parse_dot_label_cleanup:w #2 ./ #3 \s_@@_mark } + } + } +\cs_new:Npn \@@_parse_dot_label_cleanup:w #1 ./ \s_@@_mark {#1} +\cs_new:Npn \@@_parse_dot_label_aux:nw #1 #2 ./ \s_@@_mark + { \@@_currname_or_default:n {#1} / #2 } +% \end{macrocode} +% \end{macro} +% \end{macro} +% +% \begin{macro}[EXP]{\@@_currname_or_default:n} +% Uses \cs{g_@@_hook_curr_name_tl} if it is set, otherwise tries +% \cs{@currname}. If neither is set, uses the fallback value +% \verb|#1| (usually \texttt{top-level}). +% \begin{macrocode} +\cs_new:Npn \@@_currname_or_default:n #1 + { + \tl_if_empty:NTF \g_@@_hook_curr_name_tl + { + \tl_if_empty:NTF \@currname + {#1} + { \@currname } + } + { \g_@@_hook_curr_name_tl } + } +% \end{macrocode} +% \end{macro} + + + + +% \begin{macro}{\hook_gput_code:nnn} +% \begin{macro}{\@@_gput_code:nnn,\@@_gput_code:nxv,\@@_hook_gput_code_do:nnn} +% +% With \cs{hook_gput_code:nnn}\Arg{hook}\Arg{label}\Arg{code} a +% chunk of \meta{code} is added to an existing \meta{hook} labeled +% with \meta{label}. +% \begin{macrocode} +\cs_new_protected:Npn \hook_gput_code:nnn #1 #2 + { + \exp_args:Nxx \@@_gput_code:nnn + { \@@_parse_label_default:nn {#1} { top-level } } + { \@@_parse_label_default:nn {#2} { top-level } } + } +\cs_new_protected:Npn \@@_gput_code:nnn #1 #2 #3 + { +% \end{macrocode} +% First we check if the hook exists. +% \begin{macrocode} + \@@_if_marked_removal:nnTF {#1} {#2} + { \@@_unmark_removal:nn {#1} {#2} } + { +% \end{macrocode} +% First we check if the hook exists. +% \begin{macrocode} + \hook_if_exist:nTF {#1} +% \end{macrocode} +% If so we simply add (or append) the new code to the property list +% holding different chunks for the hook. At \verb=\begin{document}= +% this is then sorted into a token list for fast execution. +% \begin{macrocode} + { + \@@_hook_gput_code_do:nnn {#1} {#2} {#3} +% \end{macrocode} +% However, if there is an update within the document we need to alter +% this execution code which is done by +% \cs{@@_update_hook_code:n}. In the preamble this does nothing. +% \begin{macrocode} + \@@_update_hook_code:n {#1} + } +% \end{macrocode} +% +% \begin{macrocode} + { \@@_try_declaring_generic_hook:nnn {#1} {#2} {#3} } + } + } +\cs_generate_variant:Nn \@@_gput_code:nnn { nxv } +% \end{macrocode} +% +% This macro will unconditionally add a chunk of code to the given hook. +% \begin{macrocode} +\cs_new_protected:Npn \@@_hook_gput_code_do:nnn #1 #2 #3 + { +% However, first some debugging info if debugging is enabled: +% \begin{macrocode} + \@@_debug:n{\iow_term:x{****~ Add~ to~ + \hook_if_exist:nF {#1} { undeclared~ } + hook~ #1~ (#2) + \on@line\space <-~ \tl_to_str:n{#3}} } +% \end{macrocode} +% Then try to get the code chunk labeled \verb=#2= from the hook. +% If there's code already there, then append \verb=#3= to that, +% otherwise just put \verb=#3=. +% \begin{macrocode} + \prop_get:cnNTF { g_@@_#1_code_prop } {#2} \l_@@_return_tl + { + \prop_gput:cno { g_@@_#1_code_prop } {#2} + { \l_@@_return_tl #3 } + } + { \prop_gput:cnn { g_@@_#1_code_prop } {#2} {#3} } + } +% \end{macrocode} +% \end{macro} +% \end{macro} +% +% \begin{macro}{\@@_gput_undeclared_hook:nnn} +% Often it may happen that a package $A$ defines a hook \verb=foo=, +% but package $B$, that adds code to that hook, is loaded before $A$. +% In such case we need to add code to the hook before its declared. +% \begin{macrocode} +\cs_new_protected:Npn \@@_gput_undeclared_hook:nnn #1 #2 #3 + { + \@@_declare: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} +% 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. +% +% The wrapper \cs{@@_try_declaring_generic_hook:nnn} then defers +% \cs{hook_gput_code:nnn} if the generic hook was declared, or to +% \cs{@@_gput_undeclared_hook:nnn} otherwise (the hook was tested for +% existence before, so at this point if it isn't generic, it doesn't +% exist). +% +% The wrapper \cs{@@_try_declaring_generic_next_hook:nn} for +% next-execution hooks does the same: it defers the code to +% \cs{hook_gput_next_code:nn} if the generic hook was declared, or +% to \cs{@@_gput_next_do:nn} otherwise. +% \begin{macrocode} +\cs_new_protected:Npn \@@_try_declaring_generic_hook:nnn #1 + { + \@@_try_declaring_generic_hook:nNNnn {#1} + \hook_gput_code:nnn \@@_gput_undeclared_hook:nnn + } +\cs_new_protected:Npn \@@_try_declaring_generic_next_hook:nn #1 + { + \@@_try_declaring_generic_hook:nNNnn {#1} + \hook_gput_next_code:nn \@@_gput_next_do:nn + } +% \end{macrocode} +% +% \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 +% 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 +% predefined set for generic names. We also split off the second +% component to see if we have to make a reversed hook. In either case +% the function returns \meta{true} for a generic hook and \meta{false} +% in other cases. +% \begin{macrocode} +\cs_new_protected:Npn \@@_try_declaring_generic_hook:nNNnn #1 + { + \@@_if_file_hook:wTF #1 / / \s_@@_mark + { + \exp_args:Ne \@@_try_declaring_generic_hook_split:nNNnn + { \exp_args:Ne \@@_file_hook_normalise:n {#1} } + } + { \@@_try_declaring_generic_hook_split:nNNnn {#1} } + } +\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} + } +\prg_new_protected_conditional:Npnn \@@_try_declaring_generic_hook:wn + #1 / #2 / #3 / #4 \scan_stop: #5 { TF } + { + \tl_if_empty:nTF {#2} + { \prg_return_false: } + { + \prop_if_in:NnTF \c_@@_generics_prop {#1} + { + \hook_if_exist:nF {#5} { \hook_new: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. +% \begin{macrocode} + \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: } + } + } +% \end{macrocode} +% \end{macro} +% \end{macro} +% \end{macro} +% \end{macro} +% +% \begin{macro}[pTF]{\@@_if_file_hook:w} +% \cs{@@_if_file_hook:wTF} checks if the argument is a valid +% file-specific hook (not, for example, |file/before|, but +% |file/before/foo.tex|). If it is a file-specific hook, then it +% executes the \meta{true} branch, otherwise \meta{false}. +% +% A file-specific hook is \texttt{file/\meta{position}/\meta{name}}. +% If any of these parts don't exist, it is a general file hook or not +% a file hook at all, so the conditional evaluates to \meta{false}. +% Otherwise, it checks that the first part is |file| and that the +% \meta{position} is in the \cs{c_@@_generics_file_prop}. +% +% A property list is used here to avoid having to worry with catcodes, +% because \pkg{expl3}'s file name parsing turns all characters into +% catcode-12 tokens, which might differ from hand-input letters. +% \begin{macrocode} +\prg_new_conditional:Npnn \@@_if_file_hook:w + #1 / #2 / #3 \s_@@_mark { TF } + { + \str_if_eq:nnTF {#1} { file } + { + \bool_lazy_or:nnTF + { \tl_if_empty_p:n {#3} } + { \str_if_eq_p:nn {#3} { / } } + { \prg_return_false: } + { + \prop_if_in:NnTF \c_@@_generics_file_prop {#2} + { \prg_return_true: } + { \prg_return_false: } + } + } + { \prg_return_false: } + } +% \end{macrocode} +% \end{macro} +% +% \begin{macro}[EXP]{\@@_file_hook_normalise:n} +% \begin{macro}[EXP]{\@@_strip_double_slash:n,\@@_strip_double_slash:w} +% When a file-specific hook is found, before being declared it is +% lightly normalized by \cs{@@_file_hook_normalise:n}. The current +% implementation just replaces two consecutive slashes (|//|) by a +% single one, to cope with simple cases where the user did something +% like \verb|\def\input@path{{./mypath/}}|, in which case a hook would +% have to be \verb|\AddToHook{file/after/./mypath//file.tex}|. +% \begin{macrocode} +\cs_new:Npn \@@_file_hook_normalise:n #1 + { \@@_strip_double_slash:n {#1} } +\cs_new:Npn \@@_strip_double_slash:n #1 + { \@@_strip_double_slash:w #1 // \s_@@_mark } +\cs_new:Npn \@@_strip_double_slash:w #1 // #2 \s_@@_mark + { + \tl_if_empty:nTF {#2} + {#1} + { \@@_strip_double_slash:w #1 / #2 \s_@@_mark } + } +% \end{macrocode} +% \end{macro} +% \end{macro} + + + + + +% \begin{macro}{\c_@@_generics_prop} +% Clist holding the generic names. We don't provide any user +% interface to this as this is meant to be static. +% \begin{description} +% \item[\texttt{env}] +% The generic hooks used in \cs{begin} and \cs{end}. +% \item[\texttt{file}] +% 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=} +% \end{macrocode} +% \end{macro} +% +% \begin{macro}{\c_@@_generics_reversed_ii_prop, +% \c_@@_generics_reversed_iii_prop, +% \c_@@_generics_file_prop} +% Some of the generic hooks are supposed to use reverse ordering, these are +% the following (only the second or third sub-component is checked): +% \begin{macrocode} +\prop_const_from_keyval:Nn \c_@@_generics_reversed_ii_prop {after=,end=} +\prop_const_from_keyval:Nn \c_@@_generics_reversed_iii_prop {after=} +\prop_const_from_keyval:Nn \c_@@_generics_file_prop {before=,after=} +% \end{macrocode} +% \end{macro} + + +% \begin{macro}{\@@_update_hook_code:n} +% Before \verb=\begin{document}= this does nothing, in the body it +% reinitializes the hook code using the altered data. +% \begin{macrocode} +\cs_new_eq:NN \@@_update_hook_code:n \use_none:n +% \end{macrocode} +% \end{macro} + + +% \begin{macro}{\hook_gremove_code:nn} +% \begin{macro}{\@@_gremove_code:nn} +% +% With \cs{hook_gremove_code:nn}\Arg{hook}\Arg{label} any code +% for \meta{hook} stored under \meta{label} is removed. +% \begin{macrocode} +\cs_new_protected:Npn \hook_gremove_code:nn #1 #2 + { + \exp_args:Nxx \@@_gremove_code:nn + { \@@_parse_label_default:nn {#1} { top-level } } + { \@@_parse_label_default:nn {#2} { top-level } } + } +\cs_new_protected:Npn \@@_gremove_code:nn #1 #2 + { +% \end{macrocode} +% First check that the hook code pool exists. \cs{hook_if_exist: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} +% \end{macrocode} +% Then remove the chunk and run \cs{@@_update_hook_code:n} so +% that the execution token list reflects the change if we are after +% \verb=\begin{document}=. +% \begin{macrocode} + { + \str_if_eq:nnTF {#2} {*} + { + \prop_gclear:c { g_@@_#1_code_prop } + \clist_gclear:c { g_@@_#1_labels_clist } % for debugging only + } + { +% \end{macrocode} +% Check if the label being removed exists in the code pool. If it does, +% just call \cs{@@_gremove_code_do:nn} to do the removal, otherwise mark it +% to be removed. +% \begin{macrocode} + \prop_get:cnNTF { g_@@_#1_code_prop } {#2} \l_@@_return_tl + { \@@_gremove_code_do:nn } + { \@@_mark_removal:nn } + {#1} {#2} + } +% \end{macrocode} +% Finally update the code, if the hook exists. +% \begin{macrocode} + \hook_if_exist:nT {#1} + { \@@_update_hook_code:n {#1} } + } + { \@@_mark_removal:nn {#1} {#2} } + } +% \end{macrocode} +% +% \begin{macro}{\@@_gremove_code_do:nn} +% \begin{macrocode} +\cs_new_protected:Npn \@@_gremove_code_do:nn #1 #2 + { + \prop_gremove:cn { g_@@_#1_code_prop } {#2} +% \end{macrocode} +% Removing the dropped label from \verb=\g_@@_#1_labels_clist= is +% rather tricky, because that clists holds the labels as strings +% (i.e., not ordinary text which is what we have in \verb=#2=). +% \begin{macrocode} + \exp_args:Nco \clist_gremove_all:Nn + { g_@@_#1_labels_clist } { \tl_to_str:n {#2} } % for debugging only + } +% \end{macrocode} +% \end{macro} +% \end{macro} +% \end{macro} + + +% +% \begin{macro}{\@@_mark_removal:nn} +% Marks \meta{label} (\verb=#2=) to be removed from \meta{hook} +% (\verb=#1=). +% \begin{macrocode} +\cs_new_protected:Npn \@@_mark_removal:nn #1 #2 + { + \tl_gput_right:Nx \g_@@_removal_list_tl + { \@@_removal_tl:nn {#1} {#2} } + } +% \end{macrocode} +% \end{macro} +% +% \begin{macro}{\@@_unmark_removal:nn} +% Unmarks \meta{label} (\verb=#2=) to be removed from \meta{hook} +% (\verb=#1=). \cs{tl_gremove_once:Nx} is used rather than +% \cs{tl_gremove_all:Nx} so that two additions are needed to cancel +% two marked removals, rather than only one. +% \begin{macrocode} +\cs_new_protected:Npn \@@_unmark_removal:nn #1 #2 + { + \tl_gremove_once:Nx \g_@@_removal_list_tl + { \@@_removal_tl:nn {#1} {#2} } + } +% \end{macrocode} +% \end{macro} +% +% \begin{macro}[TF]{\@@_if_marked_removal:nn} +% Checks if the \cs{g_@@_removal_list_tl} contains the current +% \meta{label} (\verb=#2=) and \meta{hook} (\verb=#1=). +% \begin{macrocode} +\prg_new_protected_conditional:Npnn \@@_if_marked_removal:nn #1 #2 { TF } + { + \exp_args:NNx \tl_if_in:NnTF \g_@@_removal_list_tl + { \@@_removal_tl:nn {#1} {#2} } + { \prg_return_true: } { \prg_return_false: } + } +% \end{macrocode} +% \end{macro} +% +% \begin{macro}[rEXP]{\@@_removal_tl:nn} +% Builds a token list with \verb=#1= and \verb=#2= which can only be +% matched by \verb=#1= and \verb=#2=. +% \begin{macrocode} +\cs_new:Npn \@@_removal_tl:nn #1 #2 + { & \tl_to_str:n {#2} $ \tl_to_str:n {#1} $ } +% \end{macrocode} +% \end{macro} + + + + +% +% +% +% \begin{macro}{ +% \g_@@_??_rules_prop, +% \g_@@_??_code_prop, +% \g_@@_??_code_tl, +% \g_@@_??_reversed_tl, +% } +% +% Default rules applying to all hooks are stored in this property +% list. Initially it simply used an empty ``label'' name (not two +% question marks). This was a bit unfortunate, because then +% \texttt{l3doc} complains about \verb=__= in the middle of a +% command name when trying to typeset the documentation. However +% using a ``normal'' name such as \texttt{default} has the +% 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} +% expansion in the code later on. +% +% \cs{g_@@_??_code_tl} isn't used, but it has to be defined to trick +% the code into thinking that \verb=??= is actually a hook. +% \begin{macrocode} +\prop_new:c {g_@@_??_rules_prop} +\prop_new:c {g_@@_??_code_prop} +\prop_new:c {g_@@_??_code_tl} +% \end{macrocode} +% +% Default rules are always given in normal ordering (never in +% reversed ordering). If such a rule is applied to a reversed +% hook it behaves as if the rule is reversed (e.g., +% \texttt{after} becomes \texttt{before}) +% because those rules are applied first and then the order is reversed. +% \begin{macrocode} +\tl_new:c {g_@@_??_reversed_tl} +% \end{macrocode} +% \end{macro} + + + +% \begin{macro}{\@@_debug_gset_rule:nnnn} +% +% \fmi{this needs cleanup and docu correction!} +% +% With +% \cs{@@_debug_gset_rule:nnnn}\Arg{hook}\Arg{label1}\Arg{relation}\Arg{label2} +% a relation is defined between the two code labels for the given +% \meta{hook}. The special hook \texttt{??} stands for \emph{any} +% hook describing a default rule. +% \begin{macrocode} +\cs_new_protected:Npn \@@_debug_gset_rule:nnnn #1#2#3#4 + { +% \end{macrocode} +% If so we drop any existing rules with the two labels (in case +% there are any). +% \begin{macrocode} + \prop_gremove:cn{g_@@_#1_rules_prop}{#2|#4} + \prop_gremove:cn{g_@@_#1_rules_prop}{#4|#2} +% \end{macrocode} +% Then we add the new one (normalizing the input a bit, e.g., we +% always use \texttt{before} and not \texttt{after} and +% instead reorder the labels): +% \begin{macrocode} + \str_case_e:nnF {#3} + { + {before} { \prop_gput:cnn {g_@@_#1_rules_prop}{#2|#4}{<} } + {after} { \prop_gput:cnn {g_@@_#1_rules_prop}{#4|#2}{<} } +% \end{macrocode} +% More special rule types \ldots +% \begin{macrocode} + {incompatible-error} { \prop_gput:cnn {g_@@_#1_rules_prop}{#2|#4}{xE} } + {incompatible-warning} { \prop_gput:cnn {g_@@_#1_rules_prop}{#2|#4}{xW} } + {removes} { \prop_gput:cnn {g_@@_#1_rules_prop}{#2|#4}{->} } +% \end{macrocode} +% Undo a setting: +% \begin{macrocode} + {unrelated}{ \prop_gremove:cn {g_@@_#1_rules_prop}{#2|#4} + \prop_gremove:cn {g_@@_#1_rules_prop}{#4|#2} } + } + { \ERRORunknownrule } + } +% \end{macrocode} +% \end{macro} +% +% +% \subsection{Setting rules for hooks code} +% +% \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. +% \begin{macrocode} +\cs_new_protected:Npn \hook_gset_rule:nnnn #1#2#3#4 + { + \use:x + { + \@@_gset_rule:nnnn + { \@@_parse_label_default:nn {#1} { top-level } } + { \@@_parse_label_default:nn {#2} { top-level } } + {#3} + { \@@_parse_label_default:nn {#4} { top-level } } + } + } +% \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} +% \end{macrocode} +% Then we clear any previous relationship between both labels. +% \begin{macrocode} + \@@_rule_gclear:nnn {#1} {#2} {#4} +% \end{macrocode} +% Then we call the function to handle the given rule. Throw an error if the +% rule is invalid. +% \begin{macrocode} + \debug_suspend: + \cs_if_exist_use:cTF { @@_rule_#3_gset:nnn } + { + {#1} {#2} {#4} + \@@_update_hook_code:n {#1} + } + { \ERRORunknownrule } + \debug_resume: + \@@_debug_gset_rule:nnnn {#1} {#2} {#3} {#4} % for debugging + } +% \end{macrocode} +% \end{macro} +% \end{macro} +% +% \begin{macro}{\@@_rule_before_gset:nnn, \@@_rule_after_gset:nnn, +% \@@_rule_<_gset:nnn, \@@_rule_>_gset:nnn} +% Then we add the new rule. We need to normalize the rules here to +% allow for faster processing later. Given a pair of labels +% $l_A$ and $l_B$, the rule $l_A>l_B$ is the same as $l_B<l_A$\fmi{} +% said differently. But normalizing the +% forms of the rule to a single representation, say, $l_B<l_A$, then +% the time spent looking for the rules later is considerably reduced. +% +% Here we do that normalization by using \cs[no-index]{(pdf)strcmp} to +% lexically sort labels $l_A$ and $l_B$ to a fixed order. This order +% is then enforced every time these two labels are used together. +% +% Here we use \cs{@@_label_pair:nn}~\Arg{hook}~\Arg{l_A}~\Arg{l_B} +% to build a string \texttt{$l_B$\string|$l_A$} with a fixed order, and +% use \cs{@@_label_ordered:nnTF} to apply the correct rule to the pair +% of labels, depending if it was sorted or not. +% \begin{macrocode} +\cs_new_protected:Npn \@@_rule_before_gset:nnn #1#2#3 + { + \tl_gset:cx { g_@@_#1_rule_ \@@_label_pair:nn {#2} {#3} _tl } + { \@@_label_ordered:nnTF {#2} {#3} { < } { > } } + } +\cs_new_eq:cN { @@_rule_<_gset:nnn } \@@_rule_before_gset:nnn +% \end{macrocode} +% +% \begin{macrocode} +\cs_new_protected:Npn \@@_rule_after_gset:nnn #1#2#3 + { + \tl_gset:cx { g_@@_#1_rule_ \@@_label_pair:nn {#3} {#2} _tl } + { \@@_label_ordered:nnTF {#3} {#2} { < } { > } } + } +\cs_new_eq:cN { @@_rule_>_gset:nnn } \@@_rule_after_gset:nnn +% \end{macrocode} +% \end{macro} +% +% \begin{macro}{\@@_rule_removes_gset:nnn} +% This rule removes (clears, actually) the code from label |#3| if +% label |#2| is in the hook |#1|. +% \begin{macrocode} +\cs_new_protected:Npn \@@_rule_removes_gset:nnn #1#2#3 + { + \tl_gset:cx { g_@@_#1_rule_ \@@_label_pair:nn {#2} {#3} _tl } + { \@@_label_ordered:nnTF {#2} {#3} { -> } { <- } } + } +% \end{macrocode} +% \end{macro} +% +% \begin{macro}{ +% \@@_rule_incompatible-error_gset:nnn, +% \@@_rule_incompatible-warning_gset:nnn, +% } +% These relations make an error/warning if labels |#2| and |#3| appear +% together in hook |#1|. +% \begin{macrocode} +\cs_new_protected:cpn { @@_rule_incompatible-error_gset:nnn } #1#2#3 + { \tl_gset:cn { g_@@_#1_rule_ \@@_label_pair:nn {#2} {#3} _tl } { xE } } +\cs_new_protected:cpn { @@_rule_incompatible-warning_gset:nnn } #1#2#3 + { \tl_gset:cn { g_@@_#1_rule_ \@@_label_pair:nn {#2} {#3} _tl } { xW } } +% \end{macrocode} +% \end{macro} +% +% \begin{macro}{\@@_rule_unrelated_gset:nnn, \@@_rule_gclear:nnn} +% Undo a setting. \cs{@@_rule_unrelated_gset:nnn} doesn't need to do anything, +% since we use \cs{@@_rule_gclear:nnn} before setting any rule. +% \begin{macrocode} +\cs_new_protected:Npn \@@_rule_unrelated_gset:nnn #1#2#3 { } +\cs_new_protected:Npn \@@_rule_gclear:nnn #1#2#3 + { \cs_undefine:c { g_@@_#1_rule_ \@@_label_pair:nn {#2} {#3} _tl } } +% \end{macrocode} +% \end{macro} +% +% \begin{macro}[EXP]{\@@_label_pair:nn} +% Ensure that the lexically greater label comes first. +% \begin{macrocode} +\cs_new:Npn \@@_label_pair:nn #1#2 + { + \if_case:w \@@_str_compare:nn {#1} {#2} \exp_stop_f: + #1 | #1 % 0 + \or: #1 | #2 % +1 + \else: #2 | #1 % -1 + \fi: + } +% \end{macrocode} +% \end{macro} +% +% \begin{macro}[pTF]{\@@_label_ordered:nn} +% Check that labels |#1| and |#2| are in the correct order (as +% returned by \cs{@@_label_pair:nn}) and if so return true, else +% return false. +% \begin{macrocode} +\prg_new_conditional:Npnn \@@_label_ordered:nn #1#2 { TF } + { + \if_int_compare:w \@@_str_compare:nn {#1} {#2} > 0 \exp_stop_f: + \prg_return_true: + \else + \prg_return_false: + \fi: + } +% \end{macrocode} +% \end{macro} +% +% \begin{macro}[EXP]{\@@_if_label_case:nnnnn} +% To avoid doing the string comparison twice in \cs{@@_initialize_single:NNNNn} +% (once with \cs{str_if_eq:nn} and again with \cs{@@_label_ordered:nn}), +% we use a three-way branching macro that will compare |#1| and |#2| +% and expand to \cs{use_i:nnn} if they are equal, \cs{use_ii:nn} if +% |#1| is lexically greater, and \cs{use_iii:nn} otherwise. +% \begin{macrocode} +\cs_new:Npn \@@_if_label_case:nnnnn #1#2 + { + \cs:w use_ + \if_case:w \@@_str_compare:nn {#1} {#2} + i \or: ii \else: iii \fi: :nnn + \cs_end: + } +% \end{macrocode} +% \end{macro} + + +% \begin{macro}{\@@_initialize_all:} +% Initialize all known hooks (at \verb=\begin{document}=), i.e., +% update the fast execution token lists to hold the necessary code +% in the right order. +% \begin{macrocode} +\cs_new_protected:Npn \@@_initialize_all: { +% \end{macrocode} +% First we change \cs{@@_update_hook_code:n} which so far was a +% no-op to now initialize one hook. This way any later updates to +% the hook will run that code and also update the execution token +% list. +% \begin{macrocode} + \cs_gset_eq:NN \@@_update_hook_code:n \@@_initialize_hook_code:n +% \end{macrocode} +% Now we loop over all hooks that have been defined and update each +% of them. +% \begin{macrocode} + \@@_debug:n { \prop_gclear:N \g_@@_used_prop } + \seq_map_inline:Nn \g_@@_all_seq + { + \@@_update_hook_code:n {##1} + } +% \end{macrocode} +% If we are debugging we show results hook by hook for all hooks +% that have data. +% \begin{macrocode} + \@@_debug:n + { \iow_term:x{^^JAll~ initialized~ (non-empty)~ hooks:} + \prop_map_inline:Nn \g_@@_used_prop + { \iow_term:x{^^J~ ##1~ ->~ + \exp_not:v {g_@@_##1_code_tl}~ } + } + } +% +% \end{macrocode} +% After all hooks are initialized we change the ``use'' to just +% call the hook code and not initialize it (as it was done in the +% preamble. +% \begin{macrocode} + \cs_gset_eq:NN \hook_use:n \@@_use_initialized:n + \cs_gset_eq:NN \@@_preamble_hook:n \use_none:n +} +% \end{macrocode} +% \end{macro} + + + +% \begin{macro}{\@@_initialize_hook_code:n} +% Initializing or reinitializing the fast execution hook code. In +% the preamble this is selectively done in case a hook gets used +% and at \verb=\begin{document}= this is done for all hooks and +% afterwards only if the hook code changes. +% \begin{macrocode} +\cs_new_protected:Npn \@@_initialize_hook_code:n #1 { + \@@_debug:n{ \iow_term:x{^^JUpdate~ code~ for~ hook~ + '#1' \on@line :^^J} } +% \end{macrocode} +% This does the sorting and the updates. If there aren't any code +% chunks for the current hook, there is no point in even starting +% the sorting routine so we make a quick test for that and in that +% case just update \cs{g_@@_\meta{hook}_code_tl} to hold the next +% code. If there are code chunks we call +% \cs{@@_initialize_single:NNNNn} and pass to it ready made csnames +% 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} + { + \prop_if_empty:cTF {g_@@_#1_code_prop} + { \tl_gset:co {g_@@_#1_code_tl} + {\cs:w g_@@_#1_next_code_tl \cs_end: } } + { +% \end{macrocode} +% By default the algorithm sorts the code chunks and then saves the +% result in a token list for fast execution by adding the code one +% after another using \cs{tl_gput_right:NV}. When we sort code for +% a reversed hook, all we have to do is to add the code chunks in +% the opposite order into the token list. So all we have to do +% in preparation is to change two definitions used later on. +% \begin{macrocode} + \@@_if_reversed:nTF {#1} + { \cs_set_eq:NN \@@_tl_gput:NV \tl_gput_left:NV + \cs_set_eq:NN \@@_clist_gput:NV \clist_gput_left:NV } + { \cs_set_eq:NN \@@_tl_gput:NV \tl_gput_right:NV + \cs_set_eq:NN \@@_clist_gput:NV \clist_gput_right:NV } +% \end{macrocode} +% +% When sorting, some relations (namely \verb|->| \verb|<-|) need to +% act destructively on the code property lists to remove code that +% shouldn't appear in the sorted hook token list. +% \begin{macrocode} + \prop_gset_eq:Nc \g_@@_code_temp_prop { g_@@_#1_code_prop } + \@@_initialize_single:ccccn + { g_@@_#1_code_prop } { g_@@_#1_code_tl } + { g_@@_#1_next_code_tl } { g_@@_#1_labels_clist } + {#1} + \prop_gset_eq:cN { g_@@_#1_code_prop } \g_@@_code_temp_prop +% \end{macrocode} +% For debug display we want to keep track of those hooks that +% actually got code added to them, so we record that in plist. We +% use a plist to ensure that we record each hook name only once, +% i.e., we are only interested in storing the keys and the value is arbitrary +% \begin{macrocode} + \@@_debug:n{ \exp_args:NNx \prop_gput:Nnn \g_@@_used_prop {#1}{} } + } + } +} +% \end{macrocode} +% \end{macro} + + +% \begin{macro}{\g_@@_used_prop} +% All hooks that receive code (for use in debugging display). +% \begin{macrocode} +\prop_new:N\g_@@_used_prop +% \end{macrocode} +% \end{macro} + + +% +% \begin{macro}[EXP]{\@@_tl_csname:n,\@@_seq_csname:n} +% It is faster to pass a single token and expand it when necessary +% than to pass a bunch of character tokens around. +% \fmi{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 } +% \end{macrocode} +% \end{macro} +% + +% +% +% \begin{macro}{\l_@@_labels_seq,\l_@@_labels_int,\l_@@_front_tl, +% \l_@@_rear_tl,\l_@@_label_0_tl} +% +% For the sorting I am basically implementing Knuth's algorithm for +% topological sorting as given in TAOCP volume 1 pages 263--266. +% For this algorithm we need a number of local variables: +% \begin{itemize} +% \item +% List of labels used in the current hook to label code chunks: +% \begin{macrocode} +\seq_new:N \l_@@_labels_seq +% \end{macrocode} +% \item +% Number of labels used in the current hook. In Knuth's algorithm +% this is called $N$: +% \begin{macrocode} +\int_new:N \l_@@_labels_int +% \end{macrocode} +% \item +% The sorted code list to be build is managed using two pointers +% one to the front of the queue and one to the rear. We model this +% using token list pointers. Knuth calls them $F$ and $R$: +% \begin{macrocode} +\tl_new:N \l_@@_front_tl +\tl_new:N \l_@@_rear_tl +% \end{macrocode} +% \item +% The data for the start of the queue is kept in this token list, +% it corresponds to what Don calls \texttt{QLINK[0]} but since we +% aren't manipulating individual words in memory it is slightly +% differently done: +% \begin{macrocode} +\tl_new:c { \@@_tl_csname:n { 0 } } +% \end{macrocode} +% +% \end{itemize} +% \end{macro} + + +% \begin{macro}{\@@_initialize_single:NNNNn,\@@_initialize_single:ccccn} +% +% \cs{@@_initialize_single:NNNNn} implements the sorting of the code +% chunks for a hook and saves the result in the token list for fast +% execution (\verb=#3=). The arguments are \meta{hook-code-plist}, +% \meta{hook-code-tl}, \meta{hook-next-code-tl}, +% \meta{hook-ordered-labels-clist} and \meta{hook-name} (the latter +% is only used for debugging---the \meta{hook-rule-plist} is accessed +% using the \meta{hook-name}). +% +% The additional complexity compared to Don's algorithm is that we +% do not use simple positive integers but have arbitrary +% alphanumeric labels. As usual Don's data structures are chosen in +% a way that one can omit a lot of tests and I have mimicked that as +% far as possible. The result is a restriction I do not test for at +% the moment: a label can't be equal to the number 0! \fmi{Needs +% checking for, just in case} +% \begin{macrocode} +\cs_new_protected:Npn \@@_initialize_single:NNNNn #1#2#3#4#5 { +% \end{macrocode} +% +% \begin{macrocode} + \debug_suspend: +% \end{macrocode} +% Step T1: Initialize the data structure \ldots +% \begin{macrocode} + \seq_clear:N \l_@@_labels_seq + \int_zero:N \l_@@_labels_int +% \end{macrocode} +% +% Store the name of the hook: +% \begin{macrocode} + \tl_set:Nn \l_@@_cur_hook_tl {#5} +% \end{macrocode} +% +% We loop over the property list holding the code and record all +% labels listed there. Only rules for those labels are of interest +% to us. While we are at it we count them (which gives us the $N$ +% in Knuth's algorithm. The prefix |label_| is added to the variables +% to ensure that labels named |front|, |rear|, |labels|, or |return| +% don't interact with our code. +% \begin{macrocode} + \prop_map_inline:Nn #1 + { + \int_incr:N \l_@@_labels_int + \seq_put_right:Nn \l_@@_labels_seq {##1} + \tl_set:cn { \@@_tl_csname:n {##1} }{0} % the counter k for number of + % j before k rules + \seq_clear_new:c { \@@_seq_csname:n {##1} } % sequence of successors to k + % i.e., k before j rules (stores + % the names of the j's) + } +% \end{macrocode} +% Steps T2 and T3: Sort the relevant rules into the data structure\ldots +% +% This loop constitutes a square matrix of the labels in |#1| in the +% vertical and the horizontal directions. However since the rule +% $l_A\meta{rel}l_B$ is the same as $l_B\meta{rel}^{-1}l_A$ we can cut +% the loop short at the diagonal of the matrix (\emph{i.e.}, when +% both labels are equal), saving a good amount of time. The way the +% rules were set up (see the implementation of \cs{@@_rule_before_gset:nnn} +% above) ensures that we have no rule in the ignored side of the +% matrix, and all rules are seen. The rules are applied in +% \cs{@@_apply_label_pair:nnn}, which takes the properly-ordered pair +% of labels as argument. +% \begin{macrocode} + \prop_map_inline:Nn #1 + { + \prop_map_inline:Nn #1 + { + \@@_if_label_case:nnnnn {##1} {####1} + { \prop_map_break: } + { \@@_apply_label_pair:nnn {##1} {####1} } + { \@@_apply_label_pair:nnn {####1} {##1} } + {#5} + } + } +% \end{macrocode} +% Take a breath and take a look at the data structures that have +% been set up: +% \begin{macrocode} + \@@_debug:n { \@@_debug_label_data:N #1 } +% \end{macrocode} +% +% +% Step T4: +% \begin{macrocode} + \tl_set:Nn \l_@@_rear_tl { 0 } + \tl_set:cn { \@@_tl_csname:n { 0 } } { 0 } % really {l_@@_label_ \l_@@_rear_tl _tl} + \seq_map_inline:Nn \l_@@_labels_seq + { + \int_compare:nNnT { \cs:w \@@_tl_csname:n {##1} \cs_end: } = 0 + { + \tl_set:cn { \@@_tl_csname:n { \l_@@_rear_tl } }{##1} + \tl_set:Nn \l_@@_rear_tl {##1} + } + } + \tl_set_eq:Nc \l_@@_front_tl { \@@_tl_csname:n { 0 } } +% \end{macrocode} +% +% \begin{macrocode} + \tl_gclear:N #2 + \clist_gclear:N #4 +% \end{macrocode} +% +% The whole loop combines steps T5--T7: +% \begin{macrocode} + \bool_while_do:nn { ! \str_if_eq_p:Vn \l_@@_front_tl { 0 } } + { +% \end{macrocode} +% This part is step T5: +% \begin{macrocode} + \int_decr:N \l_@@_labels_int + \prop_get:NVN #1 \l_@@_front_tl \l_@@_return_tl + \@@_tl_gput:NV #2 \l_@@_return_tl +% \end{macrocode} +% +% \begin{macrocode} + \@@_clist_gput:NV #4 \l_@@_front_tl + \@@_debug:n{ \iow_term:x{Handled~ code~ for~ \l_@@_front_tl} } +% \end{macrocode} +% +% This is step T6 except that we don't use a pointer $P$ to move +% through the successors, but instead use \verb=##1= of the mapping +% function. +% \begin{macrocode} + \seq_map_inline:cn { \@@_seq_csname:n { \l_@@_front_tl } } + { + \tl_set:cx { \@@_tl_csname:n {##1} } + { \int_eval:n { \cs:w \@@_tl_csname:n {##1} \cs_end: - 1 } } + \int_compare:nNnT { \cs:w \@@_tl_csname:n {##1} \cs_end: } = 0 + { + \tl_set:cn { \@@_tl_csname:n { \l_@@_rear_tl } } {##1} + \tl_set:Nn \l_@@_rear_tl {##1} + } + } +% \end{macrocode} +% and step T7: +% \begin{macrocode} + \tl_set_eq:Nc \l_@@_front_tl { \@@_tl_csname:n { \l_@@_front_tl } } +% \end{macrocode} +% +% This is step T8: If we haven't moved the code for all labels +% (i.e., if \cs{l_@@_labels_int} is still greater than zero) we +% have a loop and our partial order can't be flattened out. +% \begin{macrocode} + } + \int_compare:nNnF \l_@@_labels_int = 0 + { + \iow_term:x{====================} + \iow_term:x{Error:~ label~ rules~ are~ incompatible:} +% \end{macrocode} +% +% This is not really the information one needs in the error case +% but will do for now \ldots \fmi{fix} +% \begin{macrocode} + \@@_debug_label_data:N #1 + \iow_term:x{====================} + } +% \end{macrocode} +% After we have added all hook code to \verb=#2= we finish it off +% with adding extra code for a one time execution. That is stored +% in \verb=#3= but is normally empty. +% \begin{macrocode} + \tl_gput_right:Nn #2 {#3} +% \end{macrocode} +% +% \begin{macrocode} + \debug_resume: +} +% \end{macrocode} +% +% \begin{macrocode} +\cs_generate_variant:Nn \@@_initialize_single:NNNNn {cccc} +% \end{macrocode} +% \end{macro} + + + +% \begin{macro}{\@@_tl_gput:NV,\@@_clist_gput:NV} +% These append either on the right (normal hook) or on the left +% (reversed hook). This is setup up in +% \cs{@@_initialize_hook_code:n}, elsewhere their behavior is undefined. +% \begin{macrocode} +\cs_new:Npn \@@_tl_gput:NV {\ERROR} +\cs_new:Npn \@@_clist_gput:NV {\ERROR} +% \end{macrocode} +% \end{macro} + + +% +% \begin{macro}{\@@_apply_label_pair:nnn,\@@_label_if_exist_apply:nnnF} +% +% This is the payload of steps T2 and T3 executed in the loop described +% above. This macro assumes |#1| and |#2| are ordered, which means that +% any rule pertaining the pair |#1| and |#2| is +% \cs{g_@@_\meta{hook}_rule_\#1\string|\#2_tl}, and not +% \cs{g_@@_\meta{hook}_rule_\#2\string|\#1_tl}. This also saves a great deal +% of time since we only need to check the order of the labels once. +% +% The arguments here are \meta{label1}, \meta{label2}, \meta{hook}, and +% \meta{hook-code-plist}. We are about to apply the next rule and +% enter it into the data structure. \cs{@@_apply_label_pair:nnn} will +% just call \cs{@@_label_if_exist_apply:nnnF} for the \meta{hook}, and +% if no rule is found, also try the \meta{hook} name \verb=??= +% denoting a default hook rule. +% +% \cs{@@_label_if_exist_apply:nnnF} will check if the rule exists for +% the given hook, and if so call \cs{@@_apply_rule:nnn}. +% \begin{macrocode} +\cs_new_protected:Npn \@@_apply_label_pair:nnn #1#2#3 + { +% \end{macrocode} +% Extra complication: as we use default rules and local hook specific +% rules we first have to check if there is a local rule and if that +% exist use it. Otherwise check if there is a default rule and use +% that. +% \begin{macrocode} + \@@_label_if_exist_apply:nnnF {#1} {#2} {#3} + { +% \end{macrocode} +% If there is no hook-specific rule we check for a default one and +% use that if it exists. +% \begin{macrocode} + \@@_label_if_exist_apply:nnnF {#1} {#2} { ?? } { } + } + } +\cs_new_protected:Npn \@@_label_if_exist_apply:nnnF #1#2#3 + { + \if_cs_exist:w g_@@_ #3 _rule_ #1 | #2 _tl \cs_end: +% \end{macrocode} +% What to do precisely depends on the type of rule we have +% encountered. If it is a \texttt{before} rule it will be handled by the +% algorithm but other types need to be managed differently. All +% this is done in \cs{@@_apply_rule:nnnN}. +% \begin{macrocode} + \@@_apply_rule:nnn {#1} {#2} {#3} + \exp_after:wN \use_none:n + \else: + \use:nn + \fi: + } +% \end{macrocode} +% \end{macro} + + + + +% \begin{macro}{\@@_apply_rule:nnn} +% This is the code executed in steps T2 and T3 while looping through +% the matrix This is part of step T3. We are about to apply the next +% rule and enter it into the data structure. The arguments are +% \meta{label1}, \meta{label2}, \meta{hook-name}, and \meta{hook-code-plist}. +% \begin{macrocode} +\cs_new_protected:Npn \@@_apply_rule:nnn #1#2#3 + { + \cs:w @@_apply_ + \cs:w g_@@_#3_reversed_tl \cs_end: rule_ + \cs:w g_@@_ #3 _rule_ #1 | #2 _tl \cs_end: :nnn \cs_end: + {#1} {#2} {#3} + } +% \end{macrocode} +% \end{macro} +% +% \begin{macro}{\@@_apply_rule_<:nnn,\@@_apply_rule_>:nnn} +% The most common cases are \texttt{\string<} and \texttt{\string>} so we handle +% that first. They are relations $\prec$ and $\succ$ in TAOCP, and +% they dictate sorting. +% \begin{macrocode} +\cs_new_protected:cpn { @@_apply_rule_<:nnn } #1#2#3 + { + \@@_debug:n { \@@_msg_pair_found:nnn {#1} {#2} {#3} } + \tl_set:cx { \@@_tl_csname:n {#2} } + { \int_eval:n{ \cs:w \@@_tl_csname:n {#2} \cs_end: + 1 } } + \seq_put_right:cn{ \@@_seq_csname:n {#1} }{#2} + } +\cs_new_protected:cpn { @@_apply_rule_>:nnn } #1#2#3 + { + \@@_debug:n { \@@_msg_pair_found:nnn {#1} {#2} {#3} } + \tl_set:cx { \@@_tl_csname:n {#1} } + { \int_eval:n{ \cs:w \@@_tl_csname:n {#1} \cs_end: + 1 } } + \seq_put_right:cn{ \@@_seq_csname:n {#2} }{#1} + } +% \end{macrocode} +% \end{macro} +% +% \begin{macro}{\@@_apply_rule_xE:nnn,\@@_apply_rule_xW:nnn} +% These relations make two labels incompatible within a hook. +% |xE| makes raises an error if the labels are found in the same +% hook, and |xW| makes it a warning. +% \begin{macrocode} +\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 } + {#1} {#2} {#3} { 1 } + \use:c { @@_apply_rule_->:nnn } {#1} {#2} {#3} + \use:c { @@_apply_rule_<-:nnn } {#1} {#2} {#3} + } +\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 } + {#1} {#2} {#3} { 0 } + } +% \end{macrocode} +% \end{macro} +% +% \begin{macro}{\@@_apply_rule_->:nnn,\@@_apply_rule_<-:nnn} +% If we see \texttt{\detokenize{->}} we have to drop code for label +% \verb=#3= and carry on. We could do a little better and trop +% everything for that label since it doesn't matter where we sort +% in the empty code. However that would complicate the algorithm a +% lot with little gain. So we still unnecessarily try to sort it in +% and depending on the rules that might result in a loop that is +% otherwise resolved. If that turns out to be a real issue, we can +% improve the code. +% +% Here the code is removed from \cs{l_@@_cur_hook_tl} rather than +% \verb=#3= because the latter may be \verb=??=, and the default +% hook doesn't store any code. Removing from \cs{l_@@_cur_hook_tl} +% makes default rules \verb=->= and \verb=<-= work properly. +% \begin{macrocode} +\cs_new_protected:cpn { @@_apply_rule_->:nnn } #1#2#3 + { + \@@_debug:n + { + \@@_msg_pair_found:nnn {#1} {#2} {#3} + \iow_term:x{--->~ Drop~ '#2'~ code~ from~ + \iow_char:N \\ g_@@_ \l_@@_cur_hook_tl _code_prop ~ because~ of~ '#1' } + } + \prop_gput:cnn { g_@@_ \l_@@_cur_hook_tl _code_prop } {#2} { } + } +\cs_new_protected:cpn { @@_apply_rule_<-:nnn } #1#2#3 + { + \@@_debug:n + { + \@@_msg_pair_found:nnn {#1} {#2} {#3} + \iow_term:x{--->~ Drop~ '#1'~ code~ from~ + \iow_char:N \\ g_@@_ \l_@@_cur_hook_tl _code_prop ~ because~ of~ '#2' } + } + \prop_gput:cnn { g_@@_ \l_@@_cur_hook_tl _code_prop } {#1} { } + } +% \end{macrocode} +% \end{macro} + +% \begin{macro}{ +% \@@_apply_-rule_<:nnn, +% \@@_apply_-rule_>:nnn, +% \@@_apply_-rule_<-:nnn, +% \@@_apply_-rule_->:nnn, +% \@@_apply_-rule_x:nnn, +% } +% Reversed rules. +% \begin{macrocode} +\cs_new_eq:cc { @@_apply_-rule_<:nnn } { @@_apply_rule_>:nnn } +\cs_new_eq:cc { @@_apply_-rule_>:nnn } { @@_apply_rule_<:nnn } +\cs_new_eq:cc { @@_apply_-rule_<-:nnn } { @@_apply_rule_<-:nnn } +\cs_new_eq:cc { @@_apply_-rule_->:nnn } { @@_apply_rule_->:nnn } +\cs_new_eq:cc { @@_apply_-rule_xE:nnn } { @@_apply_rule_xE:nnn } +\cs_new_eq:cc { @@_apply_-rule_xW:nnn } { @@_apply_rule_xW:nnn } +% \end{macrocode} +% \end{macro} + + +% \begin{macro}{\@@_msg_pair_found:nnn} +% A macro to avoid moving this many tokens around. +% \begin{macrocode} +\cs_new_protected:Npn \@@_msg_pair_found:nnn #1#2#3 + { + \iow_term:x{~ \str_if_eq:nnTF {#3} {??} {default} {~normal} ~ + rule~ \@@_label_pair:nn {#1} {#2}:~ + \use:c { g_@@_#3_rule_ \@@_label_pair:nn {#1} {#2} _tl } ~ found} + } +% \end{macrocode} +% \end{macro} + + +% \begin{macro}{\@@_debug_label_data:N} +% +% \begin{macrocode} +\cs_new_protected:Npn \@@_debug_label_data:N #1 { + \iow_term:x{Code~ labels~ for~ sorting:} + \iow_term:x{~ \seq_use:Nnnn\l_@@_labels_seq {~and~}{,~}{~and~} } % fix name! + \iow_term:x{^^J Data~ structure~ for~ label~ rules:} + \prop_map_inline:Nn #1 + { + \iow_term:x{~ ##1~ =~ \tl_use:c{ \@@_tl_csname:n {##1} }~ ->~ + \seq_use:cnnn{ \@@_seq_csname:n {##1} }{~->~}{~->~}{~->~} + } + } + \iow_term:x{} +} +% \end{macrocode} +% \end{macro} + + + +% \begin{macro}{\hook_log:n} +% This writes out information about the hook given in its argument +% onto the terminal and the \texttt{.log} file. +% \begin{macrocode} +\cs_new_protected:Npn \hook_log:n #1 + { + \exp_args:Nx \@@_log:n + { \@@_parse_label_default:nn {#1} { top-level } } + } +\cs_new_protected:Npn \@@_log:n #1 + { + \iow_term:x{^^JThe~ hook~ '#1':} +% \end{macrocode} +% +% \begin{macrocode} + \hook_if_exist:nF {#1} + { \iow_term:x {~Hook~ is~ not~ declared!} } + \@@_if_exist:nTF {#1} + { + \iow_term:x{~Code~ chunks:} + \prop_if_empty:cTF {g_@@_#1_code_prop} + { \iow_term:x{\@spaces ---} } + { + \prop_map_inline:cn {g_@@_#1_code_prop} + { \iow_term:x{\@spaces ##1~ ->~ \tl_to_str:n{##2} } } + } +% \end{macrocode} +% +% \begin{macrocode} + \iow_term:x{~Extra~ code~ next~ invocation:} + \iow_term:x{\@spaces + \tl_if_empty:cTF { g_@@_#1_next_code_tl } + {---} {->~ \str_use:c{g_@@_#1_next_code_tl} } } +% \end{macrocode} +% +% \fmi{This is currently only displaying the local rules, but it +% should also show the matching global rules!} +% +% \begin{macrocode} + \iow_term:x{~Rules:} + \prop_if_empty:cTF {g_@@_#1_rules_prop} + { \iow_term:x{\@spaces ---} } + { \prop_map_inline:cn {g_@@_#1_rules_prop} + { \iow_term:x{\@spaces ##1~ with~ relation~ ##2} } + } +% \end{macrocode} +% +% \begin{macrocode} + \hook_if_exist:nT {#1} + { \iow_term:x { ~Execution~ order + \prop_if_empty:cTF {g_@@_#1_rules_prop} + { \@@_if_reversed:nT {#1} + { ~ (after~ reversal) } + } + { ~ (after~ + \@@_if_reversed:nT {#1} {reversal~ and~} + applying~ rules) + } + : + } + \iow_term:x { \@spaces + \clist_if_empty:cTF{g_@@_#1_labels_clist} + {not~ set~ yet} + { \clist_use:cnnn {g_@@_#1_labels_clist} + { ,~ } { ,~ } { ,~ } } + } + } + } + { \iow_term:n { ~The~hook~is~empty. } } + \iow_term:n { } + } +% \end{macrocode} +% +% \end{macro} + + + +% \subsection{Specifying code for next invocation} +% +% +% +% +% +% \begin{macro}{\hook_gput_next_code:nn} +% +% \begin{macrocode} +\cs_new_protected:Npn \hook_gput_next_code:nn #1 + { + \exp_args:Nx \@@_gput_next_code:nn + { \@@_parse_label_default:nn {#1} { top-level } } + } +\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} } + } +\cs_new_protected:Npn \@@_gput_next_do:nn #1 #2 + { + \tl_gput_right:cn { g_@@_#1_next_code_tl } + { #2 \tl_gclear:c { g_@@_#1_next_code_tl } } + } +% \end{macrocode} +% \end{macro} +% +% +% \subsection{Using the hook} +% +% \begin{macro}{\hook_use:n} +% \begin{macro}[EXP]{\@@_use_initialized:n} +% \begin{macro}{\@@_preamble_hook:n} +% \cs{hook_use:n} as defined here is used in the preamble, where +% hooks aren't initialized by default. \cs{@@_use_initialized:n} is +% also defined, which is the non-\tn{protected} version for use within +% the document. Their definition is identical, except for the +% \cs{@@_preamble_hook:n} (which wouldn't hurt in the expandable +% version, but it would be an unnecessary extra expansion). +% +% \cs{@@_use_initialized:n} holds the expandable definition while in +% the preamble. \cs{@@_preamble_hook:n} initializes the hook in the +% preamble, and is redefined to \cs{use_none:n} at |\begin{document}|. +% +% Both versions do the same internally: check if the hook exist as +% given, and if so use it as quickly as possible. If it doesn't +% exist, the a call to \cs{@@_use:wn} checks for file hooks. +% +% At |\begin{document}|, all hooks are initialized, and any change in +% them causes an update, so \cs{hook_use:n} can be made expandable. +% This one is better not protected so that it can expand into nothing +% if containing no code. Also important in case of generic hooks that +% we do not generate a \cs{relax} as a side effect of checking for a +% csname. In contrast to the \TeX{} low-level +% \verb=\csname ...\endcsname= construct \cs{tl_if_exist:c} is +% careful to avoid this. +% \begin{macrocode} +\cs_new_protected:Npn \hook_use:n #1 + { + \tl_if_exist:cTF { g_@@_#1_code_tl } + { + \@@_preamble_hook:n {#1} + \cs:w g_@@_#1_code_tl \cs_end: + } + { \@@_use:wn #1 / \s_@@_mark {#1} } + } +\cs_new:Npn \@@_use_initialized:n #1 + { + \tl_if_exist:cTF { g_@@_#1_code_tl } + { \cs:w g_@@_#1_code_tl \cs_end: } + { \@@_use:wn #1 / \s_@@_mark {#1} } + } +\cs_new_protected:Npn \@@_preamble_hook:n #1 + { \@@_initialize_hook_code:n {#1} } +% \end{macrocode} +% \end{macro} +% \end{macro} +% \end{macro} +% +% \begin{macro}[EXP]{\@@_use:wn} +% \begin{macro}{\@@_try_file_hook:n,\@@_if_exist_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, +% and checks that the current hook is a file-specific hook using +% \cs{@@_if_file_hook:wTF}. If it's not, then it's a generic |file/| +% hook and is used if it exist. +% +% 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{@@_preamble_hook:n} if so, then uses the hook. +% \begin{macrocode} +\cs_new:Npn \@@_use:wn #1 / #2 \s_@@_mark #3 + { + \str_if_eq:nnTF {#1} { file } + { \@@_try_file_hook:n {#3} } + { } % Hook doesn't exist + } +\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 \@@_file_hook_normalise:n {#1} } + } + { \@@_if_exist_use:n {#1} } % file/ generic hook (e.g. file/before) + } +\cs_new_protected:Npn \@@_if_exist_use:n #1 + { + \tl_if_exist:cT { g_@@_#1_code_tl } + { + \@@_preamble_hook:n {#1} + \cs:w g_@@_#1_code_tl \cs_end: + } + } +% \end{macrocode} +% \end{macro} +% \end{macro} +% +% \begin{macro}{\hook_use_once:n} +% For hooks that can and should be used only once we have a special +% use command that remembers the hook name in +% \cs{g_@@_execute_immediately_clist}. This has the effect that any +% further code added to the hook is executed immediately rather +% than stored in the hook. +% \begin{macrocode} +\cs_new_protected:Npn \hook_use_once:n #1 + { + \tl_if_exist:cT { g_@@_#1_code_tl } + { + \clist_gput_left:Nn \g_@@_execute_immediately_clist {#1} + \hook_use:n {#1} + } + } +% \end{macrocode} +% \end{macro} + +% \subsection{Querying a hook} +% +% Simpler data types, like token lists, have three possible states; they +% can exist and be empty, exist and be non-empty, and they may not +% 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. +% 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). +% +% 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 +% (it may happen that a package $A$ defines a hook \hook{foo}, but +% it's loaded after package $B$, which adds some code to that hook. +% 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. +% +% \begin{macro}[pTF]{\hook_if_empty:n} +% Test if a hook is empty (that is, no code was added to that hook). +% A hook being empty means that \emph{both} its +% \cs[no-index]{g_@@_\meta{hook}_code_prop} and its +% \cs[no-index]{g_@@_\meta{hook}_next_code_tl} are empty. +% \begin{macrocode} +\prg_new_conditional:Npnn \hook_if_empty:n #1 { p , T , F , TF } + { + \@@_if_exist:nTF {#1} + { + \bool_lazy_and:nnTF + { \prop_if_empty_p:c { g_@@_#1_code_prop } } + { \tl_if_empty_p:c { g_@@_#1_next_code_tl } } + { \prg_return_true: } + { \prg_return_false: } + } + { \prg_return_true: } + } +% \end{macrocode} +% \end{macro} + +% \begin{macro}[pTF]{\hook_if_exist:n} +% A canonical way to test if a hook exists. A hook exists if the +% token list that stores the sorted code for that hook, +% \cs[no-index]{g_@@_\meta{hook}_code_tl}, exists. The property list +% \cs[no-index]{g_@@_\meta{hook}_code_prop} cannot be used here +% because often it is necessary to add code to a hook without knowing +% 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 } + { + \tl_if_exist:cTF { g_@@_#1_code_tl } + { \prg_return_true: } + { \prg_return_false: } + } +% \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 +% (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 } + { + \prop_if_exist:cTF { g_@@_#1_code_prop } + { \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. +% \begin{macrocode} +\prg_new_conditional:Npnn \@@_if_reversed:n #1 { p , T , F , TF } + { + \if_int_compare:w \cs:w g_@@_#1_reversed_tl \cs_end: 1 < 0 \exp_stop_f: + \prg_return_true: + \else: + \prg_return_false: + \fi: + } +% \end{macrocode} +% \end{macro} + + +% \begin{macro}{\g_@@_execute_immediately_clist} +% List of hooks that from no on should not longer receive code. +% \begin{macrocode} +\clist_new:N \g_@@_execute_immediately_clist +% \end{macrocode} +% \end{macro} +% +% \subsection{Messages} +% +% \begin{macrocode} +\msg_new:nnnn { hooks } { labels-incompatible } + { + Labels~`#1'~and~`#2'~are~incompatible + \str_if_eq:nnF {#3} {??} { ~in~hook~`#3' } .~ + \int_compare:nNnT {#4} = { 1 } + { The~code~for~both~labels~will~be~dropped. } + } + { + LaTeX~found~two~incompatible~labels~in~the~same~hook.~ + This~indicates~an~incompatibility~between~packages. + } +\msg_new:nnn { hooks } { empty-label } + { Empty~code~label~\msg_line_context:.~Using~`#1'~instead. } +% \end{macrocode} +% +% \subsection{\LaTeXe{} package interface commands} +% + + +% \begin{macro}{\NewHook,\NewReversedHook,\NewMirroredHookPair} +% Declaring new hooks \ldots +% \begin{macrocode} +\NewDocumentCommand \NewHook { m }{ \hook_new:n {#1} } +\NewDocumentCommand \NewReversedHook { m }{ \hook_new_reversed:n {#1} } +\NewDocumentCommand \NewMirroredHookPair { mm }{ \hook_new_pair:nn {#1}{#2} } +% \end{macrocode} +% \end{macro} + +% \begin{macro}{\AddToHook} +% +% \begin{macrocode} +\NewDocumentCommand \AddToHook { m o +m } + { + \clist_if_in:NnTF \g_@@_execute_immediately_clist {#1} + {#3} + { \hook_gput_code:nnn {#1} {#2} {#3} } + } +% \end{macrocode} +% \end{macro} + +% \begin{macro}{\AddToHookNext} +% +% \begin{macrocode} +\NewDocumentCommand \AddToHookNext { m +m } + { \hook_gput_next_code:nn {#1} {#2} } +% \end{macrocode} +% \end{macro} + + +% \begin{macro}{\RemoveFromHook} +% +% \begin{macrocode} +\NewDocumentCommand \RemoveFromHook { m o } + { \hook_gremove_code:nn {#1} {#2} } +% \end{macrocode} +% \end{macro} +% +% \begin{macro}{\DeclareDefaultHookLabel} +% \begin{macro}{\@@_curr_name_push:n,\@@_curr_name_pop:} +% The token list \cs{g_@@_hook_curr_name_tl} stores the name of the +% current package/file to be used as label for hooks. +% Providing a consistent interface is tricky, because packages can +% be loaded within packages, and some packages may not use +% \cs{DeclareDefaultHookLabel} to change the default label (in which case +% \cs{@currname} is used, if set). +% +% To pull that off, we keep a stack that contains the default label +% for each level of input. The bottom of the stack contains the +% default label for the top-level. Since the string \verb|top-level| +% is hardcoded, here this item of the stack is empty. Also, since +% we're in an input level, add \verb|lthooks| to the stack as well. +% This stack should never go empty, so we loop through \LaTeXe's +% file name stack, and add empty entries to \cs{g_@@_name_stack_seq} +% for each item in that stack. The last item is the \verb|top-level|, +% which also gets an empty entry. +% +% Also check for the case we're loading \texttt{lthooks} in the +% \LaTeXe{} kernel. In that case, \cs{@currname} isn't \verb|lthooks| +% and just the top-level is added to the stack as an empty entry. +% \begin{macrocode} +\str_if_eq:VnTF \@currname { lthooks } + { + \seq_gpush:Nn \g_@@_name_stack_seq { lthooks } + \cs_set_protected:Npn \@@_tmp:w #1 #2 #3 + { + \quark_if_recursion_tail_stop:n {#1} + \seq_gput_right:Nn \g_@@_name_stack_seq { } + \@@_tmp:w + } + \exp_after:wN \@@_tmp:w + \@currnamestack + \q_recursion_tail \q_recursion_tail + \q_recursion_tail \q_recursion_stop + } + { \seq_gpush:Nn \g_@@_name_stack_seq { } } +% \end{macrocode} +% +% Two commands keep track of the stack: when a file is input, +% \cs{@@_curr_name_push:n} pushes an (empty by default) label to the +% stack: +% \begin{macrocode} +\cs_new_protected:Npn \@@_curr_name_push:n #1 + { + \seq_gpush:Nn \g_@@_name_stack_seq {#1} + \tl_gset:Nn \g_@@_hook_curr_name_tl {#1} + } +% +% \end{macrocode} +% and when an input is over, the topmost item of the stack is popped, +% since the label will not be used again, and \cs{g_@@_hook_curr_name_tl} +% is updated to the now topmost item of the stack: +% \begin{macrocode} +\cs_new_protected:Npn \@@_curr_name_pop: + { + \seq_gpop:NN \g_@@_name_stack_seq \l_@@_return_tl + \seq_get:NNTF \g_@@_name_stack_seq \l_@@_return_tl + { \tl_gset_eq:NN \g_@@_hook_curr_name_tl \l_@@_return_tl } + { \ERROR_should_not_happen } + } +% \end{macrocode} +% +% The token list \cs{g_@@_hook_curr_name_tl} is but a mirror of the top +% of the stack. +% +% Now define a wrapper that replaces the top of the stack with the +% argument, and updates \cs{g_@@_hook_curr_name_tl} accordingly. +% \begin{macrocode} +\NewDocumentCommand \DeclareDefaultHookLabel { m } + { + \seq_gpop:NN \g_@@_name_stack_seq \l_@@_return_tl + \@@_curr_name_push:n {#1} + } +% \begin{macrocode} +% +% The push and pop macros are injected in \cs{@pushfilename} and +% \cs{@popfilename} so that they correctly keep track of the label.s +% \begin{macrocode} +% TODO! \pho{Properly integrate in the kernel} +\tl_gput_left:Nn \@pushfilename { \@@_curr_name_push:n { } } +\tl_gput_left:Nn \@popfilename { \@@_curr_name_pop: } +% TODO! \pho{Properly integrate in the kernel} +% \end{macrocode} +% \end{macro} +% \end{macro} +% +% +% +% +% \begin{macro}{\UseHook} +% Avoid the overhead of \pkg{xparse} and its protection that we +% don't want here (since the hook should vanish without trace if empty)! +% \begin{macrocode} +\newcommand \UseHook { \hook_use:n } +% \end{macrocode} +% \end{macro} +% +% \begin{macro}{\UseOneTimeHook} +% +% \begin{macrocode} +\cs_new_protected:Npn \UseOneTimeHook { \hook_use_once:n } +% \end{macrocode} +% \end{macro} + + + +% \begin{macro}{\ShowHook} +% +% \begin{macrocode} +\cs_new_protected:Npn \ShowHook { \hook_log:n } +% \end{macrocode} +% \end{macro} + +% \begin{macro}{\DebugHookOn,\DebugHookOff} +% +% \begin{macrocode} +\cs_new_protected:Npn \DebugHookOn { \hook_debug_on: } +\cs_new_protected:Npn \DebugHookOff { \hook_debug_off: } +% \end{macrocode} +% \end{macro} + + + +% \begin{macro}{\DeclareHookRule} +% +% \begin{macrocode} +\NewDocumentCommand \DeclareHookRule { m m m m } +{ \hook_gset_rule:nnnn {#1}{#2}{#3}{#4} } +% \end{macrocode} +% \end{macro} + +% \begin{macro}{\DeclareDefaultHookRule} +% This declaration is only supported before \verb=\begin{document}=. +% \begin{macrocode} +\NewDocumentCommand \DeclareDefaultHookRule { m m m } + { \hook_gset_rule:nnnn {??}{#1}{#2}{#3} } +\@onlypreamble\DeclareDefaultHookRule +% \end{macrocode} +% \end{macro} +% +% \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} +% \begin{macrocode} +\NewDocumentCommand \ClearHookRule { m m m } +{ \hook_gset_rule:nnnn {#1}{#2}{unrelated}{#3} } +% \end{macrocode} +% \end{macro} +% +% +% \begin{macro}{\IfHookExistTF,\IfHookEmptyTF} +% \begin{macrocode} +\NewExpandableDocumentCommand \IfHookExistTF { m } + { \hook_if_exist:nTF {#1} } +\NewExpandableDocumentCommand \IfHookEmptyTF { m } + { \hook_if_empty:nTF {#1} } +% \end{macrocode} +% \end{macro} + + + + +% \begin{macro}{\AtBeginDocument} +% +% \begin{macrocode} +\renewcommand\AtBeginDocument{\AddToHook{begindocument}} +% \end{macrocode} +% \end{macro} + +% \begin{macro}{\AtEndDocument} +% +% \begin{macrocode} +\renewcommand\AtEndDocument {\AddToHook{enddocument}} +%\renewcommand\AtEndDocument {\AddToHook{env/document/end}} % alternative impl +% \end{macrocode} +% +% \end{macro} + + + +% \subsection{Set up existing \LaTeXe{} hooks} +% +% As we are in a package calling \cs{NewHook} would label any +% already set up hook code under the package name, but we want it +% under the name \hook{top-level} so we pretend that \cs{@currname} +% is empty. +% \begin{macrocode} +\begingroup + \def\@currname{} +% \end{macrocode} +% +% \begin{macrocode} + \NewHook{begindocument} + \NewHook{enddocument} +% \end{macrocode} +% We need to initialize the mechanism at \verb=\begin{document}= +% but obviously before everything else, so we sneak\footnote{This +% needs to move to \cs{document} directly.} +% \cs{@@_initialize_all:} into the \LaTeXe{} hook name. +% +% We can't use \cs{tl_gput_left:Nn} because that complains about +% \cs{@begindocumenthook} not starting with \cs{g_} so we do this +% through the backdoor. +% \begin{macrocode} +% \tex_global:D\tl_put_left:Nn \@begindocumenthook +% {\@@_initialize_all:} +% \end{macrocode} +% There aren't many other hooks at the moment: +% \begin{macrocode} + \NewHook{rmfamily} + \NewHook{sffamily} + \NewHook{ttfamily} + \NewHook{defaultfamily} +% \end{macrocode} +% Not checked what this one does and whether it should be there (or +% is a real ``hook''. +% \begin{macrocode} + \NewHook{documentclass} +% \end{macrocode} +% +% \begin{macrocode} +\endgroup +% \end{macrocode} +% +% +% +% \section{Generic hooks for environments} +% +% +% \begin{macrocode} +\let\begin\relax % avoid redeclaration message +% \end{macrocode} +% +% \begin{macrocode} +\DeclareRobustCommand*\begin[1]{% + \UseHook{env/#1/before}% + \@ifundefined{#1}% + {\def\reserved@a{\@latex@error{Environment~#1~undefined}\@eha}}% + {\def\reserved@a{\def\@currenvir{#1}% + \edef\@currenvline{\on@line}% + \@execute@begin@hook{#1}% + \csname #1\endcsname}}% + \@ignorefalse + \begingroup\@endpefalse\reserved@a} +% \end{macrocode} +% +% Before the \cs{document} code is executed we have to first undo +% the \cs{endgroup} as there should be none for this environment to +% avoid that changes on top-level unnecessarily go to \TeX's +% savestack, and we have to initialize all hooks in the hook system. +% So we need to test for this environment name. But once it has be +% found all this testing is no longer needed and so we redefine +% \cs{@execute@begin@hook} to simply use the hook +% \begin{macrocode} +\def\@execute@begin@hook #1{% + \expandafter\ifx\csname #1\endcsname\document + \endgroup + \gdef\@execute@begin@hook##1{\UseHook{env/##1/begin}}% + \@@_initialize_all: + \@execute@begin@hook{#1}% +% \end{macrocode} +% If this is an environment before \verb=\begin{document}= we just +% run the hook. +% \begin{macrocode} + \else + \UseHook{env/#1/begin}% + \fi +} +% \end{macrocode} +% +% \begin{macrocode} +\@namedef{end~}#1{% + \UseHook{env/#1/end}% + \csname end#1\endcsname\@checkend{#1}% + \expandafter\endgroup\if@endpe\@doendpe\fi + \UseHook{env/#1/after}% + \if@ignore\@ignorefalse\ignorespaces\fi}% +% \end{macrocode} +% Version that fixes tlb3722 but the change should perhaps be made in +% \pkg{tabularx} instead. +% \begin{macrocode} +\@namedef{end~}#1{% +\romannumeral +\IfHookEmptyTF{env/#1/end}% + {\expandafter\z@}% + {\z@\UseHook{env/#1/end}}% +\csname end#1\endcsname\@checkend{#1}% +\expandafter\endgroup\if@endpe\@doendpe\fi +\UseHook{env/#1/after}% +\if@ignore\@ignorefalse\ignorespaces\fi}% +% \end{macrocode} +% +% +% +% We provide 4 high-level hook interfaces directly, the others only when +% etoolbox is loaded +% \begin{macrocode} +\newcommand\AtBeginEnvironment[1] {\AddToHook{env/#1/begin}} +\newcommand\AtEndEnvironment[1] {\AddToHook{env/#1/end}} +\newcommand\BeforeBeginEnvironment[1]{\AddToHook{env/#1/before}} +\newcommand\AfterEndEnvironment[1] {\AddToHook{env/#1/after}} +% \end{macrocode} +% +% +% +% +% +% +% +% +% +% +% +% \section{Generic hooks for file loads} +% +% +% +% +% +% +% +% \section{Hooks in \cs{begin} document} +% +% Can't have \texttt{@{}@} notation here as this is \LaTeXe{} code +% \ldots{} and makes for puzzling errors if the double \texttt{@} +% signs get substituted. +% \begin{macrocode} +%<@@=> +\ExplSyntaxOff +% \end{macrocode} +% +% The \hook{begindocument} hook was already set up earlier, here is now +% the additional one (which was originally from the \pkg{etoolbox} +% package under the name \texttt{afterpreamble}. +% \begin{macrocode} +\NewHook{begindocument/end} +% \end{macrocode} +% +% + +% \begin{macro}{\document} +% +% \begin{macrocode} +\def\document{% +% \end{macrocode} +% We do cancel the grouping as part of the \cs{begin} handling +% (this is now done inside \cs{begin} instead) so that the +% \hook{env/\meta{env}/begin} hook is not hidden inside \cs{begingroup} +% \texttt{...} \cs{endgroup}. +% \begin{macrocode} +% \endgroup +% \end{macrocode} +% +% \begin{macrocode} + \@kernel@after@env@document@begin +% \end{macrocode} +% +% Added hook to load \textsf{l3backend} code: +% \begin{macrocode} + \@expl@sys@load@backend@@ + \ifx\@unusedoptionlist\@empty\else + \@latex@warning@no@line{Unused global option(s):^^J% + \@spaces[\@unusedoptionlist]}% + \fi + \@colht\textheight + \@colroom\textheight \vsize\textheight + \columnwidth\textwidth + \@clubpenalty\clubpenalty + \if@twocolumn + \advance\columnwidth -\columnsep + \divide\columnwidth\tw@ \hsize\columnwidth \@firstcolumntrue + \fi + \hsize\columnwidth \linewidth\hsize + \begingroup\@floatplacement\@dblfloatplacement + \makeatletter\let\@writefile\@gobbletwo + \global \let \@multiplelabels \relax + \@input{\jobname.aux}% + \endgroup + \if@filesw + \immediate\openout\@mainaux\jobname.aux + \immediate\write\@mainaux{\relax}% + \fi + \process@table + \let\glb@currsize\@empty % Force math initialization. + \normalsize + \everypar{}% + \ifx\normalsfcodes\@empty + \ifnum\sfcode`\.=\@m + \let\normalsfcodes\frenchspacing + \else + \let\normalsfcodes\nonfrenchspacing + \fi + \fi + \ifx\document@default@language\m@ne + \chardef\document@default@language\language + \fi + \@noskipsecfalse + \let \@refundefined \relax +% \end{macrocode} +% +% \begin{macrocode} +% \let\AtBeginDocument\@firstofone +% \@begindocumenthook + \UseOneTimeHook{begindocument}% + \@kernel@after@begindocument +% \end{macrocode} +% +% \begin{macrocode} + \ifdim\topskip<1sp\global\topskip 1sp\relax\fi + \global\@maxdepth\maxdepth + \global\let\@begindocumenthook\@undefined + \ifx\@listfiles\@undefined + \global\let\@filelist\relax + \global\let\@addtofilelist\@gobble + \fi + \gdef\do##1{\global\let ##1\@notprerr}% + \@preamblecmds + \global\let \@nodocument \relax + \global\let\do\noexpand +% \end{macrocode} +% +% \begin{macrocode} + \UseOneTimeHook{begindocument/end}% + \ignorespaces} +% \end{macrocode} +% +% \begin{macrocode} +\let\@kernel@after@begindocument\@empty +% \end{macrocode} +% \end{macro} +% +% +% \begin{macro}{\@kernel@after@env@document@begin,\@kernel@hook@begindocument} +% +% \begin{macrocode} +\edef \@kernel@after@env@document@begin{% + \let\expandafter\noexpand\csname + g__hook_env/document/begin_code_tl\endcsname + \noexpand\@empty} +% \end{macrocode} +% \begin{macrocode} +\let\@kernel@hook@begindocument\@empty +% \end{macrocode} +% +% \end{macro} +% +% +% \section{Hooks in \cs{enddocument}} +% +% +% +% The \hook{enddocument} hook was already set up earlier, here are now +% the additional ones: +% \begin{macrocode} +\NewHook{enddocument/afterlastpage} +\NewHook{enddocument/afteraux} +\NewHook{enddocument/info} +\NewHook{enddocument/end} +% \end{macrocode} + + + +% \begin{macro}{\enddocument} +% +% +% \begin{macrocode} +\def\enddocument{% + \UseHook{enddocument}% + \@kernel@after@enddocument + \@checkend{document}% + \clearpage + \UseHook{enddocument/afterlastpage}% + \@kernel@after@enddocument@afterlastpage + \begingroup + \if@filesw + \immediate\closeout\@mainaux + \let\@setckpt\@gobbletwo + \let\@newl@bel\@testdef + \@tempswafalse + \makeatletter \@@input\jobname.aux + \fi + \UseHook{enddocument/afteraux}% +% \end{macrocode} +% Next hook is expect to contain only code for writing info +% messages on the terminal. +% \begin{macrocode} + \UseHook{enddocument/info}% + \endgroup + \UseHook{enddocument/end}% + \deadcycles\z@\@@end} +% \end{macrocode} +% The two kernel hooks above are used by the shipout code. +% \begin{macrocode} +\let\@kernel@after@enddocument\@empty +\let\@kernel@after@enddocument@afterlastpage\@empty +% \end{macrocode} +% \end{macro} +% +% +% +% \begin{macro}{\@enddocument@kernel@warnings} +% +% \begin{macrocode} +\def\@enddocument@kernel@warnings{% + \ifdim \font@submax >\fontsubfuzz\relax + \@font@warning{Size substitutions with differences\MessageBreak + up to \font@submax\space have occurred.\@gobbletwo}% + \fi + \@defaultsubs + \@refundefined + \if@filesw + \ifx \@multiplelabels \relax + \if@tempswa + \@latex@warning@no@line{Label(s) may have changed. + Rerun to get cross-references right}% + \fi + \else + \@multiplelabels + \fi + \fi +} +% \end{macrocode} +% +% \begin{macrocode} +\AddToHook{enddocument/info}[kernel/filelist]{\@dofilelist} +\AddToHook{enddocument/info}[kernel/warnings]{\@enddocument@kernel@warnings} +\DeclareHookRule{enddocument/info}{kernel/filelist}{before}{kernel/warnings} +% \end{macrocode} +% \end{macro} +% +% +% \subsection{Adjusting at \pkg{atveryend} interfaces} +% +% With the new hook management all of \pkg{atveryend} is taken care +% of. +% +% We therefore prevent the package from loading: +% \begin{macrocode} +\expandafter\let\csname ver@atveryend.sty\endcsname\fmtversion +% \end{macrocode} +% +% +% Here are new definitions for its interfaces now pointing to the +% hooks in \cs{enddocument} +% \begin{macrocode} +\newcommand\AfterLastShipout {\AddToHook{enddocument/afterlastpage}} +\newcommand\AtVeryEndDocument {\AddToHook{enddocument/afteraux}} +% \end{macrocode} +% Next one is a bit of a fake, but the result should normally be as +% expected. If not one needs to add a rule to sort the code chunks +% in \hook{enddocument/info}. +% \begin{macrocode} +\newcommand\AtEndAfterFileList{\AddToHook{enddocument/info}} +% \end{macrocode} +% +% \begin{macrocode} +\newcommand\AtVeryVeryEnd {\AddToHook{enddocument/end}} +% \end{macrocode} + +% \begin{macro}{\BeforeClearDocument} +% This one is the only one we don't implement or rather don't have +% a dedicated hook in the code. +% \begin{macrocode} +\ExplSyntaxOn +\newcommand\BeforeClearDocument[1] + { \AtEndDocument{#1} + \@DEPRECATED{BeforeClearDocument \tl_to_str:n{#1}} + } +\cs_new:Npn\@DEPRECATED #1 + {\iow_term:x{======~DEPRECATED~USAGE~#1~==========}} +% \end{macrocode} +% +% \begin{macrocode} +\ExplSyntaxOff +% \end{macrocode} +% \end{macro} +% +% \begin{macrocode} +%</2ekernel> +% \end{macrocode} + +% +% \section{A package version of the code for testing} +% + +% \begin{macrocode} +%<*package> +% \end{macrocode} + + + +% \begin{macrocode} +\RequirePackage{xparse} +\ProvidesExplPackage{lthooks}{\lthooksdate}{\lthooksversion} + {Hook management interface for LaTeX2e} +% \end{macrocode} +% +% +% \subsection{Core hook management code (kernel part)} +% +% This should run in older formats so we can't use +% \cs{IfFormatAtLeastTF} right now. +% \begin{macrocode} +\@ifl@t@r\fmtversion{2020/10/01} + {} + {\input{lthooks.ltx} + \input{ltshipout.ltx} + \input{ltfilehook.ltx} + } +% \end{macrocode} +% +% \subsection{Package options} +% +% For now we offer a simple debug option which turns on a lot of +% strange \cs{typeout} messages, nothing fancy. +% \begin{macrocode} +\ExplSyntaxOn +% \end{macrocode} +% +% \begin{macrocode} +\hook_debug_off: +\DeclareOption { debug } { \hook_debug_on: + \shipout_debug_on: } +% \end{macrocode} +% +% +% For now we offer a simple debug option which turns on a lot of +% strange \cs{typeout} messages, nothing fancy. +% \begin{macrocode} +\shipout_debug_off: +\DeclareOption { debug-shipout } { \shipout_debug_on: } +% \end{macrocode} +% +% \begin{macrocode} +\ProcessOptions +% \end{macrocode} +% +% +% \subsection{Temporarily patching package until changed} +% +% +% +% \pkg{filehook} support until that package is patched: +% \begin{macrocode} +\RequirePackage{filehook-ltx} +% \end{macrocode} +% +% +% +% \begin{macrocode} +%</package> +% \end{macrocode} +% +% +% +% \Finale +% + + diff --git a/Master/texmf-dist/source/latex-dev/base/lthooks.ins b/Master/texmf-dist/source/latex-dev/base/lthooks.ins new file mode 100644 index 00000000000..4218fb8b259 --- /dev/null +++ b/Master/texmf-dist/source/latex-dev/base/lthooks.ins @@ -0,0 +1,76 @@ +%% +%% This file will generate fast loadable files and documentation +%% driver files from the dtx file(s) in this package when run through +%% LaTeX or TeX. +%% +%% Copyright 2020 Frank Mittelbach +%% +%% +%% This file is part of the lthooks Bundle for LaTeX. +%% ------------------------------------------------------- +%% +%% It may be distributed and/or modified under the +%% conditions of the LaTeX Project Public License, either version 1.3c +%% of this license or (at your option) any later version. +%% The latest version of this license is in +%% http://www.latex-project.org/lppl.txt +%% and version 1.3c or later is part of all distributions of LaTeX +%% version 2008 or later. +%% +%% In particular, NO PERMISSION is granted to modify the contents of this +%% file since it contains the legal notices that are placed in the files +%% it generates. +%% +%% +%% +%% --------------- start of docstrip commands ------------------ +%% +\input docstrip + +\keepsilent + +\preamble + +This is a generated file. + +Copyright 2020 Frank Mittelbach + +This file was generated from file(s) of the LaTeX `lthooks Bundle'. +-------------------------------------------------------------------------- + +It may be distributed and/or modified under the +conditions of the LaTeX Project Public License, either version 1.3c +of this license or (at your option) any later version. +The latest version of this license is in + http://www.latex-project.org/lppl.txt +and version 1.3c or later is part of all distributions of LaTeX +version 2005/12/01 or later. + +This file may only be distributed together with a copy of the LaTeX +`lthooks Bundle'. You may however distribute the `lthooks Bundle' +without such generated files. + +The newest sources can be found below + + https://github.com/FrankMittelbach/fmitex/ + +where one can also log issues in case there are any. + + +\endpreamble + + +\generate{\file{lthooks.ltx}{\from{lthooks.dtx}{2ekernel}}} +\generate{\file{ltshipout.ltx}{\from{ltshipout.dtx}{2ekernel}}} +\generate{\file{ltfilehook.ltx}{\from{ltfilehook.dtx}{2ekernel}}} + + +\generate{\file{structuredlog.sty}{\from{ltfilehook.dtx}{structuredlog}}} + +\generate{\file{lthooks.sty}{\from{lthooks.dtx}{package}}} + +\generate{\file{filehook-ltx.sty}{\from{ltfilehook.dtx}{filehook-draft}}} + +\generate{\file{srclfile-ltx.sty}{\from{ltfilehook.dtx}{scrlfile-draft}}} + +\endbatchfile diff --git a/Master/texmf-dist/source/latex-dev/base/ltluatex.dtx b/Master/texmf-dist/source/latex-dev/base/ltluatex.dtx index d7ca7d05ecd..2e692a993b9 100644 --- a/Master/texmf-dist/source/latex-dev/base/ltluatex.dtx +++ b/Master/texmf-dist/source/latex-dev/base/ltluatex.dtx @@ -23,12 +23,12 @@ %<tex> \ProvidesFile\undefined\begingroup\def\ProvidesFile %<tex> #1#2[#3]{\endgroup\immediate\write-1{File: #1 #3}} %<tex>\fi -%<plain>\ProvidesFile{ltluatex.tex} +%<plain>\ProvidesFile{ltluatex.tex}% %<*driver> \ProvidesFile{ltluatex.dtx} %</driver> %<*tex> -[2020/03/07 v1.1m +[2020/06/10 v1.1n %</tex> %<plain> LuaTeX support for plain TeX (core) %<*tex> @@ -406,6 +406,14 @@ \fi % \end{macrocode} % +% \changes{v1.1n}{2020/06/10}{Define \cs{@gobble}/\cs{@firstofone} even for \LaTeX\ to allow early loading.} +% Two simple \LaTeX\ macros from |ltdefns.dtx| have to be defined here +% because ltdefns.dtx is not loaded yet when ltluatex.dtx is executed. +% \begin{macrocode} +\long\def\@gobble#1{} +\long\def\@firstofone#1{#1} +% \end{macrocode} +% % \subsection{Older \LaTeX{}/Plain \TeX\ setup} % % \begin{macrocode} @@ -521,12 +529,6 @@ \fi}% % \end{macrocode} % -% Two simple \LaTeX\ macros used in |ltlatex.sty|. -% \begin{macrocode} -\long\def\@gobble#1{} -\long\def\@firstofone#1{#1} -% \end{macrocode} -% % Fix up allocations not to clash with |etex.src|. % % \begin{macrocode} @@ -544,7 +546,6 @@ %</tex> % \end{macrocode} % -% % \subsection{Attributes} % % \begin{macro}{\newattribute} diff --git a/Master/texmf-dist/source/latex-dev/base/ltmiscen.dtx b/Master/texmf-dist/source/latex-dev/base/ltmiscen.dtx index 5fb4e77d903..c48384b8bdc 100644 --- a/Master/texmf-dist/source/latex-dev/base/ltmiscen.dtx +++ b/Master/texmf-dist/source/latex-dev/base/ltmiscen.dtx @@ -32,7 +32,7 @@ %<*driver> % \fi \ProvidesFile{ltmiscen.dtx} - [2020/04/24 v1.1t LaTeX Kernel (Misc. Environments)] + [2020/05/31 v1.1u LaTeX Kernel (Misc. Environments)] % \iffalse \documentclass{ltxdoc} \GetFileInfo{ltmiscen.dtx} @@ -780,33 +780,39 @@ % \begin{macrocode} %</2ekernel> %<*2ekernel|latexrelease> -%<latexrelease>\IncludeInRelease{2019/10/01}% -%<latexrelease> {\centering}{Make commands robust}% +%<latexrelease>\IncludeInRelease{2020/10/01}% +%<latexrelease> {\centering}{Set finaldhypendemerits}% % \end{macrocode} % % \begin{macro}{\centering} +% \changes{v1.1u}{2020/05/31}{Added \cs{finalhyphendemerits} setting (gh/247)} % \begin{macrocode} \DeclareRobustCommand\centering{% \let\\\@centercr \rightskip\@flushglue\leftskip\@flushglue + \finalhyphendemerits=\z@ \parindent\z@\parfillskip\z@skip} % \end{macrocode} % \end{macro} % % \begin{macro}{\raggedright} +% \changes{v1.1u}{2020/05/31}{Added \cs{finalhyphendemerits} setting (gh/247)} % \begin{macrocode} \DeclareRobustCommand\raggedright{% \let\\\@centercr\@rightskip\@flushglue \rightskip\@rightskip + \finalhyphendemerits=\z@ \leftskip\z@skip \parindent\z@} % \end{macrocode} % \end{macro} % % \begin{macro}{\raggedleft} +% \changes{v1.1u}{2020/05/31}{Added \cs{finalhyphendemerits} setting (gh/247)} % \begin{macrocode} \DeclareRobustCommand\raggedleft{% \let\\\@centercr \rightskip\z@skip\leftskip\@flushglue + \finalhyphendemerits=\z@ \parindent\z@\parfillskip\z@skip} % \end{macrocode} % \end{macro} @@ -814,6 +820,23 @@ % \begin{macrocode} %</2ekernel|latexrelease> %<latexrelease>\EndIncludeInRelease +%<latexrelease>\IncludeInRelease{2019/10/01}% +%<latexrelease> {\centering}{Make commands robust}% +%<latexrelease> +%<latexrelease>\DeclareRobustCommand\centering{% +%<latexrelease> \let\\\@centercr +%<latexrelease> \rightskip\@flushglue\leftskip\@flushglue +%<latexrelease> \parindent\z@\parfillskip\z@skip} +%<latexrelease>\DeclareRobustCommand\raggedright{% +%<latexrelease> \let\\\@centercr\@rightskip\@flushglue \rightskip\@rightskip +%<latexrelease> \leftskip\z@skip +%<latexrelease> \parindent\z@} +%<latexrelease>\DeclareRobustCommand\raggedleft{% +%<latexrelease> \let\\\@centercr +%<latexrelease> \rightskip\z@skip\leftskip\@flushglue +%<latexrelease> \parindent\z@\parfillskip\z@skip} +%<latexrelease>\EndIncludeInRelease +%<latexrelease> %<latexrelease>\IncludeInRelease{0000/00/00}% %<latexrelease> {\centering}{Make commands robust}% %<latexrelease> diff --git a/Master/texmf-dist/source/latex-dev/base/ltoutenc.dtx b/Master/texmf-dist/source/latex-dev/base/ltoutenc.dtx index 8831b5655f8..59c20f37700 100644 --- a/Master/texmf-dist/source/latex-dev/base/ltoutenc.dtx +++ b/Master/texmf-dist/source/latex-dev/base/ltoutenc.dtx @@ -44,7 +44,7 @@ %<*driver> % \fi \ProvidesFile{ltoutenc.dtx} - [2020/04/22 v2.0p LaTeX Kernel (font encodings)] + [2021/07/04 v2.0q LaTeX Kernel (font encodings)] % \iffalse \documentclass{ltxdoc} \GetFileInfo{ltoutenc.dtx} @@ -2869,28 +2869,70 @@ % \end{macrocode} % % LuaTeX. +% For Lua\TeX~1.10+, define a Lua function to disable any handing by the font code. +% Otherwise we reload the font without TeX ligatures. +% \changes{v2.0q}{2020/07/04}{% +% Implement \cs{remove@tlig} in Lua\TeX without font reloading} % \begin{macrocode} \def\UnicodeFontTeXLigatures{+tlig;} -% \end{macrocode} -% -% \begin{macrocode} - \def\reserved@a#1{% - \def\@remove@tlig##1{\@remove@tlig@##1\@nil#1\@nil\relax} - \def\@remove@tlig@##1#1{\@remove@tlig@@##1}} - \edef\reserved@b{\detokenize{+tlig;}} - \expandafter\reserved@a\expandafter{\reserved@b} - \def\@remove@tlig@@#1\@nil#2\relax{#1} -% \end{macrocode} -% -% \begin{macrocode} - \def\remove@tlig#1{% - \begingroup - \font\remove@tlig - \expandafter\@remove@tlig\expandafter{\fontname\font}% - \remove@tlig - \char#1\relax - \endgroup - } + \ifnum\luatexversion<110 +% \end{macrocode} +% +% \begin{macrocode} + \def\reserved@a#1{% + \def\@remove@tlig##1{\@remove@tlig@##1\@nil#1\@nil\relax} + \def\@remove@tlig@##1#1{\@remove@tlig@@##1}} + \edef\reserved@b{\detokenize{+tlig;}} + \expandafter\reserved@a\expandafter{\reserved@b} + \def\@remove@tlig@@#1\@nil#2\relax{#1} +% \end{macrocode} +% +% \begin{macrocode} + \def\remove@tlig#1{% + \begingroup + \font\remove@tlig + \expandafter\@remove@tlig\expandafter{\fontname\font}% + \remove@tlig + \char#1\relax + \endgroup + } + \else + \newluafunction\@remove@tlig@@@@ +% \end{macrocode} +% We are in the format and Lua functions can not be dumped, so we have to repeat +% the code during |\everyjob|. Therefore we first define a helper to both execute +% some code and same it for |\everyjob|: +% \begin{macrocode} + \def\now@and@everyjob#1{\toksapp\everyjob{#1}#1} +% \end{macrocode} +% Now we can define the function. Mostly we just have to insert a protected glyph +% node, which is a glyph node with subtype 256. But we have to keep track of the +% current mode to avoid inserting the glyph into a vlist. +% \begin{macrocode} + \now@and@everyjob{\directlua{ + local rawchar_func = token.create'@remove@tlig@@@@'.index + local forcehmode = tex.forcehmode + local put_next = token.put_next + local glyph_id = node.id'glyph' + local rawchar_token = token.new(rawchar_func, token.command_id'lua_call') + lua.get_functions_table()[rawchar_func] = function() + local mode = tex.nest.top.mode + if mode == 1 or mode == -1 then + put_next(rawchar_token) + return forcehmode(true) + end + local n = node.new(glyph_id, 256) + n.font = font.current() + n.char = token.scan_int() + return node.write(n) + end + token.set_lua('@remove@tlig@@@', rawchar_func, 'global', 'protected') + }} +% \end{macrocode} +% Now \verb+\remove@tlig+ can be implemented almost as in XeTeX. +% \begin{macrocode} + \def\remove@tlig#1{\@remove@tlig@@@#1\relax} + \fi % \end{macrocode} % % \begin{macrocode} diff --git a/Master/texmf-dist/source/latex-dev/base/ltshipout.dtx b/Master/texmf-dist/source/latex-dev/base/ltshipout.dtx new file mode 100644 index 00000000000..8d6150b5930 --- /dev/null +++ b/Master/texmf-dist/source/latex-dev/base/ltshipout.dtx @@ -0,0 +1,1425 @@ +% \iffalse meta-comment +% +%% File: ltshipout.dtx (C) Copyright 2020 Frank Mittelbach, LaTeX Team +% +% It may be distributed and/or modified under the conditions of the +% LaTeX Project Public License (LPPL), either version 1.3c of this +% license or (at your option) any later version. The latest version +% of this license is in the file +% +% https://www.latex-project.org/lppl.txt +% +% +% The development version of the bundle can be found below +% +% https://github.com/FrankMittelbach/... +% +% for those people who are interested or want to report an issue. +% +% \begin{macrocode} +\providecommand\ltshipoutversion{v0.9a} +\providecommand\ltshipoutdate{2020/07/15} +% \end{macrocode} +% +%<*driver> + +\documentclass{l3doc} + +% bug fix fo l3doc.cls +\ExplSyntaxOn +\cs_set_protected:Npn \__codedoc_macro_typeset_one:nN #1#2 + { + \vbox_set:Nn \l__codedoc_macro_box + { + \vbox_unpack_drop:N \l__codedoc_macro_box + \hbox { \llap { \__codedoc_print_macroname:nN {#1} #2 + \MacroFont % <----- without it the \ is in lmr10 if a link is made + \ + } } + } + \int_incr:N \l__codedoc_macro_int + } +\ExplSyntaxOff + +\EnableCrossrefs +\CodelineIndex +\begin{document} + \DocInput{ltshipout.dtx} +\end{document} +%</driver> +% +% \fi +% +% +% \long\def\fmi#1{\begin{quote}\itshape Todo: #1\end{quote}} +% +% \newcommand\hook[1]{\texttt{#1}} +% +% +% \title{The \texttt{ltshipout} package\thanks{This package has version +% \ltshipoutversion\ dated \ltshipoutdate, \copyright\ \LaTeX\ +% Project.}} +% +% \author{Frank Mittelbach} +% +% \maketitle +% +% +% \tableofcontents +% +% \section{Introduction} +% +% The code provides an interface to the \cs{shipout} primitive of +% \TeX{} which is called when a finished pages is finally +% \enquote{shipped out} to the target output file, e.g., the +% \texttt{.dvi} or \texttt{.pdf} file. +% A good portion of the code is based on ideas by Heiko Oberdiek +% implemented in his packages \pkg{atbegshi} and \pkg{atenddvi} +% even though the interfaces are somewhat +% different.\footnote{Heiko's interfaces are emulated by the kernel +% code, if a document requests his packages, so older documents +% will continue to work.} +% +% \subsection{Overloading the \cs{shipout} primitive} +% +% +% \begin{function}{\shipout} +% With this implementation \TeX's shipout primitive is no longer +% available for direct use. Instead \cs{shipout} is running some +% (complicated) code that picks up the box to be shipped out +% regardless of how that is done, i.e., as a constructed \cs{vbox} +% or \cs{hbox} or as a box register. +% +% It then stores it in a named box register. This box can then be +% manipulated through a set of hooks after which it is shipped out +% for real. +% \end{function} + +% \begin{variable}{\ShipoutBox,\l_shipout_box} +% This box register is called \cs{ShipoutBox} (alternatively available via the +% L3 name \cs{l_shipout_box}). +% \end{variable} +% + +% \begin{variable}{\l_shipout_box_ht_dim, +% \l_shipout_box_dp_dim,\l_shipout_box_wd_dim, +% \l_shipout_box_ht_plus_dp_dim} +% The shipout box dimensions are available in the L3 registers +% \cs{l_shipout_box_ht_dim}, etc.\ (there are no \LaTeXe{} +% names).\footnotemark{} These variables can be used +% inside the hook code for \hook{shipout/before}, +% \hook{shipout/foreground} and \hook{shipout/background} if needed. +% \end{variable} +% \footnotetext{Might need changing, but HO's version as strings +% is not really helpful I think).} +% +% +% +% +% \subsection{Provided hooks} +% +% \begin{variable}{shipout/before, +% shipout/foreground,shipout/background, +% shipout/firstpage, +% shipout/lastpage} +% The code offers a number of hooks into which packages (or the +% user) can add code to support different use cases. +% These are: +% \begin{description} +% \item[\hook{shipout/before}] +% +% This hook is executed after the finished page has been stored in +% \cs{ShipoutBox} / \cs{l_shipout_box}). +% It can be used to alter that box content or to discard it +% completely (see \cs{DiscardShipoutBox} below). +% +% \item[\hook{shipout/background}] +% +% This hook adds a picture environment into the background of +% the page (with the \texttt{(0,0)} coordinate in the top-left +% corner using a \cs{unitlength} of \texttt{1pt}. +% +% It should therefore only receive \cs{put} commands or other +% commands suitable in a \texttt{picture} environment and the +% vertical coordinate values would normally be +% negative. +% +% \fmi{Again, mainly \pkg{atbegshi} compatibility. Not +% sure it is best to have to always use negative +% coordinates.} +% +% Technically this is implemented by adding a zero-sized +% \cs{hbox} as the very first item into the \cs{ShipoutBox} +% containing that \texttt{picture} environment. Thus the rest of +% the box content will overprint what ever is typeset by that hook. +% +% +% \item[\hook{shipout/foreground}] +% +% This hook adds a picture environment into the foreground of +% the page (with the \texttt{(0,0)} coordinate in the top-left +% corner using a \cs{unitlength} of \texttt{1pt}. +% +% Technically this is implemented by adding a zero-sized +% \cs{hbox} as the very last item into the \cs{ShipoutBox} and +% raising it up so that it still has its \texttt{(0,0)} point in +% the top-left corner. +% But being placed after the main box content it will be typeset +% later and thus overprints it (i.e., is in the foreground). +% +% +% \item[\hook{shipout/firstpage}] +% +% The material from this hook is executed only once at the very +% beginning of the first output page. It should only contain +% \cs{special} commands needed to direct post processors +% handling the \texttt{.dvi} or \texttt{.pdf} output.\fmi{not +% sure it has to be that restrictive.} +% +% In \LaTeXe{} that was already existing but implemented as a box +% register \cs{@begindvibox}. +% \fmi{drop or at least mark the obsolete code in latex.ltx} +% +% +% \item[\hook{shipout/lastpage}] +% +% The corresponding hook to add \cs{special}s at the very end of +% the output file. It is only entered on the very last page. +% +% It may not be possible for \LaTeX{} to correctly determine which page is +% the last one without several reruns. If this happens and the +% hook is non-empty then \LaTeX{} will add an extra page to +% place the material and also request a rerun to get the correct +% placement sorted out. +% +% \end{description} +% \end{variable} +% +% As mentioned above the hook \hook{shipout/before} is executed +% first and can manipulate the prepared shipout box stored in +% \cs{ShipoutBox} or set things up for use in \cs{write} during the +% actual shipout. The other hooks are added inside hboxes to the +% box being shipped out in the following +% order: +% \begin{center} +% \begin{tabular}{ll} +% \hook{shipout/firstpage} & only on the first page \\ +% \hook{shipout/background} & \\ +% \meta{boxed content of \cs{ShipoutBox}} & \\ +% \hook{shipout/foreground} & \\ +% \hook{shipout/lastpage} & only on the last page \\ +% \end{tabular} +% \end{center} +% If any of the hooks has no code then that particular no box is +% added at that point. +% +% In a document that doesn't produce pages, e.g., only makes +% \cs{typeout}s, none of the hooks are executed (as there is no +% \cs{shipout}) not even the \hook{shipout/lastpage} hook. +% +% \begin{function}{\AtBeginDvi,\AtEndDvi} +% \cs{AtBeginDvi} is the existing \LaTeXe{} interface to fill the +% \hook{shipout/firstpage} hook. This is not really a good name +% as it is not just supporting \texttt{.dvi} but also \texttt{.pdf} +% output or \texttt{.dvx}. +% +% \cs{AtEndDvi} is the counterpart that was not available in the +% kernel but only through the package \pkg{atenddvi}. It fills the +% \hook{shipout/lastpage} hook. +% +% \fmi{better names? Any suggestions?} +% +% \end{function} +% +% \subsection{Special commands for use inside the hooks} +% +% \begin{function}{\DiscardShipoutBox,\shipout_discard_box:} +% \begin{syntax} +% \cs{AddToHookNext} \texttt{\{shipout/before\} \{...\cs{DiscardShipoutBox}...\}} +% \end{syntax} +% The \cs{DiscardShipoutBox} declaration (L3 name +% \cs{shipout_discard_box:}) +% requests that on the next +% shipout the page box is thrown away instead of being shipped to +% the \texttt{.dvi} or \texttt{.pdf} file. +% +% Typical applications wouldn't do this unconditionally, but have +% some processing logic that decides to use or not to use the page. +% +% Note that if this declaration is used directly in the document it +% may depend on the placement to which page it applies, given that +% \LaTeX{} output routine is called in an asynchronous manner! +% +% \fmi{Once we have a new mark mechanism available we can improve +% on that and make sure that the declaration applies to the page +% that contains it.} +% \end{function} +% +% In the \pkg{atbegshi} package there are a number of additional +% commands for use inside the \hook{shipout/before} hook. They +% should normally not be needed any more as one can instead simply +% add code to the hooks \hook{shipout/before}, +% \hook{shipout/background} or +% \hook{shipout/foreground}.\footnote{If that assumption turns out to +% be wrong it would be trivial to change them to public functions +% (right now they are private).} If \pkg{atbegshi} gets loaded then +% those commands become available as public functions with their original +% names as given below. +% +% +% \subsection{Information counters} +% +% +% \begin{variable}{\ReadonlyShipoutCounter,\g_shipout_readonly_int} +% \begin{syntax} +% \cs{ifnum}\cs{ReadOnlyShipoutCounter}\texttt{=...} +% \cs{int_use:N} \cs{g_shipout_readonly_int} \texttt{\% expl3 usage} +% \end{syntax} +% This integer holds the number of pages shipped out up to now +% (including the one to be shipped out when inside the output +% routine). More precisely, it is incremented only after it is +% clear that a page will be shipped out, i.e., after the +% \hook{shipout/before} hook (because that might discard the page)! + +% Just like with the \texttt{page} counter its value is +% only accurate within the output routine. In the body of the +% document it may be off by one as the output routine is called +% asynchronously! +% +% Also important: it \emph{must not} be set, only read. There are +% no provisions to prevent that but if you do, chaos will be the +% result. To emphasize this fact it is not provided as a \LaTeX{} +% counter but as a \TeX{} counter (i.e., a command), so +% \cs{Alph}\Arg{\cs{ReadonlyShipoutCounter}} etc, would not work. +% \end{variable} +% +% \begin{variable}{totalpages,\g_shipout_totalpages_int} +% \begin{syntax} +% \cs{arabic}\texttt{\{totalpages\}} +% \cs{int_use:N} \cs{g_shipout_totalpage_int} \texttt{\% expl3 usage} +% \end{syntax} +% In contrast to \cs{ReadonlyShipoutCounter}, the +% \texttt{totalpages} counter is a \LaTeX{} counter and incremented +% for each shipout attempt including those pages that are discarded +% for one or the other reason. Again \hook{shipout/before} sees +% the counter before it is incremented). +% +% Furthermore, while it is incremented for each page, its value is +% never used by \LaTeX. It can therefore be freely reset or changed by user +% code, for example, to additionally count a number of pages that +% are not build by \LaTeX\ but are added in a later part of the +% process, e.g., cover pages or picture pages made externally. +% +% Important: as this is a page-related counter its value is only +% reliable inside the output routine! +% \end{variable} +% + +% +% \subsection{Debugging shipout code} +% +% \begin{function}{\DebugShipoutOn,\DebugShipoutOff, +% \shipout_debug_on:,\shipout_debug_off:} +% \begin{syntax} +% \cs{DebugShipoutOn} +% \end{syntax} +% Turn the debugging of shipout code on or off. This displays +% changes made to the shipout data structures. \fmi{This needs +% some rationalizing and will probably not stay this way.} +% \end{function} +% +% + +% +% \section{Emulating commands from other packages} +% +% The packages in this section are no longer necessary but as they +% are used in other packages they are emulated when they are loaded +% via \cs{usepackage} or \cs{RequirePackage}. +% +% +% \subsection{Emulating \pkg{atbegshi}} +% +% +% \begin{function}{\AtBeginShipoutUpperLeft,\AtBeginShipoutUpperLeftForeground} +% \begin{syntax} +% \cs{AddToHook} \texttt{\{shipout/before\} \{...\cs{AtBeginShipoutUpperLeft}}\Arg{code}\texttt{...\}} +% \end{syntax} +% This adds a \texttt{picture} environment into the background of the shipout +% box expecting \meta{code} to contain \texttt{picture} +% commands. The same effect can be obtained by simply using kernel +% features as follows: +% \begin{quote} +% \cs{AddToHook}\texttt{\{shipout/background\}}\Arg{code} +% \end{quote} +% There is one technical difference: if +% \cs{AtBeginShipoutUpperLeft} is used several times each +% invocation is put into its own box inside the shipout box whereas +% all \meta{code} going into \hook{shipout/background} ends up +% all in the same box in the order it is added or sorted based on +% the rules for the hook chunks. +% +% \cs{AtBeginShipoutUpperLeftForeground} is similar with the +% difference that the \texttt{picture} environment is placed in the +% foreground. To model it with the kernel functions use the hook +% \hook{shipout/foreground} instead. +% \end{function} +% +% +% \begin{function}{\AtBeginShipoutAddToBox,\AtBeginShipoutAddToBoxForeground} +% \begin{syntax} +% \cs{AddToHook} \texttt{\{shipout/before\} \{...\cs{AtBeginShipoutAddToBox}}\Arg{code}\texttt{...\}} +% \end{syntax} +% These work like \cs{AtBeginShipoutUpperLeft} and +% \cs{AtBeginShipoutUpperLeftForeground} with the difference that +% \meta{code} is directly placed into an \cs{hbox} inside the +% shipout box and not surrounded by a \texttt{picture} environment. +% +% To emulate them using \hook{shipout/background} or +% \hook{shipout/foreground} you may have to wrap \meta{code} into +% a \cs{put} statement but if the code is not doing any typesetting +% just adding it to the hook should be sufficient. +% \end{function} +% + + +% \begin{function}{\AtBeginShipoutBox} +% This is the name of the shipout box as \pkg{atbegshi} knows it. +% \end{function} +% +% \begin{function}{\AtBeginShipoutInit} +% By default \pkg{atbegshi} delayed its action until +% \verb=\beg{document}=. This command was forcing it in an earlier +% place. With the new concept it does nothing. +% \end{function} +% +% \begin{function}{\AtBeginShipout,\AtBeginShipoutNext} +% \begin{syntax} +% \cs{AtBeginShipout}\Arg{code} $\equiv$ \cs{AddToHook}\texttt{\{shipout/before\}}\Arg{code} +% \cs{AtBeginShipoutNext}\Arg{code} $\equiv$ \cs{AddToHookNext}\texttt{\{shipout/before\}}\Arg{code} +% \end{syntax} +% This is equivalent to filling the \hook{shipout/before} hook +% by either using \cs{AddToHook} or \cs{AddToHookNext}, respectively. +% \end{function} +% +% \begin{function}{\AtBeginShipoutFirst,\AtBeginShipoutDiscard} +% The \pkg{atbegshi} names for \cs{AtBeginDvi} and \cs{DiscardShipoutBox}. +% \end{function} +% + + + +% \subsection{Emulating \pkg{everyshi}} +% + + +% \begin{function}{\EveryShipout} +% \begin{syntax} +% \cs{EveryShipout}\Arg{code} $\equiv$ \cs{AddToHook}\texttt{\{shipout/before\}}\Arg{code} +% \end{syntax} +% \end{function} +% +% \begin{function}{\AtNextShipout} +% \begin{syntax} +% \cs{AtNextShipout}\Arg{code} $\equiv$ \cs{AddToHookNext}\texttt{\{shipout/before\}}\Arg{code} +% \end{syntax} +% \end{function} +% + + +% \subsection{Emulating \pkg{atenddvi}} +% +% The \pkg{atenddvi} package implemented only a single command: +% \cs{AtEndDvi} and that is now available out of the box. + + + +% \subsection{Emulating \pkg{everypage}} +% +% This page takes over the original \cs{@begindvi} hook and replaces +% it. It should be all covered by the hooks offered here (details +% need checking) and thus could simply use the provided hooks +% rather than defining its own. +% +% +% +% \StopEventually{\setlength\IndexMin{200pt} \PrintIndex } +% +% +% \section{The Implementation} +% \begin{macrocode} +%<*2ekernel> +%<@@=shipout> +\ExplSyntaxOn +% \end{macrocode} +% +% +% \subsection{Debugging} +% +% \begin{macro}{\g_@@_debug_bool} +% Holds the current debugging state. +% \begin{macrocode} +\bool_new:N \g_@@_debug_bool +% \end{macrocode} +% \end{macro} +% +% \begin{macro}{\shipout_debug_on:,\shipout_debug_off:} +% \begin{macro}{\@@_debug:n} +% \begin{macro}{\@@_debug_gset:} +% Turns debugging on and off by redefining \cs{@@_debug:n}. +% \begin{macrocode} +\cs_new_eq:NN \@@_debug:n \use_none:n +\cs_new_protected:Npn \shipout_debug_on: + { + \bool_gset_true:N \g_@@_debug_bool + \@@_debug_gset: + } +\cs_new_protected:Npn \shipout_debug_off: + { + \bool_gset_false:N \g_@@_debug_bool + \@@_debug_gset: + } +\cs_new_protected:Npn \@@_debug_gset: + { + \cs_gset_protected:Npx \@@_debug:n ##1 + { \bool_if:NT \g_@@_debug_bool {##1} } + } +% \end{macrocode} +% \end{macro} +% \end{macro} +% \end{macro} +% + +% \begin{macro}{shipout/before, +% shipout/foreground,shipout/background, +% shipout/firstpage, +% shipout/lastpage} +% Declaring all hooks for the shipout code. +% \begin{macrocode} +\hook_new:n{shipout/before} +\hook_new:n{shipout/foreground} +\hook_new:n{shipout/background} +\hook_new:n{shipout/firstpage} +\hook_new:n{shipout/lastpage} +% \end{macrocode} +% \end{macro} + + + + +% \begin{macro}{\ShipoutBox,\l_shipout_box} +% The box filled with the page to be shipped out (both L3 and +% \LaTeXe{} name). +% \begin{macrocode} +\box_new:N \l_shipout_box +% \end{macrocode} +% +% \begin{macrocode} +\cs_set_eq:NN \ShipoutBox \l_shipout_box +% \end{macrocode} +% \end{macro} + + + + +% \begin{macro}{\@@_execute:} +% This is going to the be the code run by \cs{shipout}. The code +% follows closely the ideas from \pkg{atbegshi}, so not +% documenting that here for now. +% \begin{macrocode} +\cs_set:Npn\@@_execute: { + \tl_set:Nx \l_@@_group_level_tl + { \int_value:w \tex_currentgrouplevel:D } + \tex_afterassignment:D \@@_execute_test_level: + \tex_setbox:D \l_shipout_box +} +% \end{macrocode} +% \end{macro} + + +% \begin{macro}{\shipout} +% Overloading the \cs{shipout} primitive: +% \begin{macrocode} +\cs_gset_eq:NN \shipout \@@_execute: +% \end{macrocode} +% \end{macro} + + +% \begin{macro}{\l_@@_group_level_tl} +% Helper token list to record the group level at which +% \cs{@@_execute:} is encountered. \begin{macrocode} +\tl_new:N \l_@@_group_level_tl +% \end{macrocode} +% \end{macro} + + + +% \begin{macro}{\@@_execute_test_level:} +% If the group level has changed then we are still constructing +% \cs{l_shipout_box} and to continue we need to wait until the +% current group has finished, hence the \cs{tex_aftergroup:D}. +% \begin{macrocode} +\cs_new:Npn \@@_execute_test_level: { + \int_compare:nNnT + \l_@@_group_level_tl < \tex_currentgrouplevel:D + \tex_aftergroup:D + \@@_execute_cont: +} +% \end{macrocode} +% \end{macro} + + +% \begin{macro}{\@@_execute_cont:} +% When we have reached this point the shipout box has been +% processed and is available in \cs{l_shipout_box} and ready for +% real ship out (perhaps).. +% +% First we quickly check if it is void (can't happen in the +% standard \LaTeX{} output routine but \cs{shipout} might be called +% from a package that has some special processing logic). If it is +% void we aren't shipping anything out and processing ends.\footnote{In that +% case we don't reset the deadcyles, that would be up to the OR +% processing logic to do.} +% \begin{macrocode} +\cs_new:Npn \@@_execute_cont: { + \box_if_empty:NTF \l_shipout_box + { \PackageWarning{ltshipout}{Ignoring~ void~ shipout~ box} } + { +% \end{macrocode} +% Otherwise we assume that we will ship something and prepare for +% final adjustments (in particular setting the state of +% \cs{protect} while we are running the hook code). +% \begin{macrocode} + \bool_gset_false:N \g_@@_discard_bool + \set@typeset@protect +% \end{macrocode} +% We also store the current shipout box dimension in registers, so that +% they can be used in the hook code.\footnote{This is not really +% necessary as the code could access them via \cs{box_ht:N}, etc., +% but it is perhaps convenient.} +% \begin{macrocode} + \@@_get_box_size:N \l_shipout_box +% \end{macrocode} +% Then we execute the \hook{shipout/before} hook. +% \begin{macrocode} + \hook_use:n {shipout/before} +% \end{macrocode} +% In \cs{g_shipout_totalpages_int} we count all shipout attempts so +% we increment that counter already here (the other one is +% incremented later when we know for sure that we do a +% \cs{shipout}. +% +% We increment it after running the above hook so that the values +% for \cs{g_shipout_totalpages_int} and \cs{} are in sync while the +% hook is executed (in the case that totalpages isn't manually +% altered or through discarding pages that is). +% \begin{macrocode} + \int_gincr:N \g_shipout_totalpages_int +% \end{macrocode} +% The above hook might contain code that requests the page to be discarded so +% we now test for it. +% \begin{macrocode} + \bool_if:NTF \g_@@_discard_bool + { \PackageInfo{ltshipout}{Completed~ page~ discarded} + \bool_gset_false:N \g_@@_discard_bool +% \end{macrocode} +% As we are discarding the page box and not shipping anything out, +% we need to do some house cleaning and reset \TeX's deadcycles so +% that it doesn't complain about too many calls to the OR without +% any shipout. +% \begin{macrocode} + \tex_deadcycles:D \c_zero_int +% \end{macrocode} +% \fmi{In \pkg{atbegshi} the box was dropped but is that actually +% needed? Or the resetting of \cs{protect} to its kernel value?} +% \begin{macrocode} +% \group_begin: +% \box_set_eq_drop:NN \l_shipout_box \l_shipout_box +% \group_end: +% \cs_set_eq:NN \protect \exp_not:N + } +% \end{macrocode} +% Even if there was no explicit request to discard the box it is +% possible that the code for the hook \hook{shipout/before} has +% voided the box (by mistake or deliberately). We therefore test +% once more but this time make it a warning, because the best +% practice way is to use the request mechanism. +% \begin{macrocode} + { \box_if_empty:NTF \l_shipout_box + { \PackageWarning{ltshipout}{ + Shipout~ box~ was~ voided~ by~ hook,\MessageBreak + ignoring~ shipout~ box } + } +% \end{macrocode} +% Finally, if the box is still non-empty we are nearly ready to +% ship it out. +% First we increment the total page counter so that we can later +% test if we have reached the final page according to our available +% information.\footnote{Doing that earlier would be wrong because we +% might end up with the last page counted but discard and then we +% have no place to add the final objects into the output file.} +% \begin{macrocode} + { + \int_gincr:N \g_shipout_readonly_int + \@@_debug:n { + \typeout{Absolute~ page~ =~ \int_use:N \g_shipout_readonly_int + \space (target:~ \@abspage@last)} + } +% \end{macrocode} +% Then we store the box sizes again (as they may have +% changed) and then look at the hooks \hook{shipout/foreground} +% and \hook{shipout/background}. If either or both are non-empty +% we add a \texttt{picture} environment to the box (in the +% foreground and or in the background) and execute the hook code +% inside that environment. +% +% \begin{macrocode} + \@@_get_box_size:N \l_shipout_box + \@kernel@before@shipout@foreground + \hook_if_empty:nF {shipout/foreground} + { \@@_add_foreground_picture:n + { \hook_use:n {shipout/foreground} } } + \hook_if_empty:nF {shipout/background} + { \@@_add_background_picture:n + { \hook_use:n {shipout/background} } } +% \end{macrocode} +% We then run \cs{@@_execute_firstpage_hook:} that adds +% the content of the hook \hook{shipout/firstpage} to the +% start of the first page (if non-empty). It is then redefined to +% do nothing on later pages. +% \begin{macrocode} + \@@_execute_firstpage_hook: +% \end{macrocode} +% The we check if we have to add the \hook{shipout/lastpage} hook +% because we have reached the last page. This test will be false for +% all but one (and hopefully the correct) page. +% \begin{macrocode} + \int_compare:nNnT \@abspage@last = \g_shipout_readonly_int + { \hook_if_empty:nF {shipout/lastpage} + { \@@_debug:n { \typeout{Executing~ lastpage~ hook~ + on~ page~ \int_use:N \g_shipout_readonly_int } } + \_@@_add_foreground_box:n { \UseHook{shipout/lastpage} + \@kernel@after@shipout@lastpage } + } + \bool_gset_true:N \g_@@_lastpage_handled_bool + } +% \end{macrocode} +% Finally we run the actual \TeX{} primitive for shipout. As that will +% expand delayed \cs{write} statements inside the page in which +% protected commands should not expand we first change \cs{protect} +% to the appropriate definition for that case. +% \begin{macrocode} + \cs_set_eq:NN \protect \exp_not:N + \tex_shipout:D \box_use:N \l_shipout_box + } + } + } +} +% \end{macrocode} +% \end{macro} + + + +% \begin{macro}{\@kernel@after@shipout@lastpage,\@kernel@before@shipout@foreground} +% +% \begin{macrocode} +\let\@kernel@after@shipout@lastpage\@empty +\let\@kernel@before@shipout@foreground\@empty +% \end{macrocode} +% \end{macro} + + +% \begin{macro}{\@@_execute_firstpage_hook: } +% This command adds any specials into a box and adds that to the +% very beginning of the first box shipped out. After that we +% redefine it to do nothing on later pages. +% \begin{macrocode} +\cs_new:Npn \@@_execute_firstpage_hook: { +% \end{macrocode} +% Adding something to the beginning means adding it to the +% background as that layer is done first in the output. Of course +% that is only needed if the hook actually contains anything. +% \begin{macrocode} + \hook_if_empty:nF {shipout/firstpage} + { \@@_add_background_box:n { \UseHook{shipout/firstpage} } } +% \end{macrocode} +% Once we are here we change the definition to do nothing next time +% and we also change the command used to implement \cs{AtBeginDvi} +% to become a warning and not add further material to a hook that +% is never used again. +% \begin{macrocode} + \cs_gset_eq:NN \@@_execute_firstpage_hook: \prg_do_nothing: + \cs_gset:Npn \@@_add_firstpage_material:Nn ##1 ##2 { + \PackageWarning{ltshipout}{ + First~ page~ is~ already~ shipped~ out,~ ignoring\MessageBreak + \string##1 } + } +} +% \end{macrocode} +% \end{macro} + + +% \begin{macro}{\g_@@_lastpage_handled_bool} +% A boolean to signal if we have already handled the +% \hook{shipout/lastpage} hook. +% \begin{macrocode} +\bool_new:N \g_@@_lastpage_handled_bool +% \end{macrocode} +% \end{macro} + + + +% \begin{macro}{\@@_add_firstpage_material:Nn} +% This command adds material to the +% \hook{shipout/firstpage} hook. It is used in +% \cs{AtBeginDvi}, etc. The first argument is the +% command through which is it called. Initially this is ignored but +% once we are passed the first page it can be used to generate a +% warning message mentioning the right user command. +% \begin{macrocode} +\cs_new:Npn \@@_add_firstpage_material:Nn #1#2 { + \AddToHook{shipout/firstpage}{#2} +} +% \end{macrocode} +% \end{macro} + + + + + + + +% \begin{macro}{\@@_get_box_size:N} +% Store the box dimensions in dimen registers. +% \fmi{This could/should perhaps be generalized to set height depth and +% width given an arbitrary box.} +% \begin{macrocode} +\cs_new:Npn \@@_get_box_size:N #1 { + \dim_set:Nn \l_shipout_box_ht_dim { \box_ht:N #1 } + \dim_set:Nn \l_shipout_box_dp_dim { \box_dp:N #1 } + \dim_set:Nn \l_shipout_box_wd_dim { \box_wd:N #1 } + \dim_set:Nn \l_shipout_box_ht_plus_dp_dim { \l_shipout_box_ht_dim + + \l_shipout_box_dp_dim } +} +% \end{macrocode} +% \end{macro} + +% \begin{macro}{\l_shipout_box_ht_dim, +% \l_shipout_box_dp_dim,\l_shipout_box_wd_dim, +% \l_shipout_box_ht_plus_dp_dim} +% And here are the variables set by \cs{@@_get_box_size:N}. +% \begin{macrocode} +\dim_new:N \l_shipout_box_ht_dim +\dim_new:N \l_shipout_box_dp_dim +\dim_new:N \l_shipout_box_wd_dim +\dim_new:N \l_shipout_box_ht_plus_dp_dim +% \end{macrocode} +% \end{macro} + + + + + +% \begin{macro}{\g_@@_discard_bool} +% Indicate whether or not the current page box should be discarded +% \begin{macrocode} +\bool_new:N \g_@@_discard_bool +% \end{macrocode} +% \end{macro} + + + +% \begin{macro}{\l_@@_tmp_box,\l_@@_saved_badness_tl} +% We need a box for the background and foreground material and a +% token register to remember badness settings as we disable them +% during te buildup below. +% \begin{macrocode} +\box_new:N \l_@@_tmp_box +\tl_new:N \l_@@_saved_badness_tl +% \end{macrocode} +% \end{macro} + + +% \begin{macro}{\@@_add_background_box:n} +% In standard \LaTeX{} the shipout box is always a \cs{vbox} but +% here we are allow for other usage as well, in case some package +% has its own output routine. +% \begin{macrocode} +\cs_new:Npn \@@_add_background_box:n #1 +{ \@@_get_box_size:N \l_shipout_box +% \end{macrocode} +% But we start testing for a vertical box as that should be the +% normal case. +% \begin{macrocode} + \box_if_vertical:NTF \l_shipout_box + { +% \end{macrocode} +% Save current values of \cs{vfuzz} and \cs{vbadness} then change +% them to allow box manipulations without warnings. +% \begin{macrocode} + \tl_set:Nx \l_@@_saved_badness_tl + { \vfuzz=\the\vfuzz\relax + \vbadness=\the\vbadness\relax } + \vfuzz=\c_max_dim + \vbadness=\c_max_int +% \end{macrocode} +% Then we reconstruct \cs{l_shipout_box} \ldots +% \begin{macrocode} + \vbox_set_to_ht:Nnn \l_shipout_box \l_shipout_box_ht_plus_dp_dim + { +% \end{macrocode} +% \ldots{} the material in \verb=#1= is placed into a horizontal +% box with zero dimensions. +% \begin{macrocode} + \hbox_set:Nn \l_@@_tmp_box + { \l_@@_saved_badness_tl #1 } + \box_set_wd:Nn \l_@@_tmp_box \c_zero_dim + \box_set_ht:Nn \l_@@_tmp_box \c_zero_dim + \box_set_dp:Nn \l_@@_tmp_box \c_zero_dim +% \end{macrocode} +% The we typeset that box followed by whatever was in +% \cs{l_shipout_box} before (unpacked). +% \begin{macrocode} + \skip_zero:N \baselineskip + \skip_zero:N \lineskip + \skip_zero:N \lineskiplimit + \box_use:N \l_@@_tmp_box + \vbox_unpack:N \l_shipout_box +% \end{macrocode} +% The \cs{kern} ensures that the box has no depth which is +% afterwards explicitly corrected. +% \begin{macrocode} + \kern \c_zero_dim + } + \box_set_ht:Nn \l_shipout_box \l_shipout_box_ht_dim + \box_set_dp:Nn \l_shipout_box \l_shipout_box_dp_dim +% \end{macrocode} +% \fmi{The whole boxing maneuver looks a bit like overkill to me, but for +% the moment I leave.} +% \begin{macrocode} + \l_@@_saved_badness_tl + } + { +% \end{macrocode} +% A horizontal box is handled in a similar way. The last case would +% be a void box in which case we do nothing hence the missing +% \texttt{F} branch. +% \begin{macrocode} + \box_if_horizontal:NT \l_shipout_box + { + \tl_set:Nx \l_@@_saved_badness_tl + { \hfuzz=\the\hfuzz\relax + \hbadness=\the\hbadness\relax } + \hfuzz=\c_max_dim + \hbadness=\c_max_int + \hbox_set_to_wd:Nnn \l_shipout_box \l_shipout_box_wd_dim + { + \hbox_set:Nn \l_@@_tmp_box + { \l_@@_saved_badness_tl #1 } + \box_set_wd:Nn \l_@@_tmp_box \c_zero_dim + \box_set_ht:Nn \l_@@_tmp_box \c_zero_dim + \box_set_dp:Nn \l_@@_tmp_box \c_zero_dim + \box_move_up:nn + \l_shipout_box_ht_dim + { \box_use:N \l_@@_tmp_box } + \hbox_unpack:N \l_shipout_box + } + \l_@@_saved_badness_tl + } + } +} +% \end{macrocode} +% \end{macro} + + + + +% \begin{macro}{\@@_add_foreground_box:n} +% Foreground boxes are done in the same way, only the order and +% placement of boxes has to be done differently. +% \begin{macrocode} +\cs_new:Npn \@@_add_foreground_box:n #1 +{ + \box_if_vertical:NTF \l_shipout_box + { + \tl_set:Nx \l_@@_saved_badness_tl + { \vfuzz=\the\vfuzz\relax + \vbadness=\the\vbadness\relax } + \vfuzz=\c_max_dim + \vbadness=\c_max_int + \vbox_set_to_ht:Nnn \l_shipout_box \l_shipout_box_ht_plus_dp_dim + { + \hbox_set:Nn \l_@@_tmp_box + { \l_@@_saved_badness_tl #1 } + \box_set_wd:Nn \l_@@_tmp_box \c_zero_dim + \box_set_ht:Nn \l_@@_tmp_box \c_zero_dim + \box_set_dp:Nn \l_@@_tmp_box \c_zero_dim + \skip_zero:N \baselineskip + \skip_zero:N \lineskip + \skip_zero:N \lineskiplimit + \vbox_unpack:N \l_shipout_box + \kern -\l_shipout_box_ht_plus_dp_dim + \box_use:N \l_@@_tmp_box + \kern \l_shipout_box_ht_plus_dp_dim + } + \l_@@_saved_badness_tl + \box_set_ht:Nn \l_shipout_box \l_shipout_box_ht_dim + \box_set_dp:Nn \l_shipout_box \l_shipout_box_dp_dim + } + { + \box_if_horizontal:NT \l_shipout_box + { + \tl_set:Nx \l_@@_saved_badness_tl + { \hfuzz=\the\hfuzz\relax + \hbadness=\the\hbadness\relax } + \hfuzz=\c_max_dim + \hbadness=\c_max_int + \hbox_set_to_wd:Nnn \l_shipout_box \l_shipout_box_wd_dim + { + \hbox_unpack:N \l_shipout_box + \kern -\box_wd:N \l_shipout_box + \hbox_set:Nn \l_@@_tmp_box + { \l_@@_saved_badness_tl #1 } + \box_set_wd:Nn \l_@@_tmp_box \c_zero_dim + \box_set_ht:Nn \l_@@_tmp_box \c_zero_dim + \box_set_dp:Nn \l_@@_tmp_box \c_zero_dim + \box_move_up:nn { \box_ht:N \l_shipout_box } + { \box_use:N \l_@@_tmp_box } + \kern \box_wd:N \l_shipout_box + }% + \l_@@_saved_badness_tl + } + } +} +% \end{macrocode} +% \end{macro} + + + + +% \begin{macro}{\c_@@_horigin_tl,\c_@@_vorigin_tl} +% Two constants holding the offset of the top-left with respect to +% the media box. +% +% Setting the constants this way is courtesy of Bruno. +% \begin{macrocode} +\tl_gput_right:Nn \@kernel@after@begindocument { + \tl_const:Nx \c_@@_horigin_tl + { + \cs_if_exist_use:NTF \pdfvariable { horigin } + { \cs_if_exist_use:NF \pdfhorigin { 1in } } + } + \tl_const:Nx \c_@@_vorigin_tl + { + \cs_if_exist_use:NTF \pdfvariable { vorigin } + { \cs_if_exist_use:NF \pdfvorigin { 1in } } + } +} +% \end{macrocode} +% \end{macro} + + +% \begin{macro}{\@@_picture_overlay:n} +% Put the argument into a \texttt{picture} environment that doesn't take up +% any size and uses \texttt{1pt} for \cs{unitlength}. +% \fmi{Could perhaps be generalized as it might be useful elsewhere. For +% not it is not.} +% \begin{macrocode} +\cs_new:Npn \@@_picture_overlay:n #1 { + \kern -\c_@@_horigin_tl \scan_stop: + \vbox_to_zero:n { + \kern -\c_@@_vorigin_tl \scan_stop: + \unitlength 1pt \scan_stop: + \hbox_set_to_wd:Nnn \l_@@_tmp_box \c_zero_dim { \ignorespaces #1 } +% \box_set_wd:Nn \l_@@_tmp_box \c_zero_dim + \box_set_ht:Nn \l_@@_tmp_box \c_zero_dim + \box_set_dp:Nn \l_@@_tmp_box \c_zero_dim + \box_use:N \l_@@_tmp_box + \tex_vss:D + } +} +% \end{macrocode} +% \end{macro} + + +% \begin{macro}{\@@_add_background_picture:n} +% Put a \texttt{picture} env in the background of the shipout box +% with its reference point in the top-left corner. +% \begin{macrocode} +\cs_new:Npn \@@_add_background_picture:n #1 { + \@@_add_background_box:n { \@@_picture_overlay:n {#1} } +} +% \end{macrocode} +% \end{macro} + + + +% \begin{macro}{\@@_add_foreground_picture:n} +% +% Put a \texttt{picture} env in the foreground of the shipout box +% with its reference point in the top-left corner. +% \begin{macrocode} +\cs_new:Npn \@@_add_foreground_picture:n #1 { + \@@_add_foreground_box:n { \@@_picture_overlay:n {#1} } +} +% \end{macrocode} +% \end{macro} + + +% \begin{macro}{\shipout_discard:} +% Request that the next shipout box should be discarded. At the +% moment this is just setting a boolean, but we may want to augment +% this behavior that the position of the call is taken into account +% (in case \LaTeX{} looks ahead and is not using the position for +% on the next page). +% \begin{macrocode} +\cs_new:Npn \shipout_discard: { + \bool_gset_true:N \g_@@_discard_bool +} +% \end{macrocode} +% \end{macro} + + + + +% \subsection{Handling the end of job hook} + +% At the moment this is partly solved by using the existing hooks. +% But rather than putting the code into these hooks it should be +% moved to the right place directly as we shouldn't prefill hooks +% with material unless it needs to interact with other code. +% + +% +% \begin{macro}{\g_shipout_readonly_int,\ReadonlyShipoutCounter} +% We count every shipout activity that makes a page (but not those +% that are discarded) in order to know how many pages got produced. +% \begin{macrocode} +\int_new:N \g_shipout_readonly_int +% \end{macrocode} +% For \LaTeXe{} it is available as a command (i.e., a \TeX{} +% counter only. +% \begin{macrocode} +\cs_new_eq:NN \ReadonlyShipoutCounter \g_shipout_readonly_int +% \end{macrocode} +% \end{macro} + +% \begin{macro}{\g_shipout_totalpages_int,\c@totalpages} +% We count every shipout attempt (even those that are discarded) in +% tis counter. It is not used in the code but may get used in user +% code. +% \begin{macrocode} +\int_new:N \g_shipout_totalpages_int +% \end{macrocode} +% For \LaTeXe{} this is offered as a \LaTeX{} counter so can be +% easily typeset inside the output routine to display things like +% \enquote{\cs{thepage}\texttt{/}\cs{thetotalpages}}, etc. +% \begin{macrocode} +\cs_new_eq:NN \c@totalpages \g_shipout_totalpages_int +\cs_new:Npn \thetotalpages { \arabic{totalpages} } +% \end{macrocode} +% \end{macro} +% + + + +% \begin{macro}{\@abspage@last} +% In \cs{@abspage@last} record the number of pages from the last +% run. This is written to the \texttt{.aux} and this way made +% available to the next run. In case there is no \texttt{.aux} file +% or the statement is missing from it we initialize it with the +% largest possible number in \TeX{}. We use this a s the default +% because then we are inserting the \hook{shipout/lastpage} on +% the last page (or after the last page but not on page 1 for a +% multipage document. +% \begin{macrocode} +\xdef\@abspage@last{\number\maxdimen} +% \end{macrocode} +% \end{macro} + + +% \begin{macro}{\enddocument} +% +% Instead of using the hooks \hook{enddocument} and +% \hook{enddocument/afterlastpage} we add this code to private +% kernel hooks to be 100\% when it is executed and to avoid +% cluttering the hooks with data that is always there. +% +% Inside \cs{enddocument} there is a \cs{clearpage}. Just before +% that we execute this code here. There is a good change that we +% are on the last page. Therefore, if we don't know the value from +% the last run, we assume that the current page is the right +% one. So we set \cs{@abspage@last} and as a result the next +% shipout will run the \hook{shipout/lastpage} code. Of course, +% if there are floats that still need a placement this guess will +% be wrong but then rerunning the document will give us the correct +% value next time around. +% +% \begin{macro}{\@kernel@after@enddocument} +% \begin{macrocode} +\g@addto@macro \@kernel@after@enddocument { + \int_compare:nNnT \@abspage@last = \maxdimen + { +% \end{macrocode} +% We use \LaTeXe{} coding as \cs{@abspage@last} is not an L3 name. +% \begin{macrocode} + \xdef\@abspage@last{ \int_eval:n {\g_shipout_readonly_int + 1} } + } +} +% \end{macrocode} +% \end{macro} +% +% \begin{macro}{\@kernel@after@enddocument@afterlastpage} +% Once the \cs{clearpage} has done its work inside \cs{enddocument} +% we know for sure how many pages this document has, so we record +% that in the \texttt{.aux} file for the next run. +% +% \begin{macrocode} +\g@addto@macro \@kernel@after@enddocument@afterlastpage { +% \end{macrocode} +% There is one special case: If no output is produced then there is +% no point in a) recording the number as 0 will never match the +% page number of a real page and b) adding an extra page to ran the +% \hook{shipout/lastpage} is pointless as well (as it would +% remain forever). So we test for this and run the code only if +% there have been pages. +% \begin{macrocode} + \int_compare:nNnF \g_shipout_readonly_int = 0 + { +% \end{macrocode} +% This ends up in the \texttt{.aux} so we use \LaTeXe{} names here. +% \fmi{this needs an interface for \cs{nofiles} in expl3, doesn't at the moment!} +% \begin{macrocode} + \if@filesw + \iow_now:Nx \@auxout { + \gdef\string\@abspage@last {\int_use:N \g_shipout_readonly_int}} + \fi +% \end{macrocode} +% But we may have guessed wrongly earlier and we still have to run the +% \hook{shipout/lastpage} even though there is no page to place +% it into. If that is the case we make a trivial extra page and put +% it there. This temporary page will then vanish again on the next +% run but helps to keep pdf viewers happy. +% \begin{macrocode} + \bool_if:NF \g_@@_lastpage_handled_bool + { \tex_shipout:D\vbox to\textheight{ + \hbox{\UseHook{shipout/lastpage} \@kernel@after@shipout@lastpage } +% \end{macrocode} +% This extra page could be totally empty except for the hook +% content, but to help the user understanding why it is there we +% put some text into it. +% \begin{macrocode} + \@@_excuse_extra_page: + \null + } +% \end{macrocode} +% At this point we should also signal to \LaTeX{} that a rerun is +% necessary so that an appropriate message can be shown on the +% terminal. +% +% \fmi{integrate warning ``Rerun \ldots'' } +% \begin{macrocode} + } + } +} +% \end{macrocode} +% \end{macro} +% \end{macro} + + + +% \begin{macro}{\@@_excuse_extra_page:} +% Say mea culpa \ldots +% \begin{macrocode} +\cs_new:Npn \@@_excuse_extra_page: { + \vfil + \begin{center} + \bfseries Temporary~ page! + \end{center} + \LaTeX{}~ was~ unable~ to~ guess~ the~ total~ number~ of~ pages~ + correctly.~ ~ As~ there~ was~ some~ unprocessed~ data~ that~ + should~ have~ been~ added~ to~ the~ final~ page~ this~ extra~ + page~ has~ been~ added~ to~ receive~ it. + \par + If~ you~ rerun~ the~ document~ (without~ altering~ it)~ this~ + surplus~ page~ will~ go~ away,~ because~ \LaTeX{}~ now~ knows~ + how~ many~ pages~ to~ expect~ for~ this~ document. + \vfil +} +% \end{macrocode} +% \end{macro} + + + + +% \section{Legacy \LaTeXe{} interfaces} + + + +% \begin{macro}{\DiscardShipoutBox} +% Request that the next shipout box is to be discarded. +% \begin{macrocode} +\cs_new_eq:NN \DiscardShipoutBox \shipout_discard: +% \end{macrocode} +% \end{macro} + + +% \begin{macro}{\AtBeginDvi} +% +% \begin{macrocode} +\renewcommand \AtBeginDvi {\@@_add_firstpage_material:Nn \AtBeginDvi} +% \end{macrocode} +% \end{macro} + + +% \begin{macro}{\DebugShipoutOn,\DebugShipoutOff} +% +% \begin{macrocode} +\cs_new_eq:NN \DebugShipoutOn \shipout_debug_on: +\cs_new_eq:NN \DebugShipoutOff \shipout_debug_off: +% \end{macrocode} +% \end{macro} + + + + + +% \section{Package emulation for compatibility} + + +% \subsection{Package \pkg{atbegshi} emulation} + +% \begin{macro}{\AtBeginShipoutBox} +% \begin{macrocode} +\cs_new_eq:NN \AtBeginShipoutBox \ShipoutBox +% \end{macrocode} +% \end{macro} + + + +% \begin{macro}{\AtBeginShipoutInit} +% Compatibility only, we aren't delaying \ldots +% \begin{macrocode} +\cs_set_eq:NN\AtBeginShipoutInit\@empty +% \end{macrocode} +% \end{macro} + + + +% \begin{macro}{\AtBeginShipout,\AtBeginShipoutNext} +% Filling hooks +% \begin{macrocode} +\newcommand\AtBeginShipout {\AddToHook{shipout/before}} +\newcommand\AtBeginShipoutNext {\AddToHookNext{shipout/before}} +% \end{macrocode} +% \end{macro} + + + +% \begin{macro}{\AtBeginShipoutFirst} +% Slightly more complex as we need to know the name of the command under which the +% \hook{shipout/firstpage} hook is filled. +% \begin{macrocode} +\newcommand\AtBeginShipoutFirst{\@@_add_firstpage_material:Nn \AtBeginShipoutFirst} +% \end{macrocode} +% \end{macro} + + + +% This is somewhat different from the original where +% \cs{ShipoutBoxHeight} etc.\ only holds the \verb=\the\ht<box>= value. This +% may has some implications in some use cases and if that is a problem +% then it might need changing. +% +% \begin{macrocode} +\cs_new:Npn \ShipoutBoxHeight { \dim_use:N \l_shipout_box_ht_dim } +\cs_new:Npn \ShipoutBoxDepth { \dim_use:N \l_shipout_box_dp_dim } +\cs_new:Npn \ShipoutBoxWidth { \dim_use:N \l_shipout_box_wd_dim } +% \end{macrocode} + + +% \begin{macro}{\AtBeginShipoutDiscard} +% Just a different name. +% \begin{macrocode} +\cs_new_eq:NN \AtBeginShipoutDiscard \DiscardShipoutBox +% \end{macrocode} +% \end{macro} + + +% \begin{macro}{\AtBeginShipoutAddToBox,\AtBeginShipoutAddToBoxForeground, +% \AtBeginShipoutUpperLeft,\AtBeginShipoutUpperLeftForeground} +% We don't expose them. +% \begin{macrocode} +\cs_new_eq:NN \AtBeginShipoutAddToBox \@@_add_background_box:n +\cs_new_eq:NN \AtBeginShipoutAddToBoxForeground \@@_add_foreground_box:n +% \end{macrocode} +% +% \begin{macrocode} +\cs_new_eq:NN\AtBeginShipoutUpperLeft \@@_add_background_picture:n +\cs_new_eq:NN\AtBeginShipoutUpperLeftForeground \@@_add_foreground_picture:n +% \end{macrocode} +% \end{macro} + +% +% +% We prevent the package \pkg{atbegshi} from loading: +% \begin{macrocode} +\expandafter\cs_set_eq:NN\csname ver@atbegshi.sty\endcsname\fmtversion +% \end{macrocode} +% \pkg{hyperref} code (and \pkg{ltxcmds}) doesn't understand 2020-10-01 +% and thinks it is before 1994, so for now \ldots +% \begin{macrocode} +\@namedef {ver@atbegshi.sty}{2020/10/01} +% \end{macrocode} + + + +% \subsection{Package \pkg{everyshi} emulation} +% +% \begin{macro}{\EveryShipout,\AtNextShipout} +% This package has only two public commands to simulating it is easy: +% \begin{macrocode} +\cs_new_eq:NN\EveryShipout\AtBeginShipout +\cs_new_eq:NN\AtNextShipout\AtBeginShipoutNext +% \end{macrocode} +% \end{macro} +% +% \begin{macrocode} +\expandafter\cs_set_eq:NN\csname ver@everyshi.sty\endcsname\fmtversion +\@namedef {ver@everyshi.sty}{2020/10/01} +% \end{macrocode} + + + +% \subsection{Package \pkg{atenddvi} emulation} +% +% \begin{macro}{\AtEndDvi} +% This package has only one public command to simulating it is easy: +% \begin{macrocode} +\cs_new:Npn \AtEndDvi {\AddToHook{shipout/lastpage}} +% \end{macrocode} +% \end{macro} +% +% +% \begin{macrocode} +\expandafter\cs_set_eq:NN\csname ver@atenddvi.sty\endcsname\fmtversion +\@namedef {ver@atenddvi.sty}{2020/10/01} +% \end{macrocode} + + + + +% \begin{macrocode} +\ExplSyntaxOff +%</2ekernel> +% \end{macrocode} + +% \Finale +% + + +%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% +\endinput +%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% + + diff --git a/Master/texmf-dist/source/latex-dev/base/ltvers.dtx b/Master/texmf-dist/source/latex-dev/base/ltvers.dtx index dd39a67896a..ff6c9ef2445 100644 --- a/Master/texmf-dist/source/latex-dev/base/ltvers.dtx +++ b/Master/texmf-dist/source/latex-dev/base/ltvers.dtx @@ -115,7 +115,7 @@ {2020-10-01} %</2ekernel|latexrelease> %<*2ekernel> -\def\patch@level{-6} +\def\patch@level{-7} % \end{macrocode} % % \begin{macro}{\development@branch@name} diff --git a/Master/texmf-dist/source/latex-dev/base/ltxdoc.dtx b/Master/texmf-dist/source/latex-dev/base/ltxdoc.dtx index f250b2cb193..3b82ccd7597 100644 --- a/Master/texmf-dist/source/latex-dev/base/ltxdoc.dtx +++ b/Master/texmf-dist/source/latex-dev/base/ltxdoc.dtx @@ -30,7 +30,7 @@ % %<class>\NeedsTeXFormat{LaTeX2e} %<class>\ProvidesClass{ltxdoc} -%<class> [2019/09/16 v2.0y Standard LaTeX documentation class] +%<class> [2020/05/17 v2.0z Standard LaTeX documentation class] % %<*driver> \documentclass{ltxdoc} @@ -422,6 +422,8 @@ \def\partname{File} % \end{macrocode} % +% \changes{v2.0z}{2020/05/17}{Support spaces \emph{within} filenames +% (gh/218)} % \begin{macrocode} \newcommand*{\DocInclude}[1]{% \relax @@ -432,13 +434,16 @@ {\def\currentfile{#1.dtx}}% \ifnum\@auxout=\@partaux \@latexerr{\string\include\space cannot be nested}\@eha - \else \@docinclude#1 \fi} + \else + \set@curr@file@trim@spaces{#1}% + \expandafter\@docinclude\expandafter{\@curr@file} + \fi} \def\@docinclude#1 {\clearpage -\if@filesw \immediate\write\@mainaux{\string\@input{#1.aux}}\fi +\if@filesw \immediate\write\@mainaux{\string\@input{"#1.aux"}}\fi \@tempswatrue\if@partsw \@tempswafalse\edef\@tempb{#1}\@for \@tempa:=\@partlist\do{\ifx\@tempa\@tempb\@tempswatrue\fi}\fi \if@tempswa \let\@auxout\@partaux \if@filesw -\immediate\openout\@partaux #1.aux +\immediate\openout\@partaux "#1.aux" \immediate\write\@partaux{\relax}\fi % \end{macrocode} % We need to save (and later restore) various index-related diff --git a/Master/texmf-dist/source/latex-dev/base/unpack.ins b/Master/texmf-dist/source/latex-dev/base/unpack.ins index b03dd5c6b1b..ed8cb572c7d 100644 --- a/Master/texmf-dist/source/latex-dev/base/unpack.ins +++ b/Master/texmf-dist/source/latex-dev/base/unpack.ins @@ -123,6 +123,7 @@ \batchinput{latexrelease.ins} +\batchinput{lthooks.ins} \Msg{} \Msg{*********************************************} diff --git a/Master/texmf-dist/source/latex-dev/graphics/lscape.dtx b/Master/texmf-dist/source/latex-dev/graphics/lscape.dtx index e14462c9d42..ac5c16d7c20 100644 --- a/Master/texmf-dist/source/latex-dev/graphics/lscape.dtx +++ b/Master/texmf-dist/source/latex-dev/graphics/lscape.dtx @@ -17,7 +17,7 @@ %<driver> \ProvidesFile{lscape.drv} % \fi % \ProvidesFile{lscape.dtx} - [2000/10/22 v3.01 Landscape Pages (DPC)] + [2020/05/28 v3.02 Landscape Pages (DPC)] % % \iffalse %<*driver> @@ -61,6 +61,7 @@ % \changes{v3.00}{1994/10/05}{Update using LaTeX2e graphics package} % \changes{v3.01}{1999/06/22}{Added better pdfTeX support % by Scott Pakin, from graphics/3063} +% \changes{v3.02}{2020/05/28}{delay saving \cs{@makefcolumn} gh340} % % % \begin{macrocode} @@ -90,8 +91,6 @@ % \end{macrocode} % % \begin{macrocode} -\let\LS@makecol=\@makecol -\let\LS@makefcolumn=\@makefcolumn \def\LS@rot{% \setbox\@outputbox\vbox{\hbox{\rotatebox{90}{\box\@outputbox}}}} % \end{macrocode} @@ -99,6 +98,8 @@ % First set up the page size for landscape. % \begin{macrocode} \def\landscape{% + \let\LS@makecol=\@makecol + \let\LS@makefcolumn=\@makefcolumn \clearpage \begingroup \vsize=\textwidth diff --git a/Master/texmf-dist/source/latex-dev/tools/array.dtx b/Master/texmf-dist/source/latex-dev/tools/array.dtx index a84d4faf510..ff963d7f514 100644 --- a/Master/texmf-dist/source/latex-dev/tools/array.dtx +++ b/Master/texmf-dist/source/latex-dev/tools/array.dtx @@ -33,7 +33,7 @@ %<+package>\DeclareCurrentRelease{}{2020-10-01} %<+package> %<+package>\ProvidesPackage{array} -%<+package> [2020/10/01 v2.5b Tabular extension package (FMi)] +%<+package> [2020/10/01 v2.5c Tabular extension package (FMi)] % % \fi % @@ -552,14 +552,16 @@ % \item rules can be placed between columns or rows thereby enlarging % the table. % \end{enumerate} -% \texttt{array.sty} implements the second possibility while the -% default implementation in the \LaTeX{} kernel implements the first -% concept. Both concepts have their merits but one has to be aware of -% the individual implications. +% For vertical rules \texttt{array.sty} implements the second +% possibility while the default implementation in the \LaTeX{} kernel +% implements the first concept. +% Both concepts have their merits but +% one has to be aware of the individual implications. % \begin{itemize} % \item -% With standard \LaTeX{} adding rules to a table will not affect the -% width or height of the table (unless double rules are used), e.g., +% With standard \LaTeX{} adding vertical rules to a table will +% not affect the +% width of the table (unless double rules are used), e.g., % changing a preamble from \verb=lll= to \verb=l|l|l= does not % affect the document other than adding rules to the table. In % contrast, with \texttt{array.sty} a table that just fit the @@ -602,6 +604,12 @@ % \end{center} % \end{itemize} % +% Horizontal rules produced with \cs{hline} add to the table height in +% both implementations but they differ in handling double \cs{hline}s. +% In contrast a \cs{cline} does not change the table +% height.\footnote{All a bit inconsistent, but nothing that can be +% changed after being 30+ years in existence.} +% % \subsection{Comparisons with older versions of \texttt{array.sty}} % % There are some differences in the way version 2.1 treats incorrect @@ -731,7 +739,11 @@ \NeedsTeXFormat{LaTeX2e}[1995/12/01] \documentclass{ltxdoc} -\AtBeginDocument{\DeleteShortVerb{\|}} % undo the default is not used +% undo the default is not used: + +\IfFormatAtLeastTF {2020/10/01} + {\AtBeginDocument[ltxdoc]{\DeleteShortVerb{\|}} } + {\AtBeginDocument{\DeleteShortVerb{\|}} } \usepackage{array} @@ -747,6 +759,7 @@ %\OnlyDescription % comment out for implementation details %\OldMakeindex % use if your MakeIndex is pre-v2.9 + \begin{document} \DocInput{array.dtx} \end{document} @@ -2210,7 +2223,7 @@ % \cs{@yargarraycr} is the same as in the \LaTeX{} kernel % (depending on the date of the kernel with one of the two % definitions below). We therefore do not define it again. -% \changes{v2.5b}{200/04/22}{Don't define \cs{@yargarraycr} unnecessarily} +% \changes{v2.5b}{2020/04/22}{Don't define \cs{@yargarraycr} unnecessarily} % \begin{macrocode} %\def\@yargarraycr#1{\cr\noalign{\@vspace@calcify{#1}}} % 2020-10-01 %\def\@yargarraycr#1{\cr\noalign{\vskip #1}} @@ -2922,10 +2935,13 @@ % % We start by producing a single tabular row without any visible % content that will produce the external reference point in case -% "[t]" is used. +% "[t]" is used. We need to suppress the \cs{tabcolsep} in the +% \cs{multicolumn} in case there wasn't any in the real column. +% +% \changes{v2.5c}{2020/07/20}{Suppress all column space (gh/322)} % \begin{macrocode} \newcommand{\firsthline}{% - \multicolumn1c{% + \multicolumn1{@{}c@{}}{% % \end{macrocode} % Within this row we calculate "\backup@length" to be the height % plus depth of a standard line. In addition we have to add the @@ -2942,6 +2958,10 @@ % \begin{macrocode} \raise\extratabsurround\copy\@arstrutbox % \end{macrocode} +% And we should also cancel the guard otherwise we end up with two. +% \begin{macrocode} + \kern-1sp% +% \end{macrocode} % Having done all this we end the line and back up by the value of % "\backup@length" and then finally place our "\hline". This should % place the line exactly at the right place but keep the reference @@ -2963,9 +2983,12 @@ % depth of this last line increased by "\extratabsurround" without % changing the placement "\hline". % -% We start by placing the rule followed by an invisible row. +% We start by placing the rule followed by an invisible row. We +% need to suppress the \cs{tabcolsep} in the multicol in case there +% wasn't any in the real column. +% \changes{v2.5c}{2020/07/20}{Suppress all column space (gh/322)} % \begin{macrocode} -\newcommand{\lasthline}{\hline\multicolumn1c{% +\newcommand{\lasthline}{\hline\multicolumn1{@{}c@{}}{% % \end{macrocode} % We now calculate "\backup@length" to be the height and depth of % two lines plus the width of the rule. @@ -2983,8 +3006,9 @@ % reference for the whole tabular). Since this row is invisible we % can enlarge its depth by the desired amount. % \begin{macrocode} - \multicolumn1c{% + \multicolumn1{@{}c@{}}{% \lower\extratabsurround\copy\@arstrutbox + \kern-1sp% }% } % \end{macrocode} diff --git a/Master/texmf-dist/source/latex-dev/tools/varioref.dtx b/Master/texmf-dist/source/latex-dev/tools/varioref.dtx index 077103eb97a..06af00bc282 100644 --- a/Master/texmf-dist/source/latex-dev/tools/varioref.dtx +++ b/Master/texmf-dist/source/latex-dev/tools/varioref.dtx @@ -40,7 +40,7 @@ %<package>\DeclareCurrentRelease{}{2019-10-01} %<package> %<package>\ProvidesPackage{varioref} -%<package> [2020/01/23 v1.6c package for extended references (FMi)] +%<package> [2020/07/20 v1.6d package for extended references (FMi)] % \fi % %% @@ -230,6 +230,11 @@ % The optional argument the command may take is the text to use in case % both labels are placed on the current page. % +% In some languages (currently only for Japanese) the generated text +% has to typeset the page range first and the reference range +% afterwards. To accomodate for this \cs{vrefrangeformat} has been +% added to allow to alter this on a per language basis. +% % % \DescribeMacro\vpagerefrange This command is similar to |\vpageref| % but takes two mandatory arguments which are two labels denoting a @@ -708,6 +713,7 @@ \def\reftextfaraway#1{op bladsy~\pageref{#1}}% \def\reftextpagerange#1#2{op bladsye~\pageref{#1}--\pageref{#2}}% \def\reftextlabelrange#1#2{\ref{#1} tot~\ref{#2}}% + \let\vrefrangeformat\vrefrangedefaultformat }} % \end{macrocode} % @@ -723,6 +729,7 @@ \def\reftextfaraway#1{on page~\pageref{#1}}% \def\reftextpagerange#1#2{on pages~\pageref{#1}--\pageref{#2}}% \def\reftextlabelrange#1#2{\ref{#1} to~\ref{#2}}% + \let\vrefrangeformat\vrefrangedefaultformat }} % \end{macrocode} % Arabic option text was contributed by Ihab Samir Mattar @@ -743,6 +750,7 @@ \def\reftextfaraway#1{بالصفحة رقم~\pageref{#1}}% \def\reftextpagerange#1#2{بالصفحات~\pageref{#1}--\pageref{#2}}% \def\reftextlabelrange#1#2{\ref{#1} حتى~\ref{#2}}% + \let\vrefrangeformat\vrefrangedefaultformat }} % \end{macrocode} % \end{allowtofu} @@ -759,6 +767,7 @@ \def\reftextfaraway#1{auf Seite~\pageref{#1}}% \def\reftextpagerange#1#2{auf Seiten~\pageref{#1}--\pageref{#2}}% \def\reftextlabelrange#1#2{\ref{#1} bis~\ref{#2}}% + \let\vrefrangeformat\vrefrangedefaultformat }} % \end{macrocode} % \changes{v1.2d}{2000/01/11}{Added naustrian defaults (identical to german)} @@ -774,6 +783,7 @@ \def\reftextfaraway#1{auf Seite~\pageref{#1}}% \def\reftextpagerange#1#2{auf Seiten~\pageref{#1}--\pageref{#2}}% \def\reftextlabelrange#1#2{\ref{#1} bis~\ref{#2}}% + \let\vrefrangeformat\vrefrangedefaultformat }} % \end{macrocode} % Text for basque defaults was contributed by @@ -791,6 +801,7 @@ \def\reftextfaraway#1{~\pageref{#1}. orrialdean}% \def\reftextpagerange#1#2{~\pageref{#1}--\pageref{#2} orrialdeetan}% \def\reftextlabelrange#1#2{\ref{#1}.etik \ref{#2}.eraino}% + \let\vrefrangeformat\vrefrangedefaultformat }} % \end{macrocode} % Text for the Bahasa Malaysia defaults was contributed by @@ -815,6 +826,7 @@ \def\reftextfaraway#1{pada muka surat~\pageref{#1}}% \def\reftextpagerange#1#2{pada muka surat~\pageref{#1}--\pageref{#2}}% \def\reftextlabelrange#1#2{\ref{#1}--\ref{#2}}% + \let\vrefrangeformat\vrefrangedefaultformat }} % \end{macrocode} % Text for brazil defaults was contributed by @@ -837,6 +849,7 @@ \def\reftextfaraway#1{na p\'agina~\pageref{#1}}% \def\reftextpagerange#1#2{nas p\'aginas~\pageref{#1}--\pageref{#2}}% \def\reftextlabelrange#1#2{\ref{#1} a~\ref{#2}}% + \let\vrefrangeformat\vrefrangedefaultformat }} % \end{macrocode} % Text for Breton defaults was contributed by @@ -858,6 +871,7 @@ on pages~\pageref{#1}--\pageref{#2}}% \def\reftextlabelrange#1#2{\vref@stringwarning\reflabelpagerange \ref{#1} to~\ref{#2}}% + \let\vrefrangeformat\vrefrangedefaultformat }} % \end{macrocode} % Text for Bulgarian defaults was contributed by @@ -892,6 +906,7 @@ \cyrs\cyrt\cyrr\cyra\cyrn\cyri\cyrc\cyri ~\pageref{#1}--\pageref{#2}}% \def\reftextlabelrange#1#2{\ref{#1} \cyrd\cyro~\ref{#2}}% + \let\vrefrangeformat\vrefrangedefaultformat }} % \end{macrocode} % Text for catalan defaults was contributed by Robert Fuster\\ @@ -910,6 +925,7 @@ \def\reftextpagerange#1#2{a les p\`agines~\pageref{#1}--\pageref{#2}}% \def\reftextlabelrange#1#2{\ref{#1} a~\ref{#2}}% + \let\vrefrangeformat\vrefrangedefaultformat }} % \end{macrocode} % Text for croatian defaults was contributed by \v{S}ime Ungar @@ -926,6 +942,7 @@ \def\reftextfaraway#1{na stranici~\pageref{#1}}% \def\reftextpagerange#1#2{na stranicama~\pageref{#1}--\pageref{#2}}% \def\reftextlabelrange#1#2{\ref{#1} do~\ref{#2}}% + \let\vrefrangeformat\vrefrangedefaultformat }} % \end{macrocode} % Defaults for the Czech option provided by Milan Vancura @@ -943,6 +960,7 @@ \def\reftextfaraway#1{na stran\v{e}~\pageref{#1}}% \def\reftextpagerange#1#2{na stran\'ach~\pageref{#1}\--\pageref{#2}}% \def\reftextlabelrange#1#2{\ref{#1} a\v{z}~\ref{#2}}% + \let\vrefrangeformat\vrefrangedefaultformat }} % \end{macrocode} % Defaults for Danish provided by Torsten Martinsen @@ -965,6 +983,7 @@ \def\reftextfaraway#1{p\aa{} side~\pageref{#1}}% \def\reftextpagerange#1#2{p\aa{} side~\pageref{#1}--\pageref{#2}}% \def\reftextlabelrange#1#2{\ref{#1} til~\ref{#2}}% + \let\vrefrangeformat\vrefrangedefaultformat }} % \end{macrocode} % Default string for dutch have been contributed by Frank Poppe @@ -1004,6 +1023,7 @@ % \begin{macrocode} \def\reftextpagerange#1#2{op pagina's~\pageref{#1}--\pageref{#2}}% \def\reftextlabelrange#1#2{\ref{#1}--\ref{#2}}% + \let\vrefrangeformat\vrefrangedefaultformat }} % \end{macrocode} % \changes{v1.2a}{1998/08/22}{Added english defaults for @@ -1021,6 +1041,7 @@ \def\reftextfaraway#1{on page~\pageref{#1}}% \def\reftextpagerange#1#2{on pages~\pageref{#1}--\pageref{#2}}% \def\reftextlabelrange#1#2{\ref{#1} to~\ref{#2}}% + \let\vrefrangeformat\vrefrangedefaultformat }} % \end{macrocode} % @@ -1041,6 +1062,7 @@ \def\reftextfaraway#1{en pa\^go~\pageref{#1}}% \def\reftextpagerange#1#2{en pa\^goj~\pageref{#1} \^gis \pageref{#2}}% \def\reftextlabelrange#1#2{\ref{#1} \^gis~\ref{#2}}% + \let\vrefrangeformat\vrefrangedefaultformat }} % \end{macrocode} % The finnish strings were suggested by Matti Rintala @@ -1064,6 +1086,7 @@ % Can't combine numbers with the necessary suffix well. % \begin{macrocode} \def\reftextlabelrange#1#2{\ref{#1}--\ref{#2}}% + \let\vrefrangeformat\vrefrangedefaultformat }} % \end{macrocode} % French defaults are provided by Daniel Flippo @@ -1083,6 +1106,7 @@ \def\reftextfaraway#1{page~\pageref{#1}}% \def\reftextpagerange#1#2{pages~\pageref{#1}--\pageref{#2}}% \def\reftextlabelrange#1#2{\ref{#1} \`a~\ref{#2}}% + \let\vrefrangeformat\vrefrangedefaultformat }} % \end{macrocode} % Galician defaults are provided by Matthias Moebius\\ @@ -1104,6 +1128,7 @@ \def\reftextpagerange#1#2{% nas p\'axinas~\pageref{#1}-\pageref{#2}}% \def\reftextlabelrange#1#2{\ref{#1} a~\ref{#2}}% + \let\vrefrangeformat\vrefrangedefaultformat }} % \end{macrocode} % There are no good variants for German (I think and still think but @@ -1138,6 +1163,7 @@ \def\reftextfaraway#1{auf Seite~\pageref{#1}}% \def\reftextpagerange#1#2{auf den Seiten~\pageref{#1}--\pageref{#2}}% \def\reftextlabelrange#1#2{\ref{#1} bis~\ref{#2}}% + \let\vrefrangeformat\vrefrangedefaultformat }} % \end{macrocode} % \changes{v1.2d}{2000/01/11}{Added ngerman defaults (identical to german)} @@ -1155,6 +1181,7 @@ \def\reftextfaraway#1{auf Seite~\pageref{#1}}% \def\reftextpagerange#1#2{auf den Seiten~\pageref{#1}--\pageref{#2}}% \def\reftextlabelrange#1#2{\ref{#1} bis~\ref{#2}}% + \let\vrefrangeformat\vrefrangedefaultformat }} % \end{macrocode} % Defaults for greek suggested by Apostolos Syropoulos\\ @@ -1174,6 +1201,7 @@ \def\reftextpagerange#1#2{stis sel'ides\nobreakspace \pageref{#1}---\pageref{#2}}% \def\reftextlabelrange#1#2{\ref{#1} ws\nobreakspace\ref{#2}}% + \let\vrefrangeformat\vrefrangedefaultformat }} % \end{macrocode} % Icelandic defaults suggested by Thorhallur Sverrisson @@ -1194,6 +1222,7 @@ \def\reftextfaraway#1{\'{a} s\'{i}{\dh}u~\pageref{#1}}% \def\reftextpagerange#1#2{\'{a} s\'{i}{\dh}um~\pageref{#1}--\pageref{#2}}% \def\reftextlabelrange#1#2{\ref{#1} til~\ref{#2}}% + \let\vrefrangeformat\vrefrangedefaultformat }} % \end{macrocode} % Defaults for Italian suggested by Giovanni Pensa @@ -1218,8 +1247,32 @@ \def\reftextfaraway#1{a pagina~\pageref{#1}}% \def\reftextpagerange#1#2{nelle pagine~\pageref{#1}--\pageref{#2}}% \def\reftextlabelrange#1#2{da~\ref{#1} a~\ref{#2}}% + \let\vrefrangeformat\vrefrangedefaultformat + }} +% \end{macrocode} +% Defaults for Japanese. It needs a special \cs{vrefrangeformat} +% for gramatical reasons. As our standard documentation +% workflow uses pdf\TeX{} the Japanese letters can't be displayed +% easily, so you will see missing glyphs below. To see the real +% letters you have to look at the source or package file. +% \changes{v1.6d}{2020/07/20}{Option japanese added (gh/352)} +% \begin{allowtofu} +% \begin{macrocode} +\DeclareOption{japanese} + {\vref@addto\extrasjapanese{% + \def\reftextfaceafter {\reftextvario{見開き}{次}頁}% + \def\reftextfacebefore{\reftextvario{見開き}{前}頁}% + \def\reftextafter {\reftextvario{直後の}{次}頁}% + \def\reftextbefore {\reftextvario{直前の}{前}頁}% + \def\reftextcurrent {\reftextvario{この}{現}頁}% + \def\reftextfaraway#1{\pageref{#1}頁}% + \def\reftextpagerange#1#2{\pageref{#1}頁から\pageref{#2}頁}% + \def\reftextlabelrange#1#2{\ref{#1}から\ref{#2}}% + \def\vrefrangeformat#1#2#3{\vpagerefrange[{#1}]{#2}{#3}の% + \reftextlabelrange{#2}{#3}}% }} % \end{macrocode} +% \end{allowtofu} % Defaults for Hungarian by Jeff Goldberg % (\verb=jeffrey@goldmark.org=). % There is a problem with the use of the definite article @@ -1264,6 +1317,7 @@ on pages~\pageref{#1}--\pageref{#2}}% \def\reftextlabelrange#1#2{\vref@stringwarning\reflabelpagerange \ref{#1} to~\ref{#2}}% + \let\vrefrangeformat\vrefrangedefaultformat }% % \end{macrocode} % The |\AtBeginDocument| needs to be executed when the option is @@ -1304,6 +1358,7 @@ \def\reftextfaraway#1{p\aa{} side~\pageref{#1}}% \def\reftextpagerange#1#2{p\aa{} side~\pageref{#1}--\pageref{#2}}% \def\reftextlabelrange#1#2{\ref{#1} til~\ref{#2}}% + \let\vrefrangeformat\vrefrangedefaultformat }} \DeclareOption{nynorsk} {\vref@addto\extrasnynorsk{% @@ -1315,6 +1370,7 @@ \def\reftextfaraway#1{p\aa{} side~\pageref{#1}}% \def\reftextpagerange#1#2{p\aa{} side~\pageref{#1}--\pageref{#2}}% \def\reftextlabelrange#1#2{\ref{#1} til~\ref{#2}}% + \let\vrefrangeformat\vrefrangedefaultformat }} % \end{macrocode} % The polish translations have been provided by @@ -1342,6 +1398,7 @@ \def\reftextfaraway#1{na stronie~\pageref{#1}}% \def\reftextpagerange#1#2{na stronach~\pageref{#1}--\pageref{#2}}% \def\reftextlabelrange#1#2{od~\ref{#1} do~\ref{#2}}% + \let\vrefrangeformat\vrefrangedefaultformat }} % \end{macrocode} % The portuguese default text has been provided by @@ -1362,6 +1419,7 @@ \def\reftextfaraway#1{na p\'agina~\pageref{#1}}% \def\reftextpagerange#1#2{nas p\'aginas~\pageref{#1}--\pageref{#2}}% \def\reftextlabelrange#1#2{\ref{#1} a~\ref{#2}}% + \let\vrefrangeformat\vrefrangedefaultformat }} % \end{macrocode} % Updates to the default text for Romanian have been suggested by Florin Oprina @@ -1381,6 +1439,7 @@ \def\reftextfaraway#1{pe pagina~\pageref{#1}}% \def\reftextpagerange#1#2{pe paginile~\pageref{#1}--\pageref{#2}}% \def\reftextlabelrange#1#2{\ref{#1} la~\ref{#2}}% + \let\vrefrangeformat\vrefrangedefaultformat }} % \end{macrocode} % The text for russian was taken from the book in Russian by @@ -1414,6 +1473,7 @@ \def\reftextpagerange#1#2{\cyrn\cyra\ \cyrs\cyrt\cyrr\cyra\cyrn \cyri\cyrc\cyra\cyrh~\pageref{#1}--\pageref{#2}}% \def\reftextlabelrange#1#2{\cyrs~\ref{#1}\ \cyrp\cyro~\ref{#2}}% + \let\vrefrangeformat\vrefrangedefaultformat }} % \end{macrocode} % The text for slovak defaults was contributed by Miroslav Sedivy\\ @@ -1434,6 +1494,7 @@ \def\reftextfaraway#1{na strane~\pageref{#1}}% \def\reftextpagerange#1#2{na stran\'ach~\pageref{#1}--\pageref{#2}}% \def\reftextlabelrange#1#2{\ref{#1} a\v z~\ref{#2}}% + \let\vrefrangeformat\vrefrangedefaultformat }} % \end{macrocode} % The text for slovene defaults was contributed by Roman Maurer\\ @@ -1450,6 +1511,7 @@ \def\reftextfaraway#1{na strani~\pageref{#1}}% \def\reftextpagerange#1#2{na straneh~\pageref{#1}--\pageref{#2}}% \def\reftextlabelrange#1#2{od~\ref{#1} do~\ref{#2}}% + \let\vrefrangeformat\vrefrangedefaultformat }} % \end{macrocode} % Text for spanish defaults was contributed by Julio Sanchez @@ -1470,6 +1532,7 @@ \def\reftextpagerange#1#2{en las p\'aginas~\pageref{#1}% -\pageref{#2}}% \def\reftextlabelrange#1#2{\ref{#1} a~\ref{#2}}% + \let\vrefrangeformat\vrefrangedefaultformat }} % \end{macrocode} % The text for swedish was contributed by Mats Dahlgren @@ -1492,6 +1555,7 @@ \def\reftextpagerange#1#2{p\aa\ sidorna~\pageref{#1}--\pageref{#2}}% \def\reftextlabelrange#1#2{\ref{#1} till~\ref{#2}}% + \let\vrefrangeformat\vrefrangedefaultformat }} \DeclareOption{turkish} {\vref@excuse{turkish}% @@ -1505,6 +1569,7 @@ \def\reftextfaraway#1{on page~\pageref{#1}}% \def\reftextpagerange#1#2{on pages~\pageref{#1}--\pageref{#2}}% \def\reftextlabelrange#1#2{\ref{#1} to~\ref{#2}}% + \let\vrefrangeformat\vrefrangedefaultformat }} % \end{macrocode} % The text for Ukrainian defaults was contributed by Mykola Lyakhovych\\ @@ -1532,6 +1597,7 @@ \def\reftextpagerange#1#2{\cyrn\cyra\ \cyrs\cyrt\cyro\cyrr\cyrii \cyrn\cyrk\cyra\cyrh~\pageref{#1}--\pageref{#2}}% \def\reftextlabelrange#1#2{\cyrz~\ref{#1}\ \cyrd\cyro~\ref{#2}}% + \let\vrefrangeformat\vrefrangedefaultformat }} % \end{macrocode} % We support francais as an alternative to french since people @@ -2286,13 +2352,28 @@ % \changes{v1.2a}{1998/08/01}{Macro added} % \changes{v1.4s}{2006/08/07} % {Prevent loss of brackets} +% \changes{v1.6d}{2020/07/20}{Changed for Japanese (gh/352)} +% Argument two and three are implicitly picked up. % \begin{macrocode} -\newcommand\vrefrange[3][\reftextcurrent] - {\reftextlabelrange{#2}{#3} \vpagerefrange[{#1}]{#2}{#3}} +\newcommand\vrefrange[1][\reftextcurrent]{\vrefrangeformat{#1}} % \end{macrocode} +% % \end{macro} % % +% \begin{macro}{\vrefrangeformat,\vrefrangedefaultformat} +% Japanese needs a different word order in \cs{vrefrange} so this +% is separated out o that it can be changed on language level. +% \changes{v1.6d}{2020/07/20}{Macro added (gh/352)} +% \begin{macrocode} +\def\vrefrangedefaultformat#1#2#3{% + \reftextlabelrange{#2}{#3} \vpagerefrange[{#1}]{#2}{#3}} +% \end{macrocode} +% Most languages need the above definition. +% \begin{macrocode} +\let\vrefrangeformat\vrefrangedefaultformat +% \end{macrocode} +% \end{macro} % % \begin{macro}{\Vref} % \changes{v1.4a}{2002/02/24}{Macro added} diff --git a/Master/texmf-dist/source/latex-dev/tools/verbatim.dtx b/Master/texmf-dist/source/latex-dev/tools/verbatim.dtx index 121e2d4b39c..e62886e0033 100644 --- a/Master/texmf-dist/source/latex-dev/tools/verbatim.dtx +++ b/Master/texmf-dist/source/latex-dev/tools/verbatim.dtx @@ -69,6 +69,8 @@ %\fi % % +% \changes{v1.5u}{2020-07-07}{Typo repair and added a missing comment +% character} % \changes{v1.5q}{2003/08/22}{Reintroduced \cs{@noligs}, by popular % request.} % \changes{v1.5i}{1996/06/04}{Move setting of verbatim font and @@ -462,9 +464,12 @@ % the spacing. % Then we open the file and set the category codes of all special % characters: +% \changes{v1.5t}{2020-07-05}{Added quotes around the filename in +% order to allow a filename with spaces; also added a space and +% comment-character to allow for thespace dlimited argument.} % \begin{verbatim} % \@bsphack -% \immediate\openout \verbatim@out #1 +% \immediate\openout \verbatim@out "#1" % % \let\do\@makeother\dospecials % \catcode`\^^M\active %\end{verbatim} @@ -503,7 +508,7 @@ %<*package> \NeedsTeXFormat{LaTeX2e} \ProvidesPackage{verbatim} - [2019/11/10 v1.5r LaTeX2e package for verbatim enhancements] + [2020-07-07 v1.5u LaTeX2e package for verbatim enhancements] \@ifundefined{verbatim@@@}{}{\endinput} % \end{macrocode} % @@ -1567,19 +1572,31 @@ % existence of file.} % |\verbatim@input| first checks whether the file exists, using % the standard macro |\IfFileExists| which leaves the name of the -% file found in |\@filef@und|. -% Then everything is set up as in the |\verbatim| macro. +% file found in |\@filef@und|. +% Then everything is set up as in the |\verbatim| macro. But, as +% |\@verbatim| contains a call to |\every@verbatim| which +% \emph{might} contain an |\input| statement, which would overwrite +% the contents of |\@filef@und|, we need to save it by expanding it +% first. The use of |\@swaptwoargs| makes it so that the +% \emph{expansion} of |\@filef@und| gets to be the second argument +% of |\verbatim@readfile|. +% \changes{v1.5t}{2020-07-06}{Expand \cs{@filef@und} before the call +% of \cs{@verbatim} (gh/222)}m % \begin{macrocode} \def\verbatim@input#1#2{% - \IfFileExists {#2}{\@verbatim #1\relax + \IfFileExists {#2}{% + \expandafter\@swaptwoargs\expandafter + {\expandafter{\@filef@und}}% + {\@verbatim #1\relax % \end{macrocode} % Then it reads in the file, finishes off the \texttt{trivlist} % environment started by |\@verbatim| and closes the group. % This restores everything to its normal settings. % \begin{macrocode} - \verbatim@readfile{\@filef@und}\endtrivlist\endgroup\@doendpe}% + \verbatim@readfile}% + \endtrivlist\endgroup\@doendpe}% % \end{macrocode} -% If the file is not found a more or less helpful message is +% If the file is not found a more or less helpful message is % printed. The final |\endgroup| is needed to close the group % started in |\verbatiminput| above. % \begin{macrocode} |