From c27097e286db9810ed8c4e657093ca18be9422a7 Mon Sep 17 00:00:00 2001 From: Paul Beckingham Date: Sat, 28 Aug 2010 09:37:32 -0400 Subject: [PATCH 01/11] Bug - Fixed a precision problem with average age on the summary report. The problem was that average age is calculated as the sum of all ages, divided by the count. The sum was already being stored as a double, to allow for very high values, but was being truncated to an int before being divided by the count. Classic precision mishandling. --- ChangeLog | 1 + src/report.cpp | 2 +- 2 files changed, 2 insertions(+), 1 deletion(-) diff --git a/ChangeLog b/ChangeLog index 7f9790117..e28900cbf 100644 --- a/ChangeLog +++ b/ChangeLog @@ -67,6 +67,7 @@ + Fixed problem with the 'undo' command not observing the rc.color or the rc._forcecolor settings. + Fixed problem with extra blank line in the ghistory reports. + + Fixed a precision problem with average age on the summary report. + Clarified the documentation regarding the project name (taskwarrior) and the program name (task). diff --git a/src/report.cpp b/src/report.cpp index 0aa07265d..45be77a21 100644 --- a/src/report.cpp +++ b/src/report.cpp @@ -759,7 +759,7 @@ int handleReportSummary (std::string &outs) table.addCell (row, 0, (i->first == "" ? "(none)" : i->first)); table.addCell (row, 1, countPending[i->first]); if (counter[i->first]) - table.addCell (row, 2, Duration ((int) sumEntry[i->first] / counter[i->first]).format ()); + table.addCell (row, 2, Duration ((int) (sumEntry[i->first] / (double)counter[i->first])).format ()); int c = countCompleted[i->first]; int p = countPending[i->first]; From 4cd528661a2b9c902c5f89a6df21908e587b2fad Mon Sep 17 00:00:00 2001 From: Paul Beckingham Date: Sun, 29 Aug 2010 13:40:53 -0400 Subject: [PATCH 02/11] Feature #481 - color should follow rc order - Added feature #481, allowing for user control of the color rule order of precedence via the 'rule.precedence.color' configuration variable. - Color rules now obey the rc.search.case.sensitive configuration option. - The color.keyword.XXX color rule now applies to annotations too. --- ChangeLog | 4 + NEWS | 12 +- doc/man/task-color.5 | 18 +++ doc/man/taskrc.5 | 61 ++++--- src/Config.cpp | 274 ++++++++++++++++---------------- src/Context.cpp | 2 - src/command.cpp | 1 + src/rules.cpp | 324 ++++++++++++++++++++++++++------------ src/tests/color.keyword.t | 16 +- src/tests/color.project.t | 3 +- src/tests/color.tag.t | 3 +- 11 files changed, 442 insertions(+), 276 deletions(-) diff --git a/ChangeLog b/ChangeLog index e28900cbf..a5340aae1 100644 --- a/ChangeLog +++ b/ChangeLog @@ -28,6 +28,8 @@ changes to the completion percentage when it changes. + Added feature #478, which uses the colorization rules in the 'info' report. + + Added feature #481, allowing for user control of the color rule order + of precedence via the 'rule.precedence.color' configuration variable. + New 'depends' column for custom reports. + New 'blocked' report for showing blocked tasks. + Improved man pages (thanks to Andy Lester). @@ -37,6 +39,8 @@ + The 'tags' command highlights special tags. + The 'stats' and 'info' reports not obey color.alternate. + New fish shell tab completion script (thanks to Mick Koch). + + Color rules now obey the rc.search.case.sensitive configuration option. + + The color.keyword.XXX color rule now applies to annotations too. + Fixed bug #427, preventing the task edit command to parse annotation dates with spaces. + Fixed bug #433, making task command output more consistent. diff --git a/NEWS b/NEWS index 6b8d07d33..ac775f55f 100644 --- a/NEWS +++ b/NEWS @@ -6,6 +6,9 @@ New Features in taskwarrior 1.9.3 - Now supports durations in dates, such as: $ task ... due:4d $ task ... due:3wks + - 'sow', 'som' and 'soy' are now accepted in dates. 'soww' and 'eoww' are + now synonyms for 'sow' and 'eow' (ww = working week) 'socw' and 'eocw' + refer to the calendar week (starting Sunday/Monday and - Now supports the beginning of the week, month and year in dates. - Now supports 'now' as a date/time. - Now defines an overdue task as being one second after the due date, @@ -15,6 +18,7 @@ New Features in taskwarrior 1.9.3 - When completing or modifying a task, the project status is displayed. - The 'info' report is now colorized. - Certain characters (#, $, @) are now supported for use in tags. + - User-controlled color rule precedence. Please refer to the ChangeLog file for full details. There are too many to list here. @@ -31,10 +35,10 @@ New commands in taskwarrior 1.9.3 New configuration options in taskwarrior 1.9.3 - journal.time, journal.time.start.annotation, journal.time.stop.annotation - - 'sow', 'som' and 'soy' are now accepted in dates - 'soww' and 'eoww' are now synonyms for 'sow' and 'eow' (ww = working week) - 'socw' and 'eocw' refer to the calendar week (starting Sunday/Monday and - ending Saturday/Sunday) + ending Saturday/Sunday). + - Color rule precedence can now be explicitly set with the configuration + variable rule.precedence.color. Try "task show rule.pre" to show the + default settings. Newly deprecated features in taskwarrior 1.9.3 diff --git a/doc/man/task-color.5 b/doc/man/task-color.5 index 4434da50c..1576bf8d3 100644 --- a/doc/man/task-color.5 +++ b/doc/man/task-color.5 @@ -236,6 +236,20 @@ It is possible to create a very colorful mix of rules. With 256-color support, those colors can be made subtle, and complementary, but without care, this can be a visual mess. Beware! +The precedence for the color rules is determined by the configuration variable +'rule.precedence.color', which by default contains: + + due.today,active,blocked,overdue,due,keyword,project,tag,recurring,pri,tagged + +These are just the color rules with the 'color.' prefix removed. The rule +'color.due.today' is the highest precedence, and 'color.tagged' is the lowest. + +The keyword rule shown here as 'keyword' corresponds to a wildcard pattern, +meaning 'color.keyword.*', or in other words all the keyword rules. Similarly +for the 'color.tag.*' and 'color.project.*' rules. + +There is also 'color.project.none', 'color.tag.none' and 'color.pri.none'. + .SH THEMES Taskwarrior supports themes. What this really means is that with the ability to include other files into the .taskrc file, different sets of color rules can @@ -266,6 +280,10 @@ dark-green-256.theme dark-blue-256.theme .RE +You can also see how the theme will color the various tasks with the command: + + $ task color legend + Better yet, create your own, and share it. We will gladly host the theme file on . diff --git a/doc/man/taskrc.5 b/doc/man/taskrc.5 index d3ac9f749..61d9b96f8 100644 --- a/doc/man/taskrc.5 +++ b/doc/man/taskrc.5 @@ -536,40 +536,50 @@ terminal, can be obtained by running the command: .B task color .RE + .RS -The coloration rules and their defaults are: +The coloration rules are: .RE .RS -.B color.overdue=bold red -The color for overdue tasks. +.B color.due.today +Task is due today .br -.B color.due.today=bold magenta -The color of tasks due today. +.B color.active +Task is started, therefore active. .br -.B color.due=bold yellow -The color of due tasks. +.B color.blocked +Task is blocked by a dependency. .br -.B color.pri.H=bold -The color of priority:H tasks. +.B color.overdue +Task is overdue (due some time prior to now). .br -.B color.pri.M=on yellow -The color of priority:M tasks. No default value. +.B color.due +Task is coming due. .br -.B color.pri.L=on green -The color of priority:L tasks. No default value. +.B color.project.none +Task does not have an assigned project. .br -.B color.pri.none=white on blue -The color of priority: tasks. No default value. +.B color.tag.none +Task has no tags. .br -.B color.active=bold cyan -The color of active tasks. +.B color.tagged +Task has at least one tag. .br -.B color.tagged=yellow -The color of tagged tasks. +.B color.recurring +Task is recurring. .br -.B color.recurring=on red -The color for recurring tasks. +.B color.pri.H +Task has priority H. +.br +.B color.pri.M +Task has priority M. +.br +.B color.pri.L +Task has priority L. +.br +.B color.pri.none +Task has no priority. .RE .RE @@ -690,6 +700,15 @@ Colors used by the undo command, to indicate the values both before and after a change that is to be reverted. .RE +.TP +.B rule.precedence.color=overdue,tag,project,keyword,active,... +.RS +This setting specifies the precedence of the color rules, from highest to +lowest. Note that the prefix 'color.' is omitted (for brevity), and that any +wildcard values (color.tag.XXX) is shortened to 'tag', which places all specific +tag rules at the same precedence, again for brevity. +.RE + .SS SHADOW FILE .TP diff --git a/src/Config.cpp b/src/Config.cpp index 6b1568df8..6ec49c5f0 100644 --- a/src/Config.cpp +++ b/src/Config.cpp @@ -59,171 +59,170 @@ std::string Config::defaults = "\n" "# Files\n" "data.location=~/.task\n" - "locking=on # Use file-level locking\n" + "locking=on # Use file-level locking\n" "\n" "# Terminal\n" - "curses=on # Use ncurses library to determine terminal width\n" - "defaultwidth=80 # Without ncurses, assumed width\n" - "#editor=vi # Preferred text editor\n" + "curses=on # Use ncurses library to determine terminal width\n" + "defaultwidth=80 # Without ncurses, assumed width\n" + "#editor=vi # Preferred text editor\n" "\n" "# Miscellaneous\n" - "verbose=yes # Provide extra feedback\n" - "confirmation=yes # Confirmation on delete, big changes\n" - "echo.command=yes # Details on command just run\n" - "annotations=full # Level of verbosity for annotations: full, sparse or none\n" - "next=2 # How many tasks per project in next report\n" - "bulk=2 # > 2 tasks considered 'a lot', for confirmation\n" - "nag=You have higher priority tasks. # Nag message to keep you honest\n" // TODO - "search.case.sensitive=yes # Setting to no allows case insensitive searches\n" - "active.indicator=* # What to show as an active task indicator\n" - "tag.indicator=+ # What to show as a tag indicator\n" - "recurrence.indicator=R # What to show as a task recurrence indicator\n" - "recurrence.limit=1 # Number of future recurring pending tasks\n" - "undo.style=side # Undo style - can be 'side', or 'diff'\n" + "verbose=yes # Provide extra feedback\n" + "confirmation=yes # Confirmation on delete, big changes\n" + "echo.command=yes # Details on command just run\n" + "annotations=full # Level of verbosity for annotations: full, sparse or none\n" + "next=2 # How many tasks per project in next report\n" + "bulk=2 # > 2 tasks considered 'a lot', for confirmation\n" + "nag=You have higher priority tasks. # Nag message to keep you honest\n" // TODO + "search.case.sensitive=yes # Setting to no allows case insensitive searches\n" + "active.indicator=* # What to show as an active task indicator\n" + "tag.indicator=+ # What to show as a tag indicator\n" + "recurrence.indicator=R # What to show as a task recurrence indicator\n" + "recurrence.limit=1 # Number of future recurring pending tasks\n" + "undo.style=side # Undo style - can be 'side', or 'diff'\n" "\n" "# Dates\n" - "dateformat=m/d/Y # Preferred input and display date format\n" - "dateformat.holiday=YMD # Preferred input date format for holidays\n" - "dateformat.report=m/d/Y # Preferred display date format for reports\n" - "dateformat.annotation=m/d/Y # Preferred display date format for reports\n" - "weekstart=Sunday # Sunday or Monday only\n" - "displayweeknumber=yes # Show week numbers on calendar\n" - "due=7 # Task is considered due in 7 days\n" + "dateformat=m/d/Y # Preferred input and display date format\n" + "dateformat.holiday=YMD # Preferred input date format for holidays\n" + "dateformat.report=m/d/Y # Preferred display date format for reports\n" + "dateformat.annotation=m/d/Y # Preferred display date format for reports\n" + "weekstart=Sunday # Sunday or Monday only\n" + "displayweeknumber=yes # Show week numbers on calendar\n" + "due=7 # Task is considered due in 7 days\n" "\n" "# Calendar controls\n" - "calendar.legend=yes # Display the legend on calendar\n" - "calendar.details=sparse # Calendar shows information for tasks w/due dates: full, sparse or none\n" - "calendar.details.report=list # Report to use when showing task information in cal\n" - "calendar.holidays=none # Show public holidays on calendar:full, sparse or none\n" - "#monthsperline=3 # Number of calendar months on a line\n" + "calendar.legend=yes # Display the legend on calendar\n" + "calendar.details=sparse # Calendar shows information for tasks w/due dates: full, sparse or none\n" + "calendar.details.report=list # Report to use when showing task information in cal\n" + "calendar.holidays=none # Show public holidays on calendar:full, sparse or none\n" + "#monthsperline=3 # Number of calendar months on a line\n" "\n" "# Journal controls\n" - "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.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" "\n" "# Urgency Coefficients\n" - "urgency.next.coefficient=10.0 # Urgency coefficients for 'next' special tag\n" - "urgency.blocking.coefficient=9.0 # Urgency coefficients for blocking tasks\n" - "urgency.blocked.coefficient=8.0 # Urgency coefficients for blocked tasks\n" - "urgency.due.coefficient=7.0 # Urgency coefficients for due dates\n" - "urgency.priority.coefficient=6.0 # Urgency coefficients for priorities\n" - "urgency.waiting.coefficient=5.0 # Urgency coefficients for waiting status\n" - "urgency.active.coefficient=4.0 # Urgency coefficients for active tasks\n" - "urgency.project.coefficient=3.0 # Urgency coefficients for projects\n" - "urgency.tags.coefficient=2.0 # Urgency coefficients for tags\n" - "urgency.annotations.coefficient=1.0 # Urgency coefficients for annotations\n" + "urgency.next.coefficient=10.0 # Urgency coefficients for 'next' special tag\n" + "urgency.blocking.coefficient=9.0 # Urgency coefficients for blocking tasks\n" + "urgency.blocked.coefficient=8.0 # Urgency coefficients for blocked tasks\n" + "urgency.due.coefficient=7.0 # Urgency coefficients for due dates\n" + "urgency.priority.coefficient=6.0 # Urgency coefficients for priorities\n" + "urgency.waiting.coefficient=5.0 # Urgency coefficients for waiting status\n" + "urgency.active.coefficient=4.0 # Urgency coefficients for active tasks\n" + "urgency.project.coefficient=3.0 # Urgency coefficients for projects\n" + "urgency.tags.coefficient=2.0 # Urgency coefficients for tags\n" + "urgency.annotations.coefficient=1.0 # Urgency coefficients for annotations\n" "\n" - "#urgency.user.project.foo.coefficient=5.0 # Urgency coefficients for 'foo' project\n" - "#urgency.user.tag.foo.coefficient=5.0 # Urgency coefficients for 'foo' tag\n" + "#urgency.user.project.foo.coefficient=5.0 # Urgency coefficients for 'foo' project\n" + "#urgency.user.tag.foo.coefficient=5.0 # Urgency coefficients for 'foo' tag\n" "\n" "# Color controls.\n" - "color=on # Enable color\n" + "color=on # Enable color\n" #ifdef LINUX - "color.header=color3 # Color of header messages\n" - "color.footnote=color3 # Color of footnote messages\n" - "color.debug=color3 # Color of diagnostic output\n" + "color.header=color3 # Color of header messages\n" + "color.footnote=color3 # Color of footnote messages\n" + "color.debug=color3 # Color of diagnostic output\n" + "color.alternate=on color233 # Alternate color for line coloring\n" "\n" - "color.summary.bar=on rgb141 # Color of summary report progress bar\n" - "color.summary.background=on color0 # Color of summary report background\n" + "color.summary.bar=on rgb141 # Color of summary report progress bar\n" + "color.summary.background=on color0 # Color of summary report background\n" "\n" - "color.history.add=color0 on rgb500 # Color of added tasks in ghistory report\n" - "color.history.done=color0 on rgb050 # Color of completed tasks in ghistory report\n" - "color.history.delete=color0 on rgb550 # Color of deleted tasks in ghistory report\n" + "color.history.add=color0 on rgb500 # Color of added tasks in ghistory report\n" + "color.history.done=color0 on rgb050 # Color of completed tasks in ghistory report\n" + "color.history.delete=color0 on rgb550 # Color of deleted tasks in ghistory report\n" "\n" - "color.undo.before=color1 # Color of values before a change\n" - "color.undo.after=color2 # Color of values after a change\n" + "color.undo.before=color1 # Color of values before a change\n" + "color.undo.after=color2 # Color of values after a change\n" "\n" "color.calendar.today=color15 on rgb013 # Color of today in calendar\n" - "color.calendar.due=color0 on color1 # Color of days with due tasks in calendar\n" - "color.calendar.due.today=color15 on color1 # Color of today with due tasks in calendar\n" - "color.calendar.overdue=color0 on color9 # Color of days with overdue tasks in calendar\n" - "color.calendar.weekend=color235 # Color of weekend days in calendar\n" - "color.calendar.holiday=color0 on color11 # Color of public holidays in calendar\n" - "color.calendar.weeknumber=rgb013 # Color of the weeknumbers in calendar\n" + "color.calendar.due=color0 on color1 # Color of days with due tasks in calendar\n" + "color.calendar.due.today=color15 on color1 # Color of today with due tasks in calendar\n" + "color.calendar.overdue=color0 on color9 # Color of days with overdue tasks in calendar\n" + "color.calendar.weekend=color235 # Color of weekend days in calendar\n" + "color.calendar.holiday=color0 on color11 # Color of public holidays in calendar\n" + "color.calendar.weeknumber=rgb013 # Color of the weeknumbers in calendar\n" "\n" - "# The following rules are presented in their order of precedence.\n" - "# The higher the color rule is up this list, the higher precedence\n" - "# it has in determining the color for the task. Precedence is shown\n" - "# in brackets [1]\n" - "color.recurring=rgb013 # [1] Color of recur.any: tasks\n" - "color.overdue=color9 # [2] Color of overdue tasks\n" - "color.due.today=rgb400 # [3] Color of tasks due today\n" - "color.due=color1 # [4] Color of due tasks\n" - "#color.keyword.car=on blue # [5] Color of description.contains:car tasks\n" - "#color.project.garden=on green # [6] Color of project:garden tasks\n" - "#color.tag.bug=yellow # [7] Color of +bug tasks\n" - "color.active=rgb555 on rgb410 # [8] Color of active tasks\n" - "color.pri.none= # [9] Color of priority: tasks\n" - "color.pri.H=rgb255 # [9] Color of priority:H tasks\n" - "color.pri.M=rgb250 # [9] Color of priority:M tasks\n" - "color.pri.L=rgb245 # [9] Color of priority:L tasks\n" - "color.tagged=rgb031 # [10] Color of tagged tasks\n" - "color.blocked=black on white # [11] Color of blocked tasks\n" - "color.alternate=on color233 # [12] Alternate color for line coloring\n" + "# Here are the color rules.\n" + "color.recurring=rgb013 # Color of recur.any: tasks\n" + "color.overdue=color9 # Color of overdue tasks\n" + "color.due.today=rgb400 # Color of tasks due today\n" + "color.due=color1 # Color of due tasks\n" + "#color.keyword.car=on blue # Color of description.contains:car tasks\n" + "#color.project.garden=on green # Color of project:garden tasks\n" + "#color.tag.bug=yellow # Color of +bug tasks\n" + "color.active=rgb555 on rgb410 # Color of active tasks\n" + "color.pri.none= # Color of priority: tasks\n" + "color.pri.H=rgb255 # Color of priority:H tasks\n" + "color.pri.M=rgb250 # Color of priority:M tasks\n" + "color.pri.L=rgb245 # Color of priority:L tasks\n" + "color.tagged=rgb031 # Color of tagged tasks\n" + "color.blocked=black on white # Color of blocked tasks\n" #else - "color.header=yellow # Color of header messages\n" - "color.footnote=yellow # Color of footnote messages\n" - "color.debug=yellow # Color of diagnostic output\n" + "color.header=yellow # Color of header messages\n" + "color.footnote=yellow # Color of footnote messages\n" + "color.debug=yellow # Color of diagnostic output\n" + "color.alternate= # Alternate color for line coloring\n" "\n" - "color.summary.bar=on green # Color of summary report progress bar\n" - "color.summary.background=on black # Color of summary report background\n" + "color.summary.bar=on green # Color of summary report progress bar\n" + "color.summary.background=on black # Color of summary report background\n" "\n" - "color.history.add=black on red # Color of added tasks in ghistory report\n" - "color.history.done=black on green # Color of completed tasks in ghistory report\n" - "color.history.delete=black on yellow # Color of deleted tasks in ghistory report\n" + "color.history.add=black on red # Color of added tasks in ghistory report\n" + "color.history.done=black on green # Color of completed tasks in ghistory report\n" + "color.history.delete=black on yellow # Color of deleted tasks in ghistory report\n" "\n" - "color.undo.before=red # Color of values before a change\n" - "color.undo.after=green # Color of values after a change\n" + "color.undo.before=red # Color of values before a change\n" + "color.undo.after=green # Color of values after a change\n" "\n" "color.calendar.today=bold white on bright blue # Color of today in calendar\n" - "color.calendar.due=white on red # Color of days with due tasks in calendar\n" - "color.calendar.due.today=bold white on red # Color of today with due tasks in calendar\n" - "color.calendar.overdue=black on bright red # Color of days with overdue tasks in calendar\n" - "color.calendar.weekend=white on bright black # Color of weekend days in calendar\n" - "color.calendar.holiday=black on bright yellow # Color of public holidays in calendar\n" - "color.calendar.weeknumber=bold blue # Color of the weeknumbers in calendar\n" + "color.calendar.due=white on red # Color of days with due tasks in calendar\n" + "color.calendar.due.today=bold white on red # Color of today with due tasks in calendar\n" + "color.calendar.overdue=black on bright red # Color of days with overdue tasks in calendar\n" + "color.calendar.weekend=white on bright black # Color of weekend days in calendar\n" + "color.calendar.holiday=black on bright yellow # Color of public holidays in calendar\n" + "color.calendar.weeknumber=bold blue # Color of the weeknumbers in calendar\n" "\n" - "# The following rules are presented in their order of precedence.\n" - "# The higher the color rule is up this list, the higher precedence\n" - "# it has in determining the color for the task. Precedence is shown\n" - "# in brackets [1]\n" - "color.recurring=magenta # [1] Color of recur.any: tasks\n" - "color.overdue=bold red # [2] Color of overdue tasks\n" - "color.due.today=red # [3] Color of tasks due today\n" - "color.due=red # [4] Color of due tasks\n" - "#color.keyword.car=on blue # [5] Color of description.contains:car tasks\n" - "#color.project.garden=on green # [6] Color of project:garden tasks\n" - "#color.tag.bug=yellow # [7] Color of +bug tasks\n" - "color.active=black on bright green # [8] Color of active tasks\n" - "color.pri.none= # [9] Color of priority: tasks\n" - "color.pri.H=bold white # [9] Color of priority:H tasks\n" - "color.pri.M=white # [9] Color of priority:M tasks\n" - "color.pri.L= # [9] Color of priority:L tasks\n" - "color.tagged=green # [10] Color of tagged tasks\n" - "color.blocked=black on white # [11] Color of blocked tasks\n" - "color.alternate= # [12] Alternate color for line coloring\n" + "# Here are the color rules.\n" + "color.recurring=magenta # Color of recur.any: tasks\n" + "color.overdue=bold red # Color of overdue tasks\n" + "color.due.today=red # Color of tasks due today\n" + "color.due=red # Color of due tasks\n" + "#color.keyword.car=on blue # Color of description.contains:car tasks\n" + "#color.project.garden=on green # Color of project:garden tasks\n" + "#color.tag.bug=yellow # Color of +bug tasks\n" + "color.active=black on bright green # Color of active tasks\n" + "color.pri.none= # Color of priority: tasks\n" + "color.pri.H=bold white # Color of priority:H tasks\n" + "color.pri.M=white # Color of priority:M tasks\n" + "color.pri.L= # Color of priority:L tasks\n" + "color.tagged=green # Color of tagged tasks\n" + "color.blocked=black on white # Color of blocked tasks\n" #endif + "\n" + "# Here is the rule precedence order, highest to lowest.\n" + "# Note that these are just the color rule names, without the leading 'color.'\n" + "# and any trailing '.value'.\n" + "rule.precedence.color=due.today,active,blocked,overdue,due,keyword,project,tag,recurring,pri,tagged\n" "\n" "# Shadow file support\n" - "#shadow.file=/tmp/shadow.txt # Location of shadow file\n" - "#shadow.command=list # Task command for shadow file\n" - "#shadow.notify=on # Footnote when updated\n" + "#shadow.file=/tmp/shadow.txt # Location of shadow file\n" + "#shadow.command=list # Task command for shadow file\n" + "#shadow.notify=on # Footnote when updated\n" "\n" - "#default.project=foo # Default project for 'add' command\n" - "#default.priority=M # Default priority for 'add' command\n" - "default.command=list # When no arguments are specified\n" + "#default.project=foo # Default project for 'add' command\n" + "#default.priority=M # Default priority for 'add' command\n" + "default.command=list # When no arguments are specified\n" "\n" - "_forcecolor=no # Forces color to be on, even for non TTY output\n" - "blanklines=true # Use more whitespace in output\n" - "complete.all.projects=no # Include old project names in '_projects' command\n" - "complete.all.tags=no # Include old tag names in '_ags' command\n" - "list.all.projects=no # Include old project names in 'projects' command\n" - "list.all.tags=no # Include old tag names in 'tags' command\n" - "debug=no # Display diagnostics\n" - "hooks=off # Hook system master switch\n" - "fontunderline=yes # Uses underlines rather than -------\n" - "shell.prompt=task> # Prompt used by the shell command\n" + "_forcecolor=no # Forces color to be on, even for non TTY output\n" + "blanklines=true # Use more whitespace in output\n" + "complete.all.projects=no # Include old project names in '_projects' command\n" + "complete.all.tags=no # Include old tag names in '_ags' command\n" + "list.all.projects=no # Include old project names in 'projects' command\n" + "list.all.tags=no # Include old tag names in 'tags' command\n" + "debug=no # Display diagnostics\n" + "hooks=off # Hook system master switch\n" + "fontunderline=yes # Uses underlines rather than -------\n" + "shell.prompt=task> # Prompt used by the shell command\n" "\n" "# Import heuristics - alternate names for fields (comma-separated list of names)\n" "#import.synonym.bg=?\n" @@ -242,14 +241,14 @@ std::string Config::defaults = "#import.synonym.uuid=?\n" "\n" "# Export Controls\n" - "export.ical.class=PRIVATE # Could be PUBLIC, PRIVATE or CONFIDENTIAL\n" + "export.ical.class=PRIVATE # Could be PUBLIC, PRIVATE or CONFIDENTIAL\n" "\n" "# Aliases - alternate names for commands\n" - "alias.rm=delete # Alias for the delete command\n" - "alias.history=history.monthly # Prefer monthly over annual history reports\n" - "alias.ghistory=ghistory.monthly # Prefer monthly graphical over annual history reports\n" - "alias.export=export.yaml # Prefer YAML over CSV or iCal export\n" - "alias.export.vcalendar=export.ical # They are the same\n" + "alias.rm=delete # Alias for the delete command\n" + "alias.history=history.monthly # Prefer monthly over annual history reports\n" + "alias.ghistory=ghistory.monthly # Prefer monthly graphical over annual history reports\n" + "alias.export=export.yaml # Prefer YAML over CSV or iCal export\n" + "alias.export.vcalendar=export.ical # They are the same\n" "\n" "# Fields: id, uuid, project, priority, priority_long, entry, start, end, due\n" "# countdown, countdown_compact, age, age_compact, active, tags,\n" @@ -395,6 +394,7 @@ std::string Config::defaults = // // In all real use cases, Config::load is called. Config::Config () +: original_file () { } diff --git a/src/Context.cpp b/src/Context.cpp index 8aeb1073f..a2ee3e702 100644 --- a/src/Context.cpp +++ b/src/Context.cpp @@ -125,8 +125,6 @@ void Context::initialize () if (locale != "") stringtable.load (location.data + "/strings." + locale); - // TODO Handle "--version, -v" right here? - // init TDB. tdb.clear (); std::vector all; diff --git a/src/command.cpp b/src/command.cpp index 39d0ac559..1c13f3de6 100644 --- a/src/command.cpp +++ b/src/command.cpp @@ -744,6 +744,7 @@ int handleShow (std::string &outs) "import.synonym.id import.synonym.uuid complete.all.projects complete.all.tags " "search.case.sensitive hooks active.indicator tag.indicator recurrence.indicator " "recurrence.limit list.all.projects list.all.tags undo.style verbose " + "rule.precedence.color " #ifdef FEATURE_SHELL "shell.prompt " #endif diff --git a/src/rules.cpp b/src/rules.cpp index 0d576f978..88cf084ee 100644 --- a/src/rules.cpp +++ b/src/rules.cpp @@ -36,137 +36,251 @@ extern Context context; static std::map gsColor; +static std::vector gsPrecedence; //////////////////////////////////////////////////////////////////////////////// void initializeColorRules () { - std::vector ruleNames; - context.config.all (ruleNames); - foreach (it, ruleNames) + gsColor.clear (); + gsPrecedence.clear (); + + // Load all the configuration values, filter to only the ones that begin with + // "color.", then store name/value in gsColor, and name in rules. + std::vector rules; + std::vector variables; + context.config.all (variables); + foreach (it, variables) { if (it->substr (0, 6) == "color.") { Color c (context.config.get (*it)); gsColor[*it] = c; + + rules.push_back (*it); + } + } + + // Load the rule.precedence.color list, split it, then autocomplete against + // the 'rules' vector loaded above. + std::vector results; + std::vector precedence; + split (precedence, context.config.get ("rule.precedence.color"), ','); + + foreach (it, precedence) + { + // Add the leading "color." string. + std::string rule = "color." + *it; + autoComplete (rule, rules, results); + + foreach (r, results) + gsPrecedence.push_back (*r); + } +} + +//////////////////////////////////////////////////////////////////////////////// +static void colorizeBlocked (Task& task, const std::string& rule, Color& c) +{ + if (gsColor[rule].nontrivial ()) + if (task.get ("depends") != "") + c.blend (gsColor[rule]); +} + +//////////////////////////////////////////////////////////////////////////////// +static void colorizeTagged (Task& task, const std::string& rule, Color& c) +{ + if (gsColor[rule].nontrivial ()) + if (task.getTagCount ()) + c.blend (gsColor[rule]); +} + +//////////////////////////////////////////////////////////////////////////////// +static void colorizePriorityL (Task& task, const std::string& rule, Color& c) +{ + if (gsColor[rule].nontrivial ()) + if (task.get ("priority") == "L") + c.blend (gsColor[rule]); +} + +//////////////////////////////////////////////////////////////////////////////// +static void colorizePriorityM (Task& task, const std::string& rule, Color& c) +{ + if (gsColor[rule].nontrivial ()) + if (task.get ("priority") == "M") + c.blend (gsColor[rule]); +} + +//////////////////////////////////////////////////////////////////////////////// +static void colorizePriorityH (Task& task, const std::string& rule, Color& c) +{ + if (gsColor[rule].nontrivial ()) + if (task.get ("priority") == "H") + c.blend (gsColor[rule]); +} + +//////////////////////////////////////////////////////////////////////////////// +static void colorizePriorityNone (Task& task, const std::string& rule, Color& c) +{ + if (gsColor[rule].nontrivial ()) + if (task.get ("priority") == "") + c.blend (gsColor[rule]); +} + +//////////////////////////////////////////////////////////////////////////////// +static void colorizeActive (Task& task, const std::string& rule, Color& c) +{ + Task::status status = task.getStatus (); + + if (gsColor[rule].nontrivial () && + status != Task::completed && + status != Task::deleted && + task.has ("start")) + c.blend (gsColor[rule]); +} + +//////////////////////////////////////////////////////////////////////////////// +static void colorizeTag (Task& task, const std::string& rule, Color& c) +{ + if (task.hasTag (rule.substr (10))) + c.blend (gsColor[rule]); +} + +//////////////////////////////////////////////////////////////////////////////// +static void colorizeProject (Task& task, const std::string& rule, Color& c) +{ + // Observe the case sensitivity setting. + bool sensitive = context.config.getBoolean ("search.case.sensitive"); + + if (compare (task.get ("project"), rule.substr (14), sensitive)) + c.blend (gsColor[rule]); +} + +//////////////////////////////////////////////////////////////////////////////// +static void colorizeProjectNone (Task& task, const std::string& rule, Color& c) +{ + if (task.get ("project") == "") + c.blend (gsColor[rule]); +} + +//////////////////////////////////////////////////////////////////////////////// +static void colorizeTagNone (Task& task, const std::string& rule, Color& c) +{ + if (task.getTagCount () == 0) + c.blend (gsColor[rule]); +} + +//////////////////////////////////////////////////////////////////////////////// +static void colorizeKeyword (Task& task, const std::string& rule, Color& c) +{ + // Observe the case sensitivity setting. + bool sensitive = context.config.getBoolean ("search.case.sensitive"); + + // The easiest thing to check is the description, because it is just one + // attribute. + if (find (task.get ("description"), rule.substr (14), sensitive) != std::string::npos) + c.blend (gsColor[rule]); + + // Failing the description check, look at all annotations, returning on the + // first match. + else + { + Task::iterator it; + for (it = task.begin (); it != task.end (); ++it) + { + if (it->first.substr (0, 11) == "annotation_" && + find (it->second.value (), rule.substr (14), sensitive) != std::string::npos) + { + c.blend (gsColor[rule]); + return; + } } } } //////////////////////////////////////////////////////////////////////////////// -void autoColorize (Task& task, Color& c) +static void colorizeDue (Task& task, const std::string& rule, Color& c) { - // The special tag 'nocolor' overrides all auto colorization. - if (task.hasTag ("nocolor")) - return; - - // Note: fg, bg already contain colors specifically assigned via command. - // Note: These rules form a hierarchy - the last rule is King. - Task::status status = task.getStatus (); - // Colorization of the blocked. - if (gsColor["color.blocked"].nontrivial ()) - if (task.get ("depends") != "") - c.blend (gsColor["color.blocked"]); - - // Colorization of the tagged. - if (gsColor["color.tagged"].nontrivial ()) - if (task.getTagCount ()) - c.blend (gsColor["color.tagged"]); - - // Colorization of the low priority. - if (gsColor["color.pri.L"].nontrivial ()) - if (task.get ("priority") == "L") - c.blend (gsColor["color.pri.L"]); - - // Colorization of the medium priority. - if (gsColor["color.pri.M"].nontrivial ()) - if (task.get ("priority") == "M") - c.blend (gsColor["color.pri.M"]); - - // Colorization of the high priority. - if (gsColor["color.pri.H"].nontrivial ()) - if (task.get ("priority") == "H") - c.blend (gsColor["color.pri.H"]); - - // Colorization of the priority-less. - if (gsColor["color.pri.none"].nontrivial ()) - if (task.get ("priority") == "") - c.blend (gsColor["color.pri.none"]); - - // Colorization of the active, if not completed/deleted. - if (gsColor["color.active"].nontrivial () && - status != Task::completed && - status != Task::deleted) - if (task.has ("start")) - c.blend (gsColor["color.active"]); - - // Colorization by tag value. - std::map ::iterator it; - for (it = gsColor.begin (); it != gsColor.end (); ++it) - { - if (it->first.substr (0, 10) == "color.tag.") - { - std::string value = it->first.substr (10); - if (task.hasTag (value)) - c.blend (it->second); - } - } - - // Colorization by project name. - for (it = gsColor.begin (); it != gsColor.end (); ++it) - { - if (it->first.substr (0, 14) == "color.project.") - { - std::string value = lowerCase (it->first.substr (14)); - std::string project = lowerCase (task.get ("project")); - if (project.find (value) == 0) - c.blend (it->second); - } - } - - // Colorization by keyword. - for (it = gsColor.begin (); it != gsColor.end (); ++it) - { - if (it->first.substr (0, 14) == "color.keyword.") - { - std::string value = lowerCase (it->first.substr (14)); - std::string desc = lowerCase (task.get ("description")); - if (desc.find (value) != std::string::npos) - c.blend (it->second); - } - } - - // Colorization of the due and overdue. if (task.has ("due") && status != Task::completed && status != Task::deleted) { - std::string due = task.get ("due"); - switch (getDueState (due)) - { - case 1: // imminent - c.blend (gsColor["color.due"]); - break; + if (getDueState (task.get ("due")) == 1) + c.blend (gsColor[rule]); + } +} - case 2: // today - c.blend (gsColor["color.due.today"]); - break; +//////////////////////////////////////////////////////////////////////////////// +static void colorizeDueToday (Task& task, const std::string& rule, Color& c) +{ + Task::status status = task.getStatus (); - case 3: // overdue - c.blend (gsColor["color.overdue"]); - break; + if (task.has ("due") && + status != Task::completed && + status != Task::deleted) + { + if (getDueState (task.get ("due")) == 2) + c.blend (gsColor[rule]); + } +} - case 0: // not due at all - default: - break; - } +//////////////////////////////////////////////////////////////////////////////// +static void colorizeOverdue (Task& task, const std::string& rule, Color& c) +{ + Task::status status = task.getStatus (); + + if (task.has ("due") && + status != Task::completed && + status != Task::deleted) + { + if (getDueState (task.get ("due")) == 3) + c.blend (gsColor[rule]); + } +} + +//////////////////////////////////////////////////////////////////////////////// +static void colorizeRecurring (Task& task, const std::string& rule, Color& c) +{ + if (gsColor[rule].nontrivial ()) + if (task.has ("recur")) + c.blend (gsColor[rule]); +} + +//////////////////////////////////////////////////////////////////////////////// +void autoColorize (Task& task, Color& c) +{ + // The special tag 'nocolor' overrides all auto and specific colorization. + if (task.hasTag ("nocolor")) + { + c = Color (); + return; } - // Colorization of the recurring. - if (gsColor["color.recurring"].nontrivial ()) - if (task.has ("recur")) - c.blend (gsColor["color.recurring"]); + // Note: c already contains colors specifically assigned via command. + // Note: These rules form a hierarchy - the last rule is King, hence the + // reverse iterator. + std::vector ::reverse_iterator r; + for (r = gsPrecedence.rbegin (); r != gsPrecedence.rend (); ++r) + { + if (*r == "color.blocked") colorizeBlocked (task, *r, c); + else if (*r == "color.tagged") colorizeTagged (task, *r, c); + else if (*r == "color.pri.L") colorizePriorityL (task, *r, c); + else if (*r == "color.pri.M") colorizePriorityM (task, *r, c); + else if (*r == "color.pri.H") colorizePriorityH (task, *r, c); + else if (*r == "color.pri.none") colorizePriorityNone (task, *r, c); + else if (*r == "color.active") colorizeActive (task, *r, c); + else if (*r == "color.project.none") colorizeProjectNone (task, *r, c); + else if (*r == "color.tag.none") colorizeTagNone (task, *r, c); + else if (*r == "color.due") colorizeDue (task, *r, c); + else if (*r == "color.due.today") colorizeDueToday (task, *r, c); + else if (*r == "color.overdue") colorizeOverdue (task, *r, c); + else if (*r == "color.recurring") colorizeRecurring (task, *r, c); + + // Wildcards + else if (r->substr (0, 9) == "color.tag") colorizeTag (task, *r, c); + else if (r->substr (0, 13) == "color.project") colorizeProject (task, *r, c); + else if (r->substr (0, 13) == "color.keyword") colorizeKeyword (task, *r, c); + } } //////////////////////////////////////////////////////////////////////////////// diff --git a/src/tests/color.keyword.t b/src/tests/color.keyword.t index 8b09f946f..d7f1d2261 100755 --- a/src/tests/color.keyword.t +++ b/src/tests/color.keyword.t @@ -2,7 +2,7 @@ ################################################################################ ## taskwarrior - a command line task list manager. ## -## Copyright 2006 - 2010, Paul Beckingham. +## Copyright 2006 - 2010, Paul Beckingham, Federico Hernandez. ## All rights reserved. ## ## This program is free software; you can redistribute it and/or modify it under @@ -28,15 +28,18 @@ use strict; use warnings; -use Test::More tests => 8; +use Test::More tests => 9; # Create the rc file. if (open my $fh, '>', 'color.rc') { print $fh "data.location=.\n", + "search.case.sensitive=yes\n", + "color=on\n", "color.alternate=\n", "color.keyword.red=red\n", "color.keyword.green=green\n", + "color.keyword.yellow=yellow\n", "_forcecolor=1\n"; close $fh; ok (-r 'color.rc', 'Created color.rc'); @@ -46,11 +49,14 @@ if (open my $fh, '>', 'color.rc') qx{../task rc:color.rc add nothing}; qx{../task rc:color.rc add red}; qx{../task rc:color.rc add green}; +qx{../task rc:color.rc add -- annotation}; +qx{../task rc:color.rc 4 annotate yellow}; my $output = qx{../task rc:color.rc list}; -like ($output, qr/ (?!<\033\[\d\dm) .* nothing .* (?!>\033\[0m) /x, 'none'); -like ($output, qr/ \033\[31m .* red .* \033\[0m /x, 'color.keyword.red'); -like ($output, qr/ \033\[32m .* green .* \033\[0m /x, 'color.keyword.green'); +like ($output, qr/ (?!<\033\[\d\dm) .* nothing .* (?!>\033\[0m) /x, 'none'); +like ($output, qr/ \033\[31m .* red .* \033\[0m /x, 'color.keyword.red'); +like ($output, qr/ \033\[32m .* green .* \033\[0m /x, 'color.keyword.green'); +like ($output, qr/ \033\[33m .* annotation .* \033\[0m /x, 'color.keyword.yellow (annotation)'); # Cleanup. unlink 'pending.data'; diff --git a/src/tests/color.project.t b/src/tests/color.project.t index 9e9305d12..153b97d19 100755 --- a/src/tests/color.project.t +++ b/src/tests/color.project.t @@ -35,6 +35,7 @@ if (open my $fh, '>', 'color.rc') { print $fh "data.location=.\n", "color.project.x=red\n", + "color.project.none=green\n", "color.alternate=\n", "_forcecolor=1\n"; close $fh; @@ -46,7 +47,7 @@ qx{../task rc:color.rc add nothing}; qx{../task rc:color.rc add project:x red}; my $output = qx{../task rc:color.rc list}; -like ($output, qr/ (?!<\033\[\d\dm) .* nothing .* (?!>\033\[0m) /x, 'none'); +like ($output, qr/ \033\[32m .* nothing .* \033\[0m /x, 'color.project.none'); like ($output, qr/ \033\[31m .* red .* \033\[0m /x, 'color.project.red'); # Cleanup. diff --git a/src/tests/color.tag.t b/src/tests/color.tag.t index 1bbc5622f..76eeb975a 100755 --- a/src/tests/color.tag.t +++ b/src/tests/color.tag.t @@ -36,6 +36,7 @@ if (open my $fh, '>', 'color.rc') print $fh "data.location=.\n", "color.tagged=\n", "color.alternate=\n", + "color.tag.none=yellow\n", "color.tag.red=red\n", "color.tag.green=green\n", "_forcecolor=1\n"; @@ -49,7 +50,7 @@ qx{../task rc:color.rc add +red red}; qx{../task rc:color.rc add +green green}; my $output = qx{../task rc:color.rc list}; -like ($output, qr/ (?!<\033\[\d\dm) .* nothing .* (?!>\033\[0m) /x, 'none'); +like ($output, qr/ \033\[33m .* nothing .* \033\[0m /x, 'color.tag.none'); like ($output, qr/ \033\[31m .* red .* \033\[0m /x, 'color.tag.red'); like ($output, qr/ \033\[32m .* green .* \033\[0m /x, 'color.tag.green'); From d85feef7ea527f12683a27ebd04172fcc182bf75 Mon Sep 17 00:00:00 2001 From: Paul Beckingham Date: Sun, 29 Aug 2010 14:15:49 -0400 Subject: [PATCH 03/11] Documentation - taskrc.5 - Corrected man page on the subject of color rule defaults and themes. --- doc/man/taskrc.5 | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/doc/man/taskrc.5 b/doc/man/taskrc.5 index 61d9b96f8..d9e38bd94 100644 --- a/doc/man/taskrc.5 +++ b/doc/man/taskrc.5 @@ -536,9 +536,10 @@ terminal, can be obtained by running the command: .B task color .RE - .RS -The coloration rules are: +Note that no default values are listed here - the defaults now correspond to the +dark-256.theme (Linux) and dark-16.theme (other) theme values. +The coloration rules are as follows: .RE .RS From f2a5dde3a6dad60223f227fbc3b90af983b27b9b Mon Sep 17 00:00:00 2001 From: Paul Beckingham Date: Mon, 30 Aug 2010 20:37:44 -0400 Subject: [PATCH 04/11] Themes - Updated themes to include blank entries for the new color.tag.none and color.project.none rules. --- doc/rc/dark-16.theme | 4 +++- doc/rc/dark-256.theme | 4 +++- doc/rc/dark-blue-256.theme | 4 +++- doc/rc/dark-green-256.theme | 4 +++- doc/rc/dark-red-256.theme | 4 +++- doc/rc/light-16.theme | 4 +++- doc/rc/light-256.theme | 4 +++- src/Config.cpp | 4 ++++ src/command.cpp | 2 +- 9 files changed, 26 insertions(+), 8 deletions(-) diff --git a/doc/rc/dark-16.theme b/doc/rc/dark-16.theme index fb1daa63b..8ab05b737 100644 --- a/doc/rc/dark-16.theme +++ b/doc/rc/dark-16.theme @@ -60,6 +60,8 @@ color.pri.H=bold white color.pri.M=white color.pri.L= color.tagged=green -color.blocked=black on white # placeholder color +color.blocked=black on white +color.project.none= +color.tag.none= color.alternate= diff --git a/doc/rc/dark-256.theme b/doc/rc/dark-256.theme index 2773b4f4c..abcf1f7f5 100644 --- a/doc/rc/dark-256.theme +++ b/doc/rc/dark-256.theme @@ -60,6 +60,8 @@ color.pri.H=color255 color.pri.M=color250 color.pri.L=color245 color.tagged=rgb031 -color.blocked=black on white # placeholder color +color.blocked=on gray4 +color.project.none= +color.tag.none= color.alternate=on color233 diff --git a/doc/rc/dark-blue-256.theme b/doc/rc/dark-blue-256.theme index 4964c01a0..975fe367f 100644 --- a/doc/rc/dark-blue-256.theme +++ b/doc/rc/dark-blue-256.theme @@ -60,6 +60,8 @@ color.pri.H=rgb035 color.pri.M=rgb025 color.pri.L=rgb015 color.tagged=color246 -color.blocked=black on white # placeholder color +color.blocked=on gray4 +color.project.none= +color.tag.none= color.alternate=on color233 diff --git a/doc/rc/dark-green-256.theme b/doc/rc/dark-green-256.theme index b4c27ef2f..2cb3ff54a 100644 --- a/doc/rc/dark-green-256.theme +++ b/doc/rc/dark-green-256.theme @@ -60,6 +60,8 @@ color.pri.H=rgb050 color.pri.M=rgb030 color.pri.L=rgb010 color.tagged=color246 -color.blocked=black on white # placeholder color +color.blocked=on gray4 +color.project.none= +color.tag.none= color.alternate=on color233 diff --git a/doc/rc/dark-red-256.theme b/doc/rc/dark-red-256.theme index f3c8c61b1..a979a6e4b 100644 --- a/doc/rc/dark-red-256.theme +++ b/doc/rc/dark-red-256.theme @@ -60,6 +60,8 @@ color.pri.H=rgb500 color.pri.M=rgb400 color.pri.L=rgb300 color.tagged=color246 -color.blocked=black on white # placeholder color +color.blocked=on gray4 +color.project.none= +color.tag.none= color.alternate=on color233 diff --git a/doc/rc/light-16.theme b/doc/rc/light-16.theme index a3e887dd5..45fc2d880 100644 --- a/doc/rc/light-16.theme +++ b/doc/rc/light-16.theme @@ -60,6 +60,8 @@ color.pri.H=bold black color.pri.M=black color.pri.L= color.tagged=green -color.blocked=black on white # placeholder color +color.blocked=black on white +color.project.none= +color.tag.none= color.alternate= diff --git a/doc/rc/light-256.theme b/doc/rc/light-256.theme index 08dfbd0f2..2bb5e774f 100644 --- a/doc/rc/light-256.theme +++ b/doc/rc/light-256.theme @@ -59,6 +59,8 @@ color.pri.H=color232 color.pri.M=color237 color.pri.L=color242 color.tagged=rgb020 -color.blocked=black on white # placeholder color +color.blocked=on gray4 +color.project.none= +color.tag.none= color.alternate=on color254 diff --git a/src/Config.cpp b/src/Config.cpp index 6ec49c5f0..a87a99a18 100644 --- a/src/Config.cpp +++ b/src/Config.cpp @@ -150,7 +150,9 @@ std::string Config::defaults = "color.due=color1 # Color of due tasks\n" "#color.keyword.car=on blue # Color of description.contains:car tasks\n" "#color.project.garden=on green # Color of project:garden tasks\n" + "#color.project.none= # Color of tasks with no project\n" "#color.tag.bug=yellow # Color of +bug tasks\n" + "#color.tag.none= # Color of tag-less tasks\n" "color.active=rgb555 on rgb410 # Color of active tasks\n" "color.pri.none= # Color of priority: tasks\n" "color.pri.H=rgb255 # Color of priority:H tasks\n" @@ -189,7 +191,9 @@ std::string Config::defaults = "color.due=red # Color of due tasks\n" "#color.keyword.car=on blue # Color of description.contains:car tasks\n" "#color.project.garden=on green # Color of project:garden tasks\n" + "#color.project.none= # Color of tasks with no project\n" "#color.tag.bug=yellow # Color of +bug tasks\n" + "#color.tag.none= # Color of tag-less tasks\n" "color.active=black on bright green # Color of active tasks\n" "color.pri.none= # Color of priority: tasks\n" "color.pri.H=bold white # Color of priority:H tasks\n" diff --git a/src/command.cpp b/src/command.cpp index 1c13f3de6..fc3bc785e 100644 --- a/src/command.cpp +++ b/src/command.cpp @@ -1965,7 +1965,7 @@ int handleColor (std::string &outs) // actual colors. if (*item != "_forcecolor" && *item != "color" && - item->find ("color") != std::string::npos) + item->find ("color") == 0) { int row = table.addRow (); table.addCell (row, 0, *item); From 69ac9a4296275cb8340967d043aa4b4a346c6372 Mon Sep 17 00:00:00 2001 From: Paul Beckingham Date: Mon, 30 Aug 2010 23:30:48 -0400 Subject: [PATCH 05/11] Bug #461 - due:today doesn't work as a filter - due.is:today does - Fixed bug #461, in which the filter 'due:today' failed, but 'due.is:today' worked. This is because while iterating over tasks, not every task has a due date, in which case Date::Date ("") was called, which fails. - Moved 'wait' up to second position in the Att::type method, for efficiency. --- ChangeLog | 2 ++ src/Att.cpp | 15 ++++++++------- src/tests/due.t | 9 ++++++++- src/tests/wait.t | 4 ++-- 4 files changed, 20 insertions(+), 10 deletions(-) diff --git a/ChangeLog b/ChangeLog index a5340aae1..adfa36c9c 100644 --- a/ChangeLog +++ b/ChangeLog @@ -61,6 +61,8 @@ and overdue. + Fixed bug #459, which showed a confusing message when 'limit:page' was used, with few tasks. + + Fixed bug #461, in which the filter 'due:today' failed, but 'due.is:today' + worked. + Fixed bug #466, which gave the wrong error message when a custom report was missing a direction indicator for the sort order. + Fixed bug #470, which caused task to not support the color 'none'. diff --git a/src/Att.cpp b/src/Att.cpp index 21233202c..9b74bb960 100644 --- a/src/Att.cpp +++ b/src/Att.cpp @@ -415,7 +415,7 @@ bool Att::validNameValue ( "\" is not a valid status. Use 'pending', 'completed', 'deleted', 'recurring' or 'waiting'."; } - else if (! validInternalName (name) && + else if (! validInternalName (name) && ! validModifiableName (name)) throw std::string ("'") + name + "' is not a recognized attribute."; @@ -438,11 +438,11 @@ bool Att::validMod (const std::string& mod) std::string Att::type (const std::string& name) const { if (name == "due" || + name == "wait" || name == "until" || name == "start" || name == "entry" || - name == "end" || - name == "wait") + name == "end") return "date"; else if (name == "recur") @@ -542,16 +542,17 @@ bool Att::match (const Att& other) const if (mMod == "") { // Exact matches on dates should only compare m/d/y, not h:m:s. This allows - // Comapisons like "task list due:today" (bug #405). + // comparisons like "task list due:today" (bug #405). std::string which = type (mName); if (which == "date") { + if (other.mValue == "") + return false; + Date left (mValue); Date right (other.mValue); - if (left.year () != right.year () || - left.month () != right.month () || - left.day () != right.day ()) + if (! left.sameDay (right)) return false; } else diff --git a/src/tests/due.t b/src/tests/due.t index 8448fcf0e..5792fc8cd 100755 --- a/src/tests/due.t +++ b/src/tests/due.t @@ -28,7 +28,7 @@ use strict; use warnings; -use Test::More tests => 7; +use Test::More tests => 9; # Create the rc file. if (open my $fh, '>', 'due.rc') @@ -57,6 +57,13 @@ my $output = qx{../task rc:due.rc list}; like ($output, qr/\[31m.+$just.+\[0m/, 'one marked due'); like ($output, qr/\s+$almost\s+/, 'two not marked due'); +qx{../task rc:due.rc add three due:today}; +$output = qx{../task rc:due.rc list due:today}; +like ($output, qr/three/, 'due:today works as a filter'); + +$output = qx{../task rc:due.rc list due.is:today}; +like ($output, qr/three/, 'due.is:today works as a filter'); + # Cleanup. unlink 'pending.data'; ok (!-r 'pending.data', 'Removed pending.data'); diff --git a/src/tests/wait.t b/src/tests/wait.t index f30a151f0..74adaf304 100755 --- a/src/tests/wait.t +++ b/src/tests/wait.t @@ -71,8 +71,8 @@ qx{../task rc:wait.rc add wait:tomorrow tomorrow}; $output = qx{../task rc:wait.rc ls}; unlike ($output, qr/tomorrow/ms, 'waiting task invisible'); -$output = qx{../task rc:wait.rc ls wait:tomorrow}; -like ($output, qr/tomorrow/ms, 'waiting task visible when specifically asked for it'); +$output = qx{../task rc:wait.rc all status:waiting wait:tomorrow}; +like ($output, qr/tomorrow/ms, 'waiting task visible when specifically queried'); # Cleanup. unlink 'pending.data'; From 3a566460a230258f518e5557d11624ba3250784c Mon Sep 17 00:00:00 2001 From: Paul Beckingham Date: Mon, 30 Aug 2010 23:35:04 -0400 Subject: [PATCH 06/11] Unit Tests - Renamed bug.425.t to bug.425.x to reflect that fact that we're going to address this bug in 1.9.4, not 1.9.3. --- src/tests/{bug.425.t => bug.425.x} | 0 1 file changed, 0 insertions(+), 0 deletions(-) rename src/tests/{bug.425.t => bug.425.x} (100%) diff --git a/src/tests/bug.425.t b/src/tests/bug.425.x similarity index 100% rename from src/tests/bug.425.t rename to src/tests/bug.425.x From d738f778ee5ab65e1127aa72adbb055cad3662a7 Mon Sep 17 00:00:00 2001 From: Paul Beckingham Date: Tue, 31 Aug 2010 01:29:48 -0400 Subject: [PATCH 07/11] Dependencies - Restricted dependency nag message to only tasks that have dependencies. --- src/dependency.cpp | 14 +++++++++----- 1 file changed, 9 insertions(+), 5 deletions(-) diff --git a/src/dependency.cpp b/src/dependency.cpp index a0d8bc390..756b678f5 100644 --- a/src/dependency.cpp +++ b/src/dependency.cpp @@ -154,11 +154,15 @@ bool dependencyChainBroken (Task& task) std::string dependencyNag (Task& task) { std::stringstream out; - out << "# dependencyNag " - << task.id - << " " - << task.get ("uuid") - << "\n"; + + if (task.has ("depends")) + { + out << "# dependencyNag " + << task.id + << " " + << task.get ("uuid") + << "\n"; + } return out.str (); } From 808934483f37202ce1011e18a11db2a767aa419f Mon Sep 17 00:00:00 2001 From: Paul Beckingham Date: Tue, 31 Aug 2010 01:30:20 -0400 Subject: [PATCH 08/11] Recurrence - Removed vestigial mask:"..." attribute from newly generated recurring task instances. --- src/recur.cpp | 2 ++ 1 file changed, 2 insertions(+) diff --git a/src/recur.cpp b/src/recur.cpp index f3fc53a2d..2ff9462f6 100644 --- a/src/recur.cpp +++ b/src/recur.cpp @@ -114,6 +114,8 @@ void handleRecurrence () sprintf (indexMask, "%u", (unsigned int) i); rec.set ("imask", indexMask); // Store index into mask. + rec.remove ("mask"); // Remove the mask of the parent. + // Add the new task to the vector, for immediate use. modified.push_back (rec); From d8913c2f15cd572301c8015f56940f2732dc295b Mon Sep 17 00:00:00 2001 From: Paul Beckingham Date: Tue, 31 Aug 2010 23:00:47 -0400 Subject: [PATCH 09/11] Unit Tests - Bug 485 refers to the fact that recurrence values are not autocompleted, and are stored literally. --- src/tests/bug.485.x | 66 +++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 66 insertions(+) create mode 100755 src/tests/bug.485.x diff --git a/src/tests/bug.485.x b/src/tests/bug.485.x new file mode 100755 index 000000000..3013e8bcd --- /dev/null +++ b/src/tests/bug.485.x @@ -0,0 +1,66 @@ +#! /usr/bin/perl +################################################################################ +## taskwarrior - a command line task list manager. +## +## Copyright 2006 - 2010, Paul Beckingham. +## All rights reserved. +## +## This program is free software; you can redistribute it and/or modify it under +## the terms of the GNU General Public License as published by the Free Software +## Foundation; either version 2 of the License, or (at your option) any later +## version. +## +## This program is distributed in the hope that it will be useful, but WITHOUT +## ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS +## FOR A PARTICULAR PURPOSE. See the GNU General Public License for more +## details. +## +## You should have received a copy of the GNU General Public License along with +## this program; if not, write to the +## +## Free Software Foundation, Inc., +## 51 Franklin Street, Fifth Floor, +## Boston, MA +## 02110-1301 +## USA +## +################################################################################ + +use strict; +use warnings; +use Test::More tests => 9; + +# Create the rc file. +if (open my $fh, '>', 'bug.rc') +{ + print $fh "data.location=.\n"; + + close $fh; + ok (-r 'bug.rc', 'Created bug.rc'); +} + +# Bug #485 - 'task list recur:month' doesn't list monthly tasks +qx{../task rc:bug.rc add one due:tomorrow recur:monthly}; +qx{../task rc:bug.rc add two due:tomorrow recur:month}; +my $output = qx{../task rc:bug.rc list recur:monthly}; +like ($output, qr/one/, 'monthly -> monthly'); +like ($output, qr/two/, 'month -> monthly'); + +$output = qx{../task rc:bug.rc list recur:month}; +like ($output, qr/one/, 'monthly -> month'); +like ($output, qr/two/, 'month -> month'); + +# Cleanup. +unlink 'pending.data'; +ok (!-r 'pending.data', 'Removed pending.data'); + +unlink 'completed.data'; +ok (!-r 'completed.data', 'Removed completed.data'); + +unlink 'undo.data'; +ok (!-r 'undo.data', 'Removed undo.data'); + +unlink 'bug.rc'; +ok (!-r 'bug.rc', 'Removed bug.rc'); + +exit 0; From d3ca5c04e32ec482157636a26870839b8f002a40 Mon Sep 17 00:00:00 2001 From: Paul Beckingham Date: Tue, 31 Aug 2010 23:02:16 -0400 Subject: [PATCH 10/11] Code Cleanup - Improved the consistency of info output regarding recurring task parent and child tasks. --- src/report.cpp | 68 ++++++++++++++++++++++++-------------------------- 1 file changed, 32 insertions(+), 36 deletions(-) diff --git a/src/report.cpp b/src/report.cpp index 45be77a21..5e7ea8e8b 100644 --- a/src/report.cpp +++ b/src/report.cpp @@ -470,49 +470,45 @@ int handleInfo (std::string &outs) } } - if (task->getStatus () == Task::recurring || - task->has ("parent")) + // recur + if (task->has ("recur")) { - // recur - if (task->has ("recur")) - { - row = table.addRow (); - table.addCell (row, 0, "Recurrence"); - value = task->get ("recur"); - context.hooks.trigger ("format-recur", "recur", value); - table.addCell (row, 1, value); - } + row = table.addRow (); + table.addCell (row, 0, "Recurrence"); + value = task->get ("recur"); + context.hooks.trigger ("format-recur", "recur", value); + table.addCell (row, 1, value); + } - // until - if (task->has ("until")) - { - row = table.addRow (); - table.addCell (row, 0, "Recur until"); + // until + if (task->has ("until")) + { + row = table.addRow (); + table.addCell (row, 0, "Recur until"); - Date dt (atoi (task->get ("until").c_str ())); - std::string format = context.config.get ("reportdateformat"); - if (format == "") - format = context.config.get ("dateformat"); + Date dt (atoi (task->get ("until").c_str ())); + std::string format = context.config.get ("reportdateformat"); + if (format == "") + format = context.config.get ("dateformat"); - std::string until = getDueDate (*task, format); - table.addCell (row, 1, until); - } + std::string until = getDueDate (*task, format); + table.addCell (row, 1, until); + } - // mask - if (task->has ("mask")) - { - row = table.addRow (); - table.addCell (row, 0, "Mask"); - table.addCell (row, 1, task->get ("mask")); - } + // mask + if (task->getStatus () == Task::recurring) + { + row = table.addRow (); + table.addCell (row, 0, "Mask"); + table.addCell (row, 1, task->get ("mask")); + } + if (task->has ("parent")) + { // parent - if (task->has ("parent")) - { - row = table.addRow (); - table.addCell (row, 0, "Parent task"); - table.addCell (row, 1, task->get ("parent")); - } + row = table.addRow (); + table.addCell (row, 0, "Parent task"); + table.addCell (row, 1, task->get ("parent")); // imask row = table.addRow (); From 4139f32acfa55a18e21279aa492ba175d1d3a8ad Mon Sep 17 00:00:00 2001 From: Paul Beckingham Date: Tue, 31 Aug 2010 23:03:12 -0400 Subject: [PATCH 11/11] Unit Tests - Added more unit tests to verify that @ in a tag does not interfere with multiple inclusions (+) and exclusions (-). --- src/tests/bug.480.t | 63 ++++++++++++++++++++++++++++++++++++++++++++- 1 file changed, 62 insertions(+), 1 deletion(-) diff --git a/src/tests/bug.480.t b/src/tests/bug.480.t index 0db25418a..95a8d77e1 100755 --- a/src/tests/bug.480.t +++ b/src/tests/bug.480.t @@ -28,7 +28,7 @@ use strict; use warnings; -use Test::More tests => 13; +use Test::More tests => 40; # Create the rc file. if (open my $fh, '>', 'bug.rc') @@ -59,6 +59,67 @@ $output = qx{../task rc:bug.rc long -\@strange}; like ($output, qr/one/, '+ordinary implicitly included'); unlike ($output, qr/two/, '@strange explicitly excluded'); +# Bug #XXX - '-t1 -t2' doesn't seem to work, when @ characters are involved. +unlink 'pending.data'; +qx{../task rc:bug.rc add one +t1}; +qx{../task rc:bug.rc add two +t2}; +qx{../task rc:bug.rc add three +t3}; + +my $output = qx{../task rc:bug.rc list -t1}; +unlike ($output, qr/one/, 'Single: no t1'); +like ($output, qr/two/, 'Single: yes t2'); +like ($output, qr/three/, 'Single: yes t3'); + +$output = qx{../task rc:bug.rc list -t1 -t2}; +unlike ($output, qr/one/, 'Double: no t1'); +unlike ($output, qr/two/, 'Double: no t2'); +like ($output, qr/three/, 'Double: yes t3'); + +$output = qx{../task rc:bug.rc list -t1 -t2 -t3}; +unlike ($output, qr/one/, 'Triple: no t1'); +unlike ($output, qr/two/, 'Triple: no t2'); +unlike ($output, qr/three/, 'Triple: no t3'); + +# Once again, with @ characters. +qx{../task rc:bug.rc 1 +\@1}; +qx{../task rc:bug.rc 2 +\@2}; +qx{../task rc:bug.rc 3 +\@3}; + +$output = qx{../task rc:bug.rc list -\@1}; +unlike ($output, qr/one/, 'Single: no @1'); +like ($output, qr/two/, 'Single: yes @2'); +like ($output, qr/three/, 'Single: yes @3'); + +$output = qx{../task rc:bug.rc list -\@1 -\@2}; +unlike ($output, qr/one/, 'Double: no @1'); +unlike ($output, qr/two/, 'Double: no @2'); +like ($output, qr/three/, 'Double: yes @3'); + +$output = qx{../task rc:bug.rc list -\@1 -\@2 -\@3}; +unlike ($output, qr/one/, 'Triple: no @1'); +unlike ($output, qr/two/, 'Triple: no @2'); +unlike ($output, qr/three/, 'Triple: no @3'); + +# Once again, with @ characters and punctuation. +qx{../task rc:bug.rc 1 +\@foo.1}; +qx{../task rc:bug.rc 2 +\@foo.2}; +qx{../task rc:bug.rc 3 +\@foo.3}; + +$output = qx{../task rc:bug.rc list -\@foo.1}; +unlike ($output, qr/one/, 'Single: no @foo.1'); +like ($output, qr/two/, 'Single: yes @foo.2'); +like ($output, qr/three/, 'Single: yes @foo.3'); + +$output = qx{../task rc:bug.rc list -\@foo.1 -\@foo.2}; +unlike ($output, qr/one/, 'Double: no @foo.1'); +unlike ($output, qr/two/, 'Double: no @foo.2'); +like ($output, qr/three/, 'Double: yes @foo.3'); + +$output = qx{../task rc:bug.rc list -\@foo.1 -\@foo.2 -\@foo.3}; +unlike ($output, qr/one/, 'Triple: no @foo.1'); +unlike ($output, qr/two/, 'Triple: no @foo.2'); +unlike ($output, qr/three/, 'Triple: no @foo.3'); + # Cleanup. unlink 'pending.data'; ok (!-r 'pending.data', 'Removed pending.data');