34

I'm trying to highlight simple YAML (the markup language, not the CSS framework) code. I know that there is a solution using pygments and minted, however, I would prefer a solution with \lstdefinelanguage.

This is my code so far:

\lstdefinelanguage{yaml}{
  keywords={true,false,null,y,n},
  keywordstyle=\color{darkgray}\bfseries,
  ndkeywords={},
  ndkeywordstyle=\color{black}\bfseries,
  identifierstyle=\color{black},
  sensitive=false,
  %moredelim=[l]{}{:},
  comment=[l]{#},
  morecomment=[s]{/*}{*/},
  commentstyle=\color{purple}\ttfamily,
  stringstyle=\color{blue}\ttfamily,
  %morestring=[l]{-}{},
  morestring=[b]',
  morestring=[b]"
}

Two things are missing, though:

  1. I would like to print all keys bold
  2. I would like to print all strings, also the ones without qotes, to be blue.

I tried to accomplish these two things with the commented lines in the code above. Unfortunately, they did not work. Any suggestions on how I can make this work?

Here is a small YAML example:

key: value
map:
    key1: value1
    key2: value2
list:
  - element1
  - element2
# This is a comment
listOfMaps:
  - key1: value1a
    key2: value1b
  - key1: value2a
    key2: value2b

This is the output I currently get:

enter image description here

jub0bs
  • 58,916
  • 1
    Could you define "all strings, also the ones without quotes"? I'm not familiar with YAML and from the wikipedia page I cannot understand what other strings there are, apart from those in single or double quotes. – nickie Jan 07 '14 at 16:20
  • I had a bad experience with listings and YAML in a manual I wrote; I used some delimiters in order to achieve syntax highlighting, but it's quite messy. Take a look at minted, it relies on pygments under the hood and it has built-in YAML support. – Paulo Cereda Jan 07 '14 at 16:28
  • 1
    Unfortunately, because YAML uses no delimiters for its keys, you'll have a hard time implementing the key style you want. One solution would be to declare all the key names you use as listings' keywords, and set the style for those keywords as you wish. Not a very satisfying solution but viable if the number of distinct key names is small. – jub0bs Jan 07 '14 at 16:33
  • @nickie Sorry for not being clear on that. In YAML, the types of values are inferred and thus, strings are mostly not enclosed by quotes. key: value and key: "value" are equivalent, value is a string for both. I would like to color strings, either with or without quotes, in the same color. I hope, this makes it clear. –  Jan 07 '14 at 16:40
  • @Jubobs I might consider that as a fallback. And thanks for the edit. –  Jan 07 '14 at 16:41
  • @str Based on what I just read, key: value and key: "value" are indeed equivalent, but a value can only contain colon(s) if it's delimited by double quotes (see http://stackoverflow.com/questions/8783705/how-could-should-i-state-colon-punctuation-in-a-yaml-file). – jub0bs Jan 07 '14 at 16:47
  • @Jubobs Yes there are some cases where quotes are required, but in other cases they can be left away. –  Jan 07 '14 at 16:51
  • @str I only mentioned it because it complicates the parsing of the YAML code (for pretty-printing it) even further. – jub0bs Jan 07 '14 at 16:52

1 Answers1

29

Here is a partial solution involving some juggling with the literate and moredelim keys. The values, if delimited by double quotes, can contain colons.

Caveat: values that span multiple lines won't be correctly highlighted.

enter image description here

\documentclass{article}

\usepackage[dvipsnames]{xcolor}
\usepackage{listings}

\newcommand\YAMLcolonstyle{\color{red}\mdseries}
\newcommand\YAMLkeystyle{\color{black}\bfseries}
\newcommand\YAMLvaluestyle{\color{blue}\mdseries}

\makeatletter

% here is a macro expanding to the name of the language
% (handy if you decide to change it further down the road)
\newcommand\language@yaml{yaml}

\expandafter\expandafter\expandafter\lstdefinelanguage
\expandafter{\language@yaml}
{
  keywords={true,false,null,y,n},
  keywordstyle=\color{darkgray}\bfseries,
  basicstyle=\YAMLkeystyle,                                 % assuming a key comes first
  sensitive=false,
  comment=[l]{\#},
  morecomment=[s]{/*}{*/},
  commentstyle=\color{purple}\ttfamily,
  stringstyle=\YAMLvaluestyle\ttfamily,
  moredelim=[l][\color{orange}]{\&},
  moredelim=[l][\color{magenta}]{*},
  moredelim=**[il][\YAMLcolonstyle{:}\YAMLvaluestyle]{:},   % switch to value style at :
  morestring=[b]',
  morestring=[b]",
  literate =    {---}{{\ProcessThreeDashes}}3
                {>}{{\textcolor{red}\textgreater}}1     
                {|}{{\textcolor{red}\textbar}}1 
                {\ -\ }{{\mdseries\ -\ }}3,
}

% switch to key style at EOL
\lst@AddToHook{EveryLine}{\ifx\lst@language\language@yaml\YAMLkeystyle\fi}
\makeatother

\newcommand\ProcessThreeDashes{\llap{\color{cyan}\mdseries-{-}-}}

\begin{document}

\begin{lstlisting}[language=yaml]
---
key: value
map:
    key1: "foo:bar"
    key2: value2
list:
  - element1
  - element2
# This is a comment
listOfMaps:
  - key1: value1a
    key2: value1b
  - key1: value2a
    key2: value2b
---
\end{lstlisting}

\begin{lstlisting}[frame=single]
some
other
listing
\end{lstlisting}

\end{document}
jub0bs
  • 58,916