33.33 Группы атомарных изменений

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

Чтобы сделать это для одного буфера, который уже активен, просто оберните вызов atomic-change-group вокруг кода, который вносит изменения, как здесь:

(atomic-change-group
  (insert foo)
  (delete-region x y))

Если внутри тела atomic-change-group происходит ошибка (или другой нелокальный выход), он отменяет все изменения в этом буфере, которые были во время выполнения тела. Такая группа изменений не влияет на другие буферы — любые такие изменения остаются.

Если вам нужно что-то более сложное, например, чтобы изменения в различных буферах составляли одну атомарную группу, вы должны напрямую вызывать более низкоуровневые функции, которые использует atomic-change-group.

Функция: prepare-change-group &optional buffer

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

Чтобы использовать группу изменений, вы должны активировать ее. Вы должны сделать это до внесения каких-либо изменений в текст buffer.

Функция: activate-change-group handle

Эта функция активирует группу изменений, которую обозначает handle.

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

Функция: accept-change-group handle

Эта функция принимает все изменения в группе изменений, указанной handle, делая их окончательными.

Функция: cancel-change-group handle

Эта функция отменяет и отменяет все изменения в группе изменений, указанной handle.

Вы можете заставить некоторые или все изменения в группе изменений рассматриваться как единое целое для целей команд undo (см. Отмена) с помощью undo-amalgamate-change-group.

Функция: undo-amalgamate-change-group

Объединяет все изменения, внесенные в группе изменений с момента состояния, определенного handle. Эта функция удаляет все границы отмены между записями отмены изменений с момента, описанного handle. Обычно handle — это дескриптор, возвращенный prepare-change-group, в этом случае все изменения с начала группы изменений объединяются в единый блок отмены.

Ваш код должен использовать unwind-protect, чтобы гарантировать, что группа всегда завершается. Вызов activate-change-group должен находиться внутри unwind-protect на случай, если пользователь нажмет C-g сразу после его выполнения. (Это одна из причин, по которой prepare-change-group и activate-change-group являются отдельными функциями: обычно вы вызываете prepare-change-group до начала этого unwind-protect.) После завершения группы не используйте дескриптор снова — в частности, не пытайтесь завершить одну и ту же группу дважды.

Чтобы создать многобуферовую группу изменений, вызовите prepare-change-group один раз для каждого буфера, который вы хотите охватить, а затем используйте nconc для объединения возвращенных значений, как здесь:

(nconc (prepare-change-group buffer-1)
       (prepare-change-group buffer-2))

Затем вы можете активировать многобуферовую группу изменений одним вызовом activate-change-group и завершить ее одним вызовом accept-change-group или cancel-change-group.

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

Emacs отслеживает группы изменений, предполагая, что следуя каждому cdr в buffer-undo-list, он в конечном итоге доберется до cons, который был установлен во время вызова prepare-change-group.

Если buffer-undo-list больше не содержит этот cons, Emacs теряет отслеживание любых групп изменений, что приводит к ошибке при отмене группы изменений. Чтобы этого избежать, не вызывайте функции, которые могут редактировать список отмены таким образом, пока активна группа изменений: в частности, команды «объединения», такие как delete-char, которые вызывают undo-auto-amalgamate.