Phelype Oleinik and egreg provide brace-preserving variants of \MacroOne ... \MacroTwo.
With these variants sequences of explicit space-tokens, if present, are removed at the left and at the right of the set of tokens that forms the \MacroTwo-delimited argument. If the resulting set of tokens can be considered a set of tokens surrounded by an outermost pair of matching curly braces, that pair of matching curly braces will be left in place and sequences of explicit space tokens inside the curly braces will be left in place, too.
Thus a surrounding pair of matching curly braces can be used for preventing removal of spaces.
But this surrounding pair of matching curly braces for preventing removal of spaces, if present, will be left in place.
I can think of situations where using a surrounding pair of matching curly braces for preventing removal of spaces with stuff inside it is desirable, but where you prefer the curly braces that serve the purpose of preventing removal of spaces to be stripped off because the braces have fulfilled their purpose and are not needed any longer when space-removal is done and hereby removal of spaces inside the braces was prevented.
The following code provides a variant of \MacroA...\MAcroB where space-tokens at the left and at the right of the \MacroB-delimited argument, if present, and -afterwards, if present- the outermost pair of matching curly braces surrounding all the other tokens of the argument are removed but spaces surrounding things inside the outermost pair of matching surrounding curly braces, if present, are preserved.
This way one level of curly braces "surrounding everything but leading and trailing spaces" of the argument in any case is taken for a "space-token-removal preventer" which in any case is stripped off in the process.
Due to \romannumeral/\exp:w-expansion the result can be obtained by triggering two expansion steps on \MacroOne.
\ExplSyntaxOn
\cs_new:Npn \MacroOne
{
\exp:w \__mymodule_start_collecting:w \prg_do_nothing:
}
\cs_new:Npn \__mymodule_start_collecting:w #1 \MacroTwo
{
\exp_args:Ne \__mymodule_RemoveSurroundungBraces:n { \tl_trim_spaces:o { #1 } }
}
\cs_new:Nn\__mymodule_RemoveSurroundungBraces:n
{
\tl_if_blank:nTF { #1 }
{ \exp_end: #1 }
{
\exp_args:No \tl_if_empty:nTF { \use_none:n #1 }
{ \use:nn {\exp_end:} #1 }
{ \exp_end:#1 }
}
}
%----------------------------------------------------------------------------------
% \InsertSpacesAndCallMacroOne{<Argument>} defines the macro \test from the result
% of triggering two expansion-steps on \MacroOne<Argument>\MacroTwo and shows the
% meaning of \test on the console. Instances of #1 within <Argument> are replaced
% by explicit tokens before expanding \MacroOne.
%
\cs_new:Npn \InsertSpacesAndCallMacroOne #1#2
{
\cs_gset:Npn \InsertSpacesAndCallMacroOne ##1
{
\cs_set:Npn \test ####1
{
\exp_args:NNf \cs_set:Npn \test {
\exp_after:wN \exp_after:wN \exp_after:wN |
\MacroOne##1\MacroTwo|~is~the~result~of~|#1##1#2|
}
\tex_show:D \test
}
\test{~}
}
}
\exp_args:Noo \InsertSpacesAndCallMacroOne{\token_to_str:N \MacroOne}{\token_to_str:N \MacroTwo}
\ExplSyntaxOff
% #1 within the argument of \InsertSpacesAndCallMacroOne denotes space token
\InsertSpacesAndCallMacroOne{#1#1#1{#1Te#1xt#1}#1#1#1}%
\InsertSpacesAndCallMacroOne{{#1Te#1xt#1}#1#1#1}%
\InsertSpacesAndCallMacroOne{#1#1#1{#1Te#1xt#1}}%
\InsertSpacesAndCallMacroOne{#1#1#1#1Te#1xt#1#1#1#1}%
\InsertSpacesAndCallMacroOne{#1#1#1{#1Te}#1xt#1#1#1#1}%
\InsertSpacesAndCallMacroOne{#1#1#1{#1{#1Te}#1xt#1}#1#1#1}%
% For this minimal example, no document environment was needed and
% therefore not loaded. So now we end the LaTeX run with the
% "sledgehammer method", i.e., with the command \stop:
\stop
Excerpt from console output:
| Te xt | is the result of |\MacroOne { Te xt } \MacroTwo|.
| Te xt | is the result of |\MacroOne{ Te xt } \MacroTwo|.
| Te xt | is the result of |\MacroOne { Te xt }\MacroTwo|.
|Te xt| is the result of |\MacroOne Te xt \MacroTwo|.
|{ Te} xt| is the result of |\MacroOne { Te} xt \MacroTwo|.
| { Te} xt | is the result of |\MacroOne { { Te} xt } \MacroTwo|.
\mymodule_stop_collecting:(I suspect you mixed up names and also forgot to obey your own recommendation about the signature). – egreg Apr 04 '22 at 20:52\prg_do_nothing:ininterface3.pdf. What is the difference with, say,\use_none:n? – Jinwen Apr 04 '22 at 20:56\prg_do_nothing:during expansion causes TeX to remove that token from the token-stream. || Encountering the token\use_none:nduring expansion causes TeX to remove that token and a subsequent undelimited (n-type-)argument from the token-stream. ;-) – Ulrich Diez Apr 05 '22 at 12:16\MacroTwo, a pair of matching curly braces surrounding the entire arg. gets stripped off if present. A trick for preventing the removal of such a pair of braces if present is prepending a token whose o-expansion yields emptiness before having TeX grab the delimited arg. This way the case of the entire delimited arg. being surrounded by curly braces that might get stripped off is eliminated while the token prepended can easily by removed by applying o-expansion. – Ulrich Diez Apr 05 '22 at 12:17\MacroOne{ hello world }\MacroTwo, otherwise they will behave exactly the same. This is useful, for example, if you want to trim spaces by default, but still want to allow the user to say “no, I do want spaces” by writing braces around the argument – Phelype Oleinik Apr 05 '22 at 12:24\unexpandedmeans that, for example,\edef\x{ \tl_trim_spaces:n { \document } }doesn't blow up on trying to expand\document(after that,\show\xwould be\document). In other words:\tl_trim_spaces:nis expanded, but its result is not. I'll delete this comment afterwards to remain on-topic here :) – Phelype Oleinik Apr 06 '22 at 21:31\tl_trim_spaces:nto not use\unexpanded. With\unexpanded, the spaces are trimmed, but nothing in the argument is expanded. Without\unexpanded, spaces are trimmed and macros in the argument of\tl_trim_spaces:nare expanded further (and that leads to an error if the macro does not work expandably, like\document). https://pastebin.com/raw/3Vz13ZVY – Phelype Oleinik Apr 06 '22 at 21:45\stopmakes it rather look like plain TeX and I always wonder how should one compile that. – Jinwen Apr 06 '22 at 21:50\stopis a LaTeX command to end the run (useful for test documents that produce no output). You run it as you would run any LaTeX document. I prefer runningpdflatex <name-of-the-file>.texon the command line, but if you use an IDE, that should work too. If you use the command line, you'll see output in the terminal, otherwise you can look at the generated.logfile – Phelype Oleinik Apr 06 '22 at 21:57\edef,\message,\write,\expandedand with expansion driven by things like expl3's e- or x-expansion usually expandable tokens get expanded until only non-expandable tokens remain. In such contexts expanding\unexpanded{<set of tokens>}yields that the token\unexpandedand the curly braces surrounding<set of tokens>vanish while all the tokens belonging to<set of tokens>remain in place untouched/will not be expanded further during the expansion-context in question. ... – Ulrich Diez Apr 07 '22 at 12:02\tl_trim_spaces:n { #1 }after removing spaces at the left and at the right wraps the result in\unexpanded{...}to ensure that in such full-expansion-contexts none of the tokens forming the result of removing spaces gets expanded further.\exp_not:nis just the expl3-name of\unexpanded. Thus, if you wish to have the result of\tl_trim_spaces:n { #1 }without needing to care about the amount of expansion-steps that need to be triggered until the result is there, you can wrap\tl_trim_spaces:n { #1 }into an e- or x-type argument. – Ulrich Diez Apr 07 '22 at 12:10