我按照这个例子来了解如何使删除线在多个段落中起作用(其中包含引用,这是许多删除线解决方案的症结所在):
我已经创建了一个可以工作的最小可重现示例,如下所示。
\documentclass{article}
\usepackage{xcolor}
\usepackage[normalem]{ulem}
\newcommand\deleted[1]{\color{red}\let\helpcmd\sout\parhelp#1\par\relax\relax}
\newcommand\added[1]{\color{blue}\let\helpcmd\parhelp#1\par\relax\relax}
\long\def\parhelp#1\par#2\relax{%
\helpcmd{#1}\ifx\relax#2\else\par\parhelp#2\relax\fi%
}
\begin{document}
\deleted{
Example deleted section - \cite{abc} Lorem ipsum
dolor sit amet, consectetur adipiscing elit, sed
do eiusmod tempor incididunt ut labore et dolore magna aliqua.
}
\added{
Example added section - - \cite{abc} Lorem ipsum
dolor sit amet, consectetur adipiscing elit, sed
do eiusmod tempor incididunt ut labore et dolore magna aliqua.
}
\end{document}
在我使用此功能的主要文档中,我得到了这些\usepackage
语句,并且我正在复制/粘贴这些\newcommand
行和\long\def...
行(老实说,我不明白这一行,我从上面引用的 tex/SE 文章中复制了它)。
当我使用时\added{...paragraph text...}
出现以下Undefined control sequence
错误:
\added #1->\color {blue}\let \helpcmd
\parhelp #1\par \relax \relax
l.184 }
The control sequence at the end of the top line
of your error message was never \def'ed. If you have
misspelled it (e.g., `\hobx'), type `I' and the correct
spelling (e.g., `I\hbox'). Otherwise just continue,
and I'll forget about whatever was undefined.
我看不出我的文档(主要只是包含一堆\usepackage
语句)和这里最低限度可重现的示例之间有什么区别。
答案1
乍一看,人们可能会修复\let
定义中的-assignment \added
,添加一些范围以防止颜色变化永久存在,并用于\DeclareRobustCommand
获得一些稳健性:
\documentclass{article}
\usepackage{xcolor}
\usepackage[normalem]{ulem}
\makeatletter
\DeclareRobustCommand\deleted[1]{{\color{red}\let\helpcmd\sout\parhelp#1\par\relax\relax}}
\DeclareRobustCommand\added[1]{{\color{blue}\let\helpcmd\@firstofone\parhelp#1\par\relax\relax}}
\long\def\parhelp#1\par#2\relax{%
\helpcmd{#1}\ifx\relax#2\else\par\parhelp#2\relax\fi
}
\makeatother
\begin{document}
\noindent texttexttext
\noindent\deleted{ %<- this space is not removed and not striked
Example deleted section - \cite{abc} Lorem ipsum \par
dolor sit amet, consectetur adipiscing elit, sed
do eiusmod tempor incididunt ut labore et dolore magna aliqua. %<- this space is not removed and striked
}texttexttext
\noindent texttexttext
\noindent\added{ %<- this space is not removed
Example added section - - \cite{abc} Lorem ipsum \par
dolor sit amet, consectetur adipiscing elit, sed
do eiusmod tempor incididunt ut labore et dolore magna aliqua. %<- this space is not removed
}texttexttext
\end{document}
第二眼:
下面的代码是为了确保
- 鲁棒性,
\added
/参数的前导和尾随空格标记\deleted
在水平模式下被保留,但不会被删除,\added
在/ space-token的参数中,\deleted
仍然形成可丢弃的粘连,而不是段落末尾的“删除线的空白”。
\makeatletter
%%=============================================================================
%% Paraphernalia:
%% \UD@firstoftwo, \UD@secondoftwo, \UD@Exchange, \UD@PassFirstToSecond,
%% \UD@stopromannumeral, \UD@CheckWhetherNull, \UD@ExtractFirstParArg,
%% \UD@TrimLeadingTokens, \UD@TrimTrailingTokens
%%=============================================================================
\newcommand\UD@firstoftwo[2]{#1}%
\newcommand\UD@secondoftwo[2]{#2}%
\newcommand\UD@gobbletwo[2]{}%
\newcommand\UD@Exchange[2]{#2#1}%
\newcommand\UD@PassFirstToSecond[2]{#2{#1}}%
\@ifdefinable\UD@stopromannumeral{\chardef\UD@stopromannumeral=`\^^00}%
%%-----------------------------------------------------------------------------
%% Check whether argument is empty:
%%.............................................................................
%% \UD@CheckWhetherNull{<Argument which is to be checked>}%
%% {<Tokens to be delivered in case that argument
%% which is to be checked is empty>}%
%% {<Tokens to be delivered in case that argument
%% which is to be checked is not empty>}%
%%
%% The gist of this macro comes from Robert R. Schneck's \ifempty-macro:
%% <https://groups.google.com/forum/#!original/comp.text.tex/kuOEIQIrElc/lUg37FmhA74J>
\newcommand\UD@CheckWhetherNull[1]{%
\romannumeral\expandafter\UD@secondoftwo\string{\expandafter
\UD@secondoftwo\expandafter{\expandafter{\string#1}\expandafter
\UD@secondoftwo\string}\expandafter\UD@firstoftwo\expandafter{\expandafter
\UD@secondoftwo\string}\expandafter\UD@stopromannumeral\UD@secondoftwo}{%
\expandafter\UD@stopromannumeral\UD@firstoftwo}%
}%
%%-----------------------------------------------------------------------------
%% Extract first inner \par-undelimited argument:
%%
%% \UD@ExtractFirstParArg{A\par B\par C\par D\par E} yields {A}
%%
%% \UD@ExtractFirstParArg{{AB}\par C\par D\par E} yields {{AB}}
%%
%% \UD@ExtractFirstParArg{AB\par C\par D\par E} yields {AB}
%%
%% \UD@ExtractFirstParArg{{AB}} yields {{AB}}
%%
%% \UD@ExtractFirstParArg{} yields {}
%%
%% Due to \romannumeral-expansion the result is delivered after two
%% expansion-steps/after "hitting" \UD@ExtractFirstParArg with \expandafter
%% twice.
%%
%% Use frozen-\relax as delimiter for speeding things up.
%% I chose frozen-\relax because David Carlisle pointed out in
%% <https://tex.stackexchange.com/a/578877>
%% that frozen-\relax cannot be (re)defined in terms of \outer and cannot be
%% affected by \uppercase/\lowercase.
%%
%% \UD@ExtractFirstParArg's argument may contain frozen-\relax:
%% The only effect is that internally more iterations are needed for
%% obtaining the result.
%%
%%.............................................................................
\@ifdefinable\UD@RemoveFromParTillFrozenrelax{%
\expandafter\expandafter\expandafter\UD@Exchange
\expandafter\expandafter\expandafter{%
\expandafter\expandafter\ifnum0=0\fi}%
{\long\def\UD@RemoveFromParTillFrozenrelax#1\par#2}{{#1}\par}%
}%
\expandafter\UD@PassFirstToSecond\expandafter{%
\romannumeral\expandafter
\UD@PassFirstToSecond\expandafter{\romannumeral
\expandafter\expandafter\expandafter\UD@Exchange
\expandafter\expandafter\expandafter{%
\expandafter\expandafter\ifnum0=0\fi}{\UD@stopromannumeral{{}}#1\par}%
}{%
\UD@stopromannumeral\romannumeral\UD@ExtractFirstParArgLoop
}%
}{%
\newcommand\UD@ExtractFirstParArg[1]%
}%
\newcommand\UD@ExtractFirstParArgLoop[1]{%
\expandafter\UD@CheckWhetherNull\expandafter{\UD@gobbletwo#1}%
{%
\expandafter\expandafter
\expandafter\UD@stopromannumeral
\expandafter\UD@PassFirstToSecond
\expandafter{%
\romannumeral
\expandafter\UD@firstoftwo\expandafter\UD@stopromannumeral
\UD@firstoftwo#1%
}{}%
}%
{\expandafter\UD@ExtractFirstParArgLoop\expandafter{\UD@RemoveFromParTillFrozenrelax#1}}%
}%
%%-----------------------------------------------------------------------------
%% \UD@iterateParList{<tokens>}{<\par-separated list>}
%%
%% Each item of the <\par-separated list> is nested in curly braces before
%% prepending <tokens> to it. Items are separated by \par.
%%
%% I tried my best at preventing removal of curly braces.
%%
%%-----------------------------------------------------------------------------
\@ifdefinable\UD@gobbletopar{\long\def\UD@gobbletopar#1\par{}}%
\newcommand\UD@iterateParList[2]{%
\romannumeral\UD@iterateparlistloop{#2}{#1}{}{}%
}%
\newcommand\UD@iterateparlistloop[4]{%
\expandafter\UD@CheckWhetherNull\expandafter{\UD@gobbletopar#1\par}{%
\UD@stopromannumeral#4#3#2{#1}%
}{%
\expandafter\UD@PassFirstToSecond\expandafter{%
\romannumeral
\expandafter\expandafter\expandafter\UD@Exchange
\expandafter\expandafter\expandafter{%
\UD@ExtractFirstParArg{#1}%
}{\UD@stopromannumeral#4#3#2}%
}{%
\expandafter\UD@iterateparlistloop\expandafter{\UD@gobbletopar#1}{#2}{\par}%
}%
}%
}%
%%-----------------------------------------------------------------------------
%% "\UD@shifttrailspaces{<tokens>}{STUFF }" yields "<tokens>{STUFF} "
%%-----------------------------------------------------------------------------
\newcommand\UD@shifttrailspaces[2]{%
\romannumeral
\expandafter\UD@Exchange\expandafter{%
\romannumeral\UD@shifttrailspacesloop{{}}#2\UD@ForBidden/ \UD@ForBidden/\UD@ForBidden/ {{}#2}{#1}{}%
}\UD@stopromannumeral
}%
\@ifdefinable\UD@shifttrailspacesloop{%
\long\def\UD@shifttrailspacesloop#1 \UD@ForBidden/#2\UD@ForBidden/ #3#4#5{%
\UD@CheckWhetherNull{#2}{% no trailing space
\expandafter\UD@PassFirstToSecond\expandafter{\UD@firstoftwo{}#3}{\UD@stopromannumeral#4}#5%
}{% trailing space
\UD@shifttrailspacesloop#1\UD@ForBidden/ \UD@ForBidden/\UD@ForBidden/ {#1}{#4}{#5 }%
}%
}%
}%
%%-----------------------------------------------------------------------------
%% "\UD@shiftleadspaces{<tokens>}{ STUFF}" yields " <tokens>{STUFF}"
%%-----------------------------------------------------------------------------
\@ifdefinable\UD@gobblespace{\UD@firstoftwo{\def\UD@gobblespace}{} {}}%
\newcommand\UD@shiftleadspaces[2]{%
\romannumeral
\expandafter\UD@Exchange\expandafter{%
\romannumeral
\UD@shiftleadspacesloop\UD@ForBidden/#2\UD@ForBidden/ \UD@ForBidden/\UD@ForBidden/{#2}{#1}{}%
}\UD@stopromannumeral
}%
\@ifdefinable\UD@shiftleadspacesloop{%
\long\def\UD@shiftleadspacesloop#1\UD@ForBidden/ #2\UD@ForBidden/\UD@ForBidden/#3#4#5{%
\UD@CheckWhetherNull{#1}{% Leading space
\expandafter\UD@Exchange\expandafter{\expandafter{\UD@gobblespace#3}}%
{\UD@shiftleadspacesloop\UD@ForBidden/#2\UD@ForBidden/\UD@ForBidden/}{#4}{#5 }%
}{% no leading space
\UD@stopromannumeral#5#4{#3}%
}%
}%
}%
%%-----------------------------------------------------------------------------
\@ifdefinable\deleted{%
\DeclareRobustCommand\deleted[1]{{\color{red}\ifhmode\null\fi\UD@iterateParList{\UD@shifttrailspaces{\UD@shiftleadspaces{\sout}}}{#1}}}%
}%
\@ifdefinable\added{%
%\DeclareRobustCommand\added[1]{{\color{blue}\ifhmode\null\fi\UD@iterateParList{\UD@shifttrailspaces{\UD@shiftleadspaces{\@firstofone}}}{#1}}}%
\DeclareRobustCommand\added[1]{{\color{blue}\ifhmode\null\fi#1}}%
}%
\makeatother
\documentclass{article}
\usepackage{xcolor}
\usepackage[normalem]{ulem}
\begin{document}
\noindent texttexttext
\noindent\deleted{ %<- this space is not removed and not striked
Example deleted section - \cite{abc} Lorem ipsum \par
dolor sit amet, consectetur adipiscing elit, sed
do eiusmod tempor incididunt ut labore et dolore magna aliqua. %<- this space is not removed and not striked
}texttexttext
\noindent texttexttext
\noindent\added{ %<- this space is not removed
Example added section - - \cite{abc} Lorem ipsum \par
dolor sit amet, consectetur adipiscing elit, sed
do eiusmod tempor incididunt ut labore et dolore magna aliqua. %<- this space is not removed
}texttexttext
\end{document}