Merge branch '2.4.2' into lexer2

This commit is contained in:
Paul Beckingham 2015-02-23 20:22:34 -05:00
commit 0548fca88f
24 changed files with 1326 additions and 75 deletions

View file

@ -8,6 +8,7 @@
(thanks to Renato Alves). (thanks to Renato Alves).
- Eliminated some code that is not UTF8-safe. - Eliminated some code that is not UTF8-safe.
- Removed pthreads linkage. - Removed pthreads linkage.
- Implemented the context feature.
- Closed dangling pipes in execute (), resolving problems when a hook script - Closed dangling pipes in execute (), resolving problems when a hook script
forks (thanks to Jens Erat). forks (thanks to Jens Erat).

8
NEWS
View file

@ -1,15 +1,17 @@
New Features in taskwarrior 2.4.2 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 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 New configuration options in taskwarrior 2.4.2
- None - 'context' to store the current context applied.
- 'context.<name>' to store the definition of context 'name'
Newly deprecated features in taskwarrior 2.4.2 Newly deprecated features in taskwarrior 2.4.2

View file

@ -396,6 +396,46 @@ Finally, this command removes any 'name=...' entry from the .taskrc file:
task config name task config name
.TP
.B task context <name>
Sets the currectly active context. See the CONTEXT section.
Example:
task context work
.TP
.B task context delete <name>
Deletes the context with the name <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 <name> <filter>
Defines a new context with name <name> and definition <filter>. 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 .TP
.B task diagnostics .B task diagnostics
Shows diagnostic information, of the kind needed when reporting a problem. 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 .B task _config
Lists all supported configuration variables, for completion purposes. Lists all supported configuration variables, for completion purposes.
.TP
.B task _context
Lists all available context variables, for completion purposes.
.TP .TP
.B task <filter> _ids .B task <filter> _ids
Shows only the IDs of matching tasks, in the form of a list. Shows only the IDs of matching tasks, in the form of a list.
@ -962,6 +1006,66 @@ biannual, biyearly, 2yr
Every two years. Every two years.
.RE .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.<name>" configuration variable.
.SH COMMAND ABBREVIATION .SH COMMAND ABBREVIATION
All taskwarrior commands may be abbreviated as long as a unique prefix is used, All taskwarrior commands may be abbreviated as long as a unique prefix is used,
for example: for example:

View file

@ -1351,6 +1351,26 @@ of a task.
.B uda.estimate.values=trivial,small,medium,large,huge .B uda.estimate.values=trivial,small,medium,large,huge
.RE .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=<name>
.RS
Stores the value of the currently active context.
.RE
.TP
.B context.<name>=<filter>
.RS
Stores the definition of the context with the name <name>.
.RE
.SS SYNC .SS SYNC
These configuration settings are used to connect and sync tasks with the task These configuration settings are used to connect and sync tasks with the task

View file

@ -374,6 +374,64 @@ void CLI::add (const std::string& arg)
analyze (); 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 <A>::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.
//
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 // Intended to be called after ::initialize() and ::add(), to perform the final
// analysis. Analysis is also performed directly after the above, because there // analysis. Analysis is also performed directly after the above, because there
@ -476,6 +534,9 @@ void CLI::applyOverrides ()
// Extract all the FILTER-tagged items. // Extract all the FILTER-tagged items.
const std::string CLI::getFilter () const std::string CLI::getFilter ()
{ {
// Handle context setting
addContextFilter ();
std::string filter = ""; std::string filter = "";
if (_args.size ()) if (_args.size ())
{ {
@ -500,6 +561,7 @@ const std::string CLI::getFilter ()
filter = "( " + filter + " )"; filter = "( " + filter + " )";
} }
context.debug("Derived filter: '" + filter + "'");
return filter; return filter;
} }

View file

@ -77,6 +77,8 @@ public:
void entity (const std::string&, const std::string&); void entity (const std::string&, const std::string&);
void initialize (int, const char**); void initialize (int, const char**);
void add (const std::string&); void add (const std::string&);
void addContextFilter ();
void addRawFilter (const std::string& arg);
void analyze (bool parse = true, bool strict = false); void analyze (bool parse = true, bool strict = false);
void applyOverrides (); void applyOverrides ();
const std::string getFilter (); const std::string getFilter ();

View file

@ -18,6 +18,7 @@ set (commands_SRCS Command.cpp Command.h
CmdColor.cpp CmdColor.h CmdColor.cpp CmdColor.h
CmdColumns.cpp CmdColumns.h CmdColumns.cpp CmdColumns.h
CmdConfig.cpp CmdConfig.h CmdConfig.cpp CmdConfig.h
CmdContext.cpp CmdContext.h
CmdCount.cpp CmdCount.h CmdCount.cpp CmdCount.h
CmdCustom.cpp CmdCustom.h CmdCustom.cpp CmdCustom.h
CmdDelete.cpp CmdDelete.h CmdDelete.cpp CmdDelete.h

View file

@ -47,49 +47,15 @@ CmdConfig::CmdConfig ()
} }
//////////////////////////////////////////////////////////////////////////////// ////////////////////////////////////////////////////////////////////////////////
int CmdConfig::execute (std::string& output) bool CmdConfig::setConfigVariable (std::string name, std::string value, bool confirmation /* = false */)
{ {
int rc = 0;
std::stringstream out;
// Get the non-attribute, non-fancy command line arguments.
std::vector <std::string> words = context.cli.getWords ();
// Support:
// task config name value # set name to value
// task config name "" # set name to blank
// task config name # remove name
if (words.size ())
{
bool confirmation = context.config.getBoolean ("confirmation");
std::string name = words[0];
std::string value = "";
if (words.size () > 1)
{
for (unsigned int i = 1; i < words.size (); ++i)
{
if (i > 1)
value += " ";
value += words[i];
}
}
if (name != "")
{
bool change = false;
// Read .taskrc (or equivalent) // Read .taskrc (or equivalent)
std::vector <std::string> contents; std::vector <std::string> contents;
File::read (context.config._original_file, contents); File::read (context.config._original_file, contents);
// task config name value
// task config name ""
if (words.size () > 1)
{
bool found = false; bool found = false;
bool change = false;
std::vector <std::string>::iterator line; std::vector <std::string>::iterator line;
for (line = contents.begin (); line != contents.end (); ++line) for (line = contents.begin (); line != contents.end (); ++line)
{ {
@ -123,12 +89,23 @@ int CmdConfig::execute (std::string& output)
contents.push_back (name + "=" + json::encode (value)); contents.push_back (name + "=" + json::encode (value));
change = true; change = true;
} }
if (change)
File::write (context.config._original_file, contents);
return change;
} }
// task config name ////////////////////////////////////////////////////////////////////////////////
else int CmdConfig::unsetConfigVariable (std::string name, bool confirmation /* = false */)
{ {
// Read .taskrc (or equivalent)
std::vector <std::string> contents;
File::read (context.config._original_file, contents);
bool found = false; bool found = false;
bool change = false;
std::vector <std::string>::iterator line; std::vector <std::string>::iterator line;
for (line = contents.begin (); line != contents.end (); ++line) for (line = contents.begin (); line != contents.end (); ++line)
{ {
@ -152,14 +129,79 @@ int CmdConfig::execute (std::string& output)
} }
} }
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)
{
int rc = 0;
std::stringstream out;
// Get the non-attribute, non-fancy command line arguments.
std::vector <std::string> words = context.cli.getWords ();
// Support:
// task config name value # set name to value
// task config name "" # set name to blank
// task config name # remove name
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)
{
if (i > 1)
value += " ";
value += words[i];
}
}
if (name != "")
{
bool change = false;
// task config name value
// task config name ""
if (words.size () > 1)
change = setConfigVariable(name, value, confirmation);
// task config name
else
{
rc = unsetConfigVariable(name, confirmation);
if (rc == 0)
{
change = true;
found = true;
}
else if (rc == 1)
found = true;
if (!found) if (!found)
throw format (STRING_CMD_CONFIG_NO_ENTRY, name); throw format (STRING_CMD_CONFIG_NO_ENTRY, name);
} }
// Write .taskrc (or equivalent) // Show feedback depending on whether .taskrc has been rewritten
if (change) if (change)
{ {
File::write (context.config._original_file, contents);
out << format (STRING_CMD_CONFIG_FILE_MOD, out << format (STRING_CMD_CONFIG_FILE_MOD,
context.config._original_file._data) context.config._original_file._data)
<< "\n"; << "\n";

View file

@ -34,6 +34,8 @@ class CmdConfig : public Command
{ {
public: public:
CmdConfig (); 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&); int execute (std::string&);
}; };

351
src/commands/CmdContext.cpp Normal file
View file

@ -0,0 +1,351 @@
////////////////////////////////////////////////////////////////////////////////
//
// 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 <cmake.h>
#include <Context.h>
#include <sstream>
#include <algorithm>
#include <i18n.h>
#include <text.h>
#include <CmdContext.h>
#include <CmdConfig.h>
extern Context context;
////////////////////////////////////////////////////////////////////////////////
CmdContext::CmdContext ()
{
_keyword = "context";
_usage = "task context [<name> | subcommand]";
_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 <std::string> words = context.cli.getWords ();
if (words.size () > 0)
{
std::string subcommand = words[0];
if (subcommand == "define")
rc = defineContext (words, out);
else if (subcommand == "delete")
rc = deleteContext (words, out);
else if (subcommand == "list")
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);
}
output = out.str ();
return rc;
}
////////////////////////////////////////////////////////////////////////////////
// Joins all the words in the specified interval <from, to) to one string,
// which is then returned.
//
// If to is specified as 0 (default value), all the remaining words will be joined.
//
std::string CmdContext::joinWords (std::vector <std::string>& 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;
}
////////////////////////////////////////////////////////////////////////////////
// Returns all user defined contexts.
//
std::vector <std::string> CmdContext::getContexts ()
{
std::vector <std::string> 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;
}
////////////////////////////////////////////////////////////////////////////////
// Defines a new user-provided context.
// - The context definition is written into .taskrc as a context.<name> 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 <name> <filter>
// Example: task context define home project:Home
//
int CmdContext::defineContext (std::vector <std::string>& words, std::stringstream& out)
{
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 << format (STRING_CMD_CONTEXT_DEF_SUCC, words[1]) << "\n";
else
{
out << format (STRING_CMD_CONTEXT_DEF_FAIL, words[1]) << "\n";
rc = 1;
}
}
else
throw STRING_CMD_CONTEXT_DEF_USAG;
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 <name>
// Example: task context delete home
//
int CmdContext::deleteContext (std::vector <std::string>& words, std::stringstream& out)
{
int rc = 0;
if (words.size () > 1)
{
// Delete the specified context
std::string name = "context." + words[1];
bool confirmation = context.config.getBoolean ("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);
// Output feedback
if (rc == 0)
out << format (STRING_CMD_CONTEXT_DEL_SUCC, words[1]) << "\n";
else
out << format (STRING_CMD_CONTEXT_DEL_FAIL, words[1]) << "\n";
}
else
throw STRING_CMD_CONTEXT_DEL_USAG;
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 <std::string>& words, std::stringstream& out)
{
int rc = 0;
std::vector <std::string> contexts = getContexts();
if (contexts.size ())
{
std::sort (contexts.begin (), contexts.end ());
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 <std::string>::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 << STRING_CMD_CONTEXT_LIST_EMPT << "\n";
rc = 1;
}
return rc;
}
////////////////////////////////////////////////////////////////////////////////
// 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 <name>
// Example: task context home
//
int CmdContext::setContext (std::vector <std::string>& words, std::stringstream& out)
{
int rc = 0;
std::string value = words[0];
std::vector <std::string> contexts = getContexts ();
// Check that the specified context is defined
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);
if (success)
out << format (STRING_CMD_CONTEXT_SET_SUCC, value) << "\n";
else
{
out << format (STRING_CMD_CONTEXT_SET_FAIL, value) << "\n";
rc = 1;
}
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 <std::string>& words, std::stringstream& out)
{
std::string currentContext = context.config.get ("context");
if (currentContext == "")
out << STRING_CMD_CONTEXT_SHOW_EMPT << "\n";
else
{
std::string currentFilter = context.config.get ("context." + currentContext);
out << format (STRING_CMD_CONTEXT_SHOW, currentContext, currentFilter) << "\n";
}
return 0;
}
////////////////////////////////////////////////////////////////////////////////
// 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 <std::string>& words, std::stringstream& out)
{
int rc = 0;
int status = CmdConfig::unsetConfigVariable ("context", false);
if (status == 0)
out << STRING_CMD_CONTEXT_NON_SUCC << "\n";
else
{
out << STRING_CMD_CONTEXT_NON_FAIL << "\n";
rc = 1;
}
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 <std::string> userContexts = CmdContext::getContexts ();
std::vector <std::string>::iterator userContext;
for (userContext = userContexts.begin (); userContext != userContexts.end (); ++userContext)
output += *userContext + "\n";
return 0;
}
////////////////////////////////////////////////////////////////////////////////

56
src/commands/CmdContext.h Normal file
View file

@ -0,0 +1,56 @@
////////////////////////////////////////////////////////////////////////////////
//
// 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 <string>
#include <Command.h>
class CmdContext : public Command
{
public:
CmdContext ();
int execute (std::string&);
std::string joinWords (std::vector <std::string>& words, unsigned int from, unsigned int to = 0);
static std::vector <std::string> getContexts ();
int defineContext (std::vector <std::string>& words, std::stringstream& out);
int deleteContext (std::vector <std::string>& words, std::stringstream& out);
int listContexts (std::vector <std::string>& words, std::stringstream& out);
int setContext (std::vector <std::string>& words, std::stringstream& out);
int showContext (std::vector <std::string>& words, std::stringstream& out);
int unsetContext (std::vector <std::string>& words, std::stringstream& out);
};
class CmdCompletionContext : public Command
{
public:
CmdCompletionContext ();
int execute (std::string&);
};
#endif
////////////////////////////////////////////////////////////////////////////////

View file

@ -82,15 +82,7 @@ int CmdCustom::execute (std::string& output)
validateSortColumns (sortOrder); validateSortColumns (sortOrder);
// Prepend the argument list with those from the report filter. // Prepend the argument list with those from the report filter.
std::string lexeme; context.cli.addRawFilter(reportFilter);
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 ();
// Apply filter. // Apply filter.
handleRecurrence (); handleRecurrence ();

View file

@ -127,6 +127,7 @@ int CmdShow::execute (std::string& output)
" column.padding" " column.padding"
" complete.all.tags" " complete.all.tags"
" confirmation" " confirmation"
" context"
" data.location" " data.location"
" dateformat" " dateformat"
" dateformat.annotation" " dateformat.annotation"
@ -230,6 +231,7 @@ int CmdShow::execute (std::string& output)
i->first.substr (0, 14) != "color.project." && i->first.substr (0, 14) != "color.project." &&
i->first.substr (0, 10) != "color.tag." && i->first.substr (0, 10) != "color.tag." &&
i->first.substr (0, 10) != "color.uda." && i->first.substr (0, 10) != "color.uda." &&
i->first.substr (0, 8) != "context." &&
i->first.substr (0, 8) != "holiday." && i->first.substr (0, 8) != "holiday." &&
i->first.substr (0, 7) != "report." && i->first.substr (0, 7) != "report." &&
i->first.substr (0, 6) != "alias." && i->first.substr (0, 6) != "alias." &&

View file

@ -46,6 +46,7 @@
#include <CmdColumns.h> #include <CmdColumns.h>
#include <CmdCommands.h> #include <CmdCommands.h>
#include <CmdConfig.h> #include <CmdConfig.h>
#include <CmdContext.h>
#include <CmdCount.h> #include <CmdCount.h>
#include <CmdCustom.h> #include <CmdCustom.h>
#include <CmdDelete.h> #include <CmdDelete.h>
@ -109,6 +110,7 @@ void Command::factory (std::map <std::string, Command*>& all)
c = new CmdCompletionColumns (); all[c->keyword ()] = c; c = new CmdCompletionColumns (); all[c->keyword ()] = c;
c = new CmdCompletionCommands (); all[c->keyword ()] = c; c = new CmdCompletionCommands (); all[c->keyword ()] = c;
c = new CmdCompletionConfig (); 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 CmdCompletionIds (); all[c->keyword ()] = c;
c = new CmdCompletionUDAs (); all[c->keyword ()] = c; c = new CmdCompletionUDAs (); all[c->keyword ()] = c;
c = new CmdCompletionUuids (); all[c->keyword ()] = c; c = new CmdCompletionUuids (); all[c->keyword ()] = c;
@ -116,6 +118,7 @@ void Command::factory (std::map <std::string, Command*>& all)
c = new CmdCompletionTags (); all[c->keyword ()] = c; c = new CmdCompletionTags (); all[c->keyword ()] = c;
c = new CmdCompletionVersion (); all[c->keyword ()] = c; c = new CmdCompletionVersion (); all[c->keyword ()] = c;
c = new CmdConfig (); 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 CmdCount (); all[c->keyword ()] = c;
c = new CmdDelete (); all[c->keyword ()] = c; c = new CmdDelete (); all[c->keyword ()] = c;
c = new CmdDenotate (); all[c->keyword ()] = c; c = new CmdDenotate (); all[c->keyword ()] = c;

View file

@ -561,6 +561,22 @@
#define STRING_CMD_CONFIG_NO_CHANGE "Keine Änderungen durchgeführt." #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_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_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_MISMATCH "Die Anzahl von Spalten und Beschriftungen für Report '{1}' unterscheidet sich."
#define STRING_CMD_CUSTOM_SHOWN "{1} gezeigt" #define STRING_CMD_CUSTOM_SHOWN "{1} gezeigt"
#define STRING_CMD_CUSTOM_COUNT "1 Aufgabe" #define STRING_CMD_CUSTOM_COUNT "1 Aufgabe"

View file

@ -561,6 +561,22 @@
#define STRING_CMD_CONFIG_NO_CHANGE "No changes made." #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_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_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_MISMATCH "There are different numbers of columns and labels for report '{1}'."
#define STRING_CMD_CUSTOM_SHOWN "{1} shown" #define STRING_CMD_CUSTOM_SHOWN "{1} shown"
#define STRING_CMD_CUSTOM_COUNT "1 task" #define STRING_CMD_CUSTOM_COUNT "1 task"

View file

@ -561,6 +561,22 @@
#define STRING_CMD_CONFIG_NO_CHANGE "Ne ŝanĝis nenion." #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_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_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_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_SHOWN "{1} montritaj"
#define STRING_CMD_CUSTOM_COUNT "1 tasko" #define STRING_CMD_CUSTOM_COUNT "1 tasko"

View file

@ -570,6 +570,22 @@
#define STRING_CMD_CONFIG_NO_CHANGE "No se hicieron cambios." #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_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_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_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_SHOWN "{1} mostrada(s)"
#define STRING_CMD_CUSTOM_COUNT "1 tarea" #define STRING_CMD_CUSTOM_COUNT "1 tarea"

View file

@ -561,6 +561,22 @@
#define STRING_CMD_CONFIG_NO_CHANGE "No changes made." #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_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_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_MISMATCH "There are different numbers of columns and labels for report '{1}'."
#define STRING_CMD_CUSTOM_SHOWN "{1} affichées" #define STRING_CMD_CUSTOM_SHOWN "{1} affichées"
#define STRING_CMD_CUSTOM_COUNT "1 tâche" #define STRING_CMD_CUSTOM_COUNT "1 tâche"

View file

@ -560,6 +560,22 @@
#define STRING_CMD_CONFIG_NO_CHANGE "Nessuna modifica apportata." #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_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_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_MISMATCH "Differente numero di colonne ed etichette per il report '{1}'."
#define STRING_CMD_CUSTOM_SHOWN "{1} mostrato" #define STRING_CMD_CUSTOM_SHOWN "{1} mostrato"
#define STRING_CMD_CUSTOM_COUNT "1 task" #define STRING_CMD_CUSTOM_COUNT "1 task"

View file

@ -561,6 +561,22 @@
#define STRING_CMD_CONFIG_NO_CHANGE "Brak zmian." #define STRING_CMD_CONFIG_NO_CHANGE "Brak zmian."
#define STRING_CMD_CONFIG_NO_NAME "Podaj nazwę zmiennej w konfiguracji do zmiany." #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_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_MISMATCH "Liczba kolumn i nagłówków nie zgadza się dla raportu '{1}'."
#define STRING_CMD_CUSTOM_SHOWN "{1} pokazanych" #define STRING_CMD_CUSTOM_SHOWN "{1} pokazanych"
#define STRING_CMD_CUSTOM_COUNT "1 zadanie" #define STRING_CMD_CUSTOM_COUNT "1 zadanie"

View file

@ -561,6 +561,22 @@
#define STRING_CMD_CONFIG_NO_CHANGE "Nenhuma alteração efectuada." #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_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_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_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_SHOWN "{1} visiveis"
#define STRING_CMD_CUSTOM_COUNT "1 tarefa" #define STRING_CMD_CUSTOM_COUNT "1 tarefa"

View file

@ -137,6 +137,15 @@ class Task(object):
cmd = (self.taskw, "config", "--", var, value) cmd = (self.taskw, "config", "--", var, value)
return run_cmd_wait(cmd, env=self.env) 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, def runSuccess(self, args=(), input=None, merge_streams=False,
timeout=1): timeout=1):
"""Invoke task with given arguments and fail if exit code != 0 """Invoke task with given arguments and fail if exit code != 0

474
test/context.t Executable file
View file

@ -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' 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' 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' 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' 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.runError(('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' 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