Wednesday, December 12, 2012

Customization with keybindings

Much has already been written about keybindings. I'd like to demonstrate creating two new function to do something, and binding one to a global key, and one to a mode-specific key.

I've been wanting to create a function for a while that easily lets me store and paste links to an org-mode file. Org already supports this, so this should be quite easy.

I'd like to bind a key to store a link to the present point. The function org-store-link does this, so all we need is a keybinding for this, since one doesn't exist by default. We do this with the function global-set-key. But we have to choose a key sequence first. If you want a great guide on keybinding, the awesome blog Mastering Emacs has a wonderful writeup. So, as the Mastering Emacs blog notes, all keys starting with C-c and function keys to F5 onward are available for user binding. For some reason I don't understand, org-mode takes many of these bindings for itself. So, we should check what's already bround, so I need to take a look at all currently keybindings C-h b. Looking at my keybindings, I see I don't have any bindings for C-c g, so let's use that. I'll add the following in my init.el:

(define-key global-map (kbd "C-c g") 'org-store-link)

The next part is to write a small function to add the link in an org file. There's a function already org-insert-link that has our basic functionality, but by default it prompts the user twice, which I think is unnecessary. Fortunately, the method takes a LINK-LOCATION and DEFAULT-DESCRIPTION as optional arguments, so we shouldn't have to do much work. Here's what the method looks like:

(defun ash/org-link-description (link)
  "Makes a useful description from a link."
  (cond ((string-match "^file:" link)
         (file-name-nondirectory link))
        (t nil)))

(defun ash/org-paste-link ()
  "Paste all stored links without prompting."
  (interactive)
  (unwind-protect
      (flet ((read-string (prompt &optional initial-input history default-value
                                  inherit-input-method)
                          initial-input))
          (dolist (link (delete-duplicates org-stored-links :test 'equal))
            (org-insert-link nil (car link) (ash/org-link-description (car link)))))
    (setq org-stored-links nil)))

(define-key org-mode-map (kbd "C-c p") 'ash/org-paste-link)

The definition of ash/org-paste-link has a few interesting features, but what I really want to talk about is keybindings, so I won't explain why I had to do things the way I did. I may get into writing new commands in a later post.

The interesting part for keybindings here is that I bound it to org-mode-map. That means this binding will only be in effect in org-mode buffers. I found that org-mode-map is the one to use by just running M-x apropos on the regex org.*map. In general, any mode will have a map, and the pattern is usually -mode-map, where can be whatever mode you interested in adding to.

I'll add this code to my large eval-after-load form for org-mode, so that it will only evaluated once I start using org-mode. This might be a bad idea if I want to grab a link before I visit an org-mode file for the first time, since my keybinding will not be loaded at that point. I'll have to see if that is something I'm liable to do, and if so, I'll move that keybinding out.

The lessons here are:
  1. Use keybindings for common tasks.
  2. Use global keybindings for things you might want to do in any buffer.
  3. For things you only want to do depending on the mode, use the mode specific keybinding, which can be found at the package map with suffix -mode-map.
  4. Use the user-space keybindings, those starting with C-c, and the function keys F5 on up.

No comments: