2

I am looking to add highlighting to listings. I have tried to adept the techniques given by https://tex.stackexchange.com/users/23765/siracusa in the question Highlight text in code listing while also keeping syntax highlighting

However I have run into a problem, which is demonstrated in the example below. I want my listing to be inside a tcolorbox. When it isn't in a tcolorbox, the highlighting works as expected. When it is inside the box, the highlighting is rendered properly and in the right place, but it isn't displayed because the tcolorbox seems to be drawn on top of it.

Is there a way to draw the highlighting on top of the box (but still under the text) instead? I would really like to keep the core of the highlighting solution, because it plays really nicely with the listings package.

Also note that any solution that involves manually adjustments to individual codeblocks is not an option. Their contents are generated by another program, and there are thousands.

\LoadClass[10pt]{book}
\RequirePackage{fontspec}
\RequirePackage{listings}
\RequirePackage{xcolor}
\RequirePackage{atbegshi}
\RequirePackage{ifthen}
\RequirePackage{tikz}
\usetikzlibrary{calc}
\usetikzlibrary{fit}
\RequirePackage[breakable]{tcolorbox}

% HIGHLIGHTING
\makeatletter

\newcommand\if@empty[1]{%
    \if\relax\detokenize{#1}\relax
    \expandafter\@firstoftwo
    \else
    \expandafter\@secondoftwo
    \fi
}

\newcommand\ifthen[2]{\ifthenelse{#1}{#2}{}}
\newcommand\ifelse[2]{\ifthenelse{#1}{}{#2}}

% Is highlighting currently active in the listings environment?
\newif\ifhl@active

% Material executed at each page shipout
\newtoks\hl@shipout

% Extra styling parameters for the currently active highlight
\newtoks\hl@parameters

% Inline highlight style. This does not break across line ends
\newcommand\hl[2][]{\begingroup\hlstyle@start{#1}#2\endgroup}

% The highlight style for listings environments
\newcommand\hlstyle[1][]{\hlstyle@start{#1}}
\newcommand\hlstyle@start[1]{%
    \global\hl@activetrue
    \aftergroup\hlstyle@end
    \write\@auxout{\noexpand\hl@setparameters{#1}}%
    \hl@mark{S}%
}
\newcommand\hlstyle@end{%
    \hl@mark{E}%
    \global\hl@activefalse
}

% Sets the current highlight parameters
\newcommand\hl@setparameters[1]{%
    \global\hl@parameters={#1}%
}

% Writes a new node marker with the current position and page number to the .aux file
\newcommand\hl@mark[1]{%
    \ensuremath{\vcenter{\hbox{\pdfsavepos}}}%
    \write\@auxout{\noexpand\hl@processmark{#1}{\the\pdflastxpos}{\the\pdflastypos}{\arabic{page}}}%
}

% Writes the current page origin to the .aux file, and issues the current \hl@shipout material
% to be processed
\AtBeginShipout{\AtBeginShipoutUpperLeft{%
        \pdfsavepos
        \write\@auxout{\noexpand\hl@processmark{Z}{\the\pdflastxpos}{\the\pdflastypos}{\arabic{page}}}%
        \hl@doshipout
}}

% Maintains the current first and last node positions, checks whether a node marker should be used
% for the current highlighting segment, and issues a \hl@draw call if necessary.
% #1 is either 'S' (start node), 'M' (intermediate node), 'E' (end node), or 'Z' (page origin)
% #2/#3 are the x/y-coordinates, #4 is the page number
\newcommand\hl@processmark[4]{%
    \if#1S%
    \xdef\hl@firstx{#2}%
    \xdef\hl@firsty{#3}%
    \xdef\hl@firstp{#4}%
    \else\if#1M%
    \ifthenelse{\hl@firsty=#3}{%
        % Still on same line
        \xdef\hl@lastx{#2}%
        \xdef\hl@lasty{#3}%
    }{%
        \ifelse{\hl@firstx=\hl@lastx \and \hl@firsty=\hl@lasty}{%
            % Last highlight part is not empty
            \expandafter\hl@draw\expandafter{\the\hl@parameters}%
        }%
        \xdef\hl@firstx{#2}%
        \xdef\hl@firsty{#3}%
        \xdef\hl@firstp{#4}%
    }%
    \else\if#1E%
    \ifthen{\hl@firsty=#3}{%
        % Still on same line
        \xdef\hl@lastx{#2}%
        \xdef\hl@lasty{#3}%
        \ifelse{\hl@firstx=#2}{%
            % Last highlight part is not empty
            \expandafter\hl@draw\expandafter{\the\hl@parameters}%
        }%
    }%
    \else\if#1Z%
    \xdef\hl@zerox{#2}%
    \xdef\hl@zeroy{#3}%
    \fi\fi\fi\fi
}

% Add a new highlight segment to \hl@shipout to be processed on every shipout.
% #1 is the styling command used to draw the segment within a properly positioned \tikz call.
%    If #1 is empty, \hldefaultstyle is used as a default. Every styling command can take
%    arbitrary parameters, only the last four are mandatory (x_first, y_first, x_last, y_last)
\newcommand\hl@draw[1]{%
    \edef\@temp{{\noexpand\tikz [ overlay, shift = {(-\hl@zerox sp, -\hl@zeroy sp)} ]
            {\if@empty{#1}{\noexpand\hldefaultstyle}{\unexpanded{#1}}%
                {\hl@firstx sp}{\hl@firsty sp}{\hl@lastx sp}{\hl@lasty sp}}}%
        {\hl@firstp}%
    }%
    \expandafter\global\expandafter\addto@hook\expandafter\hl@shipout\expandafter{\@temp}%
}

% Processes the current \hl@shipout material. The material is a sequence of
% {TikZ commands}{page number} pairs
\def\hl@doshipout{%
    \expandafter\hl@doshipout@\the\hl@shipout{}{}\@end
}
\def\hl@doshipout@#1#2#3\@end{%
    \if@empty{#2}{}{%
        \ifthen{\arabic{page}=#2}{#1}%
        \hl@doshipout@#3\@end
    }%
}

% Hook into listings' discretionary breaks
\let\orig@lst@discretionary=\lst@discretionary
\gdef\lst@discretionary{%
    \ifhl@active
    \hl@mark{M}%
    \fi
    \orig@lst@discretionary
    \ifhl@active
    \hl@mark{M}%
    \fi
}



% Styling command for a simple marker highlights
\newcommand\hlmarkerstyle[5][yellow]{%
    \draw [ line width = \baselineskip, #1 ] (#2, #3) -- (#4, #5);
}


% Default styling command
\let\hldefaultstyle=\hlmarkerstyle

\makeatother

\newcommand\delimstyleB{\hlstyle}

%ENDHIGHLIGHTING



%% Define custom verbatim as listing allowing background color and mark notation
\let\verbatim\someundefinedcommand
\lstnewenvironment{CustomVerbatim}[1][name=main]
{
    \lstset{
        basicstyle=\fontsize{8}{8}\ttfamily,
        moredelim=**[is][\delimstyleB]{`}{`},
        aboveskip=3mm,
        breaklines=true,
        breakatwhitespace=false,
        keepspaces=true,
        extendedchars=true,
        xleftmargin=8pt,
        xrightmargin=8pt,
        framextopmargin=3pt,
        framexbottommargin=3pt,
        frame=tb,
        framerule=0pt,
        escapeinside={¬}{¬},
        postbreak=\mbox{\textcolor{blue}{$\hookrightarrow$}\space},
        #1
    }
}
{}

\newtcolorbox{codebox}[1][]
{
    #1,
    breakable,
    size=fbox,
    arc=0mm,
    colback=red,
    colframe=red,
}

\begin{document}


\begin{codebox}\begin{CustomVerbatim}%

The text of this `codeblock should be partially highlighted. However, the red box is blocking the highlighting.`

\end{CustomVerbatim}\end{codebox}



\begin{CustomVerbatim}%

The text of `this codeblock is properly partially highlighted, even across multiple lines because it` isn't in a tcolorbox

\end{CustomVerbatim}

\end{document}

Resulting page

  • I am curious what comes out of this question. Also, have you considered using minted package (with pygmets) highlighting? There might be an pygments style, that would do what you need (I would try style colorful, which highlights almost everything ...) – Tomáš Kruliš Mar 10 '20 at 13:01
  • Most assuredly the full box is taken as one \pdfsavepos. maybe there could be employed an test, that checks, whether highlighting happens inside a tcbox, or not. There are tests for determining segments of tcbox; but after that, it is beiond my skills to determine highlighting position inside box ... – Tomáš Kruliš Mar 10 '20 at 13:43
  • The minted package seems like a cool way to highlight keywords, but we want to highlight bits of the code that we deem important, like you would do with a yellow highlighter pen. It doesn't look like minted can do that. – Zoefschildpad Mar 10 '20 at 14:25
  • What does the tcolorbox need to do exactly? I imagine you simplified it for the question, but just a red box may be possible without tcolorbox, which would increase the possibilities for a solution. However, if the 'real' box is something that only tcolorbox can do, then solutions have to address that. – Marijn Mar 10 '20 at 15:00
  • The only difference between the box in the example and our actual box is the color, which I changed to red because I stripped out the color definition. The only thing we use it for is a background color for the code block. I also tried the listings own backgroundcolor argument, but that was too tight around the code, and the highlighting also did not work there. Other boxes are definitely an option. – Zoefschildpad Mar 10 '20 at 15:18

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service, privacy policy and cookie policy

Browse other questions tagged or ask your own question.