Indenting with Parinfer
A recent change to Planck has landed making use of Parinfer to improve the form entry experience, especially when entering multi-line forms.
When you enter a multi-line form into Planck, it currently doesn't automatically provide indentation for newly entered lines. For example, say you entered the form (map inc [1 (+ 2 3) 4])
, but where each number is entered on its own line. What you get (unless you type extra spaces yourself) is:
cljs.user=> (map inc [1
#_=> (+ 2
#_=> 3)
#_=> 4])
(2 6 5)
This drives me a little crazy. Indentation aids in readability, so ideally the extra needed spaces would be automatically supplied by Planck.
Parinfer can be used to achieve this. The rough idea is:
- When the return key is pressed, use Parinfer to “balance” the entered form, by adding the closing delimiters. So,
(map inc [1
would be turned into(map inc [1])
. This is done with Parinfer in “indent” mode. - Then the newline character is injected at the position the cursor was (after the
1
, but before the closing])
, letting Parinfer automatically appy indentation. This is done with Parinfer in “paren” mode. - The leading space characters are extracted from the last line of this result, and are then automatically typed on behalf of the user within Planck's UI.
With these changes, the above example ends up looking like:
cljs.user=> (map inc [1
#_=> (+ 2
#_=> 3)
#_=> 4])
(2 6 5)
This has landed in Planck 1.10. Here is a demo of it in action:
One interesting corner case that needed to be dealt with is when a user pastes a multi-line form into Planck. In that case, the form is already indented, and we'd ideally just turn off Planck's Parinfer-based indentation. Planck detects that a paste is being done by observing the character input rate. If the number of milliseconds between a return being pressed and its immediate predecessor is smaller than a threshold, indentation is disabled. This works surprisingly well in practice, and it has a minor side benefit in that it disables indentation completely when pasting gigantic multi-line forms (which might be computationally expensive).
Perhaps one day in the future, Planck may make full use of Parinfer, allowing unconstrained entry and editing of multi-line forms. But, it is cool that the Parinfer library can be used in a way like this to achieve a use case outside of its originally-intended design.
Parinfer is a great library. It has been ported to multiple languates and being used in multiple places. I'd recommend checking it out!