diff options
author | Emanuel Sprung <emanuel.sprung@gmail.com> | 2020-04-23 20:29:38 +0200 |
---|---|---|
committer | Andreas Kling <kling@serenityos.org> | 2020-11-27 21:32:41 +0100 |
commit | 3b7884ee8a7dca2ff7732e28a117143590717efe (patch) | |
tree | 07d6806297fdbfadab3d4c2c9785f175a4efd680 /Libraries/LibGUI | |
parent | 4a630d4b633d99e5870331a91adfbd6a1793e496 (diff) | |
download | serenity-3b7884ee8a7dca2ff7732e28a117143590717efe.zip |
TextEditor: Add button to match regular expression during search
Diffstat (limited to 'Libraries/LibGUI')
-rw-r--r-- | Libraries/LibGUI/CMakeLists.txt | 2 | ||||
-rw-r--r-- | Libraries/LibGUI/TextDocument.cpp | 128 | ||||
-rw-r--r-- | Libraries/LibGUI/TextDocument.h | 15 |
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 { |