02 Feb 2015
This post advertises the release of hydra.el 0.5.0.
The package was introduced in an earlier post and received some positive feedback.
Currently, it's available in both
GNU ELPA (slightly behind)
and MELPA (always current).
The newfound utility
As I originally released the Hydra, I was asked if it was similar to
this approach
described on Endless Parentheses. I
said that it's not, since Hydra commands are meant to be repeatable
instead of being called just once.
Now, after I've designated the repeatable heads with red color and the one-off heads with blue color,
Hydra can reproduce the mentioned approach. Here's how it looks like:
(global-set-key
(kbd "C-c C-v")
(defhydra hydra-toggle (:color blue)
"toggle"
("a" abbrev-mode "abbrev")
("d" toggle-debug-on-error "debug")
("f" auto-fill-mode "fill")
("t" toggle-truncate-lines "truncate")
("w" whitespace-mode "whitespace")
("q" nil "cancel")))
And this is what I see in the echo area after pressing C-c C-v:
At this point:
- q will cancel without doing anything. I think it's more
convenient than doing the equivalent C-g with the
global-set-key
approach.
- a, d, f, t, and a will call the appropriate function.
The advantage of blue Hydra heads over
global-set-key
is that you immediately get the hint: this can be useful
for rarely used commands.
- as with the old red Hydra heads, pressing anything other than
a, d, f, t, a,
or q will vanquish the Hydra and call the key binding
that you just pressed. This is different from the
global-set-key
approach, which would e.g. error to your C-c C-v C-n with
"C-c C-v C-n is undefined"
, unlike the Hydra, which would call C-n
(next-line
).
The full defhydra
syntax
Note that defhydra
looks intentionally like a defun
, so that it's
easier to remember how it works.
Here's a more verbose Hydra that's largely equivalent to the one above:
(defhydra hydra-toggle (global-map "C-c" :color red)
"toggle"
("a" abbrev-mode "abbrev" :color blue)
("d" toggle-debug-on-error "debug" :color blue)
("f" auto-fill-mode "fill" :color blue)
("t" toggle-truncate-lines "truncate" :color blue)
("w" whitespace-mode "whitespace" :color blue)
("v" recenter-top-bottom "recenter" :color red)
("q" nil "cancel" :color blue))
(global-set-key (kbd "C-c C-v") 'hydra-toggle/body)
First argument: Hydra name
This argument decides the prefix to all the functions that will be generated.
In this case the following functions will be generated:
hydra-toggle/abbrev-mode
hydra-toggle/toggle-debug-on-error
hydra-toggle/auto-fill-mode
hydra-toggle/toggle-truncate-lines
hydra-toggle/whitespace-mode
hydra-toggle/recenter-top-bottom
hydra-toggle/nil
hydra-toggle/body
The final function calls the Hydra's body, displaying the hint in the echo area and setting
the transient map. It's the return result of defhydra
, that's why it was possible to pass it
to global-set-key
in the first example.
Second argument: Hydra body
The Hydra body consists of:
- a map used for binding, like
global-map
or c++-mode-map
or projectile-mode-map
- the body prefix: it's a string passable to
kbd
that will be used
in conjunction with heads' prefixes to bind the functions
- an optional plist, which currently recognizes only the
:color
key,
which in turn can be either red (the old behavior) or blue.
It's possible to omit the map and the body prefix simultaneously (it
doesn't make sense to omit one but not the other), or just pass ()
if you want to get a red Hydra body that you can bind yourself.
Third argument: Hydra hint
This string will be used in the echo area to distinguish the current
Hydra. This is optional, it case you don't provide it, it will default
to "hydra"
.
Fourth argument: Hydra heads
Each Hydra head is a list of:
- the key extension
- the function
- optional hint
- optional plist.
Again, the plist recognizes only :color
currently. The color is inherited from the body, if
you don't specify it. In turn, the body color is red if you don't specify it.
The code above:
- binds C-c a, C-c d etc with the usual
global-set-key
approach.
- binds C-c C-v a, C-c C-v d with the new
approach, which shows you the hint right after C-c C-v and
allows you to cancel easier.
- binds C-c v to be repeatable, i.e. you can press C-c v v v. Same with C-c C-v v v v.
Some more ideas for blue Hydras
Here's one for some helm
-related functions, don't ask how
projectile-find-file
ended up here:
(global-set-key
"κ"
(defhydra hydra-helm (:color blue)
"helm"
("f" projectile-find-file "file")
("w" helm-org-wiki "wiki")
("r" helm-recentf "recent")
("s" helm-swoop "swoop")
("q" nil "quit")))
Here's one more for gnus
; I'm just getting the hang of it, so some hints are useful:
(defhydra hydra-gnus-reply (:color blue)
"reply"
("o" gnus-summary-reply-with-original "one")
("O" gnus-summary-reply)
("a" gnus-summary-wide-reply-with-original "all")
("A" gnus-summary-wide-reply)
("u" gnus-summary-very-wide-reply-with-original "universe")
("U" gnus-summary-very-wide-reply)
("q" nil "quit"))
(define-key gnus-summary-mode-map "r" 'hydra-gnus-reply/body)
I omit the hint for the commands that do the same as the previous one,
just without citing.
Outro
I hope that you enjoy the update and let me know when you invent some
efficient blue or red-and-blue Hydras. Happy hacking!
01 Feb 2015
In Emacs, of course. As I mentioned before, this blog is run using
Jekyll and a fork of the
lanyon theme.
It started out pretty convenient: thanks to jekyll serve
I would see
the live updates at http://localhost:4000/
as I was editing the
markdown. But after a few posts, I've experienced a drastic decrease
in refresh time: it went from under a second to 15 seconds. Below, I'll show how I've managed
to speed it back up.
No posts - no problem
Thanks to (require 'dired-x)
I can jump from the current post to the
posts directory with C-x C-j. It even puts the point on the current file. Now:
- m to mark to current file
- t to invert the mark, making the current file unmarked and all others marked
- D to delete all marked files. No need to worry, since
they are all version-controlled (except the original one, which isn't
being deleted),
dired
even asks you for confirmation, so I hit
y.
Now, that there is only one post in the whole blog, jekyll serve
is
much faster at refreshing.
Even faster jekyll serve
Which brings me to the next point. Since I'm calling it so much, might
as well wrap it in some Elisp. Elisp is like frosting - it makes everything better:
(defun jekyll-serve ()
(interactive)
(let* ((default-directory
(if (string-match "_posts/$" default-directory)
(directory-parent default-directory)
default-directory))
(buffer (if (get-buffer "*jekyll*")
(switch-to-buffer "*jekyll*")
(ansi-term "/bin/bash" "jekyll")))
(proc (get-buffer-process buffer)))
(term-send-string proc "jekyll serve\n")
(sit-for 3)
(browse-url "localhost:4000")))
This function, when called from the current post or the current blog, will:
- open a new
ansi-term
called *jekyll*
- issue a
jekyll serve
command to it
- wait for 3 seconds for Jekyll to start
- open
localhost:4000
in Firefox
One post isn't a blog
Here's how to quickly bring back the deleted posts.
- Fire up magit; I have
magit-status
bound to μm.
- Stage the current post with s (
magit-stage-item
).
- Start the commit with C (
magit-commit-add-log
). I'm using my own modification of magit-commit-add-log
that arranges the whitespace in a nice way, when aimed at a file instead of a hunk.
- I get
_posts/2015-02-01-blogging-about-blogging.md:
string auto-generated by C.
- I just amend it to look like
_posts/2015-02-01-blogging-about-blogging.md: add
and finalize the commit with
C-c C-c (git-commit-commit
).
- Now I have around 40 files marked as deleted, but not staged. I create a stash with zz and name it
foo
or something. Then delete the stash with magit-discard-item
, which I like to bind to d to be similar
to dired
. Now it's as if these files were never deleted.
Whoa, now that I look at it, it's a lot of steps. But somehow dired
and magit
are very similar to
Super Mario Bros: it
takes long to explain what you're doing, but as you play it, it's very
simple and natural.
31 Jan 2015
This is a follow-up to
an older post about abbrevs.
I did try to make Elisp abbrevs work, but after a few times of getting
is
expanded to indent-sexp
in strings or comments, I just could
take it no longer.
Luckily and timely, Artur Malabarba revealed his
speed-of-thought-lisp. This
package does many things, but the main idea that I liked was that the
abbrevs should only expand when in the function position, i.e. the
abbrev is:
- right after opening parenthesis
- not in a comment
- not in a string
- not in the arguments list
With that idea, I've "refactored" my old abbrevs into
Abel. It's a minor mode add-on for
abbrev-mode
. When you activate it, around a hundred abbrevs are
added to your abbrev list for emacs-lisp-mode
. When you deactivate
it, these abbrevs are disabled, and your original abbrev list is
restored. It even overrides the mode-line description to Abbrev
->
Abel
while it's active, thanks to
diminish.
So while you should try sotl
, which provides many other things, keep
abel
in mind: it's a simple upgrade to abbrev-mode
that binds no
bindings and asks no questions.
Here's the current list:
(defcustom abel-abbrevs
'(
;; basics
("a" "and")
("bp" "boundp")
("c" "concat")
("fp" "fboundp")
("ii" "interactive")
("i" "insert")
("l" "lambda")
("m" "message")
("n" "not")
("f" "format")
("u" "unless")
("up" "unwind-protect")
("w" "when")
("wl" "while")
("r" "require")
("ci" "call-interactively")
("cc" "condition-case")
("pg" "plist-get")
("sa" "save-excursion")
("sr" "save-restriction")
("smd" "save-match-data")
;; defines
("de" "declare-function")
("df" "defface")
("da" "defmacro")
("du" "defcustom")
("dv" "defvar")
;; everything with char
("bc" "backward-char")
("scb" "skip-chars-backward")
("scf" "skip-chars-forward")
("gc" "goto-char")
("fc" "forward-char")
("dc" "delete-char")
("ca" "char-after")
;; everything with region
("ra" "region-active-p")
("rb" "region-beginning")
("re" "region-end")
("ntr" "narrow-to-region")
("dr" "delete-region")
("ir" "indent-region")
;; error related
("ie" "ignore-errors")
("e" "error")
;; regex match related
("la" "looking-at")
("lb" "looking-back")
("mb" "match-beginning")
("me" "match-end")
("ms" "match-string")
("msn" "match-string-no-properties")
("rm" "replace-match")
("ro" "regexp-opt")
("rq" "regexp-quote")
("rr" "replace-regexp-in-string")
("rsb" "re-search-backward")
("rsf" "re-search-forward")
("sf" "search-forward")
("sm" "string-match")
;; words
("fw" "forward-word")
("bw" "backward-word")
;; lines
("eol" "end-of-line")
("fl" "forward-line")
("lbp" "line-beginning-position")
("lep" "line-end-position")
("nai" "newline-and-indent")
;; buffer
("bfn" "buffer-file-name")
("bn" "buffer-name")
("bs" "buffer-substring")
("bsn" "buffer-substring-no-properties")
("cb" "current-buffer")
("wcb" "with-current-buffer")
("wtb" "with-temp-buffer")
("efn" "expand-file-name")
("ff" "find-file")
("ffn" "find-file-noselect")
;; window
("ow" "other-window")
("sw" "selected-window")
;; string
("ssn" "substring-no-properties")
("ss" "substring")
("si" "split-string")
("se" "string=")
("sl" "string<")
("sp" "stringp")
;; point
("pi" "point-min")
("pa" "point-max")
("p" "point")
;; key
("gk" "global-set-key")
("dk" "define-key")
;; rest
("ah" "add-hook")
("atl" "add-to-list")
("bod" "beginning-of-defun")
("bol" "beginning-of-line")
("dm" "deactivate-mark")
("fs" "forward-sexp")
("jos" "just-one-space")
("kn" "kill-new")
("lp" "load-path")
("mm" "major-mode")
("sic" "self-insert-command")
("sn" "symbol-name")
("tap" "thing-at-point")
("tc" "this-command")
("ul" "up-list"))
"List of (ABBREV EXPANSION) used by `abel'."
:set (lambda (symbol value)
"Update abbrevs accoring to `abel-abbrevs'."
(set symbol value)
(mapc #'abel-define value))
:group 'abel)
30 Jan 2015
I wonder, when the code isn't touched in a long time, is it good (no
need for changes) or bad (became obsolete)? Let's find out. I'll
explain here
auto-yasnippet, my
second package in MELPA out of more than a dozen currently that saw
almost no changes since the initial commit two years ago, and see if
you like it.
Short description of yasnippet
YASnippet is a template
system for Emacs. It allows you to type an abbreviation and
automatically expand it into function templates. Bundled language
templates include: C, C++, C#, Perl, Python, Ruby, SQL, LaTeX, HTML,
CSS and more.
Snippet step-by-step
Here's one of the snippets that I use for emacs-lisp-mode
:
# -*- mode: snippet -*-
# name: function
# key: d
# --
(defun $1 ($2)
$0)
- The name of the snippet,
function
is more like a comment than
anything else.
- On the other hand,
key
is very important: it's what I have to
insert in the buffer to get the expansion with M-x
yas-expand
.
- Everything after
# --
is the snippet body.
- This particular snippet has two fields, in places of
$1
and $2
.
$0
is where the point will be when the snippet expansion is finished
As I expand, pressing TAB will move from field to field
until the expansion is finished.
Snippets are mode-local
Here's the corresponding snippet for clojure-mode
:
# -*- mode: snippet -*-
# name: defn
# key: d
# --
(defn $1 [$2]
$0)
As you see, the key
here is the same; you're allowed to overload
them based on the current major-mode
.
Even after quite a few posts, I still keep forgetting
Jekyll's syntax for the post header. This is my reminder:
# -*- mode: snippet -*-
# name: post
# key: post
# --
---
layout: post
title: $0
---
Mirrors in snippets
This simple snippet introduces a powerful concept, and an important
yasnippet
feature that auto-yasnippet
uses:
# -*- mode: snippet -*-
#name : class ... { ... }
# --
class $1$2
{
public:
$1($0)
};
This is a snippet for a class declaration in c++-mode
; $1
, the
name of the class, is mirrored in the name of the constructor. This
way, you don't have to enter it twice.
What auto-yasnippet
does
All the snippets listed above are pre-configured, persistent and very
rarely changed. They are like plain functions in the source
code. Each of them needs their own file and so on.
What auto-yasnippet
provides are throw-away lambdas, that don't
need a file and aren't persistent.
Basic install of auto-yasnippet
To get a usable install, you just need to bind aya-create
, which is
similar in spirit to M-w (kill-ring-save
):
(global-set-key (kbd "H-w") 'aya-create)
and aya-expand
, which is similar to C-y (yank
):
(global-set-key (kbd "H-y") 'aya-expand)
I also like to bind:
(global-set-key (kbd "C-o") 'aya-open-line)
I'm using C-o to do all of these:
expand-abbrev
yas-expand
and yas-next-field-or-maybe-expand
open-line
Example 1: JavaScript
Let's say that you have this code and want to generate more like it:
field1 = document.getElementById("field1");
Let's even assume that you know how auto-yasnippet
works and wrote down a slightly modified code beforehand:
field~1 = document.getElementById("field~1");
Here, ~
are meant to represent yasnippet
's mirrors, they will be
consistent across every expansion. Now you type H-w (aya-create
), which works on the current
line when there's no region. Your code becomes the initial one without ~
, and aya-current
variable now holds:
aya-current
;; => "field$1 = document.getElementById(\"field$1\");"
By typing e.g. H-y 2 C-o RET, H-y 3 C-o RET, H-y Final C-o RET you get:
field2 = document.getElementById("field2");
field3 = document.getElementById("field3");
fieldFinal = document.getElementById("fieldFinal");
Note again, that there would be little point to saving this snippet in
a file, since the situation where you need to use it may not come up
again.
Example 2: Java
Here's the starting code, with fields and mirrors already in place
(note one mirror named ~On
, one field named ~on
and one field
named ~true
):
class Light~On implements Runnable {
public Light~On() {}
public void run() {
System.out.println("Turning ~on lights");
light = ~true;
}
}
Since the code spans multiple lines, as is often the case with Java, you need to mark it
with a region before H-w.
Here's the final result:
class LightOn implements Runnable {
public LightOn() {}
public void run() {
System.out.println("Turning on lights");
light = true;
}
}
class LightOff implements Runnable {
public LightOff() {}
public void run() {
System.out.println("Turning off lights");
light = false;
}
}
No need for AbstractLightFactoryAdapterProvider
when we can just
copy-paste stuff with auto-yasnippet
. In fact, I should probably
emphasize that when describing auto-yasnippet
: it's just an advanced
copy-paste tool.
Example 3: C++
Suppose that I want to generate curl
from the grad
. I can start with this template:
const Point<3> curl(grad[~2][~1] - grad[~1][~2],
Here, I need less than a line, so region needs to be marked again. The result:
const Point<3> curl(grad[2][1] - grad[1][2],
grad[0][2] - grad[2][0],
grad[1][0] - grad[0][1]);
Note how annoying it would be to triple check that the indices match.
This time, I just had to check the first line.
Example 4: taking ~
out of the equation
This works only for one-line snippets with a single mirror parameter.
In the JavaScript example, you can leave a $
instead of each
occurrence of $1
, and with the point in place of the last occurrence
you call H-w (aya-create
). Here, |
represents the point:
field$ = document.getElementById("|");
The final result is the same:
field1 = document.getElementById("field1");
field2 = document.getElementById("field2");
field3 = document.getElementById("field3");
fieldFinal = document.getElementById("fieldFinal");
Outro
I hope that this package will lessen your suffering when dealing with
verbose programming languages and repetitive text, and that a day will
come when repetition is no longer needed and auto-yasnippet
will
become obsolete.
29 Jan 2015
I was inspired by Sacha Chua's recent post
explaining her window bindings, that combine both ace-window
and windmove
.
So I wrote down an update to Hydra in order to get a similar setup.
Sacha's code
Here it is:
(key-chord-define-global
"yy"
(sacha/def-rep-command
'(nil
("<left>" . windmove-left)
("<right>" . windmove-right)
("<down>" . windmove-down)
("<up>" . windmove-up)
("y" . other-window)
("h" . ace-window)
("s" . (lambda () (interactive) (ace-window 4)))
("d" . (lambda () (interactive) (ace-window 16))))))
My code
Here's what I've come up with, thanks to the newest code in
hydra:
(defun hydra-universal-argument (arg)
(interactive "P")
(setq prefix-arg (if (consp arg)
(list (* 4 (car arg)))
(if (eq arg '-)
(list -4)
'(4)))))
(defhydra hydra-window (global-map "C-M-o")
"window"
("h" windmove-left "left")
("j" windmove-down "down")
("k" windmove-up "up")
("l" windmove-right "right")
("a" ace-window "ace")
("u" hydra-universal-argument "universal")
("s" (lambda () (interactive) (ace-window 4)) "swap")
("d" (lambda () (interactive) (ace-window 16)) "delete")
("o"))
(key-chord-define-global "yy" 'hydra-window/body)
The new code should already be available in
MELPA. I'll update the code in
GNU ELPA soon, when I make
sure that there were no bugs introduced by the change.
If anyone wants to see how the defhydra
macro expands, you can check
out
hydra-test.el.
I just added a Travis CI setup, so if you're interested in starting
to test your Elisp code, you can have a very simple example.
How the defined Hydra works
With this setup:
to swap two windows (i.e. call C-u ace-window
), I can do any of:
- C-M-o s
- C-M-o ua
- yys
- yyua
to delete one window (i.e. call C-u C-u ace-window
), any of:
- C-M-o d
- C-M-o uua
- yyd
- yyuua
to move one window down, two windows right, and one window up:
Although every other shortcut except the Hydra heads will vanquish the
Hydra, sometimes I have nothing on my mind that needs doing. For that
case, as you can see above, I enter o in its own list
without a function, so that o will dismiss the Hydra
without doing anything.