Tracking Unauthorized Changes

Suppose that, in spite of our precautions against unwanted buffer-modifying commands, the user manages to invoke one anyway. The state of the crossword grid on the screen won't match the data structure in crossword-grid. How can we recover?

One way is to attach a function to after-change-functions (see the section called Clever Approach in Chapter 4) that is invoked every time the buffer changes. If the change was "unauthorized," we must somehow resynchronize the buffer and the crossword-grid data structure.

What's "unauthorized"? Trivially, it's the opposite of "authorized," so let's add a mechanism to "authorize" changes to the buffer.

(defvar crossword-changes-authorized nil
  "Are changes currently authorized?")
(make-variable-buffer-local 'crossword-changes-authorized)
(defmacro crossword-authorize (&rest subexprs)
  "Execute subexpressions, authorizing changes."
  '(let ((crossword-changes-authorized t))
     ,@subexprs))

This is a macro that can be wrapped around function bodies where buffer changes happen. It temporarily sets crossword-changes-authorized to t, executes the function body, then reverts crossword-changes-authorized to its previous value. By default, changes are not authorized. So to prevent the user from corrupting the buffer, we must rewrite crossword-insert-grid and crossword-update-display to authorize the changes they make:

(defun crossword-insert-grid (crossword) "Insert CROSSWORD into the current buffer." (crossword-authorize (mapcar 'crossword-insert-row ...

Get Writing GNU Emacs Extensions now with the O’Reilly learning platform.

O’Reilly members experience books, live events, courses curated by job role, and more from O’Reilly and nearly 200 top publishers.