From a8ac28425f841171fa4c50fc6bd823c88a1a1b06 Mon Sep 17 00:00:00 2001 From: Arne <78976058+4rneee@users.noreply.github.com> Date: Sun, 11 Jan 2026 13:00:40 +0100 Subject: [PATCH] feat(spellchecking): ignore commands for spellchecking (#6731) --- CHANGELOG.md | 2 +- .../commands/CommandController.cpp | 32 +++++++++++++++++++ .../commands/CommandController.hpp | 7 ++++ src/widgets/splits/InputHighlighter.cpp | 12 +++++-- 4 files changed, 50 insertions(+), 3 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index bdca4601..79fccb7e 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -78,7 +78,7 @@ - Dev: Unwrapped `LimitedQueueSnapshot` to `std::vector`. (#6606) - Dev: Simplified uses of `getMessageSnapshot`. (#6607) - Dev: Disabled `llvm-prefer-static-over-anonymous-namespace` in clang-tidy. (#6610) -- Dev: Added experimental spell checker support. (#6446, #6703, #6722, #6730) +- Dev: Added experimental spell checker support. (#6446, #6703, #6722, #6730, #6731) - Dev: Added Clazy linting in CI. (#6623) - Dev: Added custom clang-tidy module linting in CI. (#6626) - Dev: CMake option `USE_ALTERNATE_LINKER` now errors if the given linker can't be found. (#6692) diff --git a/src/controllers/commands/CommandController.cpp b/src/controllers/commands/CommandController.cpp index 886bc272..4be1fd20 100644 --- a/src/controllers/commands/CommandController.cpp +++ b/src/controllers/commands/CommandController.cpp @@ -733,4 +733,36 @@ QStringList CommandController::getDefaultChatterinoCommandList() return this->defaultChatterinoCommandAutoCompletions_; } +qsizetype CommandController::commandTriggerLen(QStringView text) +{ + auto words = text.split(' '); + + qsizetype triggerLen = 0; + qsizetype spaces = 0; + QString commandName{}; + + for (qsizetype i = 0; i < words.length() && spaces <= this->maxSpaces_; ++i) + { + commandName += words[i]; + triggerLen += words[i].length(); + + if (this->commands_.contains(commandName) || + this->userCommands_.contains(commandName)) + { + return triggerLen; + } + + // ignore consecutive spaces + if (!words[i].isEmpty()) + { + commandName += ' '; + ++spaces; + } + // account for the space between the current and next word + ++triggerLen; + } + + return 0; +} + } // namespace chatterino diff --git a/src/controllers/commands/CommandController.hpp b/src/controllers/commands/CommandController.hpp index 0288016c..29876e13 100644 --- a/src/controllers/commands/CommandController.hpp +++ b/src/controllers/commands/CommandController.hpp @@ -55,6 +55,13 @@ public: } #endif + /// Returns the length of the command trigger in `text`, including leading + /// spaces. If `text` does not start with a command trigger, 0 is returned. + /// Examples: + /// - " /ban forsen" returns 5 + /// - "/non-existing-command" returns 0 + qsizetype commandTriggerLen(QStringView text); + private: void load(Paths &paths); diff --git a/src/widgets/splits/InputHighlighter.cpp b/src/widgets/splits/InputHighlighter.cpp index 73bb894d..5b35b4e1 100644 --- a/src/widgets/splits/InputHighlighter.cpp +++ b/src/widgets/splits/InputHighlighter.cpp @@ -8,6 +8,7 @@ #include "common/Aliases.hpp" #include "common/LinkParser.hpp" #include "controllers/accounts/AccountController.hpp" +#include "controllers/commands/CommandController.hpp" #include "controllers/spellcheck/SpellChecker.hpp" #include "messages/Emote.hpp" #include "providers/bttv/BttvEmotes.hpp" @@ -96,19 +97,26 @@ void InputHighlighter::highlightBlock(const QString &text) auto *channel = this->channel.lock().get(); QStringView textView = text; + + // skip leading command trigger + auto cmdTriggerLen = getApp()->getCommands()->commandTriggerLen(textView); + textView = textView.sliced(cmdTriggerLen); + #if QT_VERSION >= QT_VERSION_CHECK(6, 5, 0) auto it = this->wordRegex.globalMatchView(textView); #else auto it = this->wordRegex.globalMatch(textView); #endif + while (it.hasNext()) { auto match = it.next(); auto text = match.captured(); if (!isIgnoredWord(channel, text) && !this->spellChecker.check(text)) { - this->setFormat(static_cast(match.capturedStart()), - static_cast(text.size()), this->spellFmt); + this->setFormat( + static_cast(match.capturedStart() + cmdTriggerLen), + static_cast(text.size()), this->spellFmt); } } }