Compare commits
5 Commits
d744fb8457
...
3d389e8d52
Author | SHA1 | Date |
---|---|---|
xeruf | 3d389e8d52 | |
xeruf | 28d1f4c983 | |
xeruf | 93fde86169 | |
xeruf | 769b9578fe | |
xeruf | c27ccb8282 |
|
@ -65,8 +65,8 @@ where
|
||||||
|
|
||||||
/// Build a task with informational output and optional labeled kind
|
/// Build a task with informational output and optional labeled kind
|
||||||
pub(crate) fn build_task(name: &str, tags: Vec<Tag>, kind: Option<(&str, Kind)>) -> EventBuilder {
|
pub(crate) fn build_task(name: &str, tags: Vec<Tag>, kind: Option<(&str, Kind)>) -> EventBuilder {
|
||||||
info!("Created {}task \"{name}\" with tags [{}]",
|
info!("Created {} \"{name}\" with tags [{}]",
|
||||||
kind.map(|k| k.0).unwrap_or_default(),
|
kind.map(|k| k.0).unwrap_or("task"),
|
||||||
tags.iter().map(format_tag).join(", "));
|
tags.iter().map(format_tag).join(", "));
|
||||||
EventBuilder::new(kind.map(|k| k.1).unwrap_or(TASK_KIND), name, tags)
|
EventBuilder::new(kind.map(|k| k.1).unwrap_or(TASK_KIND), name, tags)
|
||||||
}
|
}
|
||||||
|
|
24
src/main.rs
24
src/main.rs
|
@ -25,7 +25,6 @@ use tokio::sync::mpsc;
|
||||||
use tokio::sync::mpsc::Sender;
|
use tokio::sync::mpsc::Sender;
|
||||||
use tokio::time::error::Elapsed;
|
use tokio::time::error::Elapsed;
|
||||||
use tokio::time::timeout;
|
use tokio::time::timeout;
|
||||||
use xdg::BaseDirectories;
|
|
||||||
|
|
||||||
use crate::helpers::*;
|
use crate::helpers::*;
|
||||||
use crate::kinds::{BASIC_KINDS, PROPERTY_COLUMNS, PROP_KINDS, TRACKING_KIND};
|
use crate::kinds::{BASIC_KINDS, PROPERTY_COLUMNS, PROP_KINDS, TRACKING_KIND};
|
||||||
|
@ -169,7 +168,7 @@ async fn main() -> Result<()> {
|
||||||
);
|
);
|
||||||
builder.init();
|
builder.init();
|
||||||
|
|
||||||
let config_dir = or_warn!(BaseDirectories::new(), "Could not determine config directory")
|
let config_dir = or_warn!(xdg::BaseDirectories::new(), "Could not determine config directory")
|
||||||
.and_then(|d| or_warn!(d.create_config_directory("mostr"), "Could not create config directory"))
|
.and_then(|d| or_warn!(d.create_config_directory("mostr"), "Could not create config directory"))
|
||||||
.unwrap_or(PathBuf::new());
|
.unwrap_or(PathBuf::new());
|
||||||
let keysfile = config_dir.join("key");
|
let keysfile = config_dir.join("key");
|
||||||
|
@ -438,18 +437,27 @@ async fn main() -> Result<()> {
|
||||||
Some(',') =>
|
Some(',') =>
|
||||||
match arg {
|
match arg {
|
||||||
None => {
|
None => {
|
||||||
tasks.get_current_task().map_or_else(
|
match tasks.get_current_task() {
|
||||||
|| info!("With a task selected, use ,NOTE to attach NOTE and , to list all its notes"),
|
None => {
|
||||||
|task| println!("{}", task.description_events().map(|e| format!("{} {}", format_timestamp_local(&e.created_at), e.content)).join("\n")),
|
info!("With a task selected, use ,NOTE to attach NOTE and , to list all its notes");
|
||||||
);
|
tasks.recurse_activities = !tasks.recurse_activities;
|
||||||
continue 'repl;
|
info!("Toggled activities recursion to {}", tasks.recurse_activities);
|
||||||
|
}
|
||||||
|
Some(task) => {
|
||||||
|
println!("{}",
|
||||||
|
task.description_events()
|
||||||
|
.map(|e| format!("{} {}", format_timestamp_local(&e.created_at), e.content))
|
||||||
|
.join("\n"));
|
||||||
|
continue 'repl;
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
Some(arg) => {
|
Some(arg) => {
|
||||||
if arg.len() < CHARACTER_THRESHOLD {
|
if arg.len() < CHARACTER_THRESHOLD {
|
||||||
warn!("Note needs at least {CHARACTER_THRESHOLD} characters!");
|
warn!("Note needs at least {CHARACTER_THRESHOLD} characters!");
|
||||||
continue 'repl;
|
continue 'repl;
|
||||||
}
|
}
|
||||||
tasks.make_note(arg)
|
tasks.make_note(arg);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -94,9 +94,10 @@ impl Task {
|
||||||
self.event.kind == TASK_KIND
|
self.event.kind == TASK_KIND
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// Whether this is an actionable task - false if stateless
|
||||||
pub(crate) fn is_task(&self) -> bool {
|
pub(crate) fn is_task(&self) -> bool {
|
||||||
self.is_task_kind() ||
|
self.is_task_kind() ||
|
||||||
self.states().next().is_some()
|
self.props.iter().any(|event| State::try_from(event.kind).is_ok())
|
||||||
}
|
}
|
||||||
|
|
||||||
fn states(&self) -> impl DoubleEndedIterator<Item=TaskState> + '_ {
|
fn states(&self) -> impl DoubleEndedIterator<Item=TaskState> + '_ {
|
||||||
|
@ -134,7 +135,7 @@ impl Task {
|
||||||
self.state().unwrap_or_else(|| self.default_state())
|
self.state().unwrap_or_else(|| self.default_state())
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Returns None for a stateless task.
|
/// Returns None for activities.
|
||||||
pub(crate) fn state_label(&self) -> Option<ColoredString> {
|
pub(crate) fn state_label(&self) -> Option<ColoredString> {
|
||||||
self.state()
|
self.state()
|
||||||
.or_else(|| Some(self.default_state()).filter(|_| self.is_task()))
|
.or_else(|| Some(self.default_state()).filter(|_| self.is_task()))
|
||||||
|
|
71
src/tasks.rs
71
src/tasks.rs
|
@ -68,7 +68,7 @@ pub(crate) struct TasksRelay {
|
||||||
view: Vec<EventId>,
|
view: Vec<EventId>,
|
||||||
search_depth: usize,
|
search_depth: usize,
|
||||||
view_depth: usize,
|
view_depth: usize,
|
||||||
pub(crate) recurse_stateless_tasks: bool,
|
pub(crate) recurse_activities: bool,
|
||||||
|
|
||||||
/// Currently active tags
|
/// Currently active tags
|
||||||
tags: BTreeSet<Tag>,
|
tags: BTreeSet<Tag>,
|
||||||
|
@ -169,7 +169,7 @@ impl TasksRelay {
|
||||||
state: Default::default(),
|
state: Default::default(),
|
||||||
search_depth: 4,
|
search_depth: 4,
|
||||||
view_depth: 0,
|
view_depth: 0,
|
||||||
recurse_stateless_tasks: true,
|
recurse_activities: true,
|
||||||
|
|
||||||
sender,
|
sender,
|
||||||
overflow: Default::default(),
|
overflow: Default::default(),
|
||||||
|
@ -388,9 +388,12 @@ impl TasksRelay {
|
||||||
if !self.state.matches(task) {
|
if !self.state.matches(task) {
|
||||||
return vec![]
|
return vec![]
|
||||||
}
|
}
|
||||||
let mut new_depth = depth - 1;
|
let mut new_depth = depth;
|
||||||
if sparse && new_depth > self.view_depth && self.filter(task) {
|
if !self.recurse_activities || task.is_task() {
|
||||||
new_depth = self.view_depth;
|
new_depth = depth - 1;
|
||||||
|
if sparse && new_depth > self.view_depth && self.filter(task) {
|
||||||
|
new_depth = self.view_depth;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
if new_depth > 0 {
|
if new_depth > 0 {
|
||||||
let mut children = self.resolve_tasks_rec(self.tasks.children_of(&task), sparse, new_depth);
|
let mut children = self.resolve_tasks_rec(self.tasks.children_of(&task), sparse, new_depth);
|
||||||
|
@ -450,7 +453,7 @@ impl TasksRelay {
|
||||||
if current.is_empty() {
|
if current.is_empty() {
|
||||||
println!("No tasks here matching{}", self.get_prompt_suffix());
|
println!("No tasks here matching{}", self.get_prompt_suffix());
|
||||||
} else {
|
} else {
|
||||||
println!("Found some matching tasks beyond specified search depth:");
|
println!("Found matching tasks beyond specified search depth:");
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -1082,26 +1085,25 @@ impl TasksRelay {
|
||||||
Some(self.set_state_for(*id, comment, state))
|
Some(self.set_state_for(*id, comment, state))
|
||||||
}
|
}
|
||||||
|
|
||||||
pub(crate) fn make_note(&mut self, note: &str) {
|
pub(crate) fn make_note(&mut self, note: &str) -> EventId {
|
||||||
if let Some(id) = self.get_position_ref() {
|
if let Some(id) = self.get_position_ref() {
|
||||||
if self.get_by_id(id).is_some_and(|t| t.is_task()) {
|
if self.get_by_id(id).is_some_and(|t| t.is_task()) {
|
||||||
let prop = build_prop(Kind::TextNote, note.trim(), *id);
|
let prop = build_prop(Kind::TextNote, note.trim(), *id);
|
||||||
self.submit(prop);
|
return self.submit(prop)
|
||||||
return;
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
let (input, tags) = extract_tags(note.trim());
|
let (input, tags) = extract_tags(note.trim());
|
||||||
self.submit(
|
self.submit(
|
||||||
build_task(input, tags, Some(("stateless ", Kind::TextNote)))
|
build_task(input, tags, Some(("activity", Kind::TextNote)))
|
||||||
.add_tags(self.parent_tag())
|
.add_tags(self.parent_tag())
|
||||||
.add_tags(self.tags.iter().cloned())
|
.add_tags(self.tags.iter().cloned())
|
||||||
);
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
// Properties
|
// Properties
|
||||||
|
|
||||||
pub(crate) fn set_view_depth(&mut self, depth: usize) {
|
pub(crate) fn set_view_depth(&mut self, depth: usize) {
|
||||||
info!("Changed view depth to {depth}");
|
info!("Showing {depth} subtask levels");
|
||||||
self.view_depth = depth;
|
self.view_depth = depth;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -1112,7 +1114,7 @@ impl TasksRelay {
|
||||||
} else {
|
} else {
|
||||||
info!("Changed search depth to {depth}");
|
info!("Changed search depth to {depth}");
|
||||||
}
|
}
|
||||||
self.view_depth = depth;
|
self.search_depth = depth;
|
||||||
}
|
}
|
||||||
|
|
||||||
pub(crate) fn get_columns(&mut self) -> &mut Vec<String> {
|
pub(crate) fn get_columns(&mut self) -> &mut Vec<String> {
|
||||||
|
@ -1500,6 +1502,7 @@ mod tasks_test {
|
||||||
tasks.move_to(Some(parent));
|
tasks.move_to(Some(parent));
|
||||||
let pin = tasks.make_task("pin");
|
let pin = tasks.make_task("pin");
|
||||||
|
|
||||||
|
tasks.search_depth = 1;
|
||||||
assert_eq!(tasks.filtered_tasks(None, true).len(), 2);
|
assert_eq!(tasks.filtered_tasks(None, true).len(), 2);
|
||||||
assert_eq!(tasks.filtered_tasks(None, false).len(), 2);
|
assert_eq!(tasks.filtered_tasks(None, false).len(), 2);
|
||||||
assert_eq!(tasks.filtered_tasks(Some(&zero), false).len(), 0);
|
assert_eq!(tasks.filtered_tasks(Some(&zero), false).len(), 0);
|
||||||
|
@ -1515,17 +1518,18 @@ mod tasks_test {
|
||||||
assert_eq!(tasks.filtered_tasks(Some(&zero), false), vec![tasks.get_by_id(&pin).unwrap()]);
|
assert_eq!(tasks.filtered_tasks(Some(&zero), false), vec![tasks.get_by_id(&pin).unwrap()]);
|
||||||
|
|
||||||
tasks.move_to(None);
|
tasks.move_to(None);
|
||||||
assert_eq!(tasks.view_depth, 1);
|
assert_eq!(tasks.view_depth, 0);
|
||||||
assert_tasks!(tasks, [pin, test, parent]);
|
assert_tasks!(tasks, [pin, test, parent]);
|
||||||
tasks.set_view_depth(2);
|
tasks.set_view_depth(1);
|
||||||
assert_tasks!(tasks, [pin, test]);
|
assert_tasks!(tasks, [pin, test]);
|
||||||
tasks.add_tag("tag".to_string());
|
tasks.add_tag("tag".to_string());
|
||||||
assert_tasks!(tasks, [test]);
|
assert_tasks!(tasks, [test]);
|
||||||
assert_eq!(tasks.filtered_tasks(None, true), vec![tasks.get_by_id(&test).unwrap()]);
|
assert_eq!(tasks.filtered_tasks(None, true), vec![tasks.get_by_id(&test).unwrap()]);
|
||||||
|
|
||||||
tasks.submit(EventBuilder::new(Kind::Bookmarks, "", []));
|
tasks.submit(EventBuilder::new(Kind::Bookmarks, "", []));
|
||||||
tasks.clear_filters();
|
tasks.clear_filters();
|
||||||
assert_tasks!(tasks, [pin, test]);
|
assert_tasks!(tasks, [pin, test]);
|
||||||
tasks.set_view_depth(1);
|
tasks.set_view_depth(0);
|
||||||
assert_tasks!(tasks, [test, parent]);
|
assert_tasks!(tasks, [test, parent]);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -1534,7 +1538,8 @@ mod tasks_test {
|
||||||
let mut tasks = stub_tasks();
|
let mut tasks = stub_tasks();
|
||||||
tasks.make_task_and_enter("proc: tags", State::Procedure);
|
tasks.make_task_and_enter("proc: tags", State::Procedure);
|
||||||
assert_eq!(tasks.get_own_events_history().count(), 1);
|
assert_eq!(tasks.get_own_events_history().count(), 1);
|
||||||
let side = tasks.submit(build_task("side", vec![tasks.make_event_tag(&tasks.get_current_task().unwrap().event, MARKER_DEPENDS)], None));
|
let side = tasks.submit(
|
||||||
|
build_task("side", vec![tasks.make_event_tag(&tasks.get_current_task().unwrap().event, MARKER_DEPENDS)], None));
|
||||||
assert_eq!(tasks.visible_tasks(),
|
assert_eq!(tasks.visible_tasks(),
|
||||||
Vec::<&Task>::new());
|
Vec::<&Task>::new());
|
||||||
let sub_id = tasks.make_task("sub");
|
let sub_id = tasks.make_task("sub");
|
||||||
|
@ -1632,18 +1637,19 @@ mod tasks_test {
|
||||||
fn test_depth() {
|
fn test_depth() {
|
||||||
let mut tasks = stub_tasks();
|
let mut tasks = stub_tasks();
|
||||||
|
|
||||||
let t1 = tasks.make_task("t1");
|
let t1 = tasks.make_note("t1");
|
||||||
let task1 = tasks.get_by_id(&t1).unwrap();
|
let activity_t1 = tasks.get_by_id(&t1).unwrap();
|
||||||
assert_eq!(tasks.view_depth, 1);
|
assert!(!activity_t1.is_task());
|
||||||
assert_eq!(task1.pure_state(), State::Open);
|
assert_eq!(tasks.view_depth, 0);
|
||||||
|
assert_eq!(activity_t1.pure_state(), State::Open);
|
||||||
debug!("{:?}", tasks);
|
debug!("{:?}", tasks);
|
||||||
assert_eq!(tasks.visible_tasks().len(), 1);
|
assert_eq!(tasks.visible_tasks().len(), 1);
|
||||||
tasks.view_depth = 0;
|
tasks.search_depth = 0;
|
||||||
assert_eq!(tasks.visible_tasks().len(), 0);
|
assert_eq!(tasks.visible_tasks().len(), 0);
|
||||||
|
|
||||||
tasks.move_to(Some(t1));
|
tasks.move_to(Some(t1));
|
||||||
assert_position!(tasks, t1);
|
assert_position!(tasks, t1);
|
||||||
tasks.view_depth = 2;
|
tasks.search_depth = 2;
|
||||||
assert_eq!(tasks.visible_tasks().len(), 0);
|
assert_eq!(tasks.visible_tasks().len(), 0);
|
||||||
let t11 = tasks.make_task("t11: tag");
|
let t11 = tasks.make_task("t11: tag");
|
||||||
assert_eq!(tasks.visible_tasks().len(), 1);
|
assert_eq!(tasks.visible_tasks().len(), 1);
|
||||||
|
@ -1678,17 +1684,28 @@ mod tasks_test {
|
||||||
assert_tasks!(tasks, [t111, t12]);
|
assert_tasks!(tasks, [t111, t12]);
|
||||||
tasks.set_view(vec![t11]);
|
tasks.set_view(vec![t11]);
|
||||||
assert_tasks!(tasks, [t11]); // No more depth applied to view
|
assert_tasks!(tasks, [t11]); // No more depth applied to view
|
||||||
tasks.set_view_depth(1);
|
tasks.set_search_depth(1); // resets view
|
||||||
|
assert_tasks!(tasks, [t111, t12]);
|
||||||
|
tasks.set_view_depth(0);
|
||||||
assert_tasks!(tasks, [t11, t12]);
|
assert_tasks!(tasks, [t11, t12]);
|
||||||
|
|
||||||
tasks.move_to(None);
|
tasks.move_to(None);
|
||||||
assert_tasks!(tasks, [t1]);
|
|
||||||
tasks.view_depth = 2;
|
|
||||||
assert_tasks!(tasks, [t11, t12]);
|
assert_tasks!(tasks, [t11, t12]);
|
||||||
tasks.view_depth = 3;
|
tasks.recurse_activities = false;
|
||||||
|
assert_tasks!(tasks, [t1]);
|
||||||
|
tasks.view_depth = 1;
|
||||||
|
assert_tasks!(tasks, [t11, t12]);
|
||||||
|
tasks.view_depth = 2;
|
||||||
assert_tasks!(tasks, [t111, t12]);
|
assert_tasks!(tasks, [t111, t12]);
|
||||||
tasks.view_depth = 9;
|
tasks.view_depth = 9;
|
||||||
assert_tasks!(tasks, [t111, t12]);
|
assert_tasks!(tasks, [t111, t12]);
|
||||||
|
|
||||||
|
tasks.add_tag("tag".to_string());
|
||||||
|
tasks.view_depth = 0;
|
||||||
|
assert_tasks!(tasks, [t11]);
|
||||||
|
tasks.search_depth = 0;
|
||||||
|
assert_eq!(tasks.view, []);
|
||||||
|
assert_tasks!(tasks, []);
|
||||||
}
|
}
|
||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
|
|
Loading…
Reference in New Issue