From b9431b2273059aa95d9e1cc3837e33c465561296 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?J=C3=B6ran=20Karl?= <3951388+JoeKar@users.noreply.github.com> Date: Fri, 6 Oct 2023 20:39:24 +0200 Subject: [PATCH 1/4] command: Fix replace to be able to insert '$' --- internal/action/command.go | 1 + 1 file changed, 1 insertion(+) diff --git a/internal/action/command.go b/internal/action/command.go index e241d687d..eee5cde55 100644 --- a/internal/action/command.go +++ b/internal/action/command.go @@ -794,6 +794,7 @@ func (h *BufPane) ReplaceCmd(args []string) { if noRegex { search = regexp.QuoteMeta(search) + replaceStr = strings.ReplaceAll(replaceStr, "$", "$$") } replace := []byte(replaceStr) From deaf8f945a4c4d4430b0461d68039dce29270cf6 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?J=C3=B6ran=20Karl?= <3951388+JoeKar@users.noreply.github.com> Date: Thu, 25 Jan 2024 20:37:45 +0100 Subject: [PATCH 2/4] help: commands: Precise the documentation of `replace` --- runtime/help/commands.md | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/runtime/help/commands.md b/runtime/help/commands.md index 8bc4e8ab8..271a95cbc 100644 --- a/runtime/help/commands.md +++ b/runtime/help/commands.md @@ -42,6 +42,10 @@ quotes here but these are not necessary when entering the command in micro. Note that `search` must be a valid regex (unless `-l` is passed). If one of the arguments does not have any spaces in it, you may omit the quotes. + In case the search is done non-literal (without `-l`) the character `$` can't + be simply added, since `Regexp.Expand()` is used in the background to add + more complex template replacement functionality. To add a `$` a double `$$` + must be used. * `replaceall 'search' 'value'`: this will replace all occurrences of `search` with `value` without user confirmation. From 111ac9f416ad05aaf878fd9546910bfc65d8887d Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?J=C3=B6ran=20Karl?= <3951388+JoeKar@users.noreply.github.com> Date: Mon, 19 Feb 2024 18:49:44 +0100 Subject: [PATCH 3/4] help: commands: Further improvement suggested within the review Co-authored-by: Beni Cherniavsky-Paskin --- runtime/help/commands.md | 10 ++++++---- 1 file changed, 6 insertions(+), 4 deletions(-) diff --git a/runtime/help/commands.md b/runtime/help/commands.md index 271a95cbc..8e191f9fe 100644 --- a/runtime/help/commands.md +++ b/runtime/help/commands.md @@ -42,10 +42,12 @@ quotes here but these are not necessary when entering the command in micro. Note that `search` must be a valid regex (unless `-l` is passed). If one of the arguments does not have any spaces in it, you may omit the quotes. - In case the search is done non-literal (without `-l`) the character `$` can't - be simply added, since `Regexp.Expand()` is used in the background to add - more complex template replacement functionality. To add a `$` a double `$$` - must be used. + + In case the search is done non-literal (without `-l`), the 'value' + is interpreted as a template: + * `$3` or `${3}` substitutes the submatch of the 3rd (capturing group) + * `$foo` or `${foo}` substitutes the submatch of the (?Pnamed group) + * You have to write `$$` to substitute a literal dollar. * `replaceall 'search' 'value'`: this will replace all occurrences of `search` with `value` without user confirmation. From 1742cdead10eed200cabfebea884c6d42e7f40c3 Mon Sep 17 00:00:00 2001 From: Dmytro Maluka Date: Sat, 16 Mar 2024 13:15:15 +0100 Subject: [PATCH 4/4] Fix replace with '$' in a more kosher way On top of JoeKar's fix. --- internal/action/command.go | 5 ++--- internal/buffer/search.go | 12 ++++++++---- 2 files changed, 10 insertions(+), 7 deletions(-) diff --git a/internal/action/command.go b/internal/action/command.go index eee5cde55..32ed686b1 100644 --- a/internal/action/command.go +++ b/internal/action/command.go @@ -794,7 +794,6 @@ func (h *BufPane) ReplaceCmd(args []string) { if noRegex { search = regexp.QuoteMeta(search) - replaceStr = strings.ReplaceAll(replaceStr, "$", "$$") } replace := []byte(replaceStr) @@ -821,7 +820,7 @@ func (h *BufPane) ReplaceCmd(args []string) { end = h.Cursor.CurSelection[1] } if all { - nreplaced, _ = h.Buf.ReplaceRegex(start, end, regex, replace) + nreplaced, _ = h.Buf.ReplaceRegex(start, end, regex, replace, !noRegex) } else { inRange := func(l buffer.Loc) bool { return l.GreaterEqual(start) && l.LessEqual(end) @@ -851,7 +850,7 @@ func (h *BufPane) ReplaceCmd(args []string) { InfoBar.YNPrompt("Perform replacement (y,n,esc)", func(yes, canceled bool) { if !canceled && yes { - _, nrunes := h.Buf.ReplaceRegex(locs[0], locs[1], regex, replace) + _, nrunes := h.Buf.ReplaceRegex(locs[0], locs[1], regex, replace, !noRegex) searchLoc = locs[0] searchLoc.X += nrunes + locs[0].Diff(locs[1], h.Buf) diff --git a/internal/buffer/search.go b/internal/buffer/search.go index 0ab6d0c8f..14ae25174 100644 --- a/internal/buffer/search.go +++ b/internal/buffer/search.go @@ -148,7 +148,7 @@ func (b *Buffer) FindNext(s string, start, end, from Loc, down bool, useRegex bo // ReplaceRegex replaces all occurrences of 'search' with 'replace' in the given area // and returns the number of replacements made and the number of runes // added or removed on the last line of the range -func (b *Buffer) ReplaceRegex(start, end Loc, search *regexp.Regexp, replace []byte) (int, int) { +func (b *Buffer) ReplaceRegex(start, end Loc, search *regexp.Regexp, replace []byte, captureGroups bool) (int, int) { if start.GreaterThan(end) { start, end = end, start } @@ -172,9 +172,13 @@ func (b *Buffer) ReplaceRegex(start, end Loc, search *regexp.Regexp, replace []b l = util.SliceStart(l, end.X) } newText := search.ReplaceAllFunc(l, func(in []byte) []byte { - result := []byte{} - for _, submatches := range search.FindAllSubmatchIndex(in, -1) { - result = search.Expand(result, replace, in, submatches) + var result []byte + if captureGroups { + for _, submatches := range search.FindAllSubmatchIndex(in, -1) { + result = search.Expand(result, replace, in, submatches) + } + } else { + result = replace } found++ if i == end.Y {