如何使用 \exp_not:N 和 \exp_after:wN 来防止 expl3 列表中 \input 的扩展?

如何使用 \exp_not:N 和 \exp_after:wN 来防止 expl3 列表中 \input 的扩展?

我一直在致力于实现一个环境(永不嵌套)和一个命令,用于保存文档的不同部分,并在稍后使用列表expl3和在其中使用它们xparse。到目前为止,一切顺利,运行正常,但是,在分析文件时,.fls我注意到有些不对劲。当编写辅助文件然后从列表中调用它们时,这会在内存中加载两次。我拥有的代码(MWE)是这样的:

% arara: pdflatex : {action: nonstopmode, options: "-recorder"}
\documentclass{article}
\usepackage{environ,xparse,fvextra,xcolor}%

\ExplSyntaxOn

\cs_new:Npn \elementin #1
 {
    \seq_count:c {l_savecontent_content_#1_seq}%
 }

\cs_new:Npn \clearlist #1
 {
    \seq_clear_new:c {l_savecontent_content_#1_seq}%
 }

\NewDocumentCommand{\addcontent}{m +m}
 {
    \savecontent_add_content:nn { #1 } { #2 }
 }

\DeclareExpandableDocumentCommand{\usecontent}{O{1}m}
 {
    \savecontent_use_content:nn { #1 } { #2 }
 }

\cs_new_protected:Npn \savecontent_add_content:nn #1 #2
 {
 \seq_if_exist:cF { l_savecontent_content_#1_seq }
    { \seq_new:c { l_savecontent_content_#1_seq } }
 \__savecontent_add_content:nn { #1 } { #2 }
 }

\cs_new_protected:Npn \__savecontent_add_content:nn #1 #2
 {
    \tl_map_inline:nn { #2 }
  {
    \seq_gput_right:cn { l_savecontent_content_#1_seq } { ##1 }
  }
 }

\cs_new:Npn \savecontent_use_content:nn #1 #2
 {
    \seq_item:cn { l_savecontent_content_#2_seq } { #1 }
 }

\keys_define:nn { scontent }
 {
    save-cmd .tl_set:N   = \l_scontent_cmd_save_tl,%
    show-cmd .bool_set:N = \l_scontent_cmd_show_tl,% 
    save-env .tl_set:N   = \l_scontent_env_save_tl,%
    show-env .bool_set:N = \l_scontent_env_show_tl,%
    show-inf .bool_set:N = \l_scontent_inf_show_tl,%
    name-tmp .tl_set:N   = \l_scontent_tmp_name_tl,% 
    temp-ext .tl_set:N   = \l_scontent_ext_temp_tl,%
    body-env .bool_set:N = \l_scontent_env_body_tl,%
    verb     .meta:n     = { body-env = false },%
    show-all .meta:n     = { show-env = true , show-cmd = true },%
 }

\keys_set:nn { scontent }
 {
    save-cmd = content,%
    show-cmd = false,%
    save-env = content,%
    show-env = false,%
    name-tmp = \jobname,%
    temp-ext = tsc,%
    show-inf = false,%
    body-env = true,%
 }

\NewDocumentCommand{\Setscontent}{ +m }
 {
    \keys_set:nn { scontent } {#1}
 }

\newrobustcmd{\envtolist}[1]{\addcontent{ \l_scontent_env_save_tl }{{#1}}}

\newcounter{outNr}

\tl_new:N \filetolist

\NewEnviron{SAVEcontent}[1]{}

\NewDocumentEnvironment{scontent}{ !o }
 { 
   \group_begin:
   \IfNoValueF {#1} { \keys_set:nn { scontent } {#1} }
   \IfBooleanTF { \l_scontent_env_body_tl } { \SAVEcontent{#1} } 
    { \stepcounter{outNr} \VerbatimOut{\l_scontent_tmp_name_tl-\theoutNr.\l_scontent_ext_temp_tl}}
 }{
   \IfBooleanTF { \l_scontent_env_body_tl }
    {\endSAVEcontent\expandafter\envtolist\expandafter{\BODY}}
    {\endVerbatimOut
    \tl_put_right:Nx  \filetolist { {\envtolist { \exp_not:N \input { 
    \exp_after:wN \l_scontent_tmp_name_tl-\theoutNr.\l_scontent_ext_temp_tl } } } }
    \filetolist
    }
  \IfBooleanT { \l_scontent_env_show_tl } { \usecontent[-1]{ \l_scontent_env_save_tl} }
  \IfBooleanT { \l_scontent_inf_show_tl } 
    {\marginpar{\scriptsize\ttfamily saved ~ in ~ \l_scontent_env_save_tl, ~ index ~ \elementin{\l_scontent_env_save_tl}}}
  \group_end:
 }

\NewDocumentCommand{\Scontent}{!o +m}
 {
  \group_begin:
  \IfNoValueF {#1} { \keys_set:nn { scontent } {#1} }
  \addcontent{ \l_scontent_cmd_save_tl }{{#2}} % pass direct to list
  \IfBooleanT { \l_scontent_cmd_show_tl } { \usecontent[-1]{ \l_scontent_cmd_save_tl} }
  \IfBooleanT { \l_scontent_inf_show_tl } 
    {\marginpar{\scriptsize\ttfamily saved ~ in ~ \l_scontent_cmd_save_tl, ~ index ~ \elementin{\l_scontent_cmd_save_tl}}}
  \group_end:
 }
\ExplSyntaxOff
\setlength{\parindent}{0pt}
\pagestyle{empty}
\begin{document}
\Setscontent{save-env=test-env, save-cmd=test-cmd}
\section{Test scontent env OK}
The following blocks of text will be stored directly in list or in external files and
then referenced using lists, not shown directly.\par

\begin{scontent}[show-inf]
\textcolor{red}{First Text}....use environ
\end{scontent}

\begin{scontent}[verb]
\textcolor{green}{Second Text}...save in \verb+\jobname-1.tsc+
\end{scontent}

\begin{scontent}[verb]
\textcolor{blue}{Third Text}...save in \verb+\jobname-2.tsc+
\end{scontent}

Now we can see that the number of items saved in the list is \elementin{test-env} and 
we can show them in reverse order whit \verb+\usecontent[...]{test-env}+:\par

\usecontent[3]{test-env}\par\vspace{0.5cm}
\usecontent[2]{test-env}\par\vspace{0.5cm}
\usecontent[1]{test-env}\par\vspace{0.5cm}

\section{Test Scontent comand OK}

\Scontent[show-inf]{using a \textcolor{orange}{\texttt{\textbackslash Scontent}}\ command version}
No \verb+\verb+ supported by environ :( [no problem, use env for this] :)\par
\usecontent{test-cmd}
\end{document}

该文件的输出.fls是:

$ cat forum.fls |  grep \.tsc
OUTPUT forum-1.tsc
OUTPUT forum-2.tsc
INPUT forum-2.tsc
INPUT forum-2.tsc
INPUT forum-1.tsc
INPUT forum-1.tsc

并应出去:

OUTPUT forum-1.tsc
OUTPUT forum-2.tsc
INPUT forum-1.tsc
INPUT forum-2.tsc

也就是说,执行时应该\usecontent只显示,我认为问题出在或 的行上,保存时会扩展。下图显示了它的工作原理:INPUT.fls\filetolist\addtolist\input

由 pdflatex 生成的 pdf 图像(TexLive 2018 以上版本)

我已阅读文档,但其水平远高于我的水平,我仍无法找到问题的解决方案。Saludos

PS:欢迎评论以改进代码

答案1

您在 fls 文件(来自)选项中观察到的现象-recorder可能与您的代码无关。它是 LaTeX在表单\input中使用时产生的产物。\input{file}

考虑一下

\documentclass{article}
\usepackage{filecontents}
\begin{filecontents}{\jobname-test}
This is a test file
\end{filecontents}
\begin{filecontents}{\jobname-test2}
This is another test file
\end{filecontents}

\begin{document}
\input{\jobname-test}

\input \jobname-test2
\end{document}

并用 编译它pdflatex -recorder。这将创建一个 fls 文件,由于 filecontents 的原因,将有对测试文件的多个引用,但提取相关行我得到了这个

....
INPUT filename.aux
INPUT filename.aux
OUTPUT filename.aux
INPUT filename-test.tex
INPUT filename-test.tex
INPUT filename-test2.tex
OUTPUT filename.pdf
....

如您所见,\input filename表单仅创建一个条目,但\input{filename}创建了两个。

$ latexdef input

\input:
macro:->\@ifnextchar \bgroup \@iinput \@@input 


$ latexdef @iinput

\@iinput:
macro:#1->\InputIfFileExists {#1}{}{\filename@parse {#1}\edef \reserved@a {\noexpand \@missingfileerror {\filename@area \filename@base }{\ifx \filename@ext \relax tex\else \filename@ext \fi }}\reserved@a }

\InputIfFileExits打开文件进行两次读取,第一次将检查是否存在。

另一方面\@@input是 TeX 的原语,它当然只打开一次。

相关内容