app/NamedCommandCatalog.h
| Line | Branch | Exec | Source |
|---|---|---|---|
| 1 | #pragma once | ||
| 2 | #include "CommandRegistry.h" | ||
| 3 | #include <QList> | ||
| 4 | #include <QStringList> | ||
| 5 | |||
| 6 | // What follows a command name or a fixed value (Sprint 98 D-18). Each kind | ||
| 7 | // has one completion provider in CommandCompletion (rule 10) and decides | ||
| 8 | // whether a command line may be stored in the history (D-9). | ||
| 9 | enum class CommandArgument : quint8 { | ||
| 10 | None, // nothing may follow | ||
| 11 | Values, // only the fixed values of the command | ||
| 12 | Text, // free text: names, filter text (never stored) | ||
| 13 | Folder, // a folder path (stored: it names a place) | ||
| 14 | Label, // a label/IMAP keyword (stored) | ||
| 15 | Address, // a mail address (never stored) | ||
| 16 | Date, // a date word or date (stored) | ||
| 17 | Query, // a search query (never stored) | ||
| 18 | TaskLine, // "Title /list @due !priority #tag" (never stored) | ||
| 19 | EventLine, // "Title date time @place /calendar" (never stored) | ||
| 20 | RuleLine, // "conditions → actions" (never stored) | ||
| 21 | ContactLine, // "Name <address>" (never stored) | ||
| 22 | Place, // Sprint 100 D-29: a settings place — page, group or setting id (stored: it names a place) | ||
| 23 | Account, // Sprint 100 D-29: a configured account's address (never stored) | ||
| 24 | }; | ||
| 25 | |||
| 26 | // A fixed value at the argument position ("filter unread", "folder rename"). | ||
| 27 | struct NamedCommandValue { | ||
| 28 | const char *word = ""; // lowercase English, executed as typed | ||
| 29 | const char *description = ""; // QT_TRANSLATE_NOOP("NamedCommandCatalog", …) | ||
| 30 | const char *keywords = ""; // de/en search words; found, never executed (D-14) | ||
| 31 | const char *registryId = nullptr; // runs this registry command | ||
| 32 | CommandArgument then = CommandArgument::None; // what follows the value | ||
| 33 | bool thenRequired = false; | ||
| 34 | const char *thenSyntax = ""; // "<name>", "[folder]" | ||
| 35 | const char *stateKey = nullptr; // toggle value: its on/off state (S98.4.11) | ||
| 36 | CommandContexts featured; // listed under "Here" in the empty state (S98.5.1) | ||
| 37 | // D-15a: in a line of several values, values of one group exclude each | ||
| 38 | // other ("mark read unread"); an `alone` value stands only by itself | ||
| 39 | // ("filter off", "unlabel all"). | ||
| 40 | const char *group = nullptr; | ||
| 41 | bool alone = false; | ||
| 42 | }; | ||
| 43 | |||
| 44 | struct NamedCommandSpec { | ||
| 45 | QString name; | ||
| 46 | const char *category = ""; // QT_TRANSLATE_NOOP("NamedCommandCatalog", …) | ||
| 47 | const char *description = ""; // QT_TRANSLATE_NOOP("NamedCommandCatalog", …) | ||
| 48 | CommandContexts contexts; | ||
| 49 | const char *registryId = nullptr; // runs this registry command when no value says otherwise | ||
| 50 | CommandArgument argument = CommandArgument::None; | ||
| 51 | bool argumentRequired = false; | ||
| 52 | const char *syntax = ""; // argument notation of D-15, e.g. "<label>" | ||
| 53 | const char *example = ""; // a complete example line | ||
| 54 | QList<NamedCommandValue> values; | ||
| 55 | const char *keywords = ""; // de/en search words (D-14) | ||
| 56 | bool destructive = false; // never runs from a prefix or pre-highlight (D-6) | ||
| 57 | // Values, or any other text (F100-74: "filter <words>" filters the list | ||
| 58 | // by subject and sender, live while typing). | ||
| 59 | bool freeText = false; | ||
| 60 | // D-15a: several values or labels in one line ("filter unread important", | ||
| 61 | // "mark read important", "label Work Later"); see ParsedNamedCommand::values(). | ||
| 62 | bool several = false; | ||
| 63 | const char *stateKey = nullptr; // toggle command: its on/off state (S98.4.11) | ||
| 64 | CommandContexts featured; // listed under "Here" in the empty state (S98.5.1) | ||
| 65 | QStringList aliases; // removed by the clean-up (S98.4.9) | ||
| 66 | |||
| 67 | 5457 | bool requiresArgument() const { return argumentRequired; } | |
| 68 | bool optionalArgument() const { return argument != CommandArgument::None && !argumentRequired; } | ||
| 69 | const NamedCommandValue *value(const QString &word) const; | ||
| 70 | }; | ||
| 71 | |||
| 72 | struct ParsedNamedCommand { | ||
| 73 | const NamedCommandSpec *spec = nullptr; // set whenever the first word names a command | ||
| 74 | QString argument; | ||
| 75 | // The argument fits the command: an allowed value, what the value takes, | ||
| 76 | // nothing after a command without argument. | ||
| 77 | bool argumentValid = true; | ||
| 78 |
8/8✓ Branch 2 → 3 taken 2271 times.
✓ Branch 2 → 9 taken 54 times.
✓ Branch 3 → 4 taken 2202 times.
✓ Branch 3 → 9 taken 69 times.
✓ Branch 5 → 6 taken 962 times.
✓ Branch 5 → 8 taken 1240 times.
✓ Branch 7 → 8 taken 852 times.
✓ Branch 7 → 9 taken 110 times.
|
2325 | bool complete() const { return spec && argumentValid && (!spec->requiresArgument() || !argument.isEmpty()); } |
| 79 |
6/10✓ Branch 2 → 3 taken 277 times.
✗ Branch 2 → 8 not taken.
✓ Branch 4 → 5 taken 50 times.
✓ Branch 4 → 6 taken 227 times.
✓ Branch 6 → 7 taken 227 times.
✓ Branch 7 → 9 taken 277 times.
✓ Branch 10 → 11 taken 277 times.
✗ Branch 10 → 12 not taken.
✗ Branch 14 → 15 not taken.
✗ Branch 14 → 16 not taken.
|
554 | QString text() const { return spec ? spec->name + (argument.isEmpty() ? QString{} : " " + argument) : QString{}; } |
| 80 | // The fixed values the argument starts with — one at most unless the | ||
| 81 | // command takes several (D-15a) — and the text after them ("folder rename | ||
| 82 | // Projekte" → value "rename", rest "Projekte"; "filter unread important | ||
| 83 | // invoice" → values "unread", "important", rest "invoice"). | ||
| 84 | QList<const NamedCommandValue *> values() const; | ||
| 85 | const NamedCommandValue *value() const; // the first of values(), nullptr when none | ||
| 86 | QString rest() const; | ||
| 87 | // The words of the argument ("label Work Later" → "Work", "Later"). | ||
| 88 | QStringList words() const; | ||
| 89 | // The registry command this line runs: the id of its only value, else the | ||
| 90 | // command's. A line of several parts runs the command's own handler. | ||
| 91 | QString registryId() const; | ||
| 92 | }; | ||
| 93 | |||
| 94 | // Named commands of the command bar. The catalog is the single source of | ||
| 95 | // names, arguments, keywords, registry ids and help (D-18, rule 16); | ||
| 96 | // docs/COMMANDS.md is generated from referenceSnapshot() and kept in sync | ||
| 97 | // by a test. Named commands are not keyboard shortcuts: setters remain | ||
| 98 | // idempotent and arguments retain case and internal whitespace. | ||
| 99 | class NamedCommandCatalog { | ||
| 100 | public: | ||
| 101 | static const QList<NamedCommandSpec> &specs(); | ||
| 102 | static const NamedCommandSpec *find(const QString &name); | ||
| 103 | static ParsedNamedCommand parse(const QString &input); | ||
| 104 | static QStringList names(CommandContext context); | ||
| 105 | static QString description(const NamedCommandSpec &spec); | ||
| 106 | static QString description(const NamedCommandValue &value); | ||
| 107 | static QString category(const NamedCommandSpec &spec); | ||
| 108 | // D-9: whether a parsed line may enter the command history. | ||
| 109 | static bool storable(const ParsedNamedCommand &parsed); | ||
| 110 | // D-15a: whether `values` may stand together in one line: no value twice, | ||
| 111 | // one per group, an `alone` value only by itself (`more`: other words follow). | ||
| 112 | static bool combinable(const QList<const NamedCommandValue *> &values, bool more); | ||
| 113 | // Lint of D-14/D-15/D-18; one message per violation (empty = clean). | ||
| 114 | static QStringList lint(); | ||
| 115 | // Markdown reference of every command (docs/COMMANDS.md). | ||
| 116 | static QString referenceSnapshot(); | ||
| 117 | }; | ||
| 118 |