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

@ -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

@ -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

@ -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

@ -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,9 +29,12 @@ 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):
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)
@ -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

@ -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

File diff suppressed because it is too large Load diff

View file

@ -26,16 +26,16 @@
#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
{
class A2 {
public:
A2(const std::string&, Lexer::Type);
A2(const A2&);
@ -56,8 +56,7 @@ public:
};
// Represents the command line.
class CLI2
{
class CLI2 {
public:
static int minimumMatchLength;
@ -122,4 +121,3 @@ public:
};
#endif

File diff suppressed because it is too large Load diff

View file

@ -27,21 +27,21 @@
#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
{
class Context {
public:
Context() = default; // Default constructor
~Context(); // Destructor

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,22 +63,17 @@
// 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();
// rc. --> context.config
if (len > 3 &&
! name.compare (0, 3, "rc.", 3))
{
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 ())
{
if (c != Context::getContext().config.end()) {
value = Variant(c->second);
return true;
}
@ -86,54 +82,40 @@ bool getDOM (const std::string& name, Variant& value)
}
// tw.*
if (len > 3 &&
! name.compare (0, 3, "tw.", 3))
{
if (name == "tw.syncneeded")
{
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")
{
} 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);
return true;
}
else if (name == "tw.width")
{
} 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")
{
} 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")
{
else if (name == "tw.version") {
value = Variant(VERSION);
return true;
}
@ -142,37 +124,26 @@ bool getDOM (const std::string& name, Variant& value)
}
// context.*
if (len > 8 &&
! name.compare (0, 8, "context.", 8))
{
if (name == "context.program")
{
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);
return true;
}
else if (name == "context.width")
{
} 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")
{
} else if (name == "context.height") {
value = Variant(static_cast<int>(Context::getContext().terminal_height
? Context::getContext().terminal_height
: Context::getContext().getHeight()));
@ -183,19 +154,15 @@ 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")
{
if (name == "system.version") {
value = Variant(VERSION);
return true;
}
// OS type.
else if (name == "system.os")
{
else if (name == "system.os") {
value = Variant(osName());
return true;
}
@ -239,21 +206,17 @@ 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")
{
if (task && name == "id") {
value = Variant(static_cast<int>(task->id));
return true;
}
if (task && name == "urgency")
{
if (task && name == "urgency") {
value = Variant(task->urgency_c());
return true;
}
@ -272,93 +235,72 @@ bool getDOM (const std::string& name, const Task* task, Variant& value)
// 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)
{
} 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;
if (id && (!task || id != task->id)) {
if (Context::getContext().tdb2.get(id, loaded_task)) reloaded = true;
}
// Eat elements[0]/ID.
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();
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")
{
if (size == 1 && canonical == "id") {
value = Variant(static_cast<int>(ref->id));
return true;
}
if (size == 1 && canonical == "urgency")
{
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")
{
if (size == 1 && canonical == "status") {
value = Variant(ref->statusToText(ref->getStatus()));
return true;
}
Column* column = Context::getContext().columns[canonical];
if (size == 1 && column)
{
if (column->is_uda () && ! ref->has (canonical))
{
if (size == 1 && column) {
if (column->is_uda() && !ref->has(canonical)) {
value = Variant("");
return true;
}
if (column->type () == "date")
{
if (column->type() == "date") {
auto numeric = ref->get_date(canonical);
if (numeric == 0)
value = Variant("");
else
value = Variant(numeric, Variant::type_date);
}
else if (column->type () == "duration" || canonical == "recur")
{
} else if (column->type() == "duration" || canonical == "recur") {
auto period = ref->get(canonical);
Duration iso;
@ -367,8 +309,7 @@ bool getDOM (const std::string& name, const Task* task, Variant& value)
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")
} else if (column->type() == "numeric")
value = Variant(ref->get_float(canonical));
else
value = Variant(ref->get(canonical));
@ -376,54 +317,65 @@ bool getDOM (const std::string& name, const Task* task, Variant& value)
return true;
}
if (size == 2 && canonical == "tags")
{
if (size == 2 && canonical == "tags") {
value = Variant(ref->hasTag(elements[1]) ? elements[1] : "");
return true;
}
if (size == 2 && column && column->type () == "date")
{
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 (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")
{
if (size == 2 && elements[0] == "annotations" && elements[1] == "count") {
value = Variant(static_cast<int>(ref->getAnnotationCount()));
return true;
}
if (size == 3 && elements[0] == "annotations")
{
if (size == 3 && elements[0] == "annotations") {
auto annos = ref->getAnnotations();
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")
{
} else if (elements[2] == "description") {
value = Variant(i.second);
return true;
}
@ -431,18 +383,15 @@ bool getDOM (const std::string& name, const Task* task, Variant& value)
}
}
if (size == 4 && elements[0] == "annotations" && elements[2] == "entry")
{
if (size == 4 && elements[0] == "annotations" && elements[2] == "entry") {
auto annos = ref->getAnnotations();
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
@ -453,15 +402,34 @@ bool getDOM (const std::string& name, const Task* task, Variant& value)
// <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; }
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;
}
}
}
}
@ -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 ();
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
{
bool DOM::valid(const std::string& reference) const {
return _node && _node->find(reference) != nullptr;
}
////////////////////////////////////////////////////////////////////////////////
Variant DOM::get (const std::string& reference) const
{
Variant DOM::get(const std::string& reference) const {
Variant v("");
if (_node)
{
if (_node) {
auto node = _node->find(reference);
if (node != nullptr &&
node->_provider != nullptr)
{
if (node->_provider (reference, v))
return v;
if (node != nullptr && node->_provider != nullptr) {
if (node->_provider(reference, v)) return v;
}
}
@ -555,57 +510,44 @@ 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)
{
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))
{
for (const auto& element : DOM::decomposeReference(reference)) {
auto found{false};
for (auto& branch : cursor->_branches)
{
if (branch->_name == element)
{
for (auto& branch : cursor->_branches) {
if (branch->_name == element) {
cursor = branch;
found = true;
break;
}
}
if (! found)
{
if (!found) {
auto branch = new DOM::Node();
branch->_name = element;
cursor->_branches.push_back(branch);
@ -618,47 +560,35 @@ 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))
{
for (const auto& element : DOM::decomposeReference(reference)) {
auto found{false};
for (auto& branch : cursor->_branches)
{
if (branch->_name == element)
{
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;
for (auto& branch : _branches) {
if (branch->_provider) ++total;
total += branch->count();
}
@ -666,10 +596,7 @@ int DOM::Node::count () const
}
////////////////////////////////////////////////////////////////////////////////
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.
@ -677,24 +604,20 @@ std::string DOM::Node::dumpNode (
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();
}
////////////////////////////////////////////////////////////////////////////////
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();
}

View file

@ -27,16 +27,16 @@
#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&);
class DOM
{
class DOM {
public:
~DOM();
void addSource(const std::string&, bool (*)(const std::string&, Variant&));
@ -51,8 +51,7 @@ public:
std::string dump() const;
private:
class Node
{
class Node {
public:
~Node();
void addSource(const std::string&, bool (*)(const std::string&, Variant&));

View file

@ -27,28 +27,27 @@
#include <cmake.h>
// cmake.h include header must come first
#include <Eval.h>
#include <DOM.h>
#include <map>
#include <time.h>
#include <Context.h>
#include <Task.h>
#include <Color.h>
#include <shared.h>
#include <Context.h>
#include <DOM.h>
#include <Eval.h>
#include <Task.h>
#include <format.h>
#include <shared.h>
#include <time.h>
#include <map>
////////////////////////////////////////////////////////////////////////////////
// Supported operators, borrowed from C++, particularly the precedence.
// Note: table is sorted by length of operator string, so searches match
// longest first.
static struct
{
static struct {
std::string op;
int precedence;
char type; // b=binary, u=unary, c=circumfix
char associativity; // l=left, r=right, _=?
} operators[] =
{
} operators[] = {
// Operator Precedence Type Associativity
{"^", 16, 'b', 'r'}, // Exponent
@ -91,11 +90,13 @@ static struct
////////////////////////////////////////////////////////////////////////////////
// Built-in support for some named constants.
static bool namedConstants (const std::string& name, Variant& value)
{
if (name == "true") value = Variant (true);
else if (name == "false") value = Variant (false);
else if (name == "pi") value = Variant (3.14159165);
static bool namedConstants(const std::string& name, Variant& value) {
if (name == "true")
value = Variant(true);
else if (name == "false")
value = Variant(false);
else if (name == "pi")
value = Variant(3.14159165);
else
return false;
@ -104,10 +105,8 @@ static bool namedConstants (const std::string& name, Variant& value)
////////////////////////////////////////////////////////////////////////////////
// Support for evaluating DOM references (add with `e.AddSource(domSource)`)
bool domSource (const std::string& identifier, Variant& value)
{
if (getDOM (identifier, Context::getContext ().currentTask, value))
{
bool domSource(const std::string& identifier, Variant& value) {
if (getDOM(identifier, Context::getContext().currentTask, value)) {
value.source(identifier);
return true;
}
@ -116,151 +115,114 @@ bool domSource (const std::string& identifier, Variant& value)
}
////////////////////////////////////////////////////////////////////////////////
Eval::Eval ()
{
addSource (namedConstants);
}
Eval::Eval() { addSource(namedConstants); }
////////////////////////////////////////////////////////////////////////////////
void Eval::addSource (bool (*source)(const std::string&, Variant&))
{
_sources.push_back (source);
}
void Eval::addSource(bool (*source)(const std::string&, Variant&)) { _sources.push_back(source); }
////////////////////////////////////////////////////////////////////////////////
void Eval::evaluateInfixExpression (const std::string& e, Variant& v) const
{
void Eval::evaluateInfixExpression(const std::string& e, Variant& v) const {
// Reduce e to a vector of tokens.
Lexer l(e);
std::vector<std::pair<std::string, Lexer::Type>> tokens;
std::string token;
Lexer::Type type;
while (l.token (token, type))
tokens.emplace_back (token, type);
while (l.token(token, type)) tokens.emplace_back(token, type);
// Parse for syntax checking and operator replacement.
if (_debug)
Context::getContext ().debug ("FILTER Infix " + dump (tokens));
if (_debug) Context::getContext().debug("FILTER Infix " + dump(tokens));
infixParse(tokens);
if (_debug)
Context::getContext ().debug ("FILTER Infix parsed " + dump (tokens));
if (_debug) Context::getContext().debug("FILTER Infix parsed " + dump(tokens));
// Convert infix --> postfix.
infixToPostfix(tokens);
if (_debug)
Context::getContext ().debug ("FILTER Postfix " + dump (tokens));
if (_debug) Context::getContext().debug("FILTER Postfix " + dump(tokens));
// Call the postfix evaluator.
evaluatePostfixStack(tokens, v);
}
////////////////////////////////////////////////////////////////////////////////
void Eval::evaluatePostfixExpression (const std::string& e, Variant& v) const
{
void Eval::evaluatePostfixExpression(const std::string& e, Variant& v) const {
// Reduce e to a vector of tokens.
Lexer l(e);
std::vector<std::pair<std::string, Lexer::Type>> tokens;
std::string token;
Lexer::Type type;
while (l.token (token, type))
tokens.emplace_back (token, type);
while (l.token(token, type)) tokens.emplace_back(token, type);
if (_debug)
Context::getContext ().debug ("FILTER Postfix " + dump (tokens));
if (_debug) Context::getContext().debug("FILTER Postfix " + dump(tokens));
// Call the postfix evaluator.
evaluatePostfixStack(tokens, v);
}
////////////////////////////////////////////////////////////////////////////////
void Eval::compileExpression (
const std::vector <std::pair <std::string, Lexer::Type>>& precompiled)
{
void Eval::compileExpression(const std::vector<std::pair<std::string, Lexer::Type>>& precompiled) {
_compiled = precompiled;
// Parse for syntax checking and operator replacement.
if (_debug)
Context::getContext ().debug ("FILTER Infix " + dump (_compiled));
if (_debug) Context::getContext().debug("FILTER Infix " + dump(_compiled));
infixParse(_compiled);
if (_debug)
Context::getContext ().debug ("FILTER Infix parsed " + dump (_compiled));
if (_debug) Context::getContext().debug("FILTER Infix parsed " + dump(_compiled));
// Convert infix --> postfix.
infixToPostfix(_compiled);
if (_debug)
Context::getContext ().debug ("FILTER Postfix " + dump (_compiled));
if (_debug) Context::getContext().debug("FILTER Postfix " + dump(_compiled));
}
////////////////////////////////////////////////////////////////////////////////
void Eval::evaluateCompiledExpression (Variant& v)
{
void Eval::evaluateCompiledExpression(Variant& v) {
// Call the postfix evaluator.
evaluatePostfixStack(_compiled, v);
}
////////////////////////////////////////////////////////////////////////////////
void Eval::debug (bool value)
{
_debug = value;
}
void Eval::debug(bool value) { _debug = value; }
////////////////////////////////////////////////////////////////////////////////
// Static.
std::vector <std::string> Eval::getOperators ()
{
std::vector<std::string> Eval::getOperators() {
std::vector<std::string> all;
all.reserve(NUM_OPERATORS);
for (const auto &opr : operators)
all.push_back (opr.op);
for (const auto& opr : operators) all.push_back(opr.op);
return all;
}
////////////////////////////////////////////////////////////////////////////////
// Static.
std::vector <std::string> Eval::getBinaryOperators ()
{
std::vector<std::string> Eval::getBinaryOperators() {
std::vector<std::string> all;
for (const auto& opr : operators)
if (opr.type == 'b')
all.push_back (opr.op);
if (opr.type == 'b') all.push_back(opr.op);
return all;
}
////////////////////////////////////////////////////////////////////////////////
void Eval::evaluatePostfixStack (
const std::vector <std::pair <std::string, Lexer::Type>>& tokens,
Variant& result) const
{
if (tokens.size () == 0)
throw std::string ("No expression to evaluate.");
void Eval::evaluatePostfixStack(const std::vector<std::pair<std::string, Lexer::Type>>& tokens,
Variant& result) const {
if (tokens.size() == 0) throw std::string("No expression to evaluate.");
// This is stack used by the postfix evaluator.
std::vector<Variant> values;
values.reserve(tokens.size());
for (const auto& token : tokens)
{
for (const auto& token : tokens) {
// Unary operators.
if (token.second == Lexer::Type::op &&
token.first == "!")
{
if (values.size () < 1)
throw std::string ("The expression could not be evaluated.");
if (token.second == Lexer::Type::op && token.first == "!") {
if (values.size() < 1) throw std::string("The expression could not be evaluated.");
Variant right = values.back();
values.pop_back();
Variant result = !right;
values.push_back(result);
if (_debug)
Context::getContext ().debug (format ("Eval {1} ↓'{2}' → ↑'{3}'", token.first, (std::string) right, (std::string) result));
}
else if (token.second == Lexer::Type::op &&
token.first == "_neg_")
{
if (values.size () < 1)
throw std::string ("The expression could not be evaluated.");
Context::getContext().debug(format("Eval {1} ↓'{2}' → ↑'{3}'", token.first,
(std::string)right, (std::string)result));
} else if (token.second == Lexer::Type::op && token.first == "_neg_") {
if (values.size() < 1) throw std::string("The expression could not be evaluated.");
Variant right = values.back();
values.pop_back();
@ -270,21 +232,17 @@ void Eval::evaluatePostfixStack (
values.push_back(result);
if (_debug)
Context::getContext ().debug (format ("Eval {1} ↓'{2}' → ↑'{3}'", token.first, (std::string) right, (std::string) result));
}
else if (token.second == Lexer::Type::op &&
token.first == "_pos_")
{
Context::getContext().debug(format("Eval {1} ↓'{2}' → ↑'{3}'", token.first,
(std::string)right, (std::string)result));
} else if (token.second == Lexer::Type::op && token.first == "_pos_") {
// The _pos_ operator is a NOP.
if (_debug)
Context::getContext().debug(format("[{1}] eval op {2} NOP", values.size(), token.first));
}
// Binary operators.
else if (token.second == Lexer::Type::op)
{
if (values.size () < 2)
throw std::string ("The expression could not be evaluated.");
else if (token.second == Lexer::Type::op) {
if (values.size() < 2) throw std::string("The expression could not be evaluated.");
Variant right = values.back();
values.pop_back();
@ -296,57 +254,75 @@ void Eval::evaluatePostfixStack (
// Ordering these by anticipation frequency of use is a good idea.
Variant result;
if (token.first == "and") result = left && right;
else if (token.first == "or") result = left || right;
else if (token.first == "&&") result = left && right;
else if (token.first == "||") result = left || right;
else if (token.first == "<") result = left < right;
else if (token.first == "<=") result = left <= right;
else if (token.first == ">") result = left > right;
else if (token.first == ">=") result = left >= right;
else if (token.first == "==") result = left.operator== (right);
else if (token.first == "!==") result = left.operator!= (right);
else if (token.first == "=") result = left.operator_partial (right);
else if (token.first == "!=") result = left.operator_nopartial (right);
else if (token.first == "+") result = left + right;
else if (token.first == "-") result = left - right;
else if (token.first == "*") result = left * right;
else if (token.first == "/") result = left / right;
else if (token.first == "^") result = left ^ right;
else if (token.first == "%") result = left % right;
else if (token.first == "xor") result = left.operator_xor (right);
if (token.first == "and")
result = left && right;
else if (token.first == "or")
result = left || right;
else if (token.first == "&&")
result = left && right;
else if (token.first == "||")
result = left || right;
else if (token.first == "<")
result = left < right;
else if (token.first == "<=")
result = left <= right;
else if (token.first == ">")
result = left > right;
else if (token.first == ">=")
result = left >= right;
else if (token.first == "==")
result = left.operator==(right);
else if (token.first == "!==")
result = left.operator!=(right);
else if (token.first == "=")
result = left.operator_partial(right);
else if (token.first == "!=")
result = left.operator_nopartial(right);
else if (token.first == "+")
result = left + right;
else if (token.first == "-")
result = left - right;
else if (token.first == "*")
result = left * right;
else if (token.first == "/")
result = left / right;
else if (token.first == "^")
result = left ^ right;
else if (token.first == "%")
result = left % right;
else if (token.first == "xor")
result = left.operator_xor(right);
else if (contextTask) {
if (token.first == "~") result = left.operator_match (right, *contextTask);
else if (token.first == "!~") result = left.operator_nomatch (right, *contextTask);
else if (token.first == "_hastag_") result = left.operator_hastag (right, *contextTask);
else if (token.first == "_notag_") result = left.operator_notag (right, *contextTask);
if (token.first == "~")
result = left.operator_match(right, *contextTask);
else if (token.first == "!~")
result = left.operator_nomatch(right, *contextTask);
else if (token.first == "_hastag_")
result = left.operator_hastag(right, *contextTask);
else if (token.first == "_notag_")
result = left.operator_notag(right, *contextTask);
else
throw format("Unsupported operator '{1}'.", token.first);
}
else
} else
throw format("Unsupported operator '{1}'.", token.first);
values.push_back(result);
if (_debug)
Context::getContext ().debug (format ("Eval ↓'{1}' {2} ↓'{3}' → ↑'{4}'", (std::string) left, token.first, (std::string) right, (std::string) result));
Context::getContext().debug(format("Eval ↓'{1}' {2} ↓'{3}' → ↑'{4}'", (std::string)left,
token.first, (std::string)right, (std::string)result));
}
// Literals and identifiers.
else
{
else {
Variant v(token.first);
switch (token.second)
{
switch (token.second) {
case Lexer::Type::number:
if (Lexer::isAllDigits (token.first))
{
if (Lexer::isAllDigits(token.first)) {
v.cast(Variant::type_integer);
if (_debug)
Context::getContext().debug(format("Eval literal number ↑'{1}'", (std::string)v));
}
else
{
} else {
v.cast(Variant::type_real);
if (_debug)
Context::getContext().debug(format("Eval literal decimal ↑'{1}'", (std::string)v));
@ -358,29 +334,26 @@ void Eval::evaluatePostfixStack (
break;
case Lexer::Type::dom:
case Lexer::Type::identifier:
{
case Lexer::Type::identifier: {
bool found = false;
for (const auto& source : _sources)
{
if (source (token.first, v))
{
for (const auto& source : _sources) {
if (source(token.first, v)) {
if (_debug)
Context::getContext ().debug (format ("Eval identifier source '{1}' → ↑'{2}'", token.first, (std::string) v));
Context::getContext().debug(
format("Eval identifier source '{1}' → ↑'{2}'", token.first, (std::string)v));
found = true;
break;
}
}
// An identifier that fails lookup is a string.
if (! found)
{
if (!found) {
v.cast(Variant::type_string);
if (_debug)
Context::getContext ().debug (format ("Eval identifier source failed '{1}'", token.first));
Context::getContext().debug(
format("Eval identifier source failed '{1}'", token.first));
}
}
break;
} break;
case Lexer::Type::date:
v.cast(Variant::type_date);
@ -408,8 +381,7 @@ void Eval::evaluatePostfixStack (
// If there is more than one variant left on the stack, then the original
// expression was not valid.
if (values.size () != 1)
throw std::string ("The value is not an expression.");
if (values.size() != 1) throw std::string("The value is not an expression.");
result = values[0];
}
@ -428,31 +400,20 @@ void Eval::evaluatePostfixStack (
// Exponent --> Primitive ["^" Primitive]
// Primitive --> "(" Logical ")" | Variant
//
void Eval::infixParse (
std::vector <std::pair <std::string, Lexer::Type>>& infix) const
{
void Eval::infixParse(std::vector<std::pair<std::string, Lexer::Type>>& infix) const {
unsigned int i = 0;
parseLogical(infix, i);
}
////////////////////////////////////////////////////////////////////////////////
// Logical --> Regex {( "and" | "or" | "xor" ) Regex}
bool Eval::parseLogical (
std::vector <std::pair <std::string, Lexer::Type>>& infix,
unsigned int &i) const
{
if (i < infix.size () &&
parseRegex (infix, i))
{
while (i < infix.size () &&
infix[i].second == Lexer::Type::op &&
(infix[i].first == "and" ||
infix[i].first == "or" ||
infix[i].first == "xor"))
{
bool Eval::parseLogical(std::vector<std::pair<std::string, Lexer::Type>>& infix,
unsigned int& i) const {
if (i < infix.size() && parseRegex(infix, i)) {
while (i < infix.size() && infix[i].second == Lexer::Type::op &&
(infix[i].first == "and" || infix[i].first == "or" || infix[i].first == "xor")) {
++i;
if (! parseRegex (infix, i))
return false;
if (!parseRegex(infix, i)) return false;
}
return true;
@ -463,21 +424,13 @@ bool Eval::parseLogical (
////////////////////////////////////////////////////////////////////////////////
// Regex --> Equality {( "~" | "!~" ) Equality}
bool Eval::parseRegex (
std::vector <std::pair <std::string, Lexer::Type>>& infix,
unsigned int &i) const
{
if (i < infix.size () &&
parseEquality (infix, i))
{
while (i < infix.size () &&
infix[i].second == Lexer::Type::op &&
(infix[i].first == "~" ||
infix[i].first == "!~"))
{
bool Eval::parseRegex(std::vector<std::pair<std::string, Lexer::Type>>& infix,
unsigned int& i) const {
if (i < infix.size() && parseEquality(infix, i)) {
while (i < infix.size() && infix[i].second == Lexer::Type::op &&
(infix[i].first == "~" || infix[i].first == "!~")) {
++i;
if (! parseEquality (infix, i))
return false;
if (!parseEquality(infix, i)) return false;
}
return true;
@ -488,23 +441,14 @@ bool Eval::parseRegex (
////////////////////////////////////////////////////////////////////////////////
// Equality --> Comparative {( "==" | "=" | "!==" | "!=" ) Comparative}
bool Eval::parseEquality (
std::vector <std::pair <std::string, Lexer::Type>>& infix,
unsigned int &i) const
{
if (i < infix.size () &&
parseComparative (infix, i))
{
while (i < infix.size () &&
infix[i].second == Lexer::Type::op &&
(infix[i].first == "==" ||
infix[i].first == "=" ||
infix[i].first == "!==" ||
infix[i].first == "!="))
{
bool Eval::parseEquality(std::vector<std::pair<std::string, Lexer::Type>>& infix,
unsigned int& i) const {
if (i < infix.size() && parseComparative(infix, i)) {
while (i < infix.size() && infix[i].second == Lexer::Type::op &&
(infix[i].first == "==" || infix[i].first == "=" || infix[i].first == "!==" ||
infix[i].first == "!=")) {
++i;
if (! parseComparative (infix, i))
return false;
if (!parseComparative(infix, i)) return false;
}
return true;
@ -515,23 +459,14 @@ bool Eval::parseEquality (
////////////////////////////////////////////////////////////////////////////////
// Comparative --> Arithmetic {( "<=" | "<" | ">=" | ">" ) Arithmetic}
bool Eval::parseComparative (
std::vector <std::pair <std::string, Lexer::Type>>& infix,
unsigned int &i) const
{
if (i < infix.size () &&
parseArithmetic (infix, i))
{
while (i < infix.size () &&
infix[i].second == Lexer::Type::op &&
(infix[i].first == "<=" ||
infix[i].first == "<" ||
infix[i].first == ">=" ||
infix[i].first == ">"))
{
bool Eval::parseComparative(std::vector<std::pair<std::string, Lexer::Type>>& infix,
unsigned int& i) const {
if (i < infix.size() && parseArithmetic(infix, i)) {
while (i < infix.size() && infix[i].second == Lexer::Type::op &&
(infix[i].first == "<=" || infix[i].first == "<" || infix[i].first == ">=" ||
infix[i].first == ">")) {
++i;
if (! parseArithmetic (infix, i))
return false;
if (!parseArithmetic(infix, i)) return false;
}
return true;
@ -542,21 +477,13 @@ bool Eval::parseComparative (
////////////////////////////////////////////////////////////////////////////////
// Arithmetic --> Geometric {( "+" | "-" ) Geometric}
bool Eval::parseArithmetic (
std::vector <std::pair <std::string, Lexer::Type>>& infix,
unsigned int &i) const
{
if (i < infix.size () &&
parseGeometric (infix, i))
{
while (i < infix.size () &&
infix[i].second == Lexer::Type::op &&
(infix[i].first == "+" ||
infix[i].first == "-"))
{
bool Eval::parseArithmetic(std::vector<std::pair<std::string, Lexer::Type>>& infix,
unsigned int& i) const {
if (i < infix.size() && parseGeometric(infix, i)) {
while (i < infix.size() && infix[i].second == Lexer::Type::op &&
(infix[i].first == "+" || infix[i].first == "-")) {
++i;
if (! parseGeometric (infix, i))
return false;
if (!parseGeometric(infix, i)) return false;
}
return true;
@ -567,22 +494,13 @@ bool Eval::parseArithmetic (
////////////////////////////////////////////////////////////////////////////////
// Geometric --> Tag {( "*" | "/" | "%" ) Tag}
bool Eval::parseGeometric (
std::vector <std::pair <std::string, Lexer::Type>>& infix,
unsigned int &i) const
{
if (i < infix.size () &&
parseTag (infix, i))
{
while (i < infix.size () &&
infix[i].second == Lexer::Type::op &&
(infix[i].first == "*" ||
infix[i].first == "/" ||
infix[i].first == "%"))
{
bool Eval::parseGeometric(std::vector<std::pair<std::string, Lexer::Type>>& infix,
unsigned int& i) const {
if (i < infix.size() && parseTag(infix, i)) {
while (i < infix.size() && infix[i].second == Lexer::Type::op &&
(infix[i].first == "*" || infix[i].first == "/" || infix[i].first == "%")) {
++i;
if (! parseTag (infix, i))
return false;
if (!parseTag(infix, i)) return false;
}
return true;
@ -593,21 +511,13 @@ bool Eval::parseGeometric (
////////////////////////////////////////////////////////////////////////////////
// Tag --> Unary {( "_hastag_" | "_notag_" ) Unary}
bool Eval::parseTag (
std::vector <std::pair <std::string, Lexer::Type>>& infix,
unsigned int &i) const
{
if (i < infix.size () &&
parseUnary (infix, i))
{
while (i < infix.size () &&
infix[i].second == Lexer::Type::op &&
(infix[i].first == "_hastag_" ||
infix[i].first == "_notag_"))
{
bool Eval::parseTag(std::vector<std::pair<std::string, Lexer::Type>>& infix,
unsigned int& i) const {
if (i < infix.size() && parseUnary(infix, i)) {
while (i < infix.size() && infix[i].second == Lexer::Type::op &&
(infix[i].first == "_hastag_" || infix[i].first == "_notag_")) {
++i;
if (! parseUnary (infix, i))
return false;
if (!parseUnary(infix, i)) return false;
}
return true;
@ -618,24 +528,16 @@ bool Eval::parseTag (
////////////////////////////////////////////////////////////////////////////////
// Unary --> [( "-" | "+" | "!" )] Exponent
bool Eval::parseUnary (
std::vector <std::pair <std::string, Lexer::Type>>& infix,
unsigned int &i) const
{
if (i < infix.size ())
{
if (infix[i].first == "-")
{
bool Eval::parseUnary(std::vector<std::pair<std::string, Lexer::Type>>& infix,
unsigned int& i) const {
if (i < infix.size()) {
if (infix[i].first == "-") {
infix[i].first = "_neg_";
++i;
}
else if (infix[i].first == "+")
{
} else if (infix[i].first == "+") {
infix[i].first = "_pos_";
++i;
}
else if (infix[i].first == "!")
{
} else if (infix[i].first == "!") {
++i;
}
}
@ -645,20 +547,12 @@ bool Eval::parseUnary (
////////////////////////////////////////////////////////////////////////////////
// Exponent --> Primitive ["^" Primitive]
bool Eval::parseExponent (
std::vector <std::pair <std::string, Lexer::Type>>& infix,
unsigned int& i) const
{
if (i < infix.size () &&
parsePrimitive (infix, i))
{
while (i < infix.size () &&
infix[i].second == Lexer::Type::op &&
infix[i].first == "^")
{
bool Eval::parseExponent(std::vector<std::pair<std::string, Lexer::Type>>& infix,
unsigned int& i) const {
if (i < infix.size() && parsePrimitive(infix, i)) {
while (i < infix.size() && infix[i].second == Lexer::Type::op && infix[i].first == "^") {
++i;
if (! parsePrimitive (infix, i))
return false;
if (!parsePrimitive(infix, i)) return false;
}
return true;
@ -669,46 +563,31 @@ bool Eval::parseExponent (
////////////////////////////////////////////////////////////////////////////////
// Primitive --> "(" Logical ")" | Variant
bool Eval::parsePrimitive (
std::vector <std::pair <std::string, Lexer::Type>>& infix,
unsigned int &i) const
{
if (i < infix.size ())
{
if (infix[i].first == "(")
{
bool Eval::parsePrimitive(std::vector<std::pair<std::string, Lexer::Type>>& infix,
unsigned int& i) const {
if (i < infix.size()) {
if (infix[i].first == "(") {
++i;
if (i < infix.size () &&
parseLogical (infix, i))
{
if (i < infix.size () &&
infix[i].first == ")")
{
if (i < infix.size() && parseLogical(infix, i)) {
if (i < infix.size() && infix[i].first == ")") {
++i;
return true;
}
}
}
else
{
} else {
bool found = false;
for (const auto& source : _sources)
{
for (const auto& source : _sources) {
Variant v;
if (source (infix[i].first, v))
{
if (source(infix[i].first, v)) {
found = true;
break;
}
}
if (found)
{
if (found) {
++i;
return true;
}
else if (infix[i].second != Lexer::Type::op)
{
} else if (infix[i].second != Lexer::Type::op) {
++i;
return true;
}
@ -750,12 +629,9 @@ bool Eval::parsePrimitive (
// Pop the operator onto the output queue.
// Exit.
//
void Eval::infixToPostfix (
std::vector <std::pair <std::string, Lexer::Type>>& infix) const
{
void Eval::infixToPostfix(std::vector<std::pair<std::string, Lexer::Type>>& infix) const {
// Short circuit.
if (infix.size () == 1)
return;
if (infix.size() == 1) return;
// Result.
std::vector<std::pair<std::string, Lexer::Type>> postfix;
@ -768,19 +644,11 @@ void Eval::infixToPostfix (
unsigned int precedence;
char associativity;
for (auto& token : infix)
{
if (token.second == Lexer::Type::op &&
token.first == "(")
{
for (auto& token : infix) {
if (token.second == Lexer::Type::op && token.first == "(") {
op_stack.push_back(token);
}
else if (token.second == Lexer::Type::op &&
token.first == ")")
{
while (op_stack.size () &&
op_stack.back ().first != "(")
{
} else if (token.second == Lexer::Type::op && token.first == ")") {
while (op_stack.size() && op_stack.back().first != "(") {
postfix.push_back(op_stack.back());
op_stack.pop_back();
}
@ -789,34 +657,27 @@ void Eval::infixToPostfix (
op_stack.pop_back();
else
throw std::string("Mismatched parentheses in expression");
}
else if (token.second == Lexer::Type::op &&
identifyOperator (token.first, type, precedence, associativity))
{
} else if (token.second == Lexer::Type::op &&
identifyOperator(token.first, type, precedence, associativity)) {
char type2;
unsigned int precedence2;
char associativity2;
while (op_stack.size() > 0 &&
identifyOperator(op_stack.back().first, type2, precedence2, associativity2) &&
((associativity == 'l' && precedence <= precedence2) ||
(associativity == 'r' && precedence < precedence2)))
{
(associativity == 'r' && precedence < precedence2))) {
postfix.push_back(op_stack.back());
op_stack.pop_back();
}
op_stack.push_back(token);
}
else
{
} else {
postfix.push_back(token);
}
}
while (op_stack.size ())
{
if (op_stack.back ().first == "(" ||
op_stack.back ().first == ")")
while (op_stack.size()) {
if (op_stack.back().first == "(" || op_stack.back().first == ")")
throw std::string("Mismatched parentheses in expression");
postfix.push_back(op_stack.back());
@ -827,16 +688,10 @@ void Eval::infixToPostfix (
}
////////////////////////////////////////////////////////////////////////////////
bool Eval::identifyOperator (
const std::string& op,
char& type,
unsigned int& precedence,
char& associativity) const
{
for (const auto& opr : operators)
{
if (opr.op == op)
{
bool Eval::identifyOperator(const std::string& op, char& type, unsigned int& precedence,
char& associativity) const {
for (const auto& opr : operators) {
if (opr.op == op) {
type = opr.type;
precedence = opr.precedence;
associativity = opr.associativity;
@ -848,9 +703,7 @@ bool Eval::identifyOperator (
}
////////////////////////////////////////////////////////////////////////////////
std::string Eval::dump (
std::vector <std::pair <std::string, Lexer::Type>>& tokens) const
{
std::string Eval::dump(std::vector<std::pair<std::string, Lexer::Type>>& tokens) const {
// Set up a color mapping.
std::map<Lexer::Type, Color> color_map;
color_map[Lexer::Type::op] = Color("gray14 on gray6");
@ -863,10 +716,8 @@ std::string Eval::dump (
color_map[Lexer::Type::duration] = Color("rgb531 on gray6");
std::string output;
for (auto i = tokens.begin (); i != tokens.end (); ++i)
{
if (i != tokens.begin ())
output += ' ';
for (auto i = tokens.begin(); i != tokens.end(); ++i) {
if (i != tokens.begin()) output += ' ';
Color c;
if (color_map[i->second].nontrivial())

View file

@ -27,15 +27,15 @@
#ifndef INCLUDED_EVAL
#define INCLUDED_EVAL
#include <vector>
#include <string>
#include <Lexer.h>
#include <Variant.h>
#include <string>
#include <vector>
bool domSource(const std::string &, Variant &);
class Eval
{
class Eval {
public:
Eval();
@ -50,7 +50,8 @@ public:
static std::vector<std::string> getBinaryOperators();
private:
void evaluatePostfixStack (const std::vector <std::pair <std::string, Lexer::Type>>&, Variant&) const;
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;

View file

@ -27,20 +27,20 @@
#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();
@ -48,11 +48,9 @@ void Filter::subset (const std::vector <Task>& input, std::vector <Task>& output
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 (a.hasTag("FILTER")) precompiled.emplace_back(a.getToken(), a._lextype);
if (precompiled.size ())
{
if (precompiled.size()) {
Eval eval;
eval.addSource(domSource);
@ -61,44 +59,39 @@ void Filter::subset (const std::vector <Task>& input, std::vector <Task>& output
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);
Variant var;
eval.evaluateCompiledExpression(var);
if (var.get_bool ())
output.push_back (task);
if (var.get_bool()) output.push_back(task);
}
eval.debug(false);
}
else
} else
output = input;
_endCount = (int)output.size();
Context::getContext ().debug (format ("Filtered {1} tasks --> {2} tasks [list subset]", _startCount, _endCount));
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();
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 (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();
@ -113,41 +106,34 @@ void Filter::subset (std::vector <Task>& output)
eval.compileExpression(precompiled);
output.clear();
for (auto& task : pending)
{
for (auto& task : pending) {
// Set up context for any DOM references.
auto currentTask = Context::getContext().withCurrentTask(&task);
Variant var;
eval.evaluateCompiledExpression(var);
if (var.get_bool ())
output.push_back (task);
if (var.get_bool()) output.push_back(task);
}
shortcut = pendingOnly();
if (! shortcut)
{
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();
for (auto& task : completed)
{
for (auto& task : completed) {
// Set up context for any DOM references.
auto currentTask = Context::getContext().withCurrentTask(&task);
Variant var;
eval.evaluateCompiledExpression(var);
if (var.get_bool ())
output.push_back (task);
if (var.get_bool()) output.push_back(task);
}
}
eval.debug(false);
}
else
{
} else {
safety();
Timer pending_completed;
@ -156,16 +142,15 @@ void Filter::subset (std::vector <Task>& output)
}
_endCount = (int)output.size();
Context::getContext ().debug (format ("Filtered {1} tasks --> {2} tasks [{3}]", _startCount, _endCount, (shortcut ? "pending only" : "all tasks")));
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
{
bool Filter::hasFilter() const {
for (const auto& a : Context::getContext().cli2._args)
if (a.hasTag ("FILTER"))
return true;
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
@ -198,10 +181,8 @@ bool Filter::pendingOnly () const
bool pendingTag = false;
bool activeTag = false;
for (const auto& a : Context::getContext ().cli2._args)
{
if (a.hasTag ("FILTER"))
{
for (const auto& a : Context::getContext().cli2._args) {
if (a.hasTag("FILTER")) {
std::string raw = a.attribute("raw");
std::string canonical = a.attribute("canonical");
@ -215,32 +196,24 @@ bool Filter::pendingOnly () const
}
}
for (const auto& word : Context::getContext ().cli2._original_args)
{
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,31 +221,26 @@ 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 (!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.");
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?"))
confirm("This command has no filter, and will modify all (including completed and "
"deleted) tasks. Are you sure?"))
return;
// Sound the alarm.
@ -282,9 +250,6 @@ void Filter::safety () const
}
////////////////////////////////////////////////////////////////////////////////
void Filter::disableSafety ()
{
_safety = false;
}
void Filter::disableSafety() { _safety = false; }
////////////////////////////////////////////////////////////////////////////////

View file

@ -27,13 +27,13 @@
#ifndef INCLUDED_FILTER
#define INCLUDED_FILTER
#include <string>
#include <vector>
#include <Task.h>
#include <Variant.h>
class Filter
{
#include <string>
#include <vector>
class Filter {
public:
Filter() = default;
@ -51,4 +51,3 @@ private:
};
#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_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_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 ()
{
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"))
{
if (Context::getContext().config.has("hooks.location")) {
d = Directory(Context::getContext().config.get("hooks.location"));
}
else
{
} else {
d = Directory(Context::getContext().config.get("data.location"));
d += "hooks";
}
if (d.is_directory () &&
d.readable ())
{
if (d.is_directory() && d.readable()) {
_scripts = d.list();
std::sort(_scripts.begin(), _scripts.end());
if (_debug >= 1)
{
for (auto& i : _scripts)
{
if (_debug >= 1) {
for (auto& i : _scripts) {
Path p(i);
if (! p.is_directory ())
{
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")
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);
}
}
}
}
else if (_debug >= 1)
} else if (_debug >= 1)
Context::getContext().debug("Hook directory not readable: " + d._data);
_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,18 +122,14 @@ 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)
{
if (matchingScripts.size()) {
for (auto& script : matchingScripts) {
std::vector<std::string> input;
std::vector<std::string> output;
int status = callHookScript(script, input, output);
@ -151,16 +140,11 @@ void Hooks::onLaunch () const
assertNTasks(outputJSON, 0, script);
if (status == 0)
{
for (auto& message : outputFeedback)
Context::getContext ().footnote (message);
}
else
{
if (status == 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().error(message);
throw 0; // This is how hooks silently terminate processing.
}
@ -182,16 +166,13 @@ 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 ())
{
if (matchingScripts.size()) {
// Get the set of changed tasks.
std::vector<Task> tasks;
Context::getContext().tdb2.get_changes(tasks);
@ -199,12 +180,10 @@ void Hooks::onExit () const
// Convert to a vector of strings.
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)
{
for (auto& script : matchingScripts) {
std::vector<std::string> output;
int status = callHookScript(script, input, output);
@ -214,16 +193,11 @@ void Hooks::onExit () const
assertNTasks(outputJSON, 0, script);
if (status == 0)
{
for (auto& message : outputFeedback)
Context::getContext ().footnote (message);
}
else
{
if (status == 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().error(message);
throw 0; // This is how hooks silently terminate processing.
}
@ -245,23 +219,19 @@ 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 ())
{
if (matchingScripts.size()) {
// Convert task to a vector of strings.
std::vector<std::string> input;
input.push_back(task.composeJSON());
// Call the hook scripts.
for (auto& script : matchingScripts)
{
for (auto& script : matchingScripts) {
std::vector<std::string> output;
int status = callHookScript(script, input, output);
@ -269,8 +239,7 @@ void Hooks::onAdd (Task& task) const
std::vector<std::string> outputFeedback;
separateOutput(output, outputJSON, outputFeedback);
if (status == 0)
{
if (status == 0) {
assertNTasks(outputJSON, 1, script);
assertValidJSON(outputJSON, script);
assertSameTask(outputJSON, task, script);
@ -278,14 +247,10 @@ void Hooks::onAdd (Task& task) const
// Propagate forward to the next script.
input[0] = outputJSON[0];
for (auto& message : outputFeedback)
Context::getContext ().footnote (message);
}
else
{
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().error(message);
throw 0; // This is how hooks silently terminate processing.
}
@ -311,24 +276,20 @@ 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 ())
{
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
// Call the hook scripts.
for (auto& script : matchingScripts)
{
for (auto& script : matchingScripts) {
std::vector<std::string> output;
int status = callHookScript(script, input, output);
@ -336,8 +297,7 @@ void Hooks::onModify (const Task& before, Task& after) const
std::vector<std::string> outputFeedback;
separateOutput(output, outputJSON, outputFeedback);
if (status == 0)
{
if (status == 0) {
assertNTasks(outputJSON, 1, script);
assertValidJSON(outputJSON, script);
assertSameTask(outputJSON, before, script);
@ -345,14 +305,10 @@ void Hooks::onModify (const Task& before, Task& after) const
// Propagate accepted changes forward to the next script.
input[1] = outputJSON[0];
for (auto& message : outputFeedback)
Context::getContext ().footnote (message);
}
else
{
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().error(message);
throw 0; // This is how hooks silently terminate processing.
}
@ -365,22 +321,15 @@ void Hooks::onModify (const Task& before, Task& after) const
}
////////////////////////////////////////////////////////////////////////////////
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> Hooks::scripts(const std::string& event) const {
std::vector<std::string> matching;
for (const auto& i : _scripts)
{
if (i.find ("/" + event) != std::string::npos)
{
for (const auto& i : _scripts) {
if (i.find("/" + event) != std::string::npos) {
File script(i);
if (script.executable ())
matching.push_back (i);
if (script.executable()) matching.push_back(i);
}
}
@ -388,13 +337,9 @@ 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)
{
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
@ -403,45 +348,32 @@ void Hooks::separateOutput (
}
////////////////////////////////////////////////////////////////////////////////
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] != '}')
{
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
{
try {
json::value* root = json::parse(i);
if (root->type () != json::j_object)
{
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 ())
{
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 ())
{
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;
}
@ -449,16 +381,13 @@ void Hooks::assertValidJSON (
delete root;
}
catch (const std::string& 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);
if (_debug) Context::getContext().error(STRING_HOOK_ERROR_JSON + e);
throw 0;
}
catch (...)
{
catch (...) {
Context::getContext().error(STRING_HOOK_ERROR_NOPARSE + i);
throw 0;
}
@ -466,35 +395,26 @@ void Hooks::assertValidJSON (
}
////////////////////////////////////////////////////////////////////////////////
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
{
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)
{
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)
{
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;
}
@ -503,9 +423,9 @@ void Hooks::assertSameTask (
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 ()));
if (json_uuid != uuid) {
Context::getContext().error(
format(STRING_HOOK_ERROR_SAME2, uuid, json_uuid, Path(script).name()));
throw 0;
}
@ -514,25 +434,19 @@ 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)
{
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.
@ -558,54 +472,41 @@ std::vector <std::string>& Hooks::buildHookScriptArgs (std::vector <std::string>
}
////////////////////////////////////////////////////////////////////////////////
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)
{
if (_debug >= 2) {
Context::getContext().debug("Hook: input");
for (const auto& i : input)
Context::getContext ().debug (" " + i);
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)
{
if (_debug >= 2) {
Context::getContext().debug("Hooks: args");
for (const auto& arg: args)
Context::getContext ().debug (" " + arg);
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
} else
status = execute(script, args, inputStr, outputStr);
output = split(outputStr, '\n');
if (_debug >= 2)
{
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

View file

@ -27,12 +27,12 @@
#ifndef INCLUDED_HOOKS
#define INCLUDED_HOOKS
#include <vector>
#include <string>
#include <Task.h>
class Hooks
{
#include <string>
#include <vector>
class Hooks {
public:
Hooks() = default;
void initialize();
@ -45,14 +45,16 @@ public:
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;
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;
int callHookScript(const std::string&, const std::vector<std::string>&,
std::vector<std::string>&) const;
private:
bool _enabled{true};

File diff suppressed because it is too large Load diff

View file

@ -27,31 +27,41 @@
#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
{
class Lexer {
public:
// These are overridable.
static std::string dateFormat;
static std::string::size_type minimumMatchLength;
static std::map<std::string, std::string> attributes;
enum class Type { uuid, number, hex,
enum class Type {
uuid,
number,
hex,
string,
url, pair, set, separator,
url,
pair,
set,
separator,
tag,
path,
substitution, pattern,
substitution,
pattern,
op,
dom, identifier, word,
date, duration };
dom,
identifier,
word,
date,
duration
};
Lexer(const std::string&);
~Lexer() = default;
@ -73,16 +83,19 @@ public:
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&, 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 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::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&);

View file

@ -27,23 +27,25 @@
#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"
@ -53,20 +55,17 @@ static void dependency_scan (std::vector<Task> &);
////////////////////////////////////////////////////////////////////////////////
TDB2::TDB2()
: replica{tc::Replica()} // in-memory Replica
, _working_set {}
{
}
,
_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.
@ -121,7 +120,6 @@ void TDB2::add (Task& task)
if (id.has_value()) {
task.id = id.value();
}
}
////////////////////////////////////////////////////////////////////////////////
@ -139,8 +137,7 @@ 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");
@ -197,15 +194,13 @@ void TDB2::modify (Task& task)
}
////////////////////////////////////////////////////////////////////////////////
void TDB2::purge (Task& task)
{
void TDB2::purge(Task& task) {
auto uuid = task.get("uuid");
replica.delete_task(uuid);
}
////////////////////////////////////////////////////////////////////////////////
const tc::WorkingSet &TDB2::working_set ()
{
const tc::WorkingSet& TDB2::working_set() {
if (!_working_set.has_value()) {
_working_set = std::make_optional(replica.working_set());
}
@ -213,8 +208,7 @@ const tc::WorkingSet &TDB2::working_set ()
}
////////////////////////////////////////////////////////////////////////////////
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.";
@ -239,8 +232,7 @@ void TDB2::revert ()
}
////////////////////////////////////////////////////////////////////////////////
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";
@ -256,7 +248,9 @@ 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.");
@ -268,58 +262,49 @@ bool TDB2::confirm_revert (struct tc::ffi::TCReplicaOpList undo_ops)
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?");
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)
{
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"))
{
if (Context::getContext().config.getBoolean("gc")) {
replica.rebuild_working_set(true);
}
@ -327,33 +312,26 @@ void TDB2::gc ()
}
////////////////////////////////////////////////////////////////////////////////
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 ()
{
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)));
for (auto& tctask : all_tctasks) all.push_back(Task(std::move(tctask)));
return all;
}
////////////////////////////////////////////////////////////////////////////////
const std::vector <Task> TDB2::pending_tasks ()
{
const std::vector<Task> TDB2::pending_tasks() {
const tc::WorkingSet& ws = working_set();
auto largest_index = ws.largest_index();
@ -374,8 +352,7 @@ 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();
@ -392,8 +369,7 @@ const std::vector <Task> TDB2::completed_tasks ()
////////////////////////////////////////////////////////////////////////////////
// Locate task by ID, wherever it is.
bool TDB2::get (int id, Task& task)
{
bool TDB2::get(int id, Task& task) {
const tc::WorkingSet& ws = working_set();
const auto maybe_uuid = ws.by_index(id);
if (maybe_uuid) {
@ -409,8 +385,7 @@ 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) {
try {
@ -437,33 +412,24 @@ 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)
{
const std::vector<Task> TDB2::siblings(Task& task) {
std::vector<Task> results;
if (task.has ("parent"))
{
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)
{
if (i.has("parent") && i.get("parent") == parent) {
results.push_back(i);
}
}
@ -475,8 +441,7 @@ 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");
@ -517,40 +482,30 @@ const std::vector <Task> TDB2::children (Task& parent)
}
////////////////////////////////////////////////////////////////////////////////
std::string TDB2::uuid (int id)
{
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)
{
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)
{
if (lstatus != Task::completed && lstatus != Task::deleted &&
rstatus != Task::completed && rstatus != Task::deleted) {
left.is_blocked = true;
right.is_blocking = true;
}

View file

@ -27,24 +27,24 @@
#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
{
class TDB2 {
public:
static bool debug_mode;

File diff suppressed because it is too large Load diff

View file

@ -27,18 +27,18 @@
#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
{
#include <map>
#include <string>
#include <vector>
class Task {
public:
static std::string defaultProject;
static std::string defaultDue;
@ -177,11 +177,11 @@ public:
#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;
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&);

File diff suppressed because it is too large Load diff

View file

@ -27,13 +27,13 @@
#ifndef INCLUDED_VARIANT
#define INCLUDED_VARIANT
#include <Task.h>
#include <time.h>
#include <map>
#include <string>
#include <time.h>
#include <Task.h>
class Variant
{
class Variant {
public:
static std::string dateFormat;
static bool searchCaseSensitive;

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

@ -27,40 +27,37 @@
#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)
{
}
: _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();
}
@ -108,8 +105,7 @@ 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");
@ -121,19 +117,15 @@ std::string ViewTask::render (std::vector <Task>& data, std::vector <int>& seque
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;
@ -145,12 +137,10 @@ std::string ViewTask::render (std::vector <Task>& data, std::vector <int>& seque
// 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)
{
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;
@ -158,14 +148,12 @@ std::string ViewTask::render (std::vector <Task>& data, std::vector <int>& seque
ideal.push_back(global_ideal);
}
if (! print_empty_columns)
{
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
} 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,15 +162,12 @@ 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);
@ -191,34 +176,26 @@ std::string ViewTask::render (std::vector <Task>& data, std::vector <int>& seque
// 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,
sum_minimal + all_extra, sum_ideal + all_extra, overage,
_width));
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;
@ -284,13 +254,11 @@ 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)
{
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.
@ -305,14 +273,11 @@ std::string ViewTask::render (std::vector <Task>& data, std::vector <int>& seque
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], ' '));
@ -327,8 +292,7 @@ std::string ViewTask::render (std::vector <Task>& data, std::vector <int>& seque
out += "\n";
// Stop if the line limit is exceeded.
if (++_lines >= _truncate_lines && _truncate_lines != 0)
{
if (++_lines >= _truncate_lines && _truncate_lines != 0) {
Context::getContext().time_render_us += timer.total_us();
return out;
}
@ -337,8 +301,7 @@ std::string ViewTask::render (std::vector <Task>& data, std::vector <int>& seque
// Compose, render columns, in sequence.
_rows = 0;
std::vector<std::vector<std::string>> cells;
for (unsigned int s = 0; s < sequence.size (); ++s)
{
for (unsigned int s = 0; s < sequence.size(); ++s) {
max_lines = 0;
// Apply color rules to task.
@ -348,35 +311,27 @@ std::string ViewTask::render (std::vector <Task>& data, std::vector <int>& seque
// 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);
}
for (unsigned int c = 0; c < _columns.size (); ++c)
{
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);
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,14 +341,11 @@ 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)
{
for (unsigned int c = 0; c < _columns.size(); ++c) {
if (c) {
if (row_color.nontrivial())
row_color._colorize(out, intra);
else
@ -413,8 +365,7 @@ std::string ViewTask::render (std::vector <Task>& data, std::vector <int>& seque
out += "\n";
// Stop if the line limit is exceeded.
if (++_lines >= _truncate_lines && _truncate_lines != 0)
{
if (++_lines >= _truncate_lines && _truncate_lines != 0) {
Context::getContext().time_render_us += timer.total_us();
return out;
}
@ -423,8 +374,7 @@ std::string ViewTask::render (std::vector <Task>& data, std::vector <int>& seque
cells.clear();
// Stop if the row limit is exceeded.
if (++_rows >= _truncate_rows && _truncate_rows != 0)
{
if (++_rows >= _truncate_rows && _truncate_rows != 0) {
Context::getContext().time_render_us += timer.total_us();
return out;
}

View file

@ -27,23 +27,29 @@
#ifndef INCLUDED_VIEWTASK
#define INCLUDED_VIEWTASK
#include <string>
#include <vector>
#include <Task.h>
#include <Color.h>
#include <Column.h>
#include <Task.h>
class 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 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 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; }
@ -86,4 +92,3 @@ private:
#endif
////////////////////////////////////////////////////////////////////////////////

View file

@ -27,22 +27,22 @@
#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&)
{
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;}
@ -52,12 +52,10 @@ bool get (const std::string&, Variant&)
}
////////////////////////////////////////////////////////////////////////////////
int main (int argc, char** argv)
{
int main(int argc, char** argv) {
int status = 0;
try
{
try {
Context globalContext;
Context::setContext(&globalContext);
@ -74,10 +72,8 @@ int main (int argc, char** argv)
// 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'
@ -88,23 +84,18 @@ int main (int argc, char** argv)
<< " -p|--postfix Postfix expression\n"
<< '\n';
exit(1);
}
else if (!strcmp (argv[i], "-v") || !strcmp (argv[i], "--version"))
{
} 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"))
} 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;
@ -121,22 +112,18 @@ int main (int argc, char** argv)
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 ()
{
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")};
_styles = {"list", "count", "indicator"};
_examples = {"1 2 10", "[3]", Context::getContext().config.get("dependency.indicator")};
_hyphenate = false;
}
@ -59,106 +55,84 @@ 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)
{
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();
if (deptasks.size () > 0)
{
if (_style == "indicator")
{
if (deptasks.size() > 0) {
if (_style == "indicator") {
minimum = maximum = utf8_width(Context::getContext().config.get("dependency.indicator"));
}
else if (_style == "count")
{
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;
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();
unsigned int length;
for (auto& i : deptasks)
{
for (auto& i : deptasks) {
length = format(i.id).length();
if (length > minimum)
minimum = length;
if (length > minimum) minimum = length;
}
}
}
}
////////////////////////////////////////////////////////////////////////////////
void ColumnDepends::render (
std::vector <std::string>& lines,
Task& task,
int width,
Color& color)
{
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")
{
else if (_style == "count") {
renderStringRight(lines, width, color, '[' + format(static_cast<int>(deptasks.size())) + ']');
}
else if (_style == "default" ||
_style == "list")
{
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);
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);
}
@ -168,8 +142,7 @@ void ColumnDepends::modify (Task& task, const std::string& value)
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))
{
if (dep.length() >= 8 && std::regex_match(dep, valid_uuid)) {
// Full UUID, can be added directly
if (dep.length() == 36)
if (removal)
@ -178,8 +151,7 @@ void ColumnDepends::modify (Task& task, const std::string& value)
task.addDependency(dep);
// Short UUID, need to look up full form
else
{
else {
Task loaded_task;
if (Context::getContext().tdb2.get(dep, loaded_task))
if (removal)
@ -193,8 +165,7 @@ void ColumnDepends::modify (Task& task, const std::string& value)
// ID range
else if (dep.find('-') != std::string::npos &&
extractLongInteger(dep.substr(0, hyphen), lower) &&
extractLongInteger (dep.substr (hyphen + 1), upper))
{
extractLongInteger(dep.substr(hyphen + 1), upper)) {
for (long i = lower; i <= upper; i++)
if (removal)
task.removeDependency(i);

View file

@ -29,8 +29,7 @@
#include <ColTypeString.h>
class ColumnDepends : public ColumnTypeString
{
class ColumnDepends : public ColumnTypeString {
public:
ColumnDepends();

View file

@ -28,32 +28,25 @@
// 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 ()
{
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");
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";
@ -62,15 +55,11 @@ ColumnDescription::ColumnDescription ()
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,
_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 + ' ' + t + ' ' + a1 + ' ' + t + ' ' + a2 + ' ' + t + ' ' + a3 + ' ' + t + ' ' + a4,
d.substr(0, 20) + "...",
d + " [4]",
d.substr(0, 20) + "... [4]"};
@ -82,97 +71,75 @@ ColumnDescription::ColumnDescription ()
////////////////////////////////////////////////////////////////////////////////
// Set the minimum and maximum widths for the value.
void ColumnDescription::measure (Task& task, unsigned int& minimum, unsigned int& maximum)
{
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")
{
if (_style == "default" || _style == "combined") {
minimum = longestWord(description);
maximum = utf8_width(description);
if (task.annotation_count)
{
if (task.annotation_count) {
unsigned int min_anno = _indent + Datetime::length(_dateformat);
if (min_anno > minimum)
minimum = min_anno;
if (min_anno > minimum) minimum = min_anno;
for (auto& i : task.getAnnotations ())
{
for (auto& i : task.getAnnotations()) {
unsigned int len = min_anno + 1 + utf8_width(i.second);
if (len > maximum)
maximum = len;
if (len > maximum) maximum = len;
}
}
}
// Just the text
else if (_style == "desc")
{
else if (_style == "desc") {
maximum = utf8_width(description);
minimum = longestWord(description);
}
// The text <date> <anno> ...
else if (_style == "oneline")
{
else if (_style == "oneline") {
minimum = longestWord(description);
maximum = utf8_width(description);
if (task.annotation_count)
{
if (task.annotation_count) {
auto min_anno = Datetime::length(_dateformat);
for (auto& i : task.getAnnotations ())
maximum += min_anno + 1 + utf8_width (i.second);
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);
}
// 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);
}
// 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;
}
}
////////////////////////////////////////////////////////////////////////////////
void ColumnDescription::render (
std::vector <std::string>& lines,
Task& task,
int width,
Color& color)
{
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 ())
{
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;
}
@ -181,27 +148,21 @@ void ColumnDescription::render (
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")
{
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 ())
{
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;
}
@ -210,13 +171,11 @@ void ColumnDescription::render (
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")
{
else if (_style == "truncated") {
int len = utf8_width(description);
if (len > width)
renderStringLeft(lines, width, color, description.substr(0, width - 3) + "...");
@ -225,34 +184,30 @@ void ColumnDescription::render (
}
// 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);
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")
{
else if (_style == "truncated_count") {
int len = utf8_width(description);
std::string annos_count;
int len_annos = 0;
if (task.annotation_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);
}

View file

@ -29,8 +29,7 @@
#include <ColTypeString.h>
class ColumnDescription : public ColumnTypeString
{
class ColumnDescription : public ColumnTypeString {
public:
ColumnDescription();
void measure(Task&, unsigned int&, unsigned int&);

View file

@ -30,8 +30,7 @@
#include <ColDue.h>
////////////////////////////////////////////////////////////////////////////////
ColumnDue::ColumnDue ()
{
ColumnDue::ColumnDue() {
_name = "due";
_modifiable = true;
_label = "Due";
@ -40,12 +39,10 @@ ColumnDue::ColumnDue ()
////////////////////////////////////////////////////////////////////////////////
// Overriden so that style <----> label are linked.
// Note that you can not determine which gets called first.
void ColumnDue::setStyle (const std::string& 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,8 +29,7 @@
#include <ColTypeDate.h>
class ColumnDue : public ColumnTypeDate
{
class ColumnDue : public ColumnTypeDate {
public:
ColumnDue();
void setStyle(const std::string&);

View file

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

View file

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

View file

@ -30,8 +30,7 @@
#include <ColEntry.h>
////////////////////////////////////////////////////////////////////////////////
ColumnEntry::ColumnEntry ()
{
ColumnEntry::ColumnEntry() {
_name = "entry";
_modifiable = true;
_label = "Added";
@ -40,13 +39,10 @@ ColumnEntry::ColumnEntry ()
////////////////////////////////////////////////////////////////////////////////
// Overriden so that style <----> label are linked.
// Note that you can not determine which gets called first.
void ColumnEntry::setStyle (const std::string& 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,8 +29,7 @@
#include <ColTypeDate.h>
class ColumnEntry : public ColumnTypeDate
{
class ColumnEntry : public ColumnTypeDate {
public:
ColumnEntry();
void setStyle(const std::string&);

View file

@ -28,12 +28,11 @@
// cmake.h include header must come first
#include <ColID.h>
#include <math.h>
#include <format.h>
#include <math.h>
////////////////////////////////////////////////////////////////////////////////
ColumnID::ColumnID ()
{
ColumnID::ColumnID() {
_name = "id";
_style = "number";
_label = "ID";
@ -44,27 +43,27 @@ ColumnID::ColumnID ()
////////////////////////////////////////////////////////////////////////////////
// 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)
{
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);

View file

@ -29,8 +29,7 @@
#include <ColTypeNumeric.h>
class ColumnID : public ColumnTypeNumeric
{
class ColumnID : public ColumnTypeNumeric {
public:
ColumnID();
void measure(Task&, unsigned int&, unsigned int&);

View file

@ -31,8 +31,7 @@
#include <format.h>
////////////////////////////////////////////////////////////////////////////////
ColumnIMask::ColumnIMask ()
{
ColumnIMask::ColumnIMask() {
_name = "imask";
_style = "number";
_label = "Mask Index";
@ -43,22 +42,14 @@ ColumnIMask::ColumnIMask ()
////////////////////////////////////////////////////////////////////////////////
// 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,8 +29,7 @@
#include <ColTypeNumeric.h>
class ColumnIMask : public ColumnTypeNumeric
{
class ColumnIMask : public ColumnTypeNumeric {
public:
ColumnIMask();
void measure(Task&, unsigned int&, unsigned int&);

View file

@ -31,8 +31,7 @@
#include <format.h>
////////////////////////////////////////////////////////////////////////////////
ColumnLast::ColumnLast ()
{
ColumnLast::ColumnLast() {
_name = "last";
_style = "number";
_label = "Last instance";
@ -43,22 +42,14 @@ ColumnLast::ColumnLast ()
////////////////////////////////////////////////////////////////////////////////
// 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,8 +29,7 @@
#include <ColTypeNumeric.h>
class ColumnLast : public ColumnTypeNumeric
{
class ColumnLast : public ColumnTypeNumeric {
public:
ColumnLast();
void measure(Task&, unsigned int&, unsigned int&);

View file

@ -31,8 +31,7 @@
#include <format.h>
////////////////////////////////////////////////////////////////////////////////
ColumnMask::ColumnMask ()
{
ColumnMask::ColumnMask() {
_name = "mask";
_style = "default";
_label = "Mask";
@ -43,22 +42,14 @@ ColumnMask::ColumnMask ()
////////////////////////////////////////////////////////////////////////////////
// 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,8 +29,7 @@
#include <ColTypeString.h>
class ColumnMask : public ColumnTypeString
{
class ColumnMask : public ColumnTypeString {
public:
ColumnMask();
void measure(Task&, unsigned int&, unsigned int&);

View file

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

View file

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

View file

@ -31,8 +31,7 @@
#include <format.h>
////////////////////////////////////////////////////////////////////////////////
ColumnParent::ColumnParent ()
{
ColumnParent::ColumnParent() {
_name = "parent";
_style = "long";
_label = "Parent task";
@ -43,29 +42,22 @@ ColumnParent::ColumnParent ()
////////////////////////////////////////////////////////////////////////////////
// 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")
if (_style == "default" || _style == "long")
renderStringLeft(lines, width, color, task.get(_name));
else if (_style == "short")

View file

@ -29,8 +29,7 @@
#include <ColTypeString.h>
class ColumnParent : public ColumnTypeString
{
class ColumnParent : public ColumnTypeString {
public:
ColumnParent();
void measure(Task&, unsigned int&, unsigned int&);

View file

@ -30,44 +30,35 @@
#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 ()
{
ColumnProject::ColumnProject() {
_name = "project";
_style = "full";
_label = "Project";
_styles = {"full", "parent", "indented"};
_examples = {"home.garden",
"home",
" home.garden"};
_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))
{
if (task.has(_name)) {
std::string project = task.get(_name);
if (_style == "parent")
{
if (_style == "parent") {
auto period = project.find('.');
if (period != std::string::npos)
project = project.substr (0, period);
}
else if (_style == "indented")
{
if (period != std::string::npos) project = project.substr(0, period);
} else if (_style == "indented") {
project = indentProject(project, " ", '.');
}
@ -77,71 +68,51 @@ void ColumnProject::measure (Task& task, unsigned int& minimum, unsigned int& ma
}
////////////////////////////////////////////////////////////////////////////////
void ColumnProject::render (
std::vector <std::string>& lines,
Task& task,
int width,
Color& color)
{
if (task.has (_name))
{
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")
{
if (_style == "parent") {
auto period = project.find('.');
if (period != std::string::npos)
project = project.substr (0, period);
}
else if (_style == "indented")
{
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);
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);
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);
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)
{
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.")
{
if (e == "The value is not an expression.") {
task.set(_name, value);
Context::getContext().debug(label + _name + " <-- '" + value + '\'');
}
else
} else
throw;
}
}
else
{
} else {
task.set(_name, value);
Context::getContext().debug(label + _name + " <-- '" + value + '\'');
}

View file

@ -29,8 +29,7 @@
#include <ColTypeString.h>
class ColumnProject : public ColumnTypeString
{
class ColumnProject : public ColumnTypeString {
public:
ColumnProject();
void measure(Task&, unsigned int&, unsigned int&);

View file

@ -29,13 +29,13 @@
#include <ColRType.h>
#include <Context.h>
#include <shared.h>
#include <format.h>
#include <shared.h>
#include <cctype>
////////////////////////////////////////////////////////////////////////////////
ColumnRType::ColumnRType ()
{
ColumnRType::ColumnRType() {
_name = "rtype";
_style = "default";
_label = "Recurrence type";
@ -47,8 +47,7 @@ ColumnRType::ColumnRType ()
////////////////////////////////////////////////////////////////////////////////
// Overriden so that style <----> label are linked.
// Note that you can not determine which gets called first.
void ColumnRType::setStyle (const std::string& value)
{
void ColumnRType::setStyle(const std::string& value) {
Column::setStyle(value);
if (_style == "indicator" && _label == "Recurrence type")
@ -57,11 +56,9 @@ void ColumnRType::setStyle (const std::string& value)
////////////////////////////////////////////////////////////////////////////////
// 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();
else if (_style == "indicator")
@ -70,19 +67,12 @@ void ColumnRType::measure (Task& task, unsigned int& minimum, unsigned int& maxi
}
////////////////////////////////////////////////////////////////////////////////
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));
else if (_style == "indicator")
{
else if (_style == "indicator") {
std::string value{" "};
value[0] = toupper(task.get(_name)[0]);
renderStringRight(lines, width, color, value);
@ -91,10 +81,8 @@ void ColumnRType::render (
}
////////////////////////////////////////////////////////////////////////////////
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,8 +29,7 @@
#include <ColTypeString.h>
class ColumnRType : public ColumnTypeString
{
class ColumnRType : public ColumnTypeString {
public:
ColumnRType();
void setStyle(const std::string&);

View file

@ -31,16 +31,15 @@
#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 ()
{
ColumnRecur::ColumnRecur() {
_name = "recur";
_style = "duration";
_label = "Recur";
@ -52,8 +51,7 @@ ColumnRecur::ColumnRecur ()
////////////////////////////////////////////////////////////////////////////////
// Overriden so that style <----> label are linked.
// Note that you can not determine which gets called first.
void ColumnRecur::setStyle (const std::string& value)
{
void ColumnRecur::setStyle(const std::string& value) {
Column::setStyle(value);
if (_style == "indicator" && _label == "Recur")
@ -62,68 +60,51 @@ void ColumnRecur::setStyle (const std::string& value)
////////////////////////////////////////////////////////////////////////////////
// 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")
{
if (task.has(_name)) {
if (_style == "default" || _style == "duration") {
minimum = maximum = Duration(task.get(_name)).formatISO().length();
}
else if (_style == "indicator")
{
} 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")
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);
}
catch (...)
{
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
} else
throw format("The duration value '{1}' is not supported.", value);
}

View file

@ -31,8 +31,7 @@
// 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
{
class ColumnRecur : public ColumnTypeString {
public:
ColumnRecur();
void setStyle(const std::string&);

View file

@ -30,8 +30,7 @@
#include <ColScheduled.h>
////////////////////////////////////////////////////////////////////////////////
ColumnScheduled::ColumnScheduled ()
{
ColumnScheduled::ColumnScheduled() {
_name = "scheduled";
_label = "Scheduled";
}
@ -39,12 +38,10 @@ ColumnScheduled::ColumnScheduled ()
////////////////////////////////////////////////////////////////////////////////
// Overriden so that style <----> label are linked.
// Note that you can not determine which gets called first.
void ColumnScheduled::setStyle (const std::string& 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,8 +29,7 @@
#include <ColTypeDate.h>
class ColumnScheduled : public ColumnTypeDate
{
class ColumnScheduled : public ColumnTypeDate {
public:
ColumnScheduled();
void setStyle(const std::string&);

View file

@ -32,8 +32,7 @@
#include <utf8.h>
////////////////////////////////////////////////////////////////////////////////
ColumnStart::ColumnStart ()
{
ColumnStart::ColumnStart() {
_name = "start";
_label = "Started";
@ -44,21 +43,17 @@ ColumnStart::ColumnStart ()
////////////////////////////////////////////////////////////////////////////////
// Overriden so that style <----> label are linked.
// Note that you can not determine which gets called first.
void ColumnStart::setStyle (const std::string& 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"));
else
@ -69,20 +64,13 @@ void ColumnStart::measure (Task& task, unsigned int& minimum, unsigned int& maxi
}
////////////////////////////////////////////////////////////////////////////////
void ColumnStart::render (
std::vector <std::string>& lines,
Task& task,
int width,
Color& color)
{
if (task.has (_name))
{
if (_style == "active")
{
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
renderStringRight(lines, width, color,
Context::getContext().config.get("active.indicator"));
} else
ColumnTypeDate::render(lines, task, width, color);
}
}

View file

@ -29,8 +29,7 @@
#include <ColTypeDate.h>
class ColumnStart : public ColumnTypeDate
{
class ColumnStart : public ColumnTypeDate {
public:
ColumnStart();
void setStyle(const std::string&);

View file

@ -32,36 +32,29 @@
#include <utf8.h>
////////////////////////////////////////////////////////////////////////////////
ColumnStatus::ColumnStatus ()
{
ColumnStatus::ColumnStatus() {
_name = "status";
_style = "long";
_label = "Status";
_styles = {"long", "short"};
_examples = {"Pending",
"P"};
_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)
{
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)
{
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");
else if (status == Task::deleted)
@ -72,38 +65,39 @@ void ColumnStatus::measure (Task& task, unsigned int& minimum, unsigned int& max
minimum = maximum = utf8_width("Completed");
else if (status == Task::recurring)
minimum = maximum = utf8_width("Recurring");
}
else if (_style == "short")
} else if (_style == "short")
minimum = maximum = 1;
}
////////////////////////////////////////////////////////////////////////////////
void ColumnStatus::render (
std::vector <std::string>& lines,
Task& task,
int width,
Color& color)
{
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);

View file

@ -29,8 +29,7 @@
#include <ColTypeString.h>
class ColumnStatus : public ColumnTypeString
{
class ColumnStatus : public ColumnTypeString {
public:
ColumnStatus();
void setStyle(const std::string&);

View file

@ -28,74 +28,58 @@
// 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 ()
{
ColumnTags::ColumnTags() {
_name = "tags";
_style = "list";
_label = "Tags";
_styles = {"list", "indicator", "count"};
_examples = {"home @chore next",
Context::getContext ().config.get ("tag.indicator"),
"[2]"};
_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)
{
void ColumnTags::setStyle(const std::string& value) {
Column::setStyle(value);
if (_style == "indicator" &&
_label == "Tags")
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")
{
if (task.has(_name)) {
if (_style == "indicator") {
minimum = maximum = utf8_width(Context::getContext().config.get("tag.indicator"));
}
else if (_style == "count")
{
} else if (_style == "count") {
minimum = maximum = 3;
}
else if (_style == "default" ||
_style == "list")
{
} else if (_style == "default" || _style == "list") {
std::string tags = task.get(_name);
// Find the widest tag.
if (tags.find (',') != std::string::npos)
{
if (tags.find(',') != std::string::npos) {
auto all = split(tags, ',');
for (const auto& tag : all)
{
for (const auto& tag : all) {
auto length = utf8_width(tag);
if (length > minimum)
minimum = length;
if (length > minimum) minimum = length;
}
maximum = utf8_width(tags);
@ -109,46 +93,30 @@ void ColumnTags::measure (Task& task, unsigned int& minimum, unsigned int& maxim
}
////////////////////////////////////////////////////////////////////////////////
void ColumnTags::render (
std::vector <std::string>& lines,
Task& task,
int width,
Color& color)
{
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)
{
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);
for (const auto& i : all)
renderStringLeft (lines, width, color, i);
}
else
for (const auto& i : all) renderStringLeft(lines, width, color, i);
} else
renderStringLeft(lines, width, color, all[0]);
}
else if (_style == "indicator")
{
} else if (_style == "indicator") {
renderStringRight(lines, width, color, Context::getContext().config.get("tag.indicator"));
}
else if (_style == "count")
{
} 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;
@ -157,9 +125,7 @@ void ColumnTags::modify (Task& task, const std::string& 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);
@ -170,8 +136,7 @@ void ColumnTags::modify (Task& task, const std::string& value)
commasep = (std::string)value;
}
for (auto& tag : split (commasep, ','))
{
for (auto& tag : split(commasep, ',')) {
tags.push_back((std::string)tag);
Context::getContext().debug(label + "tags <-- '" + tag + '\'');

View file

@ -29,8 +29,7 @@
#include <ColTypeString.h>
class ColumnTags : public ColumnTypeString
{
class ColumnTags : public ColumnTypeString {
public:
ColumnTags();
void setStyle(const std::string&);

View file

@ -31,8 +31,7 @@
#include <format.h>
////////////////////////////////////////////////////////////////////////////////
ColumnTemplate::ColumnTemplate ()
{
ColumnTemplate::ColumnTemplate() {
_name = "template";
_style = "long";
_label = "Template task";
@ -43,29 +42,22 @@ ColumnTemplate::ColumnTemplate ()
////////////////////////////////////////////////////////////////////////////////
// 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")
if (_style == "default" || _style == "long")
renderStringLeft(lines, width, color, task.get(_name));
else if (_style == "short")

View file

@ -29,8 +29,7 @@
#include <ColTypeString.h>
class ColumnTemplate : public ColumnTypeString
{
class ColumnTemplate : public ColumnTypeString {
public:
ColumnTemplate();
void measure(Task&, unsigned int&, unsigned int&);

View file

@ -32,25 +32,17 @@
#include <Datetime.h>
#include <Duration.h>
#include <Eval.h>
#include <Variant.h>
#include <Filter.h>
#include <Variant.h>
#include <format.h>
////////////////////////////////////////////////////////////////////////////////
ColumnTypeDate::ColumnTypeDate ()
{
ColumnTypeDate::ColumnTypeDate() {
_name = "";
_type = "date";
_style = "formatted";
_label = "";
_styles = {"formatted",
"julian",
"epoch",
"iso",
"age",
"relative",
"remaining",
"countdown"};
_styles = {"formatted", "julian", "epoch", "iso", "age", "relative", "remaining", "countdown"};
Datetime now;
now -= 125; // So that "age" is non-zero.
@ -66,104 +58,70 @@ ColumnTypeDate::ColumnTypeDate ()
////////////////////////////////////////////////////////////////////////////////
// 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))
{
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");
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")
{
} else if (_style == "countdown") {
Datetime now;
minimum = maximum = Duration(date - now).formatVague(true).length();
}
else if (_style == "julian")
{
} else if (_style == "julian") {
minimum = maximum = format(date.toJulian(), 13, 12).length();
}
else if (_style == "epoch")
{
} else if (_style == "epoch") {
minimum = maximum = date.toEpochString().length();
}
else if (_style == "iso")
{
} else if (_style == "iso") {
minimum = maximum = date.toISO().length();
}
else if (_style == "age")
{
} else if (_style == "age") {
Datetime now;
if (now > date)
minimum = maximum = Duration(now - date).formatVague(true).length();
else
minimum = maximum = Duration(date - now).formatVague(true).length() + 1;
}
else if (_style == "relative")
{
} else if (_style == "relative") {
Datetime now;
if (now < date)
minimum = maximum = Duration(date - now).formatVague(true).length();
else
minimum = maximum = Duration(now - date).formatVague(true).length() + 1;
}
else if (_style == "remaining")
{
} 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))
{
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 == "")
{
if (format == "") {
format = Context::getContext().config.get("dateformat.report");
if (format == "")
format = Context::getContext ().config.get ("dateformat");
if (format == "") format = Context::getContext().config.get("dateformat");
}
renderStringLeft(lines, width, color, date.toString(format));
}
else if (_style == "countdown")
{
} else if (_style == "countdown") {
Datetime now;
renderStringRight(lines, width, color, Duration(date - now).formatVague(true));
}
else if (_style == "julian")
} else if (_style == "julian")
renderStringRight(lines, width, color, format(date.toJulian(), 13, 12));
else if (_style == "epoch")
@ -172,16 +130,13 @@ void ColumnTypeDate::render (
else if (_style == "iso")
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));
else
renderStringRight(lines, width, color, '-' + Duration(date - now).formatVague(true));
}
else if (_style == "relative")
{
} else if (_style == "relative") {
Datetime now;
if (now < date)
renderStringRight(lines, width, color, Duration(date - now).formatVague(true));
@ -189,8 +144,7 @@ void ColumnTypeDate::render (
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));
@ -199,45 +153,41 @@ void ColumnTypeDate::render (
}
////////////////////////////////////////////////////////////////////////////////
bool ColumnTypeDate::validate (const std::string& input) const
{
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);
}
catch (...)
{
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 + '\'');
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
{
} else {
evaluatedValue.cast(Variant::type_date);
Context::getContext ().debug (label + _name + " <-- '" + format ("{1}", evaluatedValue.get_date ()) + "' <-- '" + (std::string) evaluatedValue + "' <-- '" + value + '\'');
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)
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());

View file

@ -27,14 +27,14 @@
#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
{
#include <string>
#include <vector>
class ColumnTypeDate : public Column {
public:
ColumnTypeDate();
virtual void measure(Task&, unsigned int&, unsigned int&);

View file

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

View file

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

View file

@ -30,45 +30,38 @@
#include <ColTypeNumeric.h>
#include <Context.h>
#include <Eval.h>
#include <Variant.h>
#include <Filter.h>
#include <Variant.h>
#include <format.h>
////////////////////////////////////////////////////////////////////////////////
ColumnTypeNumeric::ColumnTypeNumeric ()
{
_type = "numeric";
}
ColumnTypeNumeric::ColumnTypeNumeric() { _type = "numeric"; }
////////////////////////////////////////////////////////////////////////////////
bool ColumnTypeNumeric::validate (const std::string& input) const
{
bool ColumnTypeNumeric::validate(const std::string& input) const {
return input.length() ? true : false;
}
////////////////////////////////////////////////////////////////////////////////
void ColumnTypeNumeric::modify (Task& task, const std::string& value)
{
void ColumnTypeNumeric::modify(Task& task, const std::string& value) {
// Try to evaluate 'value'. It might work.
Variant evaluatedValue;
try
{
try {
Eval e;
e.addSource(domSource);
e.evaluateInfixExpression(value, evaluatedValue);
}
catch (...)
{
catch (...) {
evaluatedValue = Variant(value);
}
std::string label = " MODIFICATION ";
Context::getContext ().debug (label + _name + " <-- '" + evaluatedValue.get_string () + "' <-- '" + value + '\'');
Context::getContext().debug(label + _name + " <-- '" + evaluatedValue.get_string() + "' <-- '" +
value + '\'');
// Convert the value of the expression to the correct type if needed
switch (evaluatedValue.type ())
{
switch (evaluatedValue.type()) {
// Expected variants - no conversion
case Variant::type_integer:
case Variant::type_real:

View file

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

View file

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

View file

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

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