我使用 algpseudocode 包来编写代码,我想在代码的某些行周围画出方框。这个答案非常接近我想要的。这个想法是用 标记代码的第一行和最后一行\tikzmark
,然后在获得的坐标周围画一个框。这种方法的问题在于它只适用于以 开头的行\State
。例如,当我\tikzmark
在 if 语句后使用 时,它不会标记行的开头,而是标记更靠右的某个位置。
以下是一个例子:
\documentclass{article}
\usepackage{algpseudocode}
\usepackage{tikz}
\usetikzlibrary{fit,tikzmark}
\newcommand\drawCodeBox[2]{%
\begin{tikzpicture}[remember picture,overlay]
\coordinate (start) at ([yshift=1.7ex]pic cs:#1);
\coordinate (end) at ([yshift=-0.3ex]pic cs:#2);
\node[inner sep=2pt,draw=red,fit=(start) (end)] {};
\end{tikzpicture}%
}
\begin{document}
\begin{algorithmic}
\State \tikzmark{s1}%
$x \gets 0$\tikzmark{e1}
\drawCodeBox{s1}{e1}
\State $y \gets 1$
\If{$x = y$}\tikzmark{s2}
\State PANIC
\EndIf\tikzmark{e2}
\drawCodeBox{s2}{e2}
\end{algorithmic}
\end{document}
这是我得到的:
期望的结果是:
答案1
要坚持你的\drawCodeBox
宏观,你必须位置s\tikzmark
位于适当的位置。我们可以将它放在 a 内,然后将其(水平)明确堆叠,使其与设置 ( ) 的元素完全一致\tikzmark{e2}
,从而向右移动。\makebox[0pt][r]
\EndIf
\algorithmicend\ \algorithmicif
\documentclass{article}
\usepackage{algpseudocode,tikz}
\usetikzlibrary{fit,tikzmark}
\newcommand\drawCodeBox[2]{%
\begin{tikzpicture}[remember picture,overlay]
\coordinate (start) at ([yshift=1.7ex]pic cs:#1);
\coordinate (end) at ([yshift=-0.3ex]pic cs:#2);
\node[inner sep=2pt,draw=red,fit=(start) (end)] {};
\end{tikzpicture}%
}
\begin{document}
\begin{algorithmic}
\State \tikzmark{s1}%
$x \gets 0$\tikzmark{e1}
\drawCodeBox{s1}{e1}
\State $y \gets 1$
\If{$x = y$}\tikzmark{s2}
\State PANIC
\EndIf\makebox[0pt][r]{\tikzmark{e2}\phantom{\algorithmicend\ \algorithmicif}}
\drawCodeBox{s2}{e2}
\end{algorithmic}
\end{document}
答案2
根据 Werner 对其答案的评论,我定义了\BoxedState
、、\BoxedIf
等\BoxedWhile
,它们在行首设置标记并绘制一个框。可选参数设置框的样式。该命令\EndBox
设置框的下端,\SetBoxEast
可用于在框中最长的线的末尾设置右端。如果最后一行是最长的,\SetBoxEast
则可以省略。
计数器tmkcount
用于保持标记的唯一性。框在其内容之前绘制,因此可以填充。限制包括:
- 该盒子不适合非常高的首行。
- 假设框内的任何线都不会比第一条线更靠左。
代码和示例:
\documentclass{article}
\usepackage{algpseudocode,tikz}
\usetikzlibrary{fit,tikzmark}
\newcounter{tmkcount}
\tikzset{%
tikzmark suffix={-\thetmkcount},%
defaultCodeBox/.style={draw=red}%
}
\newcommand{\drawCodeBox}[4]{%
\begin{tikzpicture}[remember picture,overlay]
\coordinate (start) at ([yshift=1.4ex]pic cs:#2);
\coordinate (middle) at (pic cs:#3);
\coordinate (end) at ([yshift=-0.2ex]pic cs:#4);
\node[inner sep=2pt,#1,fit=(start) (middle) (end)] {};
\end{tikzpicture}%
}
\newcommand{\BeginBox}[1]{%
\drawCodeBox{#1}{beginCB}{middleCB}{endCB}%
\tikzmark{beginCB}\tikzmark{middleCB}%
}
\newcommand{\SetBoxEast}{%
\unskip%
\tikzmark{middleCB}%
}
\newcommand{\EndBox}{%
\unskip%
\tikzmark{endCB}%
\stepcounter{tmkcount}%
}
\newcommand{\BoxedState}[1][defaultCodeBox]{\State\BeginBox{#1}\ignorespaces}
\algdef{S}[WHILE]{BoxedWhile}[2][defaultCodeBox]{\BeginBox{#1}\algorithmicwhile\ #2\ \algorithmicdo}%
\algdef{S}[FOR]{BoxedFor}[2][defaultCodeBox]{\BeginBox{#1}\algorithmicfor\ #2\ \algorithmicdo}%
\algdef{S}[FOR]{BoxedForAll}[2][defaultCodeBox]{\BeginBox{#1}\algorithmicforall\ #2\ \algorithmicdo}%
\algdef{S}[LOOP]{BoxedLoop}[1][defaultCodeBox]{\BeginBox{#1}\algorithmicloop}%
\algdef{S}[REPEAT]{BoxedRepeat}[1][defaultCodeBox]{\BeginBox{#1}\algorithmicrepeat}%
\algdef{S}[IF]{BoxedIf}[2][defaultCodeBox]{\BeginBox{#1}\algorithmicif\ #2\ \algorithmicthen}%
\algdef{C}[IF]{IF}{BoxedElsIf}[2][defaultCodeBox]{\BeginBox{#1}\algorithmicelse\ \algorithmicif\ #2\ \algorithmicthen}%
\algdef{Ce}[ELSE]{IF}{BoxedElse}{EndIf}[1][defaultCodeBox]{\BeginBox{#1}\algorithmicelse}%
\begin{document}
\begin{algorithmic}
\BoxedState $x \gets 0$
\State $y \gets x + 1$\EndBox
\If{$x < y$}
\State $x \gets x + 1$
\BoxedElse[fill=yellow]
\State do some complicated stuff\SetBoxEast
\State $y \gets y + 1$\EndBox
\EndIf
\end{algorithmic}
\end{document}