diff options
Diffstat (limited to 'Master/texmf-dist/source/latex-dev/base/ltcmd.dtx')
-rw-r--r-- | Master/texmf-dist/source/latex-dev/base/ltcmd.dtx | 571 |
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} % |