mirror of
https://github.com/Chatterino/chatterino2.git
synced 2024-11-13 19:49:51 +01:00
Add new search predicate to enable searching for messages matching a regex (#3282)
Co-authored-by: pajlada <rasmus.karlsson@pajlada.com>
This commit is contained in:
parent
c1a3814b7c
commit
06245f3713
|
@ -2,6 +2,7 @@
|
||||||
|
|
||||||
## Unversioned
|
## Unversioned
|
||||||
|
|
||||||
|
- Minor: Added new search predicate to filter for messages matching a regex (#3282)
|
||||||
- Minor: Add `{channel.name}`, `{channel.id}`, `{stream.game}`, `{stream.title}`, `{my.id}`, `{my.name}` placeholders for commands (#3155)
|
- Minor: Add `{channel.name}`, `{channel.id}`, `{stream.game}`, `{stream.title}`, `{my.id}`, `{my.name}` placeholders for commands (#3155)
|
||||||
- Minor: Remove TwitchEmotes.com attribution and the open/copy options when right-clicking a Twitch Emote. (#2214, #3136)
|
- Minor: Remove TwitchEmotes.com attribution and the open/copy options when right-clicking a Twitch Emote. (#2214, #3136)
|
||||||
- Minor: Strip leading @ and trailing , from username in /user and /usercard commands. (#3143)
|
- Minor: Strip leading @ and trailing , from username in /user and /usercard commands. (#3143)
|
||||||
|
|
|
@ -185,6 +185,7 @@ SOURCES += \
|
||||||
src/messages/search/ChannelPredicate.cpp \
|
src/messages/search/ChannelPredicate.cpp \
|
||||||
src/messages/search/LinkPredicate.cpp \
|
src/messages/search/LinkPredicate.cpp \
|
||||||
src/messages/search/MessageFlagsPredicate.cpp \
|
src/messages/search/MessageFlagsPredicate.cpp \
|
||||||
|
src/messages/search/RegexPredicate.cpp \
|
||||||
src/messages/search/SubstringPredicate.cpp \
|
src/messages/search/SubstringPredicate.cpp \
|
||||||
src/messages/SharedMessageBuilder.cpp \
|
src/messages/SharedMessageBuilder.cpp \
|
||||||
src/providers/bttv/BttvEmotes.cpp \
|
src/providers/bttv/BttvEmotes.cpp \
|
||||||
|
|
|
@ -149,6 +149,8 @@ set(SOURCE_FILES
|
||||||
messages/search/LinkPredicate.hpp
|
messages/search/LinkPredicate.hpp
|
||||||
messages/search/MessageFlagsPredicate.cpp
|
messages/search/MessageFlagsPredicate.cpp
|
||||||
messages/search/MessageFlagsPredicate.hpp
|
messages/search/MessageFlagsPredicate.hpp
|
||||||
|
messages/search/RegexPredicate.cpp
|
||||||
|
messages/search/RegexPredicate.hpp
|
||||||
messages/search/SubstringPredicate.cpp
|
messages/search/SubstringPredicate.cpp
|
||||||
messages/search/SubstringPredicate.hpp
|
messages/search/SubstringPredicate.hpp
|
||||||
|
|
||||||
|
|
22
src/messages/search/RegexPredicate.cpp
Normal file
22
src/messages/search/RegexPredicate.cpp
Normal file
|
@ -0,0 +1,22 @@
|
||||||
|
#include "RegexPredicate.hpp"
|
||||||
|
|
||||||
|
namespace chatterino {
|
||||||
|
|
||||||
|
RegexPredicate::RegexPredicate(const QString ®ex)
|
||||||
|
: regex_(regex, QRegularExpression::CaseInsensitiveOption)
|
||||||
|
{
|
||||||
|
}
|
||||||
|
|
||||||
|
bool RegexPredicate::appliesTo(const Message &message)
|
||||||
|
{
|
||||||
|
if (!regex_.isValid())
|
||||||
|
{
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
QRegularExpressionMatch match = regex_.match(message.messageText);
|
||||||
|
|
||||||
|
return match.hasMatch();
|
||||||
|
}
|
||||||
|
|
||||||
|
} // namespace chatterino
|
42
src/messages/search/RegexPredicate.hpp
Normal file
42
src/messages/search/RegexPredicate.hpp
Normal file
|
@ -0,0 +1,42 @@
|
||||||
|
#pragma once
|
||||||
|
|
||||||
|
#include "QRegularExpression"
|
||||||
|
#include "messages/search/MessagePredicate.hpp"
|
||||||
|
|
||||||
|
namespace chatterino {
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @brief MessagePredicate checking whether the message matches a given regex.
|
||||||
|
*
|
||||||
|
* This predicate will only allow messages whose `messageText` match the given
|
||||||
|
* regex.
|
||||||
|
*/
|
||||||
|
class RegexPredicate : public MessagePredicate
|
||||||
|
{
|
||||||
|
public:
|
||||||
|
/**
|
||||||
|
* @brief Create a RegexPredicate with a regex to match the message against.
|
||||||
|
*
|
||||||
|
* The message is being matched case-insensitively.
|
||||||
|
*
|
||||||
|
* @param regex the regex to match the message against
|
||||||
|
*/
|
||||||
|
RegexPredicate(const QString ®ex);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @brief Checks whether the message matches the regex passed in the
|
||||||
|
* constructor
|
||||||
|
*
|
||||||
|
* The check is done case-insensitively.
|
||||||
|
*
|
||||||
|
* @param message the message to check
|
||||||
|
* @return true if the message matches the regex, false otherwise
|
||||||
|
*/
|
||||||
|
bool appliesTo(const Message &message);
|
||||||
|
|
||||||
|
private:
|
||||||
|
/// Holds the regular expression to match the message against
|
||||||
|
QRegularExpression regex_;
|
||||||
|
};
|
||||||
|
|
||||||
|
} // namespace chatterino
|
|
@ -11,6 +11,7 @@
|
||||||
#include "messages/search/ChannelPredicate.hpp"
|
#include "messages/search/ChannelPredicate.hpp"
|
||||||
#include "messages/search/LinkPredicate.hpp"
|
#include "messages/search/LinkPredicate.hpp"
|
||||||
#include "messages/search/MessageFlagsPredicate.hpp"
|
#include "messages/search/MessageFlagsPredicate.hpp"
|
||||||
|
#include "messages/search/RegexPredicate.hpp"
|
||||||
#include "messages/search/SubstringPredicate.hpp"
|
#include "messages/search/SubstringPredicate.hpp"
|
||||||
#include "util/Shortcut.hpp"
|
#include "util/Shortcut.hpp"
|
||||||
#include "widgets/helper/ChannelView.hpp"
|
#include "widgets/helper/ChannelView.hpp"
|
||||||
|
@ -164,51 +165,56 @@ void SearchPopup::initLayout()
|
||||||
std::vector<std::unique_ptr<MessagePredicate>> SearchPopup::parsePredicates(
|
std::vector<std::unique_ptr<MessagePredicate>> SearchPopup::parsePredicates(
|
||||||
const QString &input)
|
const QString &input)
|
||||||
{
|
{
|
||||||
static QRegularExpression predicateRegex(R"(^(\w+):([\w,]+)$)");
|
// This regex captures all name:value predicate pairs into named capturing
|
||||||
|
// groups and matches all other inputs seperated by spaces as normal
|
||||||
|
// strings.
|
||||||
|
// It also ignores whitespaces in values when being surrounded by quotation
|
||||||
|
// marks, to enable inputs like this => regex:"kappa 123"
|
||||||
|
static QRegularExpression predicateRegex(
|
||||||
|
R"lit((?:(?<name>\w+):(?<value>".+?"|[^\s]+))|[^\s]+?(?=$|\s))lit");
|
||||||
|
static QRegularExpression trimQuotationMarksRegex(R"(^"|"$)");
|
||||||
|
|
||||||
|
QRegularExpressionMatchIterator it = predicateRegex.globalMatch(input);
|
||||||
|
|
||||||
std::vector<std::unique_ptr<MessagePredicate>> predicates;
|
std::vector<std::unique_ptr<MessagePredicate>> predicates;
|
||||||
auto words = input.split(' ', QString::SkipEmptyParts);
|
|
||||||
QStringList authors;
|
QStringList authors;
|
||||||
QStringList channels;
|
QStringList channels;
|
||||||
|
|
||||||
for (auto it = words.begin(); it != words.end();)
|
while (it.hasNext())
|
||||||
{
|
{
|
||||||
if (auto match = predicateRegex.match(*it); match.hasMatch())
|
QRegularExpressionMatch match = it.next();
|
||||||
|
|
||||||
|
QString name = match.captured("name");
|
||||||
|
|
||||||
|
QString value = match.captured("value");
|
||||||
|
value.remove(trimQuotationMarksRegex);
|
||||||
|
|
||||||
|
// match predicates
|
||||||
|
if (name == "from")
|
||||||
{
|
{
|
||||||
QString name = match.captured(1);
|
authors.append(value);
|
||||||
QString value = match.captured(2);
|
}
|
||||||
|
else if (name == "has" && value == "link")
|
||||||
bool remove = true;
|
{
|
||||||
|
predicates.push_back(std::make_unique<LinkPredicate>());
|
||||||
// match predicates
|
}
|
||||||
if (name == "from")
|
else if (name == "in")
|
||||||
{
|
{
|
||||||
authors.append(value);
|
channels.append(value);
|
||||||
}
|
}
|
||||||
else if (name == "has" && value == "link")
|
else if (name == "is")
|
||||||
{
|
{
|
||||||
predicates.push_back(std::make_unique<LinkPredicate>());
|
predicates.push_back(
|
||||||
}
|
std::make_unique<MessageFlagsPredicate>(value));
|
||||||
else if (name == "in")
|
}
|
||||||
{
|
else if (name == "regex")
|
||||||
channels.append(value);
|
{
|
||||||
}
|
predicates.push_back(std::make_unique<RegexPredicate>(value));
|
||||||
else if (name == "is")
|
|
||||||
{
|
|
||||||
predicates.push_back(
|
|
||||||
std::make_unique<MessageFlagsPredicate>(value));
|
|
||||||
}
|
|
||||||
else
|
|
||||||
{
|
|
||||||
remove = false;
|
|
||||||
}
|
|
||||||
|
|
||||||
// remove or advance
|
|
||||||
it = remove ? words.erase(it) : ++it;
|
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
++it;
|
predicates.push_back(
|
||||||
|
std::make_unique<SubstringPredicate>(match.captured()));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -218,10 +224,6 @@ std::vector<std::unique_ptr<MessagePredicate>> SearchPopup::parsePredicates(
|
||||||
if (!channels.empty())
|
if (!channels.empty())
|
||||||
predicates.push_back(std::make_unique<ChannelPredicate>(channels));
|
predicates.push_back(std::make_unique<ChannelPredicate>(channels));
|
||||||
|
|
||||||
if (!words.empty())
|
|
||||||
predicates.push_back(
|
|
||||||
std::make_unique<SubstringPredicate>(words.join(" ")));
|
|
||||||
|
|
||||||
return predicates;
|
return predicates;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
Loading…
Reference in a new issue