From e7e8ef305556a0e32a326db326f6eba24f4c1498 Mon Sep 17 00:00:00 2001 From: Joachim Kohlhammer Date: Thu, 8 Aug 2024 23:42:38 +0200 Subject: [PATCH] Refined the grammar for filtering workouts (#4531) * Added support to filter for numbers in the description / title / tags, similar to #4529 * Added quoted strings when filtering in description / title / tags: * Words can be grouped by putting them in quotes to search for exact matches * Keywords can be put in quotes to strip their special meaning * Examples: "Power Builder 60", "Power" 60 * Added this also to TrainerDay, keeping the syntax similar * Adjusted the autocompletion to not propose keywords when within open quotes --- src/Train/FilterEditor.cpp | 29 +++++++++++++++++++++++++---- src/Train/FilterEditor.h | 2 ++ src/Train/TrainerDayAPIQuery.l | 1 + src/Train/WorkoutFilter.l | 1 + src/Train/WorkoutFilter.y | 3 ++- 5 files changed, 31 insertions(+), 5 deletions(-) diff --git a/src/Train/FilterEditor.cpp b/src/Train/FilterEditor.cpp index f40cfeb82..7f7aded7c 100644 --- a/src/Train/FilterEditor.cpp +++ b/src/Train/FilterEditor.cpp @@ -131,13 +131,12 @@ FilterEditor::keyPressEvent if ( e->text().endsWith(" ") || e->text().endsWith(",") - || e->text().endsWith(".")) { + || e->text().endsWith(".") + || ! isSelectorPosition() + || isQuoted()) { _completer->popup()->hide(); return; } - if (! isSelectorPosition()) { - return; - } QString completionPrefix = wordLeftOfCursor(); if (completionPrefix != _completer->completionPrefix()) { @@ -232,6 +231,14 @@ FilterEditor::isSelectorPosition } +bool +FilterEditor::isQuoted +() const +{ + return feh.isQuoted(text(), cursorPosition()); +} + + /// FilterEditorHelper FilterEditorHelper::FilterEditorHelper @@ -366,6 +373,20 @@ FilterEditorHelper::isSelectorPosition } +bool +FilterEditorHelper::isQuoted +(const QString &t, int cp) const +{ + int count = 0; + for (int pos = 0; pos < cp; ++pos) { + if (t[pos] == '"') { + ++count; + } + } + return count % 2 == 1; +} + + bool FilterEditorHelper::isWordChar (QChar ch) const diff --git a/src/Train/FilterEditor.h b/src/Train/FilterEditor.h index 9f3cf7060..1ce043bae 100644 --- a/src/Train/FilterEditor.h +++ b/src/Train/FilterEditor.h @@ -41,6 +41,7 @@ public: QString wordLeftOfCursor(const QString &t, int cp) const; QString wordRightOfCursor(const QString &t, int cp) const; bool isSelectorPosition(const QString &t, int cp) const; + bool isQuoted(const QString &t, int cp) const; bool isWordChar(QChar ch) const; std::pair evaluateCompletion(const QString &originalText, const QString &completion, int cursorPosition, int selectionStart, int selectionLength) const; @@ -76,6 +77,7 @@ private: QString wordLeftOfCursor() const; QString wordRightOfCursor() const; bool isSelectorPosition() const; + bool isQuoted() const; }; #endif diff --git a/src/Train/TrainerDayAPIQuery.l b/src/Train/TrainerDayAPIQuery.l index 594115aeb..05e8eeeb7 100644 --- a/src/Train/TrainerDayAPIQuery.l +++ b/src/Train/TrainerDayAPIQuery.l @@ -51,6 +51,7 @@ extern YYSTYPE yylval; [Vv][Oo][2][Mm][Aa][Xx] { return ZONE_VO2MAX; } [Aa][Nn][Aa][Ee][Rr][Oo][Bb][Ii][Cc] { return ZONE_ANAEROBIC; } [Tt][Ee][Ss][Tt] { return ZONE_TEST; } +\"[^\"]+\" { yytext[strlen(yytext) - 1] = '\0'; TrainerDayAPIQuerylval.word = yytext + 1; return WORD; } [^ ,.]+ { TrainerDayAPIQuerylval.word = yytext; return WORD; } . { return yytext[0]; } diff --git a/src/Train/WorkoutFilter.l b/src/Train/WorkoutFilter.l index f1854c31f..6ac7c9693 100644 --- a/src/Train/WorkoutFilter.l +++ b/src/Train/WorkoutFilter.l @@ -68,6 +68,7 @@ extern YYSTYPE yylval; [Ee][Ll][Ee][Vv][Aa][Tt][Ii][Oo][Nn] { return ELEVATION; } [Gg][Rr][Aa][Dd][Ee] { return GRADE; } [zZ]([1-9]|10) { yytext += 1; WorkoutFilterlval.numValue = atoi(yytext); return ZONE; } +\"[^\"]+\" { yytext[strlen(yytext) - 1] = '\0'; WorkoutFilterlval.word = yytext + 1; return WORD; } [^ ,.]+ { WorkoutFilterlval.word = yytext; return WORD; } . { return yytext[0]; } diff --git a/src/Train/WorkoutFilter.y b/src/Train/WorkoutFilter.y index f589d7c57..4a663bc21 100644 --- a/src/Train/WorkoutFilter.y +++ b/src/Train/WorkoutFilter.y @@ -240,7 +240,8 @@ words: word words { *$1 << *$2; delete $2; $$ = $1; } | word { $$ = $1; } ; -word: WORD { $$ = new QStringList($1); } +word: WORD { $$ = new QStringList($1); } + | NUMBER { $$ = new QStringList(QString::number($1)); } ; mixedNumValue: FLOAT { $$ = $1; }