/parembrace

A different PareEdit for Light Table - powered by rewrite-cljs

Primary LanguageJavaScriptMIT LicenseMIT

Parembrace - Embrace those parens in Light Table

1. Introduction

This is another attempt at providing Paredit support for Light Table.

The plugin is scoped to only support Clojure(/ClojureScript/EDN), this limitation has opened a completely different route for implementing paredit support than previous attempts in Light Table. The plugin makes heavy use of the rewrite-cljs library. The library allows us to treat the code as an virtual AST rather than a stream of characters and provides some truly exceptional flexibility in terms of implementing paredit features (and way beyond).

It’s still some way to go, but the basic features are mostly working pretty nicely !

2. Installation

2.1. Plugin manager

You will find the plugin through the Light Table plugin manager under the name clj-light-refactor

2.2. Development

If you want try it out the lastest stuff you can clone it to the plugins folder for LightTable (check the LT docs for where)

3. Extentions

I’m planning to make it even easier than it currently is to easily add new or modified features. If you know your way around a clojure zipper you have a very good starting point. With the user plugin it should be fairly easy to add your own commands and behaviors.

4. Limitations

No sweeping this under the carpet:

  • If you write illegal Clojure code, the features will fail (do nothing or throw errors)

  • It’s not blisering fast (hoping th atom shell/electron release might help some here)

  • To keep the features snappy enough, keep your top level form size sensible (top level forms beyond 3-400 lines will not work nicely)

  • There is no strict mode option (and none coming in the immediate future)

  • Most if not all features require you to work in the context of top-level forms

5. Usage

All commands are available in the command browser. They are prefixed with Parembrace:

There are no predefined keyboard shortcuts. So you have to figure out what makes sense to you. (And configure your User keymap Maybe you prefer something close to the defaults from paredit.el or maybe vims keybindings are more to your taste.

5.1. Basics

Command Description Sample

Open list/vector/set/map

Create a list/vector/set/map pair append space and position cursor

(foo |bar) ⇒ (foo (|) bar)
(foo) ; bar| ⇒ (foo) ; bar(|
(str "he|llo") ⇒ (str "he(|llo")

Open doublequote

Create a doublequote pair

(str |"bar") ⇒ (str "|" "bar")
(str "Hello|") ⇒ "Hello\"|")

Open anonymous function

Create an empty anonymous function

(let [myfn |])
⇒ (let [myfn #{|}])

5.2. Navigation

The navigation commands have similarities to Emacs, but it’s better to compare with the movement functions in clojure.zip. It should be supereasy for you to add custom navigation commands if you are somewhat familiar with clojure.zip

Command Description Sample

Move right

Move cursor to start of right sibling

(+ |1 2) ⇒ (+ 1 |2)

Move left

Move cursor to start of left sibling

(+ 1 |2) ⇒ (+ |1 2)

Move down

Move cursor down into S-expr and position at first node

(let |[a 1]) ⇒ (let [|a 1])

Move up

Move cursor up from current S-expr to enclosing S-expr and position at start

(let [|a 1]) ⇒ (let |[a 1])

Move up right

Move cursor up from current S-expr to enclosing S-expr and position at end

(let [|a 1]) ⇒ (let [a 1]|)

Move next

Move to next node given a depth first traversal.

[|1 [2 [3]] 4] ⇒ [1 |[2 [3]] 4]
⇒ [1 [|2 [3]] 4] ⇒ [1 [2 |[3]] 4]
⇒ [1 [2 [|3]] 4] ⇒ [1 [2 [3]] |4]

Move previous

Move to previous node given a depth first traversal.

Reverse of move next

5.3. Killing

Command Description Sample

Kill

Kill all sibling nodes to the right of the current node (when in seq node)

[1 2| 3 4] ⇒ [1 2|]
[1 [|] 2] ⇒ [1 2]

Kill to end of comment (when in comment)

({:a 1 ; Hello| world
  :b 2}
({:a 1 ; Hello| \n
  :b 2}

Kill to end of string (when in string)

(str "Hello| World!")
(str "Hello|")

Kill in empty string - nukes

(str "|" "World") ⇒ (str "World")`

Kill one

Kill the node at position

[10 |20 30] ⇒ [10 |30]
[10 2|0 30] ⇒ [10 3|0]
[10 |[20] 30] ⇒ [10 |30]

Kill word in comment at position

; |hello world ⇒ ; | world

Kill word in string at position

(str "Foo
      |Bar
      Do")
(str "Foo
      |Do")

5.4. Slurping and barfing

Command Description Sample

Slurp forward

Pull in next right outer node (if none at first level, tries next etc) into current S-expression

[1 2 [|3] 4 5] ⇒ [1 2 [|3 4] 5]
[1 [[|2]] 3] ⇒ [1 [[|2 3]]]

Slurp backward

Pull in prev left outer node (if none at first level, tries next etc) into current S-expression

[1 2 [|3] 4 5] ⇒ [1 [2 |3] 4 5]
[1 [[|2]] 3] ⇒ [[[1 |2]] 3]

Slurp forward fully

Pull in all right outer-nodes into current S-expression, but only the ones at the same level as the the first one

[1 2 [|3] 4 5] ⇒ [1 2 [|3 4 5]]

(defn foo []
  (let [a 1]|)
  (println "a") ; useful
  (println "b"))
(defn foo []
  (let [a 1]
    (println "a") ; useful
    (println "b")))

Slurp backward fully

Pull in all lef outer-nodes into current S-expression, but only the ones at the same level as the the first one

[1 2 [|3] 4 5] ⇒ [[1 2 |3] 4 5]

Barf forward

Push out the rightmost node of the current S-expression into outer right form

[1 2 [|3 4] 5] ⇒ [1 2 [|3] 4 5]

Barf backward

Push out the leftmost node of the current S-expression into outer left form

[1 2 [3 |4] 5] ⇒ [1 2 3 [|4] 5]

5.5. Depth changing commands

Command Description Sample

Wrap around - (list/vector/map/set)

Wrap node at cursor in given sequence type

[1 |2 3] ⇒ [1 [|2] 3]

Wrap around slurping forward - (list/vector/map/set)

Create a new seq node of given type left of cursor pos then slurp fully into the new node

[1 |2 3 4] ⇒ [1 [|2 3 4]]

Splice

Unwrap nodes in current S-expr into enclosing S-expr

[1 [|2 3] 4] ⇒ [1 |2 3 4]

Splice - killing bacward

Remove left siblings of current given node in S-Expression and unwrap remaining into enclosing S-expression

(foo (let ((x 5))
     |(sqrt n)) bar)
(foo (sqrt n) bar)

Splice - killing forward

Remove current given node and its right siblings in S-Expression and unwrap remaining into enclosing S-expression

(a (b c |d e) f) ⇒ (a b |c f)

Split

Split current s-sexpression in two at given node

[[1 2 |3 4 5]] ⇒ [[1 2 3] [4 5]]
(str "Hello |World!") ⇒ (str "Hello " "World!")

Join

Join S-expression to the left and right of current loc.

[[1 2] |[3 4]] ⇒ [[1 2 3 4]]
(str "Hello " |"World!") ⇒ (str "Hello World!")

Raise

Kill siblings and raise current node up one level to enclosing expression.

[1 [2 |3 4]] ⇒ [1 |3]

Note
Except for the wrapping commands, placement of the cursor position after the command above is not very cleverly placed (:

5.6. Misc commands

Command Description Sample

Select expression

Select node(s) at given pos, if already a selection the selection is expanded

[1 [|2 3] 4] ⇒ [1 [2 3] 4]
⇒ [1 [2 3] 4]
[1 [2 3] 4]

Move node to previous

Move node at current location to the position of previous location given a depth first traversal

(+ 1 (+ 2 |3) 4) ⇒ (+ 1 (+ |3 2) 4)
(+ 1 (+ 2 3) |4) ⇒ (+ 1 (+ 2 3 |4))

6. Contributing

Pull requests are most welcome. Please do not include the transpiled files (_compiled) in the PR.

7. History

  • 0.1.0 Initial release With a wide range of paredit features supported. A few novel/different commands available too !

8. License

MIT, same as Light Table.