Improvements to Message Search (#1237)

* Ran clang-format

* Implement user-specific search in message history

This functionality was originally requested in #1236.

This commit changes the SearchPopup::performSearch method so that only
messages from specific users can be shown.

In order to filter for a specific user, enter their username with a
leading '@' in the search popup. You can also add an additional search
phrase which will also be considered in the search.

* Naive implementation for "from:" tags

Rebase later?

* Cleverer (?) version using Predicates

Commit adds two POC predicates: one for the author of messages, and one
for substring search in messages.

Problems/TODOs:
* Best way to register new predicates?
* Clean up tags (e.g. "from:") or not?
* Test combinations of different predicates

* Add a predicate to check for links in messages

* Remove a dumb TODO

* Rewrite SearchPopup::performSearch to be cleaner

* Ran clang-format on all files

* Remove TODO I missed earlier

* Forgot to run clang-format

peepoSadDank

* Re-use {}-initialization

Was accidentally removed when fixing earlier merge conflict.

* Does this fix line endings?

No diffs are shown locally, hopefully Git doesn't lie to me.

* Rename "predicates" directory to "search"

Resolving one conversation in the review of #1237.

* Use LinkParser in LinkPredicate

Resolving a conversation in the review of #1237.

* Predicates: Use unique_ptr instead of shared_ptr

Resolves a conversation in the review of #1237.

* Refactor of SearchPopup and AuthorPredicate

Resolving some points from the review in #1237.

* Moved parsing of comma-seperated values into AuthorPredicate
  constructor.
* Rewrite SearchPopup::parsePredicates as suggested.
* Deleted now redundant methods in SearchPopup.

* MessagePredicate::appliesTo now takes a Message&

... instead of a MessagePtr.

This resolves a conversation in the review of #1237.

* Run clang-format on two files I missed

* AuthorPredicate: Check for displayName & loginName

Resolving conversation on #1237.
This commit is contained in:
Leon Richardt
2019-09-09 15:21:49 +02:00
committed by fourtf
parent 6cd3cfe79f
commit 720e5aa25f
15 changed files with 323 additions and 26 deletions
+79 -12
View File
@@ -7,29 +7,45 @@
#include "common/Channel.hpp"
#include "messages/Message.hpp"
#include "messages/search/AuthorPredicate.hpp"
#include "messages/search/LinkPredicate.hpp"
#include "messages/search/SubstringPredicate.hpp"
#include "widgets/helper/ChannelView.hpp"
namespace chatterino {
namespace {
ChannelPtr filter(const QString &text, const QString &channelName,
const LimitedQueueSnapshot<MessagePtr> &snapshot)
ChannelPtr SearchPopup::filter(const QString &text, const QString &channelName,
const LimitedQueueSnapshot<MessagePtr> &snapshot)
{
ChannelPtr channel(new Channel(channelName, Channel::Type::None));
// Parse predicates from tags in "text"
auto predicates = parsePredicates(text);
// Check for every message whether it fulfills all predicates that have
// been registered
for (size_t i = 0; i < snapshot.size(); ++i)
{
ChannelPtr channel(new Channel(channelName, Channel::Type::None));
MessagePtr message = snapshot[i];
for (size_t i = 0; i < snapshot.size(); i++)
bool accept = true;
for (const auto &pred : predicates)
{
MessagePtr message = snapshot[i];
if (text.isEmpty() ||
message->searchText.indexOf(text, 0, Qt::CaseInsensitive) != -1)
// Discard the message as soon as one predicate fails
if (!pred->appliesTo(*message))
{
channel->addMessage(message);
accept = false;
break;
}
}
return channel;
// If all predicates match, add the message to the channel
if (accept)
channel->addMessage(message);
}
} // namespace
return channel;
}
SearchPopup::SearchPopup()
{
@@ -113,4 +129,55 @@ void SearchPopup::initLayout()
}
}
std::vector<std::unique_ptr<MessagePredicate>> SearchPopup::parsePredicates(
const QString &input)
{
static QRegularExpression predicateRegex(R"(^(\w+):([\w,]+)$)");
auto predicates = std::vector<std::unique_ptr<MessagePredicate>>();
auto words = input.split(' ', QString::SkipEmptyParts);
auto authors = QStringList();
for (auto it = words.begin(); it != words.end();)
{
if (auto match = predicateRegex.match(*it); match.hasMatch())
{
QString name = match.captured(1);
QString value = match.captured(2);
bool remove = true;
// match predicates
if (name == "from")
{
authors.append(value);
}
else if (name == "has" && value == "link")
{
predicates.push_back(std::make_unique<LinkPredicate>());
}
else
{
remove = false;
}
// remove or advance
it = remove ? words.erase(it) : ++it;
}
else
{
++it;
}
}
if (!authors.empty())
predicates.push_back(std::make_unique<AuthorPredicate>(authors));
if (!words.empty())
predicates.push_back(
std::make_unique<SubstringPredicate>(words.join(" ")));
return predicates;
}
} // namespace chatterino