From 169b0fc7aca2e74ef04958e6488af045806e48f3 Mon Sep 17 00:00:00 2001 From: Joseph Wright Date: Wed, 11 Dec 2024 09:08:32 +0000 Subject: [PATCH 01/10] Auto-generate D-type grabbers --- base/ltcmd.dtx | 77 +++++++++++++++++++------------------------------- 1 file changed, 29 insertions(+), 48 deletions(-) diff --git a/base/ltcmd.dtx b/base/ltcmd.dtx index 339d314e9..d36dfdb2f 100644 --- a/base/ltcmd.dtx +++ b/base/ltcmd.dtx @@ -34,8 +34,8 @@ %%% From File: ltcmd.dtx % % \begin{macrocode} -\def\ltcmdversion{v1.2g} -\def\ltcmddate{2025-01-21} +\def\ltcmdversion{v1.3a} +\def\ltcmddate{2025-01-22} % \end{macrocode} % %<*driver> @@ -3005,62 +3005,43 @@ % \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: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-22}{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} -\cs_new_protected:Npn \@@_grab_D:w #1#2#3 \@@_run_code: - { - \@@_grab_D_aux:NNnNNN #1 #2 {#3} \cs_set_protected_nopar:Npn - \@@_peek_nonspace_remove:NTF \use_ii:nn - } -\cs_new_protected:Npn \@@_grab_D_long:w #1#2#3 \@@_run_code: - { - \@@_grab_D_aux:NNnNNN #1 #2 {#3} \cs_set_protected:Npn - \@@_peek_nonspace_remove:NTF \use_ii:nn - } -\cs_new_protected:Npn \@@_grab_D_obey_spaces:w #1#2#3 \@@_run_code: - { - \@@_grab_D_aux:NNnNNN #1 #2 {#3} \cs_set_protected_nopar:Npn - \@@_peek_meaning_remove:NTF \use_ii:nn - } -\cs_new_protected:Npn \@@_grab_D_long_obey_spaces:w #1#2#3 \@@_run_code: +\tl_map_inline:nn { { } { _long } } { - \@@_grab_D_aux:NNnNNN #1 #2 {#3} \cs_set_protected:Npn - \@@_peek_meaning_remove:NTF \use_ii:nn - } -\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 - } -\cs_new_protected:Npn \@@_grab_D_long_no_strip:w - #1#2#3 \@@_run_code: - { - \@@_grab_D_aux:NNnNNN #1 #2 {#3} \cs_set_protected:Npn - \@@_peek_nonspace_remove:NTF \use_none:n - } -\cs_new_protected:Npn \@@_grab_D_obey_spaces_no_strip:w - #1#2#3 \@@_run_code: - { - \@@_grab_D_aux:NNnNNN #1 #2 {#3} \cs_set_protected_nopar:Npn - \@@_peek_meaning_remove:NTF \use_none:n - } -\cs_new_protected:Npn \@@_grab_D_long_obey_spaces_no_strip:w - #1#2#3 \@@_run_code: - { - \@@_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 } } + { + \cs_new_protected:cpe { @@_grab_D #1 ##1 ####1 :w } + ########1########2########3 \@@_run_code: + { + \exp_not:N \@@_grab_D_aux:NNnNNN + ########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 } + } + } + } } % \end{macrocode} % \begin{macro}{\@@_grab_D_aux:NNnNNN} From c1dfe0f2f040e195ffc76c1a6a57822225cf3ce4 Mon Sep 17 00:00:00 2001 From: Joseph Wright Date: Tue, 7 Jan 2025 22:57:13 +0000 Subject: [PATCH 02/10] Introduce an f-specification argument in ltcmd --- base/changes.txt | 4 + base/doc/ltnews41.tex | 49 ++++ base/doc/usrguide.tex | 37 ++- base/ltcmd.dtx | 307 +++++++++++++++++-- base/testfiles-ltcmd/ltcmd005.luatex.tlg | 358 ----------------------- base/testfiles-ltcmd/ltcmd005.lvt | 48 --- base/testfiles-ltcmd/ltcmd005.tlg | 52 +--- base/testfiles-ltcmd/ltcmd009.lvt | 138 +++++++++ base/testfiles-ltcmd/ltcmd009.tlg | 59 ++++ 9 files changed, 570 insertions(+), 482 deletions(-) delete mode 100644 base/testfiles-ltcmd/ltcmd005.luatex.tlg create mode 100644 base/testfiles-ltcmd/ltcmd009.lvt create mode 100644 base/testfiles-ltcmd/ltcmd009.tlg diff --git a/base/changes.txt b/base/changes.txt index d860d4a9a..680d88459 100644 --- a/base/changes.txt +++ b/base/changes.txt @@ -6,6 +6,10 @@ to completeness or accuracy and it contains some references to files that are not part of the distribution. ================================================================================ +2025-01-22 Joseph Wright + * ltcmd.dtx, usrguide.tex + New "f"-type argument + 2025-01-21 Frank Mittelbach * ltoutput.dtx (subsection{Floats}): diff --git a/base/doc/ltnews41.tex b/base/doc/ltnews41.tex index 998e1945c..42b1db4c7 100644 --- a/base/doc/ltnews41.tex +++ b/base/doc/ltnews41.tex @@ -244,6 +244,55 @@ \subsection{Socket and plug conditionals} % \githubissue{1577} +\subsection{Collecting environment bodies verbatim} + +The mechanisms in \pkg{ltcmd} (\enquote{\pkg{xparse}}) offer a powerful way to +specify a range of types of document command and environment syntax. This +includes the ability to collect the entire body of an environment, for cases +where treating it as a standard argument is useful. It is also possible in +\pkg{ltcmd} to define arguments which grab their content verbatim, another +specialist argument form. To date, however, it was not possible to combine +these two ideas. + +In this release, a new specifier~\texttt{f} is introduced, which collects the +body of an environment in a verbatim-like way. Like the existing +\texttt{v}~specification, each separate line is marked by the special +\cs{obeyedline} marker, which as standard issues a normal paragraph. Thus, this +new specifier is usable both for typesetting and collecting file contents (the +\texttt{f} indicates \enquote{\texttt{filecontents}-like}). Thus, we may use +\begin{verbatim} +\NewDocumentEnvironment + {MyVerbatim}{!O{\ttfamily} f} + {\begin{center}#1 #2\end{center}} {} +\begin{MyVerbatim}[\ttfamily\itshape] + % Some code is shown here + $y = mx + c$ +\end{MyVerbatim} +\end{verbatim} +to obtain +% We can't actually use ... +%\NewDocumentEnvironment{MyVerbatim}{!O{\ttfamily} f} +% {\begin{center} #1 #2\end{center}} {} +% ... as this would prevent LaTeX News 41 being processed with LaTeX before +% 2025-06-01! +% So instead ... +\begin{center} +\makeatletter +\def\@verbatim{% + \trivlist + \centering + \let \do \@makeother + \dospecials + \obeylines + \normalfont \ttfamily \itshape + \@noligs +} +\begin{verbatim} + % Some code is shown here + $y = mx + c$ +\end{verbatim} +\end{center} + \section{Code improvements} \subsection{Refinement of \cs{MakeTitlecase}} diff --git a/base/doc/usrguide.tex b/base/doc/usrguide.tex index 3a0141b85..1971ddade 100644 --- a/base/doc/usrguide.tex +++ b/base/doc/usrguide.tex @@ -43,7 +43,7 @@ \texttt{usrguide.tex} for full details.}% } -\date{2025-01-21} +\date{2025-01-22} \NewDocumentCommand\cs{m}{\texttt{\textbackslash\detokenize{#1}}} \NewDocumentCommand\marg{m}{\arg{#1}} @@ -860,6 +860,41 @@ \subsection{Typesetting verbatim-like material} Similarly, the \texttt{verbatim} environment sets up the meaning of \cs{par} suitable for breaking lines. +\subsection{Verbatim environments} +\label{sec:cmd:verbenv} + +In some cases, as well as grabbing an environment body you will want the +contents to be treated verbatim. This is available using the argument +specification~\texttt{f}. Like the \texttt{b} specification, this has to be the +last one. Thus for example +\begin{verbatim} +\NewDocumentEnvironment{MyVerbatim}{!O{\ttfamily} f} + {\begin{center} #1 #2\end{center}} {} +\begin{MyVerbatim}[\ttfamily\itshape] + % Some code is shown here + $y = mx + c$ +\end{MyVerbatim} +\end{verbatim} +will typeset +\NewDocumentEnvironment{MyVerbatim}{!O{\ttfamily} f} + {\begin{center} #1 #2\end{center}} {} +\begin{MyVerbatim}[\ttfamily\itshape] + % Some code is shown here + $y = mx + c$ +\end{MyVerbatim} + +As grabbing the entire contents verbatim means there are no \cs{par} tokens, +newlines are always permitted: there is no need for a |+| here. As for the +\texttt{v} specification, newlines are stored as \cs{obeyedline}. In a similar +fashion to the \texttt{b}~specification, by default \emph{newlines} are trimmed +at both ends of the body. Putting the prefix |!| before \texttt{f} suppresses +space-trimming. + +Notice that for technical reasons, we recommend that an optional argument +coming immediately before an \texttt{f} specification should not allow any +spaces, achieved by adding the \texttt{!} as showing in the example. However, +this is left as a choice for the user. + \subsection{Performance} For document commands where the argument specification is entirely diff --git a/base/ltcmd.dtx b/base/ltcmd.dtx index d36dfdb2f..19a18b25a 100644 --- a/base/ltcmd.dtx +++ b/base/ltcmd.dtx @@ -326,6 +326,13 @@ % \end{macrocode} % \end{variable} % +% \begin{variable}{\l_@@_verb_safe_bool} +% To track if optional arguments should be collected \enquote{verbatim safe}. +% \begin{macrocode} +\bool_new:N \l_@@_verb_safe_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} @@ -1045,6 +1052,7 @@ \bool_set_false:N \l_@@_some_obey_spaces_bool \bool_set_false:N \l_@@_some_long_bool \bool_set_false:N \l_@@_some_short_bool + \bool_set_false:N \l_@@_verb_safe_bool \@@_normalize_arg_spec_loop:n #1 \q_recursion_tail \q_recursion_tail \q_recursion_tail \q_recursion_stop \int_compare:nNnT \l_@@_current_arg_int > 9 @@ -1202,6 +1210,12 @@ { = { \tl_trim_spaces:n {#1} } } } } +\cs_new_protected:cpn { @@_normalize_type_*:w } #1 + { + \@@_normalize_type_aux:NnNn * {#1} + \l_@@_verb_safe_bool + { \bool_set_true:N \l_@@_verb_safe_bool } + } \cs_new_protected:Npn \@@_normalize_type_aux:NnNn #1#2#3#4 { \quark_if_recursion_tail_stop_do:nn {#2} { \@@_bad_arg_spec:wn } @@ -1331,28 +1345,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-22}{Extend to cover \texttt{f}-type grabbing} +% \begin{macro}{\@@_normalize_type_f:w} +% \changes{v1.3a}{2025-01-22}{New function} +% \begin{macro}{\@@_normalize_type_b_or_f:nn} +% \changes{v1.3a}{2025-01-22}{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_f:nn {#1} { b } } +\cs_new_protected:Npn \@@_normalize_type_f:w #1 + { \@@_normalize_type_b_or_f:nn {#1} { f } } +\cs_new_protected:Npn \@@_normalize_type_b_or_f: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 @@ -1472,6 +1498,7 @@ % \changes{v1.2c}{2023/12/22} % {Clarify error message when \texttt{!} prefix is applied to % non-trailing opt-arg (gh/1198)} +% \changes{v1.3a}{2025-01-22}{Extend to support \texttt{f}-type grabbing} % When adding an argument to the argument specification, set the % \texttt{some_long} or \texttt{some_short} booleans as appropriate % and clear the booleans keeping track of |+|, |!| and |=| markers. @@ -1501,8 +1528,15 @@ { \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} { f } } + { + } \bool_if:NT \l_@@_obey_spaces_bool { ! } + \bool_lazy_or:nnT + { \l_@@_verb_safe_bool } + { \str_if_eq_p:nn {#1} { f } } + { * } \exp_not:n {#1} } \bool_set_false:N \l_@@_long_bool @@ -1543,6 +1577,7 @@ \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 \int_zero:N \l_@@_m_args_int \bool_set_false:N \l_@@_defaults_bool \tl_clear:N \l_@@_defaults_tl @@ -1657,17 +1692,41 @@ % \end{macrocode} % \end{macro} % +% \begin{macro}{\@@_add_type_*:w} +% \changes{v1.3a}{2025-01-22}{New macro} +% \begin{macrocode} +\cs_new_protected:cpn { @@_add_type_*:w } + { + \@@_flush_m_args: + \bool_set_true:N \l_@@_verb_safe_bool + \bool_set_true:N \l_@@_prefixed_bool + \@@_prepare_signature_bypass:N + } +% \end{macrocode} +% \end{macro} +% % \begin{macro}{\@@_add_type_b:w} +% \changes{v1.3a}{2025-01-22}{Extend to cover \texttt{f}-type grabbing} +% \begin{macro}{\@@_add_type_f:w} +% \changes{v1.3a}{2025-01-22}{New function} +% \begin{macro}{\@@_add_type_b_or_f:N} +% \changes{v1.3a}{2025-01-22}{New function} % \begin{macrocode} \cs_new_protected:Npn \@@_add_type_b:w + { \@@_add_type_b_or_f:N b } +\cs_new_protected:Npn \@@_add_type_f:w + { \@@_add_type_b_or_f:N f } +\cs_new_protected:Npn \@@_add_type_b_or_f: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 +1901,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-22}{Extend to support \texttt{f}-type grabbing} % \begin{macrocode} \cs_new_protected:Npn \@@_add_grabber:N #1 { @@ -1856,12 +1916,17 @@ { \l_@@_suppress_strip_bool } { \str_if_eq_p:nn {#1} { D } } { _no_strip } + \bool_lazy_and:nnT + { \l_@@_verb_safe_bool } + { \str_if_eq_p:nn {#1} { D } } + { _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 { { @@ -3025,26 +3090,36 @@ { \tl_map_inline:nn { { } { _no_strip } } { - \cs_new_protected:cpe { @@_grab_D #1 ##1 ####1 :w } - ########1########2########3 \@@_run_code: + \tl_map_inline:nn { { } { _verb_safe } } { - \exp_not:N \@@_grab_D_aux:NNnNNN - ########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 } + \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-222}{Extend to support \texttt{f}-type grabbing} +% \begin{macro}{\@@_grab_D_verb_safe:NN} +% \changes{v1.3a}{2025-01-22}{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 @@ -3054,12 +3129,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 @@ -3094,6 +3208,7 @@ % \end{macro} % \end{macro} % \end{macro} +% \end{macro} % % \begin{macro}{\@@_grab_D_nested:NNnN} % \begin{macro}{\@@_grab_D_nested:w} @@ -3281,6 +3396,132 @@ % \end{macro} % \end{macro} % +% \begin{macro}{\@@_grab_f:w} +% \changes{v1.3a}{2025-01-22}{New \texttt{f}-type grabbing function} +% \begin{macro}{\@@_grab_f_obey_spaces:w} +% \begin{macro}{\@@_grab_f_aux:n} +% \begin{macro}{\@@_grab_f_aux:w} +% \begin{macro}{\@@_grab_f_check:w} +% \begin{macro}{\@@_grab_f_loop:w} +% \begin{macro}{\@@_grab_f_end:nn} +% \begin{macro}{\@@_grab_f_end:n} +% \begin{macro}{\@@_grab_f_end_auxi:w} +% \begin{macro}{\@@_grab_f_end_auxii:w} +% \begin{macro}{\@@_grab_f_end_auxiii: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. We then define the end marker: this +% has to have the correct category codes, so is a token list not a string +% (\texttt{end} and the end-of-env name are catcode-11). We use this to set +% up the end-of-env test, then hand over to an auxiliary. +% \begin{macrocode} +\cs_new_protected:Npn \@@_grab_f:w #1 \@@_run_code: + { + \bool_set_false:N \l_@@_obey_spaces_bool + \@@_grab_f_aux:n {#1} + } +\cs_new_protected:Npn \@@_grab_f_obey_spaces:w #1 \@@_run_code: + { + \bool_set_true:N \l_@@_obey_spaces_bool + \@@_grab_f_aux:n {#1} + } +\cs_new_protected:Npn \@@_grab_f_aux:n #1 + { + \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 + \tl_set:Ne \l_@@_tmpa_tl + { + \c_backslash_str end + \c_left_brace_str \@currenvir \c_right_brace_str + } + \use:e + { + \cs_set_protected:Npn \@@_grab_f_check:w + ##1 \l_@@_tmpa_tl ##2 \l_@@_tmpa_tl ##3 + \scan_stop: + } + { + \tl_if_empty:nTF {##3} + { + \tl_put_right:Nn \l_@@_v_arg_tl + { + ##1 + \obeyedline + } + \@@_grab_f_loop:w + } + { \@@_grab_f_end:nn {##1} {##2} } + } + \@@_grab_f_aux:w + } +% \end{macrocode} +% To allow for the need to change the category code of end-of-lines, we +% put this in a small auxiliary. +% \begin{macrocode} +\group_begin: + \char_set_catcode_other:N \^^M % + \cs_new_protected:Npn \@@_grab_f_aux:w % + { % + \cs_set_protected:Npe \@@_grab_f_loop:w ##1 ^^M % + { % + \@@_grab_f_check:w ##1 % + \l_@@_tmpa_tl % + \l_@@_tmpa_tl % + \scan_stop: % + } % + \char_set_catcode_other:N \^^M % + \@@_grab_f_loop:w % + } % +\group_end: % +\cs_new_protected:Npn \@@_grab_f_check:w { } +\cs_new_protected:Npn \@@_grab_f_loop:w { } +\cs_new_protected:Npn \@@_grab_f_end:nn #1#2 + { + \tl_if_blank:nF {#1#2} + \ERROR + \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_f_end:n } + \l_@@_v_arg_tl + } + \exp_args:NV \end \@currenvir + } +% \end{macrocode} +% Look for line markers at each end and tidy up if required. +% \begin{macrocode} +\cs_new:Npn \@@_grab_f_end:n #1 + { + \@@_grab_f_end_auxi:w \q_nil #1 \q_nil + \obeyedline \obeyedline \q_nil \q_stop + } +\cs_new:Npn \@@_grab_f_end_auxi:w #1 \q_nil \obeyedline + { \@@_grab_f_end_auxii:w #1 \q_nil } +\cs_new:Npn \@@_grab_f_end_auxii:w \q_nil #1 \obeyedline \q_nil + { \@@_grab_f_end_auxiii:w #1 \q_nil } +\cs_new:Npn \@@_grab_f_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} +% \end{macro} +% \end{macro} +% \end{macro} +% \end{macro} +% \end{macro} +% \end{macro} % % \begin{macro}{\@@_grab_m:w} % \begin{macro}{\@@_grab_m_long:w} @@ -4726,12 +4967,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-22}{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 } @@ -4755,6 +4997,15 @@ with~a~command~that~was~never~defined. \c_@@_ignore_def_tl } +% \end{macrocode} +% \changes{v1.3a}{2025-01-22}{New message \texttt{chars-dropped-last-line}} +% \begin{macrocode} +\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. } { diff --git a/base/testfiles-ltcmd/ltcmd005.luatex.tlg b/base/testfiles-ltcmd/ltcmd005.luatex.tlg deleted file mode 100644 index ad67a80c8..000000000 --- a/base/testfiles-ltcmd/ltcmd005.luatex.tlg +++ /dev/null @@ -1,358 +0,0 @@ -This is a generated file for the LaTeX2e validation system. -Don't change this file in any respect. -Author: Bruno Le Floch -============================================================ -TEST 1: Invalid '!' -============================================================ -! LaTeX cmd Error: Invalid argument prefix '!' in command '\testA'. -For immediate help type H . - ... -l. ... } -The prefix '!' is only allowed for trailing optional arguments. You tried to apply it to an optional argument before mandatory 'R(){-NoValue-}'. -LaTeX will ignore this entire definition. -! LaTeX cmd Error: Invalid argument prefix '!' in command '\testA'. -For immediate help type H . - ... -l. ... } -The prefix '!' is only allowed for trailing optional arguments. You tried to apply it to an optional argument before mandatory 'm'. -LaTeX will ignore this entire definition. -! LaTeX cmd Error: Invalid argument prefix '!' in command '\testA'. -For immediate help type H . - ... -l. ... } -The argument specification provided for command '\testA' has two '!' markers applied to the same argument; one is redundant. -! LaTeX cmd Error: Bad argument specification '!+!' for command '\testA'. -For immediate help type H . - ... -l. ... } -The argument specification provided is not valid: one or more mandatory parts are missing. -LaTeX will ignore this entire definition. -! LaTeX cmd Error: Bad argument specification '+!' for command '\testA'. -For immediate help type H . - ... -l. ... } -The argument specification provided is not valid: one or more mandatory parts are missing. -LaTeX will ignore this entire definition. -! LaTeX cmd Error: Bad argument specification '!' for command '\testA'. -For immediate help type H . - ... -l. ... } -The argument specification provided is not valid: one or more mandatory parts are missing. -LaTeX will ignore this entire definition. -! LaTeX cmd Error: Invalid argument prefix '+' in command '\testA'. -For immediate help type H . - ... -l. ... } -The argument specification provided for command '\testA' has two '+' markers applied to the same argument; one is redundant. -! LaTeX cmd Error: Bad argument specification '!o' for \testA. -For immediate help type H . - ... -l. ... } -Expandable commands must have a final mandatory argument (or no arguments at all). You cannot have a terminal optional argument with expandable commands. -! LaTeX cmd Error: Invalid argument prefix '!' in command '\testA'. -For immediate help type H . - ... -l. ... } -The prefix '!' is only allowed for trailing optional arguments. You tried to apply it to 'm'. -LaTeX will ignore this entire definition. -============================================================ -============================================================ -TEST 2: Valid '!' and avoid dropping spaces -============================================================ -Runaway argument? -! Paragraph ended before \testA was complete. - -\par -l. ... } -I suspect you've forgotten a `}', causing me to apply this -control sequence to too much text. How can we recover? -My plan is to forget the whole thing and hope for the best. -|a|-NoValue-|b|| -|a|b|-NoValue-| | -|a|b|d|| -|a|b|c[d]| \c_space_token | -Runaway argument? -! Paragraph ended before \testB was complete. - -\par -l. ... } -I suspect you've forgotten a `}', causing me to apply this -control sequence to too much text. How can we recover? -My plan is to forget the whole thing and hope for the best. -|a|\BooleanFalse |\BooleanTrue || -|a|\BooleanTrue |\BooleanFalse | | -|a|\BooleanTrue |\BooleanTrue || -|a|\BooleanTrue |\BooleanTrue | \c_space_token | -============================================================ -============================================================ -TEST 3: Environment body valid -============================================================ -============================================================ -|...|{\begin {any} \begin {unbalanced} \end {environments} \begin {provided}}{\end {nesting} \end {works} \begin {out} \end {!}}| -|...| -|-NoValue-| [...] \begin {any} \begin {unbalanced} \end {environments} \begin {provided} \end {nesting} \end {works} \begin {out} \end {!} | -|-NoValue-| -============================================================ -TEST 4: Body invalid -============================================================ -|-NoValue-|{}| -! LaTeX Error: \begin{env1} on input line ... ended by \end{env2}. -See the LaTeX manual or LaTeX Companion for explanation. -Type H for immediate help. - ... -l. ... } -Your command was ignored. -Type I to replace it with another command, -or to continue without it. -Runaway argument? -\begin -! Paragraph ended before \environment env2 was complete. - -\par -l. ... } -I suspect you've forgotten a `}', causing me to apply this -control sequence to too much text. How can we recover? -My plan is to forget the whole thing and hope for the best. -! LaTeX cmd Error: Invalid argument type 'b' in command '\testE'. -For immediate help type H . - ... -l. ... } -The letter 'b' can only be used in environment argument specifications, but not for commands. -LaTeX will ignore the entire definition. -! LaTeX cmd Error: Argument type 'b' must be last in environment 'env3'. -For immediate help type H . - ... -l. ... } -The 'b' argument type must come last but it is followed by 'm' in the argument specification. This is not allowed. -LaTeX will ignore this entire definition. -============================================================ -============================================================ -TEST 5: Invalid signatures -============================================================ -! LaTeX cmd Error: Bad argument specification 'O' for command '\testA'. -For immediate help type H . - ... -l. ... } -The argument specification provided is not valid: one or more mandatory parts are missing. -LaTeX will ignore this entire definition. -! LaTeX cmd Error: Bad argument specification 'D[]' for environment 'testA'. -For immediate help type H . - ... -l. ... } -The argument specification provided is not valid: one or more mandatory parts are missing. -LaTeX will ignore this entire definition. -! LaTeX cmd Error: Bad argument specification '!+>{\TrimSpaces }' for environment 'testA'. -For immediate help type H . - ... -l. ... } -The argument specification provided is not valid: one or more mandatory parts are missing. -LaTeX will ignore this entire definition. -! LaTeX cmd Error: Invalid argument prefix '!' in command '\testA'. -For immediate help type H . - ... -l. ... } -The prefix '!' is only allowed for trailing optional arguments. You tried to apply it to an optional argument before mandatory 'm'. -LaTeX will ignore this entire definition. -! LaTeX cmd Error: Invalid argument prefix '!' in environment 'testA'. -For immediate help type H . - ... -l. ... } -The prefix '!' is only allowed for trailing optional arguments. You tried to apply it to an optional argument before mandatory 'm'. -LaTeX will ignore this entire definition. -! LaTeX cmd Error: Too many arguments for command '\testA'. -For immediate help type H . - ... -l. ... } -The argument specification 'mmmmmmmmmmmm' asks for more than 9 arguments. This cannot be implemented. -LaTeX will ignore this entire definition. -! LaTeX cmd Error: Too many arguments for environment 'testA'. -For immediate help type H . - ... -l. ... } -The argument specification 'mmmmmmmmmmmm' asks for more than 9 arguments. This cannot be implemented. -LaTeX will ignore this entire definition. -! LaTeX cmd Error: Invalid argument prefix '!' in command '\testA'. -For immediate help type H . - ... -l. ... } -The argument specification provided for command '\testA' has two '!' markers applied to the same argument; one is redundant. -! LaTeX cmd Error: Invalid argument prefix '!' in environment 'testA'. -For immediate help type H . - ... -l. ... } -The argument specification provided for environment 'testA' has two '!' markers applied to the same argument; one is redundant. -! LaTeX cmd Error: Invalid argument type 'X' in command '\testA'. -For immediate help type H . - ... -l. ... } -The letter 'X' does not specify a known argument type. -LaTeX will ignore this entire definition. -! LaTeX cmd Error: Invalid argument type 'X' in environment 'testA'. -For immediate help type H . - ... -l. ... } -The letter 'X' does not specify a known argument type. -LaTeX will ignore this entire definition. -============================================================ -============================================================ -TEST 6: Already or not yet defined -============================================================ -! LaTeX cmd Error: Command '\space' already defined. -For immediate help type H . - ... -l. ... } -You have used \NewDocumentCommand with a command that already has a definition. -The existing definition of '\space' will not be altered. -|macro:-> | -! LaTeX cmd Error: Environment 'foo' already defined. -For immediate help type H . - ... -l. ... } -You have used \NewDocumentEnvironment with an environment that already has a definition. -The existing definition of 'foo' will not be altered. -foo -! LaTeX cmd Error: Command '\testA' undefined. -For immediate help type H . - ... -l. ... } -You have used \RenewDocumentCommand with a command that was never defined. -LaTeX will ignore this entire definition. -! LaTeX cmd Error: Environment 'testA' undefined. -For immediate help type H . - ... -l. ... } -You have used \RenewDocumentEnvironment with an environment that was never defined. -LaTeX will ignore this entire definition. -============================================================ -============================================================ -TEST 7: Not definable/multi-char -============================================================ -! LaTeX cmd Error: First argument of '\NewDocumentCommand' must be a command. -For immediate help type H . - ... -l. ... } -The first argument of '\NewDocumentCommand' should be the document command that will be defined. The provided argument '+' is a character. Perhaps a backslash is missing? -LaTeX will ignore this entire definition. -! LaTeX cmd Error: First argument of '\NewDocumentCommand' must be a command. -For immediate help type H . - ... -l. ... } -The first argument of '\NewDocumentCommand' should be the document command that will be defined. The provided argument 'space' contains more than one token. Perhaps a backslash is missing? -LaTeX will ignore this entire definition. -! LaTeX cmd Error: Argument delimiter 'ab' invalid in command '\testB'. -For immediate help type H . - ... -l. ... } -The argument specification contains 'ab' in a place where a single token is required. -LaTeX will ignore this entire definition. -! Use of \??? doesn't match its definition. - \??? - ! LaTeX cmd Error: Invalid argument {foo} to \IfBoolean... -l. ... } -If you say, e.g., `\def\a1{...}', then you must always -put `1' after `\a', since control sequence names are -made up of letters only. The macro here has not been -followed by the required stuff, so I'm ignoring it. -FALSE -! Use of \??? doesn't match its definition. - \??? - ! LaTeX cmd Error: Invalid argument {\foo } to \IfBoolean... -l. ... } -If you say, e.g., `\def\a1{...}', then you must always -put `1' after `\a', since control sequence names are -made up of letters only. The macro here has not been -followed by the required stuff, so I'm ignoring it. -! Use of \??? doesn't match its definition. - \??? - ! LaTeX cmd Error: Invalid argument {?} to \IfBoolean... -l. ... } -If you say, e.g., `\def\a1{...}', then you must always -put `1' after `\a', since control sequence names are -made up of letters only. The macro here has not been -followed by the required stuff, so I'm ignoring it. -FALSE -============================================================ -============================================================ -TEST 8: Invalid signatures for expandable commands -============================================================ -! LaTeX cmd Error: Bad argument specification 'mo' for \testA. -For immediate help type H . - ... -l. ... } -Expandable commands must have a final mandatory argument (or no arguments at all). You cannot have a terminal optional argument with expandable commands. -! LaTeX cmd Error: Invalid argument prefix '+' in command '\testA'. -For immediate help type H . - ... -l. ... } -The arguments for an expandable command must not involve short arguments after long arguments. You have tried to mix the two types when defining '\testA'. -! LaTeX cmd Error: Invalid argument type 'v' in \testA. -For immediate help type H . - ... -l. ... } -The letter 'v' specifies an argument type which cannot be used in an expandable command. -LaTeX will ignore this entire definition. -! LaTeX cmd Error: Invalid argument prefix '>' in command '\testA'. -For immediate help type H . - ... -l. ... } -The argument specification for '\testA' contains the processor function '>{\TrimSpaces }'. This is only supported for robust commands, but not for expandable ones. -LaTeX will ignore this entire definition. -============================================================ -============================================================ -TEST 9: Run-time errors -============================================================ -! LaTeX cmd Error: Circular dependency in defaults of command '\testA'. -For immediate help type H . - ... -l. ... } -The default values of two or more arguments of the command '\testA' depend on each other in a way that cannot be resolved. -\C \E \C \E \C \E \C \E -NoValue-|\C \E \C \E \C \E \C \E -NoValue--NoValue-|\C |\C \E |\E -! LaTeX cmd Error: Circular dependency in defaults of environment 'testB'. -For immediate help type H . - ... -l. ... } -The default values of two or more arguments of the environment 'testB' depend on each other in a way that cannot be resolved. -\C \E \C \E \C \E \C \E -NoValue-|\C \E \C \E \C \E \C \E -NoValue--NoValue-|\C |\C \E |\E -! LaTeX cmd Error: Too many ',' separators in argument. -For immediate help type H . - ... -l. ... } -LaTeX was asked to split the input 'a,b,c,d' at each occurrence of the separator ',' into 3 parts. Too many separators were found. -! LaTeX cmd Error: Too many ',' separators in argument. -For immediate help type H . - ... -l. ... } -LaTeX was asked to split the input 'a,b,c,d' at each occurrence of the separator ',' into 3 parts. Too many separators were found. -! LaTeX cmd Error: Verbatim-like command '\testE' illegal in argument. -For immediate help type H . - ... -l. ... } -The command '\testE' takes a verbatim argument and should therefore normally not be used in arguments of other commands or environments. LaTeX found an illegal token (\TYPE ) after '+' and will drop everything up to this point. -Expect further (low-level) errors. --NoValue- -+ -! LaTeX cmd Error: Verbatim-like environment 'testF' illegal in argument. -For immediate help type H . - ... -l. ... } -The environment 'testF' takes a verbatim argument and should therefore normally not be used in arguments of other commands or environments. LaTeX found an illegal token (\TYPE ) after '+' and will drop everything up to this point. -Expect further (low-level) errors. --NoValue- -+ -============================================================ -! LaTeX cmd Error: Verbatim-like command '\testG' ended by end of line. -For immediate help type H . - ... -l. ...\testG+ -The verbatim argument of the command '\testG' cannot contain more than one line, but the end of the current line has been reached. You may have forgotten the closing delimiter. -LaTeX will ignore '+' and you may get some additional (low-level) errors. --NoValue- -+ -! LaTeX cmd Error: Verbatim-like environment 'testH' ended by end of line. -For immediate help type H . - ... -l. ...\begin{testH}+ -The verbatim argument of the environment 'testH' cannot contain more than one line, but the end of the current line has been reached. You may have forgotten the closing delimiter. -LaTeX will ignore '+' and you may get some additional (low-level) errors. --NoValue- -+ diff --git a/base/testfiles-ltcmd/ltcmd005.lvt b/base/testfiles-ltcmd/ltcmd005.lvt index 08d4570f0..94c79952b 100644 --- a/base/testfiles-ltcmd/ltcmd005.lvt +++ b/base/testfiles-ltcmd/ltcmd005.lvt @@ -56,54 +56,6 @@ %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% -% The following tests are outside to allow \ExplSyntaxOff and \obeylines -\TEST { Environment~body~valid } { } - -\NewDocumentEnvironment { env1 } { o >{\SplitList{\par}} + b } - { \TYPE { \tl_to_str:n {|#1|#2|} } } - { \TYPE { \tl_to_str:n {|#1|} } } -\NewDocumentEnvironment { env2 } { ! o ! b } - { \TYPE { \tl_to_str:n {|#1|#2|} } } - { \TYPE { \tl_to_str:n {|#1|} } } -\ExplSyntaxOff -\begin{env1} [...] - \begin{any} - \begin{unbalanced} - \end{environments} - \begin{provided} - - \end{nesting} - \end{works} - \begin{out} - \end{!} -\end{env1} -\begin{env2} [...] - \begin{any} - \begin{unbalanced} - \end{environments} - \begin{provided} - \end{nesting} - \end{works} - \begin{out} - \end{!} -\end{env2} -\ExplSyntaxOn - -%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% -\TEST { Body~invalid } - { - \begin{env1}\end{env2} - \begin{env2} - \par - \end{env2} - \NewDocumentCommand{\testE} { b } { } - \NewDocumentEnvironment{env3} { b m } { } { } - } - -% The rest of this test tests all of xparse's messages. - -%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% - \TEST { Invalid~signatures } { % Disabled: test requires deprecated xparse.sty diff --git a/base/testfiles-ltcmd/ltcmd005.tlg b/base/testfiles-ltcmd/ltcmd005.tlg index 1a20f4d3b..c085f0b5e 100644 --- a/base/testfiles-ltcmd/ltcmd005.tlg +++ b/base/testfiles-ltcmd/ltcmd005.tlg @@ -85,49 +85,7 @@ My plan is to forget the whole thing and hope for the best. |a|\BooleanTrue |\BooleanTrue | \c_space_token | ============================================================ ============================================================ -TEST 3: Environment body valid -============================================================ -============================================================ -|...|{\begin {any} \begin {unbalanced} \end {environments} \begin {provided}}{\end {nesting} \end {works} \begin {out} \end {!}}| -|...| -|-NoValue-| [...] \begin {any} \begin {unbalanced} \end {environments} \begin {provided} \end {nesting} \end {works} \begin {out} \end {!} | -|-NoValue-| -============================================================ -TEST 4: Body invalid -============================================================ -|-NoValue-|{}| -! LaTeX Error: \begin{env1} on input line ... ended by \end{env2}. -See the LaTeX manual or LaTeX Companion for explanation. -Type H for immediate help. - ... -l. ... } -Your command was ignored. -Type I to replace it with another command, -or to continue without it. -Runaway argument? -\begin -! Paragraph ended before \environment env2 was complete. - - \par -l. ... } -I suspect you've forgotten a `}', causing me to apply this -control sequence to too much text. How can we recover? -My plan is to forget the whole thing and hope for the best. -! LaTeX cmd Error: Invalid argument type 'b' in command '\testE'. -For immediate help type H . - ... -l. ... } -The letter 'b' can only be used in environment argument specifications, but not for commands. -LaTeX will ignore the entire definition. -! LaTeX cmd Error: Argument type 'b' must be last in environment 'env3'. -For immediate help type H . - ... -l. ... } -The 'b' argument type must come last but it is followed by 'm' in the argument specification. This is not allowed. -LaTeX will ignore this entire definition. -============================================================ -============================================================ -TEST 5: Invalid signatures +TEST 3: Invalid signatures ============================================================ ! LaTeX cmd Error: Bad argument specification 'O' for command '\testA'. For immediate help type H . @@ -195,7 +153,7 @@ The letter 'X' does not specify a known argument type. LaTeX will ignore this entire definition. ============================================================ ============================================================ -TEST 6: Already or not yet defined +TEST 4: Already or not yet defined ============================================================ ! LaTeX cmd Error: Command '\space' already defined. For immediate help type H . @@ -225,7 +183,7 @@ You have used \RenewDocumentEnvironment with an environment that was never defin LaTeX will ignore this entire definition. ============================================================ ============================================================ -TEST 7: Not definable/multi-char +TEST 5: Not definable/multi-char ============================================================ ! LaTeX cmd Error: First argument of '\NewDocumentCommand' must be a command. For immediate help type H . @@ -273,7 +231,7 @@ followed by the required stuff, so I'm ignoring it. FALSE ============================================================ ============================================================ -TEST 8: Invalid signatures for expandable commands +TEST 6: Invalid signatures for expandable commands ============================================================ ! LaTeX cmd Error: Bad argument specification 'mo' for \testA. For immediate help type H . @@ -299,7 +257,7 @@ The argument specification for '\testA' contains the processor function '>{\Trim LaTeX will ignore this entire definition. ============================================================ ============================================================ -TEST 9: Run-time errors +TEST 7: Run-time errors ============================================================ ! LaTeX cmd Error: Circular dependency in defaults of command '\testA'. For immediate help type H . diff --git a/base/testfiles-ltcmd/ltcmd009.lvt b/base/testfiles-ltcmd/ltcmd009.lvt new file mode 100644 index 000000000..83b9acb7f --- /dev/null +++ b/base/testfiles-ltcmd/ltcmd009.lvt @@ -0,0 +1,138 @@ + +\documentclass{minimal} +\input{regression-test} + +\ExplSyntaxOn +\debug_on:n { check-declarations , deprecation , log-functions } +\ExplSyntaxOff +% \RequirePackage{xparse} + +\begin{document} + +\START +\AUTHOR{Bruno Le Floch, Joseph Wright} + +\ExplSyntaxOn + +% The following tests are outside to allow \ExplSyntaxOff and \obeylines +\TEST { Environment~body~valid } { } + +\NewDocumentEnvironment { env1 } { o >{\SplitList{\par}} +b } + { \TYPE { \tl_to_str:n {|#1|#2|} } } + { \TYPE { \tl_to_str:n {|#1|} } } +\NewDocumentEnvironment { env2 } { !o !b } + { \TYPE { \tl_to_str:n {|#1|#2|} } } + { \TYPE { \tl_to_str:n {|#1|} } } +\ExplSyntaxOff +\begin{env1} [...] + \begin{any} + \begin{unbalanced} + \end{environments} + \begin{provided} + + \end{nesting} + \end{works} + \begin{out} + \end{!} +\end{env1} +\begin{env2} [...] + \begin{any} + \begin{unbalanced} + \end{environments} + \begin{provided} + \end{nesting} + \end{works} + \begin{out} + \end{!} +\end{env2} +\ExplSyntaxOn + +%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% +\TEST { Body~invalid } + { + \begin{env1}\end{env2} + \begin{env2} + \par + \end{env2} + \NewDocumentCommand{\testE} { b } { } + \NewDocumentEnvironment{env3} { b m } { } { } + } + +%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% + +\BEGINTEST { Verbatim~body~collection } +\NewDocumentEnvironment { env3 } { *o f } + { \TYPE { \tl_to_str:n {|#1|#2|} } } + { } +\NewDocumentEnvironment { env4 } { *!o !f } + { \TYPE { \tl_to_str:n {|#1|#2|} } } + { } +\NewDocumentEnvironment { env5 } { *d$$ f } + { \TYPE { \tl_to_str:n {|#1|#2|} } } + { } +\NewDocumentEnvironment { env6 } { *dXX f } + { \TYPE { \tl_to_str:n {|#1|#2|} } } + { } +\NewDocumentEnvironment { env7 } { *d^ ^ f } + { \TYPE { \tl_to_str:n {|#1|#2|} } } + { } +\ExplSyntaxOff +\NewDocumentEnvironment{env8} + {*d__f} + {\TYPE{\detokenize{|#1|#2|}}} + {} + +\begin{env3}% + + Content + &\^ +\end{env3} +\begin{env3}[...] + + Content + &\^ +\end{env3} +\begin{env3} [...] + + Content + &\^ +\end{env3} +\begin{env4}% + + Content + &\^ +\end{env4} +\begin{env4}[...] + + Content + &\^ +\end{env4} +\begin{env4} [...] + + Content + &\^ +\end{env4} +\begin{env5}$...$ + + Content + &\^ +\end{env5} +\begin{env6}X...X + + Content + &\^ +\end{env6} +\begin{env7}^...^ + + Content + &\^ +\end{env7} +\begin{env8}_..._ + + Content + &\^ +\end{env8} + +\ENDTEST + +\END diff --git a/base/testfiles-ltcmd/ltcmd009.tlg b/base/testfiles-ltcmd/ltcmd009.tlg new file mode 100644 index 000000000..ffd6650ec --- /dev/null +++ b/base/testfiles-ltcmd/ltcmd009.tlg @@ -0,0 +1,59 @@ +This is a generated file for the LaTeX2e validation system. +Don't change this file in any respect. +Author: Bruno Le Floch, Joseph Wright +============================================================ +TEST 1: Environment body valid +============================================================ +============================================================ +|...|{\begin {any} \begin {unbalanced} \end {environments} \begin {provided}}{\end {nesting} \end {works} \begin {out} \end {!}}| +|...| +|-NoValue-| [...] \begin {any} \begin {unbalanced} \end {environments} \begin {provided} \end {nesting} \end {works} \begin {out} \end {!} | +|-NoValue-| +============================================================ +TEST 2: Body invalid +============================================================ +|-NoValue-|{}| +! LaTeX Error: \begin{env1} on input line ... ended by \end{env2}. +See the LaTeX manual or LaTeX Companion for explanation. +Type H for immediate help. + ... +l. ... } +Your command was ignored. +Type I to replace it with another command, +or to continue without it. +Runaway argument? +\begin +! Paragraph ended before \environment env2 was complete. + + \par +l. ... } +I suspect you've forgotten a `}', causing me to apply this +control sequence to too much text. How can we recover? +My plan is to forget the whole thing and hope for the best. +! LaTeX cmd Error: Invalid argument type 'b' in command '\testE'. +For immediate help type H . + ... +l. ... } +The letter 'b' can only be used in environment argument specifications, but not for commands. +LaTeX will ignore the entire definition. +! LaTeX cmd Error: Argument type 'b' must be last in environment 'env3'. +For immediate help type H . + ... +l. ... } +The 'b' argument type must come last but it is followed by 'm' in the argument specification. This is not allowed. +LaTeX will ignore this entire definition. +============================================================ +============================================================ +TEST 3: Verbatim body collection +============================================================ +|-NoValue-|%\obeyedline \obeyedline Content\obeyedline &\^| +|...|\obeyedline Content\obeyedline &\^| +|...|\obeyedline Content\obeyedline &\^| +|-NoValue-|%\obeyedline \obeyedline Content\obeyedline &\^\obeyedline | +|...|\obeyedline \obeyedline Content\obeyedline &\^\obeyedline | +|-NoValue-| [...]\obeyedline \obeyedline Content\obeyedline &\^\obeyedline | +|...|\obeyedline Content\obeyedline &\^| +|...|\obeyedline Content\obeyedline &\^| +|...|\obeyedline Content\obeyedline &\^| +|...|\obeyedline Content\obeyedline &\^| +============================================================ From 6dc65e2786814110a1a1d89daa205027e1fe6e30 Mon Sep 17 00:00:00 2001 From: Joseph Wright Date: Wed, 8 Jan 2025 09:57:49 +0000 Subject: [PATCH 03/10] Allow text before but not after \end{foo} for f-type env --- base/doc/usrguide.tex | 7 +++++++ base/ltcmd.dtx | 8 ++++++-- base/testfiles-ltcmd/ltcmd009.lvt | 18 ++++++++++++++++++ base/testfiles-ltcmd/ltcmd009.tlg | 7 +++++++ 4 files changed, 38 insertions(+), 2 deletions(-) diff --git a/base/doc/usrguide.tex b/base/doc/usrguide.tex index 1971ddade..a0023b9c6 100644 --- a/base/doc/usrguide.tex +++ b/base/doc/usrguide.tex @@ -890,6 +890,13 @@ \subsection{Verbatim environments} at both ends of the body. Putting the prefix |!| before \texttt{f} suppresses space-trimming. +Collection of the body takes place on a line-by-line basis: content is +collected up to the end-of-line in the source, then examined before storage. +This means that the line ending the environment (containing in the example +above |\end{MyVerbatim}|) cannot have any text \emph{after} the end of the +environment. Text \emph{before} the end of environment is treated normally, +but note that there is no trailing \cs{obeyedline} added if there is text here. + Notice that for technical reasons, we recommend that an optional argument coming immediately before an \texttt{f} specification should not allow any spaces, achieved by adding the \texttt{!} as showing in the example. However, diff --git a/base/ltcmd.dtx b/base/ltcmd.dtx index 19a18b25a..e4dd4b334 100644 --- a/base/ltcmd.dtx +++ b/base/ltcmd.dtx @@ -3483,8 +3483,12 @@ \cs_new_protected:Npn \@@_grab_f_loop:w { } \cs_new_protected:Npn \@@_grab_f_end:nn #1#2 { - \tl_if_blank:nF {#1#2} - \ERROR + \tl_put_right:Nn \l_@@_v_arg_tl {#1} + \tl_if_blank:nF {#2} + { + \msg_warning:nnee { cmd } { chars-dropped-last-line } { \exp_not:n {#2} } + { \exp_not:V \@currenvir } + } \exp_args:NNNo \group_end: \tl_set:Nn \l_@@_v_arg_tl { \l_@@_v_arg_tl } \@@_add_arg:x diff --git a/base/testfiles-ltcmd/ltcmd009.lvt b/base/testfiles-ltcmd/ltcmd009.lvt index 83b9acb7f..1ff2a8ba2 100644 --- a/base/testfiles-ltcmd/ltcmd009.lvt +++ b/base/testfiles-ltcmd/ltcmd009.lvt @@ -135,4 +135,22 @@ \ENDTEST +\BEGINTEST { Verbatim~body~collection~last~line~handling } + +\ExplSyntaxOn +\NewDocumentEnvironment { env9 } { f } + { \TYPE { \tl_to_str:n {|#1|} } } + { } +\ExplSyntaxOff + +\begin{env9} + Content +text \end{env9} + +\begin{env9} + Content +\end{env9} more test + +\ENDTEST + \END diff --git a/base/testfiles-ltcmd/ltcmd009.tlg b/base/testfiles-ltcmd/ltcmd009.tlg index ffd6650ec..7c5af9485 100644 --- a/base/testfiles-ltcmd/ltcmd009.tlg +++ b/base/testfiles-ltcmd/ltcmd009.tlg @@ -57,3 +57,10 @@ TEST 3: Verbatim body collection |...|\obeyedline Content\obeyedline &\^| |...|\obeyedline Content\obeyedline &\^| ============================================================ +============================================================ +TEST 4: Verbatim body collection last line handling +============================================================ +| Content\obeyedline text | +LaTeX cmd Warning: Characters ' more test' dropped after end of env9 environment. +| Content| +============================================================ From 801c1068451d0096053f87810ee3c896bfe11563 Mon Sep 17 00:00:00 2001 From: Joseph Wright Date: Fri, 10 Jan 2025 12:34:08 +0000 Subject: [PATCH 04/10] Allow for spaces in "\end{foo}" --- base/ltcmd.dtx | 242 ++++++++++++++++++++++-------- base/testfiles-ltcmd/ltcmd009.lvt | 35 ++++- base/testfiles-ltcmd/ltcmd009.tlg | 9 ++ 3 files changed, 223 insertions(+), 63 deletions(-) diff --git a/base/ltcmd.dtx b/base/ltcmd.dtx index e4dd4b334..71ca0de8d 100644 --- a/base/ltcmd.dtx +++ b/base/ltcmd.dtx @@ -3399,11 +3399,17 @@ % \begin{macro}{\@@_grab_f:w} % \changes{v1.3a}{2025-01-22}{New \texttt{f}-type grabbing function} % \begin{macro}{\@@_grab_f_obey_spaces:w} -% \begin{macro}{\@@_grab_f_aux:n} -% \begin{macro}{\@@_grab_f_aux:w} -% \begin{macro}{\@@_grab_f_check:w} +% \begin{macro}{\@@_grab_f_start:n} % \begin{macro}{\@@_grab_f_loop:w} -% \begin{macro}{\@@_grab_f_end:nn} +% \begin{macro}{\@@_grab_f_auxi:w} +% \begin{macro}{\@@_grab_f_auxii:w} +% \begin{macro}{\@@_grab_f_auxiii:N} +% \begin{macro}{\@@_grab_f_auxiv:} +% \begin{macro}{\@@_grab_f_auxv:} +% \begin{macro}{\@@_grab_f_auxvi:N} +% \begin{macro}{\@@_grab_f_auxvii:} +% \begin{macro}{\@@_grab_f_auxviii:} +% \begin{macro}{\@@_grab_f_end:w} % \begin{macro}{\@@_grab_f_end:n} % \begin{macro}{\@@_grab_f_end_auxi:w} % \begin{macro}{\@@_grab_f_end_auxii:w} @@ -3411,22 +3417,19 @@ % 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. We then define the end marker: this -% has to have the correct category codes, so is a token list not a string -% (\texttt{end} and the end-of-env name are catcode-11). We use this to set -% up the end-of-env test, then hand over to an auxiliary. +% and to deactivate the specials. % \begin{macrocode} \cs_new_protected:Npn \@@_grab_f:w #1 \@@_run_code: { \bool_set_false:N \l_@@_obey_spaces_bool - \@@_grab_f_aux:n {#1} + \@@_grab_f_start:n {#1} } \cs_new_protected:Npn \@@_grab_f_obey_spaces:w #1 \@@_run_code: { \bool_set_true:N \l_@@_obey_spaces_bool - \@@_grab_f_aux:n {#1} + \@@_grab_f_start:n {#1} } -\cs_new_protected:Npn \@@_grab_f_aux:n #1 +\cs_new_protected:Npn \@@_grab_f_start:n #1 { \tl_set:Nn \l_@@_signature_tl {#1} \group_begin: @@ -3435,72 +3438,181 @@ \tex_endlinechar:D = `\^^M \scan_stop: \cs_set_eq:NN \do \char_set_catcode_other:N \dospecials - \tl_set:Ne \l_@@_tmpa_tl - { - \c_backslash_str end - \c_left_brace_str \@currenvir \c_right_brace_str - } - \use:e - { - \cs_set_protected:Npn \@@_grab_f_check:w - ##1 \l_@@_tmpa_tl ##2 \l_@@_tmpa_tl ##3 - \scan_stop: - } - { - \tl_if_empty:nTF {##3} - { - \tl_put_right:Nn \l_@@_v_arg_tl - { - ##1 - \obeyedline - } - \@@_grab_f_loop:w - } - { \@@_grab_f_end:nn {##1} {##2} } - } - \@@_grab_f_aux:w + \char_set_catcode_other:n { `\^^M } + \@@_grab_f_loop:w } % \end{macrocode} -% To allow for the need to change the category code of end-of-lines, we -% put this in a small auxiliary. +% 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_f_aux:w % + \cs_new_protected:Npe \@@_grab_f_loop:w #1 ^^M % { % - \cs_set_protected:Npe \@@_grab_f_loop:w ##1 ^^M % + \exp_not:N \@@_grab_f_auxi:w #1 % + \c_backslash_str end % + \scan_stop: % + } % +\group_end: +% \end{macrocode} +% 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 + { + \cs_new_protected:Npn \exp_not:N \@@_grab_f_auxi:w + #1 \c_backslash_str end #2 \scan_stop: + } + { + \tl_put_right:Nn \l_@@_v_arg_tl {#1} + \tl_if_empty:nTF {#2} + { + \tl_put_right:Nn \l_@@_v_arg_tl { \obeyedline } + \@@_grab_f_loop:w + } + { \@@_grab_f_auxii:w #2 \scan_stop: } + } +% \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_f_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_f_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_f_auxiii:N #1 % + { % + \token_case_charcode:NnF #1 % { % - \@@_grab_f_check:w ##1 % - \l_@@_tmpa_tl % - \l_@@_tmpa_tl % - \scan_stop: % + ^^M % + { \@@_grab_f_auxiv: } % + \c_space_token % + { % + \tl_put_right:Nn \l_@@_tmpa_tl {#1} % + \@@_grab_f_auxiii:N % + } % + \c_group_begin_token % + { % + \tl_set:Nn \l_@@_tmpb_tl {#1} % + \@@_grab_f_auxvi:N % + } % + } % + { \@@_grab_f_auxv: } % + } % +\group_end: +\cs_new_protected:Npn \@@_grab_f_auxiv: + { + \tl_put_right:Ne \l_@@_v_arg_tl + { + \exp_not:V \l_@@_tmpa_tl + \exp_not:N \obeyedline + } + \@@_grab_f_loop:w + } +\cs_new_protected:Npn \@@_grab_f_auxv: + { + \tl_put_right:NV \l_@@_v_arg_tl \l_@@_tmpa_tl + \@@_grab_f_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_f_auxvi:N #1 % + { % + \exp_not:N \token_case_charcode:NnF #1 % + { % + ^^M % + { + \exp_not:N \@@_grab_f_auxvii: % + \exp_not:N \@@_grab_f_auxiv: % + }% + \c_backslash_str % + { % + \exp_not:N \@@_grab_f_auxvii: % + \exp_not:N \@@_grab_f_auxv: #1 + } % + \c_group_end_token % + { % + \tl_put_right:Nn \exp_not:N \l_@@_tmpb_tl {#1} % + \exp_not:N \@@_grab_f_auxviii: % + } % + } % + { % + \tl_put_right:Nn \exp_not:N \l_@@_tmpb_tl {#1} % + \exp_not:N \@@_grab_f_auxvi:N % } % - \char_set_catcode_other:N \^^M % - \@@_grab_f_loop:w % } % \group_end: % -\cs_new_protected:Npn \@@_grab_f_check:w { } -\cs_new_protected:Npn \@@_grab_f_loop:w { } -\cs_new_protected:Npn \@@_grab_f_end:nn #1#2 +\cs_new_protected:Npn \@@_grab_f_auxvii: + { \tl_put_right:NV \l_@@_tmpa_tl \l_@@_tmpb_tl } +\cs_new_protected:Npn \@@_grab_f_auxviii: { - \tl_put_right:Nn \l_@@_v_arg_tl {#1} - \tl_if_blank:nF {#2} - { - \msg_warning:nnee { cmd } { chars-dropped-last-line } { \exp_not:n {#2} } - { \exp_not:V \@currenvir } - } - \exp_args:NNNo \group_end: - \tl_set:Nn \l_@@_v_arg_tl { \l_@@_v_arg_tl } - \@@_add_arg:x + \str_if_eq:eeTF { \exp_not:V \l_@@_tmpb_tl } { { \@currenvir } } + { \@@_grab_f_end:w } { - \bool_if:NTF \l_@@_obey_spaces_bool - { \exp_not:V } - { \exp_args:NV \@@_grab_f_end:n } - \l_@@_v_arg_tl + \@@_grab_f_auxvii: + \@@_grab_f_auxv: } - \exp_args:NV \end \@currenvir } % \end{macrocode} +% 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_f_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_f_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_f_end:n #1 @@ -3526,6 +3638,12 @@ % \end{macro} % \end{macro} % \end{macro} +% \end{macro} +% \end{macro} +% \end{macro} +% \end{macro} +% \end{macro} +% \end{macro} % % \begin{macro}{\@@_grab_m:w} % \begin{macro}{\@@_grab_m_long:w} diff --git a/base/testfiles-ltcmd/ltcmd009.lvt b/base/testfiles-ltcmd/ltcmd009.lvt index 1ff2a8ba2..3b220d204 100644 --- a/base/testfiles-ltcmd/ltcmd009.lvt +++ b/base/testfiles-ltcmd/ltcmd009.lvt @@ -81,7 +81,6 @@ {*d__f} {\TYPE{\detokenize{|#1|#2|}}} {} - \begin{env3}% Content @@ -153,4 +152,38 @@ text \end{env9} \ENDTEST +\BEGINTEST { Verbatim~body~collection~\end~handling } + +\ExplSyntaxOn +\NewDocumentEnvironment { env10 } { f } + { \TYPE { \tl_to_str:n {|#1|} } } + { } +\ExplSyntaxOff + +\begin{env10} + Content + \end +\end{env10} + +\begin{env10} + Content + \end{foo} +\end{env10} + +\begin{env10} + Content +\end {env10} + +\begin{env10} + Content + \end{foo} + \end{foo} \end{env10} + +\begin{env10} + Content + \end{foo} + \end {foo} \end {env10} + +\ENDTEST + \END diff --git a/base/testfiles-ltcmd/ltcmd009.tlg b/base/testfiles-ltcmd/ltcmd009.tlg index 7c5af9485..53c3917ce 100644 --- a/base/testfiles-ltcmd/ltcmd009.tlg +++ b/base/testfiles-ltcmd/ltcmd009.tlg @@ -64,3 +64,12 @@ TEST 4: Verbatim body collection last line handling LaTeX cmd Warning: Characters ' more test' dropped after end of env9 environment. | Content| ============================================================ +============================================================ +TEST 5: Verbatim body collection \end handling +============================================================ +| Content\obeyedline \end| +| Content\obeyedline \end{foo}| +| Content| +| Content\obeyedline \end{foo}\obeyedline \end{foo} | +| Content\obeyedline \end{foo}\obeyedline \end {foo} | +============================================================ From a5ee67041c19dc5ceef826291824add6547d718e Mon Sep 17 00:00:00 2001 From: Joseph Wright Date: Wed, 22 Jan 2025 07:49:53 +0000 Subject: [PATCH 05/10] Warn for text on the first line for f-type env --- base/doc/usrguide.tex | 2 ++ base/ltcmd.dtx | 21 ++++++++++++++++++++- base/testfiles-ltcmd/ltcmd009.lvt | 4 ++-- base/testfiles-ltcmd/ltcmd009.tlg | 5 +++-- 4 files changed, 27 insertions(+), 5 deletions(-) diff --git a/base/doc/usrguide.tex b/base/doc/usrguide.tex index a0023b9c6..1611bb5bd 100644 --- a/base/doc/usrguide.tex +++ b/base/doc/usrguide.tex @@ -896,6 +896,8 @@ \subsection{Verbatim environments} above |\end{MyVerbatim}|) cannot have any text \emph{after} the end of the environment. Text \emph{before} the end of environment is treated normally, but note that there is no trailing \cs{obeyedline} added if there is text here. +Other than optional arguments, no text is allowed on the opening line of the +environment. Notice that for technical reasons, we recommend that an optional argument coming immediately before an \texttt{f} specification should not allow any diff --git a/base/ltcmd.dtx b/base/ltcmd.dtx index 71ca0de8d..324baea7d 100644 --- a/base/ltcmd.dtx +++ b/base/ltcmd.dtx @@ -3400,6 +3400,7 @@ % \changes{v1.3a}{2025-01-22}{New \texttt{f}-type grabbing function} % \begin{macro}{\@@_grab_f_obey_spaces:w} % \begin{macro}{\@@_grab_f_start:n} +% \begin{macro}{\@@_grab_f_first:w} % \begin{macro}{\@@_grab_f_loop:w} % \begin{macro}{\@@_grab_f_auxi:w} % \begin{macro}{\@@_grab_f_auxii:w} @@ -3439,7 +3440,7 @@ \cs_set_eq:NN \do \char_set_catcode_other:N \dospecials \char_set_catcode_other:n { `\^^M } - \@@_grab_f_loop:w + \@@_grab_f_first:w } % \end{macrocode} % Notice here and below that we cannot use |\token_to_str:N \end| as that @@ -3447,6 +3448,16 @@ % \begin{macrocode} \group_begin: \char_set_catcode_other:N \^^M % + \cs_new_protected:Npn \@@_grab_f_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_f_loop:w #1 ^^M % + } % \cs_new_protected:Npe \@@_grab_f_loop:w #1 ^^M % { % \exp_not:N \@@_grab_f_auxi:w #1 % @@ -3644,6 +3655,7 @@ % \end{macro} % \end{macro} % \end{macro} +% \end{macro} % % \begin{macro}{\@@_grab_m:w} % \begin{macro}{\@@_grab_m_long:w} @@ -5120,8 +5132,15 @@ \c_@@_ignore_def_tl } % \end{macrocode} +% \changes{v1.3a}{2025-01-22}{New message \texttt{chars-dropped-first-line}} % \changes{v1.3a}{2025-01-22}{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. } { diff --git a/base/testfiles-ltcmd/ltcmd009.lvt b/base/testfiles-ltcmd/ltcmd009.lvt index 3b220d204..8f9eee556 100644 --- a/base/testfiles-ltcmd/ltcmd009.lvt +++ b/base/testfiles-ltcmd/ltcmd009.lvt @@ -81,7 +81,7 @@ {*d__f} {\TYPE{\detokenize{|#1|#2|}}} {} -\begin{env3}% +\begin{env3} Content &\^ @@ -96,7 +96,7 @@ Content &\^ \end{env3} -\begin{env4}% +\begin{env4} Content &\^ diff --git a/base/testfiles-ltcmd/ltcmd009.tlg b/base/testfiles-ltcmd/ltcmd009.tlg index 53c3917ce..384ffa810 100644 --- a/base/testfiles-ltcmd/ltcmd009.tlg +++ b/base/testfiles-ltcmd/ltcmd009.tlg @@ -46,11 +46,12 @@ LaTeX will ignore this entire definition. ============================================================ TEST 3: Verbatim body collection ============================================================ -|-NoValue-|%\obeyedline \obeyedline Content\obeyedline &\^| +|-NoValue-|\obeyedline Content\obeyedline &\^| |...|\obeyedline Content\obeyedline &\^| |...|\obeyedline Content\obeyedline &\^| -|-NoValue-|%\obeyedline \obeyedline Content\obeyedline &\^\obeyedline | +|-NoValue-|\obeyedline \obeyedline Content\obeyedline &\^\obeyedline | |...|\obeyedline \obeyedline Content\obeyedline &\^\obeyedline | +LaTeX cmd Warning: Characters ' [...]' dropped on first line of env4 environment. |-NoValue-| [...]\obeyedline \obeyedline Content\obeyedline &\^\obeyedline | |...|\obeyedline Content\obeyedline &\^| |...|\obeyedline Content\obeyedline &\^| From 0234a0af64bfba60806eb107491732fa2fe33a04 Mon Sep 17 00:00:00 2001 From: Joseph Wright Date: Wed, 22 Jan 2025 10:30:41 +0000 Subject: [PATCH 06/10] Automate verb-protection for e.g. "o f" env. spec. --- base/doc/usrguide.tex | 7 +++ base/ltcmd.dtx | 73 +++++++++++++++++-------------- base/testfiles-ltcmd/ltcmd009.lvt | 12 ++--- 3 files changed, 54 insertions(+), 38 deletions(-) diff --git a/base/doc/usrguide.tex b/base/doc/usrguide.tex index 1611bb5bd..c015d2c99 100644 --- a/base/doc/usrguide.tex +++ b/base/doc/usrguide.tex @@ -899,6 +899,13 @@ \subsection{Verbatim environments} Other than optional arguments, no text is allowed on the opening line of the environment. +Special handling is applied to a \texttt{o}, \texttt{O}, \texttt{d} or +\texttt{D}~specification argument immediately before an +\texttt{f}~specification. This means that when the optional argument is absent, +the first character of the next line will be read properly. Issues may arise if +\emph{multiple} optional arguments are used before an \texttt{f}~specification, +and are therefore strongly discouraged. + Notice that for technical reasons, we recommend that an optional argument coming immediately before an \texttt{f} specification should not allow any spaces, achieved by adding the \texttt{!} as showing in the example. However, diff --git a/base/ltcmd.dtx b/base/ltcmd.dtx index 324baea7d..19d96e536 100644 --- a/base/ltcmd.dtx +++ b/base/ltcmd.dtx @@ -97,6 +97,10 @@ % {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-22}{New variable} +% The total number of arguments found during normalization: this is needed +% 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,10 +339,12 @@ % \end{macrocode} % \end{variable} % -% \begin{variable}{\l_@@_verb_safe_bool} -% To track if optional arguments should be collected \enquote{verbatim safe}. +% \begin{variable}{\l_@@_final_verb_bool} +% \changes{v1.3a}{2025-01-22}{New variable} +% Needed to establish if optional arguments should be collected +% \enquote{verbatim safe}. % \begin{macrocode} -\bool_new:N \l_@@_verb_safe_bool +\bool_new:N \l_@@_final_verb_bool % \end{macrocode} % \end{variable} % @@ -1052,7 +1067,6 @@ \bool_set_false:N \l_@@_some_obey_spaces_bool \bool_set_false:N \l_@@_some_long_bool \bool_set_false:N \l_@@_some_short_bool - \bool_set_false:N \l_@@_verb_safe_bool \@@_normalize_arg_spec_loop:n #1 \q_recursion_tail \q_recursion_tail \q_recursion_tail \q_recursion_stop \int_compare:nNnT \l_@@_current_arg_int > 9 @@ -1210,12 +1224,6 @@ { = { \tl_trim_spaces:n {#1} } } } } -\cs_new_protected:cpn { @@_normalize_type_*:w } #1 - { - \@@_normalize_type_aux:NnNn * {#1} - \l_@@_verb_safe_bool - { \bool_set_true:N \l_@@_verb_safe_bool } - } \cs_new_protected:Npn \@@_normalize_type_aux:NnNn #1#2#3#4 { \quark_if_recursion_tail_stop_do:nn {#2} { \@@_bad_arg_spec:wn } @@ -1498,7 +1506,6 @@ % \changes{v1.2c}{2023/12/22} % {Clarify error message when \texttt{!} prefix is applied to % non-trailing opt-arg (gh/1198)} -% \changes{v1.3a}{2025-01-22}{Extend to support \texttt{f}-type grabbing} % When adding an argument to the argument specification, set the % \texttt{some_long} or \texttt{some_short} booleans as appropriate % and clear the booleans keeping track of |+|, |!| and |=| markers. @@ -1533,10 +1540,6 @@ { ! \str_if_eq_p:nn {#1} { f } } { + } \bool_if:NT \l_@@_obey_spaces_bool { ! } - \bool_lazy_or:nnT - { \l_@@_verb_safe_bool } - { \str_if_eq_p:nn {#1} { f } } - { * } \exp_not:n {#1} } \bool_set_false:N \l_@@_long_bool @@ -1564,6 +1567,8 @@ % \subsection{Preparing the signature: general mechanism} % % \begin{macro}{\@@_prepare_signature:n} +% \changes{v1.3a}{2025-01-22}{Extend to cover \texttt{f}-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 @@ -1573,11 +1578,11 @@ % \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 \bool_set_false:N \l_@@_suppress_strip_bool - \bool_set_false:N \l_@@_verb_safe_bool \int_zero:N \l_@@_m_args_int \bool_set_false:N \l_@@_defaults_bool \tl_clear:N \l_@@_defaults_tl @@ -1585,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} } } { f } + { \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 @@ -1617,6 +1632,7 @@ % \end{macro} % \end{macro} % \end{macro} +% \end{macro} % % \subsection{Setting up a standard signature} % @@ -1692,19 +1708,6 @@ % \end{macrocode} % \end{macro} % -% \begin{macro}{\@@_add_type_*:w} -% \changes{v1.3a}{2025-01-22}{New macro} -% \begin{macrocode} -\cs_new_protected:cpn { @@_add_type_*:w } - { - \@@_flush_m_args: - \bool_set_true:N \l_@@_verb_safe_bool - \bool_set_true:N \l_@@_prefixed_bool - \@@_prepare_signature_bypass:N - } -% \end{macrocode} -% \end{macro} -% % \begin{macro}{\@@_add_type_b:w} % \changes{v1.3a}{2025-01-22}{Extend to cover \texttt{f}-type grabbing} % \begin{macro}{\@@_add_type_f:w} @@ -1916,9 +1919,15 @@ { \l_@@_suppress_strip_bool } { \str_if_eq_p:nn {#1} { D } } { _no_strip } - \bool_lazy_and:nnT - { \l_@@_verb_safe_bool } - { \str_if_eq_p:nn {#1} { D } } + \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 } diff --git a/base/testfiles-ltcmd/ltcmd009.lvt b/base/testfiles-ltcmd/ltcmd009.lvt index 8f9eee556..e29056a76 100644 --- a/base/testfiles-ltcmd/ltcmd009.lvt +++ b/base/testfiles-ltcmd/ltcmd009.lvt @@ -61,24 +61,24 @@ %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% \BEGINTEST { Verbatim~body~collection } -\NewDocumentEnvironment { env3 } { *o f } +\NewDocumentEnvironment { env3 } { o f } { \TYPE { \tl_to_str:n {|#1|#2|} } } { } -\NewDocumentEnvironment { env4 } { *!o !f } +\NewDocumentEnvironment { env4 } { !o !f } { \TYPE { \tl_to_str:n {|#1|#2|} } } { } -\NewDocumentEnvironment { env5 } { *d$$ f } +\NewDocumentEnvironment { env5 } { d$$ f } { \TYPE { \tl_to_str:n {|#1|#2|} } } { } -\NewDocumentEnvironment { env6 } { *dXX f } +\NewDocumentEnvironment { env6 } { dXX f } { \TYPE { \tl_to_str:n {|#1|#2|} } } { } -\NewDocumentEnvironment { env7 } { *d^ ^ f } +\NewDocumentEnvironment { env7 } { d^ ^ f } { \TYPE { \tl_to_str:n {|#1|#2|} } } { } \ExplSyntaxOff \NewDocumentEnvironment{env8} - {*d__f} + {d__f} {\TYPE{\detokenize{|#1|#2|}}} {} \begin{env3} From 89485c771541bf52db083b98f57ef716ccf6e8ef Mon Sep 17 00:00:00 2001 From: Joseph Wright Date: Thu, 23 Jan 2025 08:55:15 +0000 Subject: [PATCH 07/10] Re-phrase a paragraph --- base/doc/usrguide.tex | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) diff --git a/base/doc/usrguide.tex b/base/doc/usrguide.tex index c015d2c99..969c31059 100644 --- a/base/doc/usrguide.tex +++ b/base/doc/usrguide.tex @@ -43,7 +43,7 @@ \texttt{usrguide.tex} for full details.}% } -\date{2025-01-22} +\date{2025-01-23} \NewDocumentCommand\cs{m}{\texttt{\textbackslash\detokenize{#1}}} \NewDocumentCommand\marg{m}{\arg{#1}} @@ -906,10 +906,10 @@ \subsection{Verbatim environments} \emph{multiple} optional arguments are used before an \texttt{f}~specification, and are therefore strongly discouraged. -Notice that for technical reasons, we recommend that an optional argument -coming immediately before an \texttt{f} specification should not allow any -spaces, achieved by adding the \texttt{!} as showing in the example. However, -this is left as a choice for the user. +For technical reasons, we recommend that spaces are ignored when searching for +an optional argument before an \texttt{f} specification: this can be achieved +by adding the \texttt{!} modifier as showing in the example. However, this is +left as a choice for the user. \subsection{Performance} From 9220df6868515e1ac1f830687b7cf52a9b24a461 Mon Sep 17 00:00:00 2001 From: Joseph Wright Date: Thu, 23 Jan 2025 10:19:56 +0000 Subject: [PATCH 08/10] Correct usrguide text --- base/doc/usrguide.tex | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/base/doc/usrguide.tex b/base/doc/usrguide.tex index 969c31059..0cf9f0c32 100644 --- a/base/doc/usrguide.tex +++ b/base/doc/usrguide.tex @@ -906,10 +906,10 @@ \subsection{Verbatim environments} \emph{multiple} optional arguments are used before an \texttt{f}~specification, and are therefore strongly discouraged. -For technical reasons, we recommend that spaces are ignored when searching for -an optional argument before an \texttt{f} specification: this can be achieved -by adding the \texttt{!} modifier as showing in the example. However, this is -left as a choice for the user. +For technical reasons, we recommend that spaces are \emph{not} ignored when +searching for an optional argument before an \texttt{f} specification: this can +be achieved by adding the \texttt{!} modifier as showing in the example. +However, this is left as a choice for the user. \subsection{Performance} From 549b748be48039cd344c2a12a97af95d015ffef9 Mon Sep 17 00:00:00 2001 From: Joseph Wright Date: Thu, 23 Jan 2025 10:37:18 +0000 Subject: [PATCH 09/10] Switch from "f" to "c" for verb-env-collection --- base/changes.txt | 4 +- base/doc/ltnews41.tex | 8 +- base/doc/usrguide.tex | 14 +- base/ltcmd.dtx | 584 +++++++++++++++--------------- base/testfiles-ltcmd/ltcmd009.lvt | 16 +- 5 files changed, 313 insertions(+), 313 deletions(-) diff --git a/base/changes.txt b/base/changes.txt index 680d88459..a5dcc83e6 100644 --- a/base/changes.txt +++ b/base/changes.txt @@ -6,9 +6,9 @@ to completeness or accuracy and it contains some references to files that are not part of the distribution. ================================================================================ -2025-01-22 Joseph Wright +2025-01-23 Joseph Wright * ltcmd.dtx, usrguide.tex - New "f"-type argument + New "c"-type argument 2025-01-21 Frank Mittelbach diff --git a/base/doc/ltnews41.tex b/base/doc/ltnews41.tex index 42b1db4c7..5eadbe554 100644 --- a/base/doc/ltnews41.tex +++ b/base/doc/ltnews41.tex @@ -254,15 +254,15 @@ \subsection{Collecting environment bodies verbatim} specialist argument form. To date, however, it was not possible to combine these two ideas. -In this release, a new specifier~\texttt{f} is introduced, which collects the +In this release, a new specifier~\texttt{c} is introduced, which collects the body of an environment in a verbatim-like way. Like the existing \texttt{v}~specification, each separate line is marked by the special \cs{obeyedline} marker, which as standard issues a normal paragraph. Thus, this new specifier is usable both for typesetting and collecting file contents (the -\texttt{f} indicates \enquote{\texttt{filecontents}-like}). Thus, we may use +letter~\texttt{c} indicates \enquote{collect code}). Thus, we may use \begin{verbatim} \NewDocumentEnvironment - {MyVerbatim}{!O{\ttfamily} f} + {MyVerbatim}{!O{\ttfamily} c} {\begin{center}#1 #2\end{center}} {} \begin{MyVerbatim}[\ttfamily\itshape] % Some code is shown here @@ -271,7 +271,7 @@ \subsection{Collecting environment bodies verbatim} \end{verbatim} to obtain % We can't actually use ... -%\NewDocumentEnvironment{MyVerbatim}{!O{\ttfamily} f} +%\NewDocumentEnvironment{MyVerbatim}{!O{\ttfamily} c} % {\begin{center} #1 #2\end{center}} {} % ... as this would prevent LaTeX News 41 being processed with LaTeX before % 2025-06-01! diff --git a/base/doc/usrguide.tex b/base/doc/usrguide.tex index 0cf9f0c32..11616e65f 100644 --- a/base/doc/usrguide.tex +++ b/base/doc/usrguide.tex @@ -865,10 +865,10 @@ \subsection{Verbatim environments} In some cases, as well as grabbing an environment body you will want the contents to be treated verbatim. This is available using the argument -specification~\texttt{f}. Like the \texttt{b} specification, this has to be the +specification~\texttt{c}. Like the \texttt{b} specification, this has to be the last one. Thus for example \begin{verbatim} -\NewDocumentEnvironment{MyVerbatim}{!O{\ttfamily} f} +\NewDocumentEnvironment{MyVerbatim}{!O{\ttfamily} c} {\begin{center} #1 #2\end{center}} {} \begin{MyVerbatim}[\ttfamily\itshape] % Some code is shown here @@ -876,7 +876,7 @@ \subsection{Verbatim environments} \end{MyVerbatim} \end{verbatim} will typeset -\NewDocumentEnvironment{MyVerbatim}{!O{\ttfamily} f} +\NewDocumentEnvironment{MyVerbatim}{!O{\ttfamily} c} {\begin{center} #1 #2\end{center}} {} \begin{MyVerbatim}[\ttfamily\itshape] % Some code is shown here @@ -887,7 +887,7 @@ \subsection{Verbatim environments} newlines are always permitted: there is no need for a |+| here. As for the \texttt{v} specification, newlines are stored as \cs{obeyedline}. In a similar fashion to the \texttt{b}~specification, by default \emph{newlines} are trimmed -at both ends of the body. Putting the prefix |!| before \texttt{f} suppresses +at both ends of the body. Putting the prefix |!| before \texttt{c} suppresses space-trimming. Collection of the body takes place on a line-by-line basis: content is @@ -901,13 +901,13 @@ \subsection{Verbatim environments} Special handling is applied to a \texttt{o}, \texttt{O}, \texttt{d} or \texttt{D}~specification argument immediately before an -\texttt{f}~specification. This means that when the optional argument is absent, +\texttt{c}~specification. This means that when the optional argument is absent, the first character of the next line will be read properly. Issues may arise if -\emph{multiple} optional arguments are used before an \texttt{f}~specification, +\emph{multiple} optional arguments are used before an \texttt{c}~specification, and are therefore strongly discouraged. For technical reasons, we recommend that spaces are \emph{not} ignored when -searching for an optional argument before an \texttt{f} specification: this can +searching for an optional argument before an \texttt{c} specification: this can be achieved by adding the \texttt{!} modifier as showing in the example. However, this is left as a choice for the user. diff --git a/base/ltcmd.dtx b/base/ltcmd.dtx index 19d96e536..c63594808 100644 --- a/base/ltcmd.dtx +++ b/base/ltcmd.dtx @@ -35,7 +35,7 @@ % % \begin{macrocode} \def\ltcmdversion{v1.3a} -\def\ltcmddate{2025-01-22} +\def\ltcmddate{2025-01-23} % \end{macrocode} % %<*driver> @@ -137,7 +137,7 @@ % \end{variable} % % \begin{variable}{\l_@@_total_args_int} -% \changes{v1.3a}{2025-01-22}{New variable} +% \changes{v1.3a}{2025-01-23}{New variable} % The total number of arguments found during normalization: this is needed % where special action is needed for the penultimate argument. % \begin{macrocode} @@ -340,7 +340,7 @@ % \end{variable} % % \begin{variable}{\l_@@_final_verb_bool} -% \changes{v1.3a}{2025-01-22}{New variable} +% \changes{v1.3a}{2025-01-23}{New variable} % Needed to establish if optional arguments should be collected % \enquote{verbatim safe}. % \begin{macrocode} @@ -1353,20 +1353,20 @@ % \end{macro} % % \begin{macro}{\@@_normalize_type_b:w} -% \changes{v1.3a}{2025-01-22}{Extend to cover \texttt{f}-type grabbing} -% \begin{macro}{\@@_normalize_type_f:w} -% \changes{v1.3a}{2025-01-22}{New function} -% \begin{macro}{\@@_normalize_type_b_or_f:nn} -% \changes{v1.3a}{2025-01-22}{New function} +% \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_f:nn {#1} { b } } -\cs_new_protected:Npn \@@_normalize_type_f:w #1 - { \@@_normalize_type_b_or_f:nn {#1} { f } } -\cs_new_protected:Npn \@@_normalize_type_b_or_f:nn #1#2 + { \@@_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 { @@ -1537,7 +1537,7 @@ { \bool_lazy_and:nnT { \l_@@_long_bool } - { ! \str_if_eq_p:nn {#1} { f } } + { ! \str_if_eq_p:nn {#1} { c } } { + } \bool_if:NT \l_@@_obey_spaces_bool { ! } \exp_not:n {#1} @@ -1567,7 +1567,7 @@ % \subsection{Preparing the signature: general mechanism} % % \begin{macro}{\@@_prepare_signature:n} -% \changes{v1.3a}{2025-01-22}{Extend to cover \texttt{f}-type grabbing} +% \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} @@ -1599,7 +1599,7 @@ % \begin{macrocode} \cs_new_protected:Npn \@@_prepare_signature_verb_chk:n #1 { - \str_if_eq:eeTF { \tl_head:e { \tl_reverse:n {#1} } } { f } + \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 } } @@ -1709,17 +1709,17 @@ % \end{macro} % % \begin{macro}{\@@_add_type_b:w} -% \changes{v1.3a}{2025-01-22}{Extend to cover \texttt{f}-type grabbing} -% \begin{macro}{\@@_add_type_f:w} -% \changes{v1.3a}{2025-01-22}{New function} -% \begin{macro}{\@@_add_type_b_or_f:N} -% \changes{v1.3a}{2025-01-22}{New function} +% \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_f:N b } -\cs_new_protected:Npn \@@_add_type_f:w - { \@@_add_type_b_or_f:N f } -\cs_new_protected:Npn \@@_add_type_b_or_f:N #1 + { \@@_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: @@ -1904,7 +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-22}{Extend to support \texttt{f}-type grabbing} +% \changes{v1.3a}{2025-01-23}{Extend to support \texttt{c}-type grabbing} % \begin{macrocode} \cs_new_protected:Npn \@@_add_grabber:N #1 { @@ -3076,6 +3076,267 @@ % \end{macrocode} % \end{macro} % +% \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} +% \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:} +% \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} +% 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_c:w #1 \@@_run_code: + { + \bool_set_false:N \l_@@_obey_spaces_bool + \@@_grab_c_start:n {#1} + } +\cs_new_protected:Npn \@@_grab_c_obey_spaces:w #1 \@@_run_code: + { + \bool_set_true:N \l_@@_obey_spaces_bool + \@@_grab_c_start:n {#1} + } +\cs_new_protected:Npn \@@_grab_c_start:n #1 + { + \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 + } +% \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} +% 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 + { + \cs_new_protected:Npn \exp_not:N \@@_grab_c_auxi:w + #1 \c_backslash_str end #2 \scan_stop: + } + { + \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: } + } +% \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: + { + \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_c_auxv: + { + \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: + } + } +% \end{macrocode} +% 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} +% \end{macro} +% \end{macro} +% \end{macro} +% \end{macro} +% \end{macro} +% \end{macro} +% \end{macro} +% \end{macro} +% \end{macro} +% \end{macro} +% \end{macro} +% \end{macro} +% \end{macro} +% % \begin{macro} % { % \@@_grab_D:w , @@ -3088,7 +3349,7 @@ % \@@_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-22}{Auto-generate D-grabbers} +% \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. @@ -3126,9 +3387,9 @@ } % \end{macrocode} % \begin{macro}{\@@_grab_D_aux:NNnNNNN} -% \changes{v1.3a}{2025-01-222}{Extend to support \texttt{f}-type grabbing} +% \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-22}{New macro} +% \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 @@ -3405,267 +3666,6 @@ % \end{macro} % \end{macro} % -% \begin{macro}{\@@_grab_f:w} -% \changes{v1.3a}{2025-01-22}{New \texttt{f}-type grabbing function} -% \begin{macro}{\@@_grab_f_obey_spaces:w} -% \begin{macro}{\@@_grab_f_start:n} -% \begin{macro}{\@@_grab_f_first:w} -% \begin{macro}{\@@_grab_f_loop:w} -% \begin{macro}{\@@_grab_f_auxi:w} -% \begin{macro}{\@@_grab_f_auxii:w} -% \begin{macro}{\@@_grab_f_auxiii:N} -% \begin{macro}{\@@_grab_f_auxiv:} -% \begin{macro}{\@@_grab_f_auxv:} -% \begin{macro}{\@@_grab_f_auxvi:N} -% \begin{macro}{\@@_grab_f_auxvii:} -% \begin{macro}{\@@_grab_f_auxviii:} -% \begin{macro}{\@@_grab_f_end:w} -% \begin{macro}{\@@_grab_f_end:n} -% \begin{macro}{\@@_grab_f_end_auxi:w} -% \begin{macro}{\@@_grab_f_end_auxii:w} -% \begin{macro}{\@@_grab_f_end_auxiii: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_f:w #1 \@@_run_code: - { - \bool_set_false:N \l_@@_obey_spaces_bool - \@@_grab_f_start:n {#1} - } -\cs_new_protected:Npn \@@_grab_f_obey_spaces:w #1 \@@_run_code: - { - \bool_set_true:N \l_@@_obey_spaces_bool - \@@_grab_f_start:n {#1} - } -\cs_new_protected:Npn \@@_grab_f_start:n #1 - { - \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_f_first:w - } -% \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_f_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_f_loop:w #1 ^^M % - } % - \cs_new_protected:Npe \@@_grab_f_loop:w #1 ^^M % - { % - \exp_not:N \@@_grab_f_auxi:w #1 % - \c_backslash_str end % - \scan_stop: % - } % -\group_end: -% \end{macrocode} -% 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 - { - \cs_new_protected:Npn \exp_not:N \@@_grab_f_auxi:w - #1 \c_backslash_str end #2 \scan_stop: - } - { - \tl_put_right:Nn \l_@@_v_arg_tl {#1} - \tl_if_empty:nTF {#2} - { - \tl_put_right:Nn \l_@@_v_arg_tl { \obeyedline } - \@@_grab_f_loop:w - } - { \@@_grab_f_auxii:w #2 \scan_stop: } - } -% \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_f_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_f_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_f_auxiii:N #1 % - { % - \token_case_charcode:NnF #1 % - { % - ^^M % - { \@@_grab_f_auxiv: } % - \c_space_token % - { % - \tl_put_right:Nn \l_@@_tmpa_tl {#1} % - \@@_grab_f_auxiii:N % - } % - \c_group_begin_token % - { % - \tl_set:Nn \l_@@_tmpb_tl {#1} % - \@@_grab_f_auxvi:N % - } % - } % - { \@@_grab_f_auxv: } % - } % -\group_end: -\cs_new_protected:Npn \@@_grab_f_auxiv: - { - \tl_put_right:Ne \l_@@_v_arg_tl - { - \exp_not:V \l_@@_tmpa_tl - \exp_not:N \obeyedline - } - \@@_grab_f_loop:w - } -\cs_new_protected:Npn \@@_grab_f_auxv: - { - \tl_put_right:NV \l_@@_v_arg_tl \l_@@_tmpa_tl - \@@_grab_f_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_f_auxvi:N #1 % - { % - \exp_not:N \token_case_charcode:NnF #1 % - { % - ^^M % - { - \exp_not:N \@@_grab_f_auxvii: % - \exp_not:N \@@_grab_f_auxiv: % - }% - \c_backslash_str % - { % - \exp_not:N \@@_grab_f_auxvii: % - \exp_not:N \@@_grab_f_auxv: #1 - } % - \c_group_end_token % - { % - \tl_put_right:Nn \exp_not:N \l_@@_tmpb_tl {#1} % - \exp_not:N \@@_grab_f_auxviii: % - } % - } % - { % - \tl_put_right:Nn \exp_not:N \l_@@_tmpb_tl {#1} % - \exp_not:N \@@_grab_f_auxvi:N % - } % - } % -\group_end: % -\cs_new_protected:Npn \@@_grab_f_auxvii: - { \tl_put_right:NV \l_@@_tmpa_tl \l_@@_tmpb_tl } -\cs_new_protected:Npn \@@_grab_f_auxviii: - { - \str_if_eq:eeTF { \exp_not:V \l_@@_tmpb_tl } { { \@currenvir } } - { \@@_grab_f_end:w } - { - \@@_grab_f_auxvii: - \@@_grab_f_auxv: - } - } -% \end{macrocode} -% 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_f_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_f_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_f_end:n #1 - { - \@@_grab_f_end_auxi:w \q_nil #1 \q_nil - \obeyedline \obeyedline \q_nil \q_stop - } -\cs_new:Npn \@@_grab_f_end_auxi:w #1 \q_nil \obeyedline - { \@@_grab_f_end_auxii:w #1 \q_nil } -\cs_new:Npn \@@_grab_f_end_auxii:w \q_nil #1 \obeyedline \q_nil - { \@@_grab_f_end_auxiii:w #1 \q_nil } -\cs_new:Npn \@@_grab_f_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} -% \end{macro} -% \end{macro} -% \end{macro} -% \end{macro} -% \end{macro} -% \end{macro} -% \end{macro} -% \end{macro} -% \end{macro} -% \end{macro} -% \end{macro} -% \end{macro} -% \end{macro} -% % \begin{macro}{\@@_grab_m:w} % \begin{macro}{\@@_grab_m_long:w} % Collecting a single mandatory argument is quite easy. @@ -5110,7 +5110,7 @@ % \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-22}{Generalize message \texttt{arg-after-body}} +% \changes{v1.3a}{2025-01-23}{Generalize message \texttt{arg-after-body}} % \begin{macrocode} \msg_new:nnnn { cmd } { arg-after-body } { Argument~type~'#1'~must~be~last~in~#2. } @@ -5141,8 +5141,8 @@ \c_@@_ignore_def_tl } % \end{macrocode} -% \changes{v1.3a}{2025-01-22}{New message \texttt{chars-dropped-first-line}} -% \changes{v1.3a}{2025-01-22}{New message \texttt{chars-dropped-last-line}} +% \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. } diff --git a/base/testfiles-ltcmd/ltcmd009.lvt b/base/testfiles-ltcmd/ltcmd009.lvt index e29056a76..7887afb89 100644 --- a/base/testfiles-ltcmd/ltcmd009.lvt +++ b/base/testfiles-ltcmd/ltcmd009.lvt @@ -61,24 +61,24 @@ %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% \BEGINTEST { Verbatim~body~collection } -\NewDocumentEnvironment { env3 } { o f } +\NewDocumentEnvironment { env3 } { o c } { \TYPE { \tl_to_str:n {|#1|#2|} } } { } -\NewDocumentEnvironment { env4 } { !o !f } +\NewDocumentEnvironment { env4 } { !o !c } { \TYPE { \tl_to_str:n {|#1|#2|} } } { } -\NewDocumentEnvironment { env5 } { d$$ f } +\NewDocumentEnvironment { env5 } { d$$ c } { \TYPE { \tl_to_str:n {|#1|#2|} } } { } -\NewDocumentEnvironment { env6 } { dXX f } +\NewDocumentEnvironment { env6 } { dXX c } { \TYPE { \tl_to_str:n {|#1|#2|} } } { } -\NewDocumentEnvironment { env7 } { d^ ^ f } +\NewDocumentEnvironment { env7 } { d^ ^ c } { \TYPE { \tl_to_str:n {|#1|#2|} } } { } \ExplSyntaxOff \NewDocumentEnvironment{env8} - {d__f} + {d__c} {\TYPE{\detokenize{|#1|#2|}}} {} \begin{env3} @@ -137,7 +137,7 @@ \BEGINTEST { Verbatim~body~collection~last~line~handling } \ExplSyntaxOn -\NewDocumentEnvironment { env9 } { f } +\NewDocumentEnvironment { env9 } { c } { \TYPE { \tl_to_str:n {|#1|} } } { } \ExplSyntaxOff @@ -155,7 +155,7 @@ text \end{env9} \BEGINTEST { Verbatim~body~collection~\end~handling } \ExplSyntaxOn -\NewDocumentEnvironment { env10 } { f } +\NewDocumentEnvironment { env10 } { c } { \TYPE { \tl_to_str:n {|#1|} } } { } \ExplSyntaxOff From 8e1bb89bd07f16157f35ccfce1e9b4cc8e577170 Mon Sep 17 00:00:00 2001 From: Joseph Wright Date: Thu, 23 Jan 2025 16:15:58 +0000 Subject: [PATCH 10/10] Split macro markup --- base/ltcmd.dtx | 52 +++++++++++++++++++++++++------------------------- 1 file changed, 26 insertions(+), 26 deletions(-) diff --git a/base/ltcmd.dtx b/base/ltcmd.dtx index c63594808..27d25fc30 100644 --- a/base/ltcmd.dtx +++ b/base/ltcmd.dtx @@ -3082,19 +3082,6 @@ % \begin{macro}{\@@_grab_c_start:n} % \begin{macro}{\@@_grab_c_first:w} % \begin{macro}{\@@_grab_c_loop:w} -% \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:} -% \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} % 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 @@ -3146,6 +3133,19 @@ } % \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 @@ -3277,6 +3277,19 @@ } } % \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. @@ -3323,19 +3336,6 @@ % \end{macro} % \end{macro} % \end{macro} -% \end{macro} -% \end{macro} -% \end{macro} -% \end{macro} -% \end{macro} -% \end{macro} -% \end{macro} -% \end{macro} -% \end{macro} -% \end{macro} -% \end{macro} -% \end{macro} -% \end{macro} % % \begin{macro} % {