mirror of
https://github.com/Chatterino/chatterino2.git
synced 2024-11-21 22:24:07 +01:00
feat: Allow negation of search predicates (#4207)
Co-authored-by: Rasmus Karlsson <rasmus.karlsson@pajlada.com> closes https://github.com/Chatterino/chatterino2/issues/3998
This commit is contained in:
parent
4fa214a38a
commit
b7888749fe
19 changed files with 139 additions and 111 deletions
|
@ -2,6 +2,7 @@
|
||||||
|
|
||||||
## Unversioned
|
## Unversioned
|
||||||
|
|
||||||
|
- Minor: Added ability to negate search options by prefixing it with an exclamation mark (e.g. `!badge:mod` to search for messages where the author does not have the moderator badge). (#4207)
|
||||||
- Minor: Search window input will automatically use currently selected text if present. (#4178)
|
- Minor: Search window input will automatically use currently selected text if present. (#4178)
|
||||||
- Minor: Cleared up highlight sound settings (#4194)
|
- Minor: Cleared up highlight sound settings (#4194)
|
||||||
- Bugfix: Fixed highlight sounds not reloading on change properly. (#4194)
|
- Bugfix: Fixed highlight sounds not reloading on change properly. (#4194)
|
||||||
|
|
|
@ -4,20 +4,18 @@
|
||||||
|
|
||||||
namespace chatterino {
|
namespace chatterino {
|
||||||
|
|
||||||
AuthorPredicate::AuthorPredicate(const QStringList &authors)
|
AuthorPredicate::AuthorPredicate(const QString &authors, bool negate)
|
||||||
: authors_()
|
: MessagePredicate(negate)
|
||||||
|
, authors_()
|
||||||
{
|
{
|
||||||
// Check if any comma-seperated values were passed and transform those
|
// Check if any comma-seperated values were passed and transform those
|
||||||
for (const auto &entry : authors)
|
for (const auto &author : authors.split(',', Qt::SkipEmptyParts))
|
||||||
{
|
{
|
||||||
for (const auto &author : entry.split(',', Qt::SkipEmptyParts))
|
this->authors_ << author;
|
||||||
{
|
|
||||||
this->authors_ << author;
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
bool AuthorPredicate::appliesTo(const Message &message)
|
bool AuthorPredicate::appliesToImpl(const Message &message)
|
||||||
{
|
{
|
||||||
return authors_.contains(message.displayName, Qt::CaseInsensitive) ||
|
return authors_.contains(message.displayName, Qt::CaseInsensitive) ||
|
||||||
authors_.contains(message.loginName, Qt::CaseInsensitive);
|
authors_.contains(message.loginName, Qt::CaseInsensitive);
|
||||||
|
|
|
@ -16,10 +16,12 @@ public:
|
||||||
/**
|
/**
|
||||||
* @brief Create an AuthorPredicate with a list of users to search for.
|
* @brief Create an AuthorPredicate with a list of users to search for.
|
||||||
*
|
*
|
||||||
* @param authors a list of user names that a message should be sent from
|
* @param authors one or more comma-separated user names that a message should be sent from
|
||||||
|
* @param negate when set, excludes list of user names from results
|
||||||
*/
|
*/
|
||||||
AuthorPredicate(const QStringList &authors);
|
AuthorPredicate(const QString &authors, bool negate);
|
||||||
|
|
||||||
|
protected:
|
||||||
/**
|
/**
|
||||||
* @brief Checks whether the message is authored by any of the users passed
|
* @brief Checks whether the message is authored by any of the users passed
|
||||||
* in the constructor.
|
* in the constructor.
|
||||||
|
@ -28,7 +30,7 @@ public:
|
||||||
* @return true if the message was authored by one of the specified users,
|
* @return true if the message was authored by one of the specified users,
|
||||||
* false otherwise
|
* false otherwise
|
||||||
*/
|
*/
|
||||||
bool appliesTo(const Message &message);
|
bool appliesToImpl(const Message &message) override;
|
||||||
|
|
||||||
private:
|
private:
|
||||||
/// Holds the user names that will be searched for
|
/// Holds the user names that will be searched for
|
||||||
|
|
|
@ -4,35 +4,33 @@
|
||||||
|
|
||||||
namespace chatterino {
|
namespace chatterino {
|
||||||
|
|
||||||
BadgePredicate::BadgePredicate(const QStringList &badges)
|
BadgePredicate::BadgePredicate(const QString &badges, bool negate)
|
||||||
|
: MessagePredicate(negate)
|
||||||
{
|
{
|
||||||
// Check if any comma-seperated values were passed and transform those
|
// Check if any comma-seperated values were passed and transform those
|
||||||
for (const auto &entry : badges)
|
for (const auto &badge : badges.split(',', Qt::SkipEmptyParts))
|
||||||
{
|
{
|
||||||
for (const auto &badge : entry.split(',', Qt::SkipEmptyParts))
|
// convert short form name of certain badges to formal name
|
||||||
|
if (badge.compare("mod", Qt::CaseInsensitive) == 0)
|
||||||
{
|
{
|
||||||
// convert short form name of certain badges to formal name
|
this->badges_ << "moderator";
|
||||||
if (badge.compare("mod", Qt::CaseInsensitive) == 0)
|
}
|
||||||
{
|
else if (badge.compare("sub", Qt::CaseInsensitive) == 0)
|
||||||
this->badges_ << "moderator";
|
{
|
||||||
}
|
this->badges_ << "subscriber";
|
||||||
else if (badge.compare("sub", Qt::CaseInsensitive) == 0)
|
}
|
||||||
{
|
else if (badge.compare("prime", Qt::CaseInsensitive) == 0)
|
||||||
this->badges_ << "subscriber";
|
{
|
||||||
}
|
this->badges_ << "premium";
|
||||||
else if (badge.compare("prime", Qt::CaseInsensitive) == 0)
|
}
|
||||||
{
|
else
|
||||||
this->badges_ << "premium";
|
{
|
||||||
}
|
this->badges_ << badge;
|
||||||
else
|
|
||||||
{
|
|
||||||
this->badges_ << badge;
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
bool BadgePredicate::appliesTo(const Message &message)
|
bool BadgePredicate::appliesToImpl(const Message &message)
|
||||||
{
|
{
|
||||||
for (const Badge &badge : message.badges)
|
for (const Badge &badge : message.badges)
|
||||||
{
|
{
|
||||||
|
|
|
@ -16,10 +16,12 @@ public:
|
||||||
/**
|
/**
|
||||||
* @brief Create an BadgePredicate with a list of badges to search for.
|
* @brief Create an BadgePredicate with a list of badges to search for.
|
||||||
*
|
*
|
||||||
* @param badges a list of badges that a message should contain
|
* @param badges one or more comma-separated badges that a message should contain
|
||||||
|
* @param negate when set, excludes list of badges from results
|
||||||
*/
|
*/
|
||||||
BadgePredicate(const QStringList &badges);
|
BadgePredicate(const QString &badges, bool negate);
|
||||||
|
|
||||||
|
protected:
|
||||||
/**
|
/**
|
||||||
* @brief Checks whether the message contains any of the badges passed
|
* @brief Checks whether the message contains any of the badges passed
|
||||||
* in the constructor.
|
* in the constructor.
|
||||||
|
@ -28,7 +30,7 @@ public:
|
||||||
* @return true if the message contains a badge listed in the specified badges,
|
* @return true if the message contains a badge listed in the specified badges,
|
||||||
* false otherwise
|
* false otherwise
|
||||||
*/
|
*/
|
||||||
bool appliesTo(const Message &message) override;
|
bool appliesToImpl(const Message &message) override;
|
||||||
|
|
||||||
private:
|
private:
|
||||||
/// Holds the badges that will be searched for
|
/// Holds the badges that will be searched for
|
||||||
|
|
|
@ -4,20 +4,18 @@
|
||||||
|
|
||||||
namespace chatterino {
|
namespace chatterino {
|
||||||
|
|
||||||
ChannelPredicate::ChannelPredicate(const QStringList &channels)
|
ChannelPredicate::ChannelPredicate(const QString &channels, bool negate)
|
||||||
: channels_()
|
: MessagePredicate(negate)
|
||||||
|
, channels_()
|
||||||
{
|
{
|
||||||
// Check if any comma-seperated values were passed and transform those
|
// Check if any comma-seperated values were passed and transform those
|
||||||
for (const auto &entry : channels)
|
for (const auto &channel : channels.split(',', Qt::SkipEmptyParts))
|
||||||
{
|
{
|
||||||
for (const auto &channel : entry.split(',', Qt::SkipEmptyParts))
|
this->channels_ << channel;
|
||||||
{
|
|
||||||
this->channels_ << channel;
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
bool ChannelPredicate::appliesTo(const Message &message)
|
bool ChannelPredicate::appliesToImpl(const Message &message)
|
||||||
{
|
{
|
||||||
return channels_.contains(message.channelName, Qt::CaseInsensitive);
|
return channels_.contains(message.channelName, Qt::CaseInsensitive);
|
||||||
}
|
}
|
||||||
|
|
|
@ -16,10 +16,12 @@ public:
|
||||||
/**
|
/**
|
||||||
* @brief Create a ChannelPredicate with a list of channels to search for.
|
* @brief Create a ChannelPredicate with a list of channels to search for.
|
||||||
*
|
*
|
||||||
* @param channels a list of channel names that a message should be sent in
|
* @param channels one or more comma-separated channel names that a message should be sent in
|
||||||
|
* @param negate when set, excludes list of channel names from results
|
||||||
*/
|
*/
|
||||||
ChannelPredicate(const QStringList &channels);
|
ChannelPredicate(const QString &channels, bool negate);
|
||||||
|
|
||||||
|
protected:
|
||||||
/**
|
/**
|
||||||
* @brief Checks whether the message was sent in any of the channels passed
|
* @brief Checks whether the message was sent in any of the channels passed
|
||||||
* in the constructor.
|
* in the constructor.
|
||||||
|
@ -28,7 +30,7 @@ public:
|
||||||
* @return true if the message was sent in one of the specified channels,
|
* @return true if the message was sent in one of the specified channels,
|
||||||
* false otherwise
|
* false otherwise
|
||||||
*/
|
*/
|
||||||
bool appliesTo(const Message &message);
|
bool appliesToImpl(const Message &message) override;
|
||||||
|
|
||||||
private:
|
private:
|
||||||
/// Holds the channel names that will be searched for
|
/// Holds the channel names that will be searched for
|
||||||
|
|
|
@ -5,11 +5,12 @@
|
||||||
|
|
||||||
namespace chatterino {
|
namespace chatterino {
|
||||||
|
|
||||||
LinkPredicate::LinkPredicate()
|
LinkPredicate::LinkPredicate(bool negate)
|
||||||
|
: MessagePredicate(negate)
|
||||||
{
|
{
|
||||||
}
|
}
|
||||||
|
|
||||||
bool LinkPredicate::appliesTo(const Message &message)
|
bool LinkPredicate::appliesToImpl(const Message &message)
|
||||||
{
|
{
|
||||||
for (const auto &word : message.messageText.split(' ', Qt::SkipEmptyParts))
|
for (const auto &word : message.messageText.split(' ', Qt::SkipEmptyParts))
|
||||||
{
|
{
|
||||||
|
|
|
@ -12,15 +12,21 @@ namespace chatterino {
|
||||||
class LinkPredicate : public MessagePredicate
|
class LinkPredicate : public MessagePredicate
|
||||||
{
|
{
|
||||||
public:
|
public:
|
||||||
LinkPredicate();
|
/**
|
||||||
|
* @brief Create an LinkPredicate
|
||||||
|
*
|
||||||
|
* @param negate when set, excludes messages containing links from results
|
||||||
|
*/
|
||||||
|
LinkPredicate(bool negate);
|
||||||
|
|
||||||
|
protected:
|
||||||
/**
|
/**
|
||||||
* @brief Checks whether the message contains a link.
|
* @brief Checks whether the message contains a link.
|
||||||
*
|
*
|
||||||
* @param message the message to check
|
* @param message the message to check
|
||||||
* @return true if the message contains a link, false otherwise
|
* @return true if the message contains a link, false otherwise
|
||||||
*/
|
*/
|
||||||
bool appliesTo(const Message &message);
|
bool appliesToImpl(const Message &message) override;
|
||||||
};
|
};
|
||||||
|
|
||||||
} // namespace chatterino
|
} // namespace chatterino
|
||||||
|
|
|
@ -4,8 +4,9 @@
|
||||||
|
|
||||||
namespace chatterino {
|
namespace chatterino {
|
||||||
|
|
||||||
MessageFlagsPredicate::MessageFlagsPredicate(const QString &flags)
|
MessageFlagsPredicate::MessageFlagsPredicate(const QString &flags, bool negate)
|
||||||
: flags_()
|
: MessagePredicate(negate)
|
||||||
|
, flags_()
|
||||||
{
|
{
|
||||||
// Check if any comma-seperated values were passed and transform those
|
// Check if any comma-seperated values were passed and transform those
|
||||||
for (const auto &flag : flags.split(',', Qt::SkipEmptyParts))
|
for (const auto &flag : flags.split(',', Qt::SkipEmptyParts))
|
||||||
|
@ -54,13 +55,15 @@ MessageFlagsPredicate::MessageFlagsPredicate(const QString &flags)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
bool MessageFlagsPredicate::appliesTo(const Message &message)
|
bool MessageFlagsPredicate::appliesToImpl(const Message &message)
|
||||||
{
|
{
|
||||||
// Exclude timeout messages from system flag when timeout flag isn't present
|
// Exclude timeout messages from system flag when timeout flag isn't present
|
||||||
if (this->flags_.has(MessageFlag::System) &&
|
if (this->flags_.has(MessageFlag::System) &&
|
||||||
!this->flags_.has(MessageFlag::Timeout))
|
!this->flags_.has(MessageFlag::Timeout))
|
||||||
|
{
|
||||||
return message.flags.hasAny(flags_) &&
|
return message.flags.hasAny(flags_) &&
|
||||||
!message.flags.has(MessageFlag::Timeout);
|
!message.flags.has(MessageFlag::Timeout);
|
||||||
|
}
|
||||||
return message.flags.hasAny(flags_);
|
return message.flags.hasAny(flags_);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -27,9 +27,11 @@ public:
|
||||||
* "system" is used for the "System" flag.
|
* "system" is used for the "System" flag.
|
||||||
*
|
*
|
||||||
* @param flags a string comma seperated list of names for the flags a message should have
|
* @param flags a string comma seperated list of names for the flags a message should have
|
||||||
|
* @param negate when set, excludes messages containg selected flags from results
|
||||||
*/
|
*/
|
||||||
MessageFlagsPredicate(const QString &flags);
|
MessageFlagsPredicate(const QString &flags, bool negate);
|
||||||
|
|
||||||
|
protected:
|
||||||
/**
|
/**
|
||||||
* @brief Checks whether the message has any of the flags passed
|
* @brief Checks whether the message has any of the flags passed
|
||||||
* in the constructor.
|
* in the constructor.
|
||||||
|
@ -38,7 +40,7 @@ public:
|
||||||
* @return true if the message has at least one of the specified flags,
|
* @return true if the message has at least one of the specified flags,
|
||||||
* false otherwise
|
* false otherwise
|
||||||
*/
|
*/
|
||||||
bool appliesTo(const Message &message);
|
bool appliesToImpl(const Message &message) override;
|
||||||
|
|
||||||
private:
|
private:
|
||||||
/// Holds the flags that will be searched for
|
/// Holds the flags that will be searched for
|
||||||
|
|
|
@ -11,7 +11,7 @@ namespace chatterino {
|
||||||
*
|
*
|
||||||
* Message predicates define certain features a message can satisfy.
|
* Message predicates define certain features a message can satisfy.
|
||||||
* Features are represented by classes derived from this abstract class.
|
* Features are represented by classes derived from this abstract class.
|
||||||
* A derived class must override `appliesTo` in order to test for the desired
|
* A derived class must override `appliesToImpl` in order to test for the desired
|
||||||
* feature.
|
* feature.
|
||||||
*/
|
*/
|
||||||
class MessagePredicate
|
class MessagePredicate
|
||||||
|
@ -19,15 +19,43 @@ class MessagePredicate
|
||||||
public:
|
public:
|
||||||
virtual ~MessagePredicate() = default;
|
virtual ~MessagePredicate() = default;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @brief Checks whether this predicate applies to the passed message
|
||||||
|
*
|
||||||
|
* Calls the derived classes `appliedTo` implementation, and respects the `isNegated_` flag
|
||||||
|
* it's set.
|
||||||
|
*
|
||||||
|
* @param message the message to check for this predicate
|
||||||
|
* @return true if this predicate applies, false otherwise
|
||||||
|
**/
|
||||||
|
bool appliesTo(const Message &message)
|
||||||
|
{
|
||||||
|
auto result = this->appliesToImpl(message);
|
||||||
|
if (this->isNegated_)
|
||||||
|
{
|
||||||
|
return !result;
|
||||||
|
}
|
||||||
|
return result;
|
||||||
|
}
|
||||||
|
|
||||||
|
protected:
|
||||||
|
explicit MessagePredicate(bool negate)
|
||||||
|
: isNegated_(negate)
|
||||||
|
{
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @brief Checks whether this predicate applies to the passed message.
|
* @brief Checks whether this predicate applies to the passed message.
|
||||||
*
|
*
|
||||||
* Implementations of `appliesTo` should never change the message's content
|
* Implementations of `appliesToImpl` should never change the message's content
|
||||||
* in order to be compatible with other MessagePredicates.
|
* in order to be compatible with other MessagePredicates.
|
||||||
*
|
*
|
||||||
* @param message the message to check for this predicate
|
* @param message the message to check for this predicate
|
||||||
* @return true if this predicate applies, false otherwise
|
* @return true if this predicate applies, false otherwise
|
||||||
*/
|
*/
|
||||||
virtual bool appliesTo(const Message &message) = 0;
|
virtual bool appliesToImpl(const Message &message) = 0;
|
||||||
|
|
||||||
|
private:
|
||||||
|
const bool isNegated_ = false;
|
||||||
};
|
};
|
||||||
} // namespace chatterino
|
} // namespace chatterino
|
||||||
|
|
|
@ -2,12 +2,13 @@
|
||||||
|
|
||||||
namespace chatterino {
|
namespace chatterino {
|
||||||
|
|
||||||
RegexPredicate::RegexPredicate(const QString ®ex)
|
RegexPredicate::RegexPredicate(const QString ®ex, bool negate)
|
||||||
: regex_(regex, QRegularExpression::CaseInsensitiveOption)
|
: MessagePredicate(negate)
|
||||||
|
, regex_(regex, QRegularExpression::CaseInsensitiveOption)
|
||||||
{
|
{
|
||||||
}
|
}
|
||||||
|
|
||||||
bool RegexPredicate::appliesTo(const Message &message)
|
bool RegexPredicate::appliesToImpl(const Message &message)
|
||||||
{
|
{
|
||||||
if (!regex_.isValid())
|
if (!regex_.isValid())
|
||||||
{
|
{
|
||||||
|
@ -19,4 +20,4 @@ bool RegexPredicate::appliesTo(const Message &message)
|
||||||
return match.hasMatch();
|
return match.hasMatch();
|
||||||
}
|
}
|
||||||
|
|
||||||
} // namespace chatterino
|
} // namespace chatterino
|
||||||
|
|
|
@ -20,9 +20,11 @@ public:
|
||||||
* The message is being matched case-insensitively.
|
* The message is being matched case-insensitively.
|
||||||
*
|
*
|
||||||
* @param regex the regex to match the message against
|
* @param regex the regex to match the message against
|
||||||
|
* @param negate when set, excludes messages matching the regex from results
|
||||||
*/
|
*/
|
||||||
RegexPredicate(const QString ®ex);
|
RegexPredicate(const QString ®ex, bool negate);
|
||||||
|
|
||||||
|
protected:
|
||||||
/**
|
/**
|
||||||
* @brief Checks whether the message matches the regex passed in the
|
* @brief Checks whether the message matches the regex passed in the
|
||||||
* constructor
|
* constructor
|
||||||
|
@ -32,11 +34,11 @@ public:
|
||||||
* @param message the message to check
|
* @param message the message to check
|
||||||
* @return true if the message matches the regex, false otherwise
|
* @return true if the message matches the regex, false otherwise
|
||||||
*/
|
*/
|
||||||
bool appliesTo(const Message &message);
|
bool appliesToImpl(const Message &message) override;
|
||||||
|
|
||||||
private:
|
private:
|
||||||
/// Holds the regular expression to match the message against
|
/// Holds the regular expression to match the message against
|
||||||
QRegularExpression regex_;
|
QRegularExpression regex_;
|
||||||
};
|
};
|
||||||
|
|
||||||
} // namespace chatterino
|
} // namespace chatterino
|
||||||
|
|
|
@ -3,11 +3,12 @@
|
||||||
namespace chatterino {
|
namespace chatterino {
|
||||||
|
|
||||||
SubstringPredicate::SubstringPredicate(const QString &search)
|
SubstringPredicate::SubstringPredicate(const QString &search)
|
||||||
: search_(search)
|
: MessagePredicate(false)
|
||||||
|
, search_(search)
|
||||||
{
|
{
|
||||||
}
|
}
|
||||||
|
|
||||||
bool SubstringPredicate::appliesTo(const Message &message)
|
bool SubstringPredicate::appliesToImpl(const Message &message)
|
||||||
{
|
{
|
||||||
return message.searchText.contains(this->search_, Qt::CaseInsensitive);
|
return message.searchText.contains(this->search_, Qt::CaseInsensitive);
|
||||||
}
|
}
|
||||||
|
|
|
@ -22,6 +22,7 @@ public:
|
||||||
*/
|
*/
|
||||||
SubstringPredicate(const QString &search);
|
SubstringPredicate(const QString &search);
|
||||||
|
|
||||||
|
protected:
|
||||||
/**
|
/**
|
||||||
* @brief Checks whether the message contains the substring passed in the
|
* @brief Checks whether the message contains the substring passed in the
|
||||||
* constructor.
|
* constructor.
|
||||||
|
@ -31,7 +32,7 @@ public:
|
||||||
* @param message the message to check
|
* @param message the message to check
|
||||||
* @return true if the message contains the substring, false otherwise
|
* @return true if the message contains the substring, false otherwise
|
||||||
*/
|
*/
|
||||||
bool appliesTo(const Message &message);
|
bool appliesToImpl(const Message &message) override;
|
||||||
|
|
||||||
private:
|
private:
|
||||||
/// Holds the substring to search for in a message's `messageText`
|
/// Holds the substring to search for in a message's `messageText`
|
||||||
|
|
|
@ -4,19 +4,17 @@
|
||||||
|
|
||||||
namespace chatterino {
|
namespace chatterino {
|
||||||
|
|
||||||
SubtierPredicate::SubtierPredicate(const QStringList &subtiers)
|
SubtierPredicate::SubtierPredicate(const QString &subtiers, bool negate)
|
||||||
|
: MessagePredicate(negate)
|
||||||
{
|
{
|
||||||
// Check if any comma-seperated values were passed and transform those
|
// Check if any comma-seperated values were passed and transform those
|
||||||
for (const auto &entry : subtiers)
|
for (const auto &subtier : subtiers.split(',', Qt::SkipEmptyParts))
|
||||||
{
|
{
|
||||||
for (const auto &subtier : entry.split(',', Qt::SkipEmptyParts))
|
this->subtiers_ << subtier;
|
||||||
{
|
|
||||||
this->subtiers_ << subtier;
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
bool SubtierPredicate::appliesTo(const Message &message)
|
bool SubtierPredicate::appliesToImpl(const Message &message)
|
||||||
{
|
{
|
||||||
for (const Badge &badge : message.badges)
|
for (const Badge &badge : message.badges)
|
||||||
{
|
{
|
||||||
|
|
|
@ -16,10 +16,12 @@ public:
|
||||||
/**
|
/**
|
||||||
* @brief Create an SubtierPredicate with a list of subtiers to search for.
|
* @brief Create an SubtierPredicate with a list of subtiers to search for.
|
||||||
*
|
*
|
||||||
* @param subtiers a list of subtiers that a message should contain
|
* @param subtiers one or more comma-separated subtiers that the message should contain
|
||||||
|
* @param negate when set, excludes messages containing selected subtiers from results
|
||||||
*/
|
*/
|
||||||
SubtierPredicate(const QStringList &subtiers);
|
SubtierPredicate(const QString &subtiers, bool negate);
|
||||||
|
|
||||||
|
protected:
|
||||||
/**
|
/**
|
||||||
* @brief Checks whether the message contains any of the subtiers passed
|
* @brief Checks whether the message contains any of the subtiers passed
|
||||||
* in the constructor.
|
* in the constructor.
|
||||||
|
@ -28,7 +30,7 @@ public:
|
||||||
* @return true if the message contains a subtier listed in the specified subtiers,
|
* @return true if the message contains a subtier listed in the specified subtiers,
|
||||||
* false otherwise
|
* false otherwise
|
||||||
*/
|
*/
|
||||||
bool appliesTo(const Message &message) override;
|
bool appliesToImpl(const Message &message) override;
|
||||||
|
|
||||||
private:
|
private:
|
||||||
/// Holds the subtiers that will be searched for
|
/// Holds the subtiers that will be searched for
|
||||||
|
|
|
@ -302,55 +302,57 @@ std::vector<std::unique_ptr<MessagePredicate>> SearchPopup::parsePredicates(
|
||||||
// It also ignores whitespaces in values when being surrounded by quotation
|
// It also ignores whitespaces in values when being surrounded by quotation
|
||||||
// marks, to enable inputs like this => regex:"kappa 123"
|
// marks, to enable inputs like this => regex:"kappa 123"
|
||||||
static QRegularExpression predicateRegex(
|
static QRegularExpression predicateRegex(
|
||||||
R"lit((?:(?<name>\w+):(?<value>".+?"|[^\s]+))|[^\s]+?(?=$|\s))lit");
|
R"lit((?<negation>[!\-])?(?:(?<name>\w+):(?<value>".+?"|[^\s]+))|[^\s]+?(?=$|\s))lit");
|
||||||
static QRegularExpression trimQuotationMarksRegex(R"(^"|"$)");
|
static QRegularExpression trimQuotationMarksRegex(R"(^"|"$)");
|
||||||
|
|
||||||
QRegularExpressionMatchIterator it = predicateRegex.globalMatch(input);
|
QRegularExpressionMatchIterator it = predicateRegex.globalMatch(input);
|
||||||
|
|
||||||
std::vector<std::unique_ptr<MessagePredicate>> predicates;
|
std::vector<std::unique_ptr<MessagePredicate>> predicates;
|
||||||
QStringList authors;
|
|
||||||
QStringList channels;
|
|
||||||
QStringList badges;
|
|
||||||
QStringList subtiers;
|
|
||||||
|
|
||||||
while (it.hasNext())
|
while (it.hasNext())
|
||||||
{
|
{
|
||||||
QRegularExpressionMatch match = it.next();
|
QRegularExpressionMatch match = it.next();
|
||||||
|
|
||||||
QString name = match.captured("name");
|
QString name = match.captured("name");
|
||||||
|
bool isNegated = !match.captured("negation").isEmpty();
|
||||||
QString value = match.captured("value");
|
QString value = match.captured("value");
|
||||||
value.remove(trimQuotationMarksRegex);
|
value.remove(trimQuotationMarksRegex);
|
||||||
|
|
||||||
// match predicates
|
// match predicates
|
||||||
|
|
||||||
if (name == "from")
|
if (name == "from")
|
||||||
{
|
{
|
||||||
authors.append(value);
|
predicates.push_back(
|
||||||
|
std::make_unique<AuthorPredicate>(value, isNegated));
|
||||||
}
|
}
|
||||||
else if (name == "badge")
|
else if (name == "badge")
|
||||||
{
|
{
|
||||||
badges.append(value);
|
predicates.push_back(
|
||||||
|
std::make_unique<BadgePredicate>(value, isNegated));
|
||||||
}
|
}
|
||||||
else if (name == "subtier")
|
else if (name == "subtier")
|
||||||
{
|
{
|
||||||
subtiers.append(value);
|
predicates.push_back(
|
||||||
|
std::make_unique<SubtierPredicate>(value, isNegated));
|
||||||
}
|
}
|
||||||
else if (name == "has" && value == "link")
|
else if (name == "has" && value == "link")
|
||||||
{
|
{
|
||||||
predicates.push_back(std::make_unique<LinkPredicate>());
|
predicates.push_back(std::make_unique<LinkPredicate>(isNegated));
|
||||||
}
|
}
|
||||||
else if (name == "in")
|
else if (name == "in")
|
||||||
{
|
{
|
||||||
channels.append(value);
|
predicates.push_back(
|
||||||
|
std::make_unique<ChannelPredicate>(value, isNegated));
|
||||||
}
|
}
|
||||||
else if (name == "is")
|
else if (name == "is")
|
||||||
{
|
{
|
||||||
predicates.push_back(
|
predicates.push_back(
|
||||||
std::make_unique<MessageFlagsPredicate>(value));
|
std::make_unique<MessageFlagsPredicate>(value, isNegated));
|
||||||
}
|
}
|
||||||
else if (name == "regex")
|
else if (name == "regex")
|
||||||
{
|
{
|
||||||
predicates.push_back(std::make_unique<RegexPredicate>(value));
|
predicates.push_back(
|
||||||
|
std::make_unique<RegexPredicate>(value, isNegated));
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
|
@ -359,26 +361,6 @@ std::vector<std::unique_ptr<MessagePredicate>> SearchPopup::parsePredicates(
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
if (!authors.empty())
|
|
||||||
{
|
|
||||||
predicates.push_back(std::make_unique<AuthorPredicate>(authors));
|
|
||||||
}
|
|
||||||
|
|
||||||
if (!channels.empty())
|
|
||||||
{
|
|
||||||
predicates.push_back(std::make_unique<ChannelPredicate>(channels));
|
|
||||||
}
|
|
||||||
|
|
||||||
if (!badges.empty())
|
|
||||||
{
|
|
||||||
predicates.push_back(std::make_unique<BadgePredicate>(badges));
|
|
||||||
}
|
|
||||||
|
|
||||||
if (!subtiers.empty())
|
|
||||||
{
|
|
||||||
predicates.push_back(std::make_unique<SubtierPredicate>(subtiers));
|
|
||||||
}
|
|
||||||
|
|
||||||
return predicates;
|
return predicates;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
Loading…
Reference in a new issue