2
votes

I am a VIM guy - but evil-mode allowed me to productively use Emacs for my Scala development, and it did it so well that I am sort of hooked... I want to customize the environment to my liking, so I e.g. added these to my .emacs:

(define-key evil-normal-state-map (kbd "<f7>") 'ensime-typecheck-all)
(define-key evil-normal-state-map (kbd "C-]") 'ensime-edit-definition)

While in evil-normal-mode, the first line allows me to do a typecheck of my Scala code by just hitting F7, while the second one navigates me to a type/val definition via the muscle-memoried Ctrl-] (used for tags-based navigation in VIM).

My only problem is that what I've done so far is a "global" assignment - if I open a Python file, I'd want F7 to do a different thing (pyflakes or pylint). How can I assign different actions to the same key based on what kind of file is currently open in the buffer I am viewing?

In case it helps, in my .vimrc, I've done this via sections like these:

au BufNewFile,BufRead *.ml call SetupOCamlEnviron()
function! SetupOCamlEnviron()
    se shiftwidth=2
    "
    " Remap F7 to make if the file is an .ml one
    "
    noremap <buffer> <special> <F7> :make<CR>
    noremap! <buffer> <special> <F7> <ESC>:make<CR>

    "
    " Thanks to Merlin
    "
    noremap <buffer> <silent> <F6> :SyntasticCheck<CR>
    noremap! <buffer> <silent> <F6> <ESC>:SyntasticCheck<CR>
    inoremap <buffer> <C-Space> <C-x><C-o>
    noremap <buffer> <C-]> :Locate<CR>
    inoremap <buffer> <C-]> <ESC>:Locate<CR>
endfunction

How do I do this kind of thing in Emacs/evil?

EDIT: In search of a way to do this, I realized I can dispatch on different code when F7 is pressed in normal mode - so I wrote this Emacs lisp:

(defun current-buffer-extension ()
  (if (stringp (buffer-file-name))
    (file-name-extension buffer-file-name) 
    "Unknown"))

(defun handle-f7 ()
  (interactive)
  (if (string= (current-buffer-extension) "scala")
    (ensime-typecheck-all)))

(define-key evil-normal-state-map (kbd "<f7>") 'handle-f7)

...which can be extended with other tool invocations as I learn more about Emacs, by adding actions based on the file extension.

Being completely new to Emacs, I am open to suggestions...

Am I on the right track here? Is there a better way to do what I want? Have I violated some principle by hooking evil-normal-mode keystrokes?

2

2 Answers

1
votes

The thing you are looking for is called hook. There are plenty of hooks that emacs runs is specifics moments, and are fully customizable by user.

For instance, whenever entering a mode, a "mode-hook" is run. So you could do:

(add-hook 'python-mode-hook (lambda ()
                        (setq fill-column 79)
                        (local-set-key [(f3)] 'run-flake8)))

Hooks are the canonical way of customizing a lot of things in emacs, and there is plenty of examples around about those. In the example above, I define the key F3 to do something whenever I am in python mode.

Note that the example does not cope with evil keymap, but uses a local key that should have preference. If you want to assign a key to do different things depending on the evil state, you should either add some additional logic or make the evil-X-state-map buffer local and then change it the way you want (you could do that in the hook)

Edit: Regarding your question about if you have "violated some principle"... lets say that because of the hooks and the ability to set local variables, checking file extensions to customize behaviour is never (afaik) done other than for setting the mode (and that is done internally). Even checking the major-mode (that would be preferred to checking extension) is not done, as it is less scalable than just hooking preferences and functionality into modes.

1
votes

Evil has a useful facility for mode-specific keybindings: evil-define-key. As arguments, it takes:

  • the state (as a quoted symbol, in your case, 'normal),
  • an unquoted keymap (I don't use ensime, but it's probably something like ensime-mode-map),
  • the key sequence, and
  • the command to bind.

So, to make your bindings ensime-specific (again, fiddle with the specific map name to make sure it's correct):

(evil-define-key 'normal ensime-mode-map (kbd "<f7>") #'ensime-typecheck-all)
(evil-define-key 'normal ensime-mode-map (kbd "C-]") #'ensime-edit-definition)

You can also bind f7 to something else for use with python (again, tinker with the map names and commands accordingly):

(evil-define-key 'normal python-mode-map (kbd "<f7>") #'run-flake8)

(Aside: I'm using the sharp-quote (#', which is lisp-shorthand for "function") in front of command names rather than the plain quote ('). For your purposes the two are basically equivalent, but it's good to get in the habit of the former in case you start byte-compiling your setup files, as the byte-compiler will warn you if the function in question is undefined when you sharp-quote but not if you simple-quote. See a related discussion on the Emacs Stack Exchange site.)