7

I would like to highlight a function in minted by giving it a background color in order to make the connection to a visualization better visible.

I used to put the functions to be highlighted in a separate minted environment and specify a background color for the entire environment (in minted v1.7):

% minted 2011/09/17 v1.7

\documentclass{article}

\usepackage{minted}
\newminted{c}{linenos, tabsize=4, gobble=2}

\makeatletter
\newenvironment{mintedBlock}{%
    % I can not use frame=topline|bottomline because that is inside of colored box
    % copied from /usr/share/texlive/texmf-dist/tex/latex/fancyvrb/fancyvrb.sty line 918 in \def\FV@SingleFrameLine
    \noindent
    \FancyVerbRuleColor{\vrule \@width\linewidth \@height\FV@FrameRule}%
}{%
    \FancyVerbRuleColor{\vrule \@width\linewidth \@height\FV@FrameRule}%
}
\makeatother

\newcommand{\colorMain}{green!10}%
\newcommand{\colorSum}{yellow!20}%

\begin{document}

\begin{mintedBlock}
    % I am using old package versions which are buggy, so firstnumber=last gives wrong numbering.
    \begin{ccode*}{frame=none, bgcolor=white}
        #include <stdio.h>
    \end{ccode*}
    \begin{ccode*}{frame=none, firstnumber=2, bgcolor=white}
    \end{ccode*}
    \begin{ccode*}{frame=none, firstnumber=3, bgcolor=\colorSum}
        int sum(int summand1, int summand2)
        {
            return summand1 + summand2;
        }
    \end{ccode*}
    \begin{ccode*}{frame=none, firstnumber=7, bgcolor=white}
    \end{ccode*}
    \begin{ccode*}{frame=none, firstnumber=8, bgcolor=\colorMain}
        int main()
        {
            int summand1 = 1;
            int summand2 = 2;
            printf("%d + %d = %d\n", summand1, summand2, 
                sum(summand1, summand2));
        }
    \end{ccode*}
\end{mintedBlock}

\end{document}

with minted v1.7 using separate environments

The spacing of the rules was not perfect but apart from that it was good enough in minted 1.7. However I have recently upgraded to the newest version (minted 2.4.2dev) and now there are big spaces between the seperate minted environments.

I have tried to remove them with

\renewcommand{\vspace}{\@ifnextchar*\@gobbletwo\@gobble}%

which makes it a lot better but still leaves too big spaces.


Therefore I have tried it with a new approach: using escapeinside to insert tikz nodes with remember picture and drawing a rectangle around them later on.

% minted 2017/02/10 v2.4.2dev

\documentclass{article}

\usepackage{minted}
\newminted{c}{linenos, frame=lines, tabsize=4, gobble=1}

\usepackage{tikz}
\newcommand{\tikzmark}[1]{\tikz[overlay,remember picture,baseline=(#1.base)] \node (#1) {\vphantom{I}};}

\newcommand{\colorMain}{green!10}%
\newcommand{\colorSum}{yellow!20}%

\begin{document}
\begin{ccode*}{breaklines, escapeinside=~~}
    #include <stdio.h>

    ~\tikzmark{sumtop}~int sum(int summand1, int summand2)
    {
        return summand1 + summand2;
    ~\tikzmark{sumbottom}~}

    ~\tikzmark{maintop}~int main()
    {
        int summand1 = 1;
        int summand2 = 2;
        printf("%d + %d = %d\n", summand1, summand2, sum(summand1, summand2));
    ~\tikzmark{mainbottom}~}
\end{ccode*}

\begin{tikzpicture}[remember picture, overlay]
    \path[overlay, fill=\colorSum] (sumtop.north) rectangle ([xshift=\linewidth]sumbottom.south);
    \path[overlay, fill=\colorMain] (maintop.north) rectangle ([xshift=\linewidth]mainbottom.south);
\end{tikzpicture}
\end{document}

with minted v2.4.2dev using escapeinside and tikz

Unfortunately the tikz picture is above the minted code.

How can I put the tikzpicture behind the text?

Also, I am not a hundred percent happy with this approach because I will run into problems if the code happens to move across a pagebreak. Does someone have a better idea?

1
  • Just a quick note: instead of \newcommand{\colorMain}{green!10}, use \colorlet{colorMain}{green!10} and then simply use the color name instead of a newcommand.
    – Clément
    Jan 4, 2021 at 21:41

3 Answers 3

5

You have to add the picture on the page background layer, for example using eso-pic. This obviously cannot break across pages.

\documentclass{article}

\usepackage{minted}
\newminted{c}{linenos, frame=lines, tabsize=4, gobble=1}

\usepackage{tikz}
\newcommand{\tikzmark}[1]{\tikz[overlay,remember picture,baseline=(#1.base)] \node (#1) {\vphantom{I}};}

\newcommand{\colorMain}{green!10}%
\newcommand{\colorSum}{yellow!20}%

\usepackage{eso-pic}

\begin{document}
\begin{ccode*}{breaklines, escapeinside=~~}
#include <stdio.h>

~\tikzmark{sumtop}~int sum(int summand1, int summand2)
{
    return summand1 + summand2;
~\tikzmark{sumbottom}~}

~\tikzmark{maintop}~int main()
{
    int summand1 = 1;
    int summand2 = 2;
    printf("%d + %d = %d\n", summand1, summand2, sum(summand1, summand2));
~\tikzmark{mainbottom}~}
\end{ccode*}

\AddToShipoutPictureBG*{%
  \begin{tikzpicture}[remember picture, overlay]
    \path[overlay, fill=\colorSum] (sumtop.north) rectangle ([xshift=\linewidth]sumbottom.south);
    \path[overlay, fill=\colorMain] (maintop.north) rectangle ([xshift=\linewidth]mainbottom.south);
  \end{tikzpicture}%
}
\end{document}

enter image description here

2

I have found a solution for the first approach, using separate minted environments, which has the advantage that it works across page breaks.

Unfortunately the distances from the code to the surrounding text do not match those of a regular minted environment. I would appreciate hints on how to fix that.

Please note that the breaklines option is important here, it somehow influences the distance between the lines.

% minted 2017/02/10 v2.4.2dev

\documentclass{article}

\usepackage{minted}

\newminted{c}{
    linenos, 
    frame=lines, 
    tabsize=4, 
    autogobble,
}

\newif\ifFirstMintedPart
\makeatletter
\newenvironment{mintedBlock}{%
    \par
    \medskip%TODO
    \begingroup
        \setlength{\parskip}{0pt}%
        \setlength{\baselineskip}{0pt}%
        \setlength{\lineskip}{0pt}%
        \let\originalVspace=\vspace
        \renewcommand{\vspace}{\@ifnextchar*\@gobbletwo\@gobble}%
        \setlength{\fboxsep}{0pt}%
        \FirstMintedParttrue
        \noindent\FancyVerbRuleColor{\vrule \@width\linewidth \@height\FV@FrameRule}%
        \originalVspace{2pt}%TODO
        \par
}{%
        \par
        \originalVspace{2pt}%TODO
        \noindent\FancyVerbRuleColor{\vrule \@width\linewidth \@height\FV@FrameRule}%
        \par
    \endgroup
    \medskip%TODO
}
\makeatother

\newenvironment{cpart}[1]{%
    \VerbatimEnvironment
    \ifFirstMintedPart
        \newcommand\currentLineNumber{firstnumber=1}%
    \else
        \newcommand\currentLineNumber{firstnumber=last}%
    \fi
    \newcommand{\beginCCode}{\begin{ccode*}}%
    \expandafter\beginCCode\expandafter{%
        \currentLineNumber, 
        frame=none, 
        bgcolor=white, 
        breaklines, % this is important!
        #1
    }%
}{%
    \end{ccode*}%
    \global\FirstMintedPartfalse
}

\newcommand{\colorMain}{green!10}%
\newcommand{\colorSum}{yellow!20}%


\begin{document}

\begin{mintedBlock}
    \begin{cpart}{}
        #include <stdio.h>
    \end{cpart}
    \begin{cpart}{}
    \end{cpart}
    \begin{cpart}{bgcolor=\colorSum}
        int sum(int summand1, int summand2)
        {
            return summand1 + summand2;
        }
    \end{cpart}
    \begin{cpart}{}
    \end{cpart}
    \begin{cpart}{bgcolor=\colorMain, escapeinside=~~}
        int main()
        {
            int summand1 = 1;
            int summand2 = 2;
            printf("%d + %d = %d\n", summand1, summand2, ~\linebreak~sum(summand1, summand2));
        }
    \end{cpart}
\end{mintedBlock}

\end{document}

screenshot

2

A couple of years later, but just wanted to add that the solution is actually quite simple (at least now).

Things are drawn in the order in which they are declared. So it is sufficient to declare the overlay first and then the minted code.

This has the advantage of not having to worry about spacing between code segments and is perhaps easier to read than the \AddToShipoutPictureBG method (from Henri's answer), which also requires extra packages and tends to mess up the alignment of things depending on how you use it.

\documentclass{article}

\usepackage{minted}
\newminted{c}{linenos, frame=lines, tabsize=4, gobble=1}

\usepackage{tikz}
\usetikzlibrary{calc,tikzmark}

\colorlet{colorMain}{green!10}
\colorlet{colorSum}{yellow!20}

\begin{document}

\begin{tikzpicture}[remember picture, overlay]
%% x_shift is a coordinate at the end of the line
\coordinate (x_shift) at ([xshift=\linewidth]pic cs:maintop);
%% Take the x coordinate from x_shift and the y from mainbottom
%% This is a point at the end of the line and at the y coordinate
%% of where the mainbottom tkzmark was placed
\coordinate (main_br) at ({pic cs:mainbottom} -| x_shift);
\coordinate (sum_br) at ({pic cs:sumbottom} -| x_shift);
%% The y coordinates are at the baseline. So just adding 0.1 to include
%% characters that go just below the baseline
\path[overlay, fill=colorMain] ($(pic cs:sumtop) + (0,-0.1)$) rectangle ($(sum_br) + (0,-0.1)$);
\path[overlay, fill=colorSum] ($(pic cs:maintop) + (0,-0.1)$) rectangle ($(main_br) + (0,-0.1)$);
\end{tikzpicture}

\begin{ccode*}{breaklines, escapeinside=~~}
#include <stdio.h>
~\tikzmark{sumtop}~
int sum(int summand1, int summand2)
{
    return summand1 + summand2;
}~\tikzmark{sumbottom}~
~\tikzmark{maintop}~
int main()
{
    int summand1 = 1;
    int summand2 = 2;
    printf("%d + %d = %d\n", summand1, summand2, sum(summand1, summand2));
}~\tikzmark{mainbottom}~
\end{ccode*}


\end{document}

enter image description here

PS. \tikzmark can just be imported from the tikz library - there is no need to define it. Also, be careful not to use the same tikzmark name with other figures (as the coordinates will be overwritten).

5
  • Thank you for sharing your answer. It looks useful. Do you know what the -0.2 is by which you are shifting the right border? I thought it might be numbersep but that is 12pt (fancyvrb documentation section 4.1.8) and less than the -0.2.
    – jakun
    Nov 27, 2022 at 19:46
  • I am surprised that \tikzmark{sumbottom} is after the brace, yet the box starts before the brace (as it should). How does that work? I have not found tikzmark in the documentation.
    – jakun
    Nov 27, 2022 at 19:51
  • Good point; your comment made me realize why I had to put the -0.2. It comes from the fact that the mark is after the '}'. I've fixed the code so that this is not needed and added some comments for clarity. With regard to the fact that the box starts at the beginning of the line, the rectangle definition is as follows: (top_left coordinate) rectangle (bottom_right coordinate). Hence, it is enough to have sumtop and maintop on the left-most of the code for the x coordinate left position to be correct.
    – Ivan
    Nov 28, 2022 at 21:26
  • 1
    Here is the tikzmark documentation: ctan.org/pkg/tikzmark
    – Ivan
    Nov 28, 2022 at 21:27
  • well done! I wish I could give you another +1
    – jakun
    Nov 30, 2022 at 12:26

You must log in to answer this question.

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