From 89483e4c05a7d35459066f9b2e5dbb6c3d20525b Mon Sep 17 00:00:00 2001 From: pajlada Date: Sat, 28 Jun 2025 21:16:44 +0200 Subject: [PATCH] fix: don't autocomplete commands mid-sentence (#6273) --- CHANGELOG.md | 1 + .../completion/TabCompletionModel.cpp | 43 ++++++++++++++----- .../completion/TabCompletionModel.hpp | 9 +++- 3 files changed, 41 insertions(+), 12 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 7cbb6332..43393a04 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -17,6 +17,7 @@ - Minor: Made nicknames searchable in the Settings dialog search bar. (#5886) - Minor: Added hotkey Action for opening account selector. (#6192) - Minor: Add a setting to change the emote and badge thumbnail size. (#6126) +- Bugfix: Commands are no longer tab-completable in the middle of messages. (#6273) - 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: Fixed scrolling now working on inputs in the settings. (#6128) diff --git a/src/controllers/completion/TabCompletionModel.cpp b/src/controllers/completion/TabCompletionModel.cpp index 829fe8e8..375c89e7 100644 --- a/src/controllers/completion/TabCompletionModel.cpp +++ b/src/controllers/completion/TabCompletionModel.cpp @@ -27,7 +27,7 @@ void TabCompletionModel::updateResults(const QString &query, const QString &fullTextContent, int cursorPosition, bool isFirstWord) { - this->updateSourceFromQuery(query); + this->updateSourceFromQuery(query, isFirstWord); if (this->source_) { @@ -56,9 +56,10 @@ void TabCompletionModel::updateResults(const QString &query, } } -void TabCompletionModel::updateSourceFromQuery(const QString &query) +void TabCompletionModel::updateSourceFromQuery(const QString &query, + bool isFirstWord) { - auto deducedKind = this->deduceSourceKind(query); + auto deducedKind = this->deduceSourceKind(query, isFirstWord); if (!deducedKind) { // unable to determine what kind of completion is occurring @@ -71,7 +72,8 @@ void TabCompletionModel::updateSourceFromQuery(const QString &query) } std::optional - TabCompletionModel::deduceSourceKind(const QString &query) const + TabCompletionModel::deduceSourceKind(const QString &query, + bool isFirstWord) const { if (query.length() < 2 || !this->channel_.isTwitchChannel()) { @@ -88,7 +90,7 @@ std::optional { return SourceKind::Emote; } - else if (query.startsWith('/') || query.startsWith('.')) + else if (isFirstWord && (query.startsWith('/') || query.startsWith('.'))) { return SourceKind::Command; } @@ -97,14 +99,27 @@ std::optional // Therefore, we must also consider that the user could be completing an emote // OR a mention depending on their completion settings. - if (getSettings()->userCompletionOnlyWithAt) + if (isFirstWord) { - // All kinds but user are possible - return SourceKind::EmoteCommand; + if (getSettings()->userCompletionOnlyWithAt) + { + // All kinds but user are possible + return SourceKind::EmoteCommand; + } + + // Any kind is possible + return SourceKind::EmoteUserCommand; } - // Any kind is possible - return SourceKind::EmoteUserCommand; + // We don't allow for mid-message command completions, + // which means only emote or user tab completions are possible. + + if (getSettings()->userCompletionOnlyWithAt) + { + return SourceKind::Emote; + } + + return SourceKind::EmoteUser; } std::unique_ptr TabCompletionModel::buildSource( @@ -121,6 +136,14 @@ std::unique_ptr TabCompletionModel::buildSource( case SourceKind::Command: { return this->buildCommandSource(); } + case SourceKind::EmoteUser: { + std::vector> sources; + sources.push_back(this->buildEmoteSource()); + sources.push_back(this->buildUserSource(false)); + + return std::make_unique( + std::move(sources)); + } case SourceKind::EmoteCommand: { std::vector> sources; sources.push_back(this->buildEmoteSource()); diff --git a/src/controllers/completion/TabCompletionModel.hpp b/src/controllers/completion/TabCompletionModel.hpp index 56cf313e..b3b6bd80 100644 --- a/src/controllers/completion/TabCompletionModel.hpp +++ b/src/controllers/completion/TabCompletionModel.hpp @@ -41,6 +41,8 @@ private: User, // Known to be a command, i.e. started with / or . Command, + // Emote or user without : or @ . + EmoteUser, // Emote or command without : or / . EmoteCommand, // Emote, user, or command without :, @, / . @@ -51,14 +53,17 @@ private: /// The completion source will only change if the deduced completion kind /// changes (see deduceSourceKind). /// @param query Completion query - void updateSourceFromQuery(const QString &query); + /// @param isFirstWord Whether the completion is the first word in the input + void updateSourceFromQuery(const QString &query, bool isFirstWord); /// @brief Attempts to deduce the source kind from the current query. If the /// bound Channel is not a TwitchChannel or if the query is too short, no /// query type will be deduced to prevent completions. /// @param query Completion query + /// @param isFirstWord Whether the completion is the first word in the input /// @return An optional SourceKind deduced from the query - std::optional deduceSourceKind(const QString &query) const; + std::optional deduceSourceKind(const QString &query, + bool isFirstWord) const; std::unique_ptr buildSource(SourceKind kind) const;