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->use_regex = OPT_GET("Tool/Search Replace/RegExp")->GetBool();
|
||||||
settings->ignore_comments = OPT_GET("Tool/Search Replace/Skip Comments")->GetBool();
|
settings->ignore_comments = OPT_GET("Tool/Search Replace/Skip Comments")->GetBool();
|
||||||
settings->skip_tags = OPT_GET("Tool/Search Replace/Skip Tags")->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);
|
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));
|
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 "libresrc/libresrc.h"
|
||||||
#include "main.h"
|
#include "main.h"
|
||||||
#include "options.h"
|
#include "options.h"
|
||||||
|
#include "search_replace_engine.h"
|
||||||
#include "selection_controller.h"
|
#include "selection_controller.h"
|
||||||
|
|
||||||
#include <libaegisub/of_type_adaptor.h>
|
#include <libaegisub/of_type_adaptor.h>
|
||||||
|
@ -53,72 +54,35 @@ enum {
|
||||||
ACTION_INTERSECT
|
ACTION_INTERSECT
|
||||||
};
|
};
|
||||||
|
|
||||||
enum {
|
|
||||||
FIELD_TEXT = 0,
|
|
||||||
FIELD_STYLE,
|
|
||||||
FIELD_ACTOR,
|
|
||||||
FIELD_EFFECT
|
|
||||||
};
|
|
||||||
|
|
||||||
enum {
|
enum {
|
||||||
MODE_EXACT = 0,
|
MODE_EXACT = 0,
|
||||||
MODE_CONTAINS,
|
MODE_CONTAINS,
|
||||||
MODE_REGEXP
|
MODE_REGEXP
|
||||||
};
|
};
|
||||||
|
|
||||||
DEFINE_SIMPLE_EXCEPTION(BadRegex, agi::InvalidInputException, "bad_regex")
|
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) {
|
||||||
|
|
||||||
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) {
|
|
||||||
wxRegEx re;
|
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);
|
SearchReplaceSettings settings = {
|
||||||
std::function<bool (wxString)> pred = get_predicate(mode, &re, match_case, match_text);
|
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;
|
std::set<AssDialogue*> matches;
|
||||||
for (auto diag : ass->Line | agi::of_type<AssDialogue>()) {
|
for (auto diag : ass->Line | agi::of_type<AssDialogue>()) {
|
||||||
if (diag->Comment && !comments) continue;
|
if (diag->Comment && !comments) continue;
|
||||||
if (!diag->Comment && !dialogue) continue;
|
if (!diag->Comment && !dialogue) continue;
|
||||||
|
|
||||||
if (pred(diag->*field) != invert)
|
if (invert != predicate(diag, 0))
|
||||||
matches.insert(diag);
|
matches.insert(diag);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -31,16 +31,9 @@
|
||||||
|
|
||||||
static const size_t bad_pos = -1;
|
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 {
|
namespace {
|
||||||
|
DEFINE_SIMPLE_EXCEPTION(BadRegex, agi::InvalidInputException, "bad_regex")
|
||||||
|
|
||||||
auto get_dialogue_field(SearchReplaceSettings::Field field) -> decltype(&AssDialogue::Text) {
|
auto get_dialogue_field(SearchReplaceSettings::Field field) -> decltype(&AssDialogue::Text) {
|
||||||
switch (field) {
|
switch (field) {
|
||||||
case SearchReplaceSettings::Field::TEXT: return &AssDialogue::Text;
|
case SearchReplaceSettings::Field::TEXT: return &AssDialogue::Text;
|
||||||
|
@ -147,7 +140,7 @@ public:
|
||||||
};
|
};
|
||||||
|
|
||||||
template<typename Accessor>
|
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) {
|
if (settings.use_regex) {
|
||||||
int flags = wxRE_ADVANCED;
|
int flags = wxRE_ADVANCED;
|
||||||
if (!settings.match_case)
|
if (!settings.match_case)
|
||||||
|
@ -155,7 +148,7 @@ matcher get_matcher(SearchReplaceSettings const& settings, wxRegEx *regex, Acces
|
||||||
|
|
||||||
regex->Compile(settings.find, flags);
|
regex->Compile(settings.find, flags);
|
||||||
if (!regex->IsValid())
|
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 {
|
return [=](const AssDialogue *diag, size_t start) mutable -> MatchState {
|
||||||
if (!regex->Matches(a.get(diag, start)))
|
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;
|
bool match_case = settings.match_case;
|
||||||
wxString look_for = settings.find;
|
wxString look_for = settings.find;
|
||||||
if (!settings.match_case)
|
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 {
|
return [=](const AssDialogue *diag, size_t start) mutable -> MatchState {
|
||||||
auto str = a.get(diag, start);
|
auto str = a.get(diag, start);
|
||||||
|
if (full_match_only && str.size() != look_for.size())
|
||||||
|
return MatchState();
|
||||||
|
|
||||||
if (!match_case)
|
if (!match_case)
|
||||||
str.MakeLower();
|
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>
|
template<typename Iterator, typename Container>
|
||||||
Iterator circular_next(Iterator it, Container& c) {
|
Iterator circular_next(Iterator it, Container& c) {
|
||||||
++it;
|
++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)
|
SearchReplaceEngine::SearchReplaceEngine(agi::Context *c)
|
||||||
: context(c)
|
: context(c)
|
||||||
, initialized(false)
|
, initialized(false)
|
||||||
|
@ -227,7 +224,7 @@ bool SearchReplaceEngine::FindReplace(bool replace) {
|
||||||
return false;
|
return false;
|
||||||
|
|
||||||
wxRegEx r;
|
wxRegEx r;
|
||||||
auto matches = get_matcher(settings, &r);
|
auto matches = GetMatcher(settings, &r);
|
||||||
|
|
||||||
AssDialogue *line = context->selectionController->GetActiveLine();
|
AssDialogue *line = context->selectionController->GetActiveLine();
|
||||||
auto it = context->ass->Line.iterator_to(*line);
|
auto it = context->ass->Line.iterator_to(*line);
|
||||||
|
@ -306,7 +303,7 @@ bool SearchReplaceEngine::ReplaceAll() {
|
||||||
size_t count = 0;
|
size_t count = 0;
|
||||||
|
|
||||||
wxRegEx r;
|
wxRegEx r;
|
||||||
auto matches = get_matcher(settings, &r);
|
auto matches = GetMatcher(settings, &r);
|
||||||
|
|
||||||
SubtitleSelection const& sel = context->selectionController->GetSelectedSet();
|
SubtitleSelection const& sel = context->selectionController->GetSelectedSet();
|
||||||
bool selection_only = settings.limit_to == SearchReplaceSettings::Limit::SELECTED;
|
bool selection_only = settings.limit_to == SearchReplaceSettings::Limit::SELECTED;
|
||||||
|
|
|
@ -14,11 +14,21 @@
|
||||||
//
|
//
|
||||||
// Aegisub Project http://www.aegisub.org/
|
// Aegisub Project http://www.aegisub.org/
|
||||||
|
|
||||||
|
#include <functional>
|
||||||
#include <wx/string.h>
|
#include <wx/string.h>
|
||||||
|
|
||||||
namespace agi { struct Context; }
|
namespace agi { struct Context; }
|
||||||
class AssDialogue;
|
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 {
|
struct SearchReplaceSettings {
|
||||||
enum class Field {
|
enum class Field {
|
||||||
|
@ -43,6 +53,7 @@ struct SearchReplaceSettings {
|
||||||
bool use_regex;
|
bool use_regex;
|
||||||
bool ignore_comments;
|
bool ignore_comments;
|
||||||
bool skip_tags;
|
bool skip_tags;
|
||||||
|
bool exact_match;
|
||||||
};
|
};
|
||||||
|
|
||||||
class SearchReplaceEngine {
|
class SearchReplaceEngine {
|
||||||
|
@ -60,5 +71,7 @@ public:
|
||||||
|
|
||||||
void Configure(SearchReplaceSettings const& new_settings);
|
void Configure(SearchReplaceSettings const& new_settings);
|
||||||
|
|
||||||
|
static std::function<MatchState (const AssDialogue*, size_t)> GetMatcher(SearchReplaceSettings const& settings, wxRegEx *regex);
|
||||||
|
|
||||||
SearchReplaceEngine(agi::Context *c);
|
SearchReplaceEngine(agi::Context *c);
|
||||||
};
|
};
|
||||||
|
|
Loading…
Reference in a new issue