четверг, 29 ноября 2012 г.

Закрашивание полей в linux консоли - баг или фича?

Пока разбирался с Powerline в linux консоли, обнаружил весьма интересный и поучительный баг. Поучительный - потому что пришлось просмотреть исходный код сразу четырех компонентов - плагина Powerline, vim, эмулятора видеотерминала и framebuffer драйвера в ядре linux. Баг проявляется при переключении между виртуальными консолями (в т.ч. графической) и заключается в закрашивании одного из полей монитора каким-либо цветом. На этой картинке цветная полоса находится внизу. Как это воспроизвести: запустите vim в консоли, но таким образом, чтобы сразу появлялась цветная статусная строка, для этого в $HOME/.vimrc должна присутствовать строка
set laststatus=2
Затем сразу же переключитесь в другую консоль и обратно - вы получите полосу, закрашенную цветом статусной строки в одном из полей консоли. Если теперь вы закроете vim и снова переключитесь в другую консоль и обратно, то цветная полоса уйдет.

Сначала я подумал, что во всем виноваты Powerline или vim, но потом до меня дошло, что user space программа не может быть виновна в закрашивании области, которая ей даже не доступна! Значит проблема в ядре, в частности в эмуляторе видеотерминала или драйвере фреймбуфера. В подтверждение этому - следующий эксперимент.

Запустите в консоли любую псевдографическую программу, например в своей системе я нашел программу system-config-firewall-tui. Программа переведет консоль в псевдографический режим с синим полем и тремя кнопками (одна из них - Отмена, так что ничего страшного после ее запуска не не произойдет). Внимательно посмотрите на края монитора. Теперь переключитесь в другую консоль и обратно. Количество синего увеличилось, не так ли? Закройте программу и нажмите несколько раз Enter чтобы добавить черного цвета снизу. Видите точно такую же полосу, как в vim, только синего цвета? Еще одно переключение туда-обратно сотрет ее.

Изучение кода ядра вскрыло проблему. Оказывается, при переключении консоли в функции fbcon_switch() (см. drivers/video/console/fbcon.c) вызывается функция fbcon_clear_margins(), которая вызывает метод ops->clear_margins(), который, в свою очередь, закрашивает поля (margins, но как правило, это одно поле) цветом vc->vc_video_erase_char (см. функцию ud_clear_margins() в drivers/video/console/fbcon_ud.c и подобные им в других подобных файлах, которые в свою очередь вызывают функцию attr_col_ec() из drivers/video/console/fbcon.h). Проблема в том, что цвет  vc->vc_video_erase_char в псевдографике может зависеть от погоды и расположения звезд.

Зачем это сделано? Как я понял, некоторые режимы фреймбуфера могут использовать поля монитора, и при переключении в другую консоль эти поля нужно очищать, чтобы не наблюдать все время, например, кусок застывшего видео сбоку или снизу. Как очищать, каким цветом? В интернете я нашел патч, написанный еще в 2003 году, в котором предлагалось закрашивать поля черным цветом (см. здесь и здесь), однако, как я понял из дискуссии, использовать черный цвет не всегда правильно, поскольку графические дисплеи могут быть разные. Таким образом, закрашивание цветом erase char - это меньшее из зол.

воскресенье, 25 ноября 2012 г.

Шрифты Powerline в linux консоли

Отвечая на вопрос о шрифтах Powerline в linux консоли (см. комментарии к этому посту), я привел варианты, как этого можно добиться. Ни один вариант для меня не подошел в силу разных обстоятельств. Добавлять новые символы в консоли оказалось вообще не просто и какого-то единого алгоритма попросту не существует. Но мне все-таки удалось с этим справиться, и здесь я хочу поведать об этой истории успеха. Все, что здесь будет сказано, относится к Fedora 17, для других дистрибутивов действия могут слегка отличаться.


Картинка большая и кликабельная. Символы из Powerline видны в статусной строке vim (там где им и положено находиться), а также внутри присваиваемого значения переменной PROMPTLINE в функции proml редактируемого файла .bashrc (курсор находится между словами function и proml, создавая впечатление, что это одно слово, соединенное символом подчеркивания). Цветов в статусной строке нет по той простой причине, что в дефолтной схеме Powerline используется 256-цветная палитра, которая не работает в linux консоли. Так что если вы хотите нормальную цветную статусную строку Powerline в linux консоли, вам придется изменить дефолтную цветовую схему Powerline, или создать новую (схемы находятся в директории $HOME/.vim/autoload/Powerline/Colorschemes/). Также хочу отметить, что эта картинка была получена с помощью замечательной программы fbgrab из пакета fbcat, который я взял отсюда (кликнув на строке Fedora 17).

Итак, что нам понадобится для создания нового шрифта с символами из Powerline для linux консоли? Прежде всего, понимание того, что нам нужен шрифт с весьма специфичным форматом PSF, который, хотя и поддерживает Unicode mapping, все же не может содержать более 512 символов. В Fedora 17 psf шрифты находятся в директории /lib/kbd/consolefonts/. При включении режима Unicode в консоли c помощью команды unicode_start, попросту загружается новый шрифт с необходимыми символами, в частности у меня в Fedora это /lib/kbd/consolefonts/UniCyr_8x16.psf.gz, который содержит всего 256 символов. Если в вашей консоли используется шрифт terminus, то название файла будет, естественно, другим. Таблица соответствия с символами Unicode прописана внутри файла шрифта, и просмотреть ее можно с помощью команды psfgettable. Таким образом, задача сводится к созданию нового psf файла, в который добавлены символы из Powerline, при этом общее количество символов в файле не должно превышать 512. В дальнейшем, в качестве базового шрифта я буду использовать UniCyr_8x16.psf.

Что нам понадобится из программного обеспечения? Первое - программа otf2bdf для создания шрифта в промежуточном формате bdf: ее в Fedora 17 можно установить с помощью yum. Второе - программа gbdfed для конвертации шрифта bdf в формат psf: она также устанавливается с помощью yum. Третье - пакет PSF tools для манипуляции с psf шрифтами: его я скачал отсюда. Четвертое - удобный текстовый редактор (например, vim) для удаления артефактов конвертации в текстовом представлении шрифта. Пятое - исходный файл с базовыми кириллическими символами, в который мы внедрим новые символы Powerline, как я уже говорил это будет файл UniCyr_8x16.psf из директории /lib/lbd/consolefonts/.

Итак, приступим. Первое, что мы сделаем - это выделим все символы из Powerline (всего их девять) в отдельный шрифт в формате bdf. Эта задача вполне по плечу программе otf2bdf. Исходный файл должен быть получен с помощью программы  fontpatcher из плагина Powerline и иметь формат otf или ttf (см. подробности здесь). У меня данный файл называется DejaVuSansMonoForPowerline.ttf. Программа otf2bdf должна знать, какие символы нам нужны, а также соответствующие им значения Unicode. Для этого мы создадим вспомогательный файл dvusmpl.bdfmap (название не имеет значения), в котором зададим этот маппинг:
REGISTRY ISO10646
ENCODING 1
0x60 0x2B60
0x61 0x2B61
0x62 0x2B62
0x63 0x2B63
0x64 0x2B64
0x80 0x2B80
0x81 0x2B81
0x82 0x2B82
0x83 0x2B83
Строки REGISTRY и ENCODING могут содержать достаточно произвольные значения: в нашем случае они не будут важны. Далее прописан маппинг номер символа : значение Unicode. Номера символов в принципе тоже не имеют значения, однако здесь я сопоставил их со значениями Unicode, которые соответствуют символам Powerline. Первая попытка:
otf2bdf -v -c M -l '96_131' -o dvusmpl.bdf -m dvusmpl.bdfmap DejaVuSansMonoForPowerline.ttf
не увенчается успехом из-за размера сгенерированного шрифта, который окажется 12x20 вместо 8x16 (bdf формат имеет текстовое представление и размер шрифта можно найти в строке FONTBOUNDINGBOX в заголовке файла). otf2bdf позволяет настроить размер шрифта с помощью опций -rh и -rv, значения которых, однако, соответствуют не количеству пикселов по горизонтали и вертикали, а количеству точек на дюйм, поэтому поиск подходящих значений - задача творческая. После нескольких попыток я наконец подобрал подходящие значения для этих опций:
otf2bdf -v -c M -rh 66 -rv 86 -l '96_131' -o dvusmpl.bdf -m dvusmpl.bdfmap DejaVuSansMonoForPowerline.ttf
Опция -c M указывает на то, что мы хотим создать моноширинный шрифт, а опция -l '96_131' задает диапазон символов для генерации (десятиричное число 91 соответствует шестнадцатиричному 0x60, a 131 - 0x83).

Теперь у нас есть файл c символами Powerline в формате bdf. Открываем его в программе gbdfed, убеждаемся, что все 9 символов на месте (скорее всего gbdfed разобьет шрифт на 2 страницы, при этом первые 5 символов окажутся на первой, а оставшиеся 4 - на второй), и экспортируем его в формат PSF (в меню File программы). После экспорта файла у нас появится файл dvusmpl.psfu.

Следующий шаг - переводим файл dvusmpl.psfu в текстовый формат с помощью программы psf2txt из PSF Tools.
psf2txt dvusmpl.psfu dvusmpl.txt
Открываем dvusmpl.txt в текстовом редакторе и правим псевдографические изображения символов по своему вкусу: это необходимо, так как odf2bdf в процессе масштабирования шрифта исказила исходные символы (главным образом стрелки). Поскольку я все это уже проделал, то результат (с дополнительными правками - см. ниже) проще скачать отсюда.

Теперь берем исходный PSF файл UniCyr_8x16.psf.gz, раззиповываем его и переводим в текстовый формат:
psf2txt UniCyr_8x16.psf UniCyr_8x16ForPowerline.txt
В конец нового файла UniCyr_8x16ForPowerline.txt вставляем содержимое файла dvusmpl.txt, меняем  строки // Character 0 .. 8 во вставленном участке на // Character 256 .. 264. Также добавляем во вставленном участке строки с Unicode информацией для каждого отдельного символа таким же образом, как это сделано для символов исходного psf шрифта. Если вы скачали выложенный мною файл dvusmpl.txt, то достаточно просто добавить его в конец файла UniCyr_8x16ForPowerline.txt, так как все необходимые изменения в нем уже есть. Теперь идем в начало файла, ищем строку Length: 256 и заменяем ее на Length: 265 (мы ведь добавили 9 новых символов).

Переводим UniCyr_8x16ForPowerline.txt в формат psf c помощью программы txt2psf из PSF Tools:
txt2psf UniCyr_8x16ForPowerline.txt UniCyr_8x16ForPowerline.psf
Можно убедиться, что новые символы присутствуют в UniCyr_8x16ForPowerline.psf:
psfgettable PowerLineSymbols1.psf | grep -i 2b80
Зипуем файл и кладем его в директорию /lib/kbd/consolefonts/. Логинимся в виртуальной консоли и загружаем новый шрифт:
unicode_start UniCyr_8x16ForPowerline
Теперь открываем vim и смотрим, присутствуют ли в статусной строке символы из Powerline.

Если есть желание загружать новый шрифт каждый раз при входе в систему из виртуальной консоли, то можно добавить строку unicode_start UniCyr_8x16ForPowerline для linux консоли в какой-нибудь файл из директории /etc/profile.d/ (см. подробно здесь). Кроме того, теперь и в linux консоли вы сможете сделать приглашение командной строки более привлекательным (см. здесь).

Update. Выяснилось, что дефолтный консольный Unicode шрифт в Fedora 17 - это latarcyrheb-sun16 (загляните внутрь скрипта /usr/bin/unicode_start и поймете почему). В шрифте определены 512 символов, поэтому, если брать его за основу, следует не добавлять дополнительные символы, а заменить те из существующих, которые вы вряд ли будете использовать, например в позициях 460 - 488 находятся символы арабского алфавита, и если вы не читаете тексты на арабском, то можете смело вставлять новые символы куда-нибудь внутрь этого диапазона.

Последние изменения. Картинка:


Все цвета в статусной строке присутствуют, Unicode символы на месте (в т.ч. стрелочки в окне tagbar), я специально вышел из vim, чтобы показать, что в строке приглашения тоже находятся символы из Powerline.

За базовый шрифт я взял /lib/kbd/consolefonts/latarcyrheb-sun16.psfu.gz. Распаковываем, переводим в текст с помощью psf2txt, заменяем символы с 460 по 472 этим. Переводим обратно в psf:
txt2psf latarcyrheb-sun16.txt latarcyrheb-sun16_vim-unicode.psfu
Зипуем latarcyrheb-sun16_vim-unicode.psfu и кладем в /lib/kbd/consolefonts/. Файл /etc/profile.d/console_unicode.sh теперь выглядит так:
if [ "$TERM" = "linux" ]; then
    unicode_start latarcyrheb-sun16_vim-unicode
    export CONSOLEFONT_HAS_VIM_UNICODE_SYMB=1
fi
Изменения в файле $HOME/.bashrc:
case $COLORTERM in
    gnome*|mate*|konsole*)
        TERM=xterm-256color
        ;;
esac

[ -n "$XTERM_SHELL" ] && COLORTERM=xterm-256color

function proml
{
    case $COLORTERM in
        gnome*|mate*|konsole*)
            local PROMPTLINE="\[\033[38;5;167m\]\$(date +%d/%m/%y\ %H:%M)⮁⮁ \
\[\033[38;5;173m\]\u@\[\033[38;5;140m\]\h⮁⮁\[\033[38;5;173m\] \W \[\033[0m\] "
            local PROMPTLINE2='\[\033[38;5;196m\]⮀\[\033[0m\] '
            ;;
        xterm*)
            local PROMPTLINE="\[\033[38;5;167m\](\$(date +%d/%m/%y\ %H:%M))\
\[\033[38;5;173m\][\u@\[\033[38;5;140m\]\h\[\033[38;5;173m\] \W]$\[\033[0m\] "
            local PROMPTLINE2='\[\033[38;5;196m\]>\[\033[0m\] '
            ;;
        *)
            if [ "$TERM" = "linux" ] && \
               [ -n "$CONSOLEFONT_HAS_VIM_UNICODE_SYMB" ] ; then
                local PROMPTLINE="\[\033[32m\]\$(date +%d/%m/%y\ %H:%M)⮁⮁ \
\[\033[0m\]\u@\[\[\033[1;34m\]\h\[\033[0m\]⮁⮁\[ \W \033[32m\]\[\033[0m\] "
                local PROMPTLINE2='\[\033[31m\]⮀\[\033[0m\] '
            else
                local PROMPTLINE="(\$(date +%d/%m/%y\ %H:%M))[\u@\h \W]$ "
                local PROMPTLINE2=''
            fi
            ;;
    esac
    PS1=$PROMPTLINE
    PS2=$PROMPTLINE2
}

proml
unset proml
Изменения в $HOME/.vimrc:
" ---- Powerline settings
" ----
" disable Unicode symbols in linux console if font does not support them
let g:DisableUnicodeSymbols = &term =~? '^linux' &&
            \ empty($CONSOLEFONT_HAS_VIM_UNICODE_SYMB)

let g:Powerline_theme = 'default'

" solarized theme is suitable for linux console
if &t_Co < 256
    let g:Powerline_colorscheme = 'solarized'
else
    let g:Powerline_colorscheme = 'default'
endif

" fancy symbols need patched font!
if g:DisableUnicodeSymbols
    let g:Powerline_symbols = 'compatible'
else
    let g:Powerline_symbols = 'fancy'
endif

" do not use Powerline in simple console terminals
let g:DisablePowerline = &term =~? '^linux' && 0

if g:DisablePowerline
    let g:Powerline_loaded = 0
endif

if !empty($TMPDIR)
    let g:Powerline_cache_file = $TMPDIR."/Powerline_".$USER."_".
                \ g:Powerline_theme."_".g:Powerline_colorscheme."_".
                \ g:Powerline_symbols.".cache"
else
    let g:Powerline_cache_file = '/tmp/Powerline_'.$USER.'_'.
                \ g:Powerline_theme.'_'.g:Powerline_colorscheme.'_'.
                \ g:Powerline_symbols.'.cache'
endif
(я сюда еще правильную обработку кэша цветовой схемы Powerline добавил).

Да, и самое главное - патч для  цветовой схемы solarized.vim. Без него статусная строка Powerline в консоли правильно работать не будет!
--- autoload/Powerline/Colorschemes/solarized.vim   2012-11-27 13:46:28.923529118 +0400
+++ autoload/Powerline/Colorschemes/solarized.vim.new   2012-11-27 13:35:09.794349678 +0400
@@ -3,19 +3,19 @@
 " N = no focus
 " 16 hex colors as defined on http://ethanschoonover.com/solarized
 call Pl#Hi#Allocate({
-  \ 'base03'  : [8,   0x002b36],
+  \ 'base03'  : [&t_Co > 8 ? 8 : '0*',   0x002b36],
   \ 'base02'  : [0,   0x073642],
-  \ 'base01'  : [10,  0x586e75],
-  \ 'base00'  : [11,  0x657b83],
-  \ 'base0'   : [12,  0x839496],
-  \ 'base1'   : [14,  0x93a1a1],
+  \ 'base01'  : [&t_Co > 8 ? 10 : '2*',  0x586e75],
+  \ 'base00'  : [&t_Co > 8 ? 11 : '6*',  0x657b83],
+  \ 'base0'   : [&t_Co > 8 ? 12 : '1*',  0x839496],
+  \ 'base1'   : [&t_Co > 8 ? 14 : '3*',  0x93a1a1],
   \ 'base2'   : [7,   0xeee8d5],
-  \ 'base3'   : [15,  0xfdf6e3],
+  \ 'base3'   : [&t_Co > 8 ? 15 : '7*',  0xfdf6e3],
   \ 'yellow'  : [3,   0xb58900],
-  \ 'orange'  : [9,   0xcb4b16],
+  \ 'orange'  : [&t_Co > 8 ? 9 : '4*',   0xcb4b16],
   \ 'red'     : [1,   0xdc322f],
   \ 'magenta' : [5,   0xd33682],
-  \ 'violet'  : [13,  0x6c71c4],
+  \ 'violet'  : [&t_Co > 8 ? 13 : '5*',  0x6c71c4],
   \ 'blue'    : [4,   0x268bd2],
   \ 'cyan'    : [6,   0x2aa198],
   \ 'green'   : [2,   0x859900],
(зафайлил баг в Powerline, надеюсь починят).

среда, 21 ноября 2012 г.

Unicode в linux консоли Fedora 17

Отвечал сегодня на вопрос о шрифтах в linux консоли (если кто не в курсе, linux консоль - это виртуальный терминал, в который из графического десктопа можно попасть с помощью сочетания клавиш Ctrl-Alt-Fn, где n=2,3,4...). Заодно решил разобраться с поддержкой Unicode, из-за отсутствия которой в последних версиях Fedora linux консоль отображает русские символы в виде прямоугольников. В принципе, это терпимо до тех пор, пока какая-нибудь программа не выдаст сообщение об ошибке на русском языке, которое совершенно невозможно прочесть, или пока вы не решите перейти в директорию с русским названием. По всей видимости, проблема эта достаточно серьезная, если даже в Russian Fedora она все еще не решена. И появилась она, видимо, в момент перехода Fedora на systemd.

Простейший костыль - поместить в директорию /etc/profile.d/ файл console_unicode.sh со следующим содержимым:
if [ "$TERM" = "linux" ]; then
    unicode_start
fi
Это решает проблему при логине в linux консоли, но все же это не лучшее решение. Про лучшее решение интернет пока молчит.

суббота, 17 ноября 2012 г.

Статистика, diff, подсветка

Здесь я уже рассказывал, как с помощью утилиты diffstat можно выводить статистические данные по коммитам в Subversion, систематизированные по отдельным пользователям. На этот раз я хочу расширить возможности данного подхода путем абстрагирования параметров систематизации и улучшения пользовательского интерфейса. Вторая задача, в частности, означает, что команда-однострочник будет заменена программой с возможностью задания различных опций. Программа представляет собой bash-скрипт и выглядит так:
#!/bin/bash - 

PrintUsage()
{
    echo "Usage: `basename $0` [-m] [-l level] [-p pattern] [-w width] [path]"
    echo "  -m          calulate modifications, insertions and deletions"
    echo "  -l level    history level of svn log command"
    echo "  -p pattern  filter commits according pattern"
    echo "  -w width    width of diffstat histogram"
    echo "  path        in specified file or directory only"
}

while [ $1 ]
do
    case $1 in
        -h|-help|--help)    PrintUsage ; exit 0 ;;
        -m)                 modifications=-m ;;
        -l)                 shift ; level="-l $1" ;;
        -p)                 shift ; pattern=$1 ;;
        -w)                 shift ; width="-w $1" ;;
        *)                  path=$path' '$1 ;;
    esac
    shift
done

for revision in \
    `svn log $level $path | grep '^r[0-9]\+.*'$pattern | awk '{print $1}' |\
     sed 's/r//g'`
do
    svn diff -c$revision $path
done | diffstat -v $modifications -f 4 $width
Львиную долю кода составляет функция PrintUsage() и разбор опций командной строки. Основной код размещен внутри цикла for и представляет собой ту же самую, слегка видоизмененную, однострочную команду. Кстати, PrintUsage() дает полное представления об опциях программы и я не буду на них останавливаться. Я поместил этот код внутрь исполняемого файла svndiffstat в директории $HOME/bin/.

Теперь мы сделаем так, чтобы вывод скрипта был подсвечен. Я показывал, как этого можно добиться с помощью программы hl здесь и здесь. Следуя данной схеме, определим сниппет dstat в $HOME/.hlrc:
snippet dstat   -196 '\++[!-]*$' '(?<=\|)\s*\d+\s*\d+' '\d+ (?=insertion)' \
                '(?<=\()\+(?=\))' \
                -33 '\-+!*$' '(?<=\|)\s*\d+\s*\d+\s*\d+' '\d+ (?=deletion)' \
                '(?<=\()-(?=\))' \
                -30 '!+$' '(?<=\|)\s*\d+\s*\d+\s*\d+\s*\d+' \
                '\d+ (?=modification)' '(?<=\()!(?=\))' \
                -101 '(?<=\|)\s*\d+' -82 '\s+\|\s+' -155 '/' -215 '[^/]+\s+\|' \
                -203 '^\s*[^/]+/' -155 '\d+ (?=file)'
и новую функцию svndiffstat в $HOME/.hl_functions:
function svndiffstat
{
    `env which svndiffstat` $@ | hl -sdstat
}
Результат работы svndiffstat представлен на картинке:


Здесь была запрошена статистика по коммитам за февраль 2011 года. Фактически значение опции -p задает регулярное выражение для поиска внутри строки, выводимой командой svn log и начинающейся с номера ревизии, такая строка включает имя пользователя, дату коммита и число измененных строк: это и есть параметры, по которым можно осуществлять выборку. Представленная статистика была собрана в соответствии с метаданными svn, находящимися внутри текущей директории, кроме того, svndiffstat позволяет изменить директорию для сбора статистики, если таковая будет задана в конце списка аргументов командной строки.

Кстати говоря, поскольку программа hl не включает подсветку при выводе в пайп, то можно изменять правила подсветки вывода svndiffstat, например добавить подсветку слова Energy в примере на картинке:
$ svndiffstat -p 2011-02 -w 60 -m | hl -sdstat -98 Energy
Раз уж речь зашла о diff и подсветке вывода программ, то хотелось бы привести соответствующие настройки .hlrc и .hl_functions. (Кстати, раньше для подсветки diff я использовал программу colordiff. Однако colordiff имела существенный недостаток: невозможность использования всех цветов на 256-цветных терминалах.)

Итак, в .hlrc следует добавить
snippet diff    -155 -b '^Index:\s+.*' '^=+$' '^diff.*' '^Binary.*' \
                -180 '^Только.*' '^\\.*' -76 '^@.*' '^\d.*' -rb -196 '^[+>].*' \
                -26 '^[-<].*'
a в .hl_functions
function diff
{
    `env which diff` $@ | hl -sdiff
}

function svndiff
{
    svn diff $@ | hl -sdiff
}
Теперь вывод diff будет всегда подсвечиваться на экране терминала. Кроме того, здесь определена еще одна функция svndiff для подсветки, как не трудно догадаться, вывода команды svn diff. Кстати, наверняка нам понадобятся аналогичные определения cvsdiff, gitdiff, hgdiff и т.п. Чтобы не плодить множество подобных функций, можно воспользоваться командой оболочки eval внутри цикла for по всем типам VCS, которая определит все функции за нас:
for vcs in cvs svn hg git ; do
    eval "function ${vcs}diff
    {
        $vcs diff \$@ | hl -sdiff
    }"
done
В дополнение к сказанному, хочу затронуть тему вывода на экран терминала подсвеченного программного кода. Универсальные подсветчики типа hl или grc здесь не очень подходят, так как писать сложные правила подсветки для множества языков программирования с использованием одних только регулярных выражений - задача нереальная. Поэтому для этой цели я использую программу GNU source-highlight, обернув ее, как обычно, в новую функцию оболочки. Функцию я назвал cathl и поместил ее в $HOME/.bashrc.
function cathl
{
    source-highlight -f esc -q -i $@
}
Функция cathl выводит на экран терминала содержимое своего аргумента - исходного файла. Если нужно вывести подсвеченный код в другой файл, то следует добавить в список аргументов после (здесь это важно) имени исходного файла опцию -o output-file. Кроме этого в cathl в принципе доступны другие опции source-highlight, например указание языка исходного текста там где это нужно. Однако изменить формат вывода (опция -f) в cathl не получится, так как он уже закреплен за цветовыми escape последовательностями терминала (-f esc).

Главным недостатком source-highlight лично для меня является недоступность всей 256-цветовой палитры для подсветки. Однако в последней версии 3.1.7 этот недостаток отсутствует. У меня в Fedora 17 стоит более старая версия 3.1.4, ну а если вам повезло, или вы сами решите собрать source-highlight из исходников, то можете смело заменять -f esc в cathl на -f esc256.

пятница, 9 ноября 2012 г.

vim: плагин Vimwiki

После перехода из Gnome 3 на MATE встала задача перенести наработанные заметки из gnote в mnote. Задача, как это ни странно, оказалась непосильной. И поэтому я принял решение завязать с использованием десктоп-зависимых приложений для хранения заметок и найти им замену среди плагинов vim. Самым популярным плагином в vim.org, найденным по ключевому слову wiki, оказался vimwiki. Первые шаги в его использовании очень просты: запускаем vim, набираем \ww, подтверждаем, что рабочей директорией vimwiki будет $HOME/vimwiki (или выбираем другую) и можно приступать к редактированию индексной страницы.

Я не хочу вдаваться в подробности редактирования страниц в vimwiki - для этого имеется неплохая документация, да и сам я пока не набрал достаточного опыта. Хочу лишь отметить, что возможности и уровень юзабилити vimwiki весьма высоки: работать с ним действительно приятно и удобно. Дочерние страницы создаются простым нажатием на клавишу Enter, синтаксическая подсветка радует глаз, удобная навигация по иерархии страниц, возможность экспорта в HTML, независимая подсветка фрагментов кода в соответствии с языком, ссылки на ресурсы (в том числе на локальные файлы), которые открываются во внешней программе в соответствии с их типом. Правда, в последнем случае обнаружилась и ложка дегтя: для открытия файлов во внешней программе vimwiki использует команду silent !xdg-open, которая портит терминал (возможно, только у меня), и для того, чтобы вернуть его в нормальное состояние, нужно ввести Ctrl-L. Но это проблема не vimwiki, a vim, да и открывать картинки из vim я не собираюсь.

Ниже я привожу настройки, которые я добавил в файл .vimrc для vimwiki.
" ---- Vimwiki settings
" ----
let g:WikiGlobal = {}
let g:WikiGlobal.nested_syntaxes = {'c''c''c++': 'cpp', 'perl''perl',
            \ 'python''python''sh''sh'}
let g:vimwiki_list = [g:WikiGlobal]

" disable concealing of short links
let g:vimwiki_url_maxsave = 42

" redefine original <C-Up> and <C-Down> mappings in vimwiki buffers
nmap <silent> <Leader>wn <Plug>VimwikiDiaryNextDay
nmap <silent> <Leader>wp <Plug>VimwikiDiaryPrevDay

" add schemes 'vlocal:' and 'vfile:' to open files in a new tab
fun! VimwikiLinkHandler(link)
    let link = a:link
    if link =~ "vlocal:" || link =~ "vfile:"
        let link = link[1:]
    else
        return 0
    endif
    let [idx, scheme, path, subdir, lnk, ext, url] =
                \ vimwiki#base#resolve_scheme(link, 0)
    if g:vimwiki_debug
        echom 'LinkHandler: idx='.idx.', scheme=[v]'.scheme.', path='.path.
                \ ', subdir='.subdir.', lnk='.lnk.', ext='.ext.', url='.url
    endif
    if url == ''
        echom 'Vimwiki Error: Unable to resolve link!'
        return 0
    else
        call vimwiki#base#edit_file('tabnew', url, [], 0)
        return 1
    endif
endfun
Переменная g:WikiGlobal нужна в данном случае только для определения независимой подсветки фрагментов кода внутри страниц (кстати, обратим внимание на некорректную подсветку синтаксиса в районе 'c++': 'cpp'). Например, фрагмент
{{{c++
int main( void )
{
    return 0;
}
}}}
корректно подсвечен как исходник на C++.

Также я увеличил значение переменной g:vimwiki_url_maxsave до 42, чтобы ссылки на ресурсы не срезались до очень коротких, переопределил маппинги для VimwikiDiaryNextDay и VimwikiDiaryPrevDay, которые изначально соответствовали <C-Down> и <C-Up> (а эти комбинации клавиш я использую для навигации между окнами), и добавил функцию-обработчик нажатия на ссылки VimwikiLinkHandler(), в которой определена обработка новых типов ресурсов vlocal и vfile. Дело в том, что по умолчанию vimwiki открывает ссылки на файлы (они соответствуют типам ресурсов local и file) во внешней программе (как я уже говорил, с помощью xdg-open). Ну а обработчик VimwikiLinkHandler() будет открывать ресурсы новых типов vlocal и vfile в отдельной вкладке. Исходный код данного обработчика приведен в документации vimwiki, что подтверждает ее высокий уровень.

Все это здорово, но хотелось бы запускать vimwiki сразу из командной строки. Сделать это просто. Поместим в .bashrc простую функцию:
function vimwiki
{
    if (( $# == 0 )) ; then
        vim -c VimwikiIndex
        return
    fi
    if [ $1 = "-l" ] ; then
        local columns=`stty size | cut -d' ' -f2`
        for in $HOME/vimwiki/*.wiki ; do
            echo `basename $i` | sed 's/\.wiki$//'
        done | COLUMNS=$columns column
        return
    fi
    vim -c VimwikiIndex -c "VimwikiGoto $1"
}
Теперь в нашей оболочке появилась новая команда vimwiki. Если запустить ее без параметров, то откроется индексная страница vimwiki. Кроме того, эта команда позволяет вывести список всех страниц vimwiki, если ей передан аргумент -l (в предположении, что рабочей директорией vimwiki является $HOME/vimwiki), а также открыть vim на определенной странице, когда имя страницы передано ей в первом аргументе.

четверг, 1 ноября 2012 г.

Строка приглашения shell со шрифтами из Powerline

Случайно наткнулся в github на проект powerline-bash, в котором, как я понимаю, предпринята попытка перенести модель статусной строки из плагина для vim Powerline в строку приглашения shell (shell prompt). Идея интересная, однако использование цветных бэкграундов для шрифта противопоказано в моей псевдопрозрачной консоли: результат, скорее всего, будет выглядеть убого. Однако мысль о том, чтобы использовать символы стрелок из шрифта Powerline в строке приглашения, показалась мне перспективной. За сим я решил воплотить ее в жизнь. Результат представлен на картинке.


Полые и закрашенные угловые стрелки  на картинке - это и есть дополнительные символы из Powerline. Полой стрелке соответствует символ Unicode 2b81, закрашенной - 2b80. О том, как установить шрифт Powerline (хотя это совсем не шрифт, а просто дополнительные символы в вашем любимом шрифте, и термин шрифт Powerline - неверная, но удобная замена), я рассказывал здесь. Специально для пользователей Fedora, которые используют шрифты из репозитория Infinality и собирают шрифт Powerline с помощью fontpatcher, хочу напомнить: используйте не стандартный пакет fontforge, а fontforge-infinality!

После установки шрифта добавляем в .bashrc строки
case $COLORTERM in
    gnome*|mate*|konsole*)
        TERM=xterm-256color
        ;;
esac

[ -n "$XTERM_SHELL" ] && COLORTERM=xterm-256color

function proml
{
    case $COLORTERM in
        gnome*|mate*|konsole*)
            local PROMPTLINE="\[\033[38;5;167m\]\$(date +%d/%m/%y\ %H:%M)⮁⮁ \
\[\033[38;5;173m\]\u@\[\033[38;5;140m\]\h⮁⮁\[\033[38;5;173m\] \W \[\033[0m\] "
            local PROMPTLINE2='\[\033[38;5;196m\]⮀\[\033[0m\] '
            ;;
        xterm*)
            local PROMPTLINE="\[\033[38;5;167m\](\$(date +%d/%m/%y\ %H:%M))\
\[\033[38;5;173m\][\u@\[\033[38;5;140m\]\h\[\033[38;5;173m\] \W]$\[\033[0m\] "
            local PROMPTLINE2='\[\033[38;5;196m\]>\[\033[0m\] '
            ;;
        *)
            local PROMPTLINE="(\$(date +%d/%m/%y\ %H:%M))[\u@\h \W]$ "
            local PROMPTLINE2=''
            ;;
    esac
    PS1=$PROMPTLINE
    PS2=$PROMPTLINE2
}

proml
unset proml
В данном коде присутствуют символы Unicode 2b80 и 2b81. Если вы еще не установили шрифт Powerline, то они, скорее всего, не будут похожи на стрелки на вашем экране, а вероятно будут заменены вопросительными знаками или какими-нибудь кракозябрами. Кстати, если вам придется вставлять юникодные символы в vim, то делается это очень просто: в режиме ввода набираем Ctrl-V, затем u, а затем соответствующее символу значение (например 2b80).

Теперь вводим в командной строке
. ~/.bashrc
и наслаждаемся результатом.