Я использую listings
для отображения кода Ruby с подсветкой. У меня есть следующий тестовый документ:
\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}
Выглядит это примерно так:
Конечно, #{some_variable}
выделено фиолетовым/лиловым цветом, потому что я установил его как стиль строки, но это не совсем правильно, так как синтаксис #{}
выполнит содержимое вместо того, чтобы интерпретировать этот блок как строку (только если он находится внутри " "
, а не с ' '
, но я бы проигнорировал эту тонкость).
Мой вопрос в том, есть ли способ настроить подсветку так, чтобы она отображала это правильно, то есть #{some_variable}
имела цвет по умолчанию?
EDIT: с ответом, представленным SDF, теперь это выглядит так:
Если вы сравните две картинки, то увидите, что теперь экранированные апострофы вокруг random word
не игнорируются, как раньше (что было правильным поведением).
EDIT 2: хотя я смог решить эту проблему, исключив string=[d]{'},
, я заметил еще две проблемы. Пример теперь выглядит так:
\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}
Ключевые слова внутри двойных кавычек теперь подсвечиваются, а одинарные кавычки внутри двойных приводят к повторному возникновению исходной проблемы.
Это постепенно выходит из-под контроля... Может, мне действительно стоит перейти на Minted.
решение1
Примечание:
Я обновил весь ответ, чтобы учесть эти два изменения. Есть много маленьких хаков, но я боюсь, что чем точнее мы хотим использовать listings
, тем больше хаков нам нужно будет добавить. Альтернативное решение с использованием смотрите в конце ответа minted
.
Решение первоначальной проблемы с помощьюlistings
Вы можете разрешить listings
обнаружение разделителей внутри другого разделителя, добавив *
в его определение:
morestring=*[d]{"}
Затем мы определяем #{
и }
как специальные разделители. Мы придаем им собственный стиль, добавляя вторую пару квадратных скобок:
morestring=[s][]{\#\{}{\}}
Здесь мы добавляем пустые скобки, что означает, что будет использоваться стиль по умолчанию. Также не забудьте экранировать специальные символы, такие как #
, {
и т. д. Для более подробных объяснений посмотрите listings
документацию, раздел 3.3.
Замечание:
s
option означает, что начальный и конечный разделители различны, d
что они одинаковы. Нужно использовать b
вместо , d
чтобы включить экранирование обратной косой черты. Я допустил эту ошибку в своем первоначальном ответе. Также стоит отметить, что Ruby, как и большинство языков, уже имеет базовое определение, которое включает большинство строк, поэтому нет необходимости переопределять все это (если только мы не захотим переопределить его, а мы это сделаем).
Это то, \lstset
что создает вывод, как показано в первой правке автора:
\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][]{\#\{}{\}},
}
Решение дополнительных проблем
Ключевые слова внутри строк подсвечиваются
Как сказал Дэниел в комментарии, звезда вmorestring=*[d]{"}
заставляет его искать больше строкиключевые слова. Это то, что нам нужно относительно " #{
- }
строк", но это также происходит и с ключевыми словами.
listings
не позволяет указать, что именно мы будем искать внутри строк, поэтому нам придется искать другой обходной путь.
Теперь, listings
предлагает **
возможность, чтобы стили строки и ее специального содержимого могли быть кумулятивными. Например, когда мы делаем это:
morestring=**[d][\color{mauve}]{"},
keywordstyle=\bfseries,
listings
сделает ключевые слова внутри двойных кавычек жирнымиилиловый. Дело в том, что нам нужно «кумулировать» цвета.
morestring=**[d][\color{mauve}]{"},
keywordstyle=\color{blue},
В этом случае ключевые слова внутри строк обрабатываются с помощью \color{mauve} \color{blue}
, и это плохо: стиль ключевых слов переопределяет стиль строки. Мой хак заключался в замене стиля ключевых слов новой командой, которая проверяет текущий цвет и устанавливает его на синий, если он еще не лиловый:
\def\bluecolorifnotalreadymauve{%
\extractcolorspec{.}\currentcolor
\extractcolorspec{mauve}\stringcolor
\ifx\currentcolor\stringcolor\else
\color{blue}%
\fi
}
(Благодаряэтот ответдля решения.)
Теперь мы также теряем наше первоначальное #{}
исправление, потому что его (пустой) стиль "кумулируется" с \color{mauve}
from ""
. Давайте кумулируй его обратно:
morestring=[s][\color{black}]{\#\{}{\}},
Одинарные кавычки приводят #{}
к повторному появлению проблемы
Как и ключевые слова, строки в одинарных кавычках повторно обрабатываются внутри строк в двойных кавычках. И listings
не было сказано искать внутри строк в одинарных кавычках, поэтому нам придется изменить их таким же образом:
morestring=**[d]{'},
И теперь мы теряем экранирование обратной косой черты. По неизвестной причине b
option не работает с **
. Ну, раз уж мы об этом…
morestring=[d]{\\'},
Полностью обновленный MWE
\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}
Выход:
Альтернативный подход: использованиеminted
minted
уже делает все, что вы хотите, и даже больше! Вот 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}
Это вывод со стилем по умолчанию:
Главный недостатокminted
является то, что он полагается наПигментыдля выполнения обработки, что означает:
Его установка может оказаться немного сложной.
Его сложнее настроить. (Но как только мы узнаем, как это сделать, он может стать очень мощным.)