3

I would like to color my C# code listings as Visual Studio does. However, all language definitions that I found so far only color the keywords and not the user types. I understand that it requires parsing the code and may not be supported by the listings package, therefore I am ready to specify the color of each word myself. E.g. something like this:

\definecolor{usertype}{rgb}{0.17,0.57,0.68}
\begin{lstlisting}[language=C#]
public interface {\color{usertype}IEntity}
{
  {\color{usertype}Guid} Guid { get; }
  {\color{usertype}ICollection}<{\color{usertype}IComponent}> Components { get; }
  {\color{usertype}IComponent} this[string componentName] { get; }
  event {\color{usertype}EventHandler}<{\color{usertype}ComponentEventArgs}> CreatedComponent;
  event {\color{usertype}EventHandler}<{\color{usertype}ChangedAttributeEventArgs}> ChangedAttribute;
}
\end{lstlisting}

Note that some entries such as Guid are used both as a type and as an identifier. Unfortunately, this doesn't work as entries like {\color{usertype}...} are literally written into the output. How can I specify the color of individual words or code pieces in a listing? Or is there a better package than lstlisting for this?

UPDATE: I have tried using \texttt, but it ignores the spaces in the beginning of the line, requires \\ at the end of each line to force a line break and characters such as { and } must be escaped as \{ and \}.

Even better would be if I could mark certain words as belonging to a specific class via some special symbol, e.g. like this:

\begin{lstlisting}[language=C#]
public interface @IEntity
{
  @Guid Guid { get; }
  @ICollection<@IComponent> Components { get; }
  @IComponent this[string componentName] { get; }
  event @EventHandler<@ComponentEventArgs> CreatedComponent;
  event @EventHandler<@ChangedAttributeEventArgs> ChangedAttribute;
}
\end{lstlisting}

UPDATE 2: Tried using minted package. It parses the code and thus is able to highlight it better, but still doesn't highlight the user types.

  • 1
    You don't need to mark up every token separately: You can extend the type definitions with your own keywords. But the definitions are at the token level, i.e., you can't straightforwardly color the same word twice depending on context. So for this (and only for this) you need some work-around. – alexis Jan 13 '14 at 11:12
  • I posted and answer and Jubobs correctly mentioned the problem with words that are both datatypes and identifiers. If you find my answer not suitable for you problem please let me know. Meanwhile you can have a look at this link. Minted uses python pygments thus, you can write you own lexer. – Pouya Jan 13 '14 at 12:46
  • @Pouya Writing a lexer will be way more work than just manually coloring pieces that I need. I don't see your answer. Did you delete it? – Sergiy Belozorov Jan 14 '14 at 10:08
5

Only if you're on suicide watch...

Your sample code here is relatively short, so you could perform ad-hoc syntax highlighting, if you find the courage in you. However, as the length of the code increases, modifying the listing manually just to get the desired syntax highlighting becomes more and more of a pain. Imagine if you had, not merely a short sample code in a .tex file, but lots and lots of source files; you would kill yourself if you had to modify every single one of them just so they get typeset as desired when imported into a LaTeX document.

Fortunately, you can delegate a lot of that repetitive stuff to listings. It has limited lexing capabilities, but you can still come up with workarounds that make your life easier.

A solution with some automation

My understanding is that you want to get the desired syntax highlighting of of the word Guid, which can be, depending on the context, either a type or an identifier. If you don't use any dirty trick, listings is just going to use the same style for each occurence of a given "word". However you can make listings context-aware by implementing a simple finite state machine; here is one possibility using a switch and listings's emph and \emphstyle keys.

Define Guid as a special identifier class (emph=[2]{Guid}), and use a finite state machine consisting only of one Boolean state, the \ifGuidAlreadyUsed switch. The latter is used to keep track of whether Guid has already occured on the current line. The \ProcessGuid macro then hightlights the first occurence of Guid on each line, but not later occurences of Guid on that line (if any).

That should produce the desired syntax highlighting you want, under the following assumption: there are no multiple occurences of Guid type on the same line.

Furthermore, if

  1. there are other "words" that can be either a type or an identifier in your code,
  2. you happen to have a list of those words handy, and
  3. that list is not too long,

then you can extend the strategy explained above to those words without too much effort.

Note: to select the predefined language corresponding to C#, you need to to pass the value [Sharp]C to the language key; if you pass C#, you'll get an error.

enter image description here

\documentclass{article}

\usepackage{xcolor}
\usepackage{listings}

\definecolor{usertype}{rgb}{0.17,0.57,0.68} 

\makeatletter
\lstdefinestyle{SergiyCsharp}       % define custom style
{
    language = [Sharp]C,
    basicstyle = \ttfamily,
    emph =[1]                                               % basic user types
    {
        ChangedAttributeEventArgs,
        ComponentEventArgs,
        EventHandler,
        ICollection,
        IComponent,
        IEntity,
    },
    emphstyle =[1]\color{usertype},
    emph =[2]{Guid},                                % Guid user type
    emphstyle =[2]\ProcessGuid,
}

% Boolean state of a state machine dedicated to keep track of whether
% Guid has already occured on the current line or not
\newif\ifGuidAlreadyUsed
\GuidAlreadyUsedfalse       % initialised as false

% Highlight Guid if it is the first occurence on the current line;
% Do not highlight it otherwise.
\newcommand\ProcessGuid
{%
    \ifGuidAlreadyUsed%
    \else%
        \global\GuidAlreadyUsedtrue%
        \color{usertype}%
    \fi%
}

% At the end of each line, reset the state to false
\lst@AddToHook{EOL}{\global\GuidAlreadyUsedfalse}
\makeatother

\begin{document}

\begin{lstlisting}[style=SergiyCsharp]
public interface IEntity
{
  Guid Guid { get; }
  ICollection<IComponent> Components { get; }
  IComponent this[string componentName] { get; }
  event EventHandler<ComponentEventArgs> CreatedComponent;
  event EventHandler<ChangedAttributeEventArgs> ChangedAttribute;
}
\end{lstlisting}

\end{document}
  • Thanks a lot for the effort you took to write this answer. Indeed this is a nice solution. However, I was looking more at the "suicide watch" section. There will be at most 2-3 pages of code in my work, so that's not the issue to manually color it. Can you please explain how to do this on a short example? In other words - how can I embed special markers into the source code that will allow me to color parts of the code in some custom style. – Sergiy Belozorov Jan 14 '14 at 10:01
  • @SergiyByelozyorov That sounds totally unmaintainable. I'm afraid I don't have the courage to post such a solution... – jub0bs Jan 14 '14 at 16:20
  • It may be unmaintainable, but it is a solution, which will fit into my limited time frame that I have to finish my thesis :-). If you don't feel the courage to post it here, please write it to my email address: rryk [at] gmail [dot] com. Although if you post it here, I am sure other people will appreciate this until a better solution is created by someone who has more time. – Sergiy Belozorov Jan 14 '14 at 16:29
  • 2
    @SergiyByelozyorov You must be kidding. That's not how this site works... – jub0bs Jan 14 '14 at 16:31
  • 2
    @SergiyByelozyorov Sorry, but if you have 3 pages of code that you need to adjust, and you need it quick, then I'm sure the fastest solution is to do it manually. You can start with what minted outputs as an auxiliary file. – yo' Jan 14 '14 at 16:57

Your Answer

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

Not the answer you're looking for? Browse other questions tagged or ask your own question.