Search code examples
elisp

Add extra action (recenter) to `org-forward-element`?


In org-mode, strike M-} which invoke org-forward-element is very handy to jump around.

However, the matching line always stay at the bottom. So I have to manually execute (recenter).

How could add the action (recenter) to org-forward-element?

I tried a decorator solution, but does not work.

(defun add-recenter(func)
  (lambda ()
    (func)
    (recenter)))
(setq org-element-forward (add-recenter org-element-forward))

Solution

  • This seems to work:

    (advice-add 'org-forward-element :after (lambda () (recenter)))
    

    I first tried (advice-add 'org-forward-element :after 'recenter), but that didn't work, because if the advice function being added (in this case recenter) is interactive, its interactive spec overrides that of the old function (org-forward-element), which causes an error since recenter generates one argument while org-forward-element takes zero arguments.


    The reason your code doesn't work is that in Emacs Lisp variables and functions are in separate namespaces, just like Common Lisp and unlike Scheme. This is sometimes described as "Lisp-2", as opposed to "Lisp-1". See e.g. this article: http://ergoemacs.org/emacs/lisp1_vs_lisp2.html

    Basically every symbol has both a "value cell" and a "function cell". setq changes the symbol's value cell, and calling a symbol, like (foo), accesses its function cell. The corresponding Emacs Lisp code would be something like:

    (defun add-recenter(func)
      (lambda ()
        (funcall func)
        (recenter)))
    (fset 'org-element-forward (add-recenter (symbol-function 'org-element-forward)))
    

    That is essentially what the advice-add call above does.