forked from mia/Aegisub
Extract some more find/replace logic to libaegisub
This commit is contained in:
parent
cad8c80aab
commit
74ac2ab1fe
3 changed files with 85 additions and 68 deletions
|
@ -52,8 +52,24 @@ std::pair<size_t, size_t> find_range(std::string const& haystack, std::string co
|
||||||
return {match_start, match_start + needle.size()};
|
return {match_start, match_start + needle.size()};
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void parse_blocks(std::vector<std::pair<size_t, size_t>>& blocks, std::string const& str) {
|
||||||
|
blocks.clear();
|
||||||
|
|
||||||
|
size_t ovr_start = bad_pos;
|
||||||
|
size_t i = 0;
|
||||||
|
for (auto const& c : str) {
|
||||||
|
if (c == '{' && ovr_start == bad_pos)
|
||||||
|
ovr_start = i;
|
||||||
|
else if (c == '}' && ovr_start != bad_pos) {
|
||||||
|
blocks.emplace_back(ovr_start, i);
|
||||||
|
ovr_start = bad_pos;
|
||||||
|
}
|
||||||
|
++i;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
} // anonymous namespace
|
||||||
|
|
||||||
namespace agi { namespace util {
|
namespace agi { namespace util {
|
||||||
|
|
||||||
std::string strftime(const char *fmt, const tm *tmptr) {
|
std::string strftime(const char *fmt, const tm *tmptr) {
|
||||||
|
@ -129,12 +145,60 @@ std::pair<size_t, size_t> ifind(std::string const& haystack, std::string const&
|
||||||
return ret;
|
return ret;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
std::string tagless_find_helper::strip_tags(std::string const& str, size_t s) {
|
||||||
|
parse_blocks(blocks, str);
|
||||||
|
|
||||||
|
std::string out;
|
||||||
|
|
||||||
|
size_t last = s;
|
||||||
|
for (auto const& block : blocks) {
|
||||||
|
if (block.second < s) continue;
|
||||||
|
if (block.first > last)
|
||||||
|
out.append(str.begin() + last, str.begin() + block.first);
|
||||||
|
last = block.second + 1;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (last < str.size())
|
||||||
|
out.append(str.begin() + last, str.end());
|
||||||
|
|
||||||
|
start = s;
|
||||||
|
return out;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void tagless_find_helper::map_range(size_t &s, size_t &e) {
|
||||||
|
s += start;
|
||||||
|
e += start;
|
||||||
|
|
||||||
|
// Shift the start and end of the match to be relative to the unstripped
|
||||||
|
// match
|
||||||
|
for (auto const& block : blocks) {
|
||||||
|
// Any blocks before start are irrelevant as they're included in `start`
|
||||||
|
if (block.second < s) continue;
|
||||||
|
// Skip over blocks at the very beginning of the match
|
||||||
|
// < should only happen if the cursor was within an override block
|
||||||
|
// when the user started a search
|
||||||
|
if (block.first <= s) {
|
||||||
|
size_t len = block.second - std::max(block.first, s) + 1;
|
||||||
|
s += len;
|
||||||
|
e += len;
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
assert(block.first > s);
|
||||||
|
// Blocks after the match are irrelevant
|
||||||
|
if (block.first >= e) break;
|
||||||
|
|
||||||
|
// Extend the match to include blocks within the match
|
||||||
|
// Note that blocks cannot be partially within the match
|
||||||
|
e += block.second - block.first + 1;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
} // namespace util
|
||||||
|
|
||||||
#ifndef __APPLE__
|
#ifndef __APPLE__
|
||||||
namespace osx {
|
namespace osx {
|
||||||
AppNapDisabler::AppNapDisabler(std::string reason) { }
|
AppNapDisabler::AppNapDisabler(std::string reason) { }
|
||||||
AppNapDisabler::~AppNapDisabler() { }
|
AppNapDisabler::~AppNapDisabler() { }
|
||||||
}
|
}
|
||||||
#endif
|
#endif
|
||||||
}
|
} // namespace agi
|
||||||
|
|
|
@ -15,11 +15,11 @@
|
||||||
#include <algorithm>
|
#include <algorithm>
|
||||||
#include <boost/range/irange.hpp>
|
#include <boost/range/irange.hpp>
|
||||||
#include <string>
|
#include <string>
|
||||||
|
#include <vector>
|
||||||
|
|
||||||
struct tm;
|
struct tm;
|
||||||
|
|
||||||
namespace agi {
|
namespace agi { namespace util {
|
||||||
namespace util {
|
|
||||||
/// Clamp `b` to the range [`a`,`c`]
|
/// Clamp `b` to the range [`a`,`c`]
|
||||||
template<typename T>
|
template<typename T>
|
||||||
static inline T mid(T a, T b, T c) {
|
static inline T mid(T a, T b, T c) {
|
||||||
|
@ -45,6 +45,20 @@ namespace agi {
|
||||||
/// based on the unfolded length.
|
/// based on the unfolded length.
|
||||||
std::pair<size_t, size_t> ifind(std::string const& haystack, std::string const& needle);
|
std::pair<size_t, size_t> ifind(std::string const& haystack, std::string const& needle);
|
||||||
|
|
||||||
|
class tagless_find_helper {
|
||||||
|
std::vector<std::pair<size_t, size_t>> blocks;
|
||||||
|
size_t start = 0;
|
||||||
|
|
||||||
|
public:
|
||||||
|
/// Strip ASS override tags at or after `start` in `str`, and initialize
|
||||||
|
/// state for mapping ranges back to the input string
|
||||||
|
std::string strip_tags(std::string const& str, size_t start);
|
||||||
|
|
||||||
|
/// Convert a range in the string returned by `strip_tags()` to a range
|
||||||
|
/// int the string last passed to `strip_tags()`
|
||||||
|
void map_range(size_t& start, size_t& end);
|
||||||
|
};
|
||||||
|
|
||||||
/// Set the name of the calling thread in the Visual Studio debugger
|
/// Set the name of the calling thread in the Visual Studio debugger
|
||||||
/// @param name New name for the thread
|
/// @param name New name for the thread
|
||||||
void SetThreadName(const char *name);
|
void SetThreadName(const char *name);
|
||||||
|
@ -66,6 +80,4 @@ namespace agi {
|
||||||
auto range(Integer end) -> decltype(boost::irange<Integer>(0, end)) {
|
auto range(Integer end) -> decltype(boost::irange<Integer>(0, end)) {
|
||||||
return boost::irange<Integer>(0, end);
|
return boost::irange<Integer>(0, end);
|
||||||
}
|
}
|
||||||
|
} } // namespace agi::util
|
||||||
} // namespace util
|
|
||||||
} // namespace agi
|
|
||||||
|
|
|
@ -73,76 +73,17 @@ public:
|
||||||
|
|
||||||
class skip_tags_accessor {
|
class skip_tags_accessor {
|
||||||
boost::flyweight<std::string> AssDialogueBase::*field;
|
boost::flyweight<std::string> AssDialogueBase::*field;
|
||||||
std::vector<std::pair<size_t, size_t>> blocks;
|
agi::util::tagless_find_helper helper;
|
||||||
size_t start = 0;
|
|
||||||
|
|
||||||
void parse_str(std::string const& str) {
|
|
||||||
blocks.clear();
|
|
||||||
|
|
||||||
size_t ovr_start = bad_pos;
|
|
||||||
size_t i = 0;
|
|
||||||
for (auto const& c : str) {
|
|
||||||
if (c == '{' && ovr_start == bad_pos)
|
|
||||||
ovr_start = i;
|
|
||||||
else if (c == '}' && ovr_start != bad_pos) {
|
|
||||||
blocks.emplace_back(ovr_start, i);
|
|
||||||
ovr_start = bad_pos;
|
|
||||||
}
|
|
||||||
++i;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
public:
|
public:
|
||||||
skip_tags_accessor(SearchReplaceSettings::Field f) : field(get_dialogue_field(f)) { }
|
skip_tags_accessor(SearchReplaceSettings::Field f) : field(get_dialogue_field(f)) { }
|
||||||
|
|
||||||
std::string get(const AssDialogue *d, size_t s) {
|
std::string get(const AssDialogue *d, size_t s) {
|
||||||
auto const& str = get_normalized(d, field);
|
return helper.strip_tags(get_normalized(d, field), s);
|
||||||
parse_str(str);
|
|
||||||
|
|
||||||
std::string out;
|
|
||||||
|
|
||||||
size_t last = s;
|
|
||||||
for (auto const& block : blocks) {
|
|
||||||
if (block.second < s) continue;
|
|
||||||
if (block.first > last)
|
|
||||||
out.append(str.begin() + last, str.begin() + block.first);
|
|
||||||
last = block.second + 1;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (last < str.size())
|
|
||||||
out.append(str.begin() + last, str.end());
|
|
||||||
|
|
||||||
start = s;
|
|
||||||
return out;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
MatchState make_match_state(size_t s, size_t e, boost::u32regex *r = nullptr) {
|
MatchState make_match_state(size_t s, size_t e, boost::u32regex *r = nullptr) {
|
||||||
s += start;
|
helper.map_range(s, e);
|
||||||
e += start;
|
|
||||||
|
|
||||||
// Shift the start and end of the match to be relative to the unstripped
|
|
||||||
// match
|
|
||||||
for (auto const& block : blocks) {
|
|
||||||
// Any blocks before start are irrelevant as they're included in `start`
|
|
||||||
if (block.second < s) continue;
|
|
||||||
// Skip over blocks at the very beginning of the match
|
|
||||||
// < should only happen if the cursor was within an override block
|
|
||||||
// when the user started a search
|
|
||||||
if (block.first <= s) {
|
|
||||||
size_t len = block.second - std::max(block.first, s) + 1;
|
|
||||||
s += len;
|
|
||||||
e += len;
|
|
||||||
continue;
|
|
||||||
}
|
|
||||||
|
|
||||||
assert(block.first > s);
|
|
||||||
// Blocks after the match are irrelevant
|
|
||||||
if (block.first >= e) break;
|
|
||||||
|
|
||||||
// Extend the match to include blocks within the match
|
|
||||||
// Note that blocks cannot be partially within the match
|
|
||||||
e += block.second - block.first + 1;
|
|
||||||
}
|
|
||||||
return {r, s, e};
|
return {r, s, e};
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
Loading…
Reference in a new issue