Revision a91ede24c84df04fcfb986fbc928ac28bb6dfe37 authored by Joseph Wright on 21 July 2020, 09:02:33 UTC, committed by Joseph Wright on 21 July 2020, 09:02:33 UTC
1 parent 2df6a50
ltfilehook.dtx
% \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
%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
![swh spinner](/static/img/swh-spinner.gif)
Computing file changes ...