diff --git a/src/Cmd.cpp b/src/Cmd.cpp index ec3761530..63f019c14 100644 --- a/src/Cmd.cpp +++ b/src/Cmd.cpp @@ -106,6 +106,8 @@ void Cmd::load () { if (commands.size () == 0) { + commands.push_back ("_projects"); + commands.push_back ("_tags"); commands.push_back (context.stringtable.get (CMD_ADD, "add")); commands.push_back (context.stringtable.get (CMD_APPEND, "append")); commands.push_back (context.stringtable.get (CMD_ANNOTATE, "annotate")); @@ -182,7 +184,9 @@ void Cmd::allCustomReports (std::vector & all) const // Commands that do not directly modify the data files. bool Cmd::isReadOnlyCommand () { - if (command == context.stringtable.get (CMD_CALENDAR, "calendar") || + if (command == "_projects" || + command == "_tags" || + command == context.stringtable.get (CMD_CALENDAR, "calendar") || command == context.stringtable.get (CMD_COLORS, "colors") || command == context.stringtable.get (CMD_EXPORT, "export") || command == context.stringtable.get (CMD_HELP, "help") || diff --git a/src/Context.cpp b/src/Context.cpp index ae98d23c0..d5ccc21af 100644 --- a/src/Context.cpp +++ b/src/Context.cpp @@ -169,35 +169,37 @@ std::string Context::dispatch () // TODO Just look at this thing. It cries out for a dispatch table. std::string out; - if (cmd.command == "projects") { out = handleProjects (); } - else if (cmd.command == "tags") { out = handleTags (); } - else if (cmd.command == "colors") { out = handleColor (); } - else if (cmd.command == "version") { out = handleVersion (); } - else if (cmd.command == "help") { out = longUsage (); } - else if (cmd.command == "stats") { out = handleReportStats (); } - else if (cmd.command == "info") { out = handleInfo (); } - else if (cmd.command == "history") { out = handleReportHistory (); } - else if (cmd.command == "ghistory") { out = handleReportGHistory (); } - else if (cmd.command == "summary") { out = handleReportSummary (); } - else if (cmd.command == "calendar") { out = handleReportCalendar (); } - else if (cmd.command == "timesheet") { out = handleReportTimesheet (); } - else if (cmd.command == "add") { out = handleAdd (); } - else if (cmd.command == "append") { out = handleAppend (); } - else if (cmd.command == "annotate") { out = handleAnnotate (); } - else if (cmd.command == "done") { out = handleDone (); } - else if (cmd.command == "delete") { out = handleDelete (); } - else if (cmd.command == "start") { out = handleStart (); } - else if (cmd.command == "stop") { out = handleStop (); } - else if (cmd.command == "export") { out = handleExport (); } - else if (cmd.command == "import") { out = handleImport (); } - else if (cmd.command == "duplicate") { out = handleDuplicate (); } - else if (cmd.command == "edit") { out = handleEdit (); } + if (cmd.command == "projects") { out = handleProjects (); } + else if (cmd.command == "tags") { out = handleTags (); } + else if (cmd.command == "colors") { out = handleColor (); } + else if (cmd.command == "version") { out = handleVersion (); } + else if (cmd.command == "help") { out = longUsage (); } + else if (cmd.command == "stats") { out = handleReportStats (); } + else if (cmd.command == "info") { out = handleInfo (); } + else if (cmd.command == "history") { out = handleReportHistory (); } + else if (cmd.command == "ghistory") { out = handleReportGHistory (); } + else if (cmd.command == "summary") { out = handleReportSummary (); } + else if (cmd.command == "calendar") { out = handleReportCalendar (); } + else if (cmd.command == "timesheet") { out = handleReportTimesheet (); } + else if (cmd.command == "add") { out = handleAdd (); } + else if (cmd.command == "append") { out = handleAppend (); } + else if (cmd.command == "annotate") { out = handleAnnotate (); } + else if (cmd.command == "done") { out = handleDone (); } + else if (cmd.command == "delete") { out = handleDelete (); } + else if (cmd.command == "start") { out = handleStart (); } + else if (cmd.command == "stop") { out = handleStop (); } + else if (cmd.command == "export") { out = handleExport (); } + else if (cmd.command == "import") { out = handleImport (); } + else if (cmd.command == "duplicate") { out = handleDuplicate (); } + else if (cmd.command == "edit") { out = handleEdit (); } #ifdef FEATURE_SHELL - else if (cmd.command == "shell") { handleShell (); } + else if (cmd.command == "shell") { handleShell (); } #endif - else if (cmd.command == "undo") { handleUndo (); } + else if (cmd.command == "undo") { handleUndo (); } + else if (cmd.command == "_projects") { out = handleCompletionProjects (); } + else if (cmd.command == "_tags") { out = handleCompletionTags (); } else if (cmd.command == "" && - sequence.size ()) { out = handleModify (); } + sequence.size ()) { out = handleModify (); } // Command that display IDs and therefore need TDB::gc first. else if (cmd.command == "next") { if (!inShadow) tdb.gc (); out = handleReportNext (); } diff --git a/src/command.cpp b/src/command.cpp index bfb0dcc3f..d7682b26d 100644 --- a/src/command.cpp +++ b/src/command.cpp @@ -193,6 +193,31 @@ std::string handleProjects () return out.str (); } +//////////////////////////////////////////////////////////////////////////////// +std::string handleCompletionProjects () +{ + std::vector tasks; + context.tdb.lock (context.config.get ("locking", true)); + handleRecurrence (); + Filter filter; + context.tdb.loadPending (tasks, filter); + context.tdb.commit (); + context.tdb.unlock (); + + // Scan all the tasks for their project name, building a map using project + // names as keys. + std::map unique; + foreach (t, tasks) + unique[t->get ("project")] = 0; + + std::stringstream out; + foreach (project, unique) + if (project->first.length ()) + out << project->first << std::endl; + + return out.str (); +} + //////////////////////////////////////////////////////////////////////////////// std::string handleTags () { @@ -260,6 +285,36 @@ std::string handleTags () return out.str (); } +//////////////////////////////////////////////////////////////////////////////// +std::string handleCompletionTags () +{ + std::vector tasks; + context.tdb.lock (context.config.get ("locking", true)); + handleRecurrence (); + Filter filter; + context.tdb.loadPending (tasks, filter); + context.tdb.commit (); + context.tdb.unlock (); + + // Scan all the tasks for their project name, building a map using project + // names as keys. + std::map unique; + foreach (t, tasks) + { + std::vector tags; + t->getTags (tags); + + foreach (tag, tags) + unique[*tag] = 0; + } + + std::stringstream out; + foreach (tag, unique) + out << tag->first << std::endl; + + return out.str (); +} + //////////////////////////////////////////////////////////////////////////////// void handleUndo () { diff --git a/src/main.h b/src/main.h index 9a39ee35b..7993cae51 100644 --- a/src/main.h +++ b/src/main.h @@ -60,7 +60,9 @@ std::string handleExport (); std::string handleDone (); std::string handleModify (); std::string handleProjects (); +std::string handleCompletionProjects (); std::string handleTags (); +std::string handleCompletionTags (); std::string handleVersion (); std::string handleDelete (); std::string handleStart ();