On 2022-06-27 03:50, Lars Ingebrigtsen wrote: > does the same thing happen if you execute a keyboard macro > interactively? That works. Evaluating the following in *scratch* does not signal an error: (let* ((acf 0) (incacf (lambda (&rest _) (setq acf (1+ acf))))) (add-hook 'after-change-functions incacf nil t) (unwind-protect (execute-kbd-macro (kbd "x")) (remove-hook 'after-change-functions incacf t)) (when (/= acf 1) (error "a-c-f didn't run"))) The above code shows that the hook runs synchronously as expected. (The hook runs before `execute-kbd-macro' returns, not after some idle time or when execution returns to the main command loop.) > Or if you run the same code outside of an ert context? It does not work. Evaluating the following in *scratch* signals an error: (save-window-excursion (with-temp-buffer (let ((b (current-buffer))) (with-current-buffer-window b `(display-buffer-below-selected (body-function . ,(lambda (window) (select-window window t) (let ((acf 0)) (add-hook 'after-change-functions (lambda (&rest _) (setq acf (1+ acf))) nil t) (execute-kbd-macro (kbd "x")) (when (/= acf 1) (error "a-c-f didn't run")))))) nil))))