1

I'd like to annotate code listing something like this:

enter image description here

Below is what I've attempted so far. I am a little worried about having to use raisebox so that the arrow tip doesn't point to the base of the line. How can I avoid this hardcoding? I would also appreciate any advice on improvements so that it's easier to handle a few varieties of annotations as seen in the screenshot.

It would be nice, to get the desired results, if I can just specify the following:

  • the distance between the end of source line and the arrow tip

  • the length of the horizontal line and the length of the vertical line of the arrow (and whether the vertical line goes upwards or downwards)

  • and the position of the text node (whether right or left of the vertical line and vertical alignment of text node with the top or bottom.

\documentclass[twoside, openright, 10pt]{book}
\usepackage{listings}
\usepackage{tikz}
\usetikzlibrary{arrows.meta, tikzmark}

\begin{document}
  
\chapter{TiKz}

\begin{lstlisting}[caption={[MeetKarel]{MeetKarel}}, label={lst:meet_karel}, escapechar=ß]
import stanford.karel.Karel;
public class MeetKarel extends Karel { 
        public void run() {
                move();
                move();
                move();
                ß\tikzmark{b}ßpickBeeper();ß\raisebox{2.6pt}{\tikzmark{a}}ß                             
                turnLeft();
                move();
                move();
                turnLeft();
                turnLeft();
                turnLeft();ß\raisebox{2.6pt}{\tikzmark{c}}ß 
                move();
                putBeeper();
                move();
        }        
}
\end{lstlisting}
\begin{tikzpicture}[remember picture,overlay]
\draw([shift={(12ex,10ex)}] pic cs:a) ++ (0.05, 0) % shift just a little to the right
  % set inner and outer to 0 so that text is alighed with the beginning of the vertical line
  node[inner sep=0,outer sep=0, fill=yellow!80!black,text width=5cm, anchor= north west] 
  {Shifted a little above so that the arrow head points to the middle of the line.};
\draw[thin, gray, arrows = {-Stealth[inset=0pt, angle=45:7pt]}]
  ([shift={(12ex,10ex)}] pic cs:a) -- 
  ([shift={(12ex,0ex)}] pic cs:a) -- 
  ([shift={(0ex,0ex)}] pic cs:a);
\draw([shift={(12ex,-10ex)}] pic cs:c) ++ (0.05, -0.05) % shift just a little to the right and down
  % set inner and outer to 0 so that text is alighed with the beginning of the vertical line
  node[inner sep=0,outer sep=0, fill=yellow!80!black,text width=5cm, anchor= south west] 
  {I want to align base line of this text to the `c` node nicely. };
\draw[thin, gray, arrows = {-Stealth[inset=0pt, angle=45:7pt]}]
  ([shift={(12ex,-10ex)}] pic cs:c) -- 
  ([shift={(12ex,0ex)}] pic cs:c) -- 
  ([shift={(0ex,0ex)}] pic cs:c);
\end{tikzpicture}
\end{document}

Result: enter image description here

1 Answer 1

2

Here's a slightly simplified version of your code.

  1. By using the tikzmark special listings library then you don't need to specify tikzmarks in your code, they are all provided for you.
  2. The arrows start at (0.5,.5ex) away from the corresponding tikzmarks. The .5ex is half the height of an x, and looks a good vertical height to me.
  3. The lines and nodes are drawn together and the -| path construction is used to make the line bend nicely.
  4. A little bit is added to the outer xsep of the label nodes to move them away from the joining line. Other ways of achieving the same effect do seem to cause issues with the arrow placement.

All of this contrives to make the code much shorter. Various numbers could be hard-coded into a style to make it even more simple and a bit more flexible.

\documentclass[twoside, openright, 10pt]{book}
%\url{https://tex.stackexchange.com/q/657375/86}
\usepackage{listings}
\usepackage{tikz}
\usetikzlibrary{
  arrows.meta,
  tikzmark,
  calc,
  positioning
}
\usetikzmarklibrary{listings}


\begin{document}
  
\chapter{Ti\emph{k}Z}

\begin{lstlisting}[
  caption={[MeetKarel]{MeetKarel}},
  label={lst:meet_karel},
  name=karel,
  escapechar=ß
]
import stanford.karel.Karel;
public class MeetKarel extends Karel { 
        public void run() {
                move();
                move();
                move();
                pickBeeper();
                turnLeft();
                move();
                move();
                turnLeft();
                turnLeft();
                turnLeft();
                move();
                putBeeper();
                move();
        }        
}
\end{lstlisting}


\begin{tikzpicture}[
  remember picture,
  overlay,
  annotation/.style={
    inner sep=0pt,
    outer sep=0pt,
    outer xsep=1mm,
    fill=yellow!80!black,
    text width=5cm
  },
  >={Stealth[inset=0pt, angle=45:7pt]}
]
\draw[<-] (pic cs:line-karel-7-end)  ++(.5,.5ex) -| ++(2,1.5)
node[annotation, below right]
{Shifted a little above so that the arrow head points to the middle of the line.};

\draw[<-] (pic cs:line-karel-13-end) ++(0.5,.5ex) -| ++(2,-1)
node[annotation, above right]
{I want to align base line of this text to the `c` node nicely. };
\end{tikzpicture}
\end{document}

Annotated code with tikzmark

3
  • @ Andrew Stacey It becomes much simpler and easier to understand. Thanks!
    – Pyi Soe
    Commented Sep 17, 2022 at 3:01
  • @PyiSoe I'm glad it's helpful. I'm well aware that it isn't a polished version of what you are aiming for, but perhaps it can help you towards that. Commented Sep 17, 2022 at 7:08
  • Yes, absolutely.
    – Pyi Soe
    Commented Sep 17, 2022 at 9:30

You must log in to answer this question.

Not the answer you're looking for? Browse other questions tagged .