12

I'm trying to typeset SuperCollider code using the listings package.

Any "freestanding" identifiers that begin with a capital letter are class names, and should be highlighted; by "freestanding", I mean outside of strings or comments. I would be tickled pink if listings could let you define identifiers in terms of regexp, e.g. [A-Z][A-Za-z0-9_]* but the manual doesn't suggests that as a possibility.

SuperCollider has some 2300 classes... I really don't relish listing all of them literally in my preamble. I guess I could extract them by hand for each individual listing, but I'd rather not have to.

Is there a way to do this without a really massive morekeywords expression? (Perhaps using another package?)

Sample SuperCollider code:

p.clear;

~grains.addSpec(\tfreq, [1, 40, \exp]);
~grains.addSpec(\overlap, [0.1, 10, \exp]);
~grains.addSpec(\pos, [0, b.duration]);  // 3.43 is nice!
~grains.addSpec(\rate, [0.5, 2, \exp]);
~grains = { |tfreq = 25, overlap = 6, pan = 0, amp = 0.2, pos = 3.43,
   rate = 1|
   var trig = Impulse.ar(tfreq);
   TGrains.ar(2, trig, b, rate, pos, overlap / tfreq, pan, amp)
};
~grains.play;

Impulse and TGrains should be highlighted in blue. I have dozens of other listings using different capitalized keywords.

4
  • Are capital letters likely to occur elsewhere than in class names?
    – jub0bs
    Feb 8, 2014 at 4:31
  • No. Outside of /* comments */, "strings" and 'symbols' (or \symbols), every identifier starting with a capital letter is always a class name in SC. // Actually I can hack up some code in SC itself to scan my listings, pull out capitalized words, and keep only the ones that are real class names, giving me a set of only the class names I actually used -- then copy/paste that into the \lstdefinelanguage command.
    – James
    Feb 8, 2014 at 9:22
  • related: tex.stackexchange.com/questions/159245/…
    – jub0bs
    Mar 18, 2014 at 0:01

2 Answers 2

11

Edit: see https://tex.stackexchange.com/a/166159/21891 for a more complete listings language definition for the SuperCollider language.

Syntax highlighting of identifiers by regexp is not a feature of listings, but it is possible to parse identifiers in order to check whether they pass a certain test and then conditionally apply a style to them.

In the code below, all identifiers of the form [A-Z][A-Za-z0-9_]* are highlighted in a user-defined "class style". The class style below consists in boldface, green colour. You can customize the way classes are typeset by passing the appropriate declarations (e.g. \bfseries\color{red}) to the classstyle key, which I've defined for convenience.

If everything works as expected, you shouldn't have to specify your SuperCollider classes one by one anymore. Happy days :) Also, you can still define keywords starting by an uppercase letter; the keyword style will override the class style for those keywords.

References: This solution combines ideas laid out in Marco Daniel's answer, and David Carlisle's answer. The SuperCollider sample I used for this example is adapted from there.

enter image description here

\documentclass{article}

\usepackage[T1]{fontenc}
\usepackage{listings}
\usepackage{xcolor}

\renewcommand{\ttdefault}{pcr}

% patch to detect SuperCollider classes (i.e. identifiers starting by A-Z)
% and apply the corresponding class style
% ----- ugly internals -----
\makeatletter

% custom keys for controlling the styles of SuperCollider classes,
% symbols, and global variables
\lst@Key{classstyle}{}{\def\classstyle@supcol{#1}}

% local variables
\newcount\currentchar

\def\@testChar%
{%
  \ifnum\lst@mode=\lst@Pmode%
    % copy the first token in \the\lst@token to \@testChar
    \expandafter\splitfirstchar\expandafter{\the\lst@token}%
        % test for characters A through Z
        \currentchar=65
        \loop
          \expandafter\ifnum\expandafter`\@testChar=\currentchar%
            \let\lst@thestyle\classstyle@supcol%
            \let\iterate\relax
          \fi
          \advance \currentchar by 1
          \unless\ifnum\currentchar>90
        \repeat 
  \fi
  % ...but override by keyword style if a keyword is detected!
  \lsthk@DetectKeywords% 
}

% helper macros
\def\splitfirstchar#1{\@splitfirstchar#1\@nil}
\def\@splitfirstchar#1#2\@nil{\gdef\@testChar{#1}}

% apply patch to perform test on each identifier
\lst@AddToHook{Output}{\@ddedToOutput}
\let\@ddedToOutput\@testChar

\makeatother
% ----- end of ugly internals -----

% language definition
\lstdefinelanguage{SuperCollider}
{%
  alsoother     = @\$,
  morecomment   = **[l]{//},
  morecomment   = **[s]{/*}{*/},
  morestring    = **[s]{"}{"},
}[keywords,strings,comments]

% color definition
\definecolor{SCclasscolor}{RGB}{000,127,000}
\definecolor{SCcommentcolor}{RGB}{063,127,127}
\definecolor{SCstringcolor}{RGB}{186,034,034}
\colorlet{framerulecolor}{black}

% style definition
\lstdefinestyle{SupColSty}
{%
  language         = SuperCollider,
  basicstyle       = \ttfamily\footnotesize,
  stringstyle      = \color{SCstringcolor},
  commentstyle     = \color{SCcommentcolor}\itshape,
  classstyle       = \color{SCclasscolor}\bfseries,
  breaklines       = true,
  showstringspaces = false,
  frame            = single,
  rulecolor        = \color{framerulecolor},
}

% --- write source code to external file (for this example) ---
\usepackage{filecontents}
\begin{filecontents*}{Atari2600.scd}
/*
// Fredrik Olofsson

A quick demo of Fredrik Olofsson's Atari 2600 plugin, which can be downloaded from:


www.fredrikolofsson.com/pages/code-sc.html

This lovely 8-bit tune is based on an example in the helpfile.

*/

// Simple synth definition using the Atari2600 UGen:
(
SynthDef(\atari2600, {|out= 0, gate= 1, tone0= 5, 
      tone1= 8, freq0= 10, freq1= 20, amp= 1, pan= 0|
    var e, z;
    e= EnvGen.kr(Env.asr(0.01, amp, 0.05), gate, doneAction:2);
    z= Atari2600.ar(tone0, tone1, freq0, freq1, 15, 15);
    Out.ar(out, Pan2.ar(z*e, pan));
}).add;
)
"Test: Pseq SynthDef, etc. don't get highlighted as classes in strings"
// And a pattern to play it:
(
Pbind(
    \instrument, \atari2600,
    \dur, Pseq([0.25, 0.25, 0.25, 0.45], inf),
    \amp, 0.8,
    \tone0, Pseq([Pseq([2, 5], 32), Pseq([3, 5], 32)], inf),
    \tone1, 14,
    \freq0, Pseq([Pbrown(28, 31, 1, 32), 
                          Pbrown(23, 26, 3, 32)], inf),
    \freq1, Pseq([Pn(10, 16), Pn(11, 16)], inf)
).play
)
\end{filecontents*}

\begin{document}
\lstinputlisting[style=SupColSty]{Atari2600.scd}
\end{document}
0
2

Per Jubobs, what I'm asking for is impossible.

I worked around the limitation by writing code in SuperCollider itself, to find class names and drop any "false positives" (by checking against the list of classes, available by SC's own introspection).

https://github.com/jamshark70/scweek2014/blob/master/shows/scan-class-keywords.scd

This is less convenient than I hoped, but class names are now blue in my document, and this is better than plugging 2000+ class names into listings when I'm only using 50 or so.

1
  • See my answers to your two questions about highlighting SuperCollider code. It is possible after all...
    – jub0bs
    Mar 17, 2014 at 23:53

You must log in to answer this question.

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