ziontee113 / syntax-tree-surfer

A plugin for Neovim that helps you surf through your document and move elements around using the nvim-treesitter API.
MIT License
469 stars 9 forks source link

Add node holding and swapping feature #14

Closed spiderforrest closed 1 year ago

spiderforrest commented 1 year ago

I found myself needing to swap two nodes a far enough distance apart that sibling swapping was tedious, so I wrote small functions to 'hold' a node and then swap it with the focused node. Notes:

bew commented 1 year ago

Do you know about the vimscript-based plugin vim-exchange ? I'm wondering: is this is a tree-sitter-aware variant, allowing to swap any TS nodes?

spiderforrest commented 1 year ago

I did not know about vim-exchange, that looks cool, thanks.

This does allow swapping any TS nodes. The hold key just grabs the current node, and the swap key calls ts_utils.swap_nodes() on the held node and the current node.

bew commented 1 year ago

FYI vim-exchange uses the same key for hold and swap, Basically it works like this: cx i) to hold the area inside braces, that area is now highlighted in some way Move around and then cx ap to swap the paragraph with the held area. (ok maybe it's a weird example ^^)

(And other variants for different modes)

spiderforrest commented 1 year ago

Oh, that makes sense. I'll implement that when I have a minute.

spiderforrest commented 1 year ago

@bew, I added that as :STSSwapOrHold-that does what you suggested, right? I switched my personal bindings to that, it's much nicer. Thanks for the suggestion.

I ended up having it clear the held node when it's swapped, as it behaved poorly when swapped twice. I'm considering removing :STSHoldFocusedNode and :STSSwapHeldAndFocusedNodes. It doesn't make much sense to have them now that the held node is cleared when it's swapped and with :STSSwapOrHold available.

ziontee113 commented 1 year ago

Hi @spiderforrest , thank you for opening the PR. I've tried out the feature and I think it's great :+1:

I wonder if you can add Visual Mode support for the feature as well. I ran into this example upon testing the PR: Let's say we're in a Lua file, with this sample code:

vim.keymap.set("n", "vx", "<cmd>STSSelectMasterNode<cr>")

local something = "something else"

vim.keymap.set("n", "vd", function()
     vim.opt.opfunc = "v:lua.STSSwapCurrentNodeNextNormal_Dot"
     return "g@l"
end, { silent = true, expr = true })

If the user wants to swap the vim.keymap.set nodes, they can't, since the node under cursor is only vim, not the entire vim.keymap.set.... call. Either with or without supporting Visual Mode, I'm curious to hear your thoughts on this use case.

spiderforrest commented 1 year ago

I'd really like that functionality, I think it'd expand a lot on the use cases. This would be a really quick way to restructure code arbitrarily. I'm looking into implementing it now.

spiderforrest commented 1 year ago

That seems to work mostly alright. I just moved the other visual code from M.surf into a function that returns a single node. The one bug I found was it triggers three undo events, instead of one. I think that's from the vim.cmd("normal! o") calls? Honestly I don't completely understand the chunk of code I copied into get_visual_node(), so I'm not sure why that doesn't happen with STSSwapNextVisual.

ziontee113 commented 1 year ago

It works just fine on my end. I'll merge the PR for now, then I'll format with stylua and add "exit visual mode" after using STSSwapOrHoldVisual"