我在原始文档中创建了一个 MWE 相似物,然后“背景突出显示”(不知何故“突出显示”在逐字中意味着其他东西)一些文本来显示要关注的位置/内容。这个 MWE 相似物针对的是那些完全不熟悉 LaTeX 的人。
逐字文本是使用该listings
包生成的,突出显示是使用此处的自定义代码生成的:https://tex.stackexchange.com/a/49309、、tikz
和xcolor
。但是,此自定义代码的问题在于换行符。
原始文档中的 MWE 相似物在 100% 缩放后看起来像这样:
在截图中我突出显示了四行:
- 第 1 行:整条线路。
- 第 2 号线:部分线。
- 第 3 行:突出显示之前带有空格的代码。
- 第 4 行:代码与文本内联。
第 1 行和第 2 行没有问题。第 3 行和第 4 行有问题。
第 3 行和第 4 行太长,超出了边距。它们无法遵守边距,因为它们无法换行。第 3 行也不遵守突出显示文本之前的空白区域。
我的问题是:
- 有人能帮忙取消第 3 行突出显示的文本前的空白吗?
- 有人能帮助解决这个特定代码的长行/换行问题吗?
自定义代码编写已经很多年了,所以我想知道是否有人能够解决这个换行问题。我做不到,因为我不知道怎么做。
我选择这个自定义代码的原因是:
- 它保留了“语言突出显示”(而
\colorbox
删除了语言突出显示), - 我不需要将反斜杠字符“
\
”更改为{\char92}
,并且 - 它使用起来更简洁,因为定制是在序言中完成的(这比在逐字文本本身中进行定制更简洁)。
我真的希望这个自定义代码能够工作,因为它真的很好。有人能帮忙改进代码吗?
这是 MWE:
\documentclass{scrbook}
\usepackage{tikz}
\usepackage{listings}
\lstset{%
language=[LaTeX]TeX,
commentstyle=\color{gray},
breaklines=true,
frame=single,
frameround=tttt,%
framextopmargin=10pt,
framexbottommargin=7pt,
xleftmargin=22pt,
framexleftmargin=10pt,
framesep=10pt
}
% - - - - - - - - - - - - - % Code for highlighting.
\makeatletter%
\newenvironment{btHighlight}[1][]%
{\begingroup\tikzset{bt@Highlight@par/.style={#1}}\begin{lrbox}{\@tempboxa}}%
{\end{lrbox}\bt@HL@box[bt@Highlight@par]{\@tempboxa}\endgroup}
\newcommand\btHL[1][]{%
\begin{btHighlight}[#1]\bgroup\aftergroup\bt@HL@endenv%
}
\def\bt@HL@endenv{%
\end{btHighlight}%
\egroup%
}
\newcommand{\bt@HL@box}[2][]{%
\tikz[#1]{%
\pgfpathrectangle{\pgfpoint{1pt}{0pt}}{\pgfpoint{\wd #2}{\ht #2}}%
\pgfusepath{use as bounding box}%
\node[anchor=base west, fill={oposmy!30},outer sep=0pt,inner xsep=1pt, inner ysep=0pt, minimum height=\ht\strutbox+1pt,#1]{\raisebox{1pt}{\strut}\strut\usebox{#2}};%
}%
}%
\makeatother%
\lstdefinestyle{SQL}{%
moredelim=**[is][\btHL]{`}{`},%
moredelim=**[is][{\btHL[fill=green!30,draw=red,dashed,thin]}]{@}{@}%
}
% - - - - - - - - - - - - - %
\usepackage{xcolor}
\definecolor{oposmy}{HTML}{c0f1ff}
\begin{document}
\begin{lstlisting}[style=SQL]
\documentclass{scrbook}
`\usepackage{fontspec`}% #1
\setmainfont{`Latin Modern Sans Demi Cond`}% #2
\usepackage{tikz}
\usetikzlibrary{shapes.misc}
\begin{document}
\begin{tikzpicture}
`\node[draw,rounded rectangle, rounded rectangle arc length=180] {Text};`% #3
\end{tikzpicture}
Press the `F1` key for help. Press the `\tikz[baseline] \draw node[anchor=base,draw,double,rounded corners] {{\texttt{F11}}};` key for full screen.% #4
\end{document}
\end{lstlisting}
\end{document}
答案1
listings
通过设置选项,可以很容易地解决多余空格被突出显示的问题keepspaces=true
。文档中提到了此选项
keepspaces=true
告诉包不要删除空格来修复列对齐,并始终将制表符转换为空格。
使用此选项时,当列表中有换行符时,缩进有时会发生一些变化,但结果几乎相同。因此,我认为对所有列表使用此选项并不是什么大问题。
拆分突出显示的行比较棘手。listings
带有几个钩子,您可以使用它们在输入行、空行等的开头或结尾插入额外的代码。不幸的是,似乎没有一种方法可以可靠地检测每个输出行的开头和结尾,同时考虑到换行符。这是因为内部使用了标准的 TeX 机制来与换行符结合\discretionary
使用。当 TeX 执行文本时全部自由选择的换行符,我们也不能使用它们让 TeX 找出最终选择的换行符来找到行的末尾和下一行的开头。
因此,这里选择的方法是打破每一个 \discretionary
然后重新开始。这样会形成很多小的高光部分,粘合在一起形成一个明显连续的部分。
为了实现,我必须切换到突出显示方法使用 TikZ 的remember picture
功能因为它不需要在突出显示的部分周围包裹一个框,这导致我的方法无法正确获得正确平衡的括号。只要您只需要简单的背景颜色,这种方法就可以了。如果您需要绘制框或类似的东西,那么实现将变得更加困难。
扩展是一个\hlstyle
宏\hlstyleend
,它只设置全局\hl@active
开关来确定突出显示是否处于活动状态,以及实际的自由中断重新定义。它不需要重新定义\discrtionary
自身,因为listings
它有自己的版本\lst@discretionary
,可以为此目的劫持。
编辑:我注意到,如果突出显示不仅跨越两行,而且跨越两页,那么早期的解决方案就会失效。修改后的版本通过定义命令来解决这个问题\hl@p@XXX
,其中XXX
是当前突出显示标记号,用于突出显示的每个开始。这些命令中的每一个都映射到标记所在的相应页码。由于在下一次编译运行中需要页码信息,因此.aux
当该页面的 shipout 发生并且确切的页码已知时,所有命令都会写入文件,并在下一次运行中用于确定哪些突出显示段应该只打印在当前页面上。
完整示例代码如下:
\documentclass{scrbook}
\usepackage{atbegshi,ifthen,listings,tikz}
\usepackage{xcolor}
\definecolor{oposmy}{HTML}{C0F1FF}
\tikzstyle{highlighter} = [ oposmy, line width = \baselineskip ]
\makeatletter
\newcounter{highlight}
\newif\ifhl@active
\newcommand{\tikzhighlightanchor}[1]{%
\ensuremath{\vcenter{\hbox{%
\tikz[remember picture, overlay]{\coordinate (#1 highlight \arabic{highlight});}%
}}}%
}
\newcommand{\bh}{%
\stepcounter{highlight}%
\edef\hl@temp{%
\unexpanded{\noexpand\expandafter\gdef\noexpand\csname}%
hl@p@\arabic{highlight}%
\unexpanded{\noexpand\endcsname{\arabic{page}}}
}%
\expandafter\write\expandafter\@auxout\expandafter{\hl@temp}%
\tikzhighlightanchor{begin}%
}
\newcommand{\eh}{%
\tikzhighlightanchor{end}%
}
\newcommand{\hlstyle}{%
\global\hl@activetrue
\bh
\aftergroup\hlstyleend
}
\newcommand{\hlstyleend}{%
\eh
\global\hl@activefalse
}
\AtBeginShipout{\AtBeginShipoutUpperLeft{%
\ifthenelse{\value{highlight} > 0}{%
\tikz[remember picture, overlay]{\foreach \stroke in {1,...,\arabic{highlight}} {%
\ifcsname hl@p@\stroke\endcsname
\ifthenelse{\csname hl@p@\stroke\endcsname=\arabic{page}}{\draw[highlighter]
(begin highlight \stroke) -- (end highlight \stroke);}{}%
\fi
}}%
}{}%
}}
\usepackage{listings}
\lstset{%
language=[LaTeX]TeX,
commentstyle=\color{gray},
breaklines=true,
frame=single,
frameround=tttt,%
framextopmargin=10pt,
framexbottommargin=7pt,
xleftmargin=22pt,
framexleftmargin=10pt,
framesep=10pt,
keepspaces=true
}
\lstdefinestyle{SQL}{%
moredelim=**[is][\hlstyle]{`}{`},%
% moredelim=**[is][{\btHL[fill=green!30,draw=red,dashed,thin]}]{@}{@}%
}
\let\orig@lst@discretionary=\lst@discretionary
\gdef\lst@discretionary{%
\ifhl@active \eh \fi
\orig@lst@discretionary
\ifhl@active \bh \fi
}
\makeatother
\begin{document}
\vspace*{12cm}
\begin{lstlisting}[style=SQL]
\documentclass{scrbook}
`\usepackage{fontspec`}% #1
\setmainfont{`Latin Modern Sans Demi Cond`}% #2
\usepackage{tikz}
\usetikzlibrary{shapes.misc}
\begin{document}
\begin{tikzpicture}
`\node[draw,rounded rectangle, rounded rectangle arc length=180] {Text};`% #3
\end{tikzpicture}
Press the `F1` key for help. Press the `\tikz[baseline] \draw node[anchor=base,draw,double,rounded corners] {{\texttt{F11}}};` key for full screen.% #4
Press the `F1` key for help.
\end{document}
\end{lstlisting}
\end{document}
这种方法的一个缺点是必须处理大量 TikZ 标记,当使用许多/较长的高亮时,这会显著减慢编译速度。另请注意,您必须编译两次才能获得正确的高亮位置。