From fe16b5c067d522b6789e08f7a24264372cbe5813 Mon Sep 17 00:00:00 2001 From: EnumDev Date: Mon, 16 Jun 2025 13:55:02 +0300 Subject: [PATCH] Add 'find' 'replace' and 'replace-all' commands and key bindings --- config/keybindings.yml | 8 ++- src/buffer.go | 45 ++++++++++++++ src/command.go | 129 +++++++++++++++++++++++++++++++++++++++++ 3 files changed, 181 insertions(+), 1 deletion(-) diff --git a/config/keybindings.yml b/config/keybindings.yml index 4705034..3d64e04 100644 --- a/config/keybindings.yml +++ b/config/keybindings.yml @@ -14,9 +14,15 @@ keybindings: - keybinding: "Ctrl-O" cursor_modes: ["buffer"] command: "open" - - keybinding: "Ctrl-R" + - keybinding: "Ctrl-L" cursor_modes: ["buffer"] command: "reload" + - keybinding: "Ctrl-F" + cursor_modes: [ "buffer" ] + command: "find" + - keybinding: "Ctrl-R" + cursor_modes: [ "buffer" ] + command: "replace" - keybinding: "PgUp" cursor_modes: ["buffer"] command: "prev-buffer" diff --git a/src/buffer.go b/src/buffer.go index 19c9337..dfead49 100644 --- a/src/buffer.go +++ b/src/buffer.go @@ -248,6 +248,51 @@ func (buffer *Buffer) PasteText(window *Window, text string) { window.SetCursorPos(buffer.CursorPos + len(text)) } +func (buffer *Buffer) FindSubstring(substring string, afterPos int) int { + // Return no match if afterPos is larger than the buffer contents size + if afterPos >= len(buffer.Contents) { + return -1 + } + + index := strings.Index(buffer.Contents[afterPos+1:], substring) + + if index != -1 { + index += afterPos + 1 + } + return index +} + +func (buffer *Buffer) FindAndReplaceSubstring(substring, replacement string, afterPos int) int { + index := buffer.FindSubstring(substring, afterPos) + + // Return if substring isn't found + if index == -1 { + return -1 + } + + // Replace substring with replacement string + buffer.Contents = buffer.Contents[:index] + replacement + buffer.Contents[index+len(substring):] + + return index +} + +func (buffer *Buffer) FindAndReplaceAll(substring, replacement string) int { + replacements := 0 + index := 0 + for index != -1 { + index = buffer.FindAndReplaceSubstring(substring, replacement, index) + if index != -1 { + replacements++ + } + + if index == 0 { + index++ + } + } + + return replacements +} + func GetOpenFileBuffer(filename string) *Buffer { // Replace tilde with home directory if filename != "~" && strings.HasPrefix(filename, "~/") { diff --git a/src/command.go b/src/command.go index bbef303..cfbaf00 100644 --- a/src/command.go +++ b/src/command.go @@ -129,6 +129,132 @@ func initCommands() { }, } + findCmd := Command{ + cmd: "find", + run: func(window *Window, args ...string) { + if len(args) >= 1 { + input := args[0] + + if input == "" { + return + } + + pos := window.CurrentBuffer.FindSubstring(input, window.CurrentBuffer.CursorPos) + if pos >= 0 { + window.SetCursorPos(pos) + PrintMessage(window, "Match found.") + } else { + PrintMessage(window, fmt.Sprintf("'%s' not found in buffer!", input)) + } + + return + } + + inputChannel := RequestInput(window, "Substring to search for:", "") + go func() { + input := <-inputChannel + + if input == "" { + return + } + + pos := window.CurrentBuffer.FindSubstring(input, window.CurrentBuffer.CursorPos) + if pos >= 0 { + window.SetCursorPos(pos) + PrintMessage(window, "Match found.") + } else { + PrintMessage(window, fmt.Sprintf("'%s' not found in buffer!", input)) + } + }() + }, + } + + replaceCmd := Command{ + cmd: "replace", + run: func(window *Window, args ...string) { + if len(args) >= 2 { + findStr := args[0] + replaceStr := args[1] + + if findStr == "" { + return + } + + pos := window.CurrentBuffer.FindAndReplaceSubstring(findStr, replaceStr, window.CurrentBuffer.CursorPos) + if pos >= 0 { + window.SetCursorPos(pos) + PrintMessage(window, "Match replaced successfully.") + } else { + PrintMessage(window, fmt.Sprintf("'%s' not found in buffer!", findStr)) + } + + return + } + + go func() { + inputChannel := RequestInput(window, "Substring to search for:", "") + findStr := <-inputChannel + if findStr == "" { + return + } + + inputChannel = RequestInput(window, "String to replace with:", "") + replaceStr := <-inputChannel + + pos := window.CurrentBuffer.FindAndReplaceSubstring(findStr, replaceStr, window.CurrentBuffer.CursorPos) + if pos >= 0 { + window.SetCursorPos(pos) + PrintMessage(window, "Match replaced successfully.") + } else { + PrintMessage(window, fmt.Sprintf("'%s' not found in buffer!", findStr)) + } + }() + }, + } + + replaceAllCmd := Command{ + cmd: "replace-all", + run: func(window *Window, args ...string) { + if len(args) >= 2 { + findStr := args[0] + replaceStr := args[1] + + if findStr == "" { + return + } + + replacements := window.CurrentBuffer.FindAndReplaceAll(findStr, replaceStr) + if replacements > 0 { + window.SetCursorPos(window.CurrentBuffer.CursorPos) + PrintMessage(window, fmt.Sprintf("Replaced all %d matches successfully.", replacements)) + } else { + PrintMessage(window, fmt.Sprintf("'%s' not found in buffer!", findStr)) + } + + return + } + + go func() { + inputChannel := RequestInput(window, "Substring to search for:", "") + findStr := <-inputChannel + if findStr == "" { + return + } + + inputChannel = RequestInput(window, "String to replace with:", "") + replaceStr := <-inputChannel + + replacements := window.CurrentBuffer.FindAndReplaceAll(findStr, replaceStr) + if replacements > 0 { + window.SetCursorPos(window.CurrentBuffer.CursorPos) + PrintMessage(window, fmt.Sprintf("Replaced all %d matches successfully.", replacements)) + } else { + PrintMessage(window, fmt.Sprintf("'%s' not found in buffer!", findStr)) + } + }() + }, + } + prevBufferCmd := Command{ cmd: "prev-buffer", run: func(window *Window, args ...string) { @@ -353,6 +479,9 @@ func initCommands() { commands["save"] = &saveCmd commands["open"] = &openCmd commands["reload"] = &reloadCmd + commands["find"] = &findCmd + commands["replace"] = &replaceCmd + commands["replace-all"] = &replaceAllCmd commands["prev-buffer"] = &prevBufferCmd commands["next-buffer"] = &nextBufferCmd commands["new-buffer"] = &newBufferCmd