Skip to content

Commit ce7f828

Browse files
committed
Merge remote-tracking branch 'upstream/master' into chatterino7
2 parents 3a6f210 + 9d8453b commit ce7f828

25 files changed

+1043
-47
lines changed

.github/workflows/clang-tidy.yml

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -41,7 +41,7 @@ jobs:
4141

4242
- name: clang-tidy review
4343
timeout-minutes: 20
44-
uses: ZedThree/clang-tidy-review@v0.23.0
44+
uses: ZedThree/clang-tidy-review@v0.23.1
4545
with:
4646
build_dir: build-clang-tidy
4747
config_file: ".clang-tidy"
@@ -68,4 +68,4 @@ jobs:
6868
curl, unzip
6969
7070
- name: clang-tidy-review upload
71-
uses: ZedThree/clang-tidy-review/upload@v0.23.0
71+
uses: ZedThree/clang-tidy-review/upload@v0.23.1

.github/workflows/post-clang-tidy-review.yml

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -16,7 +16,7 @@ jobs:
1616
pull-requests: write
1717

1818
steps:
19-
- uses: ZedThree/clang-tidy-review/post@v0.23.0
19+
- uses: ZedThree/clang-tidy-review/post@v0.23.1
2020
with:
2121
lgtm_comment_body: ""
2222
num_comments_as_exitcode: false

CHANGELOG.md

Lines changed: 4 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -22,6 +22,7 @@
2222
- Minor: Added broadcaster-only `/poll`, `/cancelpoll`, and `/endpoll` commands. (#6583, #6605)
2323
- Minor: Added broadcaster-only `/prediction`, `/cancelprediction`, `/lockprediction`, and `/completeprediction` commands. (#6583, #6612, #6632, #6749)
2424
- Minor: Added support for BetterTTV Pro subscriber badges. (#6625, #6724)
25+
- Minor: Added backup restore dialog if settings fail to load. (#6662)
2526
- Minor: Added `debug.traceback` for plugins. (#6652)
2627
- Minor: Added title and duration options for `/clip` command. (#6669)
2728
- Minor: Added the ability to filter on messages by the author's external badges (example: `author.external_badges contains "chatterino:Top Donator"` or `author.external_badges contains "frankerfacez:bot"`). (#6709)
@@ -33,6 +34,7 @@
3334
- Minor: Added action to reset `/watching`. (#6759)
3435
- Minor: Removed messaging about running flatpak. This is already apparent in the newer Flatpak runtimes. (#6768)
3536
- Minor: Add `/(un)monitor` and `/(un)restrict` commands for moderators. (#6750, #6785)
37+
- Minor: The follower mode duration in the split header now shows hours or days if possible. (#6812)
3638
- Bugfix: Fixed context menu hotkeys not working on macOS. (#6778)
3739
- Bugfix: Moderation checks now include the lead moderator badge. (#6642)
3840
- Bugfix: Fixed lead moderator badges not being filtered by the `Channel` badge setting. (#6665)
@@ -93,7 +95,7 @@
9395
- Dev: Unwrapped `LimitedQueueSnapshot` to `std::vector`. (#6606)
9496
- Dev: Simplified uses of `getMessageSnapshot`. (#6607)
9597
- Dev: Disabled `llvm-prefer-static-over-anonymous-namespace` in clang-tidy. (#6610)
96-
- Dev: Added experimental spell checker support. (#6446, #6703, #6722, #6730, #6731, #6779, #6780)
98+
- Dev: Added experimental spell checker support. (#6446, #6703, #6722, #6730, #6731, #6779, #6780, #6822, #6751)
9799
- Dev: Added Clazy linting in CI. (#6623)
98100
- Dev: Added custom clang-tidy module linting in CI. (#6626)
99101
- Dev: CMake option `USE_ALTERNATE_LINKER` now errors if the given linker can't be found. (#6692)
@@ -118,6 +120,7 @@
118120
- Dev: Balance IPv4 and IPv6 connection attempts. (#6804)
119121
- Dev: Factored out recent messages benchmark helper. (#6815)
120122
- Dev: Added `CHATTERINO_FORCE_LTO` CMake option to skip LTO check. (#6816)
123+
- Dev: Added more tests and benchmarks for filters. (#6814)
121124

122125
## 2.5.4
123126

benchmarks/CMakeLists.txt

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -11,6 +11,7 @@ set(benchmark_SOURCES
1111
src/LinkParser.cpp
1212
src/RecentMessages.cpp
1313
src/MessageBuilding.cpp
14+
src/Filters.cpp
1415
# Add your new file above this line!
1516
)
1617

benchmarks/src/Filters.cpp

Lines changed: 131 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,131 @@
1+
// SPDX-FileCopyrightText: 2026 Contributors to Chatterino <https://chatterino.com>
2+
//
3+
// SPDX-License-Identifier: MIT
4+
5+
#include "common/Literals.hpp"
6+
#include "controllers/filters/FilterSet.hpp"
7+
#include "MessageBuilding.hpp"
8+
#include "providers/recentmessages/Impl.hpp"
9+
10+
#include <benchmark/benchmark.h>
11+
#include <QFile>
12+
#include <QJsonArray>
13+
#include <QJsonDocument>
14+
#include <QString>
15+
16+
using namespace chatterino;
17+
using namespace Qt::Literals;
18+
19+
namespace {
20+
21+
class FilterMessages : public bench::MessageBenchmark
22+
{
23+
public:
24+
explicit FilterMessages(QString name, QStringList filters)
25+
: bench::MessageBenchmark(std::move(name))
26+
, filterTexts(std::move(filters))
27+
{
28+
}
29+
30+
void run(benchmark::State &state) override
31+
{
32+
auto parsed = recentmessages::detail::parseRecentMessages(
33+
this->messages.object());
34+
auto built = recentmessages::detail::buildRecentMessages(
35+
parsed, this->chan.get());
36+
37+
QList<QUuid> filters;
38+
for (qsizetype i = 0; i < this->filterTexts.size(); i++)
39+
{
40+
// ensure deterministic order
41+
auto id = QUuid(static_cast<uint>(i), 0, 0, 0, 0, 0, 0, 0, 0, 0, 0);
42+
auto filter = std::make_shared<FilterRecord>(
43+
QString::number(i), this->filterTexts.at(i), id);
44+
if (!filter->valid())
45+
{
46+
qCDebug(chatterinoApp) << i << this->filterTexts[i];
47+
assert(false);
48+
continue;
49+
}
50+
getSettings()->filterRecords.append(filter);
51+
filters.append(id);
52+
}
53+
assert(filters.size() == this->filterTexts.size());
54+
FilterSet set(filters);
55+
assert(set.filterIds().size() == filters.size());
56+
57+
for (auto _ : state)
58+
{
59+
for (const auto &msg : built)
60+
{
61+
bool filtered = set.filter(msg, this->chan);
62+
benchmark::DoNotOptimize(filtered);
63+
benchmark::ClobberMemory();
64+
}
65+
}
66+
}
67+
68+
private:
69+
QStringList filterTexts;
70+
};
71+
72+
void BM_FilterMessages(benchmark::State &state, QString channel,
73+
QStringList filters)
74+
{
75+
FilterMessages bench(std::move(channel), std::move(filters));
76+
bench.run(state);
77+
}
78+
79+
} // namespace
80+
81+
BENCHMARK_CAPTURE(
82+
BM_FilterMessages, nymn_modmessages, u"nymn"_s,
83+
{
84+
uR".(channel.name == "nymn" && author.badges contains "moderator")."_s,
85+
});
86+
87+
BENCHMARK_CAPTURE(
88+
BM_FilterMessages, nymn_mod_party, u"nymn"_s,
89+
{
90+
uR".((author.badges contains "moderator") && (message.content contains "forsenParty"))."_s,
91+
});
92+
93+
BENCHMARK_CAPTURE(BM_FilterMessages, nymn_len40_or_sub, u"nymn"_s,
94+
{
95+
uR".(message.length < 40 || author.subbed)."_s,
96+
});
97+
98+
BENCHMARK_CAPTURE(BM_FilterMessages, nymn_no_sub, u"nymn"_s,
99+
{
100+
uR".(!flags.sub_message)."_s,
101+
});
102+
103+
BENCHMARK_CAPTURE(BM_FilterMessages, nymn_with_color, u"nymn"_s,
104+
{
105+
uR".(!author.no_color)."_s,
106+
});
107+
108+
BENCHMARK_CAPTURE(
109+
BM_FilterMessages, nymn_complex_regex, u"nymn"_s,
110+
{
111+
uR".((message.content match r"^(?!.*(?:my|complex|(re.*x))).*$"))."_s,
112+
});
113+
114+
BENCHMARK_CAPTURE(
115+
BM_FilterMessages, nymn_big_or, u"nymn"_s,
116+
{
117+
uR".((author.subbed && author.sub_length >= 6) || flags.system_message || flags.first_message || flags.automod || flags.sub_message)."_s,
118+
});
119+
120+
BENCHMARK_CAPTURE(
121+
BM_FilterMessages, nymn_with_color_and_edm_single, u"nymn"_s,
122+
{
123+
uR".(!author.no_color && message.content contains "EDM")."_s,
124+
});
125+
126+
BENCHMARK_CAPTURE(BM_FilterMessages, nymn_with_color_and_edm_separate,
127+
u"nymn"_s,
128+
{
129+
uR".(!author.no_color)."_s,
130+
uR".(message.content contains "EDM")."_s,
131+
});

benchmarks/src/MessageBuilding.cpp

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -48,6 +48,7 @@ namespace chatterino::bench {
4848
MockMessageApplication::MockMessageApplication()
4949
: highlights(this->settings, &this->accounts)
5050
{
51+
this->settings.disableSave();
5152
}
5253

5354
MessageBenchmark::MessageBenchmark(QString name)

mocks/include/mocks/BaseApplication.hpp

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -20,7 +20,7 @@ class BaseApplication : public EmptyApplication
2020
{
2121
public:
2222
BaseApplication()
23-
: settings(this->args, this->settingsDir.path())
23+
: settings(this->args, this->settingsDir.path(), /*isTest=*/true)
2424
, updates(this->paths_, this->settings)
2525
, theme(this->paths_)
2626
, fonts(this->settings)
@@ -29,7 +29,7 @@ class BaseApplication : public EmptyApplication
2929

3030
explicit BaseApplication(const QString &settingsData)
3131
: EmptyApplication(settingsData)
32-
, settings(this->args, this->settingsDir.path())
32+
, settings(this->args, this->settingsDir.path(), /*isTest=*/true)
3333
, updates(this->paths_, this->settings)
3434
, theme(this->paths_)
3535
, fonts(this->settings)

src/CMakeLists.txt

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -581,6 +581,8 @@ set(SOURCE_FILES
581581
util/AbandonObject.hpp
582582
util/AttachToConsole.cpp
583583
util/AttachToConsole.hpp
584+
util/Backup.cpp
585+
util/Backup.hpp
584586
util/BadgeRegistry.cpp
585587
util/BadgeRegistry.hpp
586588
util/BoostJsonWrap.cpp
@@ -753,6 +755,8 @@ set(SOURCE_FILES
753755
widgets/dialogs/QualityPopup.hpp
754756
widgets/dialogs/ReplyThreadPopup.cpp
755757
widgets/dialogs/ReplyThreadPopup.hpp
758+
widgets/dialogs/RestoreBackupsDialog.cpp
759+
widgets/dialogs/RestoreBackupsDialog.hpp
756760
widgets/dialogs/SelectChannelDialog.cpp
757761
widgets/dialogs/SelectChannelDialog.hpp
758762
widgets/dialogs/SelectChannelFiltersDialog.cpp

src/singletons/Settings.cpp

Lines changed: 41 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -15,13 +15,15 @@
1515
#include "controllers/nicknames/Nickname.hpp"
1616
#include "debug/Benchmark.hpp"
1717
#include "pajlada/settings/signalargs.hpp"
18+
#include "util/Backup.hpp"
1819
#include "util/WindowsHelper.hpp"
1920

2021
#include <pajlada/signals/scoped-connection.hpp>
2122

2223
namespace {
2324

2425
using namespace chatterino;
26+
using namespace Qt::Literals;
2527

2628
template <typename T>
2729
void initializeSignalVector(pajlada::Signals::SignalHolder &signalHolder,
@@ -148,7 +150,8 @@ bool Settings::toggleMutedChannel(const QString &channelName)
148150

149151
Settings *Settings::instance_ = nullptr;
150152

151-
Settings::Settings(const Args &args, const QString &settingsDirectory)
153+
Settings::Settings(const Args &args, const QString &settingsDirectory,
154+
bool isTest)
152155
: prevInstance_(Settings::instance_)
153156
, disableSaving(args.dontSaveSettings)
154157
{
@@ -157,7 +160,43 @@ Settings::Settings(const Args &args, const QString &settingsDirectory)
157160
// get global instance of the settings library
158161
auto settingsInstance = pajlada::Settings::SettingManager::getInstance();
159162

160-
settingsInstance->load(qPrintable(settingsPath));
163+
if (isTest)
164+
{
165+
settingsInstance->load(qPrintable(settingsPath));
166+
}
167+
else
168+
{
169+
backup::loadWithBackups(
170+
backup::FileData{
171+
.fileName = u"settings.json"_s,
172+
.directory = settingsDirectory,
173+
.fileKind = u"Settings"_s,
174+
.fileDescription =
175+
u"This file contains the main application settings such as accounts and hotkeys."_s,
176+
},
177+
[&]() -> ExpectedStr<void> {
178+
using LoadError = pajlada::Settings::SettingManager::LoadError;
179+
auto err = settingsInstance->load(qPrintable(settingsPath));
180+
switch (err)
181+
{
182+
case LoadError::NoError:
183+
return {}; // ok
184+
case LoadError::CannotOpenFile:
185+
return makeUnexpected(u"Failed to open '" %
186+
settingsPath % '\'');
187+
case LoadError::FileHandleError:
188+
return makeUnexpected("File handle error");
189+
case LoadError::FileReadError:
190+
return makeUnexpected("Failed to read file");
191+
case LoadError::FileSeekError:
192+
return makeUnexpected("Failed to seek in file");
193+
case LoadError::JSONParseError:
194+
return makeUnexpected("File contained malformed JSON");
195+
}
196+
assert(false);
197+
return makeUnexpected("Unknown error");
198+
});
199+
}
161200

162201
settingsInstance->setBackupEnabled(true);
163202
settingsInstance->setBackupSlots(9);

src/singletons/Settings.hpp

Lines changed: 6 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -127,7 +127,8 @@ class Settings
127127
bool disableSaving;
128128

129129
public:
130-
Settings(const Args &args, const QString &settingsDirectory);
130+
Settings(const Args &args, const QString &settingsDirectory,
131+
bool isTest = false);
131132
~Settings();
132133

133134
static Settings &instance();
@@ -394,6 +395,10 @@ class Settings
394395
"/behaviour/spellChecking/defaultDictionary",
395396
"",
396397
};
398+
IntSetting nSpellCheckingSuggestions = {
399+
"/behaviour/spellChecking/suggestions/count",
400+
-1,
401+
};
397402

398403
FloatSetting pauseOnHoverDuration = {"/behaviour/pauseOnHoverDuration", 0};
399404
EnumSetting<Qt::KeyboardModifier> pauseChatModifier = {

0 commit comments

Comments
 (0)