guns / vim-sexp

Precision Editing for S-expressions
MIT License
612 stars 33 forks source link

Recursive stackop #15

Open snoe opened 9 years ago

snoe commented 9 years ago

PR for other half of #10.

This change allows you to capture the next (or previous) element into the sibling form containing your cursor. Another way to say it is that capture will recurse up ancestor forms until it finds an element to capture and moves it into that ancestor.

See the issue for more details.

Cheers

bpstahlman commented 5 years ago

To flesh out several distinct concepts of emit and capture, I've documented 3 candidate "emit" commands, and 2 candidate "capture" commands. Would appreciate feedback from @snoe and others on the usefulness of the proposed commands. If the consensus is that only one command in each set is particularly useful (or one is significantly more useful than the others), perhaps the best approach is to change sexp's current emit/capture to work accordingly. OTOH, if multiple variants are deemed useful, perhaps it makes sense to provide several distinct emit/capture variants; alternatively, a single emit/capture command could be provided, along with an option to configure its behavior.

Note: I'm not overly attached to the proposed names - just needed a way to distinguish between the various commands... Note: In all of the examples below, | indicates cursor position.

Emit commands

Emit

sexp_emit_head_element sexp_emit_tail_element

Description: Emit [count] elements from current list, preserving cursor position unless element under cursor is emitted from list, in which case, cursor will be moved to the list bracket. Note: The only difference between this and the original sexp emit is that the original always positions the cursor on list bracket (even when element under cursor is not emitted).

Example:

Direction Count Before After
tail 3 (((foo (a |b c)) d e)) (((foo (|) a b c) e))
tail 2 (((foo (|a b c)) d e)) (((foo (|a) b c) d e))

Note: I can see an argument in favor of removing lists made empty by the emit: e.g., the empty list before a in the 1st example above.

Barf

sexp_barf_head_element sexp_barf_tail_element

Description: Emit a single element from current list [count] times, preserving cursor position relative to the element beneath it, even when the element under cursor is emitted from the list. Note: This is essentially the snoe PR. It is also closest to emacs paredit behavior, hence the name.

Example:

Direction Count Before After
tail 3 (((foo (a |b c)) d e)) (((foo (a) |b) c d e))

Eject

sexp_eject_head_element sexp_eject_tail_element

Description: Push element under cursor out of [count] containing forms, pushing any elements in its path. Note: AFAIK, there is no comparable emit command in either vim-sexp or emacs paredit.

Example:

Direction Count Before After
tail 3 (((foo (a |b c)) d e)) (((foo (a))) |b c d e)
head 2 (((foo (a |b c)) d e)) ((foo a |b ((c)) d e))

Capture command

Capture

sexp_capture_head_element sexp_capture_tail_element

Description: Pull [count] elements into the current list from ancestor forms, preserving original cursor position. Note: This command is similar to the original sexp capture, but allows elements to be pulled from lists other than the immediate parent.

Example:

Direction Count Before After
tail 3 (foo ((a |b) (c d)) e f) (foo ((a |b (c d) e f)))
head 2 (foo ((a b) (c |d)) e f) (((foo (a b) c |d)) e f)

Slurp

sexp_slurp_head_element sexp_slurp_tail_element

Description: Pull a single element into an ancestor list [count] times, preserving original cursor position. Note: This is essentially the snoe PR. It is also closest to emacs paredit behavior, hence the name.

Example:

Direction Count Before After
tail 3 (foo ((a |b) (c d)) e f) (foo ((a |b (c d) e)) f)
snoe commented 5 years ago

@bpstahlman great write up. I think your capture and emit commands could work but would take some getting used to. There might be some small benefit to matching paredit because the commands are so ingrained across the ecosystem. On the other hand, if capture/emit feel more vim-like then I think that would fall inline best with this plugin.

Honestly I would be happy with any change that keeps cursor position getting to master; I see colleagues fumble around this issue all the time.

bpstahlman commented 5 years ago

@snoe Thanks. So maybe the best path forward for now is your recursive_stackop branch. I like your basic approach, and the bugfixes seem manageable. Additional flavors of emit/capture could always be added independently at a later date if enough users deem them useful...

I strongly concur on the importance of preserving cursor position. While adding a few features on my own branch, (enhanced-sexp-objects), I added a non-trivial amount of logic to ensure cursor position is preserved across command execution. Incidentally, if you or your colleagues ever want to take a look at any of the features on that branch, I'd welcome any feedback. I've got a significant amount of code cleanup to do yet, and nothing's fixed in stone, but the features are all documented in their current form, and should be basically working. It started as a branch to enhance sexp objects to allow the ie/ae objects to expand with multiple applications (just like Vim's iw, aw, etc.), but I ended up adding several other features I find useful. Also, if you have an emacs background and haven't updated recently, you might be interested to know that convolute and several new motion commands were merged to master over a year ago.

bpstahlman commented 5 years ago

Incidentally, in case anyone happened to try the (enhanced-sexp-objects) branch I mentioned in the previous post, I just pushed a fix involving the indent-triggered whitespace cleanup.