(or emacs irrelevant)

Introducing Swiper

I like the idea of helm-swoop, but it somehow has minor annoyances that stop me from using it, like automatic helm-input, and especially the circular candidates. Or maybe it's just NIH talking. Anyway, I'm rolling my own, and it's called swiper.

swiper.png

Swiper in action

Here's how it looks like:

swiper-1.png

As you can see, the search string "dec fun pro" is transformed into a regex "\\(dec\\).*\\(fun\\).*\\(pro\\)", and each group in the matches is highlighted with a different face. This is quite similar to the way re-builder does it. In fact it's possible to use swiper as a poor man's re-builder (since it matches only single lines).

I'm doing my own matching this time, as the part-swapping behavior of helm-match-plugin is more annoying than useful.

Also note that:

  • The whole *swiper* buffer is fully syntax highlighted.
  • The appropriate parts of the matches are highlighted as well.

Swiper in a dired buffer

Here's another screenshot:

swiper-2.png

It appears that helm is ignoring the display of file attributes, since they have a sort of invisible property set. I kind of like this behavior.

See how the faces are recycled

I've defined only 4 faces currently (foxes aren't unicorns, the palette is quite limited), so they loop about if you have many groups:

swiper-3.png

Yup, the technology is there. Although a powerline theme for the helm mode line is still missing.

lispy point history

It was kind of disappointing to summarize in the last post that there weren't many new features for lispy 0.24.0. So I thought long and hard and came up with something quite obvious: since lispy offers commands to quickly manipulate point and mark, it should offer one to quickly restore them.

Here's the bulk of the new code:

(defvar lispy-pos-ring (make-ring 200)
  "Ring for point and mark position history.")
(ring-insert lispy-pos-ring 1)

(defun lispy--remember ()
  "Store the current point and mark in history."
  (if (region-active-p)
      (let ((bnd (lispy--bounds-dwim)))
        (unless (equal bnd (ring-ref lispy-pos-ring 0))
          (ring-insert lispy-pos-ring bnd)))
    (unless (eq (point) (ring-ref lispy-pos-ring 0))
    (ring-insert lispy-pos-ring (point)))))

(defun lispy-back ()
  "Move point to a previous position"
  (interactive)
  (if (zerop (ring-length lispy-pos-ring))
      (user-error "At beginning of point history")
    (let ((pt (ring-remove lispy-pos-ring 0)))
      (if (consp pt)
          (lispy--mark pt)
        (deactivate-mark)
        (goto-char pt)))))

Here, I'm using lispy-pos-ring made with make-ring to store the last 200 point and mark positions. In Elisp, a ring is basically a stack backed by a vector. When the vector space overflows, the older stuff is overwritten.

In this ring I store each time either the point, or a cons of the point and mark if the region is active. This way possible to restore the region even if it was deactivated several movement commands ago.

I've put lispy--remember into the most used navigation commands:

  • j - lispy-down
  • k - lispy-up
  • h - lispy-left
  • l - lispy-right
  • f - lispy-flow
  • i - lispy-mark-car (when the region is active)
  • a - lispy-ace-paren

And lispy-back is now bound to b. The previous binding of b - lispy-store-region-and-buffer is now bound to xB (quite close to B - the binding for lispy-ediff-regions).

I really like the new command, it's especially useful to reverse h and l. Previously, they could be reversed with f, but that can get annoying if you have to press it many times. This results in a much more relaxed editing - I know that whatever I press, I can restore the point position quickly if needed.

This feature is added to the already present list of safeguards:

  • j and k are guaranteed not to exit the parent list.
  • > and < are guaranteed not to exit the parent list.
  • C reverses itself.

Here's a recipe to copy the third item of the current list and move the point back: 4mnb. You can use it to copy a function's docstring if you're in Elisp. You'll need 3mnb for Clojure, since it weirdly has the docstring before the arguments. I think it's pretty cool: by typing 3mnb I'm basically calling (kill-new (caddr (current-sexp))) on my own code.

lispy 0.24.0 is out

The last release was a month ago, and there have been 70 commits to master since then. If you're not familiar with lispy, see my intro post for version 0.21.0. I'll just copy the release notes here, while adding a few things.

Fixes

  • DEL behaves properly after a string and one space.
  • C-k works better for expressions preceded with "#".
  • 3 should not add a space when there is one already.
  • # will not add a space after a comma.
  • C-j works better in comments after a quote.
  • lispy--eval-elisp-form first arg is now named lispy-form instead of form. It was impossible to evaluate an unrelated form variable with the previous behavior.
  • F again works properly when jumping to a jar (e.g. to defn) from Clojure source.
  • C-k won't call delete-region in some obscure branches.

Enhancements

  • P (lispy-paste) will add a newline when called from start of line. This way, nP becomes equivalent to c (lispy-clone). Of course, it's more flexible: you can do e.g. nkP.
  • xb (lispy-bind-variable) now works on regions as well. Use it to bind the current sexp or region as a let-bound variable: it will put you in iedit. When you're done with iedit, press M-m (lispy-mark-symbol) to exit iedit. If you need to move the let binding around, use a combination of C (lispy-convolute) and h (lispy-left).
  • g will ignore loaddefs.el for Elisp.
  • M-m works better in unbalanced buffers, which should be an rare thing.
  • add defhydra to lispy-tag-arity: now g will recognize defhydra statements.
  • The tag logic was improved to do less parsing.
  • lispy-outline was updated to match the standard ^;;; outline regex. Try pressing I in e.g. org.el, it's quite beautiful.
  • All lispy-eval functions will preserve the match data.
  • > will delete the extra whitespace while slurping.
  • Added undercover/Coveralls test coverage report.
  • H (lispy-ace-symbol-replace) is now a Hydra: type h to delete more, type u to undo.
  • Q (lispy-ace-char) now uses avy to jump. This change allows to cover this function with a test.

New features

p can now iterate dolist variables in Elisp.

(defun range (a b)
  (message "called range")
  (number-sequence a b))
(dolist |(i (range 1 3))
  (message "i=%d" i))

Pressing p with point where | is, will

  • call range and set i to 1
  • set i to 2
  • set i to 3
  • set i to nil
  • call range and set i to 1

This is another step toward edebug-less debugging, adding to special behavior for let, cond and labels. Remember that you can drop out of edebug with Z (lispy-edebug-stop). This function will take the current function arguments that edebug provides, store them in top-level, and exit edebug. This is really cool for setting up entry conditions for a function that you want to debug, or even a function with an empty body that you want to write. Why am I so eager to exit edebug? Because it puts the code in read-only mode, which is quite restrictive.

Incompatible changes

  • lispy-helm-columns is now a list '(60 80). The first number is the width of the tag name column, the second number is the width of both tag name and tag file. The tag name column is left-aligned, while the file column is right-aligned.
  • j and k should now move to outline when at beginning of comment. The previous behavior was to look for the first sexp in the direction. You can still do that with f.
  • I (lispy-shiftab) is now a two-way cycle, instead of three-way, like org-mode. The contents can be obtained with C-u I or C-u C-TAB.

Outro

It seems that lispy is winding down feature-wise, which is a good thing, because I'm almost out of keys - there's only Y and U left.

Possible next steps would be to improve the test coverage (currently 48%) and the documentation. Perhaps I'll try to implement some automation for generating the function reference, or learn some texinfo and write an actual manual, see how ox-texinfo holds up.

I hope that you'll grow to enjoy lispy as much as I do. Happy hacking!

org-mode block templates in Hydra

Here's a new Hydra for you:

(defhydra hydra-org-template (:color blue :hint nil)
  "
_c_enter  _q_uote    _L_aTeX:
_l_atex   _e_xample  _i_ndex:
_a_scii   _v_erse    _I_NCLUDE:
_s_rc     ^ ^        _H_TML:
_h_tml    ^ ^        _A_SCII:
"
  ("s" (hot-expand "<s"))
  ("e" (hot-expand "<e"))
  ("q" (hot-expand "<q"))
  ("v" (hot-expand "<v"))
  ("c" (hot-expand "<c"))
  ("l" (hot-expand "<l"))
  ("h" (hot-expand "<h"))
  ("a" (hot-expand "<a"))
  ("L" (hot-expand "<L"))
  ("i" (hot-expand "<i"))
  ("I" (hot-expand "<I"))
  ("H" (hot-expand "<H"))
  ("A" (hot-expand "<A"))
  ("<" self-insert-command "ins")
  ("o" nil "quit"))

(defun hot-expand (str)
  "Expand org template."
  (insert str)
  (org-try-structure-completion))

I bind it for myself like this:

(define-key org-mode-map "<"
  (lambda () (interactive)
     (if (looking-back "^")
         (hydra-org-template/body)
       (self-insert-command 1))))

This means that when I press < from the start of the line, a Hydra will be called instead of inserting <, otherwise < will be inserted.

As the default insert method for org-mode blocks is already pretty convenient, this Hydra is more of an illustration than anything, especially of the new :hint nil feature.

Just to remind you, each head has four placeholders:

  • key binding
  • body
  • hint
  • plist

When a Hydra is active, it will show its doc in the echo area in the bottom of the frame. This doc is composed of two parts: the body doc and the heads' doc. The body doc you specify yourself, the heads' doc is built by concatenating the key binding and the hint for each head into a (single) line.

If you don't specify a hint for a head, it's assumed to be ""; this head's binding will still be in the heads' doc. If you don't want a head's binding to be in the heads' doc, set the hint to nil. This is commonly done because a head is already documented in the body doc. It can sometimes become tedious to set all the hints to nil, for instance in the Hydra above, I would need to do it 13 times. Hence the :hint nil shortcut.

Here's how it looks like:

hydra-org-template.png

I'm not a Scrabble pro: clash for word score 10 is my result, although a longer word would break the nice column layout. The first two columns contain begin/end templates, while the third one contains the one-line templates.

Select the previous window with ace-window

It's strange that I haven't implemented this feature before, as it's quite a common usage pattern:

  1. You select a window with ace-window.
  2. You do some stuff there.
  3. You want to return the previous window.

aw-flip-window

In the step 3, you have to go though the whole aw-keys dispatch, only to select a window which can be pre-determined. Not any more, if you call aw-flip-window:

(defun aw-flip-window ()
  "Switch to the window you were previously in."
  (interactive)
  (aw-switch-to-window (aw--pop-window)))

So now, you could have a grid of 10 windows, select one of them with ace-window, and switch indefinitely between it and the previous window with ace-flip-window, while ignoring the other 8.

aw-ignored-buffers

Remember that if you have some window that you never want to switch to with ace-window, you can add it to aw-ignored-buffers:

(defcustom aw-ignored-buffers '("*Calc Trail*" "*LV*")
  "List of buffers to ignore when selecting window."
  :type '(repeat string))

It's not a big deal, but it's convenient at least for this scenario:

  1. I start with one active window.
  2. M-x calc; now I have three windows.
  3. I can toggle back and forth between calc and the main window with ace-window without having to type aw-keys, since *Calc Trail* is ignored, so that makes only two total windows.

Selecting last window during the ace-window dispatch

This is a really cool feature, in my opinion: for all three actions - aw-select-window, aw-swap-window, and aw-delete-window, you can select the previous window as a target with the same key n. This is, of course, customizable:

(defcustom aw-flip-keys '("n")
  "Keys which should select the last window."
  :set (lambda (sym val)
         (set sym val)
         (setq aw--flip-keys
               (mapcar (lambda (x) (aref (kbd x) 0)) val))))

So you could have a whole list of bindings that select the previous window during the aw-keys dispatch. This is cool because there's no visual feedback necessary, so this binding can be easily added to the muscle memory. The bindings don't necessarily need to be single keys, anything with one chord, e.g. C-f, is acceptable.

Here's how I've set it up for myself:

(global-set-key "ν" 'ace-window)
(csetq aw-flip-keys '("n" "ν"))

This means that:

  • I can select the previous window with νν - a double call to ace-window.
  • I can swap with the previous window with ψνν, ψ calls universal-argument for me.
  • I can delete the previous window with ψψνν.

Outro

Thanks to @luciferasm for the idea, I hope you'll enjoy the new feature.