4

Suppose a third-party plugin contains the following function:

(defun example-function ()
  (interactive)
  ;; ... do many things ...
  (goto-char (point-max))
  ;; ... do many things ...
  (goto-char (point-max))
  ;; ... do many things ...
  (goto-char (point-max)))

The function uses goto-char in various places in its body.

I want to change the behavior of the function such that every time it calls (goto-char (point-max)), it will also call (recenter -1). To that end, I added this advice to redefine the calls to goto-char:

(advice-add 'example-function
            :around
            (lambda (orig-fun &rest args)
              (cl-labels ((goto-char (pos)
                            (goto-char pos)
                            (recenter -1)))
                (apply orig-fun args))))

However, the cl-labels appears to have no effect; goto-char still retains its old behavior inside example-function. What is wrong with the advice?

(Emacs version: GNU Emacs 25.2)

1
  • You can't change the behavior of goto-char just inside example-function using an advice. You have two options: rewrite example-function to call recenter after every goto-char; or add an :after advice to goto-char which calls recenter. In the latter case, the advised goto-char will be called every time, not just from inside example-function. – NickD Jan 17 at 19:27
3

(Caveat: All from memory; I might have some of this wrong.)

WRT your code:

(lambda (orig-fun &rest args)
              (cl-labels ((goto-char (pos)
                            (goto-char pos)
                            (recenter -1)))
                (apply orig-fun args))))

You never call goto-char in the body of that cl-labels form, so your override wouldn't be used.

It's the orig-fun definition which would be calling goto-char, but that code is not within the lexical scope of the cl-labels code.

Generally, using cl-letf with (symbol-function 'foo) as the PLACE allows dynamically-scoped overrides (i.e. seen by other functions as well as the immediate code), but even then I think this kind of override could only work in uncompiled code, as goto-char has its own byte-code op, meaning that calls to it are compiled away to just that op code when it's byte-compiled (which also inhibits writing advice for goto-char itself).

2
1

I am not sure what is wrong with your code, but it seems like it should not work because you are recursively calling goto-char inside the definition. You would have to redefine the goto-char function (which is in the C code I think). I think you need to advise the goto-char function temporarily like this:

(defun example-function ()
  (interactive)
  ;; ... do many things ...
  (goto-char (point-max))
  ;; ... do many things ...
  (goto-char (point-max))
  ;; ... do many things ...
  (goto-char (point-max)))


(defun modified-goto-char (orig-fun &rest args)
  (apply orig-fun args)
  (recenter -1))


(advice-add 'example-function
            :around
            (lambda (orig-fun &rest args)
              (advice-add 'goto-char :around #'modified-goto-char)
              (apply orig-fun args)
              (advice-remove 'goto-char #'modified-goto-char)))

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.