mirror of
https://github.com/Chatterino/chatterino2.git
synced 2024-11-13 19:49:51 +01:00
refactor: Ignores and Replacements (#4965)
Fixes a freeze from a bad regex in _Ignores_ Fixes some emotes not appearing when using _Ignores_ Fixes lookahead/-behind not working in _Ignores_
This commit is contained in:
parent
d9cdc88061
commit
3d9db1d528
|
@ -35,6 +35,9 @@
|
||||||
- Bugfix: Fixed a crash when clicking `More messages below` button in a usercard and closing it quickly. (#4933)
|
- Bugfix: Fixed a crash when clicking `More messages below` button in a usercard and closing it quickly. (#4933)
|
||||||
- Bugfix: Fixed thread popup window missing messages for nested threads. (#4923)
|
- Bugfix: Fixed thread popup window missing messages for nested threads. (#4923)
|
||||||
- Bugfix: Fixed an occasional crash for channel point redemptions with text input. (#4949)
|
- Bugfix: Fixed an occasional crash for channel point redemptions with text input. (#4949)
|
||||||
|
- Bugfix: Fixed a freeze from a bad regex in _Ignores_. (#4965)
|
||||||
|
- Bugfix: Fixed some emotes not appearing when using _Ignores_. (#4965)
|
||||||
|
- Bugfix: Fixed lookahead/-behind not working in _Ignores_. (#4965)
|
||||||
- Dev: Change clang-format from v14 to v16. (#4929)
|
- Dev: Change clang-format from v14 to v16. (#4929)
|
||||||
- Dev: Fixed UTF16 encoding of `modes` file for the installer. (#4791)
|
- Dev: Fixed UTF16 encoding of `modes` file for the installer. (#4791)
|
||||||
- Dev: Temporarily disable High DPI scaling on Qt6 builds on Windows. (#4767)
|
- Dev: Temporarily disable High DPI scaling on Qt6 builds on Windows. (#4767)
|
||||||
|
|
|
@ -846,28 +846,25 @@ void TwitchMessageBuilder::appendUsername()
|
||||||
void TwitchMessageBuilder::runIgnoreReplaces(
|
void TwitchMessageBuilder::runIgnoreReplaces(
|
||||||
std::vector<TwitchEmoteOccurrence> &twitchEmotes)
|
std::vector<TwitchEmoteOccurrence> &twitchEmotes)
|
||||||
{
|
{
|
||||||
|
using SizeType = QString::size_type;
|
||||||
|
|
||||||
auto phrases = getSettings()->ignoredMessages.readOnly();
|
auto phrases = getSettings()->ignoredMessages.readOnly();
|
||||||
auto removeEmotesInRange = [](int pos, int len,
|
auto removeEmotesInRange = [&twitchEmotes](SizeType pos, SizeType len) {
|
||||||
auto &twitchEmotes) mutable {
|
// all emotes outside the range come before `it`
|
||||||
|
// all emotes in the range start at `it`
|
||||||
auto it = std::partition(
|
auto it = std::partition(
|
||||||
twitchEmotes.begin(), twitchEmotes.end(),
|
twitchEmotes.begin(), twitchEmotes.end(),
|
||||||
[pos, len](const auto &item) {
|
[pos, len](const auto &item) {
|
||||||
|
// returns true for emotes outside the range
|
||||||
return !((item.start >= pos) && item.start < (pos + len));
|
return !((item.start >= pos) && item.start < (pos + len));
|
||||||
});
|
});
|
||||||
for (auto copy = it; copy != twitchEmotes.end(); ++copy)
|
std::vector<TwitchEmoteOccurrence> emotesInRange(it,
|
||||||
{
|
twitchEmotes.end());
|
||||||
if ((*copy).ptr == nullptr)
|
|
||||||
{
|
|
||||||
qCDebug(chatterinoTwitch)
|
|
||||||
<< "remem nullptr" << (*copy).name.string;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
std::vector<TwitchEmoteOccurrence> v(it, twitchEmotes.end());
|
|
||||||
twitchEmotes.erase(it, twitchEmotes.end());
|
twitchEmotes.erase(it, twitchEmotes.end());
|
||||||
return v;
|
return emotesInRange;
|
||||||
};
|
};
|
||||||
|
|
||||||
auto shiftIndicesAfter = [&twitchEmotes](int pos, int by) mutable {
|
auto shiftIndicesAfter = [&twitchEmotes](int pos, int by) {
|
||||||
for (auto &item : twitchEmotes)
|
for (auto &item : twitchEmotes)
|
||||||
{
|
{
|
||||||
auto &index = item.start;
|
auto &index = item.start;
|
||||||
|
@ -881,14 +878,18 @@ void TwitchMessageBuilder::runIgnoreReplaces(
|
||||||
|
|
||||||
auto addReplEmotes = [&twitchEmotes](const IgnorePhrase &phrase,
|
auto addReplEmotes = [&twitchEmotes](const IgnorePhrase &phrase,
|
||||||
const auto &midrepl,
|
const auto &midrepl,
|
||||||
int startIndex) mutable {
|
SizeType startIndex) {
|
||||||
if (!phrase.containsEmote())
|
if (!phrase.containsEmote())
|
||||||
{
|
{
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#if QT_VERSION >= QT_VERSION_CHECK(6, 0, 0)
|
||||||
|
auto words = midrepl.tokenize(u' ');
|
||||||
|
#else
|
||||||
auto words = midrepl.split(' ');
|
auto words = midrepl.split(' ');
|
||||||
int pos = 0;
|
#endif
|
||||||
|
SizeType pos = 0;
|
||||||
for (const auto &word : words)
|
for (const auto &word : words)
|
||||||
{
|
{
|
||||||
for (const auto &emote : phrase.getEmotes())
|
for (const auto &emote : phrase.getEmotes())
|
||||||
|
@ -901,8 +902,9 @@ void TwitchMessageBuilder::runIgnoreReplaces(
|
||||||
<< "emote null" << emote.first.string;
|
<< "emote null" << emote.first.string;
|
||||||
}
|
}
|
||||||
twitchEmotes.push_back(TwitchEmoteOccurrence{
|
twitchEmotes.push_back(TwitchEmoteOccurrence{
|
||||||
startIndex + pos,
|
static_cast<int>(startIndex + pos),
|
||||||
startIndex + pos + (int)emote.first.string.length(),
|
static_cast<int>(startIndex + pos +
|
||||||
|
emote.first.string.length()),
|
||||||
emote.second,
|
emote.second,
|
||||||
emote.first,
|
emote.first,
|
||||||
});
|
});
|
||||||
|
@ -912,6 +914,63 @@ void TwitchMessageBuilder::runIgnoreReplaces(
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
|
auto replaceMessageAt = [&](const IgnorePhrase &phrase, SizeType from,
|
||||||
|
SizeType length, const QString &replacement) {
|
||||||
|
auto removedEmotes = removeEmotesInRange(from, length);
|
||||||
|
this->originalMessage_.replace(from, length, replacement);
|
||||||
|
auto wordStart = from;
|
||||||
|
while (wordStart > 0)
|
||||||
|
{
|
||||||
|
if (this->originalMessage_[wordStart - 1] == ' ')
|
||||||
|
{
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
--wordStart;
|
||||||
|
}
|
||||||
|
auto wordEnd = from + replacement.length();
|
||||||
|
while (wordEnd < this->originalMessage_.length())
|
||||||
|
{
|
||||||
|
if (this->originalMessage_[wordEnd] == ' ')
|
||||||
|
{
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
++wordEnd;
|
||||||
|
}
|
||||||
|
|
||||||
|
shiftIndicesAfter(static_cast<int>(from + length),
|
||||||
|
static_cast<int>(replacement.length() - length));
|
||||||
|
|
||||||
|
#if QT_VERSION >= QT_VERSION_CHECK(5, 15, 0)
|
||||||
|
auto midExtendedRef = QStringView{this->originalMessage_}.mid(
|
||||||
|
wordStart, wordEnd - wordStart);
|
||||||
|
#else
|
||||||
|
auto midExtendedRef =
|
||||||
|
this->originalMessage_.midRef(wordStart, wordEnd - wordStart);
|
||||||
|
#endif
|
||||||
|
|
||||||
|
for (auto &emote : removedEmotes)
|
||||||
|
{
|
||||||
|
if (emote.ptr == nullptr)
|
||||||
|
{
|
||||||
|
qCDebug(chatterinoTwitch)
|
||||||
|
<< "Invalid emote occurrence" << emote.name.string;
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
QRegularExpression emoteregex(
|
||||||
|
"\\b" + emote.name.string + "\\b",
|
||||||
|
QRegularExpression::UseUnicodePropertiesOption);
|
||||||
|
auto match = emoteregex.match(midExtendedRef);
|
||||||
|
if (match.hasMatch())
|
||||||
|
{
|
||||||
|
emote.start = static_cast<int>(from + match.capturedStart());
|
||||||
|
emote.end = static_cast<int>(from + match.capturedEnd());
|
||||||
|
twitchEmotes.push_back(std::move(emote));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
addReplEmotes(phrase, midExtendedRef, wordStart);
|
||||||
|
};
|
||||||
|
|
||||||
for (const auto &phrase : *phrases)
|
for (const auto &phrase : *phrases)
|
||||||
{
|
{
|
||||||
if (phrase.isBlock())
|
if (phrase.isBlock())
|
||||||
|
@ -930,144 +989,35 @@ void TwitchMessageBuilder::runIgnoreReplaces(
|
||||||
{
|
{
|
||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
|
|
||||||
QRegularExpressionMatch match;
|
QRegularExpressionMatch match;
|
||||||
int from = 0;
|
size_t iterations = 0;
|
||||||
|
SizeType from = 0;
|
||||||
while ((from = this->originalMessage_.indexOf(regex, from,
|
while ((from = this->originalMessage_.indexOf(regex, from,
|
||||||
&match)) != -1)
|
&match)) != -1)
|
||||||
{
|
{
|
||||||
int len = match.capturedLength();
|
replaceMessageAt(phrase, from, match.capturedLength(),
|
||||||
auto vret = removeEmotesInRange(from, len, twitchEmotes);
|
phrase.getReplace());
|
||||||
auto mid = this->originalMessage_.mid(from, len);
|
from += phrase.getReplace().length();
|
||||||
mid.replace(regex, phrase.getReplace());
|
iterations++;
|
||||||
|
if (iterations >= 128)
|
||||||
int midsize = mid.size();
|
|
||||||
this->originalMessage_.replace(from, len, mid);
|
|
||||||
int pos1 = from;
|
|
||||||
while (pos1 > 0)
|
|
||||||
{
|
{
|
||||||
if (this->originalMessage_[pos1 - 1] == ' ')
|
this->originalMessage_ =
|
||||||
{
|
u"Too many replacements - check your ignores!"_s;
|
||||||
break;
|
return;
|
||||||
}
|
|
||||||
--pos1;
|
|
||||||
}
|
}
|
||||||
int pos2 = from + midsize;
|
|
||||||
while (pos2 < this->originalMessage_.length())
|
|
||||||
{
|
|
||||||
if (this->originalMessage_[pos2] == ' ')
|
|
||||||
{
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
++pos2;
|
|
||||||
}
|
|
||||||
|
|
||||||
shiftIndicesAfter(from + len, midsize - len);
|
|
||||||
|
|
||||||
#if QT_VERSION >= QT_VERSION_CHECK(5, 15, 0)
|
|
||||||
auto midExtendedRef =
|
|
||||||
QStringView{this->originalMessage_}.mid(pos1, pos2 - pos1);
|
|
||||||
#else
|
|
||||||
auto midExtendedRef =
|
|
||||||
this->originalMessage_.midRef(pos1, pos2 - pos1);
|
|
||||||
#endif
|
|
||||||
|
|
||||||
for (auto &tup : vret)
|
|
||||||
{
|
|
||||||
if (tup.ptr == nullptr)
|
|
||||||
{
|
|
||||||
qCDebug(chatterinoTwitch)
|
|
||||||
<< "v nullptr" << tup.name.string;
|
|
||||||
continue;
|
|
||||||
}
|
|
||||||
QRegularExpression emoteregex(
|
|
||||||
"\\b" + tup.name.string + "\\b",
|
|
||||||
QRegularExpression::UseUnicodePropertiesOption);
|
|
||||||
auto _match = emoteregex.match(midExtendedRef);
|
|
||||||
if (_match.hasMatch())
|
|
||||||
{
|
|
||||||
int last = _match.lastCapturedIndex();
|
|
||||||
for (int i = 0; i <= last; ++i)
|
|
||||||
{
|
|
||||||
tup.start = from + _match.capturedStart();
|
|
||||||
twitchEmotes.push_back(std::move(tup));
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
addReplEmotes(phrase, midExtendedRef, pos1);
|
|
||||||
|
|
||||||
from += midsize;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
continue;
|
||||||
}
|
}
|
||||||
else
|
|
||||||
|
SizeType from = 0;
|
||||||
|
while ((from = this->originalMessage_.indexOf(
|
||||||
|
pattern, from, phrase.caseSensitivity())) != -1)
|
||||||
{
|
{
|
||||||
int from = 0;
|
replaceMessageAt(phrase, from, pattern.length(),
|
||||||
while ((from = this->originalMessage_.indexOf(
|
phrase.getReplace());
|
||||||
pattern, from, phrase.caseSensitivity())) != -1)
|
from += phrase.getReplace().length();
|
||||||
{
|
|
||||||
int len = pattern.size();
|
|
||||||
auto vret = removeEmotesInRange(from, len, twitchEmotes);
|
|
||||||
auto replace = phrase.getReplace();
|
|
||||||
|
|
||||||
int replacesize = replace.size();
|
|
||||||
this->originalMessage_.replace(from, len, replace);
|
|
||||||
|
|
||||||
int pos1 = from;
|
|
||||||
while (pos1 > 0)
|
|
||||||
{
|
|
||||||
if (this->originalMessage_[pos1 - 1] == ' ')
|
|
||||||
{
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
--pos1;
|
|
||||||
}
|
|
||||||
int pos2 = from + replacesize;
|
|
||||||
while (pos2 < this->originalMessage_.length())
|
|
||||||
{
|
|
||||||
if (this->originalMessage_[pos2] == ' ')
|
|
||||||
{
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
++pos2;
|
|
||||||
}
|
|
||||||
|
|
||||||
shiftIndicesAfter(from + len, replacesize - len);
|
|
||||||
|
|
||||||
#if QT_VERSION >= QT_VERSION_CHECK(5, 15, 0)
|
|
||||||
auto midExtendedRef =
|
|
||||||
QStringView{this->originalMessage_}.mid(pos1, pos2 - pos1);
|
|
||||||
#else
|
|
||||||
auto midExtendedRef =
|
|
||||||
this->originalMessage_.midRef(pos1, pos2 - pos1);
|
|
||||||
#endif
|
|
||||||
|
|
||||||
for (auto &tup : vret)
|
|
||||||
{
|
|
||||||
if (tup.ptr == nullptr)
|
|
||||||
{
|
|
||||||
qCDebug(chatterinoTwitch)
|
|
||||||
<< "v nullptr" << tup.name.string;
|
|
||||||
continue;
|
|
||||||
}
|
|
||||||
QRegularExpression emoteregex(
|
|
||||||
"\\b" + tup.name.string + "\\b",
|
|
||||||
QRegularExpression::UseUnicodePropertiesOption);
|
|
||||||
auto match = emoteregex.match(midExtendedRef);
|
|
||||||
if (match.hasMatch())
|
|
||||||
{
|
|
||||||
int last = match.lastCapturedIndex();
|
|
||||||
for (int i = 0; i <= last; ++i)
|
|
||||||
{
|
|
||||||
tup.start = from + match.capturedStart();
|
|
||||||
twitchEmotes.push_back(std::move(tup));
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
addReplEmotes(phrase, midExtendedRef, pos1);
|
|
||||||
|
|
||||||
from += replacesize;
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
Loading…
Reference in a new issue