chore: ensure statics are only present once in the final app (#5588)

This commit is contained in:
nerix 2024-09-14 14:13:12 +02:00 committed by GitHub
parent 2d8937f43e
commit 3d06f8612f
No known key found for this signature in database
GPG key ID: B5690EEEBB952194
14 changed files with 177 additions and 164 deletions

View file

@ -87,6 +87,7 @@
- Dev: Recent changes are now shown in the nightly release description. (#5553, #5554, #5593) - Dev: Recent changes are now shown in the nightly release description. (#5553, #5554, #5593)
- Dev: The timer for `StreamerMode` is now destroyed on the correct thread. (#5571) - Dev: The timer for `StreamerMode` is now destroyed on the correct thread. (#5571)
- Dev: Cleanup some parts of the `magic_enum` adaptation for Qt. (#5587) - Dev: Cleanup some parts of the `magic_enum` adaptation for Qt. (#5587)
- Dev: Refactored `static`s in headers to only be present once in the final app. (#5588)
## 2.5.1 ## 2.5.1

View file

@ -403,6 +403,8 @@ set(SOURCE_FILES
providers/twitch/TwitchBadges.hpp providers/twitch/TwitchBadges.hpp
providers/twitch/TwitchChannel.cpp providers/twitch/TwitchChannel.cpp
providers/twitch/TwitchChannel.hpp providers/twitch/TwitchChannel.hpp
providers/twitch/TwitchCommon.cpp
providers/twitch/TwitchCommon.hpp
providers/twitch/TwitchEmotes.cpp providers/twitch/TwitchEmotes.cpp
providers/twitch/TwitchEmotes.hpp providers/twitch/TwitchEmotes.hpp
providers/twitch/TwitchHelpers.cpp providers/twitch/TwitchHelpers.cpp

View file

@ -8,13 +8,14 @@
namespace chatterino { namespace chatterino {
constexpr QStringView LINK_CHATTERINO_WIKI = u"https://wiki.chatterino.com"; inline constexpr QStringView LINK_CHATTERINO_WIKI =
constexpr QStringView LINK_CHATTERINO_DISCORD = u"https://wiki.chatterino.com";
inline constexpr QStringView LINK_CHATTERINO_DISCORD =
u"https://discord.gg/7Y5AYhAK4z"; u"https://discord.gg/7Y5AYhAK4z";
constexpr QStringView LINK_CHATTERINO_SOURCE = inline constexpr QStringView LINK_CHATTERINO_SOURCE =
u"https://github.com/Chatterino/chatterino2"; u"https://github.com/Chatterino/chatterino2";
constexpr QStringView TWITCH_PLAYER_URL = inline constexpr QStringView TWITCH_PLAYER_URL =
u"https://player.twitch.tv/?channel=%1&parent=twitch.tv"; u"https://player.twitch.tv/?channel=%1&parent=twitch.tv";
enum class HighlightState { enum class HighlightState {
@ -23,14 +24,14 @@ enum class HighlightState {
NewMessage, NewMessage,
}; };
constexpr Qt::KeyboardModifiers SHOW_SPLIT_OVERLAY_MODIFIERS = inline constexpr Qt::KeyboardModifiers SHOW_SPLIT_OVERLAY_MODIFIERS =
Qt::ControlModifier | Qt::AltModifier; Qt::ControlModifier | Qt::AltModifier;
constexpr Qt::KeyboardModifiers SHOW_ADD_SPLIT_REGIONS = inline constexpr Qt::KeyboardModifiers SHOW_ADD_SPLIT_REGIONS =
Qt::ControlModifier | Qt::AltModifier; Qt::ControlModifier | Qt::AltModifier;
constexpr Qt::KeyboardModifiers SHOW_RESIZE_HANDLES_MODIFIERS = inline constexpr Qt::KeyboardModifiers SHOW_RESIZE_HANDLES_MODIFIERS =
Qt::ControlModifier; Qt::ControlModifier;
constexpr const char *ANONYMOUS_USERNAME_LABEL = " - anonymous - "; inline constexpr const char *ANONYMOUS_USERNAME_LABEL = " - anonymous - ";
template <typename T> template <typename T>
std::weak_ptr<T> weakOf(T *element) std::weak_ptr<T> weakOf(T *element)

View file

@ -10,13 +10,45 @@
namespace chatterino::filters { namespace chatterino::filters {
const QMap<QString, Type> MESSAGE_TYPING_CONTEXT{
{"author.badges", Type::StringList},
{"author.color", Type::Color},
{"author.name", Type::String},
{"author.no_color", Type::Bool},
{"author.subbed", Type::Bool},
{"author.sub_length", Type::Int},
{"channel.name", Type::String},
{"channel.watching", Type::Bool},
{"channel.live", Type::Bool},
{"flags.action", Type::Bool},
{"flags.highlighted", Type::Bool},
{"flags.points_redeemed", Type::Bool},
{"flags.sub_message", Type::Bool},
{"flags.system_message", Type::Bool},
{"flags.reward_message", Type::Bool},
{"flags.first_message", Type::Bool},
{"flags.elevated_message", Type::Bool},
{"flags.hype_chat", Type::Bool},
{"flags.cheer_message", Type::Bool},
{"flags.whisper", Type::Bool},
{"flags.reply", Type::Bool},
{"flags.automod", Type::Bool},
{"flags.restricted", Type::Bool},
{"flags.monitored", Type::Bool},
{"message.content", Type::String},
{"message.length", Type::Int},
{"reward.title", Type::String},
{"reward.cost", Type::Int},
{"reward.id", Type::String},
};
ContextMap buildContextMap(const MessagePtr &m, chatterino::Channel *channel) ContextMap buildContextMap(const MessagePtr &m, chatterino::Channel *channel)
{ {
auto watchingChannel = getApp()->getTwitch()->getWatchingChannel().get(); auto watchingChannel = getApp()->getTwitch()->getWatchingChannel().get();
/* /*
* Looking to add a new identifier to filters? Here's what to do: * Looking to add a new identifier to filters? Here's what to do:
* 1. Update validIdentifiersMap in Tokenizer.hpp * 1. Update validIdentifiersMap in Tokenizer.cpp
* 2. Add the identifier to the list below * 2. Add the identifier to the list below
* 3. Add the type of the identifier to MESSAGE_TYPING_CONTEXT in Filter.hpp * 3. Add the type of the identifier to MESSAGE_TYPING_CONTEXT in Filter.hpp
* 4. Add the value for the identifier to the ContextMap returned by this function * 4. Add the value for the identifier to the ContextMap returned by this function

View file

@ -22,37 +22,7 @@ namespace chatterino::filters {
// For example, flags.highlighted is a boolean variable, so it is marked as Type::Bool // For example, flags.highlighted is a boolean variable, so it is marked as Type::Bool
// below. These variable types will be used to check whether a filter "makes sense", // below. These variable types will be used to check whether a filter "makes sense",
// i.e. if all the variables and operators being used have compatible types. // i.e. if all the variables and operators being used have compatible types.
static const QMap<QString, Type> MESSAGE_TYPING_CONTEXT = { extern const QMap<QString, Type> MESSAGE_TYPING_CONTEXT;
{"author.badges", Type::StringList},
{"author.color", Type::Color},
{"author.name", Type::String},
{"author.no_color", Type::Bool},
{"author.subbed", Type::Bool},
{"author.sub_length", Type::Int},
{"channel.name", Type::String},
{"channel.watching", Type::Bool},
{"channel.live", Type::Bool},
{"flags.action", Type::Bool},
{"flags.highlighted", Type::Bool},
{"flags.points_redeemed", Type::Bool},
{"flags.sub_message", Type::Bool},
{"flags.system_message", Type::Bool},
{"flags.reward_message", Type::Bool},
{"flags.first_message", Type::Bool},
{"flags.elevated_message", Type::Bool},
{"flags.hype_chat", Type::Bool},
{"flags.cheer_message", Type::Bool},
{"flags.whisper", Type::Bool},
{"flags.reply", Type::Bool},
{"flags.automod", Type::Bool},
{"flags.restricted", Type::Bool},
{"flags.monitored", Type::Bool},
{"message.content", Type::String},
{"message.length", Type::Int},
{"reward.title", Type::String},
{"reward.cost", Type::Int},
{"reward.id", Type::String},
};
ContextMap buildContextMap(const MessagePtr &m, chatterino::Channel *channel); ContextMap buildContextMap(const MessagePtr &m, chatterino::Channel *channel);

View file

@ -2,8 +2,54 @@
#include "common/QLogging.hpp" #include "common/QLogging.hpp"
namespace {
const QRegularExpression TOKEN_REGEX(
"((r|ri)?\\\")((\\\\\")|[^\\\"])*\\\"|" // String/Regex literal
"[\\w\\.]+|" // Identifier or reserved keyword
"(<=?|>=?|!=?|==|\\|\\||&&|\\+|-|\\*|\\/|%)+|" // Operator
"[\\(\\)]|" // Parentheses
"[{},]" // List
);
} // namespace
namespace chatterino::filters { namespace chatterino::filters {
const QMap<QString, QString> VALID_IDENTIFIERS_MAP{
{"author.badges", "author badges"},
{"author.color", "author color"},
{"author.name", "author name"},
{"author.no_color", "author has no color?"},
{"author.subbed", "author subscribed?"},
{"author.sub_length", "author sub length"},
{"channel.name", "channel name"},
{"channel.watching", "/watching channel?"},
{"channel.live", "channel live?"},
{"flags.action", "action/me message?"},
{"flags.highlighted", "highlighted?"},
{"flags.points_redeemed", "redeemed points?"},
{"flags.sub_message", "sub/resub message?"},
{"flags.system_message", "system message?"},
{"flags.reward_message", "channel point reward message?"},
{"flags.first_message", "first message?"},
{"flags.elevated_message", "hype chat message?"},
// Ideally these values are unique, because ChannelFilterEditorDialog::ValueSpecifier::expressionText depends on
// std::map layout in Qt 6 and internal implementation in Qt 5.
{"flags.hype_chat", "hype chat message?"},
{"flags.cheer_message", "cheer message?"},
{"flags.whisper", "whisper message?"},
{"flags.reply", "reply message?"},
{"flags.automod", "automod message?"},
{"flags.restricted", "restricted message?"},
{"flags.monitored", "monitored message?"},
{"message.content", "message text"},
{"message.length", "message length"},
{"reward.title", "point reward title"},
{"reward.cost", "point reward cost"},
{"reward.id", "point reward id"},
};
QString tokenTypeToInfoString(TokenType type) QString tokenTypeToInfoString(TokenType type)
{ {
switch (type) switch (type)
@ -77,7 +123,7 @@ QString tokenTypeToInfoString(TokenType type)
Tokenizer::Tokenizer(const QString &text) Tokenizer::Tokenizer(const QString &text)
{ {
QRegularExpressionMatchIterator i = tokenRegex.globalMatch(text); QRegularExpressionMatchIterator i = TOKEN_REGEX.globalMatch(text);
while (i.hasNext()) while (i.hasNext())
{ {
auto capturedText = i.next().captured(); auto capturedText = i.next().captured();
@ -278,7 +324,7 @@ TokenType Tokenizer::tokenize(const QString &text)
return TokenType::STRING; return TokenType::STRING;
} }
if (validIdentifiersMap.keys().contains(text)) if (VALID_IDENTIFIERS_MAP.keys().contains(text))
{ {
return TokenType::IDENTIFIER; return TokenType::IDENTIFIER;
} }

View file

@ -8,49 +8,7 @@
namespace chatterino::filters { namespace chatterino::filters {
static const QMap<QString, QString> validIdentifiersMap = { extern const QMap<QString, QString> VALID_IDENTIFIERS_MAP;
{"author.badges", "author badges"},
{"author.color", "author color"},
{"author.name", "author name"},
{"author.no_color", "author has no color?"},
{"author.subbed", "author subscribed?"},
{"author.sub_length", "author sub length"},
{"channel.name", "channel name"},
{"channel.watching", "/watching channel?"},
{"channel.live", "channel live?"},
{"flags.action", "action/me message?"},
{"flags.highlighted", "highlighted?"},
{"flags.points_redeemed", "redeemed points?"},
{"flags.sub_message", "sub/resub message?"},
{"flags.system_message", "system message?"},
{"flags.reward_message", "channel point reward message?"},
{"flags.first_message", "first message?"},
{"flags.elevated_message", "hype chat message?"},
// Ideally these values are unique, because ChannelFilterEditorDialog::ValueSpecifier::expressionText depends on
// std::map layout in Qt 6 and internal implementation in Qt 5.
{"flags.hype_chat", "hype chat message?"},
{"flags.cheer_message", "cheer message?"},
{"flags.whisper", "whisper message?"},
{"flags.reply", "reply message?"},
{"flags.automod", "automod message?"},
{"flags.restricted", "restricted message?"},
{"flags.monitored", "monitored message?"},
{"message.content", "message text"},
{"message.length", "message length"},
{"reward.title", "point reward title"},
{"reward.cost", "point reward cost"},
{"reward.id", "point reward id"},
};
// clang-format off
static const QRegularExpression tokenRegex(
QString("((r|ri)?\\\")((\\\\\")|[^\\\"])*\\\"|") + // String/Regex literal
QString("[\\w\\.]+|") + // Identifier or reserved keyword
QString("(<=?|>=?|!=?|==|\\|\\||&&|\\+|-|\\*|\\/|%)+|") + // Operator
QString("[\\(\\)]|") + // Parentheses
QString("[{},]") // List
);
// clang-format on
enum TokenType { enum TokenType {
// control // control

View file

@ -55,7 +55,7 @@ public:
const QString &emoteID) const; const QString &emoteID) const;
}; };
static const std::shared_ptr<const EmoteMap> EMPTY_EMOTE_MAP = std::make_shared< inline const std::shared_ptr<const EmoteMap> EMPTY_EMOTE_MAP = std::make_shared<
const EmoteMap>(); // NOLINT(cert-err58-cpp) -- assume this doesn't throw an exception const EmoteMap>(); // NOLINT(cert-err58-cpp) -- assume this doesn't throw an exception
EmotePtr cachedOrMakeEmotePtr(Emote &&emote, const EmoteMap &cache); EmotePtr cachedOrMakeEmotePtr(Emote &&emote, const EmoteMap &cache);

View file

@ -0,0 +1,63 @@
#include "providers/twitch/TwitchCommon.hpp"
namespace chatterino {
const std::vector<QColor> TWITCH_USERNAME_COLORS = {
{255, 0, 0}, // Red
{0, 0, 255}, // Blue
{0, 255, 0}, // Green
{178, 34, 34}, // FireBrick
{255, 127, 80}, // Coral
{154, 205, 50}, // YellowGreen
{255, 69, 0}, // OrangeRed
{46, 139, 87}, // SeaGreen
{218, 165, 32}, // GoldenRod
{210, 105, 30}, // Chocolate
{95, 158, 160}, // CadetBlue
{30, 144, 255}, // DodgerBlue
{255, 105, 180}, // HotPink
{138, 43, 226}, // BlueViolet
{0, 255, 127}, // SpringGreen
};
const QStringList TWITCH_DEFAULT_COMMANDS{
"help",
"w",
"me",
"disconnect",
"mods",
"vips",
"color",
"commercial",
"mod",
"unmod",
"vip",
"unvip",
"ban",
"unban",
"timeout",
"untimeout",
"slow",
"slowoff",
"r9kbeta",
"r9kbetaoff",
"emoteonly",
"emoteonlyoff",
"clear",
"subscribers",
"subscribersoff",
"followers",
"followersoff",
"host",
"unhost",
"raid",
"unraid",
"delete",
"announce",
"requests",
"warn",
};
const QStringList TWITCH_WHISPER_COMMANDS{"/w", ".w"};
} // namespace chatterino

View file

@ -7,79 +7,19 @@
namespace chatterino { namespace chatterino {
#ifndef ATTR_UNUSED [[maybe_unused]] inline const char *const ANONYMOUS_USERNAME = "justinfan64537";
# ifdef Q_OS_WIN
# define ATTR_UNUSED
# else
# define ATTR_UNUSED __attribute__((unused))
# endif
#endif
static const char *ANONYMOUS_USERNAME ATTR_UNUSED = "justinfan64537"; inline constexpr int TWITCH_MESSAGE_LIMIT = 500;
static constexpr int TWITCH_MESSAGE_LIMIT = 500;
inline QByteArray getDefaultClientID() inline QByteArray getDefaultClientID()
{ {
return QByteArray("7ue61iz46fz11y3cugd0l3tawb4taal"); return QByteArrayLiteral("7ue61iz46fz11y3cugd0l3tawb4taal");
} }
static const std::vector<QColor> TWITCH_USERNAME_COLORS = { extern const std::vector<QColor> TWITCH_USERNAME_COLORS;
{255, 0, 0}, // Red
{0, 0, 255}, // Blue
{0, 255, 0}, // Green
{178, 34, 34}, // FireBrick
{255, 127, 80}, // Coral
{154, 205, 50}, // YellowGreen
{255, 69, 0}, // OrangeRed
{46, 139, 87}, // SeaGreen
{218, 165, 32}, // GoldenRod
{210, 105, 30}, // Chocolate
{95, 158, 160}, // CadetBlue
{30, 144, 255}, // DodgerBlue
{255, 105, 180}, // HotPink
{138, 43, 226}, // BlueViolet
{0, 255, 127}, // SpringGreen
};
static const QStringList TWITCH_DEFAULT_COMMANDS{ extern const QStringList TWITCH_DEFAULT_COMMANDS;
"help",
"w",
"me",
"disconnect",
"mods",
"vips",
"color",
"commercial",
"mod",
"unmod",
"vip",
"unvip",
"ban",
"unban",
"timeout",
"untimeout",
"slow",
"slowoff",
"r9kbeta",
"r9kbetaoff",
"emoteonly",
"emoteonlyoff",
"clear",
"subscribers",
"subscribersoff",
"followers",
"followersoff",
"host",
"unhost",
"raid",
"unraid",
"delete",
"announce",
"requests",
"warn",
};
static const QStringList TWITCH_WHISPER_COMMANDS{"/w", ".w"}; extern const QStringList TWITCH_WHISPER_COMMANDS;
} // namespace chatterino } // namespace chatterino

View file

@ -18,7 +18,7 @@ namespace chatterino {
// variant // variant
/// %1 <-> {id} /// %1 <-> {id}
/// %2 <-> {scale} (1.0, 2.0, 3.0) /// %2 <-> {scale} (1.0, 2.0, 3.0)
constexpr QStringView TWITCH_EMOTE_TEMPLATE = inline constexpr QStringView TWITCH_EMOTE_TEMPLATE =
u"https://static-cdn.jtvnw.net/emoticons/v2/%1/default/dark/%2"; u"https://static-cdn.jtvnw.net/emoticons/v2/%1/default/dark/%2";
struct Emote; struct Emote;
@ -63,8 +63,8 @@ using TwitchEmoteSetMap = boost::unordered_flat_map<EmoteSetId, TwitchEmoteSet>;
struct HelixChannelEmote; struct HelixChannelEmote;
constexpr QStringView TWITCH_SUB_EMOTE_SET_PREFIX = u"x-c2-s-"; inline constexpr QStringView TWITCH_SUB_EMOTE_SET_PREFIX = u"x-c2-s-";
constexpr QStringView TWITCH_BIT_EMOTE_SET_PREFIX = u"x-c2-b-"; inline constexpr QStringView TWITCH_BIT_EMOTE_SET_PREFIX = u"x-c2-b-";
struct TwitchEmoteSetMeta { struct TwitchEmoteSetMeta {
QString setID; QString setID;

View file

@ -100,7 +100,7 @@ ChannelFilterEditorDialog::ValueSpecifier::ValueSpecifier()
this->typeCombo_->insertItems( this->typeCombo_->insertItems(
0, {"Constant Text", "Constant Number", "Variable"}); 0, {"Constant Text", "Constant Number", "Variable"});
this->varCombo_->insertItems(0, filters::validIdentifiersMap.values()); this->varCombo_->insertItems(0, filters::VALID_IDENTIFIERS_MAP.values());
this->layout_->addWidget(this->typeCombo_); this->layout_->addWidget(this->typeCombo_);
this->layout_->addWidget(this->varCombo_, 1); this->layout_->addWidget(this->varCombo_, 1);
@ -142,7 +142,7 @@ void ChannelFilterEditorDialog::ValueSpecifier::setValue(const QString &value)
if (this->typeCombo_->currentIndex() == 2) if (this->typeCombo_->currentIndex() == 2)
{ {
this->varCombo_->setCurrentText( this->varCombo_->setCurrentText(
filters::validIdentifiersMap.value(value)); filters::VALID_IDENTIFIERS_MAP.value(value));
} }
else else
{ {
@ -165,7 +165,7 @@ QString ChannelFilterEditorDialog::ValueSpecifier::expressionText()
case 1: // number case 1: // number
return this->valueInput_->text(); return this->valueInput_->text();
case 2: // variable case 2: // variable
return filters::validIdentifiersMap.key( return filters::VALID_IDENTIFIERS_MAP.key(
this->varCombo_->currentText()); this->varCombo_->currentText());
default: default:
return ""; return "";

View file

@ -11,7 +11,7 @@
namespace chatterino { namespace chatterino {
constexpr int NOTEBOOK_TAB_HEIGHT = 28; inline constexpr int NOTEBOOK_TAB_HEIGHT = 28;
class SplitContainer; class SplitContainer;

View file

@ -24,8 +24,6 @@ using namespace chatterino;
using namespace chatterino::filters; using namespace chatterino::filters;
using chatterino::mock::MockChannel; using chatterino::mock::MockChannel;
TypingContext typingContext = MESSAGE_TYPING_CONTEXT;
namespace { namespace {
class MockApplication : public mock::BaseApplication class MockApplication : public mock::BaseApplication
@ -188,7 +186,8 @@ TEST(Filters, TypeSynthesis)
T type = filter.returnType(); T type = filter.returnType();
EXPECT_EQ(type, expected) EXPECT_EQ(type, expected)
<< "Filter{ " << input << " } has type " << type << " instead of " << "Filter{ " << input << " } has type " << type << " instead of "
<< expected << ".\nDebug: " << filter.debugString(typingContext); << expected
<< ".\nDebug: " << filter.debugString(MESSAGE_TYPING_CONTEXT);
} }
} }
@ -265,7 +264,7 @@ TEST(Filters, Evaluation)
EXPECT_EQ(result, expected) EXPECT_EQ(result, expected)
<< "Filter{ " << input << " } evaluated to " << result.toString() << "Filter{ " << input << " } evaluated to " << result.toString()
<< " instead of " << expected.toString() << " instead of " << expected.toString()
<< ".\nDebug: " << filter.debugString(typingContext); << ".\nDebug: " << filter.debugString(MESSAGE_TYPING_CONTEXT);
} }
} }
@ -368,7 +367,8 @@ TEST_F(FiltersF, ExpressionDebug)
EXPECT_NE(filter, nullptr) << "Filter::fromString(" << input EXPECT_NE(filter, nullptr) << "Filter::fromString(" << input
<< ") did not build a proper filter"; << ") did not build a proper filter";
const auto actualDebugString = filter->debugString(typingContext); const auto actualDebugString =
filter->debugString(MESSAGE_TYPING_CONTEXT);
EXPECT_EQ(actualDebugString, debugString) EXPECT_EQ(actualDebugString, debugString)
<< "filter->debugString() on '" << input << "' should be '" << "filter->debugString() on '" << input << "' should be '"
<< debugString << "', but got '" << actualDebugString << "'"; << debugString << "', but got '" << actualDebugString << "'";