Resolviendo el problema inicial usandolistings

Resolviendo el problema inicial usandolistings

Estoy usando listingspara mostrar el código Ruby resaltado. Tengo el siguiente documento de prueba:

\documentclass{article}

\usepackage{xcolor}
\usepackage{listings}

\definecolor{dkgreen}{rgb}{0,0.6,0}
\definecolor{mauve}{rgb}{0.58,0,0.82}

\lstdefinestyle{Ruby} {
    aboveskip=3mm,
    belowskip=3mm,
    showstringspaces=false,
    columns=flexible,
    basicstyle={\footnotesize\ttfamily},
    numberstyle={\tiny},
    numbers=left,
    keywordstyle=\color{blue},
    commentstyle=\color{dkgreen},
    stringstyle=\color{mauve},
    breaklines=true,
    breakatwhitespace=true,
    tabsize=2, 
    sensitive = true
}

\lstset{language=Ruby}

\begin{document}

\begin{lstlisting}[style=Ruby,float=ht,caption={...},label={lst:sourcecode},captionpos=b]
def some_function
  File.open(filename, 'w+') do |f|
    [...]
    # a comment
    f.puts "whatever #{some_variable} another string part"
    f.puts 'this string contains apostrophes: \'random word\''
    [...]
  end
end
\end{lstlisting}

\end{document}

Que se parece a esto:

salida de ejemplo

Por supuesto, #{some_variable}está resaltado en violeta/malva porque lo configuré como estilo de cadena, pero eso no es realmente correcto, ya que la sintaxis #{}ejecutará el contenido en lugar de interpretar este bloque como una cadena (solo si está dentro " ", no con ' ', pero estaría dispuesto a ignorar esta sutileza).

Mi pregunta es, ¿hay alguna manera de configurar el resaltado para representar esto correctamente, de modo que #{some_variable}tenga el color predeterminado?

EDITAR: con la respuesta presentada por SDF, ahora se ve así:

solución ligeramente incorrecta

Si comparas las dos imágenes, verás que ahora los apóstrofes escapados random wordno se ignoran como antes (que era el comportamiento correcto).

EDITAR 2: si bien pude resolver este problema omitiendo string=[d]{'},, noté dos problemas más. El ejemplo ahora se ve así:

\documentclass{article}

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

\definecolor{dkgreen}{rgb}{0,0.6,0}
\definecolor{gray}{rgb}{0.5,0.5,0.5}
\definecolor{mauve}{rgb}{0.58,0,0.82}
\definecolor{light-gray}{gray}{0.25}

\lstdefinestyle{Ruby} {
    aboveskip=3mm,
    belowskip=3mm,
    showstringspaces=false,
    columns=flexible,
    basicstyle={\footnotesize\ttfamily},
    numberstyle={\tiny},
    numbers=left,
    keywordstyle=\color{blue},
    commentstyle=\color{dkgreen},
    stringstyle=\color{mauve},
    breaklines=true,
    breakatwhitespace=true,
    tabsize=2, 
    sensitive = true,
    morestring=*[d]{"},
    morestring=[s][]{\#\{}{\}},
    procnamekeys={def},
    procnamestyle=\color{red},
}

\lstset{language=Ruby}

\begin{document}

\begin{lstlisting}[style=Ruby,float=ht,caption={...},label={lst:sourcecode},captionpos=b]
def some_function
  File.open(filename, 'w+') do |f|
    [...]
    # a comment
    f.puts "whatever #{some_variable} another string part"
    f.puts 'this string contains apostrophes: \'random word\''
    f.puts 'i do love keywords like class'
    f.puts "i do love keywords like class"
    f.puts "now single quotes 'inside #{double quotes}'"
    [...]
  end
end
\end{lstlisting}

\end{document}

Destacados de palabras clave desgastadas y problema de cotizaciones anidadas

Las palabras clave entre comillas dobles ahora se resaltan y las comillas simples entre comillas dobles hacen que resurja el problema original.

Esto se está yendo poco a poco de las manos... Tal vez debería cambiar a Minted.

Respuesta1

Nota: Actualicé toda la respuesta para tener en cuenta las dos ediciones. Hay muchos pequeños trucos, pero me temo que cuanto más precisos queramos usar listings, más trucos necesitaremos agregar. Consulte al final de la respuesta una solución alternativa usando minted.

Resolviendo el problema inicial usandolistings

Puede permitir listingsdetectar delimitadores dentro de otro delimitador agregando un *en su definición:

morestring=*[d]{"}

Luego definimos #{y }como delimitadores especiales. Les damos su propio estilo añadiendo un segundo par de corchetes:

morestring=[s][]{\#\{}{\}}

Aquí agregamos corchetes vacíos, lo que significa que se utilizará el estilo predeterminado. Además, no olvide utilizar caracteres de escape como #, {, etc. Para obtener explicaciones más detalladas, consulte listingsla documentación, sección 3.3.

Observación: sLa opción significa que los delimitadores de inicio y fin son diferentes, dque son iguales. Uno tiene que usar ben lugar de dpara permitir el escape de la barra invertida. Cometí ese error en mi respuesta original. También vale la pena señalar que Ruby, como la mayoría de los lenguajes, ya tiene una definición básica, que incluye la mayoría de las cadenas, por lo que no es necesario redefinirlo todo (a menos que queramos anularlo, y lo haremos).

Este es el \lstsetque produce el resultado como se ve en la primera edición del OP:

\lstdefinestyle{Ruby} {
    aboveskip=3mm,
    belowskip=3mm,
    showstringspaces=false,
    columns=flexible,
    basicstyle={\footnotesize\ttfamily},
    numberstyle={\tiny},
    numbers=left,
    keywordstyle=\color{blue},
    commentstyle=\color{dkgreen},
    stringstyle=\color{mauve},
    breaklines=true,
    breakatwhitespace=true,
    tabsize=2, 
    morestring=[d]{'}, % wrong: should be [b]
    morestring=*[d]{"},
    morestring=[s][]{\#\{}{\}},
}

Resolviendo problemas adicionales

Las palabras clave dentro de las cadenas se resaltan

Como dijo Daniel en un comentario, la estrella enmorestring=*[d]{"} hace que busque más hilosypalabras clave. Eso es lo que queremos con respecto a " #{-cadenas }", pero también ocurre con las palabras clave. listingsno permite especificar qué buscaremos exactamente dentro de las cadenas, por lo que tendremos que encontrar otra solución.

Ahora, listingsofrece una **opción para que se puedan acumular los estilos de la cadena y su contenido especial. Por ejemplo, cuando hacemos esto:

morestring=**[d][\color{mauve}]{"},
keywordstyle=\bfseries,

listingspondrá en negrita las palabras clave entre comillas doblesycolor de malva. La cuestión es que necesitamos "acumular" colores.

morestring=**[d][\color{mauve}]{"},
keywordstyle=\color{blue},

En este caso, las palabras clave dentro de las cadenas se procesan con \color{mauve} \color{blue}, y eso es malo: el estilo de la palabra clave anula el estilo de la cadena. Mi truco fue reemplazar el estilo de la palabra clave con un nuevo comando que verifica el color actual y lo establece en azul si aún no es malva:

\def\bluecolorifnotalreadymauve{%
    \extractcolorspec{.}\currentcolor
    \extractcolorspec{mauve}\stringcolor
    \ifx\currentcolor\stringcolor\else
        \color{blue}%
    \fi
}

(Gracias aesta respuestapara la solución.)

Ahora también perdemos nuestra #{}solución original, porque su estilo (vacío) se "acumula" con el \color{mauve}from "". Acumulemoslo de nuevo:

morestring=[s][\color{black}]{\#\{}{\}},

Las comillas simples hacen que el #{}problema resurja

Al igual que las palabras clave, las cadenas entre comillas simples se reprocesan dentro de cadenas entre comillas dobles. Y listingsno se le ha dicho que mire dentro de las cadenas entre comillas simples, por lo que tendremos que cambiarlas de la misma manera:

morestring=**[d]{'},

Y ahora perdemos la barra invertida para escapar. Por una razón desconocida, bla opción no funciona bien con **. Bueno, ya que estamos en ello...

morestring=[d]{\\'},

MWE completamente actualizado

\documentclass{article}

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

\definecolor{dkgreen}{rgb}{0,0.6,0}
\definecolor{gray}{rgb}{0.5,0.5,0.5}
\definecolor{mauve}{rgb}{0.58,0,0.82}
\definecolor{light-gray}{gray}{0.25}

\def\bluecolorifnotalreadymauve{%
    \extractcolorspec{.}\currentcolor
    \extractcolorspec{mauve}\stringcolor
    \ifx\currentcolor\stringcolor\else
        \color{blue}%
    \fi
}

\lstdefinestyle{Ruby} {
    aboveskip=3mm,
    belowskip=3mm,
    showstringspaces=false,
    columns=flexible,
    basicstyle=\footnotesize\ttfamily,
    numberstyle=\tiny,
    numbers=left,
    keywordstyle=\bluecolorifnotalreadymauve,
    commentstyle=\color{dkgreen},
    stringstyle=\color{mauve},
    breaklines=true,
    breakatwhitespace=true,
    tabsize=2,
    moredelim=[s][\color{black}]{\#\{}{\}}, % same as morestring in this case
    morestring=**[d]{'},
    morestring=[d]{\\'},
    morestring=**[d]{"},
    procnamekeys={def}, % bonus, for function names
    procnamestyle=\color{red},
}

\lstset{language=Ruby}

\begin{document}

\begin{lstlisting}[style=Ruby,float=ht,caption={...},label={lst:sourcecode},captionpos=b]
def some_function
  File.open(filename, 'w+') do |f|
    [...]
    # a comment
    f.puts "whatever #{some_variable} another string part"
    f.puts 'this string contains apostrophes: \'random word\''
    f.puts 'i do love keywords like class'
    f.puts "i do love keywords like class"
    f.puts "now single quotes 'inside #{double quotes}'"
    [...]
  end
end
\end{lstlisting}

\end{document}

Producción:

código Ruby actualizado con listados

Enfoque alternativo: usarminted

minted¡Ya hace todo lo que quieres y mucho más! Aquí hay un MWE:

\documentclass{article}

\usepackage{minted}

\begin{document}

\begin{listing}[H]
  \begin{minted}[fontsize=\footnotesize, linenos]{Ruby}
  def some_function
    File.open(filename, 'w+') do |f|
      [...]
      # a comment
      f.puts "whatever #{some_variable} another string part"
      f.puts 'this string contains apostrophes: \'random word\''
      f.puts 'i do love keywords like class'
      f.puts "i do love keywords like class"
      f.puts "now single quotes 'inside #{double quotes}'"
      [...]
    end
  end
  \end{minted}
  \caption{...}
\end{listing}

\end{document}

Este es el resultado con el estilo predeterminado:

código Ruby actualizado con acuñado

La principal desventaja mintedes que depende depigmentosrealizar el procesamiento, lo que significa:

  1. Puede resultar un poco complicado de instalar.

  2. Es más difícil de personalizar. (Pero una vez que sabemos cómo hacerlo, puede ser muy poderoso).

información relacionada