diff --git a/ChangeLog b/ChangeLog index 7f9790117..adfa36c9c 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. @@ -57,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'. @@ -67,6 +73,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/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..d9e38bd94 100644 --- a/doc/man/taskrc.5 +++ b/doc/man/taskrc.5 @@ -537,39 +537,50 @@ terminal, can be obtained by running the command: .RE .RS -The coloration rules and their defaults 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 -.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 +701,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/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/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/Config.cpp b/src/Config.cpp index a779edf26..9573f428c 100644 --- a/src/Config.cpp +++ b/src/Config.cpp @@ -59,172 +59,175 @@ 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" - "merge.autopush=ask # Push database to remote origin after merge: yes, no, ask\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" + "merge.autopush=ask # Push database to remote origin after merge: yes, no, ask\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.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" + "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.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" + "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" @@ -243,14 +246,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" @@ -396,6 +399,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 76d27a32b..9783948b0 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 98b4e3a76..ca6ae97d7 100644 --- a/src/command.cpp +++ b/src/command.cpp @@ -812,6 +812,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 @@ -2032,7 +2033,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); 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 (); } 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); diff --git a/src/report.cpp b/src/report.cpp index 0aa07265d..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 (); @@ -759,7 +755,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]; 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/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 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'); 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; 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'); 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';