Skip to content

Commit

Permalink
refactor: SearchQueryParser::getTextArgument
Browse files Browse the repository at this point in the history
  • Loading branch information
Swiftb0y authored and ronso0 committed Oct 10, 2023
1 parent d938e9a commit 237e381
Show file tree
Hide file tree
Showing 2 changed files with 74 additions and 56 deletions.
120 changes: 67 additions & 53 deletions src/library/searchqueryparser.cpp
@@ -1,8 +1,57 @@
#include "library/searchqueryparser.h"

#include <QRegularExpression>
#include <utility>

#include "library/searchquery.h"
#include "track/keyutils.h"
#include "util/assert.h"

namespace {

enum class Quoted : bool {
Incomplete,
Complete,
};

std::pair<QString, Quoted> consumeQuotedArgument(QString argument,
QStringList* tokens) {
DEBUG_ASSERT(argument.startsWith("\""));

argument = argument.mid(1);

int quote_index = argument.indexOf("\"");
while (quote_index == -1 && tokens->length() > 0) {
argument += " " + tokens->takeFirst();
quote_index = argument.indexOf("\"");
}

if (quote_index == -1) {
// No ending quote found. Since we think they are going to close the
// quote eventually, treat the entire token list as the argument for
// now.
return {argument, Quoted::Incomplete};
}

// Stuff the rest of the argument after the quote back into tokens.
QString remaining = argument.mid(quote_index + 1).trimmed();
if (remaining.size() != 0) {
tokens->push_front(remaining);
}

if (quote_index == 0) {
// We have found an explicit empty string ""
// return it as "" to distinguish it from an unfinished empty string
argument = kMissingFieldSearchTerm;
} else {
// Found a closing quote.
// Slice off the quote and everything after.
argument = argument.left(quote_index);
}
return {argument, Quoted::Complete};
}

} // anonymous namespace

constexpr char kNegatePrefix[] = "-";
constexpr char kFuzzyPrefix[] = "~";
Expand Down Expand Up @@ -84,12 +133,8 @@ void SearchQueryParser::setSearchColumns(QStringList searchColumns) {
}
}

QString SearchQueryParser::getTextArgument(QString argument,
QStringList* tokens,
StringMatch* matchMode) const {
if (matchMode != nullptr) {
*matchMode = StringMatch::Contains;
}
SearchQueryParser::TextArgumentResult SearchQueryParser::getTextArgument(QString argument,
QStringList* tokens) const {
// If the argument is empty, assume the user placed a space after an
// advanced search command. Consume another token and treat that as the
// argument.
Expand All @@ -99,51 +144,23 @@ QString SearchQueryParser::getTextArgument(QString argument,
argument = tokens->takeFirst();
}
}

bool shouldMatchExactly = false;
StringMatch mode = StringMatch::Contains;
bool equalRequested = false;
if (argument.startsWith("=")) {
// strip the '=' from the argument
argument = argument.mid(1);
shouldMatchExactly = true;
// TODO(ronso0) should 'tag:=string' really be equal to 'tag:="string"?
mode = StringMatch::Equals;
equalRequested = true;
}
// Deal with quoted arguments. If this token started with a quote, then
// search for the closing quote.
if (argument.startsWith("\"")) {
argument = argument.mid(1);

int quote_index = argument.indexOf("\"");
while (quote_index == -1 && tokens->length() > 0) {
argument += " " + tokens->takeFirst();
quote_index = argument.indexOf("\"");
}

if (quote_index == -1) {
// No ending quote found. Since we think they are going to close the
// quote eventually, treat the entire token list as the argument for
// now.
return argument;
}

// Stuff the rest of the argument after the quote back into tokens.
QString remaining = argument.mid(quote_index+1).trimmed();
if (remaining.size() != 0) {
tokens->push_front(remaining);
}

if (quote_index == 0) {
// We have found an explicit empty string ""
// return it as "" to distinguish it from an unfinished empty string
argument = kMissingFieldSearchTerm;
} else {
// Found a closing quote.
// Slice off the quote and everything after.
argument = argument.left(quote_index);
if (matchMode != nullptr && shouldMatchExactly) {
*matchMode = StringMatch::Equals;
}
}
Quoted quoted;
std::tie(argument, quoted) = consumeQuotedArgument(argument, tokens);
mode = equalRequested && quoted == Quoted::Complete
? StringMatch::Equals
: StringMatch::Contains;
}

return argument;
return {argument, mode};
}

void SearchQueryParser::parseTokens(QStringList tokens,
Expand All @@ -165,8 +182,7 @@ void SearchQueryParser::parseTokens(QStringList tokens,
// TODO(XXX): implement this feature.
} else if (textFilterMatch.hasMatch()) {
QString field = textFilterMatch.captured(1);
StringMatch matchMode = StringMatch::Contains;
QString argument = getTextArgument(textFilterMatch.captured(2), &tokens, &matchMode);
auto [argument, matchMode] = getTextArgument(textFilterMatch.captured(2), &tokens);

if (argument == kMissingFieldSearchTerm) {
qDebug() << "argument explicit empty";
Expand All @@ -193,9 +209,7 @@ void SearchQueryParser::parseTokens(QStringList tokens,
}
} else if (numericFilterMatch.hasMatch()) {
QString field = numericFilterMatch.captured(1);
QString argument = getTextArgument(
numericFilterMatch.captured(2), &tokens)
.trimmed();
QString argument = getTextArgument(numericFilterMatch.captured(2), &tokens).argument;

if (!argument.isEmpty()) {
if (argument == kMissingFieldSearchTerm) {
Expand All @@ -211,7 +225,7 @@ void SearchQueryParser::parseTokens(QStringList tokens,
QString field = specialFilterMatch.captured(1);
QString argument = getTextArgument(
specialFilterMatch.captured(2), &tokens)
.trimmed();
.argument;
if (!argument.isEmpty()) {
if (field == "key") {
mixxx::track::io::key::ChromaticKey key =
Expand Down Expand Up @@ -249,7 +263,7 @@ void SearchQueryParser::parseTokens(QStringList tokens,
}
// Don't trigger on a lone minus sign.
if (!token.isEmpty()) {
QString argument = getTextArgument(token, &tokens);
QString argument = getTextArgument(token, &tokens).argument;
// For untagged strings we search the track fields as well
// as the crate names the track is in. This allows the user
// to use crates like tags
Expand Down
10 changes: 7 additions & 3 deletions src/library/searchqueryparser.h
Expand Up @@ -27,9 +27,13 @@ class SearchQueryParser {
void parseTokens(QStringList tokens,
AndNode* pQuery) const;

QString getTextArgument(QString argument,
QStringList* tokens,
StringMatch* matchMode = nullptr) const;
struct TextArgumentResult {
QString argument;
StringMatch mode;
};

TextArgumentResult getTextArgument(QString argument,
QStringList* tokens) const;

TrackCollection* m_pTrackCollection;
QStringList m_queryColumns;
Expand Down

0 comments on commit 237e381

Please sign in to comment.