Structural editing

Structural editing changes code by whole forms: it moves a bracket past the next form, swaps two forms, or deletes one, and the brackets stay balanced. Jonquil's set follows Calva's paredit.

Every example on this page is run by Jonquil's test suite against the real commands, so each row shows what the command does today. In an example ‸ marks the caret and «…» a selection.

  • Where to find them. All of them are under Edit > Structure in the menus and in the command palette (Cmd/Ctrl+Shift+P).
  • Keys. None of them has a key until you turn on the Calva paredit key set in Preferences. The Key column below shows that set's keys. They use Control on every system, a Mac included, where Alt is Option. A command that says none has no key in the set, and Preferences can give it one.
  • Your other keys stay as they are on the default key set. The Calva set takes a few keys that normally do something else, listed under Keys the Calva set changes. There is no strict mode that refuses an unbalanced delete.
  • Parinfer works with or without these commands. With Parinfer on, the indentation follows the bracket a command moved, in every mode.
  • Undo. Each command is one undo step.
  • Unfinished code. Inside a list that isn't closed yet, these commands do nothing.

Slurp and barf

CommandKeyBeforeAfter
Slurp forward: the list takes in the next formCtrl+Alt+Right; Linux: Ctrl+Alt+.(a‸) b c(a‸ b) c
Barf forward: the list gives up its last formCtrl+Alt+Left; Linux: Ctrl+Alt+,(a‸ b)(a‸) b
Slurp backward: the list takes in the form before itCtrl+Alt+Shift+Lefta (‸b)(a ‸b)
Barf backward: the list gives up its first formCtrl+Alt+Shift+Right(a ‸b)a (‸b)

Raise, splice and wrap

CommandKeyBeforeAfter
Raise the form at the caret over the list around itnone(a ‸b c)‸b
Splice: remove the brackets around the caretCtrl+Alt+S(a ‸b)a ‸b
Wrap the form at the caret in ()Ctrl+Alt+Shift+P‸a b(‸a) b
Wrap the form at the caret in []Ctrl+Alt+Shift+S‸a b[‸a] b
Wrap the form at the caret in {}Ctrl+Alt+Shift+C‸a b{‸a} b
Wrap the form at the caret in #{}Ctrl+Alt+Shift+E‸a b#{‸a} b
Wrap the form at the caret in ""Ctrl+Alt+Shift+Q‸a b"‸a" b
Change the brackets around the caret to ()none[a ‸b](a ‸b)
Change the brackets around the caret to []none(a ‸b)[a ‸b]
Change the brackets around the caret to {}none(a ‸b){a ‸b}
Change the brackets around the caret to #{}none(a ‸b)#{a ‸b}

Drag, transpose, join and split

CommandKeyBeforeAfter
Drag the form forward, past the next oneAlt+Down, Ctrl+Alt+Shift+F(‸a b c)(b ‸a c)
Drag the form backward, past the one beforeAlt+Up, Ctrl+Alt+Shift+B(a ‸b c)(‸b a c)
Transpose the two forms around the caretCtrl+Alt+T(a‸ b c)(b a‸ c)
Join the two lists or strings around the caretCtrl+Shift+J(a)‸ (b)(a‸ b)
Split the list or string at the caretnone(a ‸b)(a)‸ (b)
Convolute: turn the two lists around the caret inside outCtrl+Shift+C(let [x 1] (frob ‸a b))(frob (let [x 1] ‸a b))

Kill and copy

CommandKeyBeforeAfter
Kill the form at the caret, onto the clipboardCtrl+Shift+Delete(a ‸b c)(a ‸c)
Kill the form before the caretCtrl+Alt+Backspace(a b‸ c)(a ‸ c)
Kill to the end of the line, keeping the brackets balancedMac: Ctrl+K(a ‸b c)(a ‸)
Kill to the start of the line, keeping the brackets balancedMac: Cmd+Backspace(a b ‸c)(‸c)
Kill to the end of the listCtrl+Delete(a ‸b c)(a ‸)
Kill to the start of the listCtrl+Backspace(a b ‸c)(‸c)
Splice, killing what follows the caret in the listCtrl+Alt+Shift+Deletex (a ‸b c) yx a ‸ y
Splice, killing what comes before the caret in the listCtrl+Alt+Shift+Backspacex (a b ‸c) yx ‸c y
Copy the form at the caretnone(a ‸b c)(a ‸b c)

Select

CommandKeyBeforeAfter
Expand the selection: the form at the caret, then each list around itLinux: Alt+Shift+Right(foo (b‸ar baz))(foo («bar» baz))
Shrink the selection back a stepLinux: Alt+Shift+Left(foo («bar» baz))(foo (b‸ar baz))
Extend the selection over the next formMac: Alt+Shift+Right; Linux: Ctrl+Shift+Right(a ‸b c)(a «b» c)
Extend the selection over the form before itMac: Alt+Shift+Left; Linux: Ctrl+Shift+Left(a b‸ c)(a «b» c)

Move

CommandKeyBeforeAfter
Move forward by a formCtrl+Right, Alt+Right(a ‸b c)(a b‸ c)
Move backward by a formCtrl+Left, Alt+Left(a b‸ c)(a ‸b c)
Move up to the start of the list around the caretCtrl+Up(a (b ‸c))(a ‸(b c))
Move down into the next listCtrl+Down(a‸ (b c))(a (‸b c))
Move out of the list, past its closerCtrl+Alt+Down(a (b ‸c) d)(a (b c)‸ d)
Move into the list before the caret, from the backCtrl+Alt+Up(a (b c) ‸d)(a (b c‸) d)
Move to the start of the list around the caretCtrl+Home(a b ‸c)(‸a b c)
Move to the end of the list around the caretCtrl+End(a ‸b c)(a b c‸)

Notes on a few commands

  • Slurp takes from the next list out when the innermost one has nothing after it: (a (b‸)) c becomes (a (b‸) c).
  • Kill form and the other kills copy what they remove to the clipboard. Copy form copies without removing.
  • Kill to the end of the line takes whole any form that starts on the line, and never goes past the closer of the list around the caret. Inside a string it stops at the closing quote. Kill to the start of the line mirrors it.
  • Join joins two lists of the same kind, or two strings. Split splits the list at the caret, or the string the caret is in.
  • Shrink selection goes back the way Expand selection came, and does nothing once the buffer or the selection has changed.
  • Rewrap keeps a reader prefix such as a quote. A set's # goes with its brace.

Keys the Calva set changes

With the Calva paredit set on, these keys stop doing their usual job in the editor and run a structural command, as they do in Calva:

  • Ctrl+Left and Ctrl+Right, and Alt+Left and Alt+Right (Option on a Mac), move by a form. They normally move by a word.
  • Ctrl+Up and Ctrl+Down move out of a list and into the next one.
  • Ctrl+Home and Ctrl+End go to the start and end of the list around the caret. On Linux they normally go to the start and end of the file.
  • Ctrl+Backspace and Ctrl+Delete kill to the start and end of the list. On Linux they normally delete a word.
  • On a Mac, Ctrl+K kills to the end of the line with the brackets kept balanced, and Cmd+Backspace kills to its start.

A key of your own in keys.edn still wins over the set, so you can take any of these back in Preferences.

Calva keys the set leaves out

  • Raise and the rewraps. Calva puts them on two-step keys (Ctrl+Alt+P then Ctrl+Alt+R, and Ctrl+Alt+R then a second key). Jonquil has no two-step keys yet.
  • Kill to the end and start of the line on Linux. Calva uses Ctrl+K then a second key there.
  • Split. Calva's Ctrl+Shift+S is Jonquil's Save As.
  • Expand and shrink the selection on a Mac. Calva's Ctrl+W and Ctrl+Shift+W close the tab in Jonquil. On Linux they are on Alt+Shift+Right and Left.
  • Calva's strict mode, its drags up and down a level, and its selection variants of the moves have no Jonquil command.

Every command left out is still under Edit > Structure and in the palette.

Keys on other systems and layouts

On Linux, Ctrl+Alt+Right and Left often switch workspaces, so the set also puts slurp and barf on Ctrl+Alt+. and Ctrl+Alt+, there.

On a Mac, macOS itself uses Ctrl+Up, Ctrl+Down, Ctrl+Left and Ctrl+Right for Mission Control and for switching desktops, and an app never sees a key macOS takes. Moving by a form still works on Option+Left and Option+Right. Ctrl+Up and Ctrl+Down, which move out of a list and into the next one, have no second key in the set, so they do nothing until you turn those four shortcuts off under System Settings > Keyboard > Keyboard Shortcuts > Mission Control, or give the two commands another key in Preferences.

On a Mac, Option changes the character a key sends, so Jonquil matches a key held with Option by its position on a US keyboard. On another layout (Dvorak, Colemak, AZERTY, QWERTZ) a binding written as Ctrl+Alt+S fires on the key that sits where S does on a US keyboard.

This page is docs/guide/structural-editing.md in the Jonquil repository, built from its main branch, which can be ahead of the 0.2.1 download.

Enter to open · Esc to close