From 4c3354fa50b70cb5c2fdbca18058cd006a189235 Mon Sep 17 00:00:00 2001 From: Paul Beckingham Date: Sat, 27 Nov 2010 16:38:36 -0500 Subject: [PATCH] Journal Feature - Added change log display to the 'info' command, controlled by the 'journal.info' configuration setting. --- ChangeLog | 10 ++-- NEWS | 3 ++ doc/man/taskrc.5 | 6 ++- src/Config.cpp | 1 + src/command.cpp | 2 +- src/report.cpp | 94 +++++++++++++++++++++++++++++++++---- src/util.cpp | 118 +++++++++++++++++++++++++++++++++++++++++++++++ src/util.h | 1 + 8 files changed, 219 insertions(+), 16 deletions(-) diff --git a/ChangeLog b/ChangeLog index 3cecebe1c..830a58183 100644 --- a/ChangeLog +++ b/ChangeLog @@ -2,11 +2,13 @@ ------ current release --------------------------- 1.9.4 () - + Added burndown charts - burndown.daily, burndown.weekly, burndown.monthly, - that use color.burndown.pending, color.burndown.started and - color.burndown.done colors. - + Added highlighting for the show command that indicates which values differ + + Added burndown charts - 'burndown.daily', 'burndown.weekly', + 'burndown.monthly', that use 'color.burndown.pending', 'color.burndown.started' + and 'color.burndown.done' colors. + + Added highlighting for the 'show' command that indicates which values differ from the defaults. + + Added change log display to the 'info' command, controlled by the + 'journal.info' configuration setting. + Added feature #158, regular expression support for filters and substitutions. + Added feature #247, providing infinite width reports when redirecting output to a file, by setting defaultwidth to 0. diff --git a/NEWS b/NEWS index 2cddbc81f..9f89ba7c0 100644 --- a/NEWS +++ b/NEWS @@ -7,6 +7,7 @@ New Features in taskwarrior 1.9.4 - Regular expression support in filters and substitutions. - Added highlighting for the show command that indicates which values differ from the defaults. + - Added change log display to the 'info' command. Please refer to the ChangeLog file for full details. There are too many to list here. @@ -29,6 +30,8 @@ New configuration options in taskwarrior 1.9.4 - regex=on enables regular expression searches for filters (task list a...e matches 'above') and substitutions (task /a...e/over/ changes 'above' to 'over'). Default is off, as this is an advanced feature. + - journal.info controls whether a change log for each task is displayed by + the 'info' command. Newly deprecated features in taskwarrior 1.9.4 diff --git a/doc/man/taskrc.5 b/doc/man/taskrc.5 index d74eb78ea..80b34b03a 100644 --- a/doc/man/taskrc.5 +++ b/doc/man/taskrc.5 @@ -474,7 +474,7 @@ turned off by setting the variable to none. The default value is "none". .B journal.time=no May be yes or no, and determines whether the 'start' and 'stop' commands should record an annotation when being executed. The default value is "no". The text of -the corresponding annotations is controlled by +the corresponding annotations is controlled by: .TP journal.time.start.annotation=Started task The text of the annotation that is recorded when executing the start command and @@ -484,6 +484,10 @@ having set journal.time. The text of the annotation that is recorded when executing the stop command and having set journal.time. +.TP journal.info=on +When enabled, this setting causes a change log of each task to be displayed by +the 'info' command. Default value is "on". + .SS Holidays Holidays are entered either directly in the .taskrc file or via an include file that is specified in .taskrc. For each holiday the name and the date is diff --git a/src/Config.cpp b/src/Config.cpp index 0993223f7..bc364651a 100644 --- a/src/Config.cpp +++ b/src/Config.cpp @@ -103,6 +103,7 @@ std::string Config::defaults = "journal.time=no # Record start/stop commands as annotation\n" "journal.time.start.annotation=Started task # Annotation description for the start journal entry\n" "journal.time.stop.annotation=Stopped task # Annotation description for the stop journal entry\n" + "journal.info=on # Display task journal with info command\n" "\n" "# Dependency controls\n" "dependency.reminder=on # Nags on dependency chain violations\n" diff --git a/src/command.cpp b/src/command.cpp index e58ecdb42..e30387112 100644 --- a/src/command.cpp +++ b/src/command.cpp @@ -907,7 +907,7 @@ int handleShow (std::string& outs) "default.command default.priority default.project defaultwidth due " "dependency.confirmation dependency.reminder locale displayweeknumber " "export.ical.class echo.command fontunderline locking monthsperline nag " - "next journal.time journal.time.start.annotation " + "next journal.time journal.time.start.annotation journal.info " "journal.time.stop.annotation project shadow.command shadow.file " "shadow.notify weekstart editor import.synonym.id import.synonym.uuid " "complete.all.projects complete.all.tags search.case.sensitive hooks " diff --git a/src/report.cpp b/src/report.cpp index 968ae932a..e6418eee1 100644 --- a/src/report.cpp +++ b/src/report.cpp @@ -37,15 +37,15 @@ #include #include -#include "Context.h" -#include "Directory.h" -#include "File.h" -#include "Date.h" -#include "Duration.h" -#include "Table.h" -#include "text.h" -#include "util.h" -#include "main.h" +#include +#include +#include +#include +#include +#include +#include +#include +#include #ifdef HAVE_LIBNCURSES #include @@ -396,6 +396,15 @@ int handleInfo (std::string& outs) // Filter sequence. context.filter.applySequence (tasks, context.sequence); + // Read the undo file. + std::vector undo; + if (context.config.getBoolean ("journal.info")) + { + Directory location (context.config.get ("data.location")); + std::string undoFile = location.data + "/undo.data"; + File::read (undoFile, undo); + } + // Find the task. std::stringstream out; foreach (task, tasks) @@ -614,7 +623,7 @@ int handleInfo (std::string& outs) // uuid row = table.addRow (); table.addCell (row, 0, "UUID"); - value = task->get ("uuid"); + std::string uuid = value = task->get ("uuid"); context.hooks.trigger ("format-uuid", "uuid", value); table.addCell (row, 1, value); @@ -659,17 +668,82 @@ int handleInfo (std::string& outs) //table.addCell (row, 0, "Urgency"); //table.addCell (row, 1, task->urgency ()); + Table journal; + // If an alternating row color is specified, notify the table. if (context.config.getBoolean ("color") || context.config.getBoolean ("_forcecolor")) { Color alternate (context.config.get ("color.alternate")); if (alternate.nontrivial ()) + { table.setTableAlternateColor (alternate); + journal.setTableAlternateColor (alternate); + } } out << optionalBlankLine () << table.render () << "\n"; + + journal.setTableWidth (context.getWidth ()); + journal.setDateFormat (context.config.get ("dateformat")); + + journal.addColumn ("Date"); + journal.addColumn ("Modification"); + + if ((context.config.getBoolean ("color") || context.config.getBoolean ("_forcecolor")) && + context.config.getBoolean ("fontunderline")) + { + journal.setColumnUnderline (0); + journal.setColumnUnderline (1); + } + else + journal.setTableDashedUnderline (); + + journal.setColumnWidth (0, Table::minimum); + journal.setColumnWidth (1, Table::flexible); + + journal.setColumnJustification (0, Table::left); + journal.setColumnJustification (1, Table::left); + + if (context.config.getBoolean ("journal.info") && + undo.size () > 3) + { + // Scan the undo data for entries matching this task. + std::string when; + std::string previous; + std::string current; + unsigned int i = 0; + while (i < undo.size ()) + { + when = undo[i++]; + previous = ""; + if (undo[i].substr (0, 3) == "old") + previous = undo[i++]; + + current = undo[i++]; + i++; // Separator + + if (current.find ("uuid:\"" + uuid) != std::string::npos) + { + if (previous != "") + { + int row = journal.addRow (); + + Date timestamp (atoi (when.substr (5).c_str ())); + journal.addCell (row, 0, timestamp.toString (context.config.get ("dateformat"))); + + Task before (previous.substr (4)); + Task after (current.substr (4)); + journal.addCell (row, 1, taskInfoDifferences (before, after)); + } + } + } + + if (journal.rowCount () > 0) + out << journal.render () + << "\n"; + } } if (! tasks.size ()) diff --git a/src/util.cpp b/src/util.cpp index a5b2438f1..978e79d9a 100644 --- a/src/util.cpp +++ b/src/util.cpp @@ -435,6 +435,124 @@ std::string taskDifferences (const Task& before, const Task& after) return out.str (); } +//////////////////////////////////////////////////////////////////////////////// +std::string taskInfoDifferences (const Task& before, const Task& after) +{ + // Attributes are all there is, so figure the different attribute names + // between before and after. + std::vector beforeAtts; + foreach (att, before) + beforeAtts.push_back (att->first); + + std::vector afterAtts; + foreach (att, after) + afterAtts.push_back (att->first); + + std::vector beforeOnly; + std::vector afterOnly; + listDiff (beforeAtts, afterAtts, beforeOnly, afterOnly); + + // Now start generating a description of the differences. + std::stringstream out; + foreach (name, beforeOnly) + { + if (*name == "depends") + { + std::vector deps_before; + before.getDependencies (deps_before); + std::string from; + join (from, ",", deps_before); + + out << "dependencies '" + << from + << "' deleted\n"; + } + else if (name->substr (0, 11) == "annotation_") + { + out << "annotation '" + << before.get (*name) + << "' deleted"; + } + else + { + out << *name + << " deleted\n"; + } + } + + foreach (name, afterOnly) + { + if (*name == "depends") + { + std::vector deps_after; + after.getDependencies (deps_after); + std::string to; + join (to, ",", deps_after); + + out << *name + << " set to '" + << to + << "'\n"; + } + else if (name->substr (0, 11) == "annotation_") + { + out << "annotation added '" + << after.get (*name) + << "'"; + } + else + out << *name + << " set to '" + << renderAttribute (*name, after.get (*name)) + << "'\n"; + } + + foreach (name, beforeAtts) + if (*name != "uuid" && + before.get (*name) != after.get (*name)) + { + if (*name == "depends") + { + std::vector deps_before; + before.getDependencies (deps_before); + std::string from; + join (from, ",", deps_before); + + std::vector deps_after; + after.getDependencies (deps_after); + std::string to; + join (to, ",", deps_after); + + out << *name + << " changed from '" + << from + << "' to '" + << to + << "'\n"; + } + else if (name->substr (0, 11) == "annotation_") + { + out << "annotation '" + << after.get (*name) + << "'"; + } + else + out << *name + << " changed from '" + << renderAttribute (*name, before.get (*name)) + << "' to '" + << renderAttribute (*name, after.get (*name)) + << "'\n"; + } + + + // Shouldn't just say nothing. + if (out.str ().length () == 0) + out << "No changes made.\n"; + + return out.str (); +} + //////////////////////////////////////////////////////////////////////////////// std::string renderAttribute (const std::string& name, const std::string& value) { diff --git a/src/util.h b/src/util.h index 317d80bcf..3da0b63fe 100644 --- a/src/util.h +++ b/src/util.h @@ -70,6 +70,7 @@ const std::string uuid (); bool taskDiff (const Task&, const Task&); std::string taskDifferences (const Task&, const Task&); +std::string taskInfoDifferences (const Task&, const Task&); std::string renderAttribute (const std::string&, const std::string&); #endif