[latex3-commits] [git/LaTeX3-latex3-latex2e] lthooks: new ltfilehook version (e9855c00)

Frank Mittelbach frank.mittelbach at latex-project.org
Tue Jul 14 10:27:31 CEST 2020


Repository : https://github.com/latex3/latex2e
On branch  : lthooks
Link       : https://github.com/latex3/latex2e/commit/e9855c00939d6a0781d1ef27bddff9e9599025d4

>---------------------------------------------------------------

commit e9855c00939d6a0781d1ef27bddff9e9599025d4
Author: Frank Mittelbach <frank.mittelbach at latex-project.org>
Date:   Tue Jul 14 10:27:31 2020 +0200

    new ltfilehook version


>---------------------------------------------------------------

e9855c00939d6a0781d1ef27bddff9e9599025d4
 base/ltfilehook.dtx                            | 235 +++++++++---------
 base/lthooks.dtx                               | 324 ++++++++++++++++++-------
 required/tools/testfiles/github-0222-input.tlg |   2 +-
 3 files changed, 355 insertions(+), 206 deletions(-)

diff --git a/base/ltfilehook.dtx b/base/ltfilehook.dtx
index 6dee9b67..491e3123 100644
--- a/base/ltfilehook.dtx
+++ b/base/ltfilehook.dtx
@@ -355,7 +355,46 @@
 %    \begin{macrocode}
 %<*2ekernel>
 %    \end{macrocode}
-
+%
+% \subsection{\pkg{expl3} helpers}
+%
+%    \begin{macrocode}
+%<@@=filehook>
+%    \end{macrocode}
+%
+%  \begin{macro}{\CurrentFile}
+%    User-level name for use in the file hooks.
+%    \begin{macrocode}
+\def\CurrentFile{}
+%    \end{macrocode}
+%  \end{macro}
+%
+% \begin{macro}{\@filehook at set@curr at file,\@@_normalise_file_name:n}
+%   This macro sets \cs{CurrentFile} so that it holds the name of the
+%   current input file.  This macro checks that the file exists, and
+%   if so sets \cs{CurrentFile} to expand to the actual file found
+%   (regardless, for example, if the user input a |.tex| file without
+%   typing in the extension).
+%    \begin{macrocode}
+\ExplSyntaxOn
+\tl_new:N \l_@@_internal_a_tl
+\tl_new:N \l_@@_internal_b_tl
+\tl_new:N \l_@@_internal_c_tl
+\cs_new_protected:Npn \@filehook at set@curr at 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} }
+      \l_@@_internal_a_tl \l_@@_internal_b_tl \l_@@_internal_c_tl
+    \tl_set:Nx \CurrentFile { \l_@@_internal_b_tl \l_@@_internal_c_tl }
+  }
+\ExplSyntaxOff
+%    \end{macrocode}
 %
 % \subsection{Declaring the file-related hooks}
 %
@@ -365,11 +404,9 @@
 %  explicitly declare any hook in the code below.
 %
 %  Furthermore, those named \texttt{.../after} or \texttt{.../end} are
-%  automatically declared as reversed hooks if fill with code, s this
+%  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
@@ -378,7 +415,7 @@
 %<@@=>
 %    \end{macrocode}
 %
-%  \begin{macro}{\InputIfFileExists}
+% \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
@@ -387,97 +424,69 @@
 \let\InputIfFileExists\@undefined
 \DeclareRobustCommand \InputIfFileExists[2]{%
   \IfFileExists{#1}%
-   {%
+    {%
 %    \end{macrocode}
 %    If the file exists then \cs{@curr at 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.
+%    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} normalises 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 at path}.  However only
+%    the file name and extension are returned:  the path is removed so
+%    that (say) |\AddToHook{file/before/article.cls}{code}| works
+%    regardless of where |article.cls| is in the system.
 %    \begin{macrocode}
-     \edef\reserved at a{\@filef at und 
-       \def\noexpand\@curr at file{\@curr at file}%
-     }%
+      \edef\reserved at a{\@filef at und
+        \def\noexpand\CurrentFile{\CurrentFile}%
+      }%
       \expandafter\@swaptwoargs\expandafter
-         {\reserved at a}%
-         {%
-           #2%
-           \@addtofilelist{#1}%
-           \UseHook{file/before}%
+        {\reserved at a}%
+        {%
+          #2%
+          \@addtofilelist{#1}%
+          \UseHook{file/before}%
 %    \end{macrocode}
 %    The current file name is available in \cs{@curr at file} so we use
 %    that in the specific hook.
 %    \begin{macrocode}
-           \UseHook{file/before/\@curr at file}%
-           \@@input
-         }%
+          \UseHook{file/before/\CurrentFile}%
+          \@@input
+        }%
 %    \end{macrocode}
 %    And it is restored here so we can use it once more.
 %    \begin{macrocode}
-      \UseHook{file/after/\@curr at file}%
+      \UseHook{file/after/\CurrentFile}%
       \UseHook{file/after}%
     }%
 }
 %    \end{macrocode}
-%  \end{macro}
-
-
-
-%  \begin{macro}{\set at curr@file}
-%    The implicit \texttt{.tex} extension added by \cs{input},
-%    etc.\ is rather a nuisance than a help imho.
-%
-%    So this is an attempt to get to a common state and internally
-%    always drop \texttt{.tex}. This works for all my test cases but I
-%    guess it rather has to be the other way around (always explicitly
-%    add \texttt{.tex} if it is missing and no extension given.
-%
-%    We have to do one or the other because otherwise
-%    \verb=\input{foo}=  and \verb=input{foo.tex}= would try to execute
-%    different specific hooks (\texttt{file/before/foo} and
-%    \texttt{file/before/foo.tex}) which isn't really what we want!
-%    \fmi{fix}
-%
-%    \begin{macrocode}
-\begingroup
-  \catcode`T=12
-  \catcode`E=12
-  \catcode`X=12
-%    \end{macrocode}
+% \end{macro}
 %
+% \begin{macro}{\set at curr@file}
+%   Now we just hook into \cs{set at curr@file} to add
+%   \cs{@filehook at set@curr at file} at the end, after \cs{@curr at file} is
+%   set.
 %    \begin{macrocode}
-\lowercase{
-  \gdef\set at curr@file#1{%
-    \begingroup
-      \escapechar\m at ne
-      \xdef\@curr at file{%
-        \expandafter\expandafter\expandafter\unquote at name
-        \expandafter\expandafter\expandafter{%
-        \expandafter\string
+\def\set at curr@file#1{%
+  \begingroup
+    \escapechar\m at ne
+    \xdef\@curr at file{%
+      \expandafter\expandafter\expandafter\unquote at name
+      \expandafter\expandafter\expandafter{%
+      \expandafter\string
         \csname\@firstofone#1\@empty\endcsname}}%
-      \expandafter
-      \drop at tex@extension at other\@curr at file.TEX\drop at tex@extension at other
-    \endgroup
-  }
-%    \end{macrocode}
-%    
-%    \begin{macrocode}
-  \gdef\drop at tex@extension at other#1.TEX#2\drop at tex@extension at other
-    {\gdef\@curr at file{#1}}
+  \endgroup
+  \@filehook at set@curr at file{\@curr at file}%
 }
-\endgroup
 %    \end{macrocode}
 %  \end{macro}
 %
-%
-
-%  \begin{macro}{\CurrentFile}
-%    User-level name for use in the file hooks.
-%    \begin{macrocode}
-\def\CurrentFile{\@curr at file}
-%    \end{macrocode}
-%  \end{macro}
-
-
 %  \begin{macro}{\load at onefilewithoptions}
 %    This macro is used when loading packages or classes.
 %    \begin{macrocode}
@@ -513,17 +522,19 @@
          {\@currname.\@currext}%
          {%
 %    \end{macrocode}
-%    When the current extension is \cs{@pkgextension} we are loading a
-%    package otherwise (let's hope) a class, so depending on that we
-%    execute different hooks.
+%    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
-             \UseHook{class/before}%
-             \UseHook{class/before/\@currname}%
+             \ifx\@currext\@clsextension
+               \UseHook{class/before}%
+               \UseHook{class/before/\@currname}%
+             \fi
            \fi
 %-----------------------------------------
          }%
@@ -541,8 +552,10 @@
       \UseHook{package/after/\@currname}%
       \UseHook{package/after}%
     \else
-      \UseHook{class/after/\@currname}%
-      \UseHook{class/after}%
+      \ifx\@currext\@clsextension
+        \UseHook{class/after/\@currname}%
+        \UseHook{class/after}%
+      \fi
     \fi
 %-----------------------------------------
     \@unprocessedoptions}%
@@ -602,8 +615,10 @@
              \UseHook{package/before}%
              \UseHook{package/before/\@currname}%
            \else
-             \UseHook{class/before}%
-             \UseHook{class/before/\@currname}%
+             \ifx\@currext\@clsextension
+               \UseHook{class/before}%
+               \UseHook{class/before/\@currname}%
+             \fi
            \fi
 %-----------------------------------------
     \expandafter\let\csname unprocessedoptions-\@currname.\@currext\endcsname
@@ -624,8 +639,10 @@
       \UseHook{package/after/\@currname}%
       \UseHook{package/after}%
     \else
-      \UseHook{class/after/\@currname}%
-      \UseHook{class/after}%
+      \ifx\@currext\@clsextension
+        \UseHook{class/after/\@currname}%
+        \UseHook{class/after}%
+      \fi
     \fi
 %-----------------------------------------
     \@ifl at ter\@currext{#1}{#3}{}%
@@ -641,22 +658,13 @@
     \@reset at ptions}%
   \reserved at a}
 }{}%
-
-
 %    \end{macrocode}
 %  \end{macro}
 
 
 
 
-%  \begin{macro}{\@include#1}
-%    This is for handling include hooks. As the code contains a
-%    \cs{@for} look we need to disable \texttt{expl3} conventions or
-%    chaos will happen :-(.
-%    \begin{macrocode}
-\ExplSyntaxOff  % code contains ":"
-%    \end{macrocode}
-%    
+%  \begin{macro}{\@include}
 %    \begin{macrocode}
 \def\@include#1 {%
   \clearpage
@@ -812,23 +820,6 @@
 %<*filehook-draft>
 %    \end{macrocode}
 %
-%  \begin{macro}{\drop at tex@extension}
-%    This is a helper should vanish. For one it is not correct (as it
-%    will do havoc to \texttt{foo.tex.bar.baz}) and dropping the
-%    extension is weird anyway.
-%    \fmi{fix! when default for .tex changes}
-%    \begin{macrocode}
-\def\drop at tex@extension#1{%
-  \edef\drop at tex@extension at result{%
-    \expandafter
-    \drop at tex@extension@\expanded{#1}.tex\drop at tex@extension@}%
-  }
-\def\drop at tex@extension@#1.tex#2\drop at tex@extension@{#1}%
-%    \end{macrocode}
-%  \end{macro}
-%
-%
-%    
 %    \begin{macrocode}
 \newcommand\AtBeginOfEveryFile [1]
   {\AddToHook{file/before}{#1}}
@@ -862,11 +853,9 @@
 %    For normal files we drop the \texttt{.tex} extension for now:
 %    \begin{macrocode}
 \newcommand\AtBeginOfFile [2]
-  {\drop at tex@extension{file/before/#1}%
-    \expandafter\AddToHook\expandafter{\drop at tex@extension at result}{#2}}
+  {\AddToHook{file/before/#1}{#2}}
 \newcommand\AtEndOfFile [2]
-  {\drop at tex@extension{file/after/#1}%
-   \expandafter\AddToHook\expandafter{\drop at tex@extension at result}{#2}}
+  {\AddToHook{file/after/#1}{#2}}
 %    \end{macrocode}
 %    
 %    \begin{macrocode}
@@ -940,11 +929,11 @@
 %    is only a rough draft.
 %    \begin{macrocode}
 \newcommand\BeforeClass[2]
-  {\AddToHook{file/before/#1.cls}{#2} }
+  {\AddToHook{file/before/#1.cls}{#2}}
 \newcommand\AfterClass [2]
-  {\AddToHook{file/after/#1.cls}{#2} }
+  {\AddToHook{file/after/#1.cls}{#2}}
 \newcommand\AfterAtEndOfClass [2]
-  {\AddToHook{class/after/#1}{#2} }
+  {\AddToHook{class/after/#1}{#2}}
 %    \end{macrocode}
 %
 %    \begin{macrocode}
@@ -960,13 +949,11 @@
 \newcommand\BeforeFile [2]
   {%
   \typeout{BeforeFile: #1!!!}%
-   \drop at tex@extension{file/before/#1}%
-    \expandafter\AddToHook\expandafter{\drop at tex@extension at result}{#2}}
+   \AddToHook{file/before/#1}{#2}}
 \newcommand\AfterFile [2]
   {%
     \typeout{AfterFile: #1!!!}%
-    \drop at tex@extension{file/after/#1}%
-    \expandafter\AddToHook\expandafter{\drop at tex@extension at result}{#2}}
+    \AddToHook{file/after/#1}{#2}}
 %    \end{macrocode}
 %
 %    This is missing some interfaces so disabling the package isn't
@@ -1005,7 +992,7 @@
 
 %    \begin{macrocode}
 \tl_new:N   \g_@@_nesting_prefix_tl
-\tl_gset:Nn \g_@@_nesting_prefix_tl {}
+\tl_gset:Nn \g_@@_nesting_prefix_tl { }
 %    \end{macrocode}
 %    
 %    \begin{macrocode}
@@ -1015,7 +1002,7 @@
   \iow_term:x {
     \g_@@_nesting_prefix_tl \space
     ( LEVEL~ \int_use:N \g_@@_nesting_level_int \space START )~
-    \@curr at file  ^^J
+    \CurrentFile  ^^J
   }
 }
 %    \end{macrocode}
@@ -1030,7 +1017,7 @@
     \iow_term:x {
       \g_@@_nesting_prefix_tl \space
       ( LEVEL~ \int_use:N \g_@@_nesting_level_int \space STOP )~
-      \@curr at file  ^^J
+      \CurrentFile  ^^J
     }
     \int_gdecr:N \g_@@_nesting_level_int
     \tl_gset:Nx \g_@@_nesting_prefix_tl
diff --git a/base/lthooks.dtx b/base/lthooks.dtx
index e554a827..7c87d0cf 100644
--- a/base/lthooks.dtx
+++ b/base/lthooks.dtx
@@ -1315,10 +1315,12 @@
 %
 %  \subsection{Declarations}
 %
-%  \begin{macro}{\l_@@_return_tl}
-%    A scratch variable used throughout the package.
+%  \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}
 %
@@ -1361,6 +1363,13 @@
 %    \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}
@@ -1715,38 +1724,77 @@
 %    \end{macrocode}
 % \end{macro}
 % \end{macro}
-
-%  \begin{macro}{\@@_try_declaring_generic_hook:nnn}
-%  \begin{macro}{\@@_try_declaring_generic_next_hook:nn}
-%  \begin{macro}[TF]{\@@_try_declaring_generic_hook:wn}
-%    We split the hook name at the first \texttt{/} (if any) and check
-%    if that component 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.
-%
-%    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} otherwse (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{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 it's 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} otherwse (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:wnTF #1 / / / \scan_stop: {#1}
-      { \hook_gput_code:nnn {#1} }
-      { \@@_gput_undeclared_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 normalisation) 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}
-      { \hook_gput_next_code:nn {#1} }
-      { \@@_gput_next_do:nn {#1} }
+      { #2 }
+      { #3 } {#1}
   }
 \prg_new_protected_conditional:Npnn \@@_try_declaring_generic_hook:wn
     #1 / #2 / #3 / #4 \scan_stop: #5 { TF }
@@ -1754,19 +1802,19 @@
     \tl_if_empty:nTF {#2}
       { \prg_return_false: }
       {
-        \clist_if_in:NnTF \c_@@_generics_clist {#1}
+        \prop_if_in:NnTF \c_@@_generics_prop {#1}
           {
-            \hook_new:n {#5}
+            \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 datastructure accordingly.
 %    \begin{macrocode}
-            \clist_if_in:NnTF \c_@@_generics_reversed_ii_clist {#2}
+            \prop_if_in:NnTF \c_@@_generics_reversed_ii_prop {#2}
               { \tl_gset:cn { g_@@_#5_reversed_tl } { - } }
               {
-                \clist_if_in:NnT \c_@@_generics_reversed_iii_clist {#3}
+                \prop_if_in:NnT \c_@@_generics_reversed_iii_prop {#3}
                   { \tl_gset:cn { g_@@_#5_reversed_tl } { - } }
               }
 %    \end{macrocode}
@@ -1778,26 +1826,75 @@
       }
   }
 %    \end{macrocode}
-%  \end{macro}
-%  \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 it's declared.
+% \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 parsin turns all characters into
+%   catcode-12 tokens, which might differ from hand-input letters.
 %    \begin{macrocode}
-\cs_new_protected:Npn \@@_gput_undeclared_hook:nnn #1 #2 #3
+\prg_new_conditional:Npnn \@@_if_file_hook:w
+    #1 / #2 / #3 \s_@@_mark { TF }
   {
-    \@@_declare:n {#1}
-    \@@_hook_gput_code_do:nnn {#1} {#2} {#3}
+    \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 normalised 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 at 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_clist}
+
+
+%  \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}
@@ -1807,17 +1904,20 @@
 %      The generic hooks used when loading a file
 %    \end{description}
 %    \begin{macrocode}
-\clist_const:Nn \c_@@_generics_clist {env,file,package,class,include}
+\prop_const_from_keyval:Nn \c_@@_generics_prop
+  {env=,file=,package=,class=,include=}
 %    \end{macrocode}
 %  \end{macro}
 %
-%  \begin{macro}{\c_@@_generics_reversed_ii_clist,
-%                \c_@@_generics_reversed_iii_clist}
+%  \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}
-\clist_const:Nn \c_@@_generics_reversed_ii_clist {after,end}
-\clist_const:Nn \c_@@_generics_reversed_iii_clist {after}
+\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}
 
@@ -2255,9 +2355,8 @@
 %    call the hook code and not initialize it (as it was done in the
 %    preamble.
 %    \begin{macrocode}
-  \cs_gset:Npn \hook_use:n ##1 {
-    \tl_if_exist:cT{g_@@_##1_code_tl}
-       { \cs:w g_@@_##1_code_tl\cs_end: } }
+  \cs_gset_eq:NN \hook_use:n \@@_use_initialized:n
+  \cs_gset_eq:NN \@@_preamble_hook:n \use_none:n
 }
 %    \end{macrocode}
 %  \end{macro}
@@ -2892,35 +2991,97 @@
   }
 %    \end{macrocode}
 %  \end{macro}
-
-
-%  \subsection{Using the hook}
 %
 %
+% \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 initialised 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} initialises 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 initialised, 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}
 %
+% \begin{macro}[EXP]{\@@_use:wn,\@@_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 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
+%   normalisation as during declaration, and then it is used if defined.
 %
-%  \begin{macro}{\hook_use:n}
-%    This one is better not protected \ldots 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.
-%    In this definition use protected because the initialisation code
-%    isn't expandable.
+%   \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_protected:Npn \hook_use:n #1 {
-  \tl_if_exist:cT{g_@@_#1_code_tl}
-     { \@@_initialize_hook_code:n {#1}
-       \cs:w g_@@_#1_code_tl\cs_end:
-     }
-}
+\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: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: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
@@ -2928,13 +3089,14 @@
 %    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}
-       \cs:w g_@@_#1_code_tl\cs_end:
-     }
-}
+\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}
 
diff --git a/required/tools/testfiles/github-0222-input.tlg b/required/tools/testfiles/github-0222-input.tlg
index 068586d9..0aa59da5 100644
--- a/required/tools/testfiles/github-0222-input.tlg
+++ b/required/tools/testfiles/github-0222-input.tlg
@@ -5,6 +5,6 @@ Format: LaTeX2e<2020-10-01>
 Main Class: article
 Package: verbatim
 (.tex File ignored) (.tex File ignored)
-File: "verb-test"  (verbatim)
+File: "verb-test.tex"  (verbatim)
 [1
 ] (github-0222-input.aux)





More information about the latex3-commits mailing list.