![当 \obeylines 生效时,Xparse 宏不起作用](https://linux22.com/image/296240/%E5%BD%93%20%5Cobeylines%20%E7%94%9F%E6%95%88%E6%97%B6%EF%BC%8CXparse%20%E5%AE%8F%E4%B8%8D%E8%B5%B7%E4%BD%9C%E7%94%A8.png)
这是我在 latex 中遇到的最奇怪和最难调试的情况。当\obeylines
生效时,似乎 xparse 定义的带有可选参数的宏会遇到一些问题。以下 MWE 证明了这一点(我尽力将其精简,但它仍然很长,抱歉……尽管如此,它的结构很简单。请参阅编辑部分以获得完整解释):
\documentclass[fontsize=22pt]{scrreprt}
\usepackage{etoolbox}
\usepackage{geometry,calc}
\geometry{paperwidth=1280pt,paperheight=3840pt,margin=25pt}
\usepackage{tcolorbox}
\usepackage{xparse}
\usepackage[titles]{tocloft}
\addtocounter{tocdepth}{1}
\NewDocumentCommand\mypart{m D--{n}}{
\printtocentry{#1}{#2}{part}
\item[]
}
\newcommand*{\mychapname}{}
\NewDocumentCommand\mychap{m D--{n}}{
\item[]
\end{notes}
\vspace{-8ex}
\printtocentry{#1}{#2}{chapter}
\begingroup\let\clearpage\relax
\chapter*{\large\sffamily#1}
\endgroup
\renewcommand*{\mychapname}{#1}
\vspace{-12ex}
\begin{notes}
\item[]
}
\NewDocumentCommand\printtocentry{m m m o}{
\refstepcounter{#3}
\def\mynumberline{\protect\numberline{\csname the#3\endcsname}}
\IfNoValueF{#4}{\def\mynumberline{\relax}}
\ifstrequal{#2}{t}{
\addcontentsline{toc}{#3}{\mynumberline\colorbox{yellow}{#1}}
}{}
\ifstrequal{#2}{d}{
\addcontentsline{toc}{#3}{\mynumberline\colorbox{green}{#1}}
}{}
\ifstrequal{#2}{n}{
\addcontentsline{toc}{#3}{\mynumberline#1}
}{}
}
\NewDocumentCommand\mysec{m D--{n}}{
\printtocentry{#1}{#2}{section}
\item[]\hfill
{[~\mychapname~\textbf{--}~#1~]}
}
\NewDocumentCommand\mysubsec{m D--{n}}{
\vspace{3ex}
\printtocentry{#1}{#2}{subsection}
\item[(#1)]\hfill
}
\def\removespecial{
\catcode`\_=12
\catcode`\^=12
\catcode`\$=12
%\catcode`\%=12
\catcode`\#=12
\catcode`\&=12
\catcode`\~=12
}
% the second argument is used to highlight toc entries, I think this is where the bug is
\NewDocumentCommand\myitem{O{} D--{n}}{
\vspace{1ex}
\ifstrempty{#1}{}{
\printtocentry{#1}{#2}{subsubsection}[NoNumberLine]
}
\item[#1]
%%% this line is the source of problem, \obeylines changes the catcode of ^^M
\obeylines
\obeyspaces
\removespecial
}
\renewcommand*{\\}{\textbackslash}
\usepackage[Sonny]{fncychap}
\renewcommand*{\DOCH}{}
\ChTitleVar{\large}
\usepackage[inline]{enumitem}
\newlist{notes}{description}{1}
\setlist[notes]{
leftmargin=0.3\linewidth,labelsep=2em,
itemsep=1.5ex,style=multiline,font=\normalfont
}
\begin{document}
\tableofcontents
\begin{notes}
\mypart{Part One}
% this chapter works, because \obeylines is not yet in effect
\mychap{Chap One}
\mysubsec{Subsec One}
\myitem[Item One]
...some content...
\myitem[Item Two]
...some content...
% this chapter does not work
% uncomment the next line would make it to work
%\catcode\endlinechar = 5
\mychap{Chap Two}
\mysubsec{Subsec Two}% disappear
\myitem[Item Three]% disappear
...some content...
\myitem[Item Four]% disappear
...some content...
% however, this chapter works:
\mychap{Chap Three}-d-
\mysubsec{Subsec Three}
\myitem[Item Five]
...some content...
\myitem[Item Six]
...some content...
\end{notes}
\end{document}
% vim: filetype=tex
部分结果如下:
第二章是观察问题的地方,你可以看到所有项目都消失了,只剩下项目的内容。然而,它的结构应该与第一章完全相同。
我花了很多时间尝试调试这个问题,但徒劳无功。而且我对纯文本不太了解。我想我已经确定问题出在\obeylines
xparse 定义的宏上。我还发现\catcode\endlinechar = 5
在前面添加\mychap{}
似乎可以解决问题。有人能 1) 解释这个问题和 2) 告诉我如何让它工作吗?谢谢。
编辑:我将介绍一些 MWE 的背景知识,并阐明我在这里想要实现的目标。上面的 MWE 改编自一个名为 mynotes.sty 的自定义类,正如其名称所暗示的那样,我通常用它来做笔记。它为笔记提供了一个类似书本的结构,主要部分是一个列表。如果没有这个类,它可以这样做:
\usepackage[inline]{enumitem}
\newlist{notes}{description}{1}
\part{...}
\chapter{...}
\section{...}
\begin{notes}
\item[]...
\end{notes}
\section{...}
\begin{notes}
\item[]...
\end{notes}
...
显然那里有太多了\begin{notes}...\end{notes}
。所以为了美观和节省类型,我定义了\mychap
,\myitem
等等。此外,还添加了一个功能:突出显示目录中的条目的能力。这是通过将这些D--{n}
可选参数添加到\NewDocumentCommand
s 来实现的。所以现在它的工作方式如下:
\mychap{...} % normal entry, no toc coloring
\mychap{...}-n- % the same as above, because n is the default value
\mychap{...}-d- % toc entry colored green
\mysec{...}-t- % toc entry colored yellow
历史说得够多了。现在来看看相关部分:在该类的原始实现中,缺少以下三行:
\obeylines
\obeyspaces
\removespecial
并且它很好地实现了其设计目的。直到最近,我必须记录一些主题,这使得逐字记录式的环境成为必要。同样,这可能会奏效:
\myitem[...]
\begin{verbatim}...\end{verbatim}
\myitem[...]
\begin{alltt}...\end{alltt}
但输入太多了。另外,将的功能alltt
分成三个部分具有更大的灵活性。所以我决定添加这三行来尝试模拟:
\myitem[...]
{\obeylines\obeyspaces\removespecial ...}
\myitem[...]
{\obeylines\obeyspaces\removespecial ...}
% could not figure out how to add the parentheses
% so in fact it is like:
\myitem[...]
\obeylines\obeyspaces\removespecial ...
但是,这三行破坏了原始格式,这就是上面的 MWE 所显示的。在上面的 MWE 的输出中,三个部分(第一章、第二章和第三章)的格式应该看起来完全相同,除了目录条目的颜色。添加这三行(更准确地说,只有行\obeylines
)使第二章的一部分偏离了左边缘(正如 Joseph 指出的那样)。从我的角度来看,原因是这样的:
% NOT working example
\myitem[...]
...
% \obeylines is in effect here! for some reason which I don't
% understand it breaks the following \mychap:
\mychap{Chap Two}
尝试减轻 \obeylines 的影响将使其再次按预期工作:
% working example
\myitem[...]
...
\catcode\endlinechar = 5
\mychap{Chap Two}
% works again, those three lines not causing trouble any more
更令人困惑的是,这也有效:
% working example
\myitem[...]
...
\mychap{Chap Two}-d-
这让我产生了一种错觉,认为 xparse 中可选参数的处理存在一些问题。这就是完整的故事。非常感谢你的阅读和帮助!:)
\item[]
哦是的,这里对代码中的那些空的 s 有一个解释:那是因为enumitem
需要\begin{notes}...\end{notes}
至少有一个\item[]
内部。
答案1
这是对问题的解释而不是完整的解决方案。
正如约瑟夫·赖特所言,这个问题如果用适当的最小工作示例。事实上,如果你把 MWE 简化,那么问题就很明显了:正如人们可能怀疑的那样,解析包裹是完全无辜的。
这里 (接近) 是一个 MWE。为了节省空间,我稍微压缩了一下,不幸的是可读性会受到影响,但影响不大...:
\documentclass{scrreprt} \usepackage{xparse}
\NewDocumentCommand\mychap{m D--{n}}{
\item[]\end{notes} \textbf{#1} \begin{notes}\item[] }
\NewDocumentCommand\myitem{O{} D--{n}}{\item[#1]\obeylines}
\usepackage[inline]{enumitem}
\newlist{notes}{description}{1}
\setlist[notes]{nosep,leftmargin=0.3\linewidth}% Note the LEFTMARGIN !!!
\begin{document} \begin{notes}
% this chapter works, because \obeylines is not yet in effect
\mychap{Chap One} \myitem[Item Two] ...some content...
% this chapter does not work: uncommenting the next line makes it work
%\catcode\endlinechar = 5
\mychap{Chap Two}
\myitem[Item Four] ...some content...
% however, this chapter works:
\mychap{Chap Three}-d- \myitem[Item Six] ...some content...
\end{notes} \end{document}
此 MWE 比 OP 提供的 MWE 短得多。这产生:
正如问题所示,在这个精简版中,第二项的位置是错误的
正如 OP 所说,设置\catcode\endlinechar = 5
问题就消失了,但这并不奇怪,因为这实际上撤消了前一个\obeylines
命令。
leftmargin=0.3\linewidth
正如我在 MWE 中的评论所指出的那样,问题的真正原因是环境规范notes
和命令的结合\obeylines
:使用 obelines 实际上枚举项软件包很难将左边距设置到合适的位置。如果您设置,leftmargin=0
问题就会消失。当然,这可能不是 OP 想要的。
在努力理解 OP 的代码后,我认为简单地缩进一个块enumitem
(即notes
)似乎有点过分。如果我想做这样的事情,我想我会选择使用编辑器快捷键(即 vim 中的代码片段)和一些自定义环境的组合。