add initial bulk run from pre-commit over all files

This commit is contained in:
Felix Schurk
2024-07-29 22:34:51 +02:00
parent 665aeeef61
commit 93356b39c3
418 changed files with 21354 additions and 23858 deletions

File diff suppressed because it is too large Load Diff

View File

@@ -26,100 +26,98 @@
#ifndef INCLUDED_CLI2
#define INCLUDED_CLI2
#include <string>
#include <vector>
#include <map>
#include <unordered_map>
#include <Lexer.h>
#include <FS.h>
#include <Lexer.h>
#include <map>
#include <string>
#include <unordered_map>
#include <vector>
// Represents a single argument.
class A2
{
public:
A2 (const std::string&, Lexer::Type);
A2 (const A2&);
A2& operator= (const A2&);
bool hasTag (const std::string&) const;
void tag (const std::string&);
void unTag (const std::string&);
void attribute (const std::string&, const std::string&);
const std::string attribute (const std::string&) const;
const std::string getToken () const;
const std::string dump () const;
void decompose ();
class A2 {
public:
A2(const std::string&, Lexer::Type);
A2(const A2&);
A2& operator=(const A2&);
bool hasTag(const std::string&) const;
void tag(const std::string&);
void unTag(const std::string&);
void attribute(const std::string&, const std::string&);
const std::string attribute(const std::string&) const;
const std::string getToken() const;
const std::string dump() const;
void decompose();
public:
Lexer::Type _lextype {Lexer::Type::word};
std::vector <std::string> _tags {};
std::map <std::string, std::string> _attributes {};
public:
Lexer::Type _lextype{Lexer::Type::word};
std::vector<std::string> _tags{};
std::map<std::string, std::string> _attributes{};
};
// Represents the command line.
class CLI2
{
public:
class CLI2 {
public:
static int minimumMatchLength;
static bool getOverride (int, const char**, File&);
static bool getDataLocation (int, const char**, Path&);
static void applyOverrides (int, const char**);
static bool getOverride(int, const char**, File&);
static bool getDataLocation(int, const char**, Path&);
static void applyOverrides(int, const char**);
public:
CLI2 () = default;
void alias (const std::string&, const std::string&);
void entity (const std::string&, const std::string&);
public:
CLI2() = default;
void alias(const std::string&, const std::string&);
void entity(const std::string&, const std::string&);
void add (const std::string&);
void add (const std::vector <std::string>&, int offset = 0);
void analyze ();
void addFilter (const std::string& arg);
void addModifications (const std::string& arg);
void addContext (bool readable, bool writeable);
void prepareFilter ();
const std::vector <std::string> getWords ();
const std::vector <A2> getMiscellaneous ();
bool canonicalize (std::string&, const std::string&, const std::string&);
std::string getBinary () const;
std::string getCommand (bool canonical = true) const;
const std::string dump (const std::string& title = "CLI2 Parser") const;
void add(const std::string&);
void add(const std::vector<std::string>&, int offset = 0);
void analyze();
void addFilter(const std::string& arg);
void addModifications(const std::string& arg);
void addContext(bool readable, bool writeable);
void prepareFilter();
const std::vector<std::string> getWords();
const std::vector<A2> getMiscellaneous();
bool canonicalize(std::string&, const std::string&, const std::string&);
std::string getBinary() const;
std::string getCommand(bool canonical = true) const;
const std::string dump(const std::string& title = "CLI2 Parser") const;
private:
void handleArg0 ();
void lexArguments ();
void demotion ();
void aliasExpansion ();
void canonicalizeNames ();
void categorizeArgs ();
void parenthesizeOriginalFilter ();
bool findCommand ();
bool exactMatch (const std::string&, const std::string&) const;
void desugarFilterTags ();
void findStrayModifications ();
void desugarFilterAttributes ();
void desugarFilterPatterns ();
void findIDs ();
void findUUIDs ();
void insertIDExpr ();
void lexFilterArgs ();
bool isEmptyParenExpression (std::vector<A2>::iterator it, bool forward = true) const;
void desugarFilterPlainArgs ();
void insertJunctions ();
void defaultCommand ();
std::vector <A2> lexExpression (const std::string&);
private:
void handleArg0();
void lexArguments();
void demotion();
void aliasExpansion();
void canonicalizeNames();
void categorizeArgs();
void parenthesizeOriginalFilter();
bool findCommand();
bool exactMatch(const std::string&, const std::string&) const;
void desugarFilterTags();
void findStrayModifications();
void desugarFilterAttributes();
void desugarFilterPatterns();
void findIDs();
void findUUIDs();
void insertIDExpr();
void lexFilterArgs();
bool isEmptyParenExpression(std::vector<A2>::iterator it, bool forward = true) const;
void desugarFilterPlainArgs();
void insertJunctions();
void defaultCommand();
std::vector<A2> lexExpression(const std::string&);
public:
std::multimap <std::string, std::string> _entities {};
std::map <std::string, std::string> _aliases {};
std::unordered_map <int, std::string> _canonical_cache {};
std::vector <A2> _original_args {};
std::vector <A2> _args {};
public:
std::multimap<std::string, std::string> _entities{};
std::map<std::string, std::string> _aliases{};
std::unordered_map<int, std::string> _canonical_cache{};
std::vector<A2> _original_args{};
std::vector<A2> _args{};
std::vector <std::pair <std::string, std::string>> _id_ranges {};
std::vector <std::string> _uuid_list {};
std::string _command {""};
bool _context_added {false};
std::vector<std::pair<std::string, std::string>> _id_ranges{};
std::vector<std::string> _uuid_list{};
std::string _command{""};
bool _context_added{false};
};
#endif

File diff suppressed because it is too large Load Diff

View File

@@ -27,111 +27,111 @@
#ifndef INCLUDED_CONTEXT
#define INCLUDED_CONTEXT
#include <Command.h>
#include <Column.h>
#include <Configuration.h>
#include <Task.h>
#include <TDB2.h>
#include <Hooks.h>
#include <FS.h>
#include <CLI2.h>
#include <Column.h>
#include <Command.h>
#include <Configuration.h>
#include <FS.h>
#include <Hooks.h>
#include <TDB2.h>
#include <Task.h>
#include <Timer.h>
#include <set>
class CurrentTask;
class Context
{
public:
Context () = default; // Default constructor
~Context (); // Destructor
class Context {
public:
Context() = default; // Default constructor
~Context(); // Destructor
Context (const Context&);
Context& operator= (const Context&);
Context(const Context &);
Context &operator=(const Context &);
static Context& getContext ();
static void setContext (Context*);
static Context &getContext();
static void setContext(Context *);
int initialize (int, const char**); // all startup
int run ();
int dispatch (std::string&); // command handler dispatch
int initialize(int, const char **); // all startup
int run();
int dispatch(std::string &); // command handler dispatch
int getWidth (); // determine terminal width
int getHeight (); // determine terminal height
int getWidth(); // determine terminal width
int getHeight(); // determine terminal height
std::string getTaskContext (const std::string&, std::string, bool fallback=true);
std::string getTaskContext(const std::string &, std::string, bool fallback = true);
const std::vector <std::string> getColumns () const;
void getLimits (int&, int&);
const std::vector<std::string> getColumns() const;
void getLimits(int &, int &);
bool color (); // TTY or <other>?
bool verbose (const std::string&); // Verbosity control
bool color(); // TTY or <other>?
bool verbose(const std::string &); // Verbosity control
void header (const std::string&); // Header message sink
void footnote (const std::string&); // Footnote message sink
void debug (const std::string&); // Debug message sink
void error (const std::string&); // Error message sink - non-maskable
void header(const std::string &); // Header message sink
void footnote(const std::string &); // Footnote message sink
void debug(const std::string &); // Debug message sink
void error(const std::string &); // Error message sink - non-maskable
void decomposeSortField (const std::string&, std::string&, bool&, bool&);
void debugTiming (const std::string&, const Timer&);
void decomposeSortField(const std::string &, std::string &, bool &, bool &);
void debugTiming(const std::string &, const Timer &);
CurrentTask withCurrentTask (const Task *);
CurrentTask withCurrentTask(const Task *);
friend class CurrentTask;
private:
void staticInitialization ();
void createDefaultConfig ();
void updateXtermTitle ();
void updateVerbosity ();
void loadAliases ();
void propagateDebug ();
private:
void staticInitialization();
void createDefaultConfig();
void updateXtermTitle();
void updateVerbosity();
void loadAliases();
void propagateDebug();
static Context* context;
static Context *context;
public:
CLI2 cli2 {};
std::string home_dir {};
File rc_file {"~/.taskrc"};
Path data_dir {"~/.task"};
Configuration config {};
TDB2 tdb2 {};
Hooks hooks {};
bool determine_color_use {true};
bool use_color {true};
bool verbosity_legacy {false};
std::set <std::string> verbosity {};
std::vector <std::string> headers {};
std::vector <std::string> footnotes {};
std::vector <std::string> errors {};
std::vector <std::string> debugMessages {};
std::map <std::string, Command*> commands {};
std::map <std::string, Column*> columns {};
int terminal_width {0};
int terminal_height {0};
public:
CLI2 cli2{};
std::string home_dir{};
File rc_file{"~/.taskrc"};
Path data_dir{"~/.task"};
Configuration config{};
TDB2 tdb2{};
Hooks hooks{};
bool determine_color_use{true};
bool use_color{true};
bool verbosity_legacy{false};
std::set<std::string> verbosity{};
std::vector<std::string> headers{};
std::vector<std::string> footnotes{};
std::vector<std::string> errors{};
std::vector<std::string> debugMessages{};
std::map<std::string, Command *> commands{};
std::map<std::string, Column *> columns{};
int terminal_width{0};
int terminal_height{0};
Timer timer_total {};
long time_total_us {0};
long time_init_us {0};
long time_load_us {0};
long time_gc_us {0};
long time_filter_us {0};
long time_commit_us {0};
long time_sort_us {0};
long time_render_us {0};
long time_hooks_us {0};
Timer timer_total{};
long time_total_us{0};
long time_init_us{0};
long time_load_us{0};
long time_gc_us{0};
long time_filter_us{0};
long time_commit_us{0};
long time_sort_us{0};
long time_render_us{0};
long time_hooks_us{0};
// the current task for DOM references, or NULL if there is no task
const Task * currentTask {NULL};
const Task *currentTask{NULL};
};
////////////////////////////////////////////////////////////////////////////////
// CurrentTask resets Context::currentTask to previous context task on destruction; this ensures
// that this context value is restored when exiting the scope where the context was applied.
class CurrentTask {
public:
public:
~CurrentTask();
private:
private:
CurrentTask(Context &context, const Task *previous);
Context &context;

View File

@@ -27,19 +27,20 @@
#include <cmake.h>
// cmake.h include header must come first
#include <DOM.h>
#include <sstream>
#include <map>
#include <stdlib.h>
#include <Variant.h>
#include <Lexer.h>
#include <Context.h>
#include <DOM.h>
#include <Datetime.h>
#include <Duration.h>
#include <shared.h>
#include <Lexer.h>
#include <Variant.h>
#include <format.h>
#include <shared.h>
#include <stdlib.h>
#include <util.h>
#include <map>
#include <sstream>
////////////////////////////////////////////////////////////////////////////////
// DOM Supported References:
//
@@ -62,23 +63,18 @@
// system.version
// system.os
//
bool getDOM (const std::string& name, Variant& value)
{
bool getDOM(const std::string& name, Variant& value) {
// Special case, blank refs cause problems.
if (name == "")
return false;
if (name == "") return false;
auto len = name.length ();
auto len = name.length();
// rc. --> context.config
if (len > 3 &&
! name.compare (0, 3, "rc.", 3))
{
auto key = name.substr (3);
auto c = Context::getContext ().config.find (key);
if (c != Context::getContext ().config.end ())
{
value = Variant (c->second);
if (len > 3 && !name.compare(0, 3, "rc.", 3)) {
auto key = name.substr(3);
auto c = Context::getContext().config.find(key);
if (c != Context::getContext().config.end()) {
value = Variant(c->second);
return true;
}
@@ -86,55 +82,41 @@ bool getDOM (const std::string& name, Variant& value)
}
// tw.*
if (len > 3 &&
! name.compare (0, 3, "tw.", 3))
{
if (name == "tw.syncneeded")
{
value = Variant (0);
if (Context::getContext ().tdb2.num_local_changes () > 0) {
value = Variant (1);
if (len > 3 && !name.compare(0, 3, "tw.", 3)) {
if (name == "tw.syncneeded") {
value = Variant(0);
if (Context::getContext().tdb2.num_local_changes() > 0) {
value = Variant(1);
}
return true;
}
else if (name == "tw.program")
{
value = Variant (Context::getContext ().cli2.getBinary ());
} else if (name == "tw.program") {
value = Variant(Context::getContext().cli2.getBinary());
return true;
}
else if (name == "tw.args")
{
} else if (name == "tw.args") {
std::string commandLine;
for (auto& arg : Context::getContext ().cli2._original_args)
{
if (commandLine != "")
commandLine += ' ';
for (auto& arg : Context::getContext().cli2._original_args) {
if (commandLine != "") commandLine += ' ';
commandLine += arg.attribute("raw");
}
value = Variant (commandLine);
value = Variant(commandLine);
return true;
}
else if (name == "tw.width")
{
value = Variant (static_cast<int> (Context::getContext ().terminal_width
? Context::getContext ().terminal_width
: Context::getContext ().getWidth ()));
} else if (name == "tw.width") {
value = Variant(static_cast<int>(Context::getContext().terminal_width
? Context::getContext().terminal_width
: Context::getContext().getWidth()));
return true;
}
else if (name == "tw.height")
{
value = Variant (static_cast<int> (Context::getContext ().terminal_height
? Context::getContext ().terminal_height
: Context::getContext ().getHeight ()));
} else if (name == "tw.height") {
value = Variant(static_cast<int>(Context::getContext().terminal_height
? Context::getContext().terminal_height
: Context::getContext().getHeight()));
return true;
}
else if (name == "tw.version")
{
value = Variant (VERSION);
else if (name == "tw.version") {
value = Variant(VERSION);
return true;
}
@@ -142,40 +124,29 @@ bool getDOM (const std::string& name, Variant& value)
}
// context.*
if (len > 8 &&
! name.compare (0, 8, "context.", 8))
{
if (name == "context.program")
{
value = Variant (Context::getContext ().cli2.getBinary ());
if (len > 8 && !name.compare(0, 8, "context.", 8)) {
if (name == "context.program") {
value = Variant(Context::getContext().cli2.getBinary());
return true;
}
else if (name == "context.args")
{
} else if (name == "context.args") {
std::string commandLine;
for (auto& arg : Context::getContext ().cli2._original_args)
{
if (commandLine != "")
commandLine += ' ';
for (auto& arg : Context::getContext().cli2._original_args) {
if (commandLine != "") commandLine += ' ';
commandLine += arg.attribute("raw");
}
value = Variant (commandLine);
value = Variant(commandLine);
return true;
}
else if (name == "context.width")
{
value = Variant (static_cast<int> (Context::getContext ().terminal_width
? Context::getContext ().terminal_width
: Context::getContext ().getWidth ()));
} else if (name == "context.width") {
value = Variant(static_cast<int>(Context::getContext().terminal_width
? Context::getContext().terminal_width
: Context::getContext().getWidth()));
return true;
}
else if (name == "context.height")
{
value = Variant (static_cast<int> (Context::getContext ().terminal_height
? Context::getContext ().terminal_height
: Context::getContext ().getHeight ()));
} else if (name == "context.height") {
value = Variant(static_cast<int>(Context::getContext().terminal_height
? Context::getContext().terminal_height
: Context::getContext().getHeight()));
return true;
}
@@ -183,20 +154,16 @@ bool getDOM (const std::string& name, Variant& value)
}
// system. --> Implement locally.
if (len > 7 &&
! name.compare (0, 7, "system.", 7))
{
if (len > 7 && !name.compare(0, 7, "system.", 7)) {
// Taskwarrior version number.
if (name == "system.version")
{
value = Variant (VERSION);
if (name == "system.version") {
value = Variant(VERSION);
return true;
}
// OS type.
else if (name == "system.os")
{
value = Variant (osName ());
else if (name == "system.os") {
value = Variant(osName());
return true;
}
@@ -239,210 +206,192 @@ bool getDOM (const std::string& name, Variant& value)
//
// If task is NULL, then the contextual task will be determined from the DOM
// string, if any exists.
bool getDOM (const std::string& name, const Task* task, Variant& value)
{
bool getDOM(const std::string& name, const Task* task, Variant& value) {
// Special case, blank refs cause problems.
if (name == "")
return false;
if (name == "") return false;
// Quickly deal with the most common cases.
if (task && name == "id")
{
value = Variant (static_cast<int> (task->id));
if (task && name == "id") {
value = Variant(static_cast<int>(task->id));
return true;
}
if (task && name == "urgency")
{
value = Variant (task->urgency_c ());
if (task && name == "urgency") {
value = Variant(task->urgency_c());
return true;
}
// split name on '.'
auto elements = split (name, '.');
auto elements = split(name, '.');
Task loaded_task;
// decide whether the reference is going to be the passed
// "task" or whether it's going to be a newly loaded task (if id/uuid was
// given).
const Task* ref = task;
Lexer lexer (elements[0]);
Lexer lexer(elements[0]);
std::string token;
Lexer::Type type;
// If this can be ID/UUID reference (the name contains '.'),
// lex it to figure out. Otherwise don't lex, as lexing can be slow.
if ((elements.size() > 1) and lexer.token (token, type))
{
if ((elements.size() > 1) and lexer.token(token, type)) {
bool reloaded = false;
if (type == Lexer::Type::uuid &&
token.length () == elements[0].length ())
{
if (!task || token != task->get ("uuid"))
{
if (Context::getContext ().tdb2.get (token, loaded_task))
reloaded = true;
if (type == Lexer::Type::uuid && token.length() == elements[0].length()) {
if (!task || token != task->get("uuid")) {
if (Context::getContext().tdb2.get(token, loaded_task)) reloaded = true;
}
// Eat elements[0]/UUID.
elements.erase (elements.begin ());
}
else if (type == Lexer::Type::number &&
token.find ('.') == std::string::npos)
{
auto id = strtol (token.c_str (), nullptr, 10);
if (id && (!task || id != task->id))
{
if (Context::getContext ().tdb2.get (id, loaded_task))
reloaded = true;
elements.erase(elements.begin());
} else if (type == Lexer::Type::number && token.find('.') == std::string::npos) {
auto id = strtol(token.c_str(), nullptr, 10);
if (id && (!task || id != task->id)) {
if (Context::getContext().tdb2.get(id, loaded_task)) reloaded = true;
}
// Eat elements[0]/ID.
elements.erase (elements.begin ());
elements.erase(elements.begin());
}
if (reloaded)
ref = &loaded_task;
if (reloaded) ref = &loaded_task;
}
// The remainder of this method requires a contextual task, so if we do not
// have one, delegate to the two-argument getDOM
if (!ref)
return getDOM (name, value);
if (!ref) return getDOM(name, value);
auto size = elements.size ();
auto size = elements.size();
std::string canonical;
if ((size == 1 || size == 2) && Context::getContext ().cli2.canonicalize (canonical, "attribute", elements[0]))
{
if ((size == 1 || size == 2) &&
Context::getContext().cli2.canonicalize(canonical, "attribute", elements[0])) {
// Now that 'ref' is the contextual task, and any ID/UUID is chopped off the
// elements vector, DOM resolution is now simple.
if (size == 1 && canonical == "id")
{
value = Variant (static_cast<int> (ref->id));
if (size == 1 && canonical == "id") {
value = Variant(static_cast<int>(ref->id));
return true;
}
if (size == 1 && canonical == "urgency")
{
value = Variant (ref->urgency_c ());
if (size == 1 && canonical == "urgency") {
value = Variant(ref->urgency_c());
return true;
}
// Special handling of status required for virtual waiting status
// implementation. Remove in 3.0.0.
if (size == 1 && canonical == "status")
{
value = Variant (ref->statusToText (ref->getStatus ()));
if (size == 1 && canonical == "status") {
value = Variant(ref->statusToText(ref->getStatus()));
return true;
}
Column* column = Context::getContext ().columns[canonical];
Column* column = Context::getContext().columns[canonical];
if (size == 1 && column)
{
if (column->is_uda () && ! ref->has (canonical))
{
value = Variant ("");
if (size == 1 && column) {
if (column->is_uda() && !ref->has(canonical)) {
value = Variant("");
return true;
}
if (column->type () == "date")
{
auto numeric = ref->get_date (canonical);
if (column->type() == "date") {
auto numeric = ref->get_date(canonical);
if (numeric == 0)
value = Variant ("");
value = Variant("");
else
value = Variant (numeric, Variant::type_date);
}
else if (column->type () == "duration" || canonical == "recur")
{
auto period = ref->get (canonical);
value = Variant(numeric, Variant::type_date);
} else if (column->type() == "duration" || canonical == "recur") {
auto period = ref->get(canonical);
Duration iso;
std::string::size_type cursor = 0;
if (iso.parse (period, cursor))
value = Variant (iso.toTime_t (), Variant::type_duration);
if (iso.parse(period, cursor))
value = Variant(iso.toTime_t(), Variant::type_duration);
else
value = Variant (Duration (ref->get (canonical)).toTime_t (), Variant::type_duration);
}
else if (column->type () == "numeric")
value = Variant (ref->get_float (canonical));
value = Variant(Duration(ref->get(canonical)).toTime_t(), Variant::type_duration);
} else if (column->type() == "numeric")
value = Variant(ref->get_float(canonical));
else
value = Variant (ref->get (canonical));
value = Variant(ref->get(canonical));
return true;
}
if (size == 2 && canonical == "tags")
{
value = Variant (ref->hasTag (elements[1]) ? elements[1] : "");
if (size == 2 && canonical == "tags") {
value = Variant(ref->hasTag(elements[1]) ? elements[1] : "");
return true;
}
if (size == 2 && column && column->type () == "date")
{
Datetime date (ref->get_date (canonical));
if (elements[1] == "year") { value = Variant (static_cast<int> (date.year ())); return true; }
else if (elements[1] == "month") { value = Variant (static_cast<int> (date.month ())); return true; }
else if (elements[1] == "day") { value = Variant (static_cast<int> (date.day ())); return true; }
else if (elements[1] == "week") { value = Variant (static_cast<int> (date.week ())); return true; }
else if (elements[1] == "weekday") { value = Variant (static_cast<int> (date.dayOfWeek ())); return true; }
else if (elements[1] == "julian") { value = Variant (static_cast<int> (date.dayOfYear ())); return true; }
else if (elements[1] == "hour") { value = Variant (static_cast<int> (date.hour ())); return true; }
else if (elements[1] == "minute") { value = Variant (static_cast<int> (date.minute ())); return true; }
else if (elements[1] == "second") { value = Variant (static_cast<int> (date.second ())); return true; }
if (size == 2 && column && column->type() == "date") {
Datetime date(ref->get_date(canonical));
if (elements[1] == "year") {
value = Variant(static_cast<int>(date.year()));
return true;
} else if (elements[1] == "month") {
value = Variant(static_cast<int>(date.month()));
return true;
} else if (elements[1] == "day") {
value = Variant(static_cast<int>(date.day()));
return true;
} else if (elements[1] == "week") {
value = Variant(static_cast<int>(date.week()));
return true;
} else if (elements[1] == "weekday") {
value = Variant(static_cast<int>(date.dayOfWeek()));
return true;
} else if (elements[1] == "julian") {
value = Variant(static_cast<int>(date.dayOfYear()));
return true;
} else if (elements[1] == "hour") {
value = Variant(static_cast<int>(date.hour()));
return true;
} else if (elements[1] == "minute") {
value = Variant(static_cast<int>(date.minute()));
return true;
} else if (elements[1] == "second") {
value = Variant(static_cast<int>(date.second()));
return true;
}
}
}
if (size == 2 && elements[0] == "annotations" && elements[1] == "count")
{
value = Variant (static_cast<int> (ref->getAnnotationCount ()));
if (size == 2 && elements[0] == "annotations" && elements[1] == "count") {
value = Variant(static_cast<int>(ref->getAnnotationCount()));
return true;
}
if (size == 3 && elements[0] == "annotations")
{
auto annos = ref->getAnnotations ();
if (size == 3 && elements[0] == "annotations") {
auto annos = ref->getAnnotations();
int a = strtol (elements[1].c_str (), nullptr, 10);
int a = strtol(elements[1].c_str(), nullptr, 10);
int count = 0;
// Count off the 'a'th annotation.
for (const auto& i : annos)
{
if (++count == a)
{
if (elements[2] == "entry")
{
for (const auto& i : annos) {
if (++count == a) {
if (elements[2] == "entry") {
// annotation_1234567890
// 0 ^11
value = Variant ((time_t) strtoll (i.first.substr (11).c_str (), NULL, 10), Variant::type_date);
value =
Variant((time_t)strtoll(i.first.substr(11).c_str(), NULL, 10), Variant::type_date);
return true;
}
else if (elements[2] == "description")
{
value = Variant (i.second);
} else if (elements[2] == "description") {
value = Variant(i.second);
return true;
}
}
}
}
if (size == 4 && elements[0] == "annotations" && elements[2] == "entry")
{
auto annos = ref->getAnnotations ();
if (size == 4 && elements[0] == "annotations" && elements[2] == "entry") {
auto annos = ref->getAnnotations();
int a = strtol (elements[1].c_str (), nullptr, 10);
int a = strtol(elements[1].c_str(), nullptr, 10);
int count = 0;
// Count off the 'a'th annotation.
for (const auto& i : annos)
{
if (++count == a)
{
for (const auto& i : annos) {
if (++count == a) {
// <annotations>.<N>.entry.year
// <annotations>.<N>.entry.month
// <annotations>.<N>.entry.day
@@ -452,22 +401,41 @@ bool getDOM (const std::string& name, const Task* task, Variant& value)
// <annotations>.<N>.entry.hour
// <annotations>.<N>.entry.minute
// <annotations>.<N>.entry.second
Datetime date (i.first.substr (11));
if (elements[3] == "year") { value = Variant (static_cast<int> (date.year ())); return true; }
else if (elements[3] == "month") { value = Variant (static_cast<int> (date.month ())); return true; }
else if (elements[3] == "day") { value = Variant (static_cast<int> (date.day ())); return true; }
else if (elements[3] == "week") { value = Variant (static_cast<int> (date.week ())); return true; }
else if (elements[3] == "weekday") { value = Variant (static_cast<int> (date.dayOfWeek ())); return true; }
else if (elements[3] == "julian") { value = Variant (static_cast<int> (date.dayOfYear ())); return true; }
else if (elements[3] == "hour") { value = Variant (static_cast<int> (date.hour ())); return true; }
else if (elements[3] == "minute") { value = Variant (static_cast<int> (date.minute ())); return true; }
else if (elements[3] == "second") { value = Variant (static_cast<int> (date.second ())); return true; }
Datetime date(i.first.substr(11));
if (elements[3] == "year") {
value = Variant(static_cast<int>(date.year()));
return true;
} else if (elements[3] == "month") {
value = Variant(static_cast<int>(date.month()));
return true;
} else if (elements[3] == "day") {
value = Variant(static_cast<int>(date.day()));
return true;
} else if (elements[3] == "week") {
value = Variant(static_cast<int>(date.week()));
return true;
} else if (elements[3] == "weekday") {
value = Variant(static_cast<int>(date.dayOfWeek()));
return true;
} else if (elements[3] == "julian") {
value = Variant(static_cast<int>(date.dayOfYear()));
return true;
} else if (elements[3] == "hour") {
value = Variant(static_cast<int>(date.hour()));
return true;
} else if (elements[3] == "minute") {
value = Variant(static_cast<int>(date.minute()));
return true;
} else if (elements[3] == "second") {
value = Variant(static_cast<int>(date.second()));
return true;
}
}
}
}
// Delegate to the context-free version of DOM::get.
return getDOM (name, value);
return getDOM(name, value);
}
////////////////////////////////////////////////////////////////////////////////
@@ -513,41 +481,28 @@ bool getDOM (const std::string& name, const Task* task, Variant& value)
// This makes the DOM class a reusible object.
////////////////////////////////////////////////////////////////////////////////
DOM::~DOM ()
{
delete _node;
DOM::~DOM() { delete _node; }
////////////////////////////////////////////////////////////////////////////////
void DOM::addSource(const std::string& reference, bool (*provider)(const std::string&, Variant&)) {
if (_node == nullptr) _node = new DOM::Node();
_node->addSource(reference, provider);
}
////////////////////////////////////////////////////////////////////////////////
void DOM::addSource (
const std::string& reference,
bool (*provider)(const std::string&, Variant&))
{
if (_node == nullptr)
_node = new DOM::Node ();
_node->addSource (reference, provider);
bool DOM::valid(const std::string& reference) const {
return _node && _node->find(reference) != nullptr;
}
////////////////////////////////////////////////////////////////////////////////
bool DOM::valid (const std::string& reference) const
{
return _node && _node->find (reference) != nullptr;
}
Variant DOM::get(const std::string& reference) const {
Variant v("");
////////////////////////////////////////////////////////////////////////////////
Variant DOM::get (const std::string& reference) const
{
Variant v ("");
if (_node)
{
auto node = _node->find (reference);
if (node != nullptr &&
node->_provider != nullptr)
{
if (node->_provider (reference, v))
return v;
if (_node) {
auto node = _node->find(reference);
if (node != nullptr && node->_provider != nullptr) {
if (node->_provider(reference, v)) return v;
}
}
@@ -555,60 +510,47 @@ Variant DOM::get (const std::string& reference) const
}
////////////////////////////////////////////////////////////////////////////////
int DOM::count () const
{
if (_node)
return _node->count ();
int DOM::count() const {
if (_node) return _node->count();
return 0;
}
////////////////////////////////////////////////////////////////////////////////
std::vector <std::string> DOM::decomposeReference (const std::string& reference)
{
return split (reference, '.');
std::vector<std::string> DOM::decomposeReference(const std::string& reference) {
return split(reference, '.');
}
////////////////////////////////////////////////////////////////////////////////
std::string DOM::dump () const
{
if (_node)
return _node->dump ();
std::string DOM::dump() const {
if (_node) return _node->dump();
return "";
}
////////////////////////////////////////////////////////////////////////////////
DOM::Node::~Node ()
{
for (auto& branch : _branches)
delete branch;
DOM::Node::~Node() {
for (auto& branch : _branches) delete branch;
}
////////////////////////////////////////////////////////////////////////////////
void DOM::Node::addSource (
const std::string& reference,
bool (*provider)(const std::string&, Variant&))
{
void DOM::Node::addSource(const std::string& reference,
bool (*provider)(const std::string&, Variant&)) {
auto cursor = this;
for (const auto& element : DOM::decomposeReference (reference))
{
auto found {false};
for (auto& branch : cursor->_branches)
{
if (branch->_name == element)
{
for (const auto& element : DOM::decomposeReference(reference)) {
auto found{false};
for (auto& branch : cursor->_branches) {
if (branch->_name == element) {
cursor = branch;
found = true;
break;
}
}
if (! found)
{
auto branch = new DOM::Node ();
if (!found) {
auto branch = new DOM::Node();
branch->_name = element;
cursor->_branches.push_back (branch);
cursor->_branches.push_back(branch);
cursor = branch;
}
}
@@ -618,85 +560,66 @@ void DOM::Node::addSource (
////////////////////////////////////////////////////////////////////////////////
// A valid reference is one that has a provider function.
bool DOM::Node::valid (const std::string& reference) const
{
return find (reference) != nullptr;
}
bool DOM::Node::valid(const std::string& reference) const { return find(reference) != nullptr; }
////////////////////////////////////////////////////////////////////////////////
const DOM::Node* DOM::Node::find (const std::string& reference) const
{
const DOM::Node* DOM::Node::find(const std::string& reference) const {
auto cursor = this;
for (const auto& element : DOM::decomposeReference (reference))
{
auto found {false};
for (auto& branch : cursor->_branches)
{
if (branch->_name == element)
{
for (const auto& element : DOM::decomposeReference(reference)) {
auto found{false};
for (auto& branch : cursor->_branches) {
if (branch->_name == element) {
cursor = branch;
found = true;
break;
}
}
if (! found)
break;
if (!found) break;
}
if (reference.length () && cursor != this)
return cursor;
if (reference.length() && cursor != this) return cursor;
return nullptr;
}
////////////////////////////////////////////////////////////////////////////////
int DOM::Node::count () const
{
int DOM::Node::count() const {
// Recurse and count the branches.
int total {0};
for (auto& branch : _branches)
{
if (branch->_provider)
++total;
total += branch->count ();
int total{0};
for (auto& branch : _branches) {
if (branch->_provider) ++total;
total += branch->count();
}
return total;
}
////////////////////////////////////////////////////////////////////////////////
std::string DOM::Node::dumpNode (
const DOM::Node* node,
int depth) const
{
std::string DOM::Node::dumpNode(const DOM::Node* node, int depth) const {
std::stringstream out;
// Indent.
out << std::string (depth * 2, ' ');
out << std::string(depth * 2, ' ');
out << "\033[31m" << node->_name << "\033[0m";
if (node->_provider)
out << " 0x" << std::hex << (long long) (void*) node->_provider;
if (node->_provider) out << " 0x" << std::hex << (long long)(void*)node->_provider;
out << '\n';
// Recurse for branches.
for (auto& b : node->_branches)
out << dumpNode (b, depth + 1);
for (auto& b : node->_branches) out << dumpNode(b, depth + 1);
return out.str ();
return out.str();
}
////////////////////////////////////////////////////////////////////////////////
std::string DOM::Node::dump () const
{
std::string DOM::Node::dump() const {
std::stringstream out;
out << "DOM::Node (" << count () << " nodes)\n"
<< dumpNode (this, 1);
out << "DOM::Node (" << count() << " nodes)\n" << dumpNode(this, 1);
return out.str ();
return out.str();
}
////////////////////////////////////////////////////////////////////////////////

View File

@@ -27,49 +27,48 @@
#ifndef INCLUDED_DOM
#define INCLUDED_DOM
#include <string>
#include <Variant.h>
#include <Task.h>
#include <Variant.h>
#include <string>
// 2017-04-22 Deprecated, use DOM::get.
bool getDOM (const std::string&, Variant&);
bool getDOM (const std::string&, const Task*, Variant&);
bool getDOM(const std::string&, Variant&);
bool getDOM(const std::string&, const Task*, Variant&);
class DOM
{
public:
~DOM ();
void addSource (const std::string&, bool (*)(const std::string&, Variant&));
bool valid (const std::string&) const;
/*
// TODO Task object should register a generic provider.
Variant get (const Task&, const std::string&) const;
*/
Variant get (const std::string&) const;
int count () const;
static std::vector <std::string> decomposeReference (const std::string&);
std::string dump () const;
class DOM {
public:
~DOM();
void addSource(const std::string&, bool (*)(const std::string&, Variant&));
bool valid(const std::string&) const;
/*
// TODO Task object should register a generic provider.
Variant get (const Task&, const std::string&) const;
*/
Variant get(const std::string&) const;
int count() const;
static std::vector<std::string> decomposeReference(const std::string&);
std::string dump() const;
private:
class Node
{
public:
~Node ();
void addSource (const std::string&, bool (*)(const std::string&, Variant&));
bool valid (const std::string&) const;
const DOM::Node* find (const std::string&) const;
int count () const;
std::string dumpNode (const DOM::Node*, int) const;
std::string dump () const;
private:
class Node {
public:
~Node();
void addSource(const std::string&, bool (*)(const std::string&, Variant&));
bool valid(const std::string&) const;
const DOM::Node* find(const std::string&) const;
int count() const;
std::string dumpNode(const DOM::Node*, int) const;
std::string dump() const;
public:
std::string _name {"Unknown"};
bool (*_provider)(const std::string&, Variant&) {nullptr};
std::vector <DOM::Node*> _branches {};
public:
std::string _name{"Unknown"};
bool (*_provider)(const std::string&, Variant&){nullptr};
std::vector<DOM::Node*> _branches{};
};
private:
DOM::Node* _node {nullptr};
private:
DOM::Node* _node{nullptr};
};
#endif

File diff suppressed because it is too large Load Diff

View File

@@ -27,50 +27,51 @@
#ifndef INCLUDED_EVAL
#define INCLUDED_EVAL
#include <vector>
#include <string>
#include <Lexer.h>
#include <Variant.h>
bool domSource (const std::string&, Variant&);
#include <string>
#include <vector>
class Eval
{
public:
Eval ();
bool domSource(const std::string &, Variant &);
void addSource (bool (*fn)(const std::string&, Variant&));
void evaluateInfixExpression (const std::string&, Variant&) const;
void evaluatePostfixExpression (const std::string&, Variant&) const;
void compileExpression (const std::vector <std::pair <std::string, Lexer::Type>>&);
void evaluateCompiledExpression (Variant&);
void debug (bool);
class Eval {
public:
Eval();
static std::vector <std::string> getOperators ();
static std::vector <std::string> getBinaryOperators ();
void addSource(bool (*fn)(const std::string &, Variant &));
void evaluateInfixExpression(const std::string &, Variant &) const;
void evaluatePostfixExpression(const std::string &, Variant &) const;
void compileExpression(const std::vector<std::pair<std::string, Lexer::Type>> &);
void evaluateCompiledExpression(Variant &);
void debug(bool);
private:
void evaluatePostfixStack (const std::vector <std::pair <std::string, Lexer::Type>>&, Variant&) const;
void infixToPostfix (std::vector <std::pair <std::string, Lexer::Type>>&) const;
void infixParse (std::vector <std::pair <std::string, Lexer::Type>>&) const;
bool parseLogical (std::vector <std::pair <std::string, Lexer::Type>>&, unsigned int &) const;
bool parseRegex (std::vector <std::pair <std::string, Lexer::Type>>&, unsigned int &) const;
bool parseEquality (std::vector <std::pair <std::string, Lexer::Type>>&, unsigned int &) const;
bool parseComparative (std::vector <std::pair <std::string, Lexer::Type>>&, unsigned int &) const;
bool parseArithmetic (std::vector <std::pair <std::string, Lexer::Type>>&, unsigned int &) const;
bool parseGeometric (std::vector <std::pair <std::string, Lexer::Type>>&, unsigned int &) const;
bool parseTag (std::vector <std::pair <std::string, Lexer::Type>>&, unsigned int &) const;
bool parseUnary (std::vector <std::pair <std::string, Lexer::Type>>&, unsigned int &) const;
bool parseExponent (std::vector <std::pair <std::string, Lexer::Type>>&, unsigned int &) const;
bool parsePrimitive (std::vector <std::pair <std::string, Lexer::Type>>&, unsigned int &) const;
bool identifyOperator (const std::string&, char&, unsigned int&, char&) const;
static std::vector<std::string> getOperators();
static std::vector<std::string> getBinaryOperators();
std::string dump (std::vector <std::pair <std::string, Lexer::Type>>&) const;
private:
void evaluatePostfixStack(const std::vector<std::pair<std::string, Lexer::Type>> &,
Variant &) const;
void infixToPostfix(std::vector<std::pair<std::string, Lexer::Type>> &) const;
void infixParse(std::vector<std::pair<std::string, Lexer::Type>> &) const;
bool parseLogical(std::vector<std::pair<std::string, Lexer::Type>> &, unsigned int &) const;
bool parseRegex(std::vector<std::pair<std::string, Lexer::Type>> &, unsigned int &) const;
bool parseEquality(std::vector<std::pair<std::string, Lexer::Type>> &, unsigned int &) const;
bool parseComparative(std::vector<std::pair<std::string, Lexer::Type>> &, unsigned int &) const;
bool parseArithmetic(std::vector<std::pair<std::string, Lexer::Type>> &, unsigned int &) const;
bool parseGeometric(std::vector<std::pair<std::string, Lexer::Type>> &, unsigned int &) const;
bool parseTag(std::vector<std::pair<std::string, Lexer::Type>> &, unsigned int &) const;
bool parseUnary(std::vector<std::pair<std::string, Lexer::Type>> &, unsigned int &) const;
bool parseExponent(std::vector<std::pair<std::string, Lexer::Type>> &, unsigned int &) const;
bool parsePrimitive(std::vector<std::pair<std::string, Lexer::Type>> &, unsigned int &) const;
bool identifyOperator(const std::string &, char &, unsigned int &, char &) const;
private:
std::vector <bool (*)(const std::string&, Variant&)> _sources {};
bool _debug {false};
std::vector <std::pair <std::string, Lexer::Type>> _compiled {};
std::string dump(std::vector<std::pair<std::string, Lexer::Type>> &) const;
private:
std::vector<bool (*)(const std::string &, Variant &)> _sources{};
bool _debug{false};
std::vector<std::pair<std::string, Lexer::Type>> _compiled{};
};
#endif

View File

@@ -27,145 +27,130 @@
#include <cmake.h>
// cmake.h include header must come first
#include <Filter.h>
#include <algorithm>
#include <Context.h>
#include <Timer.h>
#include <DOM.h>
#include <Eval.h>
#include <Filter.h>
#include <Timer.h>
#include <Variant.h>
#include <format.h>
#include <shared.h>
#include <algorithm>
////////////////////////////////////////////////////////////////////////////////
// Take an input set of tasks and filter into a subset.
void Filter::subset (const std::vector <Task>& input, std::vector <Task>& output)
{
void Filter::subset(const std::vector<Task>& input, std::vector<Task>& output) {
Timer timer;
_startCount = (int) input.size ();
_startCount = (int)input.size();
Context::getContext ().cli2.prepareFilter ();
Context::getContext().cli2.prepareFilter();
std::vector <std::pair <std::string, Lexer::Type>> precompiled;
for (auto& a : Context::getContext ().cli2._args)
if (a.hasTag ("FILTER"))
precompiled.emplace_back (a.getToken (), a._lextype);
std::vector<std::pair<std::string, Lexer::Type>> precompiled;
for (auto& a : Context::getContext().cli2._args)
if (a.hasTag("FILTER")) precompiled.emplace_back(a.getToken(), a._lextype);
if (precompiled.size ())
{
if (precompiled.size()) {
Eval eval;
eval.addSource (domSource);
eval.addSource(domSource);
// Debug output from Eval during compilation is useful. During evaluation
// it is mostly noise.
eval.debug (Context::getContext ().config.getInteger ("debug.parser") >= 3 ? true : false);
eval.compileExpression (precompiled);
eval.debug(Context::getContext().config.getInteger("debug.parser") >= 3 ? true : false);
eval.compileExpression(precompiled);
for (auto& task : input)
{
for (auto& task : input) {
// Set up context for any DOM references.
auto currentTask = Context::getContext ().withCurrentTask(&task);
auto currentTask = Context::getContext().withCurrentTask(&task);
Variant var;
eval.evaluateCompiledExpression (var);
if (var.get_bool ())
output.push_back (task);
eval.evaluateCompiledExpression(var);
if (var.get_bool()) output.push_back(task);
}
eval.debug (false);
}
else
eval.debug(false);
} else
output = input;
_endCount = (int) output.size ();
Context::getContext ().debug (format ("Filtered {1} tasks --> {2} tasks [list subset]", _startCount, _endCount));
Context::getContext ().time_filter_us += timer.total_us ();
_endCount = (int)output.size();
Context::getContext().debug(
format("Filtered {1} tasks --> {2} tasks [list subset]", _startCount, _endCount));
Context::getContext().time_filter_us += timer.total_us();
}
////////////////////////////////////////////////////////////////////////////////
// Take the set of all tasks and filter into a subset.
void Filter::subset (std::vector <Task>& output)
{
void Filter::subset(std::vector<Task>& output) {
Timer timer;
Context::getContext ().cli2.prepareFilter ();
Context::getContext().cli2.prepareFilter();
std::vector <std::pair <std::string, Lexer::Type>> precompiled;
for (auto& a : Context::getContext ().cli2._args)
if (a.hasTag ("FILTER"))
precompiled.emplace_back (a.getToken (), a._lextype);
std::vector<std::pair<std::string, Lexer::Type>> precompiled;
for (auto& a : Context::getContext().cli2._args)
if (a.hasTag("FILTER")) precompiled.emplace_back(a.getToken(), a._lextype);
// Shortcut indicates that only pending.data needs to be loaded.
bool shortcut = false;
if (precompiled.size ())
{
if (precompiled.size()) {
Timer timer_pending;
auto pending = Context::getContext ().tdb2.pending_tasks ();
Context::getContext ().time_filter_us -= timer_pending.total_us ();
_startCount = (int) pending.size ();
auto pending = Context::getContext().tdb2.pending_tasks();
Context::getContext().time_filter_us -= timer_pending.total_us();
_startCount = (int)pending.size();
Eval eval;
eval.addSource (domSource);
eval.addSource(domSource);
// Debug output from Eval during compilation is useful. During evaluation
// it is mostly noise.
eval.debug (Context::getContext ().config.getInteger ("debug.parser") >= 3 ? true : false);
eval.compileExpression (precompiled);
eval.debug(Context::getContext().config.getInteger("debug.parser") >= 3 ? true : false);
eval.compileExpression(precompiled);
output.clear ();
for (auto& task : pending)
{
output.clear();
for (auto& task : pending) {
// Set up context for any DOM references.
auto currentTask = Context::getContext ().withCurrentTask(&task);
auto currentTask = Context::getContext().withCurrentTask(&task);
Variant var;
eval.evaluateCompiledExpression (var);
if (var.get_bool ())
output.push_back (task);
eval.evaluateCompiledExpression(var);
if (var.get_bool()) output.push_back(task);
}
shortcut = pendingOnly ();
if (! shortcut)
{
shortcut = pendingOnly();
if (!shortcut) {
Timer timer_completed;
auto completed = Context::getContext ().tdb2.completed_tasks ();
Context::getContext ().time_filter_us -= timer_completed.total_us ();
_startCount += (int) completed.size ();
auto completed = Context::getContext().tdb2.completed_tasks();
Context::getContext().time_filter_us -= timer_completed.total_us();
_startCount += (int)completed.size();
for (auto& task : completed)
{
for (auto& task : completed) {
// Set up context for any DOM references.
auto currentTask = Context::getContext ().withCurrentTask(&task);
auto currentTask = Context::getContext().withCurrentTask(&task);
Variant var;
eval.evaluateCompiledExpression (var);
if (var.get_bool ())
output.push_back (task);
eval.evaluateCompiledExpression(var);
if (var.get_bool()) output.push_back(task);
}
}
eval.debug (false);
}
else
{
safety ();
eval.debug(false);
} else {
safety();
Timer pending_completed;
output = Context::getContext ().tdb2.all_tasks ();
Context::getContext ().time_filter_us -= pending_completed.total_us ();
output = Context::getContext().tdb2.all_tasks();
Context::getContext().time_filter_us -= pending_completed.total_us();
}
_endCount = (int) output.size ();
Context::getContext ().debug (format ("Filtered {1} tasks --> {2} tasks [{3}]", _startCount, _endCount, (shortcut ? "pending only" : "all tasks")));
Context::getContext ().time_filter_us += timer.total_us ();
_endCount = (int)output.size();
Context::getContext().debug(format("Filtered {1} tasks --> {2} tasks [{3}]", _startCount,
_endCount, (shortcut ? "pending only" : "all tasks")));
Context::getContext().time_filter_us += timer.total_us();
}
////////////////////////////////////////////////////////////////////////////////
bool Filter::hasFilter () const
{
for (const auto& a : Context::getContext ().cli2._args)
if (a.hasTag ("FILTER"))
return true;
bool Filter::hasFilter() const {
for (const auto& a : Context::getContext().cli2._args)
if (a.hasTag("FILTER")) return true;
return false;
}
@@ -174,11 +159,9 @@ bool Filter::hasFilter () const
// If the filter contains no 'or', 'xor' or 'not' operators, and only includes
// status values 'pending', 'waiting' or 'recurring', then the filter is
// guaranteed to only need data from pending.data.
bool Filter::pendingOnly () const
{
bool Filter::pendingOnly() const {
// When GC is off, there are no shortcuts.
if (! Context::getContext ().config.getBoolean ("gc"))
return false;
if (!Context::getContext().config.getBoolean("gc")) return false;
// To skip loading completed.data, there should be:
// - 'status' in filter
@@ -186,61 +169,51 @@ bool Filter::pendingOnly () const
// - no 'deleted'
// - no 'xor'
// - no 'or'
int countStatus = 0;
int countPending = 0;
int countWaiting = 0;
int countStatus = 0;
int countPending = 0;
int countWaiting = 0;
int countRecurring = 0;
int countId = (int) Context::getContext ().cli2._id_ranges.size ();
int countUUID = (int) Context::getContext ().cli2._uuid_list.size ();
int countOr = 0;
int countXor = 0;
int countNot = 0;
int countId = (int)Context::getContext().cli2._id_ranges.size();
int countUUID = (int)Context::getContext().cli2._uuid_list.size();
int countOr = 0;
int countXor = 0;
int countNot = 0;
bool pendingTag = false;
bool activeTag = false;
bool activeTag = false;
for (const auto& a : Context::getContext ().cli2._args)
{
if (a.hasTag ("FILTER"))
{
std::string raw = a.attribute ("raw");
std::string canonical = a.attribute ("canonical");
for (const auto& a : Context::getContext().cli2._args) {
if (a.hasTag("FILTER")) {
std::string raw = a.attribute("raw");
std::string canonical = a.attribute("canonical");
if (a._lextype == Lexer::Type::op && raw == "or") ++countOr;
if (a._lextype == Lexer::Type::op && raw == "xor") ++countXor;
if (a._lextype == Lexer::Type::op && raw == "not") ++countNot;
if (a._lextype == Lexer::Type::op && raw == "or") ++countOr;
if (a._lextype == Lexer::Type::op && raw == "xor") ++countXor;
if (a._lextype == Lexer::Type::op && raw == "not") ++countNot;
if (a._lextype == Lexer::Type::dom && canonical == "status") ++countStatus;
if ( raw == "pending") ++countPending;
if ( raw == "waiting") ++countWaiting;
if ( raw == "recurring") ++countRecurring;
if (raw == "pending") ++countPending;
if (raw == "waiting") ++countWaiting;
if (raw == "recurring") ++countRecurring;
}
}
for (const auto& word : Context::getContext ().cli2._original_args)
{
if (word.attribute ("raw") == "+PENDING") pendingTag = true;
if (word.attribute ("raw") == "+ACTIVE") activeTag = true;
for (const auto& word : Context::getContext().cli2._original_args) {
if (word.attribute("raw") == "+PENDING") pendingTag = true;
if (word.attribute("raw") == "+ACTIVE") activeTag = true;
}
if (countUUID) return false;
if (countUUID)
return false;
if (countOr || countXor || countNot) return false;
if (countOr || countXor || countNot)
return false;
if (pendingTag || activeTag) return true;
if (pendingTag || activeTag)
return true;
if (countStatus)
{
if (!countPending && !countWaiting && !countRecurring)
return false;
if (countStatus) {
if (!countPending && !countWaiting && !countRecurring) return false;
return true;
}
if (countId)
return true;
if (countId) return true;
return false;
}
@@ -248,43 +221,35 @@ bool Filter::pendingOnly () const
////////////////////////////////////////////////////////////////////////////////
// Disaster avoidance mechanism. If a !READONLY has no filter, then it can cause
// all tasks to be modified. This is usually not intended.
void Filter::safety () const
{
if (_safety)
{
void Filter::safety() const {
if (_safety) {
bool readonly = true;
bool filter = false;
for (const auto& a : Context::getContext ().cli2._args)
{
if (a.hasTag ("CMD") &&
! a.hasTag ("READONLY"))
readonly = false;
for (const auto& a : Context::getContext().cli2._args) {
if (a.hasTag("CMD") && !a.hasTag("READONLY")) readonly = false;
if (a.hasTag ("FILTER"))
filter = true;
if (a.hasTag("FILTER")) filter = true;
}
if (! readonly &&
! filter)
{
if (! Context::getContext ().config.getBoolean ("allow.empty.filter"))
throw std::string ("You did not specify a filter, and with the 'allow.empty.filter' value, no action is taken.");
if (!readonly && !filter) {
if (!Context::getContext().config.getBoolean("allow.empty.filter"))
throw std::string(
"You did not specify a filter, and with the 'allow.empty.filter' value, no action is "
"taken.");
// If user is willing to be asked, this can be avoided.
if (Context::getContext ().config.getBoolean ("confirmation") &&
confirm ("This command has no filter, and will modify all (including completed and deleted) tasks. Are you sure?"))
if (Context::getContext().config.getBoolean("confirmation") &&
confirm("This command has no filter, and will modify all (including completed and "
"deleted) tasks. Are you sure?"))
return;
// Sound the alarm.
throw std::string ("Command prevented from running.");
throw std::string("Command prevented from running.");
}
}
}
////////////////////////////////////////////////////////////////////////////////
void Filter::disableSafety ()
{
_safety = false;
}
void Filter::disableSafety() { _safety = false; }
////////////////////////////////////////////////////////////////////////////////

View File

@@ -27,28 +27,27 @@
#ifndef INCLUDED_FILTER
#define INCLUDED_FILTER
#include <string>
#include <vector>
#include <Task.h>
#include <Variant.h>
class Filter
{
public:
Filter () = default;
#include <string>
#include <vector>
void subset (const std::vector <Task>&, std::vector <Task>&);
void subset (std::vector <Task>&);
bool hasFilter () const;
bool pendingOnly () const;
void safety () const;
void disableSafety ();
class Filter {
public:
Filter() = default;
private:
int _startCount {0};
int _endCount {0};
bool _safety {true};
void subset(const std::vector<Task>&, std::vector<Task>&);
void subset(std::vector<Task>&);
bool hasFilter() const;
bool pendingOnly() const;
void safety() const;
void disableSafety();
private:
int _startCount{0};
int _endCount{0};
bool _safety{true};
};
#endif

View File

@@ -28,90 +28,83 @@
// cmake.h include header must come first
#include <Hooks.h>
#include <algorithm>
// If <iostream> is included, put it after <stdio.h>, because it includes
// <stdio.h>, and therefore would ignore the _WITH_GETLINE.
#ifdef FREEBSD
#define _WITH_GETLINE
#endif
#include <stdio.h>
#include <unistd.h>
#include <sys/wait.h>
#include <sys/types.h>
#include <Context.h>
#include <Variant.h>
#include <DOM.h>
#include <Lexer.h>
#include <JSON.h>
#include <Timer.h>
#include <FS.h>
#include <JSON.h>
#include <Lexer.h>
#include <Timer.h>
#include <Variant.h>
#include <format.h>
#include <shared.h>
#include <stdio.h>
#include <sys/types.h>
#include <sys/wait.h>
#include <unistd.h>
#include <util.h>
#define STRING_HOOK_ERROR_OBJECT "Hook Error: JSON Object '{...}' expected from hook script: {1}"
#define STRING_HOOK_ERROR_NODESC "Hook Error: JSON Object missing 'description' attribute from hook script: {1}"
#define STRING_HOOK_ERROR_NOUUID "Hook Error: JSON Object missing 'uuid' attribute from hook script: {1}"
#define STRING_HOOK_ERROR_SYNTAX "Hook Error: JSON syntax error in: {1}"
#define STRING_HOOK_ERROR_JSON "Hook Error: JSON "
#define STRING_HOOK_ERROR_NOPARSE "Hook Error: JSON failed to parse: "
#define STRING_HOOK_ERROR_BAD_NUM "Hook Error: Expected {1} JSON task(s), found {2}, in hook script: {3}"
#define STRING_HOOK_ERROR_SAME1 "Hook Error: JSON must be for the same task: {1}, in hook script: {2}"
#define STRING_HOOK_ERROR_SAME2 "Hook Error: JSON must be for the same task: {1} != {2}, in hook script: {3}"
#define STRING_HOOK_ERROR_OBJECT "Hook Error: JSON Object '{...}' expected from hook script: {1}"
#define STRING_HOOK_ERROR_NODESC \
"Hook Error: JSON Object missing 'description' attribute from hook script: {1}"
#define STRING_HOOK_ERROR_NOUUID \
"Hook Error: JSON Object missing 'uuid' attribute from hook script: {1}"
#define STRING_HOOK_ERROR_SYNTAX "Hook Error: JSON syntax error in: {1}"
#define STRING_HOOK_ERROR_JSON "Hook Error: JSON "
#define STRING_HOOK_ERROR_NOPARSE "Hook Error: JSON failed to parse: "
#define STRING_HOOK_ERROR_BAD_NUM \
"Hook Error: Expected {1} JSON task(s), found {2}, in hook script: {3}"
#define STRING_HOOK_ERROR_SAME1 \
"Hook Error: JSON must be for the same task: {1}, in hook script: {2}"
#define STRING_HOOK_ERROR_SAME2 \
"Hook Error: JSON must be for the same task: {1} != {2}, in hook script: {3}"
#define STRING_HOOK_ERROR_NOFEEDBACK "Hook Error: Expected feedback from failing hook script: {1}"
////////////////////////////////////////////////////////////////////////////////
void Hooks::initialize ()
{
_debug = Context::getContext ().config.getInteger ("debug.hooks");
void Hooks::initialize() {
_debug = Context::getContext().config.getInteger("debug.hooks");
// Scan <rc.hooks.location>
// <rc.data.location>/hooks
Directory d;
if (Context::getContext ().config.has ("hooks.location"))
{
d = Directory (Context::getContext ().config.get ("hooks.location"));
}
else
{
d = Directory (Context::getContext ().config.get ("data.location"));
if (Context::getContext().config.has("hooks.location")) {
d = Directory(Context::getContext().config.get("hooks.location"));
} else {
d = Directory(Context::getContext().config.get("data.location"));
d += "hooks";
}
if (d.is_directory () &&
d.readable ())
{
_scripts = d.list ();
std::sort (_scripts.begin (), _scripts.end ());
if (d.is_directory() && d.readable()) {
_scripts = d.list();
std::sort(_scripts.begin(), _scripts.end());
if (_debug >= 1)
{
for (auto& i : _scripts)
{
Path p (i);
if (! p.is_directory ())
{
std::string name = p.name ();
if (name.substr (0, 6) == "on-add" ||
name.substr (0, 9) == "on-modify" ||
name.substr (0, 9) == "on-launch" ||
name.substr (0, 7) == "on-exit")
Context::getContext ().debug ("Found hook script " + i);
if (_debug >= 1) {
for (auto& i : _scripts) {
Path p(i);
if (!p.is_directory()) {
std::string name = p.name();
if (name.substr(0, 6) == "on-add" || name.substr(0, 9) == "on-modify" ||
name.substr(0, 9) == "on-launch" || name.substr(0, 7) == "on-exit")
Context::getContext().debug("Found hook script " + i);
else
Context::getContext ().debug ("Found misnamed hook script " + i);
Context::getContext().debug("Found misnamed hook script " + i);
}
}
}
}
else if (_debug >= 1)
Context::getContext ().debug ("Hook directory not readable: " + d._data);
} else if (_debug >= 1)
Context::getContext().debug("Hook directory not readable: " + d._data);
_enabled = Context::getContext ().config.getBoolean ("hooks");
_enabled = Context::getContext().config.getBoolean("hooks");
}
////////////////////////////////////////////////////////////////////////////////
bool Hooks::enable (bool value)
{
bool Hooks::enable(bool value) {
bool old_value = _enabled;
_enabled = value;
return old_value;
@@ -129,45 +122,36 @@ bool Hooks::enable (bool value)
// - all emitted non-JSON lines are considered feedback or error messages
// depending on the status code.
//
void Hooks::onLaunch () const
{
if (! _enabled)
return;
void Hooks::onLaunch() const {
if (!_enabled) return;
Timer timer;
std::vector <std::string> matchingScripts = scripts ("on-launch");
if (matchingScripts.size ())
{
for (auto& script : matchingScripts)
{
std::vector <std::string> input;
std::vector <std::string> output;
int status = callHookScript (script, input, output);
std::vector<std::string> matchingScripts = scripts("on-launch");
if (matchingScripts.size()) {
for (auto& script : matchingScripts) {
std::vector<std::string> input;
std::vector<std::string> output;
int status = callHookScript(script, input, output);
std::vector <std::string> outputJSON;
std::vector <std::string> outputFeedback;
separateOutput (output, outputJSON, outputFeedback);
std::vector<std::string> outputJSON;
std::vector<std::string> outputFeedback;
separateOutput(output, outputJSON, outputFeedback);
assertNTasks (outputJSON, 0, script);
assertNTasks(outputJSON, 0, script);
if (status == 0)
{
for (auto& message : outputFeedback)
Context::getContext ().footnote (message);
}
else
{
assertFeedback (outputFeedback, script);
for (auto& message : outputFeedback)
Context::getContext ().error (message);
if (status == 0) {
for (auto& message : outputFeedback) Context::getContext().footnote(message);
} else {
assertFeedback(outputFeedback, script);
for (auto& message : outputFeedback) Context::getContext().error(message);
throw 0; // This is how hooks silently terminate processing.
}
}
}
Context::getContext ().time_hooks_us += timer.total_us ();
Context::getContext().time_hooks_us += timer.total_us();
}
////////////////////////////////////////////////////////////////////////////////
@@ -182,55 +166,45 @@ void Hooks::onLaunch () const
// - all emitted non-JSON lines are considered feedback or error messages
// depending on the status code.
//
void Hooks::onExit () const
{
if (! _enabled)
return;
void Hooks::onExit() const {
if (!_enabled) return;
Timer timer;
std::vector <std::string> matchingScripts = scripts ("on-exit");
if (matchingScripts.size ())
{
std::vector<std::string> matchingScripts = scripts("on-exit");
if (matchingScripts.size()) {
// Get the set of changed tasks.
std::vector <Task> tasks;
Context::getContext ().tdb2.get_changes (tasks);
std::vector<Task> tasks;
Context::getContext().tdb2.get_changes(tasks);
// Convert to a vector of strings.
std::vector <std::string> input;
std::vector<std::string> input;
input.reserve(tasks.size());
for (auto& t : tasks)
input.push_back (t.composeJSON ());
for (auto& t : tasks) input.push_back(t.composeJSON());
// Call the hook scripts, with the invariant input.
for (auto& script : matchingScripts)
{
std::vector <std::string> output;
int status = callHookScript (script, input, output);
for (auto& script : matchingScripts) {
std::vector<std::string> output;
int status = callHookScript(script, input, output);
std::vector <std::string> outputJSON;
std::vector <std::string> outputFeedback;
separateOutput (output, outputJSON, outputFeedback);
std::vector<std::string> outputJSON;
std::vector<std::string> outputFeedback;
separateOutput(output, outputJSON, outputFeedback);
assertNTasks (outputJSON, 0, script);
assertNTasks(outputJSON, 0, script);
if (status == 0)
{
for (auto& message : outputFeedback)
Context::getContext ().footnote (message);
}
else
{
assertFeedback (outputFeedback, script);
for (auto& message : outputFeedback)
Context::getContext ().error (message);
if (status == 0) {
for (auto& message : outputFeedback) Context::getContext().footnote(message);
} else {
assertFeedback(outputFeedback, script);
for (auto& message : outputFeedback) Context::getContext().error(message);
throw 0; // This is how hooks silently terminate processing.
}
}
}
Context::getContext ().time_hooks_us += timer.total_us ();
Context::getContext().time_hooks_us += timer.total_us();
}
////////////////////////////////////////////////////////////////////////////////
@@ -245,57 +219,48 @@ void Hooks::onExit () const
// - all emitted non-JSON lines are considered feedback or error messages
// depending on the status code.
//
void Hooks::onAdd (Task& task) const
{
if (! _enabled)
return;
void Hooks::onAdd(Task& task) const {
if (!_enabled) return;
Timer timer;
std::vector <std::string> matchingScripts = scripts ("on-add");
if (matchingScripts.size ())
{
std::vector<std::string> matchingScripts = scripts("on-add");
if (matchingScripts.size()) {
// Convert task to a vector of strings.
std::vector <std::string> input;
input.push_back (task.composeJSON ());
std::vector<std::string> input;
input.push_back(task.composeJSON());
// Call the hook scripts.
for (auto& script : matchingScripts)
{
std::vector <std::string> output;
int status = callHookScript (script, input, output);
for (auto& script : matchingScripts) {
std::vector<std::string> output;
int status = callHookScript(script, input, output);
std::vector <std::string> outputJSON;
std::vector <std::string> outputFeedback;
separateOutput (output, outputJSON, outputFeedback);
std::vector<std::string> outputJSON;
std::vector<std::string> outputFeedback;
separateOutput(output, outputJSON, outputFeedback);
if (status == 0)
{
assertNTasks (outputJSON, 1, script);
assertValidJSON (outputJSON, script);
assertSameTask (outputJSON, task, script);
if (status == 0) {
assertNTasks(outputJSON, 1, script);
assertValidJSON(outputJSON, script);
assertSameTask(outputJSON, task, script);
// Propagate forward to the next script.
input[0] = outputJSON[0];
for (auto& message : outputFeedback)
Context::getContext ().footnote (message);
}
else
{
assertFeedback (outputFeedback, script);
for (auto& message : outputFeedback)
Context::getContext ().error (message);
for (auto& message : outputFeedback) Context::getContext().footnote(message);
} else {
assertFeedback(outputFeedback, script);
for (auto& message : outputFeedback) Context::getContext().error(message);
throw 0; // This is how hooks silently terminate processing.
}
}
// Transfer the modified task back to the original task.
task = Task (input[0]);
task = Task(input[0]);
}
Context::getContext ().time_hooks_us += timer.total_us ();
Context::getContext().time_hooks_us += timer.total_us();
}
////////////////////////////////////////////////////////////////////////////////
@@ -311,76 +276,60 @@ void Hooks::onAdd (Task& task) const
// - all emitted non-JSON lines are considered feedback or error messages
// depending on the status code.
//
void Hooks::onModify (const Task& before, Task& after) const
{
if (! _enabled)
return;
void Hooks::onModify(const Task& before, Task& after) const {
if (!_enabled) return;
Timer timer;
std::vector <std::string> matchingScripts = scripts ("on-modify");
if (matchingScripts.size ())
{
std::vector<std::string> matchingScripts = scripts("on-modify");
if (matchingScripts.size()) {
// Convert vector of tasks to a vector of strings.
std::vector <std::string> input;
input.push_back (before.composeJSON ()); // [line 0] original, never changes
input.push_back (after.composeJSON ()); // [line 1] modified
std::vector<std::string> input;
input.push_back(before.composeJSON()); // [line 0] original, never changes
input.push_back(after.composeJSON()); // [line 1] modified
// Call the hook scripts.
for (auto& script : matchingScripts)
{
std::vector <std::string> output;
int status = callHookScript (script, input, output);
for (auto& script : matchingScripts) {
std::vector<std::string> output;
int status = callHookScript(script, input, output);
std::vector <std::string> outputJSON;
std::vector <std::string> outputFeedback;
separateOutput (output, outputJSON, outputFeedback);
std::vector<std::string> outputJSON;
std::vector<std::string> outputFeedback;
separateOutput(output, outputJSON, outputFeedback);
if (status == 0)
{
assertNTasks (outputJSON, 1, script);
assertValidJSON (outputJSON, script);
assertSameTask (outputJSON, before, script);
if (status == 0) {
assertNTasks(outputJSON, 1, script);
assertValidJSON(outputJSON, script);
assertSameTask(outputJSON, before, script);
// Propagate accepted changes forward to the next script.
input[1] = outputJSON[0];
for (auto& message : outputFeedback)
Context::getContext ().footnote (message);
}
else
{
assertFeedback (outputFeedback, script);
for (auto& message : outputFeedback)
Context::getContext ().error (message);
for (auto& message : outputFeedback) Context::getContext().footnote(message);
} else {
assertFeedback(outputFeedback, script);
for (auto& message : outputFeedback) Context::getContext().error(message);
throw 0; // This is how hooks silently terminate processing.
}
}
after = Task (input[1]);
after = Task(input[1]);
}
Context::getContext ().time_hooks_us += timer.total_us ();
Context::getContext().time_hooks_us += timer.total_us();
}
////////////////////////////////////////////////////////////////////////////////
std::vector <std::string> Hooks::list () const
{
return _scripts;
}
std::vector<std::string> Hooks::list() const { return _scripts; }
////////////////////////////////////////////////////////////////////////////////
std::vector <std::string> Hooks::scripts (const std::string& event) const
{
std::vector <std::string> matching;
for (const auto& i : _scripts)
{
if (i.find ("/" + event) != std::string::npos)
{
File script (i);
if (script.executable ())
matching.push_back (i);
std::vector<std::string> Hooks::scripts(const std::string& event) const {
std::vector<std::string> matching;
for (const auto& i : _scripts) {
if (i.find("/" + event) != std::string::npos) {
File script(i);
if (script.executable()) matching.push_back(i);
}
}
@@ -388,124 +337,95 @@ std::vector <std::string> Hooks::scripts (const std::string& event) const
}
////////////////////////////////////////////////////////////////////////////////
void Hooks::separateOutput (
const std::vector <std::string>& output,
std::vector <std::string>& json,
std::vector <std::string>& feedback) const
{
for (auto& i : output)
{
if (isJSON (i))
json.push_back (i);
void Hooks::separateOutput(const std::vector<std::string>& output, std::vector<std::string>& json,
std::vector<std::string>& feedback) const {
for (auto& i : output) {
if (isJSON(i))
json.push_back(i);
else
feedback.push_back (i);
feedback.push_back(i);
}
}
////////////////////////////////////////////////////////////////////////////////
bool Hooks::isJSON (const std::string& input) const
{
return input.length () > 2 &&
input[0] == '{' &&
input[input.length () - 1] == '}';
bool Hooks::isJSON(const std::string& input) const {
return input.length() > 2 && input[0] == '{' && input[input.length() - 1] == '}';
}
////////////////////////////////////////////////////////////////////////////////
void Hooks::assertValidJSON (
const std::vector <std::string>& input,
const std::string& script) const
{
for (auto& i : input)
{
if (i.length () < 3 ||
i[0] != '{' ||
i[i.length () - 1] != '}')
{
Context::getContext ().error (format (STRING_HOOK_ERROR_OBJECT, Path (script).name ()));
void Hooks::assertValidJSON(const std::vector<std::string>& input,
const std::string& script) const {
for (auto& i : input) {
if (i.length() < 3 || i[0] != '{' || i[i.length() - 1] != '}') {
Context::getContext().error(format(STRING_HOOK_ERROR_OBJECT, Path(script).name()));
throw 0;
}
try
{
json::value* root = json::parse (i);
if (root->type () != json::j_object)
{
Context::getContext ().error (format (STRING_HOOK_ERROR_OBJECT, Path (script).name ()));
try {
json::value* root = json::parse(i);
if (root->type() != json::j_object) {
Context::getContext().error(format(STRING_HOOK_ERROR_OBJECT, Path(script).name()));
throw 0;
}
if (((json::object*)root)->_data.find ("description") == ((json::object*)root)->_data.end ())
{
Context::getContext ().error (format (STRING_HOOK_ERROR_NODESC, Path (script).name ()));
if (((json::object*)root)->_data.find("description") == ((json::object*)root)->_data.end()) {
Context::getContext().error(format(STRING_HOOK_ERROR_NODESC, Path(script).name()));
throw 0;
}
if (((json::object*)root)->_data.find ("uuid") == ((json::object*)root)->_data.end ())
{
Context::getContext ().error (format (STRING_HOOK_ERROR_NOUUID, Path (script).name ()));
if (((json::object*)root)->_data.find("uuid") == ((json::object*)root)->_data.end()) {
Context::getContext().error(format(STRING_HOOK_ERROR_NOUUID, Path(script).name()));
throw 0;
}
delete root;
}
catch (const std::string& e)
{
Context::getContext ().error (format (STRING_HOOK_ERROR_SYNTAX, i));
if (_debug)
Context::getContext ().error (STRING_HOOK_ERROR_JSON + e);
catch (const std::string& e) {
Context::getContext().error(format(STRING_HOOK_ERROR_SYNTAX, i));
if (_debug) Context::getContext().error(STRING_HOOK_ERROR_JSON + e);
throw 0;
}
catch (...)
{
Context::getContext ().error (STRING_HOOK_ERROR_NOPARSE + i);
catch (...) {
Context::getContext().error(STRING_HOOK_ERROR_NOPARSE + i);
throw 0;
}
}
}
////////////////////////////////////////////////////////////////////////////////
void Hooks::assertNTasks (
const std::vector <std::string>& input,
unsigned int n,
const std::string& script) const
{
if (input.size () != n)
{
Context::getContext ().error (format (STRING_HOOK_ERROR_BAD_NUM, n, (int) input.size (), Path (script).name ()));
void Hooks::assertNTasks(const std::vector<std::string>& input, unsigned int n,
const std::string& script) const {
if (input.size() != n) {
Context::getContext().error(
format(STRING_HOOK_ERROR_BAD_NUM, n, (int)input.size(), Path(script).name()));
throw 0;
}
}
////////////////////////////////////////////////////////////////////////////////
void Hooks::assertSameTask (
const std::vector <std::string>& input,
const Task& task,
const std::string& script) const
{
std::string uuid = task.get ("uuid");
void Hooks::assertSameTask(const std::vector<std::string>& input, const Task& task,
const std::string& script) const {
std::string uuid = task.get("uuid");
for (auto& i : input)
{
auto root_obj = (json::object*)json::parse (i);
for (auto& i : input) {
auto root_obj = (json::object*)json::parse(i);
// If there is no UUID at all.
auto u = root_obj->_data.find ("uuid");
if (u == root_obj->_data.end () ||
u->second->type () != json::j_string)
{
Context::getContext ().error (format (STRING_HOOK_ERROR_SAME1, uuid, Path (script).name ()));
auto u = root_obj->_data.find("uuid");
if (u == root_obj->_data.end() || u->second->type() != json::j_string) {
Context::getContext().error(format(STRING_HOOK_ERROR_SAME1, uuid, Path(script).name()));
throw 0;
}
auto up = (json::string*) u->second;
auto text = up->dump ();
Lexer::dequote (text);
std::string json_uuid = json::decode (text);
if (json_uuid != uuid)
{
Context::getContext ().error (format (STRING_HOOK_ERROR_SAME2, uuid, json_uuid, Path (script).name ()));
auto up = (json::string*)u->second;
auto text = up->dump();
Lexer::dequote(text);
std::string json_uuid = json::decode(text);
if (json_uuid != uuid) {
Context::getContext().error(
format(STRING_HOOK_ERROR_SAME2, uuid, json_uuid, Path(script).name()));
throw 0;
}
@@ -514,101 +434,82 @@ void Hooks::assertSameTask (
}
////////////////////////////////////////////////////////////////////////////////
void Hooks::assertFeedback (
const std::vector <std::string>& input,
const std::string& script) const
{
void Hooks::assertFeedback(const std::vector<std::string>& input, const std::string& script) const {
bool foundSomething = false;
for (auto& i : input)
if (nontrivial (i))
foundSomething = true;
if (nontrivial(i)) foundSomething = true;
if (! foundSomething)
{
Context::getContext ().error (format (STRING_HOOK_ERROR_NOFEEDBACK, Path (script).name ()));
if (!foundSomething) {
Context::getContext().error(format(STRING_HOOK_ERROR_NOFEEDBACK, Path(script).name()));
throw 0;
}
}
////////////////////////////////////////////////////////////////////////////////
std::vector <std::string>& Hooks::buildHookScriptArgs (std::vector <std::string>& args) const
{
std::vector<std::string>& Hooks::buildHookScriptArgs(std::vector<std::string>& args) const {
Variant v;
// Hooks API version.
args.push_back ("api:2");
args.push_back("api:2");
// Command line Taskwarrior was called with.
getDOM ("context.args", v);
args.push_back ("args:" + std::string (v));
getDOM("context.args", v);
args.push_back("args:" + std::string(v));
// Command to be executed.
args.push_back ("command:" + Context::getContext ().cli2.getCommand ());
args.push_back("command:" + Context::getContext().cli2.getCommand());
// rc file used after applying all overrides.
args.push_back ("rc:" + Context::getContext ().rc_file._data);
args.push_back("rc:" + Context::getContext().rc_file._data);
// Directory containing *.data files.
args.push_back ("data:" + Context::getContext ().data_dir._data);
args.push_back("data:" + Context::getContext().data_dir._data);
// Taskwarrior version, same as returned by "task --version"
args.push_back ("version:" + std::string(VERSION));
args.push_back("version:" + std::string(VERSION));
return args;
}
////////////////////////////////////////////////////////////////////////////////
int Hooks::callHookScript (
const std::string& script,
const std::vector <std::string>& input,
std::vector <std::string>& output) const
{
if (_debug >= 1)
Context::getContext ().debug ("Hook: Calling " + script);
int Hooks::callHookScript(const std::string& script, const std::vector<std::string>& input,
std::vector<std::string>& output) const {
if (_debug >= 1) Context::getContext().debug("Hook: Calling " + script);
if (_debug >= 2)
{
Context::getContext ().debug ("Hook: input");
for (const auto& i : input)
Context::getContext ().debug (" " + i);
if (_debug >= 2) {
Context::getContext().debug("Hook: input");
for (const auto& i : input) Context::getContext().debug(" " + i);
}
std::string inputStr;
for (const auto& i : input)
inputStr += i + "\n";
for (const auto& i : input) inputStr += i + "\n";
std::vector <std::string> args;
buildHookScriptArgs (args);
if (_debug >= 2)
{
Context::getContext ().debug ("Hooks: args");
for (const auto& arg: args)
Context::getContext ().debug (" " + arg);
std::vector<std::string> args;
buildHookScriptArgs(args);
if (_debug >= 2) {
Context::getContext().debug("Hooks: args");
for (const auto& arg : args) Context::getContext().debug(" " + arg);
}
// Measure time for each hook if running in debug
int status;
std::string outputStr;
if (_debug >= 2)
{
if (_debug >= 2) {
Timer timer;
status = execute (script, args, inputStr, outputStr);
Context::getContext ().debugTiming (format ("Hooks::execute ({1})", script), timer);
}
else
status = execute (script, args, inputStr, outputStr);
status = execute(script, args, inputStr, outputStr);
Context::getContext().debugTiming(format("Hooks::execute ({1})", script), timer);
} else
status = execute(script, args, inputStr, outputStr);
output = split (outputStr, '\n');
output = split(outputStr, '\n');
if (_debug >= 2)
{
Context::getContext ().debug ("Hook: output");
if (_debug >= 2) {
Context::getContext().debug("Hook: output");
for (const auto& i : output)
if (i != "")
Context::getContext ().debug (" " + i);
if (i != "") Context::getContext().debug(" " + i);
Context::getContext ().debug (format ("Hook: Completed with status {1}", status));
Context::getContext ().debug (" "); // Blank line
Context::getContext().debug(format("Hook: Completed with status {1}", status));
Context::getContext().debug(" "); // Blank line
}
return status;

View File

@@ -27,37 +27,39 @@
#ifndef INCLUDED_HOOKS
#define INCLUDED_HOOKS
#include <vector>
#include <string>
#include <Task.h>
class Hooks
{
public:
Hooks () = default;
void initialize ();
bool enable (bool);
void onLaunch () const;
void onExit () const;
void onAdd (Task&) const;
void onModify (const Task&, Task&) const;
std::vector <std::string> list () const;
#include <string>
#include <vector>
private:
std::vector <std::string> scripts (const std::string&) const;
void separateOutput (const std::vector <std::string>&, std::vector <std::string>&, std::vector <std::string>&) const;
bool isJSON (const std::string&) const;
void assertValidJSON (const std::vector <std::string>&, const std::string&) const;
void assertNTasks (const std::vector <std::string>&, unsigned int, const std::string&) const;
void assertSameTask (const std::vector <std::string>&, const Task&, const std::string&) const;
void assertFeedback (const std::vector <std::string>&, const std::string&) const;
std::vector <std::string>& buildHookScriptArgs (std::vector <std::string>&) const;
int callHookScript (const std::string&, const std::vector <std::string>&, std::vector <std::string>&) const;
class Hooks {
public:
Hooks() = default;
void initialize();
bool enable(bool);
void onLaunch() const;
void onExit() const;
void onAdd(Task&) const;
void onModify(const Task&, Task&) const;
std::vector<std::string> list() const;
private:
bool _enabled {true};
int _debug {0};
std::vector <std::string> _scripts {};
private:
std::vector<std::string> scripts(const std::string&) const;
void separateOutput(const std::vector<std::string>&, std::vector<std::string>&,
std::vector<std::string>&) const;
bool isJSON(const std::string&) const;
void assertValidJSON(const std::vector<std::string>&, const std::string&) const;
void assertNTasks(const std::vector<std::string>&, unsigned int, const std::string&) const;
void assertSameTask(const std::vector<std::string>&, const Task&, const std::string&) const;
void assertFeedback(const std::vector<std::string>&, const std::string&) const;
std::vector<std::string>& buildHookScriptArgs(std::vector<std::string>&) const;
int callHookScript(const std::string&, const std::vector<std::string>&,
std::vector<std::string>&) const;
private:
bool _enabled{true};
int _debug{0};
std::vector<std::string> _scripts{};
};
#endif

File diff suppressed because it is too large Load Diff

View File

@@ -27,95 +27,108 @@
#ifndef INCLUDED_LEXER
#define INCLUDED_LEXER
#include <string>
#include <map>
#include <vector>
#include <cstddef>
#include <map>
#include <string>
#include <vector>
// Lexer: A UTF8 lexical analyzer for every construct used on the Taskwarrior
// command line, with additional recognized types for disambiguation.
class Lexer
{
public:
class Lexer {
public:
// These are overridable.
static std::string dateFormat;
static std::string::size_type minimumMatchLength;
static std::map <std::string, std::string> attributes;
static std::map<std::string, std::string> attributes;
enum class Type { uuid, number, hex,
string,
url, pair, set, separator,
tag,
path,
substitution, pattern,
op,
dom, identifier, word,
date, duration };
enum class Type {
uuid,
number,
hex,
string,
url,
pair,
set,
separator,
tag,
path,
substitution,
pattern,
op,
dom,
identifier,
word,
date,
duration
};
Lexer (const std::string&);
~Lexer () = default;
bool token (std::string&, Lexer::Type&);
static std::vector <std::string> split (const std::string&);
static std::string typeToString (Lexer::Type);
Lexer(const std::string&);
~Lexer() = default;
bool token(std::string&, Lexer::Type&);
static std::vector<std::string> split(const std::string&);
static std::string typeToString(Lexer::Type);
// Static helpers.
static const std::string typeName (const Lexer::Type&);
static bool isIdentifierStart (int);
static bool isIdentifierNext (int);
static bool isSingleCharOperator (int);
static bool isDoubleCharOperator (int, int, int);
static bool isTripleCharOperator (int, int, int, int);
static bool isBoundary (int, int);
static bool isHardBoundary (int, int);
static bool isPunctuation (int);
static bool isAllDigits (const std::string&);
static bool isDOM (const std::string&);
static void dequote (std::string&, const std::string& quotes = "'\"");
static bool wasQuoted (const std::string&);
static bool readWord (const std::string&, const std::string&, std::string::size_type&, std::string&);
static bool readWord (const std::string&, std::string::size_type&, std::string&);
static bool decomposePair (const std::string&, std::string&, std::string&, std::string&, std::string&);
static bool decomposeSubstitution (const std::string&, std::string&, std::string&, std::string&);
static bool decomposePattern (const std::string&, std::string&, std::string&);
static int hexToInt (int);
static int hexToInt (int, int);
static int hexToInt (int, int, int, int);
static std::string::size_type commonLength (const std::string&, const std::string&);
static std::string::size_type commonLength (const std::string&, std::string::size_type, const std::string&, std::string::size_type);
static std::string commify (const std::string&);
static std::string lowerCase (const std::string&);
static std::string ucFirst (const std::string&);
static std::string trimLeft (const std::string& in, const std::string& t = " ");
static std::string trimRight (const std::string& in, const std::string& t = " ");
static std::string trim (const std::string& in, const std::string& t = " ");
static const std::string typeName(const Lexer::Type&);
static bool isIdentifierStart(int);
static bool isIdentifierNext(int);
static bool isSingleCharOperator(int);
static bool isDoubleCharOperator(int, int, int);
static bool isTripleCharOperator(int, int, int, int);
static bool isBoundary(int, int);
static bool isHardBoundary(int, int);
static bool isPunctuation(int);
static bool isAllDigits(const std::string&);
static bool isDOM(const std::string&);
static void dequote(std::string&, const std::string& quotes = "'\"");
static bool wasQuoted(const std::string&);
static bool readWord(const std::string&, const std::string&, std::string::size_type&,
std::string&);
static bool readWord(const std::string&, std::string::size_type&, std::string&);
static bool decomposePair(const std::string&, std::string&, std::string&, std::string&,
std::string&);
static bool decomposeSubstitution(const std::string&, std::string&, std::string&, std::string&);
static bool decomposePattern(const std::string&, std::string&, std::string&);
static int hexToInt(int);
static int hexToInt(int, int);
static int hexToInt(int, int, int, int);
static std::string::size_type commonLength(const std::string&, const std::string&);
static std::string::size_type commonLength(const std::string&, std::string::size_type,
const std::string&, std::string::size_type);
static std::string commify(const std::string&);
static std::string lowerCase(const std::string&);
static std::string ucFirst(const std::string&);
static std::string trimLeft(const std::string& in, const std::string& t = " ");
static std::string trimRight(const std::string& in, const std::string& t = " ");
static std::string trim(const std::string& in, const std::string& t = " ");
// Stream Classifiers.
bool isEOS () const;
bool isString (std::string&, Lexer::Type&, const std::string&);
bool isDate (std::string&, Lexer::Type&);
bool isDuration (std::string&, Lexer::Type&);
bool isUUID (std::string&, Lexer::Type&, bool);
bool isNumber (std::string&, Lexer::Type&);
bool isInteger (std::string&, Lexer::Type&);
bool isHexNumber (std::string&, Lexer::Type&);
bool isSeparator (std::string&, Lexer::Type&);
bool isURL (std::string&, Lexer::Type&);
bool isPair (std::string&, Lexer::Type&);
bool isSet (std::string&, Lexer::Type&);
bool isTag (std::string&, Lexer::Type&);
bool isPath (std::string&, Lexer::Type&);
bool isSubstitution (std::string&, Lexer::Type&);
bool isPattern (std::string&, Lexer::Type&);
bool isOperator (std::string&, Lexer::Type&);
bool isDOM (std::string&, Lexer::Type&);
bool isIdentifier (std::string&, Lexer::Type&);
bool isWord (std::string&, Lexer::Type&);
bool isLiteral (const std::string&, bool, bool);
bool isOneOf (const std::vector <std::string>&, bool, bool);
bool isOneOf (const std::map <std::string, std::string>&, bool, bool);
bool isEOS() const;
bool isString(std::string&, Lexer::Type&, const std::string&);
bool isDate(std::string&, Lexer::Type&);
bool isDuration(std::string&, Lexer::Type&);
bool isUUID(std::string&, Lexer::Type&, bool);
bool isNumber(std::string&, Lexer::Type&);
bool isInteger(std::string&, Lexer::Type&);
bool isHexNumber(std::string&, Lexer::Type&);
bool isSeparator(std::string&, Lexer::Type&);
bool isURL(std::string&, Lexer::Type&);
bool isPair(std::string&, Lexer::Type&);
bool isSet(std::string&, Lexer::Type&);
bool isTag(std::string&, Lexer::Type&);
bool isPath(std::string&, Lexer::Type&);
bool isSubstitution(std::string&, Lexer::Type&);
bool isPattern(std::string&, Lexer::Type&);
bool isOperator(std::string&, Lexer::Type&);
bool isDOM(std::string&, Lexer::Type&);
bool isIdentifier(std::string&, Lexer::Type&);
bool isWord(std::string&, Lexer::Type&);
bool isLiteral(const std::string&, bool, bool);
bool isOneOf(const std::vector<std::string>&, bool, bool);
bool isOneOf(const std::map<std::string, std::string>&, bool, bool);
private:
private:
std::string _text;
std::size_t _cursor;
std::size_t _eos;

View File

@@ -27,64 +27,63 @@
#include <cmake.h>
// cmake.h include header must come first
#include <TDB2.h>
#include <iostream>
#include <sstream>
#include <algorithm>
#include <vector>
#include <list>
#include <unordered_set>
#include <stdlib.h>
#include <signal.h>
#include <Context.h>
#include <Color.h>
#include <Context.h>
#include <Datetime.h>
#include <TDB2.h>
#include <Table.h>
#include <shared.h>
#include <format.h>
#include <main.h>
#include <shared.h>
#include <signal.h>
#include <stdlib.h>
#include <util.h>
#include <algorithm>
#include <iostream>
#include <list>
#include <sstream>
#include <unordered_set>
#include <vector>
#include "tc/Server.h"
#include "tc/util.h"
bool TDB2::debug_mode = false;
static void dependency_scan (std::vector<Task> &);
static void dependency_scan(std::vector<Task>&);
////////////////////////////////////////////////////////////////////////////////
TDB2::TDB2 ()
: replica {tc::Replica()} // in-memory Replica
, _working_set {}
{
}
TDB2::TDB2()
: replica{tc::Replica()} // in-memory Replica
,
_working_set{} {}
////////////////////////////////////////////////////////////////////////////////
void TDB2::open_replica (const std::string& location, bool create_if_missing)
{
void TDB2::open_replica(const std::string& location, bool create_if_missing) {
replica = tc::Replica(location, create_if_missing);
}
////////////////////////////////////////////////////////////////////////////////
// Add the new task to the replica.
void TDB2::add (Task& task)
{
void TDB2::add(Task& task) {
// Ensure the task is consistent, and provide defaults if necessary.
// bool argument to validate() is "applyDefault", to apply default values for
// properties not otherwise given.
task.validate (true);
task.validate(true);
std::string uuid = task.get ("uuid");
std::string uuid = task.get("uuid");
changes[uuid] = task;
// run hooks for this new task
Context::getContext ().hooks.onAdd (task);
Context::getContext().hooks.onAdd(task);
auto innertask = replica.import_task_with_uuid (uuid);
auto innertask = replica.import_task_with_uuid(uuid);
{
auto guard = replica.mutate_task(innertask);
// add the task attributes
for (auto& attr : task.all ()) {
for (auto& attr : task.all()) {
// TaskChampion does not store uuid or id in the taskmap
if (attr == "uuid" || attr == "id") {
continue;
@@ -93,35 +92,34 @@ void TDB2::add (Task& task)
// Use `set_status` for the task status, to get expected behavior
// with respect to the working set.
else if (attr == "status") {
innertask.set_status (Task::status2tc (Task::textToStatus (task.get (attr))));
innertask.set_status(Task::status2tc(Task::textToStatus(task.get(attr))));
}
// use `set_modified` to set the modified timestamp, avoiding automatic
// updates to this field by TaskChampion.
else if (attr == "modified") {
auto mod = (time_t) std::stoi (task.get (attr));
innertask.set_modified (mod);
auto mod = (time_t)std::stoi(task.get(attr));
innertask.set_modified(mod);
}
// otherwise, just set the k/v map value
else {
innertask.set_value (attr, std::make_optional (task.get (attr)));
innertask.set_value(attr, std::make_optional(task.get(attr)));
}
}
}
auto ws = replica.working_set ();
auto ws = replica.working_set();
// get the ID that was assigned to this task
auto id = ws.by_uuid (uuid);
auto id = ws.by_uuid(uuid);
// update the cached working set with the new information
_working_set = std::make_optional (std::move (ws));
_working_set = std::make_optional(std::move(ws));
if (id.has_value ()) {
task.id = id.value();
if (id.has_value()) {
task.id = id.value();
}
}
////////////////////////////////////////////////////////////////////////////////
@@ -139,31 +137,30 @@ void TDB2::add (Task& task)
// this method. In this case, this method throws an error that will make sense
// to the user. This is especially unlikely since tasks are only deleted when
// they have been unmodified for a long time.
void TDB2::modify (Task& task)
{
void TDB2::modify(Task& task) {
// All locally modified tasks are timestamped, implicitly overwriting any
// changes the user or hooks tried to apply to the "modified" attribute.
task.setAsNow ("modified");
task.validate (false);
auto uuid = task.get ("uuid");
task.setAsNow("modified");
task.validate(false);
auto uuid = task.get("uuid");
changes[uuid] = task;
// invoke the hook and allow it to modify the task before updating
// invoke the hook and allow it to modify the task before updating
Task original;
get (uuid, original);
Context::getContext ().hooks.onModify (original, task);
get(uuid, original);
Context::getContext().hooks.onModify(original, task);
auto maybe_tctask = replica.get_task (uuid);
if (!maybe_tctask.has_value ()) {
throw std::string ("task no longer exists");
auto maybe_tctask = replica.get_task(uuid);
if (!maybe_tctask.has_value()) {
throw std::string("task no longer exists");
}
auto tctask = std::move (maybe_tctask.value ());
auto tctask = std::move(maybe_tctask.value());
auto guard = replica.mutate_task(tctask);
auto tctask_map = tctask.get_taskmap ();
auto tctask_map = tctask.get_taskmap();
std::unordered_set<std::string> seen;
for (auto k : task.all ()) {
for (auto k : task.all()) {
// ignore task keys that aren't stored
if (k == "uuid") {
continue;
@@ -183,38 +180,35 @@ void TDB2::modify (Task& task)
if (v_new == "") {
tctask.set_value(k, {});
} else {
tctask.set_value(k, make_optional (v_new));
tctask.set_value(k, make_optional(v_new));
}
}
}
// we've now added and updated properties; but must find any deleted properties
for (auto kv : tctask_map) {
if (seen.find (kv.first) == seen.end ()) {
tctask.set_value (kv.first, {});
if (seen.find(kv.first) == seen.end()) {
tctask.set_value(kv.first, {});
}
}
}
////////////////////////////////////////////////////////////////////////////////
void TDB2::purge (Task& task)
{
auto uuid = task.get ("uuid");
replica.delete_task (uuid);
void TDB2::purge(Task& task) {
auto uuid = task.get("uuid");
replica.delete_task(uuid);
}
////////////////////////////////////////////////////////////////////////////////
const tc::WorkingSet &TDB2::working_set ()
{
if (!_working_set.has_value ()) {
_working_set = std::make_optional (replica.working_set ());
const tc::WorkingSet& TDB2::working_set() {
if (!_working_set.has_value()) {
_working_set = std::make_optional(replica.working_set());
}
return _working_set.value ();
return _working_set.value();
}
////////////////////////////////////////////////////////////////////////////////
void TDB2::get_changes (std::vector <Task>& changes)
{
void TDB2::get_changes(std::vector<Task>& changes) {
std::map<std::string, Task>& changes_map = this->changes;
changes.clear();
std::transform(changes_map.begin(), changes_map.end(), std::back_inserter(changes),
@@ -222,8 +216,7 @@ void TDB2::get_changes (std::vector <Task>& changes)
}
////////////////////////////////////////////////////////////////////////////////
void TDB2::revert ()
{
void TDB2::revert() {
auto undo_ops = replica.get_undo_ops();
if (undo_ops.len == 0) {
std::cout << "No operations to undo.";
@@ -235,19 +228,18 @@ void TDB2::revert ()
} else {
replica.free_replica_ops(undo_ops);
}
replica.rebuild_working_set (false);
replica.rebuild_working_set(false);
}
////////////////////////////////////////////////////////////////////////////////
bool TDB2::confirm_revert (struct tc::ffi::TCReplicaOpList undo_ops)
{
bool TDB2::confirm_revert(struct tc::ffi::TCReplicaOpList undo_ops) {
// TODO Use show_diff rather than this basic listing of operations, though
// this might be a worthy undo.style itself.
std::cout << "The following " << undo_ops.len << " operations would be reverted:\n";
for (size_t i = 0; i < undo_ops.len; i++) {
std::cout << "- ";
tc::ffi::TCReplicaOp op = undo_ops.items[i];
switch(op.operation_type) {
switch (op.operation_type) {
case tc::ffi::TCReplicaOpType::Create:
std::cout << "Create " << replica.get_op_uuid(op);
break;
@@ -256,114 +248,100 @@ bool TDB2::confirm_revert (struct tc::ffi::TCReplicaOpList undo_ops)
break;
case tc::ffi::TCReplicaOpType::Update:
std::cout << "Update " << replica.get_op_uuid(op) << "\n";
std::cout << " " << replica.get_op_property(op) << ": " << option_string(replica.get_op_old_value(op)) << " -> " << option_string(replica.get_op_value(op));
std::cout << " " << replica.get_op_property(op) << ": "
<< option_string(replica.get_op_old_value(op)) << " -> "
<< option_string(replica.get_op_value(op));
break;
case tc::ffi::TCReplicaOpType::UndoPoint:
throw std::string ("Can't undo UndoPoint.");
throw std::string("Can't undo UndoPoint.");
break;
default:
throw std::string ("Can't undo non-operation.");
throw std::string("Can't undo non-operation.");
break;
}
std::cout << "\n";
}
return ! Context::getContext ().config.getBoolean ("confirmation") ||
confirm ("The undo command is not reversible. Are you sure you want to revert to the previous state?");
return !Context::getContext().config.getBoolean("confirmation") ||
confirm(
"The undo command is not reversible. Are you sure you want to revert to the previous "
"state?");
}
////////////////////////////////////////////////////////////////////////////////
std::string TDB2::option_string(std::string input) {
return input == "" ? "<empty>" : input;
}
std::string TDB2::option_string(std::string input) { return input == "" ? "<empty>" : input; }
////////////////////////////////////////////////////////////////////////////////
void TDB2::show_diff (
const std::string& current,
const std::string& prior,
const std::string& when)
{
Datetime lastChange (strtoll (when.c_str (), nullptr, 10));
void TDB2::show_diff(const std::string& current, const std::string& prior,
const std::string& when) {
Datetime lastChange(strtoll(when.c_str(), nullptr, 10));
// Set the colors.
Color color_red (Context::getContext ().color () ? Context::getContext ().config.get ("color.undo.before") : "");
Color color_green (Context::getContext ().color () ? Context::getContext ().config.get ("color.undo.after") : "");
Color color_red(
Context::getContext().color() ? Context::getContext().config.get("color.undo.before") : "");
Color color_green(
Context::getContext().color() ? Context::getContext().config.get("color.undo.after") : "");
auto before = prior == "" ? Task() : Task(prior);
auto after = Task(current);
if (Context::getContext ().config.get ("undo.style") == "side")
{
if (Context::getContext().config.get("undo.style") == "side") {
Table view = before.diffForUndoSide(after);
std::cout << '\n'
<< format ("The last modification was made {1}", lastChange.toString ())
<< format("The last modification was made {1}", lastChange.toString()) << '\n'
<< '\n'
<< '\n'
<< view.render ()
<< '\n';
<< view.render() << '\n';
}
else if (Context::getContext ().config.get ("undo.style") == "diff")
{
else if (Context::getContext().config.get("undo.style") == "diff") {
Table view = before.diffForUndoPatch(after, lastChange);
std::cout << '\n'
<< view.render ()
<< '\n';
std::cout << '\n' << view.render() << '\n';
}
}
////////////////////////////////////////////////////////////////////////////////
void TDB2::gc ()
{
void TDB2::gc() {
Timer timer;
// Allowed as an override, but not recommended.
if (Context::getContext ().config.getBoolean ("gc"))
{
replica.rebuild_working_set (true);
if (Context::getContext().config.getBoolean("gc")) {
replica.rebuild_working_set(true);
}
Context::getContext ().time_gc_us += timer.total_us ();
Context::getContext().time_gc_us += timer.total_us();
}
////////////////////////////////////////////////////////////////////////////////
void TDB2::expire_tasks ()
{
replica.expire_tasks ();
}
void TDB2::expire_tasks() { replica.expire_tasks(); }
////////////////////////////////////////////////////////////////////////////////
// Latest ID is that of the last pending task.
int TDB2::latest_id ()
{
const tc::WorkingSet &ws = working_set ();
return (int)ws.largest_index ();
int TDB2::latest_id() {
const tc::WorkingSet& ws = working_set();
return (int)ws.largest_index();
}
////////////////////////////////////////////////////////////////////////////////
const std::vector <Task> TDB2::all_tasks ()
{
const std::vector<Task> TDB2::all_tasks() {
auto all_tctasks = replica.all_tasks();
std::vector <Task> all;
for (auto& tctask : all_tctasks)
all.push_back (Task (std::move (tctask)));
std::vector<Task> all;
for (auto& tctask : all_tctasks) all.push_back(Task(std::move(tctask)));
return all;
}
////////////////////////////////////////////////////////////////////////////////
const std::vector <Task> TDB2::pending_tasks ()
{
const tc::WorkingSet &ws = working_set ();
auto largest_index = ws.largest_index ();
const std::vector<Task> TDB2::pending_tasks() {
const tc::WorkingSet& ws = working_set();
auto largest_index = ws.largest_index();
std::vector <Task> result;
std::vector<Task> result;
for (size_t i = 0; i <= largest_index; i++) {
auto maybe_uuid = ws.by_index (i);
if (maybe_uuid.has_value ()) {
auto maybe_task = replica.get_task (maybe_uuid.value ());
if (maybe_task.has_value ()) {
result.push_back (Task (std::move (maybe_task.value ())));
auto maybe_uuid = ws.by_index(i);
if (maybe_uuid.has_value()) {
auto maybe_task = replica.get_task(maybe_uuid.value());
if (maybe_task.has_value()) {
result.push_back(Task(std::move(maybe_task.value())));
}
}
}
@@ -374,16 +352,15 @@ const std::vector <Task> TDB2::pending_tasks ()
}
////////////////////////////////////////////////////////////////////////////////
const std::vector <Task> TDB2::completed_tasks ()
{
const std::vector<Task> TDB2::completed_tasks() {
auto all_tctasks = replica.all_tasks();
const tc::WorkingSet &ws = working_set ();
const tc::WorkingSet& ws = working_set();
std::vector <Task> result;
std::vector<Task> result;
for (auto& tctask : all_tctasks) {
// if this task is _not_ in the working set, return it.
if (!ws.by_uuid (tctask.get_uuid ())) {
result.push_back (Task (std::move (tctask)));
if (!ws.by_uuid(tctask.get_uuid())) {
result.push_back(Task(std::move(tctask)));
}
}
@@ -392,10 +369,9 @@ const std::vector <Task> TDB2::completed_tasks ()
////////////////////////////////////////////////////////////////////////////////
// Locate task by ID, wherever it is.
bool TDB2::get (int id, Task& task)
{
const tc::WorkingSet &ws = working_set ();
const auto maybe_uuid = ws.by_index (id);
bool TDB2::get(int id, Task& task) {
const tc::WorkingSet& ws = working_set();
const auto maybe_uuid = ws.by_index(id);
if (maybe_uuid) {
auto maybe_task = replica.get_task(*maybe_uuid);
if (maybe_task) {
@@ -409,25 +385,24 @@ bool TDB2::get (int id, Task& task)
////////////////////////////////////////////////////////////////////////////////
// Locate task by UUID, including by partial ID, wherever it is.
bool TDB2::get (const std::string& uuid, Task& task)
{
bool TDB2::get(const std::string& uuid, Task& task) {
// try by raw uuid, if the length is right
if (uuid.size () == 36) {
if (uuid.size() == 36) {
try {
auto maybe_task = replica.get_task (uuid);
auto maybe_task = replica.get_task(uuid);
if (maybe_task) {
task = Task{std::move (*maybe_task)};
task = Task{std::move(*maybe_task)};
return true;
}
} catch (const std::string &err) {
} catch (const std::string& err) {
return false;
}
}
// Nothing to do but iterate over all tasks and check whether it's closeEnough
for (auto& tctask : replica.all_tasks ()) {
if (closeEnough (tctask.get_uuid (), uuid, uuid.length ())) {
task = Task{std::move (tctask)};
for (auto& tctask : replica.all_tasks()) {
if (closeEnough(tctask.get_uuid(), uuid, uuid.length())) {
task = Task{std::move(tctask)};
return true;
}
}
@@ -437,34 +412,25 @@ bool TDB2::get (const std::string& uuid, Task& task)
////////////////////////////////////////////////////////////////////////////////
// Locate task by UUID, wherever it is.
bool TDB2::has (const std::string& uuid)
{
bool TDB2::has(const std::string& uuid) {
Task task;
return get(uuid, task);
}
////////////////////////////////////////////////////////////////////////////////
const std::vector <Task> TDB2::siblings (Task& task)
{
std::vector <Task> results;
if (task.has ("parent"))
{
std::string parent = task.get ("parent");
const std::vector<Task> TDB2::siblings(Task& task) {
std::vector<Task> results;
if (task.has("parent")) {
std::string parent = task.get("parent");
for (auto& i : this->pending_tasks())
{
for (auto& i : this->pending_tasks()) {
// Do not include self in results.
if (i.id != task.id)
{
if (i.id != task.id) {
// Do not include completed or deleted tasks.
if (i.getStatus () != Task::completed &&
i.getStatus () != Task::deleted)
{
if (i.getStatus() != Task::completed && i.getStatus() != Task::deleted) {
// If task has the same parent, it is a sibling.
if (i.has ("parent") &&
i.get ("parent") == parent)
{
results.push_back (i);
if (i.has("parent") && i.get("parent") == parent) {
results.push_back(i);
}
}
}
@@ -475,41 +441,40 @@ const std::vector <Task> TDB2::siblings (Task& task)
}
////////////////////////////////////////////////////////////////////////////////
const std::vector <Task> TDB2::children (Task& parent)
{
const std::vector<Task> TDB2::children(Task& parent) {
// scan _pending_ tasks for those with `parent` equal to this task
std::vector <Task> results;
std::string this_uuid = parent.get ("uuid");
std::vector<Task> results;
std::string this_uuid = parent.get("uuid");
const tc::WorkingSet &ws = working_set ();
size_t end_idx = ws.largest_index ();
const tc::WorkingSet& ws = working_set();
size_t end_idx = ws.largest_index();
for (size_t i = 0; i <= end_idx; i++) {
auto uuid_opt = ws.by_index (i);
auto uuid_opt = ws.by_index(i);
if (!uuid_opt) {
continue;
}
auto uuid = uuid_opt.value ();
auto uuid = uuid_opt.value();
// skip self-references
if (uuid == this_uuid) {
continue;
}
auto task_opt = replica.get_task (uuid_opt.value ());
auto task_opt = replica.get_task(uuid_opt.value());
if (!task_opt) {
continue;
}
auto task = std::move (task_opt.value ());
auto task = std::move(task_opt.value());
auto parent_uuid_opt = task.get_value ("parent");
auto parent_uuid_opt = task.get_value("parent");
if (!parent_uuid_opt) {
continue;
}
auto parent_uuid = parent_uuid_opt.value ();
auto parent_uuid = parent_uuid_opt.value();
if (parent_uuid == this_uuid) {
results.push_back (Task (std::move (task)));
results.push_back(Task(std::move(task)));
}
}
@@ -517,40 +482,30 @@ const std::vector <Task> TDB2::children (Task& parent)
}
////////////////////////////////////////////////////////////////////////////////
std::string TDB2::uuid (int id)
{
const tc::WorkingSet &ws = working_set ();
return ws.by_index ((size_t)id).value_or ("");
std::string TDB2::uuid(int id) {
const tc::WorkingSet& ws = working_set();
return ws.by_index((size_t)id).value_or("");
}
////////////////////////////////////////////////////////////////////////////////
int TDB2::id (const std::string& uuid)
{
const tc::WorkingSet &ws = working_set ();
return (int)ws.by_uuid (uuid).value_or (0);
int TDB2::id(const std::string& uuid) {
const tc::WorkingSet& ws = working_set();
return (int)ws.by_uuid(uuid).value_or(0);
}
////////////////////////////////////////////////////////////////////////////////
int TDB2::num_local_changes ()
{
return (int)replica.num_local_operations ();
}
int TDB2::num_local_changes() { return (int)replica.num_local_operations(); }
////////////////////////////////////////////////////////////////////////////////
int TDB2::num_reverts_possible ()
{
return (int)replica.num_undo_points ();
}
int TDB2::num_reverts_possible() { return (int)replica.num_undo_points(); }
////////////////////////////////////////////////////////////////////////////////
void TDB2::sync (tc::Server server, bool avoid_snapshots)
{
void TDB2::sync(tc::Server server, bool avoid_snapshots) {
replica.sync(std::move(server), avoid_snapshots);
}
////////////////////////////////////////////////////////////////////////////////
void TDB2::dump ()
{
void TDB2::dump() {
// TODO
}
@@ -558,24 +513,16 @@ void TDB2::dump ()
// For any task that has depenencies, follow the chain of dependencies until the
// end. Along the way, update the Task::is_blocked and Task::is_blocking data
// cache.
static void dependency_scan (std::vector<Task> &tasks)
{
for (auto& left : tasks)
{
for (auto& dep : left.getDependencyUUIDs ())
{
for (auto& right : tasks)
{
if (right.get ("uuid") == dep)
{
static void dependency_scan(std::vector<Task>& tasks) {
for (auto& left : tasks) {
for (auto& dep : left.getDependencyUUIDs()) {
for (auto& right : tasks) {
if (right.get("uuid") == dep) {
// GC hasn't run yet, check both tasks for their current status
Task::status lstatus = left.getStatus ();
Task::status rstatus = right.getStatus ();
if (lstatus != Task::completed &&
lstatus != Task::deleted &&
rstatus != Task::completed &&
rstatus != Task::deleted)
{
Task::status lstatus = left.getStatus();
Task::status rstatus = right.getStatus();
if (lstatus != Task::completed && lstatus != Task::deleted &&
rstatus != Task::completed && rstatus != Task::deleted) {
left.is_blocked = true;
right.is_blocking = true;
}

View File

@@ -27,71 +27,71 @@
#ifndef INCLUDED_TDB2
#define INCLUDED_TDB2
#include <map>
#include <unordered_set>
#include <unordered_map>
#include <vector>
#include <string>
#include <stdio.h>
#include <FS.h>
#include <Task.h>
#include <tc/WorkingSet.h>
#include <stdio.h>
#include <tc/Replica.h>
#include <tc/WorkingSet.h>
#include <map>
#include <string>
#include <unordered_map>
#include <unordered_set>
#include <vector>
namespace tc {
class Server;
}
// TDB2 Class represents all the files in the task database.
class TDB2
{
public:
class TDB2 {
public:
static bool debug_mode;
TDB2 ();
TDB2();
void open_replica (const std::string&, bool create_if_missing);
void add (Task&);
void modify (Task&);
void purge (Task&);
void get_changes (std::vector <Task>&);
void revert ();
void gc ();
void expire_tasks ();
int latest_id ();
void open_replica(const std::string &, bool create_if_missing);
void add(Task &);
void modify(Task &);
void purge(Task &);
void get_changes(std::vector<Task> &);
void revert();
void gc();
void expire_tasks();
int latest_id();
// Generalized task accessors.
const std::vector <Task> all_tasks ();
const std::vector <Task> pending_tasks ();
const std::vector <Task> completed_tasks ();
bool get (int, Task&);
bool get (const std::string&, Task&);
bool has (const std::string&);
const std::vector <Task> siblings (Task&);
const std::vector <Task> children (Task&);
const std::vector<Task> all_tasks();
const std::vector<Task> pending_tasks();
const std::vector<Task> completed_tasks();
bool get(int, Task &);
bool get(const std::string &, Task &);
bool has(const std::string &);
const std::vector<Task> siblings(Task &);
const std::vector<Task> children(Task &);
// ID <--> UUID mapping.
std::string uuid (int);
int id (const std::string&);
std::string uuid(int);
int id(const std::string &);
int num_local_changes ();
int num_reverts_possible ();
int num_local_changes();
int num_reverts_possible();
void dump ();
void dump();
void sync (tc::Server server, bool avoid_snapshots);
void sync(tc::Server server, bool avoid_snapshots);
bool confirm_revert(struct tc::ffi::TCReplicaOpList);
private:
private:
tc::Replica replica;
std::optional<tc::WorkingSet> _working_set;
// UUID -> Task containing all tasks modified in this invocation.
std::map<std::string, Task> changes;
const tc::WorkingSet &working_set ();
static std::string option_string (std::string input);
static void show_diff (const std::string&, const std::string&, const std::string&);
const tc::WorkingSet &working_set();
static std::string option_string(std::string input);
static void show_diff(const std::string &, const std::string &, const std::string &);
};
#endif

File diff suppressed because it is too large Load Diff

View File

@@ -27,27 +27,27 @@
#ifndef INCLUDED_TASK
#define INCLUDED_TASK
#include <vector>
#include <map>
#include <string>
#include <stdio.h>
#include <time.h>
#include <Datetime.h>
#include <JSON.h>
#include <Table.h>
#include <Datetime.h>
#include <stdio.h>
#include <tc/Task.h>
#include <time.h>
class Task
{
public:
#include <map>
#include <string>
#include <vector>
class Task {
public:
static std::string defaultProject;
static std::string defaultDue;
static std::string defaultScheduled;
static bool searchCaseSensitive;
static bool regex;
static std::map <std::string, std::string> attributes; // name -> type
static std::map <std::string, float> coefficients;
static std::map <std::string, std::vector <std::string>> customOrder;
static std::map<std::string, std::string> attributes; // name -> type
static std::map<std::string, float> coefficients;
static std::map<std::string, std::vector<std::string>> customOrder;
static float urgencyProjectCoefficient;
static float urgencyActiveCoefficient;
static float urgencyScheduledCoefficient;
@@ -60,159 +60,159 @@ public:
static float urgencyAgeCoefficient;
static float urgencyAgeMax;
public:
Task () = default;
bool operator== (const Task&);
bool operator!= (const Task&);
Task (const std::string&);
Task (const json::object*);
Task (tc::Task);
public:
Task() = default;
bool operator==(const Task&);
bool operator!=(const Task&);
Task(const std::string&);
Task(const json::object*);
Task(tc::Task);
void parse (const std::string&);
std::string composeJSON (bool decorate = false) const;
void parse(const std::string&);
std::string composeJSON(bool decorate = false) const;
// Status values.
enum status {pending, completed, deleted, recurring, waiting};
enum status { pending, completed, deleted, recurring, waiting };
// Date state values.
enum dateState {dateNotDue, dateAfterToday, dateLaterToday, dateEarlierToday, dateBeforeToday};
enum dateState { dateNotDue, dateAfterToday, dateLaterToday, dateEarlierToday, dateBeforeToday };
// Public data.
int id {0};
float urgency_value {0.0};
bool recalc_urgency {true};
bool is_blocked {false};
bool is_blocking {false};
int annotation_count {0};
int id{0};
float urgency_value{0.0};
bool recalc_urgency{true};
bool is_blocked{false};
bool is_blocking{false};
int annotation_count{0};
// Series of helper functions.
static status textToStatus (const std::string&);
static std::string statusToText (status);
static tc::Status status2tc (const Task::status);
static Task::status tc2status (const tc::Status);
static status textToStatus(const std::string&);
static std::string statusToText(status);
static tc::Status status2tc(const Task::status);
static Task::status tc2status(const tc::Status);
void setAsNow (const std::string&);
bool has (const std::string&) const;
std::vector <std::string> all () const;
const std::string identifier (bool shortened = false) const;
const std::string get (const std::string&) const;
const std::string& get_ref (const std::string&) const;
int get_int (const std::string&) const;
unsigned long get_ulong (const std::string&) const;
float get_float (const std::string&) const;
time_t get_date (const std::string&) const;
void set (const std::string&, const std::string&);
void set (const std::string&, long long);
void remove (const std::string&);
void setAsNow(const std::string&);
bool has(const std::string&) const;
std::vector<std::string> all() const;
const std::string identifier(bool shortened = false) const;
const std::string get(const std::string&) const;
const std::string& get_ref(const std::string&) const;
int get_int(const std::string&) const;
unsigned long get_ulong(const std::string&) const;
float get_float(const std::string&) const;
time_t get_date(const std::string&) const;
void set(const std::string&, const std::string&);
void set(const std::string&, long long);
void remove(const std::string&);
bool is_empty () const;
bool is_empty() const;
#ifdef PRODUCT_TASKWARRIOR
bool is_ready () const;
bool is_due () const;
bool is_dueyesterday () const;
bool is_duetoday () const;
bool is_duetomorrow () const;
bool is_dueweek () const;
bool is_duemonth () const;
bool is_duequarter () const;
bool is_dueyear () const;
bool is_overdue () const;
bool is_udaPresent () const;
bool is_orphanPresent () const;
bool is_ready() const;
bool is_due() const;
bool is_dueyesterday() const;
bool is_duetoday() const;
bool is_duetomorrow() const;
bool is_dueweek() const;
bool is_duemonth() const;
bool is_duequarter() const;
bool is_dueyear() const;
bool is_overdue() const;
bool is_udaPresent() const;
bool is_orphanPresent() const;
static bool isTagAttr (const std::string&);
static bool isDepAttr (const std::string&);
static bool isAnnotationAttr (const std::string&);
static bool isTagAttr(const std::string&);
static bool isDepAttr(const std::string&);
static bool isAnnotationAttr(const std::string&);
#endif
bool is_waiting () const;
bool is_waiting() const;
status getStatus () const;
void setStatus (status);
status getStatus() const;
void setStatus(status);
#ifdef PRODUCT_TASKWARRIOR
dateState getDateState (const std::string&) const;
dateState getDateState(const std::string&) const;
#endif
int getTagCount () const;
bool hasTag (const std::string&) const;
void addTag (const std::string&);
void setTags (const std::vector <std::string>&);
std::vector <std::string> getTags () const;
void removeTag (const std::string&);
int getTagCount() const;
bool hasTag(const std::string&) const;
void addTag(const std::string&);
void setTags(const std::vector<std::string>&);
std::vector<std::string> getTags() const;
void removeTag(const std::string&);
int getAnnotationCount () const;
bool hasAnnotations () const;
std::map <std::string, std::string> getAnnotations () const;
void setAnnotations (const std::map <std::string, std::string>&);
void addAnnotation (const std::string&);
void removeAnnotations ();
int getAnnotationCount() const;
bool hasAnnotations() const;
std::map<std::string, std::string> getAnnotations() const;
void setAnnotations(const std::map<std::string, std::string>&);
void addAnnotation(const std::string&);
void removeAnnotations();
#ifdef PRODUCT_TASKWARRIOR
void addDependency (int);
void addDependency(int);
#endif
void addDependency (const std::string&);
void addDependency(const std::string&);
#ifdef PRODUCT_TASKWARRIOR
void removeDependency (int);
void removeDependency (const std::string&);
bool hasDependency (const std::string&) const;
std::vector <int> getDependencyIDs () const;
std::vector <std::string> getDependencyUUIDs () const;
std::vector <Task> getBlockedTasks () const;
std::vector <Task> getDependencyTasks () const;
void removeDependency(int);
void removeDependency(const std::string&);
bool hasDependency(const std::string&) const;
std::vector<int> getDependencyIDs() const;
std::vector<std::string> getDependencyUUIDs() const;
std::vector<Task> getBlockedTasks() const;
std::vector<Task> getDependencyTasks() const;
std::vector <std::string> getUDAOrphans () const;
std::vector<std::string> getUDAOrphans() const;
void substitute (const std::string&, const std::string&, const std::string&);
void substitute(const std::string&, const std::string&, const std::string&);
#endif
void validate (bool applyDefault = true);
void validate(bool applyDefault = true);
float urgency_c () const;
float urgency ();
float urgency_c() const;
float urgency();
#ifdef PRODUCT_TASKWARRIOR
enum modType {modReplace, modPrepend, modAppend, modAnnotate};
void modify (modType, bool text_required = false);
enum modType { modReplace, modPrepend, modAppend, modAnnotate };
void modify(modType, bool text_required = false);
#endif
std::string diff (const Task& after) const;
std::string diffForInfo (const Task& after, const std::string& dateformat, long& last_timestamp, const long current_timestamp) const;
Table diffForUndoSide (const Task& after) const;
Table diffForUndoPatch (const Task& after, const Datetime& lastChange) const;
std::string diff(const Task& after) const;
std::string diffForInfo(const Task& after, const std::string& dateformat, long& last_timestamp,
const long current_timestamp) const;
Table diffForUndoSide(const Task& after) const;
Table diffForUndoPatch(const Task& after, const Datetime& lastChange) const;
private:
int determineVersion(const std::string&);
void parseJSON(const std::string&);
void parseJSON(const json::object*);
void parseTC(const tc::Task&);
void parseLegacy(const std::string&);
void validate_before(const std::string&, const std::string&);
const std::string encode(const std::string&) const;
const std::string decode(const std::string&) const;
const std::string tag2Attr(const std::string&) const;
const std::string attr2Tag(const std::string&) const;
const std::string dep2Attr(const std::string&) const;
const std::string attr2Dep(const std::string&) const;
void fixDependsAttribute();
void fixTagsAttribute();
private:
int determineVersion (const std::string&);
void parseJSON (const std::string&);
void parseJSON (const json::object*);
void parseTC (const tc::Task&);
void parseLegacy (const std::string&);
void validate_before (const std::string&, const std::string&);
const std::string encode (const std::string&) const;
const std::string decode (const std::string&) const;
const std::string tag2Attr (const std::string&) const;
const std::string attr2Tag (const std::string&) const;
const std::string dep2Attr (const std::string&) const;
const std::string attr2Dep (const std::string&) const;
void fixDependsAttribute ();
void fixTagsAttribute ();
protected:
std::map<std::string, std::string> data{};
protected:
std::map <std::string, std::string> data {};
public:
float urgency_project () const;
float urgency_active () const;
float urgency_scheduled () const;
float urgency_waiting () const;
float urgency_blocked () const;
float urgency_inherit () const;
float urgency_annotations () const;
float urgency_tags () const;
float urgency_due () const;
float urgency_blocking () const;
float urgency_age () const;
public:
float urgency_project() const;
float urgency_active() const;
float urgency_scheduled() const;
float urgency_waiting() const;
float urgency_blocked() const;
float urgency_inherit() const;
float urgency_annotations() const;
float urgency_tags() const;
float urgency_due() const;
float urgency_blocking() const;
float urgency_age() const;
};
#endif

File diff suppressed because it is too large Load Diff

View File

@@ -27,94 +27,94 @@
#ifndef INCLUDED_VARIANT
#define INCLUDED_VARIANT
#include <Task.h>
#include <time.h>
#include <map>
#include <string>
#include <time.h>
#include <Task.h>
class Variant
{
public:
class Variant {
public:
static std::string dateFormat;
static bool searchCaseSensitive;
static bool searchUsingRegex;
static bool isoEnabled;
enum type {type_boolean, type_integer, type_real, type_string, type_date, type_duration};
enum type { type_boolean, type_integer, type_real, type_string, type_date, type_duration };
Variant () = default;
Variant (const Variant&);
Variant (const bool);
Variant (const int);
Variant (const double);
Variant (const std::string&);
Variant (const char*);
Variant (const time_t, const enum type);
Variant() = default;
Variant(const Variant&);
Variant(const bool);
Variant(const int);
Variant(const double);
Variant(const std::string&);
Variant(const char*);
Variant(const time_t, const enum type);
void source (const std::string&);
const std::string& source () const;
void source(const std::string&);
const std::string& source() const;
Variant& operator= (const Variant&);
Variant& operator=(const Variant&);
bool operator&& (const Variant&) const;
bool operator|| (const Variant&) const;
bool operator_xor (const Variant&) const;
bool operator< (const Variant&) const;
bool operator<= (const Variant&) const;
bool operator> (const Variant&) const;
bool operator>= (const Variant&) const;
bool operator== (const Variant&) const;
bool operator!= (const Variant&) const;
bool operator_match (const Variant&, const Task&) const;
bool operator_nomatch (const Variant&, const Task&) const;
bool operator_partial (const Variant&) const;
bool operator_nopartial (const Variant&) const;
bool operator_hastag (const Variant&, const Task&) const;
bool operator_notag (const Variant&, const Task&) const;
bool operator! () const;
bool operator&&(const Variant&) const;
bool operator||(const Variant&) const;
bool operator_xor(const Variant&) const;
bool operator<(const Variant&) const;
bool operator<=(const Variant&) const;
bool operator>(const Variant&) const;
bool operator>=(const Variant&) const;
bool operator==(const Variant&) const;
bool operator!=(const Variant&) const;
bool operator_match(const Variant&, const Task&) const;
bool operator_nomatch(const Variant&, const Task&) const;
bool operator_partial(const Variant&) const;
bool operator_nopartial(const Variant&) const;
bool operator_hastag(const Variant&, const Task&) const;
bool operator_notag(const Variant&, const Task&) const;
bool operator!() const;
Variant& operator^= (const Variant&);
Variant operator^ (const Variant&) const;
Variant& operator^=(const Variant&);
Variant operator^(const Variant&) const;
Variant& operator-= (const Variant&);
Variant operator- (const Variant&) const;
Variant& operator-=(const Variant&);
Variant operator-(const Variant&) const;
Variant& operator+= (const Variant&);
Variant operator+ (const Variant&) const;
Variant& operator+=(const Variant&);
Variant operator+(const Variant&) const;
Variant& operator*= (const Variant&);
Variant operator* (const Variant&) const;
Variant& operator*=(const Variant&);
Variant operator*(const Variant&) const;
Variant& operator/= (const Variant&);
Variant operator/ (const Variant&) const;
Variant& operator/=(const Variant&);
Variant operator/(const Variant&) const;
Variant& operator%= (const Variant&);
Variant operator% (const Variant&) const;
Variant& operator%=(const Variant&);
Variant operator%(const Variant&) const;
operator std::string () const;
void sqrt ();
operator std::string() const;
void sqrt();
void cast (const enum type);
int type ();
bool trivial () const;
void cast(const enum type);
int type();
bool trivial() const;
bool get_bool () const;
long long get_integer () const;
double get_real () const;
const std::string& get_string () const;
time_t get_date () const;
time_t get_duration () const;
bool get_bool() const;
long long get_integer() const;
double get_real() const;
const std::string& get_string() const;
time_t get_date() const;
time_t get_duration() const;
private:
enum type _type {type_boolean};
bool _bool {false};
long long _integer {0};
double _real {0.0};
std::string _string {""};
time_t _date {0};
time_t _duration {0};
private:
enum type _type { type_boolean };
bool _bool{false};
long long _integer{0};
double _real{0.0};
std::string _string{""};
time_t _date{0};
time_t _duration{0};
std::string _source {""};
std::string _source{""};
};
#endif

View File

@@ -67,38 +67,32 @@ bool Version::is_valid() const { return major >= 0; }
////////////////////////////////////////////////////////////////////////////////
bool Version::operator<(const Version &other) const {
return std::tie(major, minor, patch) <
std::tie(other.major, other.minor, other.patch);
return std::tie(major, minor, patch) < std::tie(other.major, other.minor, other.patch);
}
////////////////////////////////////////////////////////////////////////////////
bool Version::operator<=(const Version &other) const {
return std::tie(major, minor, patch) <=
std::tie(other.major, other.minor, other.patch);
return std::tie(major, minor, patch) <= std::tie(other.major, other.minor, other.patch);
}
////////////////////////////////////////////////////////////////////////////////
bool Version::operator>(const Version &other) const {
return std::tie(major, minor, patch) >
std::tie(other.major, other.minor, other.patch);
return std::tie(major, minor, patch) > std::tie(other.major, other.minor, other.patch);
}
////////////////////////////////////////////////////////////////////////////////
bool Version::operator>=(const Version &other) const {
return std::tie(major, minor, patch) >=
std::tie(other.major, other.minor, other.patch);
return std::tie(major, minor, patch) >= std::tie(other.major, other.minor, other.patch);
}
////////////////////////////////////////////////////////////////////////////////
bool Version::operator==(const Version &other) const {
return std::tie(major, minor, patch) ==
std::tie(other.major, other.minor, other.patch);
return std::tie(major, minor, patch) == std::tie(other.major, other.minor, other.patch);
}
////////////////////////////////////////////////////////////////////////////////
bool Version::operator!=(const Version &other) const {
return std::tie(major, minor, patch) !=
std::tie(other.major, other.minor, other.patch);
return std::tie(major, minor, patch) != std::tie(other.major, other.minor, other.patch);
}
////////////////////////////////////////////////////////////////////////////////

View File

@@ -31,7 +31,7 @@
// A utility class for handling Taskwarrior versions.
class Version {
public:
public:
// Parse a version from a string. This must be of the format
// digits.digits.digits.
explicit Version(std::string version);
@@ -60,9 +60,9 @@ public:
// Convert back to a string.
operator std::string() const;
friend std::ostream& operator<<(std::ostream& os, const Version& version);
friend std::ostream &operator<<(std::ostream &os, const Version &version);
private:
private:
int major = -1;
int minor = -1;
int patch = -1;

View File

@@ -27,42 +27,39 @@
#include <cmake.h>
// cmake.h include header must come first
#include <ViewTask.h>
#include <numeric>
#include <Context.h>
#include <ViewTask.h>
#include <format.h>
#include <util.h>
#include <utf8.h>
#include <main.h>
#include <utf8.h>
#include <util.h>
#include <numeric>
////////////////////////////////////////////////////////////////////////////////
ViewTask::ViewTask ()
: _width (0)
, _left_margin (0)
, _header (0)
, _sort_header (0)
, _odd (0)
, _even (0)
, _intra_padding (1)
, _intra_odd (0)
, _intra_even (0)
, _extra_padding (0)
, _extra_odd (0)
, _extra_even (0)
, _truncate_lines (0)
, _truncate_rows (0)
, _lines (0)
, _rows (0)
{
}
ViewTask::ViewTask()
: _width(0),
_left_margin(0),
_header(0),
_sort_header(0),
_odd(0),
_even(0),
_intra_padding(1),
_intra_odd(0),
_intra_even(0),
_extra_padding(0),
_extra_odd(0),
_extra_even(0),
_truncate_lines(0),
_truncate_rows(0),
_lines(0),
_rows(0) {}
////////////////////////////////////////////////////////////////////////////////
ViewTask::~ViewTask ()
{
for (auto& col : _columns)
delete col;
ViewTask::~ViewTask() {
for (auto& col : _columns) delete col;
_columns.clear ();
_columns.clear();
}
////////////////////////////////////////////////////////////////////////////////
@@ -108,64 +105,55 @@ ViewTask::~ViewTask ()
// the larger fields. If the widest field is W0, and the second widest
// field is W1, then a solution may be achievable by reducing W0 --> W1.
//
std::string ViewTask::render (std::vector <Task>& data, std::vector <int>& sequence)
{
std::string ViewTask::render(std::vector<Task>& data, std::vector<int>& sequence) {
Timer timer;
bool const obfuscate = Context::getContext ().config.getBoolean ("obfuscate");
bool const print_empty_columns = Context::getContext ().config.getBoolean ("print.empty.columns");
std::vector <Column*> nonempty_columns;
std::vector <bool> nonempty_sort;
bool const obfuscate = Context::getContext().config.getBoolean("obfuscate");
bool const print_empty_columns = Context::getContext().config.getBoolean("print.empty.columns");
std::vector<Column*> nonempty_columns;
std::vector<bool> nonempty_sort;
// Determine minimal, ideal column widths.
std::vector <int> minimal;
std::vector <int> ideal;
std::vector<int> minimal;
std::vector<int> ideal;
for (unsigned int i = 0; i < _columns.size (); ++i)
{
for (unsigned int i = 0; i < _columns.size(); ++i) {
// Headers factor in to width calculations.
unsigned int global_min = 0;
unsigned int global_ideal = global_min;
for (unsigned int s = 0; s < sequence.size (); ++s)
{
if ((int)s >= _truncate_lines && _truncate_lines != 0)
break;
for (unsigned int s = 0; s < sequence.size(); ++s) {
if ((int)s >= _truncate_lines && _truncate_lines != 0) break;
if ((int)s >= _truncate_rows && _truncate_rows != 0)
break;
if ((int)s >= _truncate_rows && _truncate_rows != 0) break;
// Determine minimum and ideal width for this column.
unsigned int min = 0;
unsigned int ideal = 0;
_columns[i]->measure (data[sequence[s]], min, ideal);
_columns[i]->measure(data[sequence[s]], min, ideal);
if (min > global_min) global_min = min;
if (min > global_min) global_min = min;
if (ideal > global_ideal) global_ideal = ideal;
// If a fixed-width column was just measured, there is no point repeating
// the measurement for all tasks.
if (_columns[i]->is_fixed_width ())
break;
if (_columns[i]->is_fixed_width()) break;
}
if (print_empty_columns || global_min != 0)
{
unsigned int label_length = utf8_width (_columns[i]->label ());
if (label_length > global_min) global_min = label_length;
if (print_empty_columns || global_min != 0) {
unsigned int label_length = utf8_width(_columns[i]->label());
if (label_length > global_min) global_min = label_length;
if (label_length > global_ideal) global_ideal = label_length;
minimal.push_back (global_min);
ideal.push_back (global_ideal);
minimal.push_back(global_min);
ideal.push_back(global_ideal);
}
if (! print_empty_columns)
{
if (global_min != 0) // Column is nonempty
if (!print_empty_columns) {
if (global_min != 0) // Column is nonempty
{
nonempty_columns.push_back (_columns[i]);
nonempty_sort.push_back (_sort[i]);
}
else // Column is empty, drop it
nonempty_columns.push_back(_columns[i]);
nonempty_sort.push_back(_sort[i]);
} else // Column is empty, drop it
{
// Note: This is safe to do because we set _columns = nonempty_columns
// after iteration over _columns is finished.
@@ -174,51 +162,40 @@ std::string ViewTask::render (std::vector <Task>& data, std::vector <int>& seque
}
}
if (! print_empty_columns)
{
if (!print_empty_columns) {
_columns = nonempty_columns;
_sort = nonempty_sort;
}
int all_extra = _left_margin
+ (2 * _extra_padding)
+ ((_columns.size () - 1) * _intra_padding);
int all_extra = _left_margin + (2 * _extra_padding) + ((_columns.size() - 1) * _intra_padding);
// Sum the widths.
int sum_minimal = std::accumulate (minimal.begin (), minimal.end (), 0);
int sum_ideal = std::accumulate (ideal.begin (), ideal.end (), 0);
int sum_minimal = std::accumulate(minimal.begin(), minimal.end(), 0);
int sum_ideal = std::accumulate(ideal.begin(), ideal.end(), 0);
// Calculate final column widths.
int overage = _width - sum_minimal - all_extra;
Context::getContext ().debug (format ("ViewTask::render min={1} ideal={2} overage={3} width={4}",
sum_minimal + all_extra,
sum_ideal + all_extra,
overage,
_width));
Context::getContext().debug(format("ViewTask::render min={1} ideal={2} overage={3} width={4}",
sum_minimal + all_extra, sum_ideal + all_extra, overage,
_width));
std::vector <int> widths;
std::vector<int> widths;
// Ideal case. Everything fits.
if (_width == 0 || sum_ideal + all_extra <= _width)
{
if (_width == 0 || sum_ideal + all_extra <= _width) {
widths = ideal;
}
// Not enough for minimum. Decrease certain columns.
else if (overage < 0)
{
else if (overage < 0) {
// Determine which columns are the longest.
unsigned int longest = 0;
unsigned int second_longest = 0;
for (unsigned int j = 0; j < minimal.size(); j++)
{
if (minimal[j] > minimal[longest])
{
for (unsigned int j = 0; j < minimal.size(); j++) {
if (minimal[j] > minimal[longest]) {
second_longest = longest;
longest = j;
}
else if (minimal[j] > minimal[second_longest])
{
} else if (minimal[j] > minimal[second_longest]) {
second_longest = j;
}
}
@@ -226,53 +203,46 @@ std::string ViewTask::render (std::vector <Task>& data, std::vector <int>& seque
// Case 1: Shortening longest column still keeps it longest. Let it bear
// all the shortening.
widths = minimal;
if (minimal[longest] + overage >= minimal[second_longest])
widths[longest] += overage;
if (minimal[longest] + overage >= minimal[second_longest]) widths[longest] += overage;
// Case 2: Shorten the longest column to second longest length. Try to
// split shortening them evenly.
else
{
else {
int decrease = minimal[second_longest] - minimal[longest];
widths[longest] += decrease;
overage = overage - decrease;
// Attempt to decrease the two longest columns (at most to two characters)
if (-overage <= widths[longest] + widths[second_longest] - 4)
{
if (-overage <= widths[longest] + widths[second_longest] - 4) {
// Compute half of the overage, rounding up
int half_overage = overage / 2 + overage % 2;
// Decrease both larges columns by this amount
widths[longest] += half_overage;
widths[second_longest] += half_overage;
}
else
} else
// If reducing two of the longest solumns to 2 characters is not sufficient, then give up.
Context::getContext ().error (format ("The report has a minimum width of {1} and does not fit in the available width of {2}.", sum_minimal + all_extra, _width));
Context::getContext().error(format(
"The report has a minimum width of {1} and does not fit in the available width of {2}.",
sum_minimal + all_extra, _width));
}
}
// Perfect minimal width.
else if (overage == 0)
{
else if (overage == 0) {
widths = minimal;
}
// Extra space to share.
else if (overage > 0)
{
else if (overage > 0) {
widths = minimal;
// Spread 'overage' among columns where width[i] < ideal[i]
bool needed = true;
while (overage && needed)
{
while (overage && needed) {
needed = false;
for (unsigned int i = 0; i < _columns.size () && overage; ++i)
{
if (widths[i] < ideal[i])
{
for (unsigned int i = 0; i < _columns.size() && overage; ++i) {
if (widths[i] < ideal[i]) {
++widths[i];
--overage;
needed = true;
@@ -283,39 +253,34 @@ std::string ViewTask::render (std::vector <Task>& data, std::vector <int>& seque
// Compose column headers.
unsigned int max_lines = 0;
std::vector <std::vector <std::string>> headers;
for (unsigned int c = 0; c < _columns.size (); ++c)
{
headers.emplace_back ();
_columns[c]->renderHeader (headers[c], widths[c], _sort[c] ? _sort_header : _header);
std::vector<std::vector<std::string>> headers;
for (unsigned int c = 0; c < _columns.size(); ++c) {
headers.emplace_back();
_columns[c]->renderHeader(headers[c], widths[c], _sort[c] ? _sort_header : _header);
if (headers[c].size () > max_lines)
max_lines = headers[c].size ();
if (headers[c].size() > max_lines) max_lines = headers[c].size();
}
// Render column headers.
std::string left_margin = std::string (_left_margin, ' ');
std::string extra = std::string (_extra_padding, ' ');
std::string intra = std::string (_intra_padding, ' ');
std::string left_margin = std::string(_left_margin, ' ');
std::string extra = std::string(_extra_padding, ' ');
std::string intra = std::string(_intra_padding, ' ');
std::string extra_odd = Context::getContext ().color () ? _extra_odd.colorize (extra) : extra;
std::string extra_even = Context::getContext ().color () ? _extra_even.colorize (extra) : extra;
std::string intra_odd = Context::getContext ().color () ? _intra_odd.colorize (intra) : intra;
std::string intra_even = Context::getContext ().color () ? _intra_even.colorize (intra) : intra;
std::string extra_odd = Context::getContext().color() ? _extra_odd.colorize(extra) : extra;
std::string extra_even = Context::getContext().color() ? _extra_even.colorize(extra) : extra;
std::string intra_odd = Context::getContext().color() ? _intra_odd.colorize(intra) : intra;
std::string intra_even = Context::getContext().color() ? _intra_even.colorize(intra) : intra;
std::string out;
_lines = 0;
for (unsigned int i = 0; i < max_lines; ++i)
{
for (unsigned int i = 0; i < max_lines; ++i) {
out += left_margin + extra;
for (unsigned int c = 0; c < _columns.size (); ++c)
{
if (c)
out += intra;
for (unsigned int c = 0; c < _columns.size(); ++c) {
if (c) out += intra;
if (headers[c].size () < max_lines - i)
out += _header.colorize (std::string (widths[c], ' '));
if (headers[c].size() < max_lines - i)
out += _header.colorize(std::string(widths[c], ' '));
else
out += headers[c][i];
}
@@ -323,60 +288,50 @@ std::string ViewTask::render (std::vector <Task>& data, std::vector <int>& seque
out += extra;
// Trim right.
out.erase (out.find_last_not_of (' ') + 1);
out.erase(out.find_last_not_of(' ') + 1);
out += "\n";
// Stop if the line limit is exceeded.
if (++_lines >= _truncate_lines && _truncate_lines != 0)
{
Context::getContext ().time_render_us += timer.total_us ();
if (++_lines >= _truncate_lines && _truncate_lines != 0) {
Context::getContext().time_render_us += timer.total_us();
return out;
}
}
// Compose, render columns, in sequence.
_rows = 0;
std::vector <std::vector <std::string>> cells;
for (unsigned int s = 0; s < sequence.size (); ++s)
{
std::vector<std::vector<std::string>> cells;
for (unsigned int s = 0; s < sequence.size(); ++s) {
max_lines = 0;
// Apply color rules to task.
Color rule_color;
autoColorize (data[sequence[s]], rule_color);
autoColorize(data[sequence[s]], rule_color);
// Alternate rows based on |s % 2|
bool odd = (s % 2) ? true : false;
Color row_color;
if (Context::getContext ().color ())
{
if (Context::getContext().color()) {
row_color = odd ? _odd : _even;
row_color.blend (rule_color);
row_color.blend(rule_color);
}
for (unsigned int c = 0; c < _columns.size (); ++c)
{
cells.emplace_back ();
_columns[c]->render (cells[c], data[sequence[s]], widths[c], row_color);
for (unsigned int c = 0; c < _columns.size(); ++c) {
cells.emplace_back();
_columns[c]->render(cells[c], data[sequence[s]], widths[c], row_color);
if (cells[c].size () > max_lines)
max_lines = cells[c].size ();
if (cells[c].size() > max_lines) max_lines = cells[c].size();
if (obfuscate)
if (_columns[c]->type () == "string")
for (auto& line : cells[c])
line = obfuscateText (line);
if (_columns[c]->type() == "string")
for (auto& line : cells[c]) line = obfuscateText(line);
}
// Listing breaks are simply blank lines inserted when a column value
// changes.
if (s > 0 &&
_breaks.size () > 0)
{
for (const auto& b : _breaks)
{
if (data[sequence[s - 1]].get (b) != data[sequence[s]].get (b))
{
if (s > 0 && _breaks.size() > 0) {
for (const auto& b : _breaks) {
if (data[sequence[s - 1]].get(b) != data[sequence[s]].get(b)) {
out += "\n";
++_lines;
@@ -386,51 +341,46 @@ std::string ViewTask::render (std::vector <Task>& data, std::vector <int>& seque
}
}
for (unsigned int i = 0; i < max_lines; ++i)
{
for (unsigned int i = 0; i < max_lines; ++i) {
out += left_margin + (odd ? extra_odd : extra_even);
for (unsigned int c = 0; c < _columns.size (); ++c)
{
if (c)
{
if (row_color.nontrivial ())
row_color._colorize (out, intra);
for (unsigned int c = 0; c < _columns.size(); ++c) {
if (c) {
if (row_color.nontrivial())
row_color._colorize(out, intra);
else
out += (odd ? intra_odd : intra_even);
}
if (i < cells[c].size ())
if (i < cells[c].size())
out += cells[c][i];
else
row_color._colorize (out, std::string (widths[c], ' '));
row_color._colorize(out, std::string(widths[c], ' '));
}
out += (odd ? extra_odd : extra_even);
// Trim right.
out.erase (out.find_last_not_of (' ') + 1);
out.erase(out.find_last_not_of(' ') + 1);
out += "\n";
// Stop if the line limit is exceeded.
if (++_lines >= _truncate_lines && _truncate_lines != 0)
{
Context::getContext ().time_render_us += timer.total_us ();
if (++_lines >= _truncate_lines && _truncate_lines != 0) {
Context::getContext().time_render_us += timer.total_us();
return out;
}
}
cells.clear ();
cells.clear();
// Stop if the row limit is exceeded.
if (++_rows >= _truncate_rows && _truncate_rows != 0)
{
Context::getContext ().time_render_us += timer.total_us ();
if (++_rows >= _truncate_rows && _truncate_rows != 0) {
Context::getContext().time_render_us += timer.total_us();
return out;
}
}
Context::getContext ().time_render_us += timer.total_us ();
Context::getContext().time_render_us += timer.total_us();
return out;
}

View File

@@ -27,63 +27,68 @@
#ifndef INCLUDED_VIEWTASK
#define INCLUDED_VIEWTASK
#include <string>
#include <vector>
#include <Task.h>
#include <Color.h>
#include <Column.h>
#include <Task.h>
class ViewTask
{
public:
ViewTask ();
~ViewTask ();
#include <string>
#include <vector>
class ViewTask {
public:
ViewTask();
~ViewTask();
// View specifications.
void add (Column* column, bool sort = false) { _columns.push_back (column); _sort.push_back (sort); }
void width (int width) { _width = width; }
void leftMargin (int margin) { _left_margin = margin; }
void colorHeader (Color& c) { _header = c; if (!_sort_header) _sort_header = c; }
void colorSortHeader (Color& c) { _sort_header = c; }
void colorOdd (Color& c) { _odd = c; }
void colorEven (Color& c) { _even = c; }
void intraPadding (int padding) { _intra_padding = padding; }
void intraColorOdd (Color& c) { _intra_odd = c; }
void intraColorEven (Color& c) { _intra_even = c; }
void extraPadding (int padding) { _extra_padding = padding; }
void extraColorOdd (Color& c) { _extra_odd = c; }
void extraColorEven (Color& c) { _extra_even = c; }
void truncateLines (int n) { _truncate_lines = n; }
void truncateRows (int n) { _truncate_rows = n; }
void addBreak (const std::string& attr) { _breaks.push_back (attr); }
int lines () { return _lines; }
int rows () { return _rows; }
void add(Column* column, bool sort = false) {
_columns.push_back(column);
_sort.push_back(sort);
}
void width(int width) { _width = width; }
void leftMargin(int margin) { _left_margin = margin; }
void colorHeader(Color& c) {
_header = c;
if (!_sort_header) _sort_header = c;
}
void colorSortHeader(Color& c) { _sort_header = c; }
void colorOdd(Color& c) { _odd = c; }
void colorEven(Color& c) { _even = c; }
void intraPadding(int padding) { _intra_padding = padding; }
void intraColorOdd(Color& c) { _intra_odd = c; }
void intraColorEven(Color& c) { _intra_even = c; }
void extraPadding(int padding) { _extra_padding = padding; }
void extraColorOdd(Color& c) { _extra_odd = c; }
void extraColorEven(Color& c) { _extra_even = c; }
void truncateLines(int n) { _truncate_lines = n; }
void truncateRows(int n) { _truncate_rows = n; }
void addBreak(const std::string& attr) { _breaks.push_back(attr); }
int lines() { return _lines; }
int rows() { return _rows; }
// View rendering.
std::string render (std::vector <Task>&, std::vector <int>&);
std::string render(std::vector<Task>&, std::vector<int>&);
private:
std::vector <Column*> _columns;
std::vector <bool> _sort;
std::vector <std::string> _breaks;
int _width;
int _left_margin;
Color _header;
Color _sort_header;
Color _odd;
Color _even;
int _intra_padding;
Color _intra_odd;
Color _intra_even;
int _extra_padding;
Color _extra_odd;
Color _extra_even;
int _truncate_lines;
int _truncate_rows;
int _lines;
int _rows;
private:
std::vector<Column*> _columns;
std::vector<bool> _sort;
std::vector<std::string> _breaks;
int _width;
int _left_margin;
Color _header;
Color _sort_header;
Color _odd;
Color _even;
int _intra_padding;
Color _intra_odd;
Color _intra_even;
int _extra_padding;
Color _extra_odd;
Color _extra_even;
int _truncate_lines;
int _truncate_rows;
int _lines;
int _rows;
};
#endif
////////////////////////////////////////////////////////////////////////////////

View File

@@ -27,57 +27,53 @@
#include <cmake.h>
// cmake.h include header must come first
#include <iostream>
#include <string>
#include <stdlib.h>
#include <string.h>
#include <Eval.h>
#include <Context.h>
#include <Task.h>
#include <Datetime.h>
#include <Duration.h>
#include <shared.h>
#include <Eval.h>
#include <Task.h>
#include <format.h>
#include <shared.h>
#include <stdlib.h>
#include <string.h>
#include <iostream>
#include <string>
////////////////////////////////////////////////////////////////////////////////
// Constants.
bool get (const std::string&, Variant&)
{
/*
// An example, although a bad one because this is supported by default.
if (name == "pi") {value = Variant (3.14159165); return true;}
*/
bool get(const std::string&, Variant&) {
/*
// An example, although a bad one because this is supported by default.
if (name == "pi") {value = Variant (3.14159165); return true;}
*/
return false;
}
////////////////////////////////////////////////////////////////////////////////
int main (int argc, char** argv)
{
int main(int argc, char** argv) {
int status = 0;
try
{
try {
Context globalContext;
Context::setContext (&globalContext);
Context::setContext(&globalContext);
// Same operating parameters as Context::staticInitialization.
Datetime::standaloneDateEnabled = false;
Datetime::standaloneTimeEnabled = false;
Datetime::standaloneDateEnabled = false;
Datetime::standaloneTimeEnabled = false;
Duration::standaloneSecondsEnabled = false;
bool infix {true};
bool infix{true};
// Add a source for constants.
Eval e;
e.addSource (get);
e.addSource(get);
// Combine all the arguments into one expression string.
std::string expression;
for (int i = 1; i < argc; i++)
{
if (!strcmp (argv[i], "-h") || ! strcmp (argv[i], "--help"))
{
for (int i = 1; i < argc; i++) {
if (!strcmp(argv[i], "-h") || !strcmp(argv[i], "--help")) {
std::cout << '\n'
<< "Usage: " << argv[0] << " [options] '<expression>'\n"
<< '\n'
@@ -87,56 +83,47 @@ int main (int argc, char** argv)
<< " -i|--infix Infix expression (default)\n"
<< " -p|--postfix Postfix expression\n"
<< '\n';
exit (1);
}
else if (!strcmp (argv[i], "-v") || !strcmp (argv[i], "--version"))
{
exit(1);
} else if (!strcmp(argv[i], "-v") || !strcmp(argv[i], "--version")) {
std::cout << '\n'
<< format ("calc {1} built for ", VERSION)
<< osName ()
<< format("calc {1} built for ", VERSION) << osName() << '\n'
<< "Copyright (C) 2006 - 2021 T. Babej, P. Beckingham, F. Hernandez." << '\n'
<< '\n'
<< "Copyright (C) 2006 - 2021 T. Babej, P. Beckingham, F. Hernandez."
<< '\n'
<< '\n'
<< "Taskwarrior may be copied only under the terms of the MIT license, which may be found in the Taskwarrior source kit."
<< "Taskwarrior may be copied only under the terms of the MIT license, which may "
"be found in the Taskwarrior source kit."
<< '\n'
<< '\n';
exit (1);
}
else if (!strcmp (argv[i], "-d") || !strcmp (argv[i], "--debug"))
e.debug (true);
else if (!strcmp (argv[i], "-i") || !strcmp (argv[i], "--infix"))
exit(1);
} else if (!strcmp(argv[i], "-d") || !strcmp(argv[i], "--debug"))
e.debug(true);
else if (!strcmp(argv[i], "-i") || !strcmp(argv[i], "--infix"))
infix = true;
else if (!strcmp (argv[i], "-p") || !strcmp (argv[i], "--postfix"))
else if (!strcmp(argv[i], "-p") || !strcmp(argv[i], "--postfix"))
infix = false;
else
expression += std::string (argv[i]) + ' ';
expression += std::string(argv[i]) + ' ';
}
Variant result;
if (infix)
e.evaluateInfixExpression (expression, result);
e.evaluateInfixExpression(expression, result);
else
e.evaluatePostfixExpression (expression, result);
e.evaluatePostfixExpression(expression, result);
// Show any debug output.
for (const auto& i : Context::getContext ().debugMessages)
std::cout << i << '\n';
for (const auto& i : Context::getContext().debugMessages) std::cout << i << '\n';
// Show the result in string form.
std::cout << (std::string) result
<< '\n';
std::cout << (std::string)result << '\n';
}
catch (const std::string& error)
{
catch (const std::string& error) {
std::cerr << error << '\n';
status = -1;
}
catch (...)
{
catch (...) {
std::cerr << "Unknown error occured. Oops.\n";
status = -2;
}

View File

@@ -28,30 +28,26 @@
// cmake.h include header must come first
#include <ColDepends.h>
#include <algorithm>
#include <Context.h>
#include <shared.h>
#include <format.h>
#include <utf8.h>
#include <main.h>
#include <util.h>
#include <shared.h>
#include <stdlib.h>
#include <utf8.h>
#include <util.h>
#include <algorithm>
#include <regex>
#define STRING_COLUMN_LABEL_DEP "Depends"
////////////////////////////////////////////////////////////////////////////////
ColumnDepends::ColumnDepends ()
{
_name = "depends";
_style = "list";
_label = STRING_COLUMN_LABEL_DEP;
_styles = {"list",
"count",
"indicator"};
_examples = {"1 2 10",
"[3]",
Context::getContext ().config.get ("dependency.indicator")};
ColumnDepends::ColumnDepends() {
_name = "depends";
_style = "list";
_label = STRING_COLUMN_LABEL_DEP;
_styles = {"list", "count", "indicator"};
_examples = {"1 2 10", "[3]", Context::getContext().config.get("dependency.indicator")};
_hyphenate = false;
}
@@ -59,156 +55,131 @@ ColumnDepends::ColumnDepends ()
////////////////////////////////////////////////////////////////////////////////
// Overriden so that style <----> label are linked.
// Note that you can not determine which gets called first.
void ColumnDepends::setStyle (const std::string& value)
{
Column::setStyle (value);
void ColumnDepends::setStyle(const std::string& value) {
Column::setStyle(value);
if (_style == "indicator" && _label == STRING_COLUMN_LABEL_DEP) _label = _label.substr (0, Context::getContext ().config.get ("dependency.indicator").length ());
else if (_style == "count" && _label == STRING_COLUMN_LABEL_DEP) _label = "Dep";
if (_style == "indicator" && _label == STRING_COLUMN_LABEL_DEP)
_label = _label.substr(0, Context::getContext().config.get("dependency.indicator").length());
else if (_style == "count" && _label == STRING_COLUMN_LABEL_DEP)
_label = "Dep";
}
////////////////////////////////////////////////////////////////////////////////
// Set the minimum and maximum widths for the value.
void ColumnDepends::measure (Task& task, unsigned int& minimum, unsigned int& maximum)
{
void ColumnDepends::measure(Task& task, unsigned int& minimum, unsigned int& maximum) {
minimum = maximum = 0;
auto deptasks = task.getDependencyTasks ();
auto deptasks = task.getDependencyTasks();
if (deptasks.size () > 0)
{
if (_style == "indicator")
{
minimum = maximum = utf8_width (Context::getContext ().config.get ("dependency.indicator"));
if (deptasks.size() > 0) {
if (_style == "indicator") {
minimum = maximum = utf8_width(Context::getContext().config.get("dependency.indicator"));
}
else if (_style == "count")
{
minimum = maximum = 2 + format ((int) deptasks.size ()).length ();
else if (_style == "count") {
minimum = maximum = 2 + format((int)deptasks.size()).length();
}
else if (_style == "default" ||
_style == "list")
{
else if (_style == "default" || _style == "list") {
minimum = maximum = 0;
std::vector <int> blocking_ids;
std::vector<int> blocking_ids;
blocking_ids.reserve(deptasks.size());
for (auto& i : deptasks)
blocking_ids.push_back (i.id);
for (auto& i : deptasks) blocking_ids.push_back(i.id);
auto all = join (" ", blocking_ids);
maximum = all.length ();
auto all = join(" ", blocking_ids);
maximum = all.length();
unsigned int length;
for (auto& i : deptasks)
{
length = format (i.id).length ();
if (length > minimum)
minimum = length;
for (auto& i : deptasks) {
length = format(i.id).length();
if (length > minimum) minimum = length;
}
}
}
}
////////////////////////////////////////////////////////////////////////////////
void ColumnDepends::render (
std::vector <std::string>& lines,
Task& task,
int width,
Color& color)
{
auto deptasks = task.getDependencyTasks ();
void ColumnDepends::render(std::vector<std::string>& lines, Task& task, int width, Color& color) {
auto deptasks = task.getDependencyTasks();
if (deptasks.size () > 0)
{
if (_style == "indicator")
{
renderStringRight (lines, width, color, Context::getContext ().config.get ("dependency.indicator"));
if (deptasks.size() > 0) {
if (_style == "indicator") {
renderStringRight(lines, width, color,
Context::getContext().config.get("dependency.indicator"));
}
else if (_style == "count")
{
renderStringRight (lines, width, color, '[' + format (static_cast <int>(deptasks.size ())) + ']');
else if (_style == "count") {
renderStringRight(lines, width, color, '[' + format(static_cast<int>(deptasks.size())) + ']');
}
else if (_style == "default" ||
_style == "list")
{
std::vector <int> blocking_ids;
else if (_style == "default" || _style == "list") {
std::vector<int> blocking_ids;
blocking_ids.reserve(deptasks.size());
for (const auto& t : deptasks)
blocking_ids.push_back (t.id);
for (const auto& t : deptasks) blocking_ids.push_back(t.id);
auto combined = join (" ", blocking_ids);
auto combined = join(" ", blocking_ids);
std::vector <std::string> all;
wrapText (all, combined, width, _hyphenate);
std::vector<std::string> all;
wrapText(all, combined, width, _hyphenate);
for (const auto& i : all)
renderStringLeft (lines, width, color, i);
for (const auto& i : all) renderStringLeft(lines, width, color, i);
}
}
}
////////////////////////////////////////////////////////////////////////////////
void ColumnDepends::modify (Task& task, const std::string& value)
{
void ColumnDepends::modify(Task& task, const std::string& value) {
// Apply or remove dendencies in turn.
for (auto& dep : split (value, ','))
{
for (auto& dep : split(value, ',')) {
bool removal = false;
if (dep[0] == '-')
{
if (dep[0] == '-') {
removal = true;
dep = dep.substr(1);
}
auto hyphen = dep.find ('-');
long lower, upper; // For ID ranges
std::regex valid_uuid ("[a-f0-9]{8}([a-f0-9-]{4,28})?"); // TODO: Make more precise
auto hyphen = dep.find('-');
long lower, upper; // For ID ranges
std::regex valid_uuid("[a-f0-9]{8}([a-f0-9-]{4,28})?"); // TODO: Make more precise
// UUID
if (dep.length () >= 8 && std::regex_match (dep, valid_uuid))
{
// Full UUID, can be added directly
if (dep.length () == 36)
if (removal)
task.removeDependency (dep);
else
task.addDependency (dep);
if (dep.length() >= 8 && std::regex_match(dep, valid_uuid)) {
// Full UUID, can be added directly
if (dep.length() == 36)
if (removal)
task.removeDependency(dep);
else
task.addDependency(dep);
// Short UUID, need to look up full form
else
{
Task loaded_task;
if (Context::getContext ().tdb2.get (dep, loaded_task))
if (removal)
task.removeDependency (loaded_task.get ("uuid"));
else
task.addDependency (loaded_task.get ("uuid"));
else
throw format ("Dependency could not be set - task with UUID '{1}' does not exist.", dep);
}
// Short UUID, need to look up full form
else {
Task loaded_task;
if (Context::getContext().tdb2.get(dep, loaded_task))
if (removal)
task.removeDependency(loaded_task.get("uuid"));
else
task.addDependency(loaded_task.get("uuid"));
else
throw format("Dependency could not be set - task with UUID '{1}' does not exist.", dep);
}
}
// ID range
else if (dep.find ('-') != std::string::npos &&
extractLongInteger (dep.substr (0, hyphen), lower) &&
extractLongInteger (dep.substr (hyphen + 1), upper))
{
else if (dep.find('-') != std::string::npos &&
extractLongInteger(dep.substr(0, hyphen), lower) &&
extractLongInteger(dep.substr(hyphen + 1), upper)) {
for (long i = lower; i <= upper; i++)
if (removal)
task.removeDependency (i);
else
task.addDependency (i);
if (removal)
task.removeDependency(i);
else
task.addDependency(i);
}
// Simple ID
else if (extractLongInteger (dep, lower))
else if (extractLongInteger(dep, lower))
if (removal)
task.removeDependency (lower);
task.removeDependency(lower);
else
task.addDependency (lower);
task.addDependency(lower);
else
throw format ("Invalid dependency value: '{1}'", dep);
throw format("Invalid dependency value: '{1}'", dep);
}
}

View File

@@ -29,17 +29,16 @@
#include <ColTypeString.h>
class ColumnDepends : public ColumnTypeString
{
public:
ColumnDepends ();
class ColumnDepends : public ColumnTypeString {
public:
ColumnDepends();
void setStyle (const std::string&);
void measure (Task&, unsigned int&, unsigned int&);
void render (std::vector <std::string>&, Task&, int, Color&);
void modify (Task&, const std::string&);
void setStyle(const std::string&);
void measure(Task&, unsigned int&, unsigned int&);
void render(std::vector<std::string>&, Task&, int, Color&);
void modify(Task&, const std::string&);
private:
private:
bool _hyphenate;
};

View File

@@ -28,233 +28,188 @@
// cmake.h include header must come first
#include <ColDescription.h>
#include <stdlib.h>
#include <Context.h>
#include <Datetime.h>
#include <shared.h>
#include <format.h>
#include <shared.h>
#include <stdlib.h>
#include <utf8.h>
#include <util.h>
////////////////////////////////////////////////////////////////////////////////
ColumnDescription::ColumnDescription ()
{
_name = "description";
_style = "combined";
_label = "Description";
ColumnDescription::ColumnDescription() {
_name = "description";
_style = "combined";
_label = "Description";
_modifiable = true;
_styles = {"combined",
"desc",
"oneline",
"truncated",
"count",
"truncated_count"};
_styles = {"combined", "desc", "oneline", "truncated", "count", "truncated_count"};
_dateformat = Context::getContext ().config.get ("dateformat.annotation");
if (_dateformat == "")
_dateformat = Context::getContext ().config.get ("dateformat");
_dateformat = Context::getContext().config.get("dateformat.annotation");
if (_dateformat == "") _dateformat = Context::getContext().config.get("dateformat");
std::string t = Datetime ().toString (_dateformat);
std::string d = "Move your clothes down on to the lower peg";
std::string t = Datetime().toString(_dateformat);
std::string d = "Move your clothes down on to the lower peg";
std::string a1 = "Immediately before your lunch";
std::string a2 = "If you are playing in the match this afternoon";
std::string a3 = "Before you write your letter home";
std::string a4 = "If you're not getting your hair cut";
_examples = {d + "\n " + t + ' ' + a1
+ "\n " + t + ' ' + a2
+ "\n " + t + ' ' + a3
+ "\n " + t + ' ' + a4,
d,
d + ' ' + t + ' ' + a1
+ ' ' + t + ' ' + a2
+ ' ' + t + ' ' + a3
+ ' ' + t + ' ' + a4,
d.substr (0, 20) + "...",
d + " [4]",
d.substr (0, 20) + "... [4]"};
_examples = {
d + "\n " + t + ' ' + a1 + "\n " + t + ' ' + a2 + "\n " + t + ' ' + a3 + "\n " + t + ' ' +
a4,
d,
d + ' ' + t + ' ' + a1 + ' ' + t + ' ' + a2 + ' ' + t + ' ' + a3 + ' ' + t + ' ' + a4,
d.substr(0, 20) + "...",
d + " [4]",
d.substr(0, 20) + "... [4]"};
_hyphenate = Context::getContext ().config.getBoolean ("hyphenate");
_hyphenate = Context::getContext().config.getBoolean("hyphenate");
_indent = Context::getContext ().config.getInteger ("indent.annotation");
_indent = Context::getContext().config.getInteger("indent.annotation");
}
////////////////////////////////////////////////////////////////////////////////
// Set the minimum and maximum widths for the value.
void ColumnDescription::measure (Task& task, unsigned int& minimum, unsigned int& maximum)
{
std::string description = task.get (_name);
void ColumnDescription::measure(Task& task, unsigned int& minimum, unsigned int& maximum) {
std::string description = task.get(_name);
// The text
// <indent> <date> <anno>
// ...
if (_style == "default" ||
_style == "combined")
{
minimum = longestWord (description);
maximum = utf8_width (description);
if (_style == "default" || _style == "combined") {
minimum = longestWord(description);
maximum = utf8_width(description);
if (task.annotation_count)
{
unsigned int min_anno = _indent + Datetime::length (_dateformat);
if (min_anno > minimum)
minimum = min_anno;
if (task.annotation_count) {
unsigned int min_anno = _indent + Datetime::length(_dateformat);
if (min_anno > minimum) minimum = min_anno;
for (auto& i : task.getAnnotations ())
{
unsigned int len = min_anno + 1 + utf8_width (i.second);
if (len > maximum)
maximum = len;
for (auto& i : task.getAnnotations()) {
unsigned int len = min_anno + 1 + utf8_width(i.second);
if (len > maximum) maximum = len;
}
}
}
// Just the text
else if (_style == "desc")
{
maximum = utf8_width (description);
minimum = longestWord (description);
else if (_style == "desc") {
maximum = utf8_width(description);
minimum = longestWord(description);
}
// The text <date> <anno> ...
else if (_style == "oneline")
{
minimum = longestWord (description);
maximum = utf8_width (description);
else if (_style == "oneline") {
minimum = longestWord(description);
maximum = utf8_width(description);
if (task.annotation_count)
{
auto min_anno = Datetime::length (_dateformat);
for (auto& i : task.getAnnotations ())
maximum += min_anno + 1 + utf8_width (i.second);
if (task.annotation_count) {
auto min_anno = Datetime::length(_dateformat);
for (auto& i : task.getAnnotations()) maximum += min_anno + 1 + utf8_width(i.second);
}
}
// The te...
else if (_style == "truncated")
{
else if (_style == "truncated") {
minimum = 4;
maximum = utf8_width (description);
maximum = utf8_width(description);
}
// The text [2]
else if (_style == "count")
{
else if (_style == "count") {
// <description> + ' ' + '[' + <count> + ']'
maximum = utf8_width (description) + 1 + 1 + format (task.annotation_count).length () + 1;
minimum = longestWord (description);
maximum = utf8_width(description) + 1 + 1 + format(task.annotation_count).length() + 1;
minimum = longestWord(description);
}
// The te... [2]
else if (_style == "truncated_count")
{
else if (_style == "truncated_count") {
minimum = 4;
maximum = utf8_width (description) + 1 + 1 + format (task.annotation_count).length () + 1;
maximum = utf8_width(description) + 1 + 1 + format(task.annotation_count).length() + 1;
}
}
////////////////////////////////////////////////////////////////////////////////
void ColumnDescription::render (
std::vector <std::string>& lines,
Task& task,
int width,
Color& color)
{
std::string description = task.get (_name);
void ColumnDescription::render(std::vector<std::string>& lines, Task& task, int width,
Color& color) {
std::string description = task.get(_name);
// This is a description
// <date> <anno>
// ...
if (_style == "default" ||
_style == "combined")
{
if (task.annotation_count)
{
for (const auto& i : task.getAnnotations ())
{
Datetime dt (strtoll (i.first.substr (11).c_str (), nullptr, 10));
description += '\n' + std::string (_indent, ' ') + dt.toString (_dateformat) + ' ' + i.second;
if (_style == "default" || _style == "combined") {
if (task.annotation_count) {
for (const auto& i : task.getAnnotations()) {
Datetime dt(strtoll(i.first.substr(11).c_str(), nullptr, 10));
description += '\n' + std::string(_indent, ' ') + dt.toString(_dateformat) + ' ' + i.second;
}
}
std::vector <std::string> raw;
wrapText (raw, description, width, _hyphenate);
std::vector<std::string> raw;
wrapText(raw, description, width, _hyphenate);
for (const auto& i : raw)
renderStringLeft (lines, width, color, i);
for (const auto& i : raw) renderStringLeft(lines, width, color, i);
}
// This is a description
else if (_style == "desc")
{
std::vector <std::string> raw;
wrapText (raw, description, width, _hyphenate);
else if (_style == "desc") {
std::vector<std::string> raw;
wrapText(raw, description, width, _hyphenate);
for (const auto& i : raw)
renderStringLeft (lines, width, color, i);
for (const auto& i : raw) renderStringLeft(lines, width, color, i);
}
// This is a description <date> <anno> ...
else if (_style == "oneline")
{
if (task.annotation_count)
{
for (const auto& i : task.getAnnotations ())
{
Datetime dt (strtoll (i.first.substr (11).c_str (), nullptr, 10));
description += ' ' + dt.toString (_dateformat) + ' ' + i.second;
else if (_style == "oneline") {
if (task.annotation_count) {
for (const auto& i : task.getAnnotations()) {
Datetime dt(strtoll(i.first.substr(11).c_str(), nullptr, 10));
description += ' ' + dt.toString(_dateformat) + ' ' + i.second;
}
}
std::vector <std::string> raw;
wrapText (raw, description, width, _hyphenate);
std::vector<std::string> raw;
wrapText(raw, description, width, _hyphenate);
for (const auto& i : raw)
renderStringLeft (lines, width, color, i);
for (const auto& i : raw) renderStringLeft(lines, width, color, i);
}
// This is a des...
else if (_style == "truncated")
{
int len = utf8_width (description);
else if (_style == "truncated") {
int len = utf8_width(description);
if (len > width)
renderStringLeft (lines, width, color, description.substr (0, width - 3) + "...");
renderStringLeft(lines, width, color, description.substr(0, width - 3) + "...");
else
renderStringLeft (lines, width, color, description);
renderStringLeft(lines, width, color, description);
}
// This is a description [2]
else if (_style == "count")
{
if (task.annotation_count)
description += " [" + format (task.annotation_count) + ']';
else if (_style == "count") {
if (task.annotation_count) description += " [" + format(task.annotation_count) + ']';
std::vector <std::string> raw;
wrapText (raw, description, width, _hyphenate);
std::vector<std::string> raw;
wrapText(raw, description, width, _hyphenate);
for (const auto& i : raw)
renderStringLeft (lines, width, color, i);
for (const auto& i : raw) renderStringLeft(lines, width, color, i);
}
// This is a des... [2]
else if (_style == "truncated_count")
{
int len = utf8_width (description);
else if (_style == "truncated_count") {
int len = utf8_width(description);
std::string annos_count;
int len_annos = 0;
if (task.annotation_count)
{
annos_count = " [" + format (task.annotation_count) + ']';
len_annos = utf8_width (annos_count);
if (task.annotation_count) {
annos_count = " [" + format(task.annotation_count) + ']';
len_annos = utf8_width(annos_count);
len += len_annos;
}
if (len > width)
renderStringLeft (lines, width, color, description.substr (0, width - len_annos - 3) + "..." + annos_count);
renderStringLeft(lines, width, color,
description.substr(0, width - len_annos - 3) + "..." + annos_count);
else
renderStringLeft (lines, width, color, description + annos_count);
renderStringLeft(lines, width, color, description + annos_count);
}
}

View File

@@ -29,14 +29,13 @@
#include <ColTypeString.h>
class ColumnDescription : public ColumnTypeString
{
public:
ColumnDescription ();
void measure (Task&, unsigned int&, unsigned int&);
void render (std::vector <std::string>&, Task&, int, Color&);
class ColumnDescription : public ColumnTypeString {
public:
ColumnDescription();
void measure(Task&, unsigned int&, unsigned int&);
void render(std::vector<std::string>&, Task&, int, Color&);
private:
private:
bool _hyphenate;
std::string _dateformat;
int _indent;

View File

@@ -30,22 +30,19 @@
#include <ColDue.h>
////////////////////////////////////////////////////////////////////////////////
ColumnDue::ColumnDue ()
{
_name = "due";
ColumnDue::ColumnDue() {
_name = "due";
_modifiable = true;
_label = "Due";
_label = "Due";
}
////////////////////////////////////////////////////////////////////////////////
// Overriden so that style <----> label are linked.
// Note that you can not determine which gets called first.
void ColumnDue::setStyle (const std::string& value)
{
Column::setStyle (value);
void ColumnDue::setStyle(const std::string& value) {
Column::setStyle(value);
if (_style == "countdown" && _label == "Due")
_label = "Count";
if (_style == "countdown" && _label == "Due") _label = "Count";
}
////////////////////////////////////////////////////////////////////////////////

View File

@@ -29,11 +29,10 @@
#include <ColTypeDate.h>
class ColumnDue : public ColumnTypeDate
{
public:
ColumnDue ();
void setStyle (const std::string&);
class ColumnDue : public ColumnTypeDate {
public:
ColumnDue();
void setStyle(const std::string&);
};
#endif

View File

@@ -30,9 +30,8 @@
#include <ColEnd.h>
////////////////////////////////////////////////////////////////////////////////
ColumnEnd::ColumnEnd ()
{
_name = "end";
ColumnEnd::ColumnEnd() {
_name = "end";
_label = "Completed";
}

View File

@@ -29,10 +29,9 @@
#include <ColTypeDate.h>
class ColumnEnd : public ColumnTypeDate
{
public:
ColumnEnd ();
class ColumnEnd : public ColumnTypeDate {
public:
ColumnEnd();
};
#endif

View File

@@ -30,23 +30,19 @@
#include <ColEntry.h>
////////////////////////////////////////////////////////////////////////////////
ColumnEntry::ColumnEntry ()
{
_name = "entry";
ColumnEntry::ColumnEntry() {
_name = "entry";
_modifiable = true;
_label = "Added";
_label = "Added";
}
////////////////////////////////////////////////////////////////////////////////
// Overriden so that style <----> label are linked.
// Note that you can not determine which gets called first.
void ColumnEntry::setStyle (const std::string& value)
{
Column::setStyle (value);
void ColumnEntry::setStyle(const std::string& value) {
Column::setStyle(value);
if (_style == "age" &&
_label == "Added")
_label = "Age";
if (_style == "age" && _label == "Added") _label = "Age";
}
////////////////////////////////////////////////////////////////////////////////

View File

@@ -29,11 +29,10 @@
#include <ColTypeDate.h>
class ColumnEntry : public ColumnTypeDate
{
public:
ColumnEntry ();
void setStyle (const std::string&);
class ColumnEntry : public ColumnTypeDate {
public:
ColumnEntry();
void setStyle(const std::string&);
};
#endif

View File

@@ -28,48 +28,47 @@
// cmake.h include header must come first
#include <ColID.h>
#include <math.h>
#include <format.h>
#include <math.h>
////////////////////////////////////////////////////////////////////////////////
ColumnID::ColumnID ()
{
_name = "id";
_style = "number";
_label = "ID";
ColumnID::ColumnID() {
_name = "id";
_style = "number";
_label = "ID";
_modifiable = false;
_styles = {"number"};
_examples = {"123"};
_styles = {"number"};
_examples = {"123"};
}
////////////////////////////////////////////////////////////////////////////////
// Set the minimum and maximum widths for the value.
void ColumnID::measure (Task& task, unsigned int& minimum, unsigned int& maximum)
{
void ColumnID::measure(Task& task, unsigned int& minimum, unsigned int& maximum) {
int length;
if (task.id < 10) length = 1; // Fast
else if (task.id < 100) length = 2; // Fast
else if (task.id < 1000) length = 3; // Fast
else if (task.id < 10000) length = 4; // Fast
else if (task.id < 100000) length = 5; // Fast
else length = 1 + (int) log10 ((double) task.id); // Slow
if (task.id < 10)
length = 1; // Fast
else if (task.id < 100)
length = 2; // Fast
else if (task.id < 1000)
length = 3; // Fast
else if (task.id < 10000)
length = 4; // Fast
else if (task.id < 100000)
length = 5; // Fast
else
length = 1 + (int)log10((double)task.id); // Slow
minimum = maximum = length;
}
////////////////////////////////////////////////////////////////////////////////
void ColumnID::render (
std::vector <std::string>& lines,
Task& task,
int width,
Color& color)
{
// Completed and deleted tasks have no ID.
void ColumnID::render(std::vector<std::string>& lines, Task& task, int width, Color& color) {
// Completed and deleted tasks have no ID.
if (task.id)
renderInteger (lines, width, color, task.id);
renderInteger(lines, width, color, task.id);
else
renderStringRight (lines, width, color, "-");
renderStringRight(lines, width, color, "-");
}
////////////////////////////////////////////////////////////////////////////////

View File

@@ -29,14 +29,13 @@
#include <ColTypeNumeric.h>
class ColumnID : public ColumnTypeNumeric
{
public:
ColumnID ();
void measure (Task&, unsigned int&, unsigned int&);
void render (std::vector <std::string>&, Task&, int, Color&);
class ColumnID : public ColumnTypeNumeric {
public:
ColumnID();
void measure(Task&, unsigned int&, unsigned int&);
void render(std::vector<std::string>&, Task&, int, Color&);
private:
private:
};
#endif

View File

@@ -31,34 +31,25 @@
#include <format.h>
////////////////////////////////////////////////////////////////////////////////
ColumnIMask::ColumnIMask ()
{
_name = "imask";
_style = "number";
_label = "Mask Index";
ColumnIMask::ColumnIMask() {
_name = "imask";
_style = "number";
_label = "Mask Index";
_modifiable = false;
_styles = {"number"};
_examples = {"12"};
_styles = {"number"};
_examples = {"12"};
}
////////////////////////////////////////////////////////////////////////////////
// Set the minimum and maximum widths for the value.
void ColumnIMask::measure (Task& task, unsigned int& minimum, unsigned int& maximum)
{
void ColumnIMask::measure(Task& task, unsigned int& minimum, unsigned int& maximum) {
minimum = maximum = 0;
if (task.has (_name))
minimum = maximum = task.get (_name).length ();
if (task.has(_name)) minimum = maximum = task.get(_name).length();
}
////////////////////////////////////////////////////////////////////////////////
void ColumnIMask::render (
std::vector <std::string>& lines,
Task& task,
int width,
Color& color)
{
if (task.has (_name))
renderStringRight (lines, width, color, task.get (_name));
void ColumnIMask::render(std::vector<std::string>& lines, Task& task, int width, Color& color) {
if (task.has(_name)) renderStringRight(lines, width, color, task.get(_name));
}
////////////////////////////////////////////////////////////////////////////////

View File

@@ -29,14 +29,13 @@
#include <ColTypeNumeric.h>
class ColumnIMask : public ColumnTypeNumeric
{
public:
ColumnIMask ();
void measure (Task&, unsigned int&, unsigned int&);
void render (std::vector <std::string>&, Task&, int, Color&);
class ColumnIMask : public ColumnTypeNumeric {
public:
ColumnIMask();
void measure(Task&, unsigned int&, unsigned int&);
void render(std::vector<std::string>&, Task&, int, Color&);
private:
private:
};
#endif

View File

@@ -31,34 +31,25 @@
#include <format.h>
////////////////////////////////////////////////////////////////////////////////
ColumnLast::ColumnLast ()
{
_name = "last";
_style = "number";
_label = "Last instance";
ColumnLast::ColumnLast() {
_name = "last";
_style = "number";
_label = "Last instance";
_modifiable = false;
_styles = {"number"};
_examples = {"12"};
_styles = {"number"};
_examples = {"12"};
}
////////////////////////////////////////////////////////////////////////////////
// Set the minimum and maximum widths for the value.
void ColumnLast::measure (Task& task, unsigned int& minimum, unsigned int& maximum)
{
void ColumnLast::measure(Task& task, unsigned int& minimum, unsigned int& maximum) {
minimum = maximum = 0;
if (task.has (_name))
minimum = maximum = task.get (_name).length ();
if (task.has(_name)) minimum = maximum = task.get(_name).length();
}
////////////////////////////////////////////////////////////////////////////////
void ColumnLast::render (
std::vector <std::string>& lines,
Task& task,
int width,
Color& color)
{
if (task.has (_name))
renderStringRight (lines, width, color, task.get (_name));
void ColumnLast::render(std::vector<std::string>& lines, Task& task, int width, Color& color) {
if (task.has(_name)) renderStringRight(lines, width, color, task.get(_name));
}
////////////////////////////////////////////////////////////////////////////////

View File

@@ -29,14 +29,13 @@
#include <ColTypeNumeric.h>
class ColumnLast : public ColumnTypeNumeric
{
public:
ColumnLast ();
void measure (Task&, unsigned int&, unsigned int&);
void render (std::vector <std::string>&, Task&, int, Color&);
class ColumnLast : public ColumnTypeNumeric {
public:
ColumnLast();
void measure(Task&, unsigned int&, unsigned int&);
void render(std::vector<std::string>&, Task&, int, Color&);
private:
private:
};
#endif

View File

@@ -31,34 +31,25 @@
#include <format.h>
////////////////////////////////////////////////////////////////////////////////
ColumnMask::ColumnMask ()
{
_name = "mask";
_style = "default";
_label = "Mask";
ColumnMask::ColumnMask() {
_name = "mask";
_style = "default";
_label = "Mask";
_modifiable = false;
_styles = {"default"};
_examples = {"++++---"};
_styles = {"default"};
_examples = {"++++---"};
}
////////////////////////////////////////////////////////////////////////////////
// Set the minimum and maximum widths for the value.
void ColumnMask::measure (Task& task, unsigned int& minimum, unsigned int& maximum)
{
void ColumnMask::measure(Task& task, unsigned int& minimum, unsigned int& maximum) {
minimum = maximum = 0;
if (task.has (_name))
minimum = maximum = task.get (_name).length ();
if (task.has(_name)) minimum = maximum = task.get(_name).length();
}
////////////////////////////////////////////////////////////////////////////////
void ColumnMask::render (
std::vector <std::string>& lines,
Task& task,
int width,
Color& color)
{
if (task.has (_name))
renderStringLeft (lines, width, color, task.get (_name));
void ColumnMask::render(std::vector<std::string>& lines, Task& task, int width, Color& color) {
if (task.has(_name)) renderStringLeft(lines, width, color, task.get(_name));
}
////////////////////////////////////////////////////////////////////////////////

View File

@@ -29,14 +29,13 @@
#include <ColTypeString.h>
class ColumnMask : public ColumnTypeString
{
public:
ColumnMask ();
void measure (Task&, unsigned int&, unsigned int&);
void render (std::vector <std::string>&, Task&, int, Color&);
class ColumnMask : public ColumnTypeString {
public:
ColumnMask();
void measure(Task&, unsigned int&, unsigned int&);
void render(std::vector<std::string>&, Task&, int, Color&);
private:
private:
};
#endif

View File

@@ -30,9 +30,8 @@
#include <ColModified.h>
////////////////////////////////////////////////////////////////////////////////
ColumnModified::ColumnModified ()
{
_name = "modified";
ColumnModified::ColumnModified() {
_name = "modified";
_label = "Modified";
}

View File

@@ -29,10 +29,9 @@
#include <ColTypeDate.h>
class ColumnModified : public ColumnTypeDate
{
public:
ColumnModified ();
class ColumnModified : public ColumnTypeDate {
public:
ColumnModified();
};
#endif

View File

@@ -31,45 +31,37 @@
#include <format.h>
////////////////////////////////////////////////////////////////////////////////
ColumnParent::ColumnParent ()
{
_name = "parent";
_style = "long";
_label = "Parent task";
ColumnParent::ColumnParent() {
_name = "parent";
_style = "long";
_label = "Parent task";
_modifiable = false;
_styles = {"long", "short"};
_examples = {"f30cb9c3-3fc0-483f-bfb2-3bf134f00694", "f30cb9c3"};
_styles = {"long", "short"};
_examples = {"f30cb9c3-3fc0-483f-bfb2-3bf134f00694", "f30cb9c3"};
}
////////////////////////////////////////////////////////////////////////////////
// Set the minimum and maximum widths for the value.
void ColumnParent::measure (Task& task, unsigned int& minimum, unsigned int& maximum)
{
void ColumnParent::measure(Task& task, unsigned int& minimum, unsigned int& maximum) {
minimum = maximum = 0;
if (task.has (_name))
{
if (_style == "default" || _style == "long") minimum = maximum = 36;
else if (_style == "short") minimum = maximum = 8;
if (task.has(_name)) {
if (_style == "default" || _style == "long")
minimum = maximum = 36;
else if (_style == "short")
minimum = maximum = 8;
}
}
////////////////////////////////////////////////////////////////////////////////
void ColumnParent::render (
std::vector <std::string>& lines,
Task& task,
int width,
Color& color)
{
if (task.has (_name))
{
void ColumnParent::render(std::vector<std::string>& lines, Task& task, int width, Color& color) {
if (task.has(_name)) {
// f30cb9c3-3fc0-483f-bfb2-3bf134f00694 default
// f30cb9c3 short
if (_style == "default" ||
_style == "long")
renderStringLeft (lines, width, color, task.get(_name));
if (_style == "default" || _style == "long")
renderStringLeft(lines, width, color, task.get(_name));
else if (_style == "short")
renderStringLeft (lines, width, color, task.get (_name).substr (0, 8));
renderStringLeft(lines, width, color, task.get(_name).substr(0, 8));
}
}

View File

@@ -29,14 +29,13 @@
#include <ColTypeString.h>
class ColumnParent : public ColumnTypeString
{
public:
ColumnParent ();
void measure (Task&, unsigned int&, unsigned int&);
void render (std::vector <std::string>&, Task&, int, Color&);
class ColumnParent : public ColumnTypeString {
public:
ColumnParent();
void measure(Task&, unsigned int&, unsigned int&);
void render(std::vector<std::string>&, Task&, int, Color&);
private:
private:
};
#endif

View File

@@ -30,120 +30,91 @@
#include <ColProject.h>
#include <Context.h>
#include <Eval.h>
#include <Variant.h>
#include <Lexer.h>
#include <Filter.h>
#include <shared.h>
#include <Lexer.h>
#include <Variant.h>
#include <format.h>
#include <shared.h>
#include <utf8.h>
#include <util.h>
////////////////////////////////////////////////////////////////////////////////
ColumnProject::ColumnProject ()
{
_name = "project";
_style = "full";
_label = "Project";
_styles = {"full", "parent", "indented"};
_examples = {"home.garden",
"home",
" home.garden"};
_hyphenate = Context::getContext ().config.getBoolean ("hyphenate");
ColumnProject::ColumnProject() {
_name = "project";
_style = "full";
_label = "Project";
_styles = {"full", "parent", "indented"};
_examples = {"home.garden", "home", " home.garden"};
_hyphenate = Context::getContext().config.getBoolean("hyphenate");
}
////////////////////////////////////////////////////////////////////////////////
// Set the minimum and maximum widths for the value.
void ColumnProject::measure (Task& task, unsigned int& minimum, unsigned int& maximum)
{
void ColumnProject::measure(Task& task, unsigned int& minimum, unsigned int& maximum) {
minimum = maximum = 0;
if (task.has (_name))
{
std::string project = task.get (_name);
if (task.has(_name)) {
std::string project = task.get(_name);
if (_style == "parent")
{
auto period = project.find ('.');
if (period != std::string::npos)
project = project.substr (0, period);
}
else if (_style == "indented")
{
project = indentProject (project, " ", '.');
if (_style == "parent") {
auto period = project.find('.');
if (period != std::string::npos) project = project.substr(0, period);
} else if (_style == "indented") {
project = indentProject(project, " ", '.');
}
minimum = longestWord (project);
maximum = utf8_width (project);
minimum = longestWord(project);
maximum = utf8_width(project);
}
}
////////////////////////////////////////////////////////////////////////////////
void ColumnProject::render (
std::vector <std::string>& lines,
Task& task,
int width,
Color& color)
{
if (task.has (_name))
{
std::string project = task.get (_name);
if (_style == "parent")
{
auto period = project.find ('.');
if (period != std::string::npos)
project = project.substr (0, period);
}
else if (_style == "indented")
{
project = indentProject (project, " ", '.');
void ColumnProject::render(std::vector<std::string>& lines, Task& task, int width, Color& color) {
if (task.has(_name)) {
std::string project = task.get(_name);
if (_style == "parent") {
auto period = project.find('.');
if (period != std::string::npos) project = project.substr(0, period);
} else if (_style == "indented") {
project = indentProject(project, " ", '.');
}
std::vector <std::string> raw;
wrapText (raw, project, width, _hyphenate);
std::vector<std::string> raw;
wrapText(raw, project, width, _hyphenate);
for (const auto& i : raw)
renderStringLeft (lines, width, color, i);
for (const auto& i : raw) renderStringLeft(lines, width, color, i);
}
}
////////////////////////////////////////////////////////////////////////////////
void ColumnProject::modify (Task& task, const std::string& value)
{
void ColumnProject::modify(Task& task, const std::string& value) {
std::string label = " MODIFICATION ";
// Only if it's a DOM ref, eval it first.
Lexer lexer (value);
Lexer lexer(value);
std::string domRef;
Lexer::Type type;
if (lexer.token (domRef, type) &&
type == Lexer::Type::dom)
{
try
{
if (lexer.token(domRef, type) && type == Lexer::Type::dom) {
try {
Eval e;
e.addSource (domSource);
e.addSource(domSource);
Variant v;
e.evaluateInfixExpression (value, v);
task.set (_name, (std::string) v);
Context::getContext ().debug (label + _name + " <-- '" + (std::string) v + "' <-- '" + value + '\'');
}
catch (const std::string& e)
{
e.evaluateInfixExpression(value, v);
task.set(_name, (std::string)v);
Context::getContext().debug(label + _name + " <-- '" + (std::string)v + "' <-- '" + value +
'\'');
} catch (const std::string& e) {
// If the expression failed because it didn't look like an expression,
// simply store it as-is.
if (e == "The value is not an expression.")
{
task.set (_name, value);
Context::getContext ().debug (label + _name + " <-- '" + value + '\'');
}
else
if (e == "The value is not an expression.") {
task.set(_name, value);
Context::getContext().debug(label + _name + " <-- '" + value + '\'');
} else
throw;
}
}
else
{
task.set (_name, value);
Context::getContext ().debug (label + _name + " <-- '" + value + '\'');
} else {
task.set(_name, value);
Context::getContext().debug(label + _name + " <-- '" + value + '\'');
}
}

View File

@@ -29,15 +29,14 @@
#include <ColTypeString.h>
class ColumnProject : public ColumnTypeString
{
public:
ColumnProject ();
void measure (Task&, unsigned int&, unsigned int&);
void render (std::vector <std::string>&, Task&, int, Color&);
void modify (Task&, const std::string&);
class ColumnProject : public ColumnTypeString {
public:
ColumnProject();
void measure(Task&, unsigned int&, unsigned int&);
void render(std::vector<std::string>&, Task&, int, Color&);
void modify(Task&, const std::string&);
private:
private:
bool _hyphenate;
};

View File

@@ -29,72 +29,60 @@
#include <ColRType.h>
#include <Context.h>
#include <shared.h>
#include <format.h>
#include <shared.h>
#include <cctype>
////////////////////////////////////////////////////////////////////////////////
ColumnRType::ColumnRType ()
{
_name = "rtype";
_style = "default";
_label = "Recurrence type";
ColumnRType::ColumnRType() {
_name = "rtype";
_style = "default";
_label = "Recurrence type";
_modifiable = false;
_styles = {"default", "indicator"};
_examples = {"periodic", "chained"};
_styles = {"default", "indicator"};
_examples = {"periodic", "chained"};
}
////////////////////////////////////////////////////////////////////////////////
// Overriden so that style <----> label are linked.
// Note that you can not determine which gets called first.
void ColumnRType::setStyle (const std::string& value)
{
Column::setStyle (value);
void ColumnRType::setStyle(const std::string& value) {
Column::setStyle(value);
if (_style == "indicator" && _label == "Recurrence type")
_label = _label.substr (0, Context::getContext ().config.get ("rtype.indicator").length ());
_label = _label.substr(0, Context::getContext().config.get("rtype.indicator").length());
}
////////////////////////////////////////////////////////////////////////////////
// Set the minimum and maximum widths for the value.
void ColumnRType::measure (Task& task, unsigned int& minimum, unsigned int& maximum)
{
void ColumnRType::measure(Task& task, unsigned int& minimum, unsigned int& maximum) {
minimum = maximum = 0;
if (task.has (_name))
{
if (task.has(_name)) {
if (_style == "default")
minimum = maximum = task.get (_name).length ();
minimum = maximum = task.get(_name).length();
else if (_style == "indicator")
minimum = maximum = 1;
}
}
////////////////////////////////////////////////////////////////////////////////
void ColumnRType::render (
std::vector <std::string>& lines,
Task& task,
int width,
Color& color)
{
if (task.has (_name))
{
void ColumnRType::render(std::vector<std::string>& lines, Task& task, int width, Color& color) {
if (task.has(_name)) {
if (_style == "default")
renderStringRight (lines, width, color, task.get (_name));
renderStringRight(lines, width, color, task.get(_name));
else if (_style == "indicator")
{
std::string value {" "};
value[0] = toupper (task.get (_name)[0]);
renderStringRight (lines, width, color, value);
else if (_style == "indicator") {
std::string value{" "};
value[0] = toupper(task.get(_name)[0]);
renderStringRight(lines, width, color, value);
}
}
}
////////////////////////////////////////////////////////////////////////////////
bool ColumnRType::validate (const std::string& input) const
{
return input == "periodic" ||
input == "chained";
bool ColumnRType::validate(const std::string& input) const {
return input == "periodic" || input == "chained";
}
////////////////////////////////////////////////////////////////////////////////

View File

@@ -29,16 +29,15 @@
#include <ColTypeString.h>
class ColumnRType : public ColumnTypeString
{
public:
ColumnRType ();
void setStyle (const std::string&);
void measure (Task&, unsigned int&, unsigned int&);
void render (std::vector <std::string>&, Task&, int, Color&);
bool validate (const std::string&) const;
class ColumnRType : public ColumnTypeString {
public:
ColumnRType();
void setStyle(const std::string&);
void measure(Task&, unsigned int&, unsigned int&);
void render(std::vector<std::string>&, Task&, int, Color&);
bool validate(const std::string&) const;
private:
private:
};
#endif

View File

@@ -31,100 +31,81 @@
#include <Context.h>
#include <Duration.h>
#include <Eval.h>
#include <Variant.h>
#include <Lexer.h>
#include <Filter.h>
#include <shared.h>
#include <Lexer.h>
#include <Variant.h>
#include <format.h>
#include <shared.h>
#include <utf8.h>
////////////////////////////////////////////////////////////////////////////////
ColumnRecur::ColumnRecur ()
{
_name = "recur";
_style = "duration";
_label = "Recur";
ColumnRecur::ColumnRecur() {
_name = "recur";
_style = "duration";
_label = "Recur";
_modifiable = true;
_styles = {"duration", "indicator"};
_examples = {"weekly", Context::getContext ().config.get ("recurrence.indicator")};
_styles = {"duration", "indicator"};
_examples = {"weekly", Context::getContext().config.get("recurrence.indicator")};
}
////////////////////////////////////////////////////////////////////////////////
// Overriden so that style <----> label are linked.
// Note that you can not determine which gets called first.
void ColumnRecur::setStyle (const std::string& value)
{
Column::setStyle (value);
void ColumnRecur::setStyle(const std::string& value) {
Column::setStyle(value);
if (_style == "indicator" && _label == "Recur")
_label = _label.substr (0, Context::getContext ().config.get ("recurrence.indicator").length ());
_label = _label.substr(0, Context::getContext().config.get("recurrence.indicator").length());
}
////////////////////////////////////////////////////////////////////////////////
// Set the minimum and maximum widths for the value.
void ColumnRecur::measure (Task& task, unsigned int& minimum, unsigned int& maximum)
{
void ColumnRecur::measure(Task& task, unsigned int& minimum, unsigned int& maximum) {
minimum = maximum = 0;
if (task.has (_name))
{
if (_style == "default" ||
_style == "duration")
{
minimum = maximum = Duration (task.get (_name)).formatISO ().length ();
}
else if (_style == "indicator")
{
minimum = maximum = utf8_width (Context::getContext ().config.get ("recurrence.indicator"));
if (task.has(_name)) {
if (_style == "default" || _style == "duration") {
minimum = maximum = Duration(task.get(_name)).formatISO().length();
} else if (_style == "indicator") {
minimum = maximum = utf8_width(Context::getContext().config.get("recurrence.indicator"));
}
}
}
////////////////////////////////////////////////////////////////////////////////
void ColumnRecur::render (
std::vector <std::string>& lines,
Task& task,
int width,
Color& color)
{
if (task.has (_name))
{
if (_style == "default" ||
_style == "duration")
renderStringRight (lines, width, color, Duration (task.get (_name)).formatISO ());
void ColumnRecur::render(std::vector<std::string>& lines, Task& task, int width, Color& color) {
if (task.has(_name)) {
if (_style == "default" || _style == "duration")
renderStringRight(lines, width, color, Duration(task.get(_name)).formatISO());
else if (_style == "indicator")
renderStringRight (lines, width, color, Context::getContext ().config.get ("recurrence.indicator"));
renderStringRight(lines, width, color,
Context::getContext().config.get("recurrence.indicator"));
}
}
////////////////////////////////////////////////////////////////////////////////
// The duration is stored in raw form, but it must still be valid,
// and therefore is parsed first.
void ColumnRecur::modify (Task& task, const std::string& value)
{
void ColumnRecur::modify(Task& task, const std::string& value) {
// Try to evaluate 'value'. It might work.
Variant evaluatedValue;
try
{
try {
Eval e;
e.addSource (domSource);
e.evaluateInfixExpression (value, evaluatedValue);
e.addSource(domSource);
e.evaluateInfixExpression(value, evaluatedValue);
}
catch (...)
{
evaluatedValue = Variant (value);
catch (...) {
evaluatedValue = Variant(value);
}
if (evaluatedValue.type () == Variant::type_duration)
{
if (evaluatedValue.type() == Variant::type_duration) {
// Store the raw value, for 'recur'.
std::string label = " MODIFICATION ";
Context::getContext ().debug (label + _name + " <-- '" + value + '\'');
task.set (_name, value);
}
else
throw format ("The duration value '{1}' is not supported.", value);
Context::getContext().debug(label + _name + " <-- '" + value + '\'');
task.set(_name, value);
} else
throw format("The duration value '{1}' is not supported.", value);
}
////////////////////////////////////////////////////////////////////////////////

View File

@@ -31,16 +31,15 @@
// This is 'string', and not 'duration' to force the value to be stored as a
// raw duration, so that it can be reevaluated every time.
class ColumnRecur : public ColumnTypeString
{
public:
ColumnRecur ();
void setStyle (const std::string&);
void measure (Task&, unsigned int&, unsigned int&);
void render (std::vector <std::string>&, Task&, int, Color&);
void modify (Task&, const std::string&);
class ColumnRecur : public ColumnTypeString {
public:
ColumnRecur();
void setStyle(const std::string&);
void measure(Task&, unsigned int&, unsigned int&);
void render(std::vector<std::string>&, Task&, int, Color&);
void modify(Task&, const std::string&);
private:
private:
};
#endif

View File

@@ -30,21 +30,18 @@
#include <ColScheduled.h>
////////////////////////////////////////////////////////////////////////////////
ColumnScheduled::ColumnScheduled ()
{
_name = "scheduled";
ColumnScheduled::ColumnScheduled() {
_name = "scheduled";
_label = "Scheduled";
}
////////////////////////////////////////////////////////////////////////////////
// Overriden so that style <----> label are linked.
// Note that you can not determine which gets called first.
void ColumnScheduled::setStyle (const std::string& value)
{
Column::setStyle (value);
void ColumnScheduled::setStyle(const std::string& value) {
Column::setStyle(value);
if (_style == "countdown" && _label == "Scheduled")
_label = "Count";
if (_style == "countdown" && _label == "Scheduled") _label = "Count";
}
////////////////////////////////////////////////////////////////////////////////

View File

@@ -29,11 +29,10 @@
#include <ColTypeDate.h>
class ColumnScheduled : public ColumnTypeDate
{
public:
ColumnScheduled ();
void setStyle (const std::string&);
class ColumnScheduled : public ColumnTypeDate {
public:
ColumnScheduled();
void setStyle(const std::string&);
};
#endif

View File

@@ -32,58 +32,46 @@
#include <utf8.h>
////////////////////////////////////////////////////////////////////////////////
ColumnStart::ColumnStart ()
{
_name = "start";
ColumnStart::ColumnStart() {
_name = "start";
_label = "Started";
_styles.push_back ("active");
_examples.push_back (Context::getContext ().config.get ("active.indicator"));
_styles.push_back("active");
_examples.push_back(Context::getContext().config.get("active.indicator"));
}
////////////////////////////////////////////////////////////////////////////////
// Overriden so that style <----> label are linked.
// Note that you can not determine which gets called first.
void ColumnStart::setStyle (const std::string& value)
{
Column::setStyle (value);
void ColumnStart::setStyle(const std::string& value) {
Column::setStyle(value);
if (_style == "active" && _label == "Started")
_label = "A";
if (_style == "active" && _label == "Started") _label = "A";
}
////////////////////////////////////////////////////////////////////////////////
// Set the minimum and maximum widths for the value.
void ColumnStart::measure (Task& task, unsigned int& minimum, unsigned int& maximum)
{
void ColumnStart::measure(Task& task, unsigned int& minimum, unsigned int& maximum) {
minimum = maximum = 0;
if (task.has (_name))
{
if (task.has(_name)) {
if (_style == "active")
minimum = maximum = utf8_width (Context::getContext ().config.get ("active.indicator"));
minimum = maximum = utf8_width(Context::getContext().config.get("active.indicator"));
else
ColumnTypeDate::measure (task, minimum, maximum);
ColumnTypeDate::measure(task, minimum, maximum);
// TODO Throw on bad format.
}
}
////////////////////////////////////////////////////////////////////////////////
void ColumnStart::render (
std::vector <std::string>& lines,
Task& task,
int width,
Color& color)
{
if (task.has (_name))
{
if (_style == "active")
{
if (! task.has ("end"))
renderStringRight (lines, width, color, Context::getContext ().config.get ("active.indicator"));
}
else
ColumnTypeDate::render (lines, task, width, color);
void ColumnStart::render(std::vector<std::string>& lines, Task& task, int width, Color& color) {
if (task.has(_name)) {
if (_style == "active") {
if (!task.has("end"))
renderStringRight(lines, width, color,
Context::getContext().config.get("active.indicator"));
} else
ColumnTypeDate::render(lines, task, width, color);
}
}

View File

@@ -29,13 +29,12 @@
#include <ColTypeDate.h>
class ColumnStart : public ColumnTypeDate
{
public:
ColumnStart ();
void setStyle (const std::string&);
void measure (Task&, unsigned int&, unsigned int&);
void render (std::vector <std::string>&, Task&, int, Color&);
class ColumnStart : public ColumnTypeDate {
public:
ColumnStart();
void setStyle(const std::string&);
void measure(Task&, unsigned int&, unsigned int&);
void render(std::vector<std::string>&, Task&, int, Color&);
};
#endif

View File

@@ -32,81 +32,75 @@
#include <utf8.h>
////////////////////////////////////////////////////////////////////////////////
ColumnStatus::ColumnStatus ()
{
_name = "status";
_style = "long";
_label = "Status";
_styles = {"long", "short"};
_examples = {"Pending",
"P"};
ColumnStatus::ColumnStatus() {
_name = "status";
_style = "long";
_label = "Status";
_styles = {"long", "short"};
_examples = {"Pending", "P"};
}
////////////////////////////////////////////////////////////////////////////////
// Overriden so that style <----> label are linked.
// Note that you can not determine which gets called first.
void ColumnStatus::setStyle (const std::string& value)
{
Column::setStyle (value);
void ColumnStatus::setStyle(const std::string& value) {
Column::setStyle(value);
if (_style == "short" && _label == "Status")
_label = "St";
if (_style == "short" && _label == "Status") _label = "St";
}
////////////////////////////////////////////////////////////////////////////////
// Set the minimum and maximum widths for the value.
void ColumnStatus::measure (Task& task, unsigned int& minimum, unsigned int& maximum)
{
Task::status status = task.getStatus ();
void ColumnStatus::measure(Task& task, unsigned int& minimum, unsigned int& maximum) {
Task::status status = task.getStatus();
if (_style == "default" ||
_style == "long")
{
if (_style == "default" || _style == "long") {
if (status == Task::pending)
minimum = maximum = utf8_width ("Pending");
minimum = maximum = utf8_width("Pending");
else if (status == Task::deleted)
minimum = maximum = utf8_width ("Deleted");
minimum = maximum = utf8_width("Deleted");
else if (status == Task::waiting)
minimum = maximum = utf8_width ("Waiting");
minimum = maximum = utf8_width("Waiting");
else if (status == Task::completed)
minimum = maximum = utf8_width ("Completed");
minimum = maximum = utf8_width("Completed");
else if (status == Task::recurring)
minimum = maximum = utf8_width ("Recurring");
}
else if (_style == "short")
minimum = maximum = utf8_width("Recurring");
} else if (_style == "short")
minimum = maximum = 1;
}
////////////////////////////////////////////////////////////////////////////////
void ColumnStatus::render (
std::vector <std::string>& lines,
Task& task,
int width,
Color& color)
{
Task::status status = task.getStatus ();
void ColumnStatus::render(std::vector<std::string>& lines, Task& task, int width, Color& color) {
Task::status status = task.getStatus();
std::string value;
if (_style == "default" ||
_style == "long")
{
if (status == Task::pending) value = "Pending";
else if (status == Task::completed) value = "Completed";
else if (status == Task::deleted) value = "Deleted";
else if (status == Task::waiting) value = "Waiting";
else if (status == Task::recurring) value = "Recurring";
if (_style == "default" || _style == "long") {
if (status == Task::pending)
value = "Pending";
else if (status == Task::completed)
value = "Completed";
else if (status == Task::deleted)
value = "Deleted";
else if (status == Task::waiting)
value = "Waiting";
else if (status == Task::recurring)
value = "Recurring";
}
else if (_style == "short")
{
if (status == Task::pending) value = "P";
else if (status == Task::completed) value = "C";
else if (status == Task::deleted) value = "D";
else if (status == Task::waiting) value = "W";
else if (status == Task::recurring) value = "R";
else if (_style == "short") {
if (status == Task::pending)
value = "P";
else if (status == Task::completed)
value = "C";
else if (status == Task::deleted)
value = "D";
else if (status == Task::waiting)
value = "W";
else if (status == Task::recurring)
value = "R";
}
renderStringLeft (lines, width, color, value);
renderStringLeft(lines, width, color, value);
}
////////////////////////////////////////////////////////////////////////////////

View File

@@ -29,15 +29,14 @@
#include <ColTypeString.h>
class ColumnStatus : public ColumnTypeString
{
public:
ColumnStatus ();
void setStyle (const std::string&);
void measure (Task&, unsigned int&, unsigned int&);
void render (std::vector <std::string>&, Task&, int, Color&);
class ColumnStatus : public ColumnTypeString {
public:
ColumnStatus();
void setStyle(const std::string&);
void measure(Task&, unsigned int&, unsigned int&);
void render(std::vector<std::string>&, Task&, int, Color&);
private:
private:
};
#endif

View File

@@ -28,154 +28,119 @@
// cmake.h include header must come first
#include <ColTags.h>
#include <algorithm>
#include <Context.h>
#include <Eval.h>
#include <Variant.h>
#include <Filter.h>
#include <shared.h>
#include <Variant.h>
#include <format.h>
#include <utf8.h>
#include <main.h>
#include <shared.h>
#include <utf8.h>
#include <algorithm>
////////////////////////////////////////////////////////////////////////////////
ColumnTags::ColumnTags ()
{
_name = "tags";
_style = "list";
_label = "Tags";
_styles = {"list", "indicator", "count"};
_examples = {"home @chore next",
Context::getContext ().config.get ("tag.indicator"),
"[2]"};
ColumnTags::ColumnTags() {
_name = "tags";
_style = "list";
_label = "Tags";
_styles = {"list", "indicator", "count"};
_examples = {"home @chore next", Context::getContext().config.get("tag.indicator"), "[2]"};
_hyphenate = false;
}
////////////////////////////////////////////////////////////////////////////////
// Overriden so that style <----> label are linked.
// Note that you can not determine which gets called first.
void ColumnTags::setStyle (const std::string& value)
{
Column::setStyle (value);
void ColumnTags::setStyle(const std::string& value) {
Column::setStyle(value);
if (_style == "indicator" &&
_label == "Tags")
_label = _label.substr (0, Context::getContext ().config.get ("tag.indicator").length ());
if (_style == "indicator" && _label == "Tags")
_label = _label.substr(0, Context::getContext().config.get("tag.indicator").length());
else if (_style == "count" &&
_label == "Tags")
else if (_style == "count" && _label == "Tags")
_label = "Tag";
}
////////////////////////////////////////////////////////////////////////////////
// Set the minimum and maximum widths for the value.
void ColumnTags::measure (Task& task, unsigned int& minimum, unsigned int& maximum)
{
void ColumnTags::measure(Task& task, unsigned int& minimum, unsigned int& maximum) {
minimum = maximum = 0;
if (task.has (_name))
{
if (_style == "indicator")
{
minimum = maximum = utf8_width (Context::getContext ().config.get ("tag.indicator"));
}
else if (_style == "count")
{
if (task.has(_name)) {
if (_style == "indicator") {
minimum = maximum = utf8_width(Context::getContext().config.get("tag.indicator"));
} else if (_style == "count") {
minimum = maximum = 3;
}
else if (_style == "default" ||
_style == "list")
{
std::string tags = task.get (_name);
} else if (_style == "default" || _style == "list") {
std::string tags = task.get(_name);
// Find the widest tag.
if (tags.find (',') != std::string::npos)
{
auto all = split (tags, ',');
for (const auto& tag : all)
{
auto length = utf8_width (tag);
if (length > minimum)
minimum = length;
if (tags.find(',') != std::string::npos) {
auto all = split(tags, ',');
for (const auto& tag : all) {
auto length = utf8_width(tag);
if (length > minimum) minimum = length;
}
maximum = utf8_width (tags);
maximum = utf8_width(tags);
}
// No need to split a single tag.
else
minimum = maximum = utf8_width (tags);
minimum = maximum = utf8_width(tags);
}
}
}
////////////////////////////////////////////////////////////////////////////////
void ColumnTags::render (
std::vector <std::string>& lines,
Task& task,
int width,
Color& color)
{
auto all = task.getTags ();
if (all.size() > 0)
{
if (_style == "default" ||
_style == "list")
{
if (all.size () > 1)
{
std::sort (all.begin (), all.end ());
auto tags = join (" ", all);
void ColumnTags::render(std::vector<std::string>& lines, Task& task, int width, Color& color) {
auto all = task.getTags();
if (all.size() > 0) {
if (_style == "default" || _style == "list") {
if (all.size() > 1) {
std::sort(all.begin(), all.end());
auto tags = join(" ", all);
all.clear ();
wrapText (all, tags, width, _hyphenate);
all.clear();
wrapText(all, tags, width, _hyphenate);
for (const auto& i : all)
renderStringLeft (lines, width, color, i);
}
else
renderStringLeft (lines, width, color, all[0]);
}
else if (_style == "indicator")
{
renderStringRight (lines, width, color, Context::getContext ().config.get ("tag.indicator"));
}
else if (_style == "count")
{
renderStringRight (lines, width, color, '[' + format (static_cast <int> (all.size ())) + ']');
for (const auto& i : all) renderStringLeft(lines, width, color, i);
} else
renderStringLeft(lines, width, color, all[0]);
} else if (_style == "indicator") {
renderStringRight(lines, width, color, Context::getContext().config.get("tag.indicator"));
} else if (_style == "count") {
renderStringRight(lines, width, color, '[' + format(static_cast<int>(all.size())) + ']');
}
}
}
////////////////////////////////////////////////////////////////////////////////
void ColumnTags::modify (Task& task, const std::string& value)
{
void ColumnTags::modify(Task& task, const std::string& value) {
std::string label = " MODIFICATION ";
std::string commasep;
std::vector <std::string> tags;
std::vector<std::string> tags;
// If it's a DOM ref, eval it first.
Lexer lexer (value);
Lexer lexer(value);
std::string domRef;
Lexer::Type type;
if (lexer.token (domRef, type) &&
type == Lexer::Type::dom)
{
if (lexer.token(domRef, type) && type == Lexer::Type::dom) {
Eval e;
e.addSource (domSource);
e.addSource(domSource);
Variant v;
e.evaluateInfixExpression (value, v);
commasep = (std::string) v;
e.evaluateInfixExpression(value, v);
commasep = (std::string)v;
} else {
commasep = (std::string) value;
commasep = (std::string)value;
}
for (auto& tag : split (commasep, ','))
{
tags.push_back ((std::string) tag);
Context::getContext ().debug (label + "tags <-- '" + tag + '\'');
for (auto& tag : split(commasep, ',')) {
tags.push_back((std::string)tag);
Context::getContext().debug(label + "tags <-- '" + tag + '\'');
feedback_special_tags (task, tag);
feedback_special_tags(task, tag);
}
task.setTags(tags);

View File

@@ -29,16 +29,15 @@
#include <ColTypeString.h>
class ColumnTags : public ColumnTypeString
{
public:
ColumnTags ();
void setStyle (const std::string&);
void measure (Task&, unsigned int&, unsigned int&);
void render (std::vector <std::string>&, Task&, int, Color&);
void modify (Task&, const std::string&);
class ColumnTags : public ColumnTypeString {
public:
ColumnTags();
void setStyle(const std::string&);
void measure(Task&, unsigned int&, unsigned int&);
void render(std::vector<std::string>&, Task&, int, Color&);
void modify(Task&, const std::string&);
private:
private:
bool _hyphenate;
};

View File

@@ -31,45 +31,37 @@
#include <format.h>
////////////////////////////////////////////////////////////////////////////////
ColumnTemplate::ColumnTemplate ()
{
_name = "template";
_style = "long";
_label = "Template task";
ColumnTemplate::ColumnTemplate() {
_name = "template";
_style = "long";
_label = "Template task";
_modifiable = false;
_styles = {"long", "short"};
_examples = {"f30cb9c3-3fc0-483f-bfb2-3bf134f00694", "f30cb9c3"};
_styles = {"long", "short"};
_examples = {"f30cb9c3-3fc0-483f-bfb2-3bf134f00694", "f30cb9c3"};
}
////////////////////////////////////////////////////////////////////////////////
// Set the minimum and maximum widths for the value.
void ColumnTemplate::measure (Task& task, unsigned int& minimum, unsigned int& maximum)
{
void ColumnTemplate::measure(Task& task, unsigned int& minimum, unsigned int& maximum) {
minimum = maximum = 0;
if (task.has (_name))
{
if (_style == "default" || _style == "long") minimum = maximum = 36;
else if (_style == "short") minimum = maximum = 8;
if (task.has(_name)) {
if (_style == "default" || _style == "long")
minimum = maximum = 36;
else if (_style == "short")
minimum = maximum = 8;
}
}
////////////////////////////////////////////////////////////////////////////////
void ColumnTemplate::render (
std::vector <std::string>& lines,
Task& task,
int width,
Color& color)
{
if (task.has (_name))
{
void ColumnTemplate::render(std::vector<std::string>& lines, Task& task, int width, Color& color) {
if (task.has(_name)) {
// f30cb9c3-3fc0-483f-bfb2-3bf134f00694 default
// f30cb9c3 short
if (_style == "default" ||
_style == "long")
renderStringLeft (lines, width, color, task.get(_name));
if (_style == "default" || _style == "long")
renderStringLeft(lines, width, color, task.get(_name));
else if (_style == "short")
renderStringLeft (lines, width, color, task.get (_name).substr (0, 8));
renderStringLeft(lines, width, color, task.get(_name).substr(0, 8));
}
}

View File

@@ -29,14 +29,13 @@
#include <ColTypeString.h>
class ColumnTemplate : public ColumnTypeString
{
public:
ColumnTemplate ();
void measure (Task&, unsigned int&, unsigned int&);
void render (std::vector <std::string>&, Task&, int, Color&);
class ColumnTemplate : public ColumnTypeString {
public:
ColumnTemplate();
void measure(Task&, unsigned int&, unsigned int&);
void render(std::vector<std::string>&, Task&, int, Color&);
private:
private:
};
#endif

View File

@@ -32,215 +32,165 @@
#include <Datetime.h>
#include <Duration.h>
#include <Eval.h>
#include <Variant.h>
#include <Filter.h>
#include <Variant.h>
#include <format.h>
////////////////////////////////////////////////////////////////////////////////
ColumnTypeDate::ColumnTypeDate ()
{
_name = "";
_type = "date";
_style = "formatted";
_label = "";
_styles = {"formatted",
"julian",
"epoch",
"iso",
"age",
"relative",
"remaining",
"countdown"};
ColumnTypeDate::ColumnTypeDate() {
_name = "";
_type = "date";
_style = "formatted";
_label = "";
_styles = {"formatted", "julian", "epoch", "iso", "age", "relative", "remaining", "countdown"};
Datetime now;
now -= 125; // So that "age" is non-zero.
_examples = {now.toString (Context::getContext ().config.get ("dateformat")),
format (now.toJulian (), 13, 12),
now.toEpochString (),
now.toISO (),
Duration (Datetime () - now).formatVague (true),
'-' + Duration (Datetime () - now).formatVague (true),
now -= 125; // So that "age" is non-zero.
_examples = {now.toString(Context::getContext().config.get("dateformat")),
format(now.toJulian(), 13, 12),
now.toEpochString(),
now.toISO(),
Duration(Datetime() - now).formatVague(true),
'-' + Duration(Datetime() - now).formatVague(true),
"",
Duration (Datetime () - now).format ()};
Duration(Datetime() - now).format()};
}
////////////////////////////////////////////////////////////////////////////////
// Set the minimum and maximum widths for the value.
void ColumnTypeDate::measure (Task& task, unsigned int& minimum, unsigned int& maximum)
{
void ColumnTypeDate::measure(Task& task, unsigned int& minimum, unsigned int& maximum) {
minimum = maximum = 0;
if (task.has (_name))
{
Datetime date (task.get_date (_name));
if (task.has(_name)) {
Datetime date(task.get_date(_name));
if (_style == "default" ||
_style == "formatted")
{
if (_style == "default" || _style == "formatted") {
// Determine the output date format, which uses a hierarchy of definitions.
// rc.report.<report>.dateformat
// rc.dateformat.report
// rc.dateformat.
std::string format = Context::getContext ().config.get ("report." + _report + ".dateformat");
if (format == "")
format = Context::getContext ().config.get ("dateformat.report");
if (format == "")
format = Context::getContext ().config.get ("dateformat");
std::string format = Context::getContext().config.get("report." + _report + ".dateformat");
if (format == "") format = Context::getContext().config.get("dateformat.report");
if (format == "") format = Context::getContext().config.get("dateformat");
minimum = maximum = Datetime::length (format);
}
else if (_style == "countdown")
{
minimum = maximum = Datetime::length(format);
} else if (_style == "countdown") {
Datetime now;
minimum = maximum = Duration (date - now).formatVague (true).length ();
}
else if (_style == "julian")
{
minimum = maximum = format (date.toJulian (), 13, 12).length ();
}
else if (_style == "epoch")
{
minimum = maximum = date.toEpochString ().length ();
}
else if (_style == "iso")
{
minimum = maximum = date.toISO ().length ();
}
else if (_style == "age")
{
minimum = maximum = Duration(date - now).formatVague(true).length();
} else if (_style == "julian") {
minimum = maximum = format(date.toJulian(), 13, 12).length();
} else if (_style == "epoch") {
minimum = maximum = date.toEpochString().length();
} else if (_style == "iso") {
minimum = maximum = date.toISO().length();
} else if (_style == "age") {
Datetime now;
if (now > date)
minimum = maximum = Duration (now - date).formatVague (true).length ();
minimum = maximum = Duration(now - date).formatVague(true).length();
else
minimum = maximum = Duration (date - now).formatVague (true).length () + 1;
}
else if (_style == "relative")
{
minimum = maximum = Duration(date - now).formatVague(true).length() + 1;
} else if (_style == "relative") {
Datetime now;
if (now < date)
minimum = maximum = Duration (date - now).formatVague (true).length ();
minimum = maximum = Duration(date - now).formatVague(true).length();
else
minimum = maximum = Duration (now - date).formatVague (true).length () + 1;
}
else if (_style == "remaining")
{
minimum = maximum = Duration(now - date).formatVague(true).length() + 1;
} else if (_style == "remaining") {
Datetime now;
if (date > now)
minimum = maximum = Duration (date - now).formatVague (true).length ();
if (date > now) minimum = maximum = Duration(date - now).formatVague(true).length();
}
}
}
////////////////////////////////////////////////////////////////////////////////
void ColumnTypeDate::render (
std::vector <std::string>& lines,
Task& task,
int width,
Color& color)
{
if (task.has (_name))
{
Datetime date (task.get_date (_name));
void ColumnTypeDate::render(std::vector<std::string>& lines, Task& task, int width, Color& color) {
if (task.has(_name)) {
Datetime date(task.get_date(_name));
if (_style == "default" ||
_style == "formatted")
{
if (_style == "default" || _style == "formatted") {
// Determine the output date format, which uses a hierarchy of definitions.
// rc.report.<report>.dateformat
// rc.dateformat.report
// rc.dateformat
std::string format = Context::getContext ().config.get ("report." + _report + ".dateformat");
if (format == "")
{
format = Context::getContext ().config.get ("dateformat.report");
if (format == "")
format = Context::getContext ().config.get ("dateformat");
std::string format = Context::getContext().config.get("report." + _report + ".dateformat");
if (format == "") {
format = Context::getContext().config.get("dateformat.report");
if (format == "") format = Context::getContext().config.get("dateformat");
}
renderStringLeft (lines, width, color, date.toString (format));
}
else if (_style == "countdown")
{
renderStringLeft(lines, width, color, date.toString(format));
} else if (_style == "countdown") {
Datetime now;
renderStringRight (lines, width, color, Duration (date - now).formatVague (true));
}
else if (_style == "julian")
renderStringRight (lines, width, color, format (date.toJulian (), 13, 12));
renderStringRight(lines, width, color, Duration(date - now).formatVague(true));
} else if (_style == "julian")
renderStringRight(lines, width, color, format(date.toJulian(), 13, 12));
else if (_style == "epoch")
renderStringRight (lines, width, color, date.toEpochString ());
renderStringRight(lines, width, color, date.toEpochString());
else if (_style == "iso")
renderStringLeft (lines, width, color, date.toISO ());
renderStringLeft(lines, width, color, date.toISO());
else if (_style == "age")
{
else if (_style == "age") {
Datetime now;
if (now > date)
renderStringRight (lines, width, color, Duration (now - date).formatVague (true));
renderStringRight(lines, width, color, Duration(now - date).formatVague(true));
else
renderStringRight (lines, width, color, '-' + Duration (date - now).formatVague (true));
}
else if (_style == "relative")
{
renderStringRight(lines, width, color, '-' + Duration(date - now).formatVague(true));
} else if (_style == "relative") {
Datetime now;
if (now < date)
renderStringRight (lines, width, color, Duration (date - now).formatVague (true));
renderStringRight(lines, width, color, Duration(date - now).formatVague(true));
else
renderStringRight (lines, width, color, '-' + Duration (now - date).formatVague (true));
renderStringRight(lines, width, color, '-' + Duration(now - date).formatVague(true));
}
else if (_style == "remaining")
{
else if (_style == "remaining") {
Datetime now;
if (date > now)
renderStringRight (lines, width, color, Duration (date - now).formatVague (true));
renderStringRight(lines, width, color, Duration(date - now).formatVague(true));
}
}
}
////////////////////////////////////////////////////////////////////////////////
bool ColumnTypeDate::validate (const std::string& input) const
{
return input.length () ? true : false;
bool ColumnTypeDate::validate(const std::string& input) const {
return input.length() ? true : false;
}
////////////////////////////////////////////////////////////////////////////////
void ColumnTypeDate::modify (Task& task, const std::string& value)
{
void ColumnTypeDate::modify(Task& task, const std::string& value) {
// Try to evaluate 'value'. It might work.
Variant evaluatedValue;
try
{
try {
Eval e;
e.addSource (domSource);
e.evaluateInfixExpression (value, evaluatedValue);
e.addSource(domSource);
e.evaluateInfixExpression(value, evaluatedValue);
}
catch (...)
{
evaluatedValue = Variant (value);
catch (...) {
evaluatedValue = Variant(value);
}
// If v is duration, we need to convert it to date (and implicitly add now),
// else store as date.
std::string label = " MODIFICATION ";
if (evaluatedValue.type () == Variant::type_duration)
{
Context::getContext ().debug (label + _name + " <-- '" + format ("{1}", format (evaluatedValue.get_duration ())) + "' <-- '" + (std::string) evaluatedValue + "' <-- '" + value + '\'');
evaluatedValue.cast (Variant::type_date);
}
else
{
evaluatedValue.cast (Variant::type_date);
Context::getContext ().debug (label + _name + " <-- '" + format ("{1}", evaluatedValue.get_date ()) + "' <-- '" + (std::string) evaluatedValue + "' <-- '" + value + '\'');
if (evaluatedValue.type() == Variant::type_duration) {
Context::getContext().debug(label + _name + " <-- '" +
format("{1}", format(evaluatedValue.get_duration())) + "' <-- '" +
(std::string)evaluatedValue + "' <-- '" + value + '\'');
evaluatedValue.cast(Variant::type_date);
} else {
evaluatedValue.cast(Variant::type_date);
Context::getContext().debug(label + _name + " <-- '" +
format("{1}", evaluatedValue.get_date()) + "' <-- '" +
(std::string)evaluatedValue + "' <-- '" + value + '\'');
}
// If a date doesn't parse (2/29/2014) then it evaluates to zero.
if (value != "" &&
evaluatedValue.get_date () == 0)
throw format ("'{1}' is not a valid date in the '{2}' format.", value, Variant::dateFormat);
if (value != "" && evaluatedValue.get_date() == 0)
throw format("'{1}' is not a valid date in the '{2}' format.", value, Variant::dateFormat);
task.set (_name, evaluatedValue.get_date ());
task.set(_name, evaluatedValue.get_date());
}
////////////////////////////////////////////////////////////////////////////////

View File

@@ -27,20 +27,20 @@
#ifndef INCLUDED_COLTYPEDATE
#define INCLUDED_COLTYPEDATE
#include <vector>
#include <string>
#include <Column.h>
#include <Color.h>
#include <Column.h>
#include <Task.h>
class ColumnTypeDate : public Column
{
public:
ColumnTypeDate ();
virtual void measure (Task&, unsigned int&, unsigned int&);
virtual void render (std::vector <std::string>&, Task&, int, Color&);
virtual bool validate (const std::string&) const;
virtual void modify (Task&, const std::string&);
#include <string>
#include <vector>
class ColumnTypeDate : public Column {
public:
ColumnTypeDate();
virtual void measure(Task&, unsigned int&, unsigned int&);
virtual void render(std::vector<std::string>&, Task&, int, Color&);
virtual bool validate(const std::string&) const;
virtual void modify(Task&, const std::string&);
};
#endif

View File

@@ -30,50 +30,42 @@
#include <ColTypeDuration.h>
#include <Context.h>
#include <Eval.h>
#include <Variant.h>
#include <Filter.h>
#include <Variant.h>
#include <format.h>
////////////////////////////////////////////////////////////////////////////////
ColumnTypeDuration::ColumnTypeDuration ()
{
_type = "duration";
ColumnTypeDuration::ColumnTypeDuration() { _type = "duration"; }
////////////////////////////////////////////////////////////////////////////////
bool ColumnTypeDuration::validate(const std::string& input) const {
return input.length() ? true : false;
}
////////////////////////////////////////////////////////////////////////////////
bool ColumnTypeDuration::validate (const std::string& input) const
{
return input.length () ? true : false;
}
////////////////////////////////////////////////////////////////////////////////
void ColumnTypeDuration::modify (Task& task, const std::string& value)
{
void ColumnTypeDuration::modify(Task& task, const std::string& value) {
// Try to evaluate 'value'. It might work.
Variant evaluatedValue;
try
{
try {
Eval e;
e.addSource (domSource);
e.evaluateInfixExpression (value, evaluatedValue);
e.addSource(domSource);
e.evaluateInfixExpression(value, evaluatedValue);
}
catch (...)
{
evaluatedValue = Variant (value);
catch (...) {
evaluatedValue = Variant(value);
}
// The duration is stored in raw form, but it must still be valid,
// and therefore is parsed first.
std::string label = " MODIFICATION ";
if (evaluatedValue.type () == Variant::type_duration)
{
if (evaluatedValue.type() == Variant::type_duration) {
// Store the raw value, for 'recur'.
Context::getContext ().debug (label + _name + " <-- " + (std::string) evaluatedValue + " <-- '" + value + '\'');
task.set (_name, evaluatedValue);
}
else
throw format ("The duration value '{1}' is not supported.", value);
Context::getContext().debug(label + _name + " <-- " + (std::string)evaluatedValue + " <-- '" +
value + '\'');
task.set(_name, evaluatedValue);
} else
throw format("The duration value '{1}' is not supported.", value);
}
////////////////////////////////////////////////////////////////////////////////

View File

@@ -27,16 +27,16 @@
#ifndef INCLUDED_COLTYPEDURATION
#define INCLUDED_COLTYPEDURATION
#include <string>
#include <Column.h>
#include <Task.h>
class ColumnTypeDuration : public Column
{
public:
ColumnTypeDuration ();
virtual bool validate (const std::string&) const;
virtual void modify (Task&, const std::string&);
#include <string>
class ColumnTypeDuration : public Column {
public:
ColumnTypeDuration();
virtual bool validate(const std::string&) const;
virtual void modify(Task&, const std::string&);
};
#endif

View File

@@ -30,45 +30,38 @@
#include <ColTypeNumeric.h>
#include <Context.h>
#include <Eval.h>
#include <Variant.h>
#include <Filter.h>
#include <Variant.h>
#include <format.h>
////////////////////////////////////////////////////////////////////////////////
ColumnTypeNumeric::ColumnTypeNumeric ()
{
_type = "numeric";
ColumnTypeNumeric::ColumnTypeNumeric() { _type = "numeric"; }
////////////////////////////////////////////////////////////////////////////////
bool ColumnTypeNumeric::validate(const std::string& input) const {
return input.length() ? true : false;
}
////////////////////////////////////////////////////////////////////////////////
bool ColumnTypeNumeric::validate (const std::string& input) const
{
return input.length () ? true : false;
}
////////////////////////////////////////////////////////////////////////////////
void ColumnTypeNumeric::modify (Task& task, const std::string& value)
{
void ColumnTypeNumeric::modify(Task& task, const std::string& value) {
// Try to evaluate 'value'. It might work.
Variant evaluatedValue;
try
{
try {
Eval e;
e.addSource (domSource);
e.evaluateInfixExpression (value, evaluatedValue);
e.addSource(domSource);
e.evaluateInfixExpression(value, evaluatedValue);
}
catch (...)
{
evaluatedValue = Variant (value);
catch (...) {
evaluatedValue = Variant(value);
}
std::string label = " MODIFICATION ";
Context::getContext ().debug (label + _name + " <-- '" + evaluatedValue.get_string () + "' <-- '" + value + '\'');
Context::getContext().debug(label + _name + " <-- '" + evaluatedValue.get_string() + "' <-- '" +
value + '\'');
// Convert the value of the expression to the correct type if needed
switch (evaluatedValue.type ())
{
switch (evaluatedValue.type()) {
// Expected variants - no conversion
case Variant::type_integer:
case Variant::type_real:
@@ -76,16 +69,16 @@ void ColumnTypeNumeric::modify (Task& task, const std::string& value)
// Convertible variants - convert to int
case Variant::type_date:
case Variant::type_duration:
evaluatedValue.cast (Variant::type_integer);
evaluatedValue.cast(Variant::type_integer);
break;
// Non-convertible variants
case Variant::type_string:
throw format ("The value '{1}' is not a valid numeric value.", evaluatedValue.get_string ());
throw format("The value '{1}' is not a valid numeric value.", evaluatedValue.get_string());
default:
throw format ("Unexpected variant type: '{1}'", evaluatedValue.type ());
throw format("Unexpected variant type: '{1}'", evaluatedValue.type());
}
task.set (_name, evaluatedValue);
task.set(_name, evaluatedValue);
}
////////////////////////////////////////////////////////////////////////////////

View File

@@ -27,16 +27,16 @@
#ifndef INCLUDED_COLTYPENUMERIC
#define INCLUDED_COLTYPENUMERIC
#include <string>
#include <Column.h>
#include <Task.h>
class ColumnTypeNumeric : public Column
{
public:
ColumnTypeNumeric ();
virtual bool validate (const std::string&) const;
virtual void modify (Task&, const std::string&);
#include <string>
class ColumnTypeNumeric : public Column {
public:
ColumnTypeNumeric();
virtual bool validate(const std::string&) const;
virtual void modify(Task&, const std::string&);
};
#endif

View File

@@ -30,64 +30,51 @@
#include <ColTypeString.h>
#include <Context.h>
#include <Eval.h>
#include <Variant.h>
#include <Filter.h>
#include <Variant.h>
#include <format.h>
#define STRING_INVALID_MOD "The '{1}' attribute does not allow a value of '{2}'."
#define STRING_INVALID_MOD "The '{1}' attribute does not allow a value of '{2}'."
////////////////////////////////////////////////////////////////////////////////
ColumnTypeString::ColumnTypeString ()
{
_type = "string";
ColumnTypeString::ColumnTypeString() { _type = "string"; }
////////////////////////////////////////////////////////////////////////////////
bool ColumnTypeString::validate(const std::string& input) const {
return input.length() ? true : false;
}
////////////////////////////////////////////////////////////////////////////////
bool ColumnTypeString::validate (const std::string& input) const
{
return input.length () ? true : false;
}
////////////////////////////////////////////////////////////////////////////////
void ColumnTypeString::modify (Task& task, const std::string& value)
{
void ColumnTypeString::modify(Task& task, const std::string& value) {
std::string label = " MODIFICATION ";
// Only if it's a DOM ref, eval it first.
Lexer lexer (value);
Lexer lexer(value);
std::string domRef;
Lexer::Type type;
if (lexer.token (domRef, type) &&
type == Lexer::Type::dom &&
if (lexer.token(domRef, type) && type == Lexer::Type::dom &&
// Ensure 'value' contains only the DOM reference and no other tokens
// The lexer.token returns false for end-of-string.
// This works as long as all the DOM references we should support consist
// only of a single token.
lexer.token (domRef, type) == false)
{
lexer.token(domRef, type) == false) {
Eval e;
e.addSource (domSource);
e.addSource(domSource);
Variant v;
e.evaluateInfixExpression (value, v);
std::string strValue = (std::string) v;
if (validate (strValue))
{
task.set (_name, strValue);
Context::getContext ().debug (label + _name + " <-- '" + strValue + "' <-- '" + value + '\'');
}
else
throw format (STRING_INVALID_MOD, _name, value);
}
else
{
if (validate (value))
{
task.set (_name, value);
Context::getContext ().debug (label + _name + " <-- '" + value + '\'');
}
else
throw format (STRING_INVALID_MOD, _name, value);
e.evaluateInfixExpression(value, v);
std::string strValue = (std::string)v;
if (validate(strValue)) {
task.set(_name, strValue);
Context::getContext().debug(label + _name + " <-- '" + strValue + "' <-- '" + value + '\'');
} else
throw format(STRING_INVALID_MOD, _name, value);
} else {
if (validate(value)) {
task.set(_name, value);
Context::getContext().debug(label + _name + " <-- '" + value + '\'');
} else
throw format(STRING_INVALID_MOD, _name, value);
}
}

View File

@@ -27,16 +27,16 @@
#ifndef INCLUDED_COLTYPESTRING
#define INCLUDED_COLTYPESTRING
#include <string>
#include <Column.h>
#include <Task.h>
class ColumnTypeString : public Column
{
public:
ColumnTypeString ();
virtual bool validate (const std::string&) const;
virtual void modify (Task&, const std::string&);
#include <string>
class ColumnTypeString : public Column {
public:
ColumnTypeString();
virtual bool validate(const std::string&) const;
virtual void modify(Task&, const std::string&);
};
#endif

View File

@@ -31,34 +31,30 @@
#include <Context.h>
#include <Datetime.h>
#include <Duration.h>
#include <shared.h>
#include <format.h>
#include <utf8.h>
#include <shared.h>
#include <stdlib.h>
#include <utf8.h>
////////////////////////////////////////////////////////////////////////////////
ColumnUDAString::ColumnUDAString ()
{
_name = "<uda>"; // Gets overwritten at runtime.
_style = "default";
_label = "";
ColumnUDAString::ColumnUDAString() {
_name = "<uda>"; // Gets overwritten at runtime.
_style = "default";
_label = "";
_modifiable = true;
_uda = true;
_hyphenate = true;
_styles = {_style, "indicator"};
_uda = true;
_hyphenate = true;
_styles = {_style, "indicator"};
}
////////////////////////////////////////////////////////////////////////////////
bool ColumnUDAString::validate (const std::string& value) const
{
bool ColumnUDAString::validate(const std::string& value) const {
// No restrictions.
if (_values.size () == 0)
return true;
if (_values.size() == 0) return true;
// Look for exact match value.
for (auto& i : _values)
if (i == value)
return true;
if (i == value) return true;
// Fail if not found.
return false;
@@ -67,83 +63,61 @@ bool ColumnUDAString::validate (const std::string& value) const
////////////////////////////////////////////////////////////////////////////////
// Set the minimum and maximum widths for the value.
//
void ColumnUDAString::measure (Task& task, unsigned int& minimum, unsigned int& maximum)
{
void ColumnUDAString::measure(Task& task, unsigned int& minimum, unsigned int& maximum) {
minimum = maximum = 0;
if (task.has (_name))
{
if (_style == "default")
{
std::string value = task.get (_name);
if (value != "")
{
auto stripped = Color::strip (value);
maximum = longestLine (stripped);
minimum = longestWord (stripped);
if (task.has(_name)) {
if (_style == "default") {
std::string value = task.get(_name);
if (value != "") {
auto stripped = Color::strip(value);
maximum = longestLine(stripped);
minimum = longestWord(stripped);
}
}
else if (_style == "indicator")
{
auto indicator = Context::getContext ().config.get ("uda." + _name + ".indicator");
if (indicator == "")
indicator = "U";
} else if (_style == "indicator") {
auto indicator = Context::getContext().config.get("uda." + _name + ".indicator");
if (indicator == "") indicator = "U";
minimum = maximum = utf8_width (indicator);
minimum = maximum = utf8_width(indicator);
}
}
}
////////////////////////////////////////////////////////////////////////////////
void ColumnUDAString::render (
std::vector <std::string>& lines,
Task& task,
int width,
Color& color)
{
if (task.has (_name))
{
if (_style == "default")
{
std::string value = task.get (_name);
std::vector <std::string> raw;
wrapText (raw, value, width, _hyphenate);
void ColumnUDAString::render(std::vector<std::string>& lines, Task& task, int width, Color& color) {
if (task.has(_name)) {
if (_style == "default") {
std::string value = task.get(_name);
std::vector<std::string> raw;
wrapText(raw, value, width, _hyphenate);
for (auto& i : raw)
renderStringLeft (lines, width, color, i);
}
else if (_style == "indicator")
{
auto indicator = Context::getContext ().config.get ("uda." + _name + ".indicator");
if (indicator == "")
indicator = "U";
for (auto& i : raw) renderStringLeft(lines, width, color, i);
} else if (_style == "indicator") {
auto indicator = Context::getContext().config.get("uda." + _name + ".indicator");
if (indicator == "") indicator = "U";
renderStringRight (lines, width, color, indicator);
renderStringRight(lines, width, color, indicator);
}
}
}
////////////////////////////////////////////////////////////////////////////////
ColumnUDANumeric::ColumnUDANumeric ()
{
_name = "<uda>";
_type = "numeric";
_style = "default";
_label = "";
_uda = true;
_styles = {_style, "indicator"};
ColumnUDANumeric::ColumnUDANumeric() {
_name = "<uda>";
_type = "numeric";
_style = "default";
_label = "";
_uda = true;
_styles = {_style, "indicator"};
}
////////////////////////////////////////////////////////////////////////////////
bool ColumnUDANumeric::validate (const std::string& value) const
{
bool ColumnUDANumeric::validate(const std::string& value) const {
// No restrictions.
if (_values.size () == 0)
return true;
if (_values.size() == 0) return true;
// Look for exact match value.
for (auto& i : _values)
if (i == value)
return true;
if (i == value) return true;
// Fail if not found.
return false;
@@ -152,75 +126,55 @@ bool ColumnUDANumeric::validate (const std::string& value) const
////////////////////////////////////////////////////////////////////////////////
// Set the minimum and maximum widths for the value.
//
void ColumnUDANumeric::measure (Task& task, unsigned int& minimum, unsigned int& maximum)
{
void ColumnUDANumeric::measure(Task& task, unsigned int& minimum, unsigned int& maximum) {
minimum = maximum = 0;
if (task.has (_name))
{
if (_style == "default")
{
auto value = task.get (_name);
if (value != "")
minimum = maximum = value.length ();
}
else if (_style == "indicator")
{
auto indicator = Context::getContext ().config.get ("uda." + _name + ".indicator");
if (indicator == "")
indicator = "U";
if (task.has(_name)) {
if (_style == "default") {
auto value = task.get(_name);
if (value != "") minimum = maximum = value.length();
} else if (_style == "indicator") {
auto indicator = Context::getContext().config.get("uda." + _name + ".indicator");
if (indicator == "") indicator = "U";
minimum = maximum = utf8_width (indicator);
minimum = maximum = utf8_width(indicator);
}
}
}
////////////////////////////////////////////////////////////////////////////////
void ColumnUDANumeric::render (
std::vector <std::string>& lines,
Task& task,
int width,
Color& color)
{
if (task.has (_name))
{
if (_style == "default")
{
auto value = task.get (_name);
renderStringRight (lines, width, color, value);
}
else if (_style == "indicator")
{
auto indicator = Context::getContext ().config.get ("uda." + _name + ".indicator");
if (indicator == "")
indicator = "U";
void ColumnUDANumeric::render(std::vector<std::string>& lines, Task& task, int width,
Color& color) {
if (task.has(_name)) {
if (_style == "default") {
auto value = task.get(_name);
renderStringRight(lines, width, color, value);
} else if (_style == "indicator") {
auto indicator = Context::getContext().config.get("uda." + _name + ".indicator");
if (indicator == "") indicator = "U";
renderStringRight (lines, width, color, indicator);
renderStringRight(lines, width, color, indicator);
}
}
}
////////////////////////////////////////////////////////////////////////////////
ColumnUDADate::ColumnUDADate ()
{
_name = "<uda>";
_type = "date";
_style = "default";
_label = "";
_uda = true;
_styles = {_style, "indicator"};
ColumnUDADate::ColumnUDADate() {
_name = "<uda>";
_type = "date";
_style = "default";
_label = "";
_uda = true;
_styles = {_style, "indicator"};
}
////////////////////////////////////////////////////////////////////////////////
bool ColumnUDADate::validate (const std::string& value) const
{
bool ColumnUDADate::validate(const std::string& value) const {
// No restrictions.
if (_values.size () == 0)
return true;
if (_values.size() == 0) return true;
// Look for exact match value.
for (auto& i : _values)
if (i == value)
return true;
if (i == value) return true;
// Fail if not found.
return false;
@@ -229,101 +183,77 @@ bool ColumnUDADate::validate (const std::string& value) const
////////////////////////////////////////////////////////////////////////////////
// Set the minimum and maximum widths for the value.
//
void ColumnUDADate::measure (Task& task, unsigned int& minimum, unsigned int& maximum)
{
void ColumnUDADate::measure(Task& task, unsigned int& minimum, unsigned int& maximum) {
minimum = maximum = 0;
if (task.has (_name))
{
if (_style == "default")
{
auto value = task.get (_name);
if (value != "")
{
if (task.has(_name)) {
if (_style == "default") {
auto value = task.get(_name);
if (value != "") {
// Determine the output date format, which uses a hierarchy of definitions.
// rc.report.<report>.dateformat
// rc.dateformat.report
// rc.dateformat
Datetime date (strtoll (value.c_str (), nullptr, 10));
auto format = Context::getContext ().config.get ("report." + _report + ".dateformat");
if (format == "")
format = Context::getContext ().config.get ("dateformat.report");
if (format == "")
format = Context::getContext ().config.get ("dateformat");
Datetime date(strtoll(value.c_str(), nullptr, 10));
auto format = Context::getContext().config.get("report." + _report + ".dateformat");
if (format == "") format = Context::getContext().config.get("dateformat.report");
if (format == "") format = Context::getContext().config.get("dateformat");
minimum = maximum = Datetime::length (format);
minimum = maximum = Datetime::length(format);
}
}
else if (_style == "indicator")
{
auto indicator = Context::getContext ().config.get ("uda." + _name + ".indicator");
if (indicator == "")
indicator = "U";
} else if (_style == "indicator") {
auto indicator = Context::getContext().config.get("uda." + _name + ".indicator");
if (indicator == "") indicator = "U";
minimum = maximum = utf8_width (indicator);
minimum = maximum = utf8_width(indicator);
}
}
}
////////////////////////////////////////////////////////////////////////////////
void ColumnUDADate::render (
std::vector <std::string>& lines,
Task& task,
int width,
Color& color)
{
if (task.has (_name))
{
if (_style == "default")
{
auto value = task.get (_name);
void ColumnUDADate::render(std::vector<std::string>& lines, Task& task, int width, Color& color) {
if (task.has(_name)) {
if (_style == "default") {
auto value = task.get(_name);
// Determine the output date format, which uses a hierarchy of definitions.
// rc.report.<report>.dateformat
// rc.dateformat.report
// rc.dateformat.
auto format = Context::getContext ().config.get ("report." + _report + ".dateformat");
if (format == "")
{
format = Context::getContext ().config.get ("dateformat.report");
if (format == "")
format = Context::getContext ().config.get ("dateformat");
auto format = Context::getContext().config.get("report." + _report + ".dateformat");
if (format == "") {
format = Context::getContext().config.get("dateformat.report");
if (format == "") format = Context::getContext().config.get("dateformat");
}
renderStringLeft (lines, width, color, Datetime (strtoll (value.c_str (), nullptr, 10)).toString (format));
}
else if (_style == "indicator")
{
auto indicator = Context::getContext ().config.get ("uda." + _name + ".indicator");
if (indicator == "")
indicator = "U";
renderStringLeft(lines, width, color,
Datetime(strtoll(value.c_str(), nullptr, 10)).toString(format));
} else if (_style == "indicator") {
auto indicator = Context::getContext().config.get("uda." + _name + ".indicator");
if (indicator == "") indicator = "U";
renderStringRight (lines, width, color, indicator);
renderStringRight(lines, width, color, indicator);
}
}
}
////////////////////////////////////////////////////////////////////////////////
ColumnUDADuration::ColumnUDADuration ()
{
_name = "<uda>";
_type = "duration";
_style = "default";
_label = "";
_uda = true;
_styles = {_style, "indicator"};
ColumnUDADuration::ColumnUDADuration() {
_name = "<uda>";
_type = "duration";
_style = "default";
_label = "";
_uda = true;
_styles = {_style, "indicator"};
}
////////////////////////////////////////////////////////////////////////////////
bool ColumnUDADuration::validate (const std::string& value) const
{
bool ColumnUDADuration::validate(const std::string& value) const {
// No restrictions.
if (_values.size () == 0)
return true;
if (_values.size() == 0) return true;
// Look for exact match value.
for (auto& i : _values)
if (i == value)
return true;
if (i == value) return true;
// Fail if not found.
return false;
@@ -332,54 +262,36 @@ bool ColumnUDADuration::validate (const std::string& value) const
////////////////////////////////////////////////////////////////////////////////
// Set the minimum and maximum widths for the value.
//
void ColumnUDADuration::measure (Task& task, unsigned int& minimum, unsigned int& maximum)
{
void ColumnUDADuration::measure(Task& task, unsigned int& minimum, unsigned int& maximum) {
minimum = maximum = 0;
if (task.has (_name))
{
if (_style == "default")
{
auto value = task.get (_name);
if (value != "")
minimum = maximum = Duration (value).formatISO ().length ();
}
else if (_style == "indicator")
{
if (task.has (_name))
{
auto indicator = Context::getContext ().config.get ("uda." + _name + ".indicator");
if (indicator == "")
indicator = "U";
if (task.has(_name)) {
if (_style == "default") {
auto value = task.get(_name);
if (value != "") minimum = maximum = Duration(value).formatISO().length();
} else if (_style == "indicator") {
if (task.has(_name)) {
auto indicator = Context::getContext().config.get("uda." + _name + ".indicator");
if (indicator == "") indicator = "U";
minimum = maximum = utf8_width (indicator);
}
else
minimum = maximum = utf8_width(indicator);
} else
minimum = maximum = 0;
}
}
}
////////////////////////////////////////////////////////////////////////////////
void ColumnUDADuration::render (
std::vector <std::string>& lines,
Task& task,
int width,
Color& color)
{
if (task.has (_name))
{
if (_style == "default")
{
auto value = task.get (_name);
renderStringRight (lines, width, color, Duration (value).formatISO ());
}
else if (_style == "indicator")
{
auto indicator = Context::getContext ().config.get ("uda." + _name + ".indicator");
if (indicator == "")
indicator = "U";
void ColumnUDADuration::render(std::vector<std::string>& lines, Task& task, int width,
Color& color) {
if (task.has(_name)) {
if (_style == "default") {
auto value = task.get(_name);
renderStringRight(lines, width, color, Duration(value).formatISO());
} else if (_style == "indicator") {
auto indicator = Context::getContext().config.get("uda." + _name + ".indicator");
if (indicator == "") indicator = "U";
renderStringRight (lines, width, color, indicator);
renderStringRight(lines, width, color, indicator);
}
}
}

View File

@@ -27,64 +27,60 @@
#ifndef INCLUDED_COLUDA
#define INCLUDED_COLUDA
#include <ColTypeString.h>
#include <ColTypeNumeric.h>
#include <ColTypeDate.h>
#include <ColTypeDuration.h>
#include <ColTypeNumeric.h>
#include <ColTypeString.h>
////////////////////////////////////////////////////////////////////////////////
class ColumnUDAString : public ColumnTypeString
{
public:
ColumnUDAString ();
bool validate (const std::string&) const;
void measure (Task&, unsigned int&, unsigned int&);
void render (std::vector <std::string>&, Task&, int, Color&);
class ColumnUDAString : public ColumnTypeString {
public:
ColumnUDAString();
bool validate(const std::string&) const;
void measure(Task&, unsigned int&, unsigned int&);
void render(std::vector<std::string>&, Task&, int, Color&);
public:
std::vector <std::string> _values;
public:
std::vector<std::string> _values;
private:
private:
bool _hyphenate;
};
////////////////////////////////////////////////////////////////////////////////
class ColumnUDANumeric : public ColumnTypeNumeric
{
public:
ColumnUDANumeric ();
bool validate (const std::string&) const;
void measure (Task&, unsigned int&, unsigned int&);
void render (std::vector <std::string>&, Task&, int, Color&);
class ColumnUDANumeric : public ColumnTypeNumeric {
public:
ColumnUDANumeric();
bool validate(const std::string&) const;
void measure(Task&, unsigned int&, unsigned int&);
void render(std::vector<std::string>&, Task&, int, Color&);
public:
std::vector <std::string> _values;
public:
std::vector<std::string> _values;
};
////////////////////////////////////////////////////////////////////////////////
class ColumnUDADate : public ColumnTypeDate
{
public:
ColumnUDADate ();
bool validate (const std::string&) const;
void measure (Task&, unsigned int&, unsigned int&);
void render (std::vector <std::string>&, Task&, int, Color&);
class ColumnUDADate : public ColumnTypeDate {
public:
ColumnUDADate();
bool validate(const std::string&) const;
void measure(Task&, unsigned int&, unsigned int&);
void render(std::vector<std::string>&, Task&, int, Color&);
public:
std::vector <std::string> _values;
public:
std::vector<std::string> _values;
};
////////////////////////////////////////////////////////////////////////////////
class ColumnUDADuration : public ColumnTypeDuration
{
public:
ColumnUDADuration ();
bool validate (const std::string&) const;
void measure (Task&, unsigned int&, unsigned int&);
void render (std::vector <std::string>&, Task&, int, Color&);
class ColumnUDADuration : public ColumnTypeDuration {
public:
ColumnUDADuration();
bool validate(const std::string&) const;
void measure(Task&, unsigned int&, unsigned int&);
void render(std::vector<std::string>&, Task&, int, Color&);
public:
std::vector <std::string> _values;
public:
std::vector<std::string> _values;
};
#endif

View File

@@ -31,43 +31,37 @@
#include <format.h>
////////////////////////////////////////////////////////////////////////////////
ColumnUUID::ColumnUUID ()
{
_name = "uuid";
_style = "long";
_label = "UUID";
ColumnUUID::ColumnUUID() {
_name = "uuid";
_style = "long";
_label = "UUID";
_modifiable = false;
_styles = {"long", "short"};
_examples = {"f30cb9c3-3fc0-483f-bfb2-3bf134f00694", "f30cb9c3"};
_styles = {"long", "short"};
_examples = {"f30cb9c3-3fc0-483f-bfb2-3bf134f00694", "f30cb9c3"};
}
////////////////////////////////////////////////////////////////////////////////
// Set the minimum and maximum widths for the value.
void ColumnUUID::measure (Task&, unsigned int& minimum, unsigned int& maximum)
{
void ColumnUUID::measure(Task&, unsigned int& minimum, unsigned int& maximum) {
// Mandatory attribute, no need to check the value.
if (_style == "default" || _style == "long") minimum = maximum = 36;
else if (_style == "short") minimum = maximum = 8;
if (_style == "default" || _style == "long")
minimum = maximum = 36;
else if (_style == "short")
minimum = maximum = 8;
}
////////////////////////////////////////////////////////////////////////////////
void ColumnUUID::render (
std::vector <std::string>& lines,
Task& task,
int width,
Color& color)
{
void ColumnUUID::render(std::vector<std::string>& lines, Task& task, int width, Color& color) {
// No need to check the presence of UUID - all tasks have one.
// f30cb9c3-3fc0-483f-bfb2-3bf134f00694 default
// f30cb9c3 short
if (_style == "default" ||
_style == "long")
renderStringLeft (lines, width, color, task.get (_name));
if (_style == "default" || _style == "long")
renderStringLeft(lines, width, color, task.get(_name));
else if (_style == "short")
renderStringLeft (lines, width, color, task.get (_name).substr (0, 8));
renderStringLeft(lines, width, color, task.get(_name).substr(0, 8));
}
////////////////////////////////////////////////////////////////////////////////

View File

@@ -29,14 +29,13 @@
#include <ColTypeString.h>
class ColumnUUID : public ColumnTypeString
{
public:
ColumnUUID ();
void measure (Task&, unsigned int&, unsigned int&);
void render (std::vector <std::string>&, Task&, int, Color&);
class ColumnUUID : public ColumnTypeString {
public:
ColumnUUID();
void measure(Task&, unsigned int&, unsigned int&);
void render(std::vector<std::string>&, Task&, int, Color&);
private:
private:
};
#endif

View File

@@ -30,9 +30,8 @@
#include <ColUntil.h>
////////////////////////////////////////////////////////////////////////////////
ColumnUntil::ColumnUntil ()
{
_name = "until";
ColumnUntil::ColumnUntil() {
_name = "until";
_label = "Until";
}

View File

@@ -29,10 +29,9 @@
#include <ColTypeDate.h>
class ColumnUntil : public ColumnTypeDate
{
public:
ColumnUntil ();
class ColumnUntil : public ColumnTypeDate {
public:
ColumnUntil();
};
#endif

View File

@@ -31,39 +31,32 @@
#include <format.h>
////////////////////////////////////////////////////////////////////////////////
ColumnUrgency::ColumnUrgency ()
{
_name = "urgency";
_style = "real";
_label = "Urgency";
ColumnUrgency::ColumnUrgency() {
_name = "urgency";
_style = "real";
_label = "Urgency";
_modifiable = false;
_styles = {"real", "integer"};
_examples = {"4.6", "4"};
_styles = {"real", "integer"};
_examples = {"4.6", "4"};
}
////////////////////////////////////////////////////////////////////////////////
// Set the minimum and maximum widths for the value.
void ColumnUrgency::measure (Task& task, unsigned int& minimum, unsigned int& maximum)
{
void ColumnUrgency::measure(Task& task, unsigned int& minimum, unsigned int& maximum) {
if (_style == "default" || _style == "real")
minimum = maximum = format (task.urgency (), 4, 3).length ();
minimum = maximum = format(task.urgency(), 4, 3).length();
else if (_style == "integer")
minimum = maximum = format ((int)task.urgency ()).length ();
minimum = maximum = format((int)task.urgency()).length();
}
////////////////////////////////////////////////////////////////////////////////
void ColumnUrgency::render (
std::vector <std::string>& lines,
Task& task,
int width,
Color& color)
{
void ColumnUrgency::render(std::vector<std::string>& lines, Task& task, int width, Color& color) {
if (_style == "default" || _style == "real")
renderDouble (lines, width, color, task.urgency ());
renderDouble(lines, width, color, task.urgency());
else if (_style == "integer")
renderInteger (lines, width, color, static_cast <int> (task.urgency ()));
renderInteger(lines, width, color, static_cast<int>(task.urgency()));
}
////////////////////////////////////////////////////////////////////////////////

View File

@@ -29,14 +29,13 @@
#include <ColTypeNumeric.h>
class ColumnUrgency : public ColumnTypeNumeric
{
public:
ColumnUrgency ();
void measure (Task&, unsigned int&, unsigned int&);
void render (std::vector <std::string>&, Task&, int, Color&);
class ColumnUrgency : public ColumnTypeNumeric {
public:
ColumnUrgency();
void measure(Task&, unsigned int&, unsigned int&);
void render(std::vector<std::string>&, Task&, int, Color&);
private:
private:
};
#endif

View File

@@ -30,9 +30,8 @@
#include <ColWait.h>
////////////////////////////////////////////////////////////////////////////////
ColumnWait::ColumnWait ()
{
_name = "wait";
ColumnWait::ColumnWait() {
_name = "wait";
_label = "Wait";
}

View File

@@ -29,10 +29,9 @@
#include <ColTypeDate.h>
class ColumnWait : public ColumnTypeDate
{
public:
ColumnWait ();
class ColumnWait : public ColumnTypeDate {
public:
ColumnWait();
};
#endif

View File

@@ -27,10 +27,6 @@
#include <cmake.h>
// cmake.h include header must come first
#include <Column.h>
#include <algorithm>
#include <set>
#include <Context.h>
#include <ColDepends.h>
#include <ColDescription.h>
#include <ColDue.h>
@@ -43,294 +39,283 @@
#include <ColModified.h>
#include <ColParent.h>
#include <ColProject.h>
#include <ColRecur.h>
#include <ColRType.h>
#include <ColRecur.h>
#include <ColScheduled.h>
#include <ColStart.h>
#include <ColStatus.h>
#include <ColTags.h>
#include <ColTemplate.h>
#include <ColUDA.h>
#include <ColUUID.h>
#include <ColUntil.h>
#include <ColUrgency.h>
#include <ColUUID.h>
#include <ColUDA.h>
#include <ColWait.h>
#include <shared.h>
#include <Column.h>
#include <Context.h>
#include <format.h>
#include <shared.h>
#include <algorithm>
#include <set>
////////////////////////////////////////////////////////////////////////////////
// Supports the complete column definition:
//
// <type>[.<format>]
//
Column* Column::factory (const std::string& name, const std::string& report)
{
Column* Column::factory(const std::string& name, const std::string& report) {
// Decompose name into type and style.
auto dot = name.find ('.');
auto dot = name.find('.');
std::string column_name;
std::string column_style;
if (dot != std::string::npos)
{
column_name = name.substr (0, dot);
column_style = name.substr (dot + 1);
}
else
{
if (dot != std::string::npos) {
column_name = name.substr(0, dot);
column_style = name.substr(dot + 1);
} else {
column_name = name;
column_style = "default";
}
Column* c;
if (column_name == "depends") c = new ColumnDepends ();
else if (column_name == "description") c = new ColumnDescription ();
else if (column_name == "due") c = new ColumnDue ();
else if (column_name == "end") c = new ColumnEnd ();
else if (column_name == "entry") c = new ColumnEntry ();
else if (column_name == "id") c = new ColumnID ();
else if (column_name == "imask") c = new ColumnIMask ();
else if (column_name == "last") c = new ColumnLast ();
else if (column_name == "mask") c = new ColumnMask ();
else if (column_name == "modified") c = new ColumnModified ();
else if (column_name == "parent") c = new ColumnParent ();
else if (column_name == "project") c = new ColumnProject ();
else if (column_name == "recur") c = new ColumnRecur ();
else if (column_name == "rtype") c = new ColumnRType ();
else if (column_name == "scheduled") c = new ColumnScheduled ();
else if (column_name == "start") c = new ColumnStart ();
else if (column_name == "status") c = new ColumnStatus ();
else if (column_name == "tags") c = new ColumnTags ();
else if (column_name == "template") c = new ColumnTemplate ();
else if (column_name == "until") c = new ColumnUntil ();
else if (column_name == "urgency") c = new ColumnUrgency ();
else if (column_name == "uuid") c = new ColumnUUID ();
else if (column_name == "wait") c = new ColumnWait ();
if (column_name == "depends")
c = new ColumnDepends();
else if (column_name == "description")
c = new ColumnDescription();
else if (column_name == "due")
c = new ColumnDue();
else if (column_name == "end")
c = new ColumnEnd();
else if (column_name == "entry")
c = new ColumnEntry();
else if (column_name == "id")
c = new ColumnID();
else if (column_name == "imask")
c = new ColumnIMask();
else if (column_name == "last")
c = new ColumnLast();
else if (column_name == "mask")
c = new ColumnMask();
else if (column_name == "modified")
c = new ColumnModified();
else if (column_name == "parent")
c = new ColumnParent();
else if (column_name == "project")
c = new ColumnProject();
else if (column_name == "recur")
c = new ColumnRecur();
else if (column_name == "rtype")
c = new ColumnRType();
else if (column_name == "scheduled")
c = new ColumnScheduled();
else if (column_name == "start")
c = new ColumnStart();
else if (column_name == "status")
c = new ColumnStatus();
else if (column_name == "tags")
c = new ColumnTags();
else if (column_name == "template")
c = new ColumnTemplate();
else if (column_name == "until")
c = new ColumnUntil();
else if (column_name == "urgency")
c = new ColumnUrgency();
else if (column_name == "uuid")
c = new ColumnUUID();
else if (column_name == "wait")
c = new ColumnWait();
// UDA.
else if (Context::getContext ().config.has ("uda." + column_name + ".type"))
c = Column::uda (column_name);
else if (Context::getContext().config.has("uda." + column_name + ".type"))
c = Column::uda(column_name);
else
throw format ("Unrecognized column name '{1}'.", column_name);
throw format("Unrecognized column name '{1}'.", column_name);
c->setReport (report);
c->setStyle (column_style);
c->setReport(report);
c->setStyle(column_style);
return c;
}
////////////////////////////////////////////////////////////////////////////////
// Bulk column instantiation.
void Column::factory (std::map <std::string, Column*>& all)
{
void Column::factory(std::map<std::string, Column*>& all) {
Column* c;
c = new ColumnDepends (); all[c->_name] = c;
c = new ColumnDescription (); all[c->_name] = c;
c = new ColumnDue (); all[c->_name] = c;
c = new ColumnEnd (); all[c->_name] = c;
c = new ColumnEntry (); all[c->_name] = c;
c = new ColumnID (); all[c->_name] = c;
c = new ColumnIMask (); all[c->_name] = c;
c = new ColumnLast (); all[c->_name] = c;
c = new ColumnMask (); all[c->_name] = c;
c = new ColumnModified (); all[c->_name] = c;
c = new ColumnParent (); all[c->_name] = c;
c = new ColumnProject (); all[c->_name] = c;
c = new ColumnRecur (); all[c->_name] = c;
c = new ColumnRType (); all[c->_name] = c;
c = new ColumnScheduled (); all[c->_name] = c;
c = new ColumnStart (); all[c->_name] = c;
c = new ColumnStatus (); all[c->_name] = c;
c = new ColumnTags (); all[c->_name] = c;
c = new ColumnTemplate (); all[c->_name] = c;
c = new ColumnUntil (); all[c->_name] = c;
c = new ColumnUrgency (); all[c->_name] = c;
c = new ColumnUUID (); all[c->_name] = c;
c = new ColumnWait (); all[c->_name] = c;
c = new ColumnDepends();
all[c->_name] = c;
c = new ColumnDescription();
all[c->_name] = c;
c = new ColumnDue();
all[c->_name] = c;
c = new ColumnEnd();
all[c->_name] = c;
c = new ColumnEntry();
all[c->_name] = c;
c = new ColumnID();
all[c->_name] = c;
c = new ColumnIMask();
all[c->_name] = c;
c = new ColumnLast();
all[c->_name] = c;
c = new ColumnMask();
all[c->_name] = c;
c = new ColumnModified();
all[c->_name] = c;
c = new ColumnParent();
all[c->_name] = c;
c = new ColumnProject();
all[c->_name] = c;
c = new ColumnRecur();
all[c->_name] = c;
c = new ColumnRType();
all[c->_name] = c;
c = new ColumnScheduled();
all[c->_name] = c;
c = new ColumnStart();
all[c->_name] = c;
c = new ColumnStatus();
all[c->_name] = c;
c = new ColumnTags();
all[c->_name] = c;
c = new ColumnTemplate();
all[c->_name] = c;
c = new ColumnUntil();
all[c->_name] = c;
c = new ColumnUrgency();
all[c->_name] = c;
c = new ColumnUUID();
all[c->_name] = c;
c = new ColumnWait();
all[c->_name] = c;
Column::uda (all);
Column::uda(all);
}
////////////////////////////////////////////////////////////////////////////////
void Column::uda (std::map <std::string, Column*>& all)
{
void Column::uda(std::map<std::string, Column*>& all) {
// For each UDA, instantiate and initialize ColumnUDA.
std::set <std::string> udas;
std::set<std::string> udas;
for (const auto& i : Context::getContext ().config)
{
if (i.first.substr (0, 4) == "uda.")
{
std::string::size_type period = 4; // One byte after the first '.'.
for (const auto& i : Context::getContext().config) {
if (i.first.substr(0, 4) == "uda.") {
std::string::size_type period = 4; // One byte after the first '.'.
if ((period = i.first.find ('.', period)) != std::string::npos)
udas.insert (i.first.substr (4, period - 4));
if ((period = i.first.find('.', period)) != std::string::npos)
udas.insert(i.first.substr(4, period - 4));
}
}
for (const auto& uda : udas)
{
if (all.find (uda) != all.end ())
throw format ("The UDA named '{1}' is the same as a core attribute, and is not permitted.", uda);
for (const auto& uda : udas) {
if (all.find(uda) != all.end())
throw format("The UDA named '{1}' is the same as a core attribute, and is not permitted.",
uda);
Column* c = Column::uda (uda);
if (c)
all[c->_name] = c;
Column* c = Column::uda(uda);
if (c) all[c->_name] = c;
}
}
////////////////////////////////////////////////////////////////////////////////
Column* Column::uda (const std::string& name)
{
auto type = Context::getContext ().config.get ("uda." + name + ".type");
auto label = Context::getContext ().config.get ("uda." + name + ".label");
auto values = Context::getContext ().config.get ("uda." + name + ".values");
Column* Column::uda(const std::string& name) {
auto type = Context::getContext().config.get("uda." + name + ".type");
auto label = Context::getContext().config.get("uda." + name + ".label");
auto values = Context::getContext().config.get("uda." + name + ".values");
if (type == "string")
{
auto c = new ColumnUDAString ();
if (type == "string") {
auto c = new ColumnUDAString();
c->_name = name;
c->_label = label;
if (values != "")
c->_values = split (values, ',');
if (values != "") c->_values = split(values, ',');
return c;
}
else if (type == "numeric")
{
auto c = new ColumnUDANumeric ();
} else if (type == "numeric") {
auto c = new ColumnUDANumeric();
c->_name = name;
c->_label = label;
if (values != "")
c->_values = split (values, ',');
if (values != "") c->_values = split(values, ',');
return c;
}
else if (type == "date")
{
auto c = new ColumnUDADate ();
} else if (type == "date") {
auto c = new ColumnUDADate();
c->_name = name;
c->_label = label;
if (values != "")
c->_values = split (values, ',');
if (values != "") c->_values = split(values, ',');
return c;
}
else if (type == "duration")
{
auto c = new ColumnUDADuration ();
} else if (type == "duration") {
auto c = new ColumnUDADuration();
c->_name = name;
c->_label = label;
if (values != "")
c->_values = split (values, ',');
if (values != "") c->_values = split(values, ',');
return c;
}
else if (type != "")
throw std::string ("User defined attributes may only be of type 'string', 'date', 'duration' or 'numeric'.");
} else if (type != "")
throw std::string(
"User defined attributes may only be of type 'string', 'date', 'duration' or 'numeric'.");
return nullptr;
}
////////////////////////////////////////////////////////////////////////////////
Column::Column ()
: _name ("")
, _type ("string")
, _style ("default")
, _label ("")
, _report ("")
, _modifiable (true)
, _uda (false)
, _fixed_width (false)
{
}
Column::Column()
: _name(""),
_type("string"),
_style("default"),
_label(""),
_report(""),
_modifiable(true),
_uda(false),
_fixed_width(false) {}
////////////////////////////////////////////////////////////////////////////////
void Column::renderHeader (
std::vector <std::string>& lines,
int width,
Color& color)
{
if (Context::getContext ().verbose ("label") &&
_label != "")
{
void Column::renderHeader(std::vector<std::string>& lines, int width, Color& color) {
if (Context::getContext().verbose("label") && _label != "") {
// Create a basic label.
std::string header;
header.reserve (width);
header.reserve(width);
header = _label;
// Create a fungible copy.
Color c = color;
// Now underline the header, or add a dashed line.
if (Context::getContext ().color () &&
Context::getContext ().config.getBoolean ("fontunderline"))
{
c.blend (Color (Color::nocolor, Color::nocolor, true, false, false));
lines.push_back (c.colorize (leftJustify (header, width)));
}
else
{
lines.push_back (c.colorize (leftJustify (header, width)));
lines.push_back (c.colorize (std::string (width, '-')));
if (Context::getContext().color() && Context::getContext().config.getBoolean("fontunderline")) {
c.blend(Color(Color::nocolor, Color::nocolor, true, false, false));
lines.push_back(c.colorize(leftJustify(header, width)));
} else {
lines.push_back(c.colorize(leftJustify(header, width)));
lines.push_back(c.colorize(std::string(width, '-')));
}
}
}
////////////////////////////////////////////////////////////////////////////////
void Column::setStyle (const std::string& style)
{
if (style != "default" &&
std::find (_styles.begin (), _styles.end (), style) == _styles.end ())
throw format ("Unrecognized column format '{1}.{2}'", _name, style);
void Column::setStyle(const std::string& style) {
if (style != "default" && std::find(_styles.begin(), _styles.end(), style) == _styles.end())
throw format("Unrecognized column format '{1}.{2}'", _name, style);
_style = style;
}
////////////////////////////////////////////////////////////////////////////////
// All integer values are right-justified.
void Column::renderInteger (
std::vector <std::string>& lines,
int width,
Color& color,
int value)
{
lines.push_back (
color.colorize (
rightJustify (value, width)));
void Column::renderInteger(std::vector<std::string>& lines, int width, Color& color, int value) {
lines.push_back(color.colorize(rightJustify(value, width)));
}
////////////////////////////////////////////////////////////////////////////////
// All floating point values are right-justified.
void Column::renderDouble (
std::vector <std::string>& lines,
int width,
Color& color,
double value)
{
lines.push_back (
color.colorize (
rightJustify (
format (value, 4, 3), width)));
void Column::renderDouble(std::vector<std::string>& lines, int width, Color& color, double value) {
lines.push_back(color.colorize(rightJustify(format(value, 4, 3), width)));
}
////////////////////////////////////////////////////////////////////////////////
void Column::renderStringLeft (
std::vector <std::string>& lines,
int width,
Color& color,
const std::string& value)
{
lines.push_back (
color.colorize (
leftJustify (value, width)));
void Column::renderStringLeft(std::vector<std::string>& lines, int width, Color& color,
const std::string& value) {
lines.push_back(color.colorize(leftJustify(value, width)));
}
////////////////////////////////////////////////////////////////////////////////
void Column::renderStringRight (
std::vector <std::string>& lines,
int width,
Color& color,
const std::string& value)
{
lines.push_back (
color.colorize (
rightJustify (value, width)));
void Column::renderStringRight(std::vector<std::string>& lines, int width, Color& color,
const std::string& value) {
lines.push_back(color.colorize(rightJustify(value, width)));
}
////////////////////////////////////////////////////////////////////////////////

View File

@@ -27,51 +27,51 @@
#ifndef INCLUDED_COLUMN
#define INCLUDED_COLUMN
#include <vector>
#include <string>
#include <Color.h>
#include <Task.h>
class Column
{
public:
static Column* factory (const std::string&, const std::string&);
static void factory (std::map <std::string, Column*>&);
static void uda (std::map <std::string, Column*>&);
static Column* uda (const std::string&);
#include <string>
#include <vector>
Column ();
virtual ~Column () = default;
class Column {
public:
static Column* factory(const std::string&, const std::string&);
static void factory(std::map<std::string, Column*>&);
static void uda(std::map<std::string, Column*>&);
static Column* uda(const std::string&);
const std::string& name () const { return _name; }
const std::string& style () const { return _style; }
const std::string& label () const { return _label; }
const std::string& type () const { return _type; }
bool modifiable () const { return _modifiable; }
bool is_uda () const { return _uda; }
bool is_fixed_width () const { return _fixed_width; }
std::vector <std::string> styles () const { return _styles; }
std::vector <std::string> examples () const { return _examples; }
Column();
virtual ~Column() = default;
virtual void setStyle (const std::string&);
virtual void setLabel (const std::string& value) { _label = value; }
virtual void setReport (const std::string& value) { _report = value; }
const std::string& name() const { return _name; }
const std::string& style() const { return _style; }
const std::string& label() const { return _label; }
const std::string& type() const { return _type; }
bool modifiable() const { return _modifiable; }
bool is_uda() const { return _uda; }
bool is_fixed_width() const { return _fixed_width; }
std::vector<std::string> styles() const { return _styles; }
std::vector<std::string> examples() const { return _examples; }
virtual void measure (const std::string&, unsigned int&, unsigned int&) {};
virtual void measure (Task&, unsigned int&, unsigned int&) {};
virtual void renderHeader (std::vector <std::string>&, int, Color&);
virtual void render (std::vector <std::string>&, const std::string&, int, Color&) {};
virtual void render (std::vector <std::string>&, Task&, int, Color&) {};
virtual bool validate (const std::string&) const {return false;};
virtual void modify (Task&, const std::string&) {};
virtual void setStyle(const std::string&);
virtual void setLabel(const std::string& value) { _label = value; }
virtual void setReport(const std::string& value) { _report = value; }
protected:
void renderInteger (std::vector <std::string>&, int, Color&, int);
void renderDouble (std::vector <std::string>&, int, Color&, double);
void renderStringLeft (std::vector <std::string>&, int, Color&, const std::string&);
void renderStringRight (std::vector <std::string>&, int, Color&, const std::string&);
virtual void measure(const std::string&, unsigned int&, unsigned int&) {};
virtual void measure(Task&, unsigned int&, unsigned int&) {};
virtual void renderHeader(std::vector<std::string>&, int, Color&);
virtual void render(std::vector<std::string>&, const std::string&, int, Color&) {};
virtual void render(std::vector<std::string>&, Task&, int, Color&) {};
virtual bool validate(const std::string&) const { return false; };
virtual void modify(Task&, const std::string&) {};
protected:
protected:
void renderInteger(std::vector<std::string>&, int, Color&, int);
void renderDouble(std::vector<std::string>&, int, Color&, double);
void renderStringLeft(std::vector<std::string>&, int, Color&, const std::string&);
void renderStringRight(std::vector<std::string>&, int, Color&, const std::string&);
protected:
std::string _name;
std::string _type;
std::string _style;
@@ -80,8 +80,8 @@ protected:
bool _modifiable;
bool _uda;
bool _fixed_width;
std::vector <std::string> _styles;
std::vector <std::string> _examples;
std::vector<std::string> _styles;
std::vector<std::string> _examples;
};
#endif

View File

@@ -33,61 +33,55 @@
#include <main.h>
////////////////////////////////////////////////////////////////////////////////
CmdAdd::CmdAdd ()
{
_keyword = "add";
_usage = "task add <mods>";
_description = "Adds a new task";
_read_only = false;
_displays_id = false;
_needs_gc = false;
_uses_context = true;
_accepts_filter = false;
CmdAdd::CmdAdd() {
_keyword = "add";
_usage = "task add <mods>";
_description = "Adds a new task";
_read_only = false;
_displays_id = false;
_needs_gc = false;
_uses_context = true;
_accepts_filter = false;
_accepts_modifications = true;
_accepts_miscellaneous = false;
_category = Command::Category::operation;
_category = Command::Category::operation;
}
////////////////////////////////////////////////////////////////////////////////
int CmdAdd::execute (std::string& output)
{
int CmdAdd::execute(std::string& output) {
// Apply the command line modifications to the new task.
Task task;
// the task is empty, but DOM references can refer to earlier parts of the
// command line, e.g., `task add due:20110101 wait:due`.
task.modify (Task::modReplace, true);
Context::getContext ().tdb2.add (task);
task.modify(Task::modReplace, true);
Context::getContext().tdb2.add(task);
// Do not display ID 0, users cannot query by that
auto status = task.getStatus ();
auto status = task.getStatus();
// We may have a situation where both new-id and new-uuid config
// variables are set. In that case, we'll show the new-uuid, as
// it's enduring and never changes, and it's unlikely the caller
// asked for this if they just wanted a human-friendly number.
if (Context::getContext ().verbose ("new-uuid") &&
status == Task::recurring)
output += format ("Created task {1} (recurrence template).\n", task.get ("uuid"));
if (Context::getContext().verbose("new-uuid") && status == Task::recurring)
output += format("Created task {1} (recurrence template).\n", task.get("uuid"));
else if (Context::getContext ().verbose ("new-uuid") ||
(Context::getContext ().verbose ("new-id") &&
(status == Task::completed ||
status == Task::deleted)))
output += format ("Created task {1}.\n", task.get ("uuid"));
else if (Context::getContext().verbose("new-uuid") ||
(Context::getContext().verbose("new-id") &&
(status == Task::completed || status == Task::deleted)))
output += format("Created task {1}.\n", task.get("uuid"));
else if (Context::getContext ().verbose ("new-id") &&
(status == Task::pending ||
status == Task::waiting))
output += format ("Created task {1}.\n", task.id);
else if (Context::getContext().verbose("new-id") &&
(status == Task::pending || status == Task::waiting))
output += format("Created task {1}.\n", task.id);
else if (Context::getContext ().verbose ("new-id") &&
status == Task::recurring)
output += format ("Created task {1} (recurrence template).\n", task.id);
else if (Context::getContext().verbose("new-id") && status == Task::recurring)
output += format("Created task {1} (recurrence template).\n", task.id);
if (Context::getContext ().verbose ("project"))
Context::getContext ().footnote (onProjectChange (task));
if (Context::getContext().verbose("project"))
Context::getContext().footnote(onProjectChange(task));
return 0;
}

View File

@@ -27,14 +27,14 @@
#ifndef INCLUDED_CMDADD
#define INCLUDED_CMDADD
#include <string>
#include <Command.h>
class CmdAdd : public Command
{
public:
CmdAdd ();
int execute (std::string&);
#include <string>
class CmdAdd : public Command {
public:
CmdAdd();
int execute(std::string&);
};
#endif

View File

@@ -28,31 +28,28 @@
// cmake.h include header must come first
#include <CmdAliases.h>
#include <Context.h>
#include <Command.h>
#include <Context.h>
////////////////////////////////////////////////////////////////////////////////
CmdCompletionAliases::CmdCompletionAliases ()
{
_keyword = "_aliases";
_usage = "task _aliases";
_description = "Generates a list of all aliases, for autocompletion purposes";
_read_only = true;
_displays_id = false;
_needs_gc = false;
_uses_context = false;
_accepts_filter = false;
CmdCompletionAliases::CmdCompletionAliases() {
_keyword = "_aliases";
_usage = "task _aliases";
_description = "Generates a list of all aliases, for autocompletion purposes";
_read_only = true;
_displays_id = false;
_needs_gc = false;
_uses_context = false;
_accepts_filter = false;
_accepts_modifications = false;
_accepts_miscellaneous = false;
_category = Command::Category::internal;
_category = Command::Category::internal;
}
////////////////////////////////////////////////////////////////////////////////
int CmdCompletionAliases::execute (std::string& output)
{
for (const auto& alias : Context::getContext ().config)
if (alias.first.substr (0, 6) == "alias.")
output += alias.first.substr (6) + '\n';
int CmdCompletionAliases::execute(std::string& output) {
for (const auto& alias : Context::getContext().config)
if (alias.first.substr(0, 6) == "alias.") output += alias.first.substr(6) + '\n';
return 0;
}

View File

@@ -27,14 +27,14 @@
#ifndef INCLUDED_CMDALIASES
#define INCLUDED_CMDALIASES
#include <string>
#include <Command.h>
class CmdCompletionAliases : public Command
{
public:
CmdCompletionAliases ();
int execute (std::string&);
#include <string>
class CmdCompletionAliases : public Command {
public:
CmdCompletionAliases();
int execute(std::string&);
};
#endif

View File

@@ -28,111 +28,100 @@
// cmake.h include header must come first
#include <CmdAnnotate.h>
#include <iostream>
#include <Context.h>
#include <Filter.h>
#include <format.h>
#include <main.h>
#include <shared.h>
#include <format.h>
#include <iostream>
////////////////////////////////////////////////////////////////////////////////
CmdAnnotate::CmdAnnotate ()
{
_keyword = "annotate";
_usage = "task <filter> annotate <mods>";
_description = "Adds an annotation to an existing task";
_read_only = false;
_displays_id = false;
_needs_gc = false;
_uses_context = false;
_accepts_filter = true;
CmdAnnotate::CmdAnnotate() {
_keyword = "annotate";
_usage = "task <filter> annotate <mods>";
_description = "Adds an annotation to an existing task";
_read_only = false;
_displays_id = false;
_needs_gc = false;
_uses_context = false;
_accepts_filter = true;
_accepts_modifications = true;
_accepts_miscellaneous = false;
_category = Command::Category::operation;
_category = Command::Category::operation;
}
////////////////////////////////////////////////////////////////////////////////
int CmdAnnotate::execute (std::string&)
{
int CmdAnnotate::execute(std::string&) {
int rc = 0;
int count = 0;
// Apply filter.
Filter filter;
std::vector <Task> filtered;
filter.subset (filtered);
if (filtered.size () == 0)
{
Context::getContext ().footnote ("No tasks specified.");
std::vector<Task> filtered;
filter.subset(filtered);
if (filtered.size() == 0) {
Context::getContext().footnote("No tasks specified.");
return 1;
}
// TODO Complain when no modifications are specified.
// Accumulated project change notifications.
std::map <std::string, std::string> projectChanges;
std::map<std::string, std::string> projectChanges;
if(filtered.size() > 1) {
if (filtered.size() > 1) {
feedback_affected("This command will alter {1} tasks.", filtered.size());
}
for (auto& task : filtered)
{
Task before (task);
for (auto& task : filtered) {
Task before(task);
// Annotate the specified task.
std::string question = format ("Annotate task {1} '{2}'?",
task.identifier (true),
task.get ("description"));
std::string question =
format("Annotate task {1} '{2}'?", task.identifier(true), task.get("description"));
task.modify (Task::modAnnotate, true);
task.modify(Task::modAnnotate, true);
if (permission (before.diff (task) + question, filtered.size ()))
{
Context::getContext ().tdb2.modify (task);
if (permission(before.diff(task) + question, filtered.size())) {
Context::getContext().tdb2.modify(task);
++count;
feedback_affected ("Annotating task {1} '{2}'.", task);
if (Context::getContext ().verbose ("project"))
projectChanges[task.get ("project")] = onProjectChange (task, false);
feedback_affected("Annotating task {1} '{2}'.", task);
if (Context::getContext().verbose("project"))
projectChanges[task.get("project")] = onProjectChange(task, false);
// Annotate siblings.
if (task.has ("parent"))
{
if ((Context::getContext ().config.get ("recurrence.confirmation") == "prompt"
&& confirm ("This is a recurring task. Do you want to annotate all pending recurrences of this same task?")) ||
Context::getContext ().config.getBoolean ("recurrence.confirmation"))
{
auto siblings = Context::getContext ().tdb2.siblings (task);
for (auto& sibling : siblings)
{
sibling.modify (Task::modAnnotate, true);
Context::getContext ().tdb2.modify (sibling);
if (task.has("parent")) {
if ((Context::getContext().config.get("recurrence.confirmation") == "prompt" &&
confirm("This is a recurring task. Do you want to annotate all pending recurrences "
"of this same task?")) ||
Context::getContext().config.getBoolean("recurrence.confirmation")) {
auto siblings = Context::getContext().tdb2.siblings(task);
for (auto& sibling : siblings) {
sibling.modify(Task::modAnnotate, true);
Context::getContext().tdb2.modify(sibling);
++count;
feedback_affected ("Annotating recurring task {1} '{2}'.", sibling);
feedback_affected("Annotating recurring task {1} '{2}'.", sibling);
}
// Annotate the parent
Task parent;
Context::getContext ().tdb2.get (task.get ("parent"), parent);
parent.modify (Task::modAnnotate, true);
Context::getContext ().tdb2.modify (parent);
Context::getContext().tdb2.get(task.get("parent"), parent);
parent.modify(Task::modAnnotate, true);
Context::getContext().tdb2.modify(parent);
}
}
}
else
{
} else {
std::cout << "Task not annotated.\n";
rc = 1;
if (_permission_quit)
break;
if (_permission_quit) break;
}
}
// Now list the project changes.
for (const auto& change : projectChanges)
if (change.first != "")
Context::getContext ().footnote (change.second);
if (change.first != "") Context::getContext().footnote(change.second);
feedback_affected (count == 1 ? "Annotated {1} task." : "Annotated {1} tasks.", count);
feedback_affected(count == 1 ? "Annotated {1} task." : "Annotated {1} tasks.", count);
return rc;
}

View File

@@ -27,14 +27,14 @@
#ifndef INCLUDED_CMDANNOTATE
#define INCLUDED_CMDANNOTATE
#include <string>
#include <Command.h>
class CmdAnnotate : public Command
{
public:
CmdAnnotate ();
int execute (std::string&);
#include <string>
class CmdAnnotate : public Command {
public:
CmdAnnotate();
int execute(std::string&);
};
#endif

View File

@@ -28,111 +28,100 @@
// cmake.h include header must come first
#include <CmdAppend.h>
#include <iostream>
#include <Context.h>
#include <Filter.h>
#include <shared.h>
#include <format.h>
#include <main.h>
#include <shared.h>
#include <iostream>
////////////////////////////////////////////////////////////////////////////////
CmdAppend::CmdAppend ()
{
_keyword = "append";
_usage = "task <filter> append <mods>";
_description = "Appends text to an existing task description";
_read_only = false;
_displays_id = false;
_needs_gc = false;
_uses_context = false;
_accepts_filter = true;
CmdAppend::CmdAppend() {
_keyword = "append";
_usage = "task <filter> append <mods>";
_description = "Appends text to an existing task description";
_read_only = false;
_displays_id = false;
_needs_gc = false;
_uses_context = false;
_accepts_filter = true;
_accepts_modifications = true;
_accepts_miscellaneous = false;
_category = Command::Category::operation;
_category = Command::Category::operation;
}
////////////////////////////////////////////////////////////////////////////////
int CmdAppend::execute (std::string&)
{
int CmdAppend::execute(std::string&) {
int rc = 0;
int count = 0;
// Apply filter.
Filter filter;
std::vector <Task> filtered;
filter.subset (filtered);
if (filtered.size () == 0)
{
Context::getContext ().footnote ("No tasks specified.");
std::vector<Task> filtered;
filter.subset(filtered);
if (filtered.size() == 0) {
Context::getContext().footnote("No tasks specified.");
return 1;
}
// TODO Complain when no modifications are specified.
// Accumulated project change notifications.
std::map <std::string, std::string> projectChanges;
std::map<std::string, std::string> projectChanges;
if(filtered.size() > 1) {
if (filtered.size() > 1) {
feedback_affected("This command will alter {1} tasks.", filtered.size());
}
for (auto& task : filtered)
{
Task before (task);
for (auto& task : filtered) {
Task before(task);
// Append to the specified task.
auto question = format ("Append to task {1} '{2}'?",
task.identifier (true),
task.get ("description"));
auto question =
format("Append to task {1} '{2}'?", task.identifier(true), task.get("description"));
task.modify (Task::modAppend, true);
task.modify(Task::modAppend, true);
if (permission (before.diff (task) + question, filtered.size ()))
{
Context::getContext ().tdb2.modify (task);
if (permission(before.diff(task) + question, filtered.size())) {
Context::getContext().tdb2.modify(task);
++count;
feedback_affected ("Appending to task {1} '{2}'.", task);
if (Context::getContext ().verbose ("project"))
projectChanges[task.get ("project")] = onProjectChange (task, false);
feedback_affected("Appending to task {1} '{2}'.", task);
if (Context::getContext().verbose("project"))
projectChanges[task.get("project")] = onProjectChange(task, false);
// Append to siblings.
if (task.has ("parent"))
{
if ((Context::getContext ().config.get ("recurrence.confirmation") == "prompt"
&& confirm ("This is a recurring task. Do you want to append to all pending recurrences of this same task?")) ||
Context::getContext ().config.getBoolean ("recurrence.confirmation"))
{
std::vector <Task> siblings = Context::getContext ().tdb2.siblings (task);
for (auto& sibling : siblings)
{
sibling.modify (Task::modAppend, true);
Context::getContext ().tdb2.modify (sibling);
if (task.has("parent")) {
if ((Context::getContext().config.get("recurrence.confirmation") == "prompt" &&
confirm("This is a recurring task. Do you want to append to all pending recurrences "
"of this same task?")) ||
Context::getContext().config.getBoolean("recurrence.confirmation")) {
std::vector<Task> siblings = Context::getContext().tdb2.siblings(task);
for (auto& sibling : siblings) {
sibling.modify(Task::modAppend, true);
Context::getContext().tdb2.modify(sibling);
++count;
feedback_affected ("Appending to recurring task {1} '{2}'.", sibling);
feedback_affected("Appending to recurring task {1} '{2}'.", sibling);
}
// Append to the parent
Task parent;
Context::getContext ().tdb2.get (task.get ("parent"), parent);
parent.modify (Task::modAppend, true);
Context::getContext ().tdb2.modify (parent);
Context::getContext().tdb2.get(task.get("parent"), parent);
parent.modify(Task::modAppend, true);
Context::getContext().tdb2.modify(parent);
}
}
}
else
{
} else {
std::cout << "Task not appended.\n";
rc = 1;
if (_permission_quit)
break;
if (_permission_quit) break;
}
}
// Now list the project changes.
for (const auto& change : projectChanges)
if (change.first != "")
Context::getContext ().footnote (change.second);
if (change.first != "") Context::getContext().footnote(change.second);
feedback_affected (count == 1 ? "Appended {1} task." : "Appended {1} tasks.", count);
feedback_affected(count == 1 ? "Appended {1} task." : "Appended {1} tasks.", count);
return rc;
}

View File

@@ -27,14 +27,14 @@
#ifndef INCLUDED_CMDAPPEND
#define INCLUDED_CMDAPPEND
#include <string>
#include <Command.h>
class CmdAppend : public Command
{
public:
CmdAppend ();
int execute (std::string&);
#include <string>
class CmdAppend : public Command {
public:
CmdAppend();
int execute(std::string&);
};
#endif

View File

@@ -28,39 +28,37 @@
// cmake.h include header must come first
#include <CmdAttributes.h>
#include <sstream>
#include <algorithm>
#include <Context.h>
#include <Command.h>
#include <Context.h>
#include <algorithm>
#include <sstream>
////////////////////////////////////////////////////////////////////////////////
CmdZshAttributes::CmdZshAttributes ()
{
_keyword = "_zshattributes";
_usage = "task _zshattributes";
_description = "Generates a list of all attributes, for zsh autocompletion purposes";
_read_only = true;
_displays_id = false;
_needs_gc = false;
_uses_context = false;
_accepts_filter = false;
CmdZshAttributes::CmdZshAttributes() {
_keyword = "_zshattributes";
_usage = "task _zshattributes";
_description = "Generates a list of all attributes, for zsh autocompletion purposes";
_read_only = true;
_displays_id = false;
_needs_gc = false;
_uses_context = false;
_accepts_filter = false;
_accepts_modifications = false;
_accepts_miscellaneous = false;
_category = Command::Category::internal;
_category = Command::Category::internal;
}
////////////////////////////////////////////////////////////////////////////////
int CmdZshAttributes::execute (std::string& output)
{
int CmdZshAttributes::execute(std::string& output) {
// Get a list of all columns, sort them.
auto columns = Context::getContext ().getColumns ();
std::sort (columns.begin (), columns.end ());
auto columns = Context::getContext().getColumns();
std::sort(columns.begin(), columns.end());
std::stringstream out;
for (const auto& col : columns)
out << col << ':' << col << '\n';
for (const auto& col : columns) out << col << ':' << col << '\n';
output = out.str ();
output = out.str();
return 0;
}

View File

@@ -27,14 +27,14 @@
#ifndef INCLUDED_CMDATTRIBUTES
#define INCLUDED_CMDATTRIBUTES
#include <string>
#include <Command.h>
class CmdZshAttributes : public Command
{
public:
CmdZshAttributes ();
int execute (std::string&);
#include <string>
class CmdZshAttributes : public Command {
public:
CmdZshAttributes();
int execute(std::string&);
};
#endif

File diff suppressed because it is too large Load Diff

View File

@@ -27,28 +27,26 @@
#ifndef INCLUDED_CMDBURNDOWN
#define INCLUDED_CMDBURNDOWN
#include <string>
#include <Command.h>
class CmdBurndownMonthly : public Command
{
public:
CmdBurndownMonthly ();
int execute (std::string&);
#include <string>
class CmdBurndownMonthly : public Command {
public:
CmdBurndownMonthly();
int execute(std::string&);
};
class CmdBurndownWeekly : public Command
{
public:
CmdBurndownWeekly ();
int execute (std::string&);
class CmdBurndownWeekly : public Command {
public:
CmdBurndownWeekly();
int execute(std::string&);
};
class CmdBurndownDaily : public Command
{
public:
CmdBurndownDaily ();
int execute (std::string&);
class CmdBurndownDaily : public Command {
public:
CmdBurndownDaily();
int execute(std::string&);
};
#endif

View File

@@ -24,53 +24,49 @@
//
////////////////////////////////////////////////////////////////////////////////
#include <Context.h>
#include <CmdCalc.h>
#include <Filter.h>
#include <Context.h>
#include <Eval.h>
#include <Filter.h>
////////////////////////////////////////////////////////////////////////////////
CmdCalc::CmdCalc ()
{
_keyword = "calc";
_usage = "task calc <expression>";
_description = "Calculator";
_read_only = true;
_displays_id = false;
_needs_gc = false;
_uses_context = false;
_accepts_filter = false;
CmdCalc::CmdCalc() {
_keyword = "calc";
_usage = "task calc <expression>";
_description = "Calculator";
_read_only = true;
_displays_id = false;
_needs_gc = false;
_uses_context = false;
_accepts_filter = false;
_accepts_modifications = false;
_accepts_miscellaneous = true;
_category = Command::Category::misc;
_category = Command::Category::misc;
}
////////////////////////////////////////////////////////////////////////////////
int CmdCalc::execute (std::string& output)
{
int CmdCalc::execute(std::string& output) {
// Configurable infix/postfix
bool infix {true};
if (Context::getContext ().config.get ("expressions") == "postfix")
infix = false;
bool infix{true};
if (Context::getContext().config.get("expressions") == "postfix") infix = false;
// Create an evaluator with DOM access.
Eval e;
e.addSource (domSource);
e.debug (Context::getContext ().config.getBoolean ("debug"));
e.addSource(domSource);
e.debug(Context::getContext().config.getBoolean("debug"));
// Compile all the args into one expression.
std::string expression;
for (const auto& word : Context::getContext ().cli2.getWords ())
expression += word + ' ';
for (const auto& word : Context::getContext().cli2.getWords()) expression += word + ' ';
// Evaluate according to preference.
Variant result;
if (infix)
e.evaluateInfixExpression (expression, result);
e.evaluateInfixExpression(expression, result);
else
e.evaluatePostfixExpression (expression, result);
e.evaluatePostfixExpression(expression, result);
output = (std::string) result + '\n';
output = (std::string)result + '\n';
return 0;
}

View File

@@ -27,14 +27,14 @@
#ifndef INCLUDED_CMDCALC
#define INCLUDED_CMDCALC
#include <string>
#include <Command.h>
class CmdCalc : public Command
{
public:
CmdCalc ();
int execute (std::string&);
#include <string>
class CmdCalc : public Command {
public:
CmdCalc();
int execute(std::string&);
};
#endif

View File

@@ -28,51 +28,50 @@
// cmake.h include header must come first
#include <CmdCalendar.h>
#include <sstream>
#include <iomanip>
#include <stdlib.h>
#include <Context.h>
#include <Table.h>
#include <Lexer.h>
#include <shared.h>
#include <Table.h>
#include <format.h>
#include <util.h>
#include <utf8.h>
#include <main.h>
#include <shared.h>
#include <stdlib.h>
#include <utf8.h>
#include <util.h>
#include <iomanip>
#include <sstream>
////////////////////////////////////////////////////////////////////////////////
CmdCalendar::CmdCalendar ()
{
_keyword = "calendar";
_usage = "task calendar [due|<month> <year>|<year>] [y]";
_description = "Shows a calendar, with due tasks marked";
_read_only = true;
_displays_id = true;
_needs_gc = true;
_uses_context = false;
_accepts_filter = false;
CmdCalendar::CmdCalendar() {
_keyword = "calendar";
_usage = "task calendar [due|<month> <year>|<year>] [y]";
_description = "Shows a calendar, with due tasks marked";
_read_only = true;
_displays_id = true;
_needs_gc = true;
_uses_context = false;
_accepts_filter = false;
_accepts_modifications = false;
_accepts_miscellaneous = true;
_category = Command::Category::graphs;
_category = Command::Category::graphs;
}
////////////////////////////////////////////////////////////////////////////////
int CmdCalendar::execute (std::string& output)
{
int CmdCalendar::execute(std::string& output) {
int rc = 0;
auto& config = Context::getContext ().config;
auto& config = Context::getContext().config;
// Each month requires 28 text columns width. See how many will actually
// fit. But if a preference is specified, and it fits, use it.
auto width = Context::getContext ().getWidth ();
auto width = Context::getContext().getWidth();
int preferredMonthsPerLine;
if (config.has ("calendar.monthsperline"))
preferredMonthsPerLine = config.getInteger ("calendar.monthsperline");
if (config.has("calendar.monthsperline"))
preferredMonthsPerLine = config.getInteger("calendar.monthsperline");
else
// Legacy configuration variable value
preferredMonthsPerLine = config.getInteger ("monthsperline");
preferredMonthsPerLine = config.getInteger("monthsperline");
auto monthsThatFit = width / 26;
@@ -81,78 +80,75 @@ int CmdCalendar::execute (std::string& output)
monthsPerLine = preferredMonthsPerLine;
// Load the pending tasks.
handleUntil ();
handleRecurrence ();
auto tasks = Context::getContext ().tdb2.pending_tasks ();
handleUntil();
handleRecurrence();
auto tasks = Context::getContext().tdb2.pending_tasks();
Datetime today;
auto getPendingDate = false;
auto monthsToDisplay = 1;
auto mFrom = today.month ();
auto yFrom = today.year ();
auto mFrom = today.month();
auto yFrom = today.year();
auto mTo = mFrom;
auto yTo = yFrom;
// Defaults.
monthsToDisplay = monthsPerLine;
mFrom = today.month ();
yFrom = today.year ();
mFrom = today.month();
yFrom = today.year();
// Set up a vector of commands (1), for autoComplete.
std::vector <std::string> commandNames {"calendar"};
std::vector<std::string> commandNames{"calendar"};
// Set up a vector of keywords, for autoComplete.
std::vector <std::string> keywordNames {"due"};
std::vector<std::string> keywordNames{"due"};
// Set up a vector of months, for autoComplete.
std::vector <std::string> monthNames;
for (int i = 1; i <= 12; ++i)
monthNames.push_back (Lexer::lowerCase (Datetime::monthName (i)));
std::vector<std::string> monthNames;
for (int i = 1; i <= 12; ++i) monthNames.push_back(Lexer::lowerCase(Datetime::monthName(i)));
// For autoComplete results.
std::vector <std::string> matches;
std::vector<std::string> matches;
// Look at all args, regardless of sequence.
auto argMonth = 0;
auto argYear = 0;
auto argWholeYear = false;
for (auto& arg : Context::getContext ().cli2.getWords ())
{
for (auto& arg : Context::getContext().cli2.getWords()) {
// Some version of "calendar".
if (autoComplete (Lexer::lowerCase (arg), commandNames, matches, config.getInteger ("abbreviation.minimum")) == 1)
if (autoComplete(Lexer::lowerCase(arg), commandNames, matches,
config.getInteger("abbreviation.minimum")) == 1)
continue;
// "due".
else if (autoComplete (Lexer::lowerCase (arg), keywordNames, matches, config.getInteger ("abbreviation.minimum")) == 1)
else if (autoComplete(Lexer::lowerCase(arg), keywordNames, matches,
config.getInteger("abbreviation.minimum")) == 1)
getPendingDate = true;
// "y".
else if (Lexer::lowerCase (arg) == "y")
else if (Lexer::lowerCase(arg) == "y")
argWholeYear = true;
// YYYY.
else if (Lexer::isAllDigits (arg) && arg.length () == 4)
argYear = strtol (arg.c_str (), nullptr, 10);
else if (Lexer::isAllDigits(arg) && arg.length() == 4)
argYear = strtol(arg.c_str(), nullptr, 10);
// MM.
else if (Lexer::isAllDigits (arg) && arg.length () <= 2)
{
argMonth = strtol (arg.c_str (), nullptr, 10);
if (argMonth < 1 || argMonth > 12)
throw format ("Argument '{1}' is not a valid month.", arg);
else if (Lexer::isAllDigits(arg) && arg.length() <= 2) {
argMonth = strtol(arg.c_str(), nullptr, 10);
if (argMonth < 1 || argMonth > 12) throw format("Argument '{1}' is not a valid month.", arg);
}
// "January" etc.
else if (autoComplete (Lexer::lowerCase (arg), monthNames, matches, config.getInteger ("abbreviation.minimum")) == 1)
{
argMonth = Datetime::monthOfYear (matches[0]);
if (argMonth == -1)
throw format ("Argument '{1}' is not a valid month.", arg);
else if (autoComplete(Lexer::lowerCase(arg), monthNames, matches,
config.getInteger("abbreviation.minimum")) == 1) {
argMonth = Datetime::monthOfYear(matches[0]);
if (argMonth == -1) throw format("Argument '{1}' is not a valid month.", arg);
}
else
throw format ("Could not recognize argument '{1}'.", arg);
throw format("Could not recognize argument '{1}'.", arg);
}
// Supported combinations:
@@ -167,56 +163,45 @@ int CmdCalendar::execute (std::string& output)
// cal MM YYYY monthsPerLine arg arg false
// cal MM YYYY y 12 arg arg false
if (argWholeYear || (argYear && !argMonth && !argWholeYear))
monthsToDisplay = 12;
if (argWholeYear || (argYear && !argMonth && !argWholeYear)) monthsToDisplay = 12;
if (!argMonth && argYear)
mFrom = 1;
else if (argMonth && argYear)
mFrom = argMonth;
if (argYear)
yFrom = argYear;
if (argYear) yFrom = argYear;
// Now begin the data subset and rendering.
if (getPendingDate == true)
{
if (getPendingDate == true) {
// Find the oldest pending due date.
Datetime oldest (9999, 12, 31);
for (auto& task : tasks)
{
auto status = task.getStatus ();
if (status == Task::pending || status == Task::waiting)
{
if (task.has ("due") &&
!task.hasTag ("nocal"))
{
Datetime d (task.get ("due"));
Datetime oldest(9999, 12, 31);
for (auto& task : tasks) {
auto status = task.getStatus();
if (status == Task::pending || status == Task::waiting) {
if (task.has("due") && !task.hasTag("nocal")) {
Datetime d(task.get("due"));
if (d < oldest) oldest = d;
}
}
}
// Default to current month if no due date is present
if (oldest != Datetime (9999, 12, 31)) {
if (oldest != Datetime(9999, 12, 31)) {
mFrom = oldest.month();
yFrom = oldest.year();
}
}
if (config.getBoolean ("calendar.offset"))
{
auto moffset = config.getInteger ("calendar.offset.value") % 12;
auto yoffset = config.getInteger ("calendar.offset.value") / 12;
if (config.getBoolean("calendar.offset")) {
auto moffset = config.getInteger("calendar.offset.value") % 12;
auto yoffset = config.getInteger("calendar.offset.value") / 12;
mFrom += moffset;
yFrom += yoffset;
if (mFrom < 1)
{
if (mFrom < 1) {
mFrom += 12;
yFrom--;
}
else if (mFrom > 12)
{
} else if (mFrom > 12) {
mFrom -= 12;
yFrom++;
}
@@ -224,8 +209,7 @@ int CmdCalendar::execute (std::string& output)
mTo = mFrom + monthsToDisplay - 1;
yTo = yFrom;
if (mTo > 12)
{
if (mTo > 12) {
mTo -= 12;
yTo++;
}
@@ -236,15 +220,13 @@ int CmdCalendar::execute (std::string& output)
std::stringstream out;
out << '\n';
while (yFrom < yTo || (yFrom == yTo && mFrom <= mTo))
{
while (yFrom < yTo || (yFrom == yTo && mFrom <= mTo)) {
auto nextM = mFrom;
auto nextY = yFrom;
// Print month headers (cheating on the width settings, yes)
for (int i = 0 ; i < monthsPerLine ; i++)
{
auto month = Datetime::monthName (nextM);
for (int i = 0; i < monthsPerLine; i++) {
auto month = Datetime::monthName(nextM);
// 12345678901234567890123456 = 26 chars wide
// ^^ = center
@@ -261,253 +243,207 @@ int CmdCalendar::execute (std::string& output)
// +--------------------------+
auto totalWidth = 26;
auto labelWidth = month.length () + 5; // 5 = " 2009"
auto labelWidth = month.length() + 5; // 5 = " 2009"
auto leftGap = (totalWidth / 2) - (labelWidth / 2);
auto rightGap = totalWidth - leftGap - labelWidth;
out << std::setw (leftGap) << ' '
<< month
<< ' '
<< nextY
<< std::setw (rightGap) << ' ';
out << std::setw(leftGap) << ' ' << month << ' ' << nextY << std::setw(rightGap) << ' ';
if (++nextM > 12)
{
if (++nextM > 12) {
nextM = 1;
nextY++;
}
}
out << '\n'
<< optionalBlankLine ()
<< renderMonths (mFrom, yFrom, today, tasks, monthsPerLine)
<< '\n';
<< optionalBlankLine() << renderMonths(mFrom, yFrom, today, tasks, monthsPerLine) << '\n';
mFrom += monthsPerLine;
if (mFrom > 12)
{
if (mFrom > 12) {
mFrom -= 12;
++yFrom;
}
}
Color color_today (config.get ("color.calendar.today"));
Color color_due (config.get ("color.calendar.due"));
Color color_duetoday (config.get ("color.calendar.due.today"));
Color color_overdue (config.get ("color.calendar.overdue"));
Color color_weekend (config.get ("color.calendar.weekend"));
Color color_holiday (config.get ("color.calendar.holiday"));
Color color_scheduled (config.get ("color.calendar.scheduled"));
Color color_weeknumber (config.get ("color.calendar.weeknumber"));
Color color_today(config.get("color.calendar.today"));
Color color_due(config.get("color.calendar.due"));
Color color_duetoday(config.get("color.calendar.due.today"));
Color color_overdue(config.get("color.calendar.overdue"));
Color color_weekend(config.get("color.calendar.weekend"));
Color color_holiday(config.get("color.calendar.holiday"));
Color color_scheduled(config.get("color.calendar.scheduled"));
Color color_weeknumber(config.get("color.calendar.weeknumber"));
if (Context::getContext ().color () && config.getBoolean ("calendar.legend"))
{
out << "Legend: "
<< color_today.colorize ("today")
<< ", "
<< color_weekend.colorize ("weekend")
if (Context::getContext().color() && config.getBoolean("calendar.legend")) {
out << "Legend: " << color_today.colorize("today") << ", " << color_weekend.colorize("weekend")
<< ", ";
// If colorizing due dates, print legend
if (config.get ("calendar.details") != "none")
out << color_due.colorize ("due")
<< ", "
<< color_duetoday.colorize ("due-today")
<< ", "
<< color_overdue.colorize ("overdue")
<< ", "
<< color_scheduled.colorize ("scheduled")
if (config.get("calendar.details") != "none")
out << color_due.colorize("due") << ", " << color_duetoday.colorize("due-today") << ", "
<< color_overdue.colorize("overdue") << ", " << color_scheduled.colorize("scheduled")
<< ", ";
// If colorizing holidays, print legend
if (config.get ("calendar.holidays") != "none")
out << color_holiday.colorize ("holiday") << ", ";
if (config.get("calendar.holidays") != "none") out << color_holiday.colorize("holiday") << ", ";
out << color_weeknumber.colorize ("weeknumber")
<< '.'
<< optionalBlankLine ()
<< '\n';
out << color_weeknumber.colorize("weeknumber") << '.' << optionalBlankLine() << '\n';
}
if (config.get ("calendar.details") == "full" || config.get ("calendar.holidays") == "full")
{
if (config.get("calendar.details") == "full" || config.get("calendar.holidays") == "full") {
--details_mFrom;
if (details_mFrom == 0)
{
if (details_mFrom == 0) {
details_mFrom = 12;
--details_yFrom;
}
int details_dFrom = Datetime::daysInMonth (details_yFrom, details_mFrom);
int details_dFrom = Datetime::daysInMonth(details_yFrom, details_mFrom);
++mTo;
if (mTo == 13)
{
if (mTo == 13) {
mTo = 1;
++yTo;
}
Datetime date_after (details_yFrom, details_mFrom, details_dFrom);
auto after = date_after.toString (config.get ("dateformat"));
Datetime date_after(details_yFrom, details_mFrom, details_dFrom);
auto after = date_after.toString(config.get("dateformat"));
Datetime date_before (yTo, mTo, 1);
auto before = date_before.toString (config.get ("dateformat"));
Datetime date_before(yTo, mTo, 1);
auto before = date_before.toString(config.get("dateformat"));
// Table with due date information
if (config.get ("calendar.details") == "full")
{
if (config.get("calendar.details") == "full") {
// Assert that 'report' is a valid report.
auto report = config.get ("calendar.details.report");
if (Context::getContext ().commands.find (report) == Context::getContext ().commands.end ())
throw std::string ("The setting 'calendar.details.report' must contain a single report name.");
auto report = config.get("calendar.details.report");
if (Context::getContext().commands.find(report) == Context::getContext().commands.end())
throw std::string(
"The setting 'calendar.details.report' must contain a single report name.");
// TODO Fix this: cal --> task
// calendar --> taskendar
// If the executable was "cal" or equivalent, replace it with "task".
auto executable = Context::getContext ().cli2._original_args[0].attribute ("raw");
auto cal = executable.find ("cal");
if (cal != std::string::npos)
executable = executable.substr (0, cal) + PACKAGE;
auto executable = Context::getContext().cli2._original_args[0].attribute("raw");
auto cal = executable.find("cal");
if (cal != std::string::npos) executable = executable.substr(0, cal) + PACKAGE;
std::vector <std::string> args;
args.push_back ("rc:" + Context::getContext ().rc_file._data);
args.push_back ("rc.due:0");
args.push_back ("rc.verbose:label,affected,blank");
if (Context::getContext ().color ())
args.push_back ("rc._forcecolor:on");
args.push_back ("due.after:" + after);
args.push_back ("due.before:" + before);
args.push_back ("-nocal");
args.push_back (report);
std::vector<std::string> args;
args.push_back("rc:" + Context::getContext().rc_file._data);
args.push_back("rc.due:0");
args.push_back("rc.verbose:label,affected,blank");
if (Context::getContext().color()) args.push_back("rc._forcecolor:on");
args.push_back("due.after:" + after);
args.push_back("due.before:" + before);
args.push_back("-nocal");
args.push_back(report);
std::string output;
::execute (executable, args, "", output);
::execute(executable, args, "", output);
out << output;
}
// Table with holiday information
if (config.get ("calendar.holidays") == "full")
{
if (config.get("calendar.holidays") == "full") {
Table holTable;
holTable.width (Context::getContext ().getWidth ());
holTable.add ("Date");
holTable.add ("Holiday");
setHeaderUnderline (holTable);
holTable.width(Context::getContext().getWidth());
holTable.add("Date");
holTable.add("Holiday");
setHeaderUnderline(holTable);
auto dateFormat = config.get ("dateformat.holiday");
auto dateFormat = config.get("dateformat.holiday");
std::map <time_t, std::vector<std::string>> hm; // we need to store multiple holidays per day
std::map<time_t, std::vector<std::string>> hm; // we need to store multiple holidays per day
for (auto& it : config)
if (it.first.substr (0, 8) == "holiday.")
if (it.first.substr (it.first.size () - 4) == "name")
{
if (it.first.substr(0, 8) == "holiday.")
if (it.first.substr(it.first.size() - 4) == "name") {
auto holName = it.second;
auto date = config.get ("holiday." + it.first.substr (8, it.first.size () - 13) + ".date");
auto start = config.get ("holiday." + it.first.substr (8, it.first.size () - 13) + ".start");
auto end = config.get ("holiday." + it.first.substr (8, it.first.size () - 13) + ".end");
if (!date.empty ())
{
Datetime holDate (date.c_str (), dateFormat);
auto date = config.get("holiday." + it.first.substr(8, it.first.size() - 13) + ".date");
auto start =
config.get("holiday." + it.first.substr(8, it.first.size() - 13) + ".start");
auto end = config.get("holiday." + it.first.substr(8, it.first.size() - 13) + ".end");
if (!date.empty()) {
Datetime holDate(date.c_str(), dateFormat);
if (date_after < holDate && holDate < date_before)
hm[holDate.toEpoch()].push_back (holName);
hm[holDate.toEpoch()].push_back(holName);
}
if (!start.empty () && !end.empty ())
{
Datetime holStart (start.c_str (), dateFormat);
Datetime holEnd (end.c_str (), dateFormat);
if (!start.empty() && !end.empty()) {
Datetime holStart(start.c_str(), dateFormat);
Datetime holEnd(end.c_str(), dateFormat);
if (date_after < holStart && holStart < date_before)
hm[holStart.toEpoch()].push_back ("Start of " + holName);
hm[holStart.toEpoch()].push_back("Start of " + holName);
if (date_after < holEnd && holEnd < date_before)
hm[holEnd.toEpoch()].push_back ("End of " + holName);
hm[holEnd.toEpoch()].push_back("End of " + holName);
}
}
auto format = config.get ("report." +
config.get ("calendar.details.report") +
".dateformat");
if (format == "")
format = config.get ("dateformat.report");
if (format == "")
format = config.get ("dateformat");
auto format = config.get("report." + config.get("calendar.details.report") + ".dateformat");
if (format == "") format = config.get("dateformat.report");
if (format == "") format = config.get("dateformat");
for (auto& hm_it : hm)
{
for (auto& hm_it : hm) {
auto v = hm_it.second;
Datetime hDate (hm_it.first);
auto d = hDate.toString (format);
for (const auto& i : v)
{
auto row = holTable.addRow ();
holTable.set (row, 0, d);
holTable.set (row, 1, i);
Datetime hDate(hm_it.first);
auto d = hDate.toString(format);
for (const auto& i : v) {
auto row = holTable.addRow();
holTable.set(row, 0, d);
holTable.set(row, 1, i);
}
}
out << optionalBlankLine ()
<< holTable.render ()
<< '\n';
out << optionalBlankLine() << holTable.render() << '\n';
}
}
output = out.str ();
output = out.str();
return rc;
}
////////////////////////////////////////////////////////////////////////////////
std::string CmdCalendar::renderMonths (
int firstMonth,
int firstYear,
const Datetime& today,
std::vector <Task>& all,
int monthsPerLine)
{
auto& config = Context::getContext ().config;
std::string CmdCalendar::renderMonths(int firstMonth, int firstYear, const Datetime& today,
std::vector<Task>& all, int monthsPerLine) {
auto& config = Context::getContext().config;
// What day of the week does the user consider the first?
auto weekStart = Datetime::dayOfWeek (config.get ("weekstart"));
auto weekStart = Datetime::dayOfWeek(config.get("weekstart"));
if (weekStart != 0 && weekStart != 1)
throw std::string ("The 'weekstart' configuration variable may only contain 'Sunday' or 'Monday'.");
throw std::string(
"The 'weekstart' configuration variable may only contain 'Sunday' or 'Monday'.");
// Build table for the number of months to be displayed.
Table view;
setHeaderUnderline (view);
view.width (Context::getContext ().getWidth ());
for (int i = 0 ; i < (monthsPerLine * 8); i += 8)
{
if (weekStart == 1)
{
view.add ("", false);
view.add (utf8_substr (Datetime::dayName (1), 0, 2), false);
view.add (utf8_substr (Datetime::dayName (2), 0, 2), false);
view.add (utf8_substr (Datetime::dayName (3), 0, 2), false);
view.add (utf8_substr (Datetime::dayName (4), 0, 2), false);
view.add (utf8_substr (Datetime::dayName (5), 0, 2), false);
view.add (utf8_substr (Datetime::dayName (6), 0, 2), false);
view.add (utf8_substr (Datetime::dayName (0), 0, 2), false);
}
else
{
view.add ("", false);
view.add (utf8_substr (Datetime::dayName (0), 0, 2), false);
view.add (utf8_substr (Datetime::dayName (1), 0, 2), false);
view.add (utf8_substr (Datetime::dayName (2), 0, 2), false);
view.add (utf8_substr (Datetime::dayName (3), 0, 2), false);
view.add (utf8_substr (Datetime::dayName (4), 0, 2), false);
view.add (utf8_substr (Datetime::dayName (5), 0, 2), false);
view.add (utf8_substr (Datetime::dayName (6), 0, 2), false);
setHeaderUnderline(view);
view.width(Context::getContext().getWidth());
for (int i = 0; i < (monthsPerLine * 8); i += 8) {
if (weekStart == 1) {
view.add("", false);
view.add(utf8_substr(Datetime::dayName(1), 0, 2), false);
view.add(utf8_substr(Datetime::dayName(2), 0, 2), false);
view.add(utf8_substr(Datetime::dayName(3), 0, 2), false);
view.add(utf8_substr(Datetime::dayName(4), 0, 2), false);
view.add(utf8_substr(Datetime::dayName(5), 0, 2), false);
view.add(utf8_substr(Datetime::dayName(6), 0, 2), false);
view.add(utf8_substr(Datetime::dayName(0), 0, 2), false);
} else {
view.add("", false);
view.add(utf8_substr(Datetime::dayName(0), 0, 2), false);
view.add(utf8_substr(Datetime::dayName(1), 0, 2), false);
view.add(utf8_substr(Datetime::dayName(2), 0, 2), false);
view.add(utf8_substr(Datetime::dayName(3), 0, 2), false);
view.add(utf8_substr(Datetime::dayName(4), 0, 2), false);
view.add(utf8_substr(Datetime::dayName(5), 0, 2), false);
view.add(utf8_substr(Datetime::dayName(6), 0, 2), false);
}
}
// At most, we need 6 rows.
view.addRow ();
view.addRow ();
view.addRow ();
view.addRow ();
view.addRow ();
view.addRow ();
view.addRow();
view.addRow();
view.addRow();
view.addRow();
view.addRow();
view.addRow();
// Set number of days per month, months to render, and years to render.
std::vector<int> years;
@@ -515,153 +451,126 @@ std::string CmdCalendar::renderMonths (
std::vector<int> daysInMonth;
int thisYear = firstYear;
int thisMonth = firstMonth;
for (int i = 0 ; i < monthsPerLine ; i++)
{
if (thisMonth < 13)
{
years.push_back (thisYear);
}
else
{
for (int i = 0; i < monthsPerLine; i++) {
if (thisMonth < 13) {
years.push_back(thisYear);
} else {
thisMonth -= 12;
years.push_back (++thisYear);
years.push_back(++thisYear);
}
months.push_back (thisMonth);
daysInMonth.push_back (Datetime::daysInMonth (thisYear, thisMonth++));
months.push_back(thisMonth);
daysInMonth.push_back(Datetime::daysInMonth(thisYear, thisMonth++));
}
auto row = 0;
Color color_today (config.get ("color.calendar.today"));
Color color_due (config.get ("color.calendar.due"));
Color color_duetoday (config.get ("color.calendar.due.today"));
Color color_overdue (config.get ("color.calendar.overdue"));
Color color_weekend (config.get ("color.calendar.weekend"));
Color color_holiday (config.get ("color.calendar.holiday"));
Color color_scheduled (config.get ("color.calendar.scheduled"));
Color color_weeknumber (config.get ("color.calendar.weeknumber"));
Color color_today(config.get("color.calendar.today"));
Color color_due(config.get("color.calendar.due"));
Color color_duetoday(config.get("color.calendar.due.today"));
Color color_overdue(config.get("color.calendar.overdue"));
Color color_weekend(config.get("color.calendar.weekend"));
Color color_holiday(config.get("color.calendar.holiday"));
Color color_scheduled(config.get("color.calendar.scheduled"));
Color color_weeknumber(config.get("color.calendar.weeknumber"));
// Loop through months to be added on this line.
for (int mpl = 0; mpl < monthsPerLine ; mpl++)
{
for (int mpl = 0; mpl < monthsPerLine; mpl++) {
// Reset row counter for subsequent months
if (mpl != 0)
row = 0;
if (mpl != 0) row = 0;
// Loop through days in month and add to table.
for (int d = 1; d <= daysInMonth[mpl]; ++d)
{
Datetime date (years[mpl], months[mpl], d);
auto dow = date.dayOfWeek ();
auto woy = date.week ();
for (int d = 1; d <= daysInMonth[mpl]; ++d) {
Datetime date(years[mpl], months[mpl], d);
auto dow = date.dayOfWeek();
auto woy = date.week();
if (config.getBoolean ("displayweeknumber"))
view.set (row,
(8 * mpl),
// Make sure the week number is always 4 columns, space-padded.
format ((woy < 10 ? " {1}" : " {1}"), woy),
color_weeknumber);
if (config.getBoolean("displayweeknumber"))
view.set(row, (8 * mpl),
// Make sure the week number is always 4 columns, space-padded.
format((woy < 10 ? " {1}" : " {1}"), woy), color_weeknumber);
// Calculate column id.
auto thisCol = dow + // 0 = Sunday
(weekStart == 1 ? 0 : 1) + // Offset for weekStart
(8 * mpl); // Columns in 1 month
if (thisCol == (8 * mpl))
thisCol += 7;
if (thisCol == (8 * mpl)) thisCol += 7;
view.set (row, thisCol, d);
view.set(row, thisCol, d);
if (Context::getContext ().color ())
{
if (Context::getContext().color()) {
Color cellColor;
// colorize weekends
if (dow == 0 || dow == 6)
cellColor.blend (color_weekend);
if (dow == 0 || dow == 6) cellColor.blend(color_weekend);
// colorize holidays
if (config.get ("calendar.holidays") != "none")
{
auto dateFormat = config.get ("dateformat.holiday");
for (auto& hol : config)
{
if (hol.first.substr (0, 8) == "holiday.")
{
if (hol.first.substr (hol.first.size () - 4) == "date")
{
if (config.get("calendar.holidays") != "none") {
auto dateFormat = config.get("dateformat.holiday");
for (auto& hol : config) {
if (hol.first.substr(0, 8) == "holiday.") {
if (hol.first.substr(hol.first.size() - 4) == "date") {
auto value = hol.second;
Datetime holDate (value.c_str (), dateFormat);
if (holDate.sameDay (date))
cellColor.blend (color_holiday);
Datetime holDate(value.c_str(), dateFormat);
if (holDate.sameDay(date)) cellColor.blend(color_holiday);
}
if (hol.first.substr (hol.first.size () - 5) == "start" &&
config.has ("holiday." + hol.first.substr (8, hol.first.size () - 14) + ".end"))
{
if (hol.first.substr(hol.first.size() - 5) == "start" &&
config.has("holiday." + hol.first.substr(8, hol.first.size() - 14) + ".end")) {
auto start = hol.second;
auto end = config.get ("holiday." + hol.first.substr (8, hol.first.size () - 14) + ".end");
Datetime holStart (start.c_str (), dateFormat);
Datetime holEnd (end.c_str (), dateFormat);
if (holStart <= date && date <= holEnd)
cellColor.blend (color_holiday);
auto end =
config.get("holiday." + hol.first.substr(8, hol.first.size() - 14) + ".end");
Datetime holStart(start.c_str(), dateFormat);
Datetime holEnd(end.c_str(), dateFormat);
if (holStart <= date && date <= holEnd) cellColor.blend(color_holiday);
}
}
}
}
// colorize today
if (today.sameDay (date))
cellColor.blend (color_today);
if (today.sameDay(date)) cellColor.blend(color_today);
// colorize due and scheduled tasks
if (config.get ("calendar.details") != "none")
{
config.set ("due", 0);
config.set ("scheduled", 0);
if (config.get("calendar.details") != "none") {
config.set("due", 0);
config.set("scheduled", 0);
// if a date has a task that is due on that day, the due color
// takes precedence over the scheduled color
bool coloredWithDue = false;
for (auto& task : all)
{
auto status = task.getStatus ();
if ((status == Task::pending ||
status == Task::waiting ) &&
!task.hasTag ("nocal"))
{
if(task.has("scheduled") && !coloredWithDue) {
std::string scheduled = task.get ("scheduled");
Datetime scheduleddmy (strtoll (scheduled.c_str(), nullptr, 10));
for (auto& task : all) {
auto status = task.getStatus();
if ((status == Task::pending || status == Task::waiting) && !task.hasTag("nocal")) {
if (task.has("scheduled") && !coloredWithDue) {
std::string scheduled = task.get("scheduled");
Datetime scheduleddmy(strtoll(scheduled.c_str(), nullptr, 10));
if (scheduleddmy.sameDay (date))
{
if (scheduleddmy.sameDay(date)) {
cellColor.blend(color_scheduled);
}
}
if(task.has("due")) {
std::string due = task.get ("due");
Datetime duedmy (strtoll (due.c_str(), nullptr, 10));
if (task.has("due")) {
std::string due = task.get("due");
Datetime duedmy(strtoll(due.c_str(), nullptr, 10));
if (duedmy.sameDay (date))
{
if (duedmy.sameDay(date)) {
coloredWithDue = true;
switch (task.getDateState ("due"))
{
case Task::dateNotDue:
break;
switch (task.getDateState("due")) {
case Task::dateNotDue:
break;
case Task::dateAfterToday:
cellColor.blend (color_due);
break;
case Task::dateAfterToday:
cellColor.blend(color_due);
break;
case Task::dateLaterToday:
cellColor.blend (color_duetoday);
break;
case Task::dateLaterToday:
cellColor.blend(color_duetoday);
break;
case Task::dateEarlierToday:
case Task::dateBeforeToday:
cellColor.blend (color_overdue);
break;
case Task::dateEarlierToday:
case Task::dateBeforeToday:
cellColor.blend(color_overdue);
break;
}
}
}
@@ -669,19 +578,17 @@ std::string CmdCalendar::renderMonths (
}
}
view.set (row, thisCol, cellColor);
view.set(row, thisCol, cellColor);
}
// Check for end of week, and...
int eow = 6;
if (weekStart == 1)
eow = 0;
if (dow == eow && d < daysInMonth[mpl])
row++;
if (weekStart == 1) eow = 0;
if (dow == eow && d < daysInMonth[mpl]) row++;
}
}
return view.render ();
return view.render();
}
////////////////////////////////////////////////////////////////////////////////

View File

@@ -27,20 +27,20 @@
#ifndef INCLUDED_CMDCALENDAR
#define INCLUDED_CMDCALENDAR
#include <string>
#include <vector>
#include <Command.h>
#include <Datetime.h>
#include <Task.h>
#include <Command.h>
class CmdCalendar : public Command
{
public:
CmdCalendar ();
int execute (std::string&);
#include <string>
#include <vector>
private:
std::string renderMonths (int, int, const Datetime&, std::vector <Task>&, int);
class CmdCalendar : public Command {
public:
CmdCalendar();
int execute(std::string&);
private:
std::string renderMonths(int, int, const Datetime&, std::vector<Task>&, int);
};
#endif

View File

@@ -28,161 +28,137 @@
// cmake.h include header must come first
#include <CmdColor.h>
#include <sstream>
#include <Table.h>
#include <Context.h>
#include <main.h>
#include <Color.h>
#include <Context.h>
#include <Table.h>
#include <format.h>
#include <main.h>
#include <shared.h>
#include <sstream>
////////////////////////////////////////////////////////////////////////////////
CmdColor::CmdColor ()
{
_keyword = "colors";
_usage = "task colors [sample | legend]";
_description = "All colors, a sample, or a legend";
_read_only = true;
_displays_id = false;
_needs_gc = false;
_uses_context = false;
_accepts_filter = false;
CmdColor::CmdColor() {
_keyword = "colors";
_usage = "task colors [sample | legend]";
_description = "All colors, a sample, or a legend";
_read_only = true;
_displays_id = false;
_needs_gc = false;
_uses_context = false;
_accepts_filter = false;
_accepts_modifications = false;
_accepts_miscellaneous = true;
_category = Command::Category::misc;
_category = Command::Category::misc;
}
////////////////////////////////////////////////////////////////////////////////
int CmdColor::execute (std::string& output)
{
int CmdColor::execute(std::string& output) {
int rc = 0;
// Get the non-attribute, non-fancy command line arguments.
auto legend = false;
auto words = Context::getContext ().cli2.getWords ();
auto words = Context::getContext().cli2.getWords();
for (auto& word : words)
if (closeEnough ("legend", word))
legend = true;
if (closeEnough("legend", word)) legend = true;
std::stringstream out;
if (Context::getContext ().color ())
{
if (Context::getContext().color()) {
// If the description contains 'legend', show all the colors currently in
// use.
if (legend)
{
if (legend) {
out << "\nHere are the colors currently in use:\n";
Table view;
view.width (Context::getContext ().getWidth ());
if (Context::getContext ().config.getBoolean ("color"))
view.forceColor ();
view.add ("Color");
view.add ("Definition");
view.width(Context::getContext().getWidth());
if (Context::getContext().config.getBoolean("color")) view.forceColor();
view.add("Color");
view.add("Definition");
for (auto& item : Context::getContext ().config)
{
for (auto& item : Context::getContext().config) {
// Skip items with 'color' in their name, that are not referring to
// actual colors.
if (item.first != "_forcecolor" &&
item.first != "color" &&
item.first.find ("color") == 0)
{
Color color (Context::getContext ().config.get (item.first));
int row = view.addRow ();
view.set (row, 0, item.first, color);
view.set (row, 1, item.second, color);
if (item.first != "_forcecolor" && item.first != "color" && item.first.find("color") == 0) {
Color color(Context::getContext().config.get(item.first));
int row = view.addRow();
view.set(row, 0, item.first, color);
view.set(row, 1, item.second, color);
}
}
out << view.render ()
<< '\n';
out << view.render() << '\n';
}
// If there is something in the description, then assume that is a color,
// and display it as a sample.
else if (words.size ())
{
Color one ("black on bright yellow");
Color two ("underline cyan on bright blue");
Color three ("color214 on color202");
Color four ("rgb150 on rgb020");
Color five ("underline grey10 on grey3");
Color six ("red on color173");
else if (words.size()) {
Color one("black on bright yellow");
Color two("underline cyan on bright blue");
Color three("color214 on color202");
Color four("rgb150 on rgb020");
Color five("underline grey10 on grey3");
Color six("red on color173");
std::string swatch;
for (auto word = words.begin (); word != words.end (); ++word)
{
if (word != words.begin ())
swatch += ' ';
for (auto word = words.begin(); word != words.end(); ++word) {
if (word != words.begin()) swatch += ' ';
swatch += *word;
}
Color sample (swatch);
Color sample(swatch);
out << '\n'
<< "Use this command to see how colors are displayed by your terminal.\n"
<< "\n\n"
<< "16-color usage (supports underline, bold text, bright background):\n"
<< " " << one.colorize ("task color black on bright yellow") << '\n'
<< " " << two.colorize ("task color underline cyan on bright blue") << '\n'
<< " " << one.colorize("task color black on bright yellow") << '\n'
<< " " << two.colorize("task color underline cyan on bright blue") << '\n'
<< '\n'
<< "256-color usage (supports underline):\n"
<< " " << three.colorize ("task color color214 on color202") << '\n'
<< " " << four.colorize ("task color rgb150 on rgb020") << '\n'
<< " " << five.colorize ("task color underline grey10 on grey3") << '\n'
<< " " << six.colorize ("task color red on color173") << '\n'
<< " " << three.colorize("task color color214 on color202") << '\n'
<< " " << four.colorize("task color rgb150 on rgb020") << '\n'
<< " " << five.colorize("task color underline grey10 on grey3") << '\n'
<< " " << six.colorize("task color red on color173") << '\n'
<< '\n'
<< "Your sample:\n\n"
<< " " << sample.colorize ("task color " + swatch) << "\n\n";
<< " " << sample.colorize("task color " + swatch) << "\n\n";
}
// Show all supported colors. Possibly show some unsupported ones too.
else
{
else {
out << '\n'
<< "Basic colors\n"
<< ' ' << Color::colorize (" black ", "black")
<< ' ' << Color::colorize (" red ", "red")
<< ' ' << Color::colorize (" blue ", "blue")
<< ' ' << Color::colorize (" green ", "green")
<< ' ' << Color::colorize (" magenta ", "magenta")
<< ' ' << Color::colorize (" cyan ", "cyan")
<< ' ' << Color::colorize (" yellow ", "yellow")
<< ' ' << Color::colorize (" white ", "white")
<< '\n'
<< ' ' << Color::colorize (" black ", "white on black")
<< ' ' << Color::colorize (" red ", "white on red")
<< ' ' << Color::colorize (" blue ", "white on blue")
<< ' ' << Color::colorize (" green ", "black on green")
<< ' ' << Color::colorize (" magenta ", "black on magenta")
<< ' ' << Color::colorize (" cyan ", "black on cyan")
<< ' ' << Color::colorize (" yellow ", "black on yellow")
<< ' ' << Color::colorize (" white ", "black on white")
<< "\n\n";
<< ' ' << Color::colorize(" black ", "black") << ' ' << Color::colorize(" red ", "red")
<< ' ' << Color::colorize(" blue ", "blue") << ' ' << Color::colorize(" green ", "green")
<< ' ' << Color::colorize(" magenta ", "magenta") << ' '
<< Color::colorize(" cyan ", "cyan") << ' ' << Color::colorize(" yellow ", "yellow")
<< ' ' << Color::colorize(" white ", "white") << '\n'
<< ' ' << Color::colorize(" black ", "white on black") << ' '
<< Color::colorize(" red ", "white on red") << ' '
<< Color::colorize(" blue ", "white on blue") << ' '
<< Color::colorize(" green ", "black on green") << ' '
<< Color::colorize(" magenta ", "black on magenta") << ' '
<< Color::colorize(" cyan ", "black on cyan") << ' '
<< Color::colorize(" yellow ", "black on yellow") << ' '
<< Color::colorize(" white ", "black on white") << "\n\n";
out << "Effects\n"
<< ' ' << Color::colorize (" red ", "red")
<< ' ' << Color::colorize (" bold red ", "bold red")
<< ' ' << Color::colorize (" underline on blue ", "underline on blue")
<< ' ' << Color::colorize (" on green ", "black on green")
<< ' ' << Color::colorize (" on bright green ", "black on bright green")
<< ' ' << Color::colorize (" inverse ", "inverse")
<< "\n\n";
<< ' ' << Color::colorize(" red ", "red") << ' '
<< Color::colorize(" bold red ", "bold red") << ' '
<< Color::colorize(" underline on blue ", "underline on blue") << ' '
<< Color::colorize(" on green ", "black on green") << ' '
<< Color::colorize(" on bright green ", "black on bright green") << ' '
<< Color::colorize(" inverse ", "inverse") << "\n\n";
// 16 system colors.
out << "color0 - color15"
<< '\n'
<< " 0 1 2 . . .\n";
for (int r = 0; r < 2; ++r)
{
out << "color0 - color15" << '\n' << " 0 1 2 . . .\n";
for (int r = 0; r < 2; ++r) {
out << " ";
for (int c = 0; c < 8; ++c)
{
for (int c = 0; c < 8; ++c) {
std::stringstream s;
s << "on color" << (r*8 + c);
out << Color::colorize (" ", s.str ());
s << "on color" << (r * 8 + c);
out << Color::colorize(" ", s.str());
}
out << '\n';
@@ -191,43 +167,40 @@ int CmdColor::execute (std::string& output)
out << " . . . 15\n\n";
// Color cube.
out << "Color cube rgb"
<< Color::colorize ("0", "bold red")
<< Color::colorize ("0", "bold green")
<< Color::colorize ("0", "bold blue")
<< " - rgb"
<< Color::colorize ("5", "bold red")
<< Color::colorize ("5", "bold green")
<< Color::colorize ("5", "bold blue")
<< " (also color16 - color231)"
out << "Color cube rgb" << Color::colorize("0", "bold red")
<< Color::colorize("0", "bold green") << Color::colorize("0", "bold blue") << " - rgb"
<< Color::colorize("5", "bold red") << Color::colorize("5", "bold green")
<< Color::colorize("5", "bold blue") << " (also color16 - color231)" << '\n'
<< " "
<< Color::colorize(
"0 "
"1 "
"2 "
"3 "
"4 "
"5",
"bold red")
<< '\n'
<< " " << Color::colorize ("0 "
"1 "
"2 "
"3 "
"4 "
"5", "bold red")
<< '\n'
<< " " << Color::colorize ("0 1 2 3 4 5 "
"0 1 2 3 4 5 "
"0 1 2 3 4 5 "
"0 1 2 3 4 5 "
"0 1 2 3 4 5 "
"0 1 2 3 4 5", "bold blue")
<< " "
<< Color::colorize(
"0 1 2 3 4 5 "
"0 1 2 3 4 5 "
"0 1 2 3 4 5 "
"0 1 2 3 4 5 "
"0 1 2 3 4 5 "
"0 1 2 3 4 5",
"bold blue")
<< '\n';
char label [12];
for (int g = 0; g < 6; ++g)
{
snprintf (label, 12, " %d", g);
out << Color::colorize (label, "bold green");
for (int r = 0; r < 6; ++r)
{
for (int b = 0; b < 6; ++b)
{
char label[12];
for (int g = 0; g < 6; ++g) {
snprintf(label, 12, " %d", g);
out << Color::colorize(label, "bold green");
for (int r = 0; r < 6; ++r) {
for (int b = 0; b < 6; ++b) {
std::stringstream s;
s << "on rgb" << r << g << b;
out << Color::colorize (" ", s.str ());
out << Color::colorize(" ", s.str());
}
out << ' ';
@@ -242,25 +215,23 @@ int CmdColor::execute (std::string& output)
out << "Gray ramp gray0 - gray23 (also color232 - color255)\n"
<< " 0 1 2 . . . . . . 23\n"
<< " ";
for (int g = 0; g < 24; ++g)
{
for (int g = 0; g < 24; ++g) {
std::stringstream s;
s << "on gray" << g;
out << Color::colorize (" ", s.str ());
out << Color::colorize(" ", s.str());
}
out << "\n\n"
<< "Try running 'task color white on red'.\n"
<< '\n';
}
}
else
{
out << "Color is currently turned off in your .taskrc file. To enable color, remove the line 'color=off', or change the 'off' to 'on'.\n";
} else {
out << "Color is currently turned off in your .taskrc file. To enable color, remove the line "
"'color=off', or change the 'off' to 'on'.\n";
rc = 1;
}
output = out.str ();
output = out.str();
return rc;
}

Some files were not shown because too many files have changed in this diff Show More