07 Feb 2015
This will be a short post sharing two pieces of information.
A Hydra demo is on Youtube
Here is the link, and here is the current window-switching code:
(global-set-key
(kbd "C-M-o")
(defhydra hydra-window (:color amaranth)
"window"
("h" windmove-left)
("j" windmove-down)
("k" windmove-up)
("l" windmove-right)
("v" (lambda ()
(interactive)
(split-window-right)
(windmove-right))
"vert")
("x" (lambda ()
(interactive)
(split-window-below)
(windmove-down))
"horz")
("t" transpose-frame "'")
("o" delete-other-windows "one" :color blue)
("a" ace-window "ace")
("s" ace-swap-window "swap")
("d" ace-delete-window "del")
("i" ace-maximize-window "ace-one" :color blue)
("b" ido-switch-buffer "buf")
("m" headlong-bookmark-jump "bmk")
("q" nil "cancel")))
ace-window 0.7.0
is out
You can see the release notes here.
There's not a whole lot of user-visible changes, but a large portion of the code was re-written
to facilitate the use of the API. I hope that no new bugs were introduced with this change,
you can still fall back to 0.6.1 and send
me an issue if something broke. There's a benefit
in the long run, just see how simple the code has become:
(defun ace-maximize-window ()
"Ace maximize window."
(interactive)
(select-window
(aw-select " Ace - Maximize Window"))
(delete-other-windows))
Here, aw-select
just returns a selected window, and nothing else. Very easy to use.
06 Feb 2015
After having written a few Emacs packages, it happens
sometimes when I'm reading questions on Emacs Stack Exchange,
I think to myself that either:
I've done this before in package X
or:
This feature would fit nicely in package Y
On very rare occasions it happens that a question matches two packages at once.
Here is such a question:
For the following C++ function:
bool importantStuff(double a, double b);
It should output the following snippet, perhaps without the tags:
/**
* <Insert description of importantStuff>
*
* @param a <Insert description of a>
* @param b <Insert description of b>
* @return <Insert description of the return value>
*/
Well, I've got a package called function-args
for C++ that uses CEDET to:
- display tool-tips with function arguments
- jump to a semantic tag in current file
- do a bit of completion
- do a bit of generation (like inherited function signatures)
And the expansion part can be handled by auto-yasnippet, which
I've covered in an earlier post.
The code
I think it might be useful to look at the code, since it's small and shows how to use CEDET
's and
auto-yasnippet
's API:
(defun moo-doxygen ()
"Generate a doxygen yasnippet and expand it with `aya-expand'.
The point should be on the top-level function name."
(interactive)
(move-beginning-of-line nil)
(let ((tag (semantic-current-tag)))
(unless (semantic-tag-of-class-p tag 'function)
(error "Expected function, got %S" tag))
(let* ((name (semantic-tag-name tag))
(attrs (semantic-tag-attributes tag))
(args (plist-get attrs :arguments))
(ord 1))
(setq aya-current
(format
"/**
* $1
*
%s
* @return $%d
*/
"
(mapconcat
(lambda (x)
(format "* @param %s $%d"
(car x) (incf ord)))
args
"\n")
(incf ord)))
(aya-expand))))
The bonus
If you're interested in Doxygen, here's a
bit of code that I found laying around. It will prettify
e.g. <tt>Numerical Recipies</tt>
to Numerical
Recipies in the comments.
This code uses a similar approach to the one used in my posts
about prettifying Elisp regex,
and ElTeX.
(defface font-lock-doxygen-face
'((nil (:foreground "SaddleBrown" :background "#f7f7f7") ))
"Special face to highlight doxygen tags such as <tt>...</tt>
and <code>...</code>."
:group 'font-lock-highlighting-faces)
(font-lock-add-keywords
'c++-mode
'(("\\(<\\(?:code\\|tt\\)>\"?\\)\\([^<]*?\\)\\(\"?</\\(?:code\\|tt\\)>\\)"
(0 (prog1 ()
(let* ((expr (match-string-no-properties 2))
(expr-len (length expr)))
(if (eq 1 expr-len)
(compose-region (match-beginning 0)
(match-end 0)
(aref expr 0))
(compose-region (match-beginning 1)
(1+ (match-end 1))
(aref expr 0))
(compose-region (1- (match-beginning 3))
(match-end 3)
(aref expr (1- expr-len)))))))
(0 'font-lock-doxygen-face t))))
Outro
I hope that the code listed here will be useful to someone other than
me. It's a nice highlight of how a language can be made much cooler
by just having a package manager that allows to quickly glue various
things together. I mean, what would happen to JavaScript without npm
(besides stopping people requiring Angular just to use arrays)?
C++, on the other hand, is too cool to care about such things as
package managers and modules and stuff. But we're stuck with it for
performance reasons, so might as well try to make the experience more
bearable by adding some niceties to c++-mode
.
05 Feb 2015
An amaranth planted in a garden near a Rose-Tree, thus addressed it:
"What a lovely flower is the Rose, a favorite alike with Gods and
with men. I envy you your beauty and your perfume." The Rose
replied, "I indeed, dear Amaranth, flourish but for a brief season!
If no cruel hand pluck me from my stem, yet I must perish by an
early doom. But thou art immortal and dost never fade, but bloomest
for ever in renewed youth."
Prompted by the "Avoid exiting the hydra on hitting a wrong key" issue,
I decided to add a new body color for Hydra. Turns out that there exists a color that represents
immortality, and it's called amaranth. It's also very pretty.
So what amaranth Hydras do basically, is make it impossible to quit them.
Of course, it would be totally lame if there wasn't at least one way to quit them.
And what better to utilize for this purpose,
than the good-old quitters - the blue Hydra heads.
An Example code
(global-set-key
(kbd "C-z")
(defhydra hydra-vi
(:pre
(set-cursor-color "#e52b50")
:post
(set-cursor-color "#ffffff")
:color amaranth)
"vi"
("l" forward-char)
("h" backward-char)
("j" next-line)
("k" previous-line)
("m" set-mark-command "mark")
("a" move-beginning-of-line "beg")
("e" move-end-of-line "end")
("d" delete-region "del" :color blue)
("y" kill-ring-save "yank" :color blue)
("q" nil "quit")))
This Hydra has only three exit points:
- q, which does nothing
- y, which copies the active region
- d, which deletes the active region
In addition to the usual arrows on hjkl, a and
e move to beginning and end of line respectively.
Outro
I hope that you like the new idea, also thanks to
@vkazanov for the push in this
direction. Things should be backwards-compatible, so this feature
costs you nothing if you're not using it. See the release notes for 0.8.0.
One more thanks goes to Sridhar Ratnakumar for this suggestion
that lead to an improvement in auto-yasnippet. Thanks to this improvement,
I was able to quickly wrap words like this:
<font color="#FF007F">Rose</font>
04 Feb 2015
Only three commits happened since 0.6.1
, but already 0.7.0 has to
be released, since the behavior has changed slightly.
The ultimate window switching setup, revised
The code from the last post had to be changed.
Note that only ace-window
-related heads are affected, since ace-window
uses set-transient-map
as well.
This was necessary to fix issue #15 (unable to use goto-line
in a Hydra, since it requires input).
(global-set-key
(kbd "C-M-o")
(defhydra hydra-window ()
"window"
("h" windmove-left)
("j" windmove-down)
("k" windmove-up)
("l" windmove-right)
("a" (lambda ()
(interactive)
(ace-window 1)
(add-hook 'ace-window-end-once-hook
'hydra-window/body)
(throw 'hydra-disable t))
"ace")
("v" (lambda ()
(interactive)
(split-window-right)
(windmove-right))
"vert")
("x" (lambda ()
(interactive)
(split-window-below)
(windmove-down))
"horz")
("s" (lambda ()
(interactive)
(ace-window 4)
(add-hook 'ace-window-end-once-hook
'hydra-window/body)
(throw 'hydra-disable t))
"swap")
("t" transpose-frame "'")
("d" (lambda ()
(interactive)
(ace-window 16)
(add-hook 'ace-window-end-once-hook
'hydra-window/body)
(throw 'hydra-disable t))
"del")
("o" delete-other-windows "one" :color blue)
("i" ace-maximize-window "ace-one" :color blue)
("q" nil "cancel")))
You can also see an awesome addition sneak in, with transpose-frame
on t.
Technical note
If you look closely at the code, you'll see the ace-window
-related
lambdas throwing the hydra-disable
symbol. If the Hydra code catches
that symbol, it will not call set-transient-map
again, effectively
making the head that threw the symbol blue. In this example, though,
ace-window
-related functions are still red, since the Hydra is
resumed by calling hydra-window/body
in ace-window-end-once-hook
.
This is a trick that I have to play because of how ace-jump-mode
works. Hopefully, it won't be needed for other commands. But still,
it's a simple trick that you can use if you want to have a head quit
conditionally, or something.
goto-line
ad infinum
(defhydra hydra-test (global-map "M-g")
("g" goto-line "goto-line"))
With the Hydra above, it's possible to:
- M-g g
10
RET to go to line 10
- g
20
RET to go to line 20
- g
50
RET to go to line 50
etc.
Change the cursor color when a Hydra is active
Here's how the new :pre
and :post
statements work:
(global-set-key
(kbd "C-z")
(defhydra hydra-vi
(:pre
(set-cursor-color "#40e0d0")
:post
(set-cursor-color "#ffffff"))
"vi"
("l" forward-char)
("h" backward-char)
("j" next-line)
("k" previous-line)
("q" nil "quit")))
In this example, the cursor color will change for the duration of the Hydra.
Both :pre
and :post
should match to a single Elisp statement; you can use progn
to tie
a bunch of statements together if you want.
A more evil helm
Lit Wakefield has a nice article on
using hydra
and helm
together. Do check it out, I'll certainly steal some of his code.
Outro
I hope that you don't have an objection to Hydra being developed at a
fast pace with many features popping up. I try my best to keep things
backwards compatible, but I really want to quickly fix the areas where
the code is lacking. Thanks to all the people contributing
issues, especially
@atykhonov and
@nandryshak for the last two, which are the core
of this version bump.
03 Feb 2015
Hydra evolution picks up the pace with
hydra 0.6.1.
This version adds prefix arguments to all Hydras.
Some Examples
Look ma, no modifiers!
Now it's possible to write this:
(global-set-key
(kbd "C-z")
(defhydra hydra-vi ()
"vi"
("l" forward-char)
("h" backward-char)
("j" next-line)
("k" previous-line)))
And now C-z 5j7l will move 5 lines down and 7 characters
left, still with the option to press h, j,
k, l some more.
Additionally C-z C-u C-u C-u k will move 64 lines up, since C-u multiplies its
argument by 4 each time.
The good-old zoom
If you remember, this was the original Hydra:
(defhydra hydra-zoom (global-map "<f2>")
"zoom"
("g" text-scale-increase "in")
("l" text-scale-decrease "out"))
Now, <f2> g 4g 2l will zoom in 5 times, and zoom out 2
times for a total of +3 zoom.
The good-old move-splitter
(defhydra hydra-splitter (global-map "C-M-s")
"splitter"
("h" hydra-move-splitter-left)
("j" hydra-move-splitter-down)
("k" hydra-move-splitter-up)
("l" hydra-move-splitter-right))
This Hydra can benefit from numeric arguments as well: C-M-s l
40l will quickly make the right window a lot smaller.
If I wanted to type C-M-s 40 l, I would have to use this definition instead:
(global-set-key
(kbd "C-M-s")
(defhydra hydra-splitter ()
"splitter"
("h" hydra-move-splitter-left)
("j" hydra-move-splitter-down)
("k" hydra-move-splitter-up)
("l" hydra-move-splitter-right)))
For that case, I would get the hint immediately after C-M-s and would be able to give the numeric argument immediately,
but I wouldn't be able to bind anything else on C-M-s as a prefix, e.g.:
(global-set-key (kbd "C-M-s z") 'recenter-top-bottom)
The code above would give the error "Key sequence C-M-s z starts with non-prefix key C-M-s"
.
So you can pick the method that you prefer, the choice is there.
The ultimate window switching setup
(global-set-key
(kbd "C-M-o")
(defhydra hydra-window ()
"window"
("h" windmove-left)
("j" windmove-down)
("k" windmove-up)
("l" windmove-right)
("a" (lambda ()
(interactive)
(ace-window 1)
(add-hook 'ace-window-end-once-hook
'hydra-window/body))
"ace")
("v" (lambda ()
(interactive)
(split-window-right)
(windmove-right))
"vert")
("x" (lambda ()
(interactive)
(split-window-below)
(windmove-down))
"horz")
("s" (lambda ()
(interactive)
(ace-window 4)
(add-hook 'ace-window-end-once-hook
'hydra-window/body))
"swap")
("d" (lambda ()
(interactive)
(ace-window 16)
(add-hook 'ace-window-end-once-hook
'hydra-window/body))
"del")
("o" delete-other-windows "1" :color blue)
("i" ace-maximize-window "a1" :color blue)
("q" nil "cancel")))
The credit for this monster goes to bcarell, I just refined his approach
with making ace-window
not quit the hydra-window
Hydra.
This setup needs the latest ace-window.
Here's the result of C-M-o xvxv starting from a single window:
From here, I can quickly maximize the current window with o
while simultaneously quitting the Hydra; i will maximize a
window as well, but it will select it with ace-window
, instead of
maximizing the current one.
Note that, since numerical arguments are working now, 4a is
the same as s (swap) and 16a or C-u C-u
a is the same as d (delete).