Slime (and elisp-slime-nav) uses M-. to navigate to the symbol at point, and M-, to pop back to previous marks. I like this.

Evil uses . to repeat the previous editing command, C-. to replace a just repeated command with a previously executed command, and M-. to do the same in the reverse direction (useful if you overshoot with C-.). I like this as well.

I'd like to use M-. for slime-style navigation except when the previous command was C-. (evil-repeat-pop), in which case I'd like M-. to run evil-repeat-pop-next. I'm not sure how to go about implementing this.

I'm considering removing the evil M-. binding and defining a hydra that's entered upon running evil-repeat-pop (C-.) and which binds M-. to evil-repeat-pop-next in a transient keymap.

I also considered binding M-. to a wrapper function like,

(if (eql last-command 'evil-repeat-pop)

but I want the "else" clause to do whatever M-. would have done if evil wasn't enabled. This might be elisp-slime-nav-find-elisp-thing-at-point, xref-find-definitions, ggtags-find-tag-dwim, etc. depending on what modes and code-tagging approaches I'm using.

  • The Hydra way (or the equivalent with a transient keymap) sounds like the better option. What is the question, exactly? – Stefan Mar 1 '20 at 21:14

I also considered binding M-. to a wrapper function [...] but I want the "else" clause to do whatever M-. would have done if evil wasn't enabled.

You could bind it to a conditional binding, the manual explains it for menu items, but it works for key bindings as well, (elisp) Extended Menu Items:

the extended format binding looks like this:



:filter FILTER-FN

 This property provides a way to compute the menu item dynamically.
 The property value FILTER-FN should be a function of one argument;
 when it is called, its argument will be REAL-BINDING.  The
 function should return the binding to use instead.

 Emacs can call this function at any time that it does redisplay or
 operates on menu data structures, so you should write it so it can
 safely be called at any time.

(see also https://stackoverflow.com/a/36684318/319698)

Something like this: (not sure if you would need bind in a different keymap than global-map since you're using evil)

(define-key global-map (kbd "M-.")
  `(menu-item "" evil-repeat-pop :filter
              ,(lambda (cmd) (if (eq last-command 'evil-repeat-pop) cmd))))

The transient keymap approach might make more sense for your case though.


For what it may be worth, the solution suggested above did not work in its original form but it works with evil-normal-state-map:

(define-key evil-normal-state-map (kbd "M-.")
  `(menu-item "" evil-repeat-pop :filter
              ,(lambda (cmd) (if (eq last-command 'evil-repeat-pop) cmd))))

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service, privacy policy and cookie policy

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