7

There seems to be a bug in clojure-mode (mentioned here) because it does things in a mode hook (for paredit-mode) but doesn't undo them when the mode is turned off.

I understand that add-hook can register a function to run when a mode is turned on, but is there a way to run a hook when a mode is turned off?

Here's a github link see the clojure-mode code that adds the hook.

2
  • 1
    To further clarify, minor modes are entered and exited (and so hooks run on both occasions). Major modes are never 'exited' at all, so they cannot run a hook before a different major mode is called. Instead the new major mode effectively just clobbers all the buffer-local settings and starts over -- but the standard way that they do that means that change-major-mode-hook runs (with the original major mode still enabled), before anything else happens.
    – phils
    Jan 22, 2019 at 19:39
  • stackoverflow.com/a/19295380/324105 has more details about major modes and hooks, if you're interested.
    – phils
    Jan 22, 2019 at 19:46

2 Answers 2

4

You can use the hook variable change-major-mode-hook for cleaning up major mode settings. It is called right before killing the local variables of the old major mode.
Example code:

(defun clojure-leave-clojure-mode-function ()
  (when (eq major-mode 'clojure-mode)
    (message "Leaving clojure-mode.")))

(add-hook 'change-major-mode-hook #'clojure-leave-clojure-mode-function)
3
  • This is a great suggestion, but in general I think users shouldn't need to run any cleanup code manually, the major-mode should take care of this by making changes buffer locally.
    – clemera
    Jan 22, 2019 at 17:12
  • @clemera If there is a bug in clojure-mode then that bug must be fixed. That is clear. Beside that take a look at the value of change-major-mode-hook. If you have cc-mode loaded then there is at least c-leave-cc-mode-mode. It is not so uncommon to use it for cleanup tasks.
    – Tobias
    Jan 22, 2019 at 17:16
  • Ok, but the major-mode should set it.
    – clemera
    Jan 22, 2019 at 17:17
3

Not for major-modes, for minor-modes the hook runs on exit as well (See here). The major mode is responsible to make changes which affect other packages buffer locally. You probably should send a PR to change the add-hook code to install the hooks locally for the clojure-mode buffer:

(add-hook 'paredit-mode-hook #'clojure-paredit-setup nil 'local)

EDIT:

Looking at the code, this will not suffice. clojure-paredit-setup needs to be changed to do its changes buffer locally, too.

3
  • This won't solve the problem consistently, because clojure-mode adds the hook to paredit-mode-hook every time it runs. Your code removes the offending hook, but it will be replaced every time you open a new file in clojure-mode.
    – Tyler
    Jan 22, 2019 at 19:53
  • Hm, but I suggested to add it buffer locally, the idea is that when activating clojure-mode it changes paredit-mode-hook only locally, for every buffer where it's activated. This way the global paredit-mode-hook is not effected, so there is nothing that need to be undone for non clojure buffers. BUT as I pointed out in the edit, clojure-paredit-setup needs to be changed, too because it changes other global variables.
    – clemera
    Jan 22, 2019 at 22:43
  • Worth noting though, apparently that doesn't work if the buffer (or emacsclient) gets killed. So, if you want to undo something done when setting a minor mode up, apparently you need to come up with an ad-hock hack. The closest I've found to work is the delete-frame-functions hook.
    – Hi-Angel
    May 19 at 9:52

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge that you have read and understand our privacy policy and code of conduct.

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