Parkour

Under construction. (Last change: 20 June 2023)

Parkour is a Lua library and a few text editor plugins for structured editing of Lisp S-expressions.

Table of Contents


1 Distribution

Parkour base module

https://repo.or.cz/lisp-parkour.git

Vis plugin

https://repo.or.cz/vis-parkour.git

Textadept plugin

https://repo.or.cz/ta-parkour.git

Howl plugin

https://repo.or.cz/howl-parkour.git

This manual’s source

https://repo.or.cz/parkour-doc.git


2 Concepts

This chapter describes behaviour common to all plugins based on Parkour.

2.1 Objects

Parkour maps the typical objects used in text editors to S-expressions (atoms and lists):

  • word — atom, or, when the cursor is exactly on a delimiter, list or quasi-list1
  • block — parenthesized list
  • sentence — the current list or quasi-list
  • paragraph — the current top-level expression
  • section/page — form-feed2-separated group of top-level expressions

2.2 Motions

Parkour provides the following motion types:

  • Structured motions — move either horizontally (without leaving the current list or quasi-list), or diagonally (ascend or descend) through the tree of nested expressions.

    When the cursor is in code, they skip over comments. (For other ways to descend into comments, see word-descent, delete-descent, semicolon-descent)

    When the cursor is inside a quasi-list, horizontal motions land on individual words.
    Moreover, when in a group of line comments, horizontal motions will leave the current comment and enter the previous/next one, effectively treating the whole group as a single comment.

  • Word motions — always land on atoms/words. They float up or sink down in the tree hierarchy, and enter or leave strings/comments, as needed.

2.3 Electricity/DWIM

Besides the sexp-aware editing operators, which have dedicated key bindings, Parkour enhances the behaviour of common keys like BS, DEL, RET, SPC, and ;.

  • RET
    • at end of line — opens an empty indented line after the current one
    • at start of line — opens an empty indented line before the current one
    • before closing paren — opens an empty indented line after the last list element
    • on empty indented line — does nothing (only one empty line is allowed in a list, and it will be collapsed on the next reindent)
    • in line comment — splits the comment
    • elsewhere in code — puts the text to the right of the cursor on a new indented line
  • BS
    • after a closing delimiter — moves the cursor to the left of it
    • after an opening delimiter — does nothing
    • at start of line — joins it with the previous line (and cleans up any leftovers of indentation)
    • same, but after a line comment — does nothing (to prevent accidentally commenting out the current line)
    • at start of line comment — joins it with the previous one
    • in empty line comment — same, unless the empty comment is the last in a group. In that case, it splices the comment (which deletes it, effectively).
  • DEL
    • before an opening delimiter — moves the cursor to the right of it
    • before a closing delimiter — does nothing
    • at end of line — joins it with the next line (and cleans up any leftovers of indentation)
    • at start of line — does nothing (indentation cannot be manually influenced)
    • before a line comment — moves the cursor past the opening semicolons
    • at end of line comment — joins it with the next one
    • in empty line comment — same, unless the empty comment is the last in a group. In that case, it splices the comment (which deletes it, effectively).
  • SPC
    • on empty indented line — deletes it and inserts the space on the previous line
    • between a space and another character — does nothing (only one space is allowed between same-line expressions)
  • ;
    • at start of line — inserts two or three semicolons (inside a list, or at top level, respectively), and a space.
      If the line was originally empty, the cursor ends up inside the new comment, so you can start typing.
      If there was code on the line, the cursor is kept before the semicolons, so you can move away from the commented out code without having to ascend out of it first.
    • before a line comment — moves the cursor past the opening semicolons
    • elsewhere in code — shifts anything to the right of the cursor to column 40 and inserts a single semicolon

    If in the code that would be commented out there was a delimiter whose match is on another line, the delimiter and anything to the right of it is moved to the next line in order to prevent any unbalancing.

2.4 Paren autocorrect

In Clojure and Fennel square brackets are required at certain places. Parkour will insert a square bracket even if you pressed the wrong kind of delimiter (paren, brace, or even double-quote).

In some Scheme dialects (e.g., Racket) square brackets are recommended for readability reasons. If you are using such a dialect, you can get this behaviour by enabling the auto_square_brackets option.


3 Features

3.1 Editor-specific features

Some features could not be abstracted into the Parkour library and had to be implemented in the top-layer code of a particular editor plugin.

3.1.1 Vis

  • objectwise pasting

    This is a generalization over linewise pasting — pasting a previously deleted/yanked sexp object will put it before or after the current object and insert an appropriate whitespace separator (space or newline), regardless of where exactly the cursor is.
    This makes it easy to, say, transpose/barf/slurp, without using the dedicated operators, this way flattening the learning curve a bit. Of course, the dedicated operators have their advantages — fewer keypresses, keeping cursor position, atomic undo, dot-repeatability.

  • splicing delete

    When inside a list or quasi-list, d-ing with a motion that lands on one of the delimiters will remove the opposite delimiter as well. This merges two paredit commands — splice-killing-{forward,backward} and splicing, into the NORMAL-mode delete. You can use dE, dB, x, or X, but any motion that leads to the above situation will do (even something like d5h).

  • autoselect

    This reverses the typical for vi verb-noun order, and makes vi motions work like those in Kakoune.


Footnotes

(1)

Quasi-lists are any double-quoted strings, block comments, and line comments. Like lists, you can perform wrap, splice, split, and join. Unlike lists, you cannot barf, slurp, or descend into them. Also, their content is not subjected to autoformatting.

(2)

^L, ASCII code 12