From e4b84dde90a64a2b8f80b64fe2a55d5135a7c988 Mon Sep 17 00:00:00 2001 From: Krzosa Karol Date: Sat, 27 Dec 2025 15:54:10 +0100 Subject: [PATCH] Search case sensitive and word boundary --- src/basic/basic_string.cpp | 50 ++++++++++++++++++++++++------- src/basic/basic_string.h | 1 + src/basic/basic_string16.cpp | 50 ++++++++++++++++++++++++------- src/text_editor/globals.cpp | 2 ++ src/text_editor/view.cpp | 26 +++++++++++----- src/text_editor/window_search.cpp | 8 ++++- src/text_editor/window_status.cpp | 5 ++-- 7 files changed, 110 insertions(+), 32 deletions(-) diff --git a/src/basic/basic_string.cpp b/src/basic/basic_string.cpp index b950a50..42f9e63 100644 --- a/src/basic/basic_string.cpp +++ b/src/basic/basic_string.cpp @@ -206,26 +206,54 @@ API String NormalizePath(Allocator allocator, String s) { API bool Seek(String string, String find, int64_t *index_out, SeekFlag flags) { bool ignore_case = flags & SeekFlag_IgnoreCase ? true : false; - bool result = false; + bool result = false; if (flags & SeekFlag_MatchFindLast) { for (int64_t i = string.len; i != 0; i--) { - int64_t index = i - 1; - String substring = GetSlice(string, index, index + find.len); + int64_t index = i - 1; + String substring = GetSlice(string, index, index + find.len); if (AreEqual(substring, find, ignore_case)) { - if (index_out) - *index_out = index; - result = true; - break; + bool ok_boundary = true; + if (flags & SeekFlag_WordBoundary) { + String left = GetSlice(string, i - 1, i); + String right = GetSlice(string, i + find.len, i + find.len + 1); + if (left.len != 0 && !IsNonWord(left.data[0])) { + ok_boundary = false; + } + if (right.len != 0 && !IsNonWord(right.data[0])) { + ok_boundary = false; + } + } + if (ok_boundary) { + if (index_out) { + *index_out = index; + } + result = true; + break; + } } } } else { for (int64_t i = 0; i < string.len; i++) { String substring = GetSlice(string, i, i + find.len); if (AreEqual(substring, find, ignore_case)) { - if (index_out) - *index_out = i; - result = true; - break; + bool ok_boundary = true; + if (flags & SeekFlag_WordBoundary) { + String left = GetSlice(string, i - 1, i); + String right = GetSlice(string, i + find.len, i + find.len + 1); + if (left.len != 0 && !IsNonWord(left.data[0])) { + ok_boundary = false; + } + if (right.len != 0 && !IsNonWord(right.data[0])) { + ok_boundary = false; + } + } + if (ok_boundary) { + if (index_out) { + *index_out = i; + } + result = true; + break; + } } } } diff --git a/src/basic/basic_string.h b/src/basic/basic_string.h index e5a609b..a9c2a7d 100644 --- a/src/basic/basic_string.h +++ b/src/basic/basic_string.h @@ -97,6 +97,7 @@ enum { SeekFlag_None = 0, SeekFlag_IgnoreCase = 1, SeekFlag_MatchFindLast = 2, + SeekFlag_WordBoundary = 4, }; API bool Seek(String string, String find, int64_t *index_out = NULL, SeekFlag flags = SeekFlag_None); diff --git a/src/basic/basic_string16.cpp b/src/basic/basic_string16.cpp index ce7004f..ff8f953 100644 --- a/src/basic/basic_string16.cpp +++ b/src/basic/basic_string16.cpp @@ -216,26 +216,54 @@ API String16 NormalizePath(Allocator allocator, String16 s) { API bool Seek(String16 string, String16 find, int64_t *index_out, SeekFlag flags) { bool ignore_case = flags & SeekFlag_IgnoreCase ? true : false; - bool result = false; + bool result = false; if (flags & SeekFlag_MatchFindLast) { for (int64_t i = string.len; i != 0; i--) { - int64_t index = i - 1; - String16 substring = GetSlice(string, index, index + find.len); + int64_t index = i - 1; + String16 substring = GetSlice(string, index, index + find.len); if (AreEqual(substring, find, ignore_case)) { - if (index_out) - *index_out = index; - result = true; - break; + bool ok_boundary = true; + if (flags & SeekFlag_WordBoundary) { + String16 left = GetSlice(string, i - 1, i); + String16 right = GetSlice(string, i + find.len, i + find.len + 1); + if (left.len != 0 && !IsNonWord(left.data[0])) { + ok_boundary = false; + } + if (right.len != 0 && !IsNonWord(right.data[0])) { + ok_boundary = false; + } + } + if (ok_boundary) { + if (index_out) { + *index_out = index; + } + result = true; + break; + } } } } else { for (int64_t i = 0; i < string.len; i++) { String16 substring = GetSlice(string, i, i + find.len); if (AreEqual(substring, find, ignore_case)) { - if (index_out) - *index_out = i; - result = true; - break; + bool ok_boundary = true; + if (flags & SeekFlag_WordBoundary) { + String16 left = GetSlice(string, i - 1, i); + String16 right = GetSlice(string, i + find.len, i + find.len + 1); + if (left.len != 0 && !IsNonWord(left.data[0])) { + ok_boundary = false; + } + if (right.len != 0 && !IsNonWord(right.data[0])) { + ok_boundary = false; + } + } + if (ok_boundary) { + if (index_out) { + *index_out = i; + } + result = true; + break; + } } } } diff --git a/src/text_editor/globals.cpp b/src/text_editor/globals.cpp index 5ae1aed..ee464d3 100644 --- a/src/text_editor/globals.cpp +++ b/src/text_editor/globals.cpp @@ -6,6 +6,8 @@ bool Testing = false; bool AppIsRunning = true; bool WaitForEvents = true; bool RunGCThisFrame; +bool SearchCaseSensitive = false; +bool SearchWordBoundary = false; WindowID WindowIDs; ViewID ViewIDs; diff --git a/src/text_editor/view.cpp b/src/text_editor/view.cpp index a6e05de..cf4eb16 100644 --- a/src/text_editor/view.cpp +++ b/src/text_editor/view.cpp @@ -124,16 +124,20 @@ void IdentedNewLine(View *view) { } Caret FindPrev(Buffer *buffer, String16 needle, Caret caret) { - Int pos = GetFront(caret); + Int pos = GetFront(caret); String16 medium = GetString(buffer, {0, pos}); + SeekFlag flag = SearchCaseSensitive ? SeekFlag_None : SeekFlag_IgnoreCase; + if (SearchWordBoundary) { + flag |= SeekFlag_WordBoundary; + } Caret result = caret; Int index = 0; - if (Seek(medium, needle, &index, SeekFlag_MatchFindLast)) { + if (Seek(medium, needle, &index, flag | SeekFlag_MatchFindLast)) { result = MakeCaret(index, index + needle.len); } else { medium = GetString(buffer); - if (Seek(medium, needle, &index, SeekFlag_MatchFindLast)) { + if (Seek(medium, needle, &index, flag | SeekFlag_MatchFindLast)) { result = MakeCaret(index, index + needle.len); } } @@ -142,16 +146,20 @@ Caret FindPrev(Buffer *buffer, String16 needle, Caret caret) { } Caret FindNext(Buffer *buffer, String16 needle, Caret caret) { - Int pos = GetMax(caret); + Int pos = GetMax(caret); String16 medium = GetString(buffer, {pos, INT64_MAX}); + SeekFlag flag = SearchCaseSensitive ? SeekFlag_None : SeekFlag_IgnoreCase; + if (SearchWordBoundary) { + flag |= SeekFlag_WordBoundary; + } Caret result = caret; Int index = 0; - if (Seek(medium, needle, &index)) { + if (Seek(medium, needle, &index, flag)) { result = MakeCaret(pos + index + needle.len, pos + index); } else { medium = GetString(buffer); - if (Seek(medium, needle, &index)) { + if (Seek(medium, needle, &index, flag)) { result = MakeCaret(index + needle.len, index); } } @@ -163,9 +171,13 @@ Array FindAll(Allocator allocator, Buffer *buffer, String16 needle) { Array result = {allocator}; String16 string = GetString(buffer); String16 start = string; + SeekFlag flag = SearchCaseSensitive ? SeekFlag_None : SeekFlag_IgnoreCase; + if (SearchWordBoundary) { + flag |= SeekFlag_WordBoundary; + } for (;;) { Int index = 0; - if (Seek(string, needle, &index)) { + if (Seek(string, needle, &index, flag)) { Int back = index + (Int)(string.data - start.data); Int front = back + needle.len; Add(&result, MakeCaret(front, back)); diff --git a/src/text_editor/window_search.cpp b/src/text_editor/window_search.cpp index 9e0cb56..7875b27 100644 --- a/src/text_editor/window_search.cpp +++ b/src/text_editor/window_search.cpp @@ -100,7 +100,6 @@ void Command_SearchAll() { set.window->visible = false; } RegisterCommand(Command_SearchAll, "alt-f3"); - void Command_SearchAllInSearch() { if (ActiveWindowID != SearchWindowID) { return; @@ -110,6 +109,13 @@ void Command_SearchAllInSearch() { set.window->visible = false; } RegisterCommand(Command_SearchAllInSearch, "alt-enter"); +void Command_ToggleCaseSensitiveSearch() { + SearchCaseSensitive = !SearchCaseSensitive; +} RegisterCommand(Command_ToggleCaseSensitiveSearch, "alt-c"); + +void Command_ToggleSearchWordBoundary() { + SearchWordBoundary = !SearchWordBoundary; +} RegisterCommand(Command_ToggleSearchWordBoundary, "alt-w"); void SearchWindowUpdate() { BSet active = GetBSet(ActiveWindowID); diff --git a/src/text_editor/window_status.cpp b/src/text_editor/window_status.cpp index 9622dc7..272be76 100644 --- a/src/text_editor/window_status.cpp +++ b/src/text_editor/window_status.cpp @@ -81,10 +81,11 @@ void StatusWindowUpdate() { Array edits = ReplaceEx(scratch, title.view, u" |"); } - // replace data up to separator with filename and stuff const char *reopen = main.buffer->changed_on_disk ? " :Reopen" : ""; - String s = Format(scratch, "# %S:%lld:%lld%s", main.buffer->name, (long long)xy.line + 1ll, (long long)xy.col + 1ll, reopen); + const char *case_sens = SearchCaseSensitive ? " C" : ""; + const char *word_bound = SearchWordBoundary ? " W" : ""; + String s = Format(scratch, "# %S:%lld:%lld%s%s", main.buffer->name, (long long)xy.line + 1ll, (long long)xy.col + 1ll, case_sens, word_bound, reopen); For (ActiveProcesses) { if (it.view_id == main.view->id.id) { s = Format(scratch, "%S %lld :KillProcess", s, (long long)it.id);