Skip to content

Commit

Permalink
Allow for spaces in "\end{foo}"
Browse files Browse the repository at this point in the history
  • Loading branch information
josephwright committed Jan 10, 2025
1 parent 2391636 commit ca67aaf
Show file tree
Hide file tree
Showing 3 changed files with 223 additions and 64 deletions.
243 changes: 180 additions & 63 deletions base/ltcmd.dtx
Original file line number Diff line number Diff line change
Expand Up @@ -3399,34 +3399,37 @@
% \begin{macro}{\@@_grab_f:w}
% \changes{v1.3a}{2025-01-08}{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}
% \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.
% 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:
Expand All @@ -3435,75 +3438,183 @@
\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}
% 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: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}
% To allow for the need to change the category code of end-of-lines, we
% put this in a small auxiliary.
% 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:Npn \@@_grab_f_aux:w %
\cs_new_protected:Npe \@@_grab_f_auxvi:N #1 %
{ %
\cs_set_protected:Npe \@@_grab_f_loop:w ##1 ^^M %
\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: %
} %
} %
{ %
\@@_grab_f_check:w ##1 %
\l_@@_tmpa_tl %
\l_@@_tmpa_tl %
\scan_stop: %
\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
{
\@@_grab_f_end_auxi:w \q_nil #1 \q_nil
Expand All @@ -3527,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}
Expand Down
35 changes: 34 additions & 1 deletion base/testfiles-ltcmd/ltcmd009.lvt
Original file line number Diff line number Diff line change
Expand Up @@ -81,7 +81,6 @@
{*d__f}
{\TYPE{\detokenize{|#1|#2|}}}
{}
\begin{env3}%
Content
Expand Down Expand Up @@ -153,4 +152,38 @@ text \end{env9}
\ENDTEST
\BEGINTEST { Verbatim~body~collection~\end~handiling }
\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
9 changes: 9 additions & 0 deletions base/testfiles-ltcmd/ltcmd009.tlg
Original file line number Diff line number Diff line change
Expand Up @@ -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 handiling
============================================================
| Content\obeyedline \end|
| Content\obeyedline \end{foo}|
| Content|
| Content\obeyedline \end{foo}\obeyedline \end{foo} |
| Content\obeyedline \end{foo}\obeyedline \end {foo} |
============================================================

0 comments on commit ca67aaf

Please sign in to comment.