17

I started coding a minor mode to provide some keybindings. My initial attempt had a typo:

(define-minor-mode borked-mode
  "A mode defined with a broken key binding"
  nil nil
  '(([b] . 'previous-line)))

I had intended to use the b key, and immediately realized that I should have used "b" instead of [b]. So I redefined the minor mode:

(define-minor-mode borked-mode
  "A mode defined with a broken key binding"
  nil nil
  '(("b" . 'previous-line)))

However, this didn't work. This set me off on a wild goose chase through all the various ways of binding keys (i.e., (kbd ...), [ ... ], etc). Finally I realized that simply re-evaluating the (define-minor-mode ...) form was not changing anything, I was stuck with the original, broken key map. I then tried changing the keymap directly via (define-key borked-mode-map ...) and still couldn't get the corrected keymap loaded. Finally I restarted Emacs, and my minor mode was correctly loaded.

My question is: how do you update a minor mode definition as you develop it? Is there any way to flush a broken definition, or do you have to restart emacs to clear away broken bits?

3 Answers 3

14

The first time you evaluate your define-minor-mode it defines a variable borked-mode-map with the key bindings you specified. Once that symbol has been defined, however, re-evaluating your define-minor-mode won't change it.

You could delete the various borked-mode-xxx symbols using unintern and then re-evaluate your code. Try:

(unintern 'borked-mode-map)

You might be interested in expanding the define-minor-mode macro to see what it is actually doing. Put point at the end and call M-x pp-macroexpand-last-sexp. This will open a new buffer showing the expanded macro. There you'll see the defvar calls used to set up your mode variables. If you read the help for defvar you'll see that the initial value is only used if the symbol being defined is void -- once it exists, subsequent defvar calls won't change its value.

15

I think the best answer I can give you is to stay away from the "inline keybindings feature" of define-minor-mode. Use

(defvar borked-mode-map
  (let ((map (make-sparse-keymap)))
    (define-key map [b] 'previous-line)
    ...
    map))

(define-minor-mode borked-mode
  "A mode defined with a broken key binding"
  :global nil
  ...)

Instead. Then you can use C-M-x to re-evaluate those definitions.

8
  • 1
    defvar won't evaluate it's value again after C-M-x :) You'd need defparameter or a separate setf.
    – wvxvw
    Aug 27, 2015 at 18:03
  • As @wvxvw points out, this does not actually address the issue. Your example code is doing the same that the define-minor-mode macro does: calling defvar to define the map. Evaluating that defvar a second time has no effect.
    – glucas
    Aug 27, 2015 at 19:51
  • 7
    @wvxvw If you use C-M-x (eval-defun) to reevaluate the defvar then the variable is updated. This is a special case in eval-defun; if you call eval-buffer or eval-region then the existing value isn't changed. Aug 27, 2015 at 22:45
  • Ah -- that seems like the key point.
    – glucas
    Aug 28, 2015 at 3:00
  • 1
    Note that C-M-x on the defvar form only updates the mode-map variable itself. You have to also C-M-x on the define-minor-mode form to get the changed map 'installed' into the minor mode. I tried to figure out why by expanding the macros, but it's beyond me.
    – Tyler
    Sep 16, 2015 at 16:03
0

You can define your mode map like this:

(defvar dnd-mode-map
  (let ((map (make-sparse-keymap)))
    (prog1 map
      (define-key map "q" 'dnd-quit)
      (when (eq this-command 'eval-defun)
        (let ((mmap (assq 'dnd-mode minor-mode-map-alist)))
          (when mmap
            (setcdr mmap map)))))))

Now you are able to reevaluate the map definition using eval-defun and it will automatically update the stored map inside minor-mode-map-alist.

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.