![在 NewDocumentEnvironment 中插入逐字内容](https://linux22.com/image/425281/%E5%9C%A8%20NewDocumentEnvironment%20%E4%B8%AD%E6%8F%92%E5%85%A5%E9%80%90%E5%AD%97%E5%86%85%E5%AE%B9.png)
我在一个环境中工作,用于设置具有零个或多个相关提示的练习。如果我尝试在练习主体中插入逐字内容,则 Latex 解释器会挂起而不指定错误。此处显示了一个重现此问题的最小示例:
\documentclass{article}
\usepackage{listings}
\ExplSyntaxOn
% Create the exercise environment
\NewDocumentEnvironment{ex}{O{}+b}{%
\par\noindent
#2}{%
% End exercise
}
\ExplSyntaxOff
\begin{document}
\section{First section}
\begin{ex}
Create a new virtual working environment for python
% When the lstlisting environment below is uncommented
% latex hangs with this information in the console:
% Package Listings Warning: Text dropped after begin of listing on input line 24.
%
%
% (/home/henrik/.TinyTeX/texmf-dist/tex/latex/base/omscmr.fd))
% *
%\begin{lstlisting}
%pipenv install opencv-python
%\end{lstlisting}
\end{ex}
\end{document}
正如我解释这个答案时所说 https://tex.stackexchange.com/a/489459/1366 大卫·卡莱尔 (David Carlisle) 或许无法实现我的愿望。
我希望在最终版本中使用的语法如下
\begin{ex}
Description of the exercise.
Run the following command
%\begin{verbatim}
%print("Hello world")
%\end{verbatim}
\begin{hint}
Run the example from the command line with the python command
\end{hint}
\begin{hint}
The solution is 42.
\end{hint}
\end{ex}
解析此内容时,应包含一个标题,其中显示练习的编号并包含指向所包含提示(在本例中为两个)的链接,这些提示稍后插入到文档中。为此,我必须解析 ex 环境的内容/主体。输出应如下所示,生成此输出的代码包含在问题的末尾。
\documentclass{article}
\usepackage{amsmath}
\usepackage[colorlinks, linkcolor=blue, citecolor=blue, urlcolor=blue]{hyperref}
\newcounter{ex}
\numberwithin{ex}{section}
\newcounter{hint}
\numberwithin{hint}{ex}
\newcounter{solution}
\numberwithin{solution}{ex}
\makeatletter
\newcommand{\linkdest}[1]{\raisebox{1.7\baselineskip}[0pt][0pt]{\hypertarget{#1}{}}}
\makeatother
\ExplSyntaxOn
% Define variables for storing the number of hints
% and solutions given in the exercise.
\int_new:N \l_hintenv_int
\int_new:N \l_solenv_int
% Open files for storing hints and solutions.
\iow_new:N \g_hintfile_iow
\iow_new:N \g_solutionfile_iow
\iow_open:Nn \g_hintfile_iow {hintfile.tex}
\iow_open:Nn \g_solutionfile_iow {solutionfile.tex}
% Define strings to use in macros.
\tl_new:N \g_text_solution_tl
\tl_set:Nn \g_text_solution_tl { ~Solution:~ }
\tl_new:N \g_text_solution_head_tl
\tl_set:Nn \g_text_solution_head_tl { Solutino }
\tl_new:N \g_text_hint_tl
\tl_set:Nn \g_text_hint_tl { ~Hint:~ }
\tl_new:N \g_text_exercise_tl
\tl_set:Nn \g_text_exercise_tl { Exercise~ }
\tl_new:N \g_back_to_exercise_tl
\tl_set:Nn \g_back_to_exercise_tl { Back~to~exercise~ }
% Create the exercise environment
\NewDocumentEnvironment{ex}{O{}+b}{%
% Start exercise
\bigbreak
\refstepcounter{ex}
\label{exercise\theex}
\noindent
\textbf{\g_text_exercise_tl\theex{}:~#1}
\hfill
% Run a regular expression on the body of the
% exercise to count the number of hints present
% and store that number in a variable.
\regex_count:nnN {\c{begin}\{hint\}} {#2} \l_hintenv_int
\regex_count:nnN {\c{begin}\{sol\}} {#2} \l_solenv_int
% If at least one hint is provided start a list with
% links to the inserted hints.
\int_compare:nTF { \l_hintenv_int > 0 } { \g_text_hint_tl } { }
% For all integers in the range from one to
% the number of inserted hints do.
\int_step_variable:nNn {\l_hintenv_int} \l_iterator_tl{
\int_compare:nTF { \l_iterator_tl > 1 } { ,~ } { }
\hyperlink{hint\theex.\l_iterator_tl}{\l_iterator_tl}
}
% If at least one solution is provided start a list with
% links to the inserted solutions.
\int_compare:nTF { \l_solenv_int > 0 } { \g_text_solution_tl } { }
% For all integers in the range from one to
% the number of inserted solutions do.
\int_step_variable:nNn {\l_solenv_int} \l_iterator_tl{
\int_compare:nTF { \l_iterator_tl > 1 } { ,~ } { }
\hyperlink{solution\theex.\l_iterator_tl}{\l_iterator_tl}
}
\par\noindent
#2}{%
% End exercise
}
\NewDocumentEnvironment{hint}{O{}+b}{%
% hint start
\refstepcounter{hint}
\tl_set:Nx \l_temp_tl { hint\thehint }
\iow_now:Nx \g_hintfile_iow { \par\noindent}
\iow_now:Nx \g_hintfile_iow { \exp_not:N \textbf{Hint~\arabic{hint}~to~exercise~\theex}}
\iow_now:Nx \g_hintfile_iow { \hfill \g_back_to_exercise_tl }
\iow_now:Nx \g_hintfile_iow { \exp_not:N \ref{exercise\theex } }
\iow_now:Nx \g_hintfile_iow { \par\noindent}
\iow_now:Nx \g_hintfile_iow { \exp_not:N \linkdest{ \l_temp_tl } }
\iow_now:Nx \g_hintfile_iow { \exp_not:N \vspace{-0.4cm}\par\noindent}
\iow_now:Nn \g_hintfile_iow { #2 }
\iow_now:Nn \g_hintfile_iow { \bigskip}
\iow_now:Nn \g_hintfile_iow { \filbreak}
}{
% hint end
}
\NewDocumentEnvironment{sol}{O{}+b}{
% hint start
\refstepcounter{solution}
\tl_set:Nx \l_temp_tl { solution\thesolution }
\iow_now:Nx \g_solutionfile_iow { \par\noindent }
\iow_now:Nx \g_solutionfile_iow { \exp_not:N \textbf{Solution~\arabic{solution}~to~exercise~\theex}}
\iow_now:Nx \g_solutionfile_iow { \hfill \g_back_to_exercise_tl }
\iow_now:Nx \g_solutionfile_iow { \exp_not:N \ref{exercise\theex } }
\iow_now:Nx \g_solutionfile_iow { \par\noindent}
\iow_now:Nx \g_solutionfile_iow { \exp_not:N \linkdest{ \l_temp_tl } }
\iow_now:Nx \g_solutionfile_iow { \exp_not:N \vspace{-0.4cm}\par\noindent}
\iow_now:Nn \g_solutionfile_iow { #2 }
\iow_now:Nn \g_solutionfile_iow { \bigskip}
\iow_now:Nn \g_solutionfile_iow { \filbreak}
}{
% hint end
}
% Define command for closing the two files used
% for storing hints and solutions.
\NewDocumentCommand{\closehintandsolutionfile}{}{
\iow_close:N \g_hintfile_iow
\iow_close:N \g_solutionfile_iow
}
\ExplSyntaxOff
\begin{document}
\section{Exercises}
\begin{ex}
Description of the exercise.
Run the following command
%\begin{verbatim}
%print("Hello world")
%\end{verbatim}
\begin{hint}
Run the example from the command line with the python command
\end{hint}
\begin{hint}
The solution is 42.
\end{hint}
\end{ex}
\closehintandsolutionfile
\section{Hints}
\input{hintfile.tex}
\end{document}
答案1
如果其他方法都失败了,您可以在进入环境之前将逐字记录放入并保存到一个盒子中ex
。
\documentclass{article}
\usepackage{amsmath}
\usepackage[colorlinks, linkcolor=blue, citecolor=blue, urlcolor=blue]{hyperref}
\newcounter{ex}
\numberwithin{ex}{section}
\newcounter{hint}
\numberwithin{hint}{ex}
\newcounter{solution}
\numberwithin{solution}{ex}
\makeatletter
\newcommand{\linkdest}[1]{\raisebox{1.7\baselineskip}[0pt][0pt]{\hypertarget{#1}{}}}
\makeatother
\ExplSyntaxOn
% Define variables for storing the number of hints
% and solutions given in the exercise.
\int_new:N \l_hintenv_int
\int_new:N \l_solenv_int
% Open files for storing hints and solutions.
\iow_new:N \g_hintfile_iow
\iow_new:N \g_solutionfile_iow
\iow_open:Nn \g_hintfile_iow {hintfile.tex}
\iow_open:Nn \g_solutionfile_iow {solutionfile.tex}
% Define strings to use in macros.
\tl_new:N \g_text_solution_tl
\tl_set:Nn \g_text_solution_tl { ~Solution:~ }
\tl_new:N \g_text_solution_head_tl
\tl_set:Nn \g_text_solution_head_tl { Solutino }
\tl_new:N \g_text_hint_tl
\tl_set:Nn \g_text_hint_tl { ~Hint:~ }
\tl_new:N \g_text_exercise_tl
\tl_set:Nn \g_text_exercise_tl { Exercise~ }
\tl_new:N \g_back_to_exercise_tl
\tl_set:Nn \g_back_to_exercise_tl { Back~to~exercise~ }
% Create the exercise environment
\NewDocumentEnvironment{ex}{O{}+b}{%
% Start exercise
\bigbreak
\refstepcounter{ex}
\label{exercise\theex}
\noindent
\textbf{\g_text_exercise_tl\theex{}:~#1}
\hfill
% Run a regular expression on the body of the
% exercise to count the number of hints present
% and store that number in a variable.
\regex_count:nnN {\c{begin}\{hint\}} {#2} \l_hintenv_int
\regex_count:nnN {\c{begin}\{sol\}} {#2} \l_solenv_int
% If at least one hint is provided start a list with
% links to the inserted hints.
\int_compare:nTF { \l_hintenv_int > 0 } { \g_text_hint_tl } { }
% For all integers in the range from one to
% the number of inserted hints do.
\int_step_variable:nNn {\l_hintenv_int} \l_iterator_tl{
\int_compare:nTF { \l_iterator_tl > 1 } { ,~ } { }
\hyperlink{hint\theex.\l_iterator_tl}{\l_iterator_tl}
}
% If at least one solution is provided start a list with
% links to the inserted solutions.
\int_compare:nTF { \l_solenv_int > 0 } { \g_text_solution_tl } { }
% For all integers in the range from one to
% the number of inserted solutions do.
\int_step_variable:nNn {\l_solenv_int} \l_iterator_tl{
\int_compare:nTF { \l_iterator_tl > 1 } { ,~ } { }
\hyperlink{solution\theex.\l_iterator_tl}{\l_iterator_tl}
}
\par\noindent
#2}{%
% End exercise
}
\NewDocumentEnvironment{hint}{O{}+b}{%
% hint start
\refstepcounter{hint}
\tl_set:Nx \l_temp_tl { hint\thehint }
\iow_now:Nx \g_hintfile_iow { \par\noindent}
\iow_now:Nx \g_hintfile_iow { \exp_not:N \textbf{Hint~\arabic{hint}~to~exercise~\theex}}
\iow_now:Nx \g_hintfile_iow { \hfill \g_back_to_exercise_tl }
\iow_now:Nx \g_hintfile_iow { \exp_not:N \ref{exercise\theex } }
\iow_now:Nx \g_hintfile_iow { \par\noindent}
\iow_now:Nx \g_hintfile_iow { \exp_not:N \linkdest{ \l_temp_tl } }
\iow_now:Nx \g_hintfile_iow { \exp_not:N \vspace{-0.4cm}\par\noindent}
\iow_now:Nn \g_hintfile_iow { #2 }
\iow_now:Nn \g_hintfile_iow { \bigskip}
\iow_now:Nn \g_hintfile_iow { \filbreak}
}{
% hint end
}
\NewDocumentEnvironment{sol}{O{}+b}{
% hint start
\refstepcounter{solution}
\tl_set:Nx \l_temp_tl { solution\thesolution }
\iow_now:Nx \g_solutionfile_iow { \par\noindent }
\iow_now:Nx \g_solutionfile_iow { \exp_not:N \textbf{Solution~\arabic{solution}~to~exercise~\theex}}
\iow_now:Nx \g_solutionfile_iow { \hfill \g_back_to_exercise_tl }
\iow_now:Nx \g_solutionfile_iow { \exp_not:N \ref{exercise\theex } }
\iow_now:Nx \g_solutionfile_iow { \par\noindent}
\iow_now:Nx \g_solutionfile_iow { \exp_not:N \linkdest{ \l_temp_tl } }
\iow_now:Nx \g_solutionfile_iow { \exp_not:N \vspace{-0.4cm}\par\noindent}
\iow_now:Nn \g_solutionfile_iow { #2 }
\iow_now:Nn \g_solutionfile_iow { \bigskip}
\iow_now:Nn \g_solutionfile_iow { \filbreak}
}{
% hint end
}
% Define command for closing the two files used
% for storing hints and solutions.
\NewDocumentCommand{\closehintandsolutionfile}{}{
\iow_close:N \g_hintfile_iow
\iow_close:N \g_solutionfile_iow
}
\ExplSyntaxOff
\usepackage{verbatimbox}
\begin{document}
\section{Exercises}
\begin{myverbbox}{\hw}
print("Hello world")
Verbatim &^%$&\content
\end{myverbbox}
\begin{ex}
Description of the exercise.
Run the following command
\smallskip\noindent\hw
\begin{hint}
Run the example from the command line with the python command
\end{hint}
\begin{hint}
The solution is 42.
\end{hint}
\end{ex}
\closehintandsolutionfile
\section{Hints}
\input{hintfile.tex}
\end{document}
答案2
如果有LuaTeX,您可以在Lua端存储和处理逐字内容,这有助于避免这个问题。
\documentclass{article}
\usepackage{amsmath}
\usepackage{luacode}
\usepackage{expl3, xparse}
\usepackage[colorlinks, linkcolor=blue, citecolor=blue, urlcolor=blue]{hyperref}
\newcounter{ex}
\numberwithin{ex}{section}
\newcounter{hint}
\numberwithin{hint}{ex}
\newcounter{solution}
\numberwithin{solution}{ex}
\makeatletter
\newcommand{\linkdest}[1]{\raisebox{1.7\baselineskip}[0pt][0pt]{\hypertarget{#1}{}}}
\makeatother
\begin{luacode*}
verb_table = {}
function store_lines (str)
texio.write_nl("line:"..str)
if string.find (str , [[\end{ex}]] ) then
luatexbase.remove_from_callback (
"process_input_buffer" , "store_lines")
return [[\end{ex}]]
else
if str[1] ~= "%" then
table.insert(verb_table, str)
end
end
return ""
end
function register_verbatim()
verb_table = {}
luatexbase.add_to_callback(
"process_input_buffer" , store_lines , "store_lines")
end
\end{luacode*}
\ExplSyntaxOn
\newcommand{\CurVerbatim}{}
\newcommand{\BeginEx}{
\directlua{
register_verbatim()
}
}
% Define variables for storing the number of hints
% and solutions given in the exercise.
\int_new:N \l_hintenv_int
\int_new:N \l_solenv_int
% Open files for storing hints and solutions.
\iow_new:N \g_hintfile_iow
\iow_new:N \g_solutionfile_iow
\iow_open:Nn \g_hintfile_iow {hintfile.tex}
\iow_open:Nn \g_solutionfile_iow {solutionfile.tex}
% Define strings to use in macros.
\tl_new:N \g_text_solution_tl
\tl_set:Nn \g_text_solution_tl { ~Solution:~ }
\tl_new:N \g_text_solution_head_tl
\tl_set:Nn \g_text_solution_head_tl { Solutino }
\tl_new:N \g_text_hint_tl
\tl_set:Nn \g_text_hint_tl { ~Hint:~ }
\tl_new:N \g_text_exercise_tl
\tl_set:Nn \g_text_exercise_tl { Exercise~ }
\tl_new:N \g_back_to_exercise_tl
\tl_set:Nn \g_back_to_exercise_tl { Back~to~exercise~ }
\cs_generate_variant:Nn \regex_count:nnN {nVN}
% Create the exercise environment
\NewDocumentEnvironment{ex}{O{}}{%
% Begin excercise
% capture verbatim on Lua side
\BeginEx
}{%
% End excercise
% retreive the content from lua side
% save it in \CurVerbatim
\directlua{
token.set_macro("CurVerbatim", table.concat(verb_table, "~"))
}
\bigbreak
\refstepcounter{ex}
\label{exercise\theex}
\noindent
\textbf{\g_text_exercise_tl\theex{}:~#1}
\hfill
% Run a regular expression on the body of the
% exercise to count the number of hints present
% and store that number in a variable.
\regex_count:nVN {\c{begin}\{hint\}} \CurVerbatim \l_hintenv_int
\regex_count:nVN {\c{begin}\{sol\}} \CurVerbatim \l_solenv_int
% If at least one hint is provided start a list with
% links to the inserted hints.
\int_compare:nTF { \l_hintenv_int > 0 } { \g_text_hint_tl } { }
% For all integers in the range from one to
% the number of inserted hints do.
\int_step_variable:nNn {\l_hintenv_int} \l_iterator_tl{
\int_compare:nTF { \l_iterator_tl > 1 } { ,~ } { }
\hyperlink{hint\theex.\l_iterator_tl}{\l_iterator_tl}
}
% If at least one solution is provided start a list with
% links to the inserted solutions.
\int_compare:nTF { \l_solenv_int > 0 } { \g_text_solution_tl } { }
% For all integers in the range from one to
% the number of inserted solutions do.
\int_step_variable:nNn {\l_solenv_int} \l_iterator_tl{
\int_compare:nTF { \l_iterator_tl > 1 } { ,~ } { }
\hyperlink{solution\theex.\l_iterator_tl}{\l_iterator_tl}
}
\par\noindent
% write verbatim content out and read it back
\directlua{
local~verb = table.concat(verb_table, "\string\n")
local~file = io.open(tex.jobname..".tmp", "w")
file:write(verb)
file:close()
}
\input{\jobname.tmp}
}
\NewDocumentEnvironment{hint}{O{}+b}{%
% hint start
\refstepcounter{hint}
\tl_set:Nx \l_temp_tl { hint\thehint }
\iow_now:Nx \g_hintfile_iow { \par\noindent}
\iow_now:Nx \g_hintfile_iow { \exp_not:N \textbf{Hint~\arabic{hint}~to~exercise~\theex}}
\iow_now:Nx \g_hintfile_iow { \hfill \g_back_to_exercise_tl }
\iow_now:Nx \g_hintfile_iow { \exp_not:N \ref{exercise\theex } }
\iow_now:Nx \g_hintfile_iow { \par\noindent}
\iow_now:Nx \g_hintfile_iow { \exp_not:N \linkdest{ \l_temp_tl } }
\iow_now:Nx \g_hintfile_iow { \exp_not:N \vspace{-0.4cm}\par\noindent}
\iow_now:Nn \g_hintfile_iow { #2 }
\iow_now:Nn \g_hintfile_iow { \bigskip}
\iow_now:Nn \g_hintfile_iow { \filbreak}
}{
% hint end
}
\NewDocumentEnvironment{sol}{O{}+b}{
% hint start
\refstepcounter{solution}
\tl_set:Nx \l_temp_tl { solution\thesolution }
\iow_now:Nx \g_solutionfile_iow { \par\noindent }
\iow_now:Nx \g_solutionfile_iow { \exp_not:N \textbf{Solution~\arabic{solution}~to~exercise~\theex}}
\iow_now:Nx \g_solutionfile_iow { \hfill \g_back_to_exercise_tl }
\iow_now:Nx \g_solutionfile_iow { \exp_not:N \ref{exercise\theex } }
\iow_now:Nx \g_solutionfile_iow { \par\noindent}
\iow_now:Nx \g_solutionfile_iow { \exp_not:N \linkdest{ \l_temp_tl } }
\iow_now:Nx \g_solutionfile_iow { \exp_not:N \vspace{-0.4cm}\par\noindent}
\iow_now:Nn \g_solutionfile_iow { #2 }
\iow_now:Nn \g_solutionfile_iow { \bigskip}
\iow_now:Nn \g_solutionfile_iow { \filbreak}
}{
% hint end
}
% Define command for closing the two files used
% for storing hints and solutions.
\NewDocumentCommand{\closehintandsolutionfile}{}{
\iow_close:N \g_hintfile_iow
\iow_close:N \g_solutionfile_iow
}
\ExplSyntaxOff
\begin{document}
\section{Exercises}
\begin{ex}
Description of the exercise.
Run the following command
\begin{verbatim}
print("Hello world")
\end{verbatim} % need one blank line after verbatim, otherwise excaptions occur
\begin{hint}
Run the example from the command line with the python command
\end{hint}
\begin{hint}
The solution is 42.
\end{hint}
\end{ex}
\closehintandsolutionfile
\section{Hints}
\input{hintfile.tex}
\end{document}