4
votes

Q: in Emacs, how does one make mode-specific key bindings for text objects in evil?

One may bind a key in a specific state (normal, insert, etc.) in a specific mode as the following example demonstrates:

(evil-define-key 'normal org-mode "a" 'some-command)

However, it's not clear to me how to bind a key in a mode-specific way to evil-outer-text-objects-map (or its -inner- counterpart). As an alternative, it's also not clear how one might bind keys in these maps in a buffer-local way through a mode hook.

It doesn't look like evil-local-set-key will do it, because it expects a state (normal, insert, etc.) as its first argument, and that's not relevant to this task.

It's also not clear how to use local-set-key in this instance, because it expects a key and a command as arguments, but does not take a map as an argument.

2

2 Answers

2
votes

This is kinda late but for the first part of your question you can use local maps like:

(defun my-elisp-mode-configuration ()
  (with-eval-after-load 'evil
    (define-key evil-visual-state-local-map "ie" 'sp-evil-i-sexp)
    (define-key evil-operator-state-local-map "ie" 'sp-evil-i-sexp)))

(add-hook 'emacs-lisp-mode-hook #'my-elisp-mode-configuration)

In this example I bind 'inner' e operator to a custom sp-evil-i-sexp text object for elisp mode only.

Coming to your second question; evil overrides local maps so using local-set-key won't suffice. Instead you can use:

  • local evil maps with hooks; like evil-normal-state-local-map as in the previous example
  • use evil-define-key; ie: (evil-define-key 'normal emacs-lisp-mode-map (kbd " ") 'my-leader) Note that you can not override Evil's bindings this way but those unemployed or employed at a global or local level. Use the first method in case you want to override Evil bindings.
  • And there is evil-make-overriding-map which causes local-map to override Evil's bindings but this is rarely what you want because you want hjkl to work at least but useful for modes like dired which evil makes less sense.

Footnote: There is nothing special about Evil's operators or text objects from Emacs's perspective. They're just bound keymaps. Eg: i key is bound to the evil-inner-text-objects-map which includes text objects like w as in:

(define-key evil-visual-state-map "i" evil-inner-text-objects-map)
(define-key evil-inner-text-objects-map "w" 'evil-inner-word)

You can find these lines in evil-maps.el

2
votes

When reading the mailing list someone mentioned it is better to put keybindings in eval-after-load instead of hooks, so here it is:

(eval-after-load "<mode>"
  '(progn
     <object-definition>))

As for defining new text objects, I must recommend this function from @gordon-gustafson:

(defmacro define-and-bind-text-object (key start-regex end-regex)
  (let ((inner-name (make-symbol "inner-name"))
        (outer-name (make-symbol "outer-name")))
    `(progn
      (evil-define-text-object ,inner-name (count &optional beg end type)
        (evil-select-paren ,start-regex ,end-regex beg end type count t))
      (evil-define-text-object ,outer-name (count &optional beg end type)
        (evil-select-paren ,start-regex ,end-regex beg end type count nil))
      (define-key evil-inner-text-objects-map ,key (quote ,inner-name))
      (define-key evil-outer-text-objects-map ,key (quote ,outer-name)))))

So the <object-definitions> part would become:

(define-and-bind-text-object "<key>" "<start-regex>" "<end-regex>")