forked from mia/Aegisub
Use SearchReplaceEngine in DialogSelection
This commit is contained in:
parent
6be8d32929
commit
ed6c052e7f
4 changed files with 47 additions and 72 deletions
|
@ -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));
|
||||
|
|
|
@ -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);
|
||||
}
|
||||
|
||||
|
|
|
@ -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;
|
||||
|
|
|
@ -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);
|
||||
};
|
||||
|
|
Loading…
Reference in a new issue