26

Is it possible to have a custom setup for lstlistings for LISP-like languages and have matching parentheses coloured with a same colour taken from a palette?

What I am looking for is something like the minor mode rainbow-delimiters for Emacs, have a look at here: enter image description here

Suppose you have a palette of 5 colours, then nested parentheses will be coloured by looping through this palette, having 5 alternating colors in the same order.

UPDATE I am providing a little MWE, just showing some listings settings for a lisp-like language (namely CLIPS), I do not know where to begin with colored parentheses.

\documentclass{report}

\usepackage[utf8]{inputenc}

\usepackage{listings}
\usepackage{xcolor}

\definecolor{color_1}{RGB}{247, 247, 247}
\definecolor{color_2}{RGB}{186, 33, 33}
\definecolor{color_3}{RGB}{0, 128, 0}
\definecolor{color_4}{RGB}{64, 128, 128}
\definecolor{color_5}{RGB}{170, 34, 255}

\lstdefinelanguage{clips}
{
  morekeywords ={deffunction, deftemplate, defrule, deffacts, run,
    clear, reset, facts, agenda, nil, initial-fact, assert, retract,
    watch, ppdefrule, unwatch, crlf},
  sensitive=true,
  morecomment=[l]{;},
   morestring=[b]",
  basicstyle=\ttfamily\small,
  numbers=left,
  numberstyle=\tiny,
}

\lstnewenvironment{clips-code}
{\lstset{language=clips, 
  }}
{}

\begin{document}

\begin{clips-code}
  (deffunction (a b)
  ``Simple function''
  (if (< a b)
      then (printout t a)
      else (printout t b)))
\end{clips-code}

\end{document}
6
  • Are you willing to show us some starter code?
    – user31729
    Mar 29, 2015 at 17:59
  • To be fairly honest I do not know where to start to achieve this kind of effect.
    – rano
    Mar 29, 2015 at 18:13
  • 2
    At least give us a .tex file to work with. Do you want us all to retype the code snippet you've provided as a picture?
    – jon
    Mar 29, 2015 at 18:58
  • @rano It is possible, but doesn't come out of the box: you need to hack at listings a bit. Basically, you need to use a counter to keep track of the nesting level, and typeset each parenthesis in a colour corresponding to the current value of that counter. I'd be happy to look into it (when I get more time), but you need to show some effort: as jon asked, please add a minimal working example (MWE).
    – jub0bs
    Mar 29, 2015 at 22:58
  • 1
    @jon I took that picture from a emacs blog showing rainbow-delimiter! I will write a little script to include some listings code.
    – rano
    Mar 30, 2015 at 7:22

1 Answer 1

23

The listings package itself doesn't offer this feature; you need to hack at it a bit.

I won't elaborate much here, as I've left comments in my code, but the basic idea is to use a counter to keep track of the nesting level, and typeset each parenthesis in a colour corresponding to the current value of that counter (modulo 5, in your case).

I used a similar approach in my matlab-prettifier package, in order to properly highlight MATLAB's context-sensitive end keyword.

References

Screenshot

enter image description here

enter image description here

Code

\documentclass{report}

\usepackage{listings}
\usepackage{xcolor}

\definecolor{color0}{RGB}{147, 147, 147} %<--- I've changed this to make it more visible
\definecolor{color1}{RGB}{186, 033, 033}
\definecolor{color2}{RGB}{000, 128, 000}
\definecolor{color3}{RGB}{064, 128, 128}
\definecolor{color4}{RGB}{170, 034, 255}

\lstdefinelanguage{clips}{
  morekeywords = {deffunction, deftemplate, defrule, deffacts, run,
    clear, reset, facts, agenda, nil, initial-fact, assert, retract,
    watch, ppdefrule, unwatch, crlf},
  sensitive        = true,
  morecomment      = [l]{;},
  morestring       = [b]",
  basicstyle       = \ttfamily\small,
  numbers          = left,
  numberstyle      = \tiny,
  showstringspaces = false,
}

% egreg's modulo macro (see https://tex.stackexchange.com/a/34449/21891)
\def\truncdiv#1#2{((#1-(#2-1)/2)/#2)}
\def\moduloop#1#2{(#1-\truncdiv{#1}{#2}*#2)}
\def\modulo#1#2{\number\numexpr\moduloop{#1}{#2}\relax}


\makeatletter

% a TeX counter to keep track of the nesting level
\newcount\netParensCount@clisp

% Modify how ( and ) get typeset depending on the value of the counter
% (Based on Ulrike Fischer's approach to modifying characters in listings;
% see https://tex.stackexchange.com/a/231927/21891)
\lst@CCPutMacro
\lst@ProcessOther{`(}{{%
  \ifnum\lst@mode=\lst@Pmode\relax%
    \rainbow@clisp{(}%
    \global\advance\netParensCount@clisp by \@ne%
  \else
    (%
  \fi
}}%
\lst@ProcessOther{`)}{{%
  \ifnum\lst@mode=\lst@Pmode\relax%
    \global\advance\netParensCount@clisp by \m@ne%
    \rainbow@clisp{)}%
  \else
    )%
  \fi
}}%
\@empty\z@\@empty

% Color its argument based on the value of the \netParensCount@clisp counter
% (modulo 5)
\newcommand\rainbow@clisp[1]{%
  \ifcase\modulo\netParensCount@clisp 5\relax%
    \textcolor{color0}{#1}%
  \or
    \textcolor{color1}{#1}%
  \or
    \textcolor{color2}{#1}%
  \or
    \textcolor{color3}{#1}%
  \else
    \textcolor{color4}{#1}%
  \fi
}

% Alternatively, you could simplify the definition of \rainbow@clisp to...
% \newcommand\rainbow@clisp[1]{%
%   \textcolor{color\modulo\netParensCount@clisp 5}{#1}%
% }
% ... but this assumes that the colours have names of the form color<i>,
% where <i> is a positive integer

% reset the counter at the beginning of each listing
% (just in case there were unmatched parentheses in a previous listing)
\lst@AddToHook{PreInit}{%
  \global\netParensCount@clisp 0\relax%
}

\makeatother


\lstnewenvironment{clips-code}
  {\lstset{language=clips}}
  {}


\begin{document}

\begin{clips-code}
  (def function (a b)
  ``Simple function''
  (if (< a b)
      then (printout t a)
      else (printout t b)))
\end{clips-code}

\begin{clips-code}
  (define (collatz n)
    " generate the Collatz sequence (for N until it ends in 1)"
    (if (n n 1)
        '(1) ;; stop
        (cons n
            (if (= (modulo n 2) 0)
                (collatz (/ n 2))
                (collatz (1+ (* 3 n)))))))
\end{clips-code}

\end{document}
6
  • 2
    this seems perfect
    – rano
    Mar 30, 2015 at 12:03
  • There is only one flaw maybe, parenthesis in the comments are not drawn. is this correlated or am I missing something?
    – rano
    Apr 14, 2015 at 7:17
  • 1
    @rano This is due to a bug in my code; it's easy to fix, though. Give me a moment.
    – jub0bs
    Apr 14, 2015 at 7:49
  • 1
    @rano My pleasure; that was fun. And remember: a minimal working example (MWE) is often all it takes for people here to get cracking on a problem :)
    – jub0bs
    Apr 14, 2015 at 12:51
  • 1
    I wish this existed for minted ... Its scheme style is the closest thing out there to an actual syntax highlighter for e.g. the Racket language -- far superior to what listings can provide, which is basically just a keyword highlighter -- but I have yet to find a rainbow parentheses mod.
    – Mew
    Dec 8, 2022 at 19:36

You must log in to answer this question.

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