ethan-leba / tree-edit

🌲 Structural editing in Emacs for any™ language!
GNU General Public License v3.0
396 stars 16 forks source link

Discussion on providing a non-evil editing mode for tree-edit #40

Open ethan-leba opened 2 years ago

ethan-leba commented 2 years ago

New 'layered' package model I'm considering:

  1. tree-edit-core: Core structural editing logic, what tree-edit.el is currently. To be used by refactoring packages or those who aren't sold on modal editing.
  2. tree-edit: Movement layer on top of tree-edit-core, wrapped interactive editing commands which operate with a buffer-global current node, and other functions which are not strictly necessary for usage as a library. This would be extracted from evil-tree-edit. To be used by those who want an opinionated set of editing commands and are bought into modalism, but not the evil ecosystem. Same dependencies as tree-edit-core
  3. evil-tree-edit: Defines evil state, specific keybindings, etc., with dependencies on evil and avy. For the evil among us... >:)
ethan-leba commented 2 years ago

@fgeller thoughts? I'm not sure where the overlay logic is too opinionated for tree-edit (2) or not..

fgeller commented 2 years ago

:wave: sorry for the delay!

do you see other use(r)s for tree-edit-core besides tree-edit? or differently, i'd suggest splitting it if you see multiple use, alternatively you can just extract the parts from evil-tree-edit that'd be required to create something live evil-tree-edit without the evilness ;)

what i tried to do (and hope to continue now) was create a minor-mode that i'd enable for e.g. go-mode and the minor mode would then provide motion and edit functions like tree-edit-goto-next-sibling that would maintain the buffer-local state that's required for tree-edit (like the current node). then i could have buffer/mode local bindings for go-mode where i use those motion and edit commands regardless of whether i use modal editing or not. so i try to extract from evil-tree-edit and i think you could just feed that back into tree-edit and then split that further into core if need be. let me know if that does (not) make sense :)

ethan-leba commented 2 years ago

👋 sorry for the delay!

no worries! :)

do you see other use(r)s for tree-edit-core besides tree-edit? or differently, i'd suggest splitting it if you see multiple use, alternatively you can just extract the parts from evil-tree-edit that'd be required to create something live evil-tree-edit without the evilness ;)

I imagined tree-edit-core would be for refactoring/non-modal approaches. Doesn't necessarily have to exist as an entirely separate package, it could just exist in the tree-edit package so you can require that if you don't care about any of the current node stuff.

I don't think the buffer-local state like the current node wouldn't really make sense outside of a non-modal context, since you'd be moving the point and not the current node, so you'd have to select that each time and pass it into the tree-edit functions. the evil-tree-edit functions are mostly wrapping the calls to tree-edit by passing in the current node as a parameter, and then some extra avy and evil stuff.

what i tried to do (and hope to continue now) was create a minor-mode that i'd enable for e.g. go-mode and the minor mode would then provide motion and edit functions like tree-edit-goto-next-sibling that would maintain the buffer-local state that's required for tree-edit (like the current node). then i could have buffer/mode local bindings for go-mode where i use those motion and edit commands regardless of whether i use modal editing or not. so i try to extract from evil-tree-edit and i think you could just feed that back into tree-edit and then split that further into core if need be. let me know if that does (not) make sense :)

I'd love to be able to have a non-evil approach available to those who want to try out tree-edit so definitely excited to see how your approach pans out!

fgeller commented 2 years ago

got it to a state where i can use basic navigation (siblings, parent/child nodes) and node marking :grin: i added a defun that checks if point is the same as the start of current node and refreshes the current node if that's not the case - that allows me to use normal motion besides structural motion. maybe images help:

tree-edit-minor-mode

the light yellow is the current node's overlay (there's a timer to remove it if point isn't at start of the current node anymore).

if you're brave enough, the (hacky) code is here :wink: let me know if you're interested in me cleaning this up or whether you want to try another approach :grin:

would like to get some more editing commands in there, and shortcuts to go to func or type definitions and i haven't tweaked tree-edit-go.el at all, that's next :)

ethan-leba commented 2 years ago

That looks really cool!! Awesome stuff :)

I've considered using the point for node selection, but one issue i forsaw is that many nodes have the same start point. For example |broker.GetMetadata(...); could be pointing to the 'broker' identifier, or the function call, or the expression list. Do you think that would be an issue in practice?

What keybinds you have set up for this, btw? Just curious!

fgeller commented 2 years ago

What keybinds you have set up for this, btw? Just curious!

i use a somewhat strange keyboard layout called workman, but on qwerty this would be similar to hjkl (just shifted by one key):

  (define-key tree-edit-mode-map (kbd "I") #'tree-edit-goto-next-sibling)
  (define-key tree-edit-mode-map (kbd "N") #'tree-edit-goto-prev-sibling)
  (define-key tree-edit-mode-map (kbd "O") #'tree-edit-goto-parent)
  (define-key tree-edit-mode-map (kbd "E") #'tree-edit-goto-child)

i keep neoi as hjkl and the upper case versions to use structural motion.

I've considered using the point for node selection, but one issue i forsaw is that many nodes have the same start point. For example |broker.GetMetadata(...); could be pointing to the 'broker' identifier, or the function call, or the expression list. Do you think that would be an issue in practice?

not too concerned at the moment - it was a bit strange without the overlay made visible (as e.g. goto-parent didn't change point). but i wanted to use it for a while before i try changing it (first intuition was to skip the boring/redundant nodes)

That looks really cool!! Awesome stuff :)

cool! :) would you be interested in the minor-mode, ie should i pour some time into making it more presentable?

ethan-leba commented 2 years ago

Ah, interesting -- I've never gotten around to leaving QWERTY but I've always wanted to try it. So is having tree-edit-mode enabled the equivalent of being in normal state, and disabling it would be equivalent to dropping into insert mode (to put it in evil terms?)

would you be interested in the minor-mode, ie should i pour some time into making it more presentable?

Yes, if you're willing I'd love to upstream what you've been working on! Depending on the direction you take this it may be better to provide at a separate package from tree-edit (i have an org that we could put it in), but either way I'm sure theres alot we can integrate back into tree-edit main.

spiderbit commented 2 years ago

Yes I want that, too. For me this looks like paredit functionality with a unnecessary dependency on evil mode.

I happen to use another modal mode xah-fly-keys, but I also just use normal paredit for structural editing, and combine that with the normal text operations of xah-fly-keys.

I can bind the functionality to paredit like key bindings but it seems to not work without evil active? Don't see why barf and slurp needs evil to work...

Btw to not only be negative, if that would work outside of evil it would be very great package I wait for years, what I would hoped smartparens would be at some point... but never happend.

ethan-leba commented 2 years ago

Yes I want that, too. For me this looks like paredit functionality with a unnecessary dependency on evil mode.

Fair point, and this is something that will hopefully be addressed ASAP!

I can bind the functionality to paredit like key bindings but it seems to not work without evil active?

Besides the unnecessary dependency, I'm surprised this didn't work without evil active. Did you try implementing any bindings? If so I can try to help debug what's going on. The evil mode loads the language file for a given major-mode on entrance, that may be what you're running into?

Don't see why barf and slurp needs evil to work...

The evil mode's slurp and barf are a very thin wrapper around tree-edit library: https://github.com/ethan-leba/tree-edit/blob/main/evil-tree-edit.el#L275, you may have caught a bug?

Btw to not only be negative, if that would work outside of evil it would be very great package I wait for years, what I would hoped smartparens would be at some point... but never happend.

Thanks!

spiderbit commented 2 years ago

Wow exwm crashed while writing this comment and trying to reproduce this, but I have to write it again basically what happens is I start with code like that:

def test:
    if TRUE:
        [X]print ("foo")
    print ("bar")

with the Cursor at [X] using evil-tree-edit-slurp I get that error: tree-edit-slurp: Wrong type argument: user-ptrp, nil

Also I get that: user-error: Tree-edit does not support exwm-mode!

If I deactivate xah-fly-keys I can theoreticillay use ">" I think I have to press it 2 times with maybe ESC before then instead of slurping the second print statement into the if block, it moves the 1. print statement a tab / 4 spaces to the right.

Why does tree-edit not support exwm-mode? What has exwm to do with formating of text buffers?

ethan-leba commented 2 years ago

@spiderbit moved discussion and responded here: https://github.com/ethan-leba/tree-edit/discussions/48 to keep this issue focused