22 Feb 2015
This is a new feature I've added recently to lispy
that's useful when debugging regex-related code.
The gist of it is that the match data is a single global object in Emacs.
So if you call string-match
with your C-x C-e, there's no
guarantee that e.g. match-beginning
will return the proper thing with another C-x C-e,
since any package running in your Emacs could mess with the match data (packages that use timers
or post-command hooks etc.).
After getting annoyed by this a few times, I've finally added a
fail-safe to e (lispy-eval
) and p
(lispy-eval-other-window
). Here's how it looks like:
(defvar lispy-eval-match-data nil)
(defun lispy--eval-elisp-form (form lexical)
"Eval FORM and return its value.
If LEXICAL is t, evaluate using lexical scoping.
Restore and save `lispy-eval-match-data' appropriately,
so that no other packages disturb the match data."
(let (val)
(fset '\, #'identity)
(set-match-data lispy-eval-match-data)
(setq val (eval form lexical))
(setq lispy-eval-match-data (match-data))
(fset '\, nil)
val))
There's also a little dance of ignoring comma operators in the rare
case when I want to eval inside a backquoted list. The two
functions that you can take away from this exercise are match-data
and set-match-data
which appropriately return and store a list of
integers. Keeping the string separate from the regex match is a neat
way to improve performance.
21 Feb 2015
In yesterday's post
I showed how to embed variable values into the docstring, among other
things. Today, I've extended this approach to work with s-expressions.
Here's how it looks like:
(defhydra hydra-marked-items (dired-mode-map "")
"
Number of marked items: %(length (dired-get-marked-files))
"
("m" dired-mark "mark"))
This piece of code will remind you how many files you've marked so far each
time you press m.
By the way, this is the 64th post on the blog; I found out by pressing
tm in dired
buffer or the _posts
directory.
Also, I don't think that I've showed passing ""
as the keyboard prefix parameter
before. Apparently, it works and just translates to this:
(define-key dired-mode-map
"m" 'hydra-marked-items/dired-mark)
Here's how it looks like:
There's no need for a quitting key, it will auto-vanish when you press
anything other than m.
20 Feb 2015
Here's a little code I've made today for Buffer-menu-mode
:
(defhydra hydra-buffer-menu (:color pink)
"
Mark Unmark Actions Search
-------------------------------------------------------------------------
_m_: mark _u_: unmark _x_: execute _R_: re-isearch
_s_: save _U_: unmark up _b_: bury _I_: isearch
_d_: delete _g_: refresh _O_: multi-occur
_D_: delete up _T_: files only: %`Buffer-menu-files-only
_~_: modified
"
("m" Buffer-menu-mark nil)
("u" Buffer-menu-unmark nil)
("U" Buffer-menu-backup-unmark nil)
("d" Buffer-menu-delete nil)
("D" Buffer-menu-delete-backwards nil)
("s" Buffer-menu-save nil)
("~" Buffer-menu-not-modified nil)
("x" Buffer-menu-execute nil)
("b" Buffer-menu-bury nil)
("g" revert-buffer nil)
("T" Buffer-menu-toggle-files-only nil)
("O" Buffer-menu-multi-occur nil :color blue)
("I" Buffer-menu-isearch-buffers nil :color blue)
("R" Buffer-menu-isearch-buffers-regexp nil :color blue)
("c" nil "cancel")
("v" Buffer-menu-select "select" :color blue)
("o" Buffer-menu-other-window "other-window" :color blue)
("q" quit-window "quit" :color blue))
(define-key Buffer-menu-mode-map "." 'hydra-buffer-menu/body)
You can change the color on the top to your taste: either red or pink or amaranth will work.
For extensive Hydras I tend to pick pink, since I don't want to quit by accident, while still keeping
the non-head bindings. For small ones, red is better.
Here's how the result looks like:
You can install the cow with:
I didn't list it in the source, since Jekyll would wrap the long lines. The source is already in hydra-examples.el.
You only have to:
(require 'hydra-examples)
(define-key Buffer-menu-mode-map "." 'hydra-buffer-menu/body)
I've been hearing some opinions lately that the growing number of
Hydra options makes it intimidating or unclear. I hope that's not the
majority's feeling and that the gentle repetitiveness of this example
proves otherwise.
On the other hand, I've started reading "The Reasoned Schemer" this
week. Now that's intimidating.
19 Feb 2015
Two new colors are being added up to a total of five: red, blue,
amaranth, pink and teal.
I should carefully restate what they do to avoid confusion.
The three rules of Hydratics
1. A hydra may not injure a human being or, through inaction, allow a human being to come to harm.
Seriously though, see below.
Rule 1: Hydra heads are either red or blue
Once you're in a Hydra state:
- calling a red head will call the command and continue the state
- calling a blue head will call the command and stop the state
They may have a reddish or a bluish face that isn't exactly red or
blue, but that's what they are underneath. I hope you get what I mean.
Rule 2: red or blue is inherited from the body color
This is merely a convenience, you can still explicitly override each
head to be blue or red:
- if the body is red, amaranth or pink, the heads inherit red
- if the body is blue or teal, the heads inherit blue
Rule 3:
When you call a binding which isn't a head:
- amaranth, teal and pink Hydras will intercept it
- red and blue Hydras will quit and let Emacs execute your binding
Finally, on intercepting a non-head, amaranth and teal will issue a
warning and do nothing without quitting. And pink will try to call the
intercepted command without quitting. Currently only non-prefix
bindings can be called, since I haven't figured out how to do it for prefixes.
A nice table to sum things up
Thanks to @kaushalmodi for pointing me in this direction:
|----------+-----------+-----------------------+-----------------|
| Body | Head | Executing NON-HEADS | Executing HEADS |
| Color | Inherited | | |
| | Color | | |
|----------+-----------+-----------------------+-----------------|
| amaranth | red | Disallow and Continue | Continue |
| teal | blue | Disallow and Continue | Quit |
| pink | red | Allow and Continue | Continue |
| red | red | Allow and Quit | Continue |
| blue | blue | Allow and Quit | Quit |
|----------+-----------+-----------------------+-----------------|
Turns out learning Ruby wasn't a complete waste of time, at least I
learned about string interpolation. And now I'm sticking it into Elisp
packages, first tiny, and now
hydra.
How it works:
(defhydra hydra-toggle (:color pink)
"
_a_ abbrev-mode: %`abbrev-mode
_d_ debug-on-error: %`debug-on-error
_f_ auto-fill-mode: %`auto-fill-function
_g_ golden-ratio-mode: %`golden-ratio-mode
_t_ truncate-lines: %`truncate-lines
_w_ whitespace-mode: %`whitespace-mode
"
("a" abbrev-mode nil)
("d" toggle-debug-on-error nil)
("f" auto-fill-mode nil)
("g" golden-ratio-mode nil)
("t" toggle-truncate-lines nil)
("w" whitespace-mode nil)
("q" nil "cancel"))
(global-set-key (kbd "C-c C-v") 'hydra-toggle/body)
Here, using e.g. "_a_"
translates to "a"
with proper face. More
interestingly, e.g.
translates roughly to
(format "foobar %S" abbrev-mode)
This means that you actually see the state of the mode that you're
changing. The escape syntax was chosen with another intent in mind:
because of the backquote, if you have company-mode
on, you can
complete the symbols while in string.
See how it looks like in action:
Small note on pink Hydras
It's useful for instance it the above example, when I don't care about
self-inserting, but I still want to do navigation. Basically pink
Hydra is the closest thing to an actual minor mode. Thanks to
@angelic-sedition for the idea.
Small note on teal Hydras
It provides an interface similar to magit
dispatch: pressing
appropriate keys does things and pressing the wrong keys issues a
warning. The only difference between teal and amaranth is the color
inheritance, otherwise they behave exactly the same. This means that
if you want a non-quitting Hydra that will end up with more blue
heads, start with teal, otherwise, start with amaranth.
Thanks to @ffevotte for the idea.
Outro
It feels like things are finally falling into place with this package.
I hope that you like the new changes and find new cool uses for the added abilities.
Happy hacking!
18 Feb 2015
It happens to me frequently when making a demo, or just experimenting
around that I want to revert the buffer to the last saved
state. Obviously such a command exists in Emacs, and it's
unsurprisingly called revert-buffer
:
Replace current buffer text with the text of the visited file on disk.
This undoes all changes since the file was visited or saved.
But the poor old revert-buffer
isn't bound by default in Emacs. The perfect binding for it is C-x C-r:
(global-set-key
(kbd "C-x C-r")
(lambda () (interactive) (revert-buffer nil t)))
I believe the lambda is used to get the "no questions asked"
treatment. Anyway, by default Emacs binds C-x C-r to
find-file-read-only
, which really is a trash-tier command.
Why is find-file-read-only
useless?
Because C-x C-q is bound to read-only-mode
- a really
good command (made even better with wdired
and wgrep
). So
find-file-read-only
is just C-x C-f C-x C-q. And I don't
ever recall needing to use that, since v
(dired-view-file
) is almost equivalent if not better.
As a bonus, C-x C-r is mnemonic for "revert".
Auto-reverting
While on the topic, let me mention auto-revert-mode
. I have this in my config:
(global-auto-revert-mode 1)
This way, if any opened and saved file was modified outside of Emacs, it will be updated
in a short while. I was able to use this to a great advantage when writing the SVG picture
for the Xmodmap post:
- I opened the SVG in the default image mode in one Emacs instance
- and opened the same document in XML mode in another (mode is toggled with C-c C-c)
With that setup, I was able to see the picture update as I was inputting the XML.
Here's some Elisp for XML generation, if you're interested
(defun make-row (row-str ix iy fill)
(mapconcat
(lambda (x)
(format
"<g><text x=\"%d\" y=\"%d\" style=\"font-size:10px;fill:%s;font-family:Deja Vu Sans Mono\">%s</text></g>"
(+ ix (* x 30))
iy
fill
(let ((y (elt row-str x)))
(if (stringp y)
y
(make-string 1 y)))))
(number-sequence 0 (1- (length row-str)))
"\n"))
(make-row
"QWERTYUIOP"
95 140
"#2b2828")
;; =>
The last statement would generate:
Q
W
E
R
T
Y
U
I
O
P.