fix: don't autocomplete commands mid-sentence (#6273)

This commit is contained in:
pajlada
2025-06-28 21:16:44 +02:00
committed by GitHub
parent b6623cff88
commit 89483e4c05
3 changed files with 41 additions and 12 deletions
+1
View File
@@ -17,6 +17,7 @@
- Minor: Made nicknames searchable in the Settings dialog search bar. (#5886) - Minor: Made nicknames searchable in the Settings dialog search bar. (#5886)
- Minor: Added hotkey Action for opening account selector. (#6192) - Minor: Added hotkey Action for opening account selector. (#6192)
- Minor: Add a setting to change the emote and badge thumbnail size. (#6126) - 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: 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: 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) - Bugfix: Fixed scrolling now working on inputs in the settings. (#6128)
@@ -27,7 +27,7 @@ void TabCompletionModel::updateResults(const QString &query,
const QString &fullTextContent, const QString &fullTextContent,
int cursorPosition, bool isFirstWord) int cursorPosition, bool isFirstWord)
{ {
this->updateSourceFromQuery(query); this->updateSourceFromQuery(query, isFirstWord);
if (this->source_) 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) if (!deducedKind)
{ {
// unable to determine what kind of completion is occurring // unable to determine what kind of completion is occurring
@@ -71,7 +72,8 @@ void TabCompletionModel::updateSourceFromQuery(const QString &query)
} }
std::optional<TabCompletionModel::SourceKind> std::optional<TabCompletionModel::SourceKind>
TabCompletionModel::deduceSourceKind(const QString &query) const TabCompletionModel::deduceSourceKind(const QString &query,
bool isFirstWord) const
{ {
if (query.length() < 2 || !this->channel_.isTwitchChannel()) if (query.length() < 2 || !this->channel_.isTwitchChannel())
{ {
@@ -88,7 +90,7 @@ std::optional<TabCompletionModel::SourceKind>
{ {
return SourceKind::Emote; return SourceKind::Emote;
} }
else if (query.startsWith('/') || query.startsWith('.')) else if (isFirstWord && (query.startsWith('/') || query.startsWith('.')))
{ {
return SourceKind::Command; return SourceKind::Command;
} }
@@ -97,14 +99,27 @@ std::optional<TabCompletionModel::SourceKind>
// Therefore, we must also consider that the user could be completing an emote // Therefore, we must also consider that the user could be completing an emote
// OR a mention depending on their completion settings. // OR a mention depending on their completion settings.
if (getSettings()->userCompletionOnlyWithAt) if (isFirstWord)
{ {
// All kinds but user are possible if (getSettings()->userCompletionOnlyWithAt)
return SourceKind::EmoteCommand; {
// All kinds but user are possible
return SourceKind::EmoteCommand;
}
// Any kind is possible
return SourceKind::EmoteUserCommand;
} }
// Any kind is possible // We don't allow for mid-message command completions,
return SourceKind::EmoteUserCommand; // which means only emote or user tab completions are possible.
if (getSettings()->userCompletionOnlyWithAt)
{
return SourceKind::Emote;
}
return SourceKind::EmoteUser;
} }
std::unique_ptr<completion::Source> TabCompletionModel::buildSource( std::unique_ptr<completion::Source> TabCompletionModel::buildSource(
@@ -121,6 +136,14 @@ std::unique_ptr<completion::Source> TabCompletionModel::buildSource(
case SourceKind::Command: { case SourceKind::Command: {
return this->buildCommandSource(); return this->buildCommandSource();
} }
case SourceKind::EmoteUser: {
std::vector<std::unique_ptr<completion::Source>> sources;
sources.push_back(this->buildEmoteSource());
sources.push_back(this->buildUserSource(false));
return std::make_unique<completion::UnifiedSource>(
std::move(sources));
}
case SourceKind::EmoteCommand: { case SourceKind::EmoteCommand: {
std::vector<std::unique_ptr<completion::Source>> sources; std::vector<std::unique_ptr<completion::Source>> sources;
sources.push_back(this->buildEmoteSource()); sources.push_back(this->buildEmoteSource());
@@ -41,6 +41,8 @@ private:
User, User,
// Known to be a command, i.e. started with / or . // Known to be a command, i.e. started with / or .
Command, Command,
// Emote or user without : or @ .
EmoteUser,
// Emote or command without : or / . // Emote or command without : or / .
EmoteCommand, EmoteCommand,
// Emote, user, or command without :, @, / . // Emote, user, or command without :, @, / .
@@ -51,14 +53,17 @@ private:
/// The completion source will only change if the deduced completion kind /// The completion source will only change if the deduced completion kind
/// changes (see deduceSourceKind). /// changes (see deduceSourceKind).
/// @param query Completion query /// @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 /// @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 /// bound Channel is not a TwitchChannel or if the query is too short, no
/// query type will be deduced to prevent completions. /// query type will be deduced to prevent completions.
/// @param query Completion query /// @param query Completion query
/// @param isFirstWord Whether the completion is the first word in the input
/// @return An optional SourceKind deduced from the query /// @return An optional SourceKind deduced from the query
std::optional<SourceKind> deduceSourceKind(const QString &query) const; std::optional<SourceKind> deduceSourceKind(const QString &query,
bool isFirstWord) const;
std::unique_ptr<completion::Source> buildSource(SourceKind kind) const; std::unique_ptr<completion::Source> buildSource(SourceKind kind) const;