воскресенье, 28 августа 2011 г.

Патч для ctags c лучшей поддержкой C++ и исправлениями

На днях залил новый патч для ctags, в котором было исправлено несколько багов, связанных c C++, в том числе один весьма серьезный - c неправильной обработкой символа '<'. Ну а самое главное - это добавление поддержки переменных, инициализируемых конструктором, например
int a( 90 );
SomeType b( "a_word" );
В текущей версии ctags такие переменные просто пропускаются, или иногда рассматриваются как прототипы функций (также см. описание бага здесь).

Патч можно взять здесь. Он может быть наложен как на последнюю стабильную версию ctags, так и на trunk. Вместе c патчем выложен тестовый файл test.cpp, на котором можно опробовать текущую и пропатченную версии ctags и сравнить их.

Если кто-то вдруг захочет опробовать патч и найдет регрессионные ошибки, то я попрошу по возможности прокомментировать их здесь (ну или на трекере ctags). Лично я проверял патч на довольно большом C++ проекте и серьезных регрессий не нашел. Напомню, что для включения в теги локальных переменных и прототипов функций, нужно в ctags добавить опцию --c++-kinds=+pl, кроме того, для получения максимальной информации о тегах можно добавить опцию c перечислением всех возможных полей: --fields=afmikKlnsStz.

пятница, 19 августа 2011 г.

Установка Firefox 6 и Thunderbird 6 в Fedora 14

Да, я так и не перешел на Fedora 15, так как не определился, чем заменить старый добрый Gnome 2. А пользоваться обновленными продуктами от Mozilla очень хочется. Так, недавно вышли очередные мажорные версии Firefox и Thunderbird, обе за номером 6. Естественно, в репозиториях Федоры их нет, зато они нашлись в репозитории Remi. Я не подключал данный репозиторий, поэтому просто скачал соответствующие пакеты (thunderbird, firefox и xulrunner6) и установил их. К сожалению, сразу же всплыла старая проблема с ужасными шрифтами без поддержки Byte Code Interpreter. Насколько я помню, исходный код библиотеки cairo был с изменениями включен в состав Firefox для сборки из-за какого-то странного бага с никому ненужными анимированными файлами gif. Благодаря этому, результирующий вид шрифтов стал зависеть не от того, какие системные библиотеки, отвечающие за рендеринг шрифтов, используются в вашей системе, а от того, какие библиотеки были у сборщика во время компиляции. Таким образом, товарищ Remi Collet при компиляции Firefox и Thunderbird использовал опцию, которая включает измененный вариант cairo, и кроме того, он располагал библиотеками для рендеринга шрифтов (fontconfig и libXft), которые поставлялись с Fedora 14 по умолчанию, что, в общем-то, логично и неудивительно.

Итак, проблема ясна. Будем лечить. Я не захотел собирать firefox и thunderbird с нуля, а решил воспользоваться SRPM пакетами из репозитория Remi. Найти их можно здесь. Очевидно, для того чтобы правильно пересобрать эти пакеты, нужно чтобы соответствующие правильные библиотеки были представлены в вашей  системе. Проще всего это сделать (если вы до сих пор этого не сделали и не используете репозиторий infinality), подключив репозиторий russianfedora-fixes и обновившись через yum update. После обновления будут установлены правильные fontconfig и libXft. Кроме того, для сборки нам понадобятся fontconfig-devel, libXft-devel и, разумеется, cairo-devel:
yum install fontconfig-devel libXft-devel cairo-devel
Перейдем к сборке rpm. Если вы еще не завели специального псевдопользователя для этой цели (а так рекомендует Федора), то сейчас самое время. От имени суперпользователя вводим
useradd mockbuild
Я назвал его mockbuild, хотя конкретное имя ничего не значит, можно выбрать на свой вкус. Задаем ему какой-нибудь пароль:
passwd mockbuild
Для того, чтобы пользователь-сборщик не появился в списке приглашения GDM, откроем файл /usr/share/gdm/gdm.schemas, найдем там строку <key>greeter/Exclude</key> и добавим в список пользователей ниже внутри тегов <default>...</default> нашего mockbuild.

Теперь нужно установить пакет для поддержки сборки rpm пакетов:
yum install rpmdevtools
Затем логинимся как mockbuild:
su - mockbuild
и создаем дерево директорий для сборки пакетов:
rpmdev-setuptree
Устанавливаем скачанные пакеты SRPM (от имени mockbuild, конечно) с помощью простой команды
rpm -ivh firefox-6.0-1.remi.src.rpm xulrunner6-6.0-1.remi.src.rpm thunderbird-6.0-1.remi.src.rpm
После этой команды исходники попадают в rpmbuild/SOURCES, а spec файлы - в директорию rpmbuild/SPECS. Переходим в директорию со spec файлами и правим. В файлах firefox6.spec и xulrunner6.spec после строки
echo "ac_add_options --enable-system-lcms" >> .mozconfig
добавляем строку
echo "ac_add_options --enable-system-cairo" >> .mozconfig
а в файле thunderbird.spec добавляем строку
ac_add_options --enable-system-cairo
где-нибудь между строками
cat <<EOF | tee -a .mozconfig
и
EOF
(например в самом конце, прямо перед строкой EOF).

Возможно, для сборки вам придется установить дополнительные пакеты. Так, мне пришлось установить autoconf213, lcms-devel, wireless-tools-devel и libvpx-devel. С помощью yum это сделать несложно.

Переходим к построению rpm, последовательно запуская команды
rpmbuild -ba xulrunner6.spec
rpmbuild -ba thunderbird.spec
В списке нет firefox, поскольку он зависит от построенного xulrunner6, поэтому, когда эти две команды отработают (а ждать нужно достаточно долго), переходим в директорию /home/mockbuild/rpmbuild/RPMS/your_arch, где your_arch - архитектура вашей системы (например, x86_64) и от имени суперпользователя обновляем свежесобранные пакеты:
rpm -Uvh thunderbird-6.0-1.fc14.x86_64.rpm  xulrunner6-6.0-1.fc14.x86_64.rpm xulrunner6-devel-6.0-1.fc14.x86_64.rpm
Если в вашей системе уже стоит xulrunner-devel, то его нужно будет предварительно удалить, так как он конфликтует с xulrunner6-devel. После установки новых пакетов возвращаемся в директорию SPECS и от имени mockbuild строим firefox:
rpmbuild -ba firefox6.spec
После завершения сборки снова возвращаемся в директорию RPMS и от имени суперпользователя обновляем firefox:
rpm -Uvh firefox-6.0-1.fc14.x86_64.rpm
После установки запускаем новые firefox и thunderbird и наслаждаемся правильным рендерингом шрифтов и проделанной работой :)

суббота, 13 августа 2011 г.

vim: практические шаги для комфортного использования 2

Продолжение темы советов по кастомизации vim, которые помогут повысить комфорт при использовании этого замечательного редактора. На этот раз ничего патчить не будем :)

1. Новый маппинг для плагина Mark. Плагин Mark позволяет легко подсвечивать слова или паттерны, соответствующие регулярным выражениям. В отличие от встроенного механизма поиска и подсветки, Mark позволяет одновременно подсвечивать разными цветами несколько слов или паттернов. К сожалению, маппинг, принятый в Mark по умолчанию, на мой взгляд, требует изменения. Именно благодаря неудачному маппингу я некоторое время не пользовался этим плагином, хотя он и был установлен мною собственноручно.

Итак, маппинг по умолчанию: \m - подсветить слово, \n - снять подсветку, \r - ввести паттерн для подсветки. Этот маппинг ужасно тормозил (требовалось ждать около секунды, чтобы изменения вступили в силу), и я сначала думал, что это особенность реализации подсветки в Mark. Но проблема оказалась весьма тонкой. Выяснилось, что маппинг пересекается с маппингом плагина c.vim и еще одного установленного мною плагина. Так, в c.vim определено множество маппингов, начинающихся с \n и \r (\nc, \ns, \ni и т.д.). Таким образом, при вводе символов, соответствующих маппингу Mark, vim некоторое время ждал, а не введет пользователь еще один символ, который можно будет интерпретировать как маппинг из c.vim - это нормальное поведение vim.

Чтобы в следующий раз не наступать на грабли, можно посмотреть все маппинги, начинающиеся с определенного символа, скажем с \n (первый символ \ как таковой в маппинг не входит, так как является спецсимволом - так называемым лидером). Для этого нужно в командной строке vim ввести
:map \n
В новом маппинге я задействовал сочетание клавиш Ctrl-m:
nmap <C-m><C-m> <Plug>MarkSet
nmap <C-m>n     <Plug>MarkClear
nmap <C-m>r     <Plug>MarkRegex
vmap <C-m><C-m> <Plug>MarkSet
vmap <C-m>r     <Plug>MarkRegex
nmap <C-m>*     <Plug>MarkSearchCurrentNext
nmap <C-m>#     <Plug>MarkSearchCurrentPrev
nmap <C-m>/     <Plug>MarkSearchAnyNext
nmap <C-m>?     <Plug>MarkSearchAnyPrev
nmap <C-m>c     <Plug>MarkAllClear
nmap <C-m>t     <Plug>MarkToggle
nmap <C-m>s     :MarkSave<CR>
nmap <C-m>l     :MarkLoad<CR>
В принципе, тут все понятно: вместо \m нужно, удерживая клавишу Ctrl, два раза нажать m, вместо \n - нажать Ctrl-m, а затем n, вместо \r - Ctrl-m, а затем r. Кроме того, я добавил маппинг для переключения подсветки, которого нет по умолчанию: <Ctrl-m>t.

Update. По иронии судьбы Ctrl-m тоже плохой маппинг, так как это сочетание в vim полностью соответствует клавише Enter. Так что если оставить такой маппинг, то Enter в нормальном режиме будет так же тормозить, как раньше тормозил сам Mark. Клавиша Enter потенциально используется в vim достаточно часто, например в плагине taglist при переходе к метке под курсором. Поэтому в приведенных выше маппингах следует заменить все вхождения <C-m> на какое-нибудь другое, неиспользуемое сочетание клавиш, например на <C-k>.

2. Подсветка областей кода, не соответствующих принятому стандарту. Несмотря на грозное название, здесь имеется ввиду очень простая вещь. Допустим, вам требуется убедиться, что код, который вы в данный момент редактируете или только что открыли, не содержит знаков табуляции, все строки завершаются не пробельными символами и их длина не превышает 80 знаков. Для этого вам нужно иметь возможность подсвечивать паттерны регулярных выражений, соответствующих указанным требованиям. Сами паттерны не такие уж сложные, однако вы предпочитаете делать это простым нажатием пары клавиш и, кроме того, таким же простым нажатием убирать подсветку.

Здесь я покажу, как это сделать с помощью простого маппинга ,s (запятая - s), который будет включать или выключать соответствующую подсветку в зависимости от текущего состояния (т.е. реализует то, что в английском языке называется простым словом toggle).

Прежде всего объявим переменную g:RightBorder, которая будет содержать максимальную разрешенную длину строки (т.е. 80), и новую область подсветки FormatHints, с помощью которой мы будем подсвечивать код, не соответствующий стандарту:
let g:RightBorder = 80
highlight FormatHints term=standout ctermfg=250 ctermbg=229
            \ guifg=Red guibg=White
Определение FormatHints разделено на две строки как результат следования внутренней дисциплине и установленному правилу непревышения строкой 80 символов :) Здесь с помощью аргументов ctermfg и ctermbg определена подсветка для терминалов с поддержкой 256 цветов, которая, на мой взгляд, вполне подходит для темных цветовых схем (светло-серый текст на бледно-желтом фоне: в конце концов мы подсвечиваем "запрещенные" участки кода, поэтому светлое на светлом здесь вполне уместно). Поскольку я не использую GUI vim, значения для guibg и guifg выбраны произвольно.

Теперь определим функции для включения и выключения подсветки:
fun<SID>formathints()
    if !exists("w:m1") || w:m1 == 0
        let w:m1 = matchadd('FormatHints''\%>'.g:RightBorder.'v.\+'-1)
        let w:m2 = matchadd('FormatHints''[\t]'-1)
        let w:m3 = matchadd('FormatHints''[\t \r]\+$'-1)
    endif
endfun
fun<SID>formathints_hide()
    if exists("w:m1") && w:m1 > 0
        silentcall matchdelete(w:m1)
        silentcall matchdelete(w:m2)
        silentcall matchdelete(w:m3)
        let w:m1 = 0
        let w:m2 = 0
        let w:m3 = 0
    endif
endfun
С помощью переменных w:m1, w:m2 и w:m3 определяются паттерны, соответствующие областям кода, которые мы хотим подсвечивать. Приставка к переменной w: в vim означает, что будут созданы отдельные инстансы этой переменной в каждом отдельном окне vim, соответственно, наше переключение подсветки будет работать только в активном окне.

Теперь определим команды для включения и выключения подсветки:
command -bar ShowFormatHints call <SID>formathints()
command -bar HideFormatHints call <SID>formathints_hide()
Опция -bar нужна для использования команд в составе сложных выражений, содержащих элементы <Bar> в следующем итоговом маппинге:
nmap <silent> ,s :if !exists("w:m1") <Bar><Bar> w:m1 == 0 <Bar>
            \ ShowFormatHints <Bar> echo "Show format hints" <Bar> else <Bar>
            \ HideFormatHints <Bar> echo "Hide format hints" <Bar> endif<CR>
Напомним, что элементы <Bar> в маппингах расширяются в символ |, следовательно, без учета echo "Show format hints" и echo "Hide format hints", этому маппингу будет соответствовать команда
:if !exists("w:m1") || w:m1 == 0 | ShowFormatHints | else | HideFormatHints | endif
Если оставить этот маппинг без модификатора <silent> (и без дополнительных команд echo), то эта малоинформативная строка будет выводиться в командную строку vim каждый раз при нажатии ,s - добавление <silent> и команд echo делают использование этого маппинга намного более удобным за счет более информативных сообщений.

Итак, цель достигнута. Теперь при нажатии на клавиатуре комбинации ,s будет происходить переключение подсветки проблемных участков кода с выводом сопутствующей информации в командную строку vim.

3. Маппинг для переключения ограничительной колонки (colorcolumn). Цель почти такая же, как и в предыдущем пункте: создать маппинг для включения/выключения цветовой колонки, которая будет располагаться на 81-ом знаке и предупреждать пользователя о возможном нарушении стандарта кодирования, ограничивающего длину строк 80 знаками. Но реализовать эту задачу теперь проще:
nmap <silent> ,m :if &colorcolumn == 81 <Bar> set colorcolumn= <Bar>
            \ elseif !&colorcolumn <Bar> set colorcolumn=81 <Bar> endif<CR>
Новый маппинг соответствует комбинации клавиш ,m. На этот раз нет смысла выводить сообщения на дисплей, поскольку наличие или отсутствие ограничительной колонки заметно и так.

4. Маппинг для переключения режима paste. Режим paste бывает полезен, когда нужно вставить из буфера обмена многострочный участок кода без дополнительного форматирования. Маппинг соответствует комбинации клавиш ,p и выглядит следующим образом:
nmap <silent> ,p :set paste! <Bar> set paste?<CR>
Команда set paste! переключает (toggle) текущий режим paste, а команда set paste? выводит его значение на дисплей.