30 Jun 2015
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.
23 Jun 2015
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
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.
22 Jun 2015
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):
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.
19 Jun 2015
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:
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
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.
09 Jun 2015
Just a little extension to the old xc (lispy-to-cond
)
command that I've added today to lispy:
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.