28 Jan 2015
I sure do like to keep my Elisp code nice and indented.
But sometimes the indentation engine just won't listen.
lisp-indent-function
By default, the indentation is handled as if:
(setq lisp-indent-function 'lisp-indent-function)
At least in one case, it looks horrible:
(cl-labels ((square (x)
(* x x)))
(mapcar #'square '(0 1 2 3 4 5)))
;; => (0 1 4 9 16 25)
That's why I have:
(setq lisp-indent-function 'common-lisp-indent-function)
which leads to this indentation:
(cl-labels ((square (x)
(* x x)))
(mapcar #'square '(0 1 2 3 4 5)))
;; => (0 1 4 9 16 25)
Ah, much better. The indentation does change a bit in other places as
a result of this. To my experience, it can be fixed on a case-by-case
basis by declaring the indent level for the offending function or
macro.
Declaring indent level
Here's how it looks like
(defmacro lispy-save-excursion (&rest body)
"More intuitive (`save-excursion' BODY)."
(declare (indent 0))
`(let ((out (save-excursion
,@body)))
(when (bolp)
(back-to-indentation))
out))
By default, functions behave as if their indent
was declared to nil
.
Zero here means that we expect zero arguments on the same line, so that this indentation follows:
(lispy-save-excursion
(lispy--out-forward arg)
(backward-list)
(indent-sexp))
The indentation above is just like the one that the original save-excursion
has.
Note that if I hadn't declared the indent, it would look like this:
(lispy-save-excursion
(lispy--out-forward arg)
(backward-list)
(indent-sexp))
The impact is much larger for statements that require an indent
of 1:
Compare the proper thing:
(lispy-dotimes arg
(when (= (point) (point-max))
(error "Reached end of buffer"))
(forward-list))
to this horror:
(lispy-dotimes arg
(when (= (point) (point-max))
(error "Reached end of buffer"))
(forward-list))
Outro
I've shown two things you can try if you find your Elisp indentation
annoying. If you know of more, let me know.
27 Jan 2015
Well, "refactoring" is for the Java people, I simply rename things.
Just today, I had to do a big rename operation related to the
release of lispy 0.22.0.
Below, I'll share some functions and packages that I used for that.
My Github setup
Here's it (tree -da -L 1
) is:
.
├── .cask
├── gh-pages
├── .git
└── images
I've cloned my gh-pages
branch into its own git repository inside
the original lispy
repository. This way, I can rename functions in
the code and the documentation simultaneously.
Declaring functions obsolete in Elisp
It can be done like this:
(define-obsolete-function-alias 'lispy-out-forward
'lispy-right "0.21.0")
(define-obsolete-function-alias 'lispy-out-backward
'lispy-left "0.21.0")
(define-obsolete-function-alias 'lispy-out-forward-nostring
'lispy-right-nostring "0.21.0")
After version "0.21.0"
hits, any time you call lispy-left
by its
now obsolete alias lispy-out-backward
, you'll get a message:
`lispy-out-forward' is an obsolete command (as of 0.21.0); use `lispy-right' instead.
So now, since I'm releasing version "0.22.0"
, I can remove even the
alias declarations. I gave people one week of warnings to adjust (just
rename to the new name) any of their code that's calling the currently
obsolete functions.
Renaming obsolete functions in the documentation
Taking advantage of the repository setup, I can:
Step 1: call rgrep
rgrep
is a fine function, I wonder why it's not bound by default; I
bind it to C-<, taking advantage of my weird key
mappings (I'm actually pressing the C-;-l physical keys).
It requires 3 inputs:
- Symbol to search for: I call it with the point positioned on
the symbol that I want to rename in the code, in the middle of the
define-obsolete-function-alias
tag, so rgrep
picks up the
symbol name as the default and I just type RET to select
it.
- File pattern:
*.el
, the current file extension is the
default. I type *
RET, since I want to match the org
and html
files as well.
- Base directory: this directory will be will be recursively
searched for files that match the file pattern; the default
~/git/lispy/
is fine here, RET.
Step 2: call wgrep
wgrep is a fine
package, I wonder why it's not more popular. Since it's so similar to
wdired
(one of the best things since sliced bread, btw), I like to bind
the starter to C-x C-q and the finisher to C-c C-c
as well:
(eval-after-load 'grep
'(define-key grep-mode-map
(kbd "C-x C-q") 'wgrep-change-to-wgrep-mode))
(eval-after-load 'wgrep
'(define-key grep-mode-map
(kbd "C-c C-c") 'wgrep-finish-edit))
Step 3: call iedit
iedit is an amazing package, it's crazy-good.
Here's how I bind it, since once iedit-mode
is on, you can move to the next occurrence with C-i:
(global-set-key (kbd "C-M-i") 'iedit-mode)
In order to change really every occurrence in the buffer, I need to
mark the thing that I want to change, before C-M-i.
Otherwise, iedit
will automatically add symbol bounds (a nice
feature, actually), so that e.g. =lispy-out-forward=
will not match
lispy-out-forward
.
Finally, I interactively, char-by-char, rename e.g. lispy-out-forward
to lispy-right
.
The experience is similar to the popular multiple-cursors,
which I also like to use, just for different purposes.
Step 4: exiting
When I'm done:
- I exit
iedit-mode
with C-M-i.
- I exit
wgrep
with C-c C-c
- I save all affected files if I want, since they aren't saved yet and
it's still possible to revert everything.
Outro
Woah, that's a lot of steps!
True, but do note that all three tools can be used on their own for
various other tasks. See for instance my other
"refactoring" demo, that uses
iedit-mode
to unbind a let
-bound variable in Elisp (should also
work for Common Lisp, since the syntax is the same).
There's beauty and utility in having such composable tools. A lot of
the time, it's better than to just have one "Rename" button. For
instance, when only one buffer is involved, the rgrep
-wgrep
step
can be skipped and I can rename stuff with iedit-mode
only.
Or, when the playground for renaming is less than a buffer, I can:
- C-x nd -
narrow-to-defun
or C-x nn -
narrow-to-region
(both are equivalent to N in lispy
)
iedit-mode
- C-x nw -
widen
(W in lispy
)
26 Jan 2015
I'd like to share a little snippet that I like to use instead of a
plain M-s o (occur
).
smarter-than-the-average-occur
(defun occur-dwim ()
"Call `occur' with a sane default."
(interactive)
(push (if (region-active-p)
(buffer-substring-no-properties
(region-beginning)
(region-end))
(let ((sym (thing-at-point 'symbol)))
(when (stringp sym)
(regexp-quote sym))))
regexp-history)
(call-interactively 'occur))
It will offer as the default candidate:
- the current region, if it's active
- the current symbol, otherwise
It's pretty good, since I actually want the default candidate in 95%
of the cases. Some other functions, such as rgrep
are smart like
this out-of-the-box.
A Hydra that goes nicely with occur
Since occur
, like some other functions (grep
, rgrep
, compile
),
works with next-error
, it's possible to use this
Hydra to navigate the occur
matches:
(hydra-create "M-g"
'(("h" first-error "first")
("j" next-error "next")
("k" previous-error "prev")))
Hydra was introduced in an
earlier post,
and is available in GNU ELPA.
What goes around comes around
I usually do a quick internet search to see where I got the code that
I'm posting here, reason being that it's nice to list the source, as
well as there could be more advanced stuff out there on the same
topic.
I saw a few results for "occur-dwim"
so I thought that I just
copy-pasted it from somewhere. Slight disappointment turned into a
small sense of pride, when I found that the source of the copy-pastes
was my old Stack Overflow answer:)
25 Jan 2015
Currently, MELPA hosts over 2250
packages. That's
quite a large number, although it's still feasible to try most of the
popular ones. In comparison,
gives me the number 50250
. I don't recall calling apt-cache
pkgnames
ever before, while I call package-list-packages
all the
time. I suppose that this will have to change when the number of
packages grows further.
I usually explore packages just as they show up as new in package-list-packages
.
I'll share the two tools that I use for exploring them.
Smex
Smex is an M-x enhancement for Emacs.
In addition to executing commands faster, it gives you an option to jump to the current command's definition
with M-..
Here's how I bind smex
:
(require 'smex)
(global-set-key "\C-t" 'smex)
(defun smex-prepare-ido-bindings ()
(define-key ido-completion-map
(kbd "C-,") 'smex-describe-function)
(define-key ido-completion-map
(kbd "C-w") 'smex-where-is)
(define-key ido-completion-map
(kbd "C-.") 'smex-find-function)
(define-key ido-completion-map
(kbd "C-a") 'move-beginning-of-line)
;; (define-key ido-completion-map "\C-i" 'smex-helm)
;; (define-key ido-completion-map " " 'smex-helm)
)
I don't feel bad at all for unbinding transpose-chars
,
C-t is such a prime binding, that only the best commands
can deserve it. And since I touch-type, it's easier for me to hit
M-DEL and retype the word when I notice a mistake, than to
carefully navigate to the mistake location and call transpose-chars
with surgical precision. On the other hand, the number of Emacs
packages is only going to grow, so it becomes less and less feasible
to bind everything. Instead, seldom used stuff can be called though
smex
. For instance, I don't bind
markdown-toc or
speed-type and call them via
C-t instead.
Back to the point, C-t ... C-.
(smex-find-function
) allows me to quickly jump to the source of the
package that I want to explore.
Once I'm in the source code, I can quickly get an overview with
g (lispy-goto
) from
lispy. It uses CEDET's semantic
module to get a list of tags in current directory with
helm for completion. In my
opinion, it's much prettier than helm
's own helm-semantic
. It also
explores more tags for Emacs Lisp, for instance it will capture
use-package
and global-set-key
tags.
A new cute feature that I added recently is a different background for
user-visible functions, i.e. the ones that are callable by
M-x or smex
. Here's how it looks like, for the
speed-type
package (interactive functions have a purple background):
If the image text looks too small, you can right click / view image in
your browser. Here, I can see at a glance, that there are three
commands that I can call. I can also see a few variables, two of them
don't include --
, so they're probably customizable.
24 Jan 2015
I saw this question today at Stack Overflow -
Emacs: How to enable toolbar mode and menubar mode only under a certain mode?.
Myself, I never use the menubar or the toolbar. But maybe it's just
because it's useless in the modes to which I'm used to. What if there
was an option to have the menu bar on just for a few select
modes? For instance, if I decided that I want to learn Haskell
tomorrow, I wouldn't mind to have the menu bar on for a while,
especially if there was some useful stuff there in the menu.
The Code
I'm not aware of a convenient hook for this job; I tried
after-change-major-mode-hook
, and it doesn't work when switching
windows. The universal post-command-hook
would be totally lame for this task.
Then it occurred to me, that select-window
has to be called
eventually in most circumstances. Even
ace-window calls
select-window
.
This is what I came up with so far:
(defvar menubar-last
(make-ring 20))
(ring-insert menubar-last "dummy")
(defadvice select-window (after select-window-menubar activate)
(unless (equal (buffer-name) (ring-ref menubar-last 0))
(ring-insert menubar-last (buffer-name))
(let ((yes-or-no
(if (memq major-mode '(r-mode lisp-interaction-mode))
1 -1)))
(menu-bar-mode yes-or-no)
(tool-bar-mode yes-or-no))))
The Ring trickery
It's necessary at least for magit
. Doing, for instance,
ll operation switches windows many times.
So I added a check that the current buffer isn't selected twice.
In Emacs, a ring is like a stack with a limited length: you push onto
the head, and if the stack becomes too large, the stuff gets removed
from the tail. Perfect for debugging this code, since I don't care
what happened 20 window switches ago, I'm only interested in the most
recent state. One small annoyance is that the following code will
throw, if the ring is empty:
(ring-ref menubar-last 0)
I would prefer if it just returned nil
instead. Hence the trick of
initializing menubar-last
with a dummy item. Note that this
annoyance propagates to other areas: (current-kill 0)
will throw if
you haven't yet copied text since Emacs started.
Outro
I'm not sure if this code is more annoying than useful, but, at least,
I learned something new while writing it, and I hope you did too. If
you know of a better way to accomplish the original task, please do
share.