Add emote completion widget filter for only zero-width emotes using :~ (#6362)
Co-authored-by: Rasmus Karlsson <rasmus.karlsson@pajlada.com>
This commit is contained in:
@@ -23,6 +23,7 @@
|
|||||||
- Minor: Message character length label is now monospaced. (#6366)
|
- Minor: Message character length label is now monospaced. (#6366)
|
||||||
- Minor: Add a setting under Moderation -> Logs to customize the timestamp used for chat logs. (#6338)
|
- Minor: Add a setting under Moderation -> Logs to customize the timestamp used for chat logs. (#6338)
|
||||||
- Minor: Add a setting under Moderation -> Logs to use server timestamp from the message instead of the local clock time for logging. (#6346)
|
- Minor: Add a setting under Moderation -> Logs to use server timestamp from the message instead of the local clock time for logging. (#6346)
|
||||||
|
- Minor: Add feature to search for only zero-width emotes when prepended by `:~`. (#6362)
|
||||||
- Bugfix: Commands are no longer tab-completable in the middle of messages. (#6273)
|
- Bugfix: Commands are no longer tab-completable in the middle of messages. (#6273)
|
||||||
- Bugfix: Automatic streamer mode detection now works from Flatpak. (#6250)
|
- Bugfix: Automatic streamer mode detection now works from Flatpak. (#6250)
|
||||||
- Bugfix: Don't create native messaging manifest file if browser directory doesn't exist. (#6116)
|
- Bugfix: Don't create native messaging manifest file if browser directory doesn't exist. (#6116)
|
||||||
|
|||||||
@@ -13,6 +13,7 @@ Q_LOGGING_CATEGORY(chatterinoBttv, "chatterino.bttv", logThreshold);
|
|||||||
Q_LOGGING_CATEGORY(chatterinoCache, "chatterino.cache", logThreshold);
|
Q_LOGGING_CATEGORY(chatterinoCache, "chatterino.cache", logThreshold);
|
||||||
Q_LOGGING_CATEGORY(chatterinoCommands, "chatterino.commands", logThreshold);
|
Q_LOGGING_CATEGORY(chatterinoCommands, "chatterino.commands", logThreshold);
|
||||||
Q_LOGGING_CATEGORY(chatterinoCommon, "chatterino.common", logThreshold);
|
Q_LOGGING_CATEGORY(chatterinoCommon, "chatterino.common", logThreshold);
|
||||||
|
Q_LOGGING_CATEGORY(chatterinoCompletion, "chatterino.completion", logThreshold);
|
||||||
Q_LOGGING_CATEGORY(chatterinoCrashhandler, "chatterino.crashhandler",
|
Q_LOGGING_CATEGORY(chatterinoCrashhandler, "chatterino.crashhandler",
|
||||||
logThreshold);
|
logThreshold);
|
||||||
Q_LOGGING_CATEGORY(chatterinoEmoji, "chatterino.emoji", logThreshold);
|
Q_LOGGING_CATEGORY(chatterinoEmoji, "chatterino.emoji", logThreshold);
|
||||||
|
|||||||
@@ -9,6 +9,7 @@ Q_DECLARE_LOGGING_CATEGORY(chatterinoBttv);
|
|||||||
Q_DECLARE_LOGGING_CATEGORY(chatterinoCache);
|
Q_DECLARE_LOGGING_CATEGORY(chatterinoCache);
|
||||||
Q_DECLARE_LOGGING_CATEGORY(chatterinoCommands);
|
Q_DECLARE_LOGGING_CATEGORY(chatterinoCommands);
|
||||||
Q_DECLARE_LOGGING_CATEGORY(chatterinoCommon);
|
Q_DECLARE_LOGGING_CATEGORY(chatterinoCommon);
|
||||||
|
Q_DECLARE_LOGGING_CATEGORY(chatterinoCompletion);
|
||||||
Q_DECLARE_LOGGING_CATEGORY(chatterinoCrashhandler);
|
Q_DECLARE_LOGGING_CATEGORY(chatterinoCrashhandler);
|
||||||
Q_DECLARE_LOGGING_CATEGORY(chatterinoEmoji);
|
Q_DECLARE_LOGGING_CATEGORY(chatterinoEmoji);
|
||||||
Q_DECLARE_LOGGING_CATEGORY(chatterinoEnv);
|
Q_DECLARE_LOGGING_CATEGORY(chatterinoEnv);
|
||||||
|
|||||||
@@ -1,23 +1,45 @@
|
|||||||
#include "controllers/completion/strategies/ClassicEmoteStrategy.hpp"
|
#include "controllers/completion/strategies/ClassicEmoteStrategy.hpp"
|
||||||
|
|
||||||
|
#include "common/QLogging.hpp"
|
||||||
#include "singletons/Settings.hpp"
|
#include "singletons/Settings.hpp"
|
||||||
#include "util/Helpers.hpp"
|
#include "util/Helpers.hpp"
|
||||||
|
|
||||||
|
#include <algorithm>
|
||||||
|
|
||||||
namespace chatterino::completion {
|
namespace chatterino::completion {
|
||||||
|
|
||||||
|
namespace {
|
||||||
|
|
||||||
|
// NOLINTNEXTLINE(cppcoreguidelines-avoid-non-const-global-variables)
|
||||||
|
const auto &LOG = chatterinoCompletion;
|
||||||
|
|
||||||
|
} // namespace
|
||||||
|
|
||||||
void ClassicEmoteStrategy::apply(const std::vector<EmoteItem> &items,
|
void ClassicEmoteStrategy::apply(const std::vector<EmoteItem> &items,
|
||||||
std::vector<EmoteItem> &output,
|
std::vector<EmoteItem> &output,
|
||||||
const QString &query) const
|
const QString &query) const
|
||||||
{
|
{
|
||||||
|
qCDebug(LOG) << "ClassicEmoteStrategy apply" << query;
|
||||||
QString normalizedQuery = query;
|
QString normalizedQuery = query;
|
||||||
|
bool zeroWidthOnly = false;
|
||||||
if (normalizedQuery.startsWith(':'))
|
if (normalizedQuery.startsWith(':'))
|
||||||
{
|
{
|
||||||
normalizedQuery = normalizedQuery.mid(1);
|
normalizedQuery = normalizedQuery.mid(1);
|
||||||
}
|
}
|
||||||
|
if (normalizedQuery.startsWith('~'))
|
||||||
|
{
|
||||||
|
normalizedQuery = normalizedQuery.mid(1);
|
||||||
|
zeroWidthOnly = true;
|
||||||
|
}
|
||||||
|
|
||||||
// First pass: filter by contains match
|
// First pass: filter by zero-width only and contains match
|
||||||
for (const auto &item : items)
|
for (const auto &item : items)
|
||||||
{
|
{
|
||||||
|
if (zeroWidthOnly && !item.emote->zeroWidth)
|
||||||
|
{
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
if (item.searchName.contains(normalizedQuery, Qt::CaseInsensitive))
|
if (item.searchName.contains(normalizedQuery, Qt::CaseInsensitive))
|
||||||
{
|
{
|
||||||
output.push_back(item);
|
output.push_back(item);
|
||||||
@@ -52,6 +74,7 @@ void ClassicTabEmoteStrategy::apply(const std::vector<EmoteItem> &items,
|
|||||||
std::vector<EmoteItem> &output,
|
std::vector<EmoteItem> &output,
|
||||||
const QString &query) const
|
const QString &query) const
|
||||||
{
|
{
|
||||||
|
qCDebug(LOG) << "ClassicTabEmoteStrategy apply" << query;
|
||||||
bool colonStart = query.startsWith(':');
|
bool colonStart = query.startsWith(':');
|
||||||
QStringView normalizedQuery = query;
|
QStringView normalizedQuery = query;
|
||||||
if (colonStart)
|
if (colonStart)
|
||||||
|
|||||||
@@ -11,6 +11,10 @@
|
|||||||
|
|
||||||
namespace chatterino::completion {
|
namespace chatterino::completion {
|
||||||
namespace {
|
namespace {
|
||||||
|
|
||||||
|
// NOLINTNEXTLINE(cppcoreguidelines-avoid-non-const-global-variables)
|
||||||
|
const auto &LOG = chatterinoCompletion;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @brief This function calculates the "cost" of the changes that need to
|
* @brief This function calculates the "cost" of the changes that need to
|
||||||
* be done to the query to make it the value.
|
* be done to the query to make it the value.
|
||||||
@@ -67,7 +71,7 @@ int costOfEmote(QStringView query, QStringView emote, bool prioritizeUpper)
|
|||||||
// matchingFunction is used for testing if the emote should be included in the search.
|
// matchingFunction is used for testing if the emote should be included in the search.
|
||||||
void completeEmotes(
|
void completeEmotes(
|
||||||
const std::vector<EmoteItem> &items, std::vector<EmoteItem> &output,
|
const std::vector<EmoteItem> &items, std::vector<EmoteItem> &output,
|
||||||
QStringView query, bool ignoreColonForCost,
|
QStringView query, bool ignoreColonForCost, bool ignoreTildeForCost,
|
||||||
const std::function<bool(EmoteItem, Qt::CaseSensitivity)> &matchingFunction)
|
const std::function<bool(EmoteItem, Qt::CaseSensitivity)> &matchingFunction)
|
||||||
{
|
{
|
||||||
// Given these emotes: pajaW, PAJAW
|
// Given these emotes: pajaW, PAJAW
|
||||||
@@ -128,7 +132,7 @@ void completeEmotes(
|
|||||||
}
|
}
|
||||||
|
|
||||||
std::sort(output.begin(), output.end(),
|
std::sort(output.begin(), output.end(),
|
||||||
[query, prioritizeUpper, ignoreColonForCost](
|
[query, prioritizeUpper, ignoreColonForCost, ignoreTildeForCost](
|
||||||
const EmoteItem &a, const EmoteItem &b) -> bool {
|
const EmoteItem &a, const EmoteItem &b) -> bool {
|
||||||
auto tempA = a.searchName;
|
auto tempA = a.searchName;
|
||||||
auto tempB = b.searchName;
|
auto tempB = b.searchName;
|
||||||
@@ -136,10 +140,18 @@ void completeEmotes(
|
|||||||
{
|
{
|
||||||
tempA = tempA.mid(1);
|
tempA = tempA.mid(1);
|
||||||
}
|
}
|
||||||
|
if (ignoreTildeForCost && tempA.startsWith("~"))
|
||||||
|
{
|
||||||
|
tempA = tempA.mid(1);
|
||||||
|
}
|
||||||
if (ignoreColonForCost && tempB.startsWith(":"))
|
if (ignoreColonForCost && tempB.startsWith(":"))
|
||||||
{
|
{
|
||||||
tempB = tempB.mid(1);
|
tempB = tempB.mid(1);
|
||||||
}
|
}
|
||||||
|
if (ignoreTildeForCost && tempB.startsWith("~"))
|
||||||
|
{
|
||||||
|
tempB = tempB.mid(1);
|
||||||
|
}
|
||||||
|
|
||||||
auto costA = costOfEmote(query, tempA, prioritizeUpper);
|
auto costA = costOfEmote(query, tempA, prioritizeUpper);
|
||||||
auto costB = costOfEmote(query, tempB, prioritizeUpper);
|
auto costB = costOfEmote(query, tempB, prioritizeUpper);
|
||||||
@@ -159,14 +171,30 @@ void SmartEmoteStrategy::apply(const std::vector<EmoteItem> &items,
|
|||||||
std::vector<EmoteItem> &output,
|
std::vector<EmoteItem> &output,
|
||||||
const QString &query) const
|
const QString &query) const
|
||||||
{
|
{
|
||||||
|
qCDebug(LOG) << "SmartEmoteStrategy apply" << query;
|
||||||
|
std::vector<EmoteItem> filteredItems = items;
|
||||||
QString normalizedQuery = query;
|
QString normalizedQuery = query;
|
||||||
bool ignoreColonForCost = false;
|
bool ignoreColonForCost = false;
|
||||||
|
bool zeroWidthOnly = false;
|
||||||
if (normalizedQuery.startsWith(':'))
|
if (normalizedQuery.startsWith(':'))
|
||||||
{
|
{
|
||||||
normalizedQuery = normalizedQuery.mid(1);
|
normalizedQuery = normalizedQuery.mid(1);
|
||||||
ignoreColonForCost = true;
|
ignoreColonForCost = true;
|
||||||
}
|
}
|
||||||
completeEmotes(items, output, normalizedQuery, ignoreColonForCost,
|
if (normalizedQuery.startsWith('~'))
|
||||||
|
{
|
||||||
|
normalizedQuery = normalizedQuery.mid(1);
|
||||||
|
zeroWidthOnly = true;
|
||||||
|
|
||||||
|
auto [first, last] = std::ranges::remove_if(
|
||||||
|
filteredItems, [](const EmoteItem &emoteItem) {
|
||||||
|
return !emoteItem.emote->zeroWidth;
|
||||||
|
});
|
||||||
|
filteredItems.erase(first, last);
|
||||||
|
}
|
||||||
|
|
||||||
|
completeEmotes(filteredItems, output, normalizedQuery, ignoreColonForCost,
|
||||||
|
zeroWidthOnly,
|
||||||
[normalizedQuery](const EmoteItem &left,
|
[normalizedQuery](const EmoteItem &left,
|
||||||
Qt::CaseSensitivity caseHandling) {
|
Qt::CaseSensitivity caseHandling) {
|
||||||
return left.searchName.contains(normalizedQuery,
|
return left.searchName.contains(normalizedQuery,
|
||||||
@@ -178,6 +206,7 @@ void SmartTabEmoteStrategy::apply(const std::vector<EmoteItem> &items,
|
|||||||
std::vector<EmoteItem> &output,
|
std::vector<EmoteItem> &output,
|
||||||
const QString &query) const
|
const QString &query) const
|
||||||
{
|
{
|
||||||
|
qCDebug(LOG) << "SmartTabEmoteStrategy apply" << query;
|
||||||
bool colonStart = query.startsWith(':');
|
bool colonStart = query.startsWith(':');
|
||||||
QStringView normalizedQuery = query;
|
QStringView normalizedQuery = query;
|
||||||
if (colonStart)
|
if (colonStart)
|
||||||
@@ -187,7 +216,7 @@ void SmartTabEmoteStrategy::apply(const std::vector<EmoteItem> &items,
|
|||||||
}
|
}
|
||||||
|
|
||||||
completeEmotes(
|
completeEmotes(
|
||||||
items, output, normalizedQuery, false,
|
items, output, normalizedQuery, false, false,
|
||||||
[&](const EmoteItem &item, Qt::CaseSensitivity caseHandling) -> bool {
|
[&](const EmoteItem &item, Qt::CaseSensitivity caseHandling) -> bool {
|
||||||
QStringView itemQuery;
|
QStringView itemQuery;
|
||||||
if (item.isEmoji)
|
if (item.isEmoji)
|
||||||
|
|||||||
Reference in New Issue
Block a user