Take the 2-minute tour ×
TeX - LaTeX Stack Exchange is a question and answer site for users of TeX, LaTeX, ConTeXt, and related typesetting systems. It's 100% free, no registration required.

All is in the title. I would like to add a color to the text before a character with listing package.

For example, I would like to add a color to text between the beginning of a line and : or =, in a makefile.

I tried to use moredelimiters but I did not succeed.

enter image description here

\documentclass{article}

\usepackage{listings}
\usepackage{xcolor}
\definecolor{keycolor}{RGB}{172, 42, 42}
\definecolor{mbleu}{RGB}{64,96,127}
\definecolor{vimvert}{RGB}{46, 139, 87}

\lstdefinestyle{global}{
  basicstyle=\ttfamily\scriptsize\color{black!90},%
  stringstyle=\itshape\color{magenta},%
  showstringspaces=false,%
  keywordstyle={\bfseries\color{keycolor}},%
  commentstyle=\color{blue}\slshape,%
  framexleftmargin=1mm,%
  backgroundcolor=\color{black!2},%
}

\lstdefinestyle{makefile}{
  style=global,%
  morecomment=[l][commentstyle]{\#},%
  emphstyle={\color{vimvert}},%
  moredelim=[s][emphstyle]{\$(}{)}%
}

\begin{document}

\begin{lstlisting}[style=makefile]
.SUFFIXES: .inc .f .f90 .F

# all CPP processed fortran files have the extension .f90
SUFFIX=.f90

#-----------------------------------------------------------------------
# this release should be fpp clean
# we now recommend fpp as preprocessor
# if this fails go back to cpp
CPP_=fpp -f_com=no -free -w0 $*.F $*$(SUFFIX)

vasp: $(SOURCE) $(FFT3D) $(INC) main.o 
    rm -f vasp
    $(FCL) -o vasp main.o  $(SOURCE)   $(FFT3D) $(LIB) $(LINK)
makeparam: $(SOURCE) $(FFT3D) makeparam.o main.F $(INC)
    $(FCL) -o makeparam  $(LINK) makeparam.o $(SOURCE) $(FFT3D) $(LIB)
zgemmtest: zgemmtest.o base.o random.o $(INC)
    $(FCL) -o zgemmtest $(LINK) zgemmtest.o random.o base.o $(LIB)
dgemmtest: dgemmtest.o base.o random.o $(INC)
    $(FCL) -o dgemmtest $(LINK) dgemmtest.o random.o base.o $(LIB) 
ffttest: base.o smart_allocate.o mpi.o mgrid.o random.o ffttest.o $(FFT3D) $(INC)
    $(FCL) -o ffttest $(LINK) ffttest.o mpi.o mgrid.o random.o smart_allocate.o base.o $(FFT3D) $(LIB)
kpoints: $(SOURCE) $(FFT3D) makekpoints.o main.F $(INC)
    $(FCL) -o kpoints $(LINK) makekpoints.o $(SOURCE) $(FFT3D) $(LIB)

clean:  
    -rm -f *.g *.f *.o *.L *.mod ; touch *.F
\end{lstlisting}

\end{document}
share|improve this question
1  
Could you please add a MWE, so that we have a basis for experimenting? –  Daniel Nov 6 '12 at 13:30
    
I add a short example. Actually, at then end I would like the code to look like vim syntax coloring. –  Ger Nov 6 '12 at 14:01
    
@hpesoj626: I think your comment is the most common solution. Can you provide an answer please? –  Marco Daniel Mar 9 '13 at 19:51
    
@MarcoDaniel Done. Thanks. –  hpesoj626 Mar 10 '13 at 3:03

3 Answers 3

.SUFFIXES is a keyword in the pre-defined language [gnu]make. SUFFIX is pre-defined in ACSL but loading the languages through a combination of language and alsolanguage and classoffset keys don't seem to give the desired effect. I suggest that you define your makefile as in the following work-arounds.

Work-around 1

In this solution, the colon, : is also colored. Just set up your \lstdefinestyle as follows:

\lstdefinestyle{global}{
basicstyle=\ttfamily\scriptsize\color{black!90},%
stringstyle=\itshape\color{magenta},%
showstringspaces=false,%
keywordstyle={[1]\bfseries\color{keycolor}},%
keywordstyle={[2]\bfseries\color{mbleu}},%
commentstyle=\color{blue}\slshape,%
framexleftmargin=1mm,%
backgroundcolor=\color{black!2},%
}

\lstdefinestyle{makefile}{
otherkeywords={.SUFFIXES},
alsoletter={:},
morekeywords=[1]{SUFFIX, CPP_},
morekeywords=[2]{vasp:,makeparam:,zgemmtest:,dgemmtest:,ffttest:,kpoints:,clean:},
style=global,%
morecomment=[l][commentstyle]{\#},%
emphstyle={\color{vimvert}},%
moredelim=[s][\color{vimvert}]{\$(}{)}%
}

Output

enter image description here

Explanations

  • otherkeywords defines keywords that contain other characters, or start with digits.
  • alsoletter makes : a letter, otherwise, words containing these symbols will not be recognized as words.
  • You can make separate lists of morekeywords or moreemph by the syntax morekeywords=[<number>]{<list of keywords>} or moreemph=[<number>]{<list of emphs>}. Here I have chosen keywords but you can modify according to your preference. You have then to match this list with a corresponding keywordstyle or emphstyle.

Work-around 2

You might not want the colon colored. In this case, you can use (guess what?) moredelim as in moredelim=[is][\color{mbleu}]{/*}{*/}, From the manual:

More general delimiters can be defined by the key moredelim. Legal types are l and s. These types can be preceded by an i, but this time only the delimiters are discarded from the output.

And here is the full code

%\documentclass{article}
\documentclass[preview,border=5]{standalone}

\usepackage{geometry}
\geometry{margin=1in}
\usepackage[procnames]{listings}
\usepackage{xcolor}
\definecolor{keycolor}{RGB}{172, 42, 42}
\definecolor{mbleu}{RGB}{64,96,127}
\definecolor{vimvert}{RGB}{46, 139, 87}

\lstdefinestyle{global}{
basicstyle=\ttfamily\scriptsize\color{black!90},%
stringstyle=\itshape\color{magenta},%
showstringspaces=false,%
keywordstyle=\bfseries\color{keycolor},%
commentstyle=\color{blue}\slshape,%
framexleftmargin=1mm,%
backgroundcolor=\color{black!2},%
    }

\lstdefinestyle{makefile}{
otherkeywords={.SUFFIXES},
morekeywords={SUFFIX, CPP_,},
moredelim=[is][\color{mbleu}]{/*}{*/},
style=global,%
morecomment=[l][commentstyle]{\#},%
emphstyle={\color{vimvert}},%
moredelim=[s][\color{vimvert}]{\$(}{)}%
}

\begin{document}

\begin{lstlisting}[style=makefile]
.SUFFIXES: .inc .f .f90 .F

# all CPP processed fortran files have the extension .f90
SUFFIX=.f90

#-----------------------------------------------------------------------
# this release should be fpp clean
# we now recommend fpp as preprocessor
# if this fails go back to cpp
CPP_=fpp -f_com=no -free -w0 $*.F $*$(SUFFIX)

/*vasp*/: $(SOURCE) $(FFT3D) $(INC) main.o 
        rm -f vasp
        $(FCL) -o vasp main.o  $(SOURCE)   $(FFT3D) $(LIB) $(LINK)
/*makeparam*/: $(SOURCE) $(FFT3D) makeparam.o main.F $(INC)
        $(FCL) -o makeparam  $(LINK) makeparam.o $(SOURCE) $(FFT3D) $(LIB)
/*zgemmtest*/: zgemmtest.o base.o random.o $(INC)
        $(FCL) -o zgemmtest $(LINK) zgemmtest.o random.o base.o $(LIB)
/*dgemmtest*/: dgemmtest.o base.o random.o $(INC)
        $(FCL) -o dgemmtest $(LINK) dgemmtest.o random.o base.o $(LIB) 
/*ffttest*/: base.o smart_allocate.o mpi.o mgrid.o random.o ffttest.o $(FFT3D) $(INC)
        $(FCL) -o ffttest $(LINK) ffttest.o mpi.o mgrid.o random.o smart_allocate.o base.o $(FFT3D) $(LIB)
/*kpoints*/: $(SOURCE) $(FFT3D) makekpoints.o main.F $(INC)
        $(FCL) -o kpoints $(LINK) makekpoints.o $(SOURCE) $(FFT3D) $(LIB)
/*clean*/:  
        -rm -f *.g *.f *.o *.L *.mod ; touch *.F
\end{lstlisting}

\end{document}

Output

enter image description here

share|improve this answer

enter image description here

The answer posted by hpesoj626 shows two approaches that produce the desired output; they are perfectly acceptable for a small number of lines. Unfortunately, those two approaches quickly become tedious and unmaintainable as the number of targets increase, because they lack automation:

  • workaround 1 requires defining all the make target names (e.g. vasp, makeparam, etc.) as keywords;
  • workaround 2 requires altering the listing by surrounding all targets names with custom delimiters.

Because you haven't accepted his answer yet, I'm assuming you need an automated solution that requires neither defining a long list of keywords or modifying the original listing. In the following, I'm assuming that your question is really

Can I get 'listings' to automatically highlight text before a given character? If so, how?

Short answer

Yes and no; but mostly no.

More detailed answer

The listings package reads a listing in a pretty much linear fashion. As far as I know, it can't really look ahead very far and then backtrack to apply a style conditionally on what it has found.

In some cases, though, it's possible to mess with listings's internals just enough to get what you want. You can define switches, counters, etc. whose values are updated when a given character or identifier is encountered, or at certain listings "hooks". You can then use those "state variables" to conditionally apply styles.

In particular, here, in order to only get make targets highlighted in magenta, I set the current style to magenta colour at the beginning of each line (via the hook called EveryLine that listings provides) and I revert back to the "default" style (black color) as soon as I encounter a colon.

Note that my approach still requires you to define your make variables as keywords if you want them highglighted in a different style than the targets; I can't think of a robust way to have variables automatically highlighted in a style different to that of targets. However, I believe that the number of variables in a typical makefile is usually less than the number of targets, so defining each variable as listings keyword shouldn't be that much hassle...

Why are tab characters missing in action?

If you simply copy the code below, paste it in your favourite editor, and compile, you will not obtain the same output as that shown on my screenshot. That's because command lines in makefiles are preceded by tabstops, but TeX.SX replaces all tabstops by four spaces. You'll have to reinsert the tabstops in my code manually before compiling; then everything should work as expected.

\documentclass{article}

\usepackage[margin=2cm]{geometry}
\usepackage{textcomp}
\usepackage{xcolor}
\usepackage{listings}

% --- color definitions ---
\definecolor{mktext}    {RGB}{000, 043, 054}
\definecolor{mktarget}  {RGB}{211, 054, 130}
\definecolor{mkstring}  {RGB}{000, 127, 127}
\definecolor{mkvariable}{RGB}{038, 139, 210}
\definecolor{mkkeyword} {RGB}{133, 153, 000}
\definecolor{mklinecont}{RGB}{220, 050, 047}
\definecolor{mkcomment} {RGB}{088, 110, 117}
\definecolor{mkcommand} {RGB}{042, 161, 152}

% --- auxiliary macros for styles ---
\newcommand\mktextstyle{\color{mktext}}
\newcommand\mktargetstyle{\color{mktarget}}
\newcommand\mkstringstyle{\color{mkstring}}
\newcommand\mkvariablestyle{\color{mkvariable}}
\newcommand\mkkeywordstyle{\color{mkkeyword}}
\newcommand\mklinecontstyle{\color{mklinecont}}
\newcommand\mkcommentstyle{\slshape\color{mkcomment}}
\newcommand\mkcommandstyle{\color{mkcommand}}

\makeatletter
% --- context switches ---
\newif\iftarget@make@
\newif\iflinecont@make@

% --- hook into listings to keep track of context ---
\lst@AddToHook{InitVarsBOL}
{%
  \iflinecont@make@%
    \global\target@make@false%
  \else
    \global\target@make@true%
  \fi
  \global\linecont@make@false%
}

\lst@AddToHook{Output}
{%
  \ifnum\lst@mode=\lst@Pmode%
      \iftarget@make@%
        \gdef\globalstyle@make{\the@lststyle}%
        \def\lst@thestyle{\mktargetstyle}%
      \fi
    \fi
  \ifnum\lst@mode=\lst@Pmode% only test for keyword if we're in "processing" mode
    \lsthk@DetectKeywords%
  \fi
}

\lstdefinestyle{make-pretty}
{
  upquote=true,
  showstringspaces=false,
  basicstyle=\ttfamily\scriptsize\mktextstyle,
  sensitive=true,
  alsoletter=.-,
  keywords = {.CC,.CFLAGS,.PHONY,.SUFFIXES,SUFFIX,CPP_},
  keywordstyle = \mkkeywordstyle,
  moredelim=[is][\mkvariablestyle(\aftergroup\closingparen@make]{(}{)},
  moredelim=*[l][\mkcommandstyle]{\^^I},
  morestring=[b]',
  morestring=[b]",
  stringstyle=\mkstringstyle,
  literate =
    {\%}{{{\mkvariablestyle\%}}}1
    {\$}{{{\mkvariablestyle\$}}}1
    {\$*}{{{\mkvariablestyle\$*}}}2
    {\\}{{{\mklinecontstyle\lstum@backslash}}}1,
  MoreSelectCharTable =
    \processColon@make
    \processEqual@make
    \processBackslash@make
    \processTab@make,
  morecomment = [l]{\#},
  commentstyle = \mkcommentstyle,
}

% --- helper macros ---
\newcommand\closingparen@make{{\mkvariablestyle)}}

\newcommand\processEqual@make
{%
  \lst@DefSaveDef{`=}\equal@make%
  {%
    \equal@make%
    \global\target@make@false%
    \def\lst@thestyle{\globalstyle@make}%
  }%
}

\newcommand\processColon@make
{%
  \lst@DefSaveDef{`:}\colon@make%
  {%
    \colon@make%
    \global\target@make@false%
    \def\lst@thestyle{\globalstyle@make}%
  }%
}

\newcommand\processBackslash@make
{%
  \lst@DefSaveDef{`\\}\backslash@make%
  {%
    \backslash@make%
    \ifnum\lst@mode=\lst@Pmode%
      \global\linecont@make@true%
      \def\lst@thestyle{\globalstyle@make}%
    \fi
  }%
}

\newcommand\processTab@make
{%
  \lst@DefSaveDef{`^^I}\tabchar@make%
  {%
    \tabchar@make%
    \global\target@make@false%
  }%
}
\makeatother

\begin{document}
\begin{lstlisting}[style=make-pretty]
.SUFFIXES: .inc .f .f90 .F

# all CPP processed fortran files have the extension .f90
SUFFIX=.f90

#-----------------------------------------------------------------------
# this release should be fpp clean
# we now recommend fpp as preprocessor
# if this fails go back to cpp
CPP_=fpp -f_com=no -free -w0 $*.F $*$(SUFFIX)

vasp: $(SOURCE) $(FFT3D) $(INC) main.o 
    rm -f vasp
    $(FCL) -o vasp main.o  $(SOURCE)   $(FFT3D) $(LIB) $(LINK)
makeparam: $(SOURCE) $(FFT3D) makeparam.o main.F $(INC)
    $(FCL) -o makeparam  $(LINK) makeparam.o $(SOURCE) $(FFT3D) $(LIB)
zgemmtest: zgemmtest.o base.o random.o $(INC)
    $(FCL) -o zgemmtest $(LINK) zgemmtest.o random.o base.o $(LIB)
dgemmtest: dgemmtest.o base.o random.o $(INC)
    $(FCL) -o dgemmtest $(LINK) dgemmtest.o random.o base.o $(LIB) 
ffttest: base.o smart_allocate.o mpi.o mgrid.o random.o ffttest.o $(FFT3D) $(INC)
    $(FCL) -o ffttest $(LINK) ffttest.o mpi.o mgrid.o random.o smart_allocate.o base.o $(FFT3D) $(LIB)
kpoints: $(SOURCE) $(FFT3D) makekpoints.o main.F $(INC)
    $(FCL) -o kpoints $(LINK) makekpoints.o $(SOURCE) $(FFT3D) $(LIB)

clean:  
    rm -f *.g *.f *.o *.L *.mod ; touch *.F
\end{lstlisting}
\end{document}
share|improve this answer
    
Wow, you are such a hacker :-) –  Daniel Mar 11 at 17:53
    
@Daniel Nah! I've just been playing around with listings internals for one of my packages; so, when I see a question that looks answerable, I jump on it :p –  Jubobs Mar 11 at 18:00
    
Nice, but it is a little bit hard ... –  Ger 2 days ago
    
@Ger Yes, "a little bit" :) The listings package has a lot of nice features, but it doesn't allow for you asked very easily. –  Jubobs 2 days ago
up vote 1 down vote accepted

I found recently an alternative using the minted package with pygmentize. The inconvenient is that it is not a fully LaTeX solution.

minted package

\documentclass{article}

\usepackage[margin=2cm]{geometry}
\usepackage{textcomp}
\usepackage{minted}

\begin{document}
\begin{minted}{make}
.SUFFIXES: .inc .f .f90 .F

# all CPP processed fortran files have the extension .f90
SUFFIX=.f90

#-----------------------------------------------------------------------
# this release should be fpp clean
# we now recommend fpp as preprocessor
# if this fails go back to cpp
CPP_=fpp -f_com=no -free -w0 $*.F $*$(SUFFIX)

vasp: $(SOURCE) $(FFT3D) $(INC) main.o 
    rm -f vasp
    $(FCL) -o vasp main.o  $(SOURCE)   $(FFT3D) $(LIB) $(LINK)
makeparam: $(SOURCE) $(FFT3D) makeparam.o main.F $(INC)
    $(FCL) -o makeparam  $(LINK) makeparam.o $(SOURCE) $(FFT3D) $(LIB)
zgemmtest: zgemmtest.o base.o random.o $(INC)
    $(FCL) -o zgemmtest $(LINK) zgemmtest.o random.o base.o $(LIB)
dgemmtest: dgemmtest.o base.o random.o $(INC)
    $(FCL) -o dgemmtest $(LINK) dgemmtest.o random.o base.o $(LIB) 
ffttest: base.o smart_allocate.o mpi.o mgrid.o random.o ffttest.o $(FFT3D) $(INC)
    $(FCL) -o ffttest $(LINK) ffttest.o mpi.o mgrid.o random.o smart_allocate.o base.o $(FFT3D) $(LIB)
kpoints: $(SOURCE) $(FFT3D) makekpoints.o main.F $(INC)
    $(FCL) -o kpoints $(LINK) makekpoints.o $(SOURCE) $(FFT3D) $(LIB)

clean:  
    rm -f *.g *.f *.o *.L *.mod ; touch *.F
\end{minted}
\end{document}
share|improve this answer

Your Answer

 
discard

By posting your answer, you agree to the privacy policy and terms of service.

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