mirror of
https://github.com/RGBCube/serenity
synced 2025-05-31 11:18:11 +00:00
TextEditor: Add button to match regular expression during search
This commit is contained in:
parent
4a630d4b63
commit
3b7884ee8a
8 changed files with 176 additions and 18 deletions
|
@ -7,4 +7,4 @@ set(SOURCES
|
||||||
)
|
)
|
||||||
|
|
||||||
serenity_bin(TextEditor)
|
serenity_bin(TextEditor)
|
||||||
target_link_libraries(TextEditor LibWeb LibMarkdown LibGUI LibShell LibDesktop)
|
target_link_libraries(TextEditor LibWeb LibMarkdown LibGUI LibShell LibRegex LibDesktop)
|
||||||
|
|
|
@ -117,8 +117,12 @@ TextEditorWidget::TextEditorWidget()
|
||||||
dbgln("find_next(\"\")");
|
dbgln("find_next(\"\")");
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
auto found_range = m_editor->document().find_next(needle, m_editor->normalized_selection().end());
|
|
||||||
dbgln("find_next(\"{}\") returned {}", needle, found_range);
|
if (m_find_use_regex)
|
||||||
|
m_editor->document().update_regex_matches(needle);
|
||||||
|
|
||||||
|
auto found_range = m_editor->document().find_next(needle, m_editor->normalized_selection().end(), GUI::TextDocument::SearchShouldWrap::Yes, m_find_use_regex);
|
||||||
|
dbg() << "find_next(\"" << needle << "\") returned " << found_range;
|
||||||
if (found_range.is_valid()) {
|
if (found_range.is_valid()) {
|
||||||
m_editor->set_selection(found_range);
|
m_editor->set_selection(found_range);
|
||||||
} else {
|
} else {
|
||||||
|
@ -129,7 +133,12 @@ TextEditorWidget::TextEditorWidget()
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
|
||||||
m_find_previous_action = GUI::Action::create("Find previous", { Mod_Ctrl | Mod_Shift, Key_G }, Gfx::Bitmap::load_from_file("/res/icons/16x16/find-previous.png"), [&](auto&) {
|
m_find_regex_action = GUI::Action::create("Find regex", { Mod_Ctrl, Key_R }, [&](auto&) {
|
||||||
|
m_find_regex_button->set_checked(!m_find_regex_button->is_checked());
|
||||||
|
m_find_use_regex = m_find_regex_button->is_checked();
|
||||||
|
});
|
||||||
|
|
||||||
|
m_find_previous_action = GUI::Action::create("Find previous", { Mod_Ctrl | Mod_Shift, Key_G }, [&](auto&) {
|
||||||
auto needle = m_find_textbox->text();
|
auto needle = m_find_textbox->text();
|
||||||
if (needle.is_empty()) {
|
if (needle.is_empty()) {
|
||||||
dbgln("find_prev(\"\")");
|
dbgln("find_prev(\"\")");
|
||||||
|
@ -140,7 +149,10 @@ TextEditorWidget::TextEditorWidget()
|
||||||
if (!selection_start.is_valid())
|
if (!selection_start.is_valid())
|
||||||
selection_start = m_editor->normalized_selection().end();
|
selection_start = m_editor->normalized_selection().end();
|
||||||
|
|
||||||
auto found_range = m_editor->document().find_previous(needle, selection_start);
|
if (m_find_use_regex)
|
||||||
|
m_editor->document().update_regex_matches(needle);
|
||||||
|
|
||||||
|
auto found_range = m_editor->document().find_previous(needle, selection_start, GUI::TextDocument::SearchShouldWrap::Yes, m_find_use_regex);
|
||||||
|
|
||||||
dbgln("find_prev(\"{}\") returned {}", needle, found_range);
|
dbgln("find_prev(\"{}\") returned {}", needle, found_range);
|
||||||
if (found_range.is_valid()) {
|
if (found_range.is_valid()) {
|
||||||
|
@ -164,7 +176,10 @@ TextEditorWidget::TextEditorWidget()
|
||||||
if (!selection_start.is_valid())
|
if (!selection_start.is_valid())
|
||||||
selection_start = m_editor->normalized_selection().start();
|
selection_start = m_editor->normalized_selection().start();
|
||||||
|
|
||||||
auto found_range = m_editor->document().find_next(needle, selection_start);
|
if (m_find_use_regex)
|
||||||
|
m_editor->document().update_regex_matches(needle);
|
||||||
|
|
||||||
|
auto found_range = m_editor->document().find_next(needle, selection_start, GUI::TextDocument::SearchShouldWrap::Yes, m_find_use_regex);
|
||||||
|
|
||||||
if (found_range.is_valid()) {
|
if (found_range.is_valid()) {
|
||||||
m_editor->set_selection(found_range);
|
m_editor->set_selection(found_range);
|
||||||
|
@ -187,6 +202,9 @@ TextEditorWidget::TextEditorWidget()
|
||||||
if (!selection_start.is_valid())
|
if (!selection_start.is_valid())
|
||||||
selection_start = m_editor->normalized_selection().start();
|
selection_start = m_editor->normalized_selection().start();
|
||||||
|
|
||||||
|
if (m_find_use_regex)
|
||||||
|
m_editor->document().update_regex_matches(needle);
|
||||||
|
|
||||||
auto found_range = m_editor->document().find_previous(needle, selection_start);
|
auto found_range = m_editor->document().find_previous(needle, selection_start);
|
||||||
|
|
||||||
if (found_range.is_valid()) {
|
if (found_range.is_valid()) {
|
||||||
|
@ -205,12 +223,14 @@ TextEditorWidget::TextEditorWidget()
|
||||||
auto substitute = m_replace_textbox->text();
|
auto substitute = m_replace_textbox->text();
|
||||||
if (needle.is_empty())
|
if (needle.is_empty())
|
||||||
return;
|
return;
|
||||||
|
if (m_find_use_regex)
|
||||||
|
m_editor->document().update_regex_matches(needle);
|
||||||
|
|
||||||
auto found_range = m_editor->document().find_next(needle);
|
auto found_range = m_editor->document().find_next(needle, {}, GUI::TextDocument::SearchShouldWrap::Yes, m_find_use_regex);
|
||||||
while (found_range.is_valid()) {
|
while (found_range.is_valid()) {
|
||||||
m_editor->set_selection(found_range);
|
m_editor->set_selection(found_range);
|
||||||
m_editor->insert_at_cursor_or_replace_selection(substitute);
|
m_editor->insert_at_cursor_or_replace_selection(substitute);
|
||||||
found_range = m_editor->document().find_next(needle);
|
found_range = m_editor->document().find_next(needle, {}, GUI::TextDocument::SearchShouldWrap::Yes, m_find_use_regex);
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
|
||||||
|
@ -224,6 +244,11 @@ TextEditorWidget::TextEditorWidget()
|
||||||
m_find_next_button->click();
|
m_find_next_button->click();
|
||||||
};
|
};
|
||||||
|
|
||||||
|
m_find_regex_button = m_find_widget->add<GUI::Button>(".*");
|
||||||
|
m_find_regex_button->set_size_policy(GUI::SizePolicy::Fixed, GUI::SizePolicy::Fill);
|
||||||
|
m_find_regex_button->set_preferred_size(20, 0);
|
||||||
|
m_find_regex_button->set_action(*m_find_regex_action);
|
||||||
|
|
||||||
m_find_textbox->on_escape_pressed = [this] {
|
m_find_textbox->on_escape_pressed = [this] {
|
||||||
m_find_replace_widget->set_visible(false);
|
m_find_replace_widget->set_visible(false);
|
||||||
m_editor->set_focus(true);
|
m_editor->set_focus(true);
|
||||||
|
@ -358,6 +383,7 @@ TextEditorWidget::TextEditorWidget()
|
||||||
edit_menu.add_separator();
|
edit_menu.add_separator();
|
||||||
edit_menu.add_action(*m_find_replace_action);
|
edit_menu.add_action(*m_find_replace_action);
|
||||||
edit_menu.add_action(*m_find_next_action);
|
edit_menu.add_action(*m_find_next_action);
|
||||||
|
edit_menu.add_action(*m_find_regex_action);
|
||||||
edit_menu.add_action(*m_find_previous_action);
|
edit_menu.add_action(*m_find_previous_action);
|
||||||
edit_menu.add_action(*m_replace_next_action);
|
edit_menu.add_action(*m_replace_next_action);
|
||||||
edit_menu.add_action(*m_replace_previous_action);
|
edit_menu.add_action(*m_replace_previous_action);
|
||||||
|
|
|
@ -77,6 +77,7 @@ private:
|
||||||
RefPtr<GUI::Action> m_line_wrapping_setting_action;
|
RefPtr<GUI::Action> m_line_wrapping_setting_action;
|
||||||
|
|
||||||
RefPtr<GUI::Action> m_find_next_action;
|
RefPtr<GUI::Action> m_find_next_action;
|
||||||
|
RefPtr<GUI::Action> m_find_regex_action;
|
||||||
RefPtr<GUI::Action> m_find_previous_action;
|
RefPtr<GUI::Action> m_find_previous_action;
|
||||||
RefPtr<GUI::Action> m_replace_next_action;
|
RefPtr<GUI::Action> m_replace_next_action;
|
||||||
RefPtr<GUI::Action> m_replace_previous_action;
|
RefPtr<GUI::Action> m_replace_previous_action;
|
||||||
|
@ -93,6 +94,7 @@ private:
|
||||||
RefPtr<GUI::TextBox> m_replace_textbox;
|
RefPtr<GUI::TextBox> m_replace_textbox;
|
||||||
RefPtr<GUI::Button> m_find_previous_button;
|
RefPtr<GUI::Button> m_find_previous_button;
|
||||||
RefPtr<GUI::Button> m_find_next_button;
|
RefPtr<GUI::Button> m_find_next_button;
|
||||||
|
RefPtr<GUI::Button> m_find_regex_button;
|
||||||
RefPtr<GUI::Button> m_replace_previous_button;
|
RefPtr<GUI::Button> m_replace_previous_button;
|
||||||
RefPtr<GUI::Button> m_replace_next_button;
|
RefPtr<GUI::Button> m_replace_next_button;
|
||||||
RefPtr<GUI::Button> m_replace_all_button;
|
RefPtr<GUI::Button> m_replace_all_button;
|
||||||
|
@ -114,6 +116,7 @@ private:
|
||||||
bool m_document_dirty { false };
|
bool m_document_dirty { false };
|
||||||
bool m_document_opening { false };
|
bool m_document_opening { false };
|
||||||
bool m_auto_detect_preview_mode { false };
|
bool m_auto_detect_preview_mode { false };
|
||||||
|
bool m_find_use_regex { false };
|
||||||
|
|
||||||
PreviewMode m_preview_mode { PreviewMode::None };
|
PreviewMode m_preview_mode { PreviewMode::None };
|
||||||
};
|
};
|
||||||
|
|
|
@ -35,7 +35,7 @@ ProjectFile::ProjectFile(const String& name)
|
||||||
{
|
{
|
||||||
}
|
}
|
||||||
|
|
||||||
const GUI::TextDocument& ProjectFile::document() const
|
GUI::TextDocument& ProjectFile::document() const
|
||||||
{
|
{
|
||||||
if (!m_document) {
|
if (!m_document) {
|
||||||
m_document = CodeDocument::create(LexicalPath(m_name));
|
m_document = CodeDocument::create(LexicalPath(m_name));
|
||||||
|
|
|
@ -43,7 +43,7 @@ public:
|
||||||
|
|
||||||
const String& name() const { return m_name; }
|
const String& name() const { return m_name; }
|
||||||
|
|
||||||
const GUI::TextDocument& document() const;
|
GUI::TextDocument& document() const;
|
||||||
|
|
||||||
int vertical_scroll_value() const;
|
int vertical_scroll_value() const;
|
||||||
void vertical_scroll_value(int);
|
void vertical_scroll_value(int);
|
||||||
|
|
|
@ -94,4 +94,4 @@ set(GENERATED_SOURCES
|
||||||
)
|
)
|
||||||
|
|
||||||
serenity_lib(LibGUI gui)
|
serenity_lib(LibGUI gui)
|
||||||
target_link_libraries(LibGUI LibCore LibGfx LibIPC LibThread LibCpp)
|
target_link_libraries(LibGUI LibCore LibGfx LibIPC LibThread LibCpp LibRegex)
|
||||||
|
|
|
@ -30,6 +30,7 @@
|
||||||
#include <LibCore/Timer.h>
|
#include <LibCore/Timer.h>
|
||||||
#include <LibGUI/TextDocument.h>
|
#include <LibGUI/TextDocument.h>
|
||||||
#include <LibGUI/TextEditor.h>
|
#include <LibGUI/TextEditor.h>
|
||||||
|
#include <LibRegex/Regex.h>
|
||||||
#include <ctype.h>
|
#include <ctype.h>
|
||||||
|
|
||||||
namespace GUI {
|
namespace GUI {
|
||||||
|
@ -272,6 +273,8 @@ void TextDocument::notify_did_change()
|
||||||
for (auto* client : m_clients)
|
for (auto* client : m_clients)
|
||||||
client->document_did_change();
|
client->document_did_change();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
m_regex_needs_update = true;
|
||||||
}
|
}
|
||||||
|
|
||||||
void TextDocument::set_all_cursors(const TextPosition& position)
|
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 };
|
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())
|
if (needle.is_empty())
|
||||||
return {};
|
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 position = start.is_valid() ? start : TextPosition(0, 0);
|
||||||
TextPosition original_position = position;
|
TextPosition original_position = position;
|
||||||
|
|
||||||
|
@ -381,11 +451,61 @@ TextRange TextDocument::find_next(const StringView& needle, const TextPosition&
|
||||||
return {};
|
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())
|
if (needle.is_empty())
|
||||||
return {};
|
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);
|
TextPosition position = start.is_valid() ? start : TextPosition(0, 0);
|
||||||
position = previous_position_before(position, should_wrap);
|
position = previous_position_before(position, should_wrap);
|
||||||
TextPosition original_position = position;
|
TextPosition original_position = position;
|
||||||
|
@ -413,13 +533,13 @@ TextRange TextDocument::find_previous(const StringView& needle, const TextPositi
|
||||||
return {};
|
return {};
|
||||||
}
|
}
|
||||||
|
|
||||||
Vector<TextRange> TextDocument::find_all(const StringView& needle) const
|
Vector<TextRange> TextDocument::find_all(const StringView& needle, bool regmatch)
|
||||||
{
|
{
|
||||||
Vector<TextRange> ranges;
|
Vector<TextRange> ranges;
|
||||||
|
|
||||||
TextPosition position;
|
TextPosition position;
|
||||||
for (;;) {
|
for (;;) {
|
||||||
auto range = find_next(needle, position, SearchShouldWrap::No);
|
auto range = find_next(needle, position, SearchShouldWrap::No, regmatch);
|
||||||
if (!range.is_valid())
|
if (!range.is_valid())
|
||||||
break;
|
break;
|
||||||
ranges.append(range);
|
ranges.append(range);
|
||||||
|
|
|
@ -39,6 +39,7 @@
|
||||||
#include <LibGUI/UndoStack.h>
|
#include <LibGUI/UndoStack.h>
|
||||||
#include <LibGfx/Color.h>
|
#include <LibGfx/Color.h>
|
||||||
#include <LibGfx/Forward.h>
|
#include <LibGfx/Forward.h>
|
||||||
|
#include <LibRegex/Regex.h>
|
||||||
|
|
||||||
namespace GUI {
|
namespace GUI {
|
||||||
|
|
||||||
|
@ -108,10 +109,11 @@ public:
|
||||||
String text() const;
|
String text() const;
|
||||||
String text_in_range(const TextRange&) 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;
|
void update_regex_matches(const StringView&);
|
||||||
TextRange find_previous(const StringView&, const TextPosition& start = {}, SearchShouldWrap = SearchShouldWrap::Yes) const;
|
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 next_position_after(const TextPosition&, SearchShouldWrap = SearchShouldWrap::Yes) const;
|
||||||
TextPosition previous_position_before(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;
|
UndoStack m_undo_stack;
|
||||||
RefPtr<Core::Timer> m_undo_timer;
|
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 {
|
class TextDocumentLine {
|
||||||
|
|
Loading…
Add table
Add a link
Reference in a new issue