mirror of
https://github.com/GothenburgBitFactory/taskwarrior.git
synced 2025-07-30 04:23:24 +02:00
Merge branch '2.3.0' into 2.4.0
Conflicts: AUTHORS CMakeLists.txt NEWS src/A3.cpp src/CMakeLists.txt src/Config.cpp src/Duration.cpp src/Duration.h src/Nibbler.cpp src/Nibbler.h src/RX.cpp src/RX.h src/columns/ColDate.cpp src/columns/ColScheduled.cpp src/commands/Command.cpp src/legacy.cpp src/utf8.cpp src/utf8.h test/CMakeLists.txt test/bug.mergedeps.t.postponed test/duration.t.cpp test/merge.duplicates.t test/merge.simple_duplication.t test/merge.t test/nibbler.t.cpp test/roundtrip.t test/rx.t.cpp test/utf8.t.cpp
This commit is contained in:
commit
98f740e9d1
550 changed files with 6129 additions and 2976 deletions
|
@ -1,7 +1,7 @@
|
|||
////////////////////////////////////////////////////////////////////////////////
|
||||
// taskwarrior - a command line task list manager.
|
||||
//
|
||||
// Copyright 2006-2013, Paul Beckingham, Federico Hernandez.
|
||||
// Copyright 2006-2014, 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
|
||||
|
@ -25,6 +25,7 @@
|
|||
//
|
||||
////////////////////////////////////////////////////////////////////////////////
|
||||
|
||||
#include <cmake.h>
|
||||
#include <iostream>
|
||||
#include <vector>
|
||||
#include <stdlib.h>
|
||||
|
@ -35,7 +36,6 @@
|
|||
#include <util.h>
|
||||
#include <i18n.h>
|
||||
#include <Command.h>
|
||||
#include <cmake.h>
|
||||
#include <main.h>
|
||||
|
||||
#include <CmdAdd.h>
|
||||
|
@ -61,6 +61,7 @@
|
|||
#include <CmdExec.h>
|
||||
#endif
|
||||
#include <CmdExport.h>
|
||||
#include <CmdGet.h>
|
||||
#include <CmdHelp.h>
|
||||
#include <CmdHistory.h>
|
||||
#include <CmdIDs.h>
|
||||
|
@ -133,6 +134,7 @@ void Command::factory (std::map <std::string, Command*>& all)
|
|||
c = new CmdExec (); all[c->keyword ()] = c;
|
||||
#endif
|
||||
c = new CmdExport (); all[c->keyword ()] = c;
|
||||
c = new CmdGet (); all[c->keyword ()] = c;
|
||||
c = new CmdGHistoryMonthly (); all[c->keyword ()] = c;
|
||||
c = new CmdGHistoryAnnual (); all[c->keyword ()] = c;
|
||||
c = new CmdHelp (); all[c->keyword ()] = c;
|
||||
|
@ -170,16 +172,13 @@ void Command::factory (std::map <std::string, Command*>& all)
|
|||
c = new CmdZshCompletionUuids (); all[c->keyword ()] = c;
|
||||
|
||||
// Instantiate a command object for each custom report.
|
||||
std::vector <std::string> variables;
|
||||
context.config.all (variables);
|
||||
|
||||
std::vector <std::string> reports;
|
||||
std::vector <std::string>::iterator i;
|
||||
for (i = variables.begin (); i != variables.end (); ++i)
|
||||
Config::const_iterator i;
|
||||
for (i = context.config.begin (); i != context.config.end (); ++i)
|
||||
{
|
||||
if (i->substr (0, 7) == "report.")
|
||||
if (i->first.substr (0, 7) == "report.")
|
||||
{
|
||||
std::string report = i->substr (7);
|
||||
std::string report = i->first.substr (7);
|
||||
std::string::size_type columns = report.find (".columns");
|
||||
if (columns != std::string::npos)
|
||||
reports.push_back (report.substr (0, columns));
|
||||
|
@ -465,200 +464,8 @@ void Command::modify_task (
|
|||
const A3& arguments,
|
||||
std::string& description)
|
||||
{
|
||||
// Coalesce arguments together into sets to be processed as a batch.
|
||||
unsigned int pos = 0;
|
||||
Arg arg;
|
||||
while (next_mod_group (arguments, arg, pos))
|
||||
{
|
||||
// Attributes are essentially name:value pairs, and correspond directly
|
||||
// to stored attributes.
|
||||
if (arg._category == Arg::cat_attr)
|
||||
{
|
||||
std::string name;
|
||||
std::string value;
|
||||
A3::extract_attr (arg._raw, name, value);
|
||||
if (A3::is_attribute (name, name)) // Canonicalize
|
||||
{
|
||||
//std::cout << "# Command::modify_task name='" << name << "' value='" << value << "'\n";
|
||||
|
||||
// Get the column info.
|
||||
Column* column = context.columns[name];
|
||||
|
||||
if (value == "")
|
||||
{
|
||||
task.remove (name);
|
||||
}
|
||||
else
|
||||
{
|
||||
// Dependencies are used as IDs.
|
||||
if (name == "depends")
|
||||
{
|
||||
// Parse IDs
|
||||
std::vector <std::string> deps;
|
||||
split (deps, value, ',');
|
||||
|
||||
// Apply or remove dendencies in turn.
|
||||
std::vector <std::string>::iterator i;
|
||||
for (i = deps.begin (); i != deps.end (); i++)
|
||||
{
|
||||
bool removal = false;
|
||||
std::string& dep = *i;
|
||||
|
||||
if (dep[0] == '-')
|
||||
{
|
||||
removal = true;
|
||||
dep = i->substr(1, std::string::npos);
|
||||
}
|
||||
|
||||
std::vector <int> ids;
|
||||
// Crude UUID check
|
||||
if (dep.length () == 36)
|
||||
{
|
||||
int id = context.tdb2.pending.id (dep);
|
||||
ids.push_back (id);
|
||||
}
|
||||
else
|
||||
A3::extract_id (dep, ids);
|
||||
|
||||
std::vector <int>::iterator id;
|
||||
for (id = ids.begin (); id != ids.end(); id++)
|
||||
if (removal)
|
||||
task.removeDependency (*id);
|
||||
else
|
||||
task.addDependency (*id);
|
||||
}
|
||||
}
|
||||
|
||||
// Priorities are converted to upper case.
|
||||
else if (name == "priority")
|
||||
{
|
||||
task.set (name, upperCase (value));
|
||||
}
|
||||
|
||||
// Dates are special, maybe.
|
||||
else if (column->type () == "date")
|
||||
{
|
||||
// All values must be eval'd first.
|
||||
A3 value_tokens;
|
||||
value_tokens.capture (value);
|
||||
value_tokens = value_tokens.postfix (value_tokens.tokenize (value_tokens));
|
||||
|
||||
E9 e (value_tokens);
|
||||
std::string result = e.evalExpression (task);
|
||||
context.debug (std::string ("Eval '") + value + "' --> '" + result + "'");
|
||||
|
||||
// If the date value is less than 5 years, it is a duration, not a
|
||||
// date, therefore add 'now'.
|
||||
long l = (long) strtod (result.c_str (), NULL);
|
||||
if (labs (l) < 5 * 365 * 86400)
|
||||
{
|
||||
OldDuration dur (value);
|
||||
Date now;
|
||||
now += l;
|
||||
task.set (name, now.toEpochString ());
|
||||
}
|
||||
else
|
||||
{
|
||||
Date d (result, context.config.get ("dateformat"));
|
||||
task.set (name, d.toEpochString ());
|
||||
}
|
||||
}
|
||||
|
||||
// OldDurations too.
|
||||
else if (name == "recur" ||
|
||||
column->type () == "duration")
|
||||
{
|
||||
// All values must be eval'd first, in this case, just to catch errors.
|
||||
A3 value_tokens;
|
||||
value_tokens.capture (value);
|
||||
value_tokens = value_tokens.postfix (value_tokens.tokenize (value_tokens));
|
||||
|
||||
E9 e (value_tokens);
|
||||
std::string result = e.evalExpression (task);
|
||||
context.debug (std::string ("Eval '") + value + "' --> '" + result + "'");
|
||||
|
||||
OldDuration d (value);
|
||||
|
||||
// Deliberately storing the 'raw' value, which is necessary for
|
||||
// durations like 'weekday'..
|
||||
task.set (name, name == "recur" ? value : result);
|
||||
}
|
||||
|
||||
// Need handling for numeric types, used by UDAs.
|
||||
else if (column->type () == "numeric")
|
||||
{
|
||||
A3 value_tokens;
|
||||
value_tokens.capture (value);
|
||||
value_tokens = value_tokens.postfix (value_tokens.tokenize (value_tokens));
|
||||
|
||||
E9 e (value_tokens);
|
||||
std::string result = e.evalExpression (task);
|
||||
context.debug (std::string ("Eval '") + value + "' --> '" + result + "'");
|
||||
|
||||
Nibbler n (result);
|
||||
double d;
|
||||
if (n.getNumber (d) &&
|
||||
n.depleted ())
|
||||
task.set (name, result);
|
||||
else
|
||||
throw format (STRING_UDA_NUMERIC, result);
|
||||
}
|
||||
|
||||
// By default, just add/remove it.
|
||||
else
|
||||
{
|
||||
if (column->validate (value))
|
||||
task.set (name, value);
|
||||
else
|
||||
throw format (STRING_INVALID_MOD, name, value);
|
||||
}
|
||||
|
||||
// Warn about deprecated/obsolete attribute usage.
|
||||
legacyAttributeCheck (name);
|
||||
}
|
||||
}
|
||||
else
|
||||
throw format (STRING_CMD_ADD_BAD_ATTRIBUTE, name);
|
||||
}
|
||||
|
||||
// Tags need special handling because they are essentially a vector stored
|
||||
// in a single string, therefore Task::{add,remove}Tag must be called as
|
||||
// appropriate.
|
||||
else if (arg._category == Arg::cat_tag)
|
||||
{
|
||||
char type;
|
||||
std::string value;
|
||||
A3::extract_tag (arg._raw, type, value);
|
||||
|
||||
if (type == '+')
|
||||
{
|
||||
task.addTag (value);
|
||||
feedback_special_tags (task, value);
|
||||
}
|
||||
else
|
||||
task.removeTag (value);
|
||||
}
|
||||
|
||||
// Substitutions.
|
||||
else if (arg._category == Arg::cat_subst)
|
||||
{
|
||||
std::string from;
|
||||
std::string to;
|
||||
bool global;
|
||||
A3::extract_subst (arg._raw, from, to, global);
|
||||
task.substitute (from, to, global);
|
||||
}
|
||||
|
||||
// Anything else is essentially downgraded to 'word' and considered part of
|
||||
// the description.
|
||||
else
|
||||
{
|
||||
if (description.length ())
|
||||
description += " ";
|
||||
|
||||
description += arg._raw;
|
||||
}
|
||||
}
|
||||
// Utilize Task::modify
|
||||
task.modify (arguments, description);
|
||||
}
|
||||
|
||||
////////////////////////////////////////////////////////////////////////////////
|
||||
|
@ -742,27 +549,3 @@ bool Command::permission (
|
|||
}
|
||||
|
||||
////////////////////////////////////////////////////////////////////////////////
|
||||
// Special processing for modifications.
|
||||
bool Command::next_mod_group (const A3& input, Arg& arg, unsigned int& pos)
|
||||
{
|
||||
if (pos < input.size ())
|
||||
{
|
||||
arg = input[pos++];
|
||||
|
||||
if (arg._raw == "depends")
|
||||
{
|
||||
while (pos < input.size () &&
|
||||
(input[pos]._category == Arg::cat_op ||
|
||||
input[pos]._type == Arg::type_number))
|
||||
{
|
||||
arg._raw += input[pos++]._raw;
|
||||
}
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
////////////////////////////////////////////////////////////////////////////////
|
||||
|
|
Loading…
Add table
Add a link
Reference in a new issue