2

I wonder what is wrong with the following code?

(defvar my-forward-word-map
  (let ((map (make-sparse-keymap)))
    (define-key map (kbd "n") 'forward-word)
    (define-key map (kbd "p") 'my-backward-word)
    map))

(defvar my-backward-word-map
  (let ((map (make-sparse-keymap)))
    (define-key map (kbd "p") 'backward-word)
    (define-key map (kbd "n") 'my-forward-word)
    map))

(defun my-forward-word ()
  (interactive)
  (set-transient-map my-forward-word-map t))

(defun my-backward-word ()
  (interactive)
  (set-transient-map my-backward-word-map t))

(global-set-key (kbd "M-n") 'my-forward-word)
(global-set-key (kbd "M-p") 'my-backward-word)

There is a buffer with the text: |Hello world. | - this is cursor.

I press M-n, n. I get Hello| world as I expected.

Then I press p, p. I get |Hello world as was previously. And that's fine.

But when I press n again nothing happens i.e. cursor doesn't change its position no matter how many time I press n.

After n and n I expected to get Hello| world.

What's wrong?

$ emacs --version
GNU Emacs 24.4.1
Malabarba
  • 23,148
  • 6
  • 79
  • 164
Andrii Tykhonov
  • 452
  • 2
  • 13

1 Answers1

1

It looks like a bug, to me. Adding some debug message calls, I see that the first n (after p p), correctly calls my-forward-word, but the second n then is interpreted in my-backward-word-map, not in my-forward-word-map.

IOW, the call in my-forward-word of (set-transient-map my-forward-word-map t) seems to have no effect.

(And you get the same behavior if you bind M-n instead of n and M-p instead of p, in the transient maps; IOW, if you try to use only M-n and M-p.)

Unless someone points out something we're missing, you might consider filing a bug report: M-x report-emacs-bug.


Yes, I'd say that it is definitely a bug. I just tried it in Emacs 24.3, for which the function was named set-temporary-overlay-map instead of set-transient-map, and there it works. So this is apparently a regression, introduced in Emacs 24.4.

Please file a bug report.

Drew
  • 77,472
  • 10
  • 114
  • 243
  • I don't think it's a bug. The second arg to set-transient-map (which is t here) means to keep the map active as long as the user hits keys in the map, so we end up activing both maps and then activating them commulatively, which is not going to work right. – Stefan Mar 29 '15 at 04:30
  • @Stefan If this is not explained anywhere, then it's at least a documentation bug. – Malabarba Mar 29 '15 at 10:41
  • It is explained in the docstring, AFAIK. – Stefan Mar 29 '15 at 14:23
  • @Stefan. I see. t meaning keep is of course documented. But activating the map (hence both maps apparently, in this case) seems quite a different verb from setting the transient map. Apparently now that phrase means activating *a* transient map? So there can be more than one "transient*" map? And in that case this seems like an incompatible change, which presumably should have been documented in the NEWS for Emacs 24.4. Emacs 24.3 does not behave this way, in any case. Could you perhaps give a use case for multiple active transient maps? – Drew Mar 29 '15 at 14:28
  • Typical case for having multiple transient maps: the above example with M-n n C-u 5 n. The C-u now uses set-transient-map. – Stefan Mar 29 '15 at 14:34
  • Not sure what you mean by the C-u now uses set-transient-map, but OK. In that case, I suggest that you document the fact that this is not about setting the transient map but is about setting a transient map. IOW, document explicitly that there can be multiple (any number of?) transient maps, and describe how they interact (e.g. order). And it wouldn't hurt to provide such a use case in the doc, to motivate this feature. It's too bad that this wasn't done when this was introduced (and wasn't called out in NEWS). – Drew Mar 29 '15 at 14:47
  • @Drew, I sent a bug report. @Drew, @Stefan how I could temporarily workaround that issue? Any ideas? Is there any way to unset transient map? I've tried to call keyboard-escape-quit before setting transient map but that doesn't help. – Andrii Tykhonov Mar 29 '15 at 20:26
  • I don't know why you need 2 maps. AFAICT, you just need a single map, with n and p bound to backward-word and forward-word and that's it. – Stefan Mar 29 '15 at 20:29
  • @Stefan for sorry I really need two maps (even more but it doesn't matter) – Andrii Tykhonov Mar 29 '15 at 21:48
  • Then have each cammd set a map that only stays for one command (i.e. keep=nil) and that command sets the next map. – Stefan Mar 29 '15 at 23:52
  • @Stefan yes, thanks for that, this is the only workaround which I found, the only reason why I dislike it is that I need in that case to create too many commands (I have many commands) which wrapset-transient-map. – Andrii Tykhonov Mar 30 '15 at 09:04
  • Maybe you should re-think why need so many different maps. Usually, it's easier for the user's head if you use fewer maps. – Stefan Mar 30 '15 at 14:11
  • Well, may be... Regarding the users I totally agree, but the idea is based on the several (six) maps however an user can use only one. I'm as a user is going to use six. I'll show you when it will be ready. For now I have no any idea how it could be improved... Well, let's see afterwards! Firsts of all I need to implement it – Andrii Tykhonov Mar 30 '15 at 21:30