MagicDuck / grug-far.nvim

Find And Replace plugin for neovim
MIT License
836 stars 25 forks source link

Add possibility to modify the replacement string #251

Closed Groz17 closed 1 month ago

Groz17 commented 1 month ago

Similar to how in Vim you can type \= to interpret the replacement as an expression, it would be cool if you could the same with this plugin.

For example, using ripgrep the various submatch(0), ..., submatch(n) would be $0, ..., $n.

This behaviour would be triggered by a map, like <localleader>x, where x stands for expression.

You could use vimscript string-related functions like substitute(), repeat(), etc...

I'm not sure if this is even possible considering you're using external commands, but it's something that could be potentially useful.

Thanks for the plugin btw, it looks amazing!

MagicDuck commented 1 month ago

hi @Groz17,

Thanks for your kind words! πŸ˜„ I must admit that this is the first time I have heard of this vim feature although it seems like it could be super useful in some situations. If I am understanding correctly the ask is for the ability to essentially have:

replacement = f(search_match)

where f is an expression that is applied on each individual search_match (which is basically same as submatch(0)) or in theory could even be the body of a function that returns a replacement value. I can also see f being either vimscript or lua. We just need a clever enough way to specify search_match in order to deal with escaping and stuff. Maybe we just call it $0 and if people want literally $0 in their replacement text they need to use $$0 similar to ripgrep?...

Since this would happen at the plugin lua level, we don't have access to capture groups (submatch(n)), so those would not be supported (but we do get access to the full matches since we use that info for diffing). You would have to do some regex on the whole match inside the replacement to get pieces and then return the combination of pieces you want more granular control.

It would have to be a mode that you toggle, similar to how we have engine (ripgrep / astgrep) today, in order for live results update as you type to work.

An example of how this would look potentially with f being a lua function body:

image

edit: there should be a then after that if πŸ˜›

Groz17 commented 1 month ago

Yes, you understood perfectly.

I hadn't thought about using a function, that looks pretty neat. This would suggest Lua to be the default language choice, and iirc you could still use something like vim.api.nvim_eval to evaluate vimscript expressions.

I think, at least for the moment, that just having $0 to manipulate would already be magnificent.

I guess in this mode the results would update at every TextChanged event (TextChangedI event seems too much since you first need to build/return an expression).

MagicDuck commented 1 month ago

I guess in this mode the results would update at every TextChanged event (TextChangedI event seems too much since you first need to build/return an expression).

That’s a great idea! Grug-far already supports normal mode only search, so we can switch to that modality and indicate it somewhere in the UI, potentially next to the engine.

Note that it might be a while before I can work on this depending on how I get time. Should be a fun one to implement though :)

MagicDuck commented 1 month ago

@Groz17 okie, took a lot of doing, but it's finally here πŸ˜„

Both lua and vimscript "interpreters" are supported. Press <localleader>x to switch. In astgrep engine, you also have access to the meta variables:

ripgrep + lua:

image

asgrep + vimscript:

image
MagicDuck commented 1 month ago

feel free to reopen if you see any issue πŸ˜„

Groz17 commented 1 month ago

This is absolutely fantastic, congratulations! πŸ₯³ The treesitter highlight is a nice touch as well :D

This also allows to use external commands to modify the replacement string, which makes this plugin really stand out!

Functions

MagicDuck commented 1 month ago

Thank you very much! πŸ˜„ That's a really cool usage I haven't thought about. It always amazes me in a good way what shapes people have yoga-twisted this plugin in, haha.