summaryrefslogtreecommitdiff
path: root/Master/texmf-dist/source/latex-dev/base/ltcmd.dtx
diff options
context:
space:
mode:
Diffstat (limited to 'Master/texmf-dist/source/latex-dev/base/ltcmd.dtx')
-rw-r--r--Master/texmf-dist/source/latex-dev/base/ltcmd.dtx571
1 files changed, 492 insertions, 79 deletions
diff --git a/Master/texmf-dist/source/latex-dev/base/ltcmd.dtx b/Master/texmf-dist/source/latex-dev/base/ltcmd.dtx
index b0af2f7966c..928e9d89d91 100644
--- a/Master/texmf-dist/source/latex-dev/base/ltcmd.dtx
+++ b/Master/texmf-dist/source/latex-dev/base/ltcmd.dtx
@@ -2,7 +2,7 @@
%
% Copyright (C) 1999 Frank Mittelbach, Chris Rowley, David Carlisle
% Copyright (C) 2004-2008 Frank Mittelbach, The LaTeX Project
-% Copyright (C) 2009-2024
+% Copyright (C) 2009-2025
% The LaTeX Project and any individual authors listed elsewhere
% in this file.
%
@@ -34,8 +34,8 @@
%%% From File: ltcmd.dtx
%
% \begin{macrocode}
-\def\ltcmdversion{v1.2f}
-\def\ltcmddate{2024-07-06}
+\def\ltcmdversion{v1.3b}
+\def\ltcmddate{2025-02-01}
% \end{macrocode}
%
%<*driver>
@@ -97,6 +97,10 @@
%<latexrelease> {Document~command~parser}%
% \end{macrocode}
%
+% \begin{macrocode}
+\cs_generate_variant:Nn \tl_head:n { e }
+% \end{macrocode}
+%
% \subsection{Variables and constants}
%
% \begin{variable}{\l_@@_arg_spec_tl}
@@ -132,6 +136,15 @@
% \end{macrocode}
% \end{variable}
%
+% \begin{variable}{\l_@@_total_args_int}
+% \changes{v1.3a}{2025-01-23}{New variable}
+% The total number of arguments found during normalization: this is required
+% where special action is needed for the penultimate argument.
+% \begin{macrocode}
+\int_new:N \l_@@_total_args_int
+% \end{macrocode}
+% \end{variable}
+%
% \begin{variable}{\l_@@_defaults_bool, \l_@@_defaults_tl}
% The boolean indicates whether there are any argument with default
% value other than |-NoValue-|; the token list holds the code to
@@ -326,6 +339,15 @@
% \end{macrocode}
% \end{variable}
%
+% \begin{variable}{\l_@@_final_verb_bool}
+% \changes{v1.3a}{2025-01-23}{New variable}
+% Needed to establish whether optional arguments should be collected
+% \enquote{verbatim safe}.
+% \begin{macrocode}
+\bool_new:N \l_@@_final_verb_bool
+% \end{macrocode}
+% \end{variable}
+%
% \begin{variable}{\q_@@_recursion_tail,\q_@@_recursion_stop}
% \begin{macro}{\@@_if_recursion_tail_stop_do:Nn}
% \begin{macro}{\@@_use_i_delimit_by_q_recursion_stop:nw}
@@ -1331,28 +1353,40 @@
% \end{macro}
%
% \begin{macro}{\@@_normalize_type_b:w}
-% This argument type is not allowed for commands. This is only
+% \changes{v1.3a}{2025-01-23}{Extend to cover \texttt{c}-type grabbing}
+% \begin{macro}{\@@_normalize_type_c:w}
+% \changes{v1.3a}{2025-01-23}{New function}
+% \begin{macro}{\@@_normalize_type_b_or_c:nn}
+% \changes{v1.3a}{2025-01-23}{New function}
+% These argument types are not allowed for commands. They are only
% allowed at the end of the argument specification, hence we check
% that |#1| is the end.
% \begin{macrocode}
\cs_new_protected:Npn \@@_normalize_type_b:w #1
+ { \@@_normalize_type_b_or_c:nn {#1} { b } }
+\cs_new_protected:Npn \@@_normalize_type_c:w #1
+ { \@@_normalize_type_b_or_c:nn {#1} { c } }
+\cs_new_protected:Npn \@@_normalize_type_b_or_c:nn #1#2
{
\bool_if:NF \l_@@_environment_bool
{
\msg_error:nnxx { cmd } { invalid-command-arg }
- { \@@_environment_or_command: } { b }
+ { \@@_environment_or_command: } {#2}
\@@_bad_def:wn
}
\tl_clear:N \l_@@_last_delimiters_tl
- \@@_add_arg_spec:n { b }
+ \@@_add_arg_spec:n {#2}
\quark_if_recursion_tail_stop:n {#1}
- \msg_error:nnxx { cmd } { arg-after-body }
+ \msg_error:nnxxx { cmd } { arg-after-body }
+ {#2}
{ \@@_environment_or_command: }
{ \tl_to_str:n {#1} }
\@@_bad_def:wn
}
% \end{macrocode}
% \end{macro}
+% \end{macro}
+% \end{macro}
%
% \begin{macro}{\@@_single_token_check:n}
% Checks that the argument is a single (non-space) token (possibly
@@ -1501,7 +1535,10 @@
{ \bool_set_true:N \l_@@_some_short_bool }
\tl_put_right:Nx \l_@@_arg_spec_tl
{
- \bool_if:NT \l_@@_long_bool { + }
+ \bool_lazy_and:nnT
+ { \l_@@_long_bool }
+ { ! \str_if_eq_p:nn {#1} { c } }
+ { + }
\bool_if:NT \l_@@_obey_spaces_bool { ! }
\exp_not:n {#1}
}
@@ -1530,6 +1567,8 @@
% \subsection{Preparing the signature: general mechanism}
%
% \begin{macro}{\@@_prepare_signature:n}
+% \changes{v1.3a}{2025-01-23}{Extend to cover \texttt{c}-type grabbing}
+% \begin{macro}{\@@_prepare_signature_verb_chk:n}
% \begin{macro}{\@@_prepare_signature:N}
% \begin{macro}{\@@_prepare_signature_bypass:N}
% Actually creating the signature uses the same loop approach as
@@ -1539,6 +1578,7 @@
% \begin{macrocode}
\cs_new_protected:Npn \@@_prepare_signature:n #1
{
+ \int_set_eq:NN \l_@@_total_args_int \l_@@_current_arg_int
\int_zero:N \l_@@_current_arg_int
\bool_set_false:N \l_@@_long_bool
\bool_set_false:N \l_@@_obey_spaces_bool
@@ -1550,10 +1590,20 @@
\tl_clear:N \l_@@_process_one_tl
\bool_set_false:N \l_@@_process_some_bool
\tl_clear:N \l_@@_signature_tl
+ \@@_prepare_signature_verb_chk:n {#1}
\@@_prepare_signature:N #1 \q_recursion_tail \q_recursion_stop
\bool_if:NF \l_@@_expandable_bool { \@@_flush_m_args: }
}
% \end{macrocode}
+% A quick check on the final arg.~type.
+% \begin{macrocode}
+\cs_new_protected:Npn \@@_prepare_signature_verb_chk:n #1
+ {
+ \str_if_eq:eeTF { \tl_head:e { \tl_reverse:n {#1} } } { c }
+ { \bool_set_true:N \l_@@_final_verb_bool }
+ { \bool_set_false:N \l_@@_final_verb_bool }
+ }
+% \end{macrocode}
% The main looping function does not take an argument, but carries out the
% reset on the processor boolean. This is split off from the rest of the
% process so that when actually setting up processors the flag-reset can
@@ -1582,6 +1632,7 @@
% \end{macro}
% \end{macro}
% \end{macro}
+% \end{macro}
%
% \subsection{Setting up a standard signature}
%
@@ -1640,7 +1691,7 @@
% \end{macrocode}
% \end{macro}
%
-% \begin{macro}{\@@_add_type_=:}
+% \begin{macro}{\@@_add_type_=:w}
% \changes{v1.1a}{2022/08/10}{Add support for \texttt{=} modifier}
% A mix of the ideas from above: set a flag and add a processor.
% \begin{macrocode}
@@ -1658,16 +1709,27 @@
% \end{macro}
%
% \begin{macro}{\@@_add_type_b:w}
+% \changes{v1.3a}{2025-01-23}{Extend to cover \texttt{c}-type grabbing}
+% \begin{macro}{\@@_add_type_c:w}
+% \changes{v1.3a}{2025-01-23}{New function}
+% \begin{macro}{\@@_add_type_b_or_c:N}
+% \changes{v1.3a}{2025-01-23}{New function}
% \begin{macrocode}
\cs_new_protected:Npn \@@_add_type_b:w
+ { \@@_add_type_b_or_c:N b }
+\cs_new_protected:Npn \@@_add_type_c:w
+ { \@@_add_type_b_or_c:N c }
+\cs_new_protected:Npn \@@_add_type_b_or_c:N #1
{
\@@_flush_m_args:
\@@_add_default:
- \@@_add_grabber:N b
+ \@@_add_grabber:N #1
\@@_prepare_signature:N
}
% \end{macrocode}
% \end{macro}
+% \end{macro}
+% \end{macro}
%
% \begin{macro}{\@@_add_type_D:w}
% \begin{macrocode}
@@ -1842,6 +1904,7 @@
% ignored when looking for that optional argument.
% \changes{v1.0g}{2021/08/07}
% {Replicate argument processors for all embellishments (gh/639)}
+% \changes{v1.3a}{2025-01-23}{Extend to support \texttt{c}-type grabbing}
% \begin{macrocode}
\cs_new_protected:Npn \@@_add_grabber:N #1
{
@@ -1856,12 +1919,23 @@
{ \l_@@_suppress_strip_bool }
{ \str_if_eq_p:nn {#1} { D } }
{ _no_strip }
+ \bool_lazy_all:nT
+ {
+ { \l_@@_final_verb_bool }
+ { \str_if_eq_p:nn {#1} { D } }
+ {
+ \int_compare_p:nNn \l_@@_current_arg_int
+ = { \l_@@_total_args_int - 1 }
+ }
+ }
+ { _verb_safe }
:w
}
}
\bool_set_false:N \l_@@_long_bool
\bool_set_false:N \l_@@_obey_spaces_bool
\bool_set_false:N \l_@@_suppress_strip_bool
+ \bool_set_false:N \l_@@_verb_safe_bool
\tl_put_right:Nx \l_@@_process_all_tl
{
{
@@ -2254,7 +2328,7 @@
%
% \changes{v1.1c}{2023/03/12}
% {Distinguish (non-expandable) document commands starting with
-% \cs{@@_start_expandable:nNNNNn}}
+% \cs{__cmd_start_expandable:nNNNNn}}
% There's one variant: a command begins with \cs{@@_start_expandable:nNNNNn}
% may still be un-expandable/protected if it's defined by
% \cs{NewDocumentCommand} and friends, with empty or only m-type arguments.
@@ -2605,7 +2679,7 @@
%
% \changes{v1.1c}{2023/03/12}
% {Distinguish (non-expandable) document commands starting with
-% \cs{@@_start_expandable:nNNNNn}}
+% \cs{__cmd_start_expandable:nNNNNn}}
% There's one variant: a command begins with \cs{@@_start_expandable:nNNNNn}
% may still be un-expandable/protected if it's defined by
% \cs{NewDocumentCommand} and friends, with empty or only m-type arguments.
@@ -3002,68 +3076,320 @@
% \end{macrocode}
% \end{macro}
%
-% \begin{macro}
-% {
-% \@@_grab_D:w ,
-% \@@_grab_D_long:w ,
-% \@@_grab_D_obey_spaces:w ,
-% \@@_grab_D_long_obey_spaces:w ,
-% \@@_grab_D_no_strip:w ,
-% \@@_grab_D_long_no_strip:w ,
-% \@@_grab_D_obey_spaces_no_strip:w ,
-% \@@_grab_D_long_obey_spaces_no_strip:w
-% }
-% \changes{v1.1a}{2022/08/10}{Add support for skipping brace stripping}
-% The generic delimited argument grabber. The auxiliary function does
-% a peek test before calling \cs{@@_grab_D_call:Nw}, so that the
-% optional nature of the argument works as expected.
+% \begin{macro}{\@@_grab_c:w}
+% \changes{v1.3a}{2025-01-23}{New \texttt{c}-type grabbing function}
+% \begin{macro}{\@@_grab_c_obey_spaces:w}
+% \begin{macro}{\@@_grab_c_start:n}
+% \begin{macro}{\@@_grab_c_first:w}
+% \begin{macro}{\@@_grab_c_loop:w}
+% Collecting an environment body verbatim shares some ideas with the
+% \texttt{v}-type grabber, and others with the standard \texttt{filecontents}
+% environment. The start is to set the end-of-line to a predictable value
+% and to deactivate the specials.
% \begin{macrocode}
-\cs_new_protected:Npn \@@_grab_D:w #1#2#3 \@@_run_code:
+\cs_new_protected:Npn \@@_grab_c:w #1 \@@_run_code:
{
- \@@_grab_D_aux:NNnNNN #1 #2 {#3} \cs_set_protected_nopar:Npn
- \@@_peek_nonspace_remove:NTF \use_ii:nn
+ \bool_set_false:N \l_@@_obey_spaces_bool
+ \@@_grab_c_start:n {#1}
}
-\cs_new_protected:Npn \@@_grab_D_long:w #1#2#3 \@@_run_code:
+\cs_new_protected:Npn \@@_grab_c_obey_spaces:w #1 \@@_run_code:
{
- \@@_grab_D_aux:NNnNNN #1 #2 {#3} \cs_set_protected:Npn
- \@@_peek_nonspace_remove:NTF \use_ii:nn
+ \bool_set_true:N \l_@@_obey_spaces_bool
+ \@@_grab_c_start:n {#1}
}
-\cs_new_protected:Npn \@@_grab_D_obey_spaces:w #1#2#3 \@@_run_code:
+\cs_new_protected:Npn \@@_grab_c_start:n #1
{
- \@@_grab_D_aux:NNnNNN #1 #2 {#3} \cs_set_protected_nopar:Npn
- \@@_peek_meaning_remove:NTF \use_ii:nn
+ \tl_set:Nn \l_@@_signature_tl {#1}
+ \group_begin:
+ \tl_clear:N \l_@@_v_arg_tl
+ \tex_escapechar:D = 92 \scan_stop:
+ \tex_endlinechar:D = `\^^M \scan_stop:
+ \cs_set_eq:NN \do \char_set_catcode_other:N
+ \dospecials
+ \char_set_catcode_other:n { `\^^M }
+ \@@_grab_c_first:w
}
-\cs_new_protected:Npn \@@_grab_D_long_obey_spaces:w #1#2#3 \@@_run_code:
+% \end{macrocode}
+% Notice here and below that we cannot use |\token_to_str:N \end| as that
+% would have the wrong category codes for the letters.
+% \begin{macrocode}
+\group_begin:
+ \char_set_catcode_other:N \^^M %
+ \cs_new_protected:Npn \@@_grab_c_first:w #1 ^^M %
+ { %
+ \tl_if_blank:nF {#1} %
+ { %
+ \msg_warning:nnee { cmd } { chars-dropped-first-line } %
+ { \exp_not:n {#1} } %
+ { \exp_not:V \@currenvir } %
+ } %
+ \@@_grab_c_loop:w #1 ^^M %
+ } %
+ \cs_new_protected:Npe \@@_grab_c_loop:w #1 ^^M %
+ { %
+ \exp_not:N \@@_grab_c_auxi:w #1 %
+ \c_backslash_str end %
+ \scan_stop: %
+ } %
+\group_end:
+% \end{macrocode}
+% \end{macro}
+% \end{macro}
+% \end{macro}
+% \end{macro}
+% \end{macro}
+% \begin{macro}{\@@_grab_c_auxi:w}
+% \begin{macro}{\@@_grab_c_auxii:w}
+% \begin{macro}{\@@_grab_c_auxiii:N}
+% \begin{macro}{\@@_grab_c_auxiv:}
+% \begin{macro}{\@@_grab_c_auxv:}
+% \begin{macro}{\@@_grab_c_auxvi:N}
+% \begin{macro}{\@@_grab_c_auxvii:}
+% \begin{macro}{\@@_grab_c_auxviii:}
+% We need to see if the current line contains |\end| followed by the name
+% of the current environment. To do that and allow for spaces, we have to
+% work stepwise. First, establish if there is an |\end| at all: remember that
+% here we are dealing with \enquote{other} tokens. Whether these is an
+% |\end| or not, the tokens \emph{before} it form part of the line.
+% \begin{macrocode}
+\use:e
{
- \@@_grab_D_aux:NNnNNN #1 #2 {#3} \cs_set_protected:Npn
- \@@_peek_meaning_remove:NTF \use_ii:nn
+ \cs_new_protected:Npn \exp_not:N \@@_grab_c_auxi:w
+ #1 \c_backslash_str end #2 \scan_stop:
}
-\cs_new_protected:Npn \@@_grab_D_no_strip:w
- #1#2#3 \@@_run_code:
{
- \@@_grab_D_aux:NNnNNN #1 #2 {#3} \cs_set_protected_nopar:Npn
- \@@_peek_nonspace_remove:NTF \use_none:n
+ \tl_put_right:Nn \l_@@_v_arg_tl {#1}
+ \tl_if_empty:nTF {#2}
+ {
+ \tl_put_right:Nn \l_@@_v_arg_tl { \obeyedline }
+ \@@_grab_c_loop:w
+ }
+ { \@@_grab_c_auxii:w #2 \scan_stop: }
}
-\cs_new_protected:Npn \@@_grab_D_long_no_strip:w
- #1#2#3 \@@_run_code:
+% \end{macrocode}
+% There is an |\end|, so we now remove the trailing marker we needed
+% to do the test. This is stripped off, then we need to examine the
+% rest of the line one token at a time: see |verbatim.dtx| for the
+% inspiration. Notice that we use |^^M| here as the end marker: this allows
+% looping to look for multiple |\end| entries in the line.
+% \begin{macrocode}
+\group_begin:
+ \char_set_catcode_other:N \^^M %
+ \use:e %
+ { %
+ \cs_new_protected:Npe \exp_not:N \@@_grab_c_auxii:w %
+ #1 \c_backslash_str end \scan_stop: %
+ } %
+ { %
+ \tl_set:Nn \exp_not:N \l_@@_tmpa_tl
+ { \c_backslash_str end } %
+ \exp_not:N \@@_grab_c_auxiii:N #1 ^^M %
+ } %
+% \end{macrocode}
+% Within the line, we need to collect up the tokens: if we do not find
+% the end-of-environment argument, we will need those to reinsert. There
+% are three special cases here: |^^M| (end of line: tidy up and back to
+% the main loop), \verb*| | (possibly skip over) and |{| (start the inner
+% loop). Anything else means we move back to examine the rest of the line
+% for any more |\end| entries.
+% \begin{macrocode}
+ \cs_new_protected:Npn \@@_grab_c_auxiii:N #1 %
+ { %
+ \token_case_charcode:NnF #1 %
+ { %
+ ^^M %
+ { \@@_grab_c_auxiv: } %
+ \c_space_token %
+ { %
+ \tl_put_right:Nn \l_@@_tmpa_tl {#1} %
+ \@@_grab_c_auxiii:N %
+ } %
+ \c_group_begin_token %
+ { %
+ \tl_set:Nn \l_@@_tmpb_tl {#1} %
+ \@@_grab_c_auxvi:N %
+ } %
+ } %
+ { \@@_grab_c_auxv: } %
+ } %
+\group_end:
+\cs_new_protected:Npn \@@_grab_c_auxiv:
{
- \@@_grab_D_aux:NNnNNN #1 #2 {#3} \cs_set_protected:Npn
- \@@_peek_nonspace_remove:NTF \use_none:n
+ \tl_put_right:Ne \l_@@_v_arg_tl
+ {
+ \exp_not:V \l_@@_tmpa_tl
+ \exp_not:N \obeyedline
+ }
+ \@@_grab_c_loop:w
}
-\cs_new_protected:Npn \@@_grab_D_obey_spaces_no_strip:w
- #1#2#3 \@@_run_code:
+\cs_new_protected:Npn \@@_grab_c_auxv:
{
- \@@_grab_D_aux:NNnNNN #1 #2 {#3} \cs_set_protected_nopar:Npn
- \@@_peek_meaning_remove:NTF \use_none:n
+ \tl_put_right:NV \l_@@_v_arg_tl \l_@@_tmpa_tl
+ \@@_grab_c_loop:w
+ }
+% \end{macrocode}
+% In the inner loop, we again have only a few special cases. First, we could
+% again have |^^M|, in which case we tidy up using a common auxiliary. Second,
+% we check for the escape char: this cannot happen inside the end of an
+% environment and means we loop, re-inserting the token. Finally, we have
+% |}|, where we need to move on to check what has been collected. Otherwise,
+% collect up and loop. Notice here that the inner loop needs to
+% collect tokens separately: this leaves any spaces after |\end| in
+% \cs{l_@@_tmpa_tl}, so we can test \cs{l_@@_tmpb_tl} directly.
+% \begin{macrocode}
+\group_begin:
+ \char_set_catcode_other:N \^^M %
+ \cs_new_protected:Npe \@@_grab_c_auxvi:N #1 %
+ { %
+ \exp_not:N \token_case_charcode:NnF #1 %
+ { %
+ ^^M %
+ {
+ \exp_not:N \@@_grab_c_auxvii: %
+ \exp_not:N \@@_grab_c_auxiv: %
+ }%
+ \c_backslash_str %
+ { %
+ \exp_not:N \@@_grab_c_auxvii: %
+ \exp_not:N \@@_grab_c_auxv: #1
+ } %
+ \c_group_end_token %
+ { %
+ \tl_put_right:Nn \exp_not:N \l_@@_tmpb_tl {#1} %
+ \exp_not:N \@@_grab_c_auxviii: %
+ } %
+ } %
+ { %
+ \tl_put_right:Nn \exp_not:N \l_@@_tmpb_tl {#1} %
+ \exp_not:N \@@_grab_c_auxvi:N %
+ } %
+ } %
+\group_end: %
+\cs_new_protected:Npn \@@_grab_c_auxvii:
+ { \tl_put_right:NV \l_@@_tmpa_tl \l_@@_tmpb_tl }
+\cs_new_protected:Npn \@@_grab_c_auxviii:
+ {
+ \str_if_eq:eeTF { \exp_not:V \l_@@_tmpb_tl } { { \@currenvir } }
+ { \@@_grab_c_end:w }
+ {
+ \@@_grab_c_auxvii:
+ \@@_grab_c_auxv:
+ }
}
-\cs_new_protected:Npn \@@_grab_D_long_obey_spaces_no_strip:w
- #1#2#3 \@@_run_code:
+% \end{macrocode}
+% \end{macro}
+% \end{macro}
+% \end{macro}
+% \end{macro}
+% \end{macro}
+% \end{macro}
+% \end{macro}
+% \end{macro}
+% \begin{macro}{\@@_grab_c_end:w}
+% \begin{macro}{\@@_grab_c_end:n}
+% \begin{macro}{\@@_grab_c_end_auxi:w}
+% \begin{macro}{\@@_grab_c_end_auxii:w}
+% \begin{macro}{\@@_grab_c_end_auxiii:w}
+% To end the collection, we clean up the last line: once again we need to
+% find |^^M|. Once that is done, we can warn if there is anything left
+% behind.
+% \begin{macrocode}
+\group_begin:
+ \char_set_catcode_other:N \^^M %
+ \cs_new_protected:Npn \@@_grab_c_end:w #1 ^^M %
+ { %
+ \tl_if_blank:nF {#1} %
+ { %
+ \msg_warning:nnee { cmd } { chars-dropped-last-line } %
+ { \exp_not:n {#1} } %
+ { \exp_not:V \@currenvir } %
+ } %
+ \exp_args:NNNo \group_end: %
+ \tl_set:Nn \l_@@_v_arg_tl { \l_@@_v_arg_tl } %
+ \@@_add_arg:x %
+ { %
+ \bool_if:NTF \l_@@_obey_spaces_bool %
+ { \exp_not:V } %
+ { \exp_args:NV \@@_grab_c_end:n } %
+ \l_@@_v_arg_tl %
+ } %
+ \exp_args:NV \end \@currenvir %
+ } %
+\group_end: %
+% \end{macrocode}
+% Look for line markers at each end and tidy up if required.
+% \begin{macrocode}
+\cs_new:Npn \@@_grab_c_end:n #1
+ {
+ \@@_grab_c_end_auxi:w \q_nil #1 \q_nil
+ \obeyedline \obeyedline \q_nil \q_stop
+ }
+\cs_new:Npn \@@_grab_c_end_auxi:w #1 \q_nil \obeyedline
+ { \@@_grab_c_end_auxii:w #1 \q_nil }
+\cs_new:Npn \@@_grab_c_end_auxii:w \q_nil #1 \obeyedline \q_nil
+ { \@@_grab_c_end_auxiii:w #1 \q_nil }
+\cs_new:Npn \@@_grab_c_end_auxiii:w #1 \q_nil #2 \q_stop
+ { \exp_not:n {#1} }
+% \end{macrocode}
+% \end{macro}
+% \end{macro}
+% \end{macro}
+% \end{macro}
+% \end{macro}
+%
+% \begin{macro}
+% {
+% \@@_grab_D:w ,
+% \@@_grab_D_no_strip:w ,
+% \@@_grab_D_obey_spaces:w ,
+% \@@_grab_D_obey_spaces_no_strip:w ,
+% \@@_grab_D_long:w ,
+% \@@_grab_D_long_no_strip:w ,
+% \@@_grab_D_long_obey_spaces:w ,
+% \@@_grab_D_long_obey_spaces_no_strip:w
+% }
+% \changes{v1.1a}{2022/08/10}{Add support for skipping brace stripping}
+% \changes{v1.3a}{2025-01-23}{Auto-generate D-grabbers}
+% The generic delimited argument grabber. The auxiliary function does
+% a peek test before calling \cs{@@_grab_D_call:Nw}, so that the
+% optional nature of the argument works as expected.
+% \begin{macrocode}
+\tl_map_inline:nn { { } { _long } }
{
- \@@_grab_D_aux:NNnNNN #1 #2 {#3} \cs_set_protected:Npn
- \@@_peek_meaning_remove:NTF \use_none:n
+ \tl_map_inline:nn { { } { _obey_spaces } }
+ {
+ \tl_map_inline:nn { { } { _no_strip } }
+ {
+ \tl_map_inline:nn { { } { _verb_safe } }
+ {
+ \cs_new_protected:cpe { @@_grab_D #1 ##1 ####1 ########1 :w }
+ ################1################2################3
+ \@@_run_code:
+ {
+ \exp_not:N \@@_grab_D_aux:NNnNNNN
+ ################1 ################2 {################3}
+ \str_if_eq:nnTF {#1} { _long }
+ \cs_set_protected:Npn
+ \cs_set_protected_nopar:Npn
+ \str_if_eq:nnTF {##1} { _obey_spaces }
+ { \exp_not:N \@@_peek_meaning_remove:NTF }
+ { \exp_not:N \@@_peek_nonspace_remove:NTF }
+ \str_if_eq:nnTF {####1} { _no_strip }
+ { \exp_not:N \use_none:n }
+ { \exp_not:N \use_ii:nn }
+ \str_if_eq:nnTF {########1} { _verb_safe }
+ { \exp_not:N \use:n }
+ { \exp_not:N \use_none:n }
+ }
+ }
+ }
+ }
}
% \end{macrocode}
-% \begin{macro}{\@@_grab_D_aux:NNnNNN}
+% \begin{macro}{\@@_grab_D_aux:NNnNNNN}
+% \changes{v1.3a}{2025-01-23}{Extend to support \texttt{c}-type grabbing}
+% \begin{macro}{\@@_grab_D_verb_safe:NN}
+% \changes{v1.3a}{2025-01-23}{New macro}
% \begin{macro}{\@@_grab_D_aux:NNnNN}
% This is a bit complicated. The idea is that, in order to check for
% nested optional argument tokens (\texttt{[[...]]} and so on) the
@@ -3073,12 +3399,51 @@
% prevents loss of braces, and there is then a test to see if there are
% nested delimiters to handle.
% \begin{macrocode}
-\cs_new_protected:Npn \@@_grab_D_aux:NNnNNN #1#2#3#4#5#6
+\group_begin:
+ \char_set_catcode_other:N \^^M
+ \cs_new_protected:Npn \@@_grab_D_aux:NNnNNNN #1#2#3#4#5#6#7
+ {
+ \@@_grab_D_aux:NNnNN #1#2 {#3} #4 #6
+ #7
+ {
+ \group_begin:
+ \@@_grab_D_verb_safe:NN #1#5
+ }
+ #5 #1
+ {
+ #7 { \group_end: }
+ \@@_grab_D_call:Nw #1
+ }
+ {
+ #7 { \group_end: }
+ \@@_add_arg:o \c_novalue_tl
+ }
+ }
+\group_end:
+% \end{macrocode}
+% The only awkwardness here is the need to preserve the catcode of the search
+% token: this is done low-level for performance reasons. Only values that
+% are realistic are included. As this does not cover spaces when they are
+% skipped that has to be covered separately.
+% \begin{macrocode}
+\cs_new_protected:Npn \@@_grab_D_verb_safe:NN #1#2
{
- \@@_grab_D_aux:NNnNN #1#2 {#3} #4 #6
- #5 #1
- { \@@_grab_D_call:Nw #1 }
- { \@@_add_arg:o \c_novalue_tl }
+ \cs_set_eq:NN \do \char_set_catcode_other:N
+ \dospecials
+ \char_set_catcode_other:N \^^M
+ \token_if_eq_meaning:NNT #2 \@@_peek_nonspace_remove:NTF
+ { \char_set_catcode_space:n { `\ } }
+ \use:c
+ {
+ char_set_catcode_
+ \if_catcode:w \c_math_toggle_token #1 math_toggle \else:
+ \if_catcode:w ^ #1 math_superscript \else:
+ \if_catcode:w \c_math_subscript_token #1 math_subscript \else:
+ \if_catcode:w A #1 letter \else:
+ other \fi: \fi: \fi: \fi:
+ :N
+ }
+ #1
}
% \end{macrocode}
% Inside the \enquote{standard} grabber, there is a test to see if the
@@ -3113,6 +3478,7 @@
% \end{macro}
% \end{macro}
% \end{macro}
+% \end{macro}
%
% \begin{macro}{\@@_grab_D_nested:NNnN}
% \begin{macro}{\@@_grab_D_nested:w}
@@ -3300,7 +3666,6 @@
% \end{macro}
% \end{macro}
%
-%
% \begin{macro}{\@@_grab_m:w}
% \begin{macro}{\@@_grab_m_long:w}
% Collecting a single mandatory argument is quite easy.
@@ -3663,20 +4028,37 @@
% \end{macro}
%
% \begin{macro}{\@@_grab_v_aux_catcodes:}
+% \changes{v1.2g}{2025-01-21}{Store spaces and tabs as active chars}
% \begin{macro}{\@@_grab_v_aux_abort:n}
% The approach for short verbatim arguments is to make the end-line
% character a macro parameter character: this is forbidden by the
% rest of the code. Then the error branch can check what caused the
% bail out and give the appropriate error message.
% \begin{macrocode}
+%<latexrelease>\IncludeInRelease{2025/06/01}{\@@_grab_v_aux_catcodes:}%
+%<latexrelease> {Active~spaces~and~tabs}
\cs_new_protected:Npn \@@_grab_v_aux_catcodes:
{
\cs_set_eq:NN \do \char_set_catcode_other:N
\dospecials
+ \char_set_catcode_active:n { `\ }
+ \char_set_catcode_active:n { `\^^I }
\bool_if:NTF \l_@@_long_bool
{ \char_set_catcode_other:n { \tex_endlinechar:D } }
{ \char_set_catcode_parameter:n { \tex_endlinechar:D } }
}
+%<latexrelease>\EndIncludeInRelease
+%<latexrelease>\IncludeInRelease{2020/10/01}{\@@_grab_v_aux_catcodes:}%
+%<latexrelease> {Active~spaces~and~tabs}
+%<latexrelease>\cs_new_protected:Npn \@@_grab_v_aux_catcodes:
+%<latexrelease> {
+%<latexrelease> \cs_set_eq:NN \do \char_set_catcode_other:N
+%<latexrelease> \dospecials
+%<latexrelease> \bool_if:NTF \l_@@_long_bool
+%<latexrelease> { \char_set_catcode_other:n { \tex_endlinechar:D } }
+%<latexrelease> { \char_set_catcode_parameter:n { \tex_endlinechar:D } }
+%<latexrelease> }
+%<latexrelease>\EndIncludeInRelease
\cs_new_protected:Npn \@@_grab_v_aux_abort:n #1
{
\@@_grab_v_group_end:
@@ -3703,26 +4085,38 @@
%
% \begin{macro}{\@@_grab_v_aux_put:N}
% \changes{v1.2d}{2024/03/21}{Collect \cs{endlinechar} as \cs{obeyedline}}
-% Storing one token in the collected argument. Most tokens are
-% converted to category code $12$, with the exception of active
-% characters, and spaces (not sure what should be done for those).
+% \changes{v1.2g}{2025-01-21}{Simplify catcode handling}
+% Storing one token in the collected argument: everything as-is except
+% for end-of-lines, with \cs{exp_not:N} to handle actives.
% \begin{macrocode}
-%<latexrelease>\IncludeInRelease{2024/06/01}{\@@_grab_v_aux_put:N}%
-%<latexrelease> {Endlines~as~\obeyedline}
+%<latexrelease>\IncludeInRelease{2025-06-01}{\@@_grab_v_aux_put:N}%
+%<latexrelease> {Use~more~std~catcodes}
\cs_new_protected:Npn \@@_grab_v_aux_put:N #1
{
\tl_put_right:Nx \l_@@_v_arg_tl
{
- \token_if_active:NTF #1
+ \int_compare:nNnTF {`#1} = \tex_endlinechar:D
+ { \exp_not:N \obeyedline }
{ \exp_not:N #1 }
- {
- \int_compare:nNnTF {`#1} = \tex_endlinechar:D
- { \exp_not:N \obeyedline }
- { \token_to_str:N #1 }
- }
}
}
%<latexrelease>\EndIncludeInRelease
+%<latexrelease>\IncludeInRelease{2024/06/01}{\@@_grab_v_aux_put:N}%
+%<latexrelease> {Endlines~as~\obeyedline}
+%<latexrelease>\cs_new_protected:Npn \@@_grab_v_aux_put:N #1
+%<latexrelease> {
+%<latexrelease> \tl_put_right:Nx \l_@@_v_arg_tl
+%<latexrelease> {
+%<latexrelease> \token_if_active:NTF #1
+%<latexrelease> { \exp_not:N #1 }
+%<latexrelease> {
+%<latexrelease> \int_compare:nNnTF {`#1} = \tex_endlinechar:D
+%<latexrelease> { \exp_not:N \obeyedline }
+%<latexrelease> { \token_to_str:N #1 }
+%<latexrelease> }
+%<latexrelease> }
+%<latexrelease> }
+%<latexrelease>\EndIncludeInRelease
%<latexrelease>\IncludeInRelease{2020/10/01}{\@@_grab_v_aux_put:N}%
%<latexrelease> {Endlines~as~\obeyedline}
%<latexrelease>\cs_new_protected:Npn \@@_grab_v_aux_put:N #1
@@ -4568,9 +4962,9 @@
% \begin{macro}{\@@_cmd_type_cases:NnnnnnF}
% \changes{v1.0d}{2021/04/19}{Renamed \cs{__cmd_cmd_if_xparse:NTF} to
% \cs{__kernel_cmd_if_xparse:NTF} for cross-module usage}
-% \changes{v1.0d}{2021/07/30}{Added \cs{@@_cmd_type_cases:NnnnnF} for
+% \changes{v1.0d}{2021/07/30}{Added \cs{__cmd_cmd_type_cases:NnnnnF} for
% \cs{NewCommandCopy} and \cs{ShowCommand} support}
-% \changes{v1.0l}{2022/03/18}{Fix \cs{@@_cmd_type_cases:NnnnnF}
+% \changes{v1.0l}{2022/03/18}{Fix \cs{__cmd_cmd_type_cases:NnnnnF}
% prematurely expanding macros (gh/795)}
% \changes{v1.2b}{2023/12/01}{Extend for optimized commands}
% \changes{v1.2e}{2024/04/17}{Use \cs{__kernel_cs_parameter_spec:N} instead
@@ -4716,12 +5110,13 @@
% \changes{v1.0f}{2021/06/04}{Normalize various error messages}
% \changes{v1.2c}{2023/12/22}
% {Generalize message \texttt{invalid-bang} (gh/1198)}
+% \changes{v1.3a}{2025-01-23}{Generalize message \texttt{arg-after-body}}
% \begin{macrocode}
\msg_new:nnnn { cmd } { arg-after-body }
- { Argument~type~'b'~must~be~last~in~#1. }
+ { Argument~type~'#1'~must~be~last~in~#2. }
{
- The~'b'~argument~type~must~come~last~but~it~is~followed~
- by~'#2'~in~the~argument~specification.~This~is~not~allowed.
+ The~'#1'~argument~type~must~come~last~but~it~is~followed~
+ by~'#3'~in~the~argument~specification.~This~is~not~allowed.
\c_@@_ignore_def_tl
}
\msg_new:nnnn { cmd } { bad-arg-spec }
@@ -4745,6 +5140,22 @@
with~a~command~that~was~never~defined.
\c_@@_ignore_def_tl
}
+% \end{macrocode}
+% \changes{v1.3a}{2025-01-23}{New message \texttt{chars-dropped-first-line}}
+% \changes{v1.3a}{2025-01-23}{New message \texttt{chars-dropped-last-line}}
+% \begin{macrocode}
+\msg_new:nnnn { cmd } { chars-dropped-first-line }
+ { Characters~'#1'~dropped~on~first~line~of~#2~environment. }
+ {
+ LaTeX~was~collecting~a~verbatim-like~environment,~and~the~characters~
+ '#1'~were~found~after~'\begin{#2}~on~the~first~line:~this~is~not~supported.
+ }
+\msg_new:nnnn { cmd } { chars-dropped-last-line }
+ { Characters~'#1'~dropped~after~end~of~#2~environment. }
+ {
+ LaTeX~was~collecting~a~verbatim-like~environment,~and~the~characters~
+ '#1'~were~found~after~\end{#2}~on~the~last~line:~this~is~not~supported.
+ }
\msg_new:nnnn { cmd } { env-already-defined }
{ Environment~'#1'~already~defined. }
{
@@ -5295,8 +5706,10 @@
%
% \begin{macro}{\ProcessList}
% To support \cs{SplitList}.
+% \changes{v1.3b}{2025-02-01}
+% {Use \cs{tl_map_tokens:nn} instead of \cs{tl_map_function:nN}}
% \begin{macrocode}
-\cs_new_eq:NN \ProcessList \tl_map_function:nN
+\cs_new_eq:NN \ProcessList \tl_map_tokens:nn
% \end{macrocode}
% \end{macro}
%