Показаны сообщения с ярлыком publish_helper. Показать все сообщения
Показаны сообщения с ярлыком publish_helper. Показать все сообщения

воскресенье, 13 апреля 2014 г.

Несколько трюков с пакетом Latex Listings

Здесь я хочу показать три трюка с пакетом Listings, которые мне очень пригодились при определении “языка” для подсветки действий пользователя внутри командных оболочек и REPLов. Этот виртуальный язык, который я назвал shelloutput, может быть использован в плагине publish-helper. Некоторые сведения о реализации подсветки shelloutput в Listings приведены в разделе Highlighting shells and REPLs на главной странице проекта в github, там же приведен пример с картинками. Итак, определение псевдоязыка shelloutput — это и есть первый трюк, о котором я расскажу. Постановка задачи такова: обеспечить декоративное выделение блоков текста, изображающих действия пользователя внутри любых командных оболочек и REPLов, а также разделение текста с вводом пользователя (промпта) и выводом оболочки. Первая часть задачи решается без определения свойств языка и, соответственно, без участия пакета Listings. Простейший способ — определить новое окружение, внутри которого использовать какой-либо рамочный пакет, например пакет Framed.
\usepackage{framed}
\newenvironment{Leftbar}{
  \setlength\parskip{0pt}
  \setlength\OuterFrameSep{4pt}
  \setlength\partopsep{-\topsep}
  \begin{leftbar}
    \scriptsize
}{\end{leftbar}}
Это окружение, которое я назвал Leftbar, поскольку оно использует стиль leftbar из пакета Framed, должно находиться в преамбуле документа. Теперь, чтобы выделить блок текста с действиями пользователя в оболочке, нужно обернуть этот текст в тэги \begin{Leftbar} ... \end{Leftbar}. Перейдем к решению второй части задачи. Нам нужно определить новый язык, который мы сможем использовать в окружении lstlisting, которое, в свою очередь, мы будем помещать внутрь окружения Leftbar. Пусть основной стиль (basicstyle) языка shelloutput будет относиться к выводу оболочки, а стиль для ввода оболочки мы определим отдельно. Самое сложное здесь — это понять, как мы (и Listings) будем отличать ввод оболочки от вывода: промпты у всех оболочек разные и могут отсутствовать вообще. Поэтому мы не будем ориентироваться на промпт, а введем собственный виртуальный промпт, который будем вставлять в начало каждой строки, соответствующей вводу оболочки — ведь мы то знаем, где у нее ввод и вывод! В Listings есть опция moredelim, с помощью которой мы определим внешний вид ввода оболочки, передав ей наш виртуальный промпт (пусть это будет три бара и пробел — ‘||| ’) с флагами il, которые устанавливают, что Listings не должен показывать виртуальный промпт (i), и установки будут действовать в пределах всей строки (l). Вот как это может выглядеть в преамбуле документа:
\usepackage{listings}
\definecolor{shellpromptcolor}{HTML}{000000}
\definecolor{shelloutputcolor}{HTML}{666666}
\lstdefinelanguage{shelloutput}
  {basicstyle=\color{shelloutputcolor}
    \scriptsize
    \ttfamily\itshape,
   moredelim=[il][\color{shellpromptcolor}\upshape]{|||\ }}
В данном случае вывод оболочки будет подсвечен серым цветом и иметь наклонное начертание, в то время как ввод будет черным и прямым. Давайте добавим сюда небольшой, но красивый бонус — декоративные переносы строк. Для этого в преамбулу документа нужно добавить
\usepackage{MnSymbol}
\lstset{prebreak=\raisebox{0ex}[0ex][0ex]
  {\ensuremath{\rhookswarrow}}}
\lstset{postbreak=\raisebox{0ex}[0ex][0ex]
  {\ensuremath{\rcurvearrowse\space}}}
Пример использования подсветки виртуального языка shelloutput можно найти в разделе An example на странице разработки проекта vim-publish-helper. В этом примере документ, представленный на втором изображении, генерируется не напрямую из документа TeX, а опосредованно, через pandoc flavoured markdown, но результат будет совпадать независимо от языка исходного документа. Второй трюк. Listings преобразует символ переноса (-) в знак минус. Это разумно для многих языков, однако для нашего псевдоязыка shelloutput это выглядит некрасиво, во всяком случае в опциях команд оболочки, таких как ls -l --inode. Вернуть исходный символ переноса достаточно просто — определив опцию literate={-}{-}1 в преамбуле документа или внутри окружения lstlisting, однако этот простой подход ломает настройки alsoletter={-} и breakatwhitespace для символов переноса, что приводит к возможности обрыва строк при переносе на этих символах, а это выглядит очень некрасиво. Лучше всего определить макрос lst@CCPutMacro для символа переноса (фактически переопределить его, так как он уже определен — и в этом-то и проблема — в исходном коде listings.sty). Определение следует поместить в нашем окружении Leftbar, чтобы оно не повлияло на другие языки. Теперь Leftbar будет выглядеть так:
\makeatletter
\newenvironment{Leftbar}{
  \setlength\parskip{0pt}
  \setlength\OuterFrameSep{4pt}
  \setlength\partopsep{-\topsep}
  \lst@CCPutMacro\lst@ProcessLetter {"2D}{\lst@ttfamily{-{}}{-{}}}
  \@empty\z@\@empty
  \begin{leftbar}
    \scriptsize
}{\end{leftbar}}
\makeatother
Третий трюк. Русские символы (и вообще UTF-8) в выводе (и вводе) языка shelloutput. Оказывается, Listings до сих пор не поддерживает нормальную обработку национальных символов в формате UTF. Если в shelloutput встретятся русские символы, то они будут переставлены всевозможными способами с удалением пробелов и добавлением неожиданных новых строк. Проблема известная и есть вариант ее решения с помощью опции literate (см. здесь и здесь). Однако, этот способ очень verbose — лично мне он не нравится. Более простое и рабочее решение — использовать опцию escapeinside=|| в объявлении lstlisting, а внутри блока с текстом обрамлять участки с проблемными символами соответствующими эскейп-символами, в данном случае барами (|) и, возможно, внутри них тэгом \verb, который не позволит слипнуться множественным пробелам.

четверг, 9 января 2014 г.

vim: плагин publish_helper

Работу, начатую здесь и здесь, решил оформить в виде плагина vim (страница на гитхабе и страница на www.vim.org). Полная документация доступна по ссылкам, а также внутри плагина. Плагин состоит из двух частей. Первая часть - это собственно скрипт на viml, в котором определены команды MakeHtmlCodeHighlight (переименованная и улучшенная MakeBlogArticle) и MakeTexCodeHighlight (тоже улучшенная по сравнению с первой реализацией). Вторая часть написана на haskell и представляет собой фильтр для pandoc (фильтры доступны в pandoc начиная с версии 1.12), который позволяет подменять исходные блоки кода (CodeBlock) внутри абстрактного синтаксического дерева, представляющего документ, на неформатированные блоки (RawBlock), содержащие подсвеченный с помощью vim код.

Для того, чтобы показать как это работает, возьмем оригинальный пример из руководства пользователя pandoc, в котором приведена классическая реализация быстрой сортировки на haskell, и добавим туда еще пару образцов кода.
### Original example from [*Pandoc User's Guide*](http://johnmacfarlane.net/pandoc/README.html#fenced-code-blocks)

~~~~ {#mycode .haskell .numberLines hl="vim" startFrom="100"}
qsort []     = []
qsort (x:xs) = qsort (filter (< x) xs) ++ [x] ++
               qsort (filter (>= x) xs)
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~

### Small C++ code sample

~~~~ {.cpp hl="vim"}
G4UIQt *  qtSession( dynamic_cast< G4UIQt * >( session ) );

if ( qtSession )
{
    qtSession->AddMenu( histoMenuHandle, histoMenuLabel );
    BuildMenuTree( qtSession, histoMenuHandle, gDirectory->GetList() );
}
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~

### Content of my *~/.vimrc.pandoc*

~~~~ {#vimrc_pandoc .vim .numberLines hl="vim"}
syntax on

filetype on
filetype indent on
filetype plugin on
filetype plugin indent on

let g:lucius_style = 'light'
let g:lucius_contrast = 'high'
let g:lucius_contrast_bg = 'high'

set nocp    " for line breaks with backslashes
colorscheme lucius

let g:PhCtrlTrans = 0
let g:PhHtmlPreAttrs = 'style="white-space: pre-wrap;"'

runtime plugin/publish_helper.vim
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
Это синтаксическая подсветка цветовой схемы lucius для типа файла pandoc, который будет доступен, если вы установите плагин для pandoc отсюда. Я вставил ее сюда из vim дословно с помощью команды MakeHtmlCodeHighlight.

Идем дальше. Сохраняем этот пример в файле example.md и генерируем HTML файл example.html с помощью pandoc и фильтра vimhl из нашего плагина.
pandoc --standalone -t html -F vimhl -o example.html example.md
Получаем файл example.html с таким содержимым:
 

Original example from Pandoc User's Guide

100  qsort []     = []
101  qsort (x:xs) = qsort (filter (< x) xs) ++ [x] ++
102                 qsort (filter (>= x) xs)

Small C++ code sample

G4UIQt *  qtSession( dynamic_cast< G4UIQt * >( session ) );

if ( qtSession )
{
    qtSession->AddMenu( histoMenuHandle, histoMenuLabel );
    BuildMenuTree( qtSession, histoMenuHandle, gDirectory->GetList() );
}

Content of my ~/.vimrc.pandoc

 1  syntax on
 2  
 3  filetype on
 4  filetype indent on
 5  filetype plugin on
 6  filetype plugin indent on
 7  
 8  let g:lucius_style = 'light'
 9  let g:lucius_contrast = 'high'
10  let g:lucius_contrast_bg = 'high'
11  
12  set nocp    " for line breaks with backslashes
13  colorscheme lucius
14  
15  let g:PhCtrlTrans = 0
16  let g:PhHtmlPreAttrs = 'style="white-space: pre-wrap;"'
17  
18  runtime plugin/publish_helper.vim

Это цвета схемы lucius с правильной подсветкой синтаксиса всех образцов. А теперь создадим аналогичный PDF документ.
pandoc --standalone -t latex -F vimhl -o example.tex example.md
Открываем файл example.tex и вставляем в преамбулу строки
\usepackage{color}
\usepackage{xcolor}
\usepackage{fancyvrb}
\newcommand{\VerbBar}{|}
\newcommand{\VERB}{\Verb[commandchars=\\\{\}]}
\DefineVerbatimEnvironment{Highlighting}{Verbatim}{commandchars=\\\{\}}
\usepackage{framed}
\newenvironment{Shaded}{
  \definecolor{shadecolor}{rgb}{1.0, 1.0, 0.9}
  \setlength\parskip{0cm}
  \setlength\partopsep{-\topsep}
  \addtolength\partopsep{0.2cm}
  \begin{shaded}
    \scriptsize
}{\end{shaded}}
Зачем это нужно, вы можете прочесть в документации к плагину. Завершаем генерацию example.pdf.
latexmk -pdf example
Вот скриншот получившегося документа: