1 версия - 13.09.2001
Заметка с размышлениями о
vim, опубликованная 04.09.2001 (см. также), имела некоторый резонанс, в связи с чем
разговор об этом мощном редакторе хочется продолжить. Парадоксально, но,
признав некоторую сложность vim в прошлый раз, сейчас я попытаюсь
обосновать утверждение о его исключительной простоте. Парадокса,
собственно, никакого и нет: все зависит от того, с чьей позиции смотреть.
Для пользователя, только что загрузившего дистрибутив vim, он действительно
сложен. Достаточно сказать, что в файле index.txt свыше 1200 строк,
а ведь этот файл - всего лишь перечень доступных команд с краткими
описаниями в одно, максимум два предложения. 1000, пусть 500 команд не
способствуют желанию познакомиться с редактором. Не будем торопиться.
Во-первых, команды довольно часто дублируются. Во-вторых, часть из них
унаследована от vi и предполагает возможность ввода на любом
алфавитно-цифровом терминале. Современному пользователю более естественным
покажется использование функциональных клавиш и клавиш позиционирования
курсора, мыши, наконец. Все эти возможности vim, разумеется, поддерживает,
но и старые варианты набора команд не отменяются. Список сокращается:
предположим, до 150 команд. Не так уж и много для редактора, который может
"все" (уточнять и в этот раз не будем), но есть ли основания говорить об
"исключительной простоте"? Как это ни странно, есть. И основания эти
следующие:
- логика интерфейса, заложенная авторами, позволяет свести все
многообразие команд к сочетаниям значительно меньшего числа их
"составляющих";
- исключительная модифицируемость vim дает возможность создавать
"проблемно-ориентированные", при желании - очень простые
конфигурации;
- гибкая система помощи, которую, кстати, можно дополнять
самостоятельно и на любом языке;
- если же потребуется конфигурация, воспользоваться которой можно
сразу, не обременяя себя знакомством с документацией, то можно
воспользоваться возможностью создания меню, которая у vim тоже есть.
Таким образом сконфигурированный,
документированный и дополненный системой меню vim становится "образцом
дружественности" к пользователю. Вас что-то не устраивает? Cделайте так,
как считаете более удобным! Для этого не нужно обладать какими-то
специальными знаниями, но что-то ДЕЛАТЬ - действительно нужно. Здесь
придется еще раз признать (и напомнить), что vim написан программистами и
для программистов. Но поскольку ряды последних все ширятся, то и круг
пользователей также должен расширяться. Если это не так, то, возможно, и
потому, что вышеупомянутые более чем 1000 строк одного перечисления команд
кого-то "оттолкнули" при знакомстве. Именно на этого кого-то и ориентирован
данный материал.
Для начала стоит объяснить, зачем
все-таки нужно такое количество команд, если большинство редакторов вполне
обходится системой меню. Для примера предлагаю взять части текста,
выделяемые для выполнения над ними какой-то операции и называемые, как
правило, текстовыми объектами. Можно выделять их мышью, как это и делается
во многих редакторах: решение универсальное, но крайне не эргономичное -
попробуйте выделить абзацев 5-6. В общем случае текстовым объектом может
быть символ, слово, предложение, абзац, текст в целом плюс блоки, которые,
в свою очередь, могут быть ограничены угловыми, квадратными, фигурными или
круглыми скобками (мы ведь говорим о тексте программы). Курсор (или
указатель мыши) в момент выбора также может находиться в начале, конце или
внутри текстового объекта. Можно, конечно, "метить" начало и конец
текстового объекта - но это бывает так утомительно... Согласитесь, также,
что меню, отражающие все эти возможности, выглядело бы весьма громоздким. А
вот как это сделано в vim: если курсор в начале объекта, то первая буква
команды выделения - <a> (add), а если внутри, то
<i> (inner). Теперь осталось указать, что является
объектом. Вторая буква - <w>, <s>,
<p> или <b>. Нетрудно догадаться об
их происхождении : word, sentence, paragraph, block. Не ошибетесь, если
предположите, что любая из возможных скобок в качестве второго символа
команды, означает, что объект - это блок, ограниченный этим типом скобок.
Изящно. И просто. А между тем, это без малого три десятка команд в
визуальном режиме.
Логика интерфейса такова, что если вы
обнаружили какую-то возможность в одном из режимов, то почти наверняка
найдете аналогичную в другом. Упомянутые в первой статье
<Ctrl-n> и <Ctrl-p>, обеспечивающие
в режиме вставки переход к следующему (next) и предыдущему
(previous) вариантам автозаполнения, при автозаполнениях работают
практически везде. Одни и те же символы в командах, как правило, несут одну
и ту же смысловую нагрузку: <i> - идентификатор,
<d> - макроопределение, <f> - файл.
Поэтому <[i> - найти первое появление идентификатора
(фактически - определение переменной), <[d> - то же для
макроопределения. Понятие "первое появление" для файла смысла не имеет,
поэтому <[f> идентично <gf> и
означает открытие для редактирования файла, имя которого находится под
курсором. Если тем же i,d,f предшествует
<Ctrl-W> (признак window-команд), то результатом
будет открытие нового окна (а для файла - и нового буфера) с переходом к
определению переменной или макрокоманды. Если команда имеет "соседей по
смыслу", то скорее всего они будут вызываться изменением регистра второго
(уточняющего) символа. Так, <[I> и
<[D> распечатают все строки с данным идентификатором
или макропеременной. Те же символы присутствуют в составе
<Ctrl-X> (eXtended - расширенного) подрежима режима
вставки. Этот подрежим полностью ориентирован на автозаполнение. Нетрудно
догадаться, что символы <I>, <D>,
<F> в качестве завершающих (после
<Ctrl-X-Ctrl>) инициируют автозаполнение имен
переменной, макрокоманды и файла соответственно, а <n>
и <p>, как и прежде, будут означать следующий и
предыдущий варианты автозаполнения.
"Универсальными" могут быть не только
последние символы команд, как в предыдущих примерах, но и первые. Прежде
всего, это уже упоминавшиеся <Ctrl-X> - первый символ
расширенного подрежима вставки, и <Ctrl-W> -
первый символ window-команд. Как правило, команда детализируется
вторым символом. Кроме уже названных, для window-команд это:
<+> - увеличить, <-> - уменьшить,
<=> - сделать равными размеры окон,
<s> - разделить (split), <c> -
закрыть (close), <n> - открыть новое (new),
<r> и <R> - поменять местами
(rotate) окна. Еще одним "универсальным" символом может быть символ,
идентифицирующий именованный буфер или, в соответствии с документацией,
регистр. Если помните, комбинация <"x> (где 'x'
- любой символ: a-zA-Z0-9%#:-") означает использование регистра с
командами
<y>,<d>,<p> -
копирование, удаление, вставка (yank, delete, paste). Аналогично,
<qx> означает переход в режим записи в регистр, а
<q> - выход из него. Записываются как символы, так и
управляющие последовательности (фактически - команды редактора). Если в
регистр записаны команды редактора, то его содержимое можно выполнить
командой <@x>. Но это возможно уже только с регистрами
a-z. Содержимое регистра можно вставить командой
<Ctrl-R>, причем это допустимо как непосредственно в
тексте (режим вставки), так и в командном режиме. На этом
список символов - "универсальных составляющих" команд - можно считать
исчерпанным, но - не исчерпывающим, поскольку есть и уникальные комбинации,
не имеющие аналогов в других режимах. Например,
<Ctrl-U> в режиме вставки удаляет строку, а
<Ctrl-V> позволяет ввести символ посредством ввода его
трехзначного десятичного кода. В нормальном режиме
<Ctrl-L> перерисовывает экран,
<Ctrl-R> отменяет действия undo
(<u>), а <Ctrl-A> и
<Ctrl-X> инкрементируют или декрементируют число под
курсором. Так же уникальны команды поиска и замены, перечисленные в первой
статье. <U> в визуальном режиме переводит
выделенный фрагмент в верхний регистр, а <u> - в
нижний. <~> поменяет регистр для одного символа в
нормальном режиме и для выделенного фрагмента - в
визуальном.
Некоторые символы сохраняют свое
назначение и в командном режиме: <!> - фильтр,
<@x> - выполнить содержимое регистра 'x',
'<' и '>' - уменьшить и, соответственно, увеличить
"отступ" строк. Вообще, командный режим стоит несколько "особняком",
поскольку является практически самодостаточным: в нем доступны практически
все действия, инициируемые командами остальных режимов, разве что объем
ввода будет больше. Зато текст команд достаточно "прозрачен". Например:
:buffer N - перейти к буферу N;
:Print - распечатать;
:set - показать или установить опции.
А количество вводимых символов уменьшается благодаря допустимым сокращениям
и уже неоднократно упоминавшемуся автозаполнению. Перечислять эти самые
"EX" команды нет смысла - их без малого три сотни, а вот просмотреть этот
список - стоит. Хотя бы для того, чтобы знать, какие еще возможности имеет
vim. Кроме команд, дублирующих команды других режимов, мы найдем здесь
команды управления буферами (==открытыми файлами), меню, "привязкой" команд
к клавиатурным последовательностям (map), средства программирования,
индексации (tags), управления редактором и многое, многое другое.
Так мы подошли к следующему достоинству
vim, обеспечивающему его "простоту" - исключительным возможностям
настройки. Прежде всего обратимся к конфигурационному файлу vimrc
(для графического режима - gvimrc). На www.vim.org стоит взять vimrc.forall -
файл написанный Свеном Гуксом (Sven
Guckes) "на все случаи жизни". Файл прекрасно прокомментирован и
действительно при некоторых модификациях может устроить многих. Но лучше
использовать его как "руководство к действию". Познакомившись для начала с
командой map:
map \\ <C-]>
которая заменяет <Ctrl-]> (переход по ссылке в help
(tag)), на более удобную последовательность <\\>,
переходим к весьма обширному блоку определения опций (set ...). Автоотступ,
автоматическое сохранение копий, размер табуляции и так далее, и так далее:
перечисление в рамках статьи представляется невозможным -
vimrc.forall имеет объем в 75К. Но одну опцию я все-таки
приведу:
set langmap=йцу...ЙЦУ...;qwe...QWE...
Три точки в данном случае означают "все остальные символы на клавиатуре" в
нижнем и верхнем регистрах для раскладок ru и us. <;>
перед 'qwe' отделяет "подменяемый" набор от "подменяющего". Точку с запятой
(в наборе, а не разделяющую) и двойные кавычки нужно исключать (quote) с
помощью backslash (\), как обычно. Данная опция делает не нужным
переключение раскладки клавиатуры, когда требуется латинский символ в
нормальном режиме - несуществующий, как видите, недостаток vim, о
котором я писал в первой статье. Спасибо всем указавшим на эту опцию. Что
касается Свена Гукса, то, позаботившись о вводе таких необходимых ему
умляутов, он действительно не учел нашу привязанность к кириллице - вполне
простительно для жителя Берлина.
Следующая секция vimrc.forall научит
использовать сокращения - abbreviations. Дело вкуса. Занятно использовать
сокращения в качестве "автокорректора" опечаток: aslo->also. Или -
записной книжки: Ysnail->Sven Guckes<C-M>Pariser Str.
52<C-M>D-10719 Berlin. Обратите внимание, что сокращению команды
abbreviate (ab) могут предшествовать символы <i>,
<c> и <un> (insert, command, undo).
Как Вы, наверное, догадались, это означает актуальность (действенность)
сокращения для режимов вставки и команд или отмену
сокращения. Если этот небольшой список мы расширим еще символами
<a>, <n>, <o> и
<v> (all, normal, operator и visual) - то получим
список "универсальных" модификаторов, применимых и к некоторым другим
командам, таким, как map и menu. Узнаваемая логика, не правда ли?
Следующая
секция 'MAPings' содержит определения привязки команд к определяемым
пользователем клавиатурным последовательностям. Вот где истинный простор
для "подгонки" vim под Ваши вкусы. Хотите выходить из редактора по
<F10>? Пожалуйста:
map <F10> :q<CR>
imap <F10> <Esc>:q<CR>
cmap <F10> <Esc><Esc>:q<CR>
Последние две команды можно заменить одной: map!
<Esc>:q<CR>, но для этого уже нужно знать о существовании map!
(map для режимов команд и вставки) Во второй позиции может
быть и последовательность символов. Например,
map <F6>:set number<CR>
map <F6>:set nonumber<CR>
будет по <F6> включать нумерацию строк, а по
<n><F6> - выключать.
Последующие секции, описывающие применение автокоманд для использования vim
в качестве почтового клиента, PGP-шифрование и операции с синтаксисом,
очень интересны, но уже не имеют отношения к разговору о "простоте"
vim.
Вышеизложенное должно было убедить
читателя, что "не так страшен vim..." и изучать устрашающих размеров help
может быть, и не придется... У меня, во всяком случае, на каком-то этапе
знакомства в этим редактором сложилось впечатление, что имей я под рукой
1-2 странички подсказок... А почему - нет? Поскольку по
<F1> vim вызывает файл help.txt из каталога
$VIMRUNTIME/doc/ (значение $VIMRUNTIME можно уточнить по :set helpfile),
почему бы его не "подменить", сохранив оригинальный help.txt и переименовав
в help.txt свой файл? Несколько "варварский", но действенный способ.
Изящнее будет вставить свой help в существующую систему помощи: путь к
персональному help-у удлинится на пару нажатий клавиш, но зато в Вашем
распоряжении всегда будут и свой файл и оригинальная система помощи. Не
вдаваясь в тонкости работы со ссылками (tags) приведу краткий рецепт:
- Предположим, Ваш файл называется myhelp.txt. Первой строкой в
него вставим что-то вроде: *myhelp.txt* . Текст может быть любым -
это всего лишь ссылка, на которую будет позиционироваться система поиска
в help-файлах.
- В файле help.txt, в строке, например, второй (чтобы сразу была
видна на экране) вставим указатель. Например: |$MyHelp| . Текст,
опять-таки значения не имеет, а символ <$> поставлен на первую
позицию лишь затем, что бы наша ссылка оказалась поближе к началу
индексного файла tags.
- В индексный файл tags вносим строку:
$MyHelp myhelp.txt /*myhelp.txt*
Все элементы строки, надеюсь, понятны: первый - ссылка, второй - имя
файла, третий - позиция в этом файле. Файл tags - сортирован по алфавиту
и, если не хотите сортировать его заново, строку нужно поместить в
соответствующее место. В данном случае между строками, $LANG.... и
$VIM...
Готово. Запускаем vim, нажимаем
<F1> и переходим по ссылке $MyHelp
(<Ctrl-]> - по умолчанию, <\\> -
если Вы воспользовались советом Свена, или двойной клик, если предпочитаете
мышь).
Ну, а поскольку "эталоном"
дружественности интерфейса долгое время считалась система выпадающих меню,
то осталось посмотреть, как таковая реализуется в vim. Дистрибутив содержит
файл menu.vim, активный по умолчанию только для графической среды.
Для появления меню в консольном режиме во все тот же vimrc нужно
вставить следующие команды:
source $VIMRUNTIME/menu.vim
set wildmenu
set cpo-=<
set wcm=<C-Z>
map <F9> :emenu <C-Z>
На месте <F9> может быть, разумеется, любая
клавиатурная последовательность. Можно иметь личный меню-файл, тогда первая
строка должна указывать на него. Меню как меню. Но это - vim, а, значит,
его можно модифицировать. Файл menu.vim сравнительно невелик: менее
22К для версии 5.6. Часть его составляют функции, собственно и
обеспечивающие функционирование системы меню. Поскольку программирование в
vim нас на настоящий момент не интересует, то обратим внимание только на
блоки, первым словом строк которых является команда menu с
предшествующими ей уже известными модификаторами <a>,
<c>, <i>, <n>,
<o> и <v> в соответствии с
существующими режимами. Плюс tmenu - для организации всплывающих
подсказок (tooltips) в графической среде. Обычно строка такого блока
выглядит следующим образом:
amenu mp;File.&Close<Tab>:q :confirm
q<CR>
Элементы строки разделяются пробелами. В данном случае первый из них,
amenu - команда, обеспечивающая появление данной позиции меню во
всех (all) режимах. Второй - число, часть до точки которого определяет
позицию в главном (горизонтальном), а после точки - в выпадающем
(вертикальном) меню. Подобным образом лексемы до и после точки третьего
элемента строки представляют собой текстовое содержание позиции меню.
Пробелы и точки в составе этих лексем исключаются (quote) с помощью
обратной косой черты (backslash). Допустим только один служебный символ -
<Tab>. Ведущим (hot) символам в составе лексем предшествует амперсенд
(&). Четвертый элемент - собственно команда, которую нужно выполнить.
Просто и эффективно. Если команда предполагает редактирование или если Вам
требуется дополнительный контроль над командами - не завершайте строки
<CR>. Фактически, система меню просто вводит за вас необходимую
"EX"-команду.
Таким образом vim может обретести
интерфейс, проще которого (при заданном уровне функциональности) уже не
будет. Все желаемые усовершенствования Вы можете сделать сами. Средств для
этого - достаточно. Не стану утверждать, что путь к этой "простоте" так уж,
извините за тавтологию, прост, но "нет ничего ценнее хорошего инструмента".
В том числе и для программиста.
Многих возможностей vim, кстати, ни,
первая ни вторая статья даже не касались. Вне рассмотрения остались
интеграция с Perl и Python, программирование пользовательских функций,
форматирование текста и многие другие темы, достойные отдельного
обсуждения.