我想知道是否有一些宏可以轻松注释下面的列表。它们主要用于 Manning 的书籍中。
解决方案
对于任何感兴趣的人,这是我最终得到的宏:
\newcommand*{\AddNote}[5]{%
\begin{tikzpicture}[overlay, remember picture]
\coordinate (x) at (#2,0);
\coordinate (a) at ($(x)!(#1.north)!($(x)+(0,1)$)$);
\coordinate (b) at ($(a)+(0.8,0)$);
\coordinate (c) at ($(b)+(0,#3)$);
\draw [open triangle 45-] (a) -- (b) -- (c);
\node[#5] at (c) {\bf\sffamily\smaller#4};
\end{tikzpicture}%
}
用法是:\AddNote{linemarker}{x-offset}{y-offset}{text}{style}
答案1
编辑2018-05-13:这是tikzmark
所有优秀的 TeX 发行版都提供该软件包。请参阅tikzmark
文档以获取最新说明。
如果没有 MWE,很难确切地知道你在追求什么,但我思考扩展listings
可以tikzmark
处理这类事情。你tikzmark.dtx
需要TeX-SX 启动板,运行tex tikzmark.dtx
并将生成的文件放在tex
可以找到它们的地方(如果您运行latex tikzmark.dtx
或者pdflatex tikzmark.dtx
它会抱怨文件丢失 - 请忽略它)。
然后是以下代码:
\documentclass{article}
%\url{http://tex.stackexchange.com/q/86309/86}
\usepackage{listings}
\usepackage{tikz}
\usetikzlibrary{arrows,tikzmark,shadows}
\usetikzmarkextra{listings}
\tikzset{
comment/.style={
draw,
fill=blue!70,
text=white,
rounded corners,
drop shadow,
align=left,
},
}
\begin{document}
\begin{lstlisting}[language=TeX,name=texcode,numbers=left,breakatwhitespace=true,breaklines=true]
\newcommand\balloon[4]{%
\pgfmathtruncatemacro\firstline{%
#3-1
}%
\iftikzmark{line-#2-\firstline-start}{%
\iftikzmark{line-#2-#3-first}{%
\xdef\blines{({pic cs:line-#2-\firstline-start} -| {pic cs:line-#2-#3-first})}%
}{%
\iftikzmark{line-#2-#3-start}{%
\xdef\blines{({pic cs:line-#2-\firstline-start} -| {pic cs:line-#2-#3-start})}%
}{%
\xdef\blines{(pic cs:line-#2-\firstline-start)}%
}%
}%
}{%
\xdef\blines{}%
}%
\foreach \k in {#3,...,#4} {%
\iftikzmark{line-#2-\k-first}{%
\xdef\blines{\blines (pic cs:line-#2-\k-first) }
}{}
\iftikzmark{line-#2-\k-end}{%
\xdef\blines{\blines (pic cs:line-#2-\k-end) }
}{}
}%
\ifx\blines\empty
\else
\edef\temp{\noexpand\tikz[remember picture,overlay] \noexpand\node[fit={\blines},balloon] (#1) {};}%
\temp
\fi
}
\end{lstlisting}
\begin{tikzpicture}[remember picture,overlay,>=stealth']
\draw[<-,ultra thick] (pic cs:line-texcode-1-end) +(1em,.7ex) -| +(2.5,1) node[above,comment,thin] {Command name};
\draw[<-,ultra thick] (pic cs:line-texcode-3-end) ++(1em,.7ex) -| +(5.8,1) node[above right,comment,thin] {Find previous line};
\draw[<-,ultra thick] (pic cs:line-texcode-5-end) ++(1em,.7ex) -| +(2.2,.5) node[above,comment,thin] {If previous line exists, add to the list};
\draw[<-,ultra thick] (pic cs:line-texcode-18-end) ++(1em,.7ex) -| +(2.2,.5) node[above,comment,thin] {Loop through rest of lines};
\draw[<-,ultra thick] (pic cs:line-texcode-28-end) ++(1em,.7ex) -| +(1,1.5) node[above,comment,thin] {Add a node covering all the lines};
\end{tikzpicture}
\end{document}
生成:
(只有它在 PDF 中看起来更美观。请注意,有一个著名的建议用它替换,\usetikzmarkextra
这\usetikzmarklibrary
将很快发生,所以如果上面的抱怨\usetikzmarkextra
尝试切换它。)
答案2
我承诺提供一个不一定很漂亮的解决方案来扩展 Andrew Stacey 的优秀答案,以支持代码跨越多页的情况:
\documentclass{article}
\usepackage{listings}
\usepackage{tikz}
\usetikzlibrary{arrows,tikzmark,shadows,calc}
\usetikzmarklibrary{listings}
\tikzset{
comment/.style={
draw,
fill=blue!70,
text=white,
rounded corners,
drop shadow,
align=left,
},
}
\usepackage{eso-pic}
\usepackage[calc]{picture}
\newcommand{\addstufftoforegroundall}[1]{%
\AddToShipoutPictureFG{% Add <stuff> to all following pages' foreground
\put(0,\paperheight){\vtop{{\null}\makebox[0pt][l]{#1}}}%
}%
}%
\newcommand{\addstufftoforegroundthis}[1]{%
\AddToShipoutPictureFG*{% Add <stuff> to the current page foreground
\put(0,\paperheight){\vtop{{\null}\makebox[0pt][l]{#1}}}%
}%
}%
\setcounter{errorcontextlines}{\maxdimen}
\makeatletter
\def\getpicturepage#1{%
\@nameuse{save@pg@\@nameuse{save@pt@#1}}%
}
\newcommand*{\iftikzmarkcurrentpage}[3]{%
\iftikzmark{#1}{%
\ifcsname save@pg@\@nameuse{save@pt@#1}\endcsname
\expandafter\ifnum\getpicturepage{#1}=\the\c@page\relax
#2%
\else
#3%
\fi
\else
#3%
\fi
}{%
#3
}%
}
\newcommand*{\appendhookorcreatenew}[2]{%#1<- hook csname, #2<- code to add
% note: code in #2 is not expanded at this stage!
\ifcsname #1\endcsname
\expandafter\g@addto@macro\csname #1\endcsname{\unexpanded{#2}}%
\else
\@namedef{#1}{\unexpanded{#2}}%
\fi
}
\newcommand*{\deferlinecode}[3]{% #1<-listing name, #2<-line number, #3<-code
\appendhookorcreatenew{listings-deferline-#1-#2}{#3}%
}
\newcommand*{\atEOLcode}[3]{% #1<-listing name, #2<-line number, #3<-code
\appendhookorcreatenew{listings-eol-execute-#1-#2}{#3}%
}
\newcommand*{\commenton}[4][comment,thin]{% #1<- node keys, #2<- listing name, #3<- line number, #4<- comment text
\deferlinecode{#2}{#3}{\tikz[overlay,remember picture] \draw[,>=stealth',<-,ultra thick] (pic cs:line-#2-#3-end) +(1em,.7ex) -| ($(node cs:name=current page,anchor=north east)!(pic cs:line-#2-#3-end)!(node cs:name=current page,anchor=south east) +(-5cm,0cm)$) node[#1] {#4};}%
}
\lst@AddToHook{EOL}{%
\begingroup
\ifcsname listings-deferline-\lst@name-\the\c@lstnumber\endcsname
\iftikzmarkcurrentpage{line-\lst@name-\the\c@lstnumber-end}{%
\let\addstufftoforeground\addstufftoforegroundthis
}{%
\let\addstufftoforeground\addstufftoforegroundall
}%
\edef\pagehookstuff{%
\noexpand\addstufftoforeground{%
\noexpand\iftikzmarkcurrentpage{line-\lst@name-\the\c@lstnumber-end}{%
\@nameuse{listings-deferline-\lst@name-\the\c@lstnumber}%
}{}%
}%
}%
\global\expandafter\let\csname listings-deferline-\lst@name-\the\c@lstnumber\endcsname\@undefined
\pagehookstuff
\fi
\ifcsname listings-eol-execute-\lst@name-\the\c@lstnumber\endcsname
\@nameuse{listings-eol-execute-\lst@name-\the\c@lstnumber}%
\fi
\endgroup
}
\commenton[comment,thin,text width=5cm]{texcode}{3}{This line sets the title of the document but does not result in any visible output, yet.}
\atEOLcode{texcode}{3}{\vspace{5ex}}
\commenton[comment,thin]{texcode}{4}{Similarly for author.}
\atEOLcode{texcode}{4}{\vspace{2ex}}
\commenton[comment,thin]{texcode}{5}{And date.}
\commenton[comment,thin,fill=red!70,text=black]{texcode}{7}{This line actually shows them.}
\lstset{
language={[LaTeX]TeX},
numbers=left,
breaklines=true,
basicstyle=\small\ttfamily,
columns=flexible,
}
\usepackage[a5paper,landscape]{geometry}
\usepackage{url}
\begin{document}
\title{Example output of code annotation}
\author{\url{http://tex.stackexchange.com/questions/86309/macros-for-code-annotations}}
\date{16 December 2012}
\maketitle
This is only to illustrate how one might go about adding annotations to code that spans across more than one page.
The annotations to be added have to specified before the listing appears.
It is not very robust to lost notes, eg if attempting to add an annotation to a blank line where there may not be an end marker.
\begin{lstlisting}[name=texcode]
\documentclass{article}
\usepackage{listings}
\title{Sample Document}
\author{John Smith}
\date{\today}
\begin{document}
\maketitle
Hello World!
% This is a comment.
\end{document}
\end{lstlisting}
\end{document}
(示例列表借自其他地方。)
如果相邻的注释节点可能重叠,我建议在注释行下方添加一些额外的空间。很明显,我在 TikZ 中定位时遇到了很多困难。
答案3
根据以前的答案,我创建了一些通用宏,它们也可以处理跨多页的列表:
\documentclass{article}
\usepackage[a5paper,landscape]{geometry}
\usepackage{url}
\usepackage{listings}
\usepackage{tikz}
\usepackage{eso-pic}
\usepackage[calc]{picture}
\usetikzlibrary{decorations.pathreplacing,arrows,tikzmark,calc}
\usetikzmarklibrary{listings}
\newcommand{\addstufftoforegroundall}[1]{%
\AddToShipoutPictureFG{% Add <stuff> to all following pages' foreground
\put(0,\paperheight){\vtop{{\null}\makebox[0pt][l]{#1}}}%
}%
}%
\newcommand{\addstufftoforegroundthis}[1]{%
\AddToShipoutPictureFG*{% Add <stuff> to the current page foreground
\put(0,\paperheight){\vtop{{\null}\makebox[0pt][l]{#1}}}%
}%
}%
\makeatletter
\def\getpicturepage#1{%
\@nameuse{save@pg@\@nameuse{save@pt@#1}}%
}
\lst@AddToHook{EOL}{%
\begingroup
\ifcsname listings-deferline-\lst@name-\the\c@lstnumber\endcsname
\iftikzmarkcurrentpage{line-\lst@name-\the\c@lstnumber-end}{%
\let\addstufftoforeground\addstufftoforegroundthis
}{%
\let\addstufftoforeground\addstufftoforegroundall
}%
\edef\pagehookstuff{%
\noexpand\addstufftoforeground{%
\noexpand\iftikzmarkcurrentpage{line-\lst@name-\the\c@lstnumber-end}{%
\@nameuse{listings-deferline-\lst@name-\the\c@lstnumber}%
}{}%
}%
}%
\global\expandafter\let\csname listings-deferline-\lst@name-\the\c@lstnumber\endcsname\@undefined
\pagehookstuff
\fi
\ifcsname listings-eol-execute-\lst@name-\the\c@lstnumber\endcsname
\@nameuse{listings-eol-execute-\lst@name-\the\c@lstnumber}%
\fi
\endgroup
}
\newcommand*{\iftikzmarkcurrentpage}[3]{%
\iftikzmark{#1}{%
\ifcsname save@pg@\@nameuse{save@pt@#1}\endcsname
\expandafter\ifnum\getpicturepage{#1}=\the\c@page\relax
#2%
\else
#3%
\fi
\else
#3%
\fi
}{%
#3
}%
}
\newcommand*{\appendhookorcreatenew}[2]{%#1<- hook csname, #2<- code to add
% note: code in #2 is not expanded at this stage!
\ifcsname #1\endcsname
\expandafter\g@addto@macro\csname #1\endcsname{\unexpanded{#2}}%
\else
\@namedef{#1}{\unexpanded{#2}}%
\fi
}
\newcommand*{\deferlinecode}[3]{% #1<-listing name, #2<-line number, #3<-code
\appendhookorcreatenew{listings-deferline-#1-#2}{#3}%
}
\colorlet{annotationColor}{red}
\newlength\annotationLinewidth
\setlength\annotationLinewidth{0.6pt}
\tikzset{%
note/.style={annotationColor,
font=\sffamily\scriptsize,%\bfseries
align=center,
text width=2.5cm
},
every round node/.style={
draw,
shape=rounded rectangle,
rounded rectangle arc length=180,
inner sep=+.333em,
text depth=+.1ex}
}
% utility function
\makeatletter
\def\ifPositive#1{%
\@ifnextchar{-}%
{\expandafter\@secondoftwo\remove@to@nnil}%
{\expandafter\@firstoftwo\remove@to@nnil}%
#1\@nnil
}
\makeatother
%\AddNoteVBrace{listing}{line-start}{line-end}{x-pos}{text}
\newcommand*{\AddNoteVBrace}[5]{%
\deferlinecode{#1}{#2}{\tikz[overlay, remember picture]
\draw [decoration={brace,amplitude=0.5em},decorate,annotationColor,line width=\annotationLinewidth]
($([xshift=#4]pic cs:line-#1-#2-start)!([yshift=1ex]pic cs:line-#1-#2-start)!($([xshift=#4]pic cs:line-#1-#2-start)-(0,1)$)$) --
($([xshift=#4]pic cs:line-#1-#3-start)!([yshift=-.25ex]pic cs:line-#1-#3-start)!($([xshift=#4]pic cs:line-#1-#3-start)-(0,1)$)$)
node [note, pos=0.5,anchor=west] {#5};}%
}%
%\AddNoteVLine{listing}{line-start}{line-end}{x-pos}{text}
\newcommand*{\AddNoteVLine}[5]{%
\deferlinecode{#1}{#2}{\tikz[overlay, remember picture]
\draw [annotationColor,line width=\annotationLinewidth]
($([xshift=#4]pic cs:line-#1-#2-start)!([yshift=1ex]pic cs:line-#1-#2-start)!($([xshift=#4]pic cs:line-#1-#2-start)-(0,1)$)$) --
($([xshift=#4]pic cs:line-#1-#3-start)!([yshift=-.25ex]pic cs:line-#1-#3-start)!($([xshift=#4]pic cs:line-#1-#2-start)-(0,1)$)$)
node [note, align=left, pos=0.5, anchor=west] {#5};}%
}%
%\AddNoteRight{listing}{line}{label-x-offset}{label-y-offset}{text}
\newcommand*{\AddNoteRight}[5]{%
\deferlinecode{#1}{#2}{\tikz[overlay, remember picture]
\def\anchor{south}\ifx&\else \ifPositive{#4}{}{\def\anchor{north}}\fi
\draw[<-,>=stealth,annotationColor,line width=\annotationLinewidth] (pic cs:line-#1-#2-end) +(0.25,.5ex) -| +(\ifx&1\else#3\fi,\ifx&2.5ex\else#4\fi) node[note,anchor=\anchor] {#5};}%
}%
%\AddNoteTop{listing}{line}{x-pos}{label-x-offset}{label-y-offset}{text}
\newcommand*{\AddNoteTop}[6]{%
\deferlinecode{#1}{#2}{\tikz[overlay, remember picture]
\def\anchor{west}\def\align{left}
\ifx&\else \ifPositive{#4}{}{\def\anchor{east}\def\align{right}}\fi
\draw[<-,>=stealth,annotationColor,line width=\annotationLinewidth] ($([xshift=#3]pic cs:line-#1-#2-start) +(0,2.5ex)$) |- +(\ifx&1\else#4\fi,\ifx&2.5ex\else#5\fi)
node[note,align=\align,anchor=\anchor] {#6};}%
}%
%\AddNoteBottom{listing}{line}{x-pos}{label-x-offset}{label-y-offset}{text}
\newcommand*{\AddNoteBottom}[6]{%
\deferlinecode{#1}{#2}{\tikz[overlay, remember picture]
\def\anchor{west}\def\align{left}
\ifx&\else \ifPositive{#4}{}{\def\anchor{east}\def\align{right}}\fi
\draw[<-,>=stealth,annotationColor,line width=\annotationLinewidth] ($([xshift=#3]pic cs:line-#1-#2-start) +(0,-1.5ex)$) |- +(\ifx&1\else#4\fi,\ifx&-2.5ex\else-#5\fi)
node[note,align=\align,anchor=\anchor] {#6};}%
}%
%\AddBox{listing}{line}{x-start}{x-end}
\newcommand*{\AddBox}[4]{%
\deferlinecode{#1}{#2}{\tikz[overlay, remember picture]
\draw[annotationColor,line width=\annotationLinewidth] ($([xshift=#3]pic cs:line-#1-#2-start) +(-.25ex,-.25ex)$) rectangle ($([xshift=#4]pic cs:line-#1-#2-start) +(.5ex,2.0ex)$);}%
}%
%\AddUnderline{listing}{line}{x-start}{x-end}
\newcommand*{\AddUnderline}[4]{%
\deferlinecode{#1}{#2}{\tikz[overlay, remember picture]
\draw[annotationColor,line width=\annotationLinewidth] ($([xshift=#3]pic cs:line-#1-#2-start) +(0,-.25ex)$) -- ($([xshift=#4]pic cs:line-#1-#2-start) +(0,-.25ex)$);}%
}%
\AddNoteVBrace{texcode}{1}{3}{4cm}{Note VBrace}
\AddNoteVLine{texcode}{5}{7}{4cm}{Note VLine}
\AddUnderline{texcode}{9}{0cm}{3cm}
\AddBox{texcode}{11}{1cm}{3cm}
\AddNoteTop{texcode}{15}{0cm}{5cm}{}{Note Top}
\AddNoteRight{texcode}{15}{3cm}{-2cm}{Note Right}
\AddNoteBottom{texcode}{15}{1cm}{}{}{Note Bottom}
\lstset{
language={[LaTeX]TeX},
numbers=left,
breaklines=true,
basicstyle=\small\ttfamily,
columns=flexible,
}
\begin{document}
\title{Example output of code annotation}
\author{\url{http://tex.stackexchange.com/questions/86309/macros-for-code-annotations}}
\maketitle
This is only to illustrate how one might go about adding annotations to code that spans across more than one page.
The annotations to be added have to specified before the listing appears.
It is not very robust to lost notes, eg if attempting to add an annotation to a blank line where there may not be an end marker.
\begin{lstlisting}[name=texcode]
\documentclass{article}
\usepackage{listings}
\title{Sample Document}
\author{John Smith}
\date{\today}
\begin{document}
\maketitle
Hello World!
% This is a comment.
\end{document}
\end{lstlisting}
\end{document}