(or emacs irrelevant)

Context aware hydra

In this post, I'll demonstrate a snippet that came up this week in a conversation over John Kitchin's post. While the following snippet isn't inherently useful, it might help a creative user to understand Hydra a bit more and accomplish more advanced things.

This hydra demonstrates that the hint and even the key bindings aren't set in stone: you can modify them depending on the current state of your Emacs, in this case the current line number.

(defhydra hydra-vi (:hint nil)
  "vi"
  ("j" next-line)
  ("k" previous-line)
  ("n" next-line)
  ("p" previous-line))

(setq hydra-vi/hint
  '(if (evenp (line-number-at-pos))
    (prog1 (eval
            (hydra--format nil '(nil nil :hint nil)
                           "\neven: _j_ _k_\n" hydra-vi/heads))
      (define-key hydra-vi/keymap "n" nil)
      (define-key hydra-vi/keymap "p" nil)
      (define-key hydra-vi/keymap "j" 'hydra-vi/next-line)
      (define-key hydra-vi/keymap "k" 'hydra-vi/previous-line))
    (prog1 (eval
            (hydra--format nil '(nil nil :hint nil)
                           "\nodd: _n_ _p_\n" hydra-vi/heads))
      (define-key hydra-vi/keymap "j" nil)
      (define-key hydra-vi/keymap "k" nil)
      (define-key hydra-vi/keymap "n" 'hydra-vi/next-line)
      (define-key hydra-vi/keymap "p" 'hydra-vi/previous-line))))

The first statement is one of the most elementary defhydra calls. The only extra thing is that it sets the :hint to nil.

The defhydra statement generates a bunch of function and variable definitions. You can examine them closely by evaluating:

(macroexpand
 '(defhydra hydra-vi (:hint nil)
   "vi"
   ("j" next-line)
   ("k" previous-line)
   ("n" next-line)
   ("p" previous-line)))

Just paste that code into *scratch*, and press C-j. If you want pretty output (153 lines of code instead of 26), turn on lispy-mode and press E instead of C-j.

Anyway, among these defined variables is hydra-vi/hint which is evaluated each time to display the hint. So now we can just redefine hydra-vi/hint to make it so that on even lines n calls next-line, while on odd lines it's j, with the appropriate changes in the doc. The change in bindings, modifying hydra-vi/keymap - also one of the defined variables, needs to be a side-effect, since hydra-vi/hint is expected to evaluate to a string.

Just to give you some idea of how it could be used: you can have a context-aware "open" command that, for instance, delegates to open-in-pdf-tools or open-in-firefox or open-in-emacs when it detects that the point is on a link. And of course all these commands would have their own key binding that works only if the command makes sense.

This approach is described on the wiki, in case you read this post much later and want to see an up-to-date code, or even update it yourself. In case something cool comes out of this snippet, I can try to implement a more palatable API for defhydra, most likely an option to supply a function name in the docstring argument position.

New video demo - counsel-load-theme

Without further ado, here's the video link.

The code

(defun counsel--load-theme-action (x)
  "Disable current themes and load theme X."
  (condition-case nil
      (progn
        (mapc #'disable-theme custom-enabled-themes)
        (load-theme (intern x))
        (when (fboundp 'powerline-reset)
          (powerline-reset)))
    (error "Problem loading theme %s" x)))

;;;###autoload
(defun counsel-load-theme ()
  "Forward to `load-theme'.
Usable with `ivy-resume', `ivy-next-line-and-call' and
`ivy-previous-line-and-call'."
  (interactive)
  (ivy-read "Load custom theme: "
            (mapcar 'symbol-name
                    (custom-available-themes))
            :action #'counsel--load-theme-action))

It looks almost trivial, the main idea is to disable all current themes and load the new one. Additionally, try to reset the powerline, since it has to match the mode-line face, which most themes customize.

The Interface

The interface of ivy-read is the same as the built-in completing-read in first two arguments. The difference is that it also accepts a callback through the :action argument. This callback will make the completion engine aware of what needs to be done with the completion result. The presence of the callback allows these completion engine features to work:

  • ivy-resume will resume the last completion. Very useful if you change your mind on the candidate, or want to examine a related candidate.
  • ivy-next-line-and-call selects the next matching candidate and executes the callback for it.
  • ivy-previous-line-and-call selects the previous matching candidate and executes the callback for it.

I like to think of ivy-resume as a DEL or <left> for completion. As you can erase or go back one character with the same DEL binding, regardless of the last character inputted (a or B etc), in the same way you can call the completion again with the same <f6> binding, regardless of the command that required completion (counsel-git-grep or counsel-load-theme or counsel-load-library etc). ivy-resume isn't bound by default, since it needs to be a global binding. I just use this in my config:

(global-set-key [f6] 'ivy-resume)

For the functions that execute the callback while changing the candidate, the idea is:

  • C-M-n (ivy-next-line-and-call) corresponds to C-n (ivy-next-line),
  • C-M-p (ivy-previous-line-and-call) corresponds to C-n (ivy-previous-line).

I've also showed off a generic ivy feature: M-j will yank the word at point into the minibuffer. Think of it as the mirror of C-w in isearch. It could not be C-w, since I like C-w being bound to kill-region.

The command/insert mode split

counsel-load-theme.png

Finally, I show off the equivalent hydra-based modal selection method. So instead of pressing C-M-n C-M-n C-M-n C-M-p C-M-p, you can press the equivalent C-o c jjjkk. Luckily, you don't need to remember a lot of bindings for this hydra mode: just press C-o and read the hints. I'll just list the exit points, since that's usually the more important stuff:

  • To exit the "command-mode" completely, press o or the standard C-g.
  • To exit the "command-mode" with the current candidate, press f or d.
  • To exit the "command-mode" and once again edit the minibuffer, press i.

You might ask why f and d do the same. They actually mirror C-j (ivy-alt-done) and C-m (ivy-done). And if you ask what the difference between those two is, the answer is that ivy-alt-done will not exit the completion when selecting directories during file name completion. It may be possible to extend this to other types of completion where it makes sense to select something but not to exit the minibuffer.

Outro

If you're using ivy-mode, make sure to try the new features: the action-using commands should work for any command that starts with counsel-. For other commands, like package-install, you can only select one candidate with C-m.

Also do try C-M-n with counsel-find-file: you'll be able to cycle through all files in a directory without exiting the completion. Same goes for ivy-switch-buffer, which should probably be renamed to counsel-switch-buffer for consistency.

Debug Clojure with CIDER and lispy

To commemorate the release of CIDER 0.9.0, I've just added the ability to debug-step-in Clojure expressions to lispy.

This ability was present for Elisp for a very long time, and it's instrumental to my Elisp output. So now I've added exactly the same thing to Clojure.

How it works

Suppose that you have this function (borrowed from The Joy of Clojure):

(defn l->rfix
  ([a op b]
   (op a b))
  ([a op1 b op2 c]
   (op2 c (op1 a b)))
  ([a op1 b op2 c op3 d]
   (op3 d (op2 c (op1 a b)))))

An important thing is that the function needs to be loaded (probably with cider-load-file), in order for Clojure to know its location. If you only evaluate the function with C-x C-e, it won't work. Actually, the same applies to Elisp.

And then you have the corresponding function call (| is the point, as usual):

|(l->rfix 10 * 2 + 3)

As you press xj (lispy-debug-step-in), the following code is evaluated on the Clojure side:

(do
  (def a 10)
  (def op1 *)
  (def b 2)
  (def op2 +)
  (def c 3))

At the same time, you are taken to the second branch of the body of l->rfix - exactly the one that corresponds to 5 arguments. And that's it: you now have a, op1 etc defined to their proper values. You can now continue within the function body with many possible follow-ups. I'll just list the eval-related ones:

  • Use e to evaluate expression at point.
  • If you want to evaluate a symbol, mark it with M-m, end evaluate with e. Actually, I prefer to mark stuff with m and hjkl arrows, using i to mark the first element of the region. It's also possible to mark with a, and 2m, 3m etc.
  • If you want to bind a symbol in a let binding, mark both the symbol and its value, and press e. If there are many let bindings, you can navigate to the next one with either jj or 2j.
  • You can debug-step-in again if needed with xj.
  • You can flatten a function or a macro call with xf.
  • You can eval-and-insert with E.
  • You can eval-and-commented-insert with 2e.

Just to add, any of these will work properly and switch to an appropriate body branch:

(l->rfix 1 * 1 + 1)
(l->rfix 2 + 7)
(l->rfix 1 + 2 + 3 + 4)
(l->rfix (str "a" "b" "c") + 7)

Outro

Enjoy the new code, but be mindful that it's really fresh, so it will certainly have some quirks. For instance, function arguments as a map won't work since I haven't programmed for that yet. Big thanks to @bbatsov and all CIDER contributors.

A really cool thing that I hope to get in the future, is to make Z (lispy-edebug-stop) also work in Clojure. What it does currently for Elisp, is to use edebug to setup the function arguments. It may be possible to use @Malabarba's new debugger implementation for the same thing.

The advantages of using lispy-style debugger are the following:

  • You can navigate your code the way you want, not just in a way that the debugger allows you to.
  • You can multi-debug, and have sessions last for days.
  • You can edit the code as you debug. For this new code, I started debugging when there was only a function name. I added the body code expression-by-expression, simultaneously debugging it.

The disadvantage is a bit of namespace pollution, but I think it's more or less acceptable.

avy 0.3.0 is out

This is a feature-packed release consisting of 57 commits done over the course of the last month by me and many contributors.

I'm trying something different this time with the release-notes: I've started a Changlelog.org in the repository, which is much more pleasant to read (and write) in org-mode form inside Emacs:

avy-changelog-org.png

Then I used pandoc to convert the Org file to Markdown. Too bad, the pandoc install-able through apt-get knows nothing of Org mode, so I had to

cabal install pandoc

and let cabal do its thing for like 30 minutes. The exported Markdown wasn't too bad, just had to reformat most things to one line, since Github's fill-column is like 150 chars instead of the usual 80. If someone has more experience of exporting Org to Markdown, please do share: maybe there's a better way to do this.

Anyway, read the release notes either at github or inside Emacs and enjoy the cool new stuff. Big thanks to all contributors.

Also check out the wiki page on customization and other pages. I think it would be cool to have a separate page for e.g. avy-goto-char with the screenshots (or gifs) for all values of avy-style:

  • pre
  • post
  • at
  • at-full
  • de-bruijn

So if anyone is willing to contribute (and check out those overlay styles in the process), please go ahead: you can either clone the wiki with:

git clone https://github.com/abo-abo/avy.wiki.git

and push, or just edit it using Github's widgets.

Transform a LISP case into a cond with lispy

Just a little extension to the old xc (lispy-to-cond) command that I've added today to lispy:

lispy-case-to-cond.png

Previously, xc could only transform a series of nested if into a cond. You can even chain xcxi to transform a case statement into equivalent if statements.

Note also that unwanted whitespace is properly removed. What you see in the picture is actually an ERT test that's being run each time I commit new stuff. It makes sure that starting in the first buffer state and pressing xc really results in the second buffer state. With this test, it's reasonable to believe that xc will actually work the same way in an interactive scenario. Right now, there are 599 of these type of tests in lispy-test.el. To view them like in the screenshot, you can press xv.

Final note, I realize that it should be eql instead of eq, but I like eq more: it's 33% more efficient.