mirror of
https://github.com/GothenburgBitFactory/taskwarrior.git
synced 2025-07-07 20:06:36 +02:00
Merge branch '1.7.0' into timesheet
This commit is contained in:
commit
c61a295df7
37 changed files with 1037 additions and 663 deletions
|
@ -4,6 +4,14 @@
|
||||||
1.7.0 (?) ?
|
1.7.0 (?) ?
|
||||||
+ Improved the errors when parsing a corrupt or unrecognized pending.data
|
+ Improved the errors when parsing a corrupt or unrecognized pending.data
|
||||||
or completed.data file (thanks to T. Charles Yun).
|
or completed.data file (thanks to T. Charles Yun).
|
||||||
|
+ Added details to the "info" report about recurring tasks (thanks to T.
|
||||||
|
Charles Yun).
|
||||||
|
+ Now writes a sample "defaultwidth" configuration variable to the default
|
||||||
|
.taskrc file (thanks to T. Charles Yun).
|
||||||
|
+ Task allows commands that require an ID to now be given a sequence, which
|
||||||
|
is a set of IDs. This allows commands like "task delete 1 2 5-10,12".
|
||||||
|
+ Fixed bug in the ghistory report, which caused it to only show a new
|
||||||
|
month if a task was added during that month.
|
||||||
|
|
||||||
------ old releases ------------------------------
|
------ old releases ------------------------------
|
||||||
|
|
||||||
|
|
6
README
6
README
|
@ -44,7 +44,11 @@ All feedback is welcome, in addition to any bug reports or patches to:
|
||||||
|
|
||||||
task@beckingham.net
|
task@beckingham.net
|
||||||
|
|
||||||
Got an idea for an enhancement? Send a message!
|
Or better yet, get involved in the discussion at
|
||||||
|
|
||||||
|
http://groups.google.com/group/taskprogram
|
||||||
|
|
||||||
|
Got an idea for an enhancement? Post a message!
|
||||||
|
|
||||||
I have found that task makes me more productive and organized.
|
I have found that task makes me more productive and organized.
|
||||||
I hope task can do the same for you.
|
I hope task can do the same for you.
|
||||||
|
|
26
checklist.txt
Normal file
26
checklist.txt
Normal file
|
@ -0,0 +1,26 @@
|
||||||
|
Release Checklist
|
||||||
|
-----------------
|
||||||
|
|
||||||
|
- Update "Upcoming Features" document on group
|
||||||
|
- Ensure all unit tests pass on OS X
|
||||||
|
- Ensure clean build on OS X
|
||||||
|
- Make a source package (1)
|
||||||
|
- Ensure clean build on latest Fedora Core from source package
|
||||||
|
- Git clone and rebuild, ensure all unit tests pass
|
||||||
|
- Ensure clean build on latest Ubuntu from source package
|
||||||
|
- Git clone and rebuild, ensure all unit tests pass
|
||||||
|
- Ensure clean build on Windows/Cygwin from source package
|
||||||
|
- Git clone and rebuild, ensure all unit tests pass
|
||||||
|
- Make a new source package (2)
|
||||||
|
- Add actual release date to ChangeLog
|
||||||
|
- Add actual release date to html/task.html
|
||||||
|
- Merge version branch to master
|
||||||
|
- Tag master
|
||||||
|
- Make a new source package (3)
|
||||||
|
- Send source package to package maintainer
|
||||||
|
- Make OS X .pkg package
|
||||||
|
- Wait for all packages
|
||||||
|
- Upload all packages to website
|
||||||
|
- Upload all docs to website
|
||||||
|
- Send announcement to group
|
||||||
|
|
|
@ -20,7 +20,6 @@
|
||||||
<a href="shell.html">Shell</a>
|
<a href="shell.html">Shell</a>
|
||||||
<a href="config.html">Configuration</a>
|
<a href="config.html">Configuration</a>
|
||||||
<a href="color.html">Colors</a>
|
<a href="color.html">Colors</a>
|
||||||
<a href="usage.html">Usage</a>
|
|
||||||
<a href="recur.html">Recurrence</a>
|
<a href="recur.html">Recurrence</a>
|
||||||
<a href="date.html">Date Handling</a>
|
<a href="date.html">Date Handling</a>
|
||||||
<a href="faq.html">FAQ</a>
|
<a href="faq.html">FAQ</a>
|
||||||
|
|
|
@ -20,7 +20,6 @@
|
||||||
<a href="shell.html">Shell</a>
|
<a href="shell.html">Shell</a>
|
||||||
<a href="config.html">Configuration</a>
|
<a href="config.html">Configuration</a>
|
||||||
<a href="color.html">Colors</a>
|
<a href="color.html">Colors</a>
|
||||||
<a href="usage.html">Usage</a>
|
|
||||||
<a href="recur.html">Recurrence</a>
|
<a href="recur.html">Recurrence</a>
|
||||||
<a href="date.html">Date Handling</a>
|
<a href="date.html">Date Handling</a>
|
||||||
<a href="faq.html">FAQ</a>
|
<a href="faq.html">FAQ</a>
|
||||||
|
|
|
@ -20,7 +20,6 @@
|
||||||
<a href="shell.html">Shell</a>
|
<a href="shell.html">Shell</a>
|
||||||
<a href="config.html">Configuration</a>
|
<a href="config.html">Configuration</a>
|
||||||
<a href="color.html">Colors</a>
|
<a href="color.html">Colors</a>
|
||||||
<a href="usage.html">Usage</a>
|
|
||||||
<a href="recur.html">Recurrence</a>
|
<a href="recur.html">Recurrence</a>
|
||||||
<a href="date.html">Date Handling</a>
|
<a href="date.html">Date Handling</a>
|
||||||
<a href="faq.html">FAQ</a>
|
<a href="faq.html">FAQ</a>
|
||||||
|
|
|
@ -20,7 +20,6 @@
|
||||||
<a href="shell.html">Shell</a>
|
<a href="shell.html">Shell</a>
|
||||||
<a href="config.html">Configuration</a>
|
<a href="config.html">Configuration</a>
|
||||||
<a href="color.html">Colors</a>
|
<a href="color.html">Colors</a>
|
||||||
<a href="usage.html">Usage</a>
|
|
||||||
<a href="recur.html">Recurrence</a>
|
<a href="recur.html">Recurrence</a>
|
||||||
<a href="date.html">Date Handling</a>
|
<a href="date.html">Date Handling</a>
|
||||||
<a href="faq.html">FAQ</a>
|
<a href="faq.html">FAQ</a>
|
||||||
|
|
|
@ -20,7 +20,6 @@
|
||||||
<a href="shell.html">Shell</a>
|
<a href="shell.html">Shell</a>
|
||||||
<a href="config.html">Configuration</a>
|
<a href="config.html">Configuration</a>
|
||||||
<a href="color.html">Colors</a>
|
<a href="color.html">Colors</a>
|
||||||
<a href="usage.html">Usage</a>
|
|
||||||
<a href="recur.html">Recurrence</a>
|
<a href="recur.html">Recurrence</a>
|
||||||
<a href="date.html">Date Handling</a>
|
<a href="date.html">Date Handling</a>
|
||||||
<a href="faq.html">FAQ</a>
|
<a href="faq.html">FAQ</a>
|
||||||
|
|
|
@ -20,7 +20,6 @@
|
||||||
<a href="shell.html">Shell</a>
|
<a href="shell.html">Shell</a>
|
||||||
<a href="config.html">Configuration</a>
|
<a href="config.html">Configuration</a>
|
||||||
<a href="color.html">Colors</a>
|
<a href="color.html">Colors</a>
|
||||||
<a href="usage.html">Usage</a>
|
|
||||||
<a href="recur.html">Recurrence</a>
|
<a href="recur.html">Recurrence</a>
|
||||||
<a href="date.html">Date Handling</a>
|
<a href="date.html">Date Handling</a>
|
||||||
<a href="faq.html">FAQ</a>
|
<a href="faq.html">FAQ</a>
|
||||||
|
|
|
@ -20,7 +20,6 @@
|
||||||
<a href="shell.html">Shell</a>
|
<a href="shell.html">Shell</a>
|
||||||
<a href="config.html">Configuration</a>
|
<a href="config.html">Configuration</a>
|
||||||
<a href="color.html">Colors</a>
|
<a href="color.html">Colors</a>
|
||||||
<a href="usage.html">Usage</a>
|
|
||||||
<a href="recur.html">Recurrence</a>
|
<a href="recur.html">Recurrence</a>
|
||||||
<a href="date.html">Date Handling</a>
|
<a href="date.html">Date Handling</a>
|
||||||
<a href="faq.html">FAQ</a>
|
<a href="faq.html">FAQ</a>
|
||||||
|
|
|
@ -20,7 +20,6 @@
|
||||||
<a href="shell.html">Shell</a>
|
<a href="shell.html">Shell</a>
|
||||||
<a href="config.html">Configuration</a>
|
<a href="config.html">Configuration</a>
|
||||||
<a href="color.html">Colors</a>
|
<a href="color.html">Colors</a>
|
||||||
<a href="usage.html">Usage</a>
|
|
||||||
<a href="recur.html">Recurrence</a>
|
<a href="recur.html">Recurrence</a>
|
||||||
<a href="date.html">Date Handling</a>
|
<a href="date.html">Date Handling</a>
|
||||||
<a href="faq.html">FAQ</a>
|
<a href="faq.html">FAQ</a>
|
||||||
|
|
|
@ -20,7 +20,6 @@
|
||||||
<a href="shell.html">Shell</a>
|
<a href="shell.html">Shell</a>
|
||||||
<a href="config.html">Configuration</a>
|
<a href="config.html">Configuration</a>
|
||||||
<a href="color.html">Colors</a>
|
<a href="color.html">Colors</a>
|
||||||
<a href="usage.html">Usage</a>
|
|
||||||
<a href="recur.html">Recurrence</a>
|
<a href="recur.html">Recurrence</a>
|
||||||
<a href="date.html">Date Handling</a>
|
<a href="date.html">Date Handling</a>
|
||||||
<a href="faq.html">FAQ</a>
|
<a href="faq.html">FAQ</a>
|
||||||
|
|
|
@ -20,7 +20,6 @@
|
||||||
<a href="shell.html">Shell</a>
|
<a href="shell.html">Shell</a>
|
||||||
<a href="config.html">Configuration</a>
|
<a href="config.html">Configuration</a>
|
||||||
<a href="color.html">Colors</a>
|
<a href="color.html">Colors</a>
|
||||||
<a href="usage.html">Usage</a>
|
|
||||||
<a href="recur.html">Recurrence</a>
|
<a href="recur.html">Recurrence</a>
|
||||||
<a href="date.html">Date Handling</a>
|
<a href="date.html">Date Handling</a>
|
||||||
<a href="faq.html">FAQ</a>
|
<a href="faq.html">FAQ</a>
|
||||||
|
|
|
@ -20,7 +20,6 @@
|
||||||
<a href="shell.html">Shell</a>
|
<a href="shell.html">Shell</a>
|
||||||
<a href="config.html">Configuration</a>
|
<a href="config.html">Configuration</a>
|
||||||
<a href="color.html">Colors</a>
|
<a href="color.html">Colors</a>
|
||||||
<a href="usage.html">Usage</a>
|
|
||||||
<a href="recur.html">Recurrence</a>
|
<a href="recur.html">Recurrence</a>
|
||||||
<a href="date.html">Date Handling</a>
|
<a href="date.html">Date Handling</a>
|
||||||
<a href="faq.html">FAQ</a>
|
<a href="faq.html">FAQ</a>
|
||||||
|
@ -52,7 +51,7 @@
|
||||||
generated on a regular basis. Consider the example:
|
generated on a regular basis. Consider the example:
|
||||||
</p>
|
</p>
|
||||||
|
|
||||||
<pre><code>% task Pay rent due:7/1/2008 recur:monthly</code></pre>
|
<pre><code>% task add Pay rent due:7/1/2008 recur:monthly</code></pre>
|
||||||
|
|
||||||
<p>
|
<p>
|
||||||
If today's date is 7/10, for example, then that due date is in the past, and
|
If today's date is 7/10, for example, then that due date is in the past, and
|
||||||
|
@ -81,7 +80,7 @@ ID Project Pri Due Active Age Description
|
||||||
Thursdays instead:
|
Thursdays instead:
|
||||||
</p>
|
</p>
|
||||||
|
|
||||||
<pre><code>% task TPS report due:thursday recur:weekly until:8/31/2008</code></pre>
|
<pre><code>% task add TPS report due:thursday recur:weekly until:8/31/2008</code></pre>
|
||||||
|
|
||||||
<p>
|
<p>
|
||||||
This create a weekly recurring task that expires on 8/31/2008. What this means
|
This create a weekly recurring task that expires on 8/31/2008. What this means
|
||||||
|
|
154
html/sequence.html
Normal file
154
html/sequence.html
Normal file
|
@ -0,0 +1,154 @@
|
||||||
|
<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Strict//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-strict.dtd">
|
||||||
|
<html xmlns="http://www.w3.org/1999/xhtml">
|
||||||
|
<head>
|
||||||
|
<title>Task Usage</title>
|
||||||
|
<meta http-equiv="content-type" content="text/html; charset=utf-8" />
|
||||||
|
<link rel="stylesheet" href="task.css" type="text/css" />
|
||||||
|
</head>
|
||||||
|
|
||||||
|
<body>
|
||||||
|
<div id="container">
|
||||||
|
<table>
|
||||||
|
<tr>
|
||||||
|
<td>
|
||||||
|
<div id="toolbar">
|
||||||
|
<a href="task.html">Home</a>
|
||||||
|
<a href="setup.html">Setup</a>
|
||||||
|
<a href="30second.html">30-second Tutorial</a>
|
||||||
|
<a href="simple.html">Simple</a>
|
||||||
|
<a href="advanced.html">Advanced</a>
|
||||||
|
<a href="shell.html">Shell</a>
|
||||||
|
<a href="config.html">Configuration</a>
|
||||||
|
<a href="color.html">Colors</a>
|
||||||
|
<a href="recur.html">Recurrence</a>
|
||||||
|
<a href="date.html">Date Handling</a>
|
||||||
|
<a href="faq.html">FAQ</a>
|
||||||
|
<a href="versions.html">Old Versions</a>
|
||||||
|
<a href="links.html">Task on the Web</a>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div id="content">
|
||||||
|
<br />
|
||||||
|
<br />
|
||||||
|
<br />
|
||||||
|
<h1 class="title">ID Sequences</h1>
|
||||||
|
<div class="content">
|
||||||
|
<p>
|
||||||
|
Some task commands require an ID to be specified. For example:
|
||||||
|
</p>
|
||||||
|
|
||||||
|
<pre><code>% task 3 done</code></pre>
|
||||||
|
|
||||||
|
<p>
|
||||||
|
This marks a single task as done. But if you wanted to mark
|
||||||
|
several tasks as done, you could use:
|
||||||
|
</p>
|
||||||
|
|
||||||
|
<pre><code>% task 3,4,5 done</code></pre>
|
||||||
|
|
||||||
|
<p>
|
||||||
|
Which would mark tasks 3, 4 and 5 as all done. In this example,
|
||||||
|
the three IDs are consecutive, which means you could also have
|
||||||
|
entered:
|
||||||
|
</p>
|
||||||
|
|
||||||
|
<pre><code>% task 3-5 done</code></pre>
|
||||||
|
|
||||||
|
<p>
|
||||||
|
Or in a more complex example:
|
||||||
|
</p>
|
||||||
|
|
||||||
|
<pre><code>% task 1,3-5,12 23-25 done</code></pre>
|
||||||
|
|
||||||
|
<p>
|
||||||
|
This would mark tasks 1, 3, 4, 5, 12, 23, 24 and 25 as done.
|
||||||
|
Note that this example uses two sequences, separated by a space.
|
||||||
|
</p>
|
||||||
|
|
||||||
|
<p>
|
||||||
|
You must be careful though. Task tries very carefully to do
|
||||||
|
the right thing when it interprets the command line, but must
|
||||||
|
still impose some rules so that it can unambiguously read the
|
||||||
|
command. If you use one or more sequences, then they must
|
||||||
|
appear on the command line adjacent to each other. If they
|
||||||
|
are separated by something else, then task assumes the second
|
||||||
|
and subsequent set is not a sequence. Here is an example
|
||||||
|
of this:
|
||||||
|
</p>
|
||||||
|
|
||||||
|
<pre><code>% task 3 Order part number 4-123</code></pre>
|
||||||
|
|
||||||
|
<p>
|
||||||
|
Clearly the 4-123 is a part number, and not a sequence.
|
||||||
|
Task is being asked to modify the description of task 3 to be
|
||||||
|
"Order part number 4-123". Note that the ID is separated
|
||||||
|
from the part number by something other than a sequence.
|
||||||
|
Here is a bad example that task will misinterpret:
|
||||||
|
</p>
|
||||||
|
|
||||||
|
<pre><code>% task 3 4-123 is back-ordered, try again next week</code></pre>
|
||||||
|
|
||||||
|
<p>
|
||||||
|
The intent here is that task 3 have its description modified to be
|
||||||
|
"40123 is back-ordered, try again next week", but will be
|
||||||
|
misinterpreted as tasks 3, 4, 5, 6 ... 123 will all be modified
|
||||||
|
to have the description "is back-ordered, try again next week".
|
||||||
|
The solution is to quote the whole description:
|
||||||
|
</p>
|
||||||
|
|
||||||
|
<pre><code>% task 3 "4-123 is back-ordered, try again next week"</code></pre>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<br />
|
||||||
|
<br />
|
||||||
|
<div class="content">
|
||||||
|
<p>
|
||||||
|
Copyright 2006-2009, P. Beckingham. All rights reserved.
|
||||||
|
</p>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
</div>
|
||||||
|
</td>
|
||||||
|
|
||||||
|
<td align="right" valign="top" width="200px">
|
||||||
|
<br />
|
||||||
|
<br />
|
||||||
|
<br />
|
||||||
|
<br />
|
||||||
|
<br />
|
||||||
|
<br />
|
||||||
|
<br />
|
||||||
|
<br />
|
||||||
|
<br />
|
||||||
|
<br />
|
||||||
|
|
||||||
|
<script type="text/javascript"><!--
|
||||||
|
google_ad_client = "pub-9709799404235424";
|
||||||
|
/* Task Main */
|
||||||
|
google_ad_slot = "8660617875";
|
||||||
|
google_ad_width = 120;
|
||||||
|
google_ad_height = 600;
|
||||||
|
//-->
|
||||||
|
</script>
|
||||||
|
<script type="text/javascript"
|
||||||
|
src="http://pagead2.googlesyndication.com/pagead/show_ads.js">
|
||||||
|
</script>
|
||||||
|
</td>
|
||||||
|
</tr>
|
||||||
|
</table>
|
||||||
|
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<script type="text/javascript">
|
||||||
|
var gaJsHost = (("https:" == document.location.protocol) ? "https://ssl." : "http://www.");
|
||||||
|
document.write(unescape("%3Cscript src='" + gaJsHost + "google-analytics.com/ga.js' type='text/javascript'%3E%3C/script%3E"));
|
||||||
|
</script>
|
||||||
|
<script type="text/javascript">
|
||||||
|
var pageTracker = _gat._getTracker("UA-4737637-1");
|
||||||
|
pageTracker._initData();
|
||||||
|
pageTracker._trackPageview();
|
||||||
|
</script>
|
||||||
|
|
||||||
|
</body>
|
||||||
|
</html>
|
||||||
|
|
|
@ -20,7 +20,6 @@
|
||||||
<a href="shell.html">Shell</a>
|
<a href="shell.html">Shell</a>
|
||||||
<a href="config.html">Configuration</a>
|
<a href="config.html">Configuration</a>
|
||||||
<a href="color.html">Colors</a>
|
<a href="color.html">Colors</a>
|
||||||
<a href="usage.html">Usage</a>
|
|
||||||
<a href="recur.html">Recurrence</a>
|
<a href="recur.html">Recurrence</a>
|
||||||
<a href="date.html">Date Handling</a>
|
<a href="date.html">Date Handling</a>
|
||||||
<a href="faq.html">FAQ</a>
|
<a href="faq.html">FAQ</a>
|
||||||
|
|
|
@ -20,7 +20,6 @@
|
||||||
<a href="shell.html">Shell</a>
|
<a href="shell.html">Shell</a>
|
||||||
<a href="config.html">Configuration</a>
|
<a href="config.html">Configuration</a>
|
||||||
<a href="color.html">Colors</a>
|
<a href="color.html">Colors</a>
|
||||||
<a href="usage.html">Usage</a>
|
|
||||||
<a href="recur.html">Recurrence</a>
|
<a href="recur.html">Recurrence</a>
|
||||||
<a href="date.html">Date Handling</a>
|
<a href="date.html">Date Handling</a>
|
||||||
<a href="faq.html">FAQ</a>
|
<a href="faq.html">FAQ</a>
|
||||||
|
|
|
@ -20,7 +20,6 @@
|
||||||
<a href="shell.html">Shell</a>
|
<a href="shell.html">Shell</a>
|
||||||
<a href="config.html">Configuration</a>
|
<a href="config.html">Configuration</a>
|
||||||
<a href="color.html">Colors</a>
|
<a href="color.html">Colors</a>
|
||||||
<a href="usage.html">Usage</a>
|
|
||||||
<a href="recur.html">Recurrence</a>
|
<a href="recur.html">Recurrence</a>
|
||||||
<a href="date.html">Date Handling</a>
|
<a href="date.html">Date Handling</a>
|
||||||
<a href="faq.html">FAQ</a>
|
<a href="faq.html">FAQ</a>
|
||||||
|
|
|
@ -20,7 +20,6 @@
|
||||||
<a href="shell.html">Shell</a>
|
<a href="shell.html">Shell</a>
|
||||||
<a href="config.html">Configuration</a>
|
<a href="config.html">Configuration</a>
|
||||||
<a href="color.html">Colors</a>
|
<a href="color.html">Colors</a>
|
||||||
<a href="usage.html">Usage</a>
|
|
||||||
<a href="recur.html">Recurrence</a>
|
<a href="recur.html">Recurrence</a>
|
||||||
<a href="date.html">Date Handling</a>
|
<a href="date.html">Date Handling</a>
|
||||||
<a href="faq.html">FAQ</a>
|
<a href="faq.html">FAQ</a>
|
||||||
|
|
|
@ -20,7 +20,6 @@
|
||||||
<a href="shell.html">Shell</a>
|
<a href="shell.html">Shell</a>
|
||||||
<a href="config.html">Configuration</a>
|
<a href="config.html">Configuration</a>
|
||||||
<a href="color.html">Colors</a>
|
<a href="color.html">Colors</a>
|
||||||
<a href="usage.html">Usage</a>
|
|
||||||
<a href="recur.html">Recurrence</a>
|
<a href="recur.html">Recurrence</a>
|
||||||
<a href="date.html">Date Handling</a>
|
<a href="date.html">Date Handling</a>
|
||||||
<a href="faq.html">FAQ</a>
|
<a href="faq.html">FAQ</a>
|
||||||
|
@ -50,7 +49,6 @@
|
||||||
<li><a href="shell.html">Interacting with the Shell</a>
|
<li><a href="shell.html">Interacting with the Shell</a>
|
||||||
<li><a href="config.html">Configuring Task</a>
|
<li><a href="config.html">Configuring Task</a>
|
||||||
<li><a href="color.html">Color</a>
|
<li><a href="color.html">Color</a>
|
||||||
<li><a href="usage.html">Task Command Usage</a>
|
|
||||||
<li><a href="recur.html">Recurring Tasks</a>
|
<li><a href="recur.html">Recurring Tasks</a>
|
||||||
<li><a href="date.html">Date Handling</a>
|
<li><a href="date.html">Date Handling</a>
|
||||||
<li><a href="versions.html">Old Versions</a>
|
<li><a href="versions.html">Old Versions</a>
|
||||||
|
@ -59,6 +57,7 @@
|
||||||
<li><a href="custom.html">Custom Reports</a>
|
<li><a href="custom.html">Custom Reports</a>
|
||||||
<li><a href="import.html">Data Import</a>
|
<li><a href="import.html">Data Import</a>
|
||||||
<li><a href="faq.html">Frequently Asked Questions</a>
|
<li><a href="faq.html">Frequently Asked Questions</a>
|
||||||
|
<li><a href="sequence.html">ID Sequences</a>
|
||||||
</ul>
|
</ul>
|
||||||
|
|
||||||
<p>
|
<p>
|
||||||
|
@ -71,6 +70,12 @@
|
||||||
which illustrates many of task's features.
|
which illustrates many of task's features.
|
||||||
</p>
|
</p>
|
||||||
|
|
||||||
|
<p>
|
||||||
|
For the latest news, discussion of proposed task features, and
|
||||||
|
somewhere to voice your opinions, join us at
|
||||||
|
<a href="http://groups.google.com/group/taskprogram">http://groups.google.com/group/taskprogram</a>.
|
||||||
|
</p>
|
||||||
|
|
||||||
<br />
|
<br />
|
||||||
<h2 class="title">Get the Latest Stable Release</h2>
|
<h2 class="title">Get the Latest Stable Release</h2>
|
||||||
|
|
||||||
|
@ -110,6 +115,14 @@
|
||||||
<ul>
|
<ul>
|
||||||
<li>Improved the errors when parsing a corrupt or unrecognized pending.data
|
<li>Improved the errors when parsing a corrupt or unrecognized pending.data
|
||||||
or completed.data file (thanks to T. Charles Yun).
|
or completed.data file (thanks to T. Charles Yun).
|
||||||
|
<li>Added details to the "info" report about recurring tasks (thanks to T.
|
||||||
|
Charles Yun).
|
||||||
|
<li>Now writes a sample "defaultwidth" configuration variable to the default
|
||||||
|
.taskrc file (thanks to T. Charles Yun).
|
||||||
|
<li>Task allows commands that require an ID to now be given a sequence, which
|
||||||
|
is a set of IDs. This allows commands like "task delete 1 2 5-10,12".
|
||||||
|
<li>Fixed bug in the ghistory report, which caused it to only show a new
|
||||||
|
month if a task was added during that month.
|
||||||
</ul>
|
</ul>
|
||||||
|
|
||||||
<p>
|
<p>
|
||||||
|
|
153
html/usage.html
153
html/usage.html
|
@ -1,153 +0,0 @@
|
||||||
<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Strict//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-strict.dtd">
|
|
||||||
<html xmlns="http://www.w3.org/1999/xhtml">
|
|
||||||
<head>
|
|
||||||
<title>Task Usage</title>
|
|
||||||
<meta http-equiv="content-type" content="text/html; charset=utf-8" />
|
|
||||||
<link rel="stylesheet" href="task.css" type="text/css" />
|
|
||||||
</head>
|
|
||||||
|
|
||||||
<body>
|
|
||||||
<div id="container">
|
|
||||||
<table>
|
|
||||||
<tr>
|
|
||||||
<td>
|
|
||||||
<div id="toolbar">
|
|
||||||
<a href="task.html">Home</a>
|
|
||||||
<a href="setup.html">Setup</a>
|
|
||||||
<a href="30second.html">30-second Tutorial</a>
|
|
||||||
<a href="simple.html">Simple</a>
|
|
||||||
<a href="advanced.html">Advanced</a>
|
|
||||||
<a href="shell.html">Shell</a>
|
|
||||||
<a href="config.html">Configuration</a>
|
|
||||||
<a href="color.html">Colors</a>
|
|
||||||
<a href="usage.html">Usage</a>
|
|
||||||
<a href="recur.html">Recurrence</a>
|
|
||||||
<a href="date.html">Date Handling</a>
|
|
||||||
<a href="faq.html">FAQ</a>
|
|
||||||
<a href="versions.html">Old Versions</a>
|
|
||||||
<a href="links.html">Task on the Web</a>
|
|
||||||
</div>
|
|
||||||
|
|
||||||
<div id="content">
|
|
||||||
<br />
|
|
||||||
<br />
|
|
||||||
<br />
|
|
||||||
<h2 class="title"><a name="usage">Command Usage<a></h2>
|
|
||||||
<div class="content">
|
|
||||||
<pre><code>Usage: task
|
|
||||||
task add [tags] [attrs] desc...
|
|
||||||
task append [tags] [attrs] desc...
|
|
||||||
task annotate ID desc...
|
|
||||||
task completed [tags] [attrs] desc...
|
|
||||||
task ID [tags] [attrs] [desc...]
|
|
||||||
task ID /from/to/
|
|
||||||
task delete ID
|
|
||||||
task undelete ID
|
|
||||||
task info ID
|
|
||||||
task start ID
|
|
||||||
task stop ID
|
|
||||||
task done ID
|
|
||||||
task undo ID
|
|
||||||
task projects
|
|
||||||
task tags
|
|
||||||
task summary
|
|
||||||
task history
|
|
||||||
task ghistory
|
|
||||||
task next
|
|
||||||
task calendar
|
|
||||||
task active
|
|
||||||
task overdue
|
|
||||||
task stats
|
|
||||||
task export
|
|
||||||
task color
|
|
||||||
task version
|
|
||||||
task help
|
|
||||||
task list [tags] [attrs] desc...
|
|
||||||
task long [tags] [attrs] desc...
|
|
||||||
task ls [tags] [attrs] desc...
|
|
||||||
task newest [tags] [attrs] desc...
|
|
||||||
task oldest [tags] [attrs] desc...
|
|
||||||
|
|
||||||
See http://www.beckingham.net/task.html for the latest releases and a full tutorial.
|
|
||||||
|
|
||||||
ID is the numeric identifier displayed by the 'task list' command
|
|
||||||
|
|
||||||
Tags are arbitrary words, any quantity:
|
|
||||||
+tag The + means add the tag
|
|
||||||
-tag The - means remove the tag
|
|
||||||
|
|
||||||
Attributes are:
|
|
||||||
project: Project name
|
|
||||||
priority: Priority
|
|
||||||
due: Due date
|
|
||||||
recur: Recurrence frequency
|
|
||||||
until: Recurrence end date
|
|
||||||
fg: Foreground color
|
|
||||||
bg: Background color
|
|
||||||
rc: Alternate .taskrc file
|
|
||||||
|
|
||||||
Any command or attribute name may be abbreviated if still unique:
|
|
||||||
task list project:Home
|
|
||||||
task li pro:Home
|
|
||||||
|
|
||||||
Some task descriptions need to be escaped because of the shell:
|
|
||||||
task add "quoted ' quote"
|
|
||||||
task add escaped \' quote
|
|
||||||
|
|
||||||
Many characters have special meaning to the shell, including:
|
|
||||||
$ ! ' " ( ) ; \ ` * ? { } [ ] < > | & % # ~</code></pre>
|
|
||||||
<div>
|
|
||||||
|
|
||||||
<br />
|
|
||||||
<br />
|
|
||||||
<div class="content">
|
|
||||||
<p>
|
|
||||||
Copyright 2006-2009, P. Beckingham. All rights reserved.
|
|
||||||
</p>
|
|
||||||
</div>
|
|
||||||
|
|
||||||
</div>
|
|
||||||
</td>
|
|
||||||
|
|
||||||
<td align="right" valign="top" width="200px">
|
|
||||||
<br />
|
|
||||||
<br />
|
|
||||||
<br />
|
|
||||||
<br />
|
|
||||||
<br />
|
|
||||||
<br />
|
|
||||||
<br />
|
|
||||||
<br />
|
|
||||||
<br />
|
|
||||||
<br />
|
|
||||||
|
|
||||||
<script type="text/javascript"><!--
|
|
||||||
google_ad_client = "pub-9709799404235424";
|
|
||||||
/* Task Main */
|
|
||||||
google_ad_slot = "8660617875";
|
|
||||||
google_ad_width = 120;
|
|
||||||
google_ad_height = 600;
|
|
||||||
//-->
|
|
||||||
</script>
|
|
||||||
<script type="text/javascript"
|
|
||||||
src="http://pagead2.googlesyndication.com/pagead/show_ads.js">
|
|
||||||
</script>
|
|
||||||
</td>
|
|
||||||
</tr>
|
|
||||||
</table>
|
|
||||||
|
|
||||||
</div>
|
|
||||||
|
|
||||||
<script type="text/javascript">
|
|
||||||
var gaJsHost = (("https:" == document.location.protocol) ? "https://ssl." : "http://www.");
|
|
||||||
document.write(unescape("%3Cscript src='" + gaJsHost + "google-analytics.com/ga.js' type='text/javascript'%3E%3C/script%3E"));
|
|
||||||
</script>
|
|
||||||
<script type="text/javascript">
|
|
||||||
var pageTracker = _gat._getTracker("UA-4737637-1");
|
|
||||||
pageTracker._initData();
|
|
||||||
pageTracker._trackPageview();
|
|
||||||
</script>
|
|
||||||
|
|
||||||
</body>
|
|
||||||
</html>
|
|
||||||
|
|
|
@ -20,7 +20,6 @@
|
||||||
<a href="shell.html">Shell</a>
|
<a href="shell.html">Shell</a>
|
||||||
<a href="config.html">Configuration</a>
|
<a href="config.html">Configuration</a>
|
||||||
<a href="color.html">Colors</a>
|
<a href="color.html">Colors</a>
|
||||||
<a href="usage.html">Usage</a>
|
|
||||||
<a href="recur.html">Recurrence</a>
|
<a href="recur.html">Recurrence</a>
|
||||||
<a href="date.html">Date Handling</a>
|
<a href="date.html">Date Handling</a>
|
||||||
<a href="faq.html">FAQ</a>
|
<a href="faq.html">FAQ</a>
|
||||||
|
|
|
@ -149,6 +149,7 @@ void Config::createDefault (const std::string& home)
|
||||||
fprintf (out, "next=2\n");
|
fprintf (out, "next=2\n");
|
||||||
fprintf (out, "dateformat=m/d/Y\n");
|
fprintf (out, "dateformat=m/d/Y\n");
|
||||||
fprintf (out, "#monthsperline=2\n");
|
fprintf (out, "#monthsperline=2\n");
|
||||||
|
fprintf (out, "#defaultwidth=80\n");
|
||||||
fprintf (out, "curses=on\n");
|
fprintf (out, "curses=on\n");
|
||||||
fprintf (out, "color=on\n");
|
fprintf (out, "color=on\n");
|
||||||
fprintf (out, "due=7\n");
|
fprintf (out, "due=7\n");
|
||||||
|
|
15
src/T.cpp
15
src/T.cpp
|
@ -37,6 +37,7 @@ T::T ()
|
||||||
mUUID = uuid ();
|
mUUID = uuid ();
|
||||||
mStatus = pending;
|
mStatus = pending;
|
||||||
mId = 0;
|
mId = 0;
|
||||||
|
mSequence.clear ();
|
||||||
mTags.clear ();
|
mTags.clear ();
|
||||||
mAttributes.clear ();
|
mAttributes.clear ();
|
||||||
mDescription = "";
|
mDescription = "";
|
||||||
|
@ -59,6 +60,7 @@ T::T (const T& other)
|
||||||
mStatus = other.mStatus;
|
mStatus = other.mStatus;
|
||||||
mUUID = other.mUUID;
|
mUUID = other.mUUID;
|
||||||
mId = other.mId;
|
mId = other.mId;
|
||||||
|
mSequence = other.mSequence;
|
||||||
mDescription = other.mDescription;
|
mDescription = other.mDescription;
|
||||||
mTags = other.mTags;
|
mTags = other.mTags;
|
||||||
mRemoveTags = other.mRemoveTags;
|
mRemoveTags = other.mRemoveTags;
|
||||||
|
@ -74,6 +76,7 @@ T& T::operator= (const T& other)
|
||||||
mStatus = other.mStatus;
|
mStatus = other.mStatus;
|
||||||
mUUID = other.mUUID;
|
mUUID = other.mUUID;
|
||||||
mId = other.mId;
|
mId = other.mId;
|
||||||
|
mSequence = other.mSequence;
|
||||||
mDescription = other.mDescription;
|
mDescription = other.mDescription;
|
||||||
mTags = other.mTags;
|
mTags = other.mTags;
|
||||||
mRemoveTags = other.mRemoveTags;
|
mRemoveTags = other.mRemoveTags;
|
||||||
|
@ -286,6 +289,16 @@ void T::addAnnotation (const std::string& description)
|
||||||
mAnnotations[time (NULL)] = sanitized;
|
mAnnotations[time (NULL)] = sanitized;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
////////////////////////////////////////////////////////////////////////////////
|
||||||
|
bool T::sequenceContains (int id) const
|
||||||
|
{
|
||||||
|
foreach (seq, mSequence)
|
||||||
|
if (*seq == id)
|
||||||
|
return true;
|
||||||
|
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
////////////////////////////////////////////////////////////////////////////////
|
////////////////////////////////////////////////////////////////////////////////
|
||||||
// uuid status [tags] [attributes] [annotations] description
|
// uuid status [tags] [attributes] [annotations] description
|
||||||
//
|
//
|
||||||
|
@ -575,8 +588,6 @@ void T::parse (const std::string& line)
|
||||||
openAttrBracket + 1, closeAttrBracket - openAttrBracket - 1);
|
openAttrBracket + 1, closeAttrBracket - openAttrBracket - 1);
|
||||||
std::vector <std::string> pairs;
|
std::vector <std::string> pairs;
|
||||||
split (pairs, attributes, ' ');
|
split (pairs, attributes, ' ');
|
||||||
if (pairs.size () == 0)
|
|
||||||
throw std::string ("Could not find any attributes.");
|
|
||||||
|
|
||||||
for (size_t i = 0; i < pairs.size (); ++i)
|
for (size_t i = 0; i < pairs.size (); ++i)
|
||||||
{
|
{
|
||||||
|
|
6
src/T.h
6
src/T.h
|
@ -49,7 +49,9 @@ public:
|
||||||
void setUUID (const std::string& uuid) { mUUID = uuid; }
|
void setUUID (const std::string& uuid) { mUUID = uuid; }
|
||||||
|
|
||||||
int getId () const { return mId; }
|
int getId () const { return mId; }
|
||||||
void setId (int id) { mId = id; }
|
void setId (int id) { mId = id; mSequence.push_back (id); }
|
||||||
|
std::vector <int> getAllIds () const { return mSequence; }
|
||||||
|
void addId (int id) { if (mId == 0) mId = id; mSequence.push_back (id); }
|
||||||
|
|
||||||
status getStatus () const { return mStatus; }
|
status getStatus () const { return mStatus; }
|
||||||
void setStatus (status s) { mStatus = s; }
|
void setStatus (status s) { mStatus = s; }
|
||||||
|
@ -82,6 +84,7 @@ public:
|
||||||
void getAnnotations (std::map <time_t, std::string>&) const;
|
void getAnnotations (std::map <time_t, std::string>&) const;
|
||||||
void setAnnotations (const std::map <time_t, std::string>&);
|
void setAnnotations (const std::map <time_t, std::string>&);
|
||||||
void addAnnotation (const std::string&);
|
void addAnnotation (const std::string&);
|
||||||
|
bool sequenceContains (int) const;
|
||||||
|
|
||||||
const std::string compose () const;
|
const std::string compose () const;
|
||||||
const std::string composeCSV ();
|
const std::string composeCSV ();
|
||||||
|
@ -95,6 +98,7 @@ private:
|
||||||
status mStatus;
|
status mStatus;
|
||||||
std::string mUUID;
|
std::string mUUID;
|
||||||
int mId;
|
int mId;
|
||||||
|
std::vector <int> mSequence;
|
||||||
std::string mDescription;
|
std::string mDescription;
|
||||||
std::vector<std::string> mTags;
|
std::vector<std::string> mTags;
|
||||||
std::vector<std::string> mRemoveTags;
|
std::vector<std::string> mRemoveTags;
|
||||||
|
|
601
src/command.cpp
601
src/command.cpp
|
@ -193,42 +193,34 @@ std::string handleTags (TDB& tdb, T& task, Config& conf)
|
||||||
std::string handleUndelete (TDB& tdb, T& task, Config& conf)
|
std::string handleUndelete (TDB& tdb, T& task, Config& conf)
|
||||||
{
|
{
|
||||||
std::stringstream out;
|
std::stringstream out;
|
||||||
|
|
||||||
std::vector <T> all;
|
std::vector <T> all;
|
||||||
tdb.allPendingT (all);
|
tdb.allPendingT (all);
|
||||||
|
filterSequence (all, task);
|
||||||
|
|
||||||
int id = task.getId ();
|
foreach (t, all)
|
||||||
std::vector <T>::iterator it;
|
|
||||||
for (it = all.begin (); it != all.end (); ++it)
|
|
||||||
{
|
{
|
||||||
if (it->getId () == id)
|
if (t->getStatus () == T::deleted)
|
||||||
{
|
{
|
||||||
if (it->getStatus () == T::deleted)
|
if (t->getAttribute ("recur") != "")
|
||||||
{
|
out << "Task does not support 'undo' for recurring tasks.\n";
|
||||||
if (it->getAttribute ("recur") != "")
|
|
||||||
{
|
|
||||||
out << "Task does not support 'undelete' for recurring tasks." << std::endl;
|
|
||||||
return out.str ();
|
|
||||||
}
|
|
||||||
|
|
||||||
T restored (*it);
|
t->setStatus (T::pending);
|
||||||
restored.setStatus (T::pending);
|
t->removeAttribute ("end");
|
||||||
restored.removeAttribute ("end");
|
tdb.modifyT (*t);
|
||||||
tdb.modifyT (restored);
|
|
||||||
|
|
||||||
out << "Task " << id << " successfully undeleted." << std::endl;
|
out << "Task " << t->getId () << " '" << t->getDescription () << "' successfully undeleted.\n";
|
||||||
return out.str ();
|
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
out << "Task " << id << " is not deleted - therefore cannot undelete." << std::endl;
|
out << "Task " << t->getId () << " '" << t->getDescription () << "' is not deleted - therefore cannot be undeleted.\n";
|
||||||
return out.str ();
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
out << "Task " << id
|
out << "\n"
|
||||||
<< " not found - tasks can only be reliably undeleted if the undelete" << std::endl
|
<< "Please note that tasks can only be reliably undeleted if the undelete "
|
||||||
<< "command is run immediately after the errant delete command." << std::endl;
|
<< "command is run immediately after the errant delete command."
|
||||||
|
<< std::endl;
|
||||||
|
|
||||||
return out.str ();
|
return out.str ();
|
||||||
}
|
}
|
||||||
|
@ -242,37 +234,31 @@ std::string handleUndo (TDB& tdb, T& task, Config& conf)
|
||||||
|
|
||||||
std::vector <T> all;
|
std::vector <T> all;
|
||||||
tdb.allPendingT (all);
|
tdb.allPendingT (all);
|
||||||
|
filterSequence (all, task);
|
||||||
|
|
||||||
int id = task.getId ();
|
foreach (t, all)
|
||||||
std::vector <T>::iterator it;
|
|
||||||
for (it = all.begin (); it != all.end (); ++it)
|
|
||||||
{
|
{
|
||||||
if (it->getId () == id)
|
if (t->getStatus () == T::completed)
|
||||||
{
|
{
|
||||||
if (it->getStatus () == T::completed)
|
if (t->getAttribute ("recur") != "")
|
||||||
{
|
out << "Task does not support 'undo' for recurring tasks.\n";
|
||||||
if (it->getAttribute ("recur") != "")
|
|
||||||
return std::string ("Task does not support 'undo' for recurring tasks.\n");
|
|
||||||
|
|
||||||
T restored (*it);
|
t->setStatus (T::pending);
|
||||||
restored.setStatus (T::pending);
|
t->removeAttribute ("end");
|
||||||
restored.removeAttribute ("end");
|
tdb.modifyT (*t);
|
||||||
tdb.modifyT (restored);
|
|
||||||
|
|
||||||
out << "Task " << id << " successfully undone." << std::endl;
|
out << "Task " << t->getId () << " '" << t->getDescription () << "' successfully undone." << std::endl;
|
||||||
return out.str ();
|
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
out << "Task " << id << " is not done - therefore cannot be undone." << std::endl;
|
out << "Task " << t->getId () << " '" << t->getDescription () << "' is not done - therefore cannot be undone." << std::endl;
|
||||||
return out.str ();
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
out << "Task " << id
|
out << std::endl
|
||||||
<< " not found - tasks can only be reliably undone if the undo" << std::endl
|
<< "Please note that tasks can only be reliably undone if the undo "
|
||||||
<< "command is run immediately after the errant done command." << std::endl;
|
<< "command is run immediately after the errant done command."
|
||||||
|
<< std::endl;
|
||||||
|
|
||||||
return out.str ();
|
return out.str ();
|
||||||
}
|
}
|
||||||
|
@ -367,6 +353,7 @@ std::string handleVersion (Config& conf)
|
||||||
|
|
||||||
// Complain about configuration variables that are not recognized.
|
// Complain about configuration variables that are not recognized.
|
||||||
// These are the regular configuration variables.
|
// These are the regular configuration variables.
|
||||||
|
// Note that there is a leading and trailing space.
|
||||||
std::string recognized =
|
std::string recognized =
|
||||||
" blanklines color color.active color.due color.overdue color.pri.H "
|
" blanklines color color.active color.due color.overdue color.pri.H "
|
||||||
"color.pri.L color.pri.M color.pri.none color.recurring color.tagged "
|
"color.pri.L color.pri.M color.pri.none color.recurring color.tagged "
|
||||||
|
@ -387,7 +374,10 @@ std::string handleVersion (Config& conf)
|
||||||
std::vector <std::string> unrecognized;
|
std::vector <std::string> unrecognized;
|
||||||
foreach (i, all)
|
foreach (i, all)
|
||||||
{
|
{
|
||||||
if (recognized.find (*i) == std::string::npos)
|
// Disallow partial matches by tacking a leading an trailing space on each
|
||||||
|
// variable name.
|
||||||
|
std::string pattern = " " + *i + " ";
|
||||||
|
if (recognized.find (pattern) == std::string::npos)
|
||||||
{
|
{
|
||||||
// These are special configuration variables, because their name is
|
// These are special configuration variables, because their name is
|
||||||
// dynamic.
|
// dynamic.
|
||||||
|
@ -439,13 +429,20 @@ std::string handleDelete (TDB& tdb, T& task, Config& conf)
|
||||||
{
|
{
|
||||||
std::stringstream out;
|
std::stringstream out;
|
||||||
|
|
||||||
if (!conf.get (std::string ("confirmation"), false) || confirm ("Permanently delete task?"))
|
|
||||||
{
|
|
||||||
std::vector <T> all;
|
std::vector <T> all;
|
||||||
tdb.allPendingT (all);
|
tdb.allPendingT (all);
|
||||||
|
filterSequence (all, task);
|
||||||
|
|
||||||
foreach (t, all)
|
foreach (t, all)
|
||||||
{
|
{
|
||||||
if (t->getId () == task.getId ())
|
std::stringstream question;
|
||||||
|
question << "Permanently delete task "
|
||||||
|
<< t->getId ()
|
||||||
|
<< " '"
|
||||||
|
<< t->getDescription ()
|
||||||
|
<< "'?";
|
||||||
|
|
||||||
|
if (!conf.get (std::string ("confirmation"), false) || confirm (question.str ()))
|
||||||
{
|
{
|
||||||
// Check for the more complex case of a recurring task. If this is a
|
// Check for the more complex case of a recurring task. If this is a
|
||||||
// recurring task, get confirmation to delete them all.
|
// recurring task, get confirmation to delete them all.
|
||||||
|
@ -465,8 +462,9 @@ std::string handleDelete (TDB& tdb, T& task, Config& conf)
|
||||||
if (conf.get ("echo.command", true))
|
if (conf.get ("echo.command", true))
|
||||||
out << "Deleting recurring task "
|
out << "Deleting recurring task "
|
||||||
<< sibling->getId ()
|
<< sibling->getId ()
|
||||||
<< " "
|
<< " '"
|
||||||
<< sibling->getDescription ()
|
<< sibling->getDescription ()
|
||||||
|
<< "'"
|
||||||
<< std::endl;
|
<< std::endl;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -479,8 +477,9 @@ std::string handleDelete (TDB& tdb, T& task, Config& conf)
|
||||||
tdb.deleteT (*t);
|
tdb.deleteT (*t);
|
||||||
out << "Deleting recurring task "
|
out << "Deleting recurring task "
|
||||||
<< t->getId ()
|
<< t->getId ()
|
||||||
<< " "
|
<< " '"
|
||||||
<< t->getDescription ()
|
<< t->getDescription ()
|
||||||
|
<< "'"
|
||||||
<< std::endl;
|
<< std::endl;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -490,17 +489,15 @@ std::string handleDelete (TDB& tdb, T& task, Config& conf)
|
||||||
if (conf.get ("echo.command", true))
|
if (conf.get ("echo.command", true))
|
||||||
out << "Deleting task "
|
out << "Deleting task "
|
||||||
<< t->getId ()
|
<< t->getId ()
|
||||||
<< " "
|
<< " '"
|
||||||
<< t->getDescription ()
|
<< t->getDescription ()
|
||||||
|
<< "'"
|
||||||
<< std::endl;
|
<< std::endl;
|
||||||
}
|
}
|
||||||
|
|
||||||
break; // No point continuing the loop.
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
out << "Task not deleted." << std::endl;
|
out << "Task not deleted." << std::endl;
|
||||||
|
}
|
||||||
|
|
||||||
return out.str ();
|
return out.str ();
|
||||||
}
|
}
|
||||||
|
@ -508,112 +505,97 @@ std::string handleDelete (TDB& tdb, T& task, Config& conf)
|
||||||
////////////////////////////////////////////////////////////////////////////////
|
////////////////////////////////////////////////////////////////////////////////
|
||||||
std::string handleStart (TDB& tdb, T& task, Config& conf)
|
std::string handleStart (TDB& tdb, T& task, Config& conf)
|
||||||
{
|
{
|
||||||
std::vector <T> all;
|
|
||||||
tdb.pendingT (all);
|
|
||||||
|
|
||||||
std::vector <T>::iterator it;
|
|
||||||
for (it = all.begin (); it != all.end (); ++it)
|
|
||||||
{
|
|
||||||
if (it->getId () == task.getId ())
|
|
||||||
{
|
|
||||||
T original (*it);
|
|
||||||
std::stringstream out;
|
std::stringstream out;
|
||||||
|
|
||||||
if (original.getAttribute ("start") == "")
|
std::vector <T> all;
|
||||||
|
tdb.pendingT (all);
|
||||||
|
filterSequence (all, task);
|
||||||
|
|
||||||
|
foreach (t, all)
|
||||||
|
{
|
||||||
|
if (t->getAttribute ("start") == "")
|
||||||
{
|
{
|
||||||
char startTime[16];
|
char startTime[16];
|
||||||
sprintf (startTime, "%u", (unsigned int) time (NULL));
|
sprintf (startTime, "%u", (unsigned int) time (NULL));
|
||||||
original.setAttribute ("start", startTime);
|
t->setAttribute ("start", startTime);
|
||||||
|
|
||||||
original.setId (task.getId ());
|
tdb.modifyT (*t);
|
||||||
tdb.modifyT (original);
|
|
||||||
|
|
||||||
if (conf.get ("echo.command", true))
|
if (conf.get ("echo.command", true))
|
||||||
out << "Started "
|
out << "Started "
|
||||||
<< original.getId ()
|
<< t->getId ()
|
||||||
<< " "
|
<< " '"
|
||||||
<< original.getDescription ()
|
<< t->getDescription ()
|
||||||
|
<< "'"
|
||||||
<< std::endl;
|
<< std::endl;
|
||||||
nag (tdb, task, conf);
|
nag (tdb, task, conf);
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
out << "Task " << task.getId () << " already started." << std::endl;
|
out << "Task " << t->getId () << " '" << t->getDescription () << "' already started." << std::endl;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
return out.str ();
|
return out.str ();
|
||||||
}
|
}
|
||||||
}
|
|
||||||
|
|
||||||
throw std::string ("Task not found.");
|
|
||||||
return std::string (""); // To satisfy gcc.
|
|
||||||
}
|
|
||||||
|
|
||||||
////////////////////////////////////////////////////////////////////////////////
|
////////////////////////////////////////////////////////////////////////////////
|
||||||
std::string handleStop (TDB& tdb, T& task, Config& conf)
|
std::string handleStop (TDB& tdb, T& task, Config& conf)
|
||||||
{
|
{
|
||||||
std::vector <T> all;
|
|
||||||
tdb.pendingT (all);
|
|
||||||
|
|
||||||
std::vector <T>::iterator it;
|
|
||||||
for (it = all.begin (); it != all.end (); ++it)
|
|
||||||
{
|
|
||||||
if (it->getId () == task.getId ())
|
|
||||||
{
|
|
||||||
T original (*it);
|
|
||||||
std::stringstream out;
|
std::stringstream out;
|
||||||
|
|
||||||
if (original.getAttribute ("start") != "")
|
std::vector <T> all;
|
||||||
|
tdb.pendingT (all);
|
||||||
|
filterSequence (all, task);
|
||||||
|
|
||||||
|
foreach (t, all)
|
||||||
{
|
{
|
||||||
original.removeAttribute ("start");
|
if (t->getAttribute ("start") != "")
|
||||||
original.setId (task.getId ());
|
{
|
||||||
tdb.modifyT (original);
|
t->removeAttribute ("start");
|
||||||
|
tdb.modifyT (*t);
|
||||||
|
|
||||||
if (conf.get ("echo.command", true))
|
if (conf.get ("echo.command", true))
|
||||||
out << "Stopped " << original.getId () << " " << original.getDescription () << std::endl;
|
out << "Stopped " << t->getId () << " '" << t->getDescription () << "'" << std::endl;
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
out << "Task " << task.getId () << " not started." << std::endl;
|
out << "Task " << t->getId () << " '" << t->getDescription () << "' not started." << std::endl;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
return out.str ();
|
return out.str ();
|
||||||
}
|
}
|
||||||
}
|
|
||||||
|
|
||||||
throw std::string ("Task not found.");
|
|
||||||
return std::string (""); // To satisfy gcc.
|
|
||||||
}
|
|
||||||
|
|
||||||
////////////////////////////////////////////////////////////////////////////////
|
////////////////////////////////////////////////////////////////////////////////
|
||||||
std::string handleDone (TDB& tdb, T& task, Config& conf)
|
std::string handleDone (TDB& tdb, T& task, Config& conf)
|
||||||
{
|
{
|
||||||
std::stringstream out;
|
std::stringstream out;
|
||||||
|
|
||||||
if (!tdb.completeT (task))
|
std::vector <T> all;
|
||||||
|
tdb.allPendingT (all);
|
||||||
|
std::vector <T> filtered = all;
|
||||||
|
filterSequence (filtered, task);
|
||||||
|
|
||||||
|
foreach (t, filtered)
|
||||||
|
{
|
||||||
|
t->setStatus (T::completed);
|
||||||
|
if (!tdb.completeT (*t))
|
||||||
throw std::string ("Could not mark task as completed.");
|
throw std::string ("Could not mark task as completed.");
|
||||||
|
|
||||||
// Now update mask in parent.
|
// Now update mask in parent.
|
||||||
std::vector <T> all;
|
|
||||||
tdb.allPendingT (all);
|
|
||||||
foreach (t, all)
|
|
||||||
{
|
|
||||||
if (t->getId () == task.getId ())
|
|
||||||
{
|
|
||||||
if (conf.get ("echo.command", true))
|
if (conf.get ("echo.command", true))
|
||||||
out << "Completed "
|
out << "Completed "
|
||||||
<< t->getId ()
|
<< t->getId ()
|
||||||
<< " "
|
<< " '"
|
||||||
<< t->getDescription ()
|
<< t->getDescription ()
|
||||||
|
<< "'"
|
||||||
<< std::endl;
|
<< std::endl;
|
||||||
|
|
||||||
t->setStatus (T::completed);
|
|
||||||
updateRecurrenceMask (tdb, all, *t);
|
updateRecurrenceMask (tdb, all, *t);
|
||||||
break;
|
nag (tdb, task, conf);
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
nag (tdb, task, conf);
|
|
||||||
return out.str ();
|
return out.str ();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -677,163 +659,50 @@ std::string handleExport (TDB& tdb, T& task, Config& conf)
|
||||||
////////////////////////////////////////////////////////////////////////////////
|
////////////////////////////////////////////////////////////////////////////////
|
||||||
std::string handleModify (TDB& tdb, T& task, Config& conf)
|
std::string handleModify (TDB& tdb, T& task, Config& conf)
|
||||||
{
|
{
|
||||||
|
int count = 0;
|
||||||
std::stringstream out;
|
std::stringstream out;
|
||||||
std::vector <T> all;
|
std::vector <T> all;
|
||||||
tdb.allPendingT (all);
|
tdb.allPendingT (all);
|
||||||
|
|
||||||
// Lookup the complete task.
|
std::vector <T> filtered = all;
|
||||||
T complete = findT (task.getId (), all);
|
filterSequence (filtered, task);
|
||||||
|
foreach (seq, filtered)
|
||||||
|
{
|
||||||
// Perform some logical consistency checks.
|
// Perform some logical consistency checks.
|
||||||
if (task.getAttribute ("recur") != "" &&
|
if (task.getAttribute ("recur") != "" &&
|
||||||
task.getAttribute ("due") == "" &&
|
task.getAttribute ("due") == "" &&
|
||||||
complete.getAttribute ("due") == "")
|
seq->getAttribute ("due") == "")
|
||||||
throw std::string ("You cannot specify a recurring task without a due date.");
|
throw std::string ("You cannot specify a recurring task without a due date.");
|
||||||
|
|
||||||
if (task.getAttribute ("until") != "" &&
|
if (task.getAttribute ("until") != "" &&
|
||||||
task.getAttribute ("recur") == "" &&
|
task.getAttribute ("recur") == "" &&
|
||||||
complete.getAttribute ("recur") == "")
|
seq->getAttribute ("recur") == "")
|
||||||
throw std::string ("You cannot specify an until date for a non-recurring task.");
|
throw std::string ("You cannot specify an until date for a non-recurring task.");
|
||||||
|
|
||||||
int count = 0;
|
// Make all changes.
|
||||||
std::vector <T>::iterator it;
|
foreach (other, all)
|
||||||
for (it = all.begin (); it != all.end (); ++it)
|
|
||||||
{
|
{
|
||||||
if (it->getId () == complete.getId () || // Self
|
if (other->getId () == seq->getId () || // Self
|
||||||
(complete.getAttribute ("parent") != "" &&
|
(seq->getAttribute ("parent") != "" &&
|
||||||
it->getAttribute ("parent") == complete.getAttribute ("parent")) || // Sibling
|
seq->getAttribute ("parent") == other->getAttribute ("parent")) || // Sibling
|
||||||
it->getUUID () == complete.getAttribute ("parent")) // Parent
|
other->getUUID () == seq->getAttribute ("parent")) // Parent
|
||||||
{
|
{
|
||||||
T original (*it);
|
|
||||||
|
|
||||||
// A non-zero value forces a file write.
|
// A non-zero value forces a file write.
|
||||||
int changes = 0;
|
int changes = 0;
|
||||||
|
|
||||||
// Apply a new description, if any.
|
// Apply other deltas.
|
||||||
if (task.getDescription () != "")
|
changes += deltaDescription (*other, task);
|
||||||
{
|
changes += deltaTags (*other, task);
|
||||||
original.setDescription (task.getDescription ());
|
changes += deltaAttributes (*other, task);
|
||||||
++changes;
|
changes += deltaSubstitutions (*other, task);
|
||||||
}
|
|
||||||
|
|
||||||
// Apply or remove tags, if any.
|
|
||||||
std::vector <std::string> tags;
|
|
||||||
task.getTags (tags);
|
|
||||||
for (unsigned int i = 0; i < tags.size (); ++i)
|
|
||||||
{
|
|
||||||
if (tags[i][0] == '+')
|
|
||||||
original.addTag (tags[i].substr (1, std::string::npos));
|
|
||||||
else
|
|
||||||
original.addTag (tags[i]);
|
|
||||||
|
|
||||||
++changes;
|
|
||||||
}
|
|
||||||
|
|
||||||
task.getRemoveTags (tags);
|
|
||||||
for (unsigned int i = 0; i < tags.size (); ++i)
|
|
||||||
{
|
|
||||||
if (tags[i][0] == '-')
|
|
||||||
original.removeTag (tags[i].substr (1, std::string::npos));
|
|
||||||
else
|
|
||||||
original.removeTag (tags[i]);
|
|
||||||
|
|
||||||
++changes;
|
|
||||||
}
|
|
||||||
|
|
||||||
// Apply or remove attributes, if any.
|
|
||||||
std::map <std::string, std::string> attributes;
|
|
||||||
task.getAttributes (attributes);
|
|
||||||
foreach (i, attributes)
|
|
||||||
{
|
|
||||||
if (i->second == "")
|
|
||||||
original.removeAttribute (i->first);
|
|
||||||
else
|
|
||||||
{
|
|
||||||
original.setAttribute (i->first, i->second);
|
|
||||||
|
|
||||||
// If a "recur" attribute is added, upgrade to a recurring task.
|
|
||||||
if (i->first == "recur")
|
|
||||||
original.setStatus (T::recurring);
|
|
||||||
}
|
|
||||||
|
|
||||||
++changes;
|
|
||||||
}
|
|
||||||
|
|
||||||
std::string from;
|
|
||||||
std::string to;
|
|
||||||
bool global;
|
|
||||||
task.getSubstitution (from, to, global);
|
|
||||||
if (from != "")
|
|
||||||
{
|
|
||||||
std::string description = original.getDescription ();
|
|
||||||
size_t pattern;
|
|
||||||
|
|
||||||
if (global)
|
|
||||||
{
|
|
||||||
// Perform all subs on description.
|
|
||||||
while ((pattern = description.find (from)) != std::string::npos)
|
|
||||||
{
|
|
||||||
description.replace (pattern, from.length (), to);
|
|
||||||
++changes;
|
|
||||||
}
|
|
||||||
|
|
||||||
original.setDescription (description);
|
|
||||||
|
|
||||||
// Perform all subs on annotations.
|
|
||||||
std::map <time_t, std::string> annotations;
|
|
||||||
original.getAnnotations (annotations);
|
|
||||||
std::map <time_t, std::string>::iterator it;
|
|
||||||
for (it = annotations.begin (); it != annotations.end (); ++it)
|
|
||||||
{
|
|
||||||
while ((pattern = it->second.find (from)) != std::string::npos)
|
|
||||||
{
|
|
||||||
it->second.replace (pattern, from.length (), to);
|
|
||||||
++changes;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
original.setAnnotations (annotations);
|
|
||||||
}
|
|
||||||
else
|
|
||||||
{
|
|
||||||
// Perform first description substitution.
|
|
||||||
if ((pattern = description.find (from)) != std::string::npos)
|
|
||||||
{
|
|
||||||
description.replace (pattern, from.length (), to);
|
|
||||||
original.setDescription (description);
|
|
||||||
++changes;
|
|
||||||
}
|
|
||||||
// Failing that, perform the first annotation substitution.
|
|
||||||
else
|
|
||||||
{
|
|
||||||
std::map <time_t, std::string> annotations;
|
|
||||||
original.getAnnotations (annotations);
|
|
||||||
|
|
||||||
std::map <time_t, std::string>::iterator it;
|
|
||||||
for (it = annotations.begin (); it != annotations.end (); ++it)
|
|
||||||
{
|
|
||||||
if ((pattern = it->second.find (from)) != std::string::npos)
|
|
||||||
{
|
|
||||||
it->second.replace (pattern, from.length (), to);
|
|
||||||
++changes;
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
original.setAnnotations (annotations);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
if (changes)
|
if (changes)
|
||||||
tdb.modifyT (original);
|
tdb.modifyT (*other);
|
||||||
|
|
||||||
++count;
|
++count;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
}
|
||||||
if (count == 0)
|
|
||||||
throw std::string ("Task not found.");
|
|
||||||
|
|
||||||
if (conf.get ("echo.command", true))
|
if (conf.get ("echo.command", true))
|
||||||
out << "Modified " << count << " task" << (count == 1 ? "" : "s") << std::endl;
|
out << "Modified " << count << " task" << (count == 1 ? "" : "s") << std::endl;
|
||||||
|
@ -844,91 +713,46 @@ std::string handleModify (TDB& tdb, T& task, Config& conf)
|
||||||
////////////////////////////////////////////////////////////////////////////////
|
////////////////////////////////////////////////////////////////////////////////
|
||||||
std::string handleAppend (TDB& tdb, T& task, Config& conf)
|
std::string handleAppend (TDB& tdb, T& task, Config& conf)
|
||||||
{
|
{
|
||||||
|
int count = 0;
|
||||||
std::stringstream out;
|
std::stringstream out;
|
||||||
std::vector <T> all;
|
std::vector <T> all;
|
||||||
tdb.allPendingT (all);
|
tdb.allPendingT (all);
|
||||||
|
|
||||||
// Lookup the complete task.
|
std::vector <T> filtered = all;
|
||||||
T complete = findT (task.getId (), all);
|
filterSequence (filtered, task);
|
||||||
|
foreach (seq, filtered)
|
||||||
int count = 0;
|
|
||||||
std::vector <T>::iterator it;
|
|
||||||
for (it = all.begin (); it != all.end (); ++it)
|
|
||||||
{
|
{
|
||||||
if (it->getId () == complete.getId () || // Self
|
foreach (other, all)
|
||||||
(complete.getAttribute ("parent") != "" &&
|
{
|
||||||
it->getAttribute ("parent") == complete.getAttribute ("parent")) || // Sibling
|
if (other->getId () == seq->getId () || // Self
|
||||||
it->getUUID () == complete.getAttribute ("parent")) // Parent
|
(seq->getAttribute ("parent") != "" &&
|
||||||
|
seq->getAttribute ("parent") == other->getAttribute ("parent")) || // Sibling
|
||||||
|
other->getUUID () == seq->getAttribute ("parent")) // Parent
|
||||||
{
|
{
|
||||||
T original (*it);
|
|
||||||
|
|
||||||
// A non-zero value forces a file write.
|
// A non-zero value forces a file write.
|
||||||
int changes = 0;
|
int changes = 0;
|
||||||
|
|
||||||
// Apply a new description, if any.
|
// Apply other deltas.
|
||||||
if (task.getDescription () != "")
|
changes += deltaAppend (*other, task);
|
||||||
{
|
changes += deltaTags (*other, task);
|
||||||
original.setDescription (original.getDescription () +
|
changes += deltaAttributes (*other, task);
|
||||||
" " +
|
|
||||||
task.getDescription ());
|
|
||||||
++changes;
|
|
||||||
}
|
|
||||||
|
|
||||||
// Apply or remove tags, if any.
|
|
||||||
std::vector <std::string> tags;
|
|
||||||
task.getTags (tags);
|
|
||||||
for (unsigned int i = 0; i < tags.size (); ++i)
|
|
||||||
{
|
|
||||||
if (tags[i][0] == '+')
|
|
||||||
original.addTag (tags[i].substr (1, std::string::npos));
|
|
||||||
else
|
|
||||||
original.addTag (tags[i]);
|
|
||||||
|
|
||||||
++changes;
|
|
||||||
}
|
|
||||||
|
|
||||||
task.getRemoveTags (tags);
|
|
||||||
for (unsigned int i = 0; i < tags.size (); ++i)
|
|
||||||
{
|
|
||||||
if (tags[i][0] == '-')
|
|
||||||
original.removeTag (tags[i].substr (1, std::string::npos));
|
|
||||||
else
|
|
||||||
original.removeTag (tags[i]);
|
|
||||||
|
|
||||||
++changes;
|
|
||||||
}
|
|
||||||
|
|
||||||
// Apply or remove attributes, if any.
|
|
||||||
std::map <std::string, std::string> attributes;
|
|
||||||
task.getAttributes (attributes);
|
|
||||||
foreach (i, attributes)
|
|
||||||
{
|
|
||||||
if (i->second == "")
|
|
||||||
original.removeAttribute (i->first);
|
|
||||||
else
|
|
||||||
original.setAttribute (i->first, i->second);
|
|
||||||
|
|
||||||
++changes;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (changes)
|
if (changes)
|
||||||
{
|
{
|
||||||
tdb.modifyT (original);
|
tdb.modifyT (*other);
|
||||||
|
|
||||||
if (conf.get ("echo.command", true))
|
if (conf.get ("echo.command", true))
|
||||||
out << "Appended '"
|
out << "Appended '"
|
||||||
<< task.getDescription ()
|
<< task.getDescription ()
|
||||||
<< "' to task "
|
<< "' to task "
|
||||||
<< original.getId ()
|
<< other->getId ()
|
||||||
<< std::endl;
|
<< std::endl;
|
||||||
}
|
}
|
||||||
|
|
||||||
++count;
|
++count;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
}
|
||||||
if (count == 0)
|
|
||||||
throw std::string ("Task not found.");
|
|
||||||
|
|
||||||
if (conf.get ("echo.command", true))
|
if (conf.get ("echo.command", true))
|
||||||
out << "Modified " << count << " task" << (count == 1 ? "" : "s") << std::endl;
|
out << "Modified " << count << " task" << (count == 1 ? "" : "s") << std::endl;
|
||||||
|
@ -1030,28 +854,22 @@ std::string handleAnnotate (TDB& tdb, T& task, Config& conf)
|
||||||
std::stringstream out;
|
std::stringstream out;
|
||||||
std::vector <T> all;
|
std::vector <T> all;
|
||||||
tdb.pendingT (all);
|
tdb.pendingT (all);
|
||||||
|
filterSequence (all, task);
|
||||||
|
|
||||||
std::vector <T>::iterator it;
|
foreach (t, all)
|
||||||
for (it = all.begin (); it != all.end (); ++it)
|
|
||||||
{
|
{
|
||||||
if (it->getId () == task.getId ())
|
t->addAnnotation (task.getDescription ());
|
||||||
{
|
tdb.modifyT (*t);
|
||||||
it->addAnnotation (task.getDescription ());
|
|
||||||
tdb.modifyT (*it);
|
|
||||||
|
|
||||||
if (conf.get ("echo.command", true))
|
if (conf.get ("echo.command", true))
|
||||||
out << "Annotated "
|
out << "Annotated "
|
||||||
<< task.getId ()
|
<< t->getId ()
|
||||||
<< " with '"
|
<< " with '"
|
||||||
<< task.getDescription ()
|
<< t->getDescription ()
|
||||||
<< "'"
|
<< "'"
|
||||||
<< std::endl;
|
<< std::endl;
|
||||||
|
|
||||||
return out.str ();
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
throw std::string ("Task not found.");
|
|
||||||
return out.str ();
|
return out.str ();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -1067,3 +885,156 @@ T findT (int id, const std::vector <T>& all)
|
||||||
}
|
}
|
||||||
|
|
||||||
////////////////////////////////////////////////////////////////////////////////
|
////////////////////////////////////////////////////////////////////////////////
|
||||||
|
int deltaAppend (T& task, T& delta)
|
||||||
|
{
|
||||||
|
if (delta.getDescription () != "")
|
||||||
|
{
|
||||||
|
task.setDescription (
|
||||||
|
task.getDescription () +
|
||||||
|
" " +
|
||||||
|
delta.getDescription ());
|
||||||
|
|
||||||
|
return 1;
|
||||||
|
}
|
||||||
|
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
////////////////////////////////////////////////////////////////////////////////
|
||||||
|
int deltaDescription (T& task, T& delta)
|
||||||
|
{
|
||||||
|
if (delta.getDescription () != "")
|
||||||
|
{
|
||||||
|
task.setDescription (delta.getDescription ());
|
||||||
|
return 1;
|
||||||
|
}
|
||||||
|
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
////////////////////////////////////////////////////////////////////////////////
|
||||||
|
int deltaTags (T& task, T& delta)
|
||||||
|
{
|
||||||
|
int changes = 0;
|
||||||
|
|
||||||
|
// Apply or remove tags, if any.
|
||||||
|
std::vector <std::string> tags;
|
||||||
|
delta.getTags (tags);
|
||||||
|
for (unsigned int i = 0; i < tags.size (); ++i)
|
||||||
|
{
|
||||||
|
if (tags[i][0] == '+')
|
||||||
|
task.addTag (tags[i].substr (1, std::string::npos));
|
||||||
|
else
|
||||||
|
task.addTag (tags[i]);
|
||||||
|
|
||||||
|
++changes;
|
||||||
|
}
|
||||||
|
|
||||||
|
delta.getRemoveTags (tags);
|
||||||
|
for (unsigned int i = 0; i < tags.size (); ++i)
|
||||||
|
{
|
||||||
|
if (tags[i][0] == '-')
|
||||||
|
task.removeTag (tags[i].substr (1, std::string::npos));
|
||||||
|
else
|
||||||
|
task.removeTag (tags[i]);
|
||||||
|
|
||||||
|
++changes;
|
||||||
|
}
|
||||||
|
|
||||||
|
return changes;
|
||||||
|
}
|
||||||
|
|
||||||
|
////////////////////////////////////////////////////////////////////////////////
|
||||||
|
int deltaAttributes (T& task, T& delta)
|
||||||
|
{
|
||||||
|
int changes = 0;
|
||||||
|
|
||||||
|
std::map <std::string, std::string> attributes;
|
||||||
|
delta.getAttributes (attributes);
|
||||||
|
foreach (i, attributes)
|
||||||
|
{
|
||||||
|
if (i->second == "")
|
||||||
|
task.removeAttribute (i->first);
|
||||||
|
else
|
||||||
|
task.setAttribute (i->first, i->second);
|
||||||
|
|
||||||
|
++changes;
|
||||||
|
}
|
||||||
|
|
||||||
|
return changes;
|
||||||
|
}
|
||||||
|
|
||||||
|
////////////////////////////////////////////////////////////////////////////////
|
||||||
|
int deltaSubstitutions (T& task, T& delta)
|
||||||
|
{
|
||||||
|
int changes = 0;
|
||||||
|
std::string from;
|
||||||
|
std::string to;
|
||||||
|
bool global;
|
||||||
|
delta.getSubstitution (from, to, global);
|
||||||
|
|
||||||
|
if (from != "")
|
||||||
|
{
|
||||||
|
std::string description = task.getDescription ();
|
||||||
|
size_t pattern;
|
||||||
|
|
||||||
|
if (global)
|
||||||
|
{
|
||||||
|
// Perform all subs on description.
|
||||||
|
while ((pattern = description.find (from)) != std::string::npos)
|
||||||
|
{
|
||||||
|
description.replace (pattern, from.length (), to);
|
||||||
|
++changes;
|
||||||
|
}
|
||||||
|
|
||||||
|
task.setDescription (description);
|
||||||
|
|
||||||
|
// Perform all subs on annotations.
|
||||||
|
std::map <time_t, std::string> annotations;
|
||||||
|
task.getAnnotations (annotations);
|
||||||
|
std::map <time_t, std::string>::iterator it;
|
||||||
|
for (it = annotations.begin (); it != annotations.end (); ++it)
|
||||||
|
{
|
||||||
|
while ((pattern = it->second.find (from)) != std::string::npos)
|
||||||
|
{
|
||||||
|
it->second.replace (pattern, from.length (), to);
|
||||||
|
++changes;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
task.setAnnotations (annotations);
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
// Perform first description substitution.
|
||||||
|
if ((pattern = description.find (from)) != std::string::npos)
|
||||||
|
{
|
||||||
|
description.replace (pattern, from.length (), to);
|
||||||
|
task.setDescription (description);
|
||||||
|
++changes;
|
||||||
|
}
|
||||||
|
// Failing that, perform the first annotation substitution.
|
||||||
|
else
|
||||||
|
{
|
||||||
|
std::map <time_t, std::string> annotations;
|
||||||
|
task.getAnnotations (annotations);
|
||||||
|
|
||||||
|
std::map <time_t, std::string>::iterator it;
|
||||||
|
for (it = annotations.begin (); it != annotations.end (); ++it)
|
||||||
|
{
|
||||||
|
if ((pattern = it->second.find (from)) != std::string::npos)
|
||||||
|
{
|
||||||
|
it->second.replace (pattern, from.length (), to);
|
||||||
|
++changes;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
task.setAnnotations (annotations);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return changes;
|
||||||
|
}
|
||||||
|
|
||||||
|
////////////////////////////////////////////////////////////////////////////////
|
||||||
|
|
106
src/parse.cpp
106
src/parse.cpp
|
@ -301,6 +301,9 @@ static bool validAttribute (
|
||||||
////////////////////////////////////////////////////////////////////////////////
|
////////////////////////////////////////////////////////////////////////////////
|
||||||
static bool validId (const std::string& input)
|
static bool validId (const std::string& input)
|
||||||
{
|
{
|
||||||
|
if (input.length () == 0)
|
||||||
|
return false;
|
||||||
|
|
||||||
for (size_t i = 0; i < input.length (); ++i)
|
for (size_t i = 0; i < input.length (); ++i)
|
||||||
if (!::isdigit (input[i]))
|
if (!::isdigit (input[i]))
|
||||||
return false;
|
return false;
|
||||||
|
@ -308,6 +311,56 @@ static bool validId (const std::string& input)
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
////////////////////////////////////////////////////////////////////////////////
|
||||||
|
// 1,2-4,6
|
||||||
|
static bool validSequence (
|
||||||
|
const std::string& input,
|
||||||
|
std::vector <int>& ids)
|
||||||
|
{
|
||||||
|
std::vector <std::string> ranges;
|
||||||
|
split (ranges, input, ',');
|
||||||
|
|
||||||
|
std::vector <std::string>::iterator it;
|
||||||
|
for (it = ranges.begin (); it != ranges.end (); ++it)
|
||||||
|
{
|
||||||
|
std::vector <std::string> range;
|
||||||
|
split (range, *it, '-');
|
||||||
|
|
||||||
|
switch (range.size ())
|
||||||
|
{
|
||||||
|
case 1:
|
||||||
|
if (! validId (range[0]))
|
||||||
|
return false;
|
||||||
|
|
||||||
|
int id = ::atoi (range[0].c_str ());
|
||||||
|
ids.push_back (id);
|
||||||
|
break;
|
||||||
|
|
||||||
|
case 2:
|
||||||
|
{
|
||||||
|
if (! validId (range[0]) ||
|
||||||
|
! validId (range[1]))
|
||||||
|
return false;
|
||||||
|
|
||||||
|
int low = ::atoi (range[0].c_str ());
|
||||||
|
int high = ::atoi (range[1].c_str ());
|
||||||
|
if (low >= high)
|
||||||
|
return false;
|
||||||
|
|
||||||
|
for (int i = low; i <= high; ++i)
|
||||||
|
ids.push_back (i);
|
||||||
|
}
|
||||||
|
break;
|
||||||
|
|
||||||
|
default:
|
||||||
|
return false;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return ids.size () ? true : false;
|
||||||
|
}
|
||||||
|
|
||||||
////////////////////////////////////////////////////////////////////////////////
|
////////////////////////////////////////////////////////////////////////////////
|
||||||
static bool validTag (const std::string& input)
|
static bool validTag (const std::string& input)
|
||||||
{
|
{
|
||||||
|
@ -392,15 +445,23 @@ bool validDuration (std::string& input)
|
||||||
}
|
}
|
||||||
|
|
||||||
////////////////////////////////////////////////////////////////////////////////
|
////////////////////////////////////////////////////////////////////////////////
|
||||||
// Token Distinguishing characteristic
|
// Token EBNF
|
||||||
// ------- -----------------------------
|
// ------- ----------------------------------
|
||||||
// command first positional
|
// command first non-id recognized argument
|
||||||
// id \d+
|
|
||||||
// description default, accumulate
|
|
||||||
// substitution /\w+/\w*/
|
|
||||||
// tags [-+]\w+
|
|
||||||
// attributes \w+:.+
|
|
||||||
//
|
//
|
||||||
|
// substitution ::= "/" from "/" to "/g"
|
||||||
|
// | "/" from "/" to "/" ;
|
||||||
|
//
|
||||||
|
// tags ::= "+" word
|
||||||
|
// | "-" word ;
|
||||||
|
//
|
||||||
|
// attributes ::= word ":" value
|
||||||
|
// | word ":"
|
||||||
|
//
|
||||||
|
// sequence ::= \d+ "," sequence
|
||||||
|
// | \d+ "-" \d+ ;
|
||||||
|
//
|
||||||
|
// description (whatever isn't one of the above)
|
||||||
void parse (
|
void parse (
|
||||||
std::vector <std::string>& args,
|
std::vector <std::string>& args,
|
||||||
std::string& command,
|
std::string& command,
|
||||||
|
@ -409,6 +470,9 @@ void parse (
|
||||||
{
|
{
|
||||||
command = "";
|
command = "";
|
||||||
|
|
||||||
|
bool foundSequence = false;
|
||||||
|
bool foundSomethingAfterSequence = false;
|
||||||
|
|
||||||
std::string descCandidate = "";
|
std::string descCandidate = "";
|
||||||
for (size_t i = 0; i < args.size (); ++i)
|
for (size_t i = 0; i < args.size (); ++i)
|
||||||
{
|
{
|
||||||
|
@ -422,16 +486,24 @@ void parse (
|
||||||
std::string from;
|
std::string from;
|
||||||
std::string to;
|
std::string to;
|
||||||
bool global;
|
bool global;
|
||||||
|
std::vector <int> sequence;
|
||||||
|
|
||||||
// An id is the first argument found that contains all digits.
|
// An id is the first argument found that contains all digits.
|
||||||
if (lowerCase (command) != "add" && // "add" doesn't require an ID
|
if (lowerCase (command) != "add" && // "add" doesn't require an ID
|
||||||
task.getId () == 0 &&
|
validSequence (arg, sequence) &&
|
||||||
validId (arg))
|
! foundSomethingAfterSequence)
|
||||||
task.setId (::atoi (arg.c_str ()));
|
{
|
||||||
|
foundSequence = true;
|
||||||
|
foreach (id, sequence)
|
||||||
|
task.addId (*id);
|
||||||
|
}
|
||||||
|
|
||||||
// Tags begin with + or - and contain arbitrary text.
|
// Tags begin with + or - and contain arbitrary text.
|
||||||
else if (validTag (arg))
|
else if (validTag (arg))
|
||||||
{
|
{
|
||||||
|
if (foundSequence)
|
||||||
|
foundSomethingAfterSequence = true;
|
||||||
|
|
||||||
if (arg[0] == '+')
|
if (arg[0] == '+')
|
||||||
task.addTag (arg.substr (1, std::string::npos));
|
task.addTag (arg.substr (1, std::string::npos));
|
||||||
else if (arg[0] == '-')
|
else if (arg[0] == '-')
|
||||||
|
@ -442,6 +514,9 @@ void parse (
|
||||||
// value.
|
// value.
|
||||||
else if ((colon = arg.find (":")) != std::string::npos)
|
else if ((colon = arg.find (":")) != std::string::npos)
|
||||||
{
|
{
|
||||||
|
if (foundSequence)
|
||||||
|
foundSomethingAfterSequence = true;
|
||||||
|
|
||||||
std::string name = lowerCase (arg.substr (0, colon));
|
std::string name = lowerCase (arg.substr (0, colon));
|
||||||
std::string value = arg.substr (colon + 1, std::string::npos);
|
std::string value = arg.substr (colon + 1, std::string::npos);
|
||||||
|
|
||||||
|
@ -464,12 +539,18 @@ void parse (
|
||||||
// Substitution of description text.
|
// Substitution of description text.
|
||||||
else if (validSubstitution (arg, from, to, global))
|
else if (validSubstitution (arg, from, to, global))
|
||||||
{
|
{
|
||||||
|
if (foundSequence)
|
||||||
|
foundSomethingAfterSequence = true;
|
||||||
|
|
||||||
task.setSubstitution (from, to, global);
|
task.setSubstitution (from, to, global);
|
||||||
}
|
}
|
||||||
|
|
||||||
// Command.
|
// Command.
|
||||||
else if (command == "")
|
else if (command == "")
|
||||||
{
|
{
|
||||||
|
if (foundSequence)
|
||||||
|
foundSomethingAfterSequence = true;
|
||||||
|
|
||||||
std::string l = lowerCase (arg);
|
std::string l = lowerCase (arg);
|
||||||
if (isCommand (l) && validCommand (l))
|
if (isCommand (l) && validCommand (l))
|
||||||
command = l;
|
command = l;
|
||||||
|
@ -484,6 +565,9 @@ void parse (
|
||||||
// Anything else is just considered description.
|
// Anything else is just considered description.
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
|
if (foundSequence)
|
||||||
|
foundSomethingAfterSequence = true;
|
||||||
|
|
||||||
if (descCandidate.length ())
|
if (descCandidate.length ())
|
||||||
descCandidate += " ";
|
descCandidate += " ";
|
||||||
descCandidate += arg;
|
descCandidate += arg;
|
||||||
|
|
|
@ -46,6 +46,53 @@
|
||||||
#include <ncurses.h>
|
#include <ncurses.h>
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
|
////////////////////////////////////////////////////////////////////////////////
|
||||||
|
void filterSequence (std::vector<T>& all, T& task)
|
||||||
|
{
|
||||||
|
std::vector <int> sequence = task.getAllIds ();
|
||||||
|
|
||||||
|
std::vector <T> filtered;
|
||||||
|
std::vector <T>::iterator t;
|
||||||
|
for (t = all.begin (); t != all.end (); ++t)
|
||||||
|
{
|
||||||
|
std::vector <int>::iterator s;
|
||||||
|
for (s = sequence.begin (); s != sequence.end (); ++s)
|
||||||
|
if (t->getId () == *s)
|
||||||
|
filtered.push_back (*t);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (sequence.size () != filtered.size ())
|
||||||
|
{
|
||||||
|
std::vector <int> filteredSequence;
|
||||||
|
std::vector <T>::iterator fs;
|
||||||
|
for (fs = filtered.begin (); fs != filtered.end (); ++fs)
|
||||||
|
filteredSequence.push_back (fs->getId ());
|
||||||
|
|
||||||
|
std::vector <int> left;
|
||||||
|
std::vector <int> right;
|
||||||
|
listDiff (filteredSequence, sequence, left, right);
|
||||||
|
if (left.size ())
|
||||||
|
throw std::string ("Sequence filtering error - please report this error");
|
||||||
|
|
||||||
|
if (right.size ())
|
||||||
|
{
|
||||||
|
std::stringstream out;
|
||||||
|
out << "Task";
|
||||||
|
|
||||||
|
if (right.size () > 1) out << "s";
|
||||||
|
|
||||||
|
std::vector <int>::iterator s;
|
||||||
|
for (s = right.begin (); s != right.end (); ++s)
|
||||||
|
out << " " << *s;
|
||||||
|
|
||||||
|
out << " not found";
|
||||||
|
throw out.str ();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
all = filtered;
|
||||||
|
}
|
||||||
|
|
||||||
////////////////////////////////////////////////////////////////////////////////
|
////////////////////////////////////////////////////////////////////////////////
|
||||||
void filter (std::vector<T>& all, T& task)
|
void filter (std::vector<T>& all, T& task)
|
||||||
{
|
{
|
||||||
|
@ -265,6 +312,16 @@ std::string handleInfo (TDB& tdb, T& task, Config& conf)
|
||||||
std::vector <T> tasks;
|
std::vector <T> tasks;
|
||||||
tdb.allPendingT (tasks);
|
tdb.allPendingT (tasks);
|
||||||
|
|
||||||
|
// Find the task.
|
||||||
|
int count = 0;
|
||||||
|
for (unsigned int i = 0; i < tasks.size (); ++i)
|
||||||
|
{
|
||||||
|
T refTask (tasks[i]);
|
||||||
|
|
||||||
|
if (refTask.getId () == task.getId () || task.sequenceContains (refTask.getId ()))
|
||||||
|
{
|
||||||
|
++count;
|
||||||
|
|
||||||
Table table;
|
Table table;
|
||||||
table.setTableWidth (width);
|
table.setTableWidth (width);
|
||||||
table.setDateFormat (conf.get ("dateformat", "m/d/Y"));
|
table.setDateFormat (conf.get ("dateformat", "m/d/Y"));
|
||||||
|
@ -285,27 +342,23 @@ std::string handleInfo (TDB& tdb, T& task, Config& conf)
|
||||||
|
|
||||||
table.setColumnJustification (0, Table::left);
|
table.setColumnJustification (0, Table::left);
|
||||||
table.setColumnJustification (1, Table::left);
|
table.setColumnJustification (1, Table::left);
|
||||||
|
|
||||||
// Find the task.
|
|
||||||
for (unsigned int i = 0; i < tasks.size (); ++i)
|
|
||||||
{
|
|
||||||
T refTask (tasks[i]);
|
|
||||||
|
|
||||||
if (refTask.getId () == task.getId ())
|
|
||||||
{
|
|
||||||
Date now;
|
Date now;
|
||||||
|
|
||||||
int row = table.addRow ();
|
int row = table.addRow ();
|
||||||
table.addCell (row, 0, "ID");
|
table.addCell (row, 0, "ID");
|
||||||
table.addCell (row, 1, refTask.getId ());
|
table.addCell (row, 1, refTask.getId ());
|
||||||
|
|
||||||
row = table.addRow ();
|
std::string status = refTask.getStatus () == T::pending ? "Pending"
|
||||||
table.addCell (row, 0, "Status");
|
|
||||||
table.addCell (row, 1, ( refTask.getStatus () == T::pending ? "Pending"
|
|
||||||
: refTask.getStatus () == T::completed ? "Completed"
|
: refTask.getStatus () == T::completed ? "Completed"
|
||||||
: refTask.getStatus () == T::deleted ? "Deleted"
|
: refTask.getStatus () == T::deleted ? "Deleted"
|
||||||
: refTask.getStatus () == T::recurring ? "Recurring"
|
: refTask.getStatus () == T::recurring ? "Recurring"
|
||||||
: ""));
|
: "";
|
||||||
|
if (refTask.getAttribute ("parent") != "")
|
||||||
|
status += " (Recurring)";
|
||||||
|
|
||||||
|
row = table.addRow ();
|
||||||
|
table.addCell (row, 0, "Status");
|
||||||
|
table.addCell (row, 1, status);
|
||||||
|
|
||||||
std::string description = refTask.getDescription ();
|
std::string description = refTask.getDescription ();
|
||||||
std::string when;
|
std::string when;
|
||||||
|
@ -336,16 +389,25 @@ std::string handleInfo (TDB& tdb, T& task, Config& conf)
|
||||||
table.addCell (row, 1, refTask.getAttribute ("priority"));
|
table.addCell (row, 1, refTask.getAttribute ("priority"));
|
||||||
}
|
}
|
||||||
|
|
||||||
if (refTask.getStatus () == T::recurring)
|
if (refTask.getStatus () == T::recurring ||
|
||||||
|
refTask.getAttribute ("parent") != "")
|
||||||
|
{
|
||||||
|
if (refTask.getAttribute ("recur") != "")
|
||||||
{
|
{
|
||||||
row = table.addRow ();
|
row = table.addRow ();
|
||||||
table.addCell (row, 0, "Recurrence");
|
table.addCell (row, 0, "Recurrence");
|
||||||
table.addCell (row, 1, refTask.getAttribute ("recur"));
|
table.addCell (row, 1, refTask.getAttribute ("recur"));
|
||||||
|
}
|
||||||
|
|
||||||
|
if (refTask.getAttribute ("until") != "")
|
||||||
|
{
|
||||||
row = table.addRow ();
|
row = table.addRow ();
|
||||||
table.addCell (row, 0, "Recur until");
|
table.addCell (row, 0, "Recur until");
|
||||||
table.addCell (row, 1, refTask.getAttribute ("until"));
|
table.addCell (row, 1, refTask.getAttribute ("until"));
|
||||||
|
}
|
||||||
|
|
||||||
|
if (refTask.getAttribute ("mask") != "")
|
||||||
|
{
|
||||||
row = table.addRow ();
|
row = table.addRow ();
|
||||||
table.addCell (row, 0, "Mask");
|
table.addCell (row, 0, "Mask");
|
||||||
table.addCell (row, 1, refTask.getAttribute ("mask"));
|
table.addCell (row, 1, refTask.getAttribute ("mask"));
|
||||||
|
@ -356,6 +418,7 @@ std::string handleInfo (TDB& tdb, T& task, Config& conf)
|
||||||
row = table.addRow ();
|
row = table.addRow ();
|
||||||
table.addCell (row, 0, "Parent task");
|
table.addCell (row, 0, "Parent task");
|
||||||
table.addCell (row, 1, refTask.getAttribute ("parent"));
|
table.addCell (row, 1, refTask.getAttribute ("parent"));
|
||||||
|
}
|
||||||
|
|
||||||
row = table.addRow ();
|
row = table.addRow ();
|
||||||
table.addCell (row, 0, "Mask Index");
|
table.addCell (row, 0, "Mask Index");
|
||||||
|
@ -440,14 +503,14 @@ std::string handleInfo (TDB& tdb, T& task, Config& conf)
|
||||||
}
|
}
|
||||||
|
|
||||||
table.addCell (row, 1, entry + " (" + age + ")");
|
table.addCell (row, 1, entry + " (" + age + ")");
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
if (table.rowCount ())
|
|
||||||
out << optionalBlankLine (conf)
|
out << optionalBlankLine (conf)
|
||||||
<< table.render ()
|
<< table.render ()
|
||||||
<< std::endl;
|
<< std::endl;
|
||||||
else
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (! count)
|
||||||
out << "No matches." << std::endl;
|
out << "No matches." << std::endl;
|
||||||
|
|
||||||
return out.str ();
|
return out.str ();
|
||||||
|
@ -1038,6 +1101,7 @@ std::string handleReportGHistory (TDB& tdb, T& task, Config& conf)
|
||||||
if (task.getStatus () == T::deleted)
|
if (task.getStatus () == T::deleted)
|
||||||
{
|
{
|
||||||
epoch = monthlyEpoch (task.getAttribute ("end"));
|
epoch = monthlyEpoch (task.getAttribute ("end"));
|
||||||
|
groups[epoch] = 0;
|
||||||
|
|
||||||
if (deletedGroup.find (epoch) != deletedGroup.end ())
|
if (deletedGroup.find (epoch) != deletedGroup.end ())
|
||||||
deletedGroup[epoch] = deletedGroup[epoch] + 1;
|
deletedGroup[epoch] = deletedGroup[epoch] + 1;
|
||||||
|
@ -1047,6 +1111,7 @@ std::string handleReportGHistory (TDB& tdb, T& task, Config& conf)
|
||||||
else if (task.getStatus () == T::completed)
|
else if (task.getStatus () == T::completed)
|
||||||
{
|
{
|
||||||
epoch = monthlyEpoch (task.getAttribute ("end"));
|
epoch = monthlyEpoch (task.getAttribute ("end"));
|
||||||
|
groups[epoch] = 0;
|
||||||
|
|
||||||
if (completedGroup.find (epoch) != completedGroup.end ())
|
if (completedGroup.find (epoch) != completedGroup.end ())
|
||||||
completedGroup[epoch] = completedGroup[epoch] + 1;
|
completedGroup[epoch] = completedGroup[epoch] + 1;
|
||||||
|
|
10
src/task.cpp
10
src/task.cpp
|
@ -230,7 +230,13 @@ static std::string longUsage (Config& conf)
|
||||||
{
|
{
|
||||||
std::stringstream out;
|
std::stringstream out;
|
||||||
out << shortUsage (conf)
|
out << shortUsage (conf)
|
||||||
<< "ID is the numeric identifier displayed by the 'task list' command." << "\n"
|
<< "ID is the numeric identifier displayed by the 'task list' command. "
|
||||||
|
<< "You can specify multiple IDs for task commands, and multiple tasks "
|
||||||
|
<< "will be affected. To specify multiple IDs make sure you use one "
|
||||||
|
<< "of these forms:" << "\n"
|
||||||
|
<< " task delete 1,2,3" << "\n"
|
||||||
|
<< " task info 1-3" << "\n"
|
||||||
|
<< " task pri:H 1,2-5,19" << "\n"
|
||||||
<< "\n"
|
<< "\n"
|
||||||
<< "Tags are arbitrary words, any quantity:" << "\n"
|
<< "Tags are arbitrary words, any quantity:" << "\n"
|
||||||
<< " +tag The + means add the tag" << "\n"
|
<< " +tag The + means add the tag" << "\n"
|
||||||
|
@ -339,7 +345,7 @@ int main (int argc, char** argv)
|
||||||
|
|
||||||
catch (std::string& error)
|
catch (std::string& error)
|
||||||
{
|
{
|
||||||
std::cerr << error << std::endl;
|
std::cout << error << std::endl;
|
||||||
return -1;
|
return -1;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
47
src/task.h
47
src/task.h
|
@ -90,8 +90,14 @@ std::string handleUndo (TDB&, T&, Config&);
|
||||||
std::string handleColor (Config&);
|
std::string handleColor (Config&);
|
||||||
std::string handleAnnotate (TDB&, T&, Config&);
|
std::string handleAnnotate (TDB&, T&, Config&);
|
||||||
T findT (int, const std::vector <T>&);
|
T findT (int, const std::vector <T>&);
|
||||||
|
int deltaAppend (T&, T&);
|
||||||
|
int deltaDescription (T&, T&);
|
||||||
|
int deltaTags (T&, T&);
|
||||||
|
int deltaAttributes (T&, T&);
|
||||||
|
int deltaSubstitutions (T&, T&);
|
||||||
|
|
||||||
// report.cpp
|
// report.cpp
|
||||||
|
void filterSequence (std::vector<T>&, T&);
|
||||||
void filter (std::vector<T>&, T&);
|
void filter (std::vector<T>&, T&);
|
||||||
std::string handleInfo (TDB&, T&, Config&);
|
std::string handleInfo (TDB&, T&, Config&);
|
||||||
std::string handleCompleted (TDB&, T&, Config&);
|
std::string handleCompleted (TDB&, T&, Config&);
|
||||||
|
@ -152,4 +158,45 @@ void autoColorize (T&, Text::color&, Text::color&, Config&);
|
||||||
// import.cpp
|
// import.cpp
|
||||||
std::string handleImport (TDB&, T&, Config&);
|
std::string handleImport (TDB&, T&, Config&);
|
||||||
|
|
||||||
|
// list template
|
||||||
|
///////////////////////////////////////////////////////////////////////////////
|
||||||
|
template <class T> void listDiff (
|
||||||
|
const T& left, const T& right, T& leftOnly, T& rightOnly)
|
||||||
|
{
|
||||||
|
leftOnly.clear ();
|
||||||
|
rightOnly.clear ();
|
||||||
|
|
||||||
|
for (unsigned int l = 0; l < left.size (); ++l)
|
||||||
|
{
|
||||||
|
bool found = false;
|
||||||
|
for (unsigned int r = 0; r < right.size (); ++r)
|
||||||
|
{
|
||||||
|
if (left[l] == right[r])
|
||||||
|
{
|
||||||
|
found = true;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!found)
|
||||||
|
leftOnly.push_back (left[l]);
|
||||||
|
}
|
||||||
|
|
||||||
|
for (unsigned int r = 0; r < right.size (); ++r)
|
||||||
|
{
|
||||||
|
bool found = false;
|
||||||
|
for (unsigned int l = 0; l < left.size (); ++l)
|
||||||
|
{
|
||||||
|
if (left[l] == right[r])
|
||||||
|
{
|
||||||
|
found = true;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!found)
|
||||||
|
rightOnly.push_back (right[r]);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
////////////////////////////////////////////////////////////////////////////////
|
////////////////////////////////////////////////////////////////////////////////
|
||||||
|
|
1
src/tests/.gitignore
vendored
1
src/tests/.gitignore
vendored
|
@ -5,3 +5,4 @@ date.t
|
||||||
duration.t
|
duration.t
|
||||||
text.t
|
text.t
|
||||||
autocomplete.t
|
autocomplete.t
|
||||||
|
parse.t
|
||||||
|
|
|
@ -1,4 +1,5 @@
|
||||||
PROJECT = t.t tdb.t date.t duration.t t.benchmark.t text.t autocomplete.t
|
PROJECT = t.t tdb.t date.t duration.t t.benchmark.t text.t autocomplete.t \
|
||||||
|
parse.t
|
||||||
CFLAGS = -I. -I.. -Wall -pedantic -ggdb3 -fno-rtti
|
CFLAGS = -I. -I.. -Wall -pedantic -ggdb3 -fno-rtti
|
||||||
LFLAGS = -L/usr/local/lib
|
LFLAGS = -L/usr/local/lib
|
||||||
OBJECTS = ../TDB.o ../T.o ../parse.o ../text.o ../Date.o ../util.o ../Config.o
|
OBJECTS = ../TDB.o ../T.o ../parse.o ../text.o ../Date.o ../util.o ../Config.o
|
||||||
|
@ -38,3 +39,6 @@ text.t: text.t.o $(OBJECTS) test.o
|
||||||
autocomplete.t: autocomplete.t.o $(OBJECTS) test.o
|
autocomplete.t: autocomplete.t.o $(OBJECTS) test.o
|
||||||
g++ autocomplete.t.o $(OBJECTS) test.o $(LFLAGS) -o autocomplete.t
|
g++ autocomplete.t.o $(OBJECTS) test.o $(LFLAGS) -o autocomplete.t
|
||||||
|
|
||||||
|
parse.t: parse.t.o $(OBJECTS) test.o
|
||||||
|
g++ parse.t.o $(OBJECTS) test.o $(LFLAGS) -o parse.t
|
||||||
|
|
||||||
|
|
|
@ -51,49 +51,49 @@ qx{../task rc:confirm.rc add foo} for 1 .. 10;
|
||||||
|
|
||||||
# Test the various forms of "yes".
|
# Test the various forms of "yes".
|
||||||
my $output = qx{echo "yes" | ../task rc:confirm.rc del 1};
|
my $output = qx{echo "yes" | ../task rc:confirm.rc del 1};
|
||||||
like ($output, qr/Permanently delete task\? \(y\/n\)/, 'confirmation - yes works');
|
like ($output, qr/Permanently delete task 1 'foo'\? \(y\/n\)/, 'confirmation - yes works');
|
||||||
unlike ($output, qr/Task not deleted\./, 'confirmation - yes works');
|
unlike ($output, qr/Task not deleted\./, 'confirmation - yes works');
|
||||||
|
|
||||||
$output = qx{echo "ye" | ../task rc:confirm.rc del 2};
|
$output = qx{echo "ye" | ../task rc:confirm.rc del 2};
|
||||||
like ($output, qr/Permanently delete task\? \(y\/n\)/, 'confirmation - ye works');
|
like ($output, qr/Permanently delete task 2 'foo'\? \(y\/n\)/, 'confirmation - ye works');
|
||||||
unlike ($output, qr/Task not deleted\./, 'confirmation - ye works');
|
unlike ($output, qr/Task not deleted\./, 'confirmation - ye works');
|
||||||
|
|
||||||
$output = qx{echo "y" | ../task rc:confirm.rc del 3};
|
$output = qx{echo "y" | ../task rc:confirm.rc del 3};
|
||||||
like ($output, qr/Permanently delete task\? \(y\/n\)/, 'confirmation - y works');
|
like ($output, qr/Permanently delete task 3 'foo'\? \(y\/n\)/, 'confirmation - y works');
|
||||||
unlike ($output, qr/Task not deleted\./, 'confirmation - y works');
|
unlike ($output, qr/Task not deleted\./, 'confirmation - y works');
|
||||||
|
|
||||||
$output = qx{echo "YES" | ../task rc:confirm.rc del 4};
|
$output = qx{echo "YES" | ../task rc:confirm.rc del 4};
|
||||||
like ($output, qr/Permanently delete task\? \(y\/n\)/, 'confirmation - YES works');
|
like ($output, qr/Permanently delete task 4 'foo'\? \(y\/n\)/, 'confirmation - YES works');
|
||||||
unlike ($output, qr/Task not deleted\./, 'confirmation - YES works');
|
unlike ($output, qr/Task not deleted\./, 'confirmation - YES works');
|
||||||
|
|
||||||
$output = qx{echo "YE" | ../task rc:confirm.rc del 5};
|
$output = qx{echo "YE" | ../task rc:confirm.rc del 5};
|
||||||
like ($output, qr/Permanently delete task\? \(y\/n\)/, 'confirmation - YE works');
|
like ($output, qr/Permanently delete task 5 'foo'\? \(y\/n\)/, 'confirmation - YE works');
|
||||||
unlike ($output, qr/Task not deleted\./, 'confirmation - YE works');
|
unlike ($output, qr/Task not deleted\./, 'confirmation - YE works');
|
||||||
|
|
||||||
$output = qx{echo "Y" | ../task rc:confirm.rc del 6};
|
$output = qx{echo "Y" | ../task rc:confirm.rc del 6};
|
||||||
like ($output, qr/Permanently delete task\? \(y\/n\)/, 'confirmation - Y works');
|
like ($output, qr/Permanently delete task 6 'foo'\? \(y\/n\)/, 'confirmation - Y works');
|
||||||
unlike ($output, qr/Task not deleted\./, 'confirmation - Y works');
|
unlike ($output, qr/Task not deleted\./, 'confirmation - Y works');
|
||||||
|
|
||||||
# Test the various forms of "no".
|
# Test the various forms of "no".
|
||||||
$output = qx{echo "no" | ../task rc:confirm.rc del 7};
|
$output = qx{echo "no" | ../task rc:confirm.rc del 7};
|
||||||
like ($output, qr/Permanently delete task\? \(y\/n\)/, 'confirmation - no works');
|
like ($output, qr/Permanently delete task 7 'foo'\? \(y\/n\)/, 'confirmation - no works');
|
||||||
like ($output, qr/Task not deleted\./, 'confirmation - no works');
|
like ($output, qr/Task not deleted\./, 'confirmation - no works');
|
||||||
|
|
||||||
$output = qx{echo "n" | ../task rc:confirm.rc del 7};
|
$output = qx{echo "n" | ../task rc:confirm.rc del 7};
|
||||||
like ($output, qr/Permanently delete task\? \(y\/n\)/, 'confirmation - n works');
|
like ($output, qr/Permanently delete task 7 'foo'\? \(y\/n\)/, 'confirmation - n works');
|
||||||
like ($output, qr/Task not deleted\./, 'confirmation - n works');
|
like ($output, qr/Task not deleted\./, 'confirmation - n works');
|
||||||
|
|
||||||
$output = qx{echo "NO" | ../task rc:confirm.rc del 7};
|
$output = qx{echo "NO" | ../task rc:confirm.rc del 7};
|
||||||
like ($output, qr/Permanently delete task\? \(y\/n\)/, 'confirmation - NO works');
|
like ($output, qr/Permanently delete task 7 'foo'\? \(y\/n\)/, 'confirmation - NO works');
|
||||||
like ($output, qr/Task not deleted\./, 'confirmation - NO works');
|
like ($output, qr/Task not deleted\./, 'confirmation - NO works');
|
||||||
|
|
||||||
$output = qx{echo "N" | ../task rc:confirm.rc del 7};
|
$output = qx{echo "N" | ../task rc:confirm.rc del 7};
|
||||||
like ($output, qr/Permanently delete task\? \(y\/n\)/, 'confirmation - N works');
|
like ($output, qr/Permanently delete task 7 'foo'\? \(y\/n\)/, 'confirmation - N works');
|
||||||
like ($output, qr/Task not deleted\./, 'confirmation - N works');
|
like ($output, qr/Task not deleted\./, 'confirmation - N works');
|
||||||
|
|
||||||
# Test newlines.
|
# Test newlines.
|
||||||
$output = qx{cat response.txt | ../task rc:confirm.rc del 7};
|
$output = qx{cat response.txt | ../task rc:confirm.rc del 7};
|
||||||
like ($output, qr/(Permanently delete task\? \(y\/n\)) \1 \1/, 'confirmation - \n re-prompt works');
|
like ($output, qr/(Permanently delete task 7 'foo'\? \(y\/n\)) \1 \1/, 'confirmation - \n re-prompt works');
|
||||||
|
|
||||||
# Cleanup.
|
# Cleanup.
|
||||||
unlink 'pending.data';
|
unlink 'pending.data';
|
||||||
|
|
|
@ -57,7 +57,7 @@ like ($output, qr/^No matches/, 'No matches');
|
||||||
ok (-r 'completed.data', 'completed.data created');
|
ok (-r 'completed.data', 'completed.data created');
|
||||||
|
|
||||||
$output = qx{../task rc:undelete.rc undelete 1};
|
$output = qx{../task rc:undelete.rc undelete 1};
|
||||||
like ($output, qr/reliably undeleted/, 'can only be reliable undeleted...');
|
like ($output, qr/Task 1 not found/, 'Task 1 not found');
|
||||||
|
|
||||||
$output = qx{../task rc:undelete.rc info 1};
|
$output = qx{../task rc:undelete.rc info 1};
|
||||||
like ($output, qr/No matches./, 'no matches');
|
like ($output, qr/No matches./, 'no matches');
|
||||||
|
|
134
src/tests/parse.t.cpp
Normal file
134
src/tests/parse.t.cpp
Normal file
|
@ -0,0 +1,134 @@
|
||||||
|
////////////////////////////////////////////////////////////////////////////////
|
||||||
|
// task - a command line task list manager.
|
||||||
|
//
|
||||||
|
// Copyright 2006 - 2009, 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
|
||||||
|
//
|
||||||
|
////////////////////////////////////////////////////////////////////////////////
|
||||||
|
#include <iostream>
|
||||||
|
#include "task.h"
|
||||||
|
#include "test.h"
|
||||||
|
|
||||||
|
////////////////////////////////////////////////////////////////////////////////
|
||||||
|
int main (int argc, char** argv)
|
||||||
|
{
|
||||||
|
UnitTest t (18);
|
||||||
|
|
||||||
|
std::vector <std::string> args;
|
||||||
|
std::string command;
|
||||||
|
|
||||||
|
Config conf;
|
||||||
|
conf.set ("dateformat", "m/d/Y");
|
||||||
|
|
||||||
|
{
|
||||||
|
T task;
|
||||||
|
split (args, "add foo", ' ');
|
||||||
|
parse (args, command, task, conf);
|
||||||
|
t.is (command, "add", "(1) command found");
|
||||||
|
t.is (task.getId (), 0, "(1) zero id on add");
|
||||||
|
t.is (task.getDescription (), "foo", "(1) correct description");
|
||||||
|
}
|
||||||
|
|
||||||
|
{
|
||||||
|
T task;
|
||||||
|
split (args, "delete 1,3-5,7", ' ');
|
||||||
|
parse (args, command, task, conf);
|
||||||
|
std::vector <int> sequence = task.getAllIds ();
|
||||||
|
t.is (sequence.size (), (size_t)5, "(2) sequence length");
|
||||||
|
if (sequence.size () == 5)
|
||||||
|
{
|
||||||
|
t.is (sequence[0], 1, "(2) sequence[0] == 1");
|
||||||
|
t.is (sequence[1], 3, "(2) sequence[1] == 3");
|
||||||
|
t.is (sequence[2], 4, "(2) sequence[2] == 4");
|
||||||
|
t.is (sequence[3], 5, "(2) sequence[3] == 5");
|
||||||
|
t.is (sequence[4], 7, "(2) sequence[4] == 7");
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
t.fail ("(2) sequence[0] == 1");
|
||||||
|
t.fail ("(2) sequence[1] == 3");
|
||||||
|
t.fail ("(2) sequence[2] == 4");
|
||||||
|
t.fail ("(2) sequence[3] == 5");
|
||||||
|
t.fail ("(2) sequence[4] == 7");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
{
|
||||||
|
T task;
|
||||||
|
split (args, "delete 1,2 3,4", ' ');
|
||||||
|
parse (args, command, task, conf);
|
||||||
|
std::vector <int> sequence = task.getAllIds ();
|
||||||
|
t.is (sequence.size (), (size_t)4, "(3) sequence length");
|
||||||
|
if (sequence.size () == 4)
|
||||||
|
{
|
||||||
|
t.is (sequence[0], 1, "(3) sequence[0] == 1");
|
||||||
|
t.is (sequence[1], 2, "(3) sequence[1] == 2");
|
||||||
|
t.is (sequence[2], 3, "(3) sequence[2] == 3");
|
||||||
|
t.is (sequence[3], 4, "(3) sequence[3] == 4");
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
t.fail ("(3) sequence[0] == 1");
|
||||||
|
t.fail ("(3) sequence[1] == 2");
|
||||||
|
t.fail ("(3) sequence[2] == 3");
|
||||||
|
t.fail ("(3) sequence[3] == 4");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
{
|
||||||
|
T task;
|
||||||
|
split (args, "1 There are 7 days in a week", ' ');
|
||||||
|
parse (args, command, task, conf);
|
||||||
|
std::vector <int> sequence = task.getAllIds ();
|
||||||
|
t.is (sequence.size (), (size_t)1, "(4) sequence length");
|
||||||
|
if (sequence.size () == 1)
|
||||||
|
{
|
||||||
|
t.is (sequence[0], 1, "(4) sequence[0] == 1");
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
t.fail ("(4) sequence[0] == 1");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
{
|
||||||
|
T task;
|
||||||
|
args.clear ();
|
||||||
|
args.push_back ("1");
|
||||||
|
args.push_back ("4-123 is back-ordered");
|
||||||
|
parse (args, command, task, conf);
|
||||||
|
std::vector <int> sequence = task.getAllIds ();
|
||||||
|
t.is (sequence.size (), (size_t)1, "(5) sequence length");
|
||||||
|
if (sequence.size () == 1)
|
||||||
|
{
|
||||||
|
t.is (sequence[0], 1, "(5) sequence[0] == 1");
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
t.fail ("(5) sequence[0] == 1");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
////////////////////////////////////////////////////////////////////////////////
|
||||||
|
|
|
@ -28,7 +28,7 @@
|
||||||
|
|
||||||
use strict;
|
use strict;
|
||||||
use warnings;
|
use warnings;
|
||||||
use Test::More tests => 7;
|
use Test::More tests => 9;
|
||||||
|
|
||||||
# Create the rc file.
|
# Create the rc file.
|
||||||
if (open my $fh, '>', 'subst.rc')
|
if (open my $fh, '>', 'subst.rc')
|
||||||
|
@ -58,6 +58,16 @@ qx{../task rc:subst.rc 1 /bar/BAR/g};
|
||||||
$output = qx{../task rc:subst.rc info 1};
|
$output = qx{../task rc:subst.rc info 1};
|
||||||
like ($output, qr/BAR BAR BAR/, 'global substitution in annotation');
|
like ($output, qr/BAR BAR BAR/, 'global substitution in annotation');
|
||||||
|
|
||||||
|
qx{../task rc:subst.rc 1 /FOO/aaa/};
|
||||||
|
qx{../task rc:subst.rc 1 /FOO/bbb/};
|
||||||
|
qx{../task rc:subst.rc 1 /FOO/ccc/};
|
||||||
|
$output = qx{../task rc:subst.rc info 1};
|
||||||
|
like ($output, qr/aaa bbb ccc/, 'individual successive substitution in description');
|
||||||
|
|
||||||
|
qx{../task rc:subst.rc 1 /bbb//};
|
||||||
|
$output = qx{../task rc:subst.rc info 1};
|
||||||
|
like ($output, qr/aaa ccc/, 'word deletion in description');
|
||||||
|
|
||||||
# Cleanup.
|
# Cleanup.
|
||||||
unlink 'pending.data';
|
unlink 'pending.data';
|
||||||
ok (!-r 'pending.data', 'Removed pending.data');
|
ok (!-r 'pending.data', 'Removed pending.data');
|
||||||
|
|
|
@ -56,8 +56,8 @@ $output = qx{../task rc:undo.rc do 1; ../task rc:undo.rc list};
|
||||||
like ($output, qr/^No matches/, 'No matches');
|
like ($output, qr/^No matches/, 'No matches');
|
||||||
|
|
||||||
$output = qx{../task rc:undo.rc undo 1; ../task rc:undo.rc info 1};
|
$output = qx{../task rc:undo.rc undo 1; ../task rc:undo.rc info 1};
|
||||||
like ($output, qr/Task 1 not found/, 'task not found');
|
like ($output, qr/Task 1 not found/, 'Task 1 not found');
|
||||||
like ($output, qr/reliably undone/, 'can only be reliable undone...');
|
like ($output, qr/No matches/, 'No matches');
|
||||||
|
|
||||||
# Cleanup.
|
# Cleanup.
|
||||||
ok (-r 'pending.data', 'Need to remove pending.data');
|
ok (-r 'pending.data', 'Need to remove pending.data');
|
||||||
|
|
Loading…
Add table
Add a link
Reference in a new issue