diff --git a/src/util/completionmodel.cpp b/src/util/completionmodel.cpp index e94cd3a44..a13b917f1 100644 --- a/src/util/completionmodel.cpp +++ b/src/util/completionmodel.cpp @@ -7,6 +7,8 @@ #include +#include + namespace chatterino { CompletionModel::CompletionModel(const QString &_channelName) @@ -24,40 +26,41 @@ void CompletionModel::refresh() // TODO: Fix this so it properly updates with the proper api. oauth token needs proper scope for (const auto &m : emoteManager.twitchAccountEmotes) { for (const auto &emoteName : m.second.emoteCodes) { - this->addString(emoteName); + // XXX: No way to discern between a twitch global emote and sub emote right now + this->addString(emoteName, TaggedString::Type::TwitchGlobalEmote); } } // Global: BTTV Global Emotes std::vector &bttvGlobalEmoteCodes = emoteManager.bttvGlobalEmoteCodes; for (const auto &m : bttvGlobalEmoteCodes) { - this->addString(m); + this->addString(m, TaggedString::Type::BTTVGlobalEmote); } // Global: FFZ Global Emotes std::vector &ffzGlobalEmoteCodes = emoteManager.ffzGlobalEmoteCodes; for (const auto &m : ffzGlobalEmoteCodes) { - this->addString(m); + this->addString(m, TaggedString::Type::FFZGlobalEmote); } // Channel-specific: BTTV Channel Emotes std::vector &bttvChannelEmoteCodes = emoteManager.bttvChannelEmoteCodes[this->channelName.toStdString()]; for (const auto &m : bttvChannelEmoteCodes) { - this->addString(m); + this->addString(m, TaggedString::Type::BTTVChannelEmote); } // Channel-specific: FFZ Channel Emotes std::vector &ffzChannelEmoteCodes = emoteManager.ffzChannelEmoteCodes[this->channelName.toStdString()]; for (const auto &m : ffzChannelEmoteCodes) { - this->addString(m); + this->addString(m, TaggedString::Type::FFZChannelEmote); } // Global: Emojis const auto &emojiShortCodes = emoteManager.emojiShortCodes; for (const auto &m : emojiShortCodes) { - this->addString(":" + m + ":"); + this->addString(":" + m + ":", TaggedString::Type::Emoji); } // Channel-specific: Usernames @@ -76,22 +79,33 @@ void CompletionModel::refresh() // } } -void CompletionModel::addString(const std::string &str) +void CompletionModel::addString(const std::string &str, TaggedString::Type type) { // Always add a space at the end of completions - this->emotes.insert(this->createEmote(str + " ")); + this->emotes.insert({qS(str + " "), type}); } -void CompletionModel::addString(const QString &str) +void CompletionModel::addString(const QString &str, TaggedString::Type type) { // Always add a space at the end of completions - this->emotes.insert(this->createEmote(str + " ")); + this->emotes.insert({str + " ", type}); } void CompletionModel::addUser(const QString &str) { + auto ts = this->createUser(str + " "); // Always add a space at the end of completions - this->emotes.insert(this->createUser(str + " ")); + std::pair::iterator, bool> p = this->emotes.insert(ts); + if (!p.second) { + // No inseration was made, figure out if we need to replace the username. + + if (p.first->str > ts.str) { + // Replace lowercase version of name with mixed-case version + this->emotes.erase(p.first); + auto result2 = this->emotes.insert(ts); + assert(result2.second); + } + } } } // namespace chatterino diff --git a/src/util/completionmodel.hpp b/src/util/completionmodel.hpp index 0c996a195..b804b5246 100644 --- a/src/util/completionmodel.hpp +++ b/src/util/completionmodel.hpp @@ -4,7 +4,6 @@ #include -#include #include #include @@ -12,6 +11,56 @@ namespace chatterino { class CompletionModel : public QAbstractListModel { + struct TaggedString { + QString str; + + // Type will help decide the lifetime of the tagged strings + enum Type { + Username, + + // Emotes + FFZGlobalEmote = 20, + FFZChannelEmote, + BTTVGlobalEmote, + BTTVChannelEmote, + TwitchGlobalEmote, + TwitchSubscriberEmote, + Emoji, + } type; + + bool IsEmote() const + { + return this->type >= 20; + } + + bool operator<(const TaggedString &that) const + { + if (this->IsEmote()) { + if (that.IsEmote()) { + int k = QString::compare(this->str, that.str, Qt::CaseInsensitive); + if (k == 0) { + return this->str > that.str; + } else { + return k < 0; + } + } else { + return true; + } + } else { + if (that.IsEmote()) { + return false; + } else { + int k = QString::compare(this->str, that.str, Qt::CaseInsensitive); + if (k == 0) { + return false; + } else { + return k < 0; + } + } + } + } + }; + public: CompletionModel(const QString &_channelName); @@ -34,55 +83,17 @@ public: } void refresh(); - void addString(const std::string &str); - void addString(const QString &str); + void addString(const std::string &str, TaggedString::Type type); + void addString(const QString &str, TaggedString::Type type); void addUser(const QString &str); private: - struct TaggedString { - QString str; - // emote == true - // username == false - bool isEmote = true; - bool operator<(const TaggedString &that) const - { - if (this->isEmote) { - if (that.isEmote) { - int k = QString::compare(this->str, that.str, Qt::CaseInsensitive); - if (k == 0) { - return this->str > that.str; - } else { - return k < 0; - } - } else - return true; - } else { - if (that.isEmote) - return false; - else { - int k = QString::compare(this->str, that.str, Qt::CaseInsensitive); - if (k == 0) { - return this->str > that.str; - } else { - return k < 0; - } - } - } - } - }; - TaggedString createEmote(const std::string &str) - { - return TaggedString{qS(str), true}; - } - TaggedString createEmote(const QString &str) - { - return TaggedString{str, true}; - } TaggedString createUser(const QString &str) { - return TaggedString{str, false}; + return TaggedString{str, TaggedString::Type::Username}; } + std::set emotes; QString channelName;