Привет, коллеги. В целом ряде аспектов Вим ведет себя не так, как 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 доминируют над размерами терминала, которые получены через системные функции.
Удачи, коллеги!