More fun with emacs
Lately, I've been playing around with anything.el, which has been described as "Quicksilver for emacs". I've played around with it before, but have had a hard time changing my habits so that it's the first thing I think of doing when changing buffers or finding files. The main reason, I think, is that iswitch is usually just as fast.
However, I have started using git annex to store my music and
videos, and that has added some complexity to the way that I deal
with them. My usual flow is to do a git annex find
to see what
files I have on my laptop, after which I play one of them with
smplayer
. When I am done with the file, I either use git annex drop
or git annex move
to get rid of the file from my laptop.
All of that takes a lot of typing in the shell, so I wondered
whether I could speed it up using anything
. If you guessed that I
could (given that I am writing this article), give yourself a gold
star.
How I did it
anything
is highly extensible. Anyone can define their own setup
with a command like this:
(defun my-anything () (interactive) (anything-other-buffer '(anything-c-source-buffers+ anything-c-source-recentf anything-c-source-files-in-current-dir+ anything-c-source-git-annex anything-c-source-org-headline anything-c-source-bookmarks anything-c-source-bookmark-set anything-c-source-emms-files) "*anything*"))
which is just a function that tells anything
to use a bunch of
different "sources" as possible interesting things that you might
want to interact with. For example, anything-c-source-recentf
gives a list of files that you recently had open. That comes with
the anything
package, though, so it's not too interesting from our
point of view. What is interesting is
anything-c-source-git-annex
. It looks like this:
(defvar anything-c-source-git-annex '((name . "Git Annex") (candidates . (lambda () (with-current-buffer anything-current-buffer (with-temp-buffer (call-process "git" nil t nil "annex" "find") (goto-char (point-min)) (split-string (buffer-string) "\n"))))) (volatile) (action . (("Watch" . anything-c-git-annex-play) ("Drop" . anything-c-git-annex-drop) ("Move" . anything-c-git-annex-move) ("Copy" . anything-c-git-annex-copy)))))
It should hopefully be fairly easy to understand. There are two
interesting parts: getting candidate files, and the actions on the
candidate files. Getting the candidate files is as simple as
putting the results of git annex find
into a temporary buffer
(which must be in the same directory as the current buffer) and then
putting the lines of the buffer into a list. (This wouldn't work if
the file names contained newlines, but I don't have any of those.
If you wanted to be really careful, you could add the "–print0"
option to call-process
and split the string on "\0".)
The actions are all functions. The simplest is
anything-c-git-annex-drop
, which just looks like
(defun anything-c-git-annex-drop (candidate) (anything-git-annex-command "drop" (concat "\"" candidate "\"")))
where anything-git-annex-command
just starts a shell command.
More interesting is anything-c-git-annex-copy
, which is defined as
follows
(defun anything-c-git-annex-copy (candidate) (let* ((to (anything-comp-read "To Git Annex: " (anything-c-git-remotes) :must-match nil))) (anything-git-annex-command "copy" (concat "\"" candidate "\"") "--to" (concat "\"" to "\"")))) (defun anything-c-git-remotes () (with-temp-buffer (call-process "git" nil (current-buffer) nil "remote") (delete "" (split-string (buffer-string) "\n"))))
The anything-c-git-annex-copy
command needs to know to which
repository it should copy the candidate that has been selected. I
find out which one I want to copy to by listing the remotes that are
known about by the git repository that the file is in. Using
anything-comp-read
means that the remotes are listed in the same
style all of the anything
candidates are listed in. It's pretty
nice, because it keeps me in the same mode of operation as before.
Wrapping up
It's a bit hard to write about what makes anything.el
an
interesting way of doing things in emacs – a screencast might be
more helpful – but I hope some of the power is evident from this
little writeup. I'm going to try to train myself to use anything
more often. We'll see how it goes.