4

Emacs manual §31.28 says this about before-change-functions and after-change-functions:

These hook variables let you arrange to take notice of all changes in all buffers (or in a particular buffer, if you make them buffer-local).

That sounds like exactly what I need to monitor the status of the *Messages* buffer, but there is a note in the manual that says

Output of messages into the *Messages* buffer does not call these functions.

What is the significance of this message, and how can I set up functions that run every time the *Messages* buffer is modified?

8
  • 1
    What are you trying to achieve? – phils Jul 23 '15 at 21:23
  • 1
    There is no such hook. You could advise message, but it's not clear whether that's sufficient, or sensible. If you're trying to debug something, use the debug-on-message variable (available since 24.3). – phils Jul 23 '15 at 21:41
  • Question is as-is. I'm trying to avoid the XY problem, but in this case I want to know the answer to this specific question. I am not trying to debug anything, and I do not want to advise message. I want to be able to fire a function whenever the contents of *Messages* change. I appreciate your efforts in trying to solve my root problem and will consider whether there is a better way to contextualize the problem. I believe this question in its current state is unambiguously answerable according to SX guidelines, and I want to be careful about adding more information. – Matthew Piziak Jul 23 '15 at 22:26
  • 1
    @phils Advising message wouldn't work for messages emitted by primitives. It would be risky for what is probably the reason that no hook fires when a message is emitted: any error or other message emitted from that advice/hook would cause recursion, so you'd need to be careful and disable the hook (like post-command-hook) in case this happens. – Gilles 'SO- stop being evil' Oct 25 '17 at 21:32
  • 1
    @Name Indirect buffers wouldn't help, because what's special isn't the *Messages* buffer, but the code that inserts the messages into it. – Gilles 'SO- stop being evil' Oct 25 '17 at 21:33
2

I think you'd need to use polling, sadly.

Detecting via polling that a change happened isn't very hard. Detecting which change happened would in general require keeping a copy of the buffer's contents, but if you're willing to assume that the buffer is only modified by adding to its end or removing from its beginning, you can make it much cheaper.

(run-with-timer 1 t #'sm-poll-message-changes)
(add-hook 'post-command-hook #'sm-poll-message-changes)

(defvar sm--pmc-marker nil)
(defvar sm--pmc-marker-pos nil)
(defvar sm--pmc-tick nil)
(with-current-buffer (get-buffer "*Messages*")
  (setq sm--pmc-marker (point-max-marker)))
  (setq sm--pmc-marker-pos (point-max))
  (setq sm--pmc-tick (buffer-chars-modified-tick)))

(defun sm-poll-message-changes ()
  (with-current-buffer (marker-buffer sm--pmc-marker)
    (unless (eq sm--pmc-tick (buffer-chars-modified-tick))
      ;; A change happened.
      (let ((deleted-chars-at-bob
             (- sm--pmc-marker-pos sm--pmc-marker))
            (inserted-chars-at-eob
             (- (point-max) sm--pmc-marker)))
        (setq sm--pmc-marker-pos (point-max))
        (setq sm--pmc-tick (buffer-chars-modified-tick))
        (move-marker sm--pmc-marker (point-max))
        ...DO-YOUR-THING-HERE...)))))

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service, privacy policy and cookie policy

Not the answer you're looking for? Browse other questions tagged or ask your own question.