add initial bulk run from pre-commit over all files
This commit is contained in:
1
.github/workflows/release-check.yaml
vendored
1
.github/workflows/release-check.yaml
vendored
@@ -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
|
||||||
|
|
||||||
|
|||||||
@@ -2785,4 +2785,3 @@ regular usage to determine which features were needed or unnecessary.]
|
|||||||
- Usage.
|
- Usage.
|
||||||
|
|
||||||
------ start -----------------------------------
|
------ start -----------------------------------
|
||||||
|
|
||||||
|
|||||||
1
LICENSE
1
LICENSE
@@ -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.
|
||||||
|
|
||||||
|
|||||||
@@ -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
|
||||||
|
|
||||||
|
|||||||
@@ -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.
|
||||||
|
|
||||||
|
|||||||
@@ -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
|
||||||
|
|
||||||
|
|||||||
@@ -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
|
||||||
|
|
||||||
|
|||||||
@@ -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
|
||||||
|
|
||||||
|
|||||||
@@ -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
|
||||||
|
|
||||||
|
|||||||
@@ -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
|
||||||
|
|
||||||
|
|||||||
@@ -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
|
||||||
|
|
||||||
|
|||||||
@@ -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
|
||||||
|
|
||||||
|
|||||||
@@ -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
|
||||||
|
|
||||||
|
|||||||
@@ -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
|
||||||
|
|
||||||
|
|||||||
@@ -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
|
||||||
|
|
||||||
|
|||||||
@@ -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
|
||||||
|
|
||||||
|
|||||||
@@ -98,4 +98,3 @@ color.sync.rejected=
|
|||||||
# Command: undo
|
# Command: undo
|
||||||
color.undo.after=
|
color.undo.after=
|
||||||
color.undo.before=
|
color.undo.before=
|
||||||
|
|
||||||
|
|||||||
@@ -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
|
||||||
|
|
||||||
|
|||||||
@@ -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
|
||||||
|
|
||||||
|
|||||||
@@ -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
|
||||||
|
|
||||||
|
|||||||
@@ -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.
|
||||||
|
|
||||||
|
|||||||
@@ -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
|
||||||
|
|
||||||
|
|||||||
@@ -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)
|
||||||
|
|
||||||
|
|
||||||
|
|||||||
@@ -29,10 +29,13 @@ 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,
|
||||||
info = i[0:3] + ({k:v for k, v in (i.split(":") for i in i[-1].split())},)
|
input,
|
||||||
|
re.MULTILINE,
|
||||||
|
):
|
||||||
|
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)
|
||||||
return tests
|
return tests
|
||||||
@@ -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)
|
||||||
|
|||||||
@@ -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
|
||||||
|
|
||||||
|
|||||||
@@ -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)
|
||||||
|
|
||||||
|
|||||||
@@ -221,4 +221,3 @@ if (open my $fh, '>:utf8', $file)
|
|||||||
exit 0;
|
exit 0;
|
||||||
|
|
||||||
################################################################################
|
################################################################################
|
||||||
|
|
||||||
|
|||||||
@@ -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.
|
||||||
|
|
||||||
|
|||||||
@@ -32,4 +32,3 @@ fi
|
|||||||
|
|
||||||
echo Shadow file $SHADOW_FILE updated.
|
echo Shadow file $SHADOW_FILE updated.
|
||||||
exit 0
|
exit 0
|
||||||
|
|
||||||
|
|||||||
@@ -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
|
||||||
|
|
||||||
|
|||||||
2031
src/CLI2.cpp
2031
src/CLI2.cpp
File diff suppressed because it is too large
Load Diff
158
src/CLI2.h
158
src/CLI2.h
@@ -26,100 +26,98 @@
|
|||||||
#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&);
|
A2& operator=(const A2&);
|
||||||
A2& operator= (const A2&);
|
bool hasTag(const std::string&) const;
|
||||||
bool hasTag (const std::string&) const;
|
void tag(const std::string&);
|
||||||
void tag (const std::string&);
|
void unTag(const std::string&);
|
||||||
void unTag (const std::string&);
|
void attribute(const std::string&, const std::string&);
|
||||||
void attribute (const std::string&, const std::string&);
|
const std::string attribute(const std::string&) const;
|
||||||
const std::string attribute (const std::string&) const;
|
const std::string getToken() const;
|
||||||
const std::string getToken () const;
|
const std::string dump() const;
|
||||||
const std::string dump () const;
|
void decompose();
|
||||||
void decompose ();
|
|
||||||
|
|
||||||
public:
|
public:
|
||||||
Lexer::Type _lextype {Lexer::Type::word};
|
Lexer::Type _lextype{Lexer::Type::word};
|
||||||
std::vector <std::string> _tags {};
|
std::vector<std::string> _tags{};
|
||||||
std::map <std::string, std::string> _attributes {};
|
std::map<std::string, std::string> _attributes{};
|
||||||
};
|
};
|
||||||
|
|
||||||
// Represents the command line.
|
// Represents the command line.
|
||||||
class CLI2
|
class CLI2 {
|
||||||
{
|
public:
|
||||||
public:
|
|
||||||
static int minimumMatchLength;
|
static int minimumMatchLength;
|
||||||
|
|
||||||
static bool getOverride (int, const char**, File&);
|
static bool getOverride(int, const char**, File&);
|
||||||
static bool getDataLocation (int, const char**, Path&);
|
static bool getDataLocation(int, const char**, Path&);
|
||||||
static void applyOverrides (int, const char**);
|
static void applyOverrides(int, const char**);
|
||||||
|
|
||||||
public:
|
public:
|
||||||
CLI2 () = default;
|
CLI2() = default;
|
||||||
void alias (const std::string&, const std::string&);
|
void alias(const std::string&, const std::string&);
|
||||||
void entity (const std::string&, const std::string&);
|
void entity(const std::string&, const std::string&);
|
||||||
|
|
||||||
void add (const std::string&);
|
void add(const std::string&);
|
||||||
void add (const std::vector <std::string>&, int offset = 0);
|
void add(const std::vector<std::string>&, int offset = 0);
|
||||||
void analyze ();
|
void analyze();
|
||||||
void addFilter (const std::string& arg);
|
void addFilter(const std::string& arg);
|
||||||
void addModifications (const std::string& arg);
|
void addModifications(const std::string& arg);
|
||||||
void addContext (bool readable, bool writeable);
|
void addContext(bool readable, bool writeable);
|
||||||
void prepareFilter ();
|
void prepareFilter();
|
||||||
const std::vector <std::string> getWords ();
|
const std::vector<std::string> getWords();
|
||||||
const std::vector <A2> getMiscellaneous ();
|
const std::vector<A2> getMiscellaneous();
|
||||||
bool canonicalize (std::string&, const std::string&, const std::string&);
|
bool canonicalize(std::string&, const std::string&, const std::string&);
|
||||||
std::string getBinary () const;
|
std::string getBinary() const;
|
||||||
std::string getCommand (bool canonical = true) const;
|
std::string getCommand(bool canonical = true) const;
|
||||||
const std::string dump (const std::string& title = "CLI2 Parser") const;
|
const std::string dump(const std::string& title = "CLI2 Parser") const;
|
||||||
|
|
||||||
private:
|
private:
|
||||||
void handleArg0 ();
|
void handleArg0();
|
||||||
void lexArguments ();
|
void lexArguments();
|
||||||
void demotion ();
|
void demotion();
|
||||||
void aliasExpansion ();
|
void aliasExpansion();
|
||||||
void canonicalizeNames ();
|
void canonicalizeNames();
|
||||||
void categorizeArgs ();
|
void categorizeArgs();
|
||||||
void parenthesizeOriginalFilter ();
|
void parenthesizeOriginalFilter();
|
||||||
bool findCommand ();
|
bool findCommand();
|
||||||
bool exactMatch (const std::string&, const std::string&) const;
|
bool exactMatch(const std::string&, const std::string&) const;
|
||||||
void desugarFilterTags ();
|
void desugarFilterTags();
|
||||||
void findStrayModifications ();
|
void findStrayModifications();
|
||||||
void desugarFilterAttributes ();
|
void desugarFilterAttributes();
|
||||||
void desugarFilterPatterns ();
|
void desugarFilterPatterns();
|
||||||
void findIDs ();
|
void findIDs();
|
||||||
void findUUIDs ();
|
void findUUIDs();
|
||||||
void insertIDExpr ();
|
void insertIDExpr();
|
||||||
void lexFilterArgs ();
|
void lexFilterArgs();
|
||||||
bool isEmptyParenExpression (std::vector<A2>::iterator it, bool forward = true) const;
|
bool isEmptyParenExpression(std::vector<A2>::iterator it, bool forward = true) const;
|
||||||
void desugarFilterPlainArgs ();
|
void desugarFilterPlainArgs();
|
||||||
void insertJunctions ();
|
void insertJunctions();
|
||||||
void defaultCommand ();
|
void defaultCommand();
|
||||||
std::vector <A2> lexExpression (const std::string&);
|
std::vector<A2> lexExpression(const std::string&);
|
||||||
|
|
||||||
public:
|
public:
|
||||||
std::multimap <std::string, std::string> _entities {};
|
std::multimap<std::string, std::string> _entities{};
|
||||||
std::map <std::string, std::string> _aliases {};
|
std::map<std::string, std::string> _aliases{};
|
||||||
std::unordered_map <int, std::string> _canonical_cache {};
|
std::unordered_map<int, std::string> _canonical_cache{};
|
||||||
std::vector <A2> _original_args {};
|
std::vector<A2> _original_args{};
|
||||||
std::vector <A2> _args {};
|
std::vector<A2> _args{};
|
||||||
|
|
||||||
std::vector <std::pair <std::string, std::string>> _id_ranges {};
|
std::vector<std::pair<std::string, std::string>> _id_ranges{};
|
||||||
std::vector <std::string> _uuid_list {};
|
std::vector<std::string> _uuid_list{};
|
||||||
std::string _command {""};
|
std::string _command{""};
|
||||||
bool _context_added {false};
|
bool _context_added{false};
|
||||||
};
|
};
|
||||||
|
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
|
|||||||
1594
src/Context.cpp
1594
src/Context.cpp
File diff suppressed because it is too large
Load Diff
148
src/Context.h
148
src/Context.h
@@ -27,111 +27,111 @@
|
|||||||
#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
|
|
||||||
|
|
||||||
Context (const Context&);
|
Context(const Context &);
|
||||||
Context& operator= (const Context&);
|
Context &operator=(const Context &);
|
||||||
|
|
||||||
static Context& getContext ();
|
static Context &getContext();
|
||||||
static void setContext (Context*);
|
static void setContext(Context *);
|
||||||
|
|
||||||
int initialize (int, const char**); // all startup
|
int initialize(int, const char **); // all startup
|
||||||
int run ();
|
int run();
|
||||||
int dispatch (std::string&); // command handler dispatch
|
int dispatch(std::string &); // command handler dispatch
|
||||||
|
|
||||||
int getWidth (); // determine terminal width
|
int getWidth(); // determine terminal width
|
||||||
int getHeight (); // determine terminal height
|
int getHeight(); // determine terminal height
|
||||||
|
|
||||||
std::string getTaskContext (const std::string&, std::string, bool fallback=true);
|
std::string getTaskContext(const std::string &, std::string, bool fallback = true);
|
||||||
|
|
||||||
const std::vector <std::string> getColumns () const;
|
const std::vector<std::string> getColumns() const;
|
||||||
void getLimits (int&, int&);
|
void getLimits(int &, int &);
|
||||||
|
|
||||||
bool color (); // TTY or <other>?
|
bool color(); // TTY or <other>?
|
||||||
bool verbose (const std::string&); // Verbosity control
|
bool verbose(const std::string &); // Verbosity control
|
||||||
|
|
||||||
void header (const std::string&); // Header message sink
|
void header(const std::string &); // Header message sink
|
||||||
void footnote (const std::string&); // Footnote message sink
|
void footnote(const std::string &); // Footnote message sink
|
||||||
void debug (const std::string&); // Debug message sink
|
void debug(const std::string &); // Debug message sink
|
||||||
void error (const std::string&); // Error message sink - non-maskable
|
void error(const std::string &); // Error message sink - non-maskable
|
||||||
|
|
||||||
void decomposeSortField (const std::string&, std::string&, bool&, bool&);
|
void decomposeSortField(const std::string &, std::string &, bool &, bool &);
|
||||||
void debugTiming (const std::string&, const Timer&);
|
void debugTiming(const std::string &, const Timer &);
|
||||||
|
|
||||||
CurrentTask withCurrentTask (const Task *);
|
CurrentTask withCurrentTask(const Task *);
|
||||||
friend class CurrentTask;
|
friend class CurrentTask;
|
||||||
|
|
||||||
private:
|
private:
|
||||||
void staticInitialization ();
|
void staticInitialization();
|
||||||
void createDefaultConfig ();
|
void createDefaultConfig();
|
||||||
void updateXtermTitle ();
|
void updateXtermTitle();
|
||||||
void updateVerbosity ();
|
void updateVerbosity();
|
||||||
void loadAliases ();
|
void loadAliases();
|
||||||
void propagateDebug ();
|
void propagateDebug();
|
||||||
|
|
||||||
static Context* context;
|
static Context *context;
|
||||||
|
|
||||||
public:
|
public:
|
||||||
CLI2 cli2 {};
|
CLI2 cli2{};
|
||||||
std::string home_dir {};
|
std::string home_dir{};
|
||||||
File rc_file {"~/.taskrc"};
|
File rc_file{"~/.taskrc"};
|
||||||
Path data_dir {"~/.task"};
|
Path data_dir{"~/.task"};
|
||||||
Configuration config {};
|
Configuration config{};
|
||||||
TDB2 tdb2 {};
|
TDB2 tdb2{};
|
||||||
Hooks hooks {};
|
Hooks hooks{};
|
||||||
bool determine_color_use {true};
|
bool determine_color_use{true};
|
||||||
bool use_color {true};
|
bool use_color{true};
|
||||||
bool verbosity_legacy {false};
|
bool verbosity_legacy{false};
|
||||||
std::set <std::string> verbosity {};
|
std::set<std::string> verbosity{};
|
||||||
std::vector <std::string> headers {};
|
std::vector<std::string> headers{};
|
||||||
std::vector <std::string> footnotes {};
|
std::vector<std::string> footnotes{};
|
||||||
std::vector <std::string> errors {};
|
std::vector<std::string> errors{};
|
||||||
std::vector <std::string> debugMessages {};
|
std::vector<std::string> debugMessages{};
|
||||||
std::map <std::string, Command*> commands {};
|
std::map<std::string, Command *> commands{};
|
||||||
std::map <std::string, Column*> columns {};
|
std::map<std::string, Column *> columns{};
|
||||||
int terminal_width {0};
|
int terminal_width{0};
|
||||||
int terminal_height {0};
|
int terminal_height{0};
|
||||||
|
|
||||||
Timer timer_total {};
|
Timer timer_total{};
|
||||||
long time_total_us {0};
|
long time_total_us{0};
|
||||||
long time_init_us {0};
|
long time_init_us{0};
|
||||||
long time_load_us {0};
|
long time_load_us{0};
|
||||||
long time_gc_us {0};
|
long time_gc_us{0};
|
||||||
long time_filter_us {0};
|
long time_filter_us{0};
|
||||||
long time_commit_us {0};
|
long time_commit_us{0};
|
||||||
long time_sort_us {0};
|
long time_sort_us{0};
|
||||||
long time_render_us {0};
|
long time_render_us{0};
|
||||||
long time_hooks_us {0};
|
long time_hooks_us{0};
|
||||||
|
|
||||||
// the current task for DOM references, or NULL if there is no task
|
// the current task for DOM references, or NULL if there is no task
|
||||||
const Task * currentTask {NULL};
|
const Task *currentTask{NULL};
|
||||||
};
|
};
|
||||||
|
|
||||||
////////////////////////////////////////////////////////////////////////////////
|
////////////////////////////////////////////////////////////////////////////////
|
||||||
// CurrentTask resets Context::currentTask to previous context task on destruction; this ensures
|
// CurrentTask resets Context::currentTask to previous context task on destruction; this ensures
|
||||||
// that this context value is restored when exiting the scope where the context was applied.
|
// that this context value is restored when exiting the scope where the context was applied.
|
||||||
class CurrentTask {
|
class CurrentTask {
|
||||||
public:
|
public:
|
||||||
~CurrentTask();
|
~CurrentTask();
|
||||||
|
|
||||||
private:
|
private:
|
||||||
CurrentTask(Context &context, const Task *previous);
|
CurrentTask(Context &context, const Task *previous);
|
||||||
|
|
||||||
Context &context;
|
Context &context;
|
||||||
|
|||||||
545
src/DOM.cpp
545
src/DOM.cpp
@@ -27,19 +27,20 @@
|
|||||||
#include <cmake.h>
|
#include <cmake.h>
|
||||||
// cmake.h include header must come first
|
// cmake.h include header must come first
|
||||||
|
|
||||||
#include <DOM.h>
|
|
||||||
#include <sstream>
|
|
||||||
#include <map>
|
|
||||||
#include <stdlib.h>
|
|
||||||
#include <Variant.h>
|
|
||||||
#include <Lexer.h>
|
|
||||||
#include <Context.h>
|
#include <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:
|
||||||
//
|
//
|
||||||
@@ -62,23 +63,18 @@
|
|||||||
// 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 c = Context::getContext().config.find(key);
|
||||||
auto key = name.substr (3);
|
if (c != Context::getContext().config.end()) {
|
||||||
auto c = Context::getContext ().config.find (key);
|
value = Variant(c->second);
|
||||||
if (c != Context::getContext ().config.end ())
|
|
||||||
{
|
|
||||||
value = Variant (c->second);
|
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -86,55 +82,41 @@ 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") {
|
||||||
{
|
value = Variant(0);
|
||||||
if (name == "tw.syncneeded")
|
if (Context::getContext().tdb2.num_local_changes() > 0) {
|
||||||
{
|
value = Variant(1);
|
||||||
value = Variant (0);
|
|
||||||
if (Context::getContext ().tdb2.num_local_changes () > 0) {
|
|
||||||
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
|
||||||
{
|
? Context::getContext().terminal_width
|
||||||
value = Variant (static_cast<int> (Context::getContext ().terminal_width
|
: Context::getContext().getWidth()));
|
||||||
? Context::getContext ().terminal_width
|
|
||||||
: 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
|
||||||
{
|
? Context::getContext().terminal_height
|
||||||
value = Variant (static_cast<int> (Context::getContext ().terminal_height
|
: Context::getContext().getHeight()));
|
||||||
? Context::getContext ().terminal_height
|
|
||||||
: 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;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -142,40 +124,29 @@ 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") {
|
||||||
{
|
value = Variant(Context::getContext().cli2.getBinary());
|
||||||
if (name == "context.program")
|
|
||||||
{
|
|
||||||
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
|
||||||
{
|
? Context::getContext().terminal_width
|
||||||
value = Variant (static_cast<int> (Context::getContext ().terminal_width
|
: Context::getContext().getWidth()));
|
||||||
? Context::getContext ().terminal_width
|
|
||||||
: 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
|
||||||
{
|
? Context::getContext().terminal_height
|
||||||
value = Variant (static_cast<int> (Context::getContext ().terminal_height
|
: Context::getContext().getHeight()));
|
||||||
? Context::getContext ().terminal_height
|
|
||||||
: Context::getContext ().getHeight ()));
|
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -183,20 +154,16 @@ 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;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -239,210 +206,192 @@ bool getDOM (const std::string& name, Variant& value)
|
|||||||
//
|
//
|
||||||
// If task is NULL, then the contextual task will be determined from the DOM
|
// 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;
|
||||||
}
|
}
|
||||||
|
|
||||||
// split name on '.'
|
// split name on '.'
|
||||||
auto elements = split (name, '.');
|
auto elements = split(name, '.');
|
||||||
Task loaded_task;
|
Task loaded_task;
|
||||||
|
|
||||||
// decide whether the reference is going to be the passed
|
// decide whether the reference is going to be the passed
|
||||||
// "task" or whether it's going to be a newly loaded task (if id/uuid was
|
// "task" or whether it's going to be a newly loaded task (if id/uuid was
|
||||||
// given).
|
// given).
|
||||||
const Task* ref = task;
|
const Task* ref = task;
|
||||||
Lexer lexer (elements[0]);
|
Lexer lexer(elements[0]);
|
||||||
std::string token;
|
std::string token;
|
||||||
Lexer::Type type;
|
Lexer::Type type;
|
||||||
|
|
||||||
// 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 &&
|
auto id = strtol(token.c_str(), nullptr, 10);
|
||||||
token.find ('.') == std::string::npos)
|
if (id && (!task || id != task->id)) {
|
||||||
{
|
if (Context::getContext().tdb2.get(id, loaded_task)) reloaded = true;
|
||||||
auto id = strtol (token.c_str (), nullptr, 10);
|
|
||||||
if (id && (!task || id != task->id))
|
|
||||||
{
|
|
||||||
if (Context::getContext ().tdb2.get (id, loaded_task))
|
|
||||||
reloaded = true;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// Eat elements[0]/ID.
|
// 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;
|
||||||
std::string::size_type cursor = 0;
|
std::string::size_type cursor = 0;
|
||||||
if (iso.parse (period, cursor))
|
if (iso.parse(period, cursor))
|
||||||
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));
|
||||||
|
|
||||||
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") {
|
||||||
if (elements[1] == "year") { value = Variant (static_cast<int> (date.year ())); return true; }
|
value = Variant(static_cast<int>(date.year()));
|
||||||
else if (elements[1] == "month") { value = Variant (static_cast<int> (date.month ())); return true; }
|
return true;
|
||||||
else if (elements[1] == "day") { value = Variant (static_cast<int> (date.day ())); return true; }
|
} else if (elements[1] == "month") {
|
||||||
else if (elements[1] == "week") { value = Variant (static_cast<int> (date.week ())); return true; }
|
value = Variant(static_cast<int>(date.month()));
|
||||||
else if (elements[1] == "weekday") { value = Variant (static_cast<int> (date.dayOfWeek ())); return true; }
|
return true;
|
||||||
else if (elements[1] == "julian") { value = Variant (static_cast<int> (date.dayOfYear ())); return true; }
|
} else if (elements[1] == "day") {
|
||||||
else if (elements[1] == "hour") { value = Variant (static_cast<int> (date.hour ())); return true; }
|
value = Variant(static_cast<int>(date.day()));
|
||||||
else if (elements[1] == "minute") { value = Variant (static_cast<int> (date.minute ())); return true; }
|
return true;
|
||||||
else if (elements[1] == "second") { value = Variant (static_cast<int> (date.second ())); 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;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
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
|
||||||
@@ -452,22 +401,41 @@ bool getDOM (const std::string& name, const Task* task, Variant& value)
|
|||||||
// <annotations>.<N>.entry.hour
|
// <annotations>.<N>.entry.hour
|
||||||
// <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;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// Delegate to the context-free version of DOM::get.
|
// Delegate to the context-free version of DOM::get.
|
||||||
return getDOM (name, value);
|
return getDOM(name, value);
|
||||||
}
|
}
|
||||||
|
|
||||||
////////////////////////////////////////////////////////////////////////////////
|
////////////////////////////////////////////////////////////////////////////////
|
||||||
@@ -513,41 +481,28 @@ bool getDOM (const std::string& name, const Task* task, Variant& value)
|
|||||||
// This makes the DOM class a reusible object.
|
// This makes the DOM class a reusible object.
|
||||||
|
|
||||||
////////////////////////////////////////////////////////////////////////////////
|
////////////////////////////////////////////////////////////////////////////////
|
||||||
DOM::~DOM ()
|
DOM::~DOM() { delete _node; }
|
||||||
{
|
|
||||||
delete _node;
|
////////////////////////////////////////////////////////////////////////////////
|
||||||
|
void DOM::addSource(const std::string& reference, bool (*provider)(const std::string&, Variant&)) {
|
||||||
|
if (_node == nullptr) _node = new DOM::Node();
|
||||||
|
|
||||||
|
_node->addSource(reference, provider);
|
||||||
}
|
}
|
||||||
|
|
||||||
////////////////////////////////////////////////////////////////////////////////
|
////////////////////////////////////////////////////////////////////////////////
|
||||||
void DOM::addSource (
|
bool DOM::valid(const std::string& reference) const {
|
||||||
const std::string& reference,
|
return _node && _node->find(reference) != nullptr;
|
||||||
bool (*provider)(const std::string&, Variant&))
|
|
||||||
{
|
|
||||||
if (_node == nullptr)
|
|
||||||
_node = new DOM::Node ();
|
|
||||||
|
|
||||||
_node->addSource (reference, provider);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
////////////////////////////////////////////////////////////////////////////////
|
////////////////////////////////////////////////////////////////////////////////
|
||||||
bool DOM::valid (const std::string& reference) const
|
Variant DOM::get(const std::string& reference) const {
|
||||||
{
|
Variant v("");
|
||||||
return _node && _node->find (reference) != nullptr;
|
|
||||||
}
|
|
||||||
|
|
||||||
////////////////////////////////////////////////////////////////////////////////
|
if (_node) {
|
||||||
Variant DOM::get (const std::string& reference) const
|
auto node = _node->find(reference);
|
||||||
{
|
if (node != nullptr && node->_provider != nullptr) {
|
||||||
Variant v ("");
|
if (node->_provider(reference, v)) return v;
|
||||||
|
|
||||||
if (_node)
|
|
||||||
{
|
|
||||||
auto node = _node->find (reference);
|
|
||||||
if (node != nullptr &&
|
|
||||||
node->_provider != nullptr)
|
|
||||||
{
|
|
||||||
if (node->_provider (reference, v))
|
|
||||||
return v;
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -555,60 +510,47 @@ Variant DOM::get (const std::string& reference) const
|
|||||||
}
|
}
|
||||||
|
|
||||||
////////////////////////////////////////////////////////////////////////////////
|
////////////////////////////////////////////////////////////////////////////////
|
||||||
int DOM::count () const
|
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);
|
||||||
cursor = branch;
|
cursor = branch;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -618,85 +560,66 @@ 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 += branch->count();
|
||||||
++total;
|
|
||||||
total += branch->count ();
|
|
||||||
}
|
}
|
||||||
|
|
||||||
return total;
|
return total;
|
||||||
}
|
}
|
||||||
|
|
||||||
////////////////////////////////////////////////////////////////////////////////
|
////////////////////////////////////////////////////////////////////////////////
|
||||||
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.
|
||||||
out << std::string (depth * 2, ' ');
|
out << std::string(depth * 2, ' ');
|
||||||
|
|
||||||
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();
|
||||||
}
|
}
|
||||||
|
|
||||||
////////////////////////////////////////////////////////////////////////////////
|
////////////////////////////////////////////////////////////////////////////////
|
||||||
|
|||||||
69
src/DOM.h
69
src/DOM.h
@@ -27,49 +27,48 @@
|
|||||||
#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&));
|
bool valid(const std::string&) const;
|
||||||
bool valid (const std::string&) const;
|
/*
|
||||||
/*
|
// TODO Task object should register a generic provider.
|
||||||
// TODO Task object should register a generic provider.
|
Variant get (const Task&, const std::string&) const;
|
||||||
Variant get (const Task&, const std::string&) const;
|
*/
|
||||||
*/
|
Variant get(const std::string&) const;
|
||||||
Variant get (const std::string&) const;
|
int count() const;
|
||||||
int count () const;
|
static std::vector<std::string> decomposeReference(const std::string&);
|
||||||
static std::vector <std::string> decomposeReference (const std::string&);
|
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&));
|
bool valid(const std::string&) const;
|
||||||
bool valid (const std::string&) const;
|
const DOM::Node* find(const std::string&) const;
|
||||||
const DOM::Node* find (const std::string&) const;
|
int count() const;
|
||||||
int count () const;
|
std::string dumpNode(const DOM::Node*, int) const;
|
||||||
std::string dumpNode (const DOM::Node*, int) const;
|
std::string dump() const;
|
||||||
std::string dump () const;
|
|
||||||
|
|
||||||
public:
|
public:
|
||||||
std::string _name {"Unknown"};
|
std::string _name{"Unknown"};
|
||||||
bool (*_provider)(const std::string&, Variant&) {nullptr};
|
bool (*_provider)(const std::string&, Variant&){nullptr};
|
||||||
std::vector <DOM::Node*> _branches {};
|
std::vector<DOM::Node*> _branches{};
|
||||||
};
|
};
|
||||||
|
|
||||||
private:
|
private:
|
||||||
DOM::Node* _node {nullptr};
|
DOM::Node* _node{nullptr};
|
||||||
};
|
};
|
||||||
|
|
||||||
#endif
|
#endif
|
||||||
|
|||||||
813
src/Eval.cpp
813
src/Eval.cpp
File diff suppressed because it is too large
Load Diff
71
src/Eval.h
71
src/Eval.h
@@ -27,50 +27,51 @@
|
|||||||
#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>
|
||||||
|
|
||||||
bool domSource (const std::string&, Variant&);
|
#include <string>
|
||||||
|
#include <vector>
|
||||||
|
|
||||||
class Eval
|
bool domSource(const std::string &, Variant &);
|
||||||
{
|
|
||||||
public:
|
|
||||||
Eval ();
|
|
||||||
|
|
||||||
void addSource (bool (*fn)(const std::string&, Variant&));
|
class Eval {
|
||||||
void evaluateInfixExpression (const std::string&, Variant&) const;
|
public:
|
||||||
void evaluatePostfixExpression (const std::string&, Variant&) const;
|
Eval();
|
||||||
void compileExpression (const std::vector <std::pair <std::string, Lexer::Type>>&);
|
|
||||||
void evaluateCompiledExpression (Variant&);
|
|
||||||
void debug (bool);
|
|
||||||
|
|
||||||
static std::vector <std::string> getOperators ();
|
void addSource(bool (*fn)(const std::string &, Variant &));
|
||||||
static std::vector <std::string> getBinaryOperators ();
|
void evaluateInfixExpression(const std::string &, Variant &) const;
|
||||||
|
void evaluatePostfixExpression(const std::string &, Variant &) const;
|
||||||
|
void compileExpression(const std::vector<std::pair<std::string, Lexer::Type>> &);
|
||||||
|
void evaluateCompiledExpression(Variant &);
|
||||||
|
void debug(bool);
|
||||||
|
|
||||||
private:
|
static std::vector<std::string> getOperators();
|
||||||
void evaluatePostfixStack (const std::vector <std::pair <std::string, Lexer::Type>>&, Variant&) const;
|
static std::vector<std::string> getBinaryOperators();
|
||||||
void infixToPostfix (std::vector <std::pair <std::string, Lexer::Type>>&) const;
|
|
||||||
void infixParse (std::vector <std::pair <std::string, Lexer::Type>>&) const;
|
|
||||||
bool parseLogical (std::vector <std::pair <std::string, Lexer::Type>>&, unsigned int &) const;
|
|
||||||
bool parseRegex (std::vector <std::pair <std::string, Lexer::Type>>&, unsigned int &) const;
|
|
||||||
bool parseEquality (std::vector <std::pair <std::string, Lexer::Type>>&, unsigned int &) const;
|
|
||||||
bool parseComparative (std::vector <std::pair <std::string, Lexer::Type>>&, unsigned int &) const;
|
|
||||||
bool parseArithmetic (std::vector <std::pair <std::string, Lexer::Type>>&, unsigned int &) const;
|
|
||||||
bool parseGeometric (std::vector <std::pair <std::string, Lexer::Type>>&, unsigned int &) const;
|
|
||||||
bool parseTag (std::vector <std::pair <std::string, Lexer::Type>>&, unsigned int &) const;
|
|
||||||
bool parseUnary (std::vector <std::pair <std::string, Lexer::Type>>&, unsigned int &) const;
|
|
||||||
bool parseExponent (std::vector <std::pair <std::string, Lexer::Type>>&, unsigned int &) const;
|
|
||||||
bool parsePrimitive (std::vector <std::pair <std::string, Lexer::Type>>&, unsigned int &) const;
|
|
||||||
bool identifyOperator (const std::string&, char&, unsigned int&, char&) const;
|
|
||||||
|
|
||||||
std::string dump (std::vector <std::pair <std::string, Lexer::Type>>&) const;
|
private:
|
||||||
|
void evaluatePostfixStack(const std::vector<std::pair<std::string, Lexer::Type>> &,
|
||||||
|
Variant &) const;
|
||||||
|
void infixToPostfix(std::vector<std::pair<std::string, Lexer::Type>> &) const;
|
||||||
|
void infixParse(std::vector<std::pair<std::string, Lexer::Type>> &) const;
|
||||||
|
bool parseLogical(std::vector<std::pair<std::string, Lexer::Type>> &, unsigned int &) const;
|
||||||
|
bool parseRegex(std::vector<std::pair<std::string, Lexer::Type>> &, unsigned int &) const;
|
||||||
|
bool parseEquality(std::vector<std::pair<std::string, Lexer::Type>> &, unsigned int &) const;
|
||||||
|
bool parseComparative(std::vector<std::pair<std::string, Lexer::Type>> &, unsigned int &) const;
|
||||||
|
bool parseArithmetic(std::vector<std::pair<std::string, Lexer::Type>> &, unsigned int &) const;
|
||||||
|
bool parseGeometric(std::vector<std::pair<std::string, Lexer::Type>> &, unsigned int &) const;
|
||||||
|
bool parseTag(std::vector<std::pair<std::string, Lexer::Type>> &, unsigned int &) const;
|
||||||
|
bool parseUnary(std::vector<std::pair<std::string, Lexer::Type>> &, unsigned int &) const;
|
||||||
|
bool parseExponent(std::vector<std::pair<std::string, Lexer::Type>> &, unsigned int &) const;
|
||||||
|
bool parsePrimitive(std::vector<std::pair<std::string, Lexer::Type>> &, unsigned int &) const;
|
||||||
|
bool identifyOperator(const std::string &, char &, unsigned int &, char &) const;
|
||||||
|
|
||||||
private:
|
std::string dump(std::vector<std::pair<std::string, Lexer::Type>> &) const;
|
||||||
std::vector <bool (*)(const std::string&, Variant&)> _sources {};
|
|
||||||
bool _debug {false};
|
private:
|
||||||
std::vector <std::pair <std::string, Lexer::Type>> _compiled {};
|
std::vector<bool (*)(const std::string &, Variant &)> _sources{};
|
||||||
|
bool _debug{false};
|
||||||
|
std::vector<std::pair<std::string, Lexer::Type>> _compiled{};
|
||||||
};
|
};
|
||||||
|
|
||||||
#endif
|
#endif
|
||||||
|
|||||||
249
src/Filter.cpp
249
src/Filter.cpp
@@ -27,145 +27,130 @@
|
|||||||
#include <cmake.h>
|
#include <cmake.h>
|
||||||
// cmake.h include header must come first
|
// cmake.h include header must come first
|
||||||
|
|
||||||
#include <Filter.h>
|
|
||||||
#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();
|
||||||
|
|
||||||
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);
|
|
||||||
|
|
||||||
if (precompiled.size ())
|
if (precompiled.size()) {
|
||||||
{
|
|
||||||
Eval eval;
|
Eval eval;
|
||||||
eval.addSource (domSource);
|
eval.addSource(domSource);
|
||||||
|
|
||||||
// Debug output from Eval during compilation is useful. During evaluation
|
// Debug output from Eval during compilation is useful. During evaluation
|
||||||
// it is mostly noise.
|
// it is mostly noise.
|
||||||
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(
|
||||||
Context::getContext ().time_filter_us += timer.total_us ();
|
format("Filtered {1} tasks --> {2} tasks [list subset]", _startCount, _endCount));
|
||||||
|
Context::getContext().time_filter_us += timer.total_us();
|
||||||
}
|
}
|
||||||
|
|
||||||
////////////////////////////////////////////////////////////////////////////////
|
////////////////////////////////////////////////////////////////////////////////
|
||||||
// Take the set of all tasks and filter into a subset.
|
// 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();
|
||||||
_startCount = (int) pending.size ();
|
_startCount = (int)pending.size();
|
||||||
|
|
||||||
Eval eval;
|
Eval eval;
|
||||||
eval.addSource (domSource);
|
eval.addSource(domSource);
|
||||||
|
|
||||||
// Debug output from Eval during compilation is useful. During evaluation
|
// Debug output from Eval during compilation is useful. During evaluation
|
||||||
// it is mostly noise.
|
// it is mostly noise.
|
||||||
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);
|
||||||
|
|
||||||
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;
|
||||||
output = Context::getContext ().tdb2.all_tasks ();
|
output = Context::getContext().tdb2.all_tasks();
|
||||||
Context::getContext ().time_filter_us -= pending_completed.total_us ();
|
Context::getContext().time_filter_us -= pending_completed.total_us();
|
||||||
}
|
}
|
||||||
|
|
||||||
_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,
|
||||||
Context::getContext ().time_filter_us += timer.total_us ();
|
_endCount, (shortcut ? "pending only" : "all tasks")));
|
||||||
|
Context::getContext().time_filter_us += timer.total_us();
|
||||||
}
|
}
|
||||||
|
|
||||||
////////////////////////////////////////////////////////////////////////////////
|
////////////////////////////////////////////////////////////////////////////////
|
||||||
bool Filter::hasFilter () const
|
bool Filter::hasFilter() const {
|
||||||
{
|
for (const auto& a : Context::getContext().cli2._args)
|
||||||
for (const auto& a : Context::getContext ().cli2._args)
|
if (a.hasTag("FILTER")) return true;
|
||||||
if (a.hasTag ("FILTER"))
|
|
||||||
return true;
|
|
||||||
|
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
@@ -174,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
|
||||||
@@ -186,61 +169,51 @@ bool Filter::pendingOnly () const
|
|||||||
// - no 'deleted'
|
// - no 'deleted'
|
||||||
// - no 'xor'
|
// - no 'xor'
|
||||||
// - no 'or'
|
// - no 'or'
|
||||||
int countStatus = 0;
|
int countStatus = 0;
|
||||||
int countPending = 0;
|
int countPending = 0;
|
||||||
int countWaiting = 0;
|
int countWaiting = 0;
|
||||||
int countRecurring = 0;
|
int countRecurring = 0;
|
||||||
int countId = (int) Context::getContext ().cli2._id_ranges.size ();
|
int countId = (int)Context::getContext().cli2._id_ranges.size();
|
||||||
int countUUID = (int) Context::getContext ().cli2._uuid_list.size ();
|
int countUUID = (int)Context::getContext().cli2._uuid_list.size();
|
||||||
int countOr = 0;
|
int countOr = 0;
|
||||||
int countXor = 0;
|
int countXor = 0;
|
||||||
int countNot = 0;
|
int countNot = 0;
|
||||||
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 canonical = a.attribute("canonical");
|
||||||
std::string raw = a.attribute ("raw");
|
|
||||||
std::string canonical = a.attribute ("canonical");
|
|
||||||
|
|
||||||
if (a._lextype == Lexer::Type::op && raw == "or") ++countOr;
|
if (a._lextype == Lexer::Type::op && raw == "or") ++countOr;
|
||||||
if (a._lextype == Lexer::Type::op && raw == "xor") ++countXor;
|
if (a._lextype == Lexer::Type::op && raw == "xor") ++countXor;
|
||||||
if (a._lextype == Lexer::Type::op && raw == "not") ++countNot;
|
if (a._lextype == Lexer::Type::op && raw == "not") ++countNot;
|
||||||
if (a._lextype == Lexer::Type::dom && canonical == "status") ++countStatus;
|
if (a._lextype == Lexer::Type::dom && canonical == "status") ++countStatus;
|
||||||
if ( raw == "pending") ++countPending;
|
if (raw == "pending") ++countPending;
|
||||||
if ( raw == "waiting") ++countWaiting;
|
if (raw == "waiting") ++countWaiting;
|
||||||
if ( raw == "recurring") ++countRecurring;
|
if (raw == "recurring") ++countRecurring;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
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;
|
||||||
}
|
}
|
||||||
@@ -248,43 +221,35 @@ 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"))
|
||||||
{
|
throw std::string(
|
||||||
if (! Context::getContext ().config.getBoolean ("allow.empty.filter"))
|
"You did not specify a filter, and with the 'allow.empty.filter' value, no action is "
|
||||||
throw std::string ("You did not specify a filter, and with the 'allow.empty.filter' value, no action is taken.");
|
"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.
|
||||||
throw std::string ("Command prevented from running.");
|
throw std::string("Command prevented from running.");
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
////////////////////////////////////////////////////////////////////////////////
|
////////////////////////////////////////////////////////////////////////////////
|
||||||
void Filter::disableSafety ()
|
void Filter::disableSafety() { _safety = false; }
|
||||||
{
|
|
||||||
_safety = false;
|
|
||||||
}
|
|
||||||
|
|
||||||
////////////////////////////////////////////////////////////////////////////////
|
////////////////////////////////////////////////////////////////////////////////
|
||||||
|
|||||||
33
src/Filter.h
33
src/Filter.h
@@ -27,28 +27,27 @@
|
|||||||
#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>
|
||||||
public:
|
|
||||||
Filter () = default;
|
|
||||||
|
|
||||||
void subset (const std::vector <Task>&, std::vector <Task>&);
|
class Filter {
|
||||||
void subset (std::vector <Task>&);
|
public:
|
||||||
bool hasFilter () const;
|
Filter() = default;
|
||||||
bool pendingOnly () const;
|
|
||||||
void safety () const;
|
|
||||||
void disableSafety ();
|
|
||||||
|
|
||||||
private:
|
void subset(const std::vector<Task>&, std::vector<Task>&);
|
||||||
int _startCount {0};
|
void subset(std::vector<Task>&);
|
||||||
int _endCount {0};
|
bool hasFilter() const;
|
||||||
bool _safety {true};
|
bool pendingOnly() const;
|
||||||
|
void safety() const;
|
||||||
|
void disableSafety();
|
||||||
|
|
||||||
|
private:
|
||||||
|
int _startCount{0};
|
||||||
|
int _endCount{0};
|
||||||
|
bool _safety{true};
|
||||||
};
|
};
|
||||||
|
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
|
|||||||
533
src/Hooks.cpp
533
src/Hooks.cpp
@@ -28,90 +28,83 @@
|
|||||||
// cmake.h include header must come first
|
// 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_SYNTAX "Hook Error: JSON syntax error in: {1}"
|
#define STRING_HOOK_ERROR_NOUUID \
|
||||||
#define STRING_HOOK_ERROR_JSON "Hook Error: JSON "
|
"Hook Error: JSON Object missing 'uuid' attribute from hook script: {1}"
|
||||||
#define STRING_HOOK_ERROR_NOPARSE "Hook Error: JSON failed to parse: "
|
#define STRING_HOOK_ERROR_SYNTAX "Hook Error: JSON syntax error in: {1}"
|
||||||
#define STRING_HOOK_ERROR_BAD_NUM "Hook Error: Expected {1} JSON task(s), found {2}, in hook script: {3}"
|
#define STRING_HOOK_ERROR_JSON "Hook Error: JSON "
|
||||||
#define STRING_HOOK_ERROR_SAME1 "Hook Error: JSON must be for the same task: {1}, in hook script: {2}"
|
#define STRING_HOOK_ERROR_NOPARSE "Hook Error: JSON failed to parse: "
|
||||||
#define STRING_HOOK_ERROR_SAME2 "Hook Error: JSON must be for the same task: {1} != {2}, in hook script: {3}"
|
#define STRING_HOOK_ERROR_BAD_NUM \
|
||||||
|
"Hook Error: Expected {1} JSON task(s), found {2}, in hook script: {3}"
|
||||||
|
#define STRING_HOOK_ERROR_SAME1 \
|
||||||
|
"Hook Error: JSON must be for the same task: {1}, in hook script: {2}"
|
||||||
|
#define STRING_HOOK_ERROR_SAME2 \
|
||||||
|
"Hook Error: JSON must be for the same task: {1} != {2}, in hook script: {3}"
|
||||||
#define STRING_HOOK_ERROR_NOFEEDBACK "Hook Error: Expected feedback from failing hook script: {1}"
|
#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 {
|
||||||
}
|
d = Directory(Context::getContext().config.get("data.location"));
|
||||||
else
|
|
||||||
{
|
|
||||||
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();
|
||||||
{
|
std::sort(_scripts.begin(), _scripts.end());
|
||||||
_scripts = d.list ();
|
|
||||||
std::sort (_scripts.begin (), _scripts.end ());
|
|
||||||
|
|
||||||
if (_debug >= 1)
|
if (_debug >= 1) {
|
||||||
{
|
for (auto& i : _scripts) {
|
||||||
for (auto& i : _scripts)
|
Path p(i);
|
||||||
{
|
if (!p.is_directory()) {
|
||||||
Path p (i);
|
std::string name = p.name();
|
||||||
if (! p.is_directory ())
|
if (name.substr(0, 6) == "on-add" || name.substr(0, 9) == "on-modify" ||
|
||||||
{
|
name.substr(0, 9) == "on-launch" || name.substr(0, 7) == "on-exit")
|
||||||
std::string name = p.name ();
|
Context::getContext().debug("Found hook script " + i);
|
||||||
if (name.substr (0, 6) == "on-add" ||
|
|
||||||
name.substr (0, 9) == "on-modify" ||
|
|
||||||
name.substr (0, 9) == "on-launch" ||
|
|
||||||
name.substr (0, 7) == "on-exit")
|
|
||||||
Context::getContext ().debug ("Found hook script " + i);
|
|
||||||
else
|
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;
|
||||||
@@ -129,45 +122,36 @@ 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> output;
|
||||||
std::vector <std::string> input;
|
int status = callHookScript(script, input, output);
|
||||||
std::vector <std::string> output;
|
|
||||||
int status = callHookScript (script, input, output);
|
|
||||||
|
|
||||||
std::vector <std::string> outputJSON;
|
std::vector<std::string> outputJSON;
|
||||||
std::vector <std::string> outputFeedback;
|
std::vector<std::string> outputFeedback;
|
||||||
separateOutput (output, outputJSON, outputFeedback);
|
separateOutput(output, outputJSON, outputFeedback);
|
||||||
|
|
||||||
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);
|
assertFeedback(outputFeedback, script);
|
||||||
}
|
for (auto& message : outputFeedback) Context::getContext().error(message);
|
||||||
else
|
|
||||||
{
|
|
||||||
assertFeedback (outputFeedback, script);
|
|
||||||
for (auto& message : outputFeedback)
|
|
||||||
Context::getContext ().error (message);
|
|
||||||
|
|
||||||
throw 0; // This is how hooks silently terminate processing.
|
throw 0; // This is how hooks silently terminate processing.
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
Context::getContext ().time_hooks_us += timer.total_us ();
|
Context::getContext().time_hooks_us += timer.total_us();
|
||||||
}
|
}
|
||||||
|
|
||||||
////////////////////////////////////////////////////////////////////////////////
|
////////////////////////////////////////////////////////////////////////////////
|
||||||
@@ -182,55 +166,45 @@ void Hooks::onLaunch () const
|
|||||||
// - all emitted non-JSON lines are considered feedback or error messages
|
// - 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);
|
||||||
|
|
||||||
// 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);
|
|
||||||
|
|
||||||
std::vector <std::string> outputJSON;
|
std::vector<std::string> outputJSON;
|
||||||
std::vector <std::string> outputFeedback;
|
std::vector<std::string> outputFeedback;
|
||||||
separateOutput (output, outputJSON, outputFeedback);
|
separateOutput(output, outputJSON, outputFeedback);
|
||||||
|
|
||||||
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);
|
assertFeedback(outputFeedback, script);
|
||||||
}
|
for (auto& message : outputFeedback) Context::getContext().error(message);
|
||||||
else
|
|
||||||
{
|
|
||||||
assertFeedback (outputFeedback, script);
|
|
||||||
for (auto& message : outputFeedback)
|
|
||||||
Context::getContext ().error (message);
|
|
||||||
|
|
||||||
throw 0; // This is how hooks silently terminate processing.
|
throw 0; // This is how hooks silently terminate processing.
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
Context::getContext ().time_hooks_us += timer.total_us ();
|
Context::getContext().time_hooks_us += timer.total_us();
|
||||||
}
|
}
|
||||||
|
|
||||||
////////////////////////////////////////////////////////////////////////////////
|
////////////////////////////////////////////////////////////////////////////////
|
||||||
@@ -245,57 +219,48 @@ void Hooks::onExit () const
|
|||||||
// - all emitted non-JSON lines are considered feedback or error messages
|
// - 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);
|
|
||||||
|
|
||||||
std::vector <std::string> outputJSON;
|
std::vector<std::string> outputJSON;
|
||||||
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);
|
|
||||||
|
|
||||||
// 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 {
|
||||||
}
|
assertFeedback(outputFeedback, script);
|
||||||
else
|
for (auto& message : outputFeedback) Context::getContext().error(message);
|
||||||
{
|
|
||||||
assertFeedback (outputFeedback, script);
|
|
||||||
for (auto& message : outputFeedback)
|
|
||||||
Context::getContext ().error (message);
|
|
||||||
|
|
||||||
throw 0; // This is how hooks silently terminate processing.
|
throw 0; // This is how hooks silently terminate processing.
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// Transfer the modified task back to the original task.
|
// Transfer the modified task back to the original task.
|
||||||
task = Task (input[0]);
|
task = Task(input[0]);
|
||||||
}
|
}
|
||||||
|
|
||||||
Context::getContext ().time_hooks_us += timer.total_us ();
|
Context::getContext().time_hooks_us += timer.total_us();
|
||||||
}
|
}
|
||||||
|
|
||||||
////////////////////////////////////////////////////////////////////////////////
|
////////////////////////////////////////////////////////////////////////////////
|
||||||
@@ -311,76 +276,60 @@ void Hooks::onAdd (Task& task) const
|
|||||||
// - all emitted non-JSON lines are considered feedback or error messages
|
// - 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);
|
|
||||||
|
|
||||||
std::vector <std::string> outputJSON;
|
std::vector<std::string> outputJSON;
|
||||||
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);
|
|
||||||
|
|
||||||
// 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 {
|
||||||
}
|
assertFeedback(outputFeedback, script);
|
||||||
else
|
for (auto& message : outputFeedback) Context::getContext().error(message);
|
||||||
{
|
|
||||||
assertFeedback (outputFeedback, script);
|
|
||||||
for (auto& message : outputFeedback)
|
|
||||||
Context::getContext ().error (message);
|
|
||||||
|
|
||||||
throw 0; // This is how hooks silently terminate processing.
|
throw 0; // This is how hooks silently terminate processing.
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
after = Task (input[1]);
|
after = Task(input[1]);
|
||||||
}
|
}
|
||||||
|
|
||||||
Context::getContext ().time_hooks_us += timer.total_us ();
|
Context::getContext().time_hooks_us += timer.total_us();
|
||||||
}
|
}
|
||||||
|
|
||||||
////////////////////////////////////////////////////////////////////////////////
|
////////////////////////////////////////////////////////////////////////////////
|
||||||
std::vector <std::string> Hooks::list () const
|
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) {
|
||||||
{
|
File script(i);
|
||||||
if (i.find ("/" + event) != std::string::npos)
|
if (script.executable()) matching.push_back(i);
|
||||||
{
|
|
||||||
File script (i);
|
|
||||||
if (script.executable ())
|
|
||||||
matching.push_back (i);
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -388,124 +337,95 @@ std::vector <std::string> Hooks::scripts (const std::string& event) const
|
|||||||
}
|
}
|
||||||
|
|
||||||
////////////////////////////////////////////////////////////////////////////////
|
////////////////////////////////////////////////////////////////////////////////
|
||||||
void Hooks::separateOutput (
|
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
|
if (isJSON(i))
|
||||||
{
|
json.push_back(i);
|
||||||
for (auto& i : output)
|
|
||||||
{
|
|
||||||
if (isJSON (i))
|
|
||||||
json.push_back (i);
|
|
||||||
else
|
else
|
||||||
feedback.push_back (i);
|
feedback.push_back(i);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
////////////////////////////////////////////////////////////////////////////////
|
////////////////////////////////////////////////////////////////////////////////
|
||||||
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)
|
Context::getContext().error(format(STRING_HOOK_ERROR_OBJECT, Path(script).name()));
|
||||||
{
|
|
||||||
if (i.length () < 3 ||
|
|
||||||
i[0] != '{' ||
|
|
||||||
i[i.length () - 1] != '}')
|
|
||||||
{
|
|
||||||
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;
|
||||||
}
|
}
|
||||||
|
|
||||||
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) Context::getContext().error(STRING_HOOK_ERROR_JSON + e);
|
||||||
if (_debug)
|
|
||||||
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;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
////////////////////////////////////////////////////////////////////////////////
|
////////////////////////////////////////////////////////////////////////////////
|
||||||
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,
|
std::string uuid = task.get("uuid");
|
||||||
const std::string& script) const
|
|
||||||
{
|
|
||||||
std::string uuid = task.get ("uuid");
|
|
||||||
|
|
||||||
for (auto& i : input)
|
for (auto& i : input) {
|
||||||
{
|
auto root_obj = (json::object*)json::parse(i);
|
||||||
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;
|
||||||
}
|
}
|
||||||
|
|
||||||
auto up = (json::string*) u->second;
|
auto up = (json::string*)u->second;
|
||||||
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;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -514,101 +434,82 @@ 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.
|
||||||
args.push_back ("api:2");
|
args.push_back("api:2");
|
||||||
|
|
||||||
// Command line Taskwarrior was called with.
|
// Command line Taskwarrior was called with.
|
||||||
getDOM ("context.args", v);
|
getDOM("context.args", v);
|
||||||
args.push_back ("args:" + std::string (v));
|
args.push_back("args:" + std::string(v));
|
||||||
|
|
||||||
// Command to be executed.
|
// Command to be executed.
|
||||||
args.push_back ("command:" + Context::getContext ().cli2.getCommand ());
|
args.push_back("command:" + Context::getContext().cli2.getCommand());
|
||||||
|
|
||||||
// rc file used after applying all overrides.
|
// rc file used after applying all overrides.
|
||||||
args.push_back ("rc:" + Context::getContext ().rc_file._data);
|
args.push_back("rc:" + Context::getContext().rc_file._data);
|
||||||
|
|
||||||
// Directory containing *.data files.
|
// Directory containing *.data files.
|
||||||
args.push_back ("data:" + Context::getContext ().data_dir._data);
|
args.push_back("data:" + Context::getContext().data_dir._data);
|
||||||
|
|
||||||
// Taskwarrior version, same as returned by "task --version"
|
// Taskwarrior version, same as returned by "task --version"
|
||||||
args.push_back ("version:" + std::string(VERSION));
|
args.push_back("version:" + std::string(VERSION));
|
||||||
|
|
||||||
return args;
|
return args;
|
||||||
}
|
}
|
||||||
|
|
||||||
////////////////////////////////////////////////////////////////////////////////
|
////////////////////////////////////////////////////////////////////////////////
|
||||||
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) Context::getContext().debug(" " + i);
|
||||||
for (const auto& i : input)
|
|
||||||
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) Context::getContext().debug(" " + arg);
|
||||||
for (const auto& arg: args)
|
|
||||||
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
|
||||||
}
|
}
|
||||||
|
|
||||||
return status;
|
return status;
|
||||||
|
|||||||
56
src/Hooks.h
56
src/Hooks.h
@@ -27,37 +27,39 @@
|
|||||||
#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>
|
||||||
public:
|
|
||||||
Hooks () = default;
|
|
||||||
void initialize ();
|
|
||||||
bool enable (bool);
|
|
||||||
void onLaunch () const;
|
|
||||||
void onExit () const;
|
|
||||||
void onAdd (Task&) const;
|
|
||||||
void onModify (const Task&, Task&) const;
|
|
||||||
std::vector <std::string> list () const;
|
|
||||||
|
|
||||||
private:
|
class Hooks {
|
||||||
std::vector <std::string> scripts (const std::string&) const;
|
public:
|
||||||
void separateOutput (const std::vector <std::string>&, std::vector <std::string>&, std::vector <std::string>&) const;
|
Hooks() = default;
|
||||||
bool isJSON (const std::string&) const;
|
void initialize();
|
||||||
void assertValidJSON (const std::vector <std::string>&, const std::string&) const;
|
bool enable(bool);
|
||||||
void assertNTasks (const std::vector <std::string>&, unsigned int, const std::string&) const;
|
void onLaunch() const;
|
||||||
void assertSameTask (const std::vector <std::string>&, const Task&, const std::string&) const;
|
void onExit() const;
|
||||||
void assertFeedback (const std::vector <std::string>&, const std::string&) const;
|
void onAdd(Task&) const;
|
||||||
std::vector <std::string>& buildHookScriptArgs (std::vector <std::string>&) const;
|
void onModify(const Task&, Task&) const;
|
||||||
int callHookScript (const std::string&, const std::vector <std::string>&, std::vector <std::string>&) const;
|
std::vector<std::string> list() const;
|
||||||
|
|
||||||
private:
|
private:
|
||||||
bool _enabled {true};
|
std::vector<std::string> scripts(const std::string&) const;
|
||||||
int _debug {0};
|
void separateOutput(const std::vector<std::string>&, std::vector<std::string>&,
|
||||||
std::vector <std::string> _scripts {};
|
std::vector<std::string>&) const;
|
||||||
|
bool isJSON(const std::string&) const;
|
||||||
|
void assertValidJSON(const std::vector<std::string>&, const std::string&) const;
|
||||||
|
void assertNTasks(const std::vector<std::string>&, unsigned int, const std::string&) const;
|
||||||
|
void assertSameTask(const std::vector<std::string>&, const Task&, const std::string&) const;
|
||||||
|
void assertFeedback(const std::vector<std::string>&, const std::string&) const;
|
||||||
|
std::vector<std::string>& buildHookScriptArgs(std::vector<std::string>&) const;
|
||||||
|
int callHookScript(const std::string&, const std::vector<std::string>&,
|
||||||
|
std::vector<std::string>&) const;
|
||||||
|
|
||||||
|
private:
|
||||||
|
bool _enabled{true};
|
||||||
|
int _debug{0};
|
||||||
|
std::vector<std::string> _scripts{};
|
||||||
};
|
};
|
||||||
|
|
||||||
#endif
|
#endif
|
||||||
|
|||||||
1303
src/Lexer.cpp
1303
src/Lexer.cpp
File diff suppressed because it is too large
Load Diff
161
src/Lexer.h
161
src/Lexer.h
@@ -27,95 +27,108 @@
|
|||||||
#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 {
|
||||||
string,
|
uuid,
|
||||||
url, pair, set, separator,
|
number,
|
||||||
tag,
|
hex,
|
||||||
path,
|
string,
|
||||||
substitution, pattern,
|
url,
|
||||||
op,
|
pair,
|
||||||
dom, identifier, word,
|
set,
|
||||||
date, duration };
|
separator,
|
||||||
|
tag,
|
||||||
|
path,
|
||||||
|
substitution,
|
||||||
|
pattern,
|
||||||
|
op,
|
||||||
|
dom,
|
||||||
|
identifier,
|
||||||
|
word,
|
||||||
|
date,
|
||||||
|
duration
|
||||||
|
};
|
||||||
|
|
||||||
Lexer (const std::string&);
|
Lexer(const std::string&);
|
||||||
~Lexer () = default;
|
~Lexer() = default;
|
||||||
bool token (std::string&, Lexer::Type&);
|
bool token(std::string&, Lexer::Type&);
|
||||||
static std::vector <std::string> split (const std::string&);
|
static std::vector<std::string> split(const std::string&);
|
||||||
static std::string typeToString (Lexer::Type);
|
static std::string typeToString(Lexer::Type);
|
||||||
|
|
||||||
// Static helpers.
|
// Static helpers.
|
||||||
static const std::string typeName (const Lexer::Type&);
|
static const std::string typeName(const Lexer::Type&);
|
||||||
static bool isIdentifierStart (int);
|
static bool isIdentifierStart(int);
|
||||||
static bool isIdentifierNext (int);
|
static bool isIdentifierNext(int);
|
||||||
static bool isSingleCharOperator (int);
|
static bool isSingleCharOperator(int);
|
||||||
static bool isDoubleCharOperator (int, int, int);
|
static bool isDoubleCharOperator(int, int, int);
|
||||||
static bool isTripleCharOperator (int, int, int, int);
|
static bool isTripleCharOperator(int, int, int, int);
|
||||||
static bool isBoundary (int, int);
|
static bool isBoundary(int, int);
|
||||||
static bool isHardBoundary (int, int);
|
static bool isHardBoundary(int, int);
|
||||||
static bool isPunctuation (int);
|
static bool isPunctuation(int);
|
||||||
static bool isAllDigits (const std::string&);
|
static bool isAllDigits(const std::string&);
|
||||||
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&,
|
||||||
static bool readWord (const std::string&, std::string::size_type&, std::string&);
|
std::string&);
|
||||||
static bool decomposePair (const std::string&, std::string&, std::string&, std::string&, std::string&);
|
static bool readWord(const std::string&, std::string::size_type&, std::string&);
|
||||||
static bool decomposeSubstitution (const std::string&, std::string&, std::string&, std::string&);
|
static bool decomposePair(const std::string&, std::string&, std::string&, std::string&,
|
||||||
static bool decomposePattern (const std::string&, std::string&, std::string&);
|
std::string&);
|
||||||
static int hexToInt (int);
|
static bool decomposeSubstitution(const std::string&, std::string&, std::string&, std::string&);
|
||||||
static int hexToInt (int, int);
|
static bool decomposePattern(const std::string&, std::string&, std::string&);
|
||||||
static int hexToInt (int, int, int, int);
|
static int hexToInt(int);
|
||||||
static std::string::size_type commonLength (const std::string&, const std::string&);
|
static int hexToInt(int, int);
|
||||||
static std::string::size_type commonLength (const std::string&, std::string::size_type, const std::string&, std::string::size_type);
|
static int hexToInt(int, int, int, int);
|
||||||
static std::string commify (const std::string&);
|
static std::string::size_type commonLength(const std::string&, const std::string&);
|
||||||
static std::string lowerCase (const std::string&);
|
static std::string::size_type commonLength(const std::string&, std::string::size_type,
|
||||||
static std::string ucFirst (const std::string&);
|
const std::string&, std::string::size_type);
|
||||||
static std::string trimLeft (const std::string& in, const std::string& t = " ");
|
static std::string commify(const std::string&);
|
||||||
static std::string trimRight (const std::string& in, const std::string& t = " ");
|
static std::string lowerCase(const std::string&);
|
||||||
static std::string trim (const std::string& in, const std::string& t = " ");
|
static std::string ucFirst(const std::string&);
|
||||||
|
static std::string trimLeft(const std::string& in, const std::string& t = " ");
|
||||||
|
static std::string trimRight(const std::string& in, const std::string& t = " ");
|
||||||
|
static std::string trim(const std::string& in, const std::string& t = " ");
|
||||||
|
|
||||||
// Stream Classifiers.
|
// Stream Classifiers.
|
||||||
bool isEOS () const;
|
bool isEOS() const;
|
||||||
bool isString (std::string&, Lexer::Type&, const std::string&);
|
bool isString(std::string&, Lexer::Type&, const std::string&);
|
||||||
bool isDate (std::string&, Lexer::Type&);
|
bool isDate(std::string&, Lexer::Type&);
|
||||||
bool isDuration (std::string&, Lexer::Type&);
|
bool isDuration(std::string&, Lexer::Type&);
|
||||||
bool isUUID (std::string&, Lexer::Type&, bool);
|
bool isUUID(std::string&, Lexer::Type&, bool);
|
||||||
bool isNumber (std::string&, Lexer::Type&);
|
bool isNumber(std::string&, Lexer::Type&);
|
||||||
bool isInteger (std::string&, Lexer::Type&);
|
bool isInteger(std::string&, Lexer::Type&);
|
||||||
bool isHexNumber (std::string&, Lexer::Type&);
|
bool isHexNumber(std::string&, Lexer::Type&);
|
||||||
bool isSeparator (std::string&, Lexer::Type&);
|
bool isSeparator(std::string&, Lexer::Type&);
|
||||||
bool isURL (std::string&, Lexer::Type&);
|
bool isURL(std::string&, Lexer::Type&);
|
||||||
bool isPair (std::string&, Lexer::Type&);
|
bool isPair(std::string&, Lexer::Type&);
|
||||||
bool isSet (std::string&, Lexer::Type&);
|
bool isSet(std::string&, Lexer::Type&);
|
||||||
bool isTag (std::string&, Lexer::Type&);
|
bool isTag(std::string&, Lexer::Type&);
|
||||||
bool isPath (std::string&, Lexer::Type&);
|
bool isPath(std::string&, Lexer::Type&);
|
||||||
bool isSubstitution (std::string&, Lexer::Type&);
|
bool isSubstitution(std::string&, Lexer::Type&);
|
||||||
bool isPattern (std::string&, Lexer::Type&);
|
bool isPattern(std::string&, Lexer::Type&);
|
||||||
bool isOperator (std::string&, Lexer::Type&);
|
bool isOperator(std::string&, Lexer::Type&);
|
||||||
bool isDOM (std::string&, Lexer::Type&);
|
bool isDOM(std::string&, Lexer::Type&);
|
||||||
bool isIdentifier (std::string&, Lexer::Type&);
|
bool isIdentifier(std::string&, Lexer::Type&);
|
||||||
bool isWord (std::string&, Lexer::Type&);
|
bool isWord(std::string&, Lexer::Type&);
|
||||||
bool isLiteral (const std::string&, bool, bool);
|
bool isLiteral(const std::string&, bool, bool);
|
||||||
bool isOneOf (const std::vector <std::string>&, bool, bool);
|
bool isOneOf(const std::vector<std::string>&, bool, bool);
|
||||||
bool isOneOf (const std::map <std::string, std::string>&, bool, bool);
|
bool isOneOf(const std::map<std::string, std::string>&, bool, bool);
|
||||||
|
|
||||||
private:
|
private:
|
||||||
std::string _text;
|
std::string _text;
|
||||||
std::size_t _cursor;
|
std::size_t _cursor;
|
||||||
std::size_t _eos;
|
std::size_t _eos;
|
||||||
|
|||||||
375
src/TDB2.cpp
375
src/TDB2.cpp
@@ -27,64 +27,63 @@
|
|||||||
#include <cmake.h>
|
#include <cmake.h>
|
||||||
// cmake.h include header must come first
|
// cmake.h include header must come first
|
||||||
|
|
||||||
#include <TDB2.h>
|
|
||||||
#include <iostream>
|
|
||||||
#include <sstream>
|
|
||||||
#include <algorithm>
|
|
||||||
#include <vector>
|
|
||||||
#include <list>
|
|
||||||
#include <unordered_set>
|
|
||||||
#include <stdlib.h>
|
|
||||||
#include <signal.h>
|
|
||||||
#include <Context.h>
|
|
||||||
#include <Color.h>
|
#include <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"
|
||||||
|
|
||||||
bool TDB2::debug_mode = false;
|
bool TDB2::debug_mode = false;
|
||||||
static void dependency_scan (std::vector<Task> &);
|
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.
|
||||||
task.validate (true);
|
task.validate(true);
|
||||||
|
|
||||||
std::string uuid = task.get ("uuid");
|
std::string uuid = task.get("uuid");
|
||||||
changes[uuid] = task;
|
changes[uuid] = task;
|
||||||
|
|
||||||
// run hooks for this new task
|
// run hooks for this new task
|
||||||
Context::getContext ().hooks.onAdd (task);
|
Context::getContext().hooks.onAdd(task);
|
||||||
|
|
||||||
auto innertask = replica.import_task_with_uuid (uuid);
|
auto innertask = replica.import_task_with_uuid(uuid);
|
||||||
|
|
||||||
{
|
{
|
||||||
auto guard = replica.mutate_task(innertask);
|
auto guard = replica.mutate_task(innertask);
|
||||||
|
|
||||||
// add the task attributes
|
// add the task attributes
|
||||||
for (auto& attr : task.all ()) {
|
for (auto& attr : task.all()) {
|
||||||
// TaskChampion does not store uuid or id in the taskmap
|
// TaskChampion does not store uuid or id in the taskmap
|
||||||
if (attr == "uuid" || attr == "id") {
|
if (attr == "uuid" || attr == "id") {
|
||||||
continue;
|
continue;
|
||||||
@@ -93,35 +92,34 @@ void TDB2::add (Task& task)
|
|||||||
// Use `set_status` for the task status, to get expected behavior
|
// Use `set_status` for the task status, to get expected behavior
|
||||||
// with respect to the working set.
|
// with respect to the working set.
|
||||||
else if (attr == "status") {
|
else if (attr == "status") {
|
||||||
innertask.set_status (Task::status2tc (Task::textToStatus (task.get (attr))));
|
innertask.set_status(Task::status2tc(Task::textToStatus(task.get(attr))));
|
||||||
}
|
}
|
||||||
|
|
||||||
// use `set_modified` to set the modified timestamp, avoiding automatic
|
// use `set_modified` to set the modified timestamp, avoiding automatic
|
||||||
// updates to this field by TaskChampion.
|
// updates to this field by TaskChampion.
|
||||||
else if (attr == "modified") {
|
else if (attr == "modified") {
|
||||||
auto mod = (time_t) std::stoi (task.get (attr));
|
auto mod = (time_t)std::stoi(task.get(attr));
|
||||||
innertask.set_modified (mod);
|
innertask.set_modified(mod);
|
||||||
}
|
}
|
||||||
|
|
||||||
// otherwise, just set the k/v map value
|
// otherwise, just set the k/v map value
|
||||||
else {
|
else {
|
||||||
innertask.set_value (attr, std::make_optional (task.get (attr)));
|
innertask.set_value(attr, std::make_optional(task.get(attr)));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
auto ws = replica.working_set ();
|
auto ws = replica.working_set();
|
||||||
|
|
||||||
// get the ID that was assigned to this task
|
// get the ID that was assigned to this task
|
||||||
auto id = ws.by_uuid (uuid);
|
auto id = ws.by_uuid(uuid);
|
||||||
|
|
||||||
// update the cached working set with the new information
|
// update the cached working set with the new information
|
||||||
_working_set = std::make_optional (std::move (ws));
|
_working_set = std::make_optional(std::move(ws));
|
||||||
|
|
||||||
if (id.has_value ()) {
|
if (id.has_value()) {
|
||||||
task.id = id.value();
|
task.id = id.value();
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
////////////////////////////////////////////////////////////////////////////////
|
////////////////////////////////////////////////////////////////////////////////
|
||||||
@@ -139,31 +137,30 @@ void TDB2::add (Task& task)
|
|||||||
// this method. In this case, this method throws an error that will make sense
|
// 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");
|
||||||
task.validate (false);
|
task.validate(false);
|
||||||
auto uuid = task.get ("uuid");
|
auto uuid = task.get("uuid");
|
||||||
|
|
||||||
changes[uuid] = task;
|
changes[uuid] = task;
|
||||||
|
|
||||||
// invoke the hook and allow it to modify the task before updating
|
// invoke the hook and allow it to modify the task before updating
|
||||||
Task original;
|
Task original;
|
||||||
get (uuid, original);
|
get(uuid, original);
|
||||||
Context::getContext ().hooks.onModify (original, task);
|
Context::getContext().hooks.onModify(original, task);
|
||||||
|
|
||||||
auto maybe_tctask = replica.get_task (uuid);
|
auto maybe_tctask = replica.get_task(uuid);
|
||||||
if (!maybe_tctask.has_value ()) {
|
if (!maybe_tctask.has_value()) {
|
||||||
throw std::string ("task no longer exists");
|
throw std::string("task no longer exists");
|
||||||
}
|
}
|
||||||
auto tctask = std::move (maybe_tctask.value ());
|
auto tctask = std::move(maybe_tctask.value());
|
||||||
auto guard = replica.mutate_task(tctask);
|
auto guard = replica.mutate_task(tctask);
|
||||||
auto tctask_map = tctask.get_taskmap ();
|
auto tctask_map = tctask.get_taskmap();
|
||||||
|
|
||||||
std::unordered_set<std::string> seen;
|
std::unordered_set<std::string> seen;
|
||||||
for (auto k : task.all ()) {
|
for (auto k : task.all()) {
|
||||||
// ignore task keys that aren't stored
|
// ignore task keys that aren't stored
|
||||||
if (k == "uuid") {
|
if (k == "uuid") {
|
||||||
continue;
|
continue;
|
||||||
@@ -183,38 +180,35 @@ void TDB2::modify (Task& task)
|
|||||||
if (v_new == "") {
|
if (v_new == "") {
|
||||||
tctask.set_value(k, {});
|
tctask.set_value(k, {});
|
||||||
} else {
|
} else {
|
||||||
tctask.set_value(k, make_optional (v_new));
|
tctask.set_value(k, make_optional(v_new));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// we've now added and updated properties; but must find any deleted properties
|
// we've now added and updated properties; but must find any deleted properties
|
||||||
for (auto kv : tctask_map) {
|
for (auto kv : tctask_map) {
|
||||||
if (seen.find (kv.first) == seen.end ()) {
|
if (seen.find(kv.first) == seen.end()) {
|
||||||
tctask.set_value (kv.first, {});
|
tctask.set_value(kv.first, {});
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
////////////////////////////////////////////////////////////////////////////////
|
////////////////////////////////////////////////////////////////////////////////
|
||||||
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 ());
|
|
||||||
}
|
}
|
||||||
return _working_set.value ();
|
return _working_set.value();
|
||||||
}
|
}
|
||||||
|
|
||||||
////////////////////////////////////////////////////////////////////////////////
|
////////////////////////////////////////////////////////////////////////////////
|
||||||
void TDB2::get_changes (std::vector <Task>& changes)
|
void TDB2::get_changes(std::vector<Task>& changes) {
|
||||||
{
|
|
||||||
std::map<std::string, Task>& changes_map = this->changes;
|
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),
|
||||||
@@ -222,8 +216,7 @@ void TDB2::get_changes (std::vector <Task>& changes)
|
|||||||
}
|
}
|
||||||
|
|
||||||
////////////////////////////////////////////////////////////////////////////////
|
////////////////////////////////////////////////////////////////////////////////
|
||||||
void TDB2::revert ()
|
void TDB2::revert() {
|
||||||
{
|
|
||||||
auto undo_ops = replica.get_undo_ops();
|
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.";
|
||||||
@@ -235,19 +228,18 @@ void TDB2::revert ()
|
|||||||
} else {
|
} else {
|
||||||
replica.free_replica_ops(undo_ops);
|
replica.free_replica_ops(undo_ops);
|
||||||
}
|
}
|
||||||
replica.rebuild_working_set (false);
|
replica.rebuild_working_set(false);
|
||||||
}
|
}
|
||||||
|
|
||||||
////////////////////////////////////////////////////////////////////////////////
|
////////////////////////////////////////////////////////////////////////////////
|
||||||
bool TDB2::confirm_revert (struct tc::ffi::TCReplicaOpList undo_ops)
|
bool TDB2::confirm_revert(struct tc::ffi::TCReplicaOpList undo_ops) {
|
||||||
{
|
|
||||||
// TODO Use show_diff rather than this basic listing of operations, though
|
// 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";
|
||||||
for (size_t i = 0; i < undo_ops.len; i++) {
|
for (size_t i = 0; i < undo_ops.len; i++) {
|
||||||
std::cout << "- ";
|
std::cout << "- ";
|
||||||
tc::ffi::TCReplicaOp op = undo_ops.items[i];
|
tc::ffi::TCReplicaOp op = undo_ops.items[i];
|
||||||
switch(op.operation_type) {
|
switch (op.operation_type) {
|
||||||
case tc::ffi::TCReplicaOpType::Create:
|
case tc::ffi::TCReplicaOpType::Create:
|
||||||
std::cout << "Create " << replica.get_op_uuid(op);
|
std::cout << "Create " << replica.get_op_uuid(op);
|
||||||
break;
|
break;
|
||||||
@@ -256,114 +248,100 @@ 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.");
|
||||||
break;
|
break;
|
||||||
default:
|
default:
|
||||||
throw std::string ("Can't undo non-operation.");
|
throw std::string("Can't undo non-operation.");
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
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,
|
Datetime lastChange(strtoll(when.c_str(), nullptr, 10));
|
||||||
const std::string& when)
|
|
||||||
{
|
|
||||||
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);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
Context::getContext ().time_gc_us += timer.total_us ();
|
Context::getContext().time_gc_us += timer.total_us();
|
||||||
}
|
}
|
||||||
|
|
||||||
////////////////////////////////////////////////////////////////////////////////
|
////////////////////////////////////////////////////////////////////////////////
|
||||||
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 ();
|
|
||||||
|
|
||||||
std::vector <Task> result;
|
std::vector<Task> result;
|
||||||
for (size_t i = 0; i <= largest_index; i++) {
|
for (size_t i = 0; i <= largest_index; i++) {
|
||||||
auto maybe_uuid = ws.by_index (i);
|
auto maybe_uuid = ws.by_index(i);
|
||||||
if (maybe_uuid.has_value ()) {
|
if (maybe_uuid.has_value()) {
|
||||||
auto maybe_task = replica.get_task (maybe_uuid.value ());
|
auto maybe_task = replica.get_task(maybe_uuid.value());
|
||||||
if (maybe_task.has_value ()) {
|
if (maybe_task.has_value()) {
|
||||||
result.push_back (Task (std::move (maybe_task.value ())));
|
result.push_back(Task(std::move(maybe_task.value())));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -374,16 +352,15 @@ const std::vector <Task> TDB2::pending_tasks ()
|
|||||||
}
|
}
|
||||||
|
|
||||||
////////////////////////////////////////////////////////////////////////////////
|
////////////////////////////////////////////////////////////////////////////////
|
||||||
const std::vector <Task> TDB2::completed_tasks ()
|
const std::vector<Task> TDB2::completed_tasks() {
|
||||||
{
|
|
||||||
auto all_tctasks = replica.all_tasks();
|
auto all_tctasks = replica.all_tasks();
|
||||||
const tc::WorkingSet &ws = working_set ();
|
const tc::WorkingSet& ws = working_set();
|
||||||
|
|
||||||
std::vector <Task> result;
|
std::vector<Task> result;
|
||||||
for (auto& tctask : all_tctasks) {
|
for (auto& tctask : all_tctasks) {
|
||||||
// if this task is _not_ in the working set, return it.
|
// if this task is _not_ in the working set, return it.
|
||||||
if (!ws.by_uuid (tctask.get_uuid ())) {
|
if (!ws.by_uuid(tctask.get_uuid())) {
|
||||||
result.push_back (Task (std::move (tctask)));
|
result.push_back(Task(std::move(tctask)));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -392,10 +369,9 @@ const std::vector <Task> TDB2::completed_tasks ()
|
|||||||
|
|
||||||
////////////////////////////////////////////////////////////////////////////////
|
////////////////////////////////////////////////////////////////////////////////
|
||||||
// Locate task by ID, wherever it is.
|
// 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) {
|
||||||
auto maybe_task = replica.get_task(*maybe_uuid);
|
auto maybe_task = replica.get_task(*maybe_uuid);
|
||||||
if (maybe_task) {
|
if (maybe_task) {
|
||||||
@@ -409,25 +385,24 @@ bool TDB2::get (int id, Task& task)
|
|||||||
|
|
||||||
////////////////////////////////////////////////////////////////////////////////
|
////////////////////////////////////////////////////////////////////////////////
|
||||||
// Locate task by UUID, including by partial ID, wherever it is.
|
// 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 {
|
||||||
auto maybe_task = replica.get_task (uuid);
|
auto maybe_task = replica.get_task(uuid);
|
||||||
if (maybe_task) {
|
if (maybe_task) {
|
||||||
task = Task{std::move (*maybe_task)};
|
task = Task{std::move(*maybe_task)};
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
} catch (const std::string &err) {
|
} catch (const std::string& err) {
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// Nothing to do but iterate over all tasks and check whether it's closeEnough
|
// Nothing to do but iterate over all tasks and check whether it's closeEnough
|
||||||
for (auto& tctask : replica.all_tasks ()) {
|
for (auto& tctask : replica.all_tasks()) {
|
||||||
if (closeEnough (tctask.get_uuid (), uuid, uuid.length ())) {
|
if (closeEnough(tctask.get_uuid(), uuid, uuid.length())) {
|
||||||
task = Task{std::move (tctask)};
|
task = Task{std::move(tctask)};
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -437,34 +412,25 @@ 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);
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -475,41 +441,40 @@ const std::vector <Task> TDB2::siblings (Task& task)
|
|||||||
}
|
}
|
||||||
|
|
||||||
////////////////////////////////////////////////////////////////////////////////
|
////////////////////////////////////////////////////////////////////////////////
|
||||||
const std::vector <Task> TDB2::children (Task& parent)
|
const std::vector<Task> TDB2::children(Task& parent) {
|
||||||
{
|
|
||||||
// scan _pending_ tasks for those with `parent` equal to this task
|
// 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");
|
||||||
|
|
||||||
const tc::WorkingSet &ws = working_set ();
|
const tc::WorkingSet& ws = working_set();
|
||||||
size_t end_idx = ws.largest_index ();
|
size_t end_idx = ws.largest_index();
|
||||||
|
|
||||||
for (size_t i = 0; i <= end_idx; i++) {
|
for (size_t i = 0; i <= end_idx; i++) {
|
||||||
auto uuid_opt = ws.by_index (i);
|
auto uuid_opt = ws.by_index(i);
|
||||||
if (!uuid_opt) {
|
if (!uuid_opt) {
|
||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
auto uuid = uuid_opt.value ();
|
auto uuid = uuid_opt.value();
|
||||||
|
|
||||||
// skip self-references
|
// skip self-references
|
||||||
if (uuid == this_uuid) {
|
if (uuid == this_uuid) {
|
||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
|
|
||||||
auto task_opt = replica.get_task (uuid_opt.value ());
|
auto task_opt = replica.get_task(uuid_opt.value());
|
||||||
if (!task_opt) {
|
if (!task_opt) {
|
||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
auto task = std::move (task_opt.value ());
|
auto task = std::move(task_opt.value());
|
||||||
|
|
||||||
auto parent_uuid_opt = task.get_value ("parent");
|
auto parent_uuid_opt = task.get_value("parent");
|
||||||
if (!parent_uuid_opt) {
|
if (!parent_uuid_opt) {
|
||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
auto parent_uuid = parent_uuid_opt.value ();
|
auto parent_uuid = parent_uuid_opt.value();
|
||||||
|
|
||||||
if (parent_uuid == this_uuid) {
|
if (parent_uuid == this_uuid) {
|
||||||
results.push_back (Task (std::move (task)));
|
results.push_back(Task(std::move(task)));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -517,40 +482,30 @@ const std::vector <Task> TDB2::children (Task& parent)
|
|||||||
}
|
}
|
||||||
|
|
||||||
////////////////////////////////////////////////////////////////////////////////
|
////////////////////////////////////////////////////////////////////////////////
|
||||||
std::string TDB2::uuid (int id)
|
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
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -558,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;
|
||||||
}
|
}
|
||||||
|
|||||||
76
src/TDB2.h
76
src/TDB2.h
@@ -27,71 +27,71 @@
|
|||||||
#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;
|
||||||
|
|
||||||
TDB2 ();
|
TDB2();
|
||||||
|
|
||||||
void open_replica (const std::string&, bool create_if_missing);
|
void open_replica(const std::string &, bool create_if_missing);
|
||||||
void add (Task&);
|
void add(Task &);
|
||||||
void modify (Task&);
|
void modify(Task &);
|
||||||
void purge (Task&);
|
void purge(Task &);
|
||||||
void get_changes (std::vector <Task>&);
|
void get_changes(std::vector<Task> &);
|
||||||
void revert ();
|
void revert();
|
||||||
void gc ();
|
void gc();
|
||||||
void expire_tasks ();
|
void expire_tasks();
|
||||||
int latest_id ();
|
int latest_id();
|
||||||
|
|
||||||
// Generalized task accessors.
|
// Generalized task accessors.
|
||||||
const std::vector <Task> all_tasks ();
|
const std::vector<Task> all_tasks();
|
||||||
const std::vector <Task> pending_tasks ();
|
const std::vector<Task> pending_tasks();
|
||||||
const std::vector <Task> completed_tasks ();
|
const std::vector<Task> completed_tasks();
|
||||||
bool get (int, Task&);
|
bool get(int, Task &);
|
||||||
bool get (const std::string&, Task&);
|
bool get(const std::string &, Task &);
|
||||||
bool has (const std::string&);
|
bool has(const std::string &);
|
||||||
const std::vector <Task> siblings (Task&);
|
const std::vector<Task> siblings(Task &);
|
||||||
const std::vector <Task> children (Task&);
|
const std::vector<Task> children(Task &);
|
||||||
|
|
||||||
// ID <--> UUID mapping.
|
// ID <--> UUID mapping.
|
||||||
std::string uuid (int);
|
std::string uuid(int);
|
||||||
int id (const std::string&);
|
int id(const std::string &);
|
||||||
|
|
||||||
int num_local_changes ();
|
int num_local_changes();
|
||||||
int num_reverts_possible ();
|
int num_reverts_possible();
|
||||||
|
|
||||||
void dump ();
|
void dump();
|
||||||
|
|
||||||
void sync (tc::Server server, bool avoid_snapshots);
|
void sync(tc::Server server, bool avoid_snapshots);
|
||||||
bool confirm_revert(struct tc::ffi::TCReplicaOpList);
|
bool confirm_revert(struct tc::ffi::TCReplicaOpList);
|
||||||
|
|
||||||
private:
|
private:
|
||||||
tc::Replica replica;
|
tc::Replica replica;
|
||||||
std::optional<tc::WorkingSet> _working_set;
|
std::optional<tc::WorkingSet> _working_set;
|
||||||
|
|
||||||
// UUID -> Task containing all tasks modified in this invocation.
|
// UUID -> Task containing all tasks modified in this invocation.
|
||||||
std::map<std::string, Task> changes;
|
std::map<std::string, Task> changes;
|
||||||
|
|
||||||
const tc::WorkingSet &working_set ();
|
const tc::WorkingSet &working_set();
|
||||||
static std::string option_string (std::string input);
|
static std::string option_string(std::string input);
|
||||||
static void show_diff (const std::string&, const std::string&, const std::string&);
|
static void show_diff(const std::string &, const std::string &, const std::string &);
|
||||||
};
|
};
|
||||||
|
|
||||||
#endif
|
#endif
|
||||||
|
|||||||
2493
src/Task.cpp
2493
src/Task.cpp
File diff suppressed because it is too large
Load Diff
256
src/Task.h
256
src/Task.h
@@ -27,27 +27,27 @@
|
|||||||
#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>
|
||||||
public:
|
#include <vector>
|
||||||
|
|
||||||
|
class Task {
|
||||||
|
public:
|
||||||
static std::string defaultProject;
|
static std::string defaultProject;
|
||||||
static std::string defaultDue;
|
static std::string defaultDue;
|
||||||
static std::string defaultScheduled;
|
static std::string defaultScheduled;
|
||||||
static bool searchCaseSensitive;
|
static bool searchCaseSensitive;
|
||||||
static bool regex;
|
static bool regex;
|
||||||
static std::map <std::string, std::string> attributes; // name -> type
|
static std::map<std::string, std::string> attributes; // name -> type
|
||||||
static std::map <std::string, float> coefficients;
|
static std::map<std::string, float> coefficients;
|
||||||
static std::map <std::string, std::vector <std::string>> customOrder;
|
static std::map<std::string, std::vector<std::string>> customOrder;
|
||||||
static float urgencyProjectCoefficient;
|
static float urgencyProjectCoefficient;
|
||||||
static float urgencyActiveCoefficient;
|
static float urgencyActiveCoefficient;
|
||||||
static float urgencyScheduledCoefficient;
|
static float urgencyScheduledCoefficient;
|
||||||
@@ -60,159 +60,159 @@ public:
|
|||||||
static float urgencyAgeCoefficient;
|
static float urgencyAgeCoefficient;
|
||||||
static float urgencyAgeMax;
|
static float urgencyAgeMax;
|
||||||
|
|
||||||
public:
|
public:
|
||||||
Task () = default;
|
Task() = default;
|
||||||
bool operator== (const Task&);
|
bool operator==(const Task&);
|
||||||
bool operator!= (const Task&);
|
bool operator!=(const Task&);
|
||||||
Task (const std::string&);
|
Task(const std::string&);
|
||||||
Task (const json::object*);
|
Task(const json::object*);
|
||||||
Task (tc::Task);
|
Task(tc::Task);
|
||||||
|
|
||||||
void parse (const std::string&);
|
void parse(const std::string&);
|
||||||
std::string composeJSON (bool decorate = false) const;
|
std::string composeJSON(bool decorate = false) const;
|
||||||
|
|
||||||
// Status values.
|
// Status values.
|
||||||
enum status {pending, completed, deleted, recurring, waiting};
|
enum status { pending, completed, deleted, recurring, waiting };
|
||||||
|
|
||||||
// Date state values.
|
// Date state values.
|
||||||
enum dateState {dateNotDue, dateAfterToday, dateLaterToday, dateEarlierToday, dateBeforeToday};
|
enum dateState { dateNotDue, dateAfterToday, dateLaterToday, dateEarlierToday, dateBeforeToday };
|
||||||
|
|
||||||
// Public data.
|
// Public data.
|
||||||
int id {0};
|
int id{0};
|
||||||
float urgency_value {0.0};
|
float urgency_value{0.0};
|
||||||
bool recalc_urgency {true};
|
bool recalc_urgency{true};
|
||||||
bool is_blocked {false};
|
bool is_blocked{false};
|
||||||
bool is_blocking {false};
|
bool is_blocking{false};
|
||||||
int annotation_count {0};
|
int annotation_count{0};
|
||||||
|
|
||||||
// Series of helper functions.
|
// Series of helper functions.
|
||||||
static status textToStatus (const std::string&);
|
static status textToStatus(const std::string&);
|
||||||
static std::string statusToText (status);
|
static std::string statusToText(status);
|
||||||
static tc::Status status2tc (const Task::status);
|
static tc::Status status2tc(const Task::status);
|
||||||
static Task::status tc2status (const tc::Status);
|
static Task::status tc2status(const tc::Status);
|
||||||
|
|
||||||
void setAsNow (const std::string&);
|
void setAsNow(const std::string&);
|
||||||
bool has (const std::string&) const;
|
bool has(const std::string&) const;
|
||||||
std::vector <std::string> all () const;
|
std::vector<std::string> all() const;
|
||||||
const std::string identifier (bool shortened = false) const;
|
const std::string identifier(bool shortened = false) const;
|
||||||
const std::string get (const std::string&) const;
|
const std::string get(const std::string&) const;
|
||||||
const std::string& get_ref (const std::string&) const;
|
const std::string& get_ref(const std::string&) const;
|
||||||
int get_int (const std::string&) const;
|
int get_int(const std::string&) const;
|
||||||
unsigned long get_ulong (const std::string&) const;
|
unsigned long get_ulong(const std::string&) const;
|
||||||
float get_float (const std::string&) const;
|
float get_float(const std::string&) const;
|
||||||
time_t get_date (const std::string&) const;
|
time_t get_date(const std::string&) const;
|
||||||
void set (const std::string&, const std::string&);
|
void set(const std::string&, const std::string&);
|
||||||
void set (const std::string&, long long);
|
void set(const std::string&, long long);
|
||||||
void remove (const std::string&);
|
void remove(const std::string&);
|
||||||
|
|
||||||
bool is_empty () const;
|
bool is_empty() const;
|
||||||
|
|
||||||
#ifdef PRODUCT_TASKWARRIOR
|
#ifdef PRODUCT_TASKWARRIOR
|
||||||
bool is_ready () const;
|
bool is_ready() const;
|
||||||
bool is_due () const;
|
bool is_due() const;
|
||||||
bool is_dueyesterday () const;
|
bool is_dueyesterday() const;
|
||||||
bool is_duetoday () const;
|
bool is_duetoday() const;
|
||||||
bool is_duetomorrow () const;
|
bool is_duetomorrow() const;
|
||||||
bool is_dueweek () const;
|
bool is_dueweek() const;
|
||||||
bool is_duemonth () const;
|
bool is_duemonth() const;
|
||||||
bool is_duequarter () const;
|
bool is_duequarter() const;
|
||||||
bool is_dueyear () const;
|
bool is_dueyear() const;
|
||||||
bool is_overdue () const;
|
bool is_overdue() const;
|
||||||
bool is_udaPresent () const;
|
bool is_udaPresent() const;
|
||||||
bool is_orphanPresent () const;
|
bool is_orphanPresent() const;
|
||||||
|
|
||||||
static bool isTagAttr (const std::string&);
|
static bool isTagAttr(const std::string&);
|
||||||
static bool isDepAttr (const std::string&);
|
static bool isDepAttr(const std::string&);
|
||||||
static bool isAnnotationAttr (const std::string&);
|
static bool isAnnotationAttr(const std::string&);
|
||||||
#endif
|
#endif
|
||||||
bool is_waiting () const;
|
bool is_waiting() const;
|
||||||
|
|
||||||
status getStatus () const;
|
status getStatus() const;
|
||||||
void setStatus (status);
|
void setStatus(status);
|
||||||
|
|
||||||
#ifdef PRODUCT_TASKWARRIOR
|
#ifdef PRODUCT_TASKWARRIOR
|
||||||
dateState getDateState (const std::string&) const;
|
dateState getDateState(const std::string&) const;
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
int getTagCount () const;
|
int getTagCount() const;
|
||||||
bool hasTag (const std::string&) const;
|
bool hasTag(const std::string&) const;
|
||||||
void addTag (const std::string&);
|
void addTag(const std::string&);
|
||||||
void setTags (const std::vector <std::string>&);
|
void setTags(const std::vector<std::string>&);
|
||||||
std::vector <std::string> getTags () const;
|
std::vector<std::string> getTags() const;
|
||||||
void removeTag (const std::string&);
|
void removeTag(const std::string&);
|
||||||
|
|
||||||
int getAnnotationCount () const;
|
int getAnnotationCount() const;
|
||||||
bool hasAnnotations () const;
|
bool hasAnnotations() const;
|
||||||
std::map <std::string, std::string> getAnnotations () const;
|
std::map<std::string, std::string> getAnnotations() const;
|
||||||
void setAnnotations (const std::map <std::string, std::string>&);
|
void setAnnotations(const std::map<std::string, std::string>&);
|
||||||
void addAnnotation (const std::string&);
|
void addAnnotation(const std::string&);
|
||||||
void removeAnnotations ();
|
void removeAnnotations();
|
||||||
|
|
||||||
#ifdef PRODUCT_TASKWARRIOR
|
#ifdef PRODUCT_TASKWARRIOR
|
||||||
void addDependency (int);
|
void addDependency(int);
|
||||||
#endif
|
#endif
|
||||||
void addDependency (const std::string&);
|
void addDependency(const std::string&);
|
||||||
#ifdef PRODUCT_TASKWARRIOR
|
#ifdef PRODUCT_TASKWARRIOR
|
||||||
void removeDependency (int);
|
void removeDependency(int);
|
||||||
void removeDependency (const std::string&);
|
void removeDependency(const std::string&);
|
||||||
bool hasDependency (const std::string&) const;
|
bool hasDependency(const std::string&) const;
|
||||||
std::vector <int> getDependencyIDs () const;
|
std::vector<int> getDependencyIDs() const;
|
||||||
std::vector <std::string> getDependencyUUIDs () const;
|
std::vector<std::string> getDependencyUUIDs() const;
|
||||||
std::vector <Task> getBlockedTasks () const;
|
std::vector<Task> getBlockedTasks() const;
|
||||||
std::vector <Task> getDependencyTasks () const;
|
std::vector<Task> getDependencyTasks() const;
|
||||||
|
|
||||||
std::vector <std::string> getUDAOrphans () const;
|
std::vector<std::string> getUDAOrphans() const;
|
||||||
|
|
||||||
void substitute (const std::string&, const std::string&, const std::string&);
|
void substitute(const std::string&, const std::string&, const std::string&);
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
void validate (bool applyDefault = true);
|
void validate(bool applyDefault = true);
|
||||||
|
|
||||||
float urgency_c () const;
|
float urgency_c() const;
|
||||||
float urgency ();
|
float urgency();
|
||||||
|
|
||||||
#ifdef PRODUCT_TASKWARRIOR
|
#ifdef PRODUCT_TASKWARRIOR
|
||||||
enum modType {modReplace, modPrepend, modAppend, modAnnotate};
|
enum modType { modReplace, modPrepend, modAppend, modAnnotate };
|
||||||
void modify (modType, bool text_required = false);
|
void modify(modType, bool text_required = false);
|
||||||
#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,
|
||||||
Table diffForUndoSide (const Task& after) const;
|
const long current_timestamp) const;
|
||||||
Table diffForUndoPatch (const Task& after, const Datetime& lastChange) const;
|
Table diffForUndoSide(const Task& after) const;
|
||||||
|
Table diffForUndoPatch(const Task& after, const Datetime& lastChange) const;
|
||||||
|
|
||||||
|
private:
|
||||||
|
int determineVersion(const std::string&);
|
||||||
|
void parseJSON(const std::string&);
|
||||||
|
void parseJSON(const json::object*);
|
||||||
|
void parseTC(const tc::Task&);
|
||||||
|
void parseLegacy(const std::string&);
|
||||||
|
void validate_before(const std::string&, const std::string&);
|
||||||
|
const std::string encode(const std::string&) const;
|
||||||
|
const std::string decode(const std::string&) const;
|
||||||
|
const std::string tag2Attr(const std::string&) const;
|
||||||
|
const std::string attr2Tag(const std::string&) const;
|
||||||
|
const std::string dep2Attr(const std::string&) const;
|
||||||
|
const std::string attr2Dep(const std::string&) const;
|
||||||
|
void fixDependsAttribute();
|
||||||
|
void fixTagsAttribute();
|
||||||
|
|
||||||
private:
|
protected:
|
||||||
int determineVersion (const std::string&);
|
std::map<std::string, std::string> data{};
|
||||||
void parseJSON (const std::string&);
|
|
||||||
void parseJSON (const json::object*);
|
|
||||||
void parseTC (const tc::Task&);
|
|
||||||
void parseLegacy (const std::string&);
|
|
||||||
void validate_before (const std::string&, const std::string&);
|
|
||||||
const std::string encode (const std::string&) const;
|
|
||||||
const std::string decode (const std::string&) const;
|
|
||||||
const std::string tag2Attr (const std::string&) const;
|
|
||||||
const std::string attr2Tag (const std::string&) const;
|
|
||||||
const std::string dep2Attr (const std::string&) const;
|
|
||||||
const std::string attr2Dep (const std::string&) const;
|
|
||||||
void fixDependsAttribute ();
|
|
||||||
void fixTagsAttribute ();
|
|
||||||
|
|
||||||
protected:
|
public:
|
||||||
std::map <std::string, std::string> data {};
|
float urgency_project() const;
|
||||||
|
float urgency_active() const;
|
||||||
public:
|
float urgency_scheduled() const;
|
||||||
float urgency_project () const;
|
float urgency_waiting() const;
|
||||||
float urgency_active () const;
|
float urgency_blocked() const;
|
||||||
float urgency_scheduled () const;
|
float urgency_inherit() const;
|
||||||
float urgency_waiting () const;
|
float urgency_annotations() const;
|
||||||
float urgency_blocked () const;
|
float urgency_tags() const;
|
||||||
float urgency_inherit () const;
|
float urgency_due() const;
|
||||||
float urgency_annotations () const;
|
float urgency_blocking() const;
|
||||||
float urgency_tags () const;
|
float urgency_age() const;
|
||||||
float urgency_due () const;
|
|
||||||
float urgency_blocking () const;
|
|
||||||
float urgency_age () const;
|
|
||||||
};
|
};
|
||||||
|
|
||||||
#endif
|
#endif
|
||||||
|
|||||||
3186
src/Variant.cpp
3186
src/Variant.cpp
File diff suppressed because it is too large
Load Diff
130
src/Variant.h
130
src/Variant.h
@@ -27,94 +27,94 @@
|
|||||||
#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;
|
||||||
static bool searchUsingRegex;
|
static bool searchUsingRegex;
|
||||||
static bool isoEnabled;
|
static bool isoEnabled;
|
||||||
|
|
||||||
enum type {type_boolean, type_integer, type_real, type_string, type_date, type_duration};
|
enum type { type_boolean, type_integer, type_real, type_string, type_date, type_duration };
|
||||||
|
|
||||||
Variant () = default;
|
Variant() = default;
|
||||||
Variant (const Variant&);
|
Variant(const Variant&);
|
||||||
Variant (const bool);
|
Variant(const bool);
|
||||||
Variant (const int);
|
Variant(const int);
|
||||||
Variant (const double);
|
Variant(const double);
|
||||||
Variant (const std::string&);
|
Variant(const std::string&);
|
||||||
Variant (const char*);
|
Variant(const char*);
|
||||||
Variant (const time_t, const enum type);
|
Variant(const time_t, const enum type);
|
||||||
|
|
||||||
void source (const std::string&);
|
void source(const std::string&);
|
||||||
const std::string& source () const;
|
const std::string& source() const;
|
||||||
|
|
||||||
Variant& operator= (const Variant&);
|
Variant& operator=(const Variant&);
|
||||||
|
|
||||||
bool operator&& (const Variant&) const;
|
bool operator&&(const Variant&) const;
|
||||||
bool operator|| (const Variant&) const;
|
bool operator||(const Variant&) const;
|
||||||
bool operator_xor (const Variant&) const;
|
bool operator_xor(const Variant&) const;
|
||||||
bool operator< (const Variant&) const;
|
bool operator<(const Variant&) const;
|
||||||
bool operator<= (const Variant&) const;
|
bool operator<=(const Variant&) const;
|
||||||
bool operator> (const Variant&) const;
|
bool operator>(const Variant&) const;
|
||||||
bool operator>= (const Variant&) const;
|
bool operator>=(const Variant&) const;
|
||||||
bool operator== (const Variant&) const;
|
bool operator==(const Variant&) const;
|
||||||
bool operator!= (const Variant&) const;
|
bool operator!=(const Variant&) const;
|
||||||
bool operator_match (const Variant&, const Task&) const;
|
bool operator_match(const Variant&, const Task&) const;
|
||||||
bool operator_nomatch (const Variant&, const Task&) const;
|
bool operator_nomatch(const Variant&, const Task&) const;
|
||||||
bool operator_partial (const Variant&) const;
|
bool operator_partial(const Variant&) const;
|
||||||
bool operator_nopartial (const Variant&) const;
|
bool operator_nopartial(const Variant&) const;
|
||||||
bool operator_hastag (const Variant&, const Task&) const;
|
bool operator_hastag(const Variant&, const Task&) const;
|
||||||
bool operator_notag (const Variant&, const Task&) const;
|
bool operator_notag(const Variant&, const Task&) const;
|
||||||
bool operator! () const;
|
bool operator!() const;
|
||||||
|
|
||||||
Variant& operator^= (const Variant&);
|
Variant& operator^=(const Variant&);
|
||||||
Variant operator^ (const Variant&) const;
|
Variant operator^(const Variant&) const;
|
||||||
|
|
||||||
Variant& operator-= (const Variant&);
|
Variant& operator-=(const Variant&);
|
||||||
Variant operator- (const Variant&) const;
|
Variant operator-(const Variant&) const;
|
||||||
|
|
||||||
Variant& operator+= (const Variant&);
|
Variant& operator+=(const Variant&);
|
||||||
Variant operator+ (const Variant&) const;
|
Variant operator+(const Variant&) const;
|
||||||
|
|
||||||
Variant& operator*= (const Variant&);
|
Variant& operator*=(const Variant&);
|
||||||
Variant operator* (const Variant&) const;
|
Variant operator*(const Variant&) const;
|
||||||
|
|
||||||
Variant& operator/= (const Variant&);
|
Variant& operator/=(const Variant&);
|
||||||
Variant operator/ (const Variant&) const;
|
Variant operator/(const Variant&) const;
|
||||||
|
|
||||||
Variant& operator%= (const Variant&);
|
Variant& operator%=(const Variant&);
|
||||||
Variant operator% (const Variant&) const;
|
Variant operator%(const Variant&) const;
|
||||||
|
|
||||||
operator std::string () const;
|
operator std::string() const;
|
||||||
void sqrt ();
|
void sqrt();
|
||||||
|
|
||||||
void cast (const enum type);
|
void cast(const enum type);
|
||||||
int type ();
|
int type();
|
||||||
bool trivial () const;
|
bool trivial() const;
|
||||||
|
|
||||||
bool get_bool () const;
|
bool get_bool() const;
|
||||||
long long get_integer () const;
|
long long get_integer() const;
|
||||||
double get_real () const;
|
double get_real() const;
|
||||||
const std::string& get_string () const;
|
const std::string& get_string() const;
|
||||||
time_t get_date () const;
|
time_t get_date() const;
|
||||||
time_t get_duration () const;
|
time_t get_duration() const;
|
||||||
|
|
||||||
private:
|
private:
|
||||||
enum type _type {type_boolean};
|
enum type _type { type_boolean };
|
||||||
bool _bool {false};
|
bool _bool{false};
|
||||||
long long _integer {0};
|
long long _integer{0};
|
||||||
double _real {0.0};
|
double _real{0.0};
|
||||||
std::string _string {""};
|
std::string _string{""};
|
||||||
time_t _date {0};
|
time_t _date{0};
|
||||||
time_t _duration {0};
|
time_t _duration{0};
|
||||||
|
|
||||||
std::string _source {""};
|
std::string _source{""};
|
||||||
};
|
};
|
||||||
|
|
||||||
#endif
|
#endif
|
||||||
|
|||||||
@@ -67,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);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
////////////////////////////////////////////////////////////////////////////////
|
////////////////////////////////////////////////////////////////////////////////
|
||||||
|
|||||||
@@ -31,7 +31,7 @@
|
|||||||
|
|
||||||
// A utility class for handling Taskwarrior versions.
|
// A utility class for handling Taskwarrior versions.
|
||||||
class Version {
|
class Version {
|
||||||
public:
|
public:
|
||||||
// Parse a version from a string. This must be of the format
|
// Parse a version from a string. This must be of the format
|
||||||
// digits.digits.digits.
|
// digits.digits.digits.
|
||||||
explicit Version(std::string version);
|
explicit Version(std::string version);
|
||||||
@@ -60,9 +60,9 @@ public:
|
|||||||
// Convert back to a string.
|
// Convert back to a string.
|
||||||
operator std::string() const;
|
operator std::string() const;
|
||||||
|
|
||||||
friend std::ostream& operator<<(std::ostream& os, const Version& version);
|
friend std::ostream &operator<<(std::ostream &os, const Version &version);
|
||||||
|
|
||||||
private:
|
private:
|
||||||
int major = -1;
|
int major = -1;
|
||||||
int minor = -1;
|
int minor = -1;
|
||||||
int patch = -1;
|
int patch = -1;
|
||||||
|
|||||||
294
src/ViewTask.cpp
294
src/ViewTask.cpp
@@ -27,42 +27,39 @@
|
|||||||
#include <cmake.h>
|
#include <cmake.h>
|
||||||
// cmake.h include header must come first
|
// cmake.h include header must come first
|
||||||
|
|
||||||
#include <ViewTask.h>
|
|
||||||
#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();
|
||||||
}
|
}
|
||||||
|
|
||||||
////////////////////////////////////////////////////////////////////////////////
|
////////////////////////////////////////////////////////////////////////////////
|
||||||
@@ -108,64 +105,55 @@ 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");
|
||||||
bool const print_empty_columns = Context::getContext ().config.getBoolean ("print.empty.columns");
|
bool const print_empty_columns = Context::getContext().config.getBoolean("print.empty.columns");
|
||||||
std::vector <Column*> nonempty_columns;
|
std::vector<Column*> nonempty_columns;
|
||||||
std::vector <bool> nonempty_sort;
|
std::vector<bool> nonempty_sort;
|
||||||
|
|
||||||
// Determine minimal, ideal column widths.
|
// Determine minimal, ideal column widths.
|
||||||
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;
|
||||||
unsigned int ideal = 0;
|
unsigned int ideal = 0;
|
||||||
_columns[i]->measure (data[sequence[s]], min, ideal);
|
_columns[i]->measure(data[sequence[s]], min, ideal);
|
||||||
|
|
||||||
if (min > global_min) global_min = min;
|
if (min > global_min) global_min = min;
|
||||||
if (ideal > global_ideal) global_ideal = ideal;
|
if (ideal > global_ideal) global_ideal = ideal;
|
||||||
|
|
||||||
// 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;
|
||||||
minimal.push_back (global_min);
|
minimal.push_back(global_min);
|
||||||
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.
|
||||||
@@ -174,51 +162,40 @@ std::string ViewTask::render (std::vector <Task>& data, std::vector <int>& seque
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
if (! print_empty_columns)
|
if (!print_empty_columns) {
|
||||||
{
|
|
||||||
_columns = nonempty_columns;
|
_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);
|
||||||
int sum_ideal = std::accumulate (ideal.begin (), ideal.end (), 0);
|
int sum_ideal = std::accumulate(ideal.begin(), ideal.end(), 0);
|
||||||
|
|
||||||
// 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,
|
_width));
|
||||||
overage,
|
|
||||||
_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;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -226,53 +203,46 @@ std::string ViewTask::render (std::vector <Task>& data, std::vector <int>& seque
|
|||||||
// Case 1: Shortening longest column still keeps it longest. Let it bear
|
// 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;
|
||||||
@@ -283,39 +253,34 @@ 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.
|
||||||
std::string left_margin = std::string (_left_margin, ' ');
|
std::string left_margin = std::string(_left_margin, ' ');
|
||||||
std::string extra = std::string (_extra_padding, ' ');
|
std::string extra = std::string(_extra_padding, ' ');
|
||||||
std::string intra = std::string (_intra_padding, ' ');
|
std::string intra = std::string(_intra_padding, ' ');
|
||||||
|
|
||||||
std::string extra_odd = Context::getContext ().color () ? _extra_odd.colorize (extra) : extra;
|
std::string extra_odd = Context::getContext().color() ? _extra_odd.colorize(extra) : extra;
|
||||||
std::string extra_even = Context::getContext ().color () ? _extra_even.colorize (extra) : extra;
|
std::string extra_even = Context::getContext().color() ? _extra_even.colorize(extra) : extra;
|
||||||
std::string intra_odd = Context::getContext ().color () ? _intra_odd.colorize (intra) : intra;
|
std::string intra_odd = Context::getContext().color() ? _intra_odd.colorize(intra) : intra;
|
||||||
std::string intra_even = Context::getContext ().color () ? _intra_even.colorize (intra) : intra;
|
std::string intra_even = Context::getContext().color() ? _intra_even.colorize(intra) : intra;
|
||||||
|
|
||||||
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], ' '));
|
||||||
else
|
else
|
||||||
out += headers[c][i];
|
out += headers[c][i];
|
||||||
}
|
}
|
||||||
@@ -323,60 +288,50 @@ std::string ViewTask::render (std::vector <Task>& data, std::vector <int>& seque
|
|||||||
out += extra;
|
out += extra;
|
||||||
|
|
||||||
// Trim right.
|
// Trim right.
|
||||||
out.erase (out.find_last_not_of (' ') + 1);
|
out.erase(out.find_last_not_of(' ') + 1);
|
||||||
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;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// 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.
|
||||||
Color rule_color;
|
Color rule_color;
|
||||||
autoColorize (data[sequence[s]], rule_color);
|
autoColorize(data[sequence[s]], rule_color);
|
||||||
|
|
||||||
// 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;
|
||||||
|
|
||||||
@@ -386,51 +341,46 @@ std::string ViewTask::render (std::vector <Task>& data, std::vector <int>& seque
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
for (unsigned int i = 0; i < max_lines; ++i)
|
for (unsigned int i = 0; i < max_lines; ++i) {
|
||||||
{
|
|
||||||
out += left_margin + (odd ? extra_odd : extra_even);
|
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())
|
||||||
{
|
row_color._colorize(out, intra);
|
||||||
if (row_color.nontrivial ())
|
|
||||||
row_color._colorize (out, intra);
|
|
||||||
else
|
else
|
||||||
out += (odd ? intra_odd : intra_even);
|
out += (odd ? intra_odd : intra_even);
|
||||||
}
|
}
|
||||||
|
|
||||||
if (i < cells[c].size ())
|
if (i < cells[c].size())
|
||||||
out += cells[c][i];
|
out += cells[c][i];
|
||||||
else
|
else
|
||||||
row_color._colorize (out, std::string (widths[c], ' '));
|
row_color._colorize(out, std::string(widths[c], ' '));
|
||||||
}
|
}
|
||||||
|
|
||||||
out += (odd ? extra_odd : extra_even);
|
out += (odd ? extra_odd : extra_even);
|
||||||
|
|
||||||
// Trim right.
|
// Trim right.
|
||||||
out.erase (out.find_last_not_of (' ') + 1);
|
out.erase(out.find_last_not_of(' ') + 1);
|
||||||
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;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
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;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
Context::getContext ().time_render_us += timer.total_us ();
|
Context::getContext().time_render_us += timer.total_us();
|
||||||
return out;
|
return out;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
101
src/ViewTask.h
101
src/ViewTask.h
@@ -27,63 +27,68 @@
|
|||||||
#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>
|
||||||
public:
|
|
||||||
ViewTask ();
|
class ViewTask {
|
||||||
~ViewTask ();
|
public:
|
||||||
|
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) {
|
||||||
void width (int width) { _width = width; }
|
_columns.push_back(column);
|
||||||
void leftMargin (int margin) { _left_margin = margin; }
|
_sort.push_back(sort);
|
||||||
void colorHeader (Color& c) { _header = c; if (!_sort_header) _sort_header = c; }
|
}
|
||||||
void colorSortHeader (Color& c) { _sort_header = c; }
|
void width(int width) { _width = width; }
|
||||||
void colorOdd (Color& c) { _odd = c; }
|
void leftMargin(int margin) { _left_margin = margin; }
|
||||||
void colorEven (Color& c) { _even = c; }
|
void colorHeader(Color& c) {
|
||||||
void intraPadding (int padding) { _intra_padding = padding; }
|
_header = c;
|
||||||
void intraColorOdd (Color& c) { _intra_odd = c; }
|
if (!_sort_header) _sort_header = c;
|
||||||
void intraColorEven (Color& c) { _intra_even = c; }
|
}
|
||||||
void extraPadding (int padding) { _extra_padding = padding; }
|
void colorSortHeader(Color& c) { _sort_header = c; }
|
||||||
void extraColorOdd (Color& c) { _extra_odd = c; }
|
void colorOdd(Color& c) { _odd = c; }
|
||||||
void extraColorEven (Color& c) { _extra_even = c; }
|
void colorEven(Color& c) { _even = c; }
|
||||||
void truncateLines (int n) { _truncate_lines = n; }
|
void intraPadding(int padding) { _intra_padding = padding; }
|
||||||
void truncateRows (int n) { _truncate_rows = n; }
|
void intraColorOdd(Color& c) { _intra_odd = c; }
|
||||||
void addBreak (const std::string& attr) { _breaks.push_back (attr); }
|
void intraColorEven(Color& c) { _intra_even = c; }
|
||||||
int lines () { return _lines; }
|
void extraPadding(int padding) { _extra_padding = padding; }
|
||||||
int rows () { return _rows; }
|
void extraColorOdd(Color& c) { _extra_odd = c; }
|
||||||
|
void extraColorEven(Color& c) { _extra_even = c; }
|
||||||
|
void truncateLines(int n) { _truncate_lines = n; }
|
||||||
|
void truncateRows(int n) { _truncate_rows = n; }
|
||||||
|
void addBreak(const std::string& attr) { _breaks.push_back(attr); }
|
||||||
|
int lines() { return _lines; }
|
||||||
|
int rows() { return _rows; }
|
||||||
|
|
||||||
// View rendering.
|
// View rendering.
|
||||||
std::string render (std::vector <Task>&, std::vector <int>&);
|
std::string render(std::vector<Task>&, std::vector<int>&);
|
||||||
|
|
||||||
private:
|
private:
|
||||||
std::vector <Column*> _columns;
|
std::vector<Column*> _columns;
|
||||||
std::vector <bool> _sort;
|
std::vector<bool> _sort;
|
||||||
std::vector <std::string> _breaks;
|
std::vector<std::string> _breaks;
|
||||||
int _width;
|
int _width;
|
||||||
int _left_margin;
|
int _left_margin;
|
||||||
Color _header;
|
Color _header;
|
||||||
Color _sort_header;
|
Color _sort_header;
|
||||||
Color _odd;
|
Color _odd;
|
||||||
Color _even;
|
Color _even;
|
||||||
int _intra_padding;
|
int _intra_padding;
|
||||||
Color _intra_odd;
|
Color _intra_odd;
|
||||||
Color _intra_even;
|
Color _intra_even;
|
||||||
int _extra_padding;
|
int _extra_padding;
|
||||||
Color _extra_odd;
|
Color _extra_odd;
|
||||||
Color _extra_even;
|
Color _extra_even;
|
||||||
int _truncate_lines;
|
int _truncate_lines;
|
||||||
int _truncate_rows;
|
int _truncate_rows;
|
||||||
int _lines;
|
int _lines;
|
||||||
int _rows;
|
int _rows;
|
||||||
};
|
};
|
||||||
|
|
||||||
#endif
|
#endif
|
||||||
////////////////////////////////////////////////////////////////////////////////
|
////////////////////////////////////////////////////////////////////////////////
|
||||||
|
|
||||||
|
|||||||
93
src/calc.cpp
93
src/calc.cpp
@@ -27,57 +27,53 @@
|
|||||||
#include <cmake.h>
|
#include <cmake.h>
|
||||||
// cmake.h include header must come first
|
// cmake.h include header must come first
|
||||||
|
|
||||||
#include <iostream>
|
|
||||||
#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;}
|
*/
|
||||||
*/
|
|
||||||
|
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
////////////////////////////////////////////////////////////////////////////////
|
////////////////////////////////////////////////////////////////////////////////
|
||||||
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);
|
||||||
|
|
||||||
// Same operating parameters as Context::staticInitialization.
|
// Same operating parameters as Context::staticInitialization.
|
||||||
Datetime::standaloneDateEnabled = false;
|
Datetime::standaloneDateEnabled = false;
|
||||||
Datetime::standaloneTimeEnabled = false;
|
Datetime::standaloneTimeEnabled = false;
|
||||||
Duration::standaloneSecondsEnabled = false;
|
Duration::standaloneSecondsEnabled = false;
|
||||||
|
|
||||||
bool infix {true};
|
bool infix{true};
|
||||||
|
|
||||||
// Add a source for constants.
|
// Add a source for constants.
|
||||||
Eval e;
|
Eval e;
|
||||||
e.addSource (get);
|
e.addSource(get);
|
||||||
|
|
||||||
// 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'
|
||||||
@@ -87,56 +83,47 @@ int main (int argc, char** argv)
|
|||||||
<< " -i|--infix Infix expression (default)\n"
|
<< " -i|--infix Infix expression (default)\n"
|
||||||
<< " -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;
|
||||||
else if (!strcmp (argv[i], "-p") || !strcmp (argv[i], "--postfix"))
|
else if (!strcmp(argv[i], "-p") || !strcmp(argv[i], "--postfix"))
|
||||||
infix = false;
|
infix = false;
|
||||||
else
|
else
|
||||||
expression += std::string (argv[i]) + ' ';
|
expression += std::string(argv[i]) + ' ';
|
||||||
}
|
}
|
||||||
|
|
||||||
Variant result;
|
Variant result;
|
||||||
if (infix)
|
if (infix)
|
||||||
e.evaluateInfixExpression (expression, result);
|
e.evaluateInfixExpression(expression, result);
|
||||||
else
|
else
|
||||||
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;
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -28,30 +28,26 @@
|
|||||||
// cmake.h include header must come first
|
// 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", "count", "indicator"};
|
||||||
_styles = {"list",
|
_examples = {"1 2 10", "[3]", Context::getContext().config.get("dependency.indicator")};
|
||||||
"count",
|
|
||||||
"indicator"};
|
|
||||||
_examples = {"1 2 10",
|
|
||||||
"[3]",
|
|
||||||
Context::getContext ().config.get ("dependency.indicator")};
|
|
||||||
|
|
||||||
_hyphenate = false;
|
_hyphenate = false;
|
||||||
}
|
}
|
||||||
@@ -59,156 +55,131 @@ 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) minimum = length;
|
||||||
if (length > minimum)
|
|
||||||
minimum = length;
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
////////////////////////////////////////////////////////////////////////////////
|
////////////////////////////////////////////////////////////////////////////////
|
||||||
void ColumnDepends::render (
|
void ColumnDepends::render(std::vector<std::string>& lines, Task& task, int width, Color& color) {
|
||||||
std::vector <std::string>& lines,
|
auto deptasks = task.getDependencyTasks();
|
||||||
Task& task,
|
|
||||||
int width,
|
|
||||||
Color& color)
|
|
||||||
{
|
|
||||||
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);
|
||||||
}
|
}
|
||||||
|
|
||||||
auto hyphen = dep.find ('-');
|
auto hyphen = dep.find('-');
|
||||||
long lower, upper; // For ID ranges
|
long lower, upper; // For ID ranges
|
||||||
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)
|
task.removeDependency(dep);
|
||||||
task.removeDependency (dep);
|
else
|
||||||
else
|
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)
|
task.removeDependency(loaded_task.get("uuid"));
|
||||||
task.removeDependency (loaded_task.get ("uuid"));
|
else
|
||||||
else
|
task.addDependency(loaded_task.get("uuid"));
|
||||||
task.addDependency (loaded_task.get ("uuid"));
|
else
|
||||||
else
|
throw format("Dependency could not be set - task with UUID '{1}' does not exist.", dep);
|
||||||
throw format ("Dependency could not be set - task with UUID '{1}' does not exist.", dep);
|
}
|
||||||
}
|
|
||||||
}
|
}
|
||||||
// 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);
|
||||||
else
|
else
|
||||||
task.addDependency (i);
|
task.addDependency(i);
|
||||||
}
|
}
|
||||||
// Simple ID
|
// Simple ID
|
||||||
else if (extractLongInteger (dep, lower))
|
else if (extractLongInteger(dep, lower))
|
||||||
if (removal)
|
if (removal)
|
||||||
task.removeDependency (lower);
|
task.removeDependency(lower);
|
||||||
else
|
else
|
||||||
task.addDependency (lower);
|
task.addDependency(lower);
|
||||||
else
|
else
|
||||||
throw format ("Invalid dependency value: '{1}'", dep);
|
throw format("Invalid dependency value: '{1}'", dep);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -29,17 +29,16 @@
|
|||||||
|
|
||||||
#include <ColTypeString.h>
|
#include <ColTypeString.h>
|
||||||
|
|
||||||
class ColumnDepends : public ColumnTypeString
|
class ColumnDepends : public ColumnTypeString {
|
||||||
{
|
public:
|
||||||
public:
|
ColumnDepends();
|
||||||
ColumnDepends ();
|
|
||||||
|
|
||||||
void setStyle (const std::string&);
|
void setStyle(const std::string&);
|
||||||
void measure (Task&, unsigned int&, unsigned int&);
|
void measure(Task&, unsigned int&, unsigned int&);
|
||||||
void render (std::vector <std::string>&, Task&, int, Color&);
|
void render(std::vector<std::string>&, Task&, int, Color&);
|
||||||
void modify (Task&, const std::string&);
|
void modify(Task&, const std::string&);
|
||||||
|
|
||||||
private:
|
private:
|
||||||
bool _hyphenate;
|
bool _hyphenate;
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|||||||
@@ -28,233 +28,188 @@
|
|||||||
// cmake.h include header must come first
|
// 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";
|
||||||
std::string a1 = "Immediately before your lunch";
|
std::string a1 = "Immediately before your lunch";
|
||||||
std::string a2 = "If you are playing in the match this afternoon";
|
std::string a2 = "If you are playing in the match this afternoon";
|
||||||
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 + ' ' + t + ' ' + a2 + ' ' + t + ' ' + a3 + ' ' + t + ' ' + a4,
|
||||||
d + ' ' + t + ' ' + a1
|
d.substr(0, 20) + "...",
|
||||||
+ ' ' + t + ' ' + a2
|
d + " [4]",
|
||||||
+ ' ' + t + ' ' + a3
|
d.substr(0, 20) + "... [4]"};
|
||||||
+ ' ' + t + ' ' + a4,
|
|
||||||
d.substr (0, 20) + "...",
|
|
||||||
d + " [4]",
|
|
||||||
d.substr (0, 20) + "... [4]"};
|
|
||||||
|
|
||||||
_hyphenate = Context::getContext ().config.getBoolean ("hyphenate");
|
_hyphenate = Context::getContext().config.getBoolean("hyphenate");
|
||||||
|
|
||||||
_indent = Context::getContext ().config.getInteger ("indent.annotation");
|
_indent = Context::getContext().config.getInteger("indent.annotation");
|
||||||
}
|
}
|
||||||
|
|
||||||
////////////////////////////////////////////////////////////////////////////////
|
////////////////////////////////////////////////////////////////////////////////
|
||||||
// Set the minimum and maximum widths for the value.
|
// 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);
|
||||||
{
|
maximum = utf8_width(description);
|
||||||
minimum = longestWord (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) minimum = min_anno;
|
||||||
if (min_anno > minimum)
|
|
||||||
minimum = min_anno;
|
|
||||||
|
|
||||||
for (auto& i : task.getAnnotations ())
|
for (auto& i : task.getAnnotations()) {
|
||||||
{
|
unsigned int len = min_anno + 1 + utf8_width(i.second);
|
||||||
unsigned int len = min_anno + 1 + utf8_width (i.second);
|
if (len > maximum) maximum = len;
|
||||||
if (len > maximum)
|
|
||||||
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()) maximum += min_anno + 1 + utf8_width(i.second);
|
||||||
for (auto& i : task.getAnnotations ())
|
|
||||||
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,
|
std::string description = task.get(_name);
|
||||||
int width,
|
|
||||||
Color& color)
|
|
||||||
{
|
|
||||||
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)
|
Datetime dt(strtoll(i.first.substr(11).c_str(), nullptr, 10));
|
||||||
{
|
description += '\n' + std::string(_indent, ' ') + dt.toString(_dateformat) + ' ' + i.second;
|
||||||
for (const auto& i : task.getAnnotations ())
|
|
||||||
{
|
|
||||||
Datetime dt (strtoll (i.first.substr (11).c_str (), nullptr, 10));
|
|
||||||
description += '\n' + std::string (_indent, ' ') + dt.toString (_dateformat) + ' ' + i.second;
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
std::vector <std::string> raw;
|
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()) {
|
||||||
{
|
Datetime dt(strtoll(i.first.substr(11).c_str(), nullptr, 10));
|
||||||
for (const auto& i : task.getAnnotations ())
|
description += ' ' + dt.toString(_dateformat) + ' ' + i.second;
|
||||||
{
|
|
||||||
Datetime dt (strtoll (i.first.substr (11).c_str (), nullptr, 10));
|
|
||||||
description += ' ' + dt.toString (_dateformat) + ' ' + i.second;
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
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) + "...");
|
||||||
else
|
else
|
||||||
renderStringLeft (lines, width, color, description);
|
renderStringLeft(lines, width, color, description);
|
||||||
}
|
}
|
||||||
|
|
||||||
// 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);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -29,14 +29,13 @@
|
|||||||
|
|
||||||
#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&);
|
void render(std::vector<std::string>&, Task&, int, Color&);
|
||||||
void render (std::vector <std::string>&, Task&, int, Color&);
|
|
||||||
|
|
||||||
private:
|
private:
|
||||||
bool _hyphenate;
|
bool _hyphenate;
|
||||||
std::string _dateformat;
|
std::string _dateformat;
|
||||||
int _indent;
|
int _indent;
|
||||||
|
|||||||
@@ -30,22 +30,19 @@
|
|||||||
#include <ColDue.h>
|
#include <ColDue.h>
|
||||||
|
|
||||||
////////////////////////////////////////////////////////////////////////////////
|
////////////////////////////////////////////////////////////////////////////////
|
||||||
ColumnDue::ColumnDue ()
|
ColumnDue::ColumnDue() {
|
||||||
{
|
_name = "due";
|
||||||
_name = "due";
|
|
||||||
_modifiable = true;
|
_modifiable = true;
|
||||||
_label = "Due";
|
_label = "Due";
|
||||||
}
|
}
|
||||||
|
|
||||||
////////////////////////////////////////////////////////////////////////////////
|
////////////////////////////////////////////////////////////////////////////////
|
||||||
// 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";
|
|
||||||
}
|
}
|
||||||
|
|
||||||
////////////////////////////////////////////////////////////////////////////////
|
////////////////////////////////////////////////////////////////////////////////
|
||||||
|
|||||||
@@ -29,11 +29,10 @@
|
|||||||
|
|
||||||
#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&);
|
|
||||||
};
|
};
|
||||||
|
|
||||||
#endif
|
#endif
|
||||||
|
|||||||
@@ -30,9 +30,8 @@
|
|||||||
#include <ColEnd.h>
|
#include <ColEnd.h>
|
||||||
|
|
||||||
////////////////////////////////////////////////////////////////////////////////
|
////////////////////////////////////////////////////////////////////////////////
|
||||||
ColumnEnd::ColumnEnd ()
|
ColumnEnd::ColumnEnd() {
|
||||||
{
|
_name = "end";
|
||||||
_name = "end";
|
|
||||||
_label = "Completed";
|
_label = "Completed";
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -29,10 +29,9 @@
|
|||||||
|
|
||||||
#include <ColTypeDate.h>
|
#include <ColTypeDate.h>
|
||||||
|
|
||||||
class ColumnEnd : public ColumnTypeDate
|
class ColumnEnd : public ColumnTypeDate {
|
||||||
{
|
public:
|
||||||
public:
|
ColumnEnd();
|
||||||
ColumnEnd ();
|
|
||||||
};
|
};
|
||||||
|
|
||||||
#endif
|
#endif
|
||||||
|
|||||||
@@ -30,23 +30,19 @@
|
|||||||
#include <ColEntry.h>
|
#include <ColEntry.h>
|
||||||
|
|
||||||
////////////////////////////////////////////////////////////////////////////////
|
////////////////////////////////////////////////////////////////////////////////
|
||||||
ColumnEntry::ColumnEntry ()
|
ColumnEntry::ColumnEntry() {
|
||||||
{
|
_name = "entry";
|
||||||
_name = "entry";
|
|
||||||
_modifiable = true;
|
_modifiable = true;
|
||||||
_label = "Added";
|
_label = "Added";
|
||||||
}
|
}
|
||||||
|
|
||||||
////////////////////////////////////////////////////////////////////////////////
|
////////////////////////////////////////////////////////////////////////////////
|
||||||
// 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";
|
|
||||||
}
|
}
|
||||||
|
|
||||||
////////////////////////////////////////////////////////////////////////////////
|
////////////////////////////////////////////////////////////////////////////////
|
||||||
|
|||||||
@@ -29,11 +29,10 @@
|
|||||||
|
|
||||||
#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&);
|
|
||||||
};
|
};
|
||||||
|
|
||||||
#endif
|
#endif
|
||||||
|
|||||||
@@ -28,48 +28,47 @@
|
|||||||
// cmake.h include header must come first
|
// 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";
|
|
||||||
_modifiable = false;
|
_modifiable = false;
|
||||||
_styles = {"number"};
|
_styles = {"number"};
|
||||||
_examples = {"123"};
|
_examples = {"123"};
|
||||||
}
|
}
|
||||||
|
|
||||||
////////////////////////////////////////////////////////////////////////////////
|
////////////////////////////////////////////////////////////////////////////////
|
||||||
// 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,
|
// Completed and deleted tasks have no ID.
|
||||||
Task& task,
|
|
||||||
int width,
|
|
||||||
Color& color)
|
|
||||||
{
|
|
||||||
// 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);
|
||||||
else
|
else
|
||||||
renderStringRight (lines, width, color, "-");
|
renderStringRight(lines, width, color, "-");
|
||||||
}
|
}
|
||||||
|
|
||||||
////////////////////////////////////////////////////////////////////////////////
|
////////////////////////////////////////////////////////////////////////////////
|
||||||
|
|||||||
@@ -29,14 +29,13 @@
|
|||||||
|
|
||||||
#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&);
|
void render(std::vector<std::string>&, Task&, int, Color&);
|
||||||
void render (std::vector <std::string>&, Task&, int, Color&);
|
|
||||||
|
|
||||||
private:
|
private:
|
||||||
};
|
};
|
||||||
|
|
||||||
#endif
|
#endif
|
||||||
|
|||||||
@@ -31,34 +31,25 @@
|
|||||||
#include <format.h>
|
#include <format.h>
|
||||||
|
|
||||||
////////////////////////////////////////////////////////////////////////////////
|
////////////////////////////////////////////////////////////////////////////////
|
||||||
ColumnIMask::ColumnIMask ()
|
ColumnIMask::ColumnIMask() {
|
||||||
{
|
_name = "imask";
|
||||||
_name = "imask";
|
_style = "number";
|
||||||
_style = "number";
|
_label = "Mask Index";
|
||||||
_label = "Mask Index";
|
|
||||||
_modifiable = false;
|
_modifiable = false;
|
||||||
_styles = {"number"};
|
_styles = {"number"};
|
||||||
_examples = {"12"};
|
_examples = {"12"};
|
||||||
}
|
}
|
||||||
|
|
||||||
////////////////////////////////////////////////////////////////////////////////
|
////////////////////////////////////////////////////////////////////////////////
|
||||||
// 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));
|
|
||||||
}
|
}
|
||||||
|
|
||||||
////////////////////////////////////////////////////////////////////////////////
|
////////////////////////////////////////////////////////////////////////////////
|
||||||
|
|||||||
@@ -29,14 +29,13 @@
|
|||||||
|
|
||||||
#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&);
|
void render(std::vector<std::string>&, Task&, int, Color&);
|
||||||
void render (std::vector <std::string>&, Task&, int, Color&);
|
|
||||||
|
|
||||||
private:
|
private:
|
||||||
};
|
};
|
||||||
|
|
||||||
#endif
|
#endif
|
||||||
|
|||||||
@@ -31,34 +31,25 @@
|
|||||||
#include <format.h>
|
#include <format.h>
|
||||||
|
|
||||||
////////////////////////////////////////////////////////////////////////////////
|
////////////////////////////////////////////////////////////////////////////////
|
||||||
ColumnLast::ColumnLast ()
|
ColumnLast::ColumnLast() {
|
||||||
{
|
_name = "last";
|
||||||
_name = "last";
|
_style = "number";
|
||||||
_style = "number";
|
_label = "Last instance";
|
||||||
_label = "Last instance";
|
|
||||||
_modifiable = false;
|
_modifiable = false;
|
||||||
_styles = {"number"};
|
_styles = {"number"};
|
||||||
_examples = {"12"};
|
_examples = {"12"};
|
||||||
}
|
}
|
||||||
|
|
||||||
////////////////////////////////////////////////////////////////////////////////
|
////////////////////////////////////////////////////////////////////////////////
|
||||||
// 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));
|
|
||||||
}
|
}
|
||||||
|
|
||||||
////////////////////////////////////////////////////////////////////////////////
|
////////////////////////////////////////////////////////////////////////////////
|
||||||
|
|||||||
@@ -29,14 +29,13 @@
|
|||||||
|
|
||||||
#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&);
|
void render(std::vector<std::string>&, Task&, int, Color&);
|
||||||
void render (std::vector <std::string>&, Task&, int, Color&);
|
|
||||||
|
|
||||||
private:
|
private:
|
||||||
};
|
};
|
||||||
|
|
||||||
#endif
|
#endif
|
||||||
|
|||||||
@@ -31,34 +31,25 @@
|
|||||||
#include <format.h>
|
#include <format.h>
|
||||||
|
|
||||||
////////////////////////////////////////////////////////////////////////////////
|
////////////////////////////////////////////////////////////////////////////////
|
||||||
ColumnMask::ColumnMask ()
|
ColumnMask::ColumnMask() {
|
||||||
{
|
_name = "mask";
|
||||||
_name = "mask";
|
_style = "default";
|
||||||
_style = "default";
|
_label = "Mask";
|
||||||
_label = "Mask";
|
|
||||||
_modifiable = false;
|
_modifiable = false;
|
||||||
_styles = {"default"};
|
_styles = {"default"};
|
||||||
_examples = {"++++---"};
|
_examples = {"++++---"};
|
||||||
}
|
}
|
||||||
|
|
||||||
////////////////////////////////////////////////////////////////////////////////
|
////////////////////////////////////////////////////////////////////////////////
|
||||||
// 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));
|
|
||||||
}
|
}
|
||||||
|
|
||||||
////////////////////////////////////////////////////////////////////////////////
|
////////////////////////////////////////////////////////////////////////////////
|
||||||
|
|||||||
@@ -29,14 +29,13 @@
|
|||||||
|
|
||||||
#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&);
|
void render(std::vector<std::string>&, Task&, int, Color&);
|
||||||
void render (std::vector <std::string>&, Task&, int, Color&);
|
|
||||||
|
|
||||||
private:
|
private:
|
||||||
};
|
};
|
||||||
|
|
||||||
#endif
|
#endif
|
||||||
|
|||||||
@@ -30,9 +30,8 @@
|
|||||||
#include <ColModified.h>
|
#include <ColModified.h>
|
||||||
|
|
||||||
////////////////////////////////////////////////////////////////////////////////
|
////////////////////////////////////////////////////////////////////////////////
|
||||||
ColumnModified::ColumnModified ()
|
ColumnModified::ColumnModified() {
|
||||||
{
|
_name = "modified";
|
||||||
_name = "modified";
|
|
||||||
_label = "Modified";
|
_label = "Modified";
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -29,10 +29,9 @@
|
|||||||
|
|
||||||
#include <ColTypeDate.h>
|
#include <ColTypeDate.h>
|
||||||
|
|
||||||
class ColumnModified : public ColumnTypeDate
|
class ColumnModified : public ColumnTypeDate {
|
||||||
{
|
public:
|
||||||
public:
|
ColumnModified();
|
||||||
ColumnModified ();
|
|
||||||
};
|
};
|
||||||
|
|
||||||
#endif
|
#endif
|
||||||
|
|||||||
@@ -31,45 +31,37 @@
|
|||||||
#include <format.h>
|
#include <format.h>
|
||||||
|
|
||||||
////////////////////////////////////////////////////////////////////////////////
|
////////////////////////////////////////////////////////////////////////////////
|
||||||
ColumnParent::ColumnParent ()
|
ColumnParent::ColumnParent() {
|
||||||
{
|
_name = "parent";
|
||||||
_name = "parent";
|
_style = "long";
|
||||||
_style = "long";
|
_label = "Parent task";
|
||||||
_label = "Parent task";
|
|
||||||
_modifiable = false;
|
_modifiable = false;
|
||||||
_styles = {"long", "short"};
|
_styles = {"long", "short"};
|
||||||
_examples = {"f30cb9c3-3fc0-483f-bfb2-3bf134f00694", "f30cb9c3"};
|
_examples = {"f30cb9c3-3fc0-483f-bfb2-3bf134f00694", "f30cb9c3"};
|
||||||
}
|
}
|
||||||
|
|
||||||
////////////////////////////////////////////////////////////////////////////////
|
////////////////////////////////////////////////////////////////////////////////
|
||||||
// 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")
|
||||||
renderStringLeft (lines, width, color, task.get (_name).substr (0, 8));
|
renderStringLeft(lines, width, color, task.get(_name).substr(0, 8));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -29,14 +29,13 @@
|
|||||||
|
|
||||||
#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&);
|
void render(std::vector<std::string>&, Task&, int, Color&);
|
||||||
void render (std::vector <std::string>&, Task&, int, Color&);
|
|
||||||
|
|
||||||
private:
|
private:
|
||||||
};
|
};
|
||||||
|
|
||||||
#endif
|
#endif
|
||||||
|
|||||||
@@ -30,120 +30,91 @@
|
|||||||
#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", "home", " home.garden"};
|
||||||
_examples = {"home.garden",
|
_hyphenate = Context::getContext().config.getBoolean("hyphenate");
|
||||||
"home",
|
|
||||||
" home.garden"};
|
|
||||||
_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) project = project.substr(0, period);
|
||||||
if (period != std::string::npos)
|
} else if (_style == "indented") {
|
||||||
project = project.substr (0, period);
|
project = indentProject(project, " ", '.');
|
||||||
}
|
|
||||||
else if (_style == "indented")
|
|
||||||
{
|
|
||||||
project = indentProject (project, " ", '.');
|
|
||||||
}
|
}
|
||||||
|
|
||||||
minimum = longestWord (project);
|
minimum = longestWord(project);
|
||||||
maximum = utf8_width (project);
|
maximum = utf8_width(project);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
////////////////////////////////////////////////////////////////////////////////
|
////////////////////////////////////////////////////////////////////////////////
|
||||||
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,
|
std::string project = task.get(_name);
|
||||||
int width,
|
if (_style == "parent") {
|
||||||
Color& color)
|
auto period = project.find('.');
|
||||||
{
|
if (period != std::string::npos) project = project.substr(0, period);
|
||||||
if (task.has (_name))
|
} else if (_style == "indented") {
|
||||||
{
|
project = indentProject(project, " ", '.');
|
||||||
std::string project = task.get (_name);
|
|
||||||
if (_style == "parent")
|
|
||||||
{
|
|
||||||
auto period = project.find ('.');
|
|
||||||
if (period != std::string::npos)
|
|
||||||
project = project.substr (0, period);
|
|
||||||
}
|
|
||||||
else if (_style == "indented")
|
|
||||||
{
|
|
||||||
project = indentProject (project, " ", '.');
|
|
||||||
}
|
}
|
||||||
|
|
||||||
std::vector <std::string> raw;
|
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 = " [1;37;43mMODIFICATION[0m ";
|
std::string label = " [1;37;43mMODIFICATION[0m ";
|
||||||
|
|
||||||
// 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);
|
||||||
{
|
Context::getContext().debug(label + _name + " <-- '" + value + '\'');
|
||||||
task.set (_name, value);
|
|
||||||
Context::getContext ().debug (label + _name + " <-- '" + value + '\'');
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -29,15 +29,14 @@
|
|||||||
|
|
||||||
#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&);
|
void render(std::vector<std::string>&, Task&, int, Color&);
|
||||||
void render (std::vector <std::string>&, Task&, int, Color&);
|
void modify(Task&, const std::string&);
|
||||||
void modify (Task&, const std::string&);
|
|
||||||
|
|
||||||
private:
|
private:
|
||||||
bool _hyphenate;
|
bool _hyphenate;
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|||||||
@@ -29,72 +29,60 @@
|
|||||||
|
|
||||||
#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";
|
|
||||||
_modifiable = false;
|
_modifiable = false;
|
||||||
_styles = {"default", "indicator"};
|
_styles = {"default", "indicator"};
|
||||||
_examples = {"periodic", "chained"};
|
_examples = {"periodic", "chained"};
|
||||||
}
|
}
|
||||||
|
|
||||||
////////////////////////////////////////////////////////////////////////////////
|
////////////////////////////////////////////////////////////////////////////////
|
||||||
// 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")
|
||||||
_label = _label.substr (0, Context::getContext ().config.get ("rtype.indicator").length ());
|
_label = _label.substr(0, Context::getContext().config.get("rtype.indicator").length());
|
||||||
}
|
}
|
||||||
|
|
||||||
////////////////////////////////////////////////////////////////////////////////
|
////////////////////////////////////////////////////////////////////////////////
|
||||||
// Set the minimum and maximum widths for the value.
|
// 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")
|
||||||
minimum = maximum = 1;
|
minimum = maximum = 1;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
////////////////////////////////////////////////////////////////////////////////
|
////////////////////////////////////////////////////////////////////////////////
|
||||||
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);
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
////////////////////////////////////////////////////////////////////////////////
|
////////////////////////////////////////////////////////////////////////////////
|
||||||
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";
|
|
||||||
}
|
}
|
||||||
|
|
||||||
////////////////////////////////////////////////////////////////////////////////
|
////////////////////////////////////////////////////////////////////////////////
|
||||||
|
|||||||
@@ -29,16 +29,15 @@
|
|||||||
|
|
||||||
#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&);
|
void measure(Task&, unsigned int&, unsigned int&);
|
||||||
void measure (Task&, unsigned int&, unsigned int&);
|
void render(std::vector<std::string>&, Task&, int, Color&);
|
||||||
void render (std::vector <std::string>&, Task&, int, Color&);
|
bool validate(const std::string&) const;
|
||||||
bool validate (const std::string&) const;
|
|
||||||
|
|
||||||
private:
|
private:
|
||||||
};
|
};
|
||||||
|
|
||||||
#endif
|
#endif
|
||||||
|
|||||||
@@ -31,100 +31,81 @@
|
|||||||
#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";
|
|
||||||
_modifiable = true;
|
_modifiable = true;
|
||||||
_styles = {"duration", "indicator"};
|
_styles = {"duration", "indicator"};
|
||||||
_examples = {"weekly", Context::getContext ().config.get ("recurrence.indicator")};
|
_examples = {"weekly", Context::getContext().config.get("recurrence.indicator")};
|
||||||
}
|
}
|
||||||
|
|
||||||
////////////////////////////////////////////////////////////////////////////////
|
////////////////////////////////////////////////////////////////////////////////
|
||||||
// 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")
|
||||||
_label = _label.substr (0, Context::getContext ().config.get ("recurrence.indicator").length ());
|
_label = _label.substr(0, Context::getContext().config.get("recurrence.indicator").length());
|
||||||
}
|
}
|
||||||
|
|
||||||
////////////////////////////////////////////////////////////////////////////////
|
////////////////////////////////////////////////////////////////////////////////
|
||||||
// Set the minimum and maximum widths for the value.
|
// 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" ||
|
minimum = maximum = Duration(task.get(_name)).formatISO().length();
|
||||||
_style == "duration")
|
} else if (_style == "indicator") {
|
||||||
{
|
minimum = maximum = utf8_width(Context::getContext().config.get("recurrence.indicator"));
|
||||||
minimum = maximum = Duration (task.get (_name)).formatISO ().length ();
|
|
||||||
}
|
|
||||||
else if (_style == "indicator")
|
|
||||||
{
|
|
||||||
minimum = maximum = utf8_width (Context::getContext ().config.get ("recurrence.indicator"));
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
////////////////////////////////////////////////////////////////////////////////
|
////////////////////////////////////////////////////////////////////////////////
|
||||||
void ColumnRecur::render (
|
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,
|
renderStringRight(lines, width, color, Duration(task.get(_name)).formatISO());
|
||||||
Color& color)
|
|
||||||
{
|
|
||||||
if (task.has (_name))
|
|
||||||
{
|
|
||||||
if (_style == "default" ||
|
|
||||||
_style == "duration")
|
|
||||||
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 = " [1;37;43mMODIFICATION[0m ";
|
std::string label = " [1;37;43mMODIFICATION[0m ";
|
||||||
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);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
////////////////////////////////////////////////////////////////////////////////
|
////////////////////////////////////////////////////////////////////////////////
|
||||||
|
|||||||
@@ -31,16 +31,15 @@
|
|||||||
|
|
||||||
// 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&);
|
void measure(Task&, unsigned int&, unsigned int&);
|
||||||
void measure (Task&, unsigned int&, unsigned int&);
|
void render(std::vector<std::string>&, Task&, int, Color&);
|
||||||
void render (std::vector <std::string>&, Task&, int, Color&);
|
void modify(Task&, const std::string&);
|
||||||
void modify (Task&, const std::string&);
|
|
||||||
|
|
||||||
private:
|
private:
|
||||||
};
|
};
|
||||||
|
|
||||||
#endif
|
#endif
|
||||||
|
|||||||
@@ -30,21 +30,18 @@
|
|||||||
#include <ColScheduled.h>
|
#include <ColScheduled.h>
|
||||||
|
|
||||||
////////////////////////////////////////////////////////////////////////////////
|
////////////////////////////////////////////////////////////////////////////////
|
||||||
ColumnScheduled::ColumnScheduled ()
|
ColumnScheduled::ColumnScheduled() {
|
||||||
{
|
_name = "scheduled";
|
||||||
_name = "scheduled";
|
|
||||||
_label = "Scheduled";
|
_label = "Scheduled";
|
||||||
}
|
}
|
||||||
|
|
||||||
////////////////////////////////////////////////////////////////////////////////
|
////////////////////////////////////////////////////////////////////////////////
|
||||||
// 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";
|
|
||||||
}
|
}
|
||||||
|
|
||||||
////////////////////////////////////////////////////////////////////////////////
|
////////////////////////////////////////////////////////////////////////////////
|
||||||
|
|||||||
@@ -29,11 +29,10 @@
|
|||||||
|
|
||||||
#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&);
|
|
||||||
};
|
};
|
||||||
|
|
||||||
#endif
|
#endif
|
||||||
|
|||||||
@@ -32,58 +32,46 @@
|
|||||||
#include <utf8.h>
|
#include <utf8.h>
|
||||||
|
|
||||||
////////////////////////////////////////////////////////////////////////////////
|
////////////////////////////////////////////////////////////////////////////////
|
||||||
ColumnStart::ColumnStart ()
|
ColumnStart::ColumnStart() {
|
||||||
{
|
_name = "start";
|
||||||
_name = "start";
|
|
||||||
_label = "Started";
|
_label = "Started";
|
||||||
|
|
||||||
_styles.push_back ("active");
|
_styles.push_back("active");
|
||||||
_examples.push_back (Context::getContext ().config.get ("active.indicator"));
|
_examples.push_back(Context::getContext().config.get("active.indicator"));
|
||||||
}
|
}
|
||||||
|
|
||||||
////////////////////////////////////////////////////////////////////////////////
|
////////////////////////////////////////////////////////////////////////////////
|
||||||
// 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
|
||||||
ColumnTypeDate::measure (task, minimum, maximum);
|
ColumnTypeDate::measure(task, minimum, maximum);
|
||||||
|
|
||||||
// TODO Throw on bad format.
|
// TODO Throw on bad format.
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
////////////////////////////////////////////////////////////////////////////////
|
////////////////////////////////////////////////////////////////////////////////
|
||||||
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,
|
if (!task.has("end"))
|
||||||
Color& color)
|
renderStringRight(lines, width, color,
|
||||||
{
|
Context::getContext().config.get("active.indicator"));
|
||||||
if (task.has (_name))
|
} else
|
||||||
{
|
ColumnTypeDate::render(lines, task, width, color);
|
||||||
if (_style == "active")
|
|
||||||
{
|
|
||||||
if (! task.has ("end"))
|
|
||||||
renderStringRight (lines, width, color, Context::getContext ().config.get ("active.indicator"));
|
|
||||||
}
|
|
||||||
else
|
|
||||||
ColumnTypeDate::render (lines, task, width, color);
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -29,13 +29,12 @@
|
|||||||
|
|
||||||
#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&);
|
void measure(Task&, unsigned int&, unsigned int&);
|
||||||
void measure (Task&, unsigned int&, unsigned int&);
|
void render(std::vector<std::string>&, Task&, int, Color&);
|
||||||
void render (std::vector <std::string>&, Task&, int, Color&);
|
|
||||||
};
|
};
|
||||||
|
|
||||||
#endif
|
#endif
|
||||||
|
|||||||
@@ -32,81 +32,75 @@
|
|||||||
#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", "P"};
|
||||||
_examples = {"Pending",
|
|
||||||
"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)
|
||||||
minimum = maximum = utf8_width ("Deleted");
|
minimum = maximum = utf8_width("Deleted");
|
||||||
else if (status == Task::waiting)
|
else if (status == Task::waiting)
|
||||||
minimum = maximum = utf8_width ("Waiting");
|
minimum = maximum = utf8_width("Waiting");
|
||||||
else if (status == Task::completed)
|
else if (status == Task::completed)
|
||||||
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::status status = task.getStatus();
|
||||||
Task& task,
|
|
||||||
int width,
|
|
||||||
Color& color)
|
|
||||||
{
|
|
||||||
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);
|
||||||
}
|
}
|
||||||
|
|
||||||
////////////////////////////////////////////////////////////////////////////////
|
////////////////////////////////////////////////////////////////////////////////
|
||||||
|
|||||||
@@ -29,15 +29,14 @@
|
|||||||
|
|
||||||
#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&);
|
void measure(Task&, unsigned int&, unsigned int&);
|
||||||
void measure (Task&, unsigned int&, unsigned int&);
|
void render(std::vector<std::string>&, Task&, int, Color&);
|
||||||
void render (std::vector <std::string>&, Task&, int, Color&);
|
|
||||||
|
|
||||||
private:
|
private:
|
||||||
};
|
};
|
||||||
|
|
||||||
#endif
|
#endif
|
||||||
|
|||||||
@@ -28,154 +28,119 @@
|
|||||||
// cmake.h include header must come first
|
// 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", Context::getContext().config.get("tag.indicator"), "[2]"};
|
||||||
_examples = {"home @chore next",
|
|
||||||
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"));
|
||||||
{
|
} else if (_style == "count") {
|
||||||
minimum = maximum = utf8_width (Context::getContext ().config.get ("tag.indicator"));
|
|
||||||
}
|
|
||||||
else if (_style == "count")
|
|
||||||
{
|
|
||||||
minimum = maximum = 3;
|
minimum = maximum = 3;
|
||||||
}
|
} else if (_style == "default" || _style == "list") {
|
||||||
else if (_style == "default" ||
|
std::string tags = task.get(_name);
|
||||||
_style == "list")
|
|
||||||
{
|
|
||||||
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);
|
||||||
{
|
if (length > minimum) minimum = length;
|
||||||
auto length = utf8_width (tag);
|
|
||||||
if (length > minimum)
|
|
||||||
minimum = length;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
maximum = utf8_width (tags);
|
maximum = utf8_width(tags);
|
||||||
}
|
}
|
||||||
|
|
||||||
// No need to split a single tag.
|
// No need to split a single tag.
|
||||||
else
|
else
|
||||||
minimum = maximum = utf8_width (tags);
|
minimum = maximum = utf8_width(tags);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
////////////////////////////////////////////////////////////////////////////////
|
////////////////////////////////////////////////////////////////////////////////
|
||||||
void ColumnTags::render (
|
void ColumnTags::render(std::vector<std::string>& lines, Task& task, int width, Color& color) {
|
||||||
std::vector <std::string>& lines,
|
auto all = task.getTags();
|
||||||
Task& task,
|
if (all.size() > 0) {
|
||||||
int width,
|
if (_style == "default" || _style == "list") {
|
||||||
Color& color)
|
if (all.size() > 1) {
|
||||||
{
|
std::sort(all.begin(), all.end());
|
||||||
auto all = task.getTags ();
|
auto tags = join(" ", all);
|
||||||
if (all.size() > 0)
|
|
||||||
{
|
|
||||||
if (_style == "default" ||
|
|
||||||
_style == "list")
|
|
||||||
{
|
|
||||||
if (all.size () > 1)
|
|
||||||
{
|
|
||||||
std::sort (all.begin (), all.end ());
|
|
||||||
auto tags = join (" ", all);
|
|
||||||
|
|
||||||
all.clear ();
|
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
|
||||||
}
|
renderStringLeft(lines, width, color, all[0]);
|
||||||
else
|
} else if (_style == "indicator") {
|
||||||
renderStringLeft (lines, width, color, all[0]);
|
renderStringRight(lines, width, color, Context::getContext().config.get("tag.indicator"));
|
||||||
}
|
} else if (_style == "count") {
|
||||||
else if (_style == "indicator")
|
renderStringRight(lines, width, color, '[' + format(static_cast<int>(all.size())) + ']');
|
||||||
{
|
|
||||||
renderStringRight (lines, width, color, Context::getContext ().config.get ("tag.indicator"));
|
|
||||||
}
|
|
||||||
else if (_style == "count")
|
|
||||||
{
|
|
||||||
renderStringRight (lines, width, color, '[' + format (static_cast <int> (all.size ())) + ']');
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
////////////////////////////////////////////////////////////////////////////////
|
////////////////////////////////////////////////////////////////////////////////
|
||||||
void ColumnTags::modify (Task& task, const std::string& value)
|
void ColumnTags::modify(Task& task, const std::string& value) {
|
||||||
{
|
|
||||||
std::string label = " [1;37;43mMODIFICATION[0m ";
|
std::string label = " [1;37;43mMODIFICATION[0m ";
|
||||||
std::string commasep;
|
std::string commasep;
|
||||||
std::vector <std::string> tags;
|
std::vector<std::string> tags;
|
||||||
|
|
||||||
// If it's a DOM ref, eval it first.
|
// 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)
|
|
||||||
{
|
|
||||||
Eval e;
|
Eval e;
|
||||||
e.addSource (domSource);
|
e.addSource(domSource);
|
||||||
|
|
||||||
Variant v;
|
Variant v;
|
||||||
e.evaluateInfixExpression (value, v);
|
e.evaluateInfixExpression(value, v);
|
||||||
commasep = (std::string) v;
|
commasep = (std::string)v;
|
||||||
} else {
|
} else {
|
||||||
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 + '\'');
|
|
||||||
|
|
||||||
feedback_special_tags (task, tag);
|
feedback_special_tags(task, tag);
|
||||||
}
|
}
|
||||||
|
|
||||||
task.setTags(tags);
|
task.setTags(tags);
|
||||||
|
|||||||
@@ -29,16 +29,15 @@
|
|||||||
|
|
||||||
#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&);
|
void measure(Task&, unsigned int&, unsigned int&);
|
||||||
void measure (Task&, unsigned int&, unsigned int&);
|
void render(std::vector<std::string>&, Task&, int, Color&);
|
||||||
void render (std::vector <std::string>&, Task&, int, Color&);
|
void modify(Task&, const std::string&);
|
||||||
void modify (Task&, const std::string&);
|
|
||||||
|
|
||||||
private:
|
private:
|
||||||
bool _hyphenate;
|
bool _hyphenate;
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|||||||
@@ -31,45 +31,37 @@
|
|||||||
#include <format.h>
|
#include <format.h>
|
||||||
|
|
||||||
////////////////////////////////////////////////////////////////////////////////
|
////////////////////////////////////////////////////////////////////////////////
|
||||||
ColumnTemplate::ColumnTemplate ()
|
ColumnTemplate::ColumnTemplate() {
|
||||||
{
|
_name = "template";
|
||||||
_name = "template";
|
_style = "long";
|
||||||
_style = "long";
|
_label = "Template task";
|
||||||
_label = "Template task";
|
|
||||||
_modifiable = false;
|
_modifiable = false;
|
||||||
_styles = {"long", "short"};
|
_styles = {"long", "short"};
|
||||||
_examples = {"f30cb9c3-3fc0-483f-bfb2-3bf134f00694", "f30cb9c3"};
|
_examples = {"f30cb9c3-3fc0-483f-bfb2-3bf134f00694", "f30cb9c3"};
|
||||||
}
|
}
|
||||||
|
|
||||||
////////////////////////////////////////////////////////////////////////////////
|
////////////////////////////////////////////////////////////////////////////////
|
||||||
// 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")
|
||||||
renderStringLeft (lines, width, color, task.get (_name).substr (0, 8));
|
renderStringLeft(lines, width, color, task.get(_name).substr(0, 8));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -29,14 +29,13 @@
|
|||||||
|
|
||||||
#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&);
|
void render(std::vector<std::string>&, Task&, int, Color&);
|
||||||
void render (std::vector <std::string>&, Task&, int, Color&);
|
|
||||||
|
|
||||||
private:
|
private:
|
||||||
};
|
};
|
||||||
|
|
||||||
#endif
|
#endif
|
||||||
|
|||||||
@@ -32,215 +32,165 @@
|
|||||||
#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", "julian", "epoch", "iso", "age", "relative", "remaining", "countdown"};
|
||||||
_styles = {"formatted",
|
|
||||||
"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.
|
||||||
_examples = {now.toString (Context::getContext ().config.get ("dateformat")),
|
_examples = {now.toString(Context::getContext().config.get("dateformat")),
|
||||||
format (now.toJulian (), 13, 12),
|
format(now.toJulian(), 13, 12),
|
||||||
now.toEpochString (),
|
now.toEpochString(),
|
||||||
now.toISO (),
|
now.toISO(),
|
||||||
Duration (Datetime () - now).formatVague (true),
|
Duration(Datetime() - now).formatVague(true),
|
||||||
'-' + Duration (Datetime () - now).formatVague (true),
|
'-' + Duration(Datetime() - now).formatVague(true),
|
||||||
"",
|
"",
|
||||||
Duration (Datetime () - now).format ()};
|
Duration(Datetime() - now).format()};
|
||||||
}
|
}
|
||||||
|
|
||||||
////////////////////////////////////////////////////////////////////////////////
|
////////////////////////////////////////////////////////////////////////////////
|
||||||
// Set the minimum and maximum widths for the value.
|
// 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();
|
||||||
{
|
} else if (_style == "epoch") {
|
||||||
minimum = maximum = format (date.toJulian (), 13, 12).length ();
|
minimum = maximum = date.toEpochString().length();
|
||||||
}
|
} else if (_style == "iso") {
|
||||||
else if (_style == "epoch")
|
minimum = maximum = date.toISO().length();
|
||||||
{
|
} else if (_style == "age") {
|
||||||
minimum = maximum = date.toEpochString ().length ();
|
|
||||||
}
|
|
||||||
else if (_style == "iso")
|
|
||||||
{
|
|
||||||
minimum = maximum = date.toISO ().length ();
|
|
||||||
}
|
|
||||||
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,
|
Datetime date(task.get_date(_name));
|
||||||
int width,
|
|
||||||
Color& color)
|
|
||||||
{
|
|
||||||
if (task.has (_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");
|
|
||||||
}
|
}
|
||||||
|
|
||||||
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")
|
||||||
renderStringRight (lines, width, color, date.toEpochString ());
|
renderStringRight(lines, width, color, date.toEpochString());
|
||||||
|
|
||||||
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));
|
||||||
else
|
else
|
||||||
renderStringRight (lines, width, color, '-' + Duration (now - date).formatVague (true));
|
renderStringRight(lines, width, color, '-' + Duration(now - date).formatVague(true));
|
||||||
}
|
}
|
||||||
|
|
||||||
else if (_style == "remaining")
|
else if (_style == "remaining") {
|
||||||
{
|
|
||||||
Datetime now;
|
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));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
////////////////////////////////////////////////////////////////////////////////
|
////////////////////////////////////////////////////////////////////////////////
|
||||||
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 = " [1;37;43mMODIFICATION[0m ";
|
std::string label = " [1;37;43mMODIFICATION[0m ";
|
||||||
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())) + "' <-- '" +
|
||||||
evaluatedValue.cast (Variant::type_date);
|
(std::string)evaluatedValue + "' <-- '" + value + '\'');
|
||||||
}
|
evaluatedValue.cast(Variant::type_date);
|
||||||
else
|
} else {
|
||||||
{
|
evaluatedValue.cast(Variant::type_date);
|
||||||
evaluatedValue.cast (Variant::type_date);
|
Context::getContext().debug(label + _name + " <-- '" +
|
||||||
Context::getContext ().debug (label + _name + " <-- '" + format ("{1}", evaluatedValue.get_date ()) + "' <-- '" + (std::string) evaluatedValue + "' <-- '" + value + '\'');
|
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());
|
||||||
}
|
}
|
||||||
|
|
||||||
////////////////////////////////////////////////////////////////////////////////
|
////////////////////////////////////////////////////////////////////////////////
|
||||||
|
|||||||
@@ -27,20 +27,20 @@
|
|||||||
#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>
|
||||||
public:
|
|
||||||
ColumnTypeDate ();
|
class ColumnTypeDate : public Column {
|
||||||
virtual void measure (Task&, unsigned int&, unsigned int&);
|
public:
|
||||||
virtual void render (std::vector <std::string>&, Task&, int, Color&);
|
ColumnTypeDate();
|
||||||
virtual bool validate (const std::string&) const;
|
virtual void measure(Task&, unsigned int&, unsigned int&);
|
||||||
virtual void modify (Task&, const std::string&);
|
virtual void render(std::vector<std::string>&, Task&, int, Color&);
|
||||||
|
virtual bool validate(const std::string&) const;
|
||||||
|
virtual void modify(Task&, const std::string&);
|
||||||
};
|
};
|
||||||
|
|
||||||
#endif
|
#endif
|
||||||
|
|||||||
@@ -30,50 +30,42 @@
|
|||||||
#include <ColTypeDuration.h>
|
#include <ColTypeDuration.h>
|
||||||
#include <Context.h>
|
#include <Context.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>
|
||||||
|
|
||||||
////////////////////////////////////////////////////////////////////////////////
|
////////////////////////////////////////////////////////////////////////////////
|
||||||
ColumnTypeDuration::ColumnTypeDuration ()
|
ColumnTypeDuration::ColumnTypeDuration() { _type = "duration"; }
|
||||||
{
|
|
||||||
_type = "duration";
|
////////////////////////////////////////////////////////////////////////////////
|
||||||
|
bool ColumnTypeDuration::validate(const std::string& input) const {
|
||||||
|
return input.length() ? true : false;
|
||||||
}
|
}
|
||||||
|
|
||||||
////////////////////////////////////////////////////////////////////////////////
|
////////////////////////////////////////////////////////////////////////////////
|
||||||
bool ColumnTypeDuration::validate (const std::string& input) const
|
void ColumnTypeDuration::modify(Task& task, const std::string& value) {
|
||||||
{
|
|
||||||
return input.length () ? true : false;
|
|
||||||
}
|
|
||||||
|
|
||||||
////////////////////////////////////////////////////////////////////////////////
|
|
||||||
void ColumnTypeDuration::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);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// 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.
|
||||||
std::string label = " [1;37;43mMODIFICATION[0m ";
|
std::string label = " [1;37;43mMODIFICATION[0m ";
|
||||||
if (evaluatedValue.type () == Variant::type_duration)
|
if (evaluatedValue.type() == Variant::type_duration) {
|
||||||
{
|
|
||||||
// Store the raw value, for 'recur'.
|
// Store the raw value, for 'recur'.
|
||||||
Context::getContext ().debug (label + _name + " <-- " + (std::string) evaluatedValue + " <-- '" + value + '\'');
|
Context::getContext().debug(label + _name + " <-- " + (std::string)evaluatedValue + " <-- '" +
|
||||||
task.set (_name, evaluatedValue);
|
value + '\'');
|
||||||
}
|
task.set(_name, evaluatedValue);
|
||||||
else
|
} else
|
||||||
throw format ("The duration value '{1}' is not supported.", value);
|
throw format("The duration value '{1}' is not supported.", value);
|
||||||
}
|
}
|
||||||
|
|
||||||
////////////////////////////////////////////////////////////////////////////////
|
////////////////////////////////////////////////////////////////////////////////
|
||||||
|
|||||||
@@ -27,16 +27,16 @@
|
|||||||
#ifndef INCLUDED_COLTYPEDURATION
|
#ifndef INCLUDED_COLTYPEDURATION
|
||||||
#define INCLUDED_COLTYPEDURATION
|
#define INCLUDED_COLTYPEDURATION
|
||||||
|
|
||||||
#include <string>
|
|
||||||
#include <Column.h>
|
#include <Column.h>
|
||||||
#include <Task.h>
|
#include <Task.h>
|
||||||
|
|
||||||
class ColumnTypeDuration : public Column
|
#include <string>
|
||||||
{
|
|
||||||
public:
|
class ColumnTypeDuration : public Column {
|
||||||
ColumnTypeDuration ();
|
public:
|
||||||
virtual bool validate (const std::string&) const;
|
ColumnTypeDuration();
|
||||||
virtual void modify (Task&, const std::string&);
|
virtual bool validate(const std::string&) const;
|
||||||
|
virtual void modify(Task&, const std::string&);
|
||||||
};
|
};
|
||||||
|
|
||||||
#endif
|
#endif
|
||||||
|
|||||||
@@ -30,45 +30,38 @@
|
|||||||
#include <ColTypeNumeric.h>
|
#include <ColTypeNumeric.h>
|
||||||
#include <Context.h>
|
#include <Context.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>
|
||||||
|
|
||||||
////////////////////////////////////////////////////////////////////////////////
|
////////////////////////////////////////////////////////////////////////////////
|
||||||
ColumnTypeNumeric::ColumnTypeNumeric ()
|
ColumnTypeNumeric::ColumnTypeNumeric() { _type = "numeric"; }
|
||||||
{
|
|
||||||
_type = "numeric";
|
////////////////////////////////////////////////////////////////////////////////
|
||||||
|
bool ColumnTypeNumeric::validate(const std::string& input) const {
|
||||||
|
return input.length() ? true : false;
|
||||||
}
|
}
|
||||||
|
|
||||||
////////////////////////////////////////////////////////////////////////////////
|
////////////////////////////////////////////////////////////////////////////////
|
||||||
bool ColumnTypeNumeric::validate (const std::string& input) const
|
void ColumnTypeNumeric::modify(Task& task, const std::string& value) {
|
||||||
{
|
|
||||||
return input.length () ? true : false;
|
|
||||||
}
|
|
||||||
|
|
||||||
////////////////////////////////////////////////////////////////////////////////
|
|
||||||
void ColumnTypeNumeric::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);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
std::string label = " [1;37;43mMODIFICATION[0m ";
|
std::string label = " [1;37;43mMODIFICATION[0m ";
|
||||||
Context::getContext ().debug (label + _name + " <-- '" + evaluatedValue.get_string () + "' <-- '" + value + '\'');
|
Context::getContext().debug(label + _name + " <-- '" + evaluatedValue.get_string() + "' <-- '" +
|
||||||
|
value + '\'');
|
||||||
|
|
||||||
// Convert the value of the expression to the correct type if needed
|
// Convert the value of the expression to the correct type if needed
|
||||||
switch (evaluatedValue.type ())
|
switch (evaluatedValue.type()) {
|
||||||
{
|
|
||||||
// Expected variants - no conversion
|
// Expected variants - no conversion
|
||||||
case Variant::type_integer:
|
case Variant::type_integer:
|
||||||
case Variant::type_real:
|
case Variant::type_real:
|
||||||
@@ -76,16 +69,16 @@ void ColumnTypeNumeric::modify (Task& task, const std::string& value)
|
|||||||
// Convertible variants - convert to int
|
// Convertible variants - convert to int
|
||||||
case Variant::type_date:
|
case Variant::type_date:
|
||||||
case Variant::type_duration:
|
case Variant::type_duration:
|
||||||
evaluatedValue.cast (Variant::type_integer);
|
evaluatedValue.cast(Variant::type_integer);
|
||||||
break;
|
break;
|
||||||
// Non-convertible variants
|
// Non-convertible variants
|
||||||
case Variant::type_string:
|
case Variant::type_string:
|
||||||
throw format ("The value '{1}' is not a valid numeric value.", evaluatedValue.get_string ());
|
throw format("The value '{1}' is not a valid numeric value.", evaluatedValue.get_string());
|
||||||
default:
|
default:
|
||||||
throw format ("Unexpected variant type: '{1}'", evaluatedValue.type ());
|
throw format("Unexpected variant type: '{1}'", evaluatedValue.type());
|
||||||
}
|
}
|
||||||
|
|
||||||
task.set (_name, evaluatedValue);
|
task.set(_name, evaluatedValue);
|
||||||
}
|
}
|
||||||
|
|
||||||
////////////////////////////////////////////////////////////////////////////////
|
////////////////////////////////////////////////////////////////////////////////
|
||||||
|
|||||||
@@ -27,16 +27,16 @@
|
|||||||
#ifndef INCLUDED_COLTYPENUMERIC
|
#ifndef INCLUDED_COLTYPENUMERIC
|
||||||
#define INCLUDED_COLTYPENUMERIC
|
#define INCLUDED_COLTYPENUMERIC
|
||||||
|
|
||||||
#include <string>
|
|
||||||
#include <Column.h>
|
#include <Column.h>
|
||||||
#include <Task.h>
|
#include <Task.h>
|
||||||
|
|
||||||
class ColumnTypeNumeric : public Column
|
#include <string>
|
||||||
{
|
|
||||||
public:
|
class ColumnTypeNumeric : public Column {
|
||||||
ColumnTypeNumeric ();
|
public:
|
||||||
virtual bool validate (const std::string&) const;
|
ColumnTypeNumeric();
|
||||||
virtual void modify (Task&, const std::string&);
|
virtual bool validate(const std::string&) const;
|
||||||
|
virtual void modify(Task&, const std::string&);
|
||||||
};
|
};
|
||||||
|
|
||||||
#endif
|
#endif
|
||||||
|
|||||||
@@ -30,64 +30,51 @@
|
|||||||
#include <ColTypeString.h>
|
#include <ColTypeString.h>
|
||||||
#include <Context.h>
|
#include <Context.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>
|
||||||
|
|
||||||
#define STRING_INVALID_MOD "The '{1}' attribute does not allow a value of '{2}'."
|
#define STRING_INVALID_MOD "The '{1}' attribute does not allow a value of '{2}'."
|
||||||
|
|
||||||
////////////////////////////////////////////////////////////////////////////////
|
////////////////////////////////////////////////////////////////////////////////
|
||||||
ColumnTypeString::ColumnTypeString ()
|
ColumnTypeString::ColumnTypeString() { _type = "string"; }
|
||||||
{
|
|
||||||
_type = "string";
|
////////////////////////////////////////////////////////////////////////////////
|
||||||
|
bool ColumnTypeString::validate(const std::string& input) const {
|
||||||
|
return input.length() ? true : false;
|
||||||
}
|
}
|
||||||
|
|
||||||
////////////////////////////////////////////////////////////////////////////////
|
////////////////////////////////////////////////////////////////////////////////
|
||||||
bool ColumnTypeString::validate (const std::string& input) const
|
void ColumnTypeString::modify(Task& task, const std::string& value) {
|
||||||
{
|
|
||||||
return input.length () ? true : false;
|
|
||||||
}
|
|
||||||
|
|
||||||
////////////////////////////////////////////////////////////////////////////////
|
|
||||||
void ColumnTypeString::modify (Task& task, const std::string& value)
|
|
||||||
{
|
|
||||||
std::string label = " [1;37;43mMODIFICATION[0m ";
|
std::string label = " [1;37;43mMODIFICATION[0m ";
|
||||||
|
|
||||||
// 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 &&
|
|
||||||
// Ensure 'value' contains only the DOM reference and no other tokens
|
// Ensure 'value' contains only the DOM reference and no other tokens
|
||||||
// The lexer.token returns false for end-of-string.
|
// The lexer.token returns false for end-of-string.
|
||||||
// This works as long as all the DOM references we should support consist
|
// This works as long as all the DOM references we should support consist
|
||||||
// only of a single token.
|
// only of a single token.
|
||||||
lexer.token (domRef, type) == false)
|
lexer.token(domRef, type) == false) {
|
||||||
{
|
|
||||||
Eval e;
|
Eval e;
|
||||||
e.addSource (domSource);
|
e.addSource(domSource);
|
||||||
|
|
||||||
Variant v;
|
Variant v;
|
||||||
e.evaluateInfixExpression (value, v);
|
e.evaluateInfixExpression(value, v);
|
||||||
std::string strValue = (std::string) v;
|
std::string strValue = (std::string)v;
|
||||||
if (validate (strValue))
|
if (validate(strValue)) {
|
||||||
{
|
task.set(_name, strValue);
|
||||||
task.set (_name, strValue);
|
Context::getContext().debug(label + _name + " <-- '" + strValue + "' <-- '" + value + '\'');
|
||||||
Context::getContext ().debug (label + _name + " <-- '" + strValue + "' <-- '" + value + '\'');
|
} else
|
||||||
}
|
throw format(STRING_INVALID_MOD, _name, value);
|
||||||
else
|
} else {
|
||||||
throw format (STRING_INVALID_MOD, _name, value);
|
if (validate(value)) {
|
||||||
}
|
task.set(_name, value);
|
||||||
else
|
Context::getContext().debug(label + _name + " <-- '" + value + '\'');
|
||||||
{
|
} else
|
||||||
if (validate (value))
|
throw format(STRING_INVALID_MOD, _name, value);
|
||||||
{
|
|
||||||
task.set (_name, value);
|
|
||||||
Context::getContext ().debug (label + _name + " <-- '" + value + '\'');
|
|
||||||
}
|
|
||||||
else
|
|
||||||
throw format (STRING_INVALID_MOD, _name, value);
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user