From 3d06f8612fb77eed7de850af2823623fd3fffd1b Mon Sep 17 00:00:00 2001 From: nerix Date: Sat, 14 Sep 2024 14:13:12 +0200 Subject: [PATCH] chore: ensure `static`s are only present once in the final app (#5588) --- CHANGELOG.md | 1 + src/CMakeLists.txt | 2 + src/common/Common.hpp | 17 ++--- src/controllers/filters/lang/Filter.cpp | 34 ++++++++- src/controllers/filters/lang/Filter.hpp | 32 +-------- src/controllers/filters/lang/Tokenizer.cpp | 50 ++++++++++++- src/controllers/filters/lang/Tokenizer.hpp | 44 +----------- src/messages/Emote.hpp | 2 +- src/providers/twitch/TwitchCommon.cpp | 63 ++++++++++++++++ src/providers/twitch/TwitchCommon.hpp | 72 ++----------------- src/providers/twitch/TwitchEmotes.hpp | 6 +- .../dialogs/ChannelFilterEditorDialog.cpp | 6 +- src/widgets/helper/NotebookTab.hpp | 2 +- tests/src/Filters.cpp | 10 +-- 14 files changed, 177 insertions(+), 164 deletions(-) create mode 100644 src/providers/twitch/TwitchCommon.cpp diff --git a/CHANGELOG.md b/CHANGELOG.md index 4d8e97668..a11f72ffd 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -87,6 +87,7 @@ - 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: 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 diff --git a/src/CMakeLists.txt b/src/CMakeLists.txt index db6b1551d..68c9bd4e5 100644 --- a/src/CMakeLists.txt +++ b/src/CMakeLists.txt @@ -403,6 +403,8 @@ set(SOURCE_FILES providers/twitch/TwitchBadges.hpp providers/twitch/TwitchChannel.cpp providers/twitch/TwitchChannel.hpp + providers/twitch/TwitchCommon.cpp + providers/twitch/TwitchCommon.hpp providers/twitch/TwitchEmotes.cpp providers/twitch/TwitchEmotes.hpp providers/twitch/TwitchHelpers.cpp diff --git a/src/common/Common.hpp b/src/common/Common.hpp index f1ce96754..7a0436fc1 100644 --- a/src/common/Common.hpp +++ b/src/common/Common.hpp @@ -8,13 +8,14 @@ namespace chatterino { -constexpr QStringView LINK_CHATTERINO_WIKI = u"https://wiki.chatterino.com"; -constexpr QStringView LINK_CHATTERINO_DISCORD = +inline constexpr QStringView LINK_CHATTERINO_WIKI = + u"https://wiki.chatterino.com"; +inline constexpr QStringView LINK_CHATTERINO_DISCORD = u"https://discord.gg/7Y5AYhAK4z"; -constexpr QStringView LINK_CHATTERINO_SOURCE = +inline constexpr QStringView LINK_CHATTERINO_SOURCE = 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"; enum class HighlightState { @@ -23,14 +24,14 @@ enum class HighlightState { NewMessage, }; -constexpr Qt::KeyboardModifiers SHOW_SPLIT_OVERLAY_MODIFIERS = +inline constexpr Qt::KeyboardModifiers SHOW_SPLIT_OVERLAY_MODIFIERS = Qt::ControlModifier | Qt::AltModifier; -constexpr Qt::KeyboardModifiers SHOW_ADD_SPLIT_REGIONS = +inline constexpr Qt::KeyboardModifiers SHOW_ADD_SPLIT_REGIONS = Qt::ControlModifier | Qt::AltModifier; -constexpr Qt::KeyboardModifiers SHOW_RESIZE_HANDLES_MODIFIERS = +inline constexpr Qt::KeyboardModifiers SHOW_RESIZE_HANDLES_MODIFIERS = Qt::ControlModifier; -constexpr const char *ANONYMOUS_USERNAME_LABEL = " - anonymous - "; +inline constexpr const char *ANONYMOUS_USERNAME_LABEL = " - anonymous - "; template std::weak_ptr weakOf(T *element) diff --git a/src/controllers/filters/lang/Filter.cpp b/src/controllers/filters/lang/Filter.cpp index 2cda8a13b..7b9ffe38f 100644 --- a/src/controllers/filters/lang/Filter.cpp +++ b/src/controllers/filters/lang/Filter.cpp @@ -10,13 +10,45 @@ namespace chatterino::filters { +const QMap 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) { auto watchingChannel = getApp()->getTwitch()->getWatchingChannel().get(); /* * 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 * 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 diff --git a/src/controllers/filters/lang/Filter.hpp b/src/controllers/filters/lang/Filter.hpp index 7a0f44805..542590026 100644 --- a/src/controllers/filters/lang/Filter.hpp +++ b/src/controllers/filters/lang/Filter.hpp @@ -22,37 +22,7 @@ namespace chatterino::filters { // 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", // i.e. if all the variables and operators being used have compatible types. -static const QMap 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}, -}; +extern const QMap MESSAGE_TYPING_CONTEXT; ContextMap buildContextMap(const MessagePtr &m, chatterino::Channel *channel); diff --git a/src/controllers/filters/lang/Tokenizer.cpp b/src/controllers/filters/lang/Tokenizer.cpp index f25f1976b..ce4f5c16d 100644 --- a/src/controllers/filters/lang/Tokenizer.cpp +++ b/src/controllers/filters/lang/Tokenizer.cpp @@ -2,8 +2,54 @@ #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 { +const QMap 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) { switch (type) @@ -77,7 +123,7 @@ QString tokenTypeToInfoString(TokenType type) Tokenizer::Tokenizer(const QString &text) { - QRegularExpressionMatchIterator i = tokenRegex.globalMatch(text); + QRegularExpressionMatchIterator i = TOKEN_REGEX.globalMatch(text); while (i.hasNext()) { auto capturedText = i.next().captured(); @@ -278,7 +324,7 @@ TokenType Tokenizer::tokenize(const QString &text) return TokenType::STRING; } - if (validIdentifiersMap.keys().contains(text)) + if (VALID_IDENTIFIERS_MAP.keys().contains(text)) { return TokenType::IDENTIFIER; } diff --git a/src/controllers/filters/lang/Tokenizer.hpp b/src/controllers/filters/lang/Tokenizer.hpp index ced78c5d2..27dfc418a 100644 --- a/src/controllers/filters/lang/Tokenizer.hpp +++ b/src/controllers/filters/lang/Tokenizer.hpp @@ -8,49 +8,7 @@ namespace chatterino::filters { -static const QMap validIdentifiersMap = { - {"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 +extern const QMap VALID_IDENTIFIERS_MAP; enum TokenType { // control diff --git a/src/messages/Emote.hpp b/src/messages/Emote.hpp index 57e4a8e68..d0861849b 100644 --- a/src/messages/Emote.hpp +++ b/src/messages/Emote.hpp @@ -55,7 +55,7 @@ public: const QString &emoteID) const; }; -static const std::shared_ptr EMPTY_EMOTE_MAP = std::make_shared< +inline const std::shared_ptr EMPTY_EMOTE_MAP = std::make_shared< const EmoteMap>(); // NOLINT(cert-err58-cpp) -- assume this doesn't throw an exception EmotePtr cachedOrMakeEmotePtr(Emote &&emote, const EmoteMap &cache); diff --git a/src/providers/twitch/TwitchCommon.cpp b/src/providers/twitch/TwitchCommon.cpp new file mode 100644 index 000000000..91d09e6a7 --- /dev/null +++ b/src/providers/twitch/TwitchCommon.cpp @@ -0,0 +1,63 @@ +#include "providers/twitch/TwitchCommon.hpp" + +namespace chatterino { + +const std::vector 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 diff --git a/src/providers/twitch/TwitchCommon.hpp b/src/providers/twitch/TwitchCommon.hpp index a5c388245..e0fffc66d 100644 --- a/src/providers/twitch/TwitchCommon.hpp +++ b/src/providers/twitch/TwitchCommon.hpp @@ -7,79 +7,19 @@ namespace chatterino { -#ifndef ATTR_UNUSED -# ifdef Q_OS_WIN -# define ATTR_UNUSED -# else -# define ATTR_UNUSED __attribute__((unused)) -# endif -#endif +[[maybe_unused]] inline const char *const ANONYMOUS_USERNAME = "justinfan64537"; -static const char *ANONYMOUS_USERNAME ATTR_UNUSED = "justinfan64537"; - -static constexpr int TWITCH_MESSAGE_LIMIT = 500; +inline constexpr int TWITCH_MESSAGE_LIMIT = 500; inline QByteArray getDefaultClientID() { - return QByteArray("7ue61iz46fz11y3cugd0l3tawb4taal"); + return QByteArrayLiteral("7ue61iz46fz11y3cugd0l3tawb4taal"); } -static const std::vector 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 -}; +extern const std::vector TWITCH_USERNAME_COLORS; -static 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", -}; +extern const QStringList TWITCH_DEFAULT_COMMANDS; -static const QStringList TWITCH_WHISPER_COMMANDS{"/w", ".w"}; +extern const QStringList TWITCH_WHISPER_COMMANDS; } // namespace chatterino diff --git a/src/providers/twitch/TwitchEmotes.hpp b/src/providers/twitch/TwitchEmotes.hpp index 06fbbe950..eac4077d6 100644 --- a/src/providers/twitch/TwitchEmotes.hpp +++ b/src/providers/twitch/TwitchEmotes.hpp @@ -18,7 +18,7 @@ namespace chatterino { // variant /// %1 <-> {id} /// %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"; struct Emote; @@ -63,8 +63,8 @@ using TwitchEmoteSetMap = boost::unordered_flat_map; struct HelixChannelEmote; -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_SUB_EMOTE_SET_PREFIX = u"x-c2-s-"; +inline constexpr QStringView TWITCH_BIT_EMOTE_SET_PREFIX = u"x-c2-b-"; struct TwitchEmoteSetMeta { QString setID; diff --git a/src/widgets/dialogs/ChannelFilterEditorDialog.cpp b/src/widgets/dialogs/ChannelFilterEditorDialog.cpp index 85a778546..5d7d89afc 100644 --- a/src/widgets/dialogs/ChannelFilterEditorDialog.cpp +++ b/src/widgets/dialogs/ChannelFilterEditorDialog.cpp @@ -100,7 +100,7 @@ ChannelFilterEditorDialog::ValueSpecifier::ValueSpecifier() this->typeCombo_->insertItems( 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->varCombo_, 1); @@ -142,7 +142,7 @@ void ChannelFilterEditorDialog::ValueSpecifier::setValue(const QString &value) if (this->typeCombo_->currentIndex() == 2) { this->varCombo_->setCurrentText( - filters::validIdentifiersMap.value(value)); + filters::VALID_IDENTIFIERS_MAP.value(value)); } else { @@ -165,7 +165,7 @@ QString ChannelFilterEditorDialog::ValueSpecifier::expressionText() case 1: // number return this->valueInput_->text(); case 2: // variable - return filters::validIdentifiersMap.key( + return filters::VALID_IDENTIFIERS_MAP.key( this->varCombo_->currentText()); default: return ""; diff --git a/src/widgets/helper/NotebookTab.hpp b/src/widgets/helper/NotebookTab.hpp index d7067077e..6ae7802d0 100644 --- a/src/widgets/helper/NotebookTab.hpp +++ b/src/widgets/helper/NotebookTab.hpp @@ -11,7 +11,7 @@ namespace chatterino { -constexpr int NOTEBOOK_TAB_HEIGHT = 28; +inline constexpr int NOTEBOOK_TAB_HEIGHT = 28; class SplitContainer; diff --git a/tests/src/Filters.cpp b/tests/src/Filters.cpp index af2304546..6456d0594 100644 --- a/tests/src/Filters.cpp +++ b/tests/src/Filters.cpp @@ -24,8 +24,6 @@ using namespace chatterino; using namespace chatterino::filters; using chatterino::mock::MockChannel; -TypingContext typingContext = MESSAGE_TYPING_CONTEXT; - namespace { class MockApplication : public mock::BaseApplication @@ -188,7 +186,8 @@ TEST(Filters, TypeSynthesis) T type = filter.returnType(); EXPECT_EQ(type, expected) << "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) << "Filter{ " << input << " } evaluated to " << result.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 << ") did not build a proper filter"; - const auto actualDebugString = filter->debugString(typingContext); + const auto actualDebugString = + filter->debugString(MESSAGE_TYPING_CONTEXT); EXPECT_EQ(actualDebugString, debugString) << "filter->debugString() on '" << input << "' should be '" << debugString << "', but got '" << actualDebugString << "'";