我在思考时偶然发现了标题中暗示的问题 这个问题 (“用户定义环境周围的不规则间距”) 和 它的答案。
\@endparenv
所有“列表制作”环境都会调用该命令(通过\endtrivlist
),以确保“当且仅当您在
\end
命令后留出一个空行时,LaTeX 才会开始一个新段落”(引自)。它通过在环境本身的最末端对 的含义ltlists.dtx
应用“临时更改”(由 指定的意义)来实现这一点 。另一方面,“列表制作”环境还需要 在其初始化阶段 对 的含义应用“长期更改” ,更准确地说是在执行 时(参见2015/05/10,第 78-86 行)。在 中设计的用于改变 的含义的机制可确保当“列表制作”环境相互嵌套时,这两个更改以一致的方式协同工作。ltpar.dtx
\par
\par
\@trivlist
ltlists.dtx
ltpar.dtx
\par
然而,我想知道,在当前的 LaTeX 内核中,当列表环境出现在minipage
嵌套在另一个列表环境中的 中时,这种机制是否也能正常运行:事实上,在这种情况下, 应该minipage
充当“沙箱”,将内部环境与外部环境“屏蔽”,这似乎是合理的;但目前情况并非如此,如下例所示。实际上,该示例并不直接使用列表环境,而是flushleft
,但它是通过 实现的\trivlist
;这样做是为了更接近在 中讨论的情况
上述问题。
在继续阅读之前,请您编译以下代码并检查其输出以及源代码本身。
% My standard header for TeX.SX answers:
\documentclass[a4paper]{article} % To avoid confusion, let us explicitly
% declare the paper format.
\usepackage[T1]{fontenc} % Not always necessary, but recommended.
\usepackage[ascii]{inputenc} % Just to check that the source is still pure,
% 7-bit-clean ASCII when you execute it, as it
% was when I wrote it.
% End of standard header. What follows pertains to the problem at hand.
\makeatletter
\newcommand*\@SHOW[1]{%
\texttt{\char\escapechar #1} $\Longrightarrow$
\texttt{\expandafter\meaning\csname #1\endcsname}%
}
\newcommand*\SHOW{%
\myNL\@SHOW{par}\myNL\@SHOW{@par}%
}
\newcommand*\MyParShape{%
\hangafter \@ne \hangindent \thr@@ pc \noindent
}
\newcommand*\myNL{\hfill\break}
\makeatother
\begin{document}
\section{Base scenario}
\label{S:Base}
Consider the following:
\begin{flushleft}
Some text marked~(A)\@.
\end{flushleft}
Not preceded by blank line:\SHOW
\begin{flushleft}
Some text marked~(B)\@.
\end{flushleft}
Preceded by blank line:\SHOW
\MyParShape
All this is quite OK\@. This paragraph does have hanging indentation,
but the following one won't\ldots
\ldots as you can see here. Lorem ipsum dolor sit amet consectetur adipisci
elit, ridiculus mus. Fringilla eius partorietur.
\section{\texttt{flushleft} inside \texttt{flushleft}}
\label{S:fl<fl}
Now consider:
\begin{flushleft}
\setlength{\parindent}{1pc}
We nest here the same contruction presented in Section~\ref{S:Base}:
\begin{flushleft}
Some text marked~(C)\@.
\end{flushleft}
Not preceded by blank line:\SHOW
\begin{flushleft}
Some text marked~(D)\@.
\end{flushleft}
Preceded by blank line:\SHOW
\MyParShape
This paragraph has hanging indentation, but this time the hanging
indentation is carried over also to the following one.
But this could be considered OK\@ too, because, after all, we are still
inside the \texttt{list} environment implied by the outer
\texttt{flushleft}: indeed, preserving \verb|\parshape| setting is essential
for the implementation of ``list-making'' environments. And
\texttt{flushleft} environments should \textbf{not} be nested!
\end{flushleft}
Not preceded by blank line:\SHOW
Again, this is what we expect.
\section{\texttt{flushleft} inside \texttt{minipage} inside \texttt{flushleft}}
\label{S:fl<mp<fl}
But consider this other situation:
\begin{flushleft}
\begin{minipage}[b]{.9\linewidth}
\raggedright % to avoid underfull boxes
\setlength{\parindent}{1pc}
Start of the \texttt{minipage}. Note that \verb|\par| has its primitive
meaning:\SHOW
Also here, we nest the same contruction presented in
Section~\ref{S:Base}:
\begin{flushleft}
Some text marked~(E)\@.
\end{flushleft}
Not preceded by blank line:\SHOW
\begin{flushleft}
Some text marked~(F)\@.
\end{flushleft}
Preceded by blank line:\SHOW
\MyParShape
As we see, the meaning of \verb|\par| has not reverted to what it was at
the beginning. Why is this not good at all? Well, for a number of
reasons, of which the fact that hanging indentation is carried over from
one paragraph to another,\ldots
(\emph{continuing from the previous paragraph}) \ldots as you can see
here, is just the first---and rather silly---example that comes to my
mind. Lorem ipsum dolor sit amet consectetur adipisci elit, ridiculus
mus. Fringilla eius partorietur.
End of the \texttt{minipage}.
\end{minipage}
Thus, we see that, inside the \texttt{minipage}, we do \emph{not} get the
same behavior as in the ``base scenario'' of Section~\ref{S:Base}, but
rather the behavior of Section~\ref{S:fl<fl}.
\end{flushleft}
Should this be considered a (tiny) bug in the \LaTeXe\ kernel?
\clearpage
\makeatletter
\def\@minipagerestore{%
\def\@par{\let\par\@@par\par}
}
\makeatother
\section{Possible fix}
\label{S:Fix}
Consider, finally, what happens now:
\begin{flushleft}
\begin{minipage}[b]{.9\linewidth}
\raggedright % to avoid underfull boxes
\setlength{\parindent}{1pc}
Start of the \texttt{minipage}. Note that \verb|\par| has its primitive
meaning:\SHOW
Once more, we nest here the same contruction presented in
Section~\ref{S:Base}:
\begin{flushleft}
Some text marked~(G)\@.
\end{flushleft}
Not preceded by blank line:\SHOW
\begin{flushleft}
Some text marked~(H)\@.
\end{flushleft}
Preceded by blank line:\SHOW
\MyParShape
As we see, this time the meaning of \verb|\par| \emph{has} reverted to
what it was at the beginning. Indeed, hanging indentation is no longer
being carried over from one paragraph to another,\ldots
(\emph{continuing from the previous paragraph}) \ldots as you can see
here. Lorem ipsum dolor sit amet consectetur adipisci elit, ridiculus
mus. Fringilla eius partorietur.
End of the \texttt{minipage}.
\end{minipage}
Thus, we see that, inside the \texttt{minipage}, we \emph{do} get, now, the
same behavior as in the ``base scenario'' of Section~\ref{S:Base}.
\end{flushleft}
\end{document}
请注意,我们\parindent
在某些地方手动重置以使效果\@endparenv
可见。您获得的文档包含四个部分。
在第一部分中,这里以环境的简单顶层使用
flushleft
为例,作为下文介绍的其他案例的基准。在这里,我们可以看到,当“临时更改”自行发生时,其\@endarenv
含义是 如何被撤销的,而没有同时发生的“长期更改”。\par
第二部分说明了直接嵌套的情况。结果乍一看可能很奇怪,但这只是因为人们没有立即意识到内部
flushleft
实际上是嵌套在外部所表示的第一级列表中的第二级列表flushleft
。实际上,典型的用户不会意识到这一点,因为他们不知道如何实现的细节flushleft
,可以给他们的最好建议是避免嵌套
center
/ flushleft
/flushright
环境。
在第三部分中,然而,情况却不那么明朗:flushleft
包含在 中的环境难道不应该minipage
以与第一部分中所示的顶级环境相同的方式运行,而不管 本身minipage
又包含在另一个flushleft
(或center
,或 )
flushright
环境中?甚至在列表中?实际上,
minipage
重置\par
为其原始含义,但是——啊哈!——它无法以类似的方式重置\@par
,因此外部列表所做的“长期更改”渗透到内部列表,从而确定与直接嵌套相同的结果。
最后,第四节\@par
提出了一种可能的,甚至是过于明显的修复方法:在 s 的开头 包括重置minipage
。这里是通过
\@minipagerestore
钩子来完成的。
我的问题是:上述分析是否正确?与此相关的是:是否还有其他方面我没有考虑到,这意味着选择不是minipage
重新定义环境是\@par
故意的吗?
答案1
大约五年后!
LaTeX有五十种颜色\par
!楼主做了许多我认为很复杂的测试,最后得出结论 LaTeX 可能有 bug。幸好没有 bug!
OP 得出的结论是,他插入了一个\MyParshape
基于 的特殊宏,\hangafter\hangindent
并且这个宏在更多段落中延续,这可能是一个错误。如果他\@@par
当时在宏之前的段落中插入一个,那么一切都会好起来。现在我必须保存\@@par
在序言中并在测试中使用它(因为我们现在有了新的钩子系统,现在还有其他复杂情况)。在列表中,这种行为是正确的,因为可能存在更多级别的列表,并且程序需要控制它\par
。OP 应该\MyParShape
通过适当定义它来确保列表中的正确工作。
我附上了我的测试图片和代码。我删除了 OP 中不必要的代码。我们只需要测试三个条件 a)par
普通段落中的值、迷你页面中的值和列表中的值。测试 9,解决了 OP 的问题。
顺便说一下\par
,现在有一个双胞胎姐妹,当我们说\par
我们有两个时!取消注释下面代码的末尾即可获得惊喜!
\documentclass[a4paper]{scrartcl}
\usepackage{xcolor}
\definecolor{green}{rgb}{0.06,0.44,0.08}
\makeatletter
\newcommand*\MyParShape{%
\hangafter \@ne \hangindent \thr@@ pc \noindent
}
\ExplSyntaxOn
\let\ATATPAR\@@par
\def\LISTPAR{\if@newlist \advance \par@deathcycles \@ne \ifnum \par@deathcycles >\@m
\@noitemerr {\@@par }\fi \else {\@@par }\fi}
\def\PASS{{\ttfamily\color{green} ~ PASS~}}
\def\FAIL{{\ttfamily\color{red} ~ FAIL~}}
\int_new:N \testcounter
\int_gset:Nn \testcounter{1}
\cs_gset:Npn\TEST #1#2#3
{ \makeatletter
\int_use:N \testcounter :~~
\token_if_eq_meaning:NNTF #1#2{\PASS}{\FAIL}
{\ttfamily \tl_to_str:n {#3} }
\int_gincr:N \testcounter
}
\ExplSyntaxOff
\makeatother
\parindent0pt
\begin{document}
\makeatletter
\subsection*{At start of document}
\TEST{\par}{\@@par}{\par==\@@par}\par
\ExplSyntaxOn
\TEST{\par}{\tex_par:D}{\par != \tex_par:D }
\ExplSyntaxOff
\subsection*{In minipage tests}
\begin{minipage}{1\linewidth}
\TEST{\par}{\ATATPAR}{In minipage \par == \@@par}\par
\end{minipage}
\subsection*{In flushleft tests}
\begin{flushleft}
\TEST{\par}{\@par}{in flush left \par=\@par (in list definition)}\par
Some text marked~(A).
\end{flushleft}
\subsection*{Between flush left environments}
\TEST{\par}{\@@par}{between two flush enviroments \par=\@@par l3 wins} % TEST 5
\makeatother
\begin{flushleft}
Some text marked~(B).
\end{flushleft}
\subsection*{Nested minipage in flushleft}
\begin{flushleft}
\raggedright
\begin{minipage}[b]{.9\linewidth}
\makeatletter
\TEST{\par}{\@@par}{In a list first line of minipage \par=\@@par}\par
\begin{flushleft}
\TEST{\par}{\@par}{In flusleft>minipage>flushleft \par=\@par (list def.)}
Some text marked~(E).
\end{flushleft}
Not preceded by blank line:\par
\makeatletter
\TEST{\@par}{\LISTPAR}{In list \par=\@par i.e, as defined in lists}
\makeatother
\begin{flushleft}
Some text marked~(F).
\end{flushleft}
\MyParShape
\color{orange}
As we see, the meaning of \verb|\par| has not reverted to what it was at
the beginning. Why is this not good at all? Well, for a number of
reasons, of which the fact that hanging indentation is carried over from
one paragraph to another,\ldots\color{black}\ATATPAR
\makeatletter
\TEST{\@par}{\LISTPAR}{After \MyParshape \par=\@par=\LISTPAR}
\makeatother
Continuing from the previous paragraph \ldots as you can see
here\ldots. There is no issue with the \verb+\MyParShape+!
\end{minipage}
\end{flushleft}
%\ttfamily
%\ExplSyntaxOn
%\meaning\par
%
%
%\meaning\tex_par:D\par
\end{document}