AllInfo
Main: Info Blog Temp Mail


unix 2014-10-06 19-17-52

Эффективное использование Vim из песочницы


Введение

Я очень люблю редактор Vim, использую его в своей работе (для написания кода) уже больше четырех лет и хочу поделиться своим опытом его использования.

Эта статья — не набор “волшебных команд” и рецептов (cookbook, как называют такие наборы по-английски), хотя они тут тоже присутствуют, а, скорее, попытка описать, как общие принципы построения эргономичных интерфейсов можно применить в практике использования Vim, чтобы сделать из него удобную и эффективную среду работы с текстами.

Эта статья также не является tutorial’ом для начинающих пользователей Vim, хотя и им (а также пользователям Emacs) может быть интересна, поскольку некоторые упоминаемые принципы являются достаточно общими и действуют не только в системах редактирования текстов, а вообще везде, где идет речь об использовании компьютера для редактирования чего-либо. Тем не менее, я предполагаю, что читатель знаком с основными концепциями, применяемыми в Vim (режимы, регистры, буфера, команды) и не останавливаюсь на их подробном описании.

По умолчанию Vim настроен очень старомодно, и эта настройка подразумевает, что пользователь при работе с текстом будет мыслить метафорами пятидесятилетней давности, как будто бы сейчас заря эпохи UNIX. Однако дизайн Vim позволяет сделать несколько настроек, после которых система станет выглядеть вполне прилично и станет работать весьма эффективно, сочетая в себе полезные черты и древних юниксовых инструментов работы с текстом, и современных WYSIWIG-процессоров, при этом обходя, насколько это возможно, присущие им недостатки. Вот об этих настройках и приемах их использования и пойдет речь.

Работа с содержимым по модели “существительное — глагол”

Джеф Раскин, известный авторитет в области эргономики интерфейсов, в своем классическом труде “Интерфейс: новые направления в проектировании компьютерных систем” упоминает две модели работы с содержимым — “глагол — существительное”, когда сначала задается действие, а затем выбирается содержимое, к которому его нужно применить, и “существительное — глагол”, когда сначала выделяется содержимое, а затем к нему применяется какая-либо операция. В большинстве случаев предпочтительна именно модель “существительное — глагол”. В данном случае это определенно так. Добавлю, что часть приемов, о которых пойдет речь, были изобретены мной независимо от Раскина в то время, когда я еще не знал о существовании его книги, путем проб и ошибок — я пробовал разные способы выполнения действий, и через некоторое время у меня формировалось представление о том, что удобно, а что — нет.

В данном случае содержимым, над которым выполняются операции, является редактируемый текст. Выделенным содержимым, соответственно, обычно является выделенный текст.

В Vim, однако, понятие выделенного содержимого раздваивается. В качестве него могут выступать:

Блок текста, выделенный в режиме визуального выделения (обычно серым цветом)
Текст, отвечающий регулярному выражению, находящемся в регистре / (регистре текущего поиска). Иными словами, это текст, который подсвечивается желтым, когда включена опция hlsearch.


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

Подсветил — посмотрел — выполнил

Второй случай более интересен. В модели “существительное — глагол” он позволяет сделать существительным текст, отвечающий регулярному выражению в регистре /. При этом, когда включена опция :hlsearch, данный текст подсвечивается желтым. Это позволяет выработать стиль работы с текстом, который можно описать как «подсветил — посмотрел — (заменил ЭТО |удалил ЭТО |скопировал содержащие ЭТО строки | выполнил какую надо команду)», где ЭТО — текст, отвечающий регулярному выражению в регистре /. Такой стиль дает совершенно новый уровень удобства при выполнении операций типа “заменить все вхождения одного слова на другое” благодаря тому, что все вхождения заменяемого слова сразу видны в тексте. После перехода на такой стиль вернуться к старому стилю массовой замены практически невозможно (под “старым стилем” я здесь понимаю, например, реализацию команды “Заменить все” в текстовом редакторе Far manager’а, которая либо действительно заменяет все с риском заменить там, где не надо, либо запрашивает подтверждение каждой замены, создавая ненужные раздражающие запинки в работе). При использовании предварительной визуальной подстветки риск совершения ошибки при совершении массовых замен и подобных операций сведен к минимуму.

Дальше я опишу несколько настроек, которые при использовании стиля “подсветил — посмотрел — выполнил” оказались для меня очень удобными.

Строки из моего файла конфигурации _vimrc:
" по звездочке не прыгать на следующее найденное, а просто подсветить
nnoremap * *N
" выключить подсветку: повесить на горячую клавишу Ctrl-F8
nnoremap <C-F8> :nohlsearch<CR>

Поясню, что они делают. В Vim встроена команда * (звездочка, вызывается обычным нажатием Shift+8 в нормальном режиме), которая помещает текущее слово (на котором стоит курсор) в регистр поиска /, производит поиск следующего вхождения этого слова в тексте и перемещает курсор к этому следующему вхождению. Кроме этого, производится подсветка вхождений этого слова (на самом деле опять текста, отвечающего регулярному выражению в регистре поиска, но на практике в данном случае это значит именно вхождения текущего слова). По моему мнению, эта команда реализована неправильно. Я считаю ошибочным прыжок на следующее вхождение слова, потому что этот прыжок выбивает из контекста, приводя к смене текста на экране (следующее вхождение может оказаться где угодно, и текст может поменяться на совершенно незнакомый). Поэтому я использую первый мэппинг, котороый меняет семантику команды * на “подсветить текущее слово, но позицию курсора не менять”. Второй мэппинг используется для назначения горячей клавиши, сбрасывающей подсветку (по эргономическим соображениям она должна находится близко к клавише 8, на которой находится команда *, поскольку команды “подсветить” — “сбросить подсветку” очень часто используются в паре, например, для таких задач, как “подсветить все вхождения переменной, на которой я сейчас стою курсором”. После того, как локальная задача, ради которой подсвечивали переменные, решена, подсветку лучше сбросить, чтобы она не отвлекала внимание от дальнейшей работы. Причем при реальной работе промежуток времени между “подсветить” и “сбросить подсветку” может сокращаться до одной секунды). После того, как слово подсвечено, можно использовать команды n и N для прыжков вперед / назад по его вхождениям, если это необходимо.
" в визуальном режиме по команде * подсвечивать выделение
vnoremap * y :execute ":let @/=@\""<CR> :execute "set hlsearch"<CR>

Это то же, что команда * делает для слов, но примененное для фрагмента, выделенного в визуальном режиме. Используется, когда нужно подсветить не слово (обрамленное пробелами), а произвольный фрагмент, который, допустим, может содержать пробел в середине.

Использование стиля “подсветил — посмотрел — выполнил” совместно с визуальным режимом оказалось очень удобной практикой. Такое комбинирование стилей выделения используется при решении задач типа “в данной функции переименовать переменную foo в bar” и подобных. Такая (и подобные) задачи решаются последовательностью действий:

Подсвечиваем foo командой *
Переходим в режим визуального выделения и выделяем текущую функцию
Отдаем команду замены :’<,’>s//bar/g


Символы ’<,’>, означающие начало и конец текущего выделенного блока, и определяющие диапазон применения команды :s, Vim подставляет автоматически при отдаче любой команды из режима визуального выделения. Также опущен первый аргумент команды :s, т. к. набирать его нет необходимости — когда он опущен, Vim использует в качестве этого аргумента содержимое регистра текущего поиска. То есть именно то, что подсвечено желтым.

Таким образом достигается наглядность. Содержимое регистра / подсвечивается желтым, поэтому мы видим, ЧТО мы будем заменять. Блок, выделенный в режиме визуального выделения, подсвечивается серым, поэтому мы видим, ГДЕ мы будем это заменять. Остается отдать команду :s, в которой указать, НА ЧТО заменять.

Отдельного акцента заслуживает уже упоминавшаяся особенность Vim (к сожалению, не сразу понятно, что она у него имеется, и узнать о ее существовании можно либо наткнувшись на ее описание в документации либо в интернете, либо методом “тыка”), которая состоит в том, что команды, применяющиеся для массовых операций над текстом, такие, как :s (substitute, замена одного шаблона на другой), :d (delete, удаление строк, содержащих шаблон), :g (global, выполнение произвольной операции над строками, содержащими шаблон) используют содержимое регистра / в качестве своего аргумента, в случае, когда он опущен при их вызове. Это очень удобно, и к такому поведению привыкаешь моментально. Собственно, именно эта “фича” и делает возможным редактирование текста в режиме “подсветил — посмотрел — выполнил”, который настолько хорошо совместим с особенностями человеческого восприятия, что в компьютерных интерфейсах он используется повсеместно — мы выделяем файлы в файловом менеджере (они подсвечиваются), смотрим на выборку, выполняем с ними какую-либо операцию. То же самое происходит и в графических и других редакторах. Собственно, после короткого периода привыкания к редактированию в таком стиле, становится непонятно, как вообще можно по-другому.

Несколько рецептов из моей “поваренной книги Vim”

Дальше я приведу несколько команд и приемов использования Vim, которые я считаю полезными для себя, но, в принципе, их использование или неиспользование — уже дело вкуса и личных предпочтений, а также набранного опыта использования Vim. Вполне возможно, что для каких-то описанных мной ниже задач есть более эффективные способы решения.

:g//t$ — скопировать строки, содержащие подсвеченное значение, в конец файла. Если, например, надо быстро понять, как глобальная переменная (sic! — а что делать, в legacy-коде они встречаются) используется в модуле.

:g//d — удалить строки, содержащие подсвеченное значение
:g!//d — удалить строки, НЕ содержащие подсвеченного значения

Список полезных, но редко используемых команд с нетривиальным синтаксисом, которые сами не запомнятся от частого употребления, лучше записать в шпаргалку. Например, можно записывать такие и подобные вещи вещи:

Заменить каждое вхождение нескольких пустых строк на одну пустую строку (чтобы между параграфами стал одинаковый промежуток в одну линию):
:v/./,/./-j

Убрать пустые строки (в визуальном режиме)
:'<,'>g/^$/d

Раздвинуть подряд идущие строки (обратное предыдущему действие, каждая строка станет параграфом)
Нужно при форматировании текста под 76 символов, из формата, как его сохраняет Word, когда каждый абзац становится строкой в текстовом файле.
:'<,'>s/$/\r/g

Показали свою полезность следующие аккорды (фрагмент из _vimrc)
" по Alt-1 редактировать текущее слово
nnoremap <M-1> ciw

" по Alt-5 изменять хвост текущего слова
nnoremap <M-5> cw

Использование Vim для работы с кодом и программирования

Приведу несколько приемов, полезных при работе с кодом. Собственно, именно для работы с кодом Vim и создавался (в отличие от Emacs, который считается более универсальным редактором, подходящим для работы с любыми текстами, а не только с кодом. Впрочем, это известная тема для holy wars).

Удобно использовать метку ] для вставки блока кода внутрь операторных скобок и его последующего выравнивания (indenting). Тут имеется в виду, что после вставки блока внутрь операторных скобок по команде p нужно вновь выделить вставленный блок, чтобы выровнять его положение внутри скобок в соответствии с принятыми правилами форматирования. Для этого нужно выделить вставленный блок. Это можно легко сделать, если встать на первую строку вставленного блока, перейти в режим визуального выделения строк (команда V), и дать команду перейти к специальной метке, означающей последнюю строку только что вставленного блока: ‘] (апостроф, потом закрывающая квадратная скобка).

Плагин matchit для перемещения по операторным скобкам. Без комментариев must have. По команде % курсор прыгает между { и } в языках с Си-подобным синтаксисом, между begin и end в Паскале, и т. д. Плагин входит в стандартную поставку Vim.

Автодополнение по Ctrl-N. Конечно, настолько хорошо, как в IDE-средах, поддерживающих парсинг и предкомпиляцию, работать не будет, но если такого IDE нет, или неохота ставить, то вполне сгодится.

Генератор тэгов ctags и команды перемещения по тэгам Ctrl-] (пойти в функцию под курсором) и Ctrl-T (вернуться обратно). Опять же, по сравнению с IDE, получается простая и порой дубовая, но работающая навигация по коду (причем кода может быть вполне много, полмиллиона строк вполне тянет, а говорят, может тянуть и гораздо больше).

Можно использовать :r !grep <аргументы для grep> и потом Ctrl-W gf для поиска и-или замены по массиву кода (для рефакторинга). Команда :r !grep вставляет в текущее место (где стоит курсор) вывод grep’а. Команда gf (go [to] file) открывает файл, на имени которого стоит курсор, в новом окне (либо вкладке). Если имя файла хитрое (допустим, путь к файлу содержит пробелы), то можно выделить полный путь к файлу в режиме визуального выделения, и уже после этого дать команду gf (либо Ctrl-W gf).

Использование Vim в операционной системе Windows

Поскольку основная ОС у меня Windows, я выполняю некоторые дополнительные настройки Vim, чтобы горячие клавиши вели себя единообразно. В стандартную поставку входит файл настроек mswin.vim, который включается в начало файла инициализации _vimrc:
set nocompatible
source $VIMRUNTIME/mswin.vim
behave mswin

Включение этого файла приводит к тому, что команды копирования и вставки в буфер (Ctrl-C и Ctrl-V) начинают работать стандартным для Windows способом, что полностью оправдано.

Для редактирования нескольких файлов я использую вкладки. Чтобы открыть файл в Vim (добавить вкладку с файлом / файлами, если Vim уже открыт), я использую команду для меню Far:
"C:\Program Files\Vim\vim73\gvim.exe" --remote-tab-silent !&
(!& — это специальный макрос Far’а, означающий “текущий либо выделенные файлы”). Собственно, интерфейс командной строки здесь такой:
"C:\Program Files\Vim\vim73\gvim.exe" --remote-tab-silent file1 [file2] ...
Для переключения между вкладками я использую стандартные для Windows сочетания Ctrl-Tab (Shift-Ctrl-Tab). Также практика показала, что нужна возможность двигать вкладки влево-вправо, чтобы несколько файлов, которые редактируются одновременно, можно было бы сдвинуть так, чтобы они оказались в соседних вкладках. Для этого я использую следующие настройки:

" CTRL-Tab is Next tab
nnoremap <C-Tab> :tabnext<CR>

" CTRL-Shift-Tab is Previous tab
nnoremap <C-S-Tab> :tabprevious<CR>

" use Alt-Left and Alt-Right to move current tab to left or right
nnoremap <silent> <A-Left> :execute 'silent! tabmove ' . (tabpagenr()-2)<CR>
nnoremap <silent> <A-Right> :execute 'silent! tabmove ' . tabpagenr()<CR>

" CTRL-F4 is :tabclose
nnoremap <C-F4> :tabclose<CR>


При работе в режиме insert мне удобнее использовать стандартное системное переключение раскладки (которое обычно по Alt-Shift, а у меня по Caps), а не встроенное в Vim, которое по Ctrl-^. Хотя, возможно, это уже чисто личное предпочтение.

Курсор между окнами (в терминах Vim, то есть текстовыми областями, создаваемыми командами :split или :vsplit) я перемещаю клавишей Tab, что, опять же, стандартно для Windows:
" Tab is Next window
nnoremap <Tab> <C-W>w

" Shift-Tab is Previous window
nnoremap <S-Tab> <C-W>W

Хинт, неочевидный для новых пользователей Vim: для того, чтобы вставить полезную команду из шпаргалки в командную строку Vim из буфера обмена Windows, надо, находясь в режиме отдачи команды (в который переходим после нажатия двоеточия), нажать Ctrl-R (курсор сменит форму на кавычку), затем нажать Shift-8 (в данном контексте это значит “вставить содержимое регистра, в котором находится буфер обмена Windows”).

Разбивать окна (окна в терминологии Vim) удобно по сокращенным командам Ctrl-W s и Ctrl-W v, закрывать по Ctrl-W c (это быстрее, чем набирать команды :split и :vsplit)

Заключение

Мы рассмотрели некоторые принципы и приемы, применение которых в редакторе Vim позволяет повысить эффективность редактирования текста. Возможно, эта статья заинтересовала кого-то, кто не работал с Vim, но хотел (или захотел) попробовать, или тех, кто, возможно, уже видел Vim, но не начал его использовать из-за необычности и непривычности его интерфейса. Действительно, Vim — не самый простой в использовании инструмент и кривая обучения у него не самая пологая. Однако проверенный временем дизайн и огромная гибкость делают его одним из самых эффективных инструментов редактирования текстов, который не потерял актуальности и в наши дни. Более того, он развивается и выходят новые версии. Наибольшая эффективность использования Vim достигается при использовании правильных подходов, некоторые из которых (как это видится мне) я постарался осветить в данной статье.

Начинающим пользователям, которые хотят научиться использовать Vim, я бы посоветовал идти тем путем, каким шел я сам — выполнить включенный в комплект поставки tutorial, а затем постепенно читать главы User Manual из справочной системы (она там очень хороша, доступна по команде :help) по порядку, пробовать применять описанные там команды и, конечно, использовать Vim в повседневной деятельности.

18.119.123.10 / 2024-12-22_20-16-29 UTC.