mirror of
https://github.com/GothenburgBitFactory/taskwarrior.git
synced 2025-06-26 10:54:26 +02:00
[WIP] make 'waiting' status a 'virtual' status
This commit is contained in:
parent
20041c120e
commit
489a57542b
7 changed files with 50 additions and 32 deletions
|
@ -1,5 +1,12 @@
|
|||
2.6.0 () -
|
||||
|
||||
- TW #2554 Waiting is now an entirely "virtual" concept, based on a task's
|
||||
'wait' property and the current time. This is reflected in the +WAITING
|
||||
tag, and in the now-deprecated `waiting` status. Please upgrade filters
|
||||
and other automation to use `+WAITING` or `wait.after:now` instead of
|
||||
`status:waiting`, as support will be dropped in a future version.
|
||||
TaskWarrior no longer explicitly "unwaits" a task, so the "unwait' verbosity
|
||||
token is no longer available.
|
||||
- TW #1654 "Due" parsing behaviour seems inconsistent
|
||||
Thanks to Max Rossmannek.
|
||||
- TW #1788 When deleting recurring task all tasks, including completed tasks,
|
||||
|
|
|
@ -299,11 +299,10 @@ control specific occasions when output is generated. This list may contain:
|
|||
sync Feedback about sync
|
||||
filter Shows the filter used in the command
|
||||
context Show the current context. Displayed in footnote.
|
||||
unwait Notification when a task leaves the 'waiting' state
|
||||
override Notification when configuration options are overridden
|
||||
recur Notification when a new recurring task instance is created
|
||||
|
||||
"affected", "new-id", "new-uuid", "project", "unwait", "override" and "recur"
|
||||
"affected", "new-id", "new-uuid", "project", "override" and "recur"
|
||||
imply "footnote".
|
||||
|
||||
Note that the "1" setting is equivalent to all the tokens being specified,
|
||||
|
@ -312,7 +311,7 @@ and the "nothing" setting is equivalent to none of the tokens being specified.
|
|||
Here are the shortcut equivalents:
|
||||
|
||||
verbose=on
|
||||
verbose=blank,header,footnote,label,new-id,affected,edit,special,project,sync,filter,unwait,override,recur
|
||||
verbose=blank,header,footnote,label,new-id,affected,edit,special,project,sync,filter,override,recur
|
||||
|
||||
verbose=0
|
||||
verbose=blank,label,new-id,edit
|
||||
|
|
|
@ -87,7 +87,7 @@ std::string configurationDefaults =
|
|||
"\n"
|
||||
"# Miscellaneous\n"
|
||||
"# # Comma-separated list. May contain any subset of:\n"
|
||||
"verbose=blank,header,footnote,label,new-id,affected,edit,special,project,sync,filter,unwait,override,recur\n"
|
||||
"verbose=blank,header,footnote,label,new-id,affected,edit,special,project,sync,filter,override,recur\n"
|
||||
"confirmation=1 # Confirmation on delete, big changes\n"
|
||||
"recurrence=1 # Enable recurrence\n"
|
||||
"recurrence.confirmation=prompt # Confirmation for propagating changes among recurring tasks (yes/no/prompt)\n"
|
||||
|
@ -1029,7 +1029,6 @@ bool Context::verbose (const std::string& token)
|
|||
v != "project" && //
|
||||
v != "sync" && //
|
||||
v != "filter" && //
|
||||
v != "unwait" && //
|
||||
v != "override" && //
|
||||
v != "context" && //
|
||||
v != "recur") //
|
||||
|
@ -1043,7 +1042,7 @@ bool Context::verbose (const std::string& token)
|
|||
if (! verbosity.count ("footnote"))
|
||||
{
|
||||
// TODO: Some of these may not use footnotes yet. They should.
|
||||
for (auto flag : {"affected", "new-id", "new-uuid", "project", "unwait", "override", "recur"})
|
||||
for (auto flag : {"affected", "new-id", "new-uuid", "project", "override", "recur"})
|
||||
{
|
||||
if (verbosity.count (flag))
|
||||
{
|
||||
|
|
18
src/TDB2.cpp
18
src/TDB2.cpp
|
@ -355,23 +355,6 @@ void TF2::load_gc (Task& task)
|
|||
{
|
||||
Context::getContext ().tdb2.pending._tasks.push_back (task);
|
||||
}
|
||||
else if (status == "waiting")
|
||||
{
|
||||
Datetime wait (task.get_date ("wait"));
|
||||
if (wait < now)
|
||||
{
|
||||
task.set ("status", "pending");
|
||||
task.remove ("wait");
|
||||
// Unwaiting pending tasks is the only case not caught by the size()
|
||||
// checks in TDB2::gc(), so we need to signal it here.
|
||||
Context::getContext ().tdb2.pending._dirty = true;
|
||||
|
||||
if (Context::getContext ().verbose ("unwait"))
|
||||
Context::getContext ().footnote (format ("Un-waiting task {1} '{2}'", task.id, task.get ("description")));
|
||||
}
|
||||
|
||||
Context::getContext ().tdb2.pending._tasks.push_back (task);
|
||||
}
|
||||
else
|
||||
{
|
||||
Context::getContext ().tdb2.completed._tasks.push_back (task);
|
||||
|
@ -1249,7 +1232,6 @@ void TDB2::show_diff (
|
|||
// Possible scenarios:
|
||||
// - task in pending that needs to be in completed
|
||||
// - task in completed that needs to be in pending
|
||||
// - waiting task in pending that needs to be un-waited
|
||||
void TDB2::gc ()
|
||||
{
|
||||
Timer timer;
|
||||
|
|
40
src/Task.cpp
40
src/Task.cpp
|
@ -147,7 +147,9 @@ Task::status Task::textToStatus (const std::string& input)
|
|||
else if (input[0] == 'c') return Task::completed;
|
||||
else if (input[0] == 'd') return Task::deleted;
|
||||
else if (input[0] == 'r') return Task::recurring;
|
||||
else if (input[0] == 'w') return Task::waiting;
|
||||
// for compatibility, parse `w` as pending; Task::getStatus will
|
||||
// apply the virtual waiting status if appropriate
|
||||
else if (input[0] == 'w') return Task::pending;
|
||||
|
||||
throw format ("The status '{1}' is not valid.", input);
|
||||
}
|
||||
|
@ -301,12 +303,25 @@ Task::status Task::getStatus () const
|
|||
if (! has ("status"))
|
||||
return Task::pending;
|
||||
|
||||
return textToStatus (get ("status"));
|
||||
auto status = textToStatus (get ("status"));
|
||||
|
||||
// implement the "virtual" Task::waiting status, which is not stored on-disk
|
||||
// but is defined as a pending task with a `wait` attribute in the future.
|
||||
if (status == Task::pending && is_waiting ()) {
|
||||
return Task::waiting;
|
||||
}
|
||||
|
||||
return status;
|
||||
}
|
||||
|
||||
////////////////////////////////////////////////////////////////////////////////
|
||||
void Task::setStatus (Task::status status)
|
||||
{
|
||||
// the 'waiting' status is a virtual version of 'pending', so translate
|
||||
// that back to 'pending' here
|
||||
if (status == Task::waiting)
|
||||
status = Task::pending;
|
||||
|
||||
set ("status", statusToText (status));
|
||||
|
||||
recalc_urgency = true;
|
||||
|
@ -559,6 +574,23 @@ bool Task::is_overdue () const
|
|||
}
|
||||
#endif
|
||||
|
||||
////////////////////////////////////////////////////////////////////////////////
|
||||
bool Task::is_waiting () const
|
||||
{
|
||||
// note that is_waiting can return true for tasks in an actual status other
|
||||
// than pending; in this case +WAITING will be set but the status will not be
|
||||
// "waiting"
|
||||
if (has ("wait"))
|
||||
{
|
||||
Datetime now;
|
||||
Datetime wait (get_date ("wait"));
|
||||
if (wait > now)
|
||||
return true;
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
////////////////////////////////////////////////////////////////////////////////
|
||||
// Attempt an FF4 parse first, using Task::parse, and in the event of an error
|
||||
// try a JSON parse, otherwise a legacy parse (currently no legacy formats are
|
||||
|
@ -1311,7 +1343,7 @@ bool Task::hasTag (const std::string& tag) const
|
|||
if (tag == "TAGGED") return getTagCount() > 0;
|
||||
if (tag == "PARENT") return has ("mask") || has ("last"); // 2017-01-07: Deprecated in 2.6.0
|
||||
if (tag == "TEMPLATE") return has ("last") || has ("mask");
|
||||
if (tag == "WAITING") return get ("status") == "waiting";
|
||||
if (tag == "WAITING") return is_waiting ();
|
||||
if (tag == "PENDING") return get ("status") == "pending";
|
||||
if (tag == "COMPLETED") return get ("status") == "completed";
|
||||
if (tag == "DELETED") return get ("status") == "deleted";
|
||||
|
@ -2048,7 +2080,7 @@ float Task::urgency_scheduled () const
|
|||
////////////////////////////////////////////////////////////////////////////////
|
||||
float Task::urgency_waiting () const
|
||||
{
|
||||
if (get_ref ("status") == "waiting")
|
||||
if (is_waiting ())
|
||||
return 1.0;
|
||||
|
||||
return 0.0;
|
||||
|
|
|
@ -114,6 +114,7 @@ public:
|
|||
bool is_udaPresent () const;
|
||||
bool is_orphanPresent () const;
|
||||
#endif
|
||||
bool is_waiting () const;
|
||||
|
||||
status getStatus () const;
|
||||
void setStatus (status);
|
||||
|
|
|
@ -62,8 +62,6 @@ class TestWait(TestCase):
|
|||
self.assertIn("visible", out)
|
||||
self.assertIn("hidden", out)
|
||||
|
||||
self.assertIn("Un-waiting task 2 'hidden'", err)
|
||||
|
||||
|
||||
class TestBug434(TestCase):
|
||||
# Bug #434: Task should not prevent users from marking as done tasks with
|
||||
|
@ -106,7 +104,7 @@ class TestFeature2322(TestCase):
|
|||
self.t = Task()
|
||||
|
||||
def test_done_unwait(self):
|
||||
"""2322: Done should un-wait a waiting task"""
|
||||
"""2322: Done should remove the wait attribute"""
|
||||
self.t("add foo wait:tomorrow")
|
||||
code, out, err = self.t("export")
|
||||
self.assertIn('"wait":', out)
|
||||
|
@ -116,7 +114,7 @@ class TestFeature2322(TestCase):
|
|||
self.assertNotIn('"wait":', out)
|
||||
|
||||
def test_delete_unwait(self):
|
||||
"""2322: Deleteion should un-wait a waiting task"""
|
||||
"""2322: Delete should remove the wait attribute"""
|
||||
self.t("add bar wait:tomorrow")
|
||||
code, out, err = self.t("export")
|
||||
self.assertIn('"wait":', out)
|
||||
|
|
Loading…
Add table
Add a link
Reference in a new issue