[Tex/LaTex] Context dependent keyword color (listings). Question regarding (ab)using “otherkeywords”

listings

I found it difficult to phrase this question briefly. Therefore I will try to construct it in such a way that I at least ask it clearly.

My question is specific to: "otherkeywords" in the LaTeX package: "listings".


The background for my question is: I tried to create a rule to syntax highlight code (the language is not important today, I want a principle/"proof of concept"). Specifically, I want to emphasise the difference between variables, functions and methods. The reason is that they may have the same name (e.g. a global function, a class method and a class constant).

Consider the following imaginary language where "print" clearly has three different meanings.

print 'HelloWorld'
my_class.print()
my_class::print

My solution produces the LaTeX output shown below (this illustrates my idea of "different meaning, same name"). I stumbled over the somewhat satisfactory, but obviously hacked, solution when I first defined ".print" and "::print" as "otherkeywords" and later used "more keywords" to redefine them in a different "classoffset". The problem is that I cannot cannot explain why this produced the output I wanted (see \lstdefinestyle in the LaTeX code for language definition).

Output from LaTeX


My questions are:

  1. Is "otherkeywords" always linked to the lowest "classoffset", and if so, is it merely an extension of "keywords"?
  2. Why can I use ".print" and "::print" in "morekeywords" after defining them in "otherkeywords"? (My logic dictates that "." and ":" should still be "other" characters.)

For anyone interested in retracing my steps:

Produce the output shown earlier by using the LaTeX file below.

\documentclass{standalone}
\usepackage{xcolor,listings}

% Define Dummy (empty) language
\makeatletter
\lst@definelanguage{prettyprint}%
{%
}[keywords]
\makeatother

\lstdefinestyle{PRETTYPRINT}{
  language=prettyprint,%
  classoffset=0,%
    keywordstyle=\color{red!70!brown}\ttfamily,%
    morekeywords={print},%
    otherkeywords={.print,::print},%
  classoffset=1,%
    keywordstyle=\color{cyan!80!black}\ttfamily,%
    morekeywords={.print},%        
  classoffset=2,%
    keywordstyle=\color{orange!90!black}\ttfamily,%
    morekeywords={::print},%
}

\lstnewenvironment{print}{%
  \lstset{style=PRETTYPRINT}
}{}


\begin{document}
\begin{minipage}{5cm}
This is a hack...
\begin{print}
print 'HelloWorld'
my_class.print()
my_class::print()
\end{print}
\end{minipage}
\end{document}

Thank you for following to the bitter end. If you have answers to my questions, ideas for an alternative approach or any other pointers/comments, please leave a comment.

Two a side notes:
I have read the documentation and I am aware that I am not supposed to use a keyword in more than one list.
Also, I am "dead set" on using the listings package even though I know there are alternatives.

Best Answer

In the world of listings, there are mainly three kinds of characters: letter, digit, and other. (It is not pronounce or adjective, but a class named o-t-h-e-r literally.) The manual says that all keywords (should) consist of a letter followed by alpha-numeric characters. That is to say that ab, a2 are good keywords and that 12, 1b, 1?, a?, !b, !2, and !? are not keywords.

So, what makes others so special? An answer is that others are used to separate tokens. In your example, we recognize print as a method/function because it is separated by ./:: and (). Otherwise if . and : were letters we can only see two tokens, namely my_class.print() and my_class::print.

In this example, only the second bar is bold because listings sets alsoletter={.}, for [5.0]Lua.

\begin{lstlisting}[language={[5.0]Lua},morekeywords={bar}]
    foo.bar
    foo:bar
\end{lstlisting}

But sometimes we really need to color such symbols. So listings allows one to write otherkeywords={=>}. Notice that the second bar is still bold --- because listings still regards = as other.

\begin{lstlisting}[language={Haskell},morekeywords={bar}]
    foo=>bar
    foo<=bar
\end{lstlisting}

But otherkeywords does not support class number. In your example,

classoffset=2,
keywordstyle=\hakuna,
morekeywords={matata},

is equivalent to keywordstyle=[2]\hakuna, meaning applying \hakuna for all second class keyword, and morekeywords=[2]{matata}, meaning adding matata as a second class keyword.

However, as in your question 1, there is no otherkeywords=[2] syntax.

For question 2 it is hard to tell why it works. The remaining is my guess: writing morekeywords=[3]{Aal::Izz.Well} will make listings add Aal::Izz.Well as a third class key word. But listings will not see this keyword when parsing your code because, as I explained, others separates tokens. However, otherkeywords={Aal::Izz.Well} forces listings take a look, after it reads Aal, at : and do further checks.

You may try something like:

\lstnewenvironment{print}{%
    \lstset{style=PRETTYPRINT}
    \tracingmacros1
    \lstset{morekeywords=[1]{Aal::Izz.Well}}
    \tracingmacros0
    \lstset{otherkeywords={Aal::Izz.Well}}
}{}

and found that even otherkeywords add the keyword to the first class. The latter appear to be in the third class. (guess why~~)

Related Question