d11wtq
d11wtq

Reputation: 35318

First elisp attempt - minor mode for tab key not being invoked on tab?

I've decided to get my toes wet with a bit of lisp, since I want to make emacs behave a little better when I hit TAB. My command works fine. It just performs indent-for-tab-command and if nothing happens, it performs tab-to-tab-stop, on the assumption that it's unlikely I was hitting TAB just to have the point refuse to budge when I'm inside a multi-line string or some such. After the first TAB press, it continues to do tab-to-tab-stop until either editing resumes, or the point is moved elsewhere. AFAIK, my logic is ok, though my lisp code probably isn't!

Originally I just hacked this into my emacs dot files by doing (local-set-key (kbd "TAB") 'tab-dwim) for major modes where I wanted this behaviour. That worked as expected.

Then I decided that what I was doing was basically a minor mode, so I tried to move the key-binding into a minor-mode. For some reason, even though the minor mode is enabled (as indicated in the mode line, and just from toggling it on and off), my tab-dwim function isn't being invoked when I hit the TAB key. I can still invoke it with M-x as expected.

What am I doing wrong with my minor mode's :keymap?

;;;
;; TAB DWIM

; buffer-local before/after point tracking
(setq point-before-tab nil)
(setq point-after-tab nil)
(make-local-variable 'point-before-tab)
(make-local-variable 'point-after-tab)

(defun tab-dwim ()
  "Indents normally once, then switches to tab-to-tab-stop if invoked again.

tab-dwim will always perform tab-to-tab-stop if the first TAB press does not
cause the point to move."

  (interactive)

  (print "in tab-dwim now") ; THIS LINE IS NEVER INVOKED ON TAB?

  (setq point-before-tab (point))

  (if (eq point-before-tab point-after-tab) ; pressed TAB again
      (tab-to-tab-stop)
    (indent-for-tab-command))

  (if (eq (point) point-before-tab) ; point didn't move
      (tab-to-tab-stop))
  (setq point-after-tab (point)))

(define-minor-mode tab-dwim-mode
  "Toggle tab-dwim-mode.
With a non-nil argument, turns on tab-dwim-mode. With a nil argument, turns it
off.

When tab-dwim-mode is enabled, pressing the TAB key once will behave as normal,
but pressing it subsequent times, will continue to indent, using
tab-to-tab-stop.

If tab-dwim determines that the first TAB key press resulted in no movement of
the point, it will indent according to tab-to-tab-stop instead."

  :init-value nil
  :lighter " DWIM"
  :keymap
  '(([TAB] . tab-dwim)))

(provide 'tab-dwim)

Cheers,

Chris

Upvotes: 2

Views: 174

Answers (2)

event_jr
event_jr

Reputation: 17707

Yes, use "\t" or the vector format "[(tab)]".

Some additional notes for your elisp development:

  1. Avoid making global variables as much as possible. In this case, I think dynamically binding with let is appropriate. Also have a look at let*.
  2. Understand the difference between make-local-variable and make-variable-buffer-local. The way you've written your code, the buffer-local variable would only exist in the buffer that loads your package.
  3. As Nemo mentioned, it's extemely extremely extremely recommended that you use a common prefix for all variables/functions related to each package. Emacs has only one namespace, this is the "hacky" way to keep it somewhat organized.

Upvotes: 2

Nemo
Nemo

Reputation: 71555

I think you are very close.

Try this for your keymap:

'(("\t" . tab-dwim)))

Upvotes: 2

Related Questions