(or emacs irrelevant)

Hydra 0.10.0 is out

As usual, I'll just re-state the release notes, while maybe adding a bit of flavor.

New features

Define Hydra heads that don't show up in the hint at all

This can be done by setting the head's hint explicitly to nil, instead of the usual string. For instance, if you always tend to bind the arrows to hjkl, there's no point to show a hint for them.

Use a dedicated window for Hydra hints

Since version 0.10.0, setting hydra-lv to t (the default setting) will make it use a dedicated window right above the Echo Area for hints. This has the advantage that you can immediately see any message output from the functions that you call, since Hydra no longer uses message to display the hint. You can still have the old behavior by setting hydra-lv to nil.

How it looks like:

hydra-lv

Here, an error was triggered by previous-line and the message is displayed without interrupting the hint.

Allow duplicate functions in heads

Duplicate functions will be concatenated in the hint. This was already covered in yesterday's post

Add option to font-lock defhydra

If you want to nicely font-lock your defhydra statements, just add this to your config:

(require 'hydra)
(hydra-add-font-lock)

Additionally, defhydra is now to be indented as a defun, so it will be indented like this:

(defhydra hydra-goto-line (global-map "M-g"
                           :pre (linum-mode 1)
                           :post (linum-mode -1)
                           :color blue)
  ("g" goto-line "line")
  ("c" goto-char "char"))

Note that the indentation of the body argument is as if it was data and not code, i.e. the proper one. As you see, I even added defhydra to the blog's Pygments list for Elisp.

Incompatible changes

The macro hydra-create, as well as the variables that were supposed to be used with it (hydra-example-text-scale, hydra-example-move-window-splitter, hydra-example-goto-error, hydra-example-windmove) were removed. All the functionality is still there in hydra-examples.el with the better defhydra macro.

Outro

I hope that you like the new changes. And if you're byte-compiling your code that uses defhydra, don't forget to re-compile. Also, the latest version of ace-window will ignore *LV* buffer while switching, as it did for *Calc Trail* before.

Binding one function multiple times in Hydra

Here's a recent feature that's now available in hydra:

(defhydra hydra-zoom (global-map "<f2>")
  "zoom"
  ("g" text-scale-increase "in")
  ("l" text-scale-decrease "out")
  ("0" (text-scale-set 0) "reset")
  ("1" (text-scale-set 0) :bind nil)
  ("2" (text-scale-set 0) :bind nil :color blue))

Here, the entry points are <f2> g, <f2> l, and <f2> 0, the others aren't bound in the global map. You can also have the same function in both red and blue versions. Here's how the hint will look like:

hydra-multi

The multiply-defined functions will be neatly grouped together.

If you remember, (text-scale-set 0) uses a sexp syntax for a Hydra head: it will be wrapped in (lambda () (interactive) ...) automatically.

Doing sudo stuff with tramp

Somehow, I accumulated a few files owned by root that should have been owned by me. Below, I'll describe a solution to get rid of them based on dired and tramp.

Step one: open the directory with sudo privileges

I'll obviously need the sudo privilege to change the owner of a file owned by root. Might as well start with that.

Thankfully, I do have a shortcut:

(defun sudired ()
  (interactive)
  (require 'tramp)
  (let ((dir (expand-file-name default-directory)))
    (if (string-match "^/sudo:" dir)
        (user-error "Already in sudo")
      (dired (concat "/sudo::" dir)))))
(define-key dired-mode-map "!" 'sudired)

The function above will open the current directory in sudo mode. I decided to bind it to !, since the default & seems strictly better than !. The function will ask you for the password once. Afterwards, you can open other directories without having to enter the password.

Step two: find-dired

So now, while in sudo mode of the directory in question:

  1. Call M-x find-dired. It's not bound by default, and I don't bind it myself since it's quite situational (I do bind find-name-dired to F in dired-mode though).
  2. It prompts you for the directory. I just press RET, since the default one is what I need.
  3. Next, it prompts for find args. These are the arguments to the UNIX command find. Here, I need to pass -user root.
  4. The command has finished and I can see the results. I can select some of them by pressing m (dired-mark) a few times, or mark them all at once with t (dired-toggle-marks).
  5. Finally, call O (dired-do-chown) on the marked files and enter oleh.

Done. I'm sure that the admin types can cook up some find-exec-chown combo to do the same thing, but the find-dired approach is both simpler and more flexible, since I can confirm and edit the list of files being changed.

Semimap - .Xmodmap with semicolon as an additional modifier

Today, I'll share my keyboard layout, based on QWERTY, that facilitates all tech-related activities, especially Emacs. I've been using a version of this for about three years now, and only a single key has been changed in the last two years. Without further ado, here it is:

semimap

The most visible change

I've made ; stop inserting ; and instead act as Mode_switch: a modifier similar to control or meta.

  • I'm pressing ;-j to enter ;
  • I'm pressing ;-d to enter :

So I'm giving up the ability to press ; with a single key, and remapping : from one two-key combination to another two-key combination.

In return, I'm getting a QWERTY with all these wonderful shortcuts.

The RSI-savers

The general ones are:

  • RET on ;-v (symmetric with C-m)
  • DEL on ;-o

If you're writing a bunch of C++, _ on ;-s is very good.

Likewise, - on ;-a is great for LISP.

For LaTeX, \ on ;-w is good since I switched from a keyboard that had it near z to a keyboard that has it near RET.

I'm guessing ~ on ;-t isn't bad for shell, but I don't use it that much.

The basic remaps

These are just char-for-char remaps. They are composable, for instance I have dired-jump on C-:, which means that I'm pressing C-;-d.

Some math symbols are mnemonic: e-equal, l-lesser, g-greater. If you're using lispy, it might become more clear for you why I've put lispy-slurp on > and lispy-barf on <. Although pressing these keys with a shift isn't the worst thing, having them on the home row is pretty cool.

The pairs

Here are the translations meant for pairs: q - θ, f - φ, r - ρ, c - σ. These are meant for Emacs and .inputrc. In both cases, e.g. φ should insert () and go backward one char. Here's my .inputrc:

set input-meta on
set output-meta on
set convert-meta off
"θ":""\C-b""
"ω":"'\C-b'"
"\e\C-l":"\C-e | less\C-m"
"υ":">\C-b<"
"σ":"}\C-b{"
"φ":")\C-b("
"ρ":"]\C-b["

set completion-ignore-case on

With this .inputrc, it's like I have some kind of electric mode in my bash. In Emacs, the pairs are bound in a straightforward way:

(defun ins-brackets ()
  (interactive)
  (cond ((eq major-mode 'term-mode)
         (term-send-raw-string "[]")
         (term-send-raw-string "^B"))
        ((region-active-p)
         (lispy--surround-region "[" "]"))
        (t
         (insert "[]")
         (backward-char))))
(global-set-key "ρ" 'ins-brackets)

For LISP, I use lispy-specific bindings:

(define-key lispy-mode-map (kbd "φ") 'lispy-parens)
(define-key lispy-mode-map (kbd "σ") 'lispy-braces)
(define-key lispy-mode-map (kbd "ρ") 'lispy-brackets)
(define-key lispy-mode-map (kbd "θ") 'lispy-quotes)
(define-key lispy-mode-map (kbd "χ") 'lispy-right)

The rest of the Greek chars

These are bound to various Emacs functions. In fact, I initially started with all Greek chars, and bound e.g.:

(global-set-key (kbd "ε") "=")

When these remap-type bindings became mature, I've put them directly into .Xmodmap, which gave the advantage of being able to compose the keys.

These bindings are as good and usable as the control plus lower case chars. Use them wisely and you can get a very ergonomic setup. Some ideas:

  • switching buffers on ;-h
  • switching windows on ;-n
  • jumping to bookmarks on ;-m

The shifted digits

I find it much easier to press e.g. ;-4 to insert $, rather than S-4. I also restored the justice of 0 coming before 1 by mapping ;-` to 0.

Outro

I hope that you'll find some use and enjoyment in these ramappings, if you're not afraid to experiment a little. The cost of losing the semicolon is pretty minor, the bigger issue is in finding the unmodified QWERTY extremely sluggish after using this approach.

Elisp linting options

I discovered today that I was using declare-function in a wrong way. So I'll share how to use it properly.

declare-function

I was just using it to shut up the byte compiler's "not known to be defined" warning. Turns out that it can also be used to check if the functions actually exist in the file to which they point to. You can use check-declare-file to check one file, or check-declare-directory to recursively check the whole directory.

Here's an example output:

Warning (check-declare): helm-info.el said `Info-goto-node' was defined in info.el.gz: arglist mismatch
Warning (check-declare): helm-info.el said `Info-find-node' was defined in info.el.gz: arglist mismatch
Warning (check-declare): helm-plugin.el said `Info-goto-node' was defined in info.el.gz: arglist mismatch
Warning (check-declare): helm-plugin.el said `Info-find-node' was defined in info.el.gz: arglist mismatch
Warning (check-declare): helm-emms.el said `with-current-emms-playlist' was defined in emms.el: function not found
Warning (check-declare): projectile.el said `ggtags-ensure-project' was defined in ggtags.el: file not found
Warning (check-declare): projectile.el said `ggtags-update-tags' was defined in ggtags.el: file not found
Warning (check-declare): async-bytecomp.el said `package-desc-reqs' was defined in package.el.gz: function not found

My mistake was assuming that the FILE argument of declare-function was somehow related to require. But of course it had to be simply the name of the file that contains the said function. If the referenced file is in an external package, e.g. (declare-function cider-repl-return "ext:cider-repl") can be used.

checkdoc

This one is actually very useful once you embrace it. It will tsk-tsk you until all your functions are documented. And since you're already writing a docstring, might as well make it good. Sometimes this leads me to removing a function that's called only once, just so that I don't have to document it.

byte-compile-file

Leaving the obvious for last. This will speed up the code in addition to checking for errors. In dired you can use:

  • several m (dired-mark) followed by B (dired-do-byte-compile).
  • a single *% (dired-mark-files-regexp) el$ followed by B.

I even have a compile target in lispy's Makefile:

compile:
    $(CASK) exec $(EMACS) -batch $(LOAD) -l lispy-test.el -l compile.elt

Here are the contents of compile.elt:

(require 'check-declare)
(setq check-declare-ext-errors t)
(setq files '("lispy.el"
              "lispy-inline.el"
              "le-clojure.el"
              "le-scheme.el"
              "le-lisp.el"))
(mapc #'byte-compile-file files)
(ert t)
(apply #'check-declare-files files)