A cursor goto hydra for emacs

| categories: emacs, hydra | tags:

In the spirit of upping my navigation game, here we examine navigation by search like methods. You probably know about C-s with will search forward for a word, and C-r which will search backwards. This will get you to the start of a word pretty easily. It won't get you into a word though, you have to navigate to that, and it isn't too handy to get to a line, or window, or headline in an org-file. Each of these is an emacs command, which as with navigation I don't always remember. Today, we build a hydra to make this easy too.

We will use features from avy , and helm , and some standard emacs features. avy is pretty neat. It provides an interface to jump to characters, words and subwords by pressing keys. To jump to a character that is visible on the screen, you invoke avy-goto-char and press the character you want to jump to. avy will overlay a sequence of keys you then type to jump to that character. It might be more convenient to jump to a pair of characters, which you do by invoking avy-goto-char-2. Similarly, there are commands to jump to the beginning of a word, and a subword, both with variations that allow you to specify the beginning letter of the word, or to get overlays on every word.

I spend most of my days in org-files, so I frequently want to jump to an org headline in the current buffer, or some headline in an org-file in my agenda. Helm provides a nice set of functions for this in helm-org-headlines and helm-org-agenda-files-headings. We can also use helm-multi-swoop-org to use the swoop search function in all open org-buffers with helm selection. Within a buffer, you might also use the search forward and backward capabilities, or the more advanced helm-occur or swiper-helm features. Finally, I may want my cursor to go to another recent file, or open buffer.

The hydra we will develop here puts all of these commands a few keystrokes away, with a hint system to remind you what is possible. In addition to these "goto" commands, I add a character to switch to the navigation hydra we developed in the last post so I can switch to navigation if I change my mind. I also put two commands to store the current position before the goto command, and to return to that stored position conveniently. I bind this hydra to super-g, because the super key isn't used much on my Mac, and g reminds of "goto". So, here is my hydra code:

(defhydra goto (:color blue :hint nil)
  "
Goto:
^Char^              ^Word^                ^org^                    ^search^
^^^^^^^^---------------------------------------------------------------------------
_c_: 2 chars        _w_: word by char     _h_: headline in buffer  _o_: helm-occur
_C_: char           _W_: some word        _a_: heading in agenda   _p_: helm-swiper
_L_: char in line   _s_: subword by char  _q_: swoop org buffers   _f_: search forward
^  ^                _S_: some subword     ^ ^                      _b_: search backward
-----------------------------------------------------------------------------------
_B_: helm-buffers       _l_: avy-goto-line
_m_: helm-mini          _i_: ace-window
_R_: helm-recentf

_n_: Navigate           _._: mark position _/_: jump to mark
"
  ("c" avy-goto-char-2)
  ("C" avy-goto-char)
  ("L" avy-goto-char-in-line)
  ("w" avy-goto-word-1)
  ;; jump to beginning of some word
  ("W" avy-goto-word-0)
  ;; jump to subword starting with a char
  ("s" avy-goto-subword-1)
  ;; jump to some subword
  ("S" avy-goto-subword-0)

  ("l" avy-goto-line)
  ("i" ace-window)

  ("h" helm-org-headlines)
  ("a" helm-org-agenda-files-headings)
  ("q" helm-multi-swoop-org)

  ("o" helm-occur)
  ("p" swiper-helm)

  ("f" isearch-forward)
  ("b" isearch-backward)

  ("." org-mark-ring-push :color red)
  ("/" org-mark-ring-goto :color blue)
  ("B" helm-buffers-list)
  ("m" helm-mini)
  ("R" helm-recentf)
  ("n" hydra-navigate/body))

(global-set-key (kbd "s-g") 'goto/body)

As with the last navigation hydra, this is a pretty massive set of options and takes up some decent screen space at the bottom om my emacs. They are mostly here to remind me that there are better navigation options, and with practice I suspect muscle memory will provide fast navigation tools with more precision and fewer keystrokes than simple navigation.

Copyright (C) 2015 by John Kitchin. See the License for information about copying.

org-mode source

Org-mode version = 8.2.10

Discuss on Twitter

Upping my Emacs navigation game

| categories: emacs, hydra | tags:

I have been trying to up my navigation game in Emacs, by which I mean I want to get my cursor where I want it with a minimal number of keystrokes, and preferrably no mouse actions. There are lots of little and big navigations I do a lot:

  1. forward/backward by a character
  2. forward/backward by a word/subword
  3. forward/backward by a sentence
  4. forward/backward by a line
  5. to the beginning and end of a line
  6. to the beginning and end of a sentence
  7. to the beginning and end of a paragraph
  8. to the beginning and end of a page
  9. to the beginning and end of a buffer
  10. scrolling up/down
  11. into another window
  12. back and forth to buffers

Occasionally, I want to save a location so I can easily get back to it. Not all of these are strictly speaking navigation in the usual sense, but they are things I do often enough. There are Emacs commands for all these, and keyboard shortcuts for many of them, but I don't use them often, and as a result I don't remember them either.

Here I develop a hydra that will provide these features. Hydra is a super amazing, menu prompting system that provides hints to remind you of what can be done, and to access it from a character. It is possible to pass numeric and universal arguments to the commands by typing -, a number, or C-u before pressing the character.

I want some commands to be repeatable, which we get with a "red" hydra, and some commands to exit on running, which we get with a "blue" head. So, here is an over-the-top hydra for navigation.

(defhydra hydra-navigate (:color red
                          :hint nil)
  "
_f_: forward-char       _w_: forward-word       _n_: next-line
_b_: backward-char      _W_: backward-word      _p_: previous-line
^ ^                     _o_: subword-right      _,_: beginning-of-line
^ ^                     _O_: subword-left       _._: end-of-line

_s_: forward sentence   _a_: forward paragraph  _g_: forward page
_S_: backward sentence  _A_: backward paragraph _G_: backward page

_h_: helm mini _B_: buffer list _i_: window
_<left>_: previous buffer   _<right>_: next buffer
_<up>_: scroll-up           _<down>_: scroll-down

_[_: backward-sexp _]_: forward-sexp
_<_ beginning of buffer _>_ end of buffer _m_: set mark _/_: jump to mark
"
  ("f" forward-char)
  ("b" backward-char)
  ("w" forward-word)
  ("W" backward-word)
  ("n" next-line)
  ("p" previous-line)
  ("o" subword-right)
  ("O" subword-left)
  ("s" forward-sentence)
  ("S" backward-sentence)
  ("a" forward-paragraph)
  ("A" backward-paragraph)
  ("g" forward-page)
  ("G" backward-page)
  ("<right>" next-buffer)
  ("<left>" previous-buffer)
  ("h" helm-mini :color blue)
  ("i" ace-window :color blue)
  ("m" org-mark-ring-push)
  ("/" org-mark-ring-goto :color blue)
  ("B" helm-buffers-list)
  ("<up>" scroll-up)
  ("<down>" scroll-down)
  ("<" beginning-of-buffer)
  (">" end-of-buffer)
  ("." end-of-line)
  ("[" backward-sexp)
  ("]" forward-sexp)
  ("," beginning-of-line)
  ("q" nil "quit" :color blue))

(global-set-key (kbd "s-n") 'hydra-navigate/body)
hydra-navigate/body

I basically like it. The menu is a little on the large side, but it makes for easy modal navigation in a buffer, to other windows, and other buffers. On the whole for moderate cursor movements, this results in basically equal keystrokes. For example, to move 3 characters forward, we have C-f C-f C-f or C-u 3 C-f, or s-n 3 f. The advantage (I think) is a single interface to all these navigation commands with hints on what to do.

There is still another level of navigation, which is related to navigation by searching. That is a whole different level of navigation I will work on another day!

Copyright (C) 2015 by John Kitchin. See the License for information about copying.

org-mode source

Org-mode version = 8.2.10

Discuss on Twitter

Conditional hydra menus

| categories: emacs, hydra | tags:

Usually the hydra menu is hard coded in the defhydra macro. Sometimes, you would like conditional options, that is, depending on some condition we get different options when we run the hydra and not when it was defined. This is an open issue in hydra. Here we explore a way to achieve that. The idea is to construct the code for the hydra, then eval it, and run the hydra. In this example we make the conditional menu depend on whether we are on an even or odd numbered line. I use the `' syntax for defining the list of code. ` is a variation of ' (quote) that enables you to use the , operator to evaluate that element while in data mode. So, here is our first idea:

(defun my-hydra-1 ()
  (interactive)
  (eval
   `(defhydra my-hydra-1 (:color blue) "My hydra"
      ,(if (evenp (line-number-at-pos))
           '("e" (message-box "Even line") "Even")
         '("o" (message-box "Odd line") "Odd"))
      ,(when t '("a" (message-box "always true") "always"))
      ;; This does not work. you must return a legitimate hydra menu item
      ;;      ,(when nil '("n" (message-box "never") "never"))
      ))
  (my-hydra-1/body))

(my-hydra-1)
(my-hydra-1)
my-hydra

As long as it is not expensive to compute the conditionals, this seems like an easy enough way to get conditional options in a hydra. One limitation of the previous approach is our menu conditionals must return a hydra menu, and not nil. Here is an alternative approach to writing the function that solves the issue of the nil return in the last function. Here we build up the code list using append. It might seem like a macro should be used here, but I have not figured out how to get the macro to run the conditionals at the run-time. Note, we cannot use funcall on the defhydra because that is a macro.

(defun my-hydra-2 ()
  (interactive)
  (let ((conditionals '((if (evenp (line-number-at-pos))
                            '("e" (message-box "Even second") "Even")
                          '("o" (message-box "Odd second") "Odd"))
                        (when t '("a" (message-box "always true") "always"))
                        (when nil '("n" (message-box "never") "never")))))
    (eval
     (append
      '(defhydra my-hydra-2 (:color blue) "My hydra")
      (loop for cond in conditionals
            with result = (eval cond)
            if (eval cond)
            collect (eval cond))))
    (my-hydra-2/body)))

(my-hydra-2)
(my-hydra-2)

That works too. Let us try another type of syntax where the conditional statements have a cons cell with a conditional statement, and a hydra menu option for when the statement is true. This is functionally similar to our first method, but has some advantages in brevity and less quoting. We add a conditional hint here too (at some expense of additional quoting).

(defun my-hydra-3 ()
  (interactive)
  (let ((conditionals
         `(((evenp (line-number-at-pos)) . ("e" (message-box "Even second") ,(format "Even: %s" (line-number-at-pos))))
           ((oddp (line-number-at-pos)) . ("o" (message-box "Odd second") ,(format "Odd: %s" (line-number-at-pos))))
           (t . ("a" (message-box "always true") "always"))
           (nil . ("n" (message-box "never") "never")))))
    (eval
     (append
      '(defhydra my-hydra-3 (:color blue) "My hydra")
      (loop for cond in conditionals
            if (eval (car  cond))
            collect (cdr cond))))
    (my-hydra-3/body)))

(my-hydra-3)
(my-hydra-3)

I cannot figure out how to abstract this much further. There is a little redundancy in names, e.g. in the defhydra and at the end, but it is not too bad, which would usually be handled by a macro. I tried some defmacros to try this, but I could not figure out how to get the conditionals to expand at the right times, which is at run time, and not at macro expansion time. I need a macro that generates a function that has the call to defhydra in it! Maybe next year ;)

Copyright (C) 2015 by John Kitchin. See the License for information about copying.

org-mode source

Org-mode version = 8.2.10

Discuss on Twitter

org-mode links meet hydra

| categories: emacs, hydra, orgmode | tags:

I have played with a lot of options to give org-mode links extra functionality. Here are a few of the ideas I have looked at so far.

  1. Enabling right clicks on links
  2. A home made minibuffer menu in org-ref
  3. A helm buffer in org-ref

Here, I want to explore a hydra menu for a link. The idea is pretty simple, we need functions that do something with the link at point, and a hydra interface to call them. This turned out to be a little tricky. I could not get the path from the link in the link lambda function, and we need a way to pass the path to the function. I use a global variable for that. I wish there was another way to do that, but this does actually work. We illustrate it here with a more functional doi link.

(defun doi-crossref ()
  "Search DOI in CrossRef."
  (interactive)
  (browse-url
   (format
    "http://search.crossref.org/?q=%s" *doi-hydra-path*)))

(defun doi-google-scholar ()
  "Google scholar the doi."
  (interactive)
  (browse-url
   (format
    "http://scholar.google.com/scholar?q=%s" *doi-hydra-path*)))

(defun doi-pubmed ()
  "Pubmed the doi."
  (interactive)
  (browse-url
   (format
    "http://www.ncbi.nlm.nih.gov/pubmed/?term=%s"
    (url-hexify-string *doi-hydra-path*))))

 (defhydra doi-hydra ()
   "org-ref"
   ("c" doi-crossref "Crossref")
   ("g" doi-google-scholar "Google Scholar")
   ("p" doi-pubmed "Pubmed"))

(org-add-link-type "doi"
  (lambda (path) (setq *doi-hydra-path* path) (doi-hydra/body)))
lambda (path) (setq doi-hydra-path path) (doi-hydra/body)

Now for a test, 10.1021/jp047349j.

It works fine, when you click on a link, you get a minibuffer menu with context hints, and pressing any other key than is defined simply cancels the command.

Copyright (C) 2015 by John Kitchin. See the License for information about copying.

org-mode source

Org-mode version = 8.2.10

Discuss on Twitter