From 49a7e46eafa9f38859d7d9aa448877601f06b9ca Mon Sep 17 00:00:00 2001 From: Tomas Babej Date: Fri, 20 Feb 2015 20:59:22 +0100 Subject: [PATCH 01/21] CmdContext: Add initial plumbing --- src/commands/CMakeLists.txt | 1 + src/commands/CmdContext.cpp | 75 +++++++++++++++++++++++++++++++++++++ src/commands/CmdContext.h | 48 ++++++++++++++++++++++++ src/commands/Command.cpp | 3 ++ src/l10n/eng-USA.h | 2 + 5 files changed, 129 insertions(+) create mode 100644 src/commands/CmdContext.cpp create mode 100644 src/commands/CmdContext.h diff --git a/src/commands/CMakeLists.txt b/src/commands/CMakeLists.txt index 4e0288972..035d0dc14 100644 --- a/src/commands/CMakeLists.txt +++ b/src/commands/CMakeLists.txt @@ -18,6 +18,7 @@ set (commands_SRCS Command.cpp Command.h CmdColor.cpp CmdColor.h CmdColumns.cpp CmdColumns.h CmdConfig.cpp CmdConfig.h + CmdContext.cpp CmdContext.h CmdCount.cpp CmdCount.h CmdCustom.cpp CmdCustom.h CmdDelete.cpp CmdDelete.h diff --git a/src/commands/CmdContext.cpp b/src/commands/CmdContext.cpp new file mode 100644 index 000000000..dc0fe5878 --- /dev/null +++ b/src/commands/CmdContext.cpp @@ -0,0 +1,75 @@ +//////////////////////////////////////////////////////////////////////////////// +// +// Copyright 2006 - 2015, Paul Beckingham, Federico Hernandez. +// +// Permission is hereby granted, free of charge, to any person obtaining a copy +// of this software and associated documentation files (the "Software"), to deal +// in the Software without restriction, including without limitation the rights +// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +// copies of the Software, and to permit persons to whom the Software is +// furnished to do so, subject to the following conditions: +// +// The above copyright notice and this permission notice shall be included +// in all copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS +// OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL +// THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE +// SOFTWARE. +// +// http://www.opensource.org/licenses/mit-license.php +// +//////////////////////////////////////////////////////////////////////////////// + +#include +#include +#include +#include +#include + +extern Context context; + +//////////////////////////////////////////////////////////////////////////////// +CmdContext::CmdContext () +{ + _keyword = "context"; + _usage = "task context [name [value | '']]"; + _description = STRING_CMD_CONTEXT_USAGE; + _read_only = true; + _displays_id = false; +} + +//////////////////////////////////////////////////////////////////////////////// +int CmdContext::execute (std::string& output) +{ + int rc = 0; + std::stringstream out; + + // Get the non-attribute, non-fancy command line arguments. + std::vector words = context.cli.getWords (); + + return rc; +} + +//////////////////////////////////////////////////////////////////////////////// +CmdCompletionContext::CmdCompletionContext () +{ + _keyword = "_context"; + _usage = "task _context"; + _description = STRING_CMD_HCONTEXT_USAGE; + _read_only = true; + _displays_id = false; +} + +//////////////////////////////////////////////////////////////////////////////// +int CmdCompletionContext::execute (std::string& output) +{ + std::vector contexts; + + return 0; +} + +//////////////////////////////////////////////////////////////////////////////// diff --git a/src/commands/CmdContext.h b/src/commands/CmdContext.h new file mode 100644 index 000000000..93c61a65c --- /dev/null +++ b/src/commands/CmdContext.h @@ -0,0 +1,48 @@ +//////////////////////////////////////////////////////////////////////////////// +// +// Copyright 2006 - 2015, Paul Beckingham, Federico Hernandez. +// +// Permission is hereby granted, free of charge, to any person obtaining a copy +// of this software and associated documentation files (the "Software"), to deal +// in the Software without restriction, including without limitation the rights +// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +// copies of the Software, and to permit persons to whom the Software is +// furnished to do so, subject to the following conditions: +// +// The above copyright notice and this permission notice shall be included +// in all copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS +// OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL +// THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE +// SOFTWARE. +// +// http://www.opensource.org/licenses/mit-license.php +// +//////////////////////////////////////////////////////////////////////////////// + +#ifndef INCLUDED_CMDCONTEXT +#define INCLUDED_CMDCONTEXT + +#include +#include + +class CmdContext : public Command +{ +public: + CmdContext (); + int execute (std::string&); +}; + +class CmdCompletionContext : public Command +{ +public: + CmdCompletionContext (); + int execute (std::string&); +}; + +#endif +//////////////////////////////////////////////////////////////////////////////// diff --git a/src/commands/Command.cpp b/src/commands/Command.cpp index f1b8e4220..74619ad38 100644 --- a/src/commands/Command.cpp +++ b/src/commands/Command.cpp @@ -46,6 +46,7 @@ #include #include #include +#include #include #include #include @@ -109,6 +110,7 @@ void Command::factory (std::map & all) c = new CmdCompletionColumns (); all[c->keyword ()] = c; c = new CmdCompletionCommands (); all[c->keyword ()] = c; c = new CmdCompletionConfig (); all[c->keyword ()] = c; + c = new CmdCompletionContext (); all[c->keyword ()] = c; c = new CmdCompletionIds (); all[c->keyword ()] = c; c = new CmdCompletionUDAs (); all[c->keyword ()] = c; c = new CmdCompletionUuids (); all[c->keyword ()] = c; @@ -116,6 +118,7 @@ void Command::factory (std::map & all) c = new CmdCompletionTags (); all[c->keyword ()] = c; c = new CmdCompletionVersion (); all[c->keyword ()] = c; c = new CmdConfig (); all[c->keyword ()] = c; + c = new CmdContext (); all[c->keyword ()] = c; c = new CmdCount (); all[c->keyword ()] = c; c = new CmdDelete (); all[c->keyword ()] = c; c = new CmdDenotate (); all[c->keyword ()] = c; diff --git a/src/l10n/eng-USA.h b/src/l10n/eng-USA.h index a7aee545a..68feb65c7 100644 --- a/src/l10n/eng-USA.h +++ b/src/l10n/eng-USA.h @@ -561,6 +561,8 @@ #define STRING_CMD_CONFIG_NO_CHANGE "No changes made." #define STRING_CMD_CONFIG_NO_NAME "Specify the name of a config variable to modify." #define STRING_CMD_HCONFIG_USAGE "Lists all supported configuration variables, for completion purposes" +#define STRING_CMD_CONTEXT_USAGE "Set and define contexts (default filters)" +#define STRING_CMD_HCONTEXT_USAGE "Lists all supported contexts, for completion purposes" #define STRING_CMD_CUSTOM_MISMATCH "There are different numbers of columns and labels for report '{1}'." #define STRING_CMD_CUSTOM_SHOWN "{1} shown" #define STRING_CMD_CUSTOM_COUNT "1 task" From a4d5ab07e9b5fdf2d0f904c5e2c1a8c6c20aaa0e Mon Sep 17 00:00:00 2001 From: Tomas Babej Date: Sat, 21 Feb 2015 00:47:06 +0100 Subject: [PATCH 02/21] CmdConfig: Abstract saving and removal of the configuration values into separate methods --- src/commands/CmdConfig.cpp | 166 +++++++++++++++++++++++-------------- src/commands/CmdConfig.h | 2 + 2 files changed, 106 insertions(+), 62 deletions(-) diff --git a/src/commands/CmdConfig.cpp b/src/commands/CmdConfig.cpp index 8c605f527..4dd3e9b3b 100644 --- a/src/commands/CmdConfig.cpp +++ b/src/commands/CmdConfig.cpp @@ -46,6 +46,100 @@ CmdConfig::CmdConfig () _displays_id = false; } +//////////////////////////////////////////////////////////////////////////////// +bool CmdConfig::setConfigVariable (std::string name, std::string value, bool confirmation /* = false */) +{ + // Read .taskrc (or equivalent) + std::vector contents; + File::read (context.config._original_file, contents); + + bool found = false; + bool change = false; + + std::vector ::iterator line; + for (line = contents.begin (); line != contents.end (); ++line) + { + // If there is a comment on the line, it must follow the pattern. + std::string::size_type comment = line->find ("#"); + std::string::size_type pos = line->find (name + "="); + + if (pos != std::string::npos && + (comment == std::string::npos || + comment > pos)) + { + found = true; + if (!confirmation || + confirm (format (STRING_CMD_CONFIG_CONFIRM, name, context.config.get (name), value))) + { + if (comment != std::string::npos) + *line = name + "=" + json::encode (value) + " " + line->substr (comment); + else + *line = name + "=" + json::encode (value); + + change = true; + } + } + } + + // Not found, so append instead. + if (!found && + (!confirmation || + confirm (format (STRING_CMD_CONFIG_CONFIRM2, name, value)))) + { + contents.push_back (name + "=" + json::encode (value)); + change = true; + } + + if (change) + File::write (context.config._original_file, contents); + + return change; +} + +//////////////////////////////////////////////////////////////////////////////// +int CmdConfig::unsetConfigVariable (std::string name, bool confirmation /* = false */) +{ + // Read .taskrc (or equivalent) + std::vector contents; + File::read (context.config._original_file, contents); + + bool found = false; + bool change = false; + + std::vector ::iterator line; + for (line = contents.begin (); line != contents.end (); ++line) + { + // If there is a comment on the line, it must follow the pattern. + std::string::size_type comment = line->find ("#"); + std::string::size_type pos = line->find (name + "="); + + if (pos != std::string::npos && + (comment == std::string::npos || + comment > pos)) + { + found = true; + + // Remove name + if (!confirmation || + confirm (format (STRING_CMD_CONFIG_CONFIRM3, name))) + { + *line = ""; + change = true; + } + } + } + + if (change) + File::write (context.config._original_file, contents); + + if ( change && found ) + return 0; + else if ( found ) + return 1; + else + return 2; +} + //////////////////////////////////////////////////////////////////////////////// int CmdConfig::execute (std::string& output) { @@ -63,6 +157,9 @@ int CmdConfig::execute (std::string& output) { bool confirmation = context.config.getBoolean ("confirmation"); + bool change = false; + bool found = false; + std::string name = words[0]; std::string value = ""; @@ -81,76 +178,22 @@ int CmdConfig::execute (std::string& output) { bool change = false; - // Read .taskrc (or equivalent) - std::vector contents; - File::read (context.config._original_file, contents); - // task config name value // task config name "" if (words.size () > 1) - { - bool found = false; - std::vector ::iterator line; - for (line = contents.begin (); line != contents.end (); ++line) - { - // If there is a comment on the line, it must follow the pattern. - std::string::size_type comment = line->find ("#"); - std::string::size_type pos = line->find (name + "="); - - if (pos != std::string::npos && - (comment == std::string::npos || - comment > pos)) - { - found = true; - if (!confirmation || - confirm (format (STRING_CMD_CONFIG_CONFIRM, name, context.config.get (name), value))) - { - if (comment != std::string::npos) - *line = name + "=" + json::encode (value) + " " + line->substr (comment); - else - *line = name + "=" + json::encode (value); - - change = true; - } - } - } - - // Not found, so append instead. - if (!found && - (!confirmation || - confirm (format (STRING_CMD_CONFIG_CONFIRM2, name, value)))) - { - contents.push_back (name + "=" + json::encode (value)); - change = true; - } - } + change = setConfigVariable(name, value, confirmation); // task config name else { - bool found = false; - std::vector ::iterator line; - for (line = contents.begin (); line != contents.end (); ++line) + rc = unsetConfigVariable(name, confirmation); + if (rc == 0) { - // If there is a comment on the line, it must follow the pattern. - std::string::size_type comment = line->find ("#"); - std::string::size_type pos = line->find (name + "="); - - if (pos != std::string::npos && - (comment == std::string::npos || - comment > pos)) - { - found = true; - - // Remove name - if (!confirmation || - confirm (format (STRING_CMD_CONFIG_CONFIRM3, name))) - { - *line = ""; - change = true; - } - } + change = true; + found = true; } + else if (rc == 1) + found = true; if (!found) throw format (STRING_CMD_CONFIG_NO_ENTRY, name); @@ -159,7 +202,6 @@ int CmdConfig::execute (std::string& output) // Write .taskrc (or equivalent) if (change) { - File::write (context.config._original_file, contents); out << format (STRING_CMD_CONFIG_FILE_MOD, context.config._original_file._data) << "\n"; diff --git a/src/commands/CmdConfig.h b/src/commands/CmdConfig.h index 782cfaca1..f3f7e8263 100644 --- a/src/commands/CmdConfig.h +++ b/src/commands/CmdConfig.h @@ -34,6 +34,8 @@ class CmdConfig : public Command { public: CmdConfig (); + static bool setConfigVariable (std::string name, std::string value, bool confirmation = false); + static int unsetConfigVariable (std::string name, bool confirmation = false); int execute (std::string&); }; From e91063426a4ccb72c1764797f22b2d655f320366 Mon Sep 17 00:00:00 2001 From: Tomas Babej Date: Sat, 21 Feb 2015 12:17:10 +0100 Subject: [PATCH 03/21] CmdContext: Add initial implementation of the context define subcommand --- src/commands/CmdContext.cpp | 57 +++++++++++++++++++++++++++++++++++++ src/commands/CmdContext.h | 2 ++ 2 files changed, 59 insertions(+) diff --git a/src/commands/CmdContext.cpp b/src/commands/CmdContext.cpp index dc0fe5878..95c4bcfa9 100644 --- a/src/commands/CmdContext.cpp +++ b/src/commands/CmdContext.cpp @@ -29,6 +29,7 @@ #include #include #include +#include extern Context context; @@ -51,9 +52,65 @@ int CmdContext::execute (std::string& output) // Get the non-attribute, non-fancy command line arguments. std::vector words = context.cli.getWords (); + if (words.size () > 0) + { + std::string subcommand = words[0]; + if (subcommand == "define") + rc = defineContext(words, out); + } + + output = out.str (); return rc; } +//////////////////////////////////////////////////////////////////////////////// +// Joins all the words in the specified interval & words, unsigned int from, unsigned int to /* = 0 */) +{ + std::string value = ""; + + if (to == 0) + to = words.size(); + + for (unsigned int i = from; i < to; ++i) + { + if (i > from) + value += " "; + + value += words[i]; + } + + return value; +} + +//////////////////////////////////////////////////////////////////////////////// +int CmdContext::defineContext (std::vector & words, std::stringstream& out) +{ + // task context define home project:Home + if (words.size () > 2) + { + std::string name = "context." + words[1]; + std::string value = joinWords(words, 2); + // TODO: Check if the value is a proper filter + + bool confirmation = context.config.getBoolean ("confirmation"); + bool success = CmdConfig::setConfigVariable(name, value, confirmation); + + if (success) + out << "Context '" << words[1] << "' successfully defined." << "\n"; + else + out << "Context '" << words[1] << "' was not defined." << "\n"; + } + else + throw "You have to specify both context name and definition."; + + return 0; +} + //////////////////////////////////////////////////////////////////////////////// CmdCompletionContext::CmdCompletionContext () { diff --git a/src/commands/CmdContext.h b/src/commands/CmdContext.h index 93c61a65c..63f1b2847 100644 --- a/src/commands/CmdContext.h +++ b/src/commands/CmdContext.h @@ -35,6 +35,8 @@ class CmdContext : public Command public: CmdContext (); int execute (std::string&); + std::string joinWords (std::vector & words, unsigned int from, unsigned int to = 0); + int defineContext (std::vector & words, std::stringstream& out); }; class CmdCompletionContext : public Command From 636f6bfd960f16324277e4f61f8e18a2b4cbdc80 Mon Sep 17 00:00:00 2001 From: Tomas Babej Date: Sat, 21 Feb 2015 12:36:06 +0100 Subject: [PATCH 04/21] CmdContext: Add initial implementation of the delete subcommand --- src/commands/CmdContext.cpp | 30 ++++++++++++++++++++++++++++++ src/commands/CmdContext.h | 1 + 2 files changed, 31 insertions(+) diff --git a/src/commands/CmdContext.cpp b/src/commands/CmdContext.cpp index 95c4bcfa9..0ac4dc09f 100644 --- a/src/commands/CmdContext.cpp +++ b/src/commands/CmdContext.cpp @@ -55,8 +55,11 @@ int CmdContext::execute (std::string& output) if (words.size () > 0) { std::string subcommand = words[0]; + if (subcommand == "define") rc = defineContext(words, out); + else if (subcommand == "delete") + rc = deleteContext(words, out); } output = out.str (); @@ -111,6 +114,33 @@ int CmdContext::defineContext (std::vector & words, std::stringstre return 0; } +//////////////////////////////////////////////////////////////////////////////// +int CmdContext::deleteContext (std::vector & words, std::stringstream& out) +{ + // task context delete home + if (words.size () > 1) + { + std::string name = "context." + words[1]; + + bool confirmation = context.config.getBoolean ("confirmation"); + int status = CmdConfig::unsetConfigVariable(name, confirmation); + + std::string currentContext = context.config.get ("context"); + + if (currentContext == words[1]) + CmdConfig::unsetConfigVariable("context", false); + + if (status == 0) + out << "Context '" << words[1] << "' successfully undefined." << "\n"; + else + out << "Context '" << words[1] << "' was not undefined." << "\n"; + } + else + throw "You have to specify context name."; + + return 0; +} + //////////////////////////////////////////////////////////////////////////////// CmdCompletionContext::CmdCompletionContext () { diff --git a/src/commands/CmdContext.h b/src/commands/CmdContext.h index 63f1b2847..9a9c01d6a 100644 --- a/src/commands/CmdContext.h +++ b/src/commands/CmdContext.h @@ -37,6 +37,7 @@ public: int execute (std::string&); std::string joinWords (std::vector & words, unsigned int from, unsigned int to = 0); int defineContext (std::vector & words, std::stringstream& out); + int deleteContext (std::vector & words, std::stringstream& out); }; class CmdCompletionContext : public Command From edb54a51b3fbbcaca96039844b0b2cfcc097fe8f Mon Sep 17 00:00:00 2001 From: Tomas Babej Date: Sat, 21 Feb 2015 13:38:53 +0100 Subject: [PATCH 05/21] CmdContext: Add list subcommand --- src/commands/CmdContext.cpp | 66 +++++++++++++++++++++++++++++++++++++ src/commands/CmdContext.h | 2 ++ 2 files changed, 68 insertions(+) diff --git a/src/commands/CmdContext.cpp b/src/commands/CmdContext.cpp index 0ac4dc09f..9813f0196 100644 --- a/src/commands/CmdContext.cpp +++ b/src/commands/CmdContext.cpp @@ -27,7 +27,9 @@ #include #include #include +#include #include +#include #include #include @@ -60,6 +62,8 @@ int CmdContext::execute (std::string& output) rc = defineContext(words, out); else if (subcommand == "delete") rc = deleteContext(words, out); + else if (subcommand == "list") + rc = listContexts(words, out); } output = out.str (); @@ -90,6 +94,25 @@ std::string CmdContext::joinWords (std::vector & words, unsigned in return value; } +//////////////////////////////////////////////////////////////////////////////// +// Returns all user defined contexts. +// +std::vector CmdContext::getContexts () +{ + std::vector contexts; + + Config::const_iterator name; + for (name = context.config.begin (); name != context.config.end (); ++name) + { + if (name->first.substr (0, 8) == "context.") + { + contexts.push_back (name->first.substr (8)); + } + } + + return contexts; +} + //////////////////////////////////////////////////////////////////////////////// int CmdContext::defineContext (std::vector & words, std::stringstream& out) { @@ -141,6 +164,49 @@ int CmdContext::deleteContext (std::vector & words, std::stringstre return 0; } +//////////////////////////////////////////////////////////////////////////////// +int CmdContext::listContexts (std::vector & words, std::stringstream& out) +{ + int rc = 0; + std::vector contexts = getContexts(); + + if (contexts.size ()) + { + std::sort (contexts.begin (), contexts.end ()); + + // Render a list of UDA name, type, label, allowed values, + // possible default value, and finally the usage count. + ViewText view; + view.width (context.getWidth ()); + view.add (Column::factory ("string", "Name")); + view.add (Column::factory ("string", "Definition")); + + Color label (context.config.get ("color.label")); + view.colorHeader (label); + + std::vector ::iterator userContext; + for (userContext = contexts.begin (); userContext != contexts.end (); ++userContext) + { + std::string definition = context.config.get ("context." + *userContext); + + int row = view.addRow (); + view.set (row, 0, *userContext); + view.set (row, 1, definition); + } + + out << optionalBlankLine () + << view.render () + << optionalBlankLine (); + } + else + { + out << "No contexts defined." << "\n"; + rc = 1; + } + + return rc; +} + //////////////////////////////////////////////////////////////////////////////// CmdCompletionContext::CmdCompletionContext () { diff --git a/src/commands/CmdContext.h b/src/commands/CmdContext.h index 9a9c01d6a..41ff887cf 100644 --- a/src/commands/CmdContext.h +++ b/src/commands/CmdContext.h @@ -36,8 +36,10 @@ public: CmdContext (); int execute (std::string&); std::string joinWords (std::vector & words, unsigned int from, unsigned int to = 0); + std::vector getContexts (); int defineContext (std::vector & words, std::stringstream& out); int deleteContext (std::vector & words, std::stringstream& out); + int listContexts (std::vector & words, std::stringstream& out); }; class CmdCompletionContext : public Command From 5f3dd43893130100e42ac47d32cc21be799b7212 Mon Sep 17 00:00:00 2001 From: Tomas Babej Date: Sat, 21 Feb 2015 13:48:26 +0100 Subject: [PATCH 06/21] CmdCompletionContext: Implement the context completion list --- src/commands/CmdContext.cpp | 6 +++++- src/commands/CmdContext.h | 2 +- 2 files changed, 6 insertions(+), 2 deletions(-) diff --git a/src/commands/CmdContext.cpp b/src/commands/CmdContext.cpp index 9813f0196..55866419b 100644 --- a/src/commands/CmdContext.cpp +++ b/src/commands/CmdContext.cpp @@ -220,7 +220,11 @@ CmdCompletionContext::CmdCompletionContext () //////////////////////////////////////////////////////////////////////////////// int CmdCompletionContext::execute (std::string& output) { - std::vector contexts; + std::vector userContexts = CmdContext::getContexts (); + + std::vector ::iterator userContext; + for (userContext = userContexts.begin (); userContext != userContexts.end (); ++userContext) + output += *userContext + "\n"; return 0; } diff --git a/src/commands/CmdContext.h b/src/commands/CmdContext.h index 41ff887cf..d13745416 100644 --- a/src/commands/CmdContext.h +++ b/src/commands/CmdContext.h @@ -36,7 +36,7 @@ public: CmdContext (); int execute (std::string&); std::string joinWords (std::vector & words, unsigned int from, unsigned int to = 0); - std::vector getContexts (); + static std::vector getContexts (); int defineContext (std::vector & words, std::stringstream& out); int deleteContext (std::vector & words, std::stringstream& out); int listContexts (std::vector & words, std::stringstream& out); From 371ca27da0a8516c9aa0f160df5e71caff3f2455 Mon Sep 17 00:00:00 2001 From: Tomas Babej Date: Sat, 21 Feb 2015 18:04:08 +0100 Subject: [PATCH 07/21] CmdContext: Add support for setting context --- src/commands/CmdContext.cpp | 22 ++++++++++++++++++++++ src/commands/CmdContext.h | 1 + 2 files changed, 23 insertions(+) diff --git a/src/commands/CmdContext.cpp b/src/commands/CmdContext.cpp index 55866419b..367cc6eff 100644 --- a/src/commands/CmdContext.cpp +++ b/src/commands/CmdContext.cpp @@ -64,6 +64,8 @@ int CmdContext::execute (std::string& output) rc = deleteContext(words, out); else if (subcommand == "list") rc = listContexts(words, out); + else + rc = setContext(words, out); } output = out.str (); @@ -207,6 +209,26 @@ int CmdContext::listContexts (std::vector & words, std::stringstrea return rc; } +//////////////////////////////////////////////////////////////////////////////// +int CmdContext::setContext (std::vector & words, std::stringstream& out) +{ + // task context home + std::string value = words[0]; + std::vector contexts = getContexts (); + + if (std::find (contexts.begin (), contexts.end (), value) == contexts.end()) + throw format ("Context '{1}' not found.", value); + + bool success = CmdConfig::setConfigVariable("context", value, false); + + if (success) + out << "Context '" << value << "' applied." << "\n"; + else + out << "Context '" << value << "' was not applied." << "\n"; + + return 0; +} + //////////////////////////////////////////////////////////////////////////////// CmdCompletionContext::CmdCompletionContext () { diff --git a/src/commands/CmdContext.h b/src/commands/CmdContext.h index d13745416..c1ef7ab8f 100644 --- a/src/commands/CmdContext.h +++ b/src/commands/CmdContext.h @@ -40,6 +40,7 @@ public: int defineContext (std::vector & words, std::stringstream& out); int deleteContext (std::vector & words, std::stringstream& out); int listContexts (std::vector & words, std::stringstream& out); + int setContext (std::vector & words, std::stringstream& out); }; class CmdCompletionContext : public Command From 1a3550541bbd8cd0e5a61ef1d0ea7e7f20ef81f3 Mon Sep 17 00:00:00 2001 From: Tomas Babej Date: Sat, 21 Feb 2015 18:28:49 +0100 Subject: [PATCH 08/21] CmdContext: Add support for unsetting context --- src/commands/CmdContext.cpp | 20 ++++++++++++++++++++ src/commands/CmdContext.h | 1 + 2 files changed, 21 insertions(+) diff --git a/src/commands/CmdContext.cpp b/src/commands/CmdContext.cpp index 367cc6eff..1d533d3c0 100644 --- a/src/commands/CmdContext.cpp +++ b/src/commands/CmdContext.cpp @@ -64,6 +64,8 @@ int CmdContext::execute (std::string& output) rc = deleteContext(words, out); else if (subcommand == "list") rc = listContexts(words, out); + else if (subcommand == "none") + rc = unsetContext(words, out); else rc = setContext(words, out); } @@ -229,6 +231,24 @@ int CmdContext::setContext (std::vector & words, std::stringstream& return 0; } +//////////////////////////////////////////////////////////////////////////////// +int CmdContext::unsetContext (std::vector & words, std::stringstream& out) +{ + // task context none + int rc = 0; + int status = CmdConfig::unsetConfigVariable("context", false); + + if (status == 0) + out << "Context unset." << "\n"; + else + { + out << "Context not unset." << "\n"; + rc = 1; + } + + return rc; +} + //////////////////////////////////////////////////////////////////////////////// CmdCompletionContext::CmdCompletionContext () { diff --git a/src/commands/CmdContext.h b/src/commands/CmdContext.h index c1ef7ab8f..2a35029b8 100644 --- a/src/commands/CmdContext.h +++ b/src/commands/CmdContext.h @@ -41,6 +41,7 @@ public: int deleteContext (std::vector & words, std::stringstream& out); int listContexts (std::vector & words, std::stringstream& out); int setContext (std::vector & words, std::stringstream& out); + int unsetContext (std::vector & words, std::stringstream& out); }; class CmdCompletionContext : public Command From 09fe5be0862d6ad9e2ab1e77e7a420d5f26d583c Mon Sep 17 00:00:00 2001 From: Tomas Babej Date: Sat, 21 Feb 2015 18:50:14 +0100 Subject: [PATCH 09/21] CmdContext: Add show subcommand --- src/commands/CmdContext.cpp | 19 +++++++++++++++++++ src/commands/CmdContext.h | 1 + 2 files changed, 20 insertions(+) diff --git a/src/commands/CmdContext.cpp b/src/commands/CmdContext.cpp index 1d533d3c0..5378c51d2 100644 --- a/src/commands/CmdContext.cpp +++ b/src/commands/CmdContext.cpp @@ -66,6 +66,8 @@ int CmdContext::execute (std::string& output) rc = listContexts(words, out); else if (subcommand == "none") rc = unsetContext(words, out); + else if (subcommand == "show") + rc = showContext(words, out); else rc = setContext(words, out); } @@ -231,6 +233,23 @@ int CmdContext::setContext (std::vector & words, std::stringstream& return 0; } +//////////////////////////////////////////////////////////////////////////////// +int CmdContext::showContext (std::vector & words, std::stringstream& out) +{ + // task context show + std::string currentContext = context.config.get ("context"); + + if (currentContext == "") + out << "No context is currently applied." << "\n"; + else + { + std::string currentFilter = context.config.get ("context." + currentContext); + out << format ("Context '{1}' with filter '{2}' is currently applied.", currentContext, currentFilter) << "\n"; + } + + return 0; +} + //////////////////////////////////////////////////////////////////////////////// int CmdContext::unsetContext (std::vector & words, std::stringstream& out) { diff --git a/src/commands/CmdContext.h b/src/commands/CmdContext.h index 2a35029b8..4d0f1df75 100644 --- a/src/commands/CmdContext.h +++ b/src/commands/CmdContext.h @@ -41,6 +41,7 @@ public: int deleteContext (std::vector & words, std::stringstream& out); int listContexts (std::vector & words, std::stringstream& out); int setContext (std::vector & words, std::stringstream& out); + int showContext (std::vector & words, std::stringstream& out); int unsetContext (std::vector & words, std::stringstream& out); }; From 1abda3900bb12893c2f0fef5ffb2102e8689b7ff Mon Sep 17 00:00:00 2001 From: Tomas Babej Date: Sat, 21 Feb 2015 15:45:38 +0100 Subject: [PATCH 10/21] CLI: Abstract adding new raw filter to a new helper method --- src/CLI.cpp | 14 ++++++++++++++ src/CLI.h | 1 + src/commands/CmdCustom.cpp | 10 +--------- 3 files changed, 16 insertions(+), 9 deletions(-) diff --git a/src/CLI.cpp b/src/CLI.cpp index 5177a0e7c..240a07d9f 100644 --- a/src/CLI.cpp +++ b/src/CLI.cpp @@ -374,6 +374,20 @@ void CLI::add (const std::string& arg) analyze (); } +//////////////////////////////////////////////////////////////////////////////// +// Process raw string into parsed filter. +// +void CLI::addRawFilter (const std::string& arg) +{ + std::string lexeme; + Lexer::Type type; + Lexer lex (arg); + lex.ambiguity (false); + + while (lex.token (lexeme, type)) + add (lexeme); +} + //////////////////////////////////////////////////////////////////////////////// // Intended to be called after ::initialize() and ::add(), to perform the final // analysis. Analysis is also performed directly after the above, because there diff --git a/src/CLI.h b/src/CLI.h index e855db697..c3aa0e692 100644 --- a/src/CLI.h +++ b/src/CLI.h @@ -77,6 +77,7 @@ public: void entity (const std::string&, const std::string&); void initialize (int, const char**); void add (const std::string&); + void addRawFilter (const std::string& arg); void analyze (bool parse = true, bool strict = false); void applyOverrides (); const std::string getFilter (); diff --git a/src/commands/CmdCustom.cpp b/src/commands/CmdCustom.cpp index 1876dab23..4bcd46e4c 100644 --- a/src/commands/CmdCustom.cpp +++ b/src/commands/CmdCustom.cpp @@ -82,15 +82,7 @@ int CmdCustom::execute (std::string& output) validateSortColumns (sortOrder); // Prepend the argument list with those from the report filter. - std::string lexeme; - Lexer::Type type; - Lexer lex (reportFilter); - lex.ambiguity (false); - while (lex.token (lexeme, type)) - context.cli.add (lexeme); - - // Reparse after tree change. - context.cli.analyze (); + context.cli.addRawFilter(reportFilter); // Apply filter. handleRecurrence (); From 3a77a5f291ca21a4111a0549a9431fae3256c2bf Mon Sep 17 00:00:00 2001 From: Tomas Babej Date: Sat, 21 Feb 2015 16:23:45 +0100 Subject: [PATCH 11/21] CLI: Add addContextFilter method --- src/CLI.cpp | 44 ++++++++++++++++++++++++++++++++++++++++++++ src/CLI.h | 1 + 2 files changed, 45 insertions(+) diff --git a/src/CLI.cpp b/src/CLI.cpp index 240a07d9f..afcbefdb1 100644 --- a/src/CLI.cpp +++ b/src/CLI.cpp @@ -374,6 +374,50 @@ void CLI::add (const std::string& arg) analyze (); } +//////////////////////////////////////////////////////////////////////////////// +void CLI::addContextFilter () +{ + // Detect if any context is set, and bail out if not + std::string contextName = context.config.get ("context"); + + if (contextName == "") + { + context.debug("No context applied."); + return; + } + + // Detect if UUID or ID is set, and bail out + if (_args.size ()) + { + std::vector ::const_iterator a; + for (a = _args.begin (); a != _args.end (); ++a) + { + if (a->hasTag ("FILTER") && + a->hasTag ("ATTRIBUTE") && + ! a->hasTag ("TERMINATED") && + ! a->hasTag ("WORD") && + (a->attribute ("raw") == "id" || a->attribute ("raw") == "uuid")) + { + context.debug(format("UUID/ID lexeme found '{1}', not applying context.", a->attribute ("raw"))); + return; + } + } + } + + // Apply context + context.debug("Applying context: " + contextName); + std::string contextFilter = context.config.get ("context." + contextName); + + if (contextFilter == "") + context.debug("Context '" + contextName + "' not defined!"); + else + { + addRawFilter("( " + contextFilter + " )"); + if (context.verbose ("context")) + context.footnote (format("Context '{1}' applied.", contextName)); + } +} + //////////////////////////////////////////////////////////////////////////////// // Process raw string into parsed filter. // diff --git a/src/CLI.h b/src/CLI.h index c3aa0e692..634e05a91 100644 --- a/src/CLI.h +++ b/src/CLI.h @@ -77,6 +77,7 @@ public: void entity (const std::string&, const std::string&); void initialize (int, const char**); void add (const std::string&); + void addContextFilter (); void addRawFilter (const std::string& arg); void analyze (bool parse = true, bool strict = false); void applyOverrides (); From dc0502dd9fdfe789895fb0f8c456c25c477c16d7 Mon Sep 17 00:00:00 2001 From: Tomas Babej Date: Sat, 21 Feb 2015 14:35:19 +0100 Subject: [PATCH 12/21] CLI: Apply context filter directly in getFilter method --- src/CLI.cpp | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/src/CLI.cpp b/src/CLI.cpp index afcbefdb1..e059ea3a2 100644 --- a/src/CLI.cpp +++ b/src/CLI.cpp @@ -534,6 +534,9 @@ void CLI::applyOverrides () // Extract all the FILTER-tagged items. const std::string CLI::getFilter () { + // Handle context setting + addContextFilter (); + std::string filter = ""; if (_args.size ()) { @@ -558,6 +561,7 @@ const std::string CLI::getFilter () filter = "( " + filter + " )"; } + context.debug("Derived filter: '" + filter + "'"); return filter; } From 75cf742a55e18b08025301723b059a94deab3f95 Mon Sep 17 00:00:00 2001 From: Tomas Babej Date: Sun, 22 Feb 2015 15:04:22 +0100 Subject: [PATCH 13/21] tests: Add taskrc_content property --- test/basetest/task.py | 9 +++++++++ 1 file changed, 9 insertions(+) diff --git a/test/basetest/task.py b/test/basetest/task.py index a6921f7e3..73c0d2af0 100644 --- a/test/basetest/task.py +++ b/test/basetest/task.py @@ -137,6 +137,15 @@ class Task(object): cmd = (self.taskw, "config", "--", var, value) return run_cmd_wait(cmd, env=self.env) + @property + def taskrc_content(self): + """ + Returns the contents of the taskrc file. + """ + + with open(self.taskrc, "r") as f: + return f.readlines() + def runSuccess(self, args=(), input=None, merge_streams=False, timeout=1): """Invoke task with given arguments and fail if exit code != 0 From c2a9bb65e6ec05c9908dc4cfe57201a09ef82bc6 Mon Sep 17 00:00:00 2001 From: Tomas Babej Date: Sun, 22 Feb 2015 15:05:23 +0100 Subject: [PATCH 14/21] tests: Add tests for the context feature --- test/context.t | 474 +++++++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 474 insertions(+) create mode 100755 test/context.t diff --git a/test/context.t b/test/context.t new file mode 100755 index 000000000..05a4c9362 --- /dev/null +++ b/test/context.t @@ -0,0 +1,474 @@ +#!/usr/bin/env python2.7 +# -*- coding: utf-8 -*- +################################################################################ +## +## Copyright 2006 - 2015, Paul Beckingham, Federico Hernandez. +## +## Permission is hereby granted, free of charge, to any person obtaining a copy +## of this software and associated documentation files (the "Software"), to deal +## in the Software without restriction, including without limitation the rights +## to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +## copies of the Software, and to permit persons to whom the Software is +## furnished to do so, subject to the following conditions: +## +## The above copyright notice and this permission notice shall be included +## in all copies or substantial portions of the Software. +## +## THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS +## OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +## FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL +## THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +## LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +## OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE +## SOFTWARE. +## +## http://www.opensource.org/licenses/mit-license.php +## +################################################################################ + +import sys +import os +import unittest +import re + +# Ensure python finds the local simpletap module +sys.path.append(os.path.dirname(os.path.abspath(__file__))) + +from basetest import Task, TestCase + +class ContextManagementTest(TestCase): + def setUp(self): + self.t = Task() + + def test_context_define(self): + """ + Test simple context definition. + """ + + output = self.t(('context', 'define', 'work', 'project:Work'))[1] + + # Assert successful output + self.assertIn("Context 'work' successfully defined.", output) + + # Assert the config contains context definition + self.assertIn('context.work=project:Work\n', self.t.taskrc_content) + + # Assert that it contains the definition only once + is_context_line = lambda x: x == 'context.work=project:Work\n' + self.assertEqual(len(filter(is_context_line, self.t.taskrc_content)), 1) + + def test_context_redefine_same_definition(self): + """ + Test re-defining the context with the same definition. + """ + + self.t(('context', 'define', 'work', 'project:Work'))[1] + output = self.t(('context', 'define', 'work', 'project:Work'))[1] + + # Assert successful output + self.assertIn("Context 'work' successfully defined.", output) + + # Assert the config contains context definition + self.assertIn('context.work=project:Work\n', self.t.taskrc_content) + + # Assert that it contains the definition only once + is_context_line = lambda x: x == 'context.work=project:Work\n' + self.assertEqual(len(filter(is_context_line, self.t.taskrc_content)), 1) + + def test_context_redefine_different_definition(self): + """ + Test re-defining the context with different definition. + """ + + self.t(('context', 'define', 'work', 'project:Work'))[1] + output = self.t(('context', 'define', 'work', '+work'))[1] + + # Assert successful output + self.assertIn("Context 'work' successfully defined.", output) + + # Assert the config does not contain the old context definition + self.assertNotIn('context.work=project:Work\n', self.t.taskrc_content) + + # Assert the config contains context definition + self.assertIn('context.work=+work\n', self.t.taskrc_content) + + # Assert that it contains the definition only once + is_context_line = lambda x: x == 'context.work=+work\n' + self.assertEqual(len(filter(is_context_line, self.t.taskrc_content)), 1) + + def test_context_delete(self): + """ + Test simple context deletion. + """ + + self.t(('context', 'define', 'work', 'project:Work')) + output = self.t(('context', 'delete', 'work'))[1] + + # Assert correct output + self.assertIn("Context 'work' successfully undefined.", output) + + # Assert that taskrc does not countain context work definition + self.assertFalse(any('context.work=' in line for line in self.t.taskrc_content)) + + def test_context_delete_undefined(self): + """ + Test deletion of undefined context. + """ + + output = self.t(('context', 'delete', 'work'))[1] + + # Assert correct output + self.assertIn("Context 'work' was not undefined.", output) + + # Assert that taskrc does not countain context work definition + self.assertFalse(any('context.work=' in line for line in self.t.taskrc_content)) + + def test_context_delete_unset_after_removal(self): + """ + Test that context is unset if its definition has been removed. + """ + + self.t(('context', 'define', 'work', 'project:Work')) + self.t(('context', 'work')) + output = self.t(('context', 'delete', 'work'))[1] + + # Assert correct output + self.assertIn("Context 'work' successfully undefined.", output) + + # Assert that taskrc does not countain context work definition + self.assertFalse(any('context.work=' in line for line in self.t.taskrc_content)) + + # Aseert that the context is not set + output = self.t(('context', 'show'))[1] + self.assertIn('No context is currently applied.', output) + self.assertFalse(any(re.search("^context=", line) for line in self.t.taskrc_content)) + + def test_context_list(self): + """ + Test the 'context list' command. + """ + + self.t(('context', 'define', 'work', 'project:Work'))[1] + self.t(('context', 'define', 'home', '+home'))[1] + + output = self.t(('context', 'list'))[1] + + contains_work = lambda line: 'work' in line and 'project:Work' in line + contains_home = lambda line: 'home' in line and '+home' in line + + # Assert that output contains work and home context definitions exactly + # once + self.assertEqual(len(filter(contains_work, output.splitlines())), 1) + self.assertEqual(len(filter(contains_home, output.splitlines())), 1) + + def test_context_initially_empty(self): + """ + Test that no context is set initially. + """ + + self.t(('context', 'define', 'work', 'project:Work'))[1] + self.t(('context', 'define', 'home', '+home'))[1] + + output = self.t(('context', 'show'))[1] + self.assertIn('No context is currently applied.', output) + self.assertFalse(any(re.search("^context=", line) for line in self.t.taskrc_content)) + + def test_context_setting(self): + """ + Test simple context setting. + """ + + self.t(('context', 'define', 'work', 'project:Work'))[1] + self.t(('context', 'define', 'home', '+home'))[1] + + output = self.t(('context', 'home'))[1] + self.assertIn("Context 'home' applied.", output) + self.assertIn("context=home\n", self.t.taskrc_content) + + def test_context_resetting(self): + """ + Test resetting the same context. + """ + + self.t(('context', 'define', 'work', 'project:Work'))[1] + self.t(('context', 'define', 'home', '+home'))[1] + + self.t(('context', 'home'))[1] + output = self.t(('context', 'home'))[1] + self.assertIn("Context 'home' applied.", output) + + contains_home = lambda line: line == "context=home\n" + self.assertEqual(len(filter(contains_home, self.t.taskrc_content)), 1) + + def test_context_switching(self): + """ + Test changing the context. + """ + + self.t(('context', 'define', 'work', 'project:Work'))[1] + self.t(('context', 'define', 'home', '+home'))[1] + + contains_home = lambda line: line == "context=home\n" + contains_work = lambda line: line == "context=work\n" + + # Switch to home context + output = self.t(('context', 'home'))[1] + self.assertIn("Context 'home' applied.", output) + + self.assertEqual(len(filter(contains_home, self.t.taskrc_content)), 1) + + # Switch to work context + output = self.t(('context', 'work'))[1] + self.assertIn("Context 'work' applied.", output) + + self.assertNotIn("context=home\n", self.t.taskrc_content) + self.assertEqual(len(filter(contains_work, self.t.taskrc_content)), 1) + + # Switch back to home context + output = self.t(('context', 'home'))[1] + self.assertIn("Context 'home' applied.", output) + + self.assertNotIn("context=work\n", self.t.taskrc_content) + self.assertEqual(len(filter(contains_home, self.t.taskrc_content)), 1) + + def test_context_unsetting(self): + """ + Test removing the context. + """ + + self.t(('context', 'define', 'work', 'project:Work'))[1] + self.t(('context', 'define', 'home', '+home'))[1] + + self.t(('context', 'home')) + output = self.t(('context', 'none'))[1] + + # Assert expected output. + self.assertIn("Context unset.", output) + + # Assert no context definition in the taskrc + contains_any_context = lambda line: re.match('^context=', line) + self.assertFalse(any(contains_any_context(line) for line in self.t.taskrc_content)) + + # Assert no context showing up using show subcommand + output = self.t(('context', 'show'))[1] + self.assertIn("No context is currently applied.", output) + + def test_context_unsetting_after_switching(self): + """ + Test unsetting the context after changing the context around. + """ + + self.t(('context', 'define', 'work', 'project:Work'))[1] + self.t(('context', 'define', 'home', '+home'))[1] + + # Switch to contexts around + self.t(('context', 'home')) + self.t(('context', 'work')) + self.t(('context', 'home')) + + # Unset the context + output = self.t(('context', 'none'))[1] + + # Assert expected output. + self.assertIn("Context unset.", output) + + # Assert no context definition in the taskrc + contains_any_context = lambda line: re.match('^context=', line) + self.assertFalse(any(contains_any_context(line) for line in self.t.taskrc_content)) + + # Assert no context showing up using show subcommand + output = self.t(('context', 'show'))[1] + self.assertIn("No context is currently applied.", output) + + def test_context_unsetting_with_no_context_set(self): + """ + Test removing the context when no context is set. + """ + + self.t(('context', 'define', 'work', 'project:Work')) + self.t(('context', 'define', 'home', '+home')) + + output = self.t.runError(('context', 'none'))[1] + + # Assert expected output. + self.assertIn("Context not unset.", output) + + # Assert no context definition in the taskrc + contains_any_context = lambda line: re.match('^context=', line) + self.assertFalse(any(contains_any_context(line) for line in self.t.taskrc_content)) + + # Assert no context showing up using show subcommand + output = self.t(('context', 'show'))[1] + self.assertIn("No context is currently applied.", output) + + def test_context_completion(self): + """ + Test the _context command. + """ + + self.t(('context', 'define', 'work', 'project:Work')) + self.t(('context', 'define', 'home', '+home')) + + output = self.t(('_context',))[1] + + # Assert expected output. + self.assertIn("work", output.splitlines()) + self.assertIn("home", output.splitlines()) + self.assertEqual(len(output.splitlines()), 2) + + def test_context_completion(self): + """ + Test the _context command with some context set. + """ + + self.t(('context', 'define', 'work', 'project:Work')) + self.t(('context', 'define', 'home', '+home')) + + # Activete some context + self.t(('context', 'work')) + + output = self.t(('_context',))[1] + + # Assert expected output. + self.assertIn("work", output.splitlines()) + self.assertIn("home", output.splitlines()) + self.assertEqual(len(output.splitlines()), 2) + + +class ContextEvaluationTest(TestCase): + def setUp(self): + self.t = Task() + + # Setup contexts + self.t(('context', 'define', 'work', 'project:Work')) + self.t(('context', 'define', 'home', '+home')) + self.t(('context', 'define', 'today', 'due:today')) + + # Setup tasks + self.t(('add', 'project:Work', "work task")) + self.t(('add', '+home', "home task")) + self.t(('add', 'project:Work', 'due:today', 'work today task')) + self.t(('add', '+home', 'due:today', 'home today task')) + + def test_context_evaluation(self): + """ + Test the context applied with report list command. + """ + + output = self.t(('list',))[1] + + # Assert all the tasks are present in the output + self.assertIn("work task", output) + self.assertIn("home task", output) + self.assertIn("work today task", output) + self.assertIn("home today task", output) + + # Set the home context and rerun the report + self.t(('context', 'home')) + output = self.t(('list',))[1] + + # Assert all the tasks with the home tag are present in the output + self.assertNotIn("work task", output) + self.assertIn("home task", output) + self.assertNotIn("work today task", output) + self.assertIn("home today task", output) + + def test_context_evaluation_switching(self): + """ + Test swtiching context using the list report. + """ + + output = self.t(('list',))[1] + + # Assert all the tasks are present in the output + self.assertIn("work task", output) + self.assertIn("home task", output) + self.assertIn("work today task", output) + self.assertIn("home today task", output) + + # Set the home context and rerun the report + self.t(('context', 'home')) + output = self.t(('list',))[1] + + # Assert all the tasks with the home tag are present in the output + self.assertNotIn("work task", output) + self.assertIn("home task", output) + self.assertNotIn("work today task", output) + self.assertIn("home today task", output) + + # Set the work context and rerun the report + self.t(('context', 'work')) + output = self.t(('list',))[1] + + # Assert all the tasks with the home tag are present in the output + self.assertIn("work task", output) + self.assertNotIn("home task", output) + self.assertIn("work today task", output) + self.assertNotIn("home today task", output) + + # Set the today context and rerun the report + self.t(('context', 'today')) + output = self.t(('list',))[1] + + # Assert all the tasks with the home tag are present in the output + self.assertNotIn("work task", output) + self.assertNotIn("home task", output) + self.assertIn("work today task", output) + self.assertIn("home today task", output) + + def test_context_evaluation_unset(self): + """ + Test unsetting context with report list command. + """ + + self.t(('context', 'home')) + output = self.t(('list',))[1] + + # Assert all the tasks home tagged tasks are present + self.assertNotIn("work task", output) + self.assertIn("home task", output) + self.assertNotIn("work today task", output) + self.assertIn("home today task", output) + + # Set the context to none + self.t(('context', 'none')) + output = self.t(('list',))[1] + + # Assert all the tasks are present in the output + self.assertIn("work task", output) + self.assertIn("home task", output) + self.assertIn("work today task", output) + self.assertIn("home today task", output) + + def test_context_evaluation_with_user_filters(self): + """ + Test the context applied with report list command + combined with user filters. + """ + + # Set the home context + self.t(('context', 'home')) + output = self.t(('list', 'due:today'))[1] + + # Assert all the tasks are present in the output + self.assertNotIn("work task", output) + self.assertNotIn("home task", output) + self.assertNotIn("work today task", output) + self.assertIn("home today task", output) + + # Set the work context and rerun the report + self.t(('context', 'work')) + output = self.t(('list', 'due:today'))[1] + + # Assert all the tasks are present in the output + self.assertNotIn("work task", output) + self.assertNotIn("home task", output) + self.assertIn("work today task", output) + self.assertNotIn("home today task", output) + + +if __name__ == "__main__": + from simpletap import TAPTestRunner + unittest.main(testRunner=TAPTestRunner()) + +# vim: ai sts=4 et sw=4 syntax=python From 0ab1dc0c9c3a4ac2acad9ee07ca90692838ea3a3 Mon Sep 17 00:00:00 2001 From: Tomas Babej Date: Sun, 22 Feb 2015 18:02:34 +0100 Subject: [PATCH 15/21] man: Update task and taskrc manpages with the information about context --- doc/man/task.1.in | 104 ++++++++++++++++++++++++++++++++++++++++++++ doc/man/taskrc.5.in | 20 +++++++++ 2 files changed, 124 insertions(+) diff --git a/doc/man/task.1.in b/doc/man/task.1.in index a55df9d67..65352f5d4 100644 --- a/doc/man/task.1.in +++ b/doc/man/task.1.in @@ -396,6 +396,46 @@ Finally, this command removes any 'name=...' entry from the .taskrc file: task config name +.TP +.B task context +Sets the currectly active context. See the CONTEXT section. + +Example: + + task context work + +.TP +.B task context delete +Deletes the context with the name . If the context being deleted is currently +set as active, it will be unset. + +Example: + + task context delete work + +.TP +.B task context define +Defines a new context with name and definition . This command +does not affect the currently set context, just adds a new context definition. + +Examples: + + task context define work project:Work + task context define home project:Home or +home + task context define superurgent due:today and +urgent + +.TP +.B task context list +Outputs a list of available contexts along with their definitions. + +.TP +.B task context none +Unsets the currently active context, if any was set. + +.TP +.B task context none +Shows the currently active context, along with its definition. + .TP .B task diagnostics Shows diagnostic information, of the kind needed when reporting a problem. @@ -474,6 +514,10 @@ Generates a list of all commands, for autocompletion purposes. .B task _config Lists all supported configuration variables, for completion purposes. +.TP +.B task _context +Lists all available context variables, for completion purposes. + .TP .B task _ids Shows only the IDs of matching tasks, in the form of a list. @@ -962,6 +1006,66 @@ biannual, biyearly, 2yr Every two years. .RE +.SH CONTEXT +Context is a user-defined filter, which is automatically applied to all commands +that filter the task list. In particular, any report command will have its +result affected by the current active context. + + $ task list + ID Age Project Description Urg + 1 2d Sport Run 5 miles 1.42 + 2 1d Home Clean the dishes 1.14 + + $ task context home + Context 'home' applied. + + $ task list + ID Age Project Description Urg + 2 1d Home Clean the dishes 1.14 + Context 'home' applied. + +As seen in the example above, context is applied by specifying its name to the +"context" command. To change the currently applied context, just pass the +new context's name to the 'context' command. + +To unset any context, use the 'none' subcommand. + + $ task context none + Context unset. + + $ task list + ID Age Project Description Urg + 1 2d Sport Run 5 miles 1.42 + 2 1d Home Clean the dishes 1.14 + +Context can be defined using the 'define' subcommand, specifying both the name +of the new context, and it's assigned filter. + + $ task context define home + Are you sure you want to add 'context.home' with a value of 'project:Home'? (yes/no) yes + Context 'home' successfully defined. + +To remove the definition, use the 'delete' subcommand. + + $ task context delete home + Are you sure you want to remove 'context.home'? (yes/no) yes + Context 'home' successfully undefined. + +To check what is the currently active context, use the 'show' subcommand. + + $ task context show + Context 'home' with filter 'project:Home' is currently applied. + +Contexts can store arbitrarily complex filters. + + $ task context define family project:Family or +paul or +nancy + Are you sure you want to add 'context.home' with a value of 'project:Family or +paul or +nancy'? (yes/no) yes + Context 'family' successfully defined. + +Contexts are permanent, and the currently set context name is stored in the +"context" configuration variable. The context definition is stored in the +"context." configuration variable. + .SH COMMAND ABBREVIATION All taskwarrior commands may be abbreviated as long as a unique prefix is used, for example: diff --git a/doc/man/taskrc.5.in b/doc/man/taskrc.5.in index f9a76a931..b0fa4cf79 100644 --- a/doc/man/taskrc.5.in +++ b/doc/man/taskrc.5.in @@ -1351,6 +1351,26 @@ of a task. .B uda.estimate.values=trivial,small,medium,large,huge .RE +.SS CONTEXT +Context setting is a mechanism which allows the user to set a permanent filter, +thus avoiding the need to specify one filter repeatedly. More details on usage +can be found in the task(1) manpage. + +The current context is stored in the taskrc file, along with definitions for +all user provided contexts. + +.TP +.B context= +.RS +Stores the value of the currently active context. +.RE + +.TP +.B context.= +.RS +Stores the definition of the context with the name . +.RE + .SS SYNC These configuration settings are used to connect and sync tasks with the task From ee23a099f2c39d60fefc19fe0a0331adfee41757 Mon Sep 17 00:00:00 2001 From: Tomas Babej Date: Mon, 23 Feb 2015 07:51:57 +0100 Subject: [PATCH 16/21] CmdContext: Minor style and message changes --- src/commands/CmdConfig.cpp | 4 +- src/commands/CmdContext.cpp | 118 +++++++++++++++++++++++++++--------- test/context.t | 12 ++-- 3 files changed, 96 insertions(+), 38 deletions(-) diff --git a/src/commands/CmdConfig.cpp b/src/commands/CmdConfig.cpp index 4dd3e9b3b..52e883d45 100644 --- a/src/commands/CmdConfig.cpp +++ b/src/commands/CmdConfig.cpp @@ -156,13 +156,13 @@ int CmdConfig::execute (std::string& output) if (words.size ()) { bool confirmation = context.config.getBoolean ("confirmation"); - bool change = false; bool found = false; std::string name = words[0]; std::string value = ""; + // Join the remaining words into config variable's value if (words.size () > 1) { for (unsigned int i = 1; i < words.size (); ++i) @@ -199,7 +199,7 @@ int CmdConfig::execute (std::string& output) throw format (STRING_CMD_CONFIG_NO_ENTRY, name); } - // Write .taskrc (or equivalent) + // Show feedback depending on whether .taskrc has been rewritten if (change) { out << format (STRING_CMD_CONFIG_FILE_MOD, diff --git a/src/commands/CmdContext.cpp b/src/commands/CmdContext.cpp index 5378c51d2..0d940e98d 100644 --- a/src/commands/CmdContext.cpp +++ b/src/commands/CmdContext.cpp @@ -39,7 +39,7 @@ extern Context context; CmdContext::CmdContext () { _keyword = "context"; - _usage = "task context [name [value | '']]"; + _usage = "task context [ | subcommand]"; _description = STRING_CMD_CONTEXT_USAGE; _read_only = true; _displays_id = false; @@ -59,17 +59,17 @@ int CmdContext::execute (std::string& output) std::string subcommand = words[0]; if (subcommand == "define") - rc = defineContext(words, out); + rc = defineContext (words, out); else if (subcommand == "delete") - rc = deleteContext(words, out); + rc = deleteContext (words, out); else if (subcommand == "list") - rc = listContexts(words, out); + rc = listContexts (words, out); else if (subcommand == "none") - rc = unsetContext(words, out); + rc = unsetContext (words, out); else if (subcommand == "show") - rc = showContext(words, out); + rc = showContext (words, out); else - rc = setContext(words, out); + rc = setContext (words, out); } output = out.str (); @@ -77,10 +77,10 @@ int CmdContext::execute (std::string& output) } //////////////////////////////////////////////////////////////////////////////// -// Joins all the words in the specified interval & words, unsigned int from, unsigned int to /* = 0 */) { @@ -109,68 +109,98 @@ std::vector CmdContext::getContexts () Config::const_iterator name; for (name = context.config.begin (); name != context.config.end (); ++name) - { if (name->first.substr (0, 8) == "context.") - { contexts.push_back (name->first.substr (8)); - } - } return contexts; } //////////////////////////////////////////////////////////////////////////////// +// Defines a new user-provided context. +// - The context definition is written into .taskrc as a context. variable. +// - Deletion of the context requires confirmation if rc.confirmation=yes. +// +// Returns: 0 if the addition of the config variable was successful, 1 otherwise +// +// Invoked with: task context define +// Example: task context define home project:Home +// int CmdContext::defineContext (std::vector & words, std::stringstream& out) { - // task context define home project:Home + int rc = 0; + if (words.size () > 2) { std::string name = "context." + words[1]; std::string value = joinWords(words, 2); // TODO: Check if the value is a proper filter + // Set context definition config variable bool confirmation = context.config.getBoolean ("confirmation"); bool success = CmdConfig::setConfigVariable(name, value, confirmation); if (success) - out << "Context '" << words[1] << "' successfully defined." << "\n"; + out << "Context '" << words[1] << "' defined." << "\n"; else + { out << "Context '" << words[1] << "' was not defined." << "\n"; + rc = 1; + } } else - throw "You have to specify both context name and definition."; + throw "Both context name and its definition must be provided."; - return 0; + return rc; } //////////////////////////////////////////////////////////////////////////////// +// Deletes the specified context. +// - If the deleted context is currently active, unset it. +// - Deletion of the context requires confirmation if rc.confirmation=yes. +// +// Returns: 0 if the removal of the config variable was successful, 1 otherwise +// +// Invoked with: task context delete +// Example: task context delete home +// int CmdContext::deleteContext (std::vector & words, std::stringstream& out) { - // task context delete home + int rc = 0; + if (words.size () > 1) { + // Delete the specified context std::string name = "context." + words[1]; bool confirmation = context.config.getBoolean ("confirmation"); - int status = CmdConfig::unsetConfigVariable(name, confirmation); + rc = CmdConfig::unsetConfigVariable(name, confirmation); + // If the currently set context was deleted, unset it std::string currentContext = context.config.get ("context"); if (currentContext == words[1]) CmdConfig::unsetConfigVariable("context", false); - if (status == 0) - out << "Context '" << words[1] << "' successfully undefined." << "\n"; + // Output feedback + if (rc == 0) + out << "Context '" << words[1] << "' undefined." << "\n"; else out << "Context '" << words[1] << "' was not undefined." << "\n"; } else - throw "You have to specify context name."; + throw "Context name needs to be specified."; - return 0; + return rc; } //////////////////////////////////////////////////////////////////////////////// +// Render a list of context names and their definitions. +// +// Returns: 0 the resulting list is non-empty, 1 otherwise +// +// Invoked with: task context list +// Example: task context list +// int CmdContext::listContexts (std::vector & words, std::stringstream& out) { int rc = 0; @@ -180,8 +210,6 @@ int CmdContext::listContexts (std::vector & words, std::stringstrea { std::sort (contexts.begin (), contexts.end ()); - // Render a list of UDA name, type, label, allowed values, - // possible default value, and finally the usage count. ViewText view; view.width (context.getWidth ()); view.add (Column::factory ("string", "Name")); @@ -214,29 +242,51 @@ int CmdContext::listContexts (std::vector & words, std::stringstrea } //////////////////////////////////////////////////////////////////////////////// +// Sets the specified context as currently active. +// - If some other context was active, the value of currently active context +// is replaced, not added. +// - Setting of the context does not require confirmation. +// +// Returns: 0 if the setting of the context was successful, 1 otherwise +// +// Invoked with: task context +// Example: task context home +// int CmdContext::setContext (std::vector & words, std::stringstream& out) { - // task context home + int rc = 0; std::string value = words[0]; std::vector contexts = getContexts (); + // Check that the specified context is defined if (std::find (contexts.begin (), contexts.end (), value) == contexts.end()) throw format ("Context '{1}' not found.", value); + // Set the active context. + // Should always succeed, as we do not require confirmation. bool success = CmdConfig::setConfigVariable("context", value, false); if (success) out << "Context '" << value << "' applied." << "\n"; else + { out << "Context '" << value << "' was not applied." << "\n"; + rc = 1; + } - return 0; + return rc; } //////////////////////////////////////////////////////////////////////////////// +// Shows the currently active context. +// +// Returns: Always returns 0. +// +// Invoked with: task context show +// Example: task context show +// int CmdContext::showContext (std::vector & words, std::stringstream& out) { - // task context show std::string currentContext = context.config.get ("context"); if (currentContext == "") @@ -251,9 +301,17 @@ int CmdContext::showContext (std::vector & words, std::stringstream } //////////////////////////////////////////////////////////////////////////////// +// Unsets the currently active context. +// - Unsetting of the context does not require confirmation. +// +// Returns: 0 if the unsetting of the context was successful, 1 otherwise (also +// returned if no context is currently active) +// +// Invoked with: task context none +// Example: task context none +// int CmdContext::unsetContext (std::vector & words, std::stringstream& out) { - // task context none int rc = 0; int status = CmdConfig::unsetConfigVariable("context", false); diff --git a/test/context.t b/test/context.t index 05a4c9362..a185c77bc 100755 --- a/test/context.t +++ b/test/context.t @@ -48,7 +48,7 @@ class ContextManagementTest(TestCase): output = self.t(('context', 'define', 'work', 'project:Work'))[1] # Assert successful output - self.assertIn("Context 'work' successfully defined.", output) + self.assertIn("Context 'work' defined.", output) # Assert the config contains context definition self.assertIn('context.work=project:Work\n', self.t.taskrc_content) @@ -66,7 +66,7 @@ class ContextManagementTest(TestCase): output = self.t(('context', 'define', 'work', 'project:Work'))[1] # Assert successful output - self.assertIn("Context 'work' successfully defined.", output) + self.assertIn("Context 'work' defined.", output) # Assert the config contains context definition self.assertIn('context.work=project:Work\n', self.t.taskrc_content) @@ -84,7 +84,7 @@ class ContextManagementTest(TestCase): output = self.t(('context', 'define', 'work', '+work'))[1] # Assert successful output - self.assertIn("Context 'work' successfully defined.", output) + self.assertIn("Context 'work' defined.", output) # Assert the config does not contain the old context definition self.assertNotIn('context.work=project:Work\n', self.t.taskrc_content) @@ -105,7 +105,7 @@ class ContextManagementTest(TestCase): output = self.t(('context', 'delete', 'work'))[1] # Assert correct output - self.assertIn("Context 'work' successfully undefined.", output) + self.assertIn("Context 'work' undefined.", output) # Assert that taskrc does not countain context work definition self.assertFalse(any('context.work=' in line for line in self.t.taskrc_content)) @@ -115,7 +115,7 @@ class ContextManagementTest(TestCase): Test deletion of undefined context. """ - output = self.t(('context', 'delete', 'work'))[1] + output = self.t.runError(('context', 'delete', 'work'))[1] # Assert correct output self.assertIn("Context 'work' was not undefined.", output) @@ -133,7 +133,7 @@ class ContextManagementTest(TestCase): output = self.t(('context', 'delete', 'work'))[1] # Assert correct output - self.assertIn("Context 'work' successfully undefined.", output) + self.assertIn("Context 'work' undefined.", output) # Assert that taskrc does not countain context work definition self.assertFalse(any('context.work=' in line for line in self.t.taskrc_content)) From 98410cff42b7055263627ba082273ad91c7b3911 Mon Sep 17 00:00:00 2001 From: Tomas Babej Date: Mon, 23 Feb 2015 07:52:06 +0100 Subject: [PATCH 17/21] CmdShow: Add context related variables --- src/commands/CmdShow.cpp | 2 ++ 1 file changed, 2 insertions(+) diff --git a/src/commands/CmdShow.cpp b/src/commands/CmdShow.cpp index 38f7b9c94..2d33a4ead 100644 --- a/src/commands/CmdShow.cpp +++ b/src/commands/CmdShow.cpp @@ -127,6 +127,7 @@ int CmdShow::execute (std::string& output) " column.padding" " complete.all.tags" " confirmation" + " context" " data.location" " dateformat" " dateformat.annotation" @@ -230,6 +231,7 @@ int CmdShow::execute (std::string& output) i->first.substr (0, 14) != "color.project." && i->first.substr (0, 10) != "color.tag." && i->first.substr (0, 10) != "color.uda." && + i->first.substr (0, 8) != "context." && i->first.substr (0, 8) != "holiday." && i->first.substr (0, 7) != "report." && i->first.substr (0, 6) != "alias." && From 1278226c1673b6df8185be4b3823b9c53dfbf39a Mon Sep 17 00:00:00 2001 From: Tomas Babej Date: Mon, 23 Feb 2015 17:56:34 +0100 Subject: [PATCH 18/21] CmdContext: Localize the hardcoded strings --- src/commands/CmdContext.cpp | 28 ++++++++++++++-------------- src/l10n/eng-USA.h | 14 ++++++++++++++ 2 files changed, 28 insertions(+), 14 deletions(-) diff --git a/src/commands/CmdContext.cpp b/src/commands/CmdContext.cpp index 0d940e98d..93463c4ee 100644 --- a/src/commands/CmdContext.cpp +++ b/src/commands/CmdContext.cpp @@ -140,15 +140,15 @@ int CmdContext::defineContext (std::vector & words, std::stringstre bool success = CmdConfig::setConfigVariable(name, value, confirmation); if (success) - out << "Context '" << words[1] << "' defined." << "\n"; + out << format (STRING_CMD_CONTEXT_DEF_SUCC, words[1]) << "\n"; else { - out << "Context '" << words[1] << "' was not defined." << "\n"; + out << format (STRING_CMD_CONTEXT_DEF_FAIL, words[1]) << "\n"; rc = 1; } } else - throw "Both context name and its definition must be provided."; + throw STRING_CMD_CONTEXT_DEF_USAG; return rc; } @@ -183,12 +183,12 @@ int CmdContext::deleteContext (std::vector & words, std::stringstre // Output feedback if (rc == 0) - out << "Context '" << words[1] << "' undefined." << "\n"; + out << format (STRING_CMD_CONTEXT_DEL_SUCC, words[1]) << "\n"; else - out << "Context '" << words[1] << "' was not undefined." << "\n"; + out << format (STRING_CMD_CONTEXT_DEL_FAIL, words[1]) << "\n"; } else - throw "Context name needs to be specified."; + throw STRING_CMD_CONTEXT_DEL_USAG; return rc; } @@ -234,7 +234,7 @@ int CmdContext::listContexts (std::vector & words, std::stringstrea } else { - out << "No contexts defined." << "\n"; + out << STRING_CMD_CONTEXT_LIST_EMPT << "\n"; rc = 1; } @@ -260,17 +260,17 @@ int CmdContext::setContext (std::vector & words, std::stringstream& // Check that the specified context is defined if (std::find (contexts.begin (), contexts.end (), value) == contexts.end()) - throw format ("Context '{1}' not found.", value); + throw format (STRING_CMD_CONTEXT_SET_NFOU, value); // Set the active context. // Should always succeed, as we do not require confirmation. bool success = CmdConfig::setConfigVariable("context", value, false); if (success) - out << "Context '" << value << "' applied." << "\n"; + out << format (STRING_CMD_CONTEXT_SET_SUCC, value) << "\n"; else { - out << "Context '" << value << "' was not applied." << "\n"; + out << format (STRING_CMD_CONTEXT_SET_FAIL, value) << "\n"; rc = 1; } @@ -290,11 +290,11 @@ int CmdContext::showContext (std::vector & words, std::stringstream std::string currentContext = context.config.get ("context"); if (currentContext == "") - out << "No context is currently applied." << "\n"; + out << STRING_CMD_CONTEXT_SHOW_EMPT << "\n"; else { std::string currentFilter = context.config.get ("context." + currentContext); - out << format ("Context '{1}' with filter '{2}' is currently applied.", currentContext, currentFilter) << "\n"; + out << format (STRING_CMD_CONTEXT_SHOW, currentContext, currentFilter) << "\n"; } return 0; @@ -316,10 +316,10 @@ int CmdContext::unsetContext (std::vector & words, std::stringstrea int status = CmdConfig::unsetConfigVariable("context", false); if (status == 0) - out << "Context unset." << "\n"; + out << STRING_CMD_CONTEXT_NON_SUCC << "\n"; else { - out << "Context not unset." << "\n"; + out << STRING_CMD_CONTEXT_NON_FAIL << "\n"; rc = 1; } diff --git a/src/l10n/eng-USA.h b/src/l10n/eng-USA.h index 68feb65c7..cc7838142 100644 --- a/src/l10n/eng-USA.h +++ b/src/l10n/eng-USA.h @@ -562,6 +562,20 @@ #define STRING_CMD_CONFIG_NO_NAME "Specify the name of a config variable to modify." #define STRING_CMD_HCONFIG_USAGE "Lists all supported configuration variables, for completion purposes" #define STRING_CMD_CONTEXT_USAGE "Set and define contexts (default filters)" +#define STRING_CMD_CONTEXT_DEF_SUCC "Context '{1}' defined." +#define STRING_CMD_CONTEXT_DEF_FAIL "Context '{1}' not defined." +#define STRING_CMD_CONTEXT_DEF_USAG "Both context name and its definition must be provided." +#define STRING_CMD_CONTEXT_DEL_SUCC "Context '{1}' deleted." +#define STRING_CMD_CONTEXT_DEL_FAIL "Context '{1}' not deleted." +#define STRING_CMD_CONTEXT_DEL_USAG "Context name needs to be specified." +#define STRING_CMD_CONTEXT_LIST_EMPT "No contexts defined." +#define STRING_CMD_CONTEXT_SET_NFOU "Context '{1}' not found." +#define STRING_CMD_CONTEXT_SET_SUCC "Context '{1}' applied." +#define STRING_CMD_CONTEXT_SET_FAIL "Context '{1}' not applied." +#define STRING_CMD_CONTEXT_SHOW_EMPT "No context is currently applied." +#define STRING_CMD_CONTEXT_SHOW "Context '{1}' with filter '{2}' is currently applied." +#define STRING_CMD_CONTEXT_NON_SUCC "Context unset." +#define STRING_CMD_CONTEXT_NON_FAIL "Context not unset." #define STRING_CMD_HCONTEXT_USAGE "Lists all supported contexts, for completion purposes" #define STRING_CMD_CUSTOM_MISMATCH "There are different numbers of columns and labels for report '{1}'." #define STRING_CMD_CUSTOM_SHOWN "{1} shown" From 070fdf60fa29aef88642acd864cc5231a4d46daf Mon Sep 17 00:00:00 2001 From: Tomas Babej Date: Mon, 23 Feb 2015 18:18:15 +0100 Subject: [PATCH 19/21] localization: Update other locale files --- src/l10n/deu-DEU.h | 16 ++++++++++++++++ src/l10n/epo-RUS.h | 16 ++++++++++++++++ src/l10n/esp-ESP.h | 16 ++++++++++++++++ src/l10n/fra-FRA.h | 16 ++++++++++++++++ src/l10n/ita-ITA.h | 16 ++++++++++++++++ src/l10n/pol-POL.h | 16 ++++++++++++++++ src/l10n/por-PRT.h | 16 ++++++++++++++++ 7 files changed, 112 insertions(+) diff --git a/src/l10n/deu-DEU.h b/src/l10n/deu-DEU.h index 8847ab9c9..20d76442b 100644 --- a/src/l10n/deu-DEU.h +++ b/src/l10n/deu-DEU.h @@ -561,6 +561,22 @@ #define STRING_CMD_CONFIG_NO_CHANGE "Keine Änderungen durchgeführt." #define STRING_CMD_CONFIG_NO_NAME "Geben Sie den Wert der zu ändernden Option an." #define STRING_CMD_HCONFIG_USAGE "Zeigt alle unterstützten Konfigurations-Optionen zur AUtovervollständigung" +#define STRING_CMD_CONTEXT_USAGE "Set and define contexts (default filters)" +#define STRING_CMD_CONTEXT_DEF_SUCC "Context '{1}' defined." +#define STRING_CMD_CONTEXT_DEF_FAIL "Context '{1}' not defined." +#define STRING_CMD_CONTEXT_DEF_USAG "Both context name and its definition must be provided." +#define STRING_CMD_CONTEXT_DEL_SUCC "Context '{1}' deleted." +#define STRING_CMD_CONTEXT_DEL_FAIL "Context '{1}' not deleted." +#define STRING_CMD_CONTEXT_DEL_USAG "Context name needs to be specified." +#define STRING_CMD_CONTEXT_LIST_EMPT "No contexts defined." +#define STRING_CMD_CONTEXT_SET_NFOU "Context '{1}' not found." +#define STRING_CMD_CONTEXT_SET_SUCC "Context '{1}' applied." +#define STRING_CMD_CONTEXT_SET_FAIL "Context '{1}' not applied." +#define STRING_CMD_CONTEXT_SHOW_EMPT "No context is currently applied." +#define STRING_CMD_CONTEXT_SHOW "Context '{1}' with filter '{2}' is currently applied." +#define STRING_CMD_CONTEXT_NON_SUCC "Context unset." +#define STRING_CMD_CONTEXT_NON_FAIL "Context not unset." +#define STRING_CMD_HCONTEXT_USAGE "Lists all supported contexts, for completion purposes" #define STRING_CMD_CUSTOM_MISMATCH "Die Anzahl von Spalten und Beschriftungen für Report '{1}' unterscheidet sich." #define STRING_CMD_CUSTOM_SHOWN "{1} gezeigt" #define STRING_CMD_CUSTOM_COUNT "1 Aufgabe" diff --git a/src/l10n/epo-RUS.h b/src/l10n/epo-RUS.h index f78c42162..cef22854d 100644 --- a/src/l10n/epo-RUS.h +++ b/src/l10n/epo-RUS.h @@ -561,6 +561,22 @@ #define STRING_CMD_CONFIG_NO_CHANGE "Ne ŝanĝis nenion." #define STRING_CMD_CONFIG_NO_NAME "Specifu la nomon de agordvariablo, kiun vi volas modifi." #define STRING_CMD_HCONFIG_USAGE "Listigas çiun subtenatan agordan variablon, por motivo memkompletada" +#define STRING_CMD_CONTEXT_USAGE "Set and define contexts (default filters)" +#define STRING_CMD_CONTEXT_DEF_SUCC "Context '{1}' defined." +#define STRING_CMD_CONTEXT_DEF_FAIL "Context '{1}' not defined." +#define STRING_CMD_CONTEXT_DEF_USAG "Both context name and its definition must be provided." +#define STRING_CMD_CONTEXT_DEL_SUCC "Context '{1}' deleted." +#define STRING_CMD_CONTEXT_DEL_FAIL "Context '{1}' not deleted." +#define STRING_CMD_CONTEXT_DEL_USAG "Context name needs to be specified." +#define STRING_CMD_CONTEXT_LIST_EMPT "No contexts defined." +#define STRING_CMD_CONTEXT_SET_NFOU "Context '{1}' not found." +#define STRING_CMD_CONTEXT_SET_SUCC "Context '{1}' applied." +#define STRING_CMD_CONTEXT_SET_FAIL "Context '{1}' not applied." +#define STRING_CMD_CONTEXT_SHOW_EMPT "No context is currently applied." +#define STRING_CMD_CONTEXT_SHOW "Context '{1}' with filter '{2}' is currently applied." +#define STRING_CMD_CONTEXT_NON_SUCC "Context unset." +#define STRING_CMD_CONTEXT_NON_FAIL "Context not unset." +#define STRING_CMD_HCONTEXT_USAGE "Lists all supported contexts, for completion purposes" #define STRING_CMD_CUSTOM_MISMATCH "La nombroj de kolumnoj kaj de siaj titoloj ne kongruas por raporto '{1}'." #define STRING_CMD_CUSTOM_SHOWN "{1} montritaj" #define STRING_CMD_CUSTOM_COUNT "1 tasko" diff --git a/src/l10n/esp-ESP.h b/src/l10n/esp-ESP.h index fc1404724..6eb6e64e3 100644 --- a/src/l10n/esp-ESP.h +++ b/src/l10n/esp-ESP.h @@ -570,6 +570,22 @@ #define STRING_CMD_CONFIG_NO_CHANGE "No se hicieron cambios." #define STRING_CMD_CONFIG_NO_NAME "Especifique el nombre de una variable de configuración a modificar." #define STRING_CMD_HCONFIG_USAGE "Lista todas las variables de configuración soportadas, a fines de completado" +#define STRING_CMD_CONTEXT_USAGE "Set and define contexts (default filters)" +#define STRING_CMD_CONTEXT_DEF_SUCC "Context '{1}' defined." +#define STRING_CMD_CONTEXT_DEF_FAIL "Context '{1}' not defined." +#define STRING_CMD_CONTEXT_DEF_USAG "Both context name and its definition must be provided." +#define STRING_CMD_CONTEXT_DEL_SUCC "Context '{1}' deleted." +#define STRING_CMD_CONTEXT_DEL_FAIL "Context '{1}' not deleted." +#define STRING_CMD_CONTEXT_DEL_USAG "Context name needs to be specified." +#define STRING_CMD_CONTEXT_LIST_EMPT "No contexts defined." +#define STRING_CMD_CONTEXT_SET_NFOU "Context '{1}' not found." +#define STRING_CMD_CONTEXT_SET_SUCC "Context '{1}' applied." +#define STRING_CMD_CONTEXT_SET_FAIL "Context '{1}' not applied." +#define STRING_CMD_CONTEXT_SHOW_EMPT "No context is currently applied." +#define STRING_CMD_CONTEXT_SHOW "Context '{1}' with filter '{2}' is currently applied." +#define STRING_CMD_CONTEXT_NON_SUCC "Context unset." +#define STRING_CMD_CONTEXT_NON_FAIL "Context not unset." +#define STRING_CMD_HCONTEXT_USAGE "Lists all supported contexts, for completion purposes" #define STRING_CMD_CUSTOM_MISMATCH "Hay diferente número de columnas y etiquetas para el informe '{1}'." #define STRING_CMD_CUSTOM_SHOWN "{1} mostrada(s)" #define STRING_CMD_CUSTOM_COUNT "1 tarea" diff --git a/src/l10n/fra-FRA.h b/src/l10n/fra-FRA.h index 7137d09ec..cdc7bbcb9 100644 --- a/src/l10n/fra-FRA.h +++ b/src/l10n/fra-FRA.h @@ -561,6 +561,22 @@ #define STRING_CMD_CONFIG_NO_CHANGE "No changes made." #define STRING_CMD_CONFIG_NO_NAME "Specify the name of a config variable to modify." #define STRING_CMD_HCONFIG_USAGE "Lists all supported configuration variables, for completion purposes" +#define STRING_CMD_CONTEXT_USAGE "Set and define contexts (default filters)" +#define STRING_CMD_CONTEXT_DEF_SUCC "Context '{1}' defined." +#define STRING_CMD_CONTEXT_DEF_FAIL "Context '{1}' not defined." +#define STRING_CMD_CONTEXT_DEF_USAG "Both context name and its definition must be provided." +#define STRING_CMD_CONTEXT_DEL_SUCC "Context '{1}' deleted." +#define STRING_CMD_CONTEXT_DEL_FAIL "Context '{1}' not deleted." +#define STRING_CMD_CONTEXT_DEL_USAG "Context name needs to be specified." +#define STRING_CMD_CONTEXT_LIST_EMPT "No contexts defined." +#define STRING_CMD_CONTEXT_SET_NFOU "Context '{1}' not found." +#define STRING_CMD_CONTEXT_SET_SUCC "Context '{1}' applied." +#define STRING_CMD_CONTEXT_SET_FAIL "Context '{1}' not applied." +#define STRING_CMD_CONTEXT_SHOW_EMPT "No context is currently applied." +#define STRING_CMD_CONTEXT_SHOW "Context '{1}' with filter '{2}' is currently applied." +#define STRING_CMD_CONTEXT_NON_SUCC "Context unset." +#define STRING_CMD_CONTEXT_NON_FAIL "Context not unset." +#define STRING_CMD_HCONTEXT_USAGE "Lists all supported contexts, for completion purposes" #define STRING_CMD_CUSTOM_MISMATCH "There are different numbers of columns and labels for report '{1}'." #define STRING_CMD_CUSTOM_SHOWN "{1} affichées" #define STRING_CMD_CUSTOM_COUNT "1 tâche" diff --git a/src/l10n/ita-ITA.h b/src/l10n/ita-ITA.h index a7fda70e6..b3ec61fb1 100644 --- a/src/l10n/ita-ITA.h +++ b/src/l10n/ita-ITA.h @@ -560,6 +560,22 @@ #define STRING_CMD_CONFIG_NO_CHANGE "Nessuna modifica apportata." #define STRING_CMD_CONFIG_NO_NAME "Specificare il nome di una variabile di configurazione da modificare." #define STRING_CMD_HCONFIG_USAGE "Elenca le variabili di configurazione supportate, per autocompletamento" +#define STRING_CMD_CONTEXT_USAGE "Set and define contexts (default filters)" +#define STRING_CMD_CONTEXT_DEF_SUCC "Context '{1}' defined." +#define STRING_CMD_CONTEXT_DEF_FAIL "Context '{1}' not defined." +#define STRING_CMD_CONTEXT_DEF_USAG "Both context name and its definition must be provided." +#define STRING_CMD_CONTEXT_DEL_SUCC "Context '{1}' deleted." +#define STRING_CMD_CONTEXT_DEL_FAIL "Context '{1}' not deleted." +#define STRING_CMD_CONTEXT_DEL_USAG "Context name needs to be specified." +#define STRING_CMD_CONTEXT_LIST_EMPT "No contexts defined." +#define STRING_CMD_CONTEXT_SET_NFOU "Context '{1}' not found." +#define STRING_CMD_CONTEXT_SET_SUCC "Context '{1}' applied." +#define STRING_CMD_CONTEXT_SET_FAIL "Context '{1}' not applied." +#define STRING_CMD_CONTEXT_SHOW_EMPT "No context is currently applied." +#define STRING_CMD_CONTEXT_SHOW "Context '{1}' with filter '{2}' is currently applied." +#define STRING_CMD_CONTEXT_NON_SUCC "Context unset." +#define STRING_CMD_CONTEXT_NON_FAIL "Context not unset." +#define STRING_CMD_HCONTEXT_USAGE "Lists all supported contexts, for completion purposes" #define STRING_CMD_CUSTOM_MISMATCH "Differente numero di colonne ed etichette per il report '{1}'." #define STRING_CMD_CUSTOM_SHOWN "{1} mostrato" #define STRING_CMD_CUSTOM_COUNT "1 task" diff --git a/src/l10n/pol-POL.h b/src/l10n/pol-POL.h index 8394d2f93..c296c2700 100644 --- a/src/l10n/pol-POL.h +++ b/src/l10n/pol-POL.h @@ -561,6 +561,22 @@ #define STRING_CMD_CONFIG_NO_CHANGE "Brak zmian." #define STRING_CMD_CONFIG_NO_NAME "Podaj nazwę zmiennej w konfiguracji do zmiany." #define STRING_CMD_HCONFIG_USAGE "Wylistuj wszystkie zmienne konfiguracji." +#define STRING_CMD_CONTEXT_USAGE "Set and define contexts (default filters)" +#define STRING_CMD_CONTEXT_DEF_SUCC "Context '{1}' defined." +#define STRING_CMD_CONTEXT_DEF_FAIL "Context '{1}' not defined." +#define STRING_CMD_CONTEXT_DEF_USAG "Both context name and its definition must be provided." +#define STRING_CMD_CONTEXT_DEL_SUCC "Context '{1}' deleted." +#define STRING_CMD_CONTEXT_DEL_FAIL "Context '{1}' not deleted." +#define STRING_CMD_CONTEXT_DEL_USAG "Context name needs to be specified." +#define STRING_CMD_CONTEXT_LIST_EMPT "No contexts defined." +#define STRING_CMD_CONTEXT_SET_NFOU "Context '{1}' not found." +#define STRING_CMD_CONTEXT_SET_SUCC "Context '{1}' applied." +#define STRING_CMD_CONTEXT_SET_FAIL "Context '{1}' not applied." +#define STRING_CMD_CONTEXT_SHOW_EMPT "No context is currently applied." +#define STRING_CMD_CONTEXT_SHOW "Context '{1}' with filter '{2}' is currently applied." +#define STRING_CMD_CONTEXT_NON_SUCC "Context unset." +#define STRING_CMD_CONTEXT_NON_FAIL "Context not unset." +#define STRING_CMD_HCONTEXT_USAGE "Lists all supported contexts, for completion purposes" #define STRING_CMD_CUSTOM_MISMATCH "Liczba kolumn i nagłówków nie zgadza się dla raportu '{1}'." #define STRING_CMD_CUSTOM_SHOWN "{1} pokazanych" #define STRING_CMD_CUSTOM_COUNT "1 zadanie" diff --git a/src/l10n/por-PRT.h b/src/l10n/por-PRT.h index 281fe63f7..1587291ae 100644 --- a/src/l10n/por-PRT.h +++ b/src/l10n/por-PRT.h @@ -561,6 +561,22 @@ #define STRING_CMD_CONFIG_NO_CHANGE "Nenhuma alteração efectuada." #define STRING_CMD_CONFIG_NO_NAME "Especifique o nome da configuração a modificar." #define STRING_CMD_HCONFIG_USAGE "Lista todas as configurações suportadas, para fins de terminação automática" +#define STRING_CMD_CONTEXT_USAGE "Set and define contexts (default filters)" +#define STRING_CMD_CONTEXT_DEF_SUCC "Context '{1}' defined." +#define STRING_CMD_CONTEXT_DEF_FAIL "Context '{1}' not defined." +#define STRING_CMD_CONTEXT_DEF_USAG "Both context name and its definition must be provided." +#define STRING_CMD_CONTEXT_DEL_SUCC "Context '{1}' deleted." +#define STRING_CMD_CONTEXT_DEL_FAIL "Context '{1}' not deleted." +#define STRING_CMD_CONTEXT_DEL_USAG "Context name needs to be specified." +#define STRING_CMD_CONTEXT_LIST_EMPT "No contexts defined." +#define STRING_CMD_CONTEXT_SET_NFOU "Context '{1}' not found." +#define STRING_CMD_CONTEXT_SET_SUCC "Context '{1}' applied." +#define STRING_CMD_CONTEXT_SET_FAIL "Context '{1}' not applied." +#define STRING_CMD_CONTEXT_SHOW_EMPT "No context is currently applied." +#define STRING_CMD_CONTEXT_SHOW "Context '{1}' with filter '{2}' is currently applied." +#define STRING_CMD_CONTEXT_NON_SUCC "Context unset." +#define STRING_CMD_CONTEXT_NON_FAIL "Context not unset." +#define STRING_CMD_HCONTEXT_USAGE "Lists all supported contexts, for completion purposes" #define STRING_CMD_CUSTOM_MISMATCH "O número de colunas e de rótulos não é o mesmo no relatório '{1}'." #define STRING_CMD_CUSTOM_SHOWN "{1} visiveis" #define STRING_CMD_CUSTOM_COUNT "1 tarefa" From e8d385119adcf3d05fdbb0b9e8279adb2006c233 Mon Sep 17 00:00:00 2001 From: Tomas Babej Date: Mon, 23 Feb 2015 19:25:40 -0500 Subject: [PATCH 20/21] Update NEWS and ChangeLog files with the info about the context --- ChangeLog | 1 + NEWS | 8 +++++--- 2 files changed, 6 insertions(+), 3 deletions(-) diff --git a/ChangeLog b/ChangeLog index 27af663fc..9893ec545 100644 --- a/ChangeLog +++ b/ChangeLog @@ -8,6 +8,7 @@ (thanks to Renato Alves). - Eliminated some code that is not UTF8-safe. - Removed pthreads linkage. +- Implemented the context feature. - Closed dangling pipes in execute (), resolving problems when a hook script forks (thanks to Jens Erat). diff --git a/NEWS b/NEWS index cbc5371fd..bebc9560c 100644 --- a/NEWS +++ b/NEWS @@ -1,15 +1,17 @@ New Features in taskwarrior 2.4.2 - - None + - Ability to set context, which serves as a permanent user-defined filter. New commands in taskwarrior 2.4.2 - - None + - The 'context' command has been added, along with it subcommands 'define', + 'delete', 'show', 'list' and 'none'. New configuration options in taskwarrior 2.4.2 - - None + - 'context' to store the current context applied. + - 'context.' to store the definition of context 'name' Newly deprecated features in taskwarrior 2.4.2 From 394acae790c839be2ce922a591648962b192641e Mon Sep 17 00:00:00 2001 From: Paul Beckingham Date: Mon, 23 Feb 2015 20:07:53 -0500 Subject: [PATCH 21/21] CmdContext MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit - Trivіal code edits. --- src/commands/CmdContext.cpp | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) diff --git a/src/commands/CmdContext.cpp b/src/commands/CmdContext.cpp index 93463c4ee..659f667a0 100644 --- a/src/commands/CmdContext.cpp +++ b/src/commands/CmdContext.cpp @@ -132,12 +132,12 @@ int CmdContext::defineContext (std::vector & words, std::stringstre if (words.size () > 2) { std::string name = "context." + words[1]; - std::string value = joinWords(words, 2); + std::string value = joinWords (words, 2); // TODO: Check if the value is a proper filter // Set context definition config variable bool confirmation = context.config.getBoolean ("confirmation"); - bool success = CmdConfig::setConfigVariable(name, value, confirmation); + bool success = CmdConfig::setConfigVariable (name, value, confirmation); if (success) out << format (STRING_CMD_CONTEXT_DEF_SUCC, words[1]) << "\n"; @@ -259,12 +259,12 @@ int CmdContext::setContext (std::vector & words, std::stringstream& std::vector contexts = getContexts (); // Check that the specified context is defined - if (std::find (contexts.begin (), contexts.end (), value) == contexts.end()) + if (std::find (contexts.begin (), contexts.end (), value) == contexts.end ()) throw format (STRING_CMD_CONTEXT_SET_NFOU, value); // Set the active context. // Should always succeed, as we do not require confirmation. - bool success = CmdConfig::setConfigVariable("context", value, false); + bool success = CmdConfig::setConfigVariable ("context", value, false); if (success) out << format (STRING_CMD_CONTEXT_SET_SUCC, value) << "\n"; @@ -313,7 +313,7 @@ int CmdContext::showContext (std::vector & words, std::stringstream int CmdContext::unsetContext (std::vector & words, std::stringstream& out) { int rc = 0; - int status = CmdConfig::unsetConfigVariable("context", false); + int status = CmdConfig::unsetConfigVariable ("context", false); if (status == 0) out << STRING_CMD_CONTEXT_NON_SUCC << "\n";