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

View file

@ -25,4 +25,3 @@ jobs:
cd task-*.*.* &&
cmake -S. -Bbuild &&
cmake --build build --target task_executable

View file

@ -44,7 +44,7 @@
- Akash Shanmugaraj
- Andrew Savchenko
- Dathan Bennett
- Dathan Bennett
- Dustin Mitchell
- dbr/Ben
- Felix Schurk
@ -2785,4 +2785,3 @@ regular usage to determine which features were needed or unnecessary.]
- Usage.
------ start -----------------------------------

View file

@ -21,4 +21,3 @@ THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
SOFTWARE.

View file

@ -58,4 +58,3 @@
/* Undefine this to eliminate the execute command */
#define HAVE_EXECUTE 1

View file

@ -227,14 +227,14 @@ An example is to make two reports share the same description:
$ task config -- report.ls.description rc.report.list.description
This sets the description for the `ls` report to be a reference to the description of the `list` report.
This sets the description for the `ls` report to be a reference to the description of the `list` report.
This reference is not evaluated when the entry is written, but is evaluated every time the value is read, thus providing late-bound behavior.
Then if the description of the `list` report changes, so does that of the `ls` report automatically.
## Implementation Details
These notes list a series of anticipated changes to the codebase.
These notes list a series of anticipated changes to the codebase.
- The `src/columns/Col*` objects will implement type-specific and attribute-specific DOM support.
DOM reference lookup will defer to the column objects first.

View file

@ -466,4 +466,3 @@ Given that the configuration is not present in the JSON format of a task, any fi
This means that if a task contains a UDA, unless the meaning of it is understood, it MUST be preserved.
UDAs may have one of four types: string, numeric, date and duration.

View file

@ -101,7 +101,7 @@ Then configure Taskwarrior with:
$ task config sync.gcp.bucket <bucket-name>
.fi
However you can bring your own service account credentials if your
However you can bring your own service account credentials if your
`application-default` is already being used by some other application
To begin, navigate to the "IAM and Admin" section in the Navigation Menu, then select "Roles."
@ -112,8 +112,8 @@ Provide an appropriate name and description for the new role.
Add permissions to your new role using the filter "Service:storage" (not the "Filter permissions by role" input box).
Select the following permissions:
- storage.buckets.create
- storage.buckets.get
- storage.buckets.create
- storage.buckets.get
- storage.buckets.update
- storage.objects.create
- storage.objects.delete

View file

@ -96,4 +96,3 @@ color.sync.rejected=rgb103
# Command: undo
color.undo.before=rgb103
color.undo.after=rgb305

View file

@ -98,4 +98,3 @@ color.sync.rejected=red
# Command: undo
color.undo.after=green
color.undo.before=red

View file

@ -95,4 +95,3 @@ color.sync.rejected=color9
# Command: undo
color.undo.after=color2
color.undo.before=color1

View file

@ -95,4 +95,3 @@ color.sync.rejected=rgb004
# Command: undo
color.undo.after=rgb035
color.undo.before=rgb013

View file

@ -95,4 +95,3 @@ color.sync.rejected=gray5 on gray23
# Command: undo
color.undo.before=white on black
color.undo.after=black on white

View file

@ -95,4 +95,3 @@ color.sync.rejected=gray23
# Command: undo
color.undo.before=rgb013
color.undo.after=rgb035

View file

@ -95,4 +95,3 @@ color.sync.rejected=rgb200
# Command: undo
color.undo.after=rgb511
color.undo.before=rgb200

View file

@ -95,4 +95,3 @@ color.sync.rejected=rgb103
# Command: undo
color.undo.before=rgb103
color.undo.after=rgb305

View file

@ -95,4 +95,3 @@ color.sync.rejected=rgb110
# Command: undo
color.undo.before=rgb021
color.undo.after=rgb042

View file

@ -95,4 +95,3 @@ color.sync.rejected=red
# Command: undo
color.undo.before=yellow
color.undo.after=green

View file

@ -65,7 +65,7 @@ color.due.today=on rgb353
color.overdue=on rgb544
# Report: burndown
color.burndown.pending=on rgb411
color.burndown.pending=on rgb411
color.burndown.started=on rgb550
color.burndown.done=on rgb151
@ -95,4 +95,3 @@ color.sync.rejected=red
# Command: undo
color.undo.before=yellow
color.undo.after=green

View file

@ -98,4 +98,3 @@ color.sync.rejected=
# Command: undo
color.undo.after=
color.undo.before=

View file

@ -6,4 +6,3 @@ do
echo $locale
../../scripts/add-ons/update-holidays.pl --locale $locale --file holidays.${locale}.rc
done

View file

@ -112,4 +112,3 @@ color.sync.rejected=color13
# Command: undo
color.undo.after=color2
color.undo.before=color1

View file

@ -112,4 +112,3 @@ color.sync.rejected=color13
# Command: undo
color.undo.after=color2
color.undo.before=color1

View file

@ -25,4 +25,3 @@ Using a solarized light terminal, run the following:
Note that for the solarized themes, the terminal color palette needs to be set
to specific colors.

View file

@ -30,4 +30,3 @@ task rc:x add Deleted_1
task rc:x 14 mod depends:13
task rc:x 15 delete

View file

@ -7,5 +7,3 @@ configure_file(run_perf run_perf)
add_custom_target (performance ${CMAKE_BINARY_DIR}/performance/run_perf
DEPENDS task_executable
WORKING_DIRECTORY ${CMAKE_BINARY_DIR}/performance)

View file

@ -29,10 +29,13 @@ def parse_perf(input):
tests[command] = []
# Parse concatenated run_perf output
for i in re.findall("^ - task %s\\.\\.\\.\n"
"Perf task ([^ ]+) ([^ ]+) ([^ ]+) (.+)$"
% command, input, re.MULTILINE):
info = i[0:3] + ({k:v for k, v in (i.split(":") for i in i[-1].split())},)
for i in re.findall(
"^ - task %s\\.\\.\\.\n"
"Perf task ([^ ]+) ([^ ]+) ([^ ]+) (.+)$" % command,
input,
re.MULTILINE,
):
info = i[0:3] + ({k: v for k, v in (i.split(":") for i in i[-1].split())},)
pt = TaskPerf(*info)
tests[command].append(pt)
return tests
@ -61,8 +64,14 @@ with open(sys.argv[2], "r") as fh:
tests_cur = parse_perf(fh.read())
best_cur = get_best(tests_cur)
print("Previous: %s (%s)" % (tests_prev[COMMANDS[0]][0].version, tests_prev[COMMANDS[0]][0].commit))
print("Current: %s (%s)" % (tests_cur[COMMANDS[0]][0].version, tests_cur[COMMANDS[0]][0].commit))
print(
"Previous: %s (%s)"
% (tests_prev[COMMANDS[0]][0].version, tests_prev[COMMANDS[0]][0].commit)
)
print(
"Current: %s (%s)"
% (tests_cur[COMMANDS[0]][0].version, tests_cur[COMMANDS[0]][0].commit)
)
for test in COMMANDS:
print("# %s:" % test)
@ -76,7 +85,9 @@ for test in COMMANDS:
else:
percentage = "0%"
pad = max(map(len, (k, best_prev[test][k], best_cur[test][k], diff, percentage)))
pad = max(
map(len, (k, best_prev[test][k], best_cur[test][k], diff, percentage))
)
out[0] += " %s" % k.rjust(pad)
out[1] += " %s" % best_prev[test][k].rjust(pad)
out[2] += " %s" % best_cur[test][k].rjust(pad)

View file

@ -50,4 +50,3 @@ $TASK rc.debug:1 rc:perf.rc import ${CMAKE_SOURCE_DIR}/performance/export.json 2
echo 'End'
exit 0

View file

@ -3963,7 +3963,7 @@ GLOUCESTER Give me the letter, sir.
EDMUND I shall offend, either to detain or give it. The contents, as in part I understand them, are to blame.
GLOUCESTER Lets see, lets see.
EDMUND I hope, for my brothers justification, he wrote this but as an essay or taste of my virtue.
GLOUCESTER This policy and reverence of age makes the world bitter to the best of our times, keeps our fortunes from us till our oldness cannot relish them. I begin to find an idle and fond bondage in the oppression of aged tyranny, who sways, not as it hath power, but as it is suffered. Come to me, that of this I may speak more. If our father would sleep till I waked him, you should enjoy half his revenue for ever, and live the beloved of your brother,
GLOUCESTER This policy and reverence of age makes the world bitter to the best of our times, keeps our fortunes from us till our oldness cannot relish them. I begin to find an idle and fond bondage in the oppression of aged tyranny, who sways, not as it hath power, but as it is suffered. Come to me, that of this I may speak more. If our father would sleep till I waked him, you should enjoy half his revenue for ever, and live the beloved of your brother,
EDMUND It was not brought me, my lord, theres the cunning of it, I found it thrown in at the casement of my closet.
GLOUCESTER You know the character to be your brothers?
EDMUND If the matter were good, my lord, I durst swear it were his, but, in respect of that, I would fain think it were not.

View file

@ -8,4 +8,3 @@ install (DIRECTORY add-ons
FILE_PERMISSIONS OWNER_READ OWNER_WRITE OWNER_EXECUTE
GROUP_READ GROUP_EXECUTE
WORLD_READ WORLD_EXECUTE)

View file

@ -221,4 +221,3 @@ if (open my $fh, '>:utf8', $file)
exit 0;
################################################################################

View file

@ -24,4 +24,3 @@ Expected Permissions
Interface
Each hook script has a unique interface. This is documented in the example
scripts here.

View file

@ -32,4 +32,3 @@ fi
echo Shadow file $SHADOW_FILE updated.
exit 0

View file

@ -14,4 +14,3 @@ echo 'on-launch'
# - 0: JSON ignored, non-JSON is feedback.
# - non-0: JSON ignored, non-JSON is error.
exit 0

View file

@ -1,4 +1,4 @@
" Vim support file to detect Taskwarrior data and configuration files and
" Vim support file to detect Taskwarrior data and configuration files and
" single task edits
"
" Maintainer: John Florian <jflorian@doubledog.org>

View file

@ -289,4 +289,4 @@ _task_default() {
}
_arguments -s -S \
"*::task default:_task_default"
"*::task default:_task_default"

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

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