automatically disable a global minor mode for a specific major mode

chrm picture chrm · Jul 27, 2011 · Viewed 7.3k times · Source

I have centered-cursor-mode activated globaly, like this:

(require 'centered-cursor-mode)
(global-centered-cursor-mode 1)

It works fine, but there are some major modes where I would like to disable it automatically. For example slime-repl and shell.

There is another question dealing with the same problem, but another minor mode. Unfortunately the answers only offer workarounds for this specific minor mode (global-smart-tab-mode), that doesn't work with centered-cursor-mode.

I tried this hook, but it has no effect. The variable doesn't change.

(eval-after-load "slime"
  (progn
    (add-hook 'slime-repl-mode-hook (lambda ()
                                      (set (make-local-variable 'centered-cursor-mode) nil)))
    (slime-setup '(slime-repl slime-autodoc))))

Answer

phils picture phils · Jul 27, 2011

Global minor modes created with the define-globalized-minor-mode1 macro are a bit tricky. The reason your code doesn't appear to do anything is that globalized modes utilise after-change-major-mode-hook to activate the buffer-local minor mode that they control; and that hook runs immediately after the major mode's own hooks4.

Individual modes may implement custom ways of specifying some kind of black list or other method of preventing the mode from being enabled in certain circumstances, so in general it would be worth looking at the relevant M-x customize-group options for the package to see if such facilities exist. However, a nice clean general way of achieving this for ANY globalized minor mode is eluding me for the moment.

It's a shame that the MODE-enable-in-buffers function defined by that macro doesn't do the same (with-current-buffer buf (if ,global-mode ...)) check which is performed by the global mode function. If it did, you could simply use slime-repl-mode-hook to make the global mode variable buffer-local and nil.

A quick hack is to check2 what the turn-on argument is for the globalized minor mode definition (in this instance it's centered-cursor-mode itself3), and write some around advice to stop that from being evaluated for the modes in question.

(defadvice centered-cursor-mode (around my-centered-cursor-mode-turn-on-maybe)
  (unless (memq major-mode
                (list 'slime-repl-mode 'shell-mode))
    ad-do-it))
(ad-activate 'centered-cursor-mode)

Something we can do (with an easy re-usable pattern) is immediately disable the buffer-local minor mode again after it has been enabled. An after-change-major-mode-hook function added with the APPEND argument to add-hook will reliably run after the globalized minor mode has acted, and so we can do things like:

(add-hook 'term-mode-hook 'my-inhibit-global-linum-mode)

(defun my-inhibit-global-linum-mode ()
  "Counter-act `global-linum-mode'."
  (add-hook 'after-change-major-mode-hook
            (lambda () (linum-mode 0))
            :append :local))

1 or its alias define-global-minor-mode which I feel should be avoided, due to the potential for confusion with "global" minor modes created with define-minor-mode. "Globalized" minor modes, while still involving a global minor mode, work very differently in practice, so it is better to refer to them as "globalized" rather than "global".

2 C-hf define-globalized-minor-mode RET shows that turn-on is the third argument, and we check that in the mode definition with M-x find-function RET global-centered-cursor-mode RET.

3 with this approach, that fact is going to prevent you from ever enabling this minor mode with slime-repl-mode or shell-mode buffers, whereas a globalized minor mode with a separate turn-on function could still be invoked in its non-global form if you so desired.

4 https://stackoverflow.com/a/19295380/324105