打破 tcolorbox 中注释的模式

打破 tcolorbox 中注释的模式

我用它tcolorbox来在文档中获得漂亮的代码列表。由于我的文档是培训文档,因此每行代码都有注释。很多时候,代码必须分成两页,因此我使用这些breakable选项。这有时会导致注释和代码分离。

有人知道如何禁止这种分离吗?

编辑 2*

添加了修改后的 MWE 来显示多行注释的问题。

\documentclass[10pt]{article}
\usepackage[margin=4cm]{geometry}
\usepackage{filecontents}
\usepackage{lipsum}

\usepackage{listings}
\usepackage[]{matlab-prettifier}
\usepackage{tcolorbox}
\tcbuselibrary{listings,xparse,breakable}

\newtcbinputlisting{\code}[2][]{%s
    listing options={style=Matlab-editor,tabsize=2,basicstyle=\footnotesize\ttfamily},
    listing inputencoding=utf8,
    listing file={#2},
    title=\texttt{#2},
    colback=blue!5!white,colframe=blue,fonttitle=\bfseries,
    before={\vspace{1mm}},
    top=-1pt,
    bottom=-1pt,
    listing only,breakable,#1}

\makeatletter
\newcounter{linehook}

\lst@AddToHook{EOL}{%
    \addtocounter{linehook}{1}%
    \csname linehook@hook@\number\value{linehook}\endcsname
}

\lst@AddToHook{AfterBeginComment}{%
    \immediate\write\@auxout{%
        \noexpand\expandafter\gdef
        \noexpand\csname linehook@hook@\number\value{linehook}\noexpand\endcsname
        {\noexpand\breakpoint}%
    }%
}

\newcommand\breakpoint{\vadjust{\hrule \penalty-2000 \vfill}}
\makeatother

\begin{filecontents*}{\jobname.txt}
% blahblah comment (three-line comment, should not be breakable)
% blahblah comment
% blahblah comment 
blahblah 
blahblah
blahblah
blahblah
% blahblah comment
blahblah
blahblah
blahblah
% blahblah comment
blahblah
% blahblah comment
blahblah 
blahblah
blahblah 
blahblah
% blahblah comment (two-line comment, should not be breakable)
% blahblah comment
blahblah 
blahblah 
blahblah
% blahblah comment
blahblah
blahblah 
blahblah
blahblah
% blahblah comment (two-line comment, should not be breakable)
% blahblah comment (but it is :))
blahblah 
blahblah 
blahblah
% blahblah comment
blahblah 
blahblah
% blahblah comment
blahblah 
blahblah 
blahblah 
blahblah
% blahblah comment
blahblah 
blahblah
\end{filecontents*}

\begin{document}
\lipsum[1]
\lipsum[1]
\code{\jobname.txt}
\end{document}

在此处输入图片描述

编辑

更准确地说,我想避免注释——注释中断(如果注释跨越多行)和注释——代码中断。

这是一个例子。抱歉,所有blahblah

\documentclass{article}
\usepackage{filecontents}
\usepackage{lipsum}

\usepackage{listings}
\usepackage[]{matlab-prettifier}
\usepackage{tcolorbox}
\tcbuselibrary{listings,xparse,breakable}

\newtcbinputlisting{\code}[2][]{%s
    listing options={style=Matlab-editor,tabsize=2,basicstyle=\footnotesize\ttfamily},
    listing inputencoding=utf8,
    listing file={#2},
    title=\texttt{#2},
    colback=blue!5!white,colframe=blue,fonttitle=\bfseries,
    before={\vspace{1mm}},
    top=-1pt,
    bottom=-1pt,
    listing only,breakable,#1}

\begin{filecontents*}{code.txt}
% blahblah 
blahblah 
% blahblah comment
blahblah 
% blahblah comment
blahblah 
% blahblah comment
blahblah 
% blahblah comment
blahblah 
% blahblah comment
blahblah 
% blahblah comment
blahblah 
% blahblah comment
blahblah 
% blahblah comment
blahblah 
% blahblah comment
blahblah 
% blahblah comment
blahblah 
% blahblah comment
blahblah 
% blahblah comment
blahblah 
% blahblah comment
blahblah 
% blahblah comment
blahblah 
% blahblah comment
blahblah 
% blahblah comment
blahblah 
% blahblah comment
blahblah 
\end{filecontents*}


\begin{document}
\lipsum[1]
\lipsum[1]
\code{code.txt}

\end{document}

在此处输入图片描述

答案1

新版本(修订版)

如果我们不想手动添加可能的断点,我们需要一个两阶段的过程,即两次编译运行。

listings将其处理的每个源代码行都视为一个段落。我们需要在该段落结束之后但在新段落开始之前为断点添加垂直材料。使用手动断点插入可以很好地完成此操作,因为我们可以将其放在正确的位置,以便在进入垂直模式时将其插入到下一个段落结束之后。

为了实现自动插入,我们必须知道下一行是否真的是注释。但当我们发现这个信息时,已经太晚了,新的段落已经开始了。

我们要做的是在之后使用EOL钩子来执行一个独特的宏每一个输入行使用

\csname linehook@hook@\number\value{linehook}\endcsname

其中linehook是一个计数器,它为文档中出现的每行源代码提供唯一的值。 的一个有用的副作用\csname是,如果命令名称未定义,TeX 会使命令名称等于\relax,因此如果这些宏中的任何一个未定义,则不会出现错误(第一次运行时所有宏都未定义)。

此外,AfterBeginComment每当在代码中找到注释时,我们都可以使用钩子来运行一些代码listings。这时,我们知道我们需要当前值的行宏来linehook实际添加断点。因此,我们使用辅助宏将该宏的定义写入 .aux 文件\@write@linehook。在下一次编译运行中,它将在相应行的末尾被识别,并且\relax它不会被插入到垂直列表中,而是被插入\breakpoint到垂直列表中。

为了使断点不位于连续的注释行之间,我们定义了一个新的开关\ifcommentline,该开关通过钩子在每个输出行的开头重置为 false EveryPar。一旦发现注释,它就会设置为 true。在重置此开关之前,我们可以检查前一行是否是注释行。如果是,我们定义\@write@linehook为忽略它的参数,否则我们实际上让它将我们唯一的行宏写入 .aux 文件。

这是完整更新的示例。请注意,我添加了\hrule定义\breakpoint以使插入断点的位置在输出中可见。对于您的实际文档,您可以删除此命令。

\documentclass{article}
\usepackage{filecontents}
\usepackage{lipsum}

\usepackage{listings}
\usepackage[]{matlab-prettifier}
\usepackage{tcolorbox}
\tcbuselibrary{listings,xparse,breakable}

\newtcbinputlisting{\code}[2][]{%s
    listing options={style=Matlab-editor,tabsize=2,basicstyle=\footnotesize\ttfamily},
    listing inputencoding=utf8,
    listing file={#2},
    title=\texttt{#2},
    colback=blue!5!white,colframe=blue,fonttitle=\bfseries,
    before={\vspace{1mm}},
    top=-1pt,
    bottom=-1pt,
    listing only,breakable,#1}

\makeatletter
\newcounter{linehook}
\newif\ifcommentline

\lst@AddToHook{EOL}{%
    \addtocounter{linehook}{1}%
    \csname linehook@hook@\number\value{linehook}\endcsname
}

\lst@AddToHook{EveryPar}{%
    \ifcommentline
        \global\let\@write@linehook=\@gobble
    \else
        \gdef\@write@linehook{\immediate\write\@auxout}%
    \fi
    \global\commentlinefalse
}

\lst@AddToHook{AfterBeginComment}{%
    \global\commentlinetrue
    \@write@linehook{%
        \noexpand\expandafter\gdef
        \noexpand\csname linehook@hook@\number\value{linehook}\noexpand\endcsname
        {\noexpand\breakpoint}%
    }%
}

\newcommand\breakpoint{\vadjust{\hrule \penalty-2000 \vfill}}
\makeatother

\begin{filecontents*}{\jobname.txt}
% blahblah comment (three-line comment, should not be breakable)
% blahblah comment
% blahblah comment 
blahblah 
blahblah
blahblah
blahblah
% blahblah comment
blahblah
blahblah
blahblah
% blahblah comment
blahblah
% blahblah comment
blahblah 
blahblah
blahblah 
blahblah
% blahblah comment (two-line comment, should not be breakable)
% blahblah comment
blahblah 
blahblah 
blahblah
% blahblah comment
blahblah
blahblah 
blahblah
blahblah
% blahblah comment (two-line comment, should not be breakable)
% blahblah comment (but it is :))
blahblah 
blahblah 
blahblah
% blahblah comment
blahblah 
blahblah
% blahblah comment
blahblah 
blahblah 
blahblah 
blahblah
% blahblah comment
blahblah 
blahblah
\end{filecontents*}

\begin{document}
\lipsum[1]
\lipsum[1]
\code{\jobname.txt}
\end{document}

在此处输入图片描述 在此处输入图片描述


旧版

解决这个问题的一种方法是在代码中定义鼓励使用盒子中断的某些位置。

可以通过使用选项退出列表escapeinside并定义\vadjust将在下一个行尾(内部为段落分隔符)后插入的材料来完成此操作。此处插入的材料是定义“良好”断点的负惩罚,后跟\vfill填充剩余框直至最终目标高度的。

相关代码更改如下

listing options={..., escapeinside={<@}{@>}},

\newcommand\breakpoint{\vadjust{\penalty-2000 \vfill}}

然后可以将其用作<@\breakpoint@>标记逻辑代码“块”结尾的每一行。

请注意,如果您的块太长,这仍可能会给您带来不必要的中断(尽管我还没有测试过)。在这种情况下,您可以进一步降低特定位置的惩罚,以鼓励 TeX 在该行处进一步中断,或者为您想要阻止中断的行添加正惩罚。\penality -10000将强制中断;\penalty 10000永远不会在该行中断。

完整示例代码:

\documentclass{article}
\usepackage{filecontents}
\usepackage{lipsum}

\usepackage{listings}
\usepackage[]{matlab-prettifier}
\usepackage{tcolorbox}
\tcbuselibrary{listings,xparse,breakable}

\newtcbinputlisting{\code}[2][]{%s
    listing options={style=Matlab-editor,tabsize=2,basicstyle=\footnotesize\ttfamily,
        escapeinside={<@}{@>}},
    listing inputencoding=utf8,
    listing file={#2},
    title=\texttt{#2},
    colback=blue!5!white,colframe=blue,fonttitle=\bfseries,
    before={\vspace{1mm}},
    top=-1pt,
    bottom=-1pt,
    listing only,breakable,#1}

\newcommand\breakpoint{\vadjust{\penalty-2000 \vfill}}

\begin{filecontents*}{code.txt}
% blahblah comment
blahblah 
blahblah
blahblah
blahblah<@\breakpoint@> 
% blahblah comment
blahblah
blahblah
blahblah<@\breakpoint@> 
% blahblah comment
blahblah<@\breakpoint@> 
% blahblah comment
blahblah 
blahblah
blahblah 
blahblah<@\breakpoint@> 
% blahblah comment
blahblah 
blahblah 
blahblah<@\breakpoint@> 
% blahblah comment
blahblah 
blahblah<@\breakpoint@> 
% blahblah comment
blahblah 
blahblah 
blahblah<@\breakpoint@> 
% blahblah comment
blahblah 
blahblah<@\breakpoint@> 
% blahblah comment
blahblah 
blahblah 
blahblah 
blahblah<@\breakpoint@> 
% blahblah comment
blahblah 
blahblah<@\breakpoint@> 
\end{filecontents*}


\begin{document}
\lipsum[1]
\lipsum[1]
\code{code.txt}

\end{document}

在此处输入图片描述 在此处输入图片描述

相关内容