10

I'm currently writing a book chapter in bioinformatics, and I would like to provide my readers some useful command lines to invoke recent software. To that purpose, I'm using the listings package to format minimal python scripts, whose execution would produce the figures in the chapter.
The package works great in general, but I'm having difficulties getting it to break-lines properly on long strings, as shown by the following MWA, in which the string is simply not broken.

\documentclass{minimal}
\usepackage{listings}
\lstloadlanguages{Python}
\lstset{
    language=Python,
    basicstyle=\ttfamily,
    morekeywords={rna},
    breaklines=true,
    keywordstyle={\bfseries\color{purple}},
}
\begin{document}
  \begin{lstlisting}[caption={No line breaking}]
    rna = "ACUUGUAUAACCUCAAUAAUAUGGUUUGAGGGUGUCUACCAGGAACCGUAAAAUGGUGAUUACAAAAUUUGUUUAUGACAUUUUUUGUAAUCAGGAUUUUUUUU"
  \end{lstlisting}
\end{document}

In Listings package does not break, a Tex hack was given, to take the 'letter' status away from every character, thus allowing the listings package to treat long ids/strings as sequences of 'atomic tokens'. The new MWA would read:

\documentclass{minimal}
\usepackage{listings}
\lstloadlanguages{Python}
\lstset{
    language=Python,
    basicstyle=\ttfamily,
    morekeywords={rna},
    breaklines=true,
    keywordstyle={\bfseries\color{purple}},
}
\begin{document}
  \makeatletter
    \def\lst@lettertrue{\let\lst@ifletter\iffalse}
  \makeatother
  \begin{lstlisting}[caption={No line breaking}]
    rna = "ACUUGUAUAACCUCAAUAAUAUGGUUUGAGGGUGUCUACCAGGAACCGUAAAAUGGUGAUUACAAAAUUUGUUUAUGACAUUUUUUGUAAUCAGGAUUUUUUUU"
  \end{lstlisting}
\end{document}

However, compiling the above code no longer provides any syntax highlighting, as shown in the following picture (red line represents the edge of the margin): Minimal (non)-working example for line-breaking vs highlighting problem in listings package

Can one get, at the same time, line-breaking of long ids/strings without losing syntax highlighting?

4

When option linebreak is set but option breakatwhitespace is not, listings allows for a line break at characters that it treats as "other". By default, the listings package treats A,C,G, T, and U as "letters" (duh!); however, you can make listings alternately treat those five letters as "letter" and "other" locally in string literals, in order to allow for a linebreak between them.

The approach shown below automatically breaks lines in long string literals composed of A,C,G, T, and U, and doesn't compromise syntax highlighting of identifiers (keywords, etc.).

enter image description here

\documentclass{article}

\usepackage[showframe]{geometry}  % only to make page layout visible
                                  % and check that everything is OK 
\usepackage[dvipsnames]{xcolor}
\usepackage{listings}

% ----- some listings tweaks ---
\makeatletter
\newif\ifinstring@rna@          % 
\newif\iflastACGTUwasother@rna@ % switch to keep track of whether the last
                                % base typeset was treated by listings as
                                % "letter" or "other"

% we redefine the stringstyle key for convenience
\lst@Key{stringstyle}{}{\def\lst@stringstyle{\global\instring@rna@true#1}}

% new language definition for allowing for linebreaks in long DNA/RNA chains
\lstdefinelanguage{PythonRNA}%
{
    language          = Python,
    morekeywords      = {rna,True},% possibly other keywords...
    breaklines        = true,
    breakatwhitespace = false,
    SelectCharTable   =
      \ProcessOtherOrLetter@rna{A}\rna@A
      \ProcessOtherOrLetter@rna{C}\rna@C
      \ProcessOtherOrLetter@rna{G}\rna@G
      \ProcessOtherOrLetter@rna{T}\dna@T
      \ProcessOtherOrLetter@rna{U}\rna@U
}

% helper macro that alternately treats bases as "letter" or "other" in order
% to allow for linebreaks between them
\newcommand\ProcessOtherOrLetter@rna[2]
{%
  \lst@DefSaveDef{`#1}#2%
  {%
      \ifinstring@rna@%
        \iflastACGTUwasother@rna@%
          \lst@ProcessLetter #1%
          \global\lastACGTUwasother@rna@false%
        \else
          \lst@ProcessOther #1%
          \global\lastACGTUwasother@rna@true%
        \fi
      \else
        #2%
      \fi
    }%  
}

\lst@AddToHook{EndGroup}{\global\instring@rna@false}
\makeatother
% ----- END of listings tweaks ---

% ----- user style customisation ---
\lstdefinestyle{myPythonRNAstyle}
{
  language      = PythonRNA,
  stringstyle   = \color{blue},
  keywordstyle  = \bfseries\color{purple},
  basicstyle    = \ttfamily,
  emph          = {Uuu},                   % sanity check
  emphstyle     = \color{ForestGreen}, 
}

\lstset{style=myPythonRNAstyle}

\begin{document}
\begin{lstlisting}[caption={Automatic line breaking}]
rna = "ACUUGUAUAACCUCAAUAAUAUGGUUUGAGGGUGUCUACCAGGAACCGUAAAAUGGUGAUUACAAAAUUUGUUUAUGACAUUUUUUGUAAUCAGGAUUUUUUUU"
# check that identifiers are still recognised
True Uuu
\end{lstlisting}
\end{document}
2

It depends on what you want it to look like. Your second example with coloured "rna" doesn't work for me for so far. I tried to set the \def\lst@lettertrue{\let\lst@ifletter\iffalse with escape in front and behind the long string, but that failed for me.

I can just offer you solutions for manual breaking:

\documentclass{minimal}
\usepackage{xcolor}
\usepackage{listings}
\lstloadlanguages{Python}
\lstset{
    language=Python,
    basicstyle=\ttfamily,
    morekeywords={rna},
    breaklines=true,
    breakatwhitespace=false,
    keywordstyle={\bfseries\color{purple}},
    escapeinside={(*@}{@*)},
}
\begin{document}

  \begin{lstlisting}[caption={No line breaking}]
    rna ="ACUUGUAUAACCUCAAUAAUAUGGUUUGAGGGUGUCUACCAGGAACCGUAAAAUGGUGAUUACAA(*@@*)AAUUUGUUUAUGACAUUUUUUGUAAUCAGGAUUUUUUUU"

    rna ="ACUUGUAUAACCUCAAUAAUAUGGUUUGAGGGUGUCUACCAGGAACCGUAAAAUGGUGAUUACA AAAUUUGUUUAUGACAUUUUUUGUAAUCAGGAUUUUUUUU"
  \end{lstlisting}
\end{document}

If you would like to get the broken line marked as such, you should have a look at prebreak=<token>. Sorry, but I didn't really succeed with that either.

If you don't have so many long strings, breaking it like shown may be sufficient.

  • (Sorry, got caught but the "max 5min. comment edit" patrol). +1, but I cannot accept a manual line-breaking strategy, as my examples will ultimately be loaded from external (possibly long) source files, which I would like to keep functional as python scripts (if only to make sure that my suggested code compiles/runs... think "cheap unit testing"! ;) ). – Yann Ponty Jul 23 '13 at 8:21
  • 1
    Sorry, I don't get other solutions. Maybe contact the maintainer of listings to get something like maxcharnumber=40 or forcebreakat=\linewidth. Or you try to find a macro to insert a comment after x characters and let it insert (*@\allowbreak@*) at such places. – LaRiFaRi Jul 23 '13 at 9:11

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.