summaryrefslogtreecommitdiff
path: root/Libraries/LibGUI
diff options
context:
space:
mode:
authorEmanuel Sprung <emanuel.sprung@gmail.com>2020-04-23 20:29:38 +0200
committerAndreas Kling <kling@serenityos.org>2020-11-27 21:32:41 +0100
commit3b7884ee8a7dca2ff7732e28a117143590717efe (patch)
tree07d6806297fdbfadab3d4c2c9785f175a4efd680 /Libraries/LibGUI
parent4a630d4b633d99e5870331a91adfbd6a1793e496 (diff)
downloadserenity-3b7884ee8a7dca2ff7732e28a117143590717efe.zip
TextEditor: Add button to match regular expression during search
Diffstat (limited to 'Libraries/LibGUI')
-rw-r--r--Libraries/LibGUI/CMakeLists.txt2
-rw-r--r--Libraries/LibGUI/TextDocument.cpp128
-rw-r--r--Libraries/LibGUI/TextDocument.h15
3 files changed, 137 insertions, 8 deletions
diff --git a/Libraries/LibGUI/CMakeLists.txt b/Libraries/LibGUI/CMakeLists.txt
index 8cadd07b4f..a412c228bb 100644
--- a/Libraries/LibGUI/CMakeLists.txt
+++ b/Libraries/LibGUI/CMakeLists.txt
@@ -94,4 +94,4 @@ set(GENERATED_SOURCES
)
serenity_lib(LibGUI gui)
-target_link_libraries(LibGUI LibCore LibGfx LibIPC LibThread LibCpp)
+target_link_libraries(LibGUI LibCore LibGfx LibIPC LibThread LibCpp LibRegex)
diff --git a/Libraries/LibGUI/TextDocument.cpp b/Libraries/LibGUI/TextDocument.cpp
index 137cbdab94..e3fa5d804e 100644
--- a/Libraries/LibGUI/TextDocument.cpp
+++ b/Libraries/LibGUI/TextDocument.cpp
@@ -30,6 +30,7 @@
#include <LibCore/Timer.h>
#include <LibGUI/TextDocument.h>
#include <LibGUI/TextEditor.h>
+#include <LibRegex/Regex.h>
#include <ctype.h>
namespace GUI {
@@ -272,6 +273,8 @@ void TextDocument::notify_did_change()
for (auto* client : m_clients)
client->document_did_change();
}
+
+ m_regex_needs_update = true;
}
void TextDocument::set_all_cursors(const TextPosition& position)
@@ -350,11 +353,78 @@ TextPosition TextDocument::previous_position_before(const TextPosition& position
return { position.line(), position.column() - 1 };
}
-TextRange TextDocument::find_next(const StringView& needle, const TextPosition& start, SearchShouldWrap should_wrap) const
+void TextDocument::update_regex_matches(const StringView& needle)
+{
+ if (m_regex_needs_update || needle != m_regex_needle) {
+ Regex<PosixExtended> re(needle);
+
+ Vector<RegexStringView> views;
+
+ for (size_t line = 0; line < m_lines.size(); ++line) {
+ views.append(m_lines.at(line).view());
+ }
+ re.search(views, m_regex_result);
+ m_regex_needs_update = false;
+ m_regex_needle = String { needle };
+ m_regex_result_match_index = -1;
+ m_regex_result_match_capture_group_index = -1;
+ }
+}
+
+TextRange TextDocument::find_next(const StringView& needle, const TextPosition& start, SearchShouldWrap should_wrap, bool regmatch)
{
if (needle.is_empty())
return {};
+ if (regmatch) {
+ if (!m_regex_result.matches.size())
+ return {};
+
+ regex::Match match;
+ bool use_whole_match { false };
+
+ auto next_match = [&] {
+ m_regex_result_match_capture_group_index = 0;
+ if (m_regex_result_match_index == m_regex_result.matches.size() - 1) {
+ if (should_wrap == SearchShouldWrap::Yes)
+ m_regex_result_match_index = 0;
+ else
+ ++m_regex_result_match_index;
+ } else
+ ++m_regex_result_match_index;
+ };
+
+ if (m_regex_result.n_capture_groups) {
+ if (m_regex_result_match_index >= m_regex_result.capture_group_matches.size())
+ next_match();
+ else {
+ // check if last capture group has been reached
+ if (m_regex_result_match_capture_group_index >= m_regex_result.capture_group_matches.at(m_regex_result_match_index).size()) {
+ next_match();
+ } else {
+ // get to the next capture group item
+ ++m_regex_result_match_capture_group_index;
+ }
+ }
+
+ // use whole match, if there is no capture group for current index
+ if (m_regex_result_match_index >= m_regex_result.capture_group_matches.size())
+ use_whole_match = true;
+ else if (m_regex_result_match_capture_group_index >= m_regex_result.capture_group_matches.at(m_regex_result_match_index).size())
+ next_match();
+
+ } else {
+ next_match();
+ }
+
+ if (use_whole_match || !m_regex_result.capture_group_matches.at(m_regex_result_match_index).size())
+ match = m_regex_result.matches.at(m_regex_result_match_index);
+ else
+ match = m_regex_result.capture_group_matches.at(m_regex_result_match_index).at(m_regex_result_match_capture_group_index);
+
+ return TextRange { { match.line, match.column }, { match.line, match.column + match.view.length() } };
+ }
+
TextPosition position = start.is_valid() ? start : TextPosition(0, 0);
TextPosition original_position = position;
@@ -381,11 +451,61 @@ TextRange TextDocument::find_next(const StringView& needle, const TextPosition&
return {};
}
-TextRange TextDocument::find_previous(const StringView& needle, const TextPosition& start, SearchShouldWrap should_wrap) const
+TextRange TextDocument::find_previous(const StringView& needle, const TextPosition& start, SearchShouldWrap should_wrap, bool regmatch)
{
if (needle.is_empty())
return {};
+ if (regmatch) {
+ if (!m_regex_result.matches.size())
+ return {};
+
+ regex::Match match;
+ bool use_whole_match { false };
+
+ auto next_match = [&] {
+ if (m_regex_result_match_index == 0) {
+ if (should_wrap == SearchShouldWrap::Yes)
+ m_regex_result_match_index = m_regex_result.matches.size() - 1;
+ else
+ --m_regex_result_match_index;
+ } else
+ --m_regex_result_match_index;
+
+ m_regex_result_match_capture_group_index = m_regex_result.capture_group_matches.at(m_regex_result_match_index).size() - 1;
+ };
+
+ if (m_regex_result.n_capture_groups) {
+ if (m_regex_result_match_index >= m_regex_result.capture_group_matches.size())
+ next_match();
+ else {
+ // check if last capture group has been reached
+ if (m_regex_result_match_capture_group_index >= m_regex_result.capture_group_matches.at(m_regex_result_match_index).size()) {
+ next_match();
+ } else {
+ // get to the next capture group item
+ --m_regex_result_match_capture_group_index;
+ }
+ }
+
+ // use whole match, if there is no capture group for current index
+ if (m_regex_result_match_index >= m_regex_result.capture_group_matches.size())
+ use_whole_match = true;
+ else if (m_regex_result_match_capture_group_index >= m_regex_result.capture_group_matches.at(m_regex_result_match_index).size())
+ next_match();
+
+ } else {
+ next_match();
+ }
+
+ if (use_whole_match || !m_regex_result.capture_group_matches.at(m_regex_result_match_index).size())
+ match = m_regex_result.matches.at(m_regex_result_match_index);
+ else
+ match = m_regex_result.capture_group_matches.at(m_regex_result_match_index).at(m_regex_result_match_capture_group_index);
+
+ return TextRange { { match.line, match.column }, { match.line, match.column + match.view.length() } };
+ }
+
TextPosition position = start.is_valid() ? start : TextPosition(0, 0);
position = previous_position_before(position, should_wrap);
TextPosition original_position = position;
@@ -413,13 +533,13 @@ TextRange TextDocument::find_previous(const StringView& needle, const TextPositi
return {};
}
-Vector<TextRange> TextDocument::find_all(const StringView& needle) const
+Vector<TextRange> TextDocument::find_all(const StringView& needle, bool regmatch)
{
Vector<TextRange> ranges;
TextPosition position;
for (;;) {
- auto range = find_next(needle, position, SearchShouldWrap::No);
+ auto range = find_next(needle, position, SearchShouldWrap::No, regmatch);
if (!range.is_valid())
break;
ranges.append(range);
diff --git a/Libraries/LibGUI/TextDocument.h b/Libraries/LibGUI/TextDocument.h
index 27b9f4b10f..d20ef059da 100644
--- a/Libraries/LibGUI/TextDocument.h
+++ b/Libraries/LibGUI/TextDocument.h
@@ -39,6 +39,7 @@
#include <LibGUI/UndoStack.h>
#include <LibGfx/Color.h>
#include <LibGfx/Forward.h>
+#include <LibRegex/Regex.h>
namespace GUI {
@@ -108,10 +109,11 @@ public:
String text() const;
String text_in_range(const TextRange&) const;
- Vector<TextRange> find_all(const StringView& needle) const;
+ Vector<TextRange> find_all(const StringView& needle, bool regmatch = false);
- TextRange find_next(const StringView&, const TextPosition& start = {}, SearchShouldWrap = SearchShouldWrap::Yes) const;
- TextRange find_previous(const StringView&, const TextPosition& start = {}, SearchShouldWrap = SearchShouldWrap::Yes) const;
+ void update_regex_matches(const StringView&);
+ TextRange find_next(const StringView&, const TextPosition& start = {}, SearchShouldWrap = SearchShouldWrap::Yes, bool regmatch = false);
+ TextRange find_previous(const StringView&, const TextPosition& start = {}, SearchShouldWrap = SearchShouldWrap::Yes, bool regmatch = false);
TextPosition next_position_after(const TextPosition&, SearchShouldWrap = SearchShouldWrap::Yes) const;
TextPosition previous_position_before(const TextPosition&, SearchShouldWrap = SearchShouldWrap::Yes) const;
@@ -158,6 +160,13 @@ private:
UndoStack m_undo_stack;
RefPtr<Core::Timer> m_undo_timer;
+
+ RegexResult m_regex_result;
+ size_t m_regex_result_match_index { 0 };
+ size_t m_regex_result_match_capture_group_index { 0 };
+
+ bool m_regex_needs_update { true };
+ String m_regex_needle;
};
class TextDocumentLine {