Merge pull request #3566 from felixschurk/add-clang-format

Add clang-format to enforce style guide
This commit is contained in:
Dustin J. Mitchell
2024-07-29 17:45:36 -04:00
committed by GitHub
424 changed files with 21676 additions and 23888 deletions

5
.clang-format Normal file
View File

@@ -0,0 +1,5 @@
---
Language: Cpp
BasedOnStyle: Google
ColumnLimit: 100
...

2
.git-blame-ignore-revs Normal file
View File

@@ -0,0 +1,2 @@
# initial bulk run of formatting with pre-commit
93356b39c3086fdf8dd41d7357bb1c115ff69cb1

View File

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

1
.gitignore vendored
View File

@@ -1,5 +1,6 @@
cmake.h cmake.h
commit.h commit.h
.cache
*~ *~
.*.swp .*.swp
Session.vim Session.vim

19
.pre-commit-config.yaml Normal file
View File

@@ -0,0 +1,19 @@
# See https://pre-commit.com for more information
# See https://pre-commit.com/hooks.html for more hooks
repos:
- repo: https://github.com/pre-commit/pre-commit-hooks
rev: v4.6.0
hooks:
- id: trailing-whitespace
- id: end-of-file-fixer
- id: check-yaml
- id: check-added-large-files
- repo: https://github.com/pre-commit/mirrors-clang-format
rev: v18.1.8
hooks:
- id: clang-format
types_or: [c++, c]
- repo: https://github.com/psf/black
rev: 24.4.2
hooks:
- id: black

View File

@@ -2785,4 +2785,3 @@ regular usage to determine which features were needed or unnecessary.]
- Usage. - Usage.
------ start ----------------------------------- ------ 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, 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 OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
SOFTWARE. SOFTWARE.

View File

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

View File

@@ -1,30 +1,18 @@
# Coding Style # Coding Style
The coding style used for the Taskwarrior, Taskserver, and other codebases is deliberately kept simple and a little vague. The coding style used for the Taskwarrior is based on the [Google C++ Style Guide](https://google.github.io/styleguide/cppguide.html), with small modifications in the line length.
This is because there are many languages involved (C++, C, Python, sh, bash, HTML, troff and more), and specіfying those would be a major effort that detracts from the main focus which is improving the software.
Instead, the general guideline is simply this: # Automatic formatting
Make all changes and additions such that they blend in perfectly with the surrounding code, so it looks like only one person worked on the source, and that person is rigidly consistent. In order to have consistancy and automatic formatting [pre-commit](https://pre-commit.com) is used to apply [clang-format](https://clang.llvm.org/docs/ClangFormat.html) and [black](https://github.com/psf/black) are used.
In order to set them up locally please run:
```python
pip install pre-commit
pre-commit install
```
To be a little more explicit: For more information refer to the [quick-start of pre-commit](https://pre-commit.com/#quick-start).
The setup is also included in the CI, hence if one can not install it automatically then the CI will take care for it in the PR.
## C++
- All functionality in C++17 is allowed.
- Indent C++ code using two spaces, no tabs
- Surround operators and expression terms with a space.
This includes a space between a function name and its list of arguments.
- No cuddled braces
- Class names are capitalized, variable names are not
## Python
Follow [PEP8](https://www.python.org/dev/peps/pep-0008/) as much as possible.
## Rust ## Rust

View File

@@ -9,6 +9,7 @@
## Install Optional Dependencies: ## Install Optional Dependencies:
* python 3 (for running the test suite) * python 3 (for running the test suite)
* pre-commit (for applying formatting changes locally)
* clangd or ccls (for C++ integration in many editors) * clangd or ccls (for C++ integration in many editors)
* rust-analyzer (for Rust integration in many editors) * rust-analyzer (for Rust integration in many editors)

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. 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. UDAs may have one of four types: string, numeric, date and duration.

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

@@ -112,4 +112,3 @@ color.sync.rejected=color13
# Command: undo # Command: undo
color.undo.after=color2 color.undo.after=color2
color.undo.before=color1 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 Note that for the solarized themes, the terminal color palette needs to be set
to specific colors. 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 14 mod depends:13
task rc:x 15 delete 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 add_custom_target (performance ${CMAKE_BINARY_DIR}/performance/run_perf
DEPENDS task_executable DEPENDS task_executable
WORKING_DIRECTORY ${CMAKE_BINARY_DIR}/performance) WORKING_DIRECTORY ${CMAKE_BINARY_DIR}/performance)

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

File diff suppressed because it is too large Load Diff

View File

@@ -26,16 +26,16 @@
#ifndef INCLUDED_CLI2 #ifndef INCLUDED_CLI2
#define INCLUDED_CLI2 #define INCLUDED_CLI2
#include <string>
#include <vector>
#include <map>
#include <unordered_map>
#include <Lexer.h>
#include <FS.h> #include <FS.h>
#include <Lexer.h>
#include <map>
#include <string>
#include <unordered_map>
#include <vector>
// Represents a single argument. // Represents a single argument.
class A2 class A2 {
{
public: public:
A2(const std::string&, Lexer::Type); A2(const std::string&, Lexer::Type);
A2(const A2&); A2(const A2&);
@@ -56,8 +56,7 @@ public:
}; };
// Represents the command line. // Represents the command line.
class CLI2 class CLI2 {
{
public: public:
static int minimumMatchLength; static int minimumMatchLength;
@@ -122,4 +121,3 @@ public:
}; };
#endif #endif

File diff suppressed because it is too large Load Diff

View File

@@ -27,21 +27,21 @@
#ifndef INCLUDED_CONTEXT #ifndef INCLUDED_CONTEXT
#define 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 <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 <Timer.h>
#include <set> #include <set>
class CurrentTask; class CurrentTask;
class Context class Context {
{
public: public:
Context() = default; // Default constructor Context() = default; // Default constructor
~Context(); // Destructor ~Context(); // Destructor

View File

@@ -25,19 +25,22 @@
//////////////////////////////////////////////////////////////////////////////// ////////////////////////////////////////////////////////////////////////////////
#include <cmake.h> #include <cmake.h>
#include <DOM.h> // cmake.h include header must come first
#include <sstream>
#include <map>
#include <stdlib.h>
#include <Variant.h>
#include <Lexer.h>
#include <Context.h> #include <Context.h>
#include <DOM.h>
#include <Datetime.h> #include <Datetime.h>
#include <Duration.h> #include <Duration.h>
#include <shared.h> #include <Lexer.h>
#include <Variant.h>
#include <format.h> #include <format.h>
#include <shared.h>
#include <stdlib.h>
#include <util.h> #include <util.h>
#include <map>
#include <sstream>
//////////////////////////////////////////////////////////////////////////////// ////////////////////////////////////////////////////////////////////////////////
// DOM Supported References: // DOM Supported References:
// //
@@ -60,22 +63,17 @@
// system.version // system.version
// system.os // system.os
// //
bool getDOM (const std::string& name, Variant& value) bool getDOM(const std::string& name, Variant& value) {
{
// Special case, blank refs cause problems. // Special case, blank refs cause problems.
if (name == "") if (name == "") return false;
return false;
auto len = name.length(); auto len = name.length();
// rc. --> context.config // rc. --> context.config
if (len > 3 && if (len > 3 && !name.compare(0, 3, "rc.", 3)) {
! name.compare (0, 3, "rc.", 3))
{
auto key = name.substr(3); auto key = name.substr(3);
auto c = Context::getContext().config.find(key); auto c = Context::getContext().config.find(key);
if (c != Context::getContext ().config.end ()) if (c != Context::getContext().config.end()) {
{
value = Variant(c->second); value = Variant(c->second);
return true; return true;
} }
@@ -84,54 +82,40 @@ bool getDOM (const std::string& name, Variant& value)
} }
// tw.* // tw.*
if (len > 3 && if (len > 3 && !name.compare(0, 3, "tw.", 3)) {
! name.compare (0, 3, "tw.", 3)) if (name == "tw.syncneeded") {
{
if (name == "tw.syncneeded")
{
value = Variant(0); value = Variant(0);
if (Context::getContext().tdb2.num_local_changes() > 0) { if (Context::getContext().tdb2.num_local_changes() > 0) {
value = Variant(1); value = Variant(1);
} }
return true; return true;
} } else if (name == "tw.program") {
else if (name == "tw.program")
{
value = Variant(Context::getContext().cli2.getBinary()); value = Variant(Context::getContext().cli2.getBinary());
return true; return true;
} } else if (name == "tw.args") {
else if (name == "tw.args")
{
std::string commandLine; std::string commandLine;
for (auto& arg : Context::getContext ().cli2._original_args) for (auto& arg : Context::getContext().cli2._original_args) {
{ if (commandLine != "") commandLine += ' ';
if (commandLine != "")
commandLine += ' ';
commandLine += arg.attribute("raw"); commandLine += arg.attribute("raw");
} }
value = Variant(commandLine); value = Variant(commandLine);
return true; return true;
} } else if (name == "tw.width") {
else if (name == "tw.width")
{
value = Variant(static_cast<int>(Context::getContext().terminal_width value = Variant(static_cast<int>(Context::getContext().terminal_width
? Context::getContext().terminal_width ? Context::getContext().terminal_width
: Context::getContext().getWidth())); : Context::getContext().getWidth()));
return true; return true;
} } else if (name == "tw.height") {
else if (name == "tw.height")
{
value = Variant(static_cast<int>(Context::getContext().terminal_height value = Variant(static_cast<int>(Context::getContext().terminal_height
? Context::getContext().terminal_height ? Context::getContext().terminal_height
: Context::getContext().getHeight())); : Context::getContext().getHeight()));
return true; return true;
} }
else if (name == "tw.version") else if (name == "tw.version") {
{
value = Variant(VERSION); value = Variant(VERSION);
return true; return true;
} }
@@ -140,37 +124,26 @@ bool getDOM (const std::string& name, Variant& value)
} }
// context.* // context.*
if (len > 8 && if (len > 8 && !name.compare(0, 8, "context.", 8)) {
! name.compare (0, 8, "context.", 8)) if (name == "context.program") {
{
if (name == "context.program")
{
value = Variant(Context::getContext().cli2.getBinary()); value = Variant(Context::getContext().cli2.getBinary());
return true; return true;
} } else if (name == "context.args") {
else if (name == "context.args")
{
std::string commandLine; std::string commandLine;
for (auto& arg : Context::getContext ().cli2._original_args) for (auto& arg : Context::getContext().cli2._original_args) {
{ if (commandLine != "") commandLine += ' ';
if (commandLine != "")
commandLine += ' ';
commandLine += arg.attribute("raw"); commandLine += arg.attribute("raw");
} }
value = Variant(commandLine); value = Variant(commandLine);
return true; return true;
} } else if (name == "context.width") {
else if (name == "context.width")
{
value = Variant(static_cast<int>(Context::getContext().terminal_width value = Variant(static_cast<int>(Context::getContext().terminal_width
? Context::getContext().terminal_width ? Context::getContext().terminal_width
: Context::getContext().getWidth())); : Context::getContext().getWidth()));
return true; return true;
} } else if (name == "context.height") {
else if (name == "context.height")
{
value = Variant(static_cast<int>(Context::getContext().terminal_height value = Variant(static_cast<int>(Context::getContext().terminal_height
? Context::getContext().terminal_height ? Context::getContext().terminal_height
: Context::getContext().getHeight())); : Context::getContext().getHeight()));
@@ -181,19 +154,15 @@ bool getDOM (const std::string& name, Variant& value)
} }
// system. --> Implement locally. // system. --> Implement locally.
if (len > 7 && if (len > 7 && !name.compare(0, 7, "system.", 7)) {
! name.compare (0, 7, "system.", 7))
{
// Taskwarrior version number. // Taskwarrior version number.
if (name == "system.version") if (name == "system.version") {
{
value = Variant(VERSION); value = Variant(VERSION);
return true; return true;
} }
// OS type. // OS type.
else if (name == "system.os") else if (name == "system.os") {
{
value = Variant(osName()); value = Variant(osName());
return true; return true;
} }
@@ -237,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 // If task is NULL, then the contextual task will be determined from the DOM
// string, if any exists. // 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. // Special case, blank refs cause problems.
if (name == "") if (name == "") return false;
return false;
// Quickly deal with the most common cases. // Quickly deal with the most common cases.
if (task && name == "id") if (task && name == "id") {
{
value = Variant(static_cast<int>(task->id)); value = Variant(static_cast<int>(task->id));
return true; return true;
} }
if (task && name == "urgency") if (task && name == "urgency") {
{
value = Variant(task->urgency_c()); value = Variant(task->urgency_c());
return true; return true;
} }
@@ -270,93 +235,72 @@ bool getDOM (const std::string& name, const Task* task, Variant& value)
// If this can be ID/UUID reference (the name contains '.'), // If this can be ID/UUID reference (the name contains '.'),
// lex it to figure out. Otherwise don't lex, as lexing can be slow. // 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; bool reloaded = false;
if (type == Lexer::Type::uuid && if (type == Lexer::Type::uuid && token.length() == elements[0].length()) {
token.length () == elements[0].length ()) if (!task || token != task->get("uuid")) {
{ if (Context::getContext().tdb2.get(token, loaded_task)) reloaded = true;
if (!task || token != task->get ("uuid"))
{
if (Context::getContext ().tdb2.get (token, loaded_task))
reloaded = true;
} }
// Eat elements[0]/UUID. // Eat elements[0]/UUID.
elements.erase(elements.begin()); 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); auto id = strtol(token.c_str(), nullptr, 10);
if (id && (!task || id != task->id)) if (id && (!task || id != task->id)) {
{ if (Context::getContext().tdb2.get(id, loaded_task)) reloaded = true;
if (Context::getContext ().tdb2.get (id, loaded_task))
reloaded = true;
} }
// Eat elements[0]/ID. // Eat elements[0]/ID.
elements.erase(elements.begin()); elements.erase(elements.begin());
} }
if (reloaded) if (reloaded) ref = &loaded_task;
ref = &loaded_task;
} }
// The remainder of this method requires a contextual task, so if we do not // The remainder of this method requires a contextual task, so if we do not
// have one, delegate to the two-argument getDOM // have one, delegate to the two-argument getDOM
if (!ref) if (!ref) return getDOM(name, value);
return getDOM (name, value);
auto size = elements.size(); auto size = elements.size();
std::string canonical; 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 // Now that 'ref' is the contextual task, and any ID/UUID is chopped off the
// elements vector, DOM resolution is now simple. // elements vector, DOM resolution is now simple.
if (size == 1 && canonical == "id") if (size == 1 && canonical == "id") {
{
value = Variant(static_cast<int>(ref->id)); value = Variant(static_cast<int>(ref->id));
return true; return true;
} }
if (size == 1 && canonical == "urgency") if (size == 1 && canonical == "urgency") {
{
value = Variant(ref->urgency_c()); value = Variant(ref->urgency_c());
return true; return true;
} }
// Special handling of status required for virtual waiting status // Special handling of status required for virtual waiting status
// implementation. Remove in 3.0.0. // implementation. Remove in 3.0.0.
if (size == 1 && canonical == "status") if (size == 1 && canonical == "status") {
{
value = Variant(ref->statusToText(ref->getStatus())); value = Variant(ref->statusToText(ref->getStatus()));
return true; return true;
} }
Column* column = Context::getContext().columns[canonical]; Column* column = Context::getContext().columns[canonical];
if (size == 1 && column) if (size == 1 && column) {
{ if (column->is_uda() && !ref->has(canonical)) {
if (column->is_uda () && ! ref->has (canonical))
{
value = Variant(""); value = Variant("");
return true; return true;
} }
if (column->type () == "date") if (column->type() == "date") {
{
auto numeric = ref->get_date(canonical); auto numeric = ref->get_date(canonical);
if (numeric == 0) if (numeric == 0)
value = Variant(""); value = Variant("");
else else
value = Variant(numeric, Variant::type_date); 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); auto period = ref->get(canonical);
Duration iso; Duration iso;
@@ -365,8 +309,7 @@ bool getDOM (const std::string& name, const Task* task, Variant& value)
value = Variant(iso.toTime_t(), Variant::type_duration); value = Variant(iso.toTime_t(), Variant::type_duration);
else else
value = Variant(Duration(ref->get(canonical)).toTime_t(), Variant::type_duration); 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)); value = Variant(ref->get_float(canonical));
else else
value = Variant(ref->get(canonical)); value = Variant(ref->get(canonical));
@@ -374,54 +317,65 @@ bool getDOM (const std::string& name, const Task* task, Variant& value)
return true; return true;
} }
if (size == 2 && canonical == "tags") if (size == 2 && canonical == "tags") {
{
value = Variant(ref->hasTag(elements[1]) ? elements[1] : ""); value = Variant(ref->hasTag(elements[1]) ? elements[1] : "");
return true; return true;
} }
if (size == 2 && column && column->type () == "date") if (size == 2 && column && column->type() == "date") {
{
Datetime date(ref->get_date(canonical)); Datetime date(ref->get_date(canonical));
if (elements[1] == "year") { value = Variant (static_cast<int> (date.year ())); return true; } if (elements[1] == "year") {
else if (elements[1] == "month") { value = Variant (static_cast<int> (date.month ())); return true; } value = Variant(static_cast<int>(date.year()));
else if (elements[1] == "day") { value = Variant (static_cast<int> (date.day ())); return true; } return true;
else if (elements[1] == "week") { value = Variant (static_cast<int> (date.week ())); return true; } } else if (elements[1] == "month") {
else if (elements[1] == "weekday") { value = Variant (static_cast<int> (date.dayOfWeek ())); return true; } value = Variant(static_cast<int>(date.month()));
else if (elements[1] == "julian") { value = Variant (static_cast<int> (date.dayOfYear ())); return true; } return true;
else if (elements[1] == "hour") { value = Variant (static_cast<int> (date.hour ())); return true; } } else if (elements[1] == "day") {
else if (elements[1] == "minute") { value = Variant (static_cast<int> (date.minute ())); return true; } value = Variant(static_cast<int>(date.day()));
else if (elements[1] == "second") { value = Variant (static_cast<int> (date.second ())); return true; } 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())); value = Variant(static_cast<int>(ref->getAnnotationCount()));
return true; return true;
} }
if (size == 3 && elements[0] == "annotations") if (size == 3 && elements[0] == "annotations") {
{
auto annos = ref->getAnnotations(); auto annos = ref->getAnnotations();
int a = strtol(elements[1].c_str(), nullptr, 10); int a = strtol(elements[1].c_str(), nullptr, 10);
int count = 0; int count = 0;
// Count off the 'a'th annotation. // Count off the 'a'th annotation.
for (const auto& i : annos) for (const auto& i : annos) {
{ if (++count == a) {
if (++count == a) if (elements[2] == "entry") {
{
if (elements[2] == "entry")
{
// annotation_1234567890 // annotation_1234567890
// 0 ^11 // 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; return true;
} } else if (elements[2] == "description") {
else if (elements[2] == "description")
{
value = Variant(i.second); value = Variant(i.second);
return true; return true;
} }
@@ -429,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(); auto annos = ref->getAnnotations();
int a = strtol(elements[1].c_str(), nullptr, 10); int a = strtol(elements[1].c_str(), nullptr, 10);
int count = 0; int count = 0;
// Count off the 'a'th annotation. // Count off the 'a'th annotation.
for (const auto& i : annos) for (const auto& i : annos) {
{ if (++count == a) {
if (++count == a)
{
// <annotations>.<N>.entry.year // <annotations>.<N>.entry.year
// <annotations>.<N>.entry.month // <annotations>.<N>.entry.month
// <annotations>.<N>.entry.day // <annotations>.<N>.entry.day
@@ -451,15 +402,34 @@ bool getDOM (const std::string& name, const Task* task, Variant& value)
// <annotations>.<N>.entry.minute // <annotations>.<N>.entry.minute
// <annotations>.<N>.entry.second // <annotations>.<N>.entry.second
Datetime date(i.first.substr(11)); Datetime date(i.first.substr(11));
if (elements[3] == "year") { value = Variant (static_cast<int> (date.year ())); return true; } if (elements[3] == "year") {
else if (elements[3] == "month") { value = Variant (static_cast<int> (date.month ())); return true; } value = Variant(static_cast<int>(date.year()));
else if (elements[3] == "day") { value = Variant (static_cast<int> (date.day ())); return true; } return true;
else if (elements[3] == "week") { value = Variant (static_cast<int> (date.week ())); return true; } } else if (elements[3] == "month") {
else if (elements[3] == "weekday") { value = Variant (static_cast<int> (date.dayOfWeek ())); return true; } value = Variant(static_cast<int>(date.month()));
else if (elements[3] == "julian") { value = Variant (static_cast<int> (date.dayOfYear ())); return true; } return true;
else if (elements[3] == "hour") { value = Variant (static_cast<int> (date.hour ())); return true; } } else if (elements[3] == "day") {
else if (elements[3] == "minute") { value = Variant (static_cast<int> (date.minute ())); return true; } value = Variant(static_cast<int>(date.day()));
else if (elements[3] == "second") { value = Variant (static_cast<int> (date.second ())); return true; } 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;
}
} }
} }
} }
@@ -511,41 +481,28 @@ bool getDOM (const std::string& name, const Task* task, Variant& value)
// This makes the DOM class a reusible object. // This makes the DOM class a reusible object.
//////////////////////////////////////////////////////////////////////////////// ////////////////////////////////////////////////////////////////////////////////
DOM::~DOM () DOM::~DOM() { delete _node; }
{
delete _node;
}
//////////////////////////////////////////////////////////////////////////////// ////////////////////////////////////////////////////////////////////////////////
void DOM::addSource ( void DOM::addSource(const std::string& reference, bool (*provider)(const std::string&, Variant&)) {
const std::string& reference, if (_node == nullptr) _node = new DOM::Node();
bool (*provider)(const std::string&, Variant&))
{
if (_node == nullptr)
_node = new DOM::Node ();
_node->addSource(reference, provider); _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; return _node && _node->find(reference) != nullptr;
} }
//////////////////////////////////////////////////////////////////////////////// ////////////////////////////////////////////////////////////////////////////////
Variant DOM::get (const std::string& reference) const Variant DOM::get(const std::string& reference) const {
{
Variant v(""); Variant v("");
if (_node) if (_node) {
{
auto node = _node->find(reference); auto node = _node->find(reference);
if (node != nullptr && if (node != nullptr && node->_provider != nullptr) {
node->_provider != nullptr) if (node->_provider(reference, v)) return v;
{
if (node->_provider (reference, v))
return v;
} }
} }
@@ -553,57 +510,44 @@ Variant DOM::get (const std::string& reference) const
} }
//////////////////////////////////////////////////////////////////////////////// ////////////////////////////////////////////////////////////////////////////////
int DOM::count () const int DOM::count() const {
{ if (_node) return _node->count();
if (_node)
return _node->count ();
return 0; 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, '.'); return split(reference, '.');
} }
//////////////////////////////////////////////////////////////////////////////// ////////////////////////////////////////////////////////////////////////////////
std::string DOM::dump () const std::string DOM::dump() const {
{ if (_node) return _node->dump();
if (_node)
return _node->dump ();
return ""; return "";
} }
//////////////////////////////////////////////////////////////////////////////// ////////////////////////////////////////////////////////////////////////////////
DOM::Node::~Node () DOM::Node::~Node() {
{ for (auto& branch : _branches) delete branch;
for (auto& branch : _branches)
delete branch;
} }
//////////////////////////////////////////////////////////////////////////////// ////////////////////////////////////////////////////////////////////////////////
void DOM::Node::addSource ( void DOM::Node::addSource(const std::string& reference,
const std::string& reference, bool (*provider)(const std::string&, Variant&)) {
bool (*provider)(const std::string&, Variant&))
{
auto cursor = this; auto cursor = this;
for (const auto& element : DOM::decomposeReference (reference)) for (const auto& element : DOM::decomposeReference(reference)) {
{
auto found{false}; auto found{false};
for (auto& branch : cursor->_branches) for (auto& branch : cursor->_branches) {
{ if (branch->_name == element) {
if (branch->_name == element)
{
cursor = branch; cursor = branch;
found = true; found = true;
break; break;
} }
} }
if (! found) if (!found) {
{
auto branch = new DOM::Node(); auto branch = new DOM::Node();
branch->_name = element; branch->_name = element;
cursor->_branches.push_back(branch); cursor->_branches.push_back(branch);
@@ -616,47 +560,35 @@ void DOM::Node::addSource (
//////////////////////////////////////////////////////////////////////////////// ////////////////////////////////////////////////////////////////////////////////
// A valid reference is one that has a provider function. // A valid reference is one that has a provider function.
bool DOM::Node::valid (const std::string& reference) const bool DOM::Node::valid(const std::string& reference) const { return find(reference) != nullptr; }
{
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; auto cursor = this;
for (const auto& element : DOM::decomposeReference (reference)) for (const auto& element : DOM::decomposeReference(reference)) {
{
auto found{false}; auto found{false};
for (auto& branch : cursor->_branches) for (auto& branch : cursor->_branches) {
{ if (branch->_name == element) {
if (branch->_name == element)
{
cursor = branch; cursor = branch;
found = true; found = true;
break; break;
} }
} }
if (! found) if (!found) break;
break;
} }
if (reference.length () && cursor != this) if (reference.length() && cursor != this) return cursor;
return cursor;
return nullptr; return nullptr;
} }
//////////////////////////////////////////////////////////////////////////////// ////////////////////////////////////////////////////////////////////////////////
int DOM::Node::count () const int DOM::Node::count() const {
{
// Recurse and count the branches. // Recurse and count the branches.
int total{0}; int total{0};
for (auto& branch : _branches) for (auto& branch : _branches) {
{ if (branch->_provider) ++total;
if (branch->_provider)
++total;
total += branch->count(); total += branch->count();
} }
@@ -664,10 +596,7 @@ int DOM::Node::count () const
} }
//////////////////////////////////////////////////////////////////////////////// ////////////////////////////////////////////////////////////////////////////////
std::string DOM::Node::dumpNode ( std::string DOM::Node::dumpNode(const DOM::Node* node, int depth) const {
const DOM::Node* node,
int depth) const
{
std::stringstream out; std::stringstream out;
// Indent. // Indent.
@@ -675,24 +604,20 @@ std::string DOM::Node::dumpNode (
out << "\033[31m" << node->_name << "\033[0m"; out << "\033[31m" << node->_name << "\033[0m";
if (node->_provider) if (node->_provider) out << " 0x" << std::hex << (long long)(void*)node->_provider;
out << " 0x" << std::hex << (long long) (void*) node->_provider;
out << '\n'; out << '\n';
// Recurse for branches. // Recurse for branches.
for (auto& b : node->_branches) for (auto& b : node->_branches) out << dumpNode(b, depth + 1);
out << dumpNode (b, depth + 1);
return out.str(); return out.str();
} }
//////////////////////////////////////////////////////////////////////////////// ////////////////////////////////////////////////////////////////////////////////
std::string DOM::Node::dump () const std::string DOM::Node::dump() const {
{
std::stringstream out; std::stringstream out;
out << "DOM::Node (" << count () << " nodes)\n" out << "DOM::Node (" << count() << " nodes)\n" << dumpNode(this, 1);
<< dumpNode (this, 1);
return out.str(); return out.str();
} }

View File

@@ -27,16 +27,16 @@
#ifndef INCLUDED_DOM #ifndef INCLUDED_DOM
#define INCLUDED_DOM #define INCLUDED_DOM
#include <string>
#include <Variant.h>
#include <Task.h> #include <Task.h>
#include <Variant.h>
#include <string>
// 2017-04-22 Deprecated, use DOM::get. // 2017-04-22 Deprecated, use DOM::get.
bool getDOM(const std::string&, Variant&); bool getDOM(const std::string&, Variant&);
bool getDOM(const std::string&, const Task*, Variant&); bool getDOM(const std::string&, const Task*, Variant&);
class DOM class DOM {
{
public: public:
~DOM(); ~DOM();
void addSource(const std::string&, bool (*)(const std::string&, Variant&)); void addSource(const std::string&, bool (*)(const std::string&, Variant&));
@@ -51,8 +51,7 @@ public:
std::string dump() const; std::string dump() const;
private: private:
class Node class Node {
{
public: public:
~Node(); ~Node();
void addSource(const std::string&, bool (*)(const std::string&, Variant&)); void addSource(const std::string&, bool (*)(const std::string&, Variant&));

View File

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

View File

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

View File

@@ -25,20 +25,22 @@
//////////////////////////////////////////////////////////////////////////////// ////////////////////////////////////////////////////////////////////////////////
#include <cmake.h> #include <cmake.h>
#include <Filter.h> // cmake.h include header must come first
#include <algorithm>
#include <Context.h> #include <Context.h>
#include <Timer.h>
#include <DOM.h> #include <DOM.h>
#include <Eval.h> #include <Eval.h>
#include <Filter.h>
#include <Timer.h>
#include <Variant.h> #include <Variant.h>
#include <format.h> #include <format.h>
#include <shared.h> #include <shared.h>
#include <algorithm>
//////////////////////////////////////////////////////////////////////////////// ////////////////////////////////////////////////////////////////////////////////
// Take an input set of tasks and filter into a subset. // 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; Timer timer;
_startCount = (int)input.size(); _startCount = (int)input.size();
@@ -46,11 +48,9 @@ void Filter::subset (const std::vector <Task>& input, std::vector <Task>& output
std::vector<std::pair<std::string, Lexer::Type>> precompiled; std::vector<std::pair<std::string, Lexer::Type>> precompiled;
for (auto& a : Context::getContext().cli2._args) for (auto& a : Context::getContext().cli2._args)
if (a.hasTag ("FILTER")) if (a.hasTag("FILTER")) precompiled.emplace_back(a.getToken(), a._lextype);
precompiled.emplace_back (a.getToken (), a._lextype);
if (precompiled.size ()) if (precompiled.size()) {
{
Eval eval; Eval eval;
eval.addSource(domSource); eval.addSource(domSource);
@@ -59,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.debug(Context::getContext().config.getInteger("debug.parser") >= 3 ? true : false);
eval.compileExpression(precompiled); eval.compileExpression(precompiled);
for (auto& task : input) for (auto& task : input) {
{
// Set up context for any DOM references. // Set up context for any DOM references.
auto currentTask = Context::getContext().withCurrentTask(&task); auto currentTask = Context::getContext().withCurrentTask(&task);
Variant var; Variant var;
eval.evaluateCompiledExpression(var); eval.evaluateCompiledExpression(var);
if (var.get_bool ()) if (var.get_bool()) output.push_back(task);
output.push_back (task);
} }
eval.debug(false); eval.debug(false);
} } else
else
output = input; output = input;
_endCount = (int)output.size(); _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(); Context::getContext().time_filter_us += timer.total_us();
} }
//////////////////////////////////////////////////////////////////////////////// ////////////////////////////////////////////////////////////////////////////////
// Take the set of all tasks and filter into a subset. // 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; Timer timer;
Context::getContext().cli2.prepareFilter(); Context::getContext().cli2.prepareFilter();
std::vector<std::pair<std::string, Lexer::Type>> precompiled; std::vector<std::pair<std::string, Lexer::Type>> precompiled;
for (auto& a : Context::getContext().cli2._args) for (auto& a : Context::getContext().cli2._args)
if (a.hasTag ("FILTER")) if (a.hasTag("FILTER")) precompiled.emplace_back(a.getToken(), a._lextype);
precompiled.emplace_back (a.getToken (), a._lextype);
// Shortcut indicates that only pending.data needs to be loaded. // Shortcut indicates that only pending.data needs to be loaded.
bool shortcut = false; bool shortcut = false;
if (precompiled.size ()) if (precompiled.size()) {
{
Timer timer_pending; Timer timer_pending;
auto pending = Context::getContext().tdb2.pending_tasks(); auto pending = Context::getContext().tdb2.pending_tasks();
Context::getContext().time_filter_us -= timer_pending.total_us(); Context::getContext().time_filter_us -= timer_pending.total_us();
@@ -111,41 +106,34 @@ void Filter::subset (std::vector <Task>& output)
eval.compileExpression(precompiled); eval.compileExpression(precompiled);
output.clear(); output.clear();
for (auto& task : pending) for (auto& task : pending) {
{
// Set up context for any DOM references. // Set up context for any DOM references.
auto currentTask = Context::getContext().withCurrentTask(&task); auto currentTask = Context::getContext().withCurrentTask(&task);
Variant var; Variant var;
eval.evaluateCompiledExpression(var); eval.evaluateCompiledExpression(var);
if (var.get_bool ()) if (var.get_bool()) output.push_back(task);
output.push_back (task);
} }
shortcut = pendingOnly(); shortcut = pendingOnly();
if (! shortcut) if (!shortcut) {
{
Timer timer_completed; Timer timer_completed;
auto completed = Context::getContext().tdb2.completed_tasks(); auto completed = Context::getContext().tdb2.completed_tasks();
Context::getContext().time_filter_us -= timer_completed.total_us(); Context::getContext().time_filter_us -= timer_completed.total_us();
_startCount += (int)completed.size(); _startCount += (int)completed.size();
for (auto& task : completed) for (auto& task : completed) {
{
// Set up context for any DOM references. // Set up context for any DOM references.
auto currentTask = Context::getContext().withCurrentTask(&task); auto currentTask = Context::getContext().withCurrentTask(&task);
Variant var; Variant var;
eval.evaluateCompiledExpression(var); eval.evaluateCompiledExpression(var);
if (var.get_bool ()) if (var.get_bool()) output.push_back(task);
output.push_back (task);
} }
} }
eval.debug(false); eval.debug(false);
} } else {
else
{
safety(); safety();
Timer pending_completed; Timer pending_completed;
@@ -154,16 +142,15 @@ void Filter::subset (std::vector <Task>& output)
} }
_endCount = (int)output.size(); _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(); Context::getContext().time_filter_us += timer.total_us();
} }
//////////////////////////////////////////////////////////////////////////////// ////////////////////////////////////////////////////////////////////////////////
bool Filter::hasFilter () const bool Filter::hasFilter() const {
{
for (const auto& a : Context::getContext().cli2._args) for (const auto& a : Context::getContext().cli2._args)
if (a.hasTag ("FILTER")) if (a.hasTag("FILTER")) return true;
return true;
return false; return false;
} }
@@ -172,11 +159,9 @@ bool Filter::hasFilter () const
// If the filter contains no 'or', 'xor' or 'not' operators, and only includes // If the filter contains no 'or', 'xor' or 'not' operators, and only includes
// status values 'pending', 'waiting' or 'recurring', then the filter is // status values 'pending', 'waiting' or 'recurring', then the filter is
// guaranteed to only need data from pending.data. // guaranteed to only need data from pending.data.
bool Filter::pendingOnly () const bool Filter::pendingOnly() const {
{
// When GC is off, there are no shortcuts. // When GC is off, there are no shortcuts.
if (! Context::getContext ().config.getBoolean ("gc")) if (!Context::getContext().config.getBoolean("gc")) return false;
return false;
// To skip loading completed.data, there should be: // To skip loading completed.data, there should be:
// - 'status' in filter // - 'status' in filter
@@ -196,10 +181,8 @@ bool Filter::pendingOnly () const
bool pendingTag = false; bool pendingTag = false;
bool activeTag = false; bool activeTag = false;
for (const auto& a : Context::getContext ().cli2._args) for (const auto& a : Context::getContext().cli2._args) {
{ if (a.hasTag("FILTER")) {
if (a.hasTag ("FILTER"))
{
std::string raw = a.attribute("raw"); std::string raw = a.attribute("raw");
std::string canonical = a.attribute("canonical"); std::string canonical = a.attribute("canonical");
@@ -213,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") == "+PENDING") pendingTag = true;
if (word.attribute("raw") == "+ACTIVE") activeTag = true; if (word.attribute("raw") == "+ACTIVE") activeTag = true;
} }
if (countUUID) return false;
if (countUUID) if (countOr || countXor || countNot) return false;
return false;
if (countOr || countXor || countNot) if (pendingTag || activeTag) return true;
return false;
if (pendingTag || activeTag) if (countStatus) {
return true; if (!countPending && !countWaiting && !countRecurring) return false;
if (countStatus)
{
if (!countPending && !countWaiting && !countRecurring)
return false;
return true; return true;
} }
if (countId) if (countId) return true;
return true;
return false; return false;
} }
@@ -246,31 +221,26 @@ bool Filter::pendingOnly () const
//////////////////////////////////////////////////////////////////////////////// ////////////////////////////////////////////////////////////////////////////////
// Disaster avoidance mechanism. If a !READONLY has no filter, then it can cause // Disaster avoidance mechanism. If a !READONLY has no filter, then it can cause
// all tasks to be modified. This is usually not intended. // all tasks to be modified. This is usually not intended.
void Filter::safety () const void Filter::safety() const {
{ if (_safety) {
if (_safety)
{
bool readonly = true; bool readonly = true;
bool filter = false; bool filter = false;
for (const auto& a : Context::getContext ().cli2._args) for (const auto& a : Context::getContext().cli2._args) {
{ if (a.hasTag("CMD") && !a.hasTag("READONLY")) readonly = false;
if (a.hasTag ("CMD") &&
! a.hasTag ("READONLY"))
readonly = false;
if (a.hasTag ("FILTER")) if (a.hasTag("FILTER")) filter = true;
filter = true;
} }
if (! readonly && if (!readonly && !filter) {
! filter)
{
if (!Context::getContext().config.getBoolean("allow.empty.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 user is willing to be asked, this can be avoided.
if (Context::getContext().config.getBoolean("confirmation") && 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; return;
// Sound the alarm. // Sound the alarm.
@@ -280,9 +250,6 @@ void Filter::safety () const
} }
//////////////////////////////////////////////////////////////////////////////// ////////////////////////////////////////////////////////////////////////////////
void Filter::disableSafety () void Filter::disableSafety() { _safety = false; }
{
_safety = false;
}
//////////////////////////////////////////////////////////////////////////////// ////////////////////////////////////////////////////////////////////////////////

View File

@@ -27,13 +27,13 @@
#ifndef INCLUDED_FILTER #ifndef INCLUDED_FILTER
#define INCLUDED_FILTER #define INCLUDED_FILTER
#include <string>
#include <vector>
#include <Task.h> #include <Task.h>
#include <Variant.h> #include <Variant.h>
class Filter #include <string>
{ #include <vector>
class Filter {
public: public:
Filter() = default; Filter() = default;
@@ -51,4 +51,3 @@ private:
}; };
#endif #endif

View File

@@ -25,91 +25,86 @@
//////////////////////////////////////////////////////////////////////////////// ////////////////////////////////////////////////////////////////////////////////
#include <cmake.h> #include <cmake.h>
// cmake.h include header must come first
#include <Hooks.h> #include <Hooks.h>
#include <algorithm> #include <algorithm>
// If <iostream> is included, put it after <stdio.h>, because it includes // If <iostream> is included, put it after <stdio.h>, because it includes
// <stdio.h>, and therefore would ignore the _WITH_GETLINE. // <stdio.h>, and therefore would ignore the _WITH_GETLINE.
#ifdef FREEBSD #ifdef FREEBSD
#define _WITH_GETLINE #define _WITH_GETLINE
#endif #endif
#include <stdio.h>
#include <unistd.h>
#include <sys/wait.h>
#include <sys/types.h>
#include <Context.h> #include <Context.h>
#include <Variant.h>
#include <DOM.h> #include <DOM.h>
#include <Lexer.h>
#include <JSON.h>
#include <Timer.h>
#include <FS.h> #include <FS.h>
#include <JSON.h>
#include <Lexer.h>
#include <Timer.h>
#include <Variant.h>
#include <format.h> #include <format.h>
#include <shared.h> #include <shared.h>
#include <stdio.h>
#include <sys/types.h>
#include <sys/wait.h>
#include <unistd.h>
#include <util.h> #include <util.h>
#define STRING_HOOK_ERROR_OBJECT "Hook Error: JSON Object '{...}' expected from hook script: {1}" #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_NODESC \
#define STRING_HOOK_ERROR_NOUUID "Hook Error: JSON Object missing 'uuid' attribute from hook script: {1}" "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_SYNTAX "Hook Error: JSON syntax error in: {1}"
#define STRING_HOOK_ERROR_JSON "Hook Error: JSON " #define STRING_HOOK_ERROR_JSON "Hook Error: JSON "
#define STRING_HOOK_ERROR_NOPARSE "Hook Error: JSON failed to parse: " #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_BAD_NUM \
#define STRING_HOOK_ERROR_SAME1 "Hook Error: JSON must be for the same task: {1}, in hook script: {2}" "Hook Error: Expected {1} JSON task(s), found {2}, in hook script: {3}"
#define STRING_HOOK_ERROR_SAME2 "Hook Error: JSON must be for the same task: {1} != {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}" #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"); _debug = Context::getContext().config.getInteger("debug.hooks");
// Scan <rc.hooks.location> // Scan <rc.hooks.location>
// <rc.data.location>/hooks // <rc.data.location>/hooks
Directory d; Directory d;
if (Context::getContext ().config.has ("hooks.location")) if (Context::getContext().config.has("hooks.location")) {
{
d = Directory(Context::getContext().config.get("hooks.location")); d = Directory(Context::getContext().config.get("hooks.location"));
} } else {
else
{
d = Directory(Context::getContext().config.get("data.location")); d = Directory(Context::getContext().config.get("data.location"));
d += "hooks"; d += "hooks";
} }
if (d.is_directory () && if (d.is_directory() && d.readable()) {
d.readable ())
{
_scripts = d.list(); _scripts = d.list();
std::sort(_scripts.begin(), _scripts.end()); std::sort(_scripts.begin(), _scripts.end());
if (_debug >= 1) if (_debug >= 1) {
{ for (auto& i : _scripts) {
for (auto& i : _scripts)
{
Path p(i); Path p(i);
if (! p.is_directory ()) if (!p.is_directory()) {
{
std::string name = p.name(); std::string name = p.name();
if (name.substr (0, 6) == "on-add" || if (name.substr(0, 6) == "on-add" || name.substr(0, 9) == "on-modify" ||
name.substr (0, 9) == "on-modify" || name.substr(0, 9) == "on-launch" || name.substr(0, 7) == "on-exit")
name.substr (0, 9) == "on-launch" ||
name.substr (0, 7) == "on-exit")
Context::getContext().debug("Found hook script " + i); Context::getContext().debug("Found hook script " + i);
else else
Context::getContext().debug("Found misnamed hook script " + i); 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); Context::getContext().debug("Hook directory not readable: " + d._data);
_enabled = Context::getContext().config.getBoolean("hooks"); _enabled = Context::getContext().config.getBoolean("hooks");
} }
//////////////////////////////////////////////////////////////////////////////// ////////////////////////////////////////////////////////////////////////////////
bool Hooks::enable (bool value) bool Hooks::enable(bool value) {
{
bool old_value = _enabled; bool old_value = _enabled;
_enabled = value; _enabled = value;
return old_value; return old_value;
@@ -127,18 +122,14 @@ bool Hooks::enable (bool value)
// - all emitted non-JSON lines are considered feedback or error messages // - all emitted non-JSON lines are considered feedback or error messages
// depending on the status code. // depending on the status code.
// //
void Hooks::onLaunch () const void Hooks::onLaunch() const {
{ if (!_enabled) return;
if (! _enabled)
return;
Timer timer; Timer timer;
std::vector<std::string> matchingScripts = scripts("on-launch"); std::vector<std::string> matchingScripts = scripts("on-launch");
if (matchingScripts.size ()) if (matchingScripts.size()) {
{ for (auto& script : matchingScripts) {
for (auto& script : matchingScripts)
{
std::vector<std::string> input; std::vector<std::string> input;
std::vector<std::string> output; std::vector<std::string> output;
int status = callHookScript(script, input, output); int status = callHookScript(script, input, output);
@@ -149,16 +140,11 @@ void Hooks::onLaunch () const
assertNTasks(outputJSON, 0, script); assertNTasks(outputJSON, 0, script);
if (status == 0) if (status == 0) {
{ for (auto& message : outputFeedback) Context::getContext().footnote(message);
for (auto& message : outputFeedback) } else {
Context::getContext ().footnote (message);
}
else
{
assertFeedback(outputFeedback, script); assertFeedback(outputFeedback, script);
for (auto& message : outputFeedback) for (auto& message : outputFeedback) Context::getContext().error(message);
Context::getContext ().error (message);
throw 0; // This is how hooks silently terminate processing. throw 0; // This is how hooks silently terminate processing.
} }
@@ -180,16 +166,13 @@ void Hooks::onLaunch () const
// - all emitted non-JSON lines are considered feedback or error messages // - all emitted non-JSON lines are considered feedback or error messages
// depending on the status code. // depending on the status code.
// //
void Hooks::onExit () const void Hooks::onExit() const {
{ if (!_enabled) return;
if (! _enabled)
return;
Timer timer; Timer timer;
std::vector<std::string> matchingScripts = scripts("on-exit"); std::vector<std::string> matchingScripts = scripts("on-exit");
if (matchingScripts.size ()) if (matchingScripts.size()) {
{
// Get the set of changed tasks. // Get the set of changed tasks.
std::vector<Task> tasks; std::vector<Task> tasks;
Context::getContext().tdb2.get_changes(tasks); Context::getContext().tdb2.get_changes(tasks);
@@ -197,12 +180,10 @@ void Hooks::onExit () const
// Convert to a vector of strings. // Convert to a vector of strings.
std::vector<std::string> input; std::vector<std::string> input;
input.reserve(tasks.size()); input.reserve(tasks.size());
for (auto& t : tasks) for (auto& t : tasks) input.push_back(t.composeJSON());
input.push_back (t.composeJSON ());
// Call the hook scripts, with the invariant input. // Call the hook scripts, with the invariant input.
for (auto& script : matchingScripts) for (auto& script : matchingScripts) {
{
std::vector<std::string> output; std::vector<std::string> output;
int status = callHookScript(script, input, output); int status = callHookScript(script, input, output);
@@ -212,16 +193,11 @@ void Hooks::onExit () const
assertNTasks(outputJSON, 0, script); assertNTasks(outputJSON, 0, script);
if (status == 0) if (status == 0) {
{ for (auto& message : outputFeedback) Context::getContext().footnote(message);
for (auto& message : outputFeedback) } else {
Context::getContext ().footnote (message);
}
else
{
assertFeedback(outputFeedback, script); assertFeedback(outputFeedback, script);
for (auto& message : outputFeedback) for (auto& message : outputFeedback) Context::getContext().error(message);
Context::getContext ().error (message);
throw 0; // This is how hooks silently terminate processing. throw 0; // This is how hooks silently terminate processing.
} }
@@ -243,23 +219,19 @@ void Hooks::onExit () const
// - all emitted non-JSON lines are considered feedback or error messages // - all emitted non-JSON lines are considered feedback or error messages
// depending on the status code. // depending on the status code.
// //
void Hooks::onAdd (Task& task) const void Hooks::onAdd(Task& task) const {
{ if (!_enabled) return;
if (! _enabled)
return;
Timer timer; Timer timer;
std::vector<std::string> matchingScripts = scripts("on-add"); std::vector<std::string> matchingScripts = scripts("on-add");
if (matchingScripts.size ()) if (matchingScripts.size()) {
{
// Convert task to a vector of strings. // Convert task to a vector of strings.
std::vector<std::string> input; std::vector<std::string> input;
input.push_back(task.composeJSON()); input.push_back(task.composeJSON());
// Call the hook scripts. // Call the hook scripts.
for (auto& script : matchingScripts) for (auto& script : matchingScripts) {
{
std::vector<std::string> output; std::vector<std::string> output;
int status = callHookScript(script, input, output); int status = callHookScript(script, input, output);
@@ -267,8 +239,7 @@ void Hooks::onAdd (Task& task) const
std::vector<std::string> outputFeedback; std::vector<std::string> outputFeedback;
separateOutput(output, outputJSON, outputFeedback); separateOutput(output, outputJSON, outputFeedback);
if (status == 0) if (status == 0) {
{
assertNTasks(outputJSON, 1, script); assertNTasks(outputJSON, 1, script);
assertValidJSON(outputJSON, script); assertValidJSON(outputJSON, script);
assertSameTask(outputJSON, task, script); assertSameTask(outputJSON, task, script);
@@ -276,14 +247,10 @@ void Hooks::onAdd (Task& task) const
// Propagate forward to the next script. // Propagate forward to the next script.
input[0] = outputJSON[0]; input[0] = outputJSON[0];
for (auto& message : outputFeedback) for (auto& message : outputFeedback) Context::getContext().footnote(message);
Context::getContext ().footnote (message); } else {
}
else
{
assertFeedback(outputFeedback, script); assertFeedback(outputFeedback, script);
for (auto& message : outputFeedback) for (auto& message : outputFeedback) Context::getContext().error(message);
Context::getContext ().error (message);
throw 0; // This is how hooks silently terminate processing. throw 0; // This is how hooks silently terminate processing.
} }
@@ -309,24 +276,20 @@ void Hooks::onAdd (Task& task) const
// - all emitted non-JSON lines are considered feedback or error messages // - all emitted non-JSON lines are considered feedback or error messages
// depending on the status code. // depending on the status code.
// //
void Hooks::onModify (const Task& before, Task& after) const void Hooks::onModify(const Task& before, Task& after) const {
{ if (!_enabled) return;
if (! _enabled)
return;
Timer timer; Timer timer;
std::vector<std::string> matchingScripts = scripts("on-modify"); std::vector<std::string> matchingScripts = scripts("on-modify");
if (matchingScripts.size ()) if (matchingScripts.size()) {
{
// Convert vector of tasks to a vector of strings. // Convert vector of tasks to a vector of strings.
std::vector<std::string> input; std::vector<std::string> input;
input.push_back(before.composeJSON()); // [line 0] original, never changes input.push_back(before.composeJSON()); // [line 0] original, never changes
input.push_back(after.composeJSON()); // [line 1] modified input.push_back(after.composeJSON()); // [line 1] modified
// Call the hook scripts. // Call the hook scripts.
for (auto& script : matchingScripts) for (auto& script : matchingScripts) {
{
std::vector<std::string> output; std::vector<std::string> output;
int status = callHookScript(script, input, output); int status = callHookScript(script, input, output);
@@ -334,8 +297,7 @@ void Hooks::onModify (const Task& before, Task& after) const
std::vector<std::string> outputFeedback; std::vector<std::string> outputFeedback;
separateOutput(output, outputJSON, outputFeedback); separateOutput(output, outputJSON, outputFeedback);
if (status == 0) if (status == 0) {
{
assertNTasks(outputJSON, 1, script); assertNTasks(outputJSON, 1, script);
assertValidJSON(outputJSON, script); assertValidJSON(outputJSON, script);
assertSameTask(outputJSON, before, script); assertSameTask(outputJSON, before, script);
@@ -343,14 +305,10 @@ void Hooks::onModify (const Task& before, Task& after) const
// Propagate accepted changes forward to the next script. // Propagate accepted changes forward to the next script.
input[1] = outputJSON[0]; input[1] = outputJSON[0];
for (auto& message : outputFeedback) for (auto& message : outputFeedback) Context::getContext().footnote(message);
Context::getContext ().footnote (message); } else {
}
else
{
assertFeedback(outputFeedback, script); assertFeedback(outputFeedback, script);
for (auto& message : outputFeedback) for (auto& message : outputFeedback) Context::getContext().error(message);
Context::getContext ().error (message);
throw 0; // This is how hooks silently terminate processing. throw 0; // This is how hooks silently terminate processing.
} }
@@ -363,22 +321,15 @@ void Hooks::onModify (const Task& before, Task& after) const
} }
//////////////////////////////////////////////////////////////////////////////// ////////////////////////////////////////////////////////////////////////////////
std::vector <std::string> Hooks::list () const std::vector<std::string> Hooks::list() const { return _scripts; }
{
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; std::vector<std::string> matching;
for (const auto& i : _scripts) for (const auto& i : _scripts) {
{ if (i.find("/" + event) != std::string::npos) {
if (i.find ("/" + event) != std::string::npos)
{
File script(i); File script(i);
if (script.executable ()) if (script.executable()) matching.push_back(i);
matching.push_back (i);
} }
} }
@@ -386,13 +337,9 @@ std::vector <std::string> Hooks::scripts (const std::string& event) const
} }
//////////////////////////////////////////////////////////////////////////////// ////////////////////////////////////////////////////////////////////////////////
void Hooks::separateOutput ( void Hooks::separateOutput(const std::vector<std::string>& output, std::vector<std::string>& json,
const std::vector <std::string>& output, std::vector<std::string>& feedback) const {
std::vector <std::string>& json, for (auto& i : output) {
std::vector <std::string>& feedback) const
{
for (auto& i : output)
{
if (isJSON(i)) if (isJSON(i))
json.push_back(i); json.push_back(i);
else else
@@ -401,45 +348,32 @@ void Hooks::separateOutput (
} }
//////////////////////////////////////////////////////////////////////////////// ////////////////////////////////////////////////////////////////////////////////
bool Hooks::isJSON (const std::string& input) const bool Hooks::isJSON(const std::string& input) const {
{ return input.length() > 2 && input[0] == '{' && input[input.length() - 1] == '}';
return input.length () > 2 &&
input[0] == '{' &&
input[input.length () - 1] == '}';
} }
//////////////////////////////////////////////////////////////////////////////// ////////////////////////////////////////////////////////////////////////////////
void Hooks::assertValidJSON ( void Hooks::assertValidJSON(const std::vector<std::string>& input,
const std::vector <std::string>& input, const std::string& script) const {
const std::string& script) const for (auto& i : input) {
{ if (i.length() < 3 || i[0] != '{' || i[i.length() - 1] != '}') {
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())); Context::getContext().error(format(STRING_HOOK_ERROR_OBJECT, Path(script).name()));
throw 0; throw 0;
} }
try try {
{
json::value* root = json::parse(i); 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())); Context::getContext().error(format(STRING_HOOK_ERROR_OBJECT, Path(script).name()));
throw 0; 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())); Context::getContext().error(format(STRING_HOOK_ERROR_NODESC, Path(script).name()));
throw 0; 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())); Context::getContext().error(format(STRING_HOOK_ERROR_NOUUID, Path(script).name()));
throw 0; throw 0;
} }
@@ -447,16 +381,13 @@ void Hooks::assertValidJSON (
delete root; delete root;
} }
catch (const std::string& e) catch (const std::string& e) {
{
Context::getContext().error(format(STRING_HOOK_ERROR_SYNTAX, i)); Context::getContext().error(format(STRING_HOOK_ERROR_SYNTAX, i));
if (_debug) if (_debug) Context::getContext().error(STRING_HOOK_ERROR_JSON + e);
Context::getContext ().error (STRING_HOOK_ERROR_JSON + e);
throw 0; throw 0;
} }
catch (...) catch (...) {
{
Context::getContext().error(STRING_HOOK_ERROR_NOPARSE + i); Context::getContext().error(STRING_HOOK_ERROR_NOPARSE + i);
throw 0; throw 0;
} }
@@ -464,35 +395,26 @@ void Hooks::assertValidJSON (
} }
//////////////////////////////////////////////////////////////////////////////// ////////////////////////////////////////////////////////////////////////////////
void Hooks::assertNTasks ( void Hooks::assertNTasks(const std::vector<std::string>& input, unsigned int n,
const std::vector <std::string>& input, const std::string& script) const {
unsigned int n, if (input.size() != n) {
const std::string& script) const Context::getContext().error(
{ format(STRING_HOOK_ERROR_BAD_NUM, n, (int)input.size(), Path(script).name()));
if (input.size () != n)
{
Context::getContext ().error (format (STRING_HOOK_ERROR_BAD_NUM, n, (int) input.size (), Path (script).name ()));
throw 0; throw 0;
} }
} }
//////////////////////////////////////////////////////////////////////////////// ////////////////////////////////////////////////////////////////////////////////
void Hooks::assertSameTask ( void Hooks::assertSameTask(const std::vector<std::string>& input, const Task& task,
const std::vector <std::string>& input, const std::string& script) const {
const Task& task,
const std::string& script) const
{
std::string uuid = task.get("uuid"); std::string uuid = task.get("uuid");
for (auto& i : input) for (auto& i : input) {
{
auto root_obj = (json::object*)json::parse(i); auto root_obj = (json::object*)json::parse(i);
// If there is no UUID at all. // If there is no UUID at all.
auto u = root_obj->_data.find("uuid"); auto u = root_obj->_data.find("uuid");
if (u == root_obj->_data.end () || if (u == root_obj->_data.end() || u->second->type() != json::j_string) {
u->second->type () != json::j_string)
{
Context::getContext().error(format(STRING_HOOK_ERROR_SAME1, uuid, Path(script).name())); Context::getContext().error(format(STRING_HOOK_ERROR_SAME1, uuid, Path(script).name()));
throw 0; throw 0;
} }
@@ -501,9 +423,9 @@ void Hooks::assertSameTask (
auto text = up->dump(); auto text = up->dump();
Lexer::dequote(text); Lexer::dequote(text);
std::string json_uuid = json::decode(text); std::string json_uuid = json::decode(text);
if (json_uuid != uuid) if (json_uuid != uuid) {
{ Context::getContext().error(
Context::getContext ().error (format (STRING_HOOK_ERROR_SAME2, uuid, json_uuid, Path (script).name ())); format(STRING_HOOK_ERROR_SAME2, uuid, json_uuid, Path(script).name()));
throw 0; throw 0;
} }
@@ -512,25 +434,19 @@ void Hooks::assertSameTask (
} }
//////////////////////////////////////////////////////////////////////////////// ////////////////////////////////////////////////////////////////////////////////
void Hooks::assertFeedback ( void Hooks::assertFeedback(const std::vector<std::string>& input, const std::string& script) const {
const std::vector <std::string>& input,
const std::string& script) const
{
bool foundSomething = false; bool foundSomething = false;
for (auto& i : input) for (auto& i : input)
if (nontrivial (i)) if (nontrivial(i)) foundSomething = true;
foundSomething = true;
if (! foundSomething) if (!foundSomething) {
{
Context::getContext().error(format(STRING_HOOK_ERROR_NOFEEDBACK, Path(script).name())); Context::getContext().error(format(STRING_HOOK_ERROR_NOFEEDBACK, Path(script).name()));
throw 0; 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; Variant v;
// Hooks API version. // Hooks API version.
@@ -556,54 +472,41 @@ std::vector <std::string>& Hooks::buildHookScriptArgs (std::vector <std::string>
} }
//////////////////////////////////////////////////////////////////////////////// ////////////////////////////////////////////////////////////////////////////////
int Hooks::callHookScript ( int Hooks::callHookScript(const std::string& script, const std::vector<std::string>& input,
const std::string& script, std::vector<std::string>& output) const {
const std::vector <std::string>& input, if (_debug >= 1) Context::getContext().debug("Hook: Calling " + script);
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"); Context::getContext().debug("Hook: input");
for (const auto& i : input) for (const auto& i : input) Context::getContext().debug(" " + i);
Context::getContext ().debug (" " + i);
} }
std::string inputStr; std::string inputStr;
for (const auto& i : input) for (const auto& i : input) inputStr += i + "\n";
inputStr += i + "\n";
std::vector<std::string> args; std::vector<std::string> args;
buildHookScriptArgs(args); buildHookScriptArgs(args);
if (_debug >= 2) if (_debug >= 2) {
{
Context::getContext().debug("Hooks: args"); Context::getContext().debug("Hooks: args");
for (const auto& arg: args) for (const auto& arg : args) Context::getContext().debug(" " + arg);
Context::getContext ().debug (" " + arg);
} }
// Measure time for each hook if running in debug // Measure time for each hook if running in debug
int status; int status;
std::string outputStr; std::string outputStr;
if (_debug >= 2) if (_debug >= 2) {
{
Timer timer; Timer timer;
status = execute(script, args, inputStr, outputStr); status = execute(script, args, inputStr, outputStr);
Context::getContext().debugTiming(format("Hooks::execute ({1})", script), timer); Context::getContext().debugTiming(format("Hooks::execute ({1})", script), timer);
} } else
else
status = execute(script, args, inputStr, outputStr); status = execute(script, args, inputStr, outputStr);
output = split(outputStr, '\n'); output = split(outputStr, '\n');
if (_debug >= 2) if (_debug >= 2) {
{
Context::getContext().debug("Hook: output"); Context::getContext().debug("Hook: output");
for (const auto& i : output) for (const auto& i : output)
if (i != "") if (i != "") Context::getContext().debug(" " + i);
Context::getContext ().debug (" " + i);
Context::getContext().debug(format("Hook: Completed with status {1}", status)); Context::getContext().debug(format("Hook: Completed with status {1}", status));
Context::getContext().debug(" "); // Blank line Context::getContext().debug(" "); // Blank line

View File

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

File diff suppressed because it is too large Load Diff

View File

@@ -27,31 +27,41 @@
#ifndef INCLUDED_LEXER #ifndef INCLUDED_LEXER
#define INCLUDED_LEXER #define INCLUDED_LEXER
#include <string>
#include <map>
#include <vector>
#include <cstddef> #include <cstddef>
#include <map>
#include <string>
#include <vector>
// Lexer: A UTF8 lexical analyzer for every construct used on the Taskwarrior // Lexer: A UTF8 lexical analyzer for every construct used on the Taskwarrior
// command line, with additional recognized types for disambiguation. // command line, with additional recognized types for disambiguation.
class Lexer class Lexer {
{
public: public:
// These are overridable. // These are overridable.
static std::string dateFormat; static std::string dateFormat;
static std::string::size_type minimumMatchLength; static std::string::size_type minimumMatchLength;
static std::map<std::string, std::string> attributes; static std::map<std::string, std::string> attributes;
enum class Type { uuid, number, hex, enum class Type {
uuid,
number,
hex,
string, string,
url, pair, set, separator, url,
pair,
set,
separator,
tag, tag,
path, path,
substitution, pattern, substitution,
pattern,
op, op,
dom, identifier, word, dom,
date, duration }; identifier,
word,
date,
duration
};
Lexer(const std::string&); Lexer(const std::string&);
~Lexer() = default; ~Lexer() = default;
@@ -73,16 +83,19 @@ public:
static bool isDOM(const std::string&); static bool isDOM(const std::string&);
static void dequote(std::string&, const std::string& quotes = "'\""); static void dequote(std::string&, const std::string& quotes = "'\"");
static bool wasQuoted(const std::string&); 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 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 decomposeSubstitution(const std::string&, std::string&, std::string&, std::string&);
static bool decomposePattern(const std::string&, std::string&, std::string&); static bool decomposePattern(const std::string&, std::string&, std::string&);
static int hexToInt(int); static int hexToInt(int);
static int hexToInt(int, int); static int hexToInt(int, int);
static int hexToInt(int, int, 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&, 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 commify(const std::string&);
static std::string lowerCase(const std::string&); static std::string lowerCase(const std::string&);
static std::string ucFirst(const std::string&); static std::string ucFirst(const std::string&);

View File

@@ -25,23 +25,27 @@
//////////////////////////////////////////////////////////////////////////////// ////////////////////////////////////////////////////////////////////////////////
#include <cmake.h> #include <cmake.h>
#include <TDB2.h> // cmake.h include header must come first
#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 <Color.h>
#include <Context.h>
#include <Datetime.h> #include <Datetime.h>
#include <TDB2.h>
#include <Table.h> #include <Table.h>
#include <shared.h>
#include <format.h> #include <format.h>
#include <main.h> #include <main.h>
#include <shared.h>
#include <signal.h>
#include <stdlib.h>
#include <util.h> #include <util.h>
#include <algorithm>
#include <iostream>
#include <list>
#include <sstream>
#include <unordered_set>
#include <vector>
#include "tc/Server.h" #include "tc/Server.h"
#include "tc/util.h" #include "tc/util.h"
@@ -51,20 +55,17 @@ static void dependency_scan (std::vector<Task> &);
//////////////////////////////////////////////////////////////////////////////// ////////////////////////////////////////////////////////////////////////////////
TDB2::TDB2() TDB2::TDB2()
: replica{tc::Replica()} // in-memory Replica : 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); replica = tc::Replica(location, create_if_missing);
} }
//////////////////////////////////////////////////////////////////////////////// ////////////////////////////////////////////////////////////////////////////////
// Add the new task to the replica. // 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. // Ensure the task is consistent, and provide defaults if necessary.
// bool argument to validate() is "applyDefault", to apply default values for // bool argument to validate() is "applyDefault", to apply default values for
// properties not otherwise given. // properties not otherwise given.
@@ -119,7 +120,6 @@ void TDB2::add (Task& task)
if (id.has_value()) { if (id.has_value()) {
task.id = id.value(); task.id = id.value();
} }
} }
//////////////////////////////////////////////////////////////////////////////// ////////////////////////////////////////////////////////////////////////////////
@@ -137,8 +137,7 @@ void TDB2::add (Task& task)
// this method. In this case, this method throws an error that will make sense // 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 // to the user. This is especially unlikely since tasks are only deleted when
// they have been unmodified for a long time. // 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 // All locally modified tasks are timestamped, implicitly overwriting any
// changes the user or hooks tried to apply to the "modified" attribute. // changes the user or hooks tried to apply to the "modified" attribute.
task.setAsNow("modified"); task.setAsNow("modified");
@@ -195,15 +194,13 @@ void TDB2::modify (Task& task)
} }
//////////////////////////////////////////////////////////////////////////////// ////////////////////////////////////////////////////////////////////////////////
void TDB2::purge (Task& task) void TDB2::purge(Task& task) {
{
auto uuid = task.get("uuid"); auto uuid = task.get("uuid");
replica.delete_task(uuid); replica.delete_task(uuid);
} }
//////////////////////////////////////////////////////////////////////////////// ////////////////////////////////////////////////////////////////////////////////
const tc::WorkingSet &TDB2::working_set () const tc::WorkingSet& TDB2::working_set() {
{
if (!_working_set.has_value()) { if (!_working_set.has_value()) {
_working_set = std::make_optional(replica.working_set()); _working_set = std::make_optional(replica.working_set());
} }
@@ -211,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; std::map<std::string, Task>& changes_map = this->changes;
changes.clear(); changes.clear();
std::transform(changes_map.begin(), changes_map.end(), std::back_inserter(changes), std::transform(changes_map.begin(), changes_map.end(), std::back_inserter(changes),
@@ -220,8 +216,7 @@ void TDB2::get_changes (std::vector <Task>& changes)
} }
//////////////////////////////////////////////////////////////////////////////// ////////////////////////////////////////////////////////////////////////////////
void TDB2::revert () void TDB2::revert() {
{
auto undo_ops = replica.get_undo_ops(); auto undo_ops = replica.get_undo_ops();
if (undo_ops.len == 0) { if (undo_ops.len == 0) {
std::cout << "No operations to undo."; std::cout << "No operations to undo.";
@@ -237,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 // TODO Use show_diff rather than this basic listing of operations, though
// this might be a worthy undo.style itself. // this might be a worthy undo.style itself.
std::cout << "The following " << undo_ops.len << " operations would be reverted:\n"; std::cout << "The following " << undo_ops.len << " operations would be reverted:\n";
@@ -254,7 +248,9 @@ bool TDB2::confirm_revert (struct tc::ffi::TCReplicaOpList undo_ops)
break; break;
case tc::ffi::TCReplicaOpType::Update: case tc::ffi::TCReplicaOpType::Update:
std::cout << "Update " << replica.get_op_uuid(op) << "\n"; 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; break;
case tc::ffi::TCReplicaOpType::UndoPoint: case tc::ffi::TCReplicaOpType::UndoPoint:
throw std::string("Can't undo UndoPoint."); throw std::string("Can't undo UndoPoint.");
@@ -266,58 +262,49 @@ bool TDB2::confirm_revert (struct tc::ffi::TCReplicaOpList undo_ops)
std::cout << "\n"; std::cout << "\n";
} }
return !Context::getContext().config.getBoolean("confirmation") || 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) { std::string TDB2::option_string(std::string input) { return input == "" ? "<empty>" : input; }
return input == "" ? "<empty>" : input;
}
//////////////////////////////////////////////////////////////////////////////// ////////////////////////////////////////////////////////////////////////////////
void TDB2::show_diff ( void TDB2::show_diff(const std::string& current, const std::string& prior,
const std::string& current, const std::string& when) {
const std::string& prior,
const std::string& when)
{
Datetime lastChange(strtoll(when.c_str(), nullptr, 10)); Datetime lastChange(strtoll(when.c_str(), nullptr, 10));
// Set the colors. // Set the colors.
Color color_red (Context::getContext ().color () ? Context::getContext ().config.get ("color.undo.before") : ""); Color color_red(
Color color_green (Context::getContext ().color () ? Context::getContext ().config.get ("color.undo.after") : ""); 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 before = prior == "" ? Task() : Task(prior);
auto after = Task(current); 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); Table view = before.diffForUndoSide(after);
std::cout << '\n' std::cout << '\n'
<< format ("The last modification was made {1}", lastChange.toString ()) << format("The last modification was made {1}", lastChange.toString()) << '\n'
<< '\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); Table view = before.diffForUndoPatch(after, lastChange);
std::cout << '\n' std::cout << '\n' << view.render() << '\n';
<< view.render ()
<< '\n';
} }
} }
//////////////////////////////////////////////////////////////////////////////// ////////////////////////////////////////////////////////////////////////////////
void TDB2::gc () void TDB2::gc() {
{
Timer timer; Timer timer;
// Allowed as an override, but not recommended. // Allowed as an override, but not recommended.
if (Context::getContext ().config.getBoolean ("gc")) if (Context::getContext().config.getBoolean("gc")) {
{
replica.rebuild_working_set(true); replica.rebuild_working_set(true);
} }
@@ -325,33 +312,26 @@ void TDB2::gc ()
} }
//////////////////////////////////////////////////////////////////////////////// ////////////////////////////////////////////////////////////////////////////////
void TDB2::expire_tasks () void TDB2::expire_tasks() { replica.expire_tasks(); }
{
replica.expire_tasks ();
}
//////////////////////////////////////////////////////////////////////////////// ////////////////////////////////////////////////////////////////////////////////
// Latest ID is that of the last pending task. // Latest ID is that of the last pending task.
int TDB2::latest_id () int TDB2::latest_id() {
{
const tc::WorkingSet& ws = working_set(); const tc::WorkingSet& ws = working_set();
return (int)ws.largest_index(); 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(); auto all_tctasks = replica.all_tasks();
std::vector<Task> all; std::vector<Task> all;
for (auto& tctask : all_tctasks) for (auto& tctask : all_tctasks) all.push_back(Task(std::move(tctask)));
all.push_back (Task (std::move (tctask)));
return all; return all;
} }
//////////////////////////////////////////////////////////////////////////////// ////////////////////////////////////////////////////////////////////////////////
const std::vector <Task> TDB2::pending_tasks () const std::vector<Task> TDB2::pending_tasks() {
{
const tc::WorkingSet& ws = working_set(); const tc::WorkingSet& ws = working_set();
auto largest_index = ws.largest_index(); auto largest_index = ws.largest_index();
@@ -372,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(); auto all_tctasks = replica.all_tasks();
const tc::WorkingSet& ws = working_set(); const tc::WorkingSet& ws = working_set();
@@ -390,8 +369,7 @@ const std::vector <Task> TDB2::completed_tasks ()
//////////////////////////////////////////////////////////////////////////////// ////////////////////////////////////////////////////////////////////////////////
// Locate task by ID, wherever it is. // 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 tc::WorkingSet& ws = working_set();
const auto maybe_uuid = ws.by_index(id); const auto maybe_uuid = ws.by_index(id);
if (maybe_uuid) { if (maybe_uuid) {
@@ -407,8 +385,7 @@ bool TDB2::get (int id, Task& task)
//////////////////////////////////////////////////////////////////////////////// ////////////////////////////////////////////////////////////////////////////////
// Locate task by UUID, including by partial ID, wherever it is. // 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 // try by raw uuid, if the length is right
if (uuid.size() == 36) { if (uuid.size() == 36) {
try { try {
@@ -435,33 +412,24 @@ bool TDB2::get (const std::string& uuid, Task& task)
//////////////////////////////////////////////////////////////////////////////// ////////////////////////////////////////////////////////////////////////////////
// Locate task by UUID, wherever it is. // Locate task by UUID, wherever it is.
bool TDB2::has (const std::string& uuid) bool TDB2::has(const std::string& uuid) {
{
Task task; Task task;
return get(uuid, 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; std::vector<Task> results;
if (task.has ("parent")) if (task.has("parent")) {
{
std::string parent = task.get("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. // Do not include self in results.
if (i.id != task.id) if (i.id != task.id) {
{
// Do not include completed or deleted tasks. // Do not include completed or deleted tasks.
if (i.getStatus () != Task::completed && if (i.getStatus() != Task::completed && i.getStatus() != Task::deleted) {
i.getStatus () != Task::deleted)
{
// If task has the same parent, it is a sibling. // If task has the same parent, it is a sibling.
if (i.has ("parent") && if (i.has("parent") && i.get("parent") == parent) {
i.get ("parent") == parent)
{
results.push_back(i); results.push_back(i);
} }
} }
@@ -473,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 // scan _pending_ tasks for those with `parent` equal to this task
std::vector<Task> results; std::vector<Task> results;
std::string this_uuid = parent.get("uuid"); std::string this_uuid = parent.get("uuid");
@@ -515,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(); const tc::WorkingSet& ws = working_set();
return ws.by_index((size_t)id).value_or(""); 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(); const tc::WorkingSet& ws = working_set();
return (int)ws.by_uuid(uuid).value_or(0); return (int)ws.by_uuid(uuid).value_or(0);
} }
//////////////////////////////////////////////////////////////////////////////// ////////////////////////////////////////////////////////////////////////////////
int TDB2::num_local_changes () int TDB2::num_local_changes() { return (int)replica.num_local_operations(); }
{
return (int)replica.num_local_operations ();
}
//////////////////////////////////////////////////////////////////////////////// ////////////////////////////////////////////////////////////////////////////////
int TDB2::num_reverts_possible () int TDB2::num_reverts_possible() { return (int)replica.num_undo_points(); }
{
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); replica.sync(std::move(server), avoid_snapshots);
} }
//////////////////////////////////////////////////////////////////////////////// ////////////////////////////////////////////////////////////////////////////////
void TDB2::dump () void TDB2::dump() {
{
// TODO // TODO
} }
@@ -556,24 +513,16 @@ void TDB2::dump ()
// For any task that has depenencies, follow the chain of dependencies until the // 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 // end. Along the way, update the Task::is_blocked and Task::is_blocking data
// cache. // cache.
static void dependency_scan (std::vector<Task> &tasks) static void dependency_scan(std::vector<Task>& tasks) {
{ for (auto& left : tasks) {
for (auto& left : tasks) for (auto& dep : left.getDependencyUUIDs()) {
{ for (auto& right : tasks) {
for (auto& dep : left.getDependencyUUIDs ()) if (right.get("uuid") == dep) {
{
for (auto& right : tasks)
{
if (right.get ("uuid") == dep)
{
// GC hasn't run yet, check both tasks for their current status // GC hasn't run yet, check both tasks for their current status
Task::status lstatus = left.getStatus(); Task::status lstatus = left.getStatus();
Task::status rstatus = right.getStatus(); Task::status rstatus = right.getStatus();
if (lstatus != Task::completed && if (lstatus != Task::completed && lstatus != Task::deleted &&
lstatus != Task::deleted && rstatus != Task::completed && rstatus != Task::deleted) {
rstatus != Task::completed &&
rstatus != Task::deleted)
{
left.is_blocked = true; left.is_blocked = true;
right.is_blocking = true; right.is_blocking = true;
} }

View File

@@ -27,24 +27,24 @@
#ifndef INCLUDED_TDB2 #ifndef INCLUDED_TDB2
#define INCLUDED_TDB2 #define INCLUDED_TDB2
#include <map>
#include <unordered_set>
#include <unordered_map>
#include <vector>
#include <string>
#include <stdio.h>
#include <FS.h> #include <FS.h>
#include <Task.h> #include <Task.h>
#include <tc/WorkingSet.h> #include <stdio.h>
#include <tc/Replica.h> #include <tc/Replica.h>
#include <tc/WorkingSet.h>
#include <map>
#include <string>
#include <unordered_map>
#include <unordered_set>
#include <vector>
namespace tc { namespace tc {
class Server; class Server;
} }
// TDB2 Class represents all the files in the task database. // TDB2 Class represents all the files in the task database.
class TDB2 class TDB2 {
{
public: public:
static bool debug_mode; static bool debug_mode;

File diff suppressed because it is too large Load Diff

View File

@@ -27,18 +27,18 @@
#ifndef INCLUDED_TASK #ifndef INCLUDED_TASK
#define INCLUDED_TASK #define INCLUDED_TASK
#include <vector> #include <Datetime.h>
#include <map>
#include <string>
#include <stdio.h>
#include <time.h>
#include <JSON.h> #include <JSON.h>
#include <Table.h> #include <Table.h>
#include <Datetime.h> #include <stdio.h>
#include <tc/Task.h> #include <tc/Task.h>
#include <time.h>
class Task #include <map>
{ #include <string>
#include <vector>
class Task {
public: public:
static std::string defaultProject; static std::string defaultProject;
static std::string defaultDue; static std::string defaultDue;
@@ -177,11 +177,11 @@ public:
#endif #endif
std::string diff(const Task& after) const; std::string diff(const Task& after) const;
std::string diffForInfo (const Task& after, const std::string& dateformat, long& last_timestamp, const long current_timestamp) const; 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 diffForUndoSide(const Task& after) const;
Table diffForUndoPatch(const Task& after, const Datetime& lastChange) const; Table diffForUndoPatch(const Task& after, const Datetime& lastChange) const;
private: private:
int determineVersion(const std::string&); int determineVersion(const std::string&);
void parseJSON(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 #ifndef INCLUDED_VARIANT
#define INCLUDED_VARIANT #define INCLUDED_VARIANT
#include <Task.h>
#include <time.h>
#include <map> #include <map>
#include <string> #include <string>
#include <time.h>
#include <Task.h>
class Variant class Variant {
{
public: public:
static std::string dateFormat; static std::string dateFormat;
static bool searchCaseSensitive; static bool searchCaseSensitive;

View File

@@ -26,6 +26,8 @@
#include <Version.h> #include <Version.h>
#include <cmake.h> #include <cmake.h>
// cmake.h include header must come first
#include <iostream> #include <iostream>
#include <sstream> #include <sstream>
#include <string> #include <string>
@@ -65,38 +67,32 @@ bool Version::is_valid() const { return major >= 0; }
//////////////////////////////////////////////////////////////////////////////// ////////////////////////////////////////////////////////////////////////////////
bool Version::operator<(const Version &other) const { bool Version::operator<(const Version &other) const {
return std::tie(major, minor, patch) < return std::tie(major, minor, patch) < std::tie(other.major, other.minor, other.patch);
std::tie(other.major, other.minor, other.patch);
} }
//////////////////////////////////////////////////////////////////////////////// ////////////////////////////////////////////////////////////////////////////////
bool Version::operator<=(const Version &other) const { bool Version::operator<=(const Version &other) const {
return std::tie(major, minor, patch) <= return std::tie(major, minor, patch) <= std::tie(other.major, other.minor, other.patch);
std::tie(other.major, other.minor, other.patch);
} }
//////////////////////////////////////////////////////////////////////////////// ////////////////////////////////////////////////////////////////////////////////
bool Version::operator>(const Version &other) const { bool Version::operator>(const Version &other) const {
return std::tie(major, minor, patch) > return std::tie(major, minor, patch) > std::tie(other.major, other.minor, other.patch);
std::tie(other.major, other.minor, other.patch);
} }
//////////////////////////////////////////////////////////////////////////////// ////////////////////////////////////////////////////////////////////////////////
bool Version::operator>=(const Version &other) const { bool Version::operator>=(const Version &other) const {
return std::tie(major, minor, patch) >= return std::tie(major, minor, patch) >= std::tie(other.major, other.minor, other.patch);
std::tie(other.major, other.minor, other.patch);
} }
//////////////////////////////////////////////////////////////////////////////// ////////////////////////////////////////////////////////////////////////////////
bool Version::operator==(const Version &other) const { bool Version::operator==(const Version &other) const {
return std::tie(major, minor, patch) == return std::tie(major, minor, patch) == std::tie(other.major, other.minor, other.patch);
std::tie(other.major, other.minor, other.patch);
} }
//////////////////////////////////////////////////////////////////////////////// ////////////////////////////////////////////////////////////////////////////////
bool Version::operator!=(const Version &other) const { bool Version::operator!=(const Version &other) const {
return std::tie(major, minor, patch) != return std::tie(major, minor, patch) != std::tie(other.major, other.minor, other.patch);
std::tie(other.major, other.minor, other.patch);
} }
//////////////////////////////////////////////////////////////////////////////// ////////////////////////////////////////////////////////////////////////////////

View File

@@ -25,40 +25,39 @@
//////////////////////////////////////////////////////////////////////////////// ////////////////////////////////////////////////////////////////////////////////
#include <cmake.h> #include <cmake.h>
#include <ViewTask.h> // cmake.h include header must come first
#include <numeric>
#include <Context.h> #include <Context.h>
#include <ViewTask.h>
#include <format.h> #include <format.h>
#include <util.h>
#include <utf8.h>
#include <main.h> #include <main.h>
#include <utf8.h>
#include <util.h>
#include <numeric>
//////////////////////////////////////////////////////////////////////////////// ////////////////////////////////////////////////////////////////////////////////
ViewTask::ViewTask() ViewTask::ViewTask()
: _width (0) : _width(0),
, _left_margin (0) _left_margin(0),
, _header (0) _header(0),
, _sort_header (0) _sort_header(0),
, _odd (0) _odd(0),
, _even (0) _even(0),
, _intra_padding (1) _intra_padding(1),
, _intra_odd (0) _intra_odd(0),
, _intra_even (0) _intra_even(0),
, _extra_padding (0) _extra_padding(0),
, _extra_odd (0) _extra_odd(0),
, _extra_even (0) _extra_even(0),
, _truncate_lines (0) _truncate_lines(0),
, _truncate_rows (0) _truncate_rows(0),
, _lines (0) _lines(0),
, _rows (0) _rows(0) {}
{
}
//////////////////////////////////////////////////////////////////////////////// ////////////////////////////////////////////////////////////////////////////////
ViewTask::~ViewTask () ViewTask::~ViewTask() {
{ for (auto& col : _columns) delete col;
for (auto& col : _columns)
delete col;
_columns.clear(); _columns.clear();
} }
@@ -106,8 +105,7 @@ ViewTask::~ViewTask ()
// the larger fields. If the widest field is W0, and the second widest // 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. // 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; Timer timer;
bool const obfuscate = Context::getContext().config.getBoolean("obfuscate"); bool const obfuscate = Context::getContext().config.getBoolean("obfuscate");
@@ -119,19 +117,15 @@ std::string ViewTask::render (std::vector <Task>& data, std::vector <int>& seque
std::vector<int> minimal; std::vector<int> minimal;
std::vector<int> ideal; 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. // Headers factor in to width calculations.
unsigned int global_min = 0; unsigned int global_min = 0;
unsigned int global_ideal = global_min; unsigned int global_ideal = global_min;
for (unsigned int s = 0; s < sequence.size (); ++s) for (unsigned int s = 0; s < sequence.size(); ++s) {
{ if ((int)s >= _truncate_lines && _truncate_lines != 0) break;
if ((int)s >= _truncate_lines && _truncate_lines != 0)
break;
if ((int)s >= _truncate_rows && _truncate_rows != 0) if ((int)s >= _truncate_rows && _truncate_rows != 0) break;
break;
// Determine minimum and ideal width for this column. // Determine minimum and ideal width for this column.
unsigned int min = 0; unsigned int min = 0;
@@ -143,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 // If a fixed-width column was just measured, there is no point repeating
// the measurement for all tasks. // the measurement for all tasks.
if (_columns[i]->is_fixed_width ()) if (_columns[i]->is_fixed_width()) break;
break;
} }
if (print_empty_columns || global_min != 0) if (print_empty_columns || global_min != 0) {
{
unsigned int label_length = utf8_width(_columns[i]->label()); unsigned int label_length = utf8_width(_columns[i]->label());
if (label_length > global_min) global_min = label_length; if (label_length > global_min) global_min = label_length;
if (label_length > global_ideal) global_ideal = label_length; if (label_length > global_ideal) global_ideal = label_length;
@@ -156,14 +148,12 @@ std::string ViewTask::render (std::vector <Task>& data, std::vector <int>& seque
ideal.push_back(global_ideal); ideal.push_back(global_ideal);
} }
if (! print_empty_columns) if (!print_empty_columns) {
{
if (global_min != 0) // Column is nonempty if (global_min != 0) // Column is nonempty
{ {
nonempty_columns.push_back(_columns[i]); nonempty_columns.push_back(_columns[i]);
nonempty_sort.push_back(_sort[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 // Note: This is safe to do because we set _columns = nonempty_columns
// after iteration over _columns is finished. // after iteration over _columns is finished.
@@ -172,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; _columns = nonempty_columns;
_sort = nonempty_sort; _sort = nonempty_sort;
} }
int all_extra = _left_margin int all_extra = _left_margin + (2 * _extra_padding) + ((_columns.size() - 1) * _intra_padding);
+ (2 * _extra_padding)
+ ((_columns.size () - 1) * _intra_padding);
// Sum the widths. // Sum the widths.
int sum_minimal = std::accumulate(minimal.begin(), minimal.end(), 0); int sum_minimal = std::accumulate(minimal.begin(), minimal.end(), 0);
@@ -189,34 +176,26 @@ std::string ViewTask::render (std::vector <Task>& data, std::vector <int>& seque
// Calculate final column widths. // Calculate final column widths.
int overage = _width - sum_minimal - all_extra; int overage = _width - sum_minimal - all_extra;
Context::getContext().debug(format("ViewTask::render min={1} ideal={2} overage={3} width={4}", Context::getContext().debug(format("ViewTask::render min={1} ideal={2} overage={3} width={4}",
sum_minimal + all_extra, sum_minimal + all_extra, sum_ideal + all_extra, overage,
sum_ideal + all_extra,
overage,
_width)); _width));
std::vector<int> widths; std::vector<int> widths;
// Ideal case. Everything fits. // Ideal case. Everything fits.
if (_width == 0 || sum_ideal + all_extra <= _width) if (_width == 0 || sum_ideal + all_extra <= _width) {
{
widths = ideal; widths = ideal;
} }
// Not enough for minimum. Decrease certain columns. // Not enough for minimum. Decrease certain columns.
else if (overage < 0) else if (overage < 0) {
{
// Determine which columns are the longest. // Determine which columns are the longest.
unsigned int longest = 0; unsigned int longest = 0;
unsigned int second_longest = 0; unsigned int second_longest = 0;
for (unsigned int j = 0; j < minimal.size(); j++) for (unsigned int j = 0; j < minimal.size(); j++) {
{ if (minimal[j] > minimal[longest]) {
if (minimal[j] > minimal[longest])
{
second_longest = longest; second_longest = longest;
longest = j; longest = j;
} } else if (minimal[j] > minimal[second_longest]) {
else if (minimal[j] > minimal[second_longest])
{
second_longest = j; second_longest = j;
} }
} }
@@ -224,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 // Case 1: Shortening longest column still keeps it longest. Let it bear
// all the shortening. // all the shortening.
widths = minimal; widths = minimal;
if (minimal[longest] + overage >= minimal[second_longest]) if (minimal[longest] + overage >= minimal[second_longest]) widths[longest] += overage;
widths[longest] += overage;
// Case 2: Shorten the longest column to second longest length. Try to // Case 2: Shorten the longest column to second longest length. Try to
// split shortening them evenly. // split shortening them evenly.
else else {
{
int decrease = minimal[second_longest] - minimal[longest]; int decrease = minimal[second_longest] - minimal[longest];
widths[longest] += decrease; widths[longest] += decrease;
overage = overage - decrease; overage = overage - decrease;
// Attempt to decrease the two longest columns (at most to two characters) // 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 // Compute half of the overage, rounding up
int half_overage = overage / 2 + overage % 2; int half_overage = overage / 2 + overage % 2;
// Decrease both larges columns by this amount // Decrease both larges columns by this amount
widths[longest] += half_overage; widths[longest] += half_overage;
widths[second_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. // 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. // Perfect minimal width.
else if (overage == 0) else if (overage == 0) {
{
widths = minimal; widths = minimal;
} }
// Extra space to share. // Extra space to share.
else if (overage > 0) else if (overage > 0) {
{
widths = minimal; widths = minimal;
// Spread 'overage' among columns where width[i] < ideal[i] // Spread 'overage' among columns where width[i] < ideal[i]
bool needed = true; bool needed = true;
while (overage && needed) while (overage && needed) {
{
needed = false; needed = false;
for (unsigned int i = 0; i < _columns.size () && overage; ++i) for (unsigned int i = 0; i < _columns.size() && overage; ++i) {
{ if (widths[i] < ideal[i]) {
if (widths[i] < ideal[i])
{
++widths[i]; ++widths[i];
--overage; --overage;
needed = true; needed = true;
@@ -282,13 +254,11 @@ std::string ViewTask::render (std::vector <Task>& data, std::vector <int>& seque
// Compose column headers. // Compose column headers.
unsigned int max_lines = 0; unsigned int max_lines = 0;
std::vector<std::vector<std::string>> headers; 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(); headers.emplace_back();
_columns[c]->renderHeader(headers[c], widths[c], _sort[c] ? _sort_header : _header); _columns[c]->renderHeader(headers[c], widths[c], _sort[c] ? _sort_header : _header);
if (headers[c].size () > max_lines) if (headers[c].size() > max_lines) max_lines = headers[c].size();
max_lines = headers[c].size ();
} }
// Render column headers. // Render column headers.
@@ -303,14 +273,11 @@ std::string ViewTask::render (std::vector <Task>& data, std::vector <int>& seque
std::string out; std::string out;
_lines = 0; _lines = 0;
for (unsigned int i = 0; i < max_lines; ++i) for (unsigned int i = 0; i < max_lines; ++i) {
{
out += left_margin + extra; out += left_margin + extra;
for (unsigned int c = 0; c < _columns.size (); ++c) for (unsigned int c = 0; c < _columns.size(); ++c) {
{ if (c) out += intra;
if (c)
out += intra;
if (headers[c].size() < max_lines - i) if (headers[c].size() < max_lines - i)
out += _header.colorize(std::string(widths[c], ' ')); out += _header.colorize(std::string(widths[c], ' '));
@@ -325,8 +292,7 @@ std::string ViewTask::render (std::vector <Task>& data, std::vector <int>& seque
out += "\n"; out += "\n";
// Stop if the line limit is exceeded. // 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(); Context::getContext().time_render_us += timer.total_us();
return out; return out;
} }
@@ -335,8 +301,7 @@ std::string ViewTask::render (std::vector <Task>& data, std::vector <int>& seque
// Compose, render columns, in sequence. // Compose, render columns, in sequence.
_rows = 0; _rows = 0;
std::vector<std::vector<std::string>> cells; 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; max_lines = 0;
// Apply color rules to task. // Apply color rules to task.
@@ -346,35 +311,27 @@ std::string ViewTask::render (std::vector <Task>& data, std::vector <int>& seque
// Alternate rows based on |s % 2| // Alternate rows based on |s % 2|
bool odd = (s % 2) ? true : false; bool odd = (s % 2) ? true : false;
Color row_color; Color row_color;
if (Context::getContext ().color ()) if (Context::getContext().color()) {
{
row_color = odd ? _odd : _even; row_color = odd ? _odd : _even;
row_color.blend(rule_color); 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(); cells.emplace_back();
_columns[c]->render(cells[c], data[sequence[s]], widths[c], row_color); _columns[c]->render(cells[c], data[sequence[s]], widths[c], row_color);
if (cells[c].size () > max_lines) if (cells[c].size() > max_lines) max_lines = cells[c].size();
max_lines = cells[c].size ();
if (obfuscate) if (obfuscate)
if (_columns[c]->type() == "string") if (_columns[c]->type() == "string")
for (auto& line : cells[c]) for (auto& line : cells[c]) line = obfuscateText(line);
line = obfuscateText (line);
} }
// Listing breaks are simply blank lines inserted when a column value // Listing breaks are simply blank lines inserted when a column value
// changes. // changes.
if (s > 0 && if (s > 0 && _breaks.size() > 0) {
_breaks.size () > 0) for (const auto& b : _breaks) {
{ if (data[sequence[s - 1]].get(b) != data[sequence[s]].get(b)) {
for (const auto& b : _breaks)
{
if (data[sequence[s - 1]].get (b) != data[sequence[s]].get (b))
{
out += "\n"; out += "\n";
++_lines; ++_lines;
@@ -384,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); out += left_margin + (odd ? extra_odd : extra_even);
for (unsigned int c = 0; c < _columns.size (); ++c) for (unsigned int c = 0; c < _columns.size(); ++c) {
{ if (c) {
if (c)
{
if (row_color.nontrivial()) if (row_color.nontrivial())
row_color._colorize(out, intra); row_color._colorize(out, intra);
else else
@@ -411,8 +365,7 @@ std::string ViewTask::render (std::vector <Task>& data, std::vector <int>& seque
out += "\n"; out += "\n";
// Stop if the line limit is exceeded. // 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(); Context::getContext().time_render_us += timer.total_us();
return out; return out;
} }
@@ -421,8 +374,7 @@ std::string ViewTask::render (std::vector <Task>& data, std::vector <int>& seque
cells.clear(); cells.clear();
// Stop if the row limit is exceeded. // 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(); Context::getContext().time_render_us += timer.total_us();
return out; return out;
} }

View File

@@ -27,23 +27,29 @@
#ifndef INCLUDED_VIEWTASK #ifndef INCLUDED_VIEWTASK
#define INCLUDED_VIEWTASK #define INCLUDED_VIEWTASK
#include <string>
#include <vector>
#include <Task.h>
#include <Color.h> #include <Color.h>
#include <Column.h> #include <Column.h>
#include <Task.h>
class ViewTask #include <string>
{ #include <vector>
class ViewTask {
public: public:
ViewTask(); ViewTask();
~ViewTask(); ~ViewTask();
// View specifications. // 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 width(int width) { _width = width; }
void leftMargin(int margin) { _left_margin = margin; } 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 colorSortHeader(Color& c) { _sort_header = c; }
void colorOdd(Color& c) { _odd = c; } void colorOdd(Color& c) { _odd = c; }
void colorEven(Color& c) { _even = c; } void colorEven(Color& c) { _even = c; }
@@ -86,4 +92,3 @@ private:
#endif #endif
//////////////////////////////////////////////////////////////////////////////// ////////////////////////////////////////////////////////////////////////////////

View File

@@ -25,22 +25,24 @@
//////////////////////////////////////////////////////////////////////////////// ////////////////////////////////////////////////////////////////////////////////
#include <cmake.h> #include <cmake.h>
#include <iostream> // cmake.h include header must come first
#include <string>
#include <stdlib.h>
#include <string.h>
#include <Eval.h>
#include <Context.h> #include <Context.h>
#include <Task.h>
#include <Datetime.h> #include <Datetime.h>
#include <Duration.h> #include <Duration.h>
#include <shared.h> #include <Eval.h>
#include <Task.h>
#include <format.h> #include <format.h>
#include <shared.h>
#include <stdlib.h>
#include <string.h>
#include <iostream>
#include <string>
//////////////////////////////////////////////////////////////////////////////// ////////////////////////////////////////////////////////////////////////////////
// Constants. // 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. // An example, although a bad one because this is supported by default.
if (name == "pi") {value = Variant (3.14159165); return true;} if (name == "pi") {value = Variant (3.14159165); return true;}
@@ -50,12 +52,10 @@ bool get (const std::string&, Variant&)
} }
//////////////////////////////////////////////////////////////////////////////// ////////////////////////////////////////////////////////////////////////////////
int main (int argc, char** argv) int main(int argc, char** argv) {
{
int status = 0; int status = 0;
try try {
{
Context globalContext; Context globalContext;
Context::setContext(&globalContext); Context::setContext(&globalContext);
@@ -72,10 +72,8 @@ int main (int argc, char** argv)
// Combine all the arguments into one expression string. // Combine all the arguments into one expression string.
std::string expression; std::string expression;
for (int i = 1; i < argc; i++) for (int i = 1; i < argc; i++) {
{ if (!strcmp(argv[i], "-h") || !strcmp(argv[i], "--help")) {
if (!strcmp (argv[i], "-h") || ! strcmp (argv[i], "--help"))
{
std::cout << '\n' std::cout << '\n'
<< "Usage: " << argv[0] << " [options] '<expression>'\n" << "Usage: " << argv[0] << " [options] '<expression>'\n"
<< '\n' << '\n'
@@ -86,23 +84,18 @@ int main (int argc, char** argv)
<< " -p|--postfix Postfix expression\n" << " -p|--postfix Postfix expression\n"
<< '\n'; << '\n';
exit(1); 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' std::cout << '\n'
<< format ("calc {1} built for ", VERSION) << format("calc {1} built for ", VERSION) << osName() << '\n'
<< osName () << "Copyright (C) 2006 - 2021 T. Babej, P. Beckingham, F. Hernandez." << '\n'
<< '\n' << '\n'
<< "Copyright (C) 2006 - 2021 T. Babej, P. Beckingham, F. Hernandez." << "Taskwarrior may be copied only under the terms of the MIT license, which may "
<< '\n' "be found in the Taskwarrior source kit."
<< '\n'
<< "Taskwarrior may be copied only under the terms of the MIT license, which may be found in the Taskwarrior source kit."
<< '\n' << '\n'
<< '\n'; << '\n';
exit(1); 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); e.debug(true);
else if (!strcmp(argv[i], "-i") || !strcmp(argv[i], "--infix")) else if (!strcmp(argv[i], "-i") || !strcmp(argv[i], "--infix"))
infix = true; infix = true;
@@ -119,22 +112,18 @@ int main (int argc, char** argv)
e.evaluatePostfixExpression(expression, result); e.evaluatePostfixExpression(expression, result);
// Show any debug output. // Show any debug output.
for (const auto& i : Context::getContext ().debugMessages) for (const auto& i : Context::getContext().debugMessages) std::cout << i << '\n';
std::cout << i << '\n';
// Show the result in string form. // Show the result in string form.
std::cout << (std::string) result std::cout << (std::string)result << '\n';
<< '\n';
} }
catch (const std::string& error) catch (const std::string& error) {
{
std::cerr << error << '\n'; std::cerr << error << '\n';
status = -1; status = -1;
} }
catch (...) catch (...) {
{
std::cerr << "Unknown error occured. Oops.\n"; std::cerr << "Unknown error occured. Oops.\n";
status = -2; status = -2;
} }

View File

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

View File

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

View File

@@ -25,33 +25,28 @@
//////////////////////////////////////////////////////////////////////////////// ////////////////////////////////////////////////////////////////////////////////
#include <cmake.h> #include <cmake.h>
// cmake.h include header must come first
#include <ColDescription.h> #include <ColDescription.h>
#include <stdlib.h>
#include <Context.h> #include <Context.h>
#include <Datetime.h> #include <Datetime.h>
#include <shared.h>
#include <format.h> #include <format.h>
#include <shared.h>
#include <stdlib.h>
#include <utf8.h> #include <utf8.h>
#include <util.h> #include <util.h>
//////////////////////////////////////////////////////////////////////////////// ////////////////////////////////////////////////////////////////////////////////
ColumnDescription::ColumnDescription () ColumnDescription::ColumnDescription() {
{
_name = "description"; _name = "description";
_style = "combined"; _style = "combined";
_label = "Description"; _label = "Description";
_modifiable = true; _modifiable = true;
_styles = {"combined", _styles = {"combined", "desc", "oneline", "truncated", "count", "truncated_count"};
"desc",
"oneline",
"truncated",
"count",
"truncated_count"};
_dateformat = Context::getContext().config.get("dateformat.annotation"); _dateformat = Context::getContext().config.get("dateformat.annotation");
if (_dateformat == "") if (_dateformat == "") _dateformat = Context::getContext().config.get("dateformat");
_dateformat = Context::getContext ().config.get ("dateformat");
std::string t = Datetime().toString(_dateformat); std::string t = Datetime().toString(_dateformat);
std::string d = "Move your clothes down on to the lower peg"; std::string d = "Move your clothes down on to the lower peg";
@@ -60,15 +55,11 @@ ColumnDescription::ColumnDescription ()
std::string a3 = "Before you write your letter home"; std::string a3 = "Before you write your letter home";
std::string a4 = "If you're not getting your hair cut"; std::string a4 = "If you're not getting your hair cut";
_examples = {d + "\n " + t + ' ' + a1 _examples = {
+ "\n " + t + ' ' + a2 d + "\n " + t + ' ' + a1 + "\n " + t + ' ' + a2 + "\n " + t + ' ' + a3 + "\n " + t + ' ' +
+ "\n " + t + ' ' + a3 a4,
+ "\n " + t + ' ' + a4,
d, d,
d + ' ' + t + ' ' + a1 d + ' ' + t + ' ' + a1 + ' ' + t + ' ' + a2 + ' ' + t + ' ' + a3 + ' ' + t + ' ' + a4,
+ ' ' + t + ' ' + a2
+ ' ' + t + ' ' + a3
+ ' ' + t + ' ' + a4,
d.substr(0, 20) + "...", d.substr(0, 20) + "...",
d + " [4]", d + " [4]",
d.substr(0, 20) + "... [4]"}; d.substr(0, 20) + "... [4]"};
@@ -80,97 +71,75 @@ ColumnDescription::ColumnDescription ()
//////////////////////////////////////////////////////////////////////////////// ////////////////////////////////////////////////////////////////////////////////
// Set the minimum and maximum widths for the value. // 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); std::string description = task.get(_name);
// The text // The text
// <indent> <date> <anno> // <indent> <date> <anno>
// ... // ...
if (_style == "default" || if (_style == "default" || _style == "combined") {
_style == "combined")
{
minimum = longestWord(description); minimum = longestWord(description);
maximum = utf8_width(description); maximum = utf8_width(description);
if (task.annotation_count) if (task.annotation_count) {
{
unsigned int min_anno = _indent + Datetime::length(_dateformat); unsigned int min_anno = _indent + Datetime::length(_dateformat);
if (min_anno > minimum) if (min_anno > minimum) minimum = min_anno;
minimum = min_anno;
for (auto& i : task.getAnnotations ()) for (auto& i : task.getAnnotations()) {
{
unsigned int len = min_anno + 1 + utf8_width(i.second); unsigned int len = min_anno + 1 + utf8_width(i.second);
if (len > maximum) if (len > maximum) maximum = len;
maximum = len;
} }
} }
} }
// Just the text // Just the text
else if (_style == "desc") else if (_style == "desc") {
{
maximum = utf8_width(description); maximum = utf8_width(description);
minimum = longestWord(description); minimum = longestWord(description);
} }
// The text <date> <anno> ... // The text <date> <anno> ...
else if (_style == "oneline") else if (_style == "oneline") {
{
minimum = longestWord(description); minimum = longestWord(description);
maximum = utf8_width(description); maximum = utf8_width(description);
if (task.annotation_count) if (task.annotation_count) {
{
auto min_anno = Datetime::length(_dateformat); auto min_anno = Datetime::length(_dateformat);
for (auto& i : task.getAnnotations ()) for (auto& i : task.getAnnotations()) maximum += min_anno + 1 + utf8_width(i.second);
maximum += min_anno + 1 + utf8_width (i.second);
} }
} }
// The te... // The te...
else if (_style == "truncated") else if (_style == "truncated") {
{
minimum = 4; minimum = 4;
maximum = utf8_width(description); maximum = utf8_width(description);
} }
// The text [2] // The text [2]
else if (_style == "count") else if (_style == "count") {
{
// <description> + ' ' + '[' + <count> + ']' // <description> + ' ' + '[' + <count> + ']'
maximum = utf8_width(description) + 1 + 1 + format(task.annotation_count).length() + 1; maximum = utf8_width(description) + 1 + 1 + format(task.annotation_count).length() + 1;
minimum = longestWord(description); minimum = longestWord(description);
} }
// The te... [2] // The te... [2]
else if (_style == "truncated_count") else if (_style == "truncated_count") {
{
minimum = 4; minimum = 4;
maximum = utf8_width(description) + 1 + 1 + format(task.annotation_count).length() + 1; maximum = utf8_width(description) + 1 + 1 + format(task.annotation_count).length() + 1;
} }
} }
//////////////////////////////////////////////////////////////////////////////// ////////////////////////////////////////////////////////////////////////////////
void ColumnDescription::render ( void ColumnDescription::render(std::vector<std::string>& lines, Task& task, int width,
std::vector <std::string>& lines, Color& color) {
Task& task,
int width,
Color& color)
{
std::string description = task.get(_name); std::string description = task.get(_name);
// This is a description // This is a description
// <date> <anno> // <date> <anno>
// ... // ...
if (_style == "default" || if (_style == "default" || _style == "combined") {
_style == "combined") if (task.annotation_count) {
{ for (const auto& i : task.getAnnotations()) {
if (task.annotation_count)
{
for (const auto& i : task.getAnnotations ())
{
Datetime dt(strtoll(i.first.substr(11).c_str(), nullptr, 10)); Datetime dt(strtoll(i.first.substr(11).c_str(), nullptr, 10));
description += '\n' + std::string(_indent, ' ') + dt.toString(_dateformat) + ' ' + i.second; description += '\n' + std::string(_indent, ' ') + dt.toString(_dateformat) + ' ' + i.second;
} }
@@ -179,27 +148,21 @@ void ColumnDescription::render (
std::vector<std::string> raw; std::vector<std::string> raw;
wrapText(raw, description, width, _hyphenate); wrapText(raw, description, width, _hyphenate);
for (const auto& i : raw) for (const auto& i : raw) renderStringLeft(lines, width, color, i);
renderStringLeft (lines, width, color, i);
} }
// This is a description // This is a description
else if (_style == "desc") else if (_style == "desc") {
{
std::vector<std::string> raw; std::vector<std::string> raw;
wrapText(raw, description, width, _hyphenate); wrapText(raw, description, width, _hyphenate);
for (const auto& i : raw) for (const auto& i : raw) renderStringLeft(lines, width, color, i);
renderStringLeft (lines, width, color, i);
} }
// This is a description <date> <anno> ... // This is a description <date> <anno> ...
else if (_style == "oneline") else if (_style == "oneline") {
{ if (task.annotation_count) {
if (task.annotation_count) for (const auto& i : task.getAnnotations()) {
{
for (const auto& i : task.getAnnotations ())
{
Datetime dt(strtoll(i.first.substr(11).c_str(), nullptr, 10)); Datetime dt(strtoll(i.first.substr(11).c_str(), nullptr, 10));
description += ' ' + dt.toString(_dateformat) + ' ' + i.second; description += ' ' + dt.toString(_dateformat) + ' ' + i.second;
} }
@@ -208,13 +171,11 @@ void ColumnDescription::render (
std::vector<std::string> raw; std::vector<std::string> raw;
wrapText(raw, description, width, _hyphenate); wrapText(raw, description, width, _hyphenate);
for (const auto& i : raw) for (const auto& i : raw) renderStringLeft(lines, width, color, i);
renderStringLeft (lines, width, color, i);
} }
// This is a des... // This is a des...
else if (_style == "truncated") else if (_style == "truncated") {
{
int len = utf8_width(description); int len = utf8_width(description);
if (len > width) if (len > width)
renderStringLeft(lines, width, color, description.substr(0, width - 3) + "..."); renderStringLeft(lines, width, color, description.substr(0, width - 3) + "...");
@@ -223,34 +184,30 @@ void ColumnDescription::render (
} }
// This is a description [2] // This is a description [2]
else if (_style == "count") else if (_style == "count") {
{ if (task.annotation_count) description += " [" + format(task.annotation_count) + ']';
if (task.annotation_count)
description += " [" + format (task.annotation_count) + ']';
std::vector<std::string> raw; std::vector<std::string> raw;
wrapText(raw, description, width, _hyphenate); wrapText(raw, description, width, _hyphenate);
for (const auto& i : raw) for (const auto& i : raw) renderStringLeft(lines, width, color, i);
renderStringLeft (lines, width, color, i);
} }
// This is a des... [2] // This is a des... [2]
else if (_style == "truncated_count") else if (_style == "truncated_count") {
{
int len = utf8_width(description); int len = utf8_width(description);
std::string annos_count; std::string annos_count;
int len_annos = 0; int len_annos = 0;
if (task.annotation_count) if (task.annotation_count) {
{
annos_count = " [" + format(task.annotation_count) + ']'; annos_count = " [" + format(task.annotation_count) + ']';
len_annos = utf8_width(annos_count); len_annos = utf8_width(annos_count);
len += len_annos; len += len_annos;
} }
if (len > width) 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 else
renderStringLeft(lines, width, color, description + annos_count); renderStringLeft(lines, width, color, description + annos_count);
} }

View File

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

View File

@@ -25,11 +25,12 @@
//////////////////////////////////////////////////////////////////////////////// ////////////////////////////////////////////////////////////////////////////////
#include <cmake.h> #include <cmake.h>
// cmake.h include header must come first
#include <ColDue.h> #include <ColDue.h>
//////////////////////////////////////////////////////////////////////////////// ////////////////////////////////////////////////////////////////////////////////
ColumnDue::ColumnDue () ColumnDue::ColumnDue() {
{
_name = "due"; _name = "due";
_modifiable = true; _modifiable = true;
_label = "Due"; _label = "Due";
@@ -38,12 +39,10 @@ ColumnDue::ColumnDue ()
//////////////////////////////////////////////////////////////////////////////// ////////////////////////////////////////////////////////////////////////////////
// Overriden so that style <----> label are linked. // Overriden so that style <----> label are linked.
// Note that you can not determine which gets called first. // 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); Column::setStyle(value);
if (_style == "countdown" && _label == "Due") if (_style == "countdown" && _label == "Due") _label = "Count";
_label = "Count";
} }
//////////////////////////////////////////////////////////////////////////////// ////////////////////////////////////////////////////////////////////////////////

View File

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

View File

@@ -25,11 +25,12 @@
//////////////////////////////////////////////////////////////////////////////// ////////////////////////////////////////////////////////////////////////////////
#include <cmake.h> #include <cmake.h>
// cmake.h include header must come first
#include <ColEnd.h> #include <ColEnd.h>
//////////////////////////////////////////////////////////////////////////////// ////////////////////////////////////////////////////////////////////////////////
ColumnEnd::ColumnEnd () ColumnEnd::ColumnEnd() {
{
_name = "end"; _name = "end";
_label = "Completed"; _label = "Completed";
} }

View File

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

View File

@@ -25,11 +25,12 @@
//////////////////////////////////////////////////////////////////////////////// ////////////////////////////////////////////////////////////////////////////////
#include <cmake.h> #include <cmake.h>
// cmake.h include header must come first
#include <ColEntry.h> #include <ColEntry.h>
//////////////////////////////////////////////////////////////////////////////// ////////////////////////////////////////////////////////////////////////////////
ColumnEntry::ColumnEntry () ColumnEntry::ColumnEntry() {
{
_name = "entry"; _name = "entry";
_modifiable = true; _modifiable = true;
_label = "Added"; _label = "Added";
@@ -38,13 +39,10 @@ ColumnEntry::ColumnEntry ()
//////////////////////////////////////////////////////////////////////////////// ////////////////////////////////////////////////////////////////////////////////
// Overriden so that style <----> label are linked. // Overriden so that style <----> label are linked.
// Note that you can not determine which gets called first. // 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); Column::setStyle(value);
if (_style == "age" && if (_style == "age" && _label == "Added") _label = "Age";
_label == "Added")
_label = "Age";
} }
//////////////////////////////////////////////////////////////////////////////// ////////////////////////////////////////////////////////////////////////////////

View File

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

View File

@@ -25,13 +25,14 @@
//////////////////////////////////////////////////////////////////////////////// ////////////////////////////////////////////////////////////////////////////////
#include <cmake.h> #include <cmake.h>
// cmake.h include header must come first
#include <ColID.h> #include <ColID.h>
#include <math.h>
#include <format.h> #include <format.h>
#include <math.h>
//////////////////////////////////////////////////////////////////////////////// ////////////////////////////////////////////////////////////////////////////////
ColumnID::ColumnID () ColumnID::ColumnID() {
{
_name = "id"; _name = "id";
_style = "number"; _style = "number";
_label = "ID"; _label = "ID";
@@ -42,27 +43,27 @@ ColumnID::ColumnID ()
//////////////////////////////////////////////////////////////////////////////// ////////////////////////////////////////////////////////////////////////////////
// Set the minimum and maximum widths for the value. // 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; int length;
if (task.id < 10) length = 1; // Fast if (task.id < 10)
else if (task.id < 100) length = 2; // Fast length = 1; // Fast
else if (task.id < 1000) length = 3; // Fast else if (task.id < 100)
else if (task.id < 10000) length = 4; // Fast length = 2; // Fast
else if (task.id < 100000) length = 5; // Fast else if (task.id < 1000)
else length = 1 + (int) log10 ((double) task.id); // Slow 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; minimum = maximum = length;
} }
//////////////////////////////////////////////////////////////////////////////// ////////////////////////////////////////////////////////////////////////////////
void ColumnID::render ( void ColumnID::render(std::vector<std::string>& lines, Task& task, int width, Color& color) {
std::vector <std::string>& lines,
Task& task,
int width,
Color& color)
{
// Completed and deleted tasks have no ID. // Completed and deleted tasks have no ID.
if (task.id) if (task.id)
renderInteger(lines, width, color, task.id); renderInteger(lines, width, color, task.id);

View File

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

View File

@@ -25,12 +25,13 @@
//////////////////////////////////////////////////////////////////////////////// ////////////////////////////////////////////////////////////////////////////////
#include <cmake.h> #include <cmake.h>
// cmake.h include header must come first
#include <ColIMask.h> #include <ColIMask.h>
#include <format.h> #include <format.h>
//////////////////////////////////////////////////////////////////////////////// ////////////////////////////////////////////////////////////////////////////////
ColumnIMask::ColumnIMask () ColumnIMask::ColumnIMask() {
{
_name = "imask"; _name = "imask";
_style = "number"; _style = "number";
_label = "Mask Index"; _label = "Mask Index";
@@ -41,22 +42,14 @@ ColumnIMask::ColumnIMask ()
//////////////////////////////////////////////////////////////////////////////// ////////////////////////////////////////////////////////////////////////////////
// Set the minimum and maximum widths for the value. // 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; minimum = maximum = 0;
if (task.has (_name)) if (task.has(_name)) minimum = maximum = task.get(_name).length();
minimum = maximum = task.get (_name).length ();
} }
//////////////////////////////////////////////////////////////////////////////// ////////////////////////////////////////////////////////////////////////////////
void ColumnIMask::render ( void ColumnIMask::render(std::vector<std::string>& lines, Task& task, int width, Color& color) {
std::vector <std::string>& lines, if (task.has(_name)) renderStringRight(lines, width, color, task.get(_name));
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> #include <ColTypeNumeric.h>
class ColumnIMask : public ColumnTypeNumeric class ColumnIMask : public ColumnTypeNumeric {
{
public: public:
ColumnIMask(); ColumnIMask();
void measure(Task&, unsigned int&, unsigned int&); void measure(Task&, unsigned int&, unsigned int&);

View File

@@ -25,12 +25,13 @@
//////////////////////////////////////////////////////////////////////////////// ////////////////////////////////////////////////////////////////////////////////
#include <cmake.h> #include <cmake.h>
// cmake.h include header must come first
#include <ColLast.h> #include <ColLast.h>
#include <format.h> #include <format.h>
//////////////////////////////////////////////////////////////////////////////// ////////////////////////////////////////////////////////////////////////////////
ColumnLast::ColumnLast () ColumnLast::ColumnLast() {
{
_name = "last"; _name = "last";
_style = "number"; _style = "number";
_label = "Last instance"; _label = "Last instance";
@@ -41,22 +42,14 @@ ColumnLast::ColumnLast ()
//////////////////////////////////////////////////////////////////////////////// ////////////////////////////////////////////////////////////////////////////////
// Set the minimum and maximum widths for the value. // 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; minimum = maximum = 0;
if (task.has (_name)) if (task.has(_name)) minimum = maximum = task.get(_name).length();
minimum = maximum = task.get (_name).length ();
} }
//////////////////////////////////////////////////////////////////////////////// ////////////////////////////////////////////////////////////////////////////////
void ColumnLast::render ( void ColumnLast::render(std::vector<std::string>& lines, Task& task, int width, Color& color) {
std::vector <std::string>& lines, if (task.has(_name)) renderStringRight(lines, width, color, task.get(_name));
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> #include <ColTypeNumeric.h>
class ColumnLast : public ColumnTypeNumeric class ColumnLast : public ColumnTypeNumeric {
{
public: public:
ColumnLast(); ColumnLast();
void measure(Task&, unsigned int&, unsigned int&); void measure(Task&, unsigned int&, unsigned int&);

View File

@@ -25,12 +25,13 @@
//////////////////////////////////////////////////////////////////////////////// ////////////////////////////////////////////////////////////////////////////////
#include <cmake.h> #include <cmake.h>
// cmake.h include header must come first
#include <ColMask.h> #include <ColMask.h>
#include <format.h> #include <format.h>
//////////////////////////////////////////////////////////////////////////////// ////////////////////////////////////////////////////////////////////////////////
ColumnMask::ColumnMask () ColumnMask::ColumnMask() {
{
_name = "mask"; _name = "mask";
_style = "default"; _style = "default";
_label = "Mask"; _label = "Mask";
@@ -41,22 +42,14 @@ ColumnMask::ColumnMask ()
//////////////////////////////////////////////////////////////////////////////// ////////////////////////////////////////////////////////////////////////////////
// Set the minimum and maximum widths for the value. // 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; minimum = maximum = 0;
if (task.has (_name)) if (task.has(_name)) minimum = maximum = task.get(_name).length();
minimum = maximum = task.get (_name).length ();
} }
//////////////////////////////////////////////////////////////////////////////// ////////////////////////////////////////////////////////////////////////////////
void ColumnMask::render ( void ColumnMask::render(std::vector<std::string>& lines, Task& task, int width, Color& color) {
std::vector <std::string>& lines, if (task.has(_name)) renderStringLeft(lines, width, color, task.get(_name));
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> #include <ColTypeString.h>
class ColumnMask : public ColumnTypeString class ColumnMask : public ColumnTypeString {
{
public: public:
ColumnMask(); ColumnMask();
void measure(Task&, unsigned int&, unsigned int&); void measure(Task&, unsigned int&, unsigned int&);

View File

@@ -25,11 +25,12 @@
//////////////////////////////////////////////////////////////////////////////// ////////////////////////////////////////////////////////////////////////////////
#include <cmake.h> #include <cmake.h>
// cmake.h include header must come first
#include <ColModified.h> #include <ColModified.h>
//////////////////////////////////////////////////////////////////////////////// ////////////////////////////////////////////////////////////////////////////////
ColumnModified::ColumnModified () ColumnModified::ColumnModified() {
{
_name = "modified"; _name = "modified";
_label = "Modified"; _label = "Modified";
} }

View File

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

View File

@@ -25,12 +25,13 @@
//////////////////////////////////////////////////////////////////////////////// ////////////////////////////////////////////////////////////////////////////////
#include <cmake.h> #include <cmake.h>
// cmake.h include header must come first
#include <ColParent.h> #include <ColParent.h>
#include <format.h> #include <format.h>
//////////////////////////////////////////////////////////////////////////////// ////////////////////////////////////////////////////////////////////////////////
ColumnParent::ColumnParent () ColumnParent::ColumnParent() {
{
_name = "parent"; _name = "parent";
_style = "long"; _style = "long";
_label = "Parent task"; _label = "Parent task";
@@ -41,29 +42,22 @@ ColumnParent::ColumnParent ()
//////////////////////////////////////////////////////////////////////////////// ////////////////////////////////////////////////////////////////////////////////
// Set the minimum and maximum widths for the value. // 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; minimum = maximum = 0;
if (task.has (_name)) if (task.has(_name)) {
{ if (_style == "default" || _style == "long")
if (_style == "default" || _style == "long") minimum = maximum = 36; minimum = maximum = 36;
else if (_style == "short") minimum = maximum = 8; else if (_style == "short")
minimum = maximum = 8;
} }
} }
//////////////////////////////////////////////////////////////////////////////// ////////////////////////////////////////////////////////////////////////////////
void ColumnParent::render ( void ColumnParent::render(std::vector<std::string>& lines, Task& task, int width, Color& color) {
std::vector <std::string>& lines, if (task.has(_name)) {
Task& task,
int width,
Color& color)
{
if (task.has (_name))
{
// f30cb9c3-3fc0-483f-bfb2-3bf134f00694 default // f30cb9c3-3fc0-483f-bfb2-3bf134f00694 default
// f30cb9c3 short // f30cb9c3 short
if (_style == "default" || if (_style == "default" || _style == "long")
_style == "long")
renderStringLeft(lines, width, color, task.get(_name)); renderStringLeft(lines, width, color, task.get(_name));
else if (_style == "short") else if (_style == "short")

View File

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

View File

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

View File

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

View File

@@ -25,15 +25,17 @@
//////////////////////////////////////////////////////////////////////////////// ////////////////////////////////////////////////////////////////////////////////
#include <cmake.h> #include <cmake.h>
// cmake.h include header must come first
#include <ColRType.h> #include <ColRType.h>
#include <Context.h> #include <Context.h>
#include <shared.h>
#include <format.h> #include <format.h>
#include <shared.h>
#include <cctype> #include <cctype>
//////////////////////////////////////////////////////////////////////////////// ////////////////////////////////////////////////////////////////////////////////
ColumnRType::ColumnRType () ColumnRType::ColumnRType() {
{
_name = "rtype"; _name = "rtype";
_style = "default"; _style = "default";
_label = "Recurrence type"; _label = "Recurrence type";
@@ -45,8 +47,7 @@ ColumnRType::ColumnRType ()
//////////////////////////////////////////////////////////////////////////////// ////////////////////////////////////////////////////////////////////////////////
// Overriden so that style <----> label are linked. // Overriden so that style <----> label are linked.
// Note that you can not determine which gets called first. // 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); Column::setStyle(value);
if (_style == "indicator" && _label == "Recurrence type") if (_style == "indicator" && _label == "Recurrence type")
@@ -55,11 +56,9 @@ void ColumnRType::setStyle (const std::string& value)
//////////////////////////////////////////////////////////////////////////////// ////////////////////////////////////////////////////////////////////////////////
// Set the minimum and maximum widths for the 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; minimum = maximum = 0;
if (task.has (_name)) if (task.has(_name)) {
{
if (_style == "default") if (_style == "default")
minimum = maximum = task.get(_name).length(); minimum = maximum = task.get(_name).length();
else if (_style == "indicator") else if (_style == "indicator")
@@ -68,19 +67,12 @@ void ColumnRType::measure (Task& task, unsigned int& minimum, unsigned int& maxi
} }
//////////////////////////////////////////////////////////////////////////////// ////////////////////////////////////////////////////////////////////////////////
void ColumnRType::render ( void ColumnRType::render(std::vector<std::string>& lines, Task& task, int width, Color& color) {
std::vector <std::string>& lines, if (task.has(_name)) {
Task& task,
int width,
Color& color)
{
if (task.has (_name))
{
if (_style == "default") if (_style == "default")
renderStringRight(lines, width, color, task.get(_name)); renderStringRight(lines, width, color, task.get(_name));
else if (_style == "indicator") else if (_style == "indicator") {
{
std::string value{" "}; std::string value{" "};
value[0] = toupper(task.get(_name)[0]); value[0] = toupper(task.get(_name)[0]);
renderStringRight(lines, width, color, value); renderStringRight(lines, width, color, value);
@@ -89,10 +81,8 @@ void ColumnRType::render (
} }
//////////////////////////////////////////////////////////////////////////////// ////////////////////////////////////////////////////////////////////////////////
bool ColumnRType::validate (const std::string& input) const bool ColumnRType::validate(const std::string& input) const {
{ return input == "periodic" || input == "chained";
return input == "periodic" ||
input == "chained";
} }
//////////////////////////////////////////////////////////////////////////////// ////////////////////////////////////////////////////////////////////////////////

View File

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

View File

@@ -25,20 +25,21 @@
//////////////////////////////////////////////////////////////////////////////// ////////////////////////////////////////////////////////////////////////////////
#include <cmake.h> #include <cmake.h>
// cmake.h include header must come first
#include <ColRecur.h> #include <ColRecur.h>
#include <Context.h> #include <Context.h>
#include <Duration.h> #include <Duration.h>
#include <Eval.h> #include <Eval.h>
#include <Variant.h>
#include <Lexer.h>
#include <Filter.h> #include <Filter.h>
#include <shared.h> #include <Lexer.h>
#include <Variant.h>
#include <format.h> #include <format.h>
#include <shared.h>
#include <utf8.h> #include <utf8.h>
//////////////////////////////////////////////////////////////////////////////// ////////////////////////////////////////////////////////////////////////////////
ColumnRecur::ColumnRecur () ColumnRecur::ColumnRecur() {
{
_name = "recur"; _name = "recur";
_style = "duration"; _style = "duration";
_label = "Recur"; _label = "Recur";
@@ -50,8 +51,7 @@ ColumnRecur::ColumnRecur ()
//////////////////////////////////////////////////////////////////////////////// ////////////////////////////////////////////////////////////////////////////////
// Overriden so that style <----> label are linked. // Overriden so that style <----> label are linked.
// Note that you can not determine which gets called first. // 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); Column::setStyle(value);
if (_style == "indicator" && _label == "Recur") if (_style == "indicator" && _label == "Recur")
@@ -60,68 +60,51 @@ void ColumnRecur::setStyle (const std::string& value)
//////////////////////////////////////////////////////////////////////////////// ////////////////////////////////////////////////////////////////////////////////
// Set the minimum and maximum widths for the 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; minimum = maximum = 0;
if (task.has (_name)) if (task.has(_name)) {
{ if (_style == "default" || _style == "duration") {
if (_style == "default" ||
_style == "duration")
{
minimum = maximum = Duration(task.get(_name)).formatISO().length(); 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")); minimum = maximum = utf8_width(Context::getContext().config.get("recurrence.indicator"));
} }
} }
} }
//////////////////////////////////////////////////////////////////////////////// ////////////////////////////////////////////////////////////////////////////////
void ColumnRecur::render ( void ColumnRecur::render(std::vector<std::string>& lines, Task& task, int width, Color& color) {
std::vector <std::string>& lines, if (task.has(_name)) {
Task& task, if (_style == "default" || _style == "duration")
int width,
Color& color)
{
if (task.has (_name))
{
if (_style == "default" ||
_style == "duration")
renderStringRight(lines, width, color, Duration(task.get(_name)).formatISO()); renderStringRight(lines, width, color, Duration(task.get(_name)).formatISO());
else if (_style == "indicator") 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, // The duration is stored in raw form, but it must still be valid,
// and therefore is parsed first. // 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. // Try to evaluate 'value'. It might work.
Variant evaluatedValue; Variant evaluatedValue;
try try {
{
Eval e; Eval e;
e.addSource(domSource); e.addSource(domSource);
e.evaluateInfixExpression(value, evaluatedValue); e.evaluateInfixExpression(value, evaluatedValue);
} }
catch (...) catch (...) {
{
evaluatedValue = Variant(value); evaluatedValue = Variant(value);
} }
if (evaluatedValue.type () == Variant::type_duration) if (evaluatedValue.type() == Variant::type_duration) {
{
// Store the raw value, for 'recur'. // Store the raw value, for 'recur'.
std::string label = " MODIFICATION "; std::string label = " MODIFICATION ";
Context::getContext().debug(label + _name + " <-- '" + value + '\''); Context::getContext().debug(label + _name + " <-- '" + value + '\'');
task.set(_name, value); task.set(_name, value);
} } else
else
throw format("The duration value '{1}' is not supported.", value); 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 // 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. // raw duration, so that it can be reevaluated every time.
class ColumnRecur : public ColumnTypeString class ColumnRecur : public ColumnTypeString {
{
public: public:
ColumnRecur(); ColumnRecur();
void setStyle(const std::string&); void setStyle(const std::string&);

View File

@@ -25,11 +25,12 @@
//////////////////////////////////////////////////////////////////////////////// ////////////////////////////////////////////////////////////////////////////////
#include <cmake.h> #include <cmake.h>
// cmake.h include header must come first
#include <ColScheduled.h> #include <ColScheduled.h>
//////////////////////////////////////////////////////////////////////////////// ////////////////////////////////////////////////////////////////////////////////
ColumnScheduled::ColumnScheduled () ColumnScheduled::ColumnScheduled() {
{
_name = "scheduled"; _name = "scheduled";
_label = "Scheduled"; _label = "Scheduled";
} }
@@ -37,12 +38,10 @@ ColumnScheduled::ColumnScheduled ()
//////////////////////////////////////////////////////////////////////////////// ////////////////////////////////////////////////////////////////////////////////
// Overriden so that style <----> label are linked. // Overriden so that style <----> label are linked.
// Note that you can not determine which gets called first. // 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); Column::setStyle(value);
if (_style == "countdown" && _label == "Scheduled") if (_style == "countdown" && _label == "Scheduled") _label = "Count";
_label = "Count";
} }
//////////////////////////////////////////////////////////////////////////////// ////////////////////////////////////////////////////////////////////////////////

View File

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

View File

@@ -25,13 +25,14 @@
//////////////////////////////////////////////////////////////////////////////// ////////////////////////////////////////////////////////////////////////////////
#include <cmake.h> #include <cmake.h>
// cmake.h include header must come first
#include <ColStart.h> #include <ColStart.h>
#include <Context.h> #include <Context.h>
#include <utf8.h> #include <utf8.h>
//////////////////////////////////////////////////////////////////////////////// ////////////////////////////////////////////////////////////////////////////////
ColumnStart::ColumnStart () ColumnStart::ColumnStart() {
{
_name = "start"; _name = "start";
_label = "Started"; _label = "Started";
@@ -42,21 +43,17 @@ ColumnStart::ColumnStart ()
//////////////////////////////////////////////////////////////////////////////// ////////////////////////////////////////////////////////////////////////////////
// Overriden so that style <----> label are linked. // Overriden so that style <----> label are linked.
// Note that you can not determine which gets called first. // 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); Column::setStyle(value);
if (_style == "active" && _label == "Started") if (_style == "active" && _label == "Started") _label = "A";
_label = "A";
} }
//////////////////////////////////////////////////////////////////////////////// ////////////////////////////////////////////////////////////////////////////////
// Set the minimum and maximum widths for the value. // 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; minimum = maximum = 0;
if (task.has (_name)) if (task.has(_name)) {
{
if (_style == "active") if (_style == "active")
minimum = maximum = utf8_width(Context::getContext().config.get("active.indicator")); minimum = maximum = utf8_width(Context::getContext().config.get("active.indicator"));
else else
@@ -67,20 +64,13 @@ void ColumnStart::measure (Task& task, unsigned int& minimum, unsigned int& maxi
} }
//////////////////////////////////////////////////////////////////////////////// ////////////////////////////////////////////////////////////////////////////////
void ColumnStart::render ( void ColumnStart::render(std::vector<std::string>& lines, Task& task, int width, Color& color) {
std::vector <std::string>& lines, if (task.has(_name)) {
Task& task, if (_style == "active") {
int width,
Color& color)
{
if (task.has (_name))
{
if (_style == "active")
{
if (!task.has("end")) if (!task.has("end"))
renderStringRight (lines, width, color, Context::getContext ().config.get ("active.indicator")); renderStringRight(lines, width, color,
} Context::getContext().config.get("active.indicator"));
else } else
ColumnTypeDate::render(lines, task, width, color); ColumnTypeDate::render(lines, task, width, color);
} }
} }

View File

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

View File

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

View File

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

View File

@@ -25,75 +25,61 @@
//////////////////////////////////////////////////////////////////////////////// ////////////////////////////////////////////////////////////////////////////////
#include <cmake.h> #include <cmake.h>
// cmake.h include header must come first
#include <ColTags.h> #include <ColTags.h>
#include <algorithm>
#include <Context.h> #include <Context.h>
#include <Eval.h> #include <Eval.h>
#include <Variant.h>
#include <Filter.h> #include <Filter.h>
#include <shared.h> #include <Variant.h>
#include <format.h> #include <format.h>
#include <utf8.h>
#include <main.h> #include <main.h>
#include <shared.h>
#include <utf8.h>
#include <algorithm>
//////////////////////////////////////////////////////////////////////////////// ////////////////////////////////////////////////////////////////////////////////
ColumnTags::ColumnTags () ColumnTags::ColumnTags() {
{
_name = "tags"; _name = "tags";
_style = "list"; _style = "list";
_label = "Tags"; _label = "Tags";
_styles = {"list", "indicator", "count"}; _styles = {"list", "indicator", "count"};
_examples = {"home @chore next", _examples = {"home @chore next", Context::getContext().config.get("tag.indicator"), "[2]"};
Context::getContext ().config.get ("tag.indicator"),
"[2]"};
_hyphenate = false; _hyphenate = false;
} }
//////////////////////////////////////////////////////////////////////////////// ////////////////////////////////////////////////////////////////////////////////
// Overriden so that style <----> label are linked. // Overriden so that style <----> label are linked.
// Note that you can not determine which gets called first. // 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); Column::setStyle(value);
if (_style == "indicator" && if (_style == "indicator" && _label == "Tags")
_label == "Tags")
_label = _label.substr(0, Context::getContext().config.get("tag.indicator").length()); _label = _label.substr(0, Context::getContext().config.get("tag.indicator").length());
else if (_style == "count" && else if (_style == "count" && _label == "Tags")
_label == "Tags")
_label = "Tag"; _label = "Tag";
} }
//////////////////////////////////////////////////////////////////////////////// ////////////////////////////////////////////////////////////////////////////////
// Set the minimum and maximum widths for the value. // 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; minimum = maximum = 0;
if (task.has (_name)) if (task.has(_name)) {
{ if (_style == "indicator") {
if (_style == "indicator")
{
minimum = maximum = utf8_width(Context::getContext().config.get("tag.indicator")); minimum = maximum = utf8_width(Context::getContext().config.get("tag.indicator"));
} } else if (_style == "count") {
else if (_style == "count")
{
minimum = maximum = 3; minimum = maximum = 3;
} } else if (_style == "default" || _style == "list") {
else if (_style == "default" ||
_style == "list")
{
std::string tags = task.get(_name); std::string tags = task.get(_name);
// Find the widest tag. // Find the widest tag.
if (tags.find (',') != std::string::npos) if (tags.find(',') != std::string::npos) {
{
auto all = split(tags, ','); auto all = split(tags, ',');
for (const auto& tag : all) for (const auto& tag : all) {
{
auto length = utf8_width(tag); auto length = utf8_width(tag);
if (length > minimum) if (length > minimum) minimum = length;
minimum = length;
} }
maximum = utf8_width(tags); maximum = utf8_width(tags);
@@ -107,46 +93,30 @@ void ColumnTags::measure (Task& task, unsigned int& minimum, unsigned int& maxim
} }
//////////////////////////////////////////////////////////////////////////////// ////////////////////////////////////////////////////////////////////////////////
void ColumnTags::render ( void ColumnTags::render(std::vector<std::string>& lines, Task& task, int width, Color& color) {
std::vector <std::string>& lines,
Task& task,
int width,
Color& color)
{
auto all = task.getTags(); auto all = task.getTags();
if (all.size() > 0) if (all.size() > 0) {
{ if (_style == "default" || _style == "list") {
if (_style == "default" || if (all.size() > 1) {
_style == "list")
{
if (all.size () > 1)
{
std::sort(all.begin(), all.end()); std::sort(all.begin(), all.end());
auto tags = join(" ", all); auto tags = join(" ", all);
all.clear(); all.clear();
wrapText(all, tags, width, _hyphenate); wrapText(all, tags, width, _hyphenate);
for (const auto& i : all) for (const auto& i : all) renderStringLeft(lines, width, color, i);
renderStringLeft (lines, width, color, i); } else
}
else
renderStringLeft(lines, width, color, all[0]); renderStringLeft(lines, width, color, all[0]);
} } else if (_style == "indicator") {
else if (_style == "indicator")
{
renderStringRight(lines, width, color, Context::getContext().config.get("tag.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())) + ']'); 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 label = " MODIFICATION ";
std::string commasep; std::string commasep;
std::vector<std::string> tags; std::vector<std::string> tags;
@@ -155,9 +125,7 @@ void ColumnTags::modify (Task& task, const std::string& value)
Lexer lexer(value); Lexer lexer(value);
std::string domRef; std::string domRef;
Lexer::Type type; Lexer::Type type;
if (lexer.token (domRef, type) && if (lexer.token(domRef, type) && type == Lexer::Type::dom) {
type == Lexer::Type::dom)
{
Eval e; Eval e;
e.addSource(domSource); e.addSource(domSource);
@@ -168,8 +136,7 @@ void ColumnTags::modify (Task& task, const std::string& value)
commasep = (std::string)value; commasep = (std::string)value;
} }
for (auto& tag : split (commasep, ',')) for (auto& tag : split(commasep, ',')) {
{
tags.push_back((std::string)tag); tags.push_back((std::string)tag);
Context::getContext().debug(label + "tags <-- '" + tag + '\''); Context::getContext().debug(label + "tags <-- '" + tag + '\'');

View File

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

View File

@@ -25,12 +25,13 @@
//////////////////////////////////////////////////////////////////////////////// ////////////////////////////////////////////////////////////////////////////////
#include <cmake.h> #include <cmake.h>
// cmake.h include header must come first
#include <ColTemplate.h> #include <ColTemplate.h>
#include <format.h> #include <format.h>
//////////////////////////////////////////////////////////////////////////////// ////////////////////////////////////////////////////////////////////////////////
ColumnTemplate::ColumnTemplate () ColumnTemplate::ColumnTemplate() {
{
_name = "template"; _name = "template";
_style = "long"; _style = "long";
_label = "Template task"; _label = "Template task";
@@ -41,29 +42,22 @@ ColumnTemplate::ColumnTemplate ()
//////////////////////////////////////////////////////////////////////////////// ////////////////////////////////////////////////////////////////////////////////
// Set the minimum and maximum widths for the value. // 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; minimum = maximum = 0;
if (task.has (_name)) if (task.has(_name)) {
{ if (_style == "default" || _style == "long")
if (_style == "default" || _style == "long") minimum = maximum = 36; minimum = maximum = 36;
else if (_style == "short") minimum = maximum = 8; else if (_style == "short")
minimum = maximum = 8;
} }
} }
//////////////////////////////////////////////////////////////////////////////// ////////////////////////////////////////////////////////////////////////////////
void ColumnTemplate::render ( void ColumnTemplate::render(std::vector<std::string>& lines, Task& task, int width, Color& color) {
std::vector <std::string>& lines, if (task.has(_name)) {
Task& task,
int width,
Color& color)
{
if (task.has (_name))
{
// f30cb9c3-3fc0-483f-bfb2-3bf134f00694 default // f30cb9c3-3fc0-483f-bfb2-3bf134f00694 default
// f30cb9c3 short // f30cb9c3 short
if (_style == "default" || if (_style == "default" || _style == "long")
_style == "long")
renderStringLeft(lines, width, color, task.get(_name)); renderStringLeft(lines, width, color, task.get(_name));
else if (_style == "short") else if (_style == "short")

View File

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

View File

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

View File

@@ -27,14 +27,14 @@
#ifndef INCLUDED_COLTYPEDATE #ifndef INCLUDED_COLTYPEDATE
#define INCLUDED_COLTYPEDATE #define INCLUDED_COLTYPEDATE
#include <vector>
#include <string>
#include <Column.h>
#include <Color.h> #include <Color.h>
#include <Column.h>
#include <Task.h> #include <Task.h>
class ColumnTypeDate : public Column #include <string>
{ #include <vector>
class ColumnTypeDate : public Column {
public: public:
ColumnTypeDate(); ColumnTypeDate();
virtual void measure(Task&, unsigned int&, unsigned int&); virtual void measure(Task&, unsigned int&, unsigned int&);

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