12 Feb 2015
As I was refactoring my .emacs
, I found a silly wrapper around
describe-function
that uses ido-completing-read
. I think I was
aware at the time that
ido-ubiquitous
could simply make describe-function
use ido-completing-read
. The
problem was that it also affected the completing-read
in functions
that became headlong-bookmark-jump
from
headlong
(see the corresponding post
if you missed it).
A bit of code to make things right
(defun ido-occasional-completing-read
(prompt collection
&optional predicate require-match initial-input
hist def inherit-input-method)
"Use `ido-completing-read' if the collection isn't too large.
Fall back to `completing-read' otherwise."
(let ((filtered-collection
(all-completions "" collection predicate)))
(if (<= (length filtered-collection) 30000)
(ido-completing-read
prompt filtered-collection nil
require-match initial-input hist
def nil)
(completing-read
prompt collection predicate
require-match initial-input hist
def inherit-input-method))))
;;;###autoload
(defmacro with-ido-completion (fun)
"Wrap FUN in another interactive function with ido completion."
`(defun ,(intern (concat (symbol-name fun) "/with-ido")) ()
,(format "Forward to `%S' with ido completion." fun)
(interactive)
(let ((completing-read-function
'ido-occasional-completing-read))
(call-interactively #',fun))))
The only thing that ido-occasional-completing-read
does is to
pre-filter collection
with predicate
and pass it on to
ido-completing-read
. And with-ido-completion
is just a convenience wrapper.
Example
(global-set-key (kbd "<f1> f")
(with-ido-completion describe-function))
(global-set-key (kbd "<f1> v")
(with-ido-completion describe-variable))
(global-set-key (kbd "<f2> i")
(with-ido-completion info-lookup-symbol))
Here, with-ido-completion
will generate
e.g. describe-function/with-ido
, which will subsequently be bound to
<f1> f. The good-old describe-function
is left unaffected.
Note, that if I turn on helm-mode
at this point, it will override
describe-function
with its own completion, but it will not touch
describe-function/with-ido
which I bound. This can be useful e.g. if
I want to use helm-mode
(or icy-mode
or icomplete-mode
) for some
completion, but not all.
You can find the package at ido-occasional.
11 Feb 2015
I've been cleaning up my .emacs lately, with the intention to put my
whole config on Github. Today, I'll show you a useful function for
describing the current buffer's key bindings. The function will
generate something like this (with all letters, of course):
;; (global-set-key (kbd "a") 'self-insert-command)
;; ...
;; (global-set-key (kbd "C-b") 'backward-char)
;; ...
;; (global-set-key (kbd "M-c") 'subword-capitalize)
;; ...
;; (global-set-key (kbd "C-M-d") 'down-list)
;; ...
;; (global-set-key (kbd "η") 'save-and-switch-buffer)
;; ...
;; (global-set-key (kbd "C-θ") 'ins-single-quotes)
;; ...
;; (global-set-key (kbd "M-ι") 'nil)
;; ...
;; (global-set-key (kbd "C-M-κ") 'nil)
First, I'll show you how I wrote it down as a newbie, and
then today's corrections with some remarks.
Old style
Please don't try this at home:
;;;###autoload
(defun keys-latin ()
(loop for c from ?a to ?z
collect (string c)))
;;;###autoload
(defun keys-greek ()
(loop for c from ?α to ?ω
collect (string c)))
(require 'dash)
;;;###autoload
(defun keys-describe-prefix (letters prefix)
(->> letters
(mapcar (lambda (letter) (concat prefix letter)))
(mapcar (lambda (key)
(cons key
(prin1-to-string (key-binding (kbd key))))))
(mapcar (lambda (binding)
(concat
";; (global-set-key (kbd \""
(car binding)
"\") '"
(cdr binding)
")\n")))
(apply #'concat)))
;;;###autoload
(defun keys-describe-prefixes ()
(interactive)
(with-output-to-temp-buffer "*Bindings*"
(mapcar
(lambda (f-letters)
(mapcar (lambda (prefix)
(princ (keys-describe-prefix f-letters prefix))
(princ "\n\n"))
'("" "C-" "M-" "C-M-")))
(list (keys-latin) (keys-greek)))))
Corrections
Redundant autoloads
Since the entry point of the whole thing is keys-describe-prefixes
, only it needs to be autoloaded.
Once an autoloaded function is called, it will load the whole buffer. So if e.g. keys-latin
is
not being used anywhere else outside this file, it doesn't need an autoload.
Redundant functions
keys-latin
and keys-greek
are actually very small and not used anywhere else. It might be better
to just inline them into keys-describe-prefixes
.
Redundant libraries
Here, dash
is required just for the ->>
macro, which can actually
be obtained from a core library subr-x
as thread-last
.
But even then, it's just better to unwind the whole thing. After that, it becomes clear that
the three consecutive mapcar
s could be folded into a single mapcar
with the help of a let
binding.
After the fold, it starts to look silly, since I'm cons
ing just to
take a car
and cdr
later:
(defun keys-describe-prefix (letters prefix)
(apply #'concat
(mapcar
(lambda (letter)
(let* ((key (concat prefix letter))
(binding
(cons key
(prin1-to-string
(key-binding (kbd key))))))
(concat
";; (global-set-key (kbd \""
(car binding)
"\") '"
(cdr binding)
")\n")))
letters)))
Here's a simplification, removing binding
:
(defun keys-describe-prefix (letters prefix)
(apply #'concat
(mapcar
(lambda (letter)
(let ((key (concat prefix letter)))
(concat
";; (global-set-key (kbd \""
key
"\") '"
(prin1-to-string
(key-binding (kbd key)))
")\n")))
letters)))
I guess that I didn't know about format
function back then, and the fact that "%S"
key is equivalent
to prin1-to-string
:
(defun keys-describe-prefix (letters prefix)
(apply #'concat
(mapcar
(lambda (letter)
(let ((key (concat prefix letter)))
(format ";; (global-set-key (kbd \"%s\") '%S)\n"
key
(key-binding (kbd key)))))
letters)))
Next, the combination (apply #'concat (mapcar ...))
is already implemented in C as mapconcat
:
(defun keys-describe-prefix (letters prefix)
(mapconcat
(lambda (letter)
(let ((key (concat prefix letter)))
(format ";; (global-set-key (kbd \"%s\") '%S)"
key
(key-binding (kbd key)))))
letters
"\n"))
In the end, keys-describe-prefix
turned out to be so small that I could just inline it into
keys-describe-prefixes
.
Final version
Note that here I also replaced mapcar
with dolist
:
;;;###autoload
(defun keys-describe-prefixes ()
(interactive)
(with-output-to-temp-buffer "*Bindings*"
(dolist (letter-group (list
(cl-loop for c from ?a to ?z
collect (string c))
(cl-loop for c from ?α to ?ω
collect (string c))))
(dolist (prefix '("" "C-" "M-" "C-M-"))
(princ (mapconcat
(lambda (letter)
(let ((key (concat prefix letter)))
(format ";; (global-set-key (kbd \"%s\") '%S)"
key
(key-binding (kbd key)))))
letter-group
"\n"))
(princ "\n\n")))))
I hope that this sort of analysis can be useful for people starting to
learn Elisp. And if you have corrections for the final version, do
let me know, I don't mind being schooled, as long as I get better in
the end.
The output of keys-describe-prefixes
can be used to learn some
bindings that you didn't know about, and also as a template to
redefine some bindings that you don't need.
10 Feb 2015
Heh, I guess I was so busy at work that I missed the opportunity to
make an especially good post for the 50-post landmark. At least
according to M-= (count-words-region
) in my _posts'
dired
buffer, this is the 53rd post.
ido-backspace
This echoes to my very second post,
easy helm improvement,
which was about implementing a similar function for helm
.
So here it is:
(ido-mode)
(require 'delsel)
(defun ido-backspace ()
"Forward to `backward-delete-char'.
On error (read-only), quit without selecting."
(interactive)
(condition-case nil
(backward-delete-char 1)
(error
(minibuffer-keyboard-quit))))
(define-key ido-common-completion-map (kbd "DEL") 'ido-backspace)
With this setup, when e.g. calling ido-switch-buffer
, you can cancel
with DEL. This, of course, will also work for
smex and
some
other packages that allow for
ido
completion:
(setq magit-completing-read-function #'magit-ido-completing-read)
(setq lispy-completion-method 'ido)
Note that the define-key
part of the code takes an advantage of a
recent patch to ido
that's available in the trunk. You can find the
old way of binding keys for ido
in an earlier post,
tilde in ido-find-file.
That post actually played a small role in speeding up the patch, for
which we should thank @tarsius.
Note that this behavior will not affect ido-find-file
, since it has its own map.
09 Feb 2015
Intro
As I was releasing lispy 0.23.0, which added
an option for methods of jumping to semantic tags other than helm
, I've discovered
ido-vertical-mode. I also made some changes to
it, so now it looks like this:
The changes are:
- the current text is highlighted in all matches
- the current number of total matches is displayed in the first line
- I also customized the face for the first match and the only match (you can't see that one here)
Helm is still the big gun when it comes to completing lists with hundreds of candidates,
but with the above changes, ido-vertical-mode
comes close to what I need when jumping to tags.
The only thing missing now is that the selected candidate is always on the first line,
even when I press C-n, which isn't very intuitive.
Other completion methods available in lispy-goto
Depending on the modes you have enabled, you can complete with:
helm
ido
ido-vertical-mode
icomplete-mode
icy-mode
- no mode, just plain completion
lispy-eval-other-window
now uses ace-window
This function bound to p is very convenient for debugging
Elisp: it allows you to eval the current sexp in the context of other
window. This is very powerful: you can run and debug your function as
you write it, statement by statement. Previously, this setup was
viable only in a two-window split, because using other-window
, it
wasn't predictable in which window the eval would take place. Now, if
there are more than two windows, aw-select
will select the window
for the eval. This selection will be remembered, so you don't have to
select for the following evals, unless the window configuration
changes.
Outro
There are more changes that you can find in
the release notes
if you're interested. You can find the new ido-vertical
stuff in
my fork, until it
gets merged.
08 Feb 2015
I'll just list the new features from the release notes here.
Keyboard quit
hydra-keyboard-quit
set to "C-g"
means that it's possible to quit an amaranth Hydra
with C-g. You can customize this variable.
:pre
and :post
refinement
:post
and :pre
keys in the body PLIST
can be either a single
sexp or a function name. The function doesn't need to be interactive.
Support for local Hydra heads via :bind
property
Example:
(defhydra hydra-next-error (global-map "C-x")
"next-error"
("`" next-error "next")
("j" next-error "next" :bind nil)
("k" previous-error "previous" :bind nil))
What it does:
- binds C-x ` to
next-error
.
- does not bind C-x j and C-x k
- you can still do C-x `jjkk
Thanks, @ffevotte.
Support for :bind
property in Hydra body
The body, like the heads will recognize the :bind
property in PLIST
.
The heads will inherit it, just like they do with :color
.
The :bind
property can be nil or a lambda of global-set-key
format.
Example:
(defhydra hydra-goto (global-map "M-g"
:bind
(lambda (key cmd)
(bind-key key cmd)))
("g" goto-line "goto-line" :bind global-set-key)
("c" goto-char "goto-char"))
Here, bind-key
will be used to bind goto-char
to M-g c,
since c head has inherited body's :bind
property. Note
that since bind-key
is a macro, it was necessary to wrap it in a
lambda.
However, global-set-key
will be used to bind goto-line
to M-g g, this :bind
property
was overridden in the g head.
Since this commit, it's not possible to pass a lambda instead of the
whole BODY
argument, as was advertised before. Just put it on
:bind
now.
hydra/body
will pass the initial current-prefix-arg
along
Example:
(global-set-key
(kbd "C-z")
(defhydra hydra-vi ()
"vi"
("l" forward-char)
("q" nil "quit")))
Now, C-u C-z l will result in (forward-char 4)
. All the other l
will normally call (forward-char 1)
, unless an additional prefix is given. The previous
behavior allowed only for C-z C-u l to get (forward-char 4)
.
Allow a sexp as head's CMD parameter
Example:
(defhydra hydra-launcher (:color blue)
"Launch"
("h" man "man")
("r" (browse-url "http://www.reddit.com/r/emacs/") "reddit")
("w" (browse-url "http://www.emacswiki.org/") "emacswiki")
("s" shell "shell")
("q" nil "cancel"))
(global-set-key (kbd "C-c r") 'hydra-launcher/body)
Here, r and w heads are using this feature.
Here's what will be generated, if you're interested:
(defun hydra-launcher/lambda-w nil
"Create a hydra with no body and the heads:
\"h\": `man',
\"r\": `(browse-url \"http://www.reddit.com/r/emacs/\")',
\"w\": `(browse-url \"http://www.emacswiki.org/\")',
\"s\": `shell',
\"q\": `nil'
The body can be accessed via `hydra-launcher/body'.
Call the head: `(browse-url \"http://www.emacswiki.org/\")'."
(interactive)
(hydra-disable)
(catch (quote hydra-disable)
(call-interactively
(function
(lambda nil
(interactive)
(browse-url "http://www.emacswiki.org/"))))))
Obsolete declarations
hydra-create
and all old examples in hydra-examples.el are now obsolete.
You can still use them for a short while, but they will be removed soon.
You should take the time to switch from hydra-create
to defhydra
. All the old examples
are provided in the new style in hydra-examples.el. However, they will not be evaluated
through (require 'hydra-examples)
unless you (setq hydra-examples-verbatim t)
beforehand. This is because I have no idea what kind of bindings will work for you, you
should decide yourself. But I am providing you with a template. The number of examples
has also grown to six.