mirror of
https://github.com/GothenburgBitFactory/taskwarrior.git
synced 2025-07-07 20:06:36 +02:00
Bug Fix - custom report filters
- Added support for using parent in a filter. - Fixed bug that ignored custom report filters. - Made Context::parse and Context::autoFilter reentrant.
This commit is contained in:
parent
4d43b77441
commit
b932d9b9b7
4 changed files with 67 additions and 28 deletions
12
src/Att.cpp
12
src/Att.cpp
|
@ -42,6 +42,8 @@ static const char* internalNames[] =
|
||||||
"entry",
|
"entry",
|
||||||
"start",
|
"start",
|
||||||
"end",
|
"end",
|
||||||
|
"parent",
|
||||||
|
"uuid",
|
||||||
"mask",
|
"mask",
|
||||||
"imask",
|
"imask",
|
||||||
"limit",
|
"limit",
|
||||||
|
@ -378,8 +380,16 @@ bool Att::validNameValue (
|
||||||
"\" is not a valid status. Use 'pending', 'completed', 'deleted' or 'recurring'.";
|
"\" is not a valid status. Use 'pending', 'completed', 'deleted' or 'recurring'.";
|
||||||
}
|
}
|
||||||
|
|
||||||
|
else if (name == "parent")
|
||||||
|
{
|
||||||
|
}
|
||||||
|
|
||||||
|
else if (name == "uuid")
|
||||||
|
{
|
||||||
|
}
|
||||||
|
|
||||||
else
|
else
|
||||||
throw std::string ("'") + name + "' is an unrecognized attribute.";
|
throw std::string ("'") + name + "' is not a recognized attribute.";
|
||||||
|
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
|
@ -339,6 +339,18 @@ void Context::loadCorrectConfigFile ()
|
||||||
|
|
||||||
////////////////////////////////////////////////////////////////////////////////
|
////////////////////////////////////////////////////////////////////////////////
|
||||||
void Context::parse ()
|
void Context::parse ()
|
||||||
|
{
|
||||||
|
parse (args, cmd, task, sequence, subst, filter);
|
||||||
|
}
|
||||||
|
|
||||||
|
////////////////////////////////////////////////////////////////////////////////
|
||||||
|
void Context::parse (
|
||||||
|
std::vector <std::string>& parseArgs,
|
||||||
|
Cmd& parseCmd,
|
||||||
|
Task& parseTask,
|
||||||
|
Sequence& parseSequence,
|
||||||
|
Subst& parseSubst,
|
||||||
|
Filter& parseFilter)
|
||||||
{
|
{
|
||||||
Att attribute;
|
Att attribute;
|
||||||
tagAdditions.clear ();
|
tagAdditions.clear ();
|
||||||
|
@ -348,7 +360,7 @@ void Context::parse ()
|
||||||
bool foundSequence = false;
|
bool foundSequence = false;
|
||||||
bool foundSomethingAfterSequence = false;
|
bool foundSomethingAfterSequence = false;
|
||||||
|
|
||||||
foreach (arg, args)
|
foreach (arg, parseArgs)
|
||||||
{
|
{
|
||||||
if (!terminated)
|
if (!terminated)
|
||||||
{
|
{
|
||||||
|
@ -361,12 +373,12 @@ void Context::parse ()
|
||||||
|
|
||||||
// Sequence
|
// Sequence
|
||||||
// Note: "add" doesn't require an ID
|
// Note: "add" doesn't require an ID
|
||||||
else if (cmd.command != "add" &&
|
else if (parseCmd.command != "add" &&
|
||||||
! foundSomethingAfterSequence &&
|
! foundSomethingAfterSequence &&
|
||||||
sequence.valid (*arg))
|
parseSequence.valid (*arg))
|
||||||
{
|
{
|
||||||
header ("parse sequence '" + *arg + "'");
|
header ("parse sequence '" + *arg + "'");
|
||||||
sequence.parse (*arg);
|
parseSequence.parse (*arg);
|
||||||
foundSequence = true;
|
foundSequence = true;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -379,8 +391,12 @@ void Context::parse ()
|
||||||
if (foundSequence)
|
if (foundSequence)
|
||||||
foundSomethingAfterSequence = true;
|
foundSomethingAfterSequence = true;
|
||||||
|
|
||||||
|
if (arg->find (',') != std::string::npos)
|
||||||
|
throw stringtable.get (TAGS_NO_COMMA,
|
||||||
|
"Tags are not permitted to contain commas.");
|
||||||
|
|
||||||
tagAdditions.push_back (arg->substr (1, std::string::npos));
|
tagAdditions.push_back (arg->substr (1, std::string::npos));
|
||||||
task.addTag (arg->substr (1, std::string::npos));
|
parseTask.addTag (arg->substr (1, std::string::npos));
|
||||||
}
|
}
|
||||||
|
|
||||||
// Tags to remove begin with '-'.
|
// Tags to remove begin with '-'.
|
||||||
|
@ -417,25 +433,25 @@ void Context::parse ()
|
||||||
attribute.mod (mod);
|
attribute.mod (mod);
|
||||||
attribute.value (value);
|
attribute.value (value);
|
||||||
|
|
||||||
task[attribute.name ()] = attribute;
|
parseTask[attribute.name ()] = attribute;
|
||||||
}
|
}
|
||||||
|
|
||||||
// Substitution of description and/or annotation text.
|
// Substitution of description and/or annotation text.
|
||||||
else if (subst.valid (*arg))
|
else if (parseSubst.valid (*arg))
|
||||||
{
|
{
|
||||||
if (foundSequence)
|
if (foundSequence)
|
||||||
foundSomethingAfterSequence = true;
|
foundSomethingAfterSequence = true;
|
||||||
|
|
||||||
header ("parse subst '" + *arg + "'");
|
header ("parse subst '" + *arg + "'");
|
||||||
subst.parse (*arg);
|
parseSubst.parse (*arg);
|
||||||
}
|
}
|
||||||
|
|
||||||
// It might be a command if one has not already been found.
|
// It might be a command if one has not already been found.
|
||||||
else if (cmd.command == "" &&
|
else if (parseCmd.command == "" &&
|
||||||
cmd.valid (*arg))
|
parseCmd.valid (*arg))
|
||||||
{
|
{
|
||||||
header ("parse cmd '" + *arg + "'");
|
header ("parse cmd '" + *arg + "'");
|
||||||
cmd.parse (*arg);
|
parseCmd.parse (*arg);
|
||||||
|
|
||||||
if (foundSequence)
|
if (foundSequence)
|
||||||
foundSomethingAfterSequence = true;
|
foundSomethingAfterSequence = true;
|
||||||
|
@ -469,26 +485,26 @@ void Context::parse ()
|
||||||
if (descCandidate != "" && noVerticalSpace (descCandidate))
|
if (descCandidate != "" && noVerticalSpace (descCandidate))
|
||||||
{
|
{
|
||||||
header ("parse description '" + descCandidate + "'");
|
header ("parse description '" + descCandidate + "'");
|
||||||
task.set ("description", descCandidate);
|
parseTask.set ("description", descCandidate);
|
||||||
}
|
}
|
||||||
|
|
||||||
// TODO task.validate () ?
|
// TODO task.validate () ?
|
||||||
|
|
||||||
// Read-only command (reports, status, info ...) use filters. Write commands
|
// Read-only command (reports, status, info ...) use filters. Write commands
|
||||||
// (add, done ...) do not.
|
// (add, done ...) do not.
|
||||||
if (cmd.isReadOnlyCommand ())
|
if (parseCmd.isReadOnlyCommand ())
|
||||||
autoFilter ();
|
autoFilter (parseTask, parseFilter);
|
||||||
|
|
||||||
// If no command was specified, and there were no command line arguments
|
// If no command was specified, and there were no command line arguments
|
||||||
// then invoke the default command.
|
// then invoke the default command.
|
||||||
if (cmd.command == "" && args.size () == 0)
|
if (parseCmd.command == "" && parseArgs.size () == 0)
|
||||||
{
|
{
|
||||||
std::string defaultCommand = config.get ("default.command");
|
std::string defaultCommand = config.get ("default.command");
|
||||||
if (defaultCommand != "")
|
if (defaultCommand != "")
|
||||||
{
|
{
|
||||||
// Stuff the command line.
|
// Stuff the command line.
|
||||||
args.clear ();
|
parseArgs.clear ();
|
||||||
split (args, defaultCommand, ' ');
|
split (parseArgs, defaultCommand, ' ');
|
||||||
header ("[task " + defaultCommand + "]");
|
header ("[task " + defaultCommand + "]");
|
||||||
|
|
||||||
// Reinitialize the context and recurse.
|
// Reinitialize the context and recurse.
|
||||||
|
@ -500,9 +516,9 @@ void Context::parse ()
|
||||||
|
|
||||||
////////////////////////////////////////////////////////////////////////////////
|
////////////////////////////////////////////////////////////////////////////////
|
||||||
// Add all the attributes in the task to the filter. All except uuid.
|
// Add all the attributes in the task to the filter. All except uuid.
|
||||||
void Context::autoFilter ()
|
void Context::autoFilter (Task& t, Filter& f)
|
||||||
{
|
{
|
||||||
foreach (att, task)
|
foreach (att, t)
|
||||||
{
|
{
|
||||||
// Words are found in the description using the .has modifier.
|
// Words are found in the description using the .has modifier.
|
||||||
if (att->first == "description")
|
if (att->first == "description")
|
||||||
|
@ -511,7 +527,7 @@ void Context::autoFilter ()
|
||||||
split (words, att->second.value (), ' ');
|
split (words, att->second.value (), ' ');
|
||||||
foreach (word, words)
|
foreach (word, words)
|
||||||
{
|
{
|
||||||
filter.push_back (Att ("description", "has", *word));
|
f.push_back (Att ("description", "has", *word));
|
||||||
header ("auto filter: " + att->first + ".has:" + *word);
|
header ("auto filter: " + att->first + ".has:" + *word);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -519,7 +535,7 @@ void Context::autoFilter ()
|
||||||
// Projects are matched left-most.
|
// Projects are matched left-most.
|
||||||
else if (att->first == "project")
|
else if (att->first == "project")
|
||||||
{
|
{
|
||||||
filter.push_back (Att ("project", "startswith", att->second.value ()));
|
f.push_back (Att ("project", "startswith", att->second.value ()));
|
||||||
header ("auto filter: " + att->first + ".startswith:" + att->second.value ());
|
header ("auto filter: " + att->first + ".startswith:" + att->second.value ());
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -529,7 +545,7 @@ void Context::autoFilter ()
|
||||||
else if (att->first != "uuid" &&
|
else if (att->first != "uuid" &&
|
||||||
att->first != "tags")
|
att->first != "tags")
|
||||||
{
|
{
|
||||||
filter.push_back (att->second);
|
f.push_back (att->second);
|
||||||
header ("auto filter: " + att->first + ":" + att->second.value ());
|
header ("auto filter: " + att->first + ":" + att->second.value ());
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -537,14 +553,14 @@ void Context::autoFilter ()
|
||||||
// Include tagAdditions.
|
// Include tagAdditions.
|
||||||
foreach (tag, tagAdditions)
|
foreach (tag, tagAdditions)
|
||||||
{
|
{
|
||||||
filter.push_back (Att ("tags", "has", *tag));
|
f.push_back (Att ("tags", "has", *tag));
|
||||||
header ("auto filter: +" + *tag);
|
header ("auto filter: +" + *tag);
|
||||||
}
|
}
|
||||||
|
|
||||||
// Include tagRemovals.
|
// Include tagRemovals.
|
||||||
foreach (tag, tagRemovals)
|
foreach (tag, tagRemovals)
|
||||||
{
|
{
|
||||||
filter.push_back (Att ("tags", "hasnt", *tag));
|
f.push_back (Att ("tags", "hasnt", *tag));
|
||||||
header ("auto filter: -" + *tag);
|
header ("auto filter: -" + *tag);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -58,10 +58,11 @@ public:
|
||||||
void clearMessages ();
|
void clearMessages ();
|
||||||
|
|
||||||
void parse ();
|
void parse ();
|
||||||
|
void parse (std::vector <std::string>&, Cmd&, Task&, Sequence&, Subst&, Filter&);
|
||||||
|
|
||||||
private:
|
private:
|
||||||
void loadCorrectConfigFile ();
|
void loadCorrectConfigFile ();
|
||||||
void autoFilter ();
|
void autoFilter (Task&, Filter&);
|
||||||
|
|
||||||
public:
|
public:
|
||||||
Config config;
|
Config config;
|
||||||
|
|
|
@ -80,11 +80,23 @@ std::string handleCustomReport (const std::string& report)
|
||||||
std::string filterList = context.config.get ("report." + report + ".filter");
|
std::string filterList = context.config.get ("report." + report + ".filter");
|
||||||
std::vector <std::string> filterArgs;
|
std::vector <std::string> filterArgs;
|
||||||
split (filterArgs, filterList, ' ');
|
split (filterArgs, filterList, ' ');
|
||||||
|
{
|
||||||
|
Cmd cmd (report);
|
||||||
|
Task task;
|
||||||
|
Sequence sequence;
|
||||||
|
Subst subst;
|
||||||
|
Filter filter;
|
||||||
|
context.parse (filterArgs, cmd, task, sequence, subst, filter);
|
||||||
|
|
||||||
|
context.sequence.combine (sequence);
|
||||||
|
|
||||||
|
foreach (att, filter)
|
||||||
|
context.filter.push_back (*att);
|
||||||
|
}
|
||||||
|
|
||||||
// Get all the tasks.
|
// Get all the tasks.
|
||||||
std::vector <Task> tasks;
|
std::vector <Task> tasks;
|
||||||
context.tdb.lock (context.config.get ("locking", true));
|
context.tdb.lock (context.config.get ("locking", true));
|
||||||
// TODO Include filter from custom report.
|
|
||||||
context.tdb.load (tasks, context.filter);
|
context.tdb.load (tasks, context.filter);
|
||||||
handleRecurrence (tasks);
|
handleRecurrence (tasks);
|
||||||
context.tdb.commit ();
|
context.tdb.commit ();
|
||||||
|
|
Loading…
Add table
Add a link
Reference in a new issue