Отслеживание изменений буфера
Использовать before-change-functions и after-change-functions на практике бывает сложно из-за ряда подводных камней: например, эти два вызова не всегда правильно образуют пару, некоторые вызовы могут отсутствовать — либо потому, что некоторые примитивы Emacs не смогли правильно сопоставить их, либо из-за неправильного использования inhibit-modification-hooks. Кроме того, на эти функции-перехватчики распространяется множество ограничений: в частности, они в принципе не должны изменять текущий буфер или использовать операцию, которая может блокировать выполнение, и должны выполняться быстро, поскольку некоторые команды могут вызывать их много раз.
Библиотека Track-Changes предоставляет альтернативный API, основанный на этих функциях-перехватчиках. Первое важное отличие от after-change-functions заключается в том, что вместо границ изменения и длины предыдущего содержимого она предоставляет границы изменения и фактическое предыдущее содержимое этой области. Необходимость извлекать информацию из исходного содержимого буфера — одна из основных причин, по которым некоторым пакетам приходится использовать и before-change-functions, и after-change-functions, а затем пытаться сопоставить их.
Второе отличие состоит в том, что уведомление об изменении отделено от его обработки, а все изменения, происходящие между первым изменением и фактической обработкой, автоматически объединяются в одну операцию изменения. Это позволяет естественным и простым образом обрабатывать изменения с большей периодичностью, например один раз за команду, и снимает большинство ограничений, распространяющихся на обычные функции-перехватчики изменений, благодаря чему можно использовать блокирующие операции или изменять буфер.
Чтобы начать отслеживание изменений, вызовите track-changes-register, передав в качестве аргумента функцию signal. В результате возвращается id отслеживателя, который используется для идентификации вашего отслеживателя изменений в других функциях библиотеки. При изменении буфера библиотека вызывает функцию signal, чтобы уведомить вас об этом изменении, и сразу начинает накапливать последующие изменения в одно объединённое изменение. Функция signal служит только для уведомления о произошедшем изменении и не получает его описания. Кроме того, библиотека не будет вызывать её снова, пока вы не получите сведения об изменении.
Чтобы получить сведения об изменениях, вызовите track-changes-fetch. Эта функция предоставляет границы изменений, накопившихся с момента последнего вызова, а также предыдущее содержимое этой области. Она также повторно «взводит» функцию signal, чтобы библиотека снова вызвала её после следующего изменения буфера.
- Функция: track-changes-register signal &key nobefore disjoint immediate
-
Эта функция создаёт новый отслеживатель изменений. Отслеживатели изменений остаются абстрактными сущностями, поэтому мы рассматриваем их просто как идентификаторы, и функция возвращает id отслеживателя.
signal — это функция, которую библиотека будет вызывать, чтобы уведомить об изменении. Иногда она будет вызываться с одним аргументом, а иногда — с двумя. При первом изменении буфера с момента последнего вызова этим отслеживателем
track-changes-fetchбиблиотека вызывает функцию signal с одним аргументом, содержащим id отслеживателя.По умолчанию функция signal вызывается не сразу, а с задержкой в 0 секунд, задаваемой таймером (см. раздел Таймеры отложенного выполнения). Обычно это желательно, чтобы функция signal не вызывалась слишком часто и выполнялась в контексте, допускающем более широкий набор операций. Это избавляет клиентский код от необходимости беспокоиться о производительности и о том, какие операции могут вызвать проблемы. Если клиентскому коду требуется больший контроль, в качестве аргумента immediate можно указать значение, отличное от
nil. В этом случае библиотека вызывает функцию signal непосредственно изafter-change-functions. Учтите, что в таком случае функция signal должна избегать изменения буфера и использования операций, которые могут блокировать выполнение.Если вам не нужно фактическое предыдущее содержимое буфера, а эта библиотека используется только для объединения множества небольших изменений в одно крупное и переноса обработки на более удобное время, для аргумента nobefore можно указать значение, отличное от
nil. В таком случаеtrack-change-fetchпредоставляет только длину предыдущего содержимого, как иafter-change-functions. Это также позволяет библиотеке сократить объём выполняемой работы.Возможно, вы захотите объединять множество небольших изменений в более крупные, но не захотите делать это, если изменения расположены слишком далеко друг от друга. Если для аргумента disjoint указать значение, отличное от
nil, библиотека сообщит вам о предстоящем изменении, которое находится «далеко» от уже ожидающих обработки изменений: она немедленно вызовет функцию signal, на этот раз передав ей два аргумента — id отслеживателя и число символов между предстоящим изменением и уже ожидающими обработки изменениями. Само по себе это не мешает объединить новое изменение с предыдущими, поэтому, если вы считаете, что предстоящее изменение действительно слишком далеко, сразу вызовитеtrack-change-fetch. Учтите, что при вызове функции signal из-за несмежного изменения вызов выполняется непосредственно изbefore-change-functions, поэтому применяются обычные ограничения на изменение буфера и использование операций, которые могут блокировать выполнение.
- Функция: track-changes-fetch id func
-
Эта функция позволяет узнать, что изменилось в буфере. Передав id отслеживателя, вы позволяете библиотеке определить, какие изменения уже были обнаружены этим отслеживателем. Вместо того чтобы возвращать описание изменений,
track-changes-fetchвызывает функцию func и передаёт ей это описание в виде трёх аргументов: beg, end и before, гдеbeg..endзадают границы изменённой области, а before описывает её предыдущее содержимое. Обычно before — это строка с предыдущим текстом изменённой области, но если при вызовеtrack-changes-registerдля аргумента nobefore было указано значение, отличное отnil, вместо строки передаётся количество символов в этом предыдущем тексте.Если с момента последнего вызова изменений не произошло,
track-changes-fetchпросто не вызывает func и возвращаетnil. Если изменения произошли, функция вызывает func и возвращает значение, возвращённое функцией func. Однако обратите внимание: функция func вызывается только один раз, независимо от количества изменений; они сводятся к одной тройке beg/end/before.В некоторых случаях библиотека не получает уведомления обо всех изменениях, например из-за ошибки в низкоуровневом коде на C или неправильного использования
inhibit-modification-hooks. Обнаружив такую проблему, библиотека передаёт функции func областьbeg..end, охватывающую весь буфер, а аргумент before получает значение-символerror, указывающее на то, что библиотеке не удалось определить, что именно изменилось.После завершения функции func
track-changes-fetchснова включает функцию signal, чтобы она вызывалась при следующем изменении. Именно поэтому эта функция вызывает func, а не возвращает описание: так вы можете обрабатывать изменение, не опасаясь, что функция signal сработает в процессе, поскольку она включается снова только после завершения func.
- Переменная: track-changes-undo-only
Если ваш код использует
track-changesдля внесения дополнительных изменений в буфер (например, чтобы пометить отредактированные части буфера), возможно, вы захотите отказаться от этих изменений, если они вызваны отменой (которая, предположительно, также отменяет изменения, внесённые вами при соответствующем редактировании). Для этой целиtrack-changes-fetchсвязываетtrack-changes-undo-onlyсо значением, отличным отnil, во время вызовов func, если изменения произошли в результате отмены.
- Функция: track-changes-unregister id
Эта функция сообщает библиотеке, что отслеживателю id больше не нужно получать сведения об изменениях буфера. Большинству клиентов не потребуется прекращать отслеживание изменений, но для таких клиентов, как второстепенные режимы, важно вызвать эту функцию при отключении второстепенного режима. В противном случае отслеживатель продолжит накапливать изменения и потреблять всё больше ресурсов.
Copyright © 1990-1996, 1998-2026 Free Software Foundation, Inc.
Licensed under the GNU GPL license.
https://www.gnu.org/software/emacs/manual/html_node/elisp/Tracking-changes.html