refactor: revamp visible task algorithm
This commit is contained in:
parent
5cd82e8581
commit
bd32e61212
237
src/tasks.rs
237
src/tasks.rs
|
@ -542,16 +542,55 @@ impl TasksRelay {
|
|||
current
|
||||
}
|
||||
|
||||
// TODO this is a relict for tests
|
||||
#[deprecated]
|
||||
fn visible_tasks(&self) -> Vec<&Task> {
|
||||
if self.search_depth == 0 {
|
||||
return vec![];
|
||||
let tasks = self.viewed_tasks();
|
||||
if self.view.is_empty() && !tasks.is_empty() {
|
||||
let bookmarks = self.bookmarked_tasks_deduped(&tasks);
|
||||
return bookmarks.chain(tasks.into_iter()).collect_vec();
|
||||
}
|
||||
if !self.view.is_empty() {
|
||||
return self.view.iter().flat_map(|id| self.get_by_id(id)).collect();
|
||||
tasks
|
||||
}
|
||||
|
||||
fn viewed_tasks(&self) -> Vec<&Task> {
|
||||
let view = self.view.iter()
|
||||
.flat_map(|id| self.get_by_id(id))
|
||||
.collect_vec();
|
||||
if self.search_depth > 0 && view.is_empty() {
|
||||
self.resolve_tasks_rec(
|
||||
self.tasks.children_for(self.get_position()),
|
||||
true,
|
||||
self.search_depth + self.view_depth,
|
||||
)
|
||||
} else {
|
||||
self.resolve_tasks_rec(view.into_iter(), true, self.view_depth)
|
||||
}
|
||||
self.filtered_tasks(self.get_position(), true)
|
||||
}
|
||||
|
||||
fn bookmarked_tasks_deduped(&self, visible: &[&Task]) -> impl Iterator<Item=&Task> {
|
||||
let tree = visible.iter()
|
||||
.flat_map(|task| self.traverse_up_from(Some(task.event.id)))
|
||||
.unique();
|
||||
let pos = self.get_position();
|
||||
let ids: HashSet<&EventId> = tree.map(|t| t.get_id()).chain(pos.as_ref()).collect();
|
||||
// TODO add recent tasks (most time tracked + recently created)
|
||||
self.bookmarks.iter()
|
||||
.chain(
|
||||
// Latest
|
||||
self.tasks.values()
|
||||
.sorted_unstable().rev()
|
||||
.take(3).map(|t| t.get_id()))
|
||||
.chain(
|
||||
// Highest Prio
|
||||
self.tasks.values()
|
||||
.filter_map(|t| t.priority().filter(|p| *p > 35).map(|p| (p, t)))
|
||||
.sorted_unstable()
|
||||
.take(3).map(|(_, t)| t.get_id())
|
||||
)
|
||||
.filter(|id| !ids.contains(id))
|
||||
.filter_map(|id| self.get_by_id(id))
|
||||
.filter(|t| self.filter(t))
|
||||
.sorted_by_cached_key(|t| self.sorting_key(t))
|
||||
.dedup()
|
||||
}
|
||||
|
||||
fn get_property(&self, task: &Task, str: &str) -> String {
|
||||
|
@ -978,7 +1017,7 @@ impl TasksRelay {
|
|||
fn context_hashtags(&self) -> impl Iterator<Item=Tag> + use<'_> {
|
||||
self.tags.iter().map(Tag::from)
|
||||
}
|
||||
|
||||
|
||||
/// Creates a task following the current state
|
||||
///
|
||||
/// Sanitizes input
|
||||
|
@ -1274,12 +1313,12 @@ impl TasksRelay {
|
|||
MARKER_PROPERTY
|
||||
} else {
|
||||
// Activity if parent is not a task
|
||||
prop = prop.add_tags(self.context_hashtags());
|
||||
prop = prop.tags(self.context_hashtags());
|
||||
MARKER_PARENT
|
||||
};
|
||||
info!("Created {} {format}", if marker == MARKER_PROPERTY { "note" } else { "activity" } );
|
||||
self.submit(
|
||||
prop.add_tags(
|
||||
prop.tags(
|
||||
self.get_position().map(|pos| self.make_event_tag_from_id(pos, marker))))
|
||||
}
|
||||
|
||||
|
@ -1343,35 +1382,10 @@ impl Display for TasksRelay {
|
|||
writeln!(lock)?;
|
||||
}
|
||||
|
||||
let position = self.get_position();
|
||||
let mut current: Vec<&Task>;
|
||||
let view = self.view.iter()
|
||||
.flat_map(|id| self.get_by_id(id))
|
||||
.collect_vec();
|
||||
if self.search_depth > 0 && view.is_empty() {
|
||||
current = self.resolve_tasks_rec(
|
||||
self.tasks.children_for(position),
|
||||
true,
|
||||
self.search_depth + self.view_depth,
|
||||
);
|
||||
if current.is_empty() {
|
||||
if !self.tags.is_empty() {
|
||||
let mut children = self.tasks.children_for(position).peekable();
|
||||
if children.peek().is_some() {
|
||||
current = self.resolve_tasks_rec(children, true, 9);
|
||||
if current.is_empty() {
|
||||
writeln!(lock, "No tasks here matching{}", self.get_prompt_suffix())?;
|
||||
} else {
|
||||
writeln!(lock, "Found matching tasks beyond specified search depth:")?;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
} else {
|
||||
current = self.resolve_tasks_rec(view.into_iter(), true, self.view_depth);
|
||||
}
|
||||
let visible = self.viewed_tasks();
|
||||
|
||||
if current.is_empty() {
|
||||
if visible.is_empty() {
|
||||
writeln!(lock, "No tasks here matching{}", self.get_prompt_suffix())?;
|
||||
let (label, times) = self.times_tracked();
|
||||
let mut times_recent = times.rev().take(6).collect_vec();
|
||||
times_recent.reverse();
|
||||
|
@ -1380,32 +1394,8 @@ impl Display for TasksRelay {
|
|||
return Ok(());
|
||||
}
|
||||
|
||||
let tree = current.iter()
|
||||
.flat_map(|task| self.traverse_up_from(Some(task.event.id)))
|
||||
.unique();
|
||||
let ids: HashSet<&EventId> = tree.map(|t| t.get_id()).chain(position.as_ref()).collect();
|
||||
if self.view.is_empty() {
|
||||
let mut bookmarks =
|
||||
// TODO add recent tasks (most time tracked + recently created)
|
||||
self.bookmarks.iter()
|
||||
.chain(
|
||||
// Latest
|
||||
self.tasks.values()
|
||||
.sorted_unstable().rev()
|
||||
.take(3).map(|t| t.get_id()))
|
||||
.chain(
|
||||
// Highest Prio
|
||||
self.tasks.values()
|
||||
.filter_map(|t| t.priority().filter(|p| *p > 35).map(|p| (p, t)))
|
||||
.sorted_unstable()
|
||||
.take(3).map(|(_, t)| t.get_id())
|
||||
)
|
||||
.filter(|id| !ids.contains(id))
|
||||
.filter_map(|id| self.get_by_id(id))
|
||||
.filter(|t| self.filter(t))
|
||||
.sorted_by_cached_key(|t| self.sorting_key(t))
|
||||
.dedup()
|
||||
.peekable();
|
||||
let mut bookmarks = self.bookmarked_tasks_deduped(&visible).peekable();
|
||||
if bookmarks.peek().is_some() {
|
||||
writeln!(lock, "{}", Colorize::bold("Quick Access"))?;
|
||||
for task in bookmarks {
|
||||
|
@ -1424,9 +1414,9 @@ impl Display for TasksRelay {
|
|||
// TODO hide empty columns
|
||||
writeln!(lock, "{}", self.properties.join(" \t").bold())?;
|
||||
|
||||
let count = current.len();
|
||||
let count = visible.len();
|
||||
let mut total_time = 0;
|
||||
for task in current {
|
||||
for task in visible {
|
||||
writeln!(
|
||||
lock,
|
||||
"{}",
|
||||
|
@ -1781,7 +1771,7 @@ mod tasks_test {
|
|||
};
|
||||
}
|
||||
|
||||
macro_rules! assert_tasks {
|
||||
macro_rules! assert_tasks_visible {
|
||||
($left:expr, $right:expr $(,)?) => {
|
||||
assert_eq!(
|
||||
$left
|
||||
|
@ -1794,6 +1784,22 @@ mod tasks_test {
|
|||
};
|
||||
}
|
||||
|
||||
macro_rules! assert_tasks_view {
|
||||
($left:expr, $right:expr $(,)?) => {
|
||||
let tasks = $left.viewed_tasks();
|
||||
assert_eq!(
|
||||
tasks
|
||||
.iter()
|
||||
.map(|t| t.event.id)
|
||||
.collect::<HashSet<EventId>>(),
|
||||
HashSet::from($right),
|
||||
"Tasks Visible: {:?}\nExpected: {:?}",
|
||||
tasks,
|
||||
$right.map(|id| $left.get_relative_path(id))
|
||||
)
|
||||
};
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_recursive_closing() {
|
||||
let mut tasks = stub_tasks();
|
||||
|
@ -1819,13 +1825,31 @@ mod tasks_test {
|
|||
assert_eq!(tasks.nonclosed_tasks().next(), None);
|
||||
assert_eq!(tasks.all_hashtags(), Default::default());
|
||||
}
|
||||
|
||||
|
||||
#[test]
|
||||
fn test_tags() {
|
||||
fn test_context() {
|
||||
let mut tasks = stub_tasks();
|
||||
tasks.update_tags(["dp", "yeah"].into_iter().map(Hashtag::from));
|
||||
assert_eq!(tasks.get_prompt_suffix(), " #dp #yeah");
|
||||
tasks.remove_tag("Y");
|
||||
assert_eq!(tasks.tags, ["dp"].into_iter().map(Hashtag::from).collect());
|
||||
tasks.set_priority(Some(HIGH_PRIO));
|
||||
assert_eq!(tasks.get_prompt_suffix(), " #dp *85");
|
||||
let id = tasks.make_task("test # tag");
|
||||
let task1 = tasks.get_by_id(&id).unwrap();
|
||||
assert_eq!(task1.priority(), Some(HIGH_PRIO));
|
||||
assert_eq!(
|
||||
task1.list_hashtags().collect_vec(),
|
||||
vec!["DP", "tag"].into_iter().map(Hashtag::from).collect_vec()
|
||||
);
|
||||
tasks.make_task_and_enter("another *4", State::Pending);
|
||||
assert_eq!(tasks.get_current_task().unwrap().priority(), Some(40));
|
||||
tasks.make_note("*3");
|
||||
let task2 = tasks.get_current_task().unwrap();
|
||||
assert_eq!(task2.descriptions().next(), None);
|
||||
assert_eq!(task2.priority(), Some(30));
|
||||
|
||||
tasks.pubkey = Some(Keys::generate().public_key);
|
||||
}
|
||||
|
||||
#[test]
|
||||
|
@ -1836,13 +1860,13 @@ mod tasks_test {
|
|||
EventBuilder::new(TASK_KIND, "sub")
|
||||
.tags([tasks.make_event_tag_from_id(parent, MARKER_PARENT)])
|
||||
);
|
||||
assert_eq!(tasks.visible_tasks().len(), 1);
|
||||
assert_eq!(tasks.viewed_tasks().len(), 1);
|
||||
tasks.track_at(Timestamp::now(), Some(sub));
|
||||
assert_eq!(tasks.get_own_events_history().count(), 1);
|
||||
|
||||
tasks.make_dependent_sibling("sibling");
|
||||
assert_eq!(tasks.len(), 3);
|
||||
assert_eq!(tasks.visible_tasks().len(), 2);
|
||||
assert_eq!(tasks.viewed_tasks().len(), 2);
|
||||
}
|
||||
|
||||
#[test]
|
||||
|
@ -1851,7 +1875,7 @@ mod tasks_test {
|
|||
let zero = EventId::all_zeros();
|
||||
let test = tasks.make_task("test # tag");
|
||||
let parent = tasks.make_task("parent");
|
||||
assert_eq!(tasks.visible_tasks().len(), 2);
|
||||
assert_eq!(tasks.viewed_tasks().len(), 2);
|
||||
tasks.move_to(Some(parent));
|
||||
let pin = tasks.make_task("pin");
|
||||
|
||||
|
@ -1867,7 +1891,7 @@ mod tasks_test {
|
|||
EventBuilder::new(Kind::Bookmarks, "")
|
||||
.tags([Tag::event(pin), Tag::event(zero)])
|
||||
);
|
||||
assert_eq!(tasks.visible_tasks().len(), 1);
|
||||
assert_eq!(tasks.viewed_tasks().len(), 1);
|
||||
assert_eq!(tasks.filtered_tasks(Some(pin), true).len(), 0);
|
||||
assert_eq!(tasks.filtered_tasks(Some(pin), false).len(), 0);
|
||||
assert_eq!(tasks.filtered_tasks(Some(zero), true).len(), 0);
|
||||
|
@ -1878,11 +1902,11 @@ mod tasks_test {
|
|||
|
||||
tasks.move_to(None);
|
||||
assert_eq!(tasks.view_depth, 0);
|
||||
assert_tasks!(tasks, [pin, test, parent]);
|
||||
assert_tasks_visible!(tasks, [pin, test, parent]);
|
||||
tasks.set_view_depth(1);
|
||||
assert_tasks!(tasks, [pin, test]);
|
||||
assert_tasks_visible!(tasks, [pin, test]);
|
||||
tasks.add_tag("tag");
|
||||
assert_tasks!(tasks, [test]);
|
||||
assert_tasks_visible!(tasks, [test]);
|
||||
assert_eq!(
|
||||
tasks.filtered_tasks(None, true),
|
||||
vec![tasks.get_by_id(&test).unwrap()]
|
||||
|
@ -1890,9 +1914,9 @@ mod tasks_test {
|
|||
|
||||
tasks.submit(EventBuilder::new(Kind::Bookmarks, ""));
|
||||
tasks.clear_filters();
|
||||
assert_tasks!(tasks, [pin, test]);
|
||||
assert_tasks_visible!(tasks, [pin, test]);
|
||||
tasks.set_view_depth(0);
|
||||
assert_tasks!(tasks, [test, parent]);
|
||||
assert_tasks_visible!(tasks, [test, parent]);
|
||||
}
|
||||
|
||||
#[test]
|
||||
|
@ -1904,9 +1928,9 @@ mod tasks_test {
|
|||
EventBuilder::new(TASK_KIND, "side")
|
||||
.tags([tasks.make_event_tag(&tasks.get_current_task().unwrap().event, MARKER_DEPENDS)])
|
||||
);
|
||||
assert_eq!(tasks.visible_tasks(), Vec::<&Task>::new());
|
||||
assert_eq!(tasks.viewed_tasks(), Vec::<&Task>::new());
|
||||
let sub_id = tasks.make_task("sub");
|
||||
assert_tasks!(tasks, [sub_id]);
|
||||
assert_tasks_view!(tasks, [sub_id]);
|
||||
assert_eq!(tasks.len(), 3);
|
||||
let sub = tasks.get_by_id(&sub_id).unwrap();
|
||||
assert_eq!(sub.get_dependendees(), Vec::<&EventId>::new());
|
||||
|
@ -1920,20 +1944,20 @@ mod tasks_test {
|
|||
|
||||
let id1 = tasks.filter_or_create(zero, "newer");
|
||||
assert_eq!(tasks.len(), 1);
|
||||
assert_eq!(tasks.visible_tasks().len(), 0);
|
||||
assert_eq!(tasks.viewed_tasks().len(), 0);
|
||||
assert_eq!(tasks.get_by_id(&id1.unwrap()).unwrap().parent_id(), zero.as_ref());
|
||||
|
||||
tasks.move_to(zero);
|
||||
assert_eq!(tasks.visible_tasks().len(), 1);
|
||||
assert_eq!(tasks.viewed_tasks().len(), 1);
|
||||
let sub = tasks.make_task("test");
|
||||
assert_eq!(tasks.len(), 2);
|
||||
assert_eq!(tasks.visible_tasks().len(), 2);
|
||||
assert_eq!(tasks.viewed_tasks().len(), 2);
|
||||
assert_eq!(tasks.get_by_id(&sub).unwrap().parent_id(), zero.as_ref());
|
||||
|
||||
// Do not substring match invisible subtask
|
||||
let id2 = tasks.filter_or_create(None, "#new-is gold wrapped").unwrap();
|
||||
assert_eq!(tasks.len(), 3);
|
||||
assert_eq!(tasks.visible_tasks().len(), 2);
|
||||
assert_eq!(tasks.viewed_tasks().len(), 2);
|
||||
let new2 = tasks.get_by_id(&id2).unwrap();
|
||||
assert_eq!(new2.props, Default::default());
|
||||
|
||||
|
@ -2009,32 +2033,32 @@ mod tasks_test {
|
|||
assert_eq!(tasks.view_depth, 0);
|
||||
assert_eq!(activity_t1.pure_state(), State::Open);
|
||||
debug!("{:?}", tasks);
|
||||
assert_eq!(tasks.visible_tasks().len(), 1);
|
||||
assert_eq!(tasks.viewed_tasks().len(), 1);
|
||||
tasks.search_depth = 0;
|
||||
assert_eq!(tasks.visible_tasks().len(), 0);
|
||||
assert_eq!(tasks.viewed_tasks().len(), 0);
|
||||
tasks.recurse_activities = false;
|
||||
assert_eq!(tasks.filtered_tasks(None, false).len(), 1);
|
||||
|
||||
tasks.move_to(Some(t1));
|
||||
assert_position!(tasks, t1);
|
||||
tasks.search_depth = 2;
|
||||
assert_eq!(tasks.visible_tasks().len(), 0);
|
||||
assert_eq!(tasks.viewed_tasks().len(), 0);
|
||||
let t11 = tasks.make_task("t11 # tag");
|
||||
assert_eq!(tasks.visible_tasks().len(), 1);
|
||||
assert_eq!(tasks.viewed_tasks().len(), 1);
|
||||
assert_eq!(tasks.get_task_path(Some(t11)), "t1>t11");
|
||||
assert_eq!(tasks.get_relative_path(t11), "t11");
|
||||
let t12 = tasks.make_task("t12");
|
||||
assert_eq!(tasks.visible_tasks().len(), 2);
|
||||
assert_eq!(tasks.viewed_tasks().len(), 2);
|
||||
|
||||
tasks.move_to(Some(t11));
|
||||
assert_position!(tasks, t11);
|
||||
assert_eq!(tasks.visible_tasks().len(), 0);
|
||||
assert_eq!(tasks.viewed_tasks().len(), 0);
|
||||
let t111 = tasks.make_task("t111");
|
||||
assert_tasks!(tasks, [t111]);
|
||||
assert_tasks_view!(tasks, [t111]);
|
||||
assert_eq!(tasks.get_task_path(Some(t111)), "t1>t11>t111");
|
||||
assert_eq!(tasks.get_relative_path(t111), "t111");
|
||||
tasks.view_depth = 2;
|
||||
assert_tasks!(tasks, [t111]);
|
||||
assert_tasks_view!(tasks, [t111]);
|
||||
|
||||
assert_eq!(ChildIterator::from(&tasks, EventId::all_zeros()).get_all().len(), 1);
|
||||
assert_eq!(ChildIterator::from(&tasks, EventId::all_zeros()).get_depth(0).len(), 1);
|
||||
|
@ -2049,32 +2073,37 @@ mod tasks_test {
|
|||
assert_eq!(tasks.get_own_events_history().count(), 3);
|
||||
assert_eq!(tasks.get_relative_path(t111), "t11>t111");
|
||||
assert_eq!(tasks.view_depth, 2);
|
||||
assert_tasks!(tasks, [t111, t12]);
|
||||
tasks.set_view(vec![t11]);
|
||||
assert_tasks!(tasks, [t11]); // No more depth applied to view
|
||||
tasks.set_search_depth(1); // resets view
|
||||
assert_tasks!(tasks, [t111, t12]);
|
||||
tasks.set_search_depth(1);
|
||||
assert_tasks_view!(tasks, [t111, t12]);
|
||||
tasks.set_view_depth(0);
|
||||
assert_tasks!(tasks, [t11, t12]);
|
||||
assert_tasks_view!(tasks, [t11, t12]);
|
||||
tasks.set_view(vec![t11]);
|
||||
assert_tasks_view!(tasks, [t11]);
|
||||
tasks.set_view_depth(1);
|
||||
assert_tasks_view!(tasks, [t111]);
|
||||
tasks.set_search_depth(1); // resets view
|
||||
assert_tasks_view!(tasks, [t111, t12]);
|
||||
tasks.set_view_depth(0);
|
||||
assert_tasks_view!(tasks, [t11, t12]);
|
||||
|
||||
tasks.move_to(None);
|
||||
tasks.recurse_activities = true;
|
||||
assert_tasks!(tasks, [t11, t12]);
|
||||
assert_tasks_view!(tasks, [t11, t12]);
|
||||
tasks.recurse_activities = false;
|
||||
assert_tasks!(tasks, [t1]);
|
||||
assert_tasks_view!(tasks, [t1]);
|
||||
tasks.view_depth = 1;
|
||||
assert_tasks!(tasks, [t11, t12]);
|
||||
assert_tasks_view!(tasks, [t11, t12]);
|
||||
tasks.view_depth = 2;
|
||||
assert_tasks!(tasks, [t111, t12]);
|
||||
assert_tasks_view!(tasks, [t111, t12]);
|
||||
tasks.view_depth = 9;
|
||||
assert_tasks!(tasks, [t111, t12]);
|
||||
assert_tasks_view!(tasks, [t111, t12]);
|
||||
|
||||
tasks.add_tag("tag");
|
||||
tasks.view_depth = 0;
|
||||
assert_tasks!(tasks, [t11]);
|
||||
assert_tasks_view!(tasks, [t11]);
|
||||
tasks.search_depth = 0;
|
||||
assert_eq!(tasks.view, []);
|
||||
assert_tasks!(tasks, []);
|
||||
assert_tasks_view!(tasks, []);
|
||||
}
|
||||
|
||||
#[test]
|
||||
|
|
Loading…
Reference in New Issue