Specific color for different tags and comments using Listings package

Problem:

The > symbol for HTML tags in the MWE has a different color than the < symbol. Would like to have both the same color.

Minimal Working Example:

\documentclass{scrreprt}
\makeatletter
\usepackage{color}
\definecolor{editorLightGray}{cmyk}{0.05, 0.05, 0.05, 0.1}
\definecolor{editorGray}{cmyk}{0.6, 0.55, 0.55, 0.2}
\definecolor{editorPurple}{cmyk}{0.5, 1, 0, 0}
\definecolor{editorWhite}{cmyk}{0, 0, 0, 0}
\definecolor{editorBlack}{cmyk}{1, 1, 1, 1}
\definecolor{editorOrange}{cmyk}{0, 0.8, 1, 0}
\definecolor{editorBlue}{cmyk}{1, 0.6, 0, 0}
\definecolor{editorPink}{cmyk}{0, 1, 0, 0}
\usepackage{upquote}
\usepackage{listings}
% CSS
\lstdefinelanguage{CSS}{
keywords={color,background-image:,margin,padding,font,weight,display,position,top,left,right,bottom,list,style,border,size,white,space,min,width, transition:, transform:, transition-property, transition-duration, transition-timing-function},
sensitive=true,
morecomment=[l]{//},
morecomment=[s]{/*}{*/},
morestring=[b]',
morestring=[b]",
alsoletter={:},
alsodigit={-}
}

% JavaScript
\lstdefinelanguage{JavaScript}{
morekeywords={typeof, new, true, false, catch, function, return, null, catch, switch, var, if, in, while, do, else, case, break},
morecomment=[s]{/*}{*/},
morecomment=[l]//,
morestring=[b]",
morestring=[b]'
}

\lstdefinelanguage{HTML5}{
language=html,
sensitive=true,
alsoletter={<>=-+},
morecomment=[s]{<!-}{-->},
tag=[s],
otherkeywords={
% General
% >,
% Standard tags
<!DOCTYPE,
% body
</body, <body,
% Divs
</div, <div, </div>,
% Paragraphs
</p, <p, </p>,
% scripts
</script, <script,
% More tags...
<canvas, /canvas>, <svg, <rect, <animateTransform, </rect>, </svg>, <video, <source, <iframe, </iframe>, </video>, <image, </image>
},
ndkeywords={=,
% General
+=,
% HTML attributes
charset=, src=, id=, width=, height=, style=, type=, rel=, href=,
% SVG attributes
fill=, attributeName=, begin=, dur=, from=, to=, poster=, controls=, x=, y=, repeatCount=, xlink:href=,
% CSS properties
margin:, padding:, background-image:, border:, top:, left:, position:, width:, height:,
% CSS3 properties
transform:, -moz-transform:, -webkit-transform:,
animation:, -webkit-animation:,
transition:,  transition-duration:, transition-property:, transition-timing-function:,
},
}

\lstdefinelanguage{PHP}{
morestring=[s]{'}{'},
morestring=[b]",
sensitive=true,
keywords=[1]{require_once, try, new, catch, die, echo},
keywords=[2]{setAttribute, getMessage, PDO, sprintf},
keywords=[3]{PDOException, PDO::ATTR_ERRMODE, PDO::ERRMODE_EXCEPTION, php},
keywords=[4]{athletics}
}

\lstset{%
% General design
backgroundcolor=\color{white},
basicstyle={\small\ttfamily},
frame=l,
% line-numbers
xleftmargin={14pt},
numbers=left,
stepnumber=1,
firstnumber=1,
numberfirstline=true,
% Code design
identifierstyle=\color{editorOrange},
keywordstyle=[1]\color{editorPink},
keywordstyle=[2]\color{editorBlue},
keywordstyle=[3]\color{editorBlack},
keywordstyle=[4]\color{editorBlue},
stringstyle=\color{editorPurple},
% Code
language=HTML5,
alsolanguage=CSS,
alsolanguage=JavaScript,
alsolanguage=PHP,
alsodigit={.:},
tabsize=2,
showtabs=false,
showspaces=false,
showstringspaces=false,
extendedchars=true,
breaklines=false,
% German umlauts
literate=%
{Ö}{{\"O}}1
{Ä}{{\"A}}1
{Ü}{{\"U}}1
{ß}{{\ss}}1
{ü}{{\"u}}1
{ä}{{\"a}}1
{ö}{{\"o}}1
}

\makeatother
\begin{document}
\begin{lstlisting}
<!DOCTYPE html>
<html>
<title>Listings Style Test</title>
<meta charset="UTF-8">
<style>
/* CSS Test */
* {
border: 0;
margin: 0;
}
</style>
<body>
<!-- Paragraphs are fine -->
<div id="box">
<p>
Hello World
</p>
<p>Hello World</p>
<p id="test">Hello World</p>
<p></p>
</div>
<div>Test</div>
<!-- HTML script is not consistent -->
<script src="js/benchmark.js"></script>
<script>
function createSquare(x, y) {
// This is a comment.
var square = document.createElement('div');
square.style.width = square.style.height = '50px';
square.style.backgroundColor = 'blue';

/*
* This is another comment.
*/
square.style.position = 'absolute';
square.style.left = x + 'px';
square.style.top = y + 'px';

var body = document.getElementsByTagName('body')[0];
body.appendChild(square);
};

// Please take a look at +=
// German umlaut test: Berührungspunkt ermitteln
var x = event.touches[0].pageX;
var y = event.touches[0].pageY;
var lookAtThis += 1;
});
</script>
</body>
</html>
\end{lstlisting}
\end{document}


Desired outcome:

The > symbol for comments should be grey while the > symbol for the HTML tags should be the same color as < symbol.

• So, you have defined otherkeywords in forms of e.g, <div. Why not defining them like <div>? Is there any particular reasons for this? – Pouya Jun 29 '15 at 13:37
• @Pouya If I do that, then some tags such as <html>, <head>, etc. will change to orange color instead of pink. If I remove the > from otherkeywords they will turn into the correct color (pink), but a new problem emerge. Tags like <!DOCTYPE html> and <meta charset="UTF-8"> will have their < and > turn into black instead of being pink and the tags will again turn into orange. I guess this is related to these having HTML attributes. – kexxcream Jun 29 '15 at 14:51
• Well, I think you should add a screenshot because what I see and what you explained are completely different. When I compile your code, your keywords such as <html are blue (neither pink, nor orange!) and if I change them to <html> they remain blue. – Pouya Jun 29 '15 at 16:21
• @Pouya Ok, it's updated. You should see the problem now with the > for HTML tags and for HTML comments. – kexxcream Jul 1 '15 at 20:48

My very first attempt is something like morekeywords={>, etc}. But every > at the end of comments is colored pink. I cannot override its color by, say, moewkeywords=[9]{-->}. Then I found that it is probably because < and > are special in this case.

More precisely, Listings actually takes care of tags by the following: when you say language=html, everything from < to > are styled by tagstyle=. So I write tagstyle=\color{editorBlue} in the example. Notice the color of = and the pig part. (Keywords like div's and p's are not blue because keywordstlye= overrides it.)

\lstset{
language=html,
tagstyle=\color{editorBlue},
basicstyle={\small\ttfamily},
identifierstyle=\color{editorOrange},
keywordstyle=\color{editorPink},
stringstyle=\color{editorPurple}
}
\begin{lstlisting}
<!DOCTYPE html>
<html>
<body>
<!-- Paragraphs are fine -->
<div id="box">
<p>Hello World</p>
<p id="test">Hello World</p>
<pig weight=120kg></pig>
</div>
<div>Test</div>
</body>
</html>
\end{lstlisting}


Part II

You may also notice that <!-- --> is not colored properly. Here is the story:

A comment declaration starts with <!, followed by zero or more comments, followed by >.
A comment starts and ends with --, and does not contain any occurrence of --.

So technically speaking Listings does it right, though it is not what we want. To color comments from <! to >, use morecomment=:

\lstdefinelanguage{HTML5}{
language=html,
tagstyle=\color{editorBlue},
markfirstintag,
morecomment=[s]{<!-}{-->},
}
\lstset{
language=HTML5
}
\begin{lstlisting}
<!DOCTYPE html>
<html>
<body>
<!-- Paragraphs are fine -->
<div id="box">
<p>Hello World</p>
<p id="test">Hello World</p>
<pig weight=120kg></pig>
</div>
<div>Test</div>
</body>
</html>
\end{lstlisting}


Part III

You can style tags further by hacking \lst@BeginTag and \lst@EndTag: (Notice that ='s are black.)

\makeatletter
\gdef\lst@BeginTag{%
\lst@DelimOpen
\lst@ifextags\else
{\let\lst@ifkeywords\iftrue
\lst@ifmarkfirstintag\lst@firstintagtrue\fi\color{editorBlack}}}
\gdef\lst@EndTag{\lst@DelimClose\lst@ifextags\else\color{editorBlue}}
\lstset{
morekeywords=[2]{weight},
keywordstyle=[2]\color{violet},
morekeywords=[3]{kg},
keywordstyle=[3]\color{brown},
otherkeywords={120},
morekeywords=[4]{120},
keywordstyle=[4]\color{green},
}
\begin{lstlisting}
<!DOCTYPE html>
<html>
<body>
<!-- Paragraphs are fine -->
<div id="box">
<p>Hello World</p>
<p id="test">Hello World</p>
<pig weight=120kg></pig>
</div>
<div>Test</div>
</body>
</html>
\end{lstlisting}


Part IV

(To be irresponsible, I want to say that it is not wise to mix HTML, CSS, javascript, and any backends.)

To be practical, the PHP parser for instance, is encoded in \lst@definelanguage{PHP} independently. I cannot see any relation between it and the HTML-part. The logic of alsolanguage= is to load all settings together without the ability of escaping from language_A to language_B. In fact, the document supports escaping to LATEX and says

1. Don’t use any commands of the listings package when you have escaped to LATEX.

I am not sure what do you want to do with PHP codes. If you want to color the whole line monochromatically, try morecomment=[s][\color{PHP}]{<?}{?>}. In the worst case, you can leave lstlisting and enter it again with different language=. (line number? easy I guess.)

\begin{lstlisting}[language=HTML,numbers=left,firstnumber=100]
<p>This is going to be ignored by PHP and displayed by the browser.</p>
<p>This is going to be ignored by PHP and displayed by the browser.</p>
\end{lstlisting}
\vspace*{-10pt}
\begin{lstlisting}[language=PHP ,numbers=left,firstnumber=last]
<?php echo 'While this is going to be parsed.'; ?>
<?php echo 'While this is going to be parsed.'; ?>
\end{lstlisting}
\vspace*{-10pt}
\begin{lstlisting}[language=HTML,numbers=left,firstnumber=last]
<p>This will also be ignored by PHP and displayed by the browser.</p>
<p>This will also be ignored by PHP and displayed by the browser.</p>
\end{lstlisting}


Part V

• characters ! and / use a consistent color to form !DOCTYPE and /html.
• Attributes and = use the same color.
• The right hand sides are always "string" by the standard.

So let me present my final version:

• Set ! and / to be letters; Forget all tag names as keywords. They are colored simply because markfirstintag=true. (Add some back if you want to distinguish tag names. Remember to add XXX and /XXX at the same time)
• Forget attributes as keywords; instead, hack \lst@BeginTag to color attributes and = at the same time. (Similarly, add YYY= back if needed)
• Thank God the right hand side is covered by stringstyle=. (You probably can distinguish those strings by redefining \lst@stringstyle inside \lst@BeginTag.)

\lstdefinelanguage{HTML5}{
language=html,
alsoletter={!/},
markfirstintag=true,
morecomment=[s]{<!--}{-->},
keywords={},
}
\lstset{
language=HTML5,
tagstyle=\color{editorBlue},
basicstyle={\small\ttfamily},
identifierstyle=\color{editorOrange},
keywordstyle=\color{editorPink},
stringstyle=\color{editorPurple},
}
\makeatletter
\gdef\lst@BeginTag{%
\lst@DelimOpen
\lst@ifextags\else
{\let\lst@ifkeywords\iftrue
\lst@ifmarkfirstintag\lst@firstintagtrue\fi\color{editorLightGray}}}
\gdef\lst@EndTag{\lst@DelimClose\lst@ifextags\else\color{editorBlue}}
\begin{lstlisting}
<!DOCTYPE html>
<html>
<body>
<!-- Paragraphs are fine -->
<div id="box">
<p>Hello World</p>
<p id="test">Hello World</p>
<pig weight="120kg" fat="100kg"></pig>
</div>
<div>Test</div>
</body>
</html>
\end{lstlisting}

• Thank you a million for this. I understand everything clearly except for Part III. Exactly how do you decide the color of the equal sign (=)? – kexxcream Jul 5 '15 at 5:56
• @kexxcream it is currently affected by the color assignment in the definition of \lst@BeginTag. – Symbol 1 Jul 5 '15 at 6:01
• I understand, but what part actually commands all equal signs to be black? I don't see that. – kexxcream Jul 5 '15 at 6:03
• @kexxcream Roughly speaking, Listings treats everything between < and > as tags. And this is implemented by declaring <> as a pair of delimiter, and then insert \lst@BeginTag right after < and \lst@EndTag right before >. So you can also see that all / are black, too. For more information, look up \gdef\lst@TagKey#1#2 in lstmisc.sty. – Symbol 1 Jul 5 '15 at 6:13
• Thanks but when I implement this I get the following: (1) Package keyval Error: tagstyle undefined. (2) Package keyval Error: markfirstintag undefined. – kexxcream Jul 5 '15 at 6:45