Use SearchReplaceEngine in DialogSelection

This commit is contained in:
Thomas Goyne 2013-01-13 08:23:50 -08:00
parent 6be8d32929
commit ed6c052e7f
4 changed files with 47 additions and 72 deletions

View File

@ -58,6 +58,7 @@ DialogSearchReplace::DialogSearchReplace(agi::Context* c, bool replace)
settings->use_regex = OPT_GET("Tool/Search Replace/RegExp")->GetBool();
settings->ignore_comments = OPT_GET("Tool/Search Replace/Skip Comments")->GetBool();
settings->skip_tags = OPT_GET("Tool/Search Replace/Skip Tags")->GetBool();
settings->exact_match = false;
auto find_sizer = new wxFlexGridSizer(2, 2, 5, 15);
find_edit = new wxComboBox(this, -1, "", wxDefaultPosition, wxSize(300, -1), recent_find, wxCB_DROPDOWN, wxGenericValidator(&settings->find));

View File

@ -32,6 +32,7 @@
#include "libresrc/libresrc.h"
#include "main.h"
#include "options.h"
#include "search_replace_engine.h"
#include "selection_controller.h"
#include <libaegisub/of_type_adaptor.h>
@ -53,72 +54,35 @@ enum {
ACTION_INTERSECT
};
enum {
FIELD_TEXT = 0,
FIELD_STYLE,
FIELD_ACTOR,
FIELD_EFFECT
};
enum {
MODE_EXACT = 0,
MODE_CONTAINS,
MODE_REGEXP
};
DEFINE_SIMPLE_EXCEPTION(BadRegex, agi::InvalidInputException, "bad_regex")
static boost::flyweight<wxString> AssDialogue::* get_field(int field_n) {
switch(field_n) {
case FIELD_TEXT: return &AssDialogue::Text; break;
case FIELD_STYLE: return &AssDialogue::Style; break;
case FIELD_ACTOR: return &AssDialogue::Actor; break;
case FIELD_EFFECT: return &AssDialogue::Effect; break;
default: throw agi::InternalError("Bad field", 0);
}
}
std::function<bool (wxString)> get_predicate(int mode, wxRegEx *re, bool match_case, wxString const& match_text) {
using std::placeholders::_1;
switch (mode) {
case MODE_REGEXP:
return [=](wxString str) { return re->Matches(str); };
case MODE_EXACT:
if (match_case)
return std::bind(std::equal_to<wxString>(), match_text, _1);
else
return bind(std::equal_to<wxString>(), match_text.Lower(), std::bind(&wxString::Lower, _1));
case MODE_CONTAINS:
if (match_case)
return std::bind(&wxString::Contains, _1, match_text);
else
return bind(&wxString::Contains, std::bind(&wxString::Lower, _1), match_text.Lower());
break;
default: throw agi::InternalError("Bad mode", 0);
}
}
static std::set<AssDialogue*> process(wxString match_text, bool match_case, int mode, bool invert, bool comments, bool dialogue, int field_n, AssFile *ass) {
static std::set<AssDialogue*> process(wxString const& match_text, bool match_case, int mode, bool invert, bool comments, bool dialogue, int field_n, AssFile *ass) {
wxRegEx re;
if (mode == MODE_REGEXP) {
int flags = wxRE_ADVANCED;
if (!match_case)
flags |= wxRE_ICASE;
if (!re.Compile(match_text, flags))
throw BadRegex("Syntax error in regular expression", 0);
match_case = false;
}
boost::flyweight<wxString> AssDialogue::*field = get_field(field_n);
std::function<bool (wxString)> pred = get_predicate(mode, &re, match_case, match_text);
SearchReplaceSettings settings = {
match_text,
wxString(),
static_cast<SearchReplaceSettings::Field>(field_n),
SearchReplaceSettings::Limit::ALL,
match_case,
mode == MODE_REGEXP,
false,
false,
mode == MODE_EXACT
};
auto predicate = SearchReplaceEngine::GetMatcher(settings, &re);
std::set<AssDialogue*> matches;
for (auto diag : ass->Line | agi::of_type<AssDialogue>()) {
if (diag->Comment && !comments) continue;
if (!diag->Comment && !dialogue) continue;
if (pred(diag->*field) != invert)
if (invert != predicate(diag, 0))
matches.insert(diag);
}

View File

@ -31,16 +31,9 @@
static const size_t bad_pos = -1;
struct MatchState {
wxRegEx *re;
size_t start, end;
MatchState() : re(nullptr), start(0), end(-1) { }
MatchState(size_t s, size_t e, wxRegEx *re) : re(re), start(s), end(e) { }
operator bool() { return end != bad_pos; }
};
namespace {
DEFINE_SIMPLE_EXCEPTION(BadRegex, agi::InvalidInputException, "bad_regex")
auto get_dialogue_field(SearchReplaceSettings::Field field) -> decltype(&AssDialogue::Text) {
switch (field) {
case SearchReplaceSettings::Field::TEXT: return &AssDialogue::Text;
@ -147,7 +140,7 @@ public:
};
template<typename Accessor>
matcher get_matcher(SearchReplaceSettings const& settings, wxRegEx *regex, Accessor a) {
matcher get_matcher(SearchReplaceSettings const& settings, wxRegEx *regex, Accessor&& a) {
if (settings.use_regex) {
int flags = wxRE_ADVANCED;
if (!settings.match_case)
@ -155,7 +148,7 @@ matcher get_matcher(SearchReplaceSettings const& settings, wxRegEx *regex, Acces
regex->Compile(settings.find, flags);
if (!regex->IsValid())
return [](const AssDialogue*, size_t) { return MatchState(); };
throw BadRegex("Invalid syntax in regular expression", nullptr);
return [=](const AssDialogue *diag, size_t start) mutable -> MatchState {
if (!regex->Matches(a.get(diag, start)))
@ -167,6 +160,7 @@ matcher get_matcher(SearchReplaceSettings const& settings, wxRegEx *regex, Acces
};
}
bool full_match_only = settings.exact_match;
bool match_case = settings.match_case;
wxString look_for = settings.find;
if (!settings.match_case)
@ -174,6 +168,9 @@ matcher get_matcher(SearchReplaceSettings const& settings, wxRegEx *regex, Acces
return [=](const AssDialogue *diag, size_t start) mutable -> MatchState {
auto str = a.get(diag, start);
if (full_match_only && str.size() != look_for.size())
return MatchState();
if (!match_case)
str.MakeLower();
@ -185,12 +182,6 @@ matcher get_matcher(SearchReplaceSettings const& settings, wxRegEx *regex, Acces
};
}
matcher get_matcher(SearchReplaceSettings const& settings, wxRegEx *regex) {
if (settings.skip_tags)
return get_matcher(settings, regex, skip_tags_accessor(settings.field));
return get_matcher(settings, regex, noop_accessor(settings.field));
}
template<typename Iterator, typename Container>
Iterator circular_next(Iterator it, Container& c) {
++it;
@ -201,6 +192,12 @@ Iterator circular_next(Iterator it, Container& c) {
}
std::function<MatchState (const AssDialogue*, size_t)> SearchReplaceEngine::GetMatcher(SearchReplaceSettings const& settings, wxRegEx *regex) {
if (settings.skip_tags)
return get_matcher(settings, regex, skip_tags_accessor(settings.field));
return get_matcher(settings, regex, noop_accessor(settings.field));
}
SearchReplaceEngine::SearchReplaceEngine(agi::Context *c)
: context(c)
, initialized(false)
@ -227,7 +224,7 @@ bool SearchReplaceEngine::FindReplace(bool replace) {
return false;
wxRegEx r;
auto matches = get_matcher(settings, &r);
auto matches = GetMatcher(settings, &r);
AssDialogue *line = context->selectionController->GetActiveLine();
auto it = context->ass->Line.iterator_to(*line);
@ -306,7 +303,7 @@ bool SearchReplaceEngine::ReplaceAll() {
size_t count = 0;
wxRegEx r;
auto matches = get_matcher(settings, &r);
auto matches = GetMatcher(settings, &r);
SubtitleSelection const& sel = context->selectionController->GetSelectedSet();
bool selection_only = settings.limit_to == SearchReplaceSettings::Limit::SELECTED;

View File

@ -14,11 +14,21 @@
//
// Aegisub Project http://www.aegisub.org/
#include <functional>
#include <wx/string.h>
namespace agi { struct Context; }
class AssDialogue;
struct MatchState;
class wxRegEx;
struct MatchState {
wxRegEx *re;
size_t start, end;
MatchState() : re(nullptr), start(0), end(-1) { }
MatchState(size_t s, size_t e, wxRegEx *re) : re(re), start(s), end(e) { }
operator bool() const { return end != (size_t)-1; }
};
struct SearchReplaceSettings {
enum class Field {
@ -43,6 +53,7 @@ struct SearchReplaceSettings {
bool use_regex;
bool ignore_comments;
bool skip_tags;
bool exact_match;
};
class SearchReplaceEngine {
@ -60,5 +71,7 @@ public:
void Configure(SearchReplaceSettings const& new_settings);
static std::function<MatchState (const AssDialogue*, size_t)> GetMatcher(SearchReplaceSettings const& settings, wxRegEx *regex);
SearchReplaceEngine(agi::Context *c);
};