helix
helix copied to clipboard
Support regex substitution command
Support regex substitution, comparable to vim :s
or VSCode search & replace.
I propose supporting regex replacements for selection s
, files/
, and the workspace scopes <space> + /
.
This could be accomplished a few ways, but I would specifically propose using /
as a reserved character for regex select operations. This would allow supporting replacement through the common /select/replace/g
syntax wherever regex is used.
As far as I can tell workspace scope find replace is considerably more complex to implement than the others. The current grep -> fuzzy filter files
user interface would likely be unergonomic compared to a live_grep
UI for the regex replacement usecase.
Q: Why not use idiomatic helix multi cursor functionality s:
to replace matches
A: I believe the most productive feature of regex substitution are capture groups which are not replicated with multi-cursor.
For accessing regex matches: https://github.com/helix-editor/helix/issues/2167
The Helix equivalent for this is to select the buffer (%
) then search within the selection via regex (s
) and then change the contents (c
). You can typically accomplish anything you would otherwise accomplish with regex substitutions by operating on (sub)selections.
The only other reason to add a command line this is to be able to map a keybinding to it. With #1383 though you could map a key to the search and replace workflow as well.
@the-mikedavis Genuinely, thank you for the feedback.
I really appreciate the effort to keep Helix as lean as possible and without duplicate workflows. I entirely agree that the mutli-cursor flow is superior to regex replacement in the majority of cases.
In my experience there are a couple areas where multi-cursor is either more time consuming or unable to effectively solve a problem:
- Confirming replacements can't be implemented nicely with multi-cursor as far as I am aware.
- Running substitution across a workspace with multi-cursor is essentially not possible.
- Complex regex substitution across many instances is almost always faster due to being able to revise both the selection and replacement before confirming the edit.
I am concerned with the feature resulting in bloat when I get around to implementing it. For this reason, I was thinking of simply increasing the expressiveness of the existing substitution and picker dialogs to support standard regex expressions such as /gc
.
I am new to contributing to Helix so I wanted to make sure that this would be a welcome feature and in alignment with the communities goals before I get started on implementation.
I would love to know your thoughts @the-mikedavis
I don't mean to shoot your idea down 😅, it's just that IMO this doesn't belong in the core. I also might be out of touch with what other members think - others may agree this should be implemented within Helix.
For those three points I would use a tool built for the task like fastmod (outside of Helix). In other situations, a rename done through LSP rename
could be appropriate.
Being a refactoring swiss-army-knife is out of scope for the core IMO - I would prefer stuff like fastmod or semgrep live as plugins.
@the-mikedavis I think that is a perfectly respectable position. Considering I mostly work with large codebases I expect any editor to have solid regex support comparable to to VSCode search and the popular vim grep/ripgrep UIs.
That being said, I might be overestimating the impact of such as feature for the average user. I am more than happy to reconsider this as a plugin.
As far as I can tell Helix is capable of having competitive performance to ripgrep & fzf with some planned improvements. Part of me just thinks it would be cool to have these features in an extremely performant package with UI.
i would prefer to have a command to input :rg for the ripgrep commands... so that i dont need to go to another terminal to input that.... and for sure, if there's a realtime search result list on the right side would be added bonus when inputing the rg command... and when putting the -r (replace argv) ... it shows a preview of modified content in different color would be 200% extra bonus...
This feature is very useful. I will often use the ability to replace regular expressions in my text editing process.
For those three points I would use a tool built for the task like fastmod (outside of Helix). In other situations, a rename done through LSP rename could be appropriate.
Being a refactoring swiss-army-knife is out of scope for the core IMO - I would prefer stuff like fastmod or semgrep live as plugins.
As a text editor, helix should not assume that regex substitution is only used for code editing. Users can use regular expressions to process any type of text, not just code. This is a more general ability than refactoring functions.
The syntax of zero-Length assertions
and capturing group
in regular expressions
gives powerful capabilities to regular expression substitution.
helix supports multiple cursors and recording macros. These functions are useful. But this is not enough to achieve "regex substitution" capability. (I'm happy to provide some examples if needed).
I wanted to voice my support for this feature as well.
I feel there's less of a use-case within code, but it's absolutely essential when editing any other non-code file. With those files (markdown, etc.), it's easily my most used command. Something as simple as the preparation of creating hyperlinks out of a list of text (as I'm fixing up a lot of files like this at the moment):
* Reading 1: This is a title to a link
* Reading 2: This is another title
With vim, the command is as easy as :%s/\v(: )(.+)$/\1\[\2\]\(
to get:
* Reading 1: [This is a title to a link](
* Reading 2: [This is another title](
There have been a lot of more complex examples I've come across over the past few weeks of testing out helix, but a proper substitution command (alongside the existing open issue of soft wrap, again usually for non-code files) are the biggest barriers to helix being incredible for both code and non-code text alike.
Confirming replacements can't be implemented nicely with multi-cursor as far as I am aware
Very important workflow, being able to cycle through matches and confirming which to change. In vim that's done via :%s/select/replace/gc
. Right now with helix it would seem I'd have to run a diff
afterwards to confirm my select didn't catch any unintended collateral.
@adrian5 You can press %
to select the whole buffer, then s
to filter by regex. If this gives you multiple selections, you can cycle through them by pressing n
to discard a selection or )
to keep it. If everything looks good, press c
to change all selections.
Ah, neat. I didn't know that.
I have to agree with @the-mikedavis here, I don't think this belongs in core. The whole editing paradigm is centered around interactive text replacement, not batch operations. With the exception of substitutions across multiple files (which is certainly of debatable value), it's likely to do what you want with multiple cursors.
There are many excellent tools out there for non-interactive text editing. For whole workspace changes, I'd just use sed
myself. 🤷♂️
With regex search/replace, you have the ability to use match groups and references to customize how your text will be replaced, so, e.g. :%s/"\(.*\)"/'\1'/
to replace double quotes with single quotes across a file. Or :%s/\([A-z_]\):/"\1":/
to help convert a javascript structure into a valid JSON structure. Does the average user do this often? I have no idea, but having some answer to this use case in core seems like table stakes. Certainly it's a pretty common thing for the vim users who (I assume?) make up most of helix's convert base.
An idea: Running %s...c
sets special registers "1
, "2
, "&
or something like that that correspond with match groups or the whole match, that will magically insert (using ctrl-R) the right object into each selection. I can imagine this being architecturally challenging since now these registers have to have some kind of per-selection structure, and things like behavior of backspace might need some finesse, but it preserves interactivity & instant feedback, and in combination with selection-jumping would provide probably 95+% of what :%
does.
Doing this in a plugin... I dunno. Maybe as an officially-blessed 1st party plugin? Part of what's so appealing about Helix is how powerful it is out of the box without the need for endless configuration of poorly-interacting plugins a la modern vim. Full-featured interactive search/replace is universally-applicable across editing tasks, and 100% batch-mode tools like sed are not really a replacement.
I have no idea, but having some answer to this use case in core seems like table stakes. Certainly it's a pretty common thing for the vim users who (I assume?) make up most of helix's convert base.
Not that my opinion is especially important, but as a long-time Vim user I agree 100%, and I consider the ability to use capture groups an essential feature, for reasons already stated by other folks here.
An idea: Running
%s...c
sets special registers"1
,"2
,"&
or something like that that correspond with match groups or the whole match, that will magically insert (using ctrl-R) the right object into each selection.
This is how Kakoune does it (which maybe you know, since you mentioned Ctrl-R). IDK if it is important to be consistent with Kakoune, since it's not very widely used at this time, but it wouldn't be a bad thing to use the same keystrokes, if there's no compelling reason to do otherwise.
I use regex captures almost daily in neovim. A good example is deciding that I want to invert a match in rust for some reason:
:'<,'>s/\(.*\)=>\(.*\),/\2 => \1,
I added my own command: https://github.com/pmeredit/helix/tree/topic/regex_command
I don't think this is master-worthy since it doesn't do things like live matching, but it's nice for any time you really need capture replacement (just uses regex crate replace and replace_all, so the capture groups are $0, $1, $n)
Can case-agnostic case-preserving replace be done with the current model?
An outside opinion: Helix makes it convenient enough to pipe the selection through external tools, and sed
is designed for this. What is the advantage of building a subset of sed
into Helix? I get the desire to reach for familiar features from Vim but having used Helix for a couple of weeks I no longer see a great need, in my own use cases. Multiple cursors or keyboard macros work great for most bulk interactive changes, and when neither of these, nor the LSP, are of help, it's a good indication that the change is complex enough that external tools like sed
(and awk
etc.) may be a better choice. The big advantage I see here is that you can start naming your repetitive editing processes and save yourself time in the future.
@adrian5 You can press
%
to select the whole buffer, thens
to filter by regex. If this gives you multiple selections, you can cycle through them by pressingn
to discard a selection or)
to keep it. If everything looks good, pressc
to change all selections.
That's a really neat trick! @lukepighetti I think it can be a good topic for another cool Helix video. Usage of (?-i) prefix to switch case could also benefit from some light and it's within the same topic.