Найти в Дзене
Блокнот математика

Рубрика "Секреты Вим". Опции совместимости

Привет, коллеги. В целом ряде аспектов Вим ведет себя не так, как vi. Но иногда поведение vi может быть вам предпочтительнее, а порой совместимость нужна по техническим причинам.

Есть флаг compatible, включающий полностью vi-подобное поведение и влияющий на многие опции, в том числе на cpoptions. Но мы обсудим именно ее: какие "старые" варианты поведения можно включить и надо ли оно вам.

Я советую Вам прочесть материал и пользоваться как справочником при случае. У меня немного подробнее, чем в Вим-Справке.

Опция cpoptions означает "compatability options", то есть "опции совместимости", и позволяет включать такое поведение, как в vi — в тех случаях, когда по умолчанию в Вим другое поведение. Причем некоторые флаги в опции по умолчанию стоят, вот такая вот странность))

Технически опция содержит набор букв-флагов: присутствие буквы означает включение соответствующего поведения по умолчанию. Флаги могут идти подряд или разделяться запятыми. Добавлять или убавлять флаги лучше с помощью команды :set+= или :set-=, например:

:set cpoptions-=a

При этом гарантированно ничего не будет испорчено и не надо волноваться, который там по счету удаляемый флаг и не присутствует ли уже флаг добавляемый.

Флаги касаются самых разных областей деятельности. Давайте обсудим все достаточно вдумчиво.

Чтение и запись

Флаг a означает, что команда :read с именем файла выставит "имя альтернативного файла", или решетку, в указанное имя.

Команда :read вставит файл в текущий буфер в позицию курсора, а с этим флагом у вас останется доступ к самому файлу. По умолчанию этот флаг присутствует и вы можете, например, легко открыть файл, который только что вставили в текущий файл: :tabnew #

Совершенно аналогично действует флаг A. Если он присутствует, то команда :write (или :w сокращенно) с именем файла выставляет указанное имя в "имя альтернативного файла". Это позволяет повторить запись при неудаче:

:w #

Еще одна пара флагов для этих команд: f и F. Если они присутствуют, то соответствующие команды связывают буфер с указанным файлом (если буфер пока ни с каким файлом не связан). То есть если вы в пустой буфер "всосете" содержимое файла book.txt, то этот буфер окажется связан с этим файлом — если флаг f присутствует. Но он по умолчанию отсутствует.

Аналогично, флаг F предписывает команде записи :w с именем файла связать несвязанный буфер с этим файлом. Это привычно и логично, и флаг по умолчанию задан. Имейте это в виду, потому что если что-то пошло не так, вы должны знать, что просто снесли случайно cpoptions. Это бывает.

Дополняет комплект флаг P, который делает то же для команды дополнения файла:

:w >> file.txt

Рассмотрим флаги + и i. Если флаг + присутствует, то команда записи в файл (:w file.name) сбросит признак измененности буфера. Хотя буфер может быть связан с файлом и может отличаться от него. В этом режиме признак несохраненности означает, что изменения не сохранены вообще. В режиме по умолчанию (без флага) признак означает, что буфер не связан с файлом или отличается от своего файла.

Если флаг i присутствует, то прерванное чтение файла оставляет его несохраненным.

Флаг g переводит курсор на первую строку, если выполнена команда :edit без аргументов (считывает текущий файл с диска).

Наконец, есть флаги O, W и Z. Первый отключает предупреждение о перезаписи файла — даже если файл изначально не существовал. Без флага есть защита от уничтожения файла, созданного извне Вим; с флагом этой защиты нет.

Второй запрещает перезапись файла, открытого только для чтения. Без него команда :w! попробует преодолеть защиту.

Третий не восстанавливает флаг "только для чтения", если он был преодолен командой :w!

Привязки и сокращения

Флаг B отключает особый смысл слеша \ в привязках и сокращениях, в том числе в меню. По умолчанию он присутствует.

Команда :imap X \<esc> при включенном (на момент выполнения команды!) флаге заменит в режиме вставки Х на \ и потом будет "нажата" клавиша <ESC>, то есть осуществлен выход из режима вставки. Если же флаг убран, то Х будет заменен на текст <esc>. Слеш отключит специальный смысл этого сочетания. Флаг < в обоих случаях должен быть выключен.

Ключ < отключает <>-нотацию специальных клавиш, таких, как <C-Q>, <ESC>, <CR>, <Left>, <PgUp>, <END> и т.п.

Еще есть флаг k, отключающий коды клавиш. Если вы воспользуетесь <C-V>, чтобы вставить код клавиши (esc или иной) как часть привязки, то флаг надо убрать. По умолчанию он и убран.

Флаг b относится к "конвейеру" |. Дело в том, что он позволяет выполнять команды по очереди, но как быть с привязками? Иногда надо создать привязку, а потом что-то сделать; а иногда в правой части привязки надо выполнить две или более команд.

Если флаг выключен (так по умолчанию), то \| считается внутренним конвейером, а | — внешним. Например:

:map X iI love Vim<esc>\|:normal yyp<CR>

создаст привязку, которая по нажатию X вставит фразу, скопирует ее и вставит строкой ниже. При этом

:map X iI love Vim<esc>|:normal yyp<CR>

создаст привязку, которая вставляет фразу, и потом скопирует ту строку, что под курсором, и продублирует ее.

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

:map X iI love Vim\|:normal yyp<CR>

создаст привязку, вставляющую текст "I love Vim\", и потом скопирует строку под курсором.

Это не слишком логично, но так в vi. Не то, чтобы нас это волновало, но стоит знать.

Вообще, есть три способа вставить палку | в правую часть привязки или сокращения.

1. Последовательность <Bar>:

:map _l :!ls <Bar> more^M

Разумеется, флаг < должен отсутствовать.

2. Символ \|:

:map _l :!ls \| more^M

при отсутствующем флаге b. Это мы уже обсудили.

3. Можно литерально вставить символ через <C-V>:

:map _l :!ls <C-V>| more^M

Это всегда должно работать. Здесь <C-V> вводится как есть, то есть control и v.

При значении cpoptions по умолчанию все три метода работают.

Флаг K предписывает не ждать полного кода клавиши посреди привязки. Так, можно прервать привязку <F1><F1>, нажав <ESC> после первой <F1>.

Поиск и регулярные выражения

Флаг c меняет смещение при поиске. Если флаг задан, то новый поиск идет от конца текущего, а без него — от начала (со сдвигом на один символ). Так, поиск /666 в строке 666666666 найдет три вхождения с флагом и семь — без него. По умолчанию включен.

Флаг l означает, что слеш \ в классе символов в [] означает сам себя, за исключением \], \^, \- и \\.

Так, с флагом [ \t] совпадет с пробелом, слешем и t, а без него — с пробелом и табуляцией. По умолчанию выключен.

Есть еще более брутальный флаг \ со сходным смыслом, только исключением является только \].

Флаг o предписывает забывать смещения строк при поиске. При повторе поиска он будет без смещения.

Флаг ; относится к поиску по строке. Если он задан, то команды повтора поиска , и ; не двигают курсор в том случае, если курсор непосредственно перед искомым символом. Например, в строке 121212121212, курсор стоит на первом символе, и мы делаем поиск t2. Курсор и так стоит перед двойкой, ничего не происходит. Теперь ; будет прыгать по единичкам вправо, а , — влево. С включенным флагом эти команды двигать курсор не будут, вроде как и так уже найдено.

Флаг m относится к режиму showmatch, в котором курсор прыгает на полсекунды на парную скобку. Без флага курсор прыгает на полсекунды на парную скобку, но может вернуться раньше, если нажата клавиша. С ним всегда ждет полсекунды.

Флаги M и % тоже модифицируют поиск скобок, только теперь командой %. Без флага M эта команда ищет парную скобку, учитывая слеши, то есть ( и \( считаются разными скобками. С флагом слеши игнорируются.

Флаг % включает vi-совместимый поиск скобок. Не считаются скобками команды препроцессора (решетка)if и другие, а также комментарии вида /* ... */. Зато скобки в кавычках считаются как обычно. Без флага скобки считаются парами отдельно вне кавычек и внутри них.

Флаг D запрещает ввод диграфов через <C-K> после команд поиска по строке (t,f и др).

Повтор действий

Флаг r означает, что команда повтора команды (.) трактует / как последнюю команду поиска, а не как ту команду поиска, которая была применена. Например, мы хотим удалить текст от курсора до слова Оля: d/Оля

Затем мы осуществляем поиск строки Маша. А потом решаем повторить команду (удалить текст от курсора до имени) командой "точка".

Если флага нет (по умолчанию так), удалится текст до "Оля", а если есть, то до "Маша".

Флаг y позволяет повторять команду копирования в буфер командой повтора "точка".

Флаг / предписывает использовать предыдущую строку замены, если в качестве строки замен в :s/// укаан символ %.

Флаг { предписывает командам { и } останавливаться на скобке { в начале строки.

Тэги

Флаг t заменяет "последнее искомое" выражение на тэг, по которому был переход. По умолчанию отключен, последнее искомое меняется только при поиске. Но тэг попадает в историю поиска в любом случае.

Флаг d регламентирует трактовку символа ./ в опции tags, которая описывает, где искать файл тегов. С флагом этот символ значит "текущий каталог", а без — каталог с текущим файлом.

Отступы

Флаг I не удаляет отступ, сделанный autoindent в том случае, если сразу после того, как отступ сделан, курсор был сдвинут вверх или вниз.

Флаг p включает старый алгоритм отступов для LISP.

Соединение строк

Флаг j относится к соединению строк, и предписывает добавлять второй пробел только после точки, но не после восклицательного и вопросительного знаков.

Флаг q влияет на позицию курсора при соединении более, чем двух строк: курсор будет там, где был бы при соединении двух.

Командная строка

Флаг e относится к выполнению команд в регистре в командной строке: :@r. Если флаг присутствует, то добавляется символ конца строки, так что последняя строка будет выполнена. Без флага она повиснет в командной строке и ее можно отредактировать. Например, напишем текст:

/666
delete
put

Выделим этот текст через v (до последней t, но не дальше!) и удалим в регистр q: "qd

Флаг по умолчанию выставлен. Напишем что-то вроде

555
666
777

и введем :@q , нажав ввод. Строчки 666 и 777 поменяются местами. Теперь уберем флаг: :set cpoptions-=e

Ввод нам придется нажать самим, зато можно отредактировать команду.

Флаг x означает, что нажатие <esc> в ходе редактирования команды в командной строке выполнит команду (а не отменит и выйдет из режима командной строки).

Флаг * включает трактовку команды :* как :@ (выполнение регистра). Без него эта команда трактуется как :'<,'>, то есть как предыдущее выделение. Например, если вы выделили строки с помощью V, то потом можно удалить их командой :*delete

Пробельные символы

Флаг H указывает, куда перемещать курсор команде I (которая начинает вставку в начале строки, но после начальных пробелов) в том случае, когда на строке одни пробелы. С флагом она переместит курсор перед последним пробелом, то он как бы "текст". Без него (так по умолчанию) курсор окажется после последнего пробела, то есть "текстом" считается только конец строки.

Флаг L меняет поведение табуляции в режиме скрытых символов.

Флаг v включает любопытный режим: удаляемые клавишей Backspace символы не исчезают сразу — только после "ухода" курсора или при выходе из режима вставки.

Флаг w меняет поведение команды cw (она удаляет слово или конец слова), если она применена на пробельном символе. Без флага она удалит все пробелы до следующего слова, а с ним — только один символ.

Внешние фильтры

Флаг R удаляет метки строк, пропущенных через внешний фильтр.

А флаг ! при повторе фильтрации через внешнюю команду, использует по умолчанию последнюю использованную внешнюю команду; без флага используется команда, через которую, последний раз фильтровали.

Прочее

Флаг C относится к выполнению скриптов: он отменяет трактовку строк, начинающихся со слеша \, как строк продолжения. Вообще, в Вим именно так нестандартно записывается продолжение строки. Но для команд :append и :insert это может вызвать проблему, если выставляемое начинается со слеша. Вот для этого и предусмотрен флаг. Его включают перед и выключают после операции. Если операция в функции, то флаг нужно выставить перед определением функции, а не в ней.

Флаг E включает "строгости": команды-операторы y, d, c и другие должны получать хоть бы один символ текста. Пустой диапазон рассматривается как ошибка. Так, dG будет ошибкой, если вы и так в конце файла.

Флаг J уточняет понятие предложения: после точки, восклицательного или вопросительного знаков должно идти два пробела. Табуляция не считается за пробел.

Флаг n предписывает использовать столбец, в котором отображаются номера строк, для фолдинга. Может быть удобно для экономии места на маленьких экранах.

Флаги s и S касаются локальных опций. Если есть s, но не S, то опции задаются при первом входе в буфер, если оба отсутствуют, то при создании. Если присутствует S, то опции задаются при каждом входе. Задаются — значит, копируются опции текущего буфера (за рядом очевидных исключений).

Флаг u делает операцию отмены как в vi: отмена u отменяет последнюю операцию, в том числе себя. То есть можно отменить/вернуть одно действие. Неудобно.

Флаг X выглядит полезным: если команда режима замены R применена с повторителем, то замещаемый текст удаляется один раз.

Так, если у нас строка "666 — это число", курсор на первой шестерке, мы нажимаем в нормальном режиме 2R, вводим 360 и выходим из режима посредством <ESC>, то увидим текст "360360это число". С флагом же будет "360360 — это число".

Интересен флаг $: при изменениях в пределах строки (например, cw) удаляемый текст не удаляется сразу, а помечается значком $ в конце. Потом исчезнет: после выхода из режима вставки или при смещении курсора. Может быть полезно, когда надо видеть, что вы заменили.

С флагом - (минус) команды вертикального перемещения (k,j,- и другие) ничего не делают, если должны переместить курсор выше первой или ниже последней строки. Без флага курсор попадет на первую или последнюю строку. Например, если курсор на третьей строке и мы отдадим команду 5k, то без флага курсор окажется на первой строке, а с ним — не двинется с места. Это может быть полезно при автоматизации действий, чтобы не напортить.

Флаг > предписывает добавлять конец строки к тексту, добавляемому в регистр. Так, пусть у нас текст "I love Vim", курсор на I и мы выполняем команды "qyww"Qyww"Qywe"qp

Пояснение: "q указывает регистр, в который копировать, а "Q — регистр, который надо дополнить. yw предписывает скопировать в регистр слово, а w — сместиться на начало следующего слова. Наконец, p вставляет накопленный текст.

Без флага мы получим I love VimI love Vim, а с ним

I love VimI
love
Vim

Флаг "решетка" отключает повторители перед D, o, O — они не имеют эффекта.

Флаг & предписывает не удалять swap-файл при выходе, если в него всё было слито командой :preserve. Имеет эффект только при выходе.

Флаг "точка" в Виме не нужен, это чисто для совместимости с vi: команды смен каталога :chdir и :cd выдадут ошибку, если файл не сохранен. Вим помнит полный путь.

Флаг | означает, что переменные окружения $LINES и $COLUMNS доминируют над размерами терминала, которые получены через системные функции.

Удачи, коллеги!

Научно-популярные каналы на Дзене: путеводитель
Новости популярной науки12 марта 2022