Справочник по VIM

Режим сверки отличий

СПРАВОЧНИК ПО VIM — Брам Мооленаар

 

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

Основные сведения об этом режиме приводятся в разделе 08.7 Руководства пользователя.

  1. Запуск режима сверки отличий
  2. Просмотр различий
  3. Перемещение между различиями
  4. Копирование различий
  5. Настройка режима сверки отличий

Vi не имеет такой возможности.

1. Запуск режима сверки отличий

Проще всего начать редактирование в режиме сверки отличий путём запуска Vim по команде «vimdiff». Эта команда запускает Vim, как обычно, но также включает несколько дополнительных настроек, предназначенных для просмотра различий между файлами в списке аргументов.

vimdiff файл1 файл2 [файл3 [файл4]]

Эта команда эквивалентна следующей:

vim -d файл1 файл2 [файл3 [файл4]]

Вы также можете использовать аналогичные команды для работы в графическом интерфейсе, а именно «gvimdiff» и «vim -d -g». Кроме того, возможно использование режима отображения различий только для просмотра — в этом случае Vim запускается по команде «viewdiff» или «gviewdiff». Для запуска в ограниченном режиме можно также использовать ключ командной строки «-r»,  см. |-Z|.

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

Эта возможность работает только в том случае, если на вашей системе доступна стандартная команда «diff». См. справку по опции ‘diffexpr’.

При запуске в режиме сверки отличий Vim открывает отдельное окно для каждого файла, как при использовании ключа командной строки |-O|. При этом используется вертикальное разделение окон. Если вы предпочитаете разделение по горизонтали, то добавьте в командную строку ключ |-o|:

vimdiff -o файл1 файл2 [файл3]

Для каждого из файлов в списке аргументов настраиваются следующие значения опций:

‘diff’включена
‘scrollbind’включена
‘scrollopt’включает значение «hor»
‘wrap’выключена
‘foldmethod’«diff»
‘foldcolumn’2

Указанные опции настраиваются как местные по отношению к окну. При редактировании другого файла их значения сбрасываются к существующим глобальным значениям.

Показанные отличия на самом деле являются отличиями между буферами. Если вы внесёте изменения после загрузки файла, то они будут включены в отображаемые различия. Вам может потребоваться время от времени выполнять команду «:diffupdate», поскольку не все изменения приводят к немедленному обновлению различий на экране.

Вы можете указать в файле .vimrc определённые команды, которые должны выполняться только в режиме отображения различий. Это достигается с помощью следующей конструкции:

if &diff
" настройки для режима отображения различий
else
" настройки для обычного режима
endif

Если Vim уже запущен, то переход в режим отображения различий можно осуществить тремя способами.

:diffsplit {имя_файла}Файл с заданным {именем_файла} открывается в новом окне.  В активном и в новом окне настраиваются опции, которые применяются при использовании «vimdiff». См. также ‘diffexpr’.
:diffthisАктивное окно добавляется в набор окон, для которых включён режим отображения различий. В этом окне настраиваются опции, которые применяются при использовании «vimdiff».
:diffpatch {файл_заплатки}Указанный {файл_заплатки} применяется к текущему буферу, после чего результат применения заплатки отображается в новом буфере. При этом настраиваются опции, которые применяются при использовании «vimdiff».
{файл_заплатки} может быть в любом формате, который понимает программа «patch», или который может быть обработан выражением, заданным в значении опции ‘patchexpr’. Обратите внимание, что {файл_заплатки} должен содержать заплатку только для одного файла, а именно — для текущего файла. Если в {файле_заплатки} содержатся заплатки также и для других файлов, то результаты могут быть непредсказуемыми.  Vim изменяет рабочий каталог на /tmp, чтобы избежать случайного применения заплатки к файлам в рабочем каталоге. Тем не менее, это всё равно может привести к созданию файлов «.rej». Кроме того, заплатки могут быть применены к файлам, если в файле заплатки указаны абсолютные пути к файлам.

Чтобы указанные команды использовали вертикальное разделение окон, применяйте их с командой |:vertical|. Например:

:vert diffsplit main.c~
:vert diffpatch /tmp/diff

Опция ‘diff’ может быть включена не более чем у четырёх буферов одновременно.

Поскольку значения опций запоминаются вместе с буфером, то вы можете перейти к редактированию какого-либо иного файла, и после возвращения к редактированию прежнего буфера снова использовать режим отображения различий. Чтобы отключить режим сверки отличий, достаточно просто отключить опцию ‘diff’ и, возможно, избавиться от колонки складок:

:set nodiff foldcolumn=0

2. Просмотр различий

В окнах, которые используют режим отображения различий, отображается текст, в котором подсвечиваются обнаруженные различия. При выполнении прокрутки текста опция ‘scrollbind’ заставляет также прокручиваться текст и в других окнах, использующих этот режим. Если вы используете вертикальное разделение окон, то текст в разных окнах должен правильно выравниваться относительно друг друга.

Выравнивание текста может быть нарушено в следующих случаях:

  • при включённой опции ‘wrap’: некоторые длинные строки могут занимать экране две и более строк экрана;
  • при открытии складок в одном окне, в то время как в других окнах складки остаются закрытыми;
  • при выключенной опции ‘scrollbind’;
  • при внесении изменений в текст;
  • если в значении опции ‘diffopt’ отсутствует «filler», то при вставке или удалении строк выравнивание может быть нарушено.

Все буферы, которые редактировались в окне с включённой опцией ‘diff’ будут включены в набор файлов для просмотра различий. Это также касается и скрытых буферов — чтобы это было возможно, они должны быть загружены в такое окно перед скрытием.

Поскольку опция ‘diff’ является местной по отношению к окну, вы можете просматривать один и тот же буфер одновременно и в окне с включённым режимом просмотра отличий, и в обычном окне. Кроме того, вы можете просматривать изменения, внесённые в буфер, однако, поскольку Vim не может использовать два буфера для одного и того же файла, вам потребуется скопировать исходную версию файла и просматривать различия между отредактированным вариантом и этой копией. Например:

:!cp % tempfile
:diffsplit tempfile

Выгруженный из памяти буфер не может быть использован в режиме просмотра различий, однако вы вполне можете использовать скрытые буферы. Чтобы закрыть окно без выгрузки содержимого буфера из памяти, следует использовать команду «:hide».

Vim пытается обновлять различия при внесении изменений в текст, таких как вставка или удаление строк. Более сложные изменения и изменения внесённые в пределах одной строки могут не обновляться, но вы можете всегда сделать это вручную с помощью команды

:diffupdate

На месте строки, которая отсутствуют в одном окне, но присутствует в другом, Vim выводит «строку-заполнитель». Строка-заполнитель указывает, что в данном файле строка была удалена, либо соответствующая строка была добавлена в другом файле. Если вы не хотите использовать строку-заполнитель, то вам следует исключить «filler» из значения опции ‘diffopt’.

Для скрытия текста, в котором не обнаружены изменения, используются складки.  Команды, позволяющие работать со складками, рассматриваются в разделе справочника |складки|.

Количество строк контекста вокруг различающихся строк, которые не прячутся в складки, может быть настроено с помощью опции ‘diffopt’. Например, чтобы использовать 3 контекстных строки:

:set diffopt=filler,context:3

Различия подсвечиваются с помощью следующих групп подсветки:

DiffAddДобавленные (вставленные) строки. Это строки, которые существуют в данном буфере и отсутствуют в другом буфере.
DiffChangeИзменённые строки.
DiffTextИзменённый текст внутри изменённой строки. Vim ищет первый и последний (с конца строки) отличающиеся символы и подсвечивает весь текст, расположенный между этими символами. Это означает, что неотличающиеся части текста внутри такого фрагмента всё равно будут подсвечиваться с помощью данной группы подсветки.
DiffDeleteУдалённые строки. Также называются «строками-заполнителями», поскольку они на самом деле не существуют в данном буфере.

3. Перемещение между различиями

Для перемещения между различиями используется две команды:

[cПеремещение назад к предыдущему началу различия. При использовании числовой приставки команда выполняется соответствующее количество раз.
]cПеремещение вперёд к следующему началу различия. При использовании числовой приставки команда выполняется соответствующее количество раз.

Если различие отсутствует и курсор не может быть перемещён, то подаётся звуковой сигнал об ошибке.

4. Копирование различий

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

:[диапазон]diffg[et] [буфер]Команда изменяет текущий буфер таким образом, что он становится идентичным другому буферу, заданному параметром [буфер]. Если [буфер] не задан, то команда выполняется только в том случае, когда имеется всего два буфера в режиме сверки отличий.
О [диапазоне] см. ниже.
:[диапазон]diffpu[t] [буфер]Изменить указанный буфер (или единственный другой буфер) таким образом, чтобы свести на нет различия с текущим буфером. Команда работает точно так же, как «:diffget», но вместо текущего буфера изменения вносятся в другой буфер.
О [диапазоне] см. ниже.
doТо же, что и «:diffget» без указания параметра и диапазона. Мнемоника этой команды происходит от английского слова «Obtain», т.е. «получить» («dg» не может быть использовано с этой целью, поскольку это может быть началом команды «dgg»!).
dpТо же, что и «:diffput» без указания параметра и диапазона.

Если [диапазон] не задан, то эти команды затрагивают различия, расположенные в позиции курсора или непосредственно перед ним. При использовании диапазона Vim пытается получить или передать только указанные строки. Это не всегда возможно, если в одном из буферов имеются удалённые строки.

Удалённые строки могут находиться в самом конце буфера, ниже последней строки. Если курсор находится в последней строке буфера и над этой строкой отсутствует различие, то команды «:diffget» и «do» получают соответствующие строки из другого буфера.

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

:1,$+1diffget

Обратите внимание, что хотя удалённые строки отображаются на экране, они не являются строками текста. Вы не можете, например, переместить курсор в такую строку. Чтобы заполнить удалённые строки строками из другого буфера, вам следует применить команду «:diffget» на строке, которая следует сразу после удалённых строк.

В качестве параметра «[буфер]» может выступать номер буфера, шаблон имени буфера или часть имени буфера. Приведём несколько примеров:

:diffgetИспользовать другой буфер в режиме сверки отличий
:diffget 3Использовать буфер 3
:diffget v2Использовать буфер, имя которого соответствует «v2» и который находится в режиме сверки отличий (например, «file.c.v2»)

5. Настройка режима сверки отличий

См. также |’diffopt’| и справку по элементу «diff» опции ‘fillchars’.

ОБНАРУЖЕНИЕ РАЗЛИЧИЙ

Для обнаружения различий можно использовать стандартную программу «diff», или какой-либо иной способ, для чего существует специальная опция ‘diffexpr’. Опция ‘diffexpr’ применяется для настройки использования какой-либо другой программы сравнения файлов и обнаружения различий, отличной от стандартной программы «diff».

Если значение опции ‘diffexpr’ не задано, то Vim использует для обнаружения различий между файлами file1 и file2 следующую команду:

diff file1 file2 > outfile

Символ «>» заменяется в этой команде на значение опции ‘shellredir’.

Вывод программы «diff» должен быть в обычном формате «ed». НЕ используйте так называемый «контекстный» формат различий. Формат, который понимает Vim выглядит, например, так:

1a2
> bbb
4d4
< 111
7c7
< GGG
---
> ggg

Здесь, «1a2» добавляет строку «bbb», «4d4» удаляет строку «111», а «7с7» заменяет строку «GGG» на «ggg».

Опция ‘diffexpr’ в качестве значения может иметь выражение, которое вычисляется Vim-ом для получения вышеуказанного формата описания различий.  При этом используются следующие переменные, значениями которых являются имена использованных файлов:

v:fname_inисходный файл
v:fname_newновая версия того же самого файла
v:fname_outполученный файл отличий

Кроме того, опция ‘diffexpr’ использует значения «icase» и «iwhite» в опции ‘diffopt’. Выражение, заданное в опции ‘diffexpr’, не должно изменять значение опций ‘lines’ и ‘columns’.

Пример (выполняется почти та же операция, что и при использовании пустой опции ‘diffexpr’):

set diffexpr=MyDiff()
function MyDiff()
let opt = ""
if &diffopt =~ "icase"
let opt = opt . "-i "
endif
if &diffopt =~ "iwhite"
let opt = opt . "-b "
endif
silent execute "!diff -a --binary " . opt . v:fname_in . " " .
\ v:fname_new . " > " . v:fname_out
endfunction

Аргумент «-a» используется для того, чтобы обеспечить сравнение файлов в виде текста, поскольку сравнение файлов в бинарном виде не очень полезно. Аргумент «—binary» используется для чтения файлов в бинарном режиме, чтобы символ CTRL-Z не приводил к завершению текста при выполнении этого примера в DOS.

Vim выполняет проверку на правильность формата вывода программы обнаружения различий. Если Vim обнаружит, что используется неправильный формат, то выводится сообщение об ошибке. Возможные причины возникновения этой ошибки:

  • Программа «diff» не может быть выполнена.
  • Программа «diff» не создаёт файл различий в стандартном стиле «ed» (см. выше).
  • Опция ‘shell’ и другие опции, связанные с настройкой оболочки, имеют неправильное значение. Проверьте, правильно ли выполняется обработка текста с использованием программ-фильтров, например с помощью команды «:!sort».
  • Вы используете неработающее выражение в качестве значения опции ‘diffexpr’.

Если причина появления ошибки не совсем ясна, то попробуйте включить опцию ‘verbose’, чтобы увидеть больше сообщений.

ИСПОЛЬЗОВАНИЕ ЗАПЛАТОК

Опция ‘patchexpr’ позволяет использовать для наложения заплаток выражение вместо стандартной программы «patch».

Если опция ‘patchexpr’ не задана, то Vim выполняет вызов стандартной программы «patch» следующим образом:

patch -o outfile origfile < patchfile

Это должно работать с подавляющим большинством версий программы «patch». Обратите внимание, что символ CR в середине строки может привести к проблемам, так как он воспринимается в качестве символа переноса строки.

Если вас не устраивает стандартное решение, то вы можете использовать для достижения той же цели собственное выражение, которое следует задавать в виде значения опции ‘patchexpr’. При этом используются следующие переменные, значениями которых являются имена использованных файлов:

v:fname_inисходный файл
v:fname_diffфайл-заплатка
v:fname_outфайл с внесёнными после наложения заплатки изменениями

Пример (выполняются те же действия, то и при пустом значении опции ‘patchexpr’):

let patchexpr=MyPatch
function MyPatch
:call system("patch -o " . v:fname_out . " " . v:fname_in .
\ " < " . v:fname_diff)
endfunction

Убедитесь, что при использовании программы «patch» не возникает нежелательных побочных эффектов. Следите за созданными дополнительно файлами, их следует удалять. Выражение должно применять заплатку к файлу и не делать ничего лишнего.

Vim меняет рабочий каталог на «/tmp» или какой-нибудь другой временный каталог перед тем, как начать вычисление выражения, заданного в значении опции ‘patchexpr’. Это, по идее, должно предупредить применение заплатки к файлам в рабочем каталоге. Кроме того, Vim удаляет файлы, начинающиеся с имени в значении переменной v:fname_in и заканчивающиеся на «.rej» или «.orig».